pax_global_header00006660000000000000000000000064124103414160014506gustar00rootroot0000000000000052 comment=47994115f1324e88c66806e555252c1e4c173b2e dynamips-0.2.14/000077500000000000000000000000001241034141600134165ustar00rootroot00000000000000dynamips-0.2.14/.gitignore000066400000000000000000000000721241034141600154050ustar00rootroot00000000000000.DS_Store */.DS_Store .project /build/* /CMakeLists.txt.* dynamips-0.2.14/.travis.yml000066400000000000000000000014151241034141600155300ustar00rootroot00000000000000language: c compiler: - gcc - clang env: - DYNAMIPS_ARCH=amd64 ARCH=:amd64 CMAKE_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu - DYNAMIPS_ARCH=x86 ARCH=:i386 CMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu - DYNAMIPS_ARCH=nojit ARCH= CMAKE_LIBRARY_PATH= before_install: sudo apt-get update -qq install: - sudo apt-get install -q libelf-dev$ARCH - sudo apt-get --no-install-recommends install -q uuid-dev$ARCH - sudo apt-get --no-install-recommends install -q libpcap0.8-dev$ARCH - sudo apt-get install -q libc6-dev-i386 before_script: - mkdir build - cd build script: - cmake -DDYNAMIPS_ARCH=$DYNAMIPS_ARCH -DDYNAMIPS_CODE=both -DDYNAMIPS_RENAME= -DANY_COMPILER=1 -DCMAKE_LIBRARY_PATH=$CMAKE_LIBRARY_PATH -DCMAKE_INSTALL_PREFIX=$(pwd)/test .. - make install dynamips-0.2.14/CMakeLists.txt000066400000000000000000000036441241034141600161650ustar00rootroot00000000000000# dynamips - build system # Control variables: # - DYNAMIPS_RENAME : rename executable to dynamips (auto;stable;unstable;) # - DYNAMIPS_CODE : set default code (stable;unstable;both) # - DYNAMIPS_ARCH : build target architecture (amd64;x86;nojit) # - ANY_COMPILER : skip compiler check (assumes gcc) # - CMAKE_INSTALL_PREFIX : where to install set ( CMAKE_LEGACY_CYGWIN_WIN32 0 ) # Remove when CMake >= 2.8.4 is required cmake_minimum_required ( VERSION 2.8 ) cmake_policy ( VERSION 2.8 ) cmake_policy ( SET CMP0017 NEW ) # Prefer files from the CMake module directory when including from there. foreach ( _dir "/usr/sfw" # search in "Sun FreeWare" (SunOS) "/opt/csw" # search in "OpenCSW" (SunOS) ) if ( IS_ABSOLUTE "${_dir}" AND IS_DIRECTORY "${_dir}" ) list ( APPEND CMAKE_PREFIX_PATH "${_dir}" ) endif () endforeach () if ( CMAKE_PREFIX_PATH ) message ( STATUS "CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" ) endif ( CMAKE_PREFIX_PATH ) list ( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ) message ( STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}" ) project ( dynamips C ) set ( DYNAMIPS_VERSION_TRAIN 0.2.14 ) set ( DYNAMIPS_VERSION_SUB ) include ( utils ) include ( dependencies ) include ( configure ) add_subdirectory ( man ) add_subdirectory ( common ) add_subdirectory ( stable ) add_subdirectory ( unstable ) install_docs ( "ChangeLog" "COPYING" "MAINTAINERS" "README" "README.hypervisor" "RELEASE-NOTES" "TODO" ) # uninstall target - leaves behind any directory created during install set ( _templatefile "${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" ) set ( _scriptfile "${CMAKE_BINARY_DIR}/cmake_uninstall.cmake" ) configure_file ( "${_templatefile}" "${_scriptfile}" IMMEDIATE @ONLY ) add_custom_target ( uninstall COMMAND ${CMAKE_COMMAND} -P "${_scriptfile}" COMMENT "Uninstall files listed in install_manifest.txt" ) print_summary () dynamips-0.2.14/COPYING000066400000000000000000000431031241034141600144520ustar00rootroot00000000000000 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. dynamips-0.2.14/ChangeLog000066400000000000000000003023301241034141600151710ustar00rootroot00000000000000/* * Cisco router simulation platform. */ 15-Aug-2005, the project is starting. 18-Aug-2005 ----------- - Added support for: sll, addu/subu. - Fixed bugs with b... branch instructions (bad computation of new pc). - Added patch recording for blocks. - A sequence of MIPS instructions (without memory access instructions) from IOS was successfully run ! (need to check correctness). 22-Aug-2005 ----------- - Implemented a basic memory subsystem with an mmaped file. - Added memory access instructions (lb,lbu,lh,lhu,lw,lwu,ld,sb,sh,sw,sd). - Added a cross-compiled test program. 23-Aug-2005 ----------- - Fixed slti, sltiu, slt instructions. - Fixed memory endianness. - Fixed host memory addressing (bad haddr computation). 24-Aug-2005 ----------- - Added a generic memory address lookup function. - Added a minimal ELF loader. 28-Aug-2005 ----------- - ELF loader now loads files page per page. - Added device support (with RAM support). - Added MTS32 support for mapping of 4 Kb pages. - Added MTS32 support for device mapping in kernel mode and generic virtual to physical mapping (preparation for TLB). - Added a dummy console driver. - Added mult,multu,div,divu instructions. 29-Aug-2005 ----------- - Added a Red-Black trees module. - Block locator uses RB tree module. - It is now possible to run a program with multiple functions! - ELF loader now support multiple program headers and entry point is now used. - Fixed x86 buffer adjustment. - Modified console to be at the same address than c7200 DUART (to test microcode). - Memory access functions now update PC. - Added sra, srl, sllv, srlv, srav instructions. 30-Aug-2005 ----------- - Added dsrl32, dsll32, dsra, dsrav, dsra32 instructions. - Added bgtz, blez, bgez instructions. - Fixed sltu instruction. - ELF Entry point is now sign-extended. - Added blezl, bgtzl instructions. - Fixed the MIPS scanner to use unsigned 64-bits integers for addresses. - Added unknown opcode management (basic, it just print the unhandled opcode value and continues). - Added cache instruction. - Added bltzal, bltzall, bgezal, bgezall instructions. 31-Aug-2005 ----------- - Added System coprocessor (CP0) definitions. - Added tlbr instruction. - Added basic tlbwi instruction (doesn't change the MTS mappings now). - Added dmfc0, dmtc0, mfc0, mtc0 instructions. - Added TLB dump functions. - Added dummy pref/prefi instructions. - Added basic exception trigger. - Added syscall, break instructions. 01-Sep-2005 ----------- - Fixed exception trigger. - Added dmfc1, dmtc1, mfc1, mtc1, sdc1 instructions. - Fixed the break instruction. - Modified the memory handling to be more friendly with instructions like sdc1 and for more safety in case of badly written device drivers. - Added ldc1 instruction. 02-Sep-2005 ----------- - Fixed sltiu/sltu instructions that were doing signed comparisons instead of unsigned comparisons (mis-use of jit). - Added lwr/lwl, swr/swl, ldl/ldr, sdr/sdl, ll/sc instructions (not tested). - Added PCI bus management. - Added GT64010 (PCI controller) device. - Added DEC 21050 (PCI bridge) device. - Added NVRAM. 03-Sep-2005 ----------- - Beginning of work on IRQs. 04-Sep-2005 ----------- - Timer IRQ should be OK. - Added C7200 bay management and Midplane FPGA. - Fixed the JR instruction which was broken in a case like that: lwu k1,0(k1) jr k1 move k1,zero 05-Sep-2005 ----------- - Fixed jalr (as jr) instruction. - Added RAM "aliasing". - Added SRAM management. - Fixed NVRAM size. - Fixed ldr, swl/swr instructions. - Added c7200 IOcard. - Count register is now properly handled. - Added basic functions to set/clear interrupts. - Modified the console driver to trigger DUART interrupt. - IOS now starts! 06-Sep-2005 ----------- - Added "move" virtual instruction. It correspond to "addu" with rt=0. Here it avoids an useless add. - Added b/bal virtual instructions. - Packed NVRAM. 08-Sep-2005 ----------- - Added Dallas DS1620 temperature sensors. - Added voltage sensors. - Power supplies are now faked. - IOS Environmental Monitor doesn't try to shutdown the router anymore! - Device list is now displayed at startup. 18-Sep-2005 ----------- - Added Address Error Load/Save, TLB Load/Save exceptions. TODO: Branch Delay Slot management. - Modified the default value in config register in CP0 to set appropriate cache sizes. Now it is possible to boot compressed images directly! - Added the teq/teqi instructions. - Hmm, seen some IOS images (k9?) that seem to use the FPU (CP1). This does not prevent the image to boot, but some features will probably be not functional. 19-Sep-2005 ----------- - Tweaked the console driver to generate a dummy interrupt. It avoids the console problems. - Fixed the serial driver mueslix PCI vendor/device code. - The midplane FPGA now returns voltages based on virtual PA EEPROM presence. Up to 6 PA-8T were successfully emulated! 20-Sep-2005 ----------- - Included amd64-codegen.h with a (near empty) translator. - Some code factorization. 21-Sep-2005 ----------- - Added CPU state save/restore on disk. - Sending SIGQUIT to the process dumps the MIPS64 VM state on disk. (only CPU state is saved for now). Device state is not saved, so this does not work with booted IOS images for now (it works for an IOS image being self-decompressing, though). - Some code cleanups. 23-Sep-2005 ----------- - Fixed "Dirty" bit in TLB entries. - Extended the number of TLB entries from 48 to 64. - Added tlbp instruction. - Modified the nmc93c46 eeprom driver to allow more than 32 bytes reading. 24-Sep-2005 ----------- - Added a symbol table loader. 26-Sep-2005 ----------- - Added support for writing in PCI registers - Added a RAW file loader. - Added "add" instruction (without exception support). 07-Oct-2005 ----------- - Continued work on SMP support. 08-Oct-2005 ----------- - Replaced memory operations with fastcalls instead of asmlinkage functions. - Continued work on SMP support. Now it should be complete, except for LL/SC instructions. 10-Oct-2005 ----------- - A lot of work on amd64 jit compiler. - The memory management should be 64-bit compliant now. 17-Oct-2005 ----------- - Fixed a bug in SRA instruction in amd64 jit code. Dynamips is now working on amd64! 26-Oct-2005 ----------- - Created a virtual device giving the VM parameters. - Added command line parsing (patch from Nicolas Szalay). - Fixed ERET instruction (LLbit clearing was lacking). - Fixed BREAK instruction (x86/amd64). - Fixed 2 GB limit for log file. - Fixed clobbered registers in x86 translation. 28-Oct-2005 ----------- - Added a non-JIT mode (not completely working now) - Added "-j" flag on the command line to disable JIT. - Fixed some stupid bugs in shift operations. 31-Oct-2005 ----------- - Thanks to the great help of mtve, we have a working DEC21140 Ethernet driver and now the virtual C7200 can see the real world!!! 01-Nov-2005 ----------- - Beginning of a NETIO module to handle network I/O with external world. - Studied (a bit) the PA-8T controller on a real router. - It is now possible to set the base MAC address of the router using the "-m" flag. - Improved logging - The dec21140 code is generalized and it is now possible to have a PA-FE-TX in any slot. 02-Nov-2005 ----------- - Fixed some problems with the NVRAM when it is empty ; - Added a "net_io.c" module that is an abstraction layer for network communications (TAP, Unix sockets...) - Added the "virtual" MIPS "li" instruction which is in fact "addiu reg,zero,value". This permits to have a more optimized JIT code. - Code factorization in the DEC21140 module. - mtve (aka "Mtv Europe") sent a patch for much better console handling, merge is planned on 03-Nov. - Added a "PA driver" framework. 03-Nov-2005 ----------- - The console is better handled: special keys are working, and there is a FIFO buffer to avoid character loss (added a "dev_vtty.c" module). - Merging with Mtve patch, that cleans NETIO module and adds features to the console. - Added a clock divisor hack, but it is not completely working. todo: implement a command line option for this. 08-Nov-2005 ----------- - Mtve added tcp client/server NETIO. - The clock divisor is now working! It is set by the "-k" command line option. - Fixed the csr5 register handling in DEC21140 driver code. 13-Nov-2005 ----------- - A lot of cosmetic changes from Mtve. - Modified the infrastructure to allow the use of CPU groups that share a certain number of devices (per-CPU device array). All device drivers have been modified to handle this. - Took a look at LSI ATMizer II+, which is the circuit used on PA-A3-OC3 boards. The ATMizer is based on a MIPS CPU, so it should be possible to emulate it. - Added a per-CPU dedicated thread. 23-Nov-2005 ----------- - The PA-A1 ATM stuff is working!!! 24-Nov-2005 ----------- - Fixed a bug in the PA-A1 driver in TX buffer chaining. 25-Nov-2005 ----------- - Address bus masking, which clears bits 32 and 33 of physical addresses. It avoids the use of device aliasing. - Fixed the DEC21140 TX part which was incomplete. 29-Nov-2005 ----------- - There are still bugs in big packet forwarding. This is probably the ATM driver (added more debugging info). - Fabien Devaux wrote a README explaining the command line options. 30-Nov-2005 ----------- - Code cleanup in the net_io module. - Added the "ptask" (periodic tasks) module, this is mainly for TX ring scanning of network devices. 01-Dec-2005 ----------- - Added a memory logger (FIFO of 10 accesses), this was useful to debug the PA-A1 RX problem. - Fixed a RX buffer chaining problem in PA-A1. 02-Dec-2005 ----------- - Added support for PA-A1 reset ("clear int", "shut/no shut"). - Optimized the IRQ handling (irq_pending flag in CPU structure is used as a "cache"). 05-Dec-2005 ----------- - Optimized the cp0 count/compare process for the x86_64 platform (unfortunately, this doesn't work for the x86 platform). 06-Dec-2005 ----------- - Modified the NVRAM handling to allow calendar emulation ("sh calendar" is now working). 12-Dec-2005 ----------- - Added support for the NPE-400. - Playing with the PA-POS-2OC3 (called "Charlotte" by Cisco). 15-Dec-2005 ----------- - Added teq/teqi instructions to the x86_64 jit. 20-Dec-2005 ----------- - Added a Virtual ATM switch fabric to emulate an ATM backbone. 22-Dec-2005 ----------- - Final release for 0.2.2 train (0.2.2i). 23-Dec-2005 ----------- - Starting 0.2.3 train (0.2.3a) - Added proper NPE-150 support. - Added bit-bucket support (zeroed memory zone). - Added NPE type selection. 24-Dec-2005 ----------- - Added beginning of support for NPE-300 (endianness problems with PCI controllers ?) 28-Dec-2005 ----------- - Fixed the endianness problem with the NPE-300. - Added NPE300-IO-FE. 31-Dec-2005 ----------- - Added CLI option to load a symbol file. - Moved JIT code from dynamips.c to mips64_jit.c - Added support for NPE-100. - Updated the README file for NPE type selection. - Suppressed the annoying message "%%Error: Unrecognized I/O card in bay 0" 03-Jan-2006 ----------- - The bit clearing in physical addresses was done stupidly. Only the bit 33 is used to bypass L2 caching. Re-introduced SRAM aliasing. PHY address 0x100000000 is in fact for PCI I/O space. - Began to play with the Cirrus Logic PD6729. - Added Bootflash based on Intel 28F008SA chips (1 Mb) - Fixed the alignment error occuring with "show version". 04-Jan-2006 ----------- - Better understanding of Flash SIMM register with bank number and bank size: modified the Flash SIMM code to handle a 8 Mb flash in 1 bank (emulation of Intel 28F016SA chip) which should be sufficient to store crashinfo files or misc files like configs,... - Added ROM remote logging capabilities (with remote emulator control). 05-Jan-2006 ----------- - Integrated HEC and AAL5 field computation functions found at: http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/ (Author is Charles Michael Heard). - Added HEC field management to the virtual ATM switch fabric. - Added HEC and AAL5 CRC fields to the TX part of the TNETA1570 driver. 06-Jan-2006 ----------- - Added block timestamping, to evaluate use of JIT blocks and eventually enhance algorithm of block classifying. - Added a flag to the virtual devices to prevent use of MMAPed zones by MTS (used by NVRAM driver). - Added functions to extract IOS configuration from NVRAM. Pressing "Ctrl-] + c" nows write configuration to disk at any time. 08-Jan-2006 ----------- - Added support for PEM (NPE-B) EEPROM. It will probably allow support of NPE-175/NPE-225. 09-Jan-2006 ----------- - Added support for NPE-175/NPE-225 and added NPEB-IO-FE. 10-Jan-2006 ----------- - Enhancements to the remote console over TCP port. - Port to the Cygwin environment, allowing the emulator to run on Windows machines! 11-Jan-2006 ----------- - Added a feature to push an IOS configuration into NVRAM ("-C" command line option). - Added enhanced checks to the NVRAM config export. - Added a variant of udp_connect() function for systems that do not support RFC 2553. - Added support for AUX port (TCP port). 12-Jan-2006 ----------- - Better memory logger. - Added breakpoint capabilities. - Fixed a segfault occuring on some IOS images with x86_64 JIT. (modified the minimum remaining size in JIT buffer adjustment). 13-Jan-2006 ----------- - The ROM is now embedded in the source code (added an utility to convert ELF file to C code). It is possible to use an alternate ROM using the "-R" option. - Enhanced the MTS lookups to allow use of embedded ROM for code execution. 15-Jan-2006 ----------- - Added PCI I/O space management. - Added a fake Cirrus Logic PD6729 PCI-to-PCMCIA host adapter. - Cleanups in NPE initialization code. - Fixed some bridge problems with the NPE-300. - Code factorization in the C7200 initialization module. - Generalization of C7200-IO-FE driver for all NPE types. - Added Fault History support. - Added chassis selection (standard or VXR) - Finally found the bug in the non-jit emulation code! It was a cast problem in mult/multu instructions. 16-Jan-2006 ----------- - Added generic hash tables. - Added "instruction lookup tables" (ILT), which greatly improve performance of non-JIT mode. - Added instruction statistics for the non-JIT mode ("Ctrl-] + j") 17-Jan-2006 ----------- - Added a port for non-x86 hosts. The emulator ran successfully on a Tru64 Unix box (Alpha CPU)! It should also be able to compile and run on MacOS X (PowerPC). 24-Jan-2006 ----------- - Added support for DMA channels on Galileo GT64k controllers since it seems that they may be used by IOS (NPE-400 for sure, other NPE?). 25-Jan-2006 ----------- - Added Linux Raw Ethernet driver to the NetIO infrastructure. - Hmm, I'm probably missing something in the DMA/SRAM interaction and endianness... 26-Jan-2006 ----------- - The DMA problem is finally fixed, in fact the SRAM can be accessed with byte-swapping. - Added an unicast limiter to the DEC21140, to receive only trafic directed to the virtual machine (+ multicast/broadcast). This is done by analyzing the setup frames (MAC address filter). 27-Jan-2006 ----------- - The Linux Raw Ethernet driver needs promiscuous mode to be enabled to receive appropriate packets, added the required code. - Added endianness definitions, and replaced hton[sl]/ntoh[sl] by the new definitions (for exchanges between VM and host). 28-Jan-2006 ----------- - Added parser infrastructure. 29-Jan-2006 ----------- - Added a virtual bridge system. - Release 0.2.3b 31-Jan-2006 ----------- - Fixed a stupid bug in DMA handling, which in certain conditions can cause an infinite loop. 01-Feb-2006 ----------- - Added an "instruction block hash" which avoids lookups in the instruction red-black tree. The IBH table takes only 16 Kb of memory. - Added CRC-12 and CRC-16 functions for future use. - Added a "gen_eth" NetIO driver, which allow to access ethernet interfaces in a portable way (tested on Linux and Cygwin). - Fixes to the PowerPC build on Darwin thanks to Olivier Cahagne. - Minor cosmetic changes to display supported/unsupported PA and NPE. - Release 0.2.3c 06-Feb-2006 ----------- - Added working Serial interfaces (PA-4T+) ! - Bug: CDP not working with serial interfaces ("debug cdp events" reports a checksum problem). 07-Feb-2006 ----------- - Started the 0.2.4 release. - Modified the command line parser to allow many NIO per PA (for example for serials). - A lot of code cleanups in the C7200 initialization. - Added PA-8T support. - To fix: CDP with Serial interfaces: using cp_len (instead of cp_len+4) in packet rx fixes the problem but breaks IP. Probably something related to padding, see how a CDP packet is sent (special flag in tx ring ?) - Added virtual "beqz" instruction. - Added the "-X" option, allowing to use host memory directly instead of mapping a file to simulate RAM (faster). - Beginning of code reorganization to allow multiple instance contexts. 08-Feb-2006 ----------- - Added a basic virtual Frame-Relay switch. Unfortunately, LMI must be implemented to maintain links up. - Finally found the problem related to CDP! It was simply a padding problem. 09-Feb-2006 ----------- - Add support of promiscuous mode for the DEC21140 "unicast limiter": promiscuous mode is used in bridging configurations. - Fixed another bug with the serial interfaces: it seems that there is a "length substractor" for the txring. 10-Feb-2006 ----------- - Played a little with the PA-POS-OC3, without success. 14-Feb-2006 ----------- - Finally implemented the basic LMI stuff (only ANSI Annex D). Fortunately, IOS rocks and uses auto-detection, so no extra configuration is needed. - Integrated a Makefile patch from Max Khon for FreeBSD. - Updated documentation. - Release 0.2.4. 15-Feb-2006 ----------- - Fixed a cosmetic bug when displaying MAC address at startup. - Fixed a stupid bug in "srav" instruction in x86_64 JIT code. The arithmetic shift operation was done on a 64-bit basic instead of 32-bit, causing bad propagation of the sign-bit. - Added support for multiple instances (no CLI present yet). 17-Feb-2006 ----------- - Fixed 2 bugs in Frame-Relay switch: a crash occured when receiving a packet gave an error, and if many DLCI with the same ID on different interfaces were used, only one was announced on one interface. 18-Feb-2006 ----------- - Began work on advanced configuration parsing (instances). 19-Feb-2006 ----------- - Is the count/compare mechanism pertinent ? 23-Feb-2006 ----------- - Continued work on advanced config parsing (instances,NIO). - Added registry infrastructure. - Fixed some mistakes in documentation. 28-Feb-2006 ----------- - Continued work on advanced config parsing (NIO). - Added an IRQ counter. - Continued debugging on recurrent crashes problem. - Added proper checks to mts32_raw_lookup (at least it avoids the coredump...) - Added display of all CP0 registers (except TLB). 02-Mar-2006 ----------- - Hardened a little the IRQ subsystem, but this doesn't fix the problem. - Added a debugging mode to track jumps to address 0 (x86_64 only!). - Fixed the teq/teqi instructions in amd64/x86 modes that were buggy. 04-Mar-2006 ----------- - Added some assembly optimized routines for x86 hosts. - Fixed a stupid bug in mts32_raw_lookup (op_size and op_type were swapped). 06-Mar-2006 ----------- - Added Unix and Null NETIO types for config files. - Fixes for incorrect behavior in virtual memory access routines, in case of exception. 08-Mar-2006 ----------- - Removed the configuration file parsing, in favor of a remote control system for use with the dynagen project. 09-Mar-2006 ----------- - Added basic hypervisor infrastructure. - Added basic NIO code to the hypervisor commands. 10-Mar-2006 ----------- - Added the hypervisor NIO code. - Added Frame-Relay and ATM switches to the hypervisor commands. 11-Mar-2006 ----------- - Played (successfully) with OIR (Online Insertion and Removal). OIR will require device deletion... 12-Mar-2006 ----------- - Fixed EEPROM code which used global variables. - Added the necessary code for basic OIR. - Added PCI device removal. 15-Mar-2006 ----------- - A lot of work on C3600 integration (although it is not functional). - Added a basic ns16552 driver. 17-Mar-2006 ----------- - Added some hypervisor commands for PA setup and OIR. 20-Mar-2006 ----------- - Hardened the Mueslix driver: after OIR, a "no shut" on a new interface crashed the virtual router. Now, IRQ status is managed appropriately. 24-Mar-2006 ----------- - Added ptask (periodic task) removal. - Added a basic (nearly empty) C3600 IO FPGA chip. 25-Mar-2006 ----------- - Added a NIO RX multiplexer. - Added NIO unset operation for PA. 26-Mar-2006 ----------- - Modified the virtual ATM/Frame-Relay switches and NIO bridge to use the new NIO RX multiplexer. - Added the NIO bridges to the hypervisor commands. - Fixed a stupid bug in hypervisor ATM/FR switch command, the VC were badly parsed. - Fixed a memory leak in the hypervisor module (tokens not freed). 27-Mar-2006 ----------- - Added appropriate locks to the ATM and Frame-Relay switches. - Added VC removal for ATM and Frame-Relay switches. 28-Mar-2006 ----------- - Added registry management to the ATM/FR switches and NIO bridge. 30-Mar-2006 ----------- - Added a refcount system to the NIO RX multiplexer. 31-Mar-2006 ----------- - Added NIO removal for NIO bridges. - Added NIO bridge removal. - Modified the NIO bridge code to use the registry infrastructure. - Same as above, for ATM and Frame-Relay switches. 03-Apr-2006 ----------- - Added the required infrastructure for online removal. - Added hypervisor commands for PA removal and to display C7200 hardware. - Fixed a bug in PCI device removal. - It's now possible to change cards on the fly! - The Mueslix driver was incorrectly modified: the TX ring scanner must run even if the NIO is not defined. - Modified the DEC21140 driver to report a Link Down when the NIO is not defined. 04-Apr-2006 ----------- - Added packet dump (for debugging) to NetIO receive/send operations. 08-Apr-2006 ----------- - The SMP bug seems to be fixed! The irq_cause field is now manipulated atomically (inline asm for x86 and x86_64, pthread mutex on other machines). 12-Apr-2006 ----------- - Added a VM abstraction layer (for different platforms). - Fix of X86 assembly file for Cygwin. - The IOS config file is now saved with a specific filename from the VM instance. 13-Apr-2006 ----------- - Added per-instance logfile. 14-Apr-2006 ----------- - Fixed a stupid bug in non-JIT mode with IRQs. 17-Apr-2006 ----------- - bugfix: "null" NIO creation didn't record the NIO in registry. - bugfix: mueslix logging. - bugfix: adding an NIO to the RX listener list (bad double linked-list) - bugfix: bad fgets() use in NIO bridge and ATM/FR switches. 18-Apr-2006 ----------- - bugfix: the same NIO could be recorded many times in the NIO RX multiplexer due to bad design in NIO adding. 19-Apr-2006 ----------- - Beginning of work on AMD Am79c971 ethernet controller (used by PA-4E and PA-8E). 20-Apr-2006 ----------- - The emulation of AMD Am79c971 seems to be working very well. - Modified the physical memory dump function to use the VM log file. 21-Apr-2006 ----------- - Modified the PA-4E/PA-8E EEPROM definitions to work with VXR midplanes. - PA-4E is now usable. 23-Apr-2006 ----------- - Added "shutdown" operation for PA-4E/PA-8E drivers. 24-Apr-2006 ----------- - Enhanced lock file management (with POSIX locks). - The devices are now dependent from VMs instead of being dependent of CPU groups. This makes a lot of things cleaner. 27-Apr-2006 ----------- - Added IRQ clearing with GT64K (DMA transfers). - Study of GT64120 controller to understand address decoding process. 28-Apr-2006 ----------- - Fixed a bug with NPE-175/NPE-225 PEM EEPROM selection. - The NPE-400 is now able to support 512 Mb of DRAM! (the supplemental memory is in fact considered as IO memory) - Added a safety check with RAM size. - The CPU identifier (PRID register) is now fixed depending on the NPE board. 01-May-2006 ----------- - Modified device subsystem to order devices by physical addresses, optimized dev_lookup/dev_lookup_next. - Added infrastructure for various object cleanup. - Added "shutdown" code for SRAM, NVRAM, Bootflash, Zero, remote control devices. - Added CPU deletion support (incomplete). 02-May-2006 ----------- - Added PCI bus removal. - Added "shutdown" code for GT64010 and GT64120. - Added CFC0/CTC0 instructions (R7000) and handling for associated registers. TODO: customized vector spacing. 04-May-2006 ----------- - Introduction of the new MTS64 subsystem! - Note: incorrect uses of assert(), especially in insn_lookup.c module. 05-May-2006 ----------- - Added a free list of chunks for MTS64 + some basic optimizations. - Added basic PCI/HT stuff for SB-1 processor (dev_sb1_pci.c module). - Added AP1011 (Sturgeon HyperTransport-PCI Bridge) device. - Fixed masks for MFC0/MTC0 instructions, and added new definition for MFC0 to access set 1. - Fixed incorrect uses of assert(). 07-May-2006 ----------- - Modified the console handling to be more efficient. - Added global invalidation of the MTS64 cache. 08-May-2006 ----------- - Added selective invalidation of the MTS64 cache (for TLB management). - Added MTS64 statistics. - Modified the MTS64 hash settings. Origin: shift: 12, hash_bits: 16. Replaced by shift: 16, hash_bits: 12 -> more efficient and consumes less memory! (logical since IOS uses large TLB entries). - Modified cp0 module to use an abstraction layer for MTS access, allowing 32 or 64 bit modes. 09-May-2006 ----------- - Fixed MTS64 on x86_64 machines. - The clock divisor can now be set per VM and with an hypervisor command. 10-May-2006 ----------- - Added "shutdown" code for RAM and ROM devices. - Added VM object dump ("Ctrl-] + o") 11-May-2006 ----------- - Bugfix: missing initialization of the address length parameter of accept() in the main hypervisor module. - Bugfix: missing initialization of registry memory pool (noticed with Valgrind). - Added NPE and midplane selections to the hypervisor. 12-May-2006 ----------- - Added NIO deletion to the hypervisor. - Fixed status message in hypervisor for object deletion. - Added "sub" instruction (without exception support). 14-May-2006 ----------- - Fixed CPU state change. It is now possible to reboot at any time using "Ctrl-] + k". - Added VTTY deletion. - Added "c7200 stop" command to the hypervisor (not finished). - The memory-mapped devices of port adapters are now enabled dynamically, depending on the PCI BAR (Base Address Registers) settings. This avoids use of hardcoded values. - Integrated a patch from Philipp Brenner, which fixes ELF loading problem on Cygwin when default text file type is set to DOS. Many thanks to him. (symptom was: "load_elf_image: elf_begin: I/O error: raw read") 15-May-2006 ----------- - Added "c7200 set_config" and "c7200 set_mac_addr" hypervisor commands. - Fixed a stupid bug in PCI bus removal. - Added more complete shutdown code. 16-May-2006 ----------- - Fixed uninitialized MTS64 allocated entries (seen with Valgrind). - Added missing unmapping of memory-mapped files (reported by Greg). - The VTTY shutdown doesn't close stdin anymore (causing terminal problems at exit). - Fixed the shutdown procedure, added VM_STATUS_SHUTDOWN as status for a VM. Adapted the virtual CPU synchronization. 17-May-2006 ----------- - Some basic work on the hypervisor main modules (cleanup). 19-May-2006 ----------- - bugfix: base MAC address setup broken (reported by Greg). - Added global deletion of: ATM/FR switches, NetIO bridges, NetIO descriptors, C7200 instances ... - Added "hypervisor reset" command to go back to a clean state. 20-May-2006 ----------- - Modified the CPU synchronization system which was not working correctly. 21-May-2006 ----------- - bugfix: NIO unset in PA-4T+/PA-8T driver. - Added C7200 PA cleanup code. 22-May-2006 ----------- - Better handling of PA/NIO removal. - Base MAC addresses are now generated automatically if not specified. ("cafe..0000") 23-May-2006 ----------- - Added "VDE" NetIO type to connect to UML switches / Virtual Distributed Ethernet switches. - Some cleanups in NetIO code. - Added shutdown code for MTS32/MTS64 (not used yet). 24-May-2006 ----------- - Minor optimizations for non-JIT mode. - bugfix: crash when dumping instruction block tree (JIT). - Some memory leak fixes (again with Valgrind, definitely this tool rocks). TOFIX: * clpd6729 + PCI I/O space (to check carefully). * IO/supplemental memory created with dev_create_ram(). - Use of MTS shutdown code. 25-May-2006 ----------- - The compressed IOS images were not booting anymore: fixed the MIPS config register at startup (reported by Davide Brini). - Minor code cleanup for MIPS CPU reset. - I/O and supplemental memory are now created with dev_ram_init(). - bugfix: in PA shutdown code, the driver shutdown operation was called even if the driver was not initialized. - bugfix: order of memory freeing in c7200_free_instance() wasn't good, producing a segfault (seen on windows machines). 26-May-2006 ----------- - Played with the PA-POS-OC3 driver, it seems that it is working. Need to check if the TX ring guess is correct (ie, with "routing" conditions). 27-May-2006 ----------- - PA-POS-OC3: Fixed the TX ring part to work with multiple buffers. There is still a problem with buffer addresses (doesn't work with platforms using SRAM). 29-May-2006 ----------- - Playing with ISDN emulation (PA-4B), and Munich32 chip. Interfaces are only visible for now. 30-May-2006 ----------- - Moved VM lockfile deletion to VM instance freeing function. - Ugly hack to allow the POS driver to work with SRAM-based platforms. - Added support for "VDE" NIO in hypervisor. - Beginning of hypervisor documentation (README.hypervisor). 31-May-2006 ----------- - Removed "c7200 trigger_oir" hypervisor command which was redundant. 01-Jun-2006 ----------- - bugfix: fixed some reference counting leaks with default ATM and Frame-Relay switches and NIO bridge. - bugfix: DLCI were not announced in ascending order in LMI packets. - studied a bit how to use larger bootflash sizes. 02-Jun-2006 ----------- - DEC21140: Added multicast flag management for received frames. - Debugging of ISL problem reported by Valentin. - Added CRC-32 functions to the appropriate module. 03-Jun-2006 ----------- - ISL is now working with DEC21140. It seems that to handle ISL, another chip (FPGA ?) is present on PA-FE-TX and C7200-IO-FE to add the second FCS field. - Added basic disassembly code (need to do something more generic). 04-Jun-2006 ----------- - Added virtual instruction "bnez" (basic optimization). - Added RM7000 "mul" instruction (not tested). - Added teq/teqi instructions to non-JIT mode. 05-Jun-2006 ----------- - Added output packet exclusion to PCAP module (not possible with WinPCAP though). - Enhanced packet filtering in dec21140 emulation (for Windows users). - bugfix: stupid cast problem in bootflash code preventing proper unmapping. (seen with /proc//maps on Linux). - Added debugging message for device removal. - bugfix: memory not freed / file not closed with ELF loader. - bugfix: config register keeping the "ignore config" flag between instance reloads. - Added "c7200 set_conf_reg" hypervisor command. - ==> pre19 - Experiment: Pending IRQ are now checked only at jump instructions. 06-Jun-2006 ----------- - bugfix: bootflash not working anymore with previous bugfix. - Some optimizations for Program Counter (PC) handling in JIT mode. - Some optimizations for non-JIT mode (use of fastcalls). - Added a performance counter. 07-Jun-2006 ----------- - Added configuration saving command for hypervisor ("hypervisor save_config ") - The SB-1 DUART is working, allowing NPE-G1 console to work :) - Environmental monitor working with NPE-G1. - NPE-G1 next priorities: Ethernet (because it delays boot) and NVRAM (seems to be at a different address?) 08-Jun-2006 ----------- - NVRAM is now ok on NPE-G1 (it is at a different physical address). 13-Jun-2006 ----------- - Optimizations on fast lw/sw operations (x86 only for now). - Changed offset size for branches (x86_jump32 instead of x86_jump8). 14-Jun-2006 ----------- - Base MAC address now generated from PID and instance ID. 15-Jun-2006 ----------- - Optimizations on fast lw/sw operations (x86_64). - bugfix: fixed console problems when using TCP mode on Windows platforms (Telnet, Putty). It just requires to ignore LF (Line Feed) character (BTS entry #4) 16-Jun-2006 ----------- - bugfix: importing config to NVRAM was broken (device lookup was done on "cacheable" devices only in physmem* functions) (BTS entry #5) 17-Jun-2006 ----------- - bugfix: memory not freed when using host memory to emulate virtual RAM. (BTS entry #8). - Minor enhancements to clpd6729 driver. 19-Jun-2006 ----------- - Included a patch from Peter Ross (suxen_drol@hotmail.com) which allows to bind console and AUX ports to real serial ports. Many thanks to him. 20-Jun-2006 ----------- - Playing with the PCMCIA stuff (especially CIS) 21-Jun-2006 ----------- - Continuing on PCMCIA. 22-Jun-2006 ----------- - The PCMCIA ATA disk is working, although the ATA command set is not completely implemented. 23-Jun-2006 ----------- - Added virtual ethernet switch module. - bugfix: linux_eth file descriptor was not used correctly with NIO RXL. - Modified the ELF loader to use all sections. 26-Jun-2006 ----------- - Modified the NIO RX handling (packet receiving is done in NIO module). 27-Jun-2006 ----------- - bugfix: PA-4E/PA-8E not working with IOS 12.0T (incorrect device length). (BTS entry #17). - Some cleanups (untested though) in ATM and Frame-Relay switch modules (locking). 29-Jun-2006 ----------- - Added support for 2nd ATA disk. - Added command line options for disk0: and disk1: ATA devices. By default, disk0: has a capacity of 64 Mb, disk1: is not defined. - Added hypervisor commands "c7200 set_disk0" and "c7200 set_disk1". - Fixed some mistakes and typos in documentation. 30-Jun-2006 ----------- - Factorized code of fast memory operations to have something cleaner. 02-Jul-2006 ----------- - Modified the JIT compiler to translate pages instead of "blocks" (improves performance). 03-Jul-2006 ----------- - Some code cleanup for the new JIT compiler. - bugfix: PA-4T+/PA-8T not working with IOS 12.0T (incorrect device length). (BTS entry #23). - Added JIT flush to limit the memory used for translation. At this time, this is basic, we count the number of translated pages and we flush when this number reaches a threshold (512 pages seems to be a good value). - Integrated a patch from Philipp Brenner (BTS entry #21) Description: "In standard mode it intercepts SIGINT and sends a CTRL+C (0x03) to the target's vtty_con buffer, while in hypervisor mode it gracefully shuts down the hypervisor which causes the application to quit." 04-Jul-2006 ----------- - Fixed a stupid bug in mts_cache (phys_page boundary not checked). - Optimized JIT flushing and insn block allocation. - Changed the maximum number of translated pages to 2048, which seems to be a more adequate value. 05-Jul-2006 ----------- - Added an "exec zone" which is a pool of host executable pages. This zone is limited by default to 64 Mb for Linux/Unix and 16 Mb for Cygwin. When the zone is fully used, the JIT structures are flushed (removed the maximum number of translated pages). Now, there is no need to disable ExecShield or similar systems. - Added explicit error message when failing to open an ELF file. - Fixed getopt_long() usage. - Added CLI option "--exec-area " to define exec zone size for the default VM. 06-Jul-2006 ----------- - Added "tlbwr" (TLB Write Random) instruction and coprocessor 0 random register management. - Added "c7200 set_exec_area" hypervisor command. - Allowed the hypervisor to run on Windows 2000 machines (ip_listen() version is now ok for non-rfc2553 systems). 07-Jul-2006 ----------- - Added command line help for "--exec-area" option. - Updated documentation. 11-Jul-2006 ----------- - bugfix: missing pointer cleanup in c7200_pa_shutdown() (BTS entry #30) - bugfix: incomplete shutdown code for NIO (little memory leak + no freeing code for NIO with null type). - bugfix: memory leak in udp_connect() + incorrect error checking. - Added cleanup code for general log file. - bugfix: memory leak in hypervisor command execution. - Added logging for hypervisor commands. - Invalid instructions in delay slots are now properly handled by the JIT compiler (error message + CPU stop). 13-Jul-2006 ----------- - Added an idle loop detector (--idle-pc CLI option + "Ctrl-] + i" key) - Added "c7200 set_idle_pc" hypervisor command. 14-Jul-2006 ----------- - Updated documentation. - Checked the idle loop system on Windows and Linux x86_64, seems to be working correctly. - Added idle loop system to non-JIT mode. - Fixed a bug in "cache" instruction when used in non-JIT mode. 16-Jul-2006 ----------- - Integrated a patch from Peter Ross (suxen_drol@hotmail.com) for the Makefile. 17-Jul-2006 ----------- - bugfix: no empty mips64_emit_invalid_delay_slot() function for non-JIT build. - Added definition for mmap() MAP_ANONYMOUS flag for systems where only MAP_ANON is defined. - Accurate Timer IRQ (added --timer-itv parameter for tuning). 18-Jul-2006 ----------- - Fixed inter-pages jumps in delay slots (reported by nula) - Added a check preventing to run the idle-pc feature when an idle-pc value is already defined (since it would give biased results). - bugfix: VTTY list not locked in vtty_create() - bugfix: ensure the CPU is running before incrementing pending timer IRQs. - Added a checklist for minimal C7200 hardware components. It checks good init of ram, rom, nvram and zero devices. 19-Jul-2006 ----------- - Some work on JIT tuning (flush). 20-Jul-2006 ----------- - Better info logging for VTTY and C7200 modules. - Added a debug level for VMs. 21-Jul-2006 ----------- - Various code cleanups. - Added "c7200 set_debug_level" hypervisor command and "--vm-debug" command line option. Now, by default, less details are printed. - Fixed a bug with select() on Cygwin platform: before, only 64 FDs could be used. 24-Jul-2006 ----------- - bugfix: accept_fd was not closed in VTTY module (BTS entry #42) - bugfix: properly handle CR/LF in configuration files (FR, ATM, ...) (BTS entry #36) - bugfix: accept fd not correctly printed in log file. - Added a mini-parser for hypervisor: allows directory with blanks, ... - bugfix: bad use of setsockopt() in the hypervisor. 25-Jul-2006 ----------- - Added the capability to use Null NetIO with the hypervisor ("nio create_null" command). - Added a FIFO NetIO for intra-hypervisor communications. - Merged a patch from Peter Ross for incorrect idle-pc parsing with 64-bit values. - Non-JIT mode optimization for instruction fetch (although it could be enhanced again). - Various code cleanups. 26-Jul-2006 ----------- - Added "ethsw clear_mac_addr_table" to clear MAC address table of a virtual ethernet switch. - Added "ethsw show_mac_addr_table" to show all MAC addresses learnt by a virtual ethernet switch. - Fixed a potential bug with jalr/jr instructions, where the stack is used to save the return PC. If the instruction in the delay slot returned directly to the jit main loop (exception,...), the stack would be in an inconsistent state. This fix is also required for Darwin/x86. 27-Jul-2006 ----------- - Stack alignment for Darwin/x86 in memop functions and unknown opcode handling. - The Darwin/x86 build requires -mdynamic-no-pic as compilation option. - Fixed some inline assembly mistakes with "lock" prefix. - The Darwin/x86 port seems to be working correctly. - Fixed the amd64 version of "jr" instruction similarly to the x86 version (no problem with "jalr" in this case). 29-Jul-2006 ----------- - Merged a patch from Peter Ross (suxen_drol@hotmail.com): * disable dynamips escape commands for serial * support receipt of char 0x00 * force the device to into raw mode (cfmakeraw) * include additional uart register addresses in c7200 iofpga switch. The README is also updated. - Fixed some sign problems in VTTY module. 31-Jul-2006 ----------- - Merged a parch from Peter Ross for correct handling of Ctrl-C when a TCP console is used. - Code cleanup (JIT usage flag per VM, ...) - C3600: implemented a working ns16552 console driver. - C3600: fixed clpd6719 driver. - C3600: working environmental monitor. - C3600: changed default RAM to 128 Mb. - Fixed clobbered registers on AMD64 platform ("r14" was missing). 01-Aug-2006 ----------- - Added command line support for C3600 instances. - C3600: added appropriate deletion code. - C3600: added mainboard EEPROM support. - C3600: added bootflash of 8 Mb. - C3600: analyzed triggering of various IRQs. 03-Aug-2006 ----------- - C3600: added NM EEPROM support, slot activation now depends on the NM state. - Fixed nmc93c46 EEPROM code to set the data out bit to a high value when not reading data (was required for C3600 NM EEPROM) + various bug-fixes and cleanups. 04-Aug-2006 ----------- - C7200: some cleanups in PA function naming. - C7200: code refactoring for PA drivers. - C7200: ethernet stuff is now independent of dec21140 and am79c971 code. - C3600: added NM management stuff. - Mueslix serial stuff is now independent for C3600 and C7200. - C3600: added NM-1E, NM-1FE and NM-4T network modules. => problems: * no keepalive required for NM-1E/NM-1FE (media status ?) * NM-4T packet delivery failure when end of RX ring is hit. 06-Aug-2006 ----------- - Modified Am79c971 MII registers to keep the link up in FastEthernet mode. - Merged a patch from Peter Ross for Console and AUX ports optimizations (especially with TCP mode) * console and aux FILE streams * reimplement vtty_read_and_store() as state machine: - parse telnet escape codes, instead of forwarding them to router. - display instance and router name in xterm/vt title bar. - Modified the Mueslix driver to have something working for both C3600 and C7200 models. Added some debugging info. - C3600: better ethernet NM code. - C3600: Added NM-4E (4 ethernet ports) network module. 07-Aug-2006 ----------- - Fixed the remote control driver which incorrectly used static variables. - NVRAM address can now be known with the remote control driver. - Command line usage is now correctly displayed depending on the selected platform. 08-Aug-2006 ----------- - Some work on PA-4B (Munich32 chip). 09-Aug-2006 ----------- - Continuing on PA-4B (trying to understand TP3420). 10-Aug-2006 ----------- - C7200: Added PA Mgmt IRQ support (required for PA-4B, seen on a real c7200 with PA-4B offered by Vernon Missouri). - Added TP3420 definitions. 15-Aug-2006 ----------- - Continuing on PA-4B. Better understanding of the TX ring. Fixed interrupt queue management. 19-Aug-2006 ----------- - Added hv_vm.c module to handle generic VM operations. - Added hv_c3600.c module for the Cisco 3600 platform. - Fixed a bug in VTTY flushing. 20-Aug-2006 ----------- - Added code to gen_eth module for Cygwin to prevent WinPCAP from giving back transmitted packets. It requires WinPCAP 0.4-alpha1 or better. TODO: need to be tested! 21-Aug-2006 ----------- - Fixed C3660 boot. Trying to understand/discover the hardware. Environmental monitor should be ok. 24-Aug-2006 ----------- - Added NS16552 flush optimization (Peter Ross). - Fixed missing periodic task removal in NS16552. 26-Aug-2006 ----------- - C3660: * Better understanding of NM presence / EEPROM registers. * Analyzed registers used when a Net IRQ is triggered. 28-Aug-2006 ----------- - C3660: Network Modules are now working! Remaining stuff: OIR. 29-Aug-2006 ----------- - Added more generic code to handle Cisco EEPROM to change easily chassis MAC addresses. - Updated README with C3600 information. - Updated hypervisor documentation (README.hypervisor). - Added dated package build to the Makefile (make packdev) - Checked WinPCAP fix of 20-Aug-2006: working. - Added the capability for high priority IRQ (like NetIO IRQ) to break the idle loop. Great latency improvement with NetIO IRQ. - C3600: the DUART irq is now higly prioritized. It gives better console reactivity. 30-Aug-2006 ----------- - Huge performance improvement on dec21140 and am79c971 drivers, by transmitting up to 16 packets in one TX ring scan pass. - Added the same to Serial driver (untested). - Added "unicast limiter" feature to the am79c971 driver, similarly to the dec21140 driver. Not having this caused the virtual instances to see unrelated host traffic when using linux_eth/gen_eth NIO and consequently a bad performance was obtained. Thanks to Greg for noticing that. - Beginning of rework of PCI subsystem for PCI bridge support. (Greg noticed that with 12.2T releases, the PCI bus numbering is different for the C3660). 31-Aug-2006 ----------- - Finished the PCI subsystem rework. - C7200: Fixed PCI bridge use for midplanes (std: dec21050, vxr: dec21150). 01-Sep-2006 ----------- - Finally fixed the Serial problem on C3620 platform (hack in the driver to trigger interrupts differently). - Cleanup for VM PCI bus pool and C7200 "hidden" I/O bridge was missing. 03-Sep-2006 ----------- - Added safety checks to the PCI bus management. - Added a PLX9060 device (very basic for now). - Rewritten the PA-POS-OC3 driver to use the new PLX9060 driver. - Cleanup: removed use of c7200 bay info in drivers (everything uses PCI for configuration). - C7200: removed the bay module which contained static definitions of PCI bus, physical addresses for PA, ... Now everything is handled dynamically by the PCI subsystem and the remaining drivers (PA-4B, PA-POS-OC3) have been fixed. - C3600: removed PCI bus info from the bay module (due to PCI bridge new code). 04-Sep-2006 ----------- - Fixed some typos in comments. - Added a standalone utility to extract IOS configuration from an NVRAM file. - Removed pcireg.h (from NetBSD) which was just used for one definition. - Added a "COPYING" file for the GPLv2 licence. 05-Sep-2006 ----------- - Added basic packet filtering framework, to simulate packet loss or alteration. - Hypervisor commands now supports a variable number of parameters. - bugfix: doing a "sh run" and then pressing "Escape" was causing trouble to the VTTY (the state machine was in an incorrect state). 06-Sep-2006 ----------- - Removed erroneous special handling of intra-page jumps in delay slots. (problem with c3640 12.0(7)T IP+ image). 07-Sep-2006 ----------- - C3600: processors ID are now correct. - Avoid removing the JIT block of the caller with the "cache" instruction. - Added "hypervisor version" to allow clients (Dynagen) to know the current version. - Added missing explanation for "-P " command line parameter. - C7200: Fixed incorrect cleanup of IO PCI bridge. 08-Sep-2006 ----------- - Better code for VM error messages. - Fixed the write attempts to ROM at startup. 09-Sep-2006 ----------- - C3600: added support for io memory size, which prevents Smart Init from running (hypervisor command: c3600 set_iomem ). - C3600: fixed incorrect handling of commands for this platform, which caused port 0 to be disabled when manipulating other ports. 11-Sep-2006 ----------- - Fixed loop overflow in idle pc computation. - Added base64 encoding/decoding module from Yannick Le Teigner. - Added hypervisor commands "vm push_config" and "vm extract_config" to manage IOS configurations. Extracting to base64 is ok (verified with a third party decoder: http://www.opinionatedgeek.com/dotnet/tools/Base64Decode/Default.aspx ) - C3600: NVRAM checksum is now correctly computed. 12-Sep-2006 ----------- - Following a suggestion of Yannick Le Teigner, applied the optimization principle to PA-A1 card already used in dec21140, ... Latency is clearly better (on my system, this dropped from about 150ms to 50 ms). 14-Sep-2006 ----------- - Incorrect display of device name in PLX9060 driver. - Added manpages provided by Erik Wenzel (erik@debian.org). Many thanks to him for this and for his work on packaging for Debian. - Final 0.2.5 release. - C7200: Added missing NVRAM checksum. 15/21-Sep-2006 ----------- - Working on NM-16ESW. 22-Sep-2006 ----------- - NM-16ESW: interfaces are now up (handles specifically the appropriate MII register). 23/25-Sep-2006 -------------- - Still continuing on the NM-16ESW (first packets received/transmitted). 26-Sep-2006 ----------- - NM-16ESW: seems to be working! (although there are still a lot of things to do). 28-Sep-2006 ----------- - Added a hack to allow Ethernet NM to work correctly in 3620/3640 (seen when directly attached to the real network with very few packets incoming). - NM-16ESW: proper support for tx ring scatter/gather support. - NM-16ESW: fixed a bug in ARL insertion. - NM-16ESW: send directly BPDU packets to the CPU. 29-Sep-2006 ----------- - NM-16ESW: added support for "trunks" (ie etherchannel). - Added "Ctrl+p" sequence key to dump Port Adapter / Network Module info (only NM-16ESW reports something at this time). 30-Sep-2006 ----------- - NM-16ESW: proper management of station movement (etherchannel is handled). 01-Oct-2006 ----------- - NM-16ESW: added register handling. 02-Oct-2006 ----------- - NM-16ESW: playing with the port mirroring feature. 04-Oct-2006 ----------- - Finally understood how to handle link status changes! Now, "shut"/"no shut" is working. Better understanding of MII registers in general. 05-Oct-2006 ----------- - Fixed bug described in BTS entry #65 (in the case where two virtual addresses point at the same physical address, the JIT can use inappropriate block). This was a "silent" bug. 07-Oct-2006 ----------- - Modified the hypervisor parser to allow long lines (to upload IOS configs). 09-Oct-2006 ----------- - NM-16ESW: added ingress and egress port mirroring (todo: BPDU and CDP packets shouldn't be replicated). 10-Oct-2006 ----------- - 0.2.6-RC1 release. - NM-16ESW: implemented ARL count. Fixed the MAC address table display problems. 11-Oct-2006 ----------- - C3600: fixed NVRAM configuration export. - Merged a patch for Solaris/x86 from Damjan Marion. - Cleaned the EEPROM stuff to have something more generic. - NM-16ESW: base MAC address is now automatically generated. 12-Oct-2006 ----------- - NM-16ESW: added support to discard input packets. 13-Oct-2006 ----------- - NM-16ESW: modified the discard support to let BPDU packets arrive to the CPU. - Added online setup of idle-pc through the hypervisor command "vm set_idle_pc_online ". - Values computed for idle-pc are now stored in the virtual MIPS CPU and can be known through the hypervisor command "vm show_idle_pc_prop ". 15-Oct-2006 ----------- - Added a timer module. - NM-16ESW: added an ARL ager that removes expired MAC addresses. 18-Oct-2006 ----------- - Added hypervisor commands to tune idle-pc parameters ("vm set_idle_max" and "vm set_idle_sleep_time"). - Centralized NM/PA EEPROM to have common definitions for platforms with the same type of network interfaces. - 0.2.6-RC2 release. 19-Oct-2006 ----------- - C2691: added sketelon code. - C2691: working DUART. - C2691: added mainboard EEPROM. 20-Oct-2006 ----------- - Bugfix: dec21140 and amd79c97x were incorrectly discarding frames based on the source address, preventing HSRP/VRRP to work (BTS entry #75). - C2691: added hypervisor module to handle instances. 22-Oct-2006 ----------- - C2691: trying to understand the network interrupt mechanism. 23-Oct-2006 ----------- - C2691: fixed Galileo interrupt (IRQ 3), allowing packets to be received! - Added a basic GT96100 system controller (for C2691). - GT96100: added MII registers. - Added a basic Flash device to be used as ROM+NVRAM for C2691. - C2691: NVRAM (simulated from flash) is now ok. - C2691: Analyzed environmental monitor register. No warning anymore. 24-Oct-2006 ----------- - C2691: NVRAM config export is working, config import requires more work (since write operations to flash are not immediate). - Allowed Galileo DMA IRQ to preempt the idle loop. - Fixed ATA Flash "current of sectors per card" which must be in LSW/MSW order and not MSW/LSW. - Added another ATA Flash access method (for c2691). - C2691: added support for CompactFlash. 25-Oct-2006 ----------- - GT96100: added some definitions for the Ethernet part. 26-Oct-2006 ----------- - Working on TX part for GT96100. Unfortunately, don't know yet how to signal an interrupt for it on the 2691 platform :( 27-Oct-2006 ----------- - C2691: found how to announce correctly NM presence in slot 1. - GT96100: finally found how to notify appropriately the network interrupt: the Serial Cause Register has to be handled. - C2691: added the glue code to set up GT96100 ethernet ports. - GT96100: added RX part. Packets are now sucessfully handled. - C2691: better understanding of network interrupt status registers. - C2691: added correct flash code detection. - GT96100: fixed the "PHY reset" problem. - C2691: added default correct chassis base MAC address. 28-Oct-2006 ----------- - GT96100: added hash function (Mode 0 ok, Mode 1 to fix) for Ethernet address filtering + appropriate definitions. 29-Oct-2006 ----------- - GT96100: fixed hash mode 1 (not tested). - GT96100: added address filtering process. 30-Oct-2006 ----------- - GT96100: added minimal MIB counters. - Implemented LBA mode for PCMCIA ATA disk devices and fixed data access. - Changed the default idle sleep time from 50ms to 30ms to avoid timer IRQ loss and timer drift. - Added hypervisor command "vm show_timer_drift " to display potential problem with a given idle-pc value. - C2691: added proper support for IO memory. - NM-16ESW: generalized code to support multiple platforms. - C2691: added support for NM-16ESW. 31-Oct-2006 ----------- - C3725: initial support. Hardware very similar to c2691: Cisco rocks! - C3745: added skeleton. - C3745: fixed GT96100 address. - C3745: added PCI bridges. - C3745: Network Modules are working! - C3745: CompactFlash working. 01-Nov-2006 ----------- - C3745: finally understood how to handle the system EEPROMs. - Updated documentation (README). - GT96100: minor enhancement of interrupt handling. 02-Nov-2006 ----------- - Added support for "ghost" RAM: instances use a RAM file (previously created) and use copy-on-write on it. It allows to share common memory between instances and so less memory is used. 03-Nov-2006 ----------- - Better support for ghost RAM, added hypervisor commands "vm set_ghost_status" and "vm set_ghost_file". 04-Nov-2006 ----------- - NM-16ESW: fixed incorrect BPDU handling (which caused to add a 2nd 802.1Q tag to BPDU packets) and now 0100.0ccc.cccd is recognized as a BPDU MAC address (BTS entry #). - DEC21140: fixed CSR8 handling (missed frame counter), which returned an undefined value (BTS entry #81). - Merged a patch from Rostislav Opocensky who added support for C3600 in nvram_export utility. - Re-enaebled debugging info for NM-16ESW on 2691/3600/37xx. 05-Nov-2006 ----------- - NM-16ESW: fixed a crash due to incomplete device removal. 06-Nov-2006 ----------- - Validated use of Serial interfaces with 2691/3725/3745. - Correct config register handling for 3600/2691/3725/3745 platforms (config register setting was ignored). - Added detection of empty NVRAM for 2691/3725/3745 to set the ignore config flag in the config register (to boot faster). - C3745: Fixed NVRAM properties. - C7200/C3600: rewrite of configuration push into NVRAM (can now be done offline). - C2691/C3725/C3745: added support for config push into ROM flash (simulated NVRAM). 07-Nov-2006 ----------- - Modified config export from NVRAM for all platforms (can now be done offline). - Better handling of base MAC address (2691/3725/3745 + NM-16ESW). 08-Nov-2006 ----------- - Rework of the MTS subsystem (generalization of MTS64 algorithms to MTS32 which was broken). - Fast memory operations for 32-bit mode. 09-Nov-2006 ----------- - Rewrite of the MIPS TLB lookup operation. 10-Nov-2006 ----------- - Fixed another bug in TLB lookup. - Beginning of work for PA-MC-8TE1 support. - Added hypervisor commands to send messages to instances VTTY. - Working on PLX PCI9054 for PA-MC-8TE1. 11-Nov-2006 ----------- - Continuing a bit on PA-MC-8TE1. 14-Nov-2006 ----------- - Correct handling of VPN2 mask for TLB lookup. - bugfix: support of c3745 was missing in the hypervisor. - Merged a patch from Akim Dreyer for Debian support. - 0.2.6-RC3 release. 15-Nov-2006 ----------- - C2691/C3725: fixed a crash occuring on some IOS images due to incorrect platform type. - Modified performance counter to have stats for instructions or blocks (cannot be used simultaneously). - "beq" jump code (x86) was incorrectly using non-local jump. - Bugfix: incorrect number of arguments for hypervisor commands related to ghost file handling. - 0.2.6-RC4 release. 17-Nov-2006 ----------- - Major code cleanup to make devices independent from the virtual processors. - Generic CPU stuff. 20-Nov-2006 ----------- - Merged of patch from Thomas Pani to list VM TCP console ports (required for gDynagen). - NM-16ESW: fixed (again) BPDU handling (incorrect VLAN tagging with non trunk ports). - Very minimal PowerPC definitions. Only non-JIT mode at this time. - PPC: added "crorc", "cror", "crnor", "crnand", "crandc", "crand". - PPC: added "and", "andc", "andi", "andis", "creqv", "crxor". - PPC: added "eqv", "isync", "mfmsr", "mtmsr", "nand", "nor", "or", "orc". - PPC: added "ori", "oris", "sync", "xor", "xori", "xoris", "mfcr". - PPC: added "addi", "addis". 21-Nov-2006 ----------- - PPC: added "cmp", "cmpl", "cmpi", "cmpli", "extsb", "extsh". - PPC: added "add", "add.", "and.", "andc.", "extsb.", "extsh.". - PPC: added a dummy memory access function (does nothing yet). - PPC: added "lbz", "lbzu", "lbzux", "lbzx". - PPC: added "lhz", "lhzu", "lhzux", "lhzx". - PPC: added "lwz", "lwzu", "lwzux", "lwzx". - PPC: added "stb", "stbu", "stbux", "stbx". - PPC: added "sth", "sthu", "sthux", "sthx". - PPC: added "stw", "stwu", "stwux", "stwx". - PPC: added "xor.", "b", "ba", "bl", "bla". - bugfix: hypervisor commands specific to CPU crashed if the VM was not started (noticed by Greg). 22-Nov-2006 ----------- - PPC: added "addo", "addo.", "addc", "addc.", "addco", "addco." - PPC: added "addic", "addic.", "adde", "adde.", "addeo", "addeo." - PPC: added "neg", "neg.", "nego", "nego.", "nand.", "nor.", "or.", "orc." - PPC: added "slw", "slw.", "srw", "srw." 23-Nov-2006 ----------- - PPC: added "rlwimi", "rlwimi.", "rlwinm", "rlwinm.", "rlwnm", "rlwnm." 24-Nov-2006 ----------- - PPC: added "mulhw", "mulhw.", "mulhwu", "mulhwu.", "mulli". - PPC: added "mullw", "mullw.", "mullwo", "mullwo." - PPC: added "subf", "subf.", "subfo", "subfo." - PPC: added "bc", "bca", "bcl", "bcla", "bclr", "bclrl". - PPC: added minimal memory operation glue code. 25-Nov-2006 ----------- - PPC: added dump functions for MMU registers. - PPC: added support for BAT registers. 26-Nov-2006 ----------- - PPC: fixed branch offset computation. - PPC: added a dummy virtual machines for tests. - PPC: fixed bclrx opcodes. - PPC: added "mflr", "mtlr". 27-Nov-2006 ----------- - PPC: fixes in comparison functions. - PPC: added "subfc", "subfc.", "subfco", "subfco." 28-Nov-2006 ----------- - PPC: added "subfic", "mfctr". - PPC: fixed a lot of bugs. 29-Nov-2006 ----------- - PPC: added "rfi", "lha", "lhau", "lhaux", "lhax", "addze", "addme". - PPC: fixed "mtcrf". 30-Nov-2006 ----------- - Added IRQ routing vectors for virtual machines. 01-Dec-2006 ----------- - Playing with NPE-G2, just for fun. 02-Dec-2006 ----------- - NPE-G2: environmental monitor is now working! 03-Dec-2006 ----------- - PPC: added an empty MV64460 controller (need the datasheet). - PPC: added "lmw" instruction, fixed "stmw". - PPC: added "cntlzw". 05-Dec-2006 ----------- - NPE-G2: analyzed midplane data: this is similar to other NPE. - Added a basic PLX6520CB PCI bridge. - NPE-G2: added appropriate PCI bridges and PCI busses for Port Adapters. 06-Dec-2006 ----------- - NPE-G2: added PCI I/O space. - NPE-G2: added CLPD6729, it is now possible to use PCMCIA ATA disks. 08-Dec-2006 ----------- - MV64460: added ugly experimental SDMA support to have a console for the NPE-G2 (using the GT96100A datasheet). This code must be rewritten. - MV64460/NPE-G2: playing with the various IRQs. 09-Dec-2006 ----------- - PPC: added idle-pc support. 10-Dec-2006 ----------- - PPC: added virtual breakpoint support. 15-Dec-2006 ----------- - PPC: added functions to manually set up page tables. - MV64460: added more interrupt definitions (with help of GT96100A manual). 19-Dec-2006 ----------- - MV64460: fixed interrupt loss problem. 20-Dec-2006 ----------- - Basic port of the microcode for the PowerPC platforms. IOS now starts normally (like the MIPS64 platforms). 21-Dec-2006 ----------- - NPE-G2: found OIR IRQ. - MV64460: added more definitions (for SDMA). - Cleanup of MIPS64 JIT code (better naming). 22-Dec-2006 ----------- - PPC32: added the minimal JIT core infrastructure. - PPC32-JIT: added unknown opcode handling. - PPC32-JIT: added some instructions. 23-24-Dec-2006 -------------- - PPC32-JIT: added more instructions. 26-Dec-2006 ----------- - PPC32-JIT: continuing... 27-Dec-2006 ----------- - PPC32-JIT: added fast memory operations. 28-Dec-2006 ----------- - Added PPC32 nojit support. - PPC32: better checking for IRQs. 29-Dec-2006 ----------- - PPC32-JIT: added "amd64" support. - PPC32: added "lwbrx" and "stwbrx" instructions. 30-Dec-2006 ----------- - PPC32: added "tw"/"twi" instructions. - PPC32: added TLB support for PowerPC 405. - PPC32: added "tlbre", "tlbwe", "iccci", "dccci", "mfdcr", "mtdcr" for PowerPC 405. - Fixed a bug with ILT tables (incorrect count - last entry was missing). 31-Dec-2006 ----------- - PPC32: added "dcbst" instruction. 05-Jan-2007 ----------- - NPE-G2: correct handling of DUART interrupt with I/O card. 07-Jan-2007 ----------- - NPE-G2: correct console selection (NPE or I/O board) depending on slot 0. - PPC32: fixup for decrementer interrupt. - Generic CPU MTS rebuild (mips64/ppc32). - Split of memory.c / mips64_mem.c. memory.c will only contain generic functions independent of the CPU model. - Memlogger is now independent of CPU type (untested). 08-Jan-2007 ----------- - Rewrite of the MTS subsystem to have less overhead with inlined operations -> better performance. 09-Jan-2007 ----------- - Finished the MTS rewrite (amd64, statistics). - NPE-G2: added a 64 Mb bootflash (needs to be tested). 10-Jan-2007 ----------- - PPC32: added clean loading of BAT registers. - Fixed device remapping. 12-Jan-2007 ----------- - PPC32: added "lswi", "stswi", "lswx", "stswx". 13-Jan-2007 ----------- - PPC32: fixed carry evaluation for "srawi" instruction. 15-Jan-2007 ----------- - Playing with c2600 to see if support can be added. 17-Jan-2007 ----------- - C2600: adding skeleton code, added NM selection. However, unable to get a positive result for "sh diag 0". 19-Jan-2007 ----------- - Experimenting with sparse memory to reduce virtual memory use in hypervisor mode. 20-Jan-2007 ----------- - Continuing on sparse memory work. 21-Jan-2007 ----------- - C2600: added mainboard basic support. - MPC860: added a basic IDMA support. - C2600: first packet exchange :) 22-Jan-2007 ----------- - C2600: added basic mainboard drivers for integrated Ethernet ports. - C2600: added IRQ preemption for network interrupt and DUART. - Fixed use of sparse memory with ghost files. - Added a command line option to test sparse memory ("--sparse-mem"). - Added locking for shared ghost images. 24-Jan-2007 ----------- - Validation of dynagen 0.8.3 with sparse memory and ghost image enabled. - MIPS64: fixed a bug of the JIT which incorrectly handled jump instructions with a delay slot in another page. 25-Jan-2007 ----------- - C2600: added NVRAM configuration import/export and appropriate config register setting if NVRAM is empty at startup. - C2600: added a basic bootflash support. 26-Jan-2007 ----------- - C2600: io-mem size support. 31-Jan-2007 ----------- - i8254x: added skeleton code (for PA-2FE-TX). 01-Feb-2007 ----------- - i8254x: added MDIO code. 04-Feb-2006 ----------- - C2600: added "set_chassis" hypervisor command. - C2600: possible chassis/mainboards now displayed at command line help. 05-Feb-2007 ----------- - Fixed a bug when handling packet discard flag on NM-16ESW (seems to be only valid for native packets). 06-Feb-2007 ----------- - Working on i8254x TX part. Problem with TX ring wrapping. 08-Feb-2007 ----------- - Added packet capture to NIO filters, with PCAP output (Greg Anuzelli). - i82543: RX and TX parts are now basically working (of course, no IP/TCP checksum offloading, but this doesn't seem to be used). 09-Feb-2007 ----------- - C7200: added C7200-IO-2FE card, based on Intel i82543 chips. - C7200: added PA-GE interface, but TX part is broken. - i8254x: TDH/TDT and RDH/RDT registers are located at a different address. PA-GE is now working. 12-Feb-2007 ----------- - i8254x: delayed RX IRQ. - MIPS64: fixed (again) TLB lookup for entries in ksseg and kseg3. - Added a "byte-swapping" device. 13-Feb-2007 ----------- - i8254x: added byte-swapped data transfer. - PA-POS-OC3: applied byte-swapped data transfer for TX ring (RX ring doesn't seem to need this - strange). 14-Feb-2007 ----------- - C7200: added C7200-IO-GE+E, based on Intel i82543 chips. 16-Feb-2007 ----------- - MIPS64: fixed a problem of jump in delay slots (occuring with a "forward" jump). This caused a malfunction for C7200-IO-2FE and C7200-IO-GE-E cards. - C7200: adjusted the byte-swapped zone for NPE-300 io-memory. 18-Feb-2007 ----------- - PPC32: Remove JIT compiled pages when a write occurs (required for c2600). 19-Feb-2007 ----------- - PPC32: Optimization for writes on JIT page at address 0. 20-Feb-2007 ----------- - Working on MSFC1 - just for fun (this will probably never route any packet). - Fixed DEC21140 TX ring interrupt generation. 21-Feb-2007 ----------- - Continuing on MSFC1 (added EEPROM, SRAM, ...) - Added NMC93C56 EEPROM support. 23-Feb-2007 ----------- - MPC860: fixed a bug in DMA handling which caused incorrect writes out of DPRAM memory. - PPC32: added missing breakpoint function for JIT. - C7200: rework on network interrupts, for clean handling per slot/port. 24-Feb-2007 ----------- - C7200: Net IRQs are now correctly dispatched. Need to fix all drivers to generate IRQs correctly. - DEC21140: proper management of CSR5 register for new interrupt handling. - i8254x: converted to new interrupt handling. - Am79c971: fixed interrupt management + a bug with IRQ mask. - PA-A1/PA-POS-OC3/Mueslix: fixed interrupt management. 25-Feb-2007 ----------- - C2691: updated interrupt infrastructure. - NM-16ESW: fixed interrupt handling. 26-Feb-2007 ----------- - C3725/C3745: updated interrupt infrastructure. 27-Feb-2007 ----------- - C2600: updated interrupt infrastructure. 02-Mar-2007 ----------- - Added a cache for the instruction lookup tables (ILT), allowing a faster start. 03-Mar-2007 ----------- - Fixed instruction tables (problem seen on amd64 platforms). 07-Mar-2007 ----------- - MIPS64/PPC32: various code cleanups in JIT code. - MIPS64: replace the JIT block lookup algorithm based on physical pages by s-boxes (x86 only). 08-Mar-2007 ----------- - PPC32: JIT block lookup optimization (x86 only). 09-Mar-2007 ----------- - MIPS64/PPC32: JIT block lookup optimization (amd64). 12-Mar-2007 ----------- - PPC32: fixed invalid hash index in JIT block invalidation (thanks to Greg). 13-Mar-2007 ----------- - Updated the Mueslix driver and fixed it for packets > 128 bytes (IRQ clearing delay required because of the new interrupt system). - Allowed MTU up to 18000 bytes on the Mueslix driver. This required an update of the NIO core. - Full dump of the idle-pc values when no "good" value can be determined. 25-Mar-2007 ----------- - Store the idle-pc values in the CPU structure when no "good" value is available, so they are readable/usable by dynagen. - PA-POS-OC3: fixed memory copy which caused invalid frames to be sent. 27-Mar-2007 ----------- - Added a device access counter for each CPU for diagnostics purposes. 29-Mar-2007 ----------- - PPC32: optimizations for eflags->cr computing. 30-Mar-2007 ----------- - PPC32: optimizations in CR handling (split in 8 fields). 31-Mar-2007 ----------- - PPC32: added CR optimizations to amd64 backend. 02-Apr-2007 ----------- - Fixed "nojit" build (reported by Philipp Brenner, BTS entry #156). - AMD Am79c970 FastEthernet interfaces now announce 100 Mb/s Full duplex. 04-Apr-2007 ----------- - Fixed build (NetIO filters) when PCAP is lacking. 09-Apr-2007 ----------- - Added NM-1A-OC3MM EEPROM definition for future work. - NM-16ESW: filter CDP specifically to not propagate frames to all ports. 12-Apr-2007 ----------- - Added an hypervisor command to disable direct jumps between JIT blocks (vm set_blk_direct_jump <0|1>). 15-Apr-2007 => 22-Apr-2007 =========================== - PPC32: rewrite of JIT with peephole and CR flags optimizations. 23-Apr-2007 ----------- - PPC32: converted amd64 JIT to new system. 24-Apr-2007 ----------- - MIPS64: fixed a bug in cache instruction preventing compressed IOS images to boot. - PPC32: same as above for ICBI instruction. - PPC32: fixed ADDZE instruction. 25-Apr-2007 ----------- - C3725: fixed interrupt problems with slot 2 (bad shift - only 4 irq lines per port). 28-Apr-2007 ----------- - Changed instruction counters to 32-bit type, to get more accurate results. 29-Apr-2007 ----------- - Experimenting with a per-register memory translation cache. 30-Apr-2007 ----------- - C2600: playing with MPC860 SPI to access WIC eeproms (slot 0 only). 01-May-2007 ----------- - MPC860: continuing on SPI. - Rewriting/Refactoring code to handle network interfaces (required for WIC support). 02-May-2007 ----------- - Continuing rework of network interfaces. 03-May-2007 ----------- - Added slot handling hypervisor commands to VM module. - Fixed some bugs/lacking checks in network card module. - C2600: added WIC EEPROM read through MPC860 SPI code. - C2600: beginning of work for basic SCC implementation. 04-May-2007 ----------- - C2600: continuing on SCC. - C2600: WIC-1T is now working! - C2600: added WIC-2T (async mode is not supported). - C1700: introduction of this platform (took c2600 as base model). 05-May-2007 ----------- - MPC860: added SPI relocation support (required for c1700). - C1700: added NVRAM. - C1700: added WIC detection / correct EEPROM support. - MPC860: beginning of work on Fast Ethernet Controller. 07-May-2007 ----------- - MPC860: Fast Ethernet Controller basically working (MII registers required). - MPC860: better handling of interrupt levels. - C1700: added WIC support (WIC-1T/WIC-2T). 14/15-May-2007 -------------- - Flash code rework (required for c1700) to have something more generic. 16-May-2007 ----------- - New flash code re-enabled for all routers requiring it (NPE-G2 bootflash is broken however). - C1700: added WIC-1ENET code base with EEPROM MAC address programming. 18-May-2007 ----------- - Memory exceptions are now handled through setjmp/longjmp, avoiding error checks in memory access functions. 19-May-2007 ----------- - C2600: fixed PCI handling which prevented i82559 device to work. In fact, the PCI bridge is in a Xilinx device, and the output of "sh pci hardware" was very different from a real router. - i8255x (eepro100): added basic code (no RX/TX, only MII working). 20-May-2007 ----------- - Continuing on i8255x. 21-May-2007 ----------- - i8255x: Intel doesn't provide enough info in its documentation for the RX flexible mode... 22-May-2007 ----------- - i8255x: RX flexible mode works similarly to the 82596CA chipset. Packet TX and RX seems ok. 23-May-2007 ----------- - C2691/C3725/C3745: fixed GT96100 binding to slot 0. - GT96100/ETH: fixed interrupt handling for TX packets (the interrupt was incorrectly disabled when no packet was available on a ring, causing packet loss when the two ports were enabled) - BTS entry #171. 25-May-2007 ----------- - C3660: correct support of mainboard FastEthernet ports with the new card subsystem. - C7200: fixed I/O card definition with new card subsystem. - Added compatibility mode with old version for slot binding in the command line. 01-Jun-2007 ----------- - Heavily fixed/tested the Serial drivers to correctly transmit/receive frames. Now the CRC is not anymore transmitted and the correct size is set for RX rings (TODO: crc-32 for Mueslix and PA-POS-OC3). 03-Jun-2007 ----------- - Fixed incorrect free in deletion of frame-relay switch VC. 04-Jun-2007 ----------- - PA-POS-OC3/Mueslix: added correct handling of crc-16/crc-32. 10-Jun-2007 ----------- - MIPS64: correct handling of stack for MacOS X (alignment). - MSFC1: cleaned up module to use the new card infrastructure. - Frame-Relay switch: removed "trailing" handling which was in fact only the CRC + final byte. 09-Jul-2007 ----------- - Fixed PA-FE-TX initialization (EEPROM setting lacking). 11-Jul-2007 ----------- - PPC32-JIT: fixed stack alignment for MacOSX/Darwin. - PPC32-JIT: fixed perf counter and breakpoints. 13-Jul-2007 ----------- - Integrated patches from FreeBSD (Pavel I Volkov), excepted for the vtty part. 14-17-Jul-2007 -------------- - Rework of VM infrastructure. 17-Jul-2007 ----------- - Added basic plugin subsystem. - CPU: added custom handler support for undefined memory accesses. 21-22-Jul-2007 -------------- - GT96100: working on SDMA and MPSC. 23-Jul-2007 ----------- - C3745: finally understood how to handle WIC EEPROMs. 24-Jul-2007 ----------- - C3745: added support of WIC-1T & WIC-2T. - GT96100: fixed SDMA handling. 25-Jul-2007 ----------- - C2691/C3725: added WIC-1T and WIC-2T similarly to C3745. - C1700: fixed mainboard definitions, added proper support of C1710 (no WIC port, Ethernet port connected on MPC860 SCC1). - C1700: added 1751 and 1760 definitions. 29-Jul-2007 ----------- - MPC860: fixed MII registers for 1710, 1721 and 1760. 01-Aug-2007 ----------- - C2600/C2691/C3725/C3745: Added NM-CIDS/NM-NAM which can be connected to a PC emulator. - C2600: added a check for NM-CIDS/NM-NAM which require XM models. 18-Aug-2007 ----------- - Added WIC address space definitions for platforms that support them. 19-Aug-2007 ----------- - Added ISL support for am79c970 based cards (NM-1FE-TX and others). 27-Aug-2007 ----------- - GT96100: missing check for NULL pointer in set_nio/unset_nio. - MPC860: same bugfix (SCC and FEC). 31-Aug-2007 ----------- - MIPS/PPC: modified device memory access to allow devices not at a 4k page boundary (like WICs on 2600). - Added fake WIC serial drivers, just to catch the memory accesses. 06-Sep-2007 ----------- - PPC32: Fixed BCTR instruction on amd64 jit. 08-Sep-2007 ----------- - Added an object store (through hypervisor) for Dynagen. 11-Sep-2007 ----------- - Proper cleanup of gt_data/mpc_data fields for MPC860 and GT96100 based platforms (start/stop/exit/crash symptom with Dynagen). 12-Sep-2007 ----------- - Fixed udp_recv simple program which was broken. - Added "delete_all" command for hypervisor object store. - Added appropriate code for serial WIC drivers on 1700. 14-Sep-2007 ----------- - Added TCP keepalives on VTTY connections (xabrouck@cisco.com) - Fixed Flash code. 18-Sep-2007 ----------- - Added a virtual ATM SAR (segmentation and reassembly) engine. - Added a virtual ATM bridge device (RFC1483). 21-Sep-2007 ----------- - GT96100: fixed CRC handling in MPSC HDLC channels (2 bytes, not 4!) 30-Sep-2007 ----------- - PPC32-JIT: fixed CR signed evaluation in case of overflow. 04-Oct-2007 ----------- - Added ISL support for integrated ports on 2691/3725/3745. 07-Oct-2007 ----------- - Fixed non-JIT build for PPC32. 08-Oct-2007 ----------- - MV64460: rewrite of SDMA code to follow GT96100 model. 09-Oct-2007 ----------- - MV64460: added MPSC registers (as for GT96100). 10-Oct-2007 ----------- - MV64460: added SRAM. +-----------------------. | Release: v0.2.8-RC2 | +-----------------------' 29-Oct-2007 ----------- - C3745: some work on OIR, but unfortunately only NM-4T works at the moment. 30-Oct-2007 ----------- - C3660: added OIR support. 27-Nov-2007 ----------- - C7200: added support for jacket card (C7200-JC-PA) and support for PA in slot 7. 29-Nov-2007 ----------- - Fixed AAL5 CRC. 15-Jan-2008 ----------- - Added Multicast NIO 19-Jan-2008 ----------- - Added "nio set_mcast_ttl" hypervisor command to change TTL for multicast NIOs. 25-Jan-2008 ----------- - Added 64-bit support for MacOS X 10.5 - Leopard (stack alignment). 31-Mar-2008 ----------- - PPC32: Fixed DIVW instruction (incorrect cast: unsigned instead of signed). This was causing problems with OSPF (and probably other protocols) on 2600 platforms. 01-Apr-2008 ----------- - Some minor info added (OS name) to ease debugging. 24-Apr-2008 ----------- - Added NIO traffic statistics (packets/bytes in/out). 25-Apr-2008 ----------- - Added QinQ support in integrated ethernet switch (untested). 07-May-2008 ----------- - More coherent naming for JIT variables ("blocks" -> "tcb"). - Fix QinQ input vector in Ethernet switch (thanks to Pavel Skovajsa). 16-May-2008 ----------- - Merged PPC-host JIT patch from Zhe Fang. Jun-2008 -------- - Worked on a JIT code sharing system (only MIPS ported at this time) 03-Jul-2008 ----------- - MIPS64: fixed a lot of mistakes in handling of TLB entries. 27-Nov-2008 ----------- - Added "udp auto" NIO to avoid fixed port allocation which can easily fail. - Bug fixes in socket handling (incorrect setsockopt() for non-RFC2553 systems) +------------------------------------------------------. | Last known development version: v0.2.8-20090305-18 \ | Some of the changes only exist in unstable. / +------------------------------------------------------' 30-Mar-2011 (dynamips-community fork) ------------------------------------- - Online Insertion and Removal (OIR) support for c3745 (NM-4T only) and c3660. - c7200: support for jacket card (C7200-JC-PA) and support for PA in slot 7. - ATM: AAL5 CRC bug fix. - New Multicast NIO. - "nio set_mcast_ttl" hypervisor command to change TTL for multicast NIOs. - 64-bit support for MacOS X 10.5 - Leopard (stack alignment). - PPC32: DIVW instruction fix (incorrect cast: unsigned instead of signed). This was causing problems with OSPF (and probably other protocols) on 2600 platforms. - Some minor info added (OS name) to ease debugging. - NIO traffic statistics (packets/bytes in/out). - QinQ support in integrated Ethernet switch. - More coherent naming for JIT variables ("blocks" -> "tcb"). - PPC-host JIT patch from Zhe Fang. - JIT code sharing system (only MIPS ported at this time) - MIPS64 fixes in handling of TLB entries. - "udp auto" NIO to avoid fixed port allocation which can easily fail. - Bug fixes in socket handling (incorrect setsockopt() for non-RFC2553 systems) - New source code layout to allow stable and unstable to share code in common in a single distribution. - Able to set system id at runtime in cli mode using -I or set_system_id in hypervisor mode. This is for routers 1700,2600,3600,3725,3745, and 7200 (not npe-400 or npe-g1 or 2). System id format is dependent on router, e.g. -I 4279256517 or -I FTX0945W0MY (these are the current defaults). - AUX port for routers 1700 to 3745 now works. - u16552 correctly emulates the latch bit, and decodes more registers. - Telnet to AUX or CONSOLE is flushed much earlier. - Make clean improved. - IOS reload command for non c7200 now terminates the emulation. - Setting mac address using -m works for non c7200 as well. - Using inappropriate flag which is platform sensitive gives an error. - Flag added --noctrl which disables ctrl+] - Flag added --notelnetmsg which disables welcome text on telnet AUX and CONSOLE. - Flag added --filepid which saves the pid of dynamips to a file. - Bug fix: xx:xx:xx:xx:xx:xx mac addresses in -m now accepted. - Bug fix: JayTee patch included for sizeof error in networking - Bug fix: closing telnet to AUX/CONSOLE may produce SIGPIPE which is now trapped when using non hypervisor mode. - Bug fix: ghost file using -G now opened correctly as READONLY. - Cisco Pagent and CallGen image support for c1700, c2600, c36xx, c37xx, c7200 NPE-100 to NPE-300: (see http://www.sadikhov.com/forum/index.php?showtopic=157426&st=20 for details). +---------------------------------. | Release: v0.2.8-RC3-community | +---------------------------------' 12-Nov-2011 ----------- - FreeBSD patches (http://www.freebsd.org/cgi/cvsweb.cgi/ports/emulators/dynamips-community/files/). 26-Jun-2012 ----------- - Fixes hypervisor problem of only using default port with a bind address. +---------------------------------. | Release: v0.2.8-RC4-community | +---------------------------------' 26-Jun-2012 ----------- - Support for RFC2553 + binding on specific addresses for console/aux connections. - True AF independent code (RFC2553). Accepts IPv6 and IPv4 clients in the same console session. - Update CFLAGS and LIBS for Cygwin. 18-Oct-2012 ----------- - show_cpu_usage command for the vm, used by GNS3 for auto idlepc calculation. +---------------------------------. | Release: v0.2.8-RC5-community | +---------------------------------' 30-Jan-2013 ----------- - Fixed crash when a router (2691, 3725 or 3745) is powered off. - Multiplatform get_cpu_time function. - Added rt lib into Makefile (for Linux compilation). 30-Jan-2013 ----------- - Fixed QinQ. - Simplified TTYPE message. +---------------------------------. | Release: v0.2.8-RC6-community | +---------------------------------' 11-Jun-2013 ----------- - Manual patches - Patch: avoid dangling pointers when the ppc32 cpu is recompiling a tcb and an exec page needs to be allocated, causing the tcb to be flushed. 12-Jun-2013 to 15-Jun-2013 -------------------------- - Updated README, changelogs, and makefiles. - Bumped version to v0.2.8-RC7-community. +---------------------------------. | Release: v0.2.8-RC7-community | +---------------------------------' 18-Jun-2013 to 04-Jul-2013 -------------------------- - Fixed crashes in x86_64 builds by decreasing the optimization level to O2. It was also reported that using O2 makes packet delivery in general, and frame relay especially more reliable. - Fixed many memory leaks. - Updated documentation and makefiles (works with parallel jobs). - Bumped version to v0.2.8-community. +-----------------------------. | Release: v0.2.8-community | +-----------------------------' 10-Jul-2013 to 31-Jul-2013 (renamed to dynamips) ------------------------------------------------ - Added NVRAM filesystem. - Added generic_nvram_extract_config and generic_nvram_push_config that use the NVRAM filesystem. - Reimplemented the nvram_extract_config/nvram_push_config functions of the platforms c1700/c2600/c2692/c3600/c3725/c3745/c7200/c6msfc1. - Fixed the file handle leak in c6sup1_nvram_extract_config. - Renamed from Dynamips-community to the original Dynamips, updated documentation and makefiles. - Bumped version to v0.2.9. Fixed issue #11 - nvram:private-config is destroyed when we write nvram:startup-config Fixed issue #13 - C3745 has an emulated NVRAM that is not continuous, so we can generate an invalid checksum when the config is written Fixed issue #14 - a file descriptor is leaked every time the config is extracted/read +-------------------. | Release: v0.2.9 | +-------------------' 05-Aug-2013 to 07-Sep-2013 -------------------------- - Add an option to produce debug symbols and override the optimization level to the makefiles. - Fix leak of a mutex and a condition if thread creation fails in timer_create_queue. - Remove unnecessary call to pthread_exit, the main thread is waiting with pthread_join. - Add volatile qualifier to variables 'list' and 'running' of timer_queue_t. They are modified and read in different threads. - Remove the asynchronous cancellation of the timer threads. It is not safe and pthread_cancel is never called. - Signal the timer queue condition before releasing the mutex, as it should be used. - Check the running flag before waiting for the condition in timer_loop. This avoids "The Lost Wake-Up Problem" when the program terminates before the timer thread is waiting. - Add fs_nvram_verify. - Reimplement nvram_export. - Rename vm_instance_t.ios_config to ios_startup_config. - Expand vm_platform_t.nvram_push_config to receive both startup-config and private-config. - Expand vm_platform_t.nvram_extract_config to get both startup-config and private-config. - Add vm_instance_t.ios_private_config. - Rework m_read_file to read the whole file at once. - Rework vm_nvram_push_config to handle both config files (NULL to keep existing data). - Extend hypervisor command 'set_config': - new optional argument for the file with private data. - new behaviour: an empty string "" means no config file. - Rework vm_ios_set_config to handle both config files (NULL to keep existing data). - Update documentation. - Extend hypervisor command 'extract_config': - return both startup-config and private-config data. - empty data is sent as an empty string ''. - Update documentation. - Extend hypervisor command 'push_config': - new optional argument for the private data. - new behavior: the string '(keep)' means no data is provided and it should preserve existing data. - Update documentation. - Add command line options --private-config and --startup-config. - Update documentation. - Fix warning "dereferencing type-punned pointer will break strict-aliasing rules". - Fix m32_tx_acquire not checking the HOLD bit properly. Note: the datasheet isn't available anymore, so the intended behavior was inferred from surrounding code; it was assumed that the hold bit is managed elsewhere - Fix warning "suggest parentheses around operand of ‘!’ or change ‘&’ to ‘&&’ or ‘!’ to ‘~’". - Fix warnings "assignment discards ‘const’ qualifier from pointer target type" by copying the backplane/supervisor eeproms. - Fix memory leaks of c6sup1_t.slot_eeprom data. - Fix warnings "‘’ defined but not used". __unused for function that are not referenced __maybe_unused for functions that are referenced in excluded code - Fix warnings "label ‘’ defined but not used". - Fix warnings "unused variable ‘’". UNUSED() for variables that are not used __maybe_used for variables that are used in excluded code - Fix warnings "variable ‘’ set but not used". delete unneeded variables that don't help understand the surrounding code __maybe_unused for the other variables - Adjust Makefiles: - clean nvram_export.o - add missing $(BIN_EXT) - add target 'both' to facilitate test compilations - Bump version to v0.2.10. Implemented enhancement #15 - extend hypervisor commands push_config, extract_config, and set_config Implemented enhancement #16 - reimplement nvram_export Fixed issue #17 - Dynamips hangs on startup if no (or unknown) parameters are given v0.2.8 and 0.2.9 +--------------------. | Release: v0.2.10 | +--------------------' 16-Sep-2013 to 10-Feb-2014 -------------------------- - Make memzone_map_* functions return NULL on failure. - Add memzone_map_exec_area. - Add memzone_unmap. - Add memzone_sync and memzone_sync_all. - Remove unnecessary includes to sys/mman.h. - Add documentation on the hypervisor module "object_store". - Update documentation on the hypervisor module "hypervisor". - Add documentation on the hypervisor module "vm_debug". - Update documentation on the hypervisor module "ethsw". - Add documentation on the hypervisor module "atm_bridge". - Update documentation on the hypervisor module "nio". - Fix hypervisor command push_config not receiving 3 arguments. - Update documentation on the hypervisor module "vm". - Update documentation on the hypervisor module "c7200". - Update documentation on the hypervisor module "c3600". - Add documentation on the hypervisor module "c3745". - Add documentation on the hypervisor module "c3725". - Add documentation on the hypervisor module "c2691". - Add documentation on the hypervisor module "c2600". - Add documentation on the hypervisor module "c1700". - Update documentation. - Add registry_rename. - Add hypervisor command "vm rename ". - Fix documentation of hypervisor command vm create. - Add hypervisor command "nio rename ". - Add hypervisor command "nio_bridge rename ". - Add hypervisor command "frsw rename ". - Add hypervisor command "atmsw rename ". - Document when hypervisor mode became available. - Add hypervisor command "atm_bridge rename ". - Add hypervisor command "atm_bridge rename ". - Add hypervisor command "ethsw rename ". - Add hypervisor command "object_store rename ". - Add hypervisor command "vm get_status ". - Warn the VM isn't running when connecting to the console/aux port. - Update changelog. - Replay old text when you connect to the console/aux port. - Fix hypervisor command 'vm rename' not renaming it's files. - Fix crash in vm_free with an alternate ROM filename. - Makefiles: - Remove un-needed code - Stable makefile updates - Top level makefile updates - Unstable makefile updates - Remove powerpc comment - Remove references to asmdefs - Define arm architecture (thanks to Ubuntu for the patch) - Change default NPE for c7200 platform from npe-200 to npe-400. - Fixed wrong variable being passed to registry_rename in vm_rename_instance. - Possibility to set the system id for c2691 routers. - Hypervisor commands to get the base MAC address of routers. - Add in the README.hypervisor that the get_mac_addr hypervisor commands are available since version 0.2.11 - Add hypervisor command 'vm clean_delete '. - Add missing changes to the hypervisor_mode.7 man page. - Make sure IPV6_JOIN_GROUP is defined in cygwin. - Bump version to v0.2.11. Fixed issue #20 - segmentation fault when accessing a device that failed to memory map it's file Implemented enhancement #21 - Renaming devices Closed issue #22 - Hypervisor documentation is out of date Implemented enhancement #36 - hypervisor command to clean the files of a vm Fixed issue #27 - Rename issues. +--------------------. | Release: v0.2.11 | +--------------------' 10-Feb-2014 to 27-Mar-2014 -------------------------- - Missing space in hypervisor_mode.7 manpage (line 310) - gt96k: more debug info - Fix packet looping on platforms with Gt96k FE. - The specs were not clear on whether the M-bit was a "Miss" or "Match". - It was being treated as a "Match" bit but it turns out IOS treats it as a "Miss" bit. @see http://forum.gns3.net/post26316.html#p26316 - Gt96k patch correcting the reporting of processed transmit descriptors back to IOS. - Fixes missing interrupt when gt96k doesn't own the first trasmit descriptor. - Fixes incorrect clearing of the Own bit. (last must wait for transmission, the rest are cleared when processed) - Fixes offset when writing back the cmd word of the non-last descriptors. (missing from 0.2.6-RC3) - Affects c2691, c3725 and c3745. @see http://forum.gns3.net/post26454.html#p26454 - Clarify the meaning of the +4 offsets with GT_SDMA_CMD_OFFSET. - Silence forgotten debug message. - Change default idlemax value from 1500 to 500. - Add hypervisor command 'vm_debug pmem_cfind'. - Bump version to v0.2.12. Fixed issue #29 - packet loss with multicast traffic Closed issue #31 - create hypervisor command to find a pattern in the router memory +--------------------. | Release: v0.2.12 | +--------------------' 04-Apr-2014 to 05-Jul-2014 -------------------------- - MPC860: Add debug message for the unimplemented CP reset command - Report the partid of dev_am79c971 type AM79C971_TYPE_10BASE_T as Am79C970A. - Fixes the "AMD Unknown" that shows up in "show controllers". IOS calls this "AMD Presidio" or "AmdP2". - MPC860 FEC: (logic is inlined in dev_mpc860.c for now) - implement PHY LXT970A, which was hardcoded as values in the MII registers - if sending with link down, report no heartbeat and lost carrier - PCMCIA disk: - annotate the "Card Information Structure" data - add missing CISTPL_END - Document Cisco EEPROM format v1 and v4. - Add CMake build system. - Missing empty line at end of fs_nvram.c - Fix unused function warnings. - Remove unused duplicate of physmem_get_hptr from stable/mips64_jit.c - Add test builds using Travis CI. - Create new README.md based on README for GitHub frontpage. - Add common.h. - Move generic includes, types and macros from utils.h to common.h. - Minimize includes in sbox.c/h. - Minimize includes in crc.c/h. - Fix unstable nojit compilation. - Fix unused variable warning in ppc32_jit.c. - Use dev_dec21140.c from unstable. - Fix bandwidth statistics - Check if the NIO can trasmit - Add #pragma once to common.h - Rename common.h to dynamips_common.h. - Delete ds1620.h. - This file is not used and most of the constants are in dev_ds1620.c. - Tell the compiler that mem_bswap32 does not require aligned data. - Minimize headers. - define FD_SETSIZE for Cygwin in dynamips_common.h - Rename double underscore macros: - rename __not_aligned to _not_aligned - rename __maybe_unused to _maybe_used - rename __unused to _unused - Use an exclusive lock while dumping captured packets. (thread safety) - Respect the snapshot length of the descriptor. - Report failed setups of hypervisor command 'nio setup_filter'. - Make a single FAT16 partition when pcmia disks are first created. - Change pcap snapshot length to 65535. - Remove old Makefiles to avoid confusion. (not gonna be maintained) - Improve TX performance of i8254x (PA-GE) by sending up to 16 packets at a time. - Merge pull request #45 from candlerb/candlerb/txperformance - Print the device name when debugging pci accesses. - Indicate the TODO list is from the original author. Final fix for issue #9 - Reproducable crash Fixed issue #38 - Unknown file system detected Fixed issue #41 - "Frame is Too Long" error in Wireshark Merge pull request #45 from candlerb/candlerb/txperformance +--------------------. | Release: v0.2.13 | +--------------------' 10-Jul-2014 to 01-Sep-2014 -------------------------- - Add -DUSE_UNSTABLE when making an unstable build (MacOSX) - Add optional argument 'format' to hypervisor commands - 'send_con_msg' and 'send_aux_msg'. - Report "X byte(s) written" on succeess. - String formats: * plain - plain string (default, old behavior) * base64 - base64 encoded string - Use an auxiliary variable to record configured ram size for npe-400 Fixed issue #49 - IOS crashes after router restart Fixed issue #50 - vm send_con_msg Fixed issue #55 - 'unstable' installs 'stable' version on Mac OS X +--------------------+ | Release: v0.2.14 | +--------------------+ dynamips-0.2.14/MAINTAINERS000066400000000000000000000004141241034141600151120ustar00rootroot00000000000000 Flávio J. Saraiva - dynamips maintainer and sporadic developer Jeremy Grossmann - GNS3 Programmer & Benevolent Dictator for Life. Daniel Lintott - Makefile Maintainer and Debian Package Maintainer dynamips-0.2.14/README000066400000000000000000000423141241034141600143020ustar00rootroot00000000000000Dynamips ======== Authors of this document: Fabien Devaux, Christophe Fillot, MtvE, Gordon Russell, Jeremy Grossmann and Flávio J. Saraiva. This is a continuation of Dynamips, based on the last development version and improved with patches wrote by various people from the community. This fork was named Dynamips-community up to the 0.2.8-community release and renamed to the original Dynamips on the 0.2.9 release. You can compile two different versions of dynamips with this code. Edit the Makefile to set the flags to suit your environment. One of the flags, DYNAMIPS_CODE, can be "stable" or "unstable". Unstable is the code which contains most of the development code, and is in particular suitable for use on a 64 bit Mac. Unfortunately this has proved to be unstable on other platforms. Stable contains the same code as Unstable, minus some mips64 bit optimisations and tcb code which seems to trigger instability on a number of platforms. You should probably use stable unless you have a very good reason. License: GNU GPLv2 Website: http://www.gns3.net/dynamips/ Forum: http://forum.gns3.net/ Repository: https://github.com/GNS3/dynamips Bugtracker: https://github.com/GNS3/dynamips/issues Original websites: http://www.ipflow.utc.fr/index.php/Cisco_7200_Simulator http://www.ipflow.utc.fr/blog/ Help for Cisco router simulator (Dynamips) ========================================== Emulated hardware ***************** The emulator currently supports the following platforms: - Cisco 7200 (NPE-100 to NPE-400) - Cisco 3600 (3620, 3640 and 3660) - Cisco 2691 - Cisco 3725 - Cisco 3745 - Cisco 2600 (2610 to 2650XM) - Cisco 1700 (1710 to 1760) By default, a Cisco 7206VXR with NPE-200 (256 Mb of DRAM) is emulated. To emulate another platform, use the "-P" command line option (for example, "-P 3725" or "-P 3600"). For the 7200, you can change the NPE type with the "-t" option. It is possible to select "npe-100", "npe-150", "npe-175", "npe-200", "npe-225", "npe-300" and "npe-400". The "npe-g1" is not working. For the 3600, a 3640 with 128 Mb is emulated by default. You can change this with the "-t" option and by specifying "3620" or "3660". Don't forget to set the chassis type depending on your IOS image, a c3660 image will not run on c3640 hardware and vice-versa. Remark: PCMCIA card emulation is not supported yet with Cisco 3600. Command Line Options overview ***************************** -l : Set logging file (default is dynamips_log.txt) -j : Disable the JIT compiler, very slow --exec-area : Set the exec area size (default: 64 Mb) --idle-pc : Set the idle PC (default: disabled) --timer-itv : Timer IRQ interval check (default: 1000) -i : Set instance ID -r : Set the virtual RAM size -o : Set the virtual ROM size -n : Set the NVRAM size -c : Set the configuration register -m : Set the MAC address of the chassis (default: automatically generated) -C, --startup-config : Import IOS configuration file into NVRAM --private-config : Import IOS configuration file into NVRAM -X : Do not use a file to simulate RAM (faster) -R : Load an alternate ROM (default: embedded) -k : Set the clock divisor (default: 4) -T : Console is on TCP -U : Console in on serial interface (default is on the terminal) -A : AUX is on TCP -B : AUX is on serial interface (default is no AUX port) --disk0 : Set PCMCIA ATA disk0: size --disk1 : Set PCMCIA ATA disk1: size -a : Virtual ATM switch configuration file -f : Virtual Frame-Relay switch configuration file -E : Virtual Ethernet switch configuration file -b : Virtual bridge configuration file -e : Show network device list of the host machine Options specific to the Cisco 7200 series: -t : Select NPE type (default: "npe-200") -M : Select Midplane ("std" or "vxr") -p : Define a Port Adapter -s : Bind a Network IO interface to a Port Adapter Options specific to the Cisco 3600 series ("dynamips -P 3600 --help"): -t : Select Chassis type (default: "3640") --iomem-size : IO memory (in percents, default: 5) -p : Define a Network Module -s : Bind a Network IO interface to a Network Module Options specific to the Cisco 2691 series ("dynamips -P 2691 --help"): --iomem-size : IO memory (in percents, default: 5) -p : Define a Network Module -s : Bind a Network IO interface to a Network Module Options specific to the Cisco 3725 series ("dynamips -P 3725 --help"): --iomem-size : IO memory (in percents, default: 5) -p : Define a Network Module -s : Bind a Network IO interface to a Network Module Options specific to the Cisco 3745 series ("dynamips -P 3745 --help"): --iomem-size : IO memory (in percents, default: 5) -p : Define a Network Module -s : Bind a Network IO interface to a Network Module Command Line Options details **************************** -k : Specify the clock divider (integer) based on the host clock. Alter the value to match the CISCO clock with the real time. The command "show clock" at the IOS' CLI will help you set this value. --idle-pc : The "idle PC" feature allows you to run a router instance without having a 100% CPU load. This implies that you can run a larger number of instances per real machine. To determine the "idle PC", start normally the emulator with your Cisco IOS image, and a totally IOS empty configuration (although not mandatory, this will give better results). When the image is fully booted, wait for the "Press RETURN to get started!" message prompt, but do not press Enter key. Wait about 5 seconds, then press "Ctrl-] + i". Some statistics will be gathered during 10 seconds. At the end, the emulator will display a list of possible values to pass to the "--idle-pc" option. You may have to try some values before finding the good one. To check if the idle PC value is good, just boot the Cisco IOS image, and check your CPU load when the console prompt is available. If it is low, you have found a good value, keep it preciously. Important remarks: ================== * An "idle PC" value is *specific* to a Cisco IOS image. You cannot boot a different IOS image without proceeding as described above. * Do not run the process while having the "autoconfiguration" prompt. --exec_area : The exec area is a pool of host memory used to store pages translated by the JIT (they contain the native code corresponding to MIPS code pages). Cisco 7200 Port Adapter Description "": ------------------------------------------------ Format: slot:pa_driver slot: the number of the physical slot (starts from 0) pa_driver: the name of a Port Adapter driver in: - C7200-IO-FE (FastEthernet, slot 0 only) - PA-FE-TX (FastEthernet, slots 1 to 6) - PA-4E (Ethernet, 4 ports) - PA-8E (Ethernet, 8 ports) - PA-4T+ (Serial, 4 ports) - PA-8T (Serial, 8 ports) - PA-A1 (ATM) Cisco 3600 Network Module Description "": -------------------------------------------------- Format: slot:nm_driver slot: the number of the physical slot (starts from 0) nm_driver: the name of a Network Module driver in: - NM-1E (Ethernet, 1 port) - NM-4E (Ethernet, 4 ports) - NM-1FE-TX (FastEthernet, 1 port) - NM-4T (Serial, 4 ports) - NM-16ESW (Ethernet switch module, 16 ports) - Leopard-2FE (Cisco 3660 FastEthernet in slot 0, automatically used) Cisco 2691/3725/3745 Network Module Description "": ------------------------------------------------------------ Format: slot:nm_driver slot: the number of the physical slot (starts from 0) nm_driver: the name of a Network Module driver in: - NM-1FE-TX (FastEthernet, 1 port) - NM-4T (Serial, 4 ports) - NM-16ESW (Ethernet switch module, 16 ports) - GT96100-FE (2 integrated ports, automatically used) NIO binding to Port Adapter "" and Network Modules "": ---------------------------------------------------------------------- Format: slot:port:netio_type[:netio_parameters] slot : the number of the physical slot (starts from 0) port : the port in the specified slot (starts from 0) netio_type : host interface for communication unix:: Use unix sockets for local communication. is created and represents the local NIC. is the file used by the other interface. (ex. "/tmp/local:/tmp/remote") vde:: For use with UML (User-Mode-Linux) or VDE switches. VDE stands for "Virtual Distributed Ethernet". Please refer to : http://sourceforge.net/projects/vde/ tap: Use a virtual ethernet device for communication. is the name of the tap device (ex. "tap0") gen_eth: Use a real ethernet device for communication, using libpcap 0.9 or WinPcap. Works on Windows and Unix systems. is the name of the Ethernet device (ex. "eth0") The device list can be found using the "-e" option. linux_eth: Use a real ethernet device for communication (Linux specific). is the name of the Ethernet device (ex. "eth0") udp::: Use an UDP socket for connection between remote instances. is the port we listen to. is the host listening the port you want to connect to. is the port you want to connect to. (ex. "1000:somehost:2000" and "2000:otherhost:1000" on the other side) tcp_cli:: Client side of a tcp connection. is the ip address of the server. is the port to connect to. tcp_ser: Server side of a tcp connection. is the port to listen to. null Dummy netio (used for testing/debugging), no parameters needed. VTTY binding to real serial port device "": ---------------------------------------------------- Format: {:baudrate{:databits{:parity{:stopbits{:hwflow}}}}}} device: character device name, e.g. /dev/ttyS0 baudrate: baudrate databits: number of databits. parity: data parity: N=none, O=odd, E=even, stopbits: number of stop bits hwflow: hardware flow control (0=disable, 1=enable) Note that the device field is mandatory, however other fields are optional. (dynamips will default to 9600, 8, N, 1, no hardware flow control) Note that access to the escape commands (described below) through a serial port are deliberately prevented, as the escape commands interfere with serial encapsulation protocols. Escape commands *************** You can press ^] (Ctrl + ]) at any time, followed by one of these characters: o : Show the VM object list d : Show the device list r : Dump MIPS CPU registers t : Dump MIPS TLB entries m : Dump the latest memory accesses s : Suspend CPU emulation u : Resume CPU emulation q : Quit the emulator b : Dump the instruction block tree h : JIT hash table statistics l : MTS64 cache statistics c : Write IOS configuration to disk (ios_cfg.txt) j : Non-JIT mode statistics i : Determine an idling pointer counter x : Experimentations (can crash the box!) ^]: Send ^] If you press an unrecognized key, help will be shown. Note: on Windows, it may be the "Ctrl + $" sequence. Virtual Bridge ************** The virtual bridge is used to emulate a shared network between emulator instances. Any emulator instance can act as a virtual bridge. The configuration file (specified by the "-b" option) contains a list of NetIO descriptors, with the following syntax: interface_name:netio_type[:netio_parameters] Example: # Connection to instance "I0" I0:udp:10000:127.0.0.1:10001 # Connection to instance "I1" I1:udp:10002:127.0.0.1:10003 # Connection to instance "I2" I2:udp:10004:127.0.0.1:10005 The "I0" instance would be launched with the following parameters: dynamips ios.bin -p 1:PA-FE-TX -s 1:0:udp:10001:127.0.0.1:10000 Virtual Ethernet switch *********************** The virtual ethernet switch is used to emulate an Ethernet network between emulator instances. This switch supports access and trunk ports (802.1Q). ISL will be available in a future release. Any emulator instance can act as a virtual ethernet switch. The configuration file (specified by the "-E" option) contains a list of NetIO descriptors (representing interfaces) and a list of interface properties (access/trunk port, VLAN info...) The interface definition is similar to Port Adapters: IF:interface_name:netio_type[:netio_parameters] 1) Configuring an Access Port syntax: ACCESS:interface_name:vlan_id 2) Configuration a 802.1Q Trunk Port syntax: DOT1Q:interface_name:native_vlan The native VLAN is not tagged. On Cisco devices, by default the native VLAN is VLAN 1. Example of configuration file: IF:E0:udp:10000:127.0.0.1:10001 IF:E1:udp:10002:127.0.0.1:10003 IF:E2:gen_eth:eth0 DOT1Q:E0:1 ACCESS:E1:4 DOT1Q:E2:1 Virtual ATM switch ****************** The virtual ATM switch fabric is used to emulate an ATM backbone between emulator instances. The use of this virtual switch is not mandatory, you can directly connect emulator instances for point-to-point ATM connections. Please note that only basic VP/VC switching is supported, there is no support for ILMI/QSAAL/... or other specific ATM protocols. Any emulator instance can act as a virtual ATM switch. Example of configuration file (specified by the "-a" option): # Virtual Interface List IF:A0:udp:10001:127.0.0.1:10000 IF:A1:udp:10002:127.0.0.1:10003 IF:A2:udp:10004:127.0.0.1:10005 # VP connection between I0 and I1 VP:A0:10:A1:20 VP:A1:20:A0:10 # VP connection between I0 and I2 VP:A0:11:A2:30 VP:A2:30:A0:11 # VC connection between I1 and I2 VC:A1:5:2:A2:7:3 VC:A2:7:3:A1:5:2 In this example, we have 3 virtual interfaces, A0, A1 and A2. The syntax for interface definition is similar to Port Adapters: IF:interface_name:netio_type[:netio_parameters] You can do VP switching or VC switching: 1) VP switching syntax: VP:input_if:input_vpi:output_if:output_vpi 2) VC switching syntax: VC:input_if:input_vpi:input_vci:output_if:output_vpi:output_vci Testing the Virtual ATM switch with one dynamips instance ********************************************************* (Contribution of Mtv Europe) Virtual ATM switch configuration file ("atm.cfg"): IF:A0:udp:10003:127.0.0.1:10001 IF:A1:udp:10004:127.0.0.1:10002 # a0/vpi=1/vci=100 connects to a1/vpi=2/vci=200 VC:A0:1:100:A1:2:200 VC:A1:2:200:A0:1:100 Invoking dynamips: ./dynamips -p 1:PA-A1 -s 1:0:udp:10001:127.0.0.1:10003 \ -p 2:PA-A1 -s 2:0:udp:10002:127.0.0.1:10004 \ -a atm.cfg IOS.BIN (note input ports of IOS interfaces are output ports of ATM switch interfaces, and vice versa). IOS configuration: ip cef ip vrf test rd 1:1 route-target both 1:1 int a1/0 no shut int a1/0.2 p ip addr 1.1.1.1 255.255.255.0 pvc 1/100 interface a2/0 no shut interface a2/0.2 p ip vrf forwarding test ip addr 1.1.1.2 255.255.255.0 pvc 2/200 ! # ping 1.1.1.2 !!!!! Virtual Frame-Relay switch ************************** The virtual Frame-Relay switch fabric is used to emulate a Frame-Relay backbone between emulator instances. The use of this virtual switch is not mandatory, you can directly connect emulator instances with appropriate IOS configuration. Any emulator instance can act as a virtual Frame-Relay switch. There is only a basic implementation of the LMI protocol (ANSI Annex D), which is probably not conforming but works with Cisco IOS. Fortunately, Cisco IOS is able to detect automatically the LMI protocol. Example of configuration file (specified by the "-f" option): # Virtual Interface List IF:S0:udp:10001:127.0.0.1:10000 IF:S1:udp:10002:127.0.0.1:10003 # DLCI switching between S0 and S1 VC:S0:200:S1:100 VC:S1:100:S0:200 In this example, we have 2 virtual interfaces, S0 and S1. The syntax for interface definition is similar to Port Adapters: IF:interface_name:netio_type[:netio_parameters] DLCI switching syntax: VC:input_if:input_dlci:output_if:output_dlci In the example above, the switch is configured to switch packets received on interface S0 with DLCI 200 to interface S1 with DLCI 100, and vice-versa. == EOF == dynamips-0.2.14/README.hypervisor000066400000000000000000000661431241034141600165210ustar00rootroot00000000000000Description of the hypervisor mode ================================== The hypervisor mode of dynamips allows you to run simultaneously many virtual router instances, and to simulate ATM, Ethernet or Frame-Relay networks. Available since version 0.2.5. Running dynamips in hypervisor mode =================================== dynamips -H [:] Managing the hypervisor ======================= You can connect directly to the TCP control port with telnet, or use dynagen/dynagui that will pass commands transparently. The second method is highly recommended. The command syntax is simple: [arguments...] For example: "vm start R1" starts virtual instance named "R1". The modules that are currently defined are given below: * hypervisor : General hypervisor management * vm : General virtual machine (VM) management * vm_debug : General virtual machine (VM) debugging * c7200 : Virtual instances of Cisco 7200 * c3745 : Virtual instances of Cisco 3745 * c3725 : Virtual instances of Cisco 3725 * c3600 : Virtual instances of Cisco 3600 * c2691 : Virtual instances of Cisco 2691 * c2600 : Virtual instances of Cisco 2600 * c1700 : Virtual instances of Cisco 1700 * nio : Network Input/Output (NIO) descriptors * nio_bridge : NIO bridges (shared media) * atmsw : ATM switches * atm_bridge : ATM bridges * frsw : Frame-Relay switches * ethsw : Ethernet switches * object_store : Object store Hypervisor management module ("hypervisor") ============================================ * "hypervisor version" : Display the version of dynamips. * "hypervisor module_list" : Display the module list. * "hypervisor cmd_list " : Display commands recognized by the specified module. * "hypervisor close" : Close the current session. * "hypervisor stop" : Destroy all objects and stop hypervisor. * "hypervisor reset" : Destroy all objects. (used to get an empty configuration) * "hypervisor working_dir " : Set the directory to use to store files. * "hypervisor save_config " : Save the configuration of all objects into the specified file. * "hypervisor parser_test [ [... ]]" : Display up to 10 arguments. (since version 0.2.6-RC1) * "hypervisor uuid" : Display the local uuid. (since version 0.2.8-RC3) * "hypervisor tsg_stats" : Dump statistics about JIT code sharing to the console. (since version 0.2.8-RC3, unstable) Virtual Machine module ("vm") ============================= * "vm list" : List all VM instances. (c7200, c3745, c3725, c3600, c2691, c2600, c1700) * "vm list_con_ports" : List all VM console TCP port. (since version 0.2.6-RC5) * "vm create " : Create a new router instance. The ID must be unique and is used to name files on disk. (since version 0.2.8-RC1) * "vm rename " : Rename a router instance. (since version 0.2.11) * "vm delete " : Delete the specified instance. (since version 0.2.8-RC1) * "vm clean_delete " : Delete the specified instance and all the related files (best effort). (since version 0.2.11) * "vm start " : Start the instance. At least the IOS image must be set. (since version 0.2.8-RC1) * "vm stop " : Stop the instance. The settings are kept. (since version 0.2.8-RC1) * "vm get_status " : Get the status of a VM instance. Return values: 0=inactive, 1=shutting down, 2=running, 3=suspended. (since version 0.2.11) * "vm set_tsg " : Set translation sharing group. (since version 0.2.8-RC3-community, unstable) * "vm set_debug_level " : Set the debug level (which is a number) for a VM. By default, no specific debug is enabled (level = 0). * "vm set_ios " : Set the IOS image file to use. There is no default. * "vm set_config []" : Set the config files that are pushed to startup-config and private-config in NVRAM when the instance is started. To keep existing data, use an empty string ('') for the filename. The optional is an empty string by default. (supports since version 0.2.10) * "vm extract_config " : Get the contents of the config files startup-config and private-config from NVRAM. The data of each file is encoded in a Base64 string, surrounded by single quotes. (returns private-config since version 0.2.10) * "vm push_config []" : Push configuration to the config files startup-config and private-config in NVRAM. The data is a Base64 encoded string, or '(keep)' to keep existing data. The optional is '(keep)' by default. (supports since version 0.2.11) * "vm set_ram " : Set the RAM size, specified in Mbytes. * "vm set_nvram " : Set the NVRAM size, specified in Kbytes. * "vm set_ram_mmap <0|1>" : Enable/Disable use of a mapped file to simulate router memory. By default, a mapped file is used. This is a bit slower, but requires less memory. * "vm set_sparse_mem <0|1>" : Enable/disable use of sparse memory. (since version 0.2.7-RC1) * "vm suspend " : Suspend execution of the instance. * "vm resume " : Resume execution of the instance. * "vm set_clock_divisor " : Set the clock divisor value. The higher is the value, the faster is the clock in the virtual machine. The default is 4, but it is often required to adjust it. * "vm set_blk_direct_jump <0|1>" : Enable/disable use of block direct jump. (compatibility option, since version 0.2.7-RC2) * "vm set_idle_pc " : Set the idle Pointer Counter (PC). You must determine it through the method explained in the main README file. * "vm set_idle_pc_online " : Set the idle PC value when the CPU is online. (since version 0.2.6-RC2) * "vm get_idle_pc_prop " : Get the idle PC proposals. Takes 1000 measurements and records up to 10 idle PC proposals. There is a 10ms wait between each measurement. (since version 0.2.6-RC2) * "vm show_idle_pc_prop " : Dump the idle PC proposals. (since version 0.2.6-RC2) * "vm set_idle_max " : Set CPU idle max value. (since version 0.2.6-RC2) * "vm set_idle_sleep_time " : Set CPU idle sleep time value. (since version 0.2.6-RC2) * "vm show_timer_drift " : Show info about potential timer drift. (since version 0.2.6-RC3) * "vm set_ghost_file " : Set ghost RAM file. (since version 0.2.6-RC3, needs an extra bogus argument before version 0.2.6-RC4) * "vm set_ghost_status " : Set ghost RAM status. (since version 0.2.6-RC3, needs an extra bogus argument before version 0.2.6-RC4) * "vm set_exec_area " : Set the exec area size. The exec area is a pool of host memory used to store pages translated by the JIT (they contain the native code corresponding to MIPS code pages). * "vm set_disk0 " : Set size of PCMCIA ATA disk0. * "vm set_disk1 " : Set size of PCMCIA ATA disk1. * "vm set_conf_reg " : Set the config register value. The default is 0x2102. * "vm set_con_tcp_port " : Set the TCP port to use for console. By default, no TCP port is chosen, meaning that you cannot get access to the console. * "vm set_aux_tcp_port " : Set the TCP port to use for AUX port. By default, no TCP port is chosen, meaning that you cannot get access to the AUX port. * "vm cpu_info " : Show info about the CPU identified by "cpu_id". The boot CPU (which is typically the only CPU) has ID 0. * "vm cpu_usage " : Show cpu usage of dynamips in seconds. (experimental) The instance must exist, "cpu_id" is ignored. (since version 0.2.8-RC5-community) * "vm send_con_msg []" : (since version 0.2.6-RC3) Send a message on the console. It only writes the bytes that fit in the console buffer. (since version 0.2.14) The optional argument indicates the string format. On success it will report "X byte(s) written". String formats: * plain - plain string (default, old behavior) * base64 - base64 encoded string * "vm send_aux_msg []" : (since version 0.2.6-RC3) Send a message on the AUX port. It only writes the bytes that fit in the aux buffer. (since version 0.2.14) The optional argument indicates the string format. On success it will report "X byte(s) written". String formats: * plain - plain string (default, old behavior) * base64 - base64 encoded string * "vm slot_bindings " : Show slot bindings. (since version 0.2.8-RC1) * "vm slot_nio_bindings " : Show NIO bindings for the specified slot. (since version 0.2.8-RC1) * "vm slot_add_binding " : Add a slot binding. (since version 0.2.8-RC1) * "vm slot_remove_binding " : Remove a slot binding . (since version 0.2.8-RC1) * "vm slot_add_nio_binding " : Add a NIO binding for a slot/port. (since version 0.2.8-RC1) * "vm slot_remove_nio_binding " : Remove a NIO binding for a slot/port. (since version 0.2.8-RC1) * "vm slot_enable_nio " : Enable NIO of the specified slot/port. (since version 0.2.8-RC1) * "vm slot_disable_nio " : Disable NIO of the specified slot/port. (since version 0.2.8-RC1) * "vm slot_oir_start " : OIR to start a slot/subslot. (since version 0.2.8-RC3-community) * "vm slot_oir_stop " : OIR to stop a slot/subslot. (since version 0.2.8-RC3-community) Virtual Machine debugging module ("vm_debug") ======================================= Available since version 0.2.6-RC1. * "vm_debug show_cpu_regs " : Dump CPU registers to the console. * "vm_debug show_cpu_mmu " : Dump CPU MMU info to the console. (since version 0.2.7-RC1) * "vm_debug set_cpu_reg " : Set the value of a CPU register. * "vm_debug add_cpu_breakpoint
" : Add a breakpoint. * "vm_debug remove_cpu_breakpoint
" : Remove a breakpoint. * "vm_debug pmem_w32
" : Write a 32-bit memory word to physical memory. * "vm_debug pmem_r32
" : Read a 32-bit memory word from physical memory. * "vm_debug pmem_w16
" : Write a 16-bit memory word to physical memory. * "vm_debug pmem_r16
" : Read a 16-bit memory word from physical memory. * "vm_debug pmem_cfind [ []]" : Find a sequence of bytes in physical memory interval [first,last]. The byte sequence is composed of hexadecimal characters and must have a length multiple of 2. The interval defaults to the complete memory space. Only the memory of cacheable devices (ram, rom, disks, ...) is searched. (since version 0.2.12) Virtual Cisco 7200 instances module ("c7200") ============================================== * "c7200 list" : List all existing Cisco 7200 instances. * "c7200 set_npe " : Set the NPE model. For example: npe-100, npe-400, ... The default is "npe-400". * "c7200 set_midplane " : Set the midplane model, it can be either "std" or "vxr". The default is "vxr". * "c7200 get_mac_addr " : Get the base MAC address of the router. By default, the address is automatically generated with this pattern : ca..0000 (Cisco format). (since version 0.2.11) * "c7200 set_mac_addr " : Set the base MAC address of the router. The MAC address patern can be the Cisco format (e.g. ca01.1234.0000) or the standard format (e.g. ca:01:12:34:00:00). * "c7200 set_system_id " : Set the system id. (since version 0.2.8-RC3-community) * "c7200 set_temp_sensor " : Set temperature for a DS1620 sensor. This can be used to simulate environmental problems like overheat. (since version 0.2.8-RC3-community) * "c7200 set_power_supply <0|1>" : Set power supply status. This can be used to simulate environmental problems like power loss. (since version 0.2.8-RC3-community) * "c7200 show_hardware " : Display virtual hardware info about the instance. Virtual Cisco 3745 instances module ("c3745") ============================================== Available since version 0.2.6-RC3. * "c3745 list" : List all existing Cisco 3745 instances. * "c3745 set_iomem " : Set the I/O mem size. * "c3745 get_mac_addr " : Get the base MAC address of the router. By default, the address is automatically generated with this pattern : c4..0000 (Cisco format). (since version 0.2.11) * "c3745 set_mac_addr " : Set the base MAC address of the router. The MAC address patern can be the Cisco format (e.g. c401.1234.0000) or the standard format (e.g. c4:01:12:34:00:00). * "c3745 set_system_id " : Set the system id. (since version 0.2.8-RC3-community) * "c3745 show_hardware " : Display virtual hardware info about the instance. Virtual Cisco 3725 instances module ("c3725") ============================================== Available since version 0.2.6-RC3. * "c3725 list" : List all existing Cisco 3725 instances. * "c3725 set_iomem " : Set the I/O mem size. * "c3725 get_mac_addr " : Get the base MAC address of the router. By default, the address is automatically generated with this pattern : c2..0000 (Cisco format). (since version 0.2.11) * "c3725 set_mac_addr " : Set the base MAC address of the router. The MAC address patern can be the Cisco format (e.g. c201.1234.0000) or the standard format (e.g. c2:01:12:34:00:00). * "c3725 set_system_id " : Set the system id. (since version 0.2.8-RC3-community) * "c3725 show_hardware " : Display virtual hardware info about the instance. Virtual Cisco 3600 instances module ("c3600") ============================================== * "c3600 list" : List all existing Cisco 3600 instances. * "c3600 set_chassis " : Set the chassis model. Possible values: 3620, 3640, 3660. The default is "3640". * "c3600 set_iomem " : Set the I/O mem size. * "c3600 get_mac_addr " : Get the base MAC address of the router. By default, the address is automatically generated with this pattern : cc..0000 (Cisco format). (since version 0.2.11) * "c3600 set_mac_addr " : Set the base MAC address of the router. The MAC address patern can be the Cisco format (e.g. cc01.1234.0000) or the standard format (e.g. cc:01:12:34:00:00). * "c3600 set_system_id " : Set the system id. (since version 0.2.8-RC3-community) * "c3600 show_hardware " : Display virtual hardware info about the instance. Virtual Cisco 2691 instances module ("c2691") ============================================== Available since version 0.2.6-RC3. * "c2691 list" : List all existing Cisco 2691 instances. * "c2691 set_iomem " : Set the I/O mem size. * "c2691 get_mac_addr " : Get the base MAC address of the router. By default, the address is automatically generated with this pattern : c0..0000 (Cisco format). (since version 0.2.11) * "c2691 set_mac_addr " : Set the base MAC address of the router. The MAC address patern can be the Cisco format (e.g. c001.1234.0000) or the standard format (e.g. c0:01:12:34:00:00). * "c2691 set_system_id " : Set the system id. (since version 0.2.11) * "c2691 show_hardware " : Display virtual hardware info about the instance. Virtual Cisco 2600 instances module ("c2600") ============================================== Available since version 0.2.7-RC1. * "c2600 list" : List all existing Cisco 2600 instances. * "c2600 set_chassis " : Set the chassis model. Possible values: 2610, 2611, 2620, 2621, 2610XM, 2611XM, 2620XM, 2621XM, 2650XM, 2651XM. The default is "2610". * "c2600 set_iomem " : Set the I/O mem size. * "c2600 get_mac_addr " : Get the base MAC address of the router. By default, the address is automatically generated with this pattern : c8..0000 (Cisco format). (since version 0.2.11) * "c2600 set_mac_addr " : Set the base MAC address of the router. The MAC address patern can be the Cisco format (e.g. c801.1234.0000) or the standard format (e.g. c8:01:12:34:00:00). * "c2600 set_system_id " : Set the system id. (since version 0.2.8-RC3-community) * "c2600 show_hardware " : Display virtual hardware info about the instance. Virtual Cisco 1700 instances module ("c1700") ============================================== Available since version 0.2.8-RC1. * "c1700 list" : List all existing Cisco 1700 instances. * "c1700 set_chassis " : Set the chassis model. Possible values: 1710, 1720, 1721, 1750, 1751, 1760. The default is "1720". * "c1700 set_iomem " : Set the I/O mem size. * "c1700 get_mac_addr " : Get the base MAC address of the router. By default, the address is automatically generated with this pattern : d0..0000 (Cisco format). (since version 0.2.11) * "c1700 set_mac_addr " : Set the base MAC address of the router. The MAC address patern can be the Cisco format (e.g. d001.1234.0000) or the standard format (e.g. d0:01:12:34:00:00). * "c1700 set_system_id " : Set the system id. (since version 0.2.8-RC3-community) * "c1700 show_hardware " : Display virtual hardware info about the instance. Network Input/Output (NIO) module ("nio") ========================================== * "nio list" : List all exiting NIOs. * "nio create_udp " : Create an UDP NIO with the specified parameters. * "nio create_udp_auto " : Create an auto UDP NIO. (since version 0.2.8-RC3-community) * "nio connect_udp_auto " : Connect an UDP Auto NIO to a remote host/port. (since version 0.2.8-RC3-community) * "nio create_mcast " : Create a Multicast NIO. (since version 0.2.8-RC3-community) * "nio set_mcast_ttl " : Set TTL for a Multicast NIO. (since version 0.2.8-RC3-community) * "nio create_unix " : Create an UNIX NIO with the specified parameters. * "nio create_vde " : Create a VDE NIO with the specified parameters. VDE stands for "Virtual Distributed Ethernet" and is compatible with UML (User-Mode-Linux) switch. * "nio create_tap " : Create a TAP NIO. TAP devices are supported only on Linux and FreeBSD and require root access. * "nio create_gen_eth " : Create a generic ethernet NIO, using PCAP (0.9.4 and greater). It requires root access. Available if compiled with GEN_ETH. * "nio create_linux_eth " : Create a Linux ethernet NIO. It requires root access and is supported only on Linux platforms. Available if compiled with LINUX_ETH. * "nio create_null " : Create a Null NIO. * "nio create_fifo " : Create a FIFO NIO. * "nio crossconnect_fifo " : Establish a cross-connect between 2 FIFO NIO. * "nio rename " : Rename a NIO. (since version 0.2.11) * "nio delete " : Delete the specified NIO. The NIO can be deleted only when it is not anymore in use by another object. * "nio set_debug " : Enable/Disable debugging for the specified NIO. When debugging is enabled, received and emitted packets are displayed at screen. It is mainly used to debug interface drivers. * "nio bind_filter " : Bind a packet filter. Direction is 0 for receiving, 1 for sending, 2 for both. Filter "freq_drop" drops packets. Filter "capture" captures packets and is only available if compiled with GEN_ETH. * "nio unbind_filter " : Unbind a packet filter. * "nio setup_filter [ [...]]" : Setup a packet filter for a given NIO. The arguments are passed on to the setup function of the filter. Filter "freq_drop" has 1 argument "". It will drop everything with a -1 frequency, drop every Nth packet with a positive frequency, or drop nothing. Filter "capture" has 2 arguments " ". It will capture packets to the target output file. The link type name is a case-insensitive DLT_ name from the pcap library constants with the DLT_ part removed. * "nio get_stats " : Get statistics of a NIO. (since version 0.2.8-RC3-community) * "nio reset_stats " : Reset statistics of a NIO. (since version 0.2.8-RC3-community) * "nio set_bandwidth " : Set bandwidth constraint. (since version 0.2.8-RC3-community) NIO bridge module ("nio_bridge") ================================= * "nio_bridge list" : List all NIO bridges. * "nio_bridge create " : Create a NIO bridge. A NIO bridge acts as a shared media (a kind of hub). * "nio_bridge rename " : Rename a NIO bridge. (since version 0.2.11) * "nio_bridge delete " : Delete a NIO bridge. * "nio_bridge add_nio " : Add a NIO as new port in a NIO bridge. The NIO must be created through the "nio" module. * "nio_bridge remove_nio " : Remove the specified NIO as member of the NIO bridge. Virtual Ethernet switch module ("ethsw") ================================== * "ethsw list" : List all Ethernet switches. * "ethsw create " : Create a new Ethernet switch. * "ethsw rename " : Rename an Ethernet switch. (since version 0.2.11) * "ethsw delete " : Delete the specified Ethernet switch. * "ethsw add_nio " : Add a NIO as new port in an Ethernet switch. The NIO must be created through the "nio" module. * "ethsw remove_nio " : Remove the specified NIO as member of the Ethernet switch. * "ethsw set_access_port " : Set the specified port as an ACCESS port in VLAN . * "ethsw set_dot1q_port " : Set the specified port as a 802.1Q trunk port, with native VLAN . * "ethsw set_qinq_port " : Set the specified port as a trunk (QinQ) port. (since version 0.2.3-RC3-community) * "ethsw clear_mac_addr_table " : Clear the MAC address table. * "ethsw show_mac_addr_table " : Show the MAC address table (output format: Ethernet address, VLAN, NIO) Virtual ATM switch module ("atmsw") ============================= * "atmsw list" : List all ATM switches. * "atmsw create " : Create a new ATM switch. * "atmsw rename " : Rename an ATM switch. (since version 0.2.11) * "atmsw delete " : Delete the specified ATM switch. * "atmsw create_vpc " : Create a new Virtual Path connection (unidirectional). * "atmsw delete_vpc " : Delete a Virtual Path connection (unidirectional). * "atmsw create_vcc " : Create a new Virtual Channel connection (unidirectional). * "atmsw delete_vcc " : Delete a Virtual Channel connection (unidirectional). Virtual ATM bridge module ("atm_bridge") ================================== Available since version 0.2.8-RC2. * "atm_bridge list" : List all ATM bridges. * "atm_bridge create " : Create a new ATM bridge. * "atm_bridge rename " : Rename an ATM bridge. (since version 0.2.11) * "atm_bridge delete " : Delete an ATM bridge. * "atm_bridge configure " : Configure an ATM bridge. * "atm_bridge unconfigure " : Unconfigure an ATM bridge. Virtual Frame-Relay switch module ("frsw") ==================================== * "frsw list" : List all Frame-Relay switches. * "frsw create " : Create a new Frame-Relay switch. * "frsw rename " : Rename a Frame-Relay switch. (since version 0.2.11) * "frsw delete " : Delete the specified Frame-Relay switch. * "frsw create_vc " : Create a new Virtual Circuit connection (unidirectional). * "frsw delete_vc " : Delete a Virtual Circuit connection (unidirectional). Object store module ("object_store") ==================================== Available since version 0.2.8-RC2. * "object_store write " : Write an object, data provided in base64 encoding. * "object_store read " : Read an object and return data in base64 encoding. * "object_store rename " : Rename an object. (since version 0.2.11) * "object_store delete " : Delete an object from the store. * "object_store delete_all" : Delete all objects from the store * "object_store list" : Object list. dynamips-0.2.14/README.md000066400000000000000000000050241241034141600146760ustar00rootroot00000000000000# Dynamips (Cisco Router Emulator) [![Build Status](https://travis-ci.org/GNS3/dynamips.svg?branch=master)](https://travis-ci.org/GNS3/dynamips) ## Overview Authors of this document: Fabien Devaux, Christophe Fillot, MtvE, Gordon Russell, Jeremy Grossmann and Flávio J. Saraiva. Converted to markdown format by Daniel Lintott. This is a continuation of Dynamips, based on the last development version and improved with patches wrote by various people from the community. This fork was named Dynamips-community up to the 0.2.8-community release and renamed to the original Dynamips on the 0.2.9 release. You can compile two different versions of Dynamips with this code. Edit the Makefile to set the flags to suit your environment. One of the flags, DYNAMIPS_CODE, can be "stable" or "unstable". Unstable is the code which contains most of the development code, and is in particular suitable for use on a 64 bit Mac. Unfortunately this has proved to be unstable on other platforms. Stable contains the same code as Unstable, minus some mips64 bit optimisations and tcb code which seems to trigger instability on a number of platforms. You should probably use stable unless you have a very good reason. For more information on the how to use Dynamips see the README file License: GNU GPLv2 ### How to compile Dynamips Dynamips now uses the CMake build system. To compile Dynamips you will need CMake and a working GCC or Clang compiler, as well as the build dependencies. #### Build Dependencies On Debian based systems the following build dependencies are required and can be installed using apt-get: - libelf-dev - uuid-dev - libpcap0.8-dev Similar packages should be available for most distributions, consult your distributions package list to find them. #### Compiling Either download and extract a source tarball from the releases page or clone the Git repository using: ``` git clone git://github.com/GNS3/dynamips.git cd dynamips mkdir build cd build cmake .. ``` This will generate the Makefiles required for compiling Dynamips. To just build Dynamips simple run: ``` make ``` or to build and install Dynamips run: ``` make install ``` The specify a differant installation location run: ``` cmake -DCMAKE_INSTALL_PREFIX=/target/path .. ``` ### Useful Information Website: http://www.gns3.net/dynamips/ Forum: http://forum.gns3.net/ Repository: https://github.com/GNS3/dynamips Bugtracker: https://github.com/GNS3/dynamips/issues ### Original websites http://www.ipflow.utc.fr/index.php/Cisco_7200_Simulator http://www.ipflow.utc.fr/blog/ dynamips-0.2.14/RELEASE-NOTES000066400000000000000000000024501241034141600153100ustar00rootroot00000000000000Release Notes for Cisco router simulator (Dynamips) =================================================== Version: v0.2.14 Release date: Tuesday, September 23, 2014 (2014-09-23) Source code: https://github.com/GNS3/dynamips/tree/v0.2.14 License: GNU GPLv2 What's New since v0.2.13 ======================== Allow building unstable version on MacOSX Add optional argument 'format' to hypervisor commands 'send_con_msg' and 'send_aux_msg'. Report "X byte(s) written" on succeess. - String formats: * plain - plain string (default, old behavior) * base64 - base64 encoded string Fix issue with 7200 IOS crashing after restart Fixed issue #49 - IOS crashes after router restart Fixed issue #50 - vm send_con_msg Fixed issue #55 - 'unstable' installs 'stable' version on Mac OS X Known Issues ============ Reload from the router console doesn't work. - https://github.com/GNS3/dynamips/issues/6 Using a self decompressing image can lead to an invalid IA and stop the cpu. - https://github.com/GNS3/dynamips/issues/8 Packet transmission rate is limited. - https://github.com/GNS3/dynamips/issues/46 c1700 IOS Images start once and then fail to boot on second boot - https://github.com/GNS3/dynamips/issues/54 There is lots of missing hardware functionality. There are memory leaks. dynamips-0.2.14/TODO000066400000000000000000000020241241034141600141040ustar00rootroot00000000000000XXX this TODO list is from the original author, it is kept as reference - Correct branch delay slot handling with exceptions. - Complete/accurate console driver. - Memory write protection and proper TLB management. - TLB exceptions. - FPU operations. - add/addi: to implement with integer overflow exception support ? - Multicore CPU ? + TODO list on Web-site. object referencing ================== * at this time, only at high-level, should be done at low-level ? * nio management for atmsw/frsw/bridge/...: really useful to record NIOs ? * nio bindings for instances: add_nio_binding / pa_set_nio / pa_unset_nio ... add_nio_binding is used before instance startup. ===> add_nio_binding/remove_nio_binding: manage NIO bindings with object reference. ===> pa_set_nio/pa_unset_nio become pa_enable_nio/pa_disable_nio => without NIO parameter (NIO must be defined with add_nio_binding) ===> remove_nio_binding: check the NIO is not used anymore in instance ??? ===> Remark: a NIO can be used many times (example: shared eth media)dynamips-0.2.14/cmake/000077500000000000000000000000001241034141600144765ustar00rootroot00000000000000dynamips-0.2.14/cmake/FindLibElf.cmake000066400000000000000000000033301241034141600174350ustar00rootroot00000000000000# Note: copied from https://github.com/facebook/hhvm/blob/master/CMake/FindLibElf.cmake # - Try to find libelf # Once done this will define # # LIBELF_FOUND - system has libelf # LIBELF_INCLUDE_DIRS - the libelf include directory # LIBELF_LIBRARIES - Link these to use libelf # LIBELF_DEFINITIONS - Compiler switches required for using libelf # # Copyright (c) 2008 Bernhard Walle # # Redistribution and use is allowed according to the terms of the New # BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # if (LIBELF_LIBRARIES AND LIBELF_INCLUDE_DIRS) set (LibElf_FIND_QUIETLY TRUE) endif (LIBELF_LIBRARIES AND LIBELF_INCLUDE_DIRS) find_path (LIBELF_INCLUDE_DIRS NAMES libelf.h PATHS /usr/include /usr/include/libelf /usr/local/include /usr/local/include/libelf /opt/local/include /opt/local/include/libelf /sw/include /sw/include/libelf ENV CPATH) find_library (LIBELF_LIBRARIES NAMES elf PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBELF_FOUND to TRUE if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibElf DEFAULT_MSG LIBELF_LIBRARIES LIBELF_INCLUDE_DIRS) if ( 0 ) #not needed in dynamips SET(CMAKE_REQUIRED_LIBRARIES elf) INCLUDE(CheckCXXSourceCompiles) CHECK_CXX_SOURCE_COMPILES("#include int main() { Elf *e = (Elf*)0; size_t sz; elf_getshdrstrndx(e, &sz); return 0; }" ELF_GETSHDRSTRNDX) endif () mark_as_advanced(LIBELF_INCLUDE_DIRS LIBELF_LIBRARIES ELF_GETSHDRSTRNDX) dynamips-0.2.14/cmake/FindPCAP.cmake000066400000000000000000000100351241034141600170230ustar00rootroot00000000000000# Note: copied from https://code.google.com/p/loganon/source/browse/cmake/FindPCAP.cmake # added library name wpcap (for WinPcap) ################################################################### # # Copyright (c) 2006 Frederic Heem, # 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 the Telsey 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 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 # 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. # ################################################################### # - Find pcap # Find the PCAP includes and library # http://www.tcpdump.org/ # # The environment variable PCAPDIR allows to specficy where to find # libpcap in non standard location. # # PCAP_INCLUDE_DIRS - where to find pcap.h, etc. # PCAP_LIBRARIES - List of libraries when using pcap. # PCAP_FOUND - True if pcap found. IF(EXISTS $ENV{PCAPDIR}) FIND_PATH(PCAP_INCLUDE_DIR NAMES pcap/pcap.h pcap.h PATHS $ENV{PCAPDIR} NO_DEFAULT_PATH ) FIND_LIBRARY(PCAP_LIBRARY NAMES pcap PATHS $ENV{PCAPDIR} NO_DEFAULT_PATH ) ELSE(EXISTS $ENV{PCAPDIR}) FIND_PATH(PCAP_INCLUDE_DIR NAMES pcap/pcap.h pcap.h ) FIND_LIBRARY(PCAP_LIBRARY NAMES pcap wpcap ) ENDIF(EXISTS $ENV{PCAPDIR}) SET(PCAP_INCLUDE_DIRS ${PCAP_INCLUDE_DIR}) SET(PCAP_LIBRARIES ${PCAP_LIBRARY}) IF(PCAP_INCLUDE_DIRS) MESSAGE(STATUS "Pcap include dirs set to ${PCAP_INCLUDE_DIRS}") ELSE(PCAP_INCLUDE_DIRS) MESSAGE(FATAL " Pcap include dirs cannot be found") ENDIF(PCAP_INCLUDE_DIRS) IF(PCAP_LIBRARIES) MESSAGE(STATUS "Pcap library set to ${PCAP_LIBRARIES}") ELSE(PCAP_LIBRARIES) MESSAGE(FATAL "Pcap library cannot be found") ENDIF(PCAP_LIBRARIES) #Functions if ( 0 )# not needed in dynamips INCLUDE(CheckFunctionExists) SET(CMAKE_REQUIRED_INCLUDES ${PCAP_INCLUDE_DIRS}) SET(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARIES}) CHECK_FUNCTION_EXISTS("pcap_breakloop" HAVE_PCAP_BREAKLOOP) CHECK_FUNCTION_EXISTS("pcap_datalink_name_to_val" HAVE_PCAP_DATALINK_NAME_TO_VAL) CHECK_FUNCTION_EXISTS("pcap_datalink_val_to_name" HAVE_PCAP_DATALINK_VAL_TO_NAME) CHECK_FUNCTION_EXISTS("pcap_findalldevs" HAVE_PCAP_FINDALLDEVS) CHECK_FUNCTION_EXISTS("pcap_freecode" HAVE_PCAP_FREECODE) CHECK_FUNCTION_EXISTS("pcap_get_selectable_fd" HAVE_PCAP_GET_SELECTABLE_FD) CHECK_FUNCTION_EXISTS("pcap_lib_version" HAVE_PCAP_LIB_VERSION) CHECK_FUNCTION_EXISTS("pcap_list_datalinks" HAVE_PCAP_LIST_DATALINKS) CHECK_FUNCTION_EXISTS("pcap_open_dead" HAVE_PCAP_OPEN_DEAD) CHECK_FUNCTION_EXISTS("pcap_set_datalink" HAVE_PCAP_SET_DATALINK) endif () #Is pcap found ? IF(PCAP_INCLUDE_DIRS AND PCAP_LIBRARIES) SET( PCAP_FOUND "YES" ) ENDIF(PCAP_INCLUDE_DIRS AND PCAP_LIBRARIES) MARK_AS_ADVANCED( PCAP_LIBRARIES PCAP_INCLUDE_DIRS ) dynamips-0.2.14/cmake/FindUUID.cmake000066400000000000000000000010371241034141600170500ustar00rootroot00000000000000# Note: copied from https://github.com/stevedekorte/io/blob/master/modules/FindUUID.cmake # Base Io build system # Written by Jeremy Tregunna # # Find libuuid FIND_PATH(UUID_INCLUDE_DIR uuid/uuid.h) FIND_LIBRARY(UUID_LIBRARY NAMES uuid PATH) IF(NOT UUID_LIBRARY) SET(UUID_LIBRARY "") ENDIF(NOT UUID_LIBRARY) IF(UUID_INCLUDE_DIR) SET(UUID_FOUND TRUE) ENDIF(UUID_INCLUDE_DIR) IF(NOT UUID_FOUND) IF(UUID_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Could not find UUID") ENDIF(UUID_FIND_REQUIRED) ENDIF(NOT UUID_FOUND) dynamips-0.2.14/cmake/cmake_uninstall.cmake.in000066400000000000000000000021521241034141600212560ustar00rootroot00000000000000# copied from http://www.cmake.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") exec_program( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif(NOT "${rm_retval}" STREQUAL 0) else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") endforeach(file) dynamips-0.2.14/cmake/configure.cmake000066400000000000000000000172551241034141600174730ustar00rootroot00000000000000# dynamips - configure options # - DYNAMIPS_ARCH # - DYNAMIPS_CODE # - BUILD_NVRAM_EXPORT # - BUILD_UDP_SEND (default OFF) # - BUILD_UDP_RECV (default OFF) # - ENABLE_LARGEFILE # - ENABLE_LINUX_ETH # - ENABLE_GEN_ETH # - ENABLE_IPV6 # accumulators: # - DYNAMIPS_FLAGS # - DYNAMIPS_DEFINITIONS # - DYNAMIPS_INCLUDES # - DYNAMIPS_LIBRARIES # XXX assumes utils.cmake and dependencies.cmake were included message ( STATUS "configure - BEGIN" ) # DYNAMIPS_VERSION set ( DYNAMIPS_VERSION "\"${DYNAMIPS_VERSION_TRAIN}${DYNAMIPS_VERSION_SUB}\"" ) list ( APPEND DYNAMIPS_DEFINITIONS "-DDYNAMIPS_VERSION=${DYNAMIPS_VERSION}" ) message ( STATUS "DYNAMIPS_VERSION=${DYNAMIPS_VERSION}" ) # Target architecture: (set in dependencies file) # - Use "x86" to build for x86 (32-bit) # - Use "amd64" to build for x86_64 (64-bit) # - Use "nojit" to build for other architectures (no recompilation) set ( JIT_ARCH "\"${DYNAMIPS_ARCH}\"" ) set ( JIT_CPU "CPU_${DYNAMIPS_ARCH}" ) set ( MIPS64_ARCH_INC_FILE "\"mips64_${DYNAMIPS_ARCH}_trans.h\"" ) set ( PPC32_ARCH_INC_FILE "\"ppc32_${DYNAMIPS_ARCH}_trans.h\"" ) list ( APPEND DYNAMIPS_DEFINITIONS "-DJIT_ARCH=${JIT_ARCH}" "-DJIT_CPU=${JIT_CPU}" "-DMIPS64_ARCH_INC_FILE=${MIPS64_ARCH_INC_FILE}" "-DPPC32_ARCH_INC_FILE=${PPC32_ARCH_INC_FILE}" ) if ( APPLE AND "amd64" STREQUAL "${DYNAMIPS_ARCH}" ) list ( APPEND DYNAMIPS_DEFINITIONS "-DMAC64HACK" ) endif() print_variables ( DYNAMIPS_ARCH ) # Target code: # - "stable" # - "unstable" # - "both" (stable + unstable) # - "none" if ( APPLE ) set ( _default "unstable" ) else () set ( _default "stable" ) endif () set ( DYNAMIPS_CODE "${_default}" CACHE STRING "Target code (stable;unstable;both;none)" ) set_property ( CACHE DYNAMIPS_ARCH PROPERTY STRINGS "stable" "unstable" "both" "none" ) if ( NOT DYNAMIPS_CODE ) set ( DYNAMIPS_CODE "${_default}" ) endif () if ( "stable" STREQUAL DYNAMIPS_CODE ) set ( BUILD_DYNAMIPS_STABLE ON ) set ( BUILD_DYNAMIPS_UNSTABLE OFF ) elseif ( "unstable" STREQUAL DYNAMIPS_CODE ) set ( BUILD_DYNAMIPS_STABLE OFF ) set ( BUILD_DYNAMIPS_UNSTABLE ON ) elseif ( "both" STREQUAL DYNAMIPS_CODE ) set ( BUILD_DYNAMIPS_STABLE ON ) set ( BUILD_DYNAMIPS_UNSTABLE ON ) elseif ( "none" STREQUAL DYNAMIPS_CODE ) set ( BUILD_DYNAMIPS_STABLE OFF ) set ( BUILD_DYNAMIPS_UNSTABLE OFF ) else () message ( FATAL_ERROR "unknown target code DYNAMIPS_CODE=${DYNAMIPS_CODE} (stable;unstable;both;none)" ) endif () print_variables ( DYNAMIPS_CODE BUILD_DYNAMIPS_STABLE BUILD_DYNAMIPS_UNSTABLE ) # Rename target (auto;stable;unstable;) # XXX should auto or not renaming be the default? set ( DYNAMIPS_RENAME "auto" CACHE STRING "which executable is renamed to dynamips (auto;stable;unstable;)" ) set_property ( CACHE DYNAMIPS_RENAME PROPERTY STRINGS "auto" "stable" "unstable" ) set ( DYNAMIPS_RENAME_TARGET ) if ( "auto" STREQUAL DYNAMIPS_RENAME ) foreach ( _target "${DYNAMIPS_CODE}" "stable" "unstable" ) string ( TOUPPER "BUILD_DYNAMIPS_${_target}" _var ) if ( ${_var} ) set ( DYNAMIPS_RENAME_TARGET "dynamips_${DYNAMIPS_ARCH}_${_target}" ) break () endif () endforeach () elseif ( "stable" STREQUAL DYNAMIPS_RENAME OR "unstable" STREQUAL DYNAMIPS_RENAME ) set ( DYNAMIPS_RENAME_TARGET "dynamips_${DYNAMIPS_ARCH}_${DYNAMIPS_RENAME}" ) elseif ( DYNAMIPS_RENAME ) message ( FATAL_ERROR "unknown rename target DYNAMIPS_RENAME=${DYNAMIPS_RENAME} (auto;stable;unstable;)" ) endif () print_variables ( DYNAMIPS_RENAME DYNAMIPS_RENAME_TARGET ) # other executables option ( BUILD_NVRAM_EXPORT "build the nvram_export executable" ON ) option ( BUILD_UDP_SEND "build the udp_send executable" OFF ) option ( BUILD_UDP_RECV "build the udp_recv executable" OFF ) print_variables ( BUILD_NVRAM_EXPORT BUILD_UDP_SEND BUILD_UDP_RECV ) # ENABLE_LARGEFILE if ( LIBELF_LARGEFILE ) option ( ENABLE_LARGEFILE "compile with large file support" ON ) endif () if ( ENABLE_LARGEFILE ) list ( APPEND DYNAMIPS_DEFINITIONS "-D_FILE_OFFSET_BITS=64" "-D_LARGEFILE_SOURCE" "-D_LARGEFILE64_SOURCE" ) endif ( ENABLE_LARGEFILE ) # ENABLE_LINUX_ETH if ( "Linux" STREQUAL "${CMAKE_SYSTEM_NAME}" ) option ( ENABLE_LINUX_ETH "Linux ethernet support with RAW sockets (linux_eth)" ON ) print_variables ( ENABLE_LINUX_ETH ) endif () if ( ENABLE_LINUX_ETH ) list ( APPEND DYNAMIPS_DEFINITIONS "-DLINUX_ETH" ) endif () # ENABLE_GEN_ETH if ( HAVE_PCAP ) option ( ENABLE_GEN_ETH "Generic Ethernet support with libpcap/winpcap (gen_eth)" ON ) print_variables ( ENABLE_GEN_ETH ) endif () if ( ENABLE_GEN_ETH ) list ( APPEND DYNAMIPS_DEFINITIONS "-DGEN_ETH" ) list ( APPEND DYNAMIPS_LIBRARIES ${PCAP_LIBRARIES} ) endif () # ENABLE_IPV6 if ( HAVE_IPV6 ) option ( ENABLE_IPV6 "IPv6 support (RFC 2553)" ON ) print_variables ( ENABLE_IPV6 ) endif () if ( ENABLE_IPV6 ) list ( APPEND DYNAMIPS_DEFINITIONS "-DHAS_RFC2553=1" ) else () list ( APPEND DYNAMIPS_DEFINITIONS "-DHAS_RFC2553=0" ) endif () # target system if ( "SunOS" STREQUAL "${CMAKE_SYSTEM_NAME}" ) list ( APPEND DYNAMIPS_DEFINITIONS "-DSUNOS" "-DINADDR_NONE=0xFFFFFFFF" ) endif () if ( CYGWIN ) list ( APPEND DYNAMIPS_DEFINITIONS "-DCYGWIN" ) endif () if ( "CYGWIN" STREQUAL "${CMAKE_SYSTEM_NAME}" ) # XXX maybe keep it as CYGWIN? set ( OSNAME "Windows" ) elseif ( CMAKE_SYSTEM_NAME ) set ( OSNAME "${CMAKE_SYSTEM_NAME}" ) else () print_variables ( CMAKE_SYSTEM CMAKE_SYSTEM_NAME CMAKE_SYSTEM_PROCESSOR CMAKE_SYSTEM_VERSION ) message ( WARNING "missing system name" ) set ( OSNAME "unknown" ) endif () list ( APPEND DYNAMIPS_DEFINITIONS "-DOSNAME=${OSNAME}" ) # final setup include ( GNUInstallDirs ) set ( CMAKE_C_FLAGS ) foreach ( _flag ${DYNAMIPS_FLAGS} ) set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_flag}" ) endforeach () string ( STRIP "${CMAKE_C_FLAGS}" CMAKE_C_FLAGS ) add_definitions ( ${DYNAMIPS_DEFINITIONS} ) include_directories ( ${DYNAMIPS_INCLUDES} ) print_variables ( DYNAMIPS_FLAGS DYNAMIPS_DEFINITIONS DYNAMIPS_INCLUDES DYNAMIPS_LIBRARIES ) # summary macro ( print_summary ) message ( "Summary:" ) message ( " CMAKE_INSTALL_PREFIX : ${CMAKE_INSTALL_PREFIX}" ) message ( " DYNAMIPS_ARCH : ${DYNAMIPS_ARCH}" ) message ( " DYNAMIPS_CODE : ${DYNAMIPS_CODE}" ) if ( DYNAMIPS_RENAME_TARGET ) set ( _rename "${DYNAMIPS_RENAME_TARGET} -> dynamips" ) else () set ( _rename "don't rename" ) endif () message ( " DYNAMIPS_RENAME : ${_rename} (${DYNAMIPS_RENAME})" ) message ( " BUILD_NVRAM_EXPORT : ${BUILD_NVRAM_EXPORT}" ) message ( " BUILD_UDP_SEND : ${BUILD_UDP_SEND}" ) message ( " BUILD_UDP_RECV : ${BUILD_UDP_RECV}" ) if ( DEFINED ENABLE_LARGEFILE ) set ( _largefile "ENABLE_LARGEFILE=${ENABLE_LARGEFILE}" ) else () set ( _largefile "libelf is incompatible" ) endif () message ( " Large File support : ${_largefile}" ) if ( DEFINED ENABLE_LINUX_ETH ) set ( _linux_eth "ENABLE_LINUX_ETH=${ENABLE_LINUX_ETH}" ) else () set ( _linux_eth "no, not Linux" ) endif () message ( " Linux Ethernet (RAW sockets) : ${_linux_eth} (linux_eth)" ) if ( DEFINED ENABLE_GEN_ETH ) set ( _gen_eth "ENABLE_GEN_ETH=${ENABLE_GEN_ETH}" ) else () set ( _gen_eth "libpcap/winpcap not found" ) endif () message ( " Generic Ethernet (libpcap/WinPcap) : ${_gen_eth} (gen_eth)" ) if ( DEFINED ENABLE_IPV6 ) set ( _ipv6 "ENABLE_IPV6=${ENABLE_IPV6}" ) else () set ( _ipv6 "no, missing headers or functions" ) endif () message ( " IPv6 support (RFC 2553) : ${_ipv6}" ) endmacro ( print_summary ) message ( STATUS "configure - END" ) dynamips-0.2.14/cmake/dependencies.cmake000066400000000000000000000233601241034141600201320ustar00rootroot00000000000000# dynamips - check dependencies # - libdl : maybe required # - librt : maybe required # - libsocket : maybe required # - libelf : required # - libuuid : required # - pthreads : required # - libpcap/winpcap : optional # accumulators: # - DYNAMIPS_FLAGS # - DYNAMIPS_DEFINITIONS # - DYNAMIPS_INCLUDES # - DYNAMIPS_LIBRARIES # XXX assumes utils.cmake was included message ( STATUS "dependencies - BEGIN" ) # gnu compiler if ( NOT CMAKE_COMPILER_IS_GNUCC AND NOT ANY_COMPILER ) print_variables ( CMAKE_COMPILER_IS_GNUCC CMAKE_C_COMPILER CMAKE_C_COMPILER_ABI CMAKE_C_COMPILER_ID CMAKE_C_COMPILER_VERSION ) message ( FATAL_ERROR "Not a GNU C compiler. " "The source and build system assumes gcc so it might not compile. " "Invoke cmake with -DANY_COMPILER=1 to skip this check. " ) endif ( NOT CMAKE_COMPILER_IS_GNUCC AND NOT ANY_COMPILER ) set ( DYNAMIPS_FLAGS -Wall -O2 -fomit-frame-pointer ) set ( DYNAMIPS_DEFINITIONS ) set ( DYNAMIPS_INCLUDES ) set ( DYNAMIPS_LIBRARIES ${CMAKE_DL_LIBS} ) if ( CYGWIN ) list ( APPEND DYNAMIPS_FLAGS -static -static-libgcc ) endif ( CYGWIN ) macro ( set_cmake_required ) set ( CMAKE_REQUIRED_FLAGS ${DYNAMIPS_FLAGS} ) set ( CMAKE_REQUIRED_DEFINITIONS ${DYNAMIPS_DEFINITIONS} ) set ( CMAKE_REQUIRED_INCLUDES ${DYNAMIPS_INCLUDES} ) set ( CMAKE_REQUIRED_LIBRARIES ${DYNAMIPS_LIBRARIES} ) endmacro ( set_cmake_required ) # Target architecture: # - Use "amd64" to build for x86_64 (64-bit) # - Use "x86" to build for x86 (32-bit) # - Use "nojit" to build for other architectures (no recompilation) # Based on https://github.com/petroules/solar-cmake/blob/master/TargetArch.cmake set ( _code " #if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64) int main (void) { return 0; } #else #error cmake_FAIL #endif " ) set_cmake_required () list ( INSERT CMAKE_REQUIRED_FLAGS 0 -m64 ) check_c_source_compiles ( "${_code}" ARCH_AMD64 ) set ( _code " #if defined(__i386) || defined(__i386__) || defined(_M_IX86) int main (void) { return 0; } #else #error cmake_FAIL #endif " ) set_cmake_required () list ( INSERT CMAKE_REQUIRED_FLAGS 0 -m32 ) check_c_source_compiles ( "${_code}" ARCH_X86 ) if ( ARCH_AMD64 ) set ( _default "amd64" ) elseif ( ARCH_X86 ) set ( _default "x86" ) else () set ( _default "nojit" ) endif () set ( DYNAMIPS_ARCH "${_default}" CACHE STRING "Target architecture (amd64;x86;nojit)" ) set_property ( CACHE DYNAMIPS_ARCH PROPERTY STRINGS "amd64" "x86" "nojit" ) if ( NOT DYNAMIPS_ARCH ) set ( DYNAMIPS_ARCH "${_default}" ) endif () if ( "amd64" STREQUAL "${DYNAMIPS_ARCH}" AND ARCH_AMD64 ) list ( INSERT DYNAMIPS_FLAGS 0 -m64 ) elseif ( "x86" STREQUAL "${DYNAMIPS_ARCH}" AND ARCH_X86 ) list ( INSERT DYNAMIPS_FLAGS 0 -m32 ) elseif ( NOT "nojit" STREQUAL "${DYNAMIPS_ARCH}" ) print_variables ( ARCH_AMD64 ARCH_X86 DYNAMIPS_ARCH ) message ( FATAL_ERROR "cannot build target arch DYNAMIPS_ARCH=${DYNAMIPS_ARCH}" ) endif () print_variables ( ARCH_AMD64 ARCH_X86 DYNAMIPS_ARCH ) # Compiler flags foreach ( _flag -mdynamic-no-pic # Mac OS X ) standard_variable_name ( _var "FLAG_${_flag}" ) set_cmake_required () check_c_compiler_flag ( ${_flag} ${_var} ) if ( ${_var} ) list ( APPEND DYNAMIPS_FLAGS ${_flag} ) endif () endforeach () # librt (clock_gettime) set_cmake_required () check_library_exists ( rt clock_gettime "time.h" USE_LIBRT ) if ( USE_LIBRT ) list ( APPEND DYNAMIPS_LIBRARIES rt ) print_variables ( USE_LIBRT ) endif () # libsocket (connect) set_cmake_required () check_library_exists ( socket connect "sys/socket.h" USE_LIBSOCKET ) if ( USE_LIBSOCKET ) list ( APPEND DYNAMIPS_LIBRARIES socket ) print_variables ( USE_LIBSOCKET ) endif () # libnsl (gethostbyname) set_cmake_required () check_library_exists ( nsl gethostbyname "netdb.h" USE_LIBNSL ) if ( USE_LIBNSL ) list ( APPEND DYNAMIPS_LIBRARIES nsl ) print_variables ( USE_LIBNSL ) endif () # libelf set_cmake_required () find_package ( LibElf REQUIRED ) print_variables ( LIBELF_FOUND LIBELF_INCLUDE_DIRS LIBELF_LIBRARIES LIBELF_DEFINITIONS ) # make sure it can be used set_cmake_required () list ( APPEND CMAKE_REQUIRED_DEFINITIONS ${LIBELF_DEFINITIONS} ) list ( APPEND CMAKE_REQUIRED_INCLUDES ${LIBELF_INCLUDE_DIRS} ) check_arch_library ( LIBELF_VALID elf_begin "libelf.h" LIBELF_LIBRARIES elf ) if ( NOT LIBELF_VALID ) bad_arch_library ( FATAL_ERROR "libelf" "LIBELF_INCLUDE_DIRS and LIBELF_LIBRARIES" ) endif () list ( APPEND DYNAMIPS_DEFINITIONS ${LIBELF_DEFINITIONS} ) list ( APPEND DYNAMIPS_INCLUDES ${LIBELF_INCLUDE_DIRS} ) list ( APPEND DYNAMIPS_LIBRARIES ${LIBELF_LIBRARIES} ) # XXX some old libelf's aren't large file aware with ILP32 set ( _code " #define _FILE_OFFSET_BITS 64 #define _LARGEFILE_SOURCE #define _LARGEFILE_SOURCE64 #include int main() { return 0; } " ) set_cmake_required () check_c_source_compiles ( "${_code}" LIBELF_LARGEFILE ) print_variables ( LIBELF_LARGEFILE ) # libuuid set_cmake_required () find_package ( UUID REQUIRED ) print_variables ( UUID_FOUND UUID_INCLUDE_DIR UUID_LIBRARY ) # make sure it can be used set_cmake_required () list ( APPEND CMAKE_REQUIRED_INCLUDES ${UUID_INCLUDE_DIR} ) check_arch_library ( UUID_VALID uuid_generate "uuid/uuid.h" UUID_LIBRARY uuid ) if ( NOT UUID_VALID ) bad_arch_library ( FATAL_ERROR "uuid" "UUID_INCLUDE_DIR and UUID_LIBRARY" ) endif () list ( APPEND DYNAMIPS_INCLUDES ${UUID_INCLUDE_DIR} ) list ( APPEND DYNAMIPS_LIBRARIES ${UUID_LIBRARY} ) # pthreads set ( CMAKE_THREAD_PREFER_PTHREAD 1 ) set_cmake_required () find_package ( Threads REQUIRED ) if ( CMAKE_USE_PTHREADS_INIT ) list ( APPEND DYNAMIPS_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} ) print_variables ( CMAKE_THREAD_LIBS_INIT CMAKE_USE_PTHREADS_INIT ) # ok else () print_variables ( CMAKE_THREAD_LIBS_INIT CMAKE_USE_SPROC_INIT CMAKE_USE_WIN32_THREADS_INIT CMAKE_USE_PTHREADS_INIT CMAKE_HP_PTHREADS_INIT ) message ( FATAL_ERROR "Not using pthreads. " "The source assumes pthreads is available. " ) endif () # libpcap/winpcap (optional) set_cmake_required () find_package ( PCAP ) print_variables ( PCAP_FOUND PCAP_INCLUDE_DIRS PCAP_LIBRARIES ) set ( HAVE_PCAP ${PCAP_FOUND} ) if ( HAVE_PCAP ) # make sure it can be used set_cmake_required () list ( APPEND CMAKE_REQUIRED_INCLUDES ${PCAP_INCLUDE_DIRS} ) check_arch_library ( PCAP_VALID pcap_open_live "pcap.h" PCAP_LIBRARIES pcap wpcap ) if ( NOT PCAP_VALID ) bad_arch_library ( WARNING "pcap/wpcap" "PCAP_INCLUDE_DIRS and PCAP_LIBRARIES" ) endif () set ( HAVE_PCAP ${PCAP_VALID} ) endif () if ( HAVE_PCAP AND CYGWIN ) # cygwin requires pcap_open set_cmake_required () list ( APPEND CMAKE_REQUIRED_DEFINITIONS "-DHAVE_REMOTE" ) list ( APPEND CMAKE_REQUIRED_INCLUDES ${PCAP_INCLUDE_DIRS} ) list ( APPEND CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARIES} ) check_function_exists ( pcap_open HAVE_PCAP_OPEN ) set ( HAVE_PCAP ${HAVE_PCAP_OPEN} ) endif () print_variables ( HAVE_PCAP ) # headers # TODO minimize headers in the source set ( _missing ) foreach ( _header #standalone "arpa/inet.h" "arpa/telnet.h" #dev_vtty.c "assert.h" "ctype.h" "dlfcn.h" #plugin.c "errno.h" "fcntl.h" "getopt.h" #dynamips.c "glob.h" #"libelf.h" #find_package #"linux/if.h" #linux_eth.c (LINUX_ETH) #"linux/if_packet.h" #linux_eth.c (LINUX_ETH) "netdb.h" #"netinet/if_ether.h" #linux_eth.c (LINUX_ETH) #"netinet/tcp.h" #dev_vtty.c #"pcap.h" #find_package (GEN_ETH) #"pthread.h" #find_package "setjmp.h" "signal.h" "stdarg.h" "stddef.h" "stdio.h" "stdlib.h" "string.h" "sys/ioctl.h" "sys/mman.h" #utils.c "sys/select.h" "sys/socket.h" "sys/stat.h" "sys/time.h" "sys/types.h" "sys/uio.h" #atm_vsar.c "sys/un.h" "sys/wait.h" "termios.h" "time.h" "unistd.h" #"uuid/uuid.h" #find_package ) standard_variable_name ( _var "HAVE_${_header}" ) set_cmake_required () check_include_file ( "${_header}" ${_var} ) if ( NOT "${${_var}}" ) print_variables ( ${_var} ) list ( APPEND _missing ${_header} ) endif () endforeach () if ( HAVE_SYS_TYPES_H ) foreach ( _header #requires sys/types.h "netinet/tcp.h" ) standard_variable_name ( _var "HAVE_${_header}" ) set_cmake_required () check_include_files ( "sys/types.h;${_header}" ${_var} ) if ( NOT "${${_var}}" ) print_variables ( ${_var} ) list ( APPEND _missing ${_header} ) endif () endforeach () endif ( HAVE_SYS_TYPES_H ) if ( _missing ) message ( FATAL_ERROR "missing headers: ${_missing}" ) endif ( _missing ) # posix_memalign check_function_exists ( posix_memalign HAVE_POSIX_MEMALIGN ) if ( HAVE_POSIX_MEMALIGN ) list ( APPEND DYNAMIPS_DEFINITIONS "-DHAS_POSIX_MEMALIGN=1" ) else () list ( APPEND DYNAMIPS_DEFINITIONS "-DHAS_POSIX_MEMALIGN=0" ) endif () print_variables ( HAVE_POSIX_MEMALIGN ) # IPv6 (RFC 2553) set ( _headers "sys/socket.h" "arpa/inet.h" "net/if.h" "netdb.h" "netinet/in.h" ) check_include_files ( "${_headers}" IPV6_HEADERS ) check_function_exists ( getaddrinfo HAVE_GETADDRINFO ) check_function_exists ( freeaddrinfo HAVE_FREEADDRINFO ) check_function_exists ( gai_strerror HAVE_GAI_STRERROR ) check_function_exists ( inet_pton HAVE_INET_PTON ) check_function_exists ( inet_ntop HAVE_INET_NTOP ) # TODO AF_INET6, PF_INET6, AI_PASSIVE, IPPROTO_IPV6, IPV6_JOIN_GROUP, # IPV6_MULTICAST_HOPS, INET6_ADDRSTRLEN, struct sockaddr_storage, # struct sockaddr_in6, struct ipv6_mreq if ( IPV6_HEADERS AND HAVE_GETADDRINFO AND HAVE_FREEADDRINFO AND HAVE_GAI_STRERROR AND HAVE_INET_PTON AND HAVE_INET_NTOP ) set ( HAVE_IPV6 1 ) else () set ( HAVE_IPV6 0 ) endif () print_variables ( HAVE_IPV6 ) message ( STATUS "dependencies - END" ) dynamips-0.2.14/cmake/utils.cmake000066400000000000000000000062141241034141600166430ustar00rootroot00000000000000# dynamips - utility functions # standard checks include ( CheckCCompilerFlag ) include ( CheckCSourceCompiles ) include ( CheckCSourceRuns ) include ( CheckCXXCompilerFlag ) include ( CheckCXXSourceCompiles ) include ( CheckCXXSourceRuns ) include ( CheckFunctionExists ) include ( CheckIncludeFile ) include ( CheckIncludeFileCXX ) include ( CheckIncludeFiles ) include ( CheckLibraryExists ) include ( CheckPrototypeDefinition ) include ( CheckStructHasMember ) include ( CheckSymbolExists ) include ( CheckTypeSize ) include ( CheckVariableExists ) # print variables function ( print_variables ) foreach ( _var ${ARGV} ) message ( STATUS "${_var}=${${_var}}" ) endforeach () endfunction ( print_variables ) # convert the name to a standard variable name macro ( standard_variable_name _var _name ) string ( REGEX REPLACE "[^a-zA-Z0-9]" "_" ${_var} "${_name}" ) string ( TOUPPER "${${_var}}" ${_var} ) endmacro () # check if we can compile with the library for the target architecture macro ( check_arch_library _var _func _header _libvar ) set ( _n 0 ) set ( ${_var} ) foreach ( _lib "${${_libvar}}" ${ARGN} ) check_library_exists ( "${_lib}" ${_func} ${_header} ${_var}_${_n} ) if ( ${_var}_${_n} ) # success set ( ${_var} 1 ) if ( NOT "${_lib}" STREQUAL "${${_libvar}}" ) set ( ${_libvar} ${_lib} ) print_variables ( ${_libvar} ) endif () break () endif () math ( EXPR _n "${_n}+1" ) endforeach () endmacro () # could not compile with the library for the target architecture macro ( bad_arch_library _type _lib _vars ) message ( ${_type} "${_lib} was found but cannot be used with DYNAMIPS_ARCH=${DYNAMIPS_ARCH}. " "Make sure the library for the target architecture is installed. " "If needed, you can set the variables ${_vars} manually. " ) endmacro () # rename target DYNAMIPS_RENAME_TARGET to dynamips macro ( maybe_rename_to_dynamips _target ) if ( "${_target}" STREQUAL "${DYNAMIPS_RENAME_TARGET}" ) set_target_properties ( ${_target} PROPERTIES OUTPUT_NAME "dynamips" ) endif() endmacro ( maybe_rename_to_dynamips _target ) # install executables function ( install_executable _target ) install ( TARGETS ${_target} RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT "executables" ) endfunction ( install_executable ) # install docs function ( install_docs ) if ( ${ARGC} LESS 1 ) # no docs return () endif () install ( FILES ${ARGV} DESTINATION "${CMAKE_INSTALL_DOCDIR}" COMPONENT "docs" ) endfunction ( install_docs ) # install man pages function ( install_man_pages ) if ( ${ARGC} LESS 1 ) # no man pages return () endif () foreach ( _file ${ARGV} ) string ( REGEX REPLACE "^.*\\." "" _page "${_file}" ) if ( NOT "${_page}" MATCHES "[0-9]+" ) message ( FATAL_ERROR "not a man page: ${_file}" ) endif () install ( FILES "${_file}" DESTINATION "${CMAKE_INSTALL_MANDIR}/man${_page}" COMPONENT "docs" ) endforeach () endfunction ( install_man_pages ) dynamips-0.2.14/common/000077500000000000000000000000001241034141600147065ustar00rootroot00000000000000dynamips-0.2.14/common/CMakeLists.txt000066400000000000000000000000341241034141600174430ustar00rootroot00000000000000# common # nothing for now dynamips-0.2.14/common/atm.c000066400000000000000000000426411241034141600156420ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * ATM utility functions and Virtual ATM switch. * * HEC and AAL5 CRC computation functions are from Charles Michael Heard * and can be found at (no licence specified, this is to check!): * * http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/ */ #include #include #include #include #include #include #include #include #include #include "utils.h" #include "registry.h" #include "atm.h" #include "net_io.h" /* RFC1483 bridged mode header */ m_uint8_t atm_rfc1483b_header[ATM_RFC1483B_HLEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x80, 0xc2, 0x00, 0x07, 0x00, 0x00, }; /********************************************************************/ #define HEC_GENERATOR 0x107 /* x^8 + x^2 + x + 1 */ #define COSET_LEADER 0x055 /* x^6 + x^4 + x^2 + 1 */ m_uint8_t hec_syndrome_table[256]; /* Generate a table of CRC-8 syndromes for all possible input bytes */ static void gen_syndrome_table(void) { int i,j,syndrome; for(i=0;i<256;i++) { syndrome = i; for(j=0;j<8;j++) { if (syndrome & 0x80) syndrome = (syndrome << 1) ^ HEC_GENERATOR; else syndrome = (syndrome << 1); } hec_syndrome_table[i] = (unsigned char)syndrome; } } /* Compute HEC field for ATM header */ m_uint8_t atm_compute_hec(m_uint8_t *cell_header) { register m_uint8_t hec_accum = 0; register int i; /* * calculate CRC-8 remainder over first four bytes of cell header. * exclusive-or with coset leader & insert into fifth header byte. */ for(i=0;i<4;i++) hec_accum = hec_syndrome_table[hec_accum ^ cell_header[i]]; return(hec_accum ^ COSET_LEADER); } /* Insert HEC field into an ATM header */ void atm_insert_hec(m_uint8_t *cell_header) { cell_header[4] = atm_compute_hec(cell_header); } /* Initialize ATM code (for HEC checksums) */ void atm_init(void) { gen_syndrome_table(); } /* VPC hash function */ static inline u_int atmsw_vpc_hash(u_int vpi) { return((vpi ^ (vpi >> 8)) & (ATMSW_VP_HASH_SIZE-1)); } /* VCC hash function */ static inline u_int atmsw_vcc_hash(u_int vpi,u_int vci) { return((vpi ^ vci) & (ATMSW_VC_HASH_SIZE-1)); } /* VP lookup */ atmsw_vp_conn_t *atmsw_vp_lookup(atmsw_table_t *t,netio_desc_t *input, u_int vpi) { atmsw_vp_conn_t *swc; for(swc=t->vp_table[atmsw_vpc_hash(vpi)];swc;swc=swc->next) if ((swc->input == input) && (swc->vpi_in == vpi)) return swc; return NULL; } /* VC lookup */ atmsw_vc_conn_t *atmsw_vc_lookup(atmsw_table_t *t,netio_desc_t *input, u_int vpi,u_int vci) { atmsw_vc_conn_t *swc; for(swc=t->vc_table[atmsw_vcc_hash(vpi,vci)];swc;swc=swc->next) if ((swc->input == input) && (swc->vpi_in == vpi) && (swc->vci_in == vci)) return swc; return NULL; } /* VP switching */ void atmsw_vp_switch(atmsw_vp_conn_t *vpc,m_uint8_t *cell) { m_uint32_t atm_hdr; /* rewrite the atm header with new vpi */ atm_hdr = m_ntoh32(cell); atm_hdr &= ~ATM_HDR_VPI_MASK; atm_hdr |= vpc->vpi_out << ATM_HDR_VPI_SHIFT; m_hton32(cell,atm_hdr); /* recompute HEC field */ atm_insert_hec(cell); /* update the statistics counter */ vpc->cell_cnt++; } /* VC switching */ void atmsw_vc_switch(atmsw_vc_conn_t *vcc,m_uint8_t *cell) { m_uint32_t atm_hdr; /* rewrite the atm header with new vpi/vci */ atm_hdr = m_ntoh32(cell); atm_hdr &= ~(ATM_HDR_VPI_MASK|ATM_HDR_VCI_MASK); atm_hdr |= vcc->vpi_out << ATM_HDR_VPI_SHIFT; atm_hdr |= vcc->vci_out << ATM_HDR_VCI_SHIFT; m_hton32(cell,atm_hdr); /* recompute HEC field */ atm_insert_hec(cell); /* update the statistics counter */ vcc->cell_cnt++; } /* Handle an ATM cell */ ssize_t atmsw_handle_cell(atmsw_table_t *t,netio_desc_t *input, m_uint8_t *cell) { m_uint32_t atm_hdr,vpi,vci; netio_desc_t *output = NULL; atmsw_vp_conn_t *vpc; atmsw_vc_conn_t *vcc; ssize_t len; /* Extract VPI/VCI information */ atm_hdr = m_ntoh32(cell); vpi = (atm_hdr & ATM_HDR_VPI_MASK) >> ATM_HDR_VPI_SHIFT; vci = (atm_hdr & ATM_HDR_VCI_MASK) >> ATM_HDR_VCI_SHIFT; /* VP switching */ if ((vpc = atmsw_vp_lookup(t,input,vpi)) != NULL) { atmsw_vp_switch(vpc,cell); output = vpc->output; } else { /* VC switching */ if ((vcc = atmsw_vc_lookup(t,input,vpi,vci)) != NULL) { atmsw_vc_switch(vcc,cell); output = vcc->output; } } len = netio_send(output,cell,ATM_CELL_SIZE); if (len != ATM_CELL_SIZE) { t->cell_drop++; return(-1); } return(0); } /* Receive an ATM cell */ static int atmsw_recv_cell(netio_desc_t *nio,u_char *atm_cell,ssize_t cell_len, atmsw_table_t *t) { int res; if (cell_len != ATM_CELL_SIZE) return(-1); ATMSW_LOCK(t); res = atmsw_handle_cell(t,nio,atm_cell); ATMSW_UNLOCK(t); return(res); } /* Acquire a reference to an ATM switch (increment reference count) */ atmsw_table_t *atmsw_acquire(char *name) { return(registry_find(name,OBJ_TYPE_ATMSW)); } /* Release an ATM switch (decrement reference count) */ int atmsw_release(char *name) { return(registry_unref(name,OBJ_TYPE_ATMSW)); } /* Create a virtual switch table */ atmsw_table_t *atmsw_create_table(char *name) { atmsw_table_t *t; /* Allocate a new switch structure */ if (!(t = malloc(sizeof(*t)))) return NULL; memset(t,0,sizeof(*t)); pthread_mutex_init(&t->lock,NULL); mp_create_fixed_pool(&t->mp,"ATM Switch"); if (!(t->name = mp_strdup(&t->mp,name))) goto err_name; /* Record this object in registry */ if (registry_add(t->name,OBJ_TYPE_ATMSW,t) == -1) { fprintf(stderr,"atmsw_create_table: unable to create switch '%s'\n", name); goto err_reg; } return t; err_reg: err_name: mp_free_pool(&t->mp); free(t); return NULL; } /* Free resources used by a VPC */ static void atmsw_release_vpc(atmsw_vp_conn_t *swc) { if (swc) { /* release input NIO */ if (swc->input) { netio_rxl_remove(swc->input); netio_release(swc->input->name); } /* release output NIO */ if (swc->output) netio_release(swc->output->name); } } /* Free resources used by a VCC */ static void atmsw_release_vcc(atmsw_vc_conn_t *swc) { if (swc) { /* release input NIO */ if (swc->input) { netio_rxl_remove(swc->input); netio_release(swc->input->name); } /* release output NIO */ if (swc->output) netio_release(swc->output->name); } } /* Create a VP switch connection */ int atmsw_create_vpc(atmsw_table_t *t,char *nio_input,u_int vpi_in, char *nio_output,u_int vpi_out) { atmsw_vp_conn_t *swc; u_int hbucket; ATMSW_LOCK(t); /* Allocate a new switch connection */ if (!(swc = mp_alloc(&t->mp,sizeof(*swc)))) { ATMSW_UNLOCK(t); return(-1); } swc->input = netio_acquire(nio_input); swc->output = netio_acquire(nio_output); swc->vpi_in = vpi_in; swc->vpi_out = vpi_out; /* Check these NIOs are valid and the input VPI does not exists */ if (!swc->input || !swc->output || atmsw_vp_lookup(t,swc->input,vpi_in)) goto error; /* Add as a RX listener */ if (netio_rxl_add(swc->input,(netio_rx_handler_t)atmsw_recv_cell, t,NULL) == -1) goto error; hbucket = atmsw_vpc_hash(vpi_in); swc->next = t->vp_table[hbucket]; t->vp_table[hbucket] = swc; ATMSW_UNLOCK(t); return(0); error: ATMSW_UNLOCK(t); atmsw_release_vpc(swc); mp_free(swc); return(-1); } /* Delete a VP switch connection */ int atmsw_delete_vpc(atmsw_table_t *t,char *nio_input,u_int vpi_in, char *nio_output,u_int vpi_out) { netio_desc_t *input,*output; atmsw_vp_conn_t **swc,*p; u_int hbucket; ATMSW_LOCK(t); input = registry_exists(nio_input,OBJ_TYPE_NIO); output = registry_exists(nio_output,OBJ_TYPE_NIO); if (!input || !output) { ATMSW_UNLOCK(t); return(-1); } hbucket = atmsw_vpc_hash(vpi_in); for(swc=&t->vp_table[hbucket];*swc;swc=&(*swc)->next) { p = *swc; if ((p->input == input) && (p->output == output) && (p->vpi_in == vpi_in) && (p->vpi_out == vpi_out)) { /* found a matching VP, remove it */ *swc = (*swc)->next; ATMSW_UNLOCK(t); atmsw_release_vpc(p); mp_free(p); return(0); } } ATMSW_UNLOCK(t); return(-1); } /* Create a VC switch connection */ int atmsw_create_vcc(atmsw_table_t *t, char *input,u_int vpi_in,u_int vci_in, char *output,u_int vpi_out,u_int vci_out) { atmsw_vc_conn_t *swc; u_int hbucket; ATMSW_LOCK(t); /* Allocate a new switch connection */ if (!(swc = mp_alloc(&t->mp,sizeof(*swc)))) { ATMSW_UNLOCK(t); return(-1); } swc->input = netio_acquire(input); swc->output = netio_acquire(output); swc->vpi_in = vpi_in; swc->vci_in = vci_in; swc->vpi_out = vpi_out; swc->vci_out = vci_out; /* Ensure that there is not already VP switching */ if (atmsw_vp_lookup(t,swc->input,vpi_in) != NULL) { fprintf(stderr,"atmsw_create_vcc: VP switching already exists for " "VPI=%u\n",vpi_in); goto error; } /* Check these NIOs are valid and the input VPI does not exists */ if (!swc->input || !swc->output || atmsw_vc_lookup(t,swc->input,vpi_in,vci_in)) goto error; /* Add as a RX listener */ if (netio_rxl_add(swc->input,(netio_rx_handler_t)atmsw_recv_cell, t,NULL) == -1) goto error; hbucket = atmsw_vcc_hash(vpi_in,vci_in); swc->next = t->vc_table[hbucket]; t->vc_table[hbucket] = swc; ATMSW_UNLOCK(t); return(0); error: ATMSW_UNLOCK(t); atmsw_release_vcc(swc); mp_free(swc); return(-1); } /* Delete a VC switch connection */ int atmsw_delete_vcc(atmsw_table_t *t, char *nio_input,u_int vpi_in,u_int vci_in, char *nio_output,u_int vpi_out,u_int vci_out) { netio_desc_t *input,*output; atmsw_vc_conn_t **swc,*p; u_int hbucket; ATMSW_LOCK(t); input = registry_exists(nio_input,OBJ_TYPE_NIO); output = registry_exists(nio_output,OBJ_TYPE_NIO); hbucket = atmsw_vcc_hash(vpi_in,vci_in); for(swc=&t->vc_table[hbucket];*swc;swc=&(*swc)->next) { p = *swc; if ((p->input == input) && (p->output == output) && (p->vpi_in == vpi_in) && (p->vci_in == vci_in) && (p->vpi_out == vpi_out) && (p->vci_out == vci_out)) { /* found a matching VP, remove it */ *swc = (*swc)->next; ATMSW_UNLOCK(t); atmsw_release_vcc(p); mp_free(p); return(0); } } ATMSW_UNLOCK(t); return(-1); } /* Free resources used by an ATM switch */ static int atmsw_free(void *data,void *arg) { atmsw_table_t *t = data; atmsw_vp_conn_t *vp; atmsw_vc_conn_t *vc; int i; /* Remove all VPs */ for(i=0;ivp_table[i];vp;vp=vp->next) atmsw_release_vpc(vp); /* Remove all VCs */ for(i=0;ivc_table[i];vc;vc=vc->next) atmsw_release_vcc(vc); mp_free_pool(&t->mp); free(t); return(TRUE); } /* Delete an ATM switch */ int atmsw_delete(char *name) { return(registry_delete_if_unused(name,OBJ_TYPE_ATMSW,atmsw_free,NULL)); } /* Delete all ATM switches */ int atmsw_delete_all(void) { return(registry_delete_type(OBJ_TYPE_ATMSW,atmsw_free,NULL)); } /* Save the configuration of an ATM switch */ void atmsw_save_config(atmsw_table_t *t,FILE *fd) { atmsw_vp_conn_t *vp; atmsw_vc_conn_t *vc; int i; fprintf(fd,"atmsw create %s\n",t->name); ATMSW_LOCK(t); for(i=0;ivp_table[i];vp;vp=vp->next) { fprintf(fd,"atmsw create_vpc %s %s %u %s %u\n", t->name,vp->input->name,vp->vpi_in, vp->output->name,vp->vpi_out); } } for(i=0;ivc_table[i];vc;vc=vc->next) { fprintf(fd,"atmsw create_vcc %s %s %u %u %s %u %u\n", t->name,vc->input->name,vc->vpi_in,vc->vci_in, vc->output->name,vc->vpi_out,vc->vci_out); } } ATMSW_UNLOCK(t); fprintf(fd,"\n"); } /* Save configurations of all ATM switches */ static void atmsw_reg_save_config(registry_entry_t *entry,void *opt,int *err) { atmsw_save_config((atmsw_table_t *)entry->data,(FILE *)opt); } void atmsw_save_config_all(FILE *fd) { registry_foreach_type(OBJ_TYPE_ATMSW,atmsw_reg_save_config,fd,NULL); } /* Create a new interface */ int atmsw_cfg_create_if(atmsw_table_t *t,char **tokens,int count) { netio_desc_t *nio = NULL; int nio_type; /* at least: IF, interface name, NetIO type */ if (count < 3) { fprintf(stderr,"atmsw_cfg_create_if: invalid interface description\n"); return(-1); } nio_type = netio_get_type(tokens[2]); switch(nio_type) { case NETIO_TYPE_UNIX: if (count != 5) { fprintf(stderr,"ATMSW: invalid number of arguments " "for UNIX NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_unix(tokens[1],tokens[3],tokens[4]); break; case NETIO_TYPE_UDP: if (count != 6) { fprintf(stderr,"ATMSW: invalid number of arguments " "for UDP NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_udp(tokens[1],atoi(tokens[3]), tokens[4],atoi(tokens[5])); break; case NETIO_TYPE_MCAST: if (count != 5) { fprintf(stderr,"ATMSW: invalid number of arguments " "for Multicast NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_mcast(tokens[1],tokens[3],atoi(tokens[4])); break; case NETIO_TYPE_TCP_CLI: if (count != 5) { fprintf(stderr,"ATMSW: invalid number of arguments " "for TCP CLI NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_tcp_cli(tokens[1],tokens[3],tokens[4]); break; case NETIO_TYPE_TCP_SER: if (count != 4) { fprintf(stderr,"ATMSW: invalid number of arguments " "for TCP SER NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_tcp_ser(tokens[1],tokens[3]); break; default: fprintf(stderr,"ATMSW: unknown/invalid NETIO type '%s'\n", tokens[2]); } if (!nio) { fprintf(stderr,"ATMSW: unable to create NETIO descriptor of " "interface %s\n",tokens[1]); return(-1); } netio_release(nio->name); return(0); } /* Create a new Virtual Path Connection */ int atmsw_cfg_create_vpc(atmsw_table_t *t,char **tokens,int count) { /* 5 parameters: "VP", InputIF, InVPI, OutputIF, OutVPI */ if (count != 5) { fprintf(stderr,"ATMSW: invalid VPC descriptor.\n"); return(-1); } return(atmsw_create_vpc(t,tokens[1],atoi(tokens[2]), tokens[3],atoi(tokens[4]))); } /* Create a new Virtual Channel Connection */ int atmsw_cfg_create_vcc(atmsw_table_t *t,char **tokens,int count) { /* 7 parameters: "VP", InputIF, InVPI/VCI, OutputIF, OutVPI/VCI */ if (count != 7) { fprintf(stderr,"ATMSW: invalid VCC descriptor.\n"); return(-1); } return(atmsw_create_vcc(t,tokens[1],atoi(tokens[2]),atoi(tokens[3]), tokens[4],atoi(tokens[5]),atoi(tokens[6]))); } #define ATMSW_MAX_TOKENS 16 /* Handle an ATMSW configuration line */ int atmsw_handle_cfg_line(atmsw_table_t *t,char *str) { char *tokens[ATMSW_MAX_TOKENS]; int count; if ((count = m_strsplit(str,':',tokens,ATMSW_MAX_TOKENS)) <= 1) return(-1); if (!strcmp(tokens[0],"IF")) return(atmsw_cfg_create_if(t,tokens,count)); else if (!strcmp(tokens[0],"VP")) return(atmsw_cfg_create_vpc(t,tokens,count)); else if (!strcmp(tokens[0],"VC")) return(atmsw_cfg_create_vcc(t,tokens,count)); fprintf(stderr,"ATMSW: Unknown statement \"%s\" (allowed: IF,VP,VC)\n", tokens[0]); return(-1); } /* Read an ATMSW configuration file */ int atmsw_read_cfg_file(atmsw_table_t *t,char *filename) { char buffer[1024],*ptr; FILE *fd; if (!(fd = fopen(filename,"r"))) { perror("fopen"); return(-1); } while(!feof(fd)) { if (!fgets(buffer,sizeof(buffer),fd)) break; /* skip comments and end of line */ if ((ptr = strpbrk(buffer,"#\r\n")) != NULL) *ptr = 0; /* analyze non-empty lines */ if (strchr(buffer,':')) atmsw_handle_cfg_line(t,buffer); } fclose(fd); return(0); } /* Start a virtual ATM switch */ int atmsw_start(char *filename) { atmsw_table_t *t; if (!(t = atmsw_create_table("default"))) { fprintf(stderr,"ATMSW: unable to create virtual fabric table.\n"); return(-1); } if (atmsw_read_cfg_file(t,filename) == -1) { fprintf(stderr,"ATMSW: unable to parse configuration file.\n"); return(-1); } atmsw_release("default"); return(0); } dynamips-0.2.14/common/atm.h000066400000000000000000000073151241034141600156460ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * ATM definitions. */ #ifndef __ATM_H__ #define __ATM_H__ #include #include "utils.h" #include "mempool.h" #include "net_io.h" /* ATM payload size */ #define ATM_HDR_SIZE 5 #define ATM_PAYLOAD_SIZE 48 #define ATM_CELL_SIZE (ATM_HDR_SIZE + ATM_PAYLOAD_SIZE) #define ATM_AAL5_TRAILER_SIZE 8 #define ATM_AAL5_TRAILER_POS (ATM_CELL_SIZE - ATM_AAL5_TRAILER_SIZE) /* ATM header structure */ #define ATM_HDR_VPI_MASK 0xFFF00000 #define ATM_HDR_VPI_SHIFT 20 #define ATM_HDR_VCI_MASK 0x000FFFF0 #define ATM_HDR_VCI_SHIFT 4 #define ATM_HDR_PTI_MASK 0x0000000E #define ATM_HDR_PTI_SHIFT 1 /* PTI bits */ #define ATM_PTI_EOP 0x00000002 /* End of packet */ #define ATM_PTI_CONGESTION 0x00000004 /* Congestion detected */ #define ATM_PTI_NETWORK 0x00000008 /* Network traffic */ /* VP-level switch table */ typedef struct atmsw_vp_conn atmsw_vp_conn_t; struct atmsw_vp_conn { atmsw_vp_conn_t *next; netio_desc_t *input,*output; u_int vpi_in,vpi_out; m_uint64_t cell_cnt; }; /* VC-level switch table */ typedef struct atmsw_vc_conn atmsw_vc_conn_t; struct atmsw_vc_conn { atmsw_vc_conn_t *next; netio_desc_t *input,*output; u_int vpi_in,vci_in; u_int vpi_out,vci_out; m_uint64_t cell_cnt; }; /* Virtual ATM switch table */ #define ATMSW_NIO_MAX 32 #define ATMSW_VP_HASH_SIZE 256 #define ATMSW_VC_HASH_SIZE 1024 typedef struct atmsw_table atmsw_table_t; struct atmsw_table { char *name; pthread_mutex_t lock; mempool_t mp; m_uint64_t cell_drop; atmsw_vp_conn_t *vp_table[ATMSW_VP_HASH_SIZE]; atmsw_vc_conn_t *vc_table[ATMSW_VC_HASH_SIZE]; }; #define ATMSW_LOCK(t) pthread_mutex_lock(&(t)->lock) #define ATMSW_UNLOCK(t) pthread_mutex_unlock(&(t)->lock) /* RFC1483 bridged mode header */ #define ATM_RFC1483B_HLEN 10 extern m_uint8_t atm_rfc1483b_header[ATM_RFC1483B_HLEN]; /* Compute HEC field for ATM header */ m_uint8_t atm_compute_hec(m_uint8_t *cell_header); /* Insert HEC field into an ATM header */ void atm_insert_hec(m_uint8_t *cell_header); /* Update the CRC on the data block one byte at a time */ m_uint32_t atm_update_crc(m_uint32_t crc_accum,m_uint8_t *ptr,int len); /* Initialize ATM code (for HEC checksums) */ void atm_init(void); /* Acquire a reference to an ATM switch (increment reference count) */ atmsw_table_t *atmsw_acquire(char *name); /* Release an ATM switch (decrement reference count) */ int atmsw_release(char *name); /* Create a virtual switch table */ atmsw_table_t *atmsw_create_table(char *name); /* Create a VP switch connection */ int atmsw_create_vpc(atmsw_table_t *t,char *nio_input,u_int vpi_in, char *nio_output,u_int vpi_out); /* Delete a VP switch connection */ int atmsw_delete_vpc(atmsw_table_t *t,char *nio_input,u_int vpi_in, char *nio_output,u_int vpi_out); /* Create a VC switch connection */ int atmsw_create_vcc(atmsw_table_t *t, char *input,u_int vpi_in,u_int vci_in, char *output,u_int vpi_out,u_int vci_out); /* Delete a VC switch connection */ int atmsw_delete_vcc(atmsw_table_t *t, char *nio_input,u_int vpi_in,u_int vci_in, char *nio_output,u_int vpi_out,u_int vci_out); /* Save the configuration of an ATM switch */ void atmsw_save_config(atmsw_table_t *t,FILE *fd); /* Save configurations of all ATM switches */ void atmsw_save_config_all(FILE *fd); /* Delete an ATM switch */ int atmsw_delete(char *name); /* Delete all ATM switches */ int atmsw_delete_all(void); /* Start a virtual ATM switch */ int atmsw_start(char *filename); #endif dynamips-0.2.14/common/atm_bridge.c000066400000000000000000000220011241034141600171420ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * ATM bridge (RFC1483) */ #include #include #include #include #include #include #include #include #include #include "utils.h" #include "registry.h" #include "net_io.h" #include "atm.h" #include "atm_vsar.h" #include "atm_bridge.h" #define ATM_BRIDGE_LOCK(t) pthread_mutex_lock(&(t)->lock) #define ATM_BRIDGE_UNLOCK(t) pthread_mutex_unlock(&(t)->lock) /* Acquire a reference to an ATM bridge (increment reference count) */ atm_bridge_t *atm_bridge_acquire(char *name) { return(registry_find(name,OBJ_TYPE_ATM_BRIDGE)); } /* Release an ATM switch (decrement reference count) */ int atm_bridge_release(char *name) { return(registry_unref(name,OBJ_TYPE_ATM_BRIDGE)); } /* Receive an ATM cell */ static int atm_bridge_recv_cell(netio_desc_t *nio, u_char *atm_cell,ssize_t cell_len, atm_bridge_t *t) { m_uint32_t atm_hdr,vpi,vci; int status,res = 0; if (cell_len != ATM_CELL_SIZE) return(-1); ATM_BRIDGE_LOCK(t); /* check the VPI/VCI */ atm_hdr = m_ntoh32(atm_cell); vpi = (atm_hdr & ATM_HDR_VPI_MASK) >> ATM_HDR_VPI_SHIFT; vci = (atm_hdr & ATM_HDR_VCI_MASK) >> ATM_HDR_VCI_SHIFT; if ((t->vpi != vpi) || (t->vci != vci)) goto done; if ((status = atm_aal5_recv(&t->arc,atm_cell)) == 1) { /* Got AAL5 packet, check RFC1483b encapsulation */ if ((t->arc.len > ATM_RFC1483B_HLEN) && !memcmp(t->arc.buffer,atm_rfc1483b_header,ATM_RFC1483B_HLEN)) { netio_send(t->eth_nio, t->arc.buffer+ATM_RFC1483B_HLEN, t->arc.len-ATM_RFC1483B_HLEN); } atm_aal5_recv_reset(&t->arc); } else { if (status < 0) { atm_aal5_recv_reset(&t->arc); res = -1; } } done: ATM_BRIDGE_UNLOCK(t); return(res); } /* Receive an Ethernet packet */ static int atm_bridge_recv_pkt(netio_desc_t *nio,u_char *pkt,ssize_t len, atm_bridge_t *t) { return(atm_aal5_send_rfc1483b(t->atm_nio,t->vpi,t->vci,pkt,len)); } /* Create a virtual ATM bridge */ atm_bridge_t *atm_bridge_create(char *name) { atm_bridge_t *t; /* Allocate a new switch structure */ if (!(t = malloc(sizeof(*t)))) return NULL; memset(t,0,sizeof(*t)); pthread_mutex_init(&t->lock,NULL); atm_aal5_recv_reset(&t->arc); if (!(t->name = strdup(name))) goto err_name; /* Record this object in registry */ if (registry_add(t->name,OBJ_TYPE_ATM_BRIDGE,t) == -1) { fprintf(stderr,"atm_bridge_create: unable to create bridge '%s'\n", name); goto err_reg; } return t; err_reg: free(t->name); err_name: free(t); return NULL; } /* Configure an ATM bridge */ int atm_bridge_configure(atm_bridge_t *t,char *eth_nio, char *atm_nio,u_int vpi,u_int vci) { netio_desc_t *e_nio,*a_nio; ATM_BRIDGE_LOCK(t); if (t->eth_nio || t->atm_nio) goto error; e_nio = netio_acquire(eth_nio); a_nio = netio_acquire(atm_nio); if (!e_nio || !a_nio) goto error; t->eth_nio = e_nio; t->atm_nio = a_nio; t->vpi = vpi; t->vci = vci; /* Add ATM RX listener */ if (netio_rxl_add(t->atm_nio,(netio_rx_handler_t)atm_bridge_recv_cell, t,NULL) == -1) goto error; /* Add Ethernet RX listener */ if (netio_rxl_add(t->eth_nio,(netio_rx_handler_t)atm_bridge_recv_pkt, t,NULL) == -1) goto error; ATM_BRIDGE_UNLOCK(t); return(0); error: ATM_BRIDGE_UNLOCK(t); return(-1); } /* Release NIO used by an ATM bridge */ static void atm_bridge_clear_config(atm_bridge_t *t) { if (t != NULL) { /* release ethernet NIO */ if (t->eth_nio) { netio_rxl_remove(t->eth_nio); netio_release(t->eth_nio->name); } /* release ATM NIO */ if (t->atm_nio) { netio_rxl_remove(t->atm_nio); netio_release(t->atm_nio->name); } t->eth_nio = t->atm_nio = NULL; } } /* Unconfigure an ATM bridge */ int atm_bridge_unconfigure(atm_bridge_t *t) { ATM_BRIDGE_LOCK(t); atm_bridge_clear_config(t); ATM_BRIDGE_UNLOCK(t); return(0); } /* Free resources used by an ATM bridge */ static int atm_bridge_free(void *data,void *arg) { atm_bridge_t *t = data; atm_bridge_clear_config(t); free(t->name); free(t); return(TRUE); } /* Delete an ATM bridge */ int atm_bridge_delete(char *name) { return(registry_delete_if_unused(name,OBJ_TYPE_ATM_BRIDGE, atm_bridge_free,NULL)); } /* Delete all ATM switches */ int atm_bridge_delete_all(void) { return(registry_delete_type(OBJ_TYPE_ATM_BRIDGE,atm_bridge_free,NULL)); } /* Create a new interface */ int atm_bridge_cfg_create_if(atm_bridge_t *t,char **tokens,int count) { netio_desc_t *nio = NULL; int nio_type; /* at least: IF, interface name, NetIO type */ if (count < 3) { fprintf(stderr,"atmsw_cfg_create_if: invalid interface description\n"); return(-1); } nio_type = netio_get_type(tokens[2]); switch(nio_type) { case NETIO_TYPE_UNIX: if (count != 5) { fprintf(stderr,"ATMSW: invalid number of arguments " "for UNIX NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_unix(tokens[1],tokens[3],tokens[4]); break; case NETIO_TYPE_UDP: if (count != 6) { fprintf(stderr,"ATMSW: invalid number of arguments " "for UDP NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_udp(tokens[1],atoi(tokens[3]), tokens[4],atoi(tokens[5])); break; case NETIO_TYPE_MCAST: if (count != 5) { fprintf(stderr,"ATMSW: invalid number of arguments " "for Multicast NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_mcast(tokens[1],tokens[3],atoi(tokens[4])); break; case NETIO_TYPE_TCP_CLI: if (count != 5) { fprintf(stderr,"ATMSW: invalid number of arguments " "for TCP CLI NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_tcp_cli(tokens[1],tokens[3],tokens[4]); break; case NETIO_TYPE_TCP_SER: if (count != 4) { fprintf(stderr,"ATMSW: invalid number of arguments " "for TCP SER NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_tcp_ser(tokens[1],tokens[3]); break; default: fprintf(stderr,"ATMSW: unknown/invalid NETIO type '%s'\n", tokens[2]); } if (!nio) { fprintf(stderr,"ATMSW: unable to create NETIO descriptor of " "interface %s\n",tokens[1]); return(-1); } netio_release(nio->name); return(0); } /* Bridge setup */ int atm_bridge_cfg_setup(atm_bridge_t *t,char **tokens,int count) { /* 5 parameters: "BRIDGE", Eth_IF, ATM_IF, VPI, VCI */ if (count != 5) { fprintf(stderr,"ATM Bridge: invalid VPC descriptor.\n"); return(-1); } return(atm_bridge_configure(t,tokens[1],tokens[2], atoi(tokens[3]),atoi(tokens[4]))); } #define ATM_BRIDGE_MAX_TOKENS 16 /* Handle an ATMSW configuration line */ int atm_bridge_handle_cfg_line(atm_bridge_t *t,char *str) { char *tokens[ATM_BRIDGE_MAX_TOKENS]; int count; if ((count = m_strsplit(str,':',tokens,ATM_BRIDGE_MAX_TOKENS)) <= 1) return(-1); if (!strcmp(tokens[0],"IF")) return(atm_bridge_cfg_create_if(t,tokens,count)); else if (!strcmp(tokens[0],"BRIDGE")) return(atm_bridge_cfg_setup(t,tokens,count)); fprintf(stderr,"ATM Bridge: " "Unknown statement \"%s\" (allowed: IF,BRIDGE)\n", tokens[0]); return(-1); } /* Read an ATM bridge configuration file */ int atm_bridge_read_cfg_file(atm_bridge_t *t,char *filename) { char buffer[1024],*ptr; FILE *fd; if (!(fd = fopen(filename,"r"))) { perror("fopen"); return(-1); } while(!feof(fd)) { if (!fgets(buffer,sizeof(buffer),fd)) break; /* skip comments and end of line */ if ((ptr = strpbrk(buffer,"#\r\n")) != NULL) *ptr = 0; /* analyze non-empty lines */ if (strchr(buffer,':')) atm_bridge_handle_cfg_line(t,buffer); } fclose(fd); return(0); } /* Start a virtual ATM bridge */ int atm_bridge_start(char *filename) { atm_bridge_t *t; if (!(t = atm_bridge_create("default"))) { fprintf(stderr,"ATM Bridge: unable to create virtual fabric table.\n"); return(-1); } if (atm_bridge_read_cfg_file(t,filename) == -1) { fprintf(stderr,"ATM Bridge: unable to parse configuration file.\n"); return(-1); } atm_bridge_release("default"); return(0); } dynamips-0.2.14/common/atm_bridge.h000066400000000000000000000022441241034141600171560ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * ATM bridge (RFC1483) */ #ifndef __ATM_BRIDGE_H__ #define __ATM_BRIDGE_H__ #include #include "utils.h" #include "net_io.h" #include "atm.h" #include "atm_vsar.h" typedef struct atm_bridge atm_bridge_t; struct atm_bridge { char *name; pthread_mutex_t lock; netio_desc_t *eth_nio,*atm_nio; u_int vpi,vci; struct atm_reas_context arc; }; /* Acquire a reference to an ATM bridge (increment reference count) */ atm_bridge_t *atm_bridge_acquire(char *name); /* Release an ATM switch (decrement reference count) */ int atm_bridge_release(char *name); /* Create a virtual ATM bridge */ atm_bridge_t *atm_bridge_create(char *name); /* Configure an ATM bridge */ int atm_bridge_configure(atm_bridge_t *t,char *eth_nio, char *atm_nio,u_int vpi,u_int vci); /* Unconfigure an ATM bridge */ int atm_bridge_unconfigure(atm_bridge_t *t); /* Delete an ATM bridge */ int atm_bridge_delete(char *name); /* Delete all ATM switches */ int atm_bridge_delete_all(void); /* Start a virtual ATM bridge */ int atm_bridge_start(char *filename); #endif dynamips-0.2.14/common/atm_vsar.c000066400000000000000000000123331241034141600166700ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * ATM Virtual Segmentation & Reassembly Engine. */ #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "registry.h" #include "crc.h" #include "atm.h" #include "net_io.h" #include "atm_vsar.h" /* Segmentation Context */ struct atm_seg_context { netio_desc_t *nio; m_uint8_t txfifo_cell[ATM_CELL_SIZE]; size_t txfifo_pos,txfifo_avail; size_t aal5_len; m_uint32_t aal5_crc; m_uint32_t atm_hdr; char *buffer; size_t buf_len; }; /* Send the ATM cell in FIFO */ static void atm_send_cell(struct atm_seg_context *asc) { m_hton32(asc->txfifo_cell,asc->atm_hdr); atm_insert_hec(asc->txfifo_cell); netio_send(asc->nio,asc->txfifo_cell,ATM_CELL_SIZE); } /* Clear the TX fifo */ static void atm_clear_tx_fifo(struct atm_seg_context *asc) { asc->txfifo_avail = ATM_PAYLOAD_SIZE; asc->txfifo_pos = ATM_HDR_SIZE; memset(asc->txfifo_cell,0,ATM_CELL_SIZE); } /* Add padding to the FIFO */ static void atm_add_tx_padding(struct atm_seg_context *asc,size_t len) { if (len > asc->txfifo_avail) len = asc->txfifo_avail; memset(&asc->txfifo_cell[asc->txfifo_pos],0,len); asc->txfifo_pos += len; asc->txfifo_avail -= len; } /* Send the TX fifo if it is empty */ static void atm_send_fifo(struct atm_seg_context *asc) { if (!asc->txfifo_avail) { asc->aal5_crc = crc32_compute(~asc->aal5_crc, &asc->txfifo_cell[ATM_HDR_SIZE], ATM_PAYLOAD_SIZE); atm_send_cell(asc); atm_clear_tx_fifo(asc); } } /* Store a packet in the TX FIFO */ static int atm_store_fifo(struct atm_seg_context *asc) { size_t len; len = m_min(asc->buf_len,asc->txfifo_avail); memcpy(&asc->txfifo_cell[asc->txfifo_pos],asc->buffer,len); asc->buffer += len; asc->buf_len -= len; asc->txfifo_pos += len; asc->txfifo_avail -= len; if (!asc->txfifo_avail) { atm_send_fifo(asc); return(TRUE); } return(FALSE); } /* Add the AAL5 trailer to the TX FIFO */ static void atm_aal5_add_trailer(struct atm_seg_context *asc) { m_uint8_t *trailer; trailer = &asc->txfifo_cell[ATM_AAL5_TRAILER_POS]; /* Control field + Length */ m_hton32(trailer,asc->aal5_len); /* Final CRC-32 computation */ asc->aal5_crc = crc32_compute(~asc->aal5_crc, &asc->txfifo_cell[ATM_HDR_SIZE], ATM_PAYLOAD_SIZE - 4); m_hton32(trailer+4,asc->aal5_crc); /* Consider the FIFO as full */ asc->txfifo_avail = 0; } /* Send an AAL5 packet through an NIO (segmentation) */ int atm_aal5_send(netio_desc_t *nio,u_int vpi,u_int vci, struct iovec *iov,int iovcnt) { struct atm_seg_context asc; int i; asc.nio = nio; asc.aal5_len = 0; asc.aal5_crc = 0; /* will be inverted by first CRC update */ atm_clear_tx_fifo(&asc); /* prepare the atm header */ asc.atm_hdr = vpi << ATM_HDR_VPI_SHIFT; asc.atm_hdr |= vci << ATM_HDR_VCI_SHIFT; for(i=0;i 0) atm_store_fifo(&asc); } /* * Add the PDU trailer. If we have enough room, add it in the last cell, * otherwise create a new one. */ if (asc.txfifo_avail < ATM_AAL5_TRAILER_SIZE) { atm_add_tx_padding(&asc,asc.txfifo_avail); atm_send_fifo(&asc); } /* Set AAL5 end of packet in ATM header (PTI field) */ asc.atm_hdr |= ATM_PTI_EOP; atm_add_tx_padding(&asc,asc.txfifo_avail - ATM_AAL5_TRAILER_SIZE); atm_aal5_add_trailer(&asc); atm_send_cell(&asc); return(0); } /* Reset a receive context */ void atm_aal5_recv_reset(struct atm_reas_context *arc) { arc->buf_pos = 0; arc->len = 0; } /* Receive an ATM cell and process reassembly */ int atm_aal5_recv(struct atm_reas_context *arc,m_uint8_t *cell) { m_uint32_t atm_hdr; /* Check buffer boundary */ if ((arc->buf_pos + ATM_PAYLOAD_SIZE) > ATM_REAS_MAX_SIZE) { atm_aal5_recv_reset(arc); return(-1); } /* Get the PTI field: we cannot handle "network" traffic */ atm_hdr = m_ntoh32(cell); if (atm_hdr & ATM_PTI_NETWORK) return(2); /* Copy the payload */ memcpy(&arc->buffer[arc->buf_pos],&cell[ATM_HDR_SIZE],ATM_PAYLOAD_SIZE); arc->buf_pos += ATM_PAYLOAD_SIZE; /* * If this is the last cell of the packet, get the real length (the * trailer is at the end). */ if (atm_hdr & ATM_PTI_EOP) { arc->len = m_ntoh16(&cell[ATM_AAL5_TRAILER_POS+2]); return((arc->len <= arc->buf_pos) ? 1 : -2); } return(0); } /* Send a packet through a rfc1483 bridge encap */ int atm_aal5_send_rfc1483b(netio_desc_t *nio,u_int vpi,u_int vci, void *pkt,size_t len) { struct iovec vec[2]; vec[0].iov_base = (void *)atm_rfc1483b_header; vec[0].iov_len = ATM_RFC1483B_HLEN; vec[1].iov_base = pkt; vec[1].iov_len = len; return(atm_aal5_send(nio,vpi,vci,vec,2)); } dynamips-0.2.14/common/atm_vsar.h000066400000000000000000000017011241034141600166720ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * ATM Virtual Segmentation & Reassembly Engine. */ #ifndef __ATM_VSAR_H__ #define __ATM_VSAR_H__ #include #include "utils.h" #include "net_io.h" #define ATM_REAS_MAX_SIZE 16384 /* Reassembly Context */ struct atm_reas_context { m_uint8_t buffer[ATM_REAS_MAX_SIZE]; size_t buf_pos; size_t len; }; /* Send an AAL5 packet through an NIO (segmentation) */ int atm_aal5_send(netio_desc_t *nio,u_int vpi,u_int vci, struct iovec *iov,int iovcnt); /* Reset a receive context */ void atm_aal5_recv_reset(struct atm_reas_context *arc); /* Receive an ATM cell and process reassembly */ int atm_aal5_recv(struct atm_reas_context *arc,m_uint8_t *cell); /* Send a packet through a rfc1483 bridge encap */ int atm_aal5_send_rfc1483b(netio_desc_t *nio,u_int vpi,u_int vci, void *pkt,size_t len); #endif dynamips-0.2.14/common/base64.c000066400000000000000000000063571241034141600161510ustar00rootroot00000000000000/* * base64.c -- base-64 conversion routines. * Copyright (C)2002 by Eric S. Raymond. * * For license terms, see the file COPYING in this directory. * * This base 64 encoding is defined in RFC2045 section 6.8, * "Base64 Content-Transfer-Encoding", but lines must not be broken in the * scheme used here. */ #include static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; #define BAD -1 static const char base64val[] = { BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD, 62, BAD,BAD,BAD, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,BAD,BAD, BAD,BAD,BAD,BAD, BAD, 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,BAD, BAD,BAD,BAD,BAD, BAD, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,BAD, BAD,BAD,BAD,BAD }; #define DECODE64(c) (isascii(c) ? base64val[c] : BAD) /* Encode into base64 */ void base64_encode(unsigned char *out,const unsigned char *in,int inlen) { /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */ for (; inlen >= 3; inlen -= 3) { *out++ = base64digits[in[0] >> 2]; *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)]; *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; *out++ = base64digits[in[2] & 0x3f]; in += 3; } if (inlen > 0) { unsigned char fragment; *out++ = base64digits[in[0] >> 2]; fragment = (in[0] << 4) & 0x30; if (inlen > 1) fragment |= in[1] >> 4; *out++ = base64digits[fragment]; *out++ = (inlen < 2) ? '=' : base64digits[(in[1] << 2) & 0x3c]; *out++ = '='; } *out = '\0'; } /* * Decode from base64. * * base 64 to raw bytes in quasi-big-endian order, returning count of bytes * maxlen limits output buffer size, set to zero to ignore. */ int base64_decode(unsigned char *out,const unsigned char *in,int maxlen) { register unsigned char digit1, digit2, digit3, digit4; int len = 0; if (in[0] == '+' && in[1] == ' ') in += 2; if (*in == '\r') return(0); do { digit1 = in[0]; if (DECODE64(digit1) == BAD) return(-1); digit2 = in[1]; if (DECODE64(digit2) == BAD) return(-1); digit3 = in[2]; if (digit3 != '=' && DECODE64(digit3) == BAD) return(-1); digit4 = in[3]; if (digit4 != '=' && DECODE64(digit4) == BAD) return(-1); in += 4; ++len; if (maxlen && len > maxlen) return(-1); *out++ = (DECODE64(digit1) << 2) | (DECODE64(digit2) >> 4); if (digit3 != '=') { ++len; if (maxlen && (len > maxlen)) return(-1); *out++ = ((DECODE64(digit2) << 4) & 0xf0) | (DECODE64(digit3) >> 2); if (digit4 != '=') { ++len; if (maxlen && (len > maxlen)) return(-1); *out++ = ((DECODE64(digit3) << 6) & 0xc0) | DECODE64(digit4); } } } while(*in && (*in != '\r') && (digit4 != '=')); return(len); } dynamips-0.2.14/common/base64.h000066400000000000000000000010161241034141600161410ustar00rootroot00000000000000/* * base64.c -- base-64 conversion routines. * * For license terms, see the file COPYING in this directory. * * This base 64 encoding is defined in RFC2045 section 6.8, * "Base64 Content-Transfer-Encoding", but lines must not be broken in the * scheme used here. */ #ifndef __BASE64_H__ #define __BASE64_H__ /* Encode into base64 */ void base64_encode(unsigned char *out,const unsigned char *in,int inlen); /* Decode from base64 */ int base64_decode(unsigned char *out,const unsigned char *in,int maxlen); #endif dynamips-0.2.14/common/bin2c.c000066400000000000000000000015371241034141600160550ustar00rootroot00000000000000#include #include #include int main(int argc,char *argv[]) { unsigned char buffer[8]; FILE *fd_in,*fd_out; size_t len; int i; if (argc != 3) { fprintf(stderr,"Usage: %s \n",argv[0]); exit(EXIT_FAILURE); } if (!(fd_in = fopen(argv[1],"r"))) { fprintf(stderr,"Unable to open file \"%s\"\n",argv[1]); exit(EXIT_FAILURE); } if (!(fd_out = fopen(argv[2],"w"))) { fprintf(stderr,"Unable to create file \"%s\"\n",argv[2]); exit(EXIT_FAILURE); } while(!feof(fd_in)) { len = fread(buffer,1,sizeof(buffer),fd_in); if (len == 0) break; fprintf(fd_out," "); for(i=0;i #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "cisco_card.h" /* Get cisco card type description */ char *cisco_card_get_type_desc(int dev_type) { switch(dev_type) { case CISCO_CARD_TYPE_PA: return("Port Adapter (PA)"); case CISCO_CARD_TYPE_NM: return("Network Module (NM)"); case CISCO_CARD_TYPE_WIC: return("WAN Interface Card (WIC)"); default: return("Unknown"); } } /* Set EEPROM definition for the specified Cisco card */ int cisco_card_set_eeprom(vm_instance_t *vm,struct cisco_card *card, const struct cisco_eeprom *eeprom) { if (!eeprom) return(0); if (cisco_eeprom_copy(&card->eeprom,eeprom) == -1) { vm_error(vm,"cisco_card_set_eeprom: no memory (eeprom=%p).\n",eeprom); return(-1); } return(0); } /* Unset EEPROM definition */ int cisco_card_unset_eeprom(struct cisco_card *card) { cisco_eeprom_free(&card->eeprom); return(0); } /* Check if a card has a valid EEPROM defined */ int cisco_card_check_eeprom(struct cisco_card *card) { return(cisco_eeprom_valid(&card->eeprom)); } /* Create a card structure */ static inline struct cisco_card *cisco_card_create(u_int card_type) { struct cisco_card *card; if ((card = malloc(sizeof(*card))) != NULL) { memset(card,0,sizeof(*card)); card->card_type = card_type; } return card; } /* Find a NIO binding */ static struct cisco_nio_binding * cisco_card_find_nio_binding(struct cisco_card *card,u_int port_id) { struct cisco_nio_binding *nb; if (!card) return NULL; for(nb=card->nio_list;nb;nb=nb->next) if (nb->port_id == port_id) return nb; return NULL; } /* Remove all NIO bindings */ static void cisco_card_remove_all_nio_bindings(vm_instance_t *vm,struct cisco_card *card) { struct cisco_nio_binding *nb,*next; for(nb=card->nio_list;nb;nb=next) { next = nb->next; /* tell the slot driver to stop using this NIO */ if (card->driver) card->driver->card_unset_nio(vm,card,nb->port_id); /* unreference NIO object */ netio_release(nb->nio->name); free(nb); } card->nio_list = NULL; } /* Enable all NIO for the specified card */ static inline void cisco_card_enable_all_nio(vm_instance_t *vm,struct cisco_card *card) { struct cisco_nio_binding *nb; if (card && card->driver && card->drv_info) for(nb=card->nio_list;nb;nb=nb->next) card->driver->card_set_nio(vm,card,nb->port_id,nb->nio); } /* Disable all NIO for the specified card */ static inline void cisco_card_disable_all_nio(vm_instance_t *vm,struct cisco_card *card) { struct cisco_nio_binding *nb; if (card && card->driver && card->drv_info) for(nb=card->nio_list;nb;nb=nb->next) card->driver->card_unset_nio(vm,card,nb->port_id); } /* Initialize a card */ static inline int cisco_card_init(vm_instance_t *vm,struct cisco_card *card,u_int id) { size_t len; /* Check that a device type is defined for this card */ if (!card || !card->dev_type || !card->driver) return(-1); /* Allocate device name */ len = strlen(card->dev_type) + 10; if (!(card->dev_name = malloc(len))) { vm_error(vm,"unable to allocate device name.\n"); return(-1); } snprintf(card->dev_name,len,"%s(%u)",card->dev_type,id); /* Initialize card driver */ if (card->driver->card_init(vm,card) == -1) { vm_error(vm,"unable to initialize card type '%s' (id %u)\n", card->dev_type,id); return(-1); } return(0); } /* Shutdown card */ static int cisco_card_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Check that a device type is defined for this card */ if (!card || !card->dev_type || !card->driver) return(-1); /* Shutdown the NM driver */ if (card->drv_info && (card->driver->card_shutdown(vm,card) == -1)) { vm_error(vm,"unable to shutdown card type '%s' (slot %u/%u)\n", card->dev_type,card->slot_id,card->subslot_id); return(-1); } free(card->dev_name); card->dev_name = NULL; card->drv_info = NULL; return(0); } /* Show info for the specified card */ static int cisco_card_show_info(vm_instance_t *vm,struct cisco_card *card) { /* Check that a device type is defined for this card */ if (!card || !card->driver || !card->driver->card_show_info) return(-1); card->driver->card_show_info(vm,card); return(0); } /* Save config for the specified card */ static int cisco_card_save_config(vm_instance_t *vm,struct cisco_card *card, FILE *fd) { struct cisco_nio_binding *nb; if (card != NULL) { fprintf(fd,"vm add_slot_binding %s %u %u %s\n", vm->name,card->slot_id,card->subslot_id,card->dev_type); for(nb=card->nio_list;nb;nb=nb->next) { fprintf(fd,"vm add_nio_binding %s %u %u %s\n", vm->name,card->slot_id,nb->orig_port_id,nb->nio->name); } } return(0); } /* Find a driver in a driver array */ static struct cisco_card_driver * cisco_card_find_driver(struct cisco_card_driver **array,char *dev_type) { int i; for(i=0;array[i]!=NULL;i++) if (!strcmp(array[i]->dev_type,dev_type)) return array[i]; return NULL; } /* ======================================================================== */ /* High level routines for managing VM slots. */ /* ======================================================================== */ /* Get slot info */ struct cisco_card *vm_slot_get_card_ptr(vm_instance_t *vm,u_int slot_id) { if (slot_id >= vm->nr_slots) return NULL; return(vm->slots[slot_id]); } /* Get info for a slot/port (with sub-cards) */ static int vm_slot_get_info(vm_instance_t *vm,u_int slot_id,u_int port_id, struct cisco_card ***rc,u_int *real_port_id) { struct cisco_card *card; u_int wic_id,card_type; if (slot_id >= VM_MAX_SLOTS) { *rc = NULL; return(-1); } *rc = &vm->slots[slot_id]; card = vm->slots[slot_id]; card_type = (card != NULL) ? card->card_type : CISCO_CARD_TYPE_UNDEF; switch(card_type) { /* * Handle WICs which are sub-slots for Network Modules (NM). * Numbering: wic #0 => port_id = 0x10 * wic #1 => port_id = 0x20 */ case CISCO_CARD_TYPE_NM: if (card->driver->wic_slots > 0) { wic_id = port_id >> 4; if (wic_id >= (CISCO_CARD_MAX_WIC+1)) { vm_error(vm,"Invalid wic_id %u (slot %u)\n",wic_id,slot_id); return(-1); } if (wic_id >= 0x01) { /* wic card */ *rc = &card->sub_slots[wic_id - 1]; *real_port_id = port_id & 0x0F; } else { /* main card */ *real_port_id = port_id; } } else { *real_port_id = port_id; } return(0); /* No translation for Cisco 7200 Port Adapters and WICs */ case CISCO_CARD_TYPE_PA: case CISCO_CARD_TYPE_WIC: *real_port_id = port_id; return(0); /* Not initialized yet */ default: *real_port_id = port_id; return(0); } } /* Translate a port ID (for sub-cards) */ static u_int vm_slot_translate_port_id(vm_instance_t *vm,u_int slot_id,u_int port_id, struct cisco_card **rc) { struct cisco_card **tmp; u_int real_port_id = 0; vm_slot_get_info(vm,slot_id,port_id,&tmp,&real_port_id); *rc = *tmp; return(real_port_id); } /* Check if a slot has an active card */ int vm_slot_active(vm_instance_t *vm,u_int slot_id,u_int port_id) { struct cisco_card **rc; u_int real_port_id; if (vm_slot_get_info(vm,slot_id,port_id,&rc,&real_port_id) == -1) return(FALSE); if ((*rc == NULL) || ((*rc)->dev_type == NULL)) return(FALSE); return(TRUE); } /* Set a flag for a card */ int vm_slot_set_flag(vm_instance_t *vm,u_int slot_id,u_int port_id,u_int flag) { struct cisco_card **rc; u_int real_port_id; if (vm_slot_get_info(vm,slot_id,port_id,&rc,&real_port_id) == -1) return(FALSE); if (*rc == NULL) return(FALSE); (*rc)->card_flags |= flag; return(TRUE); } /* Add a slot binding */ int vm_slot_add_binding(vm_instance_t *vm,char *dev_type, u_int slot_id,u_int port_id) { struct cisco_card_driver *driver,**drv_array; struct cisco_card **rc,*card,*nc,*parent; u_int real_port_id,card_type,card_id; if (vm_slot_get_info(vm,slot_id,port_id,&rc,&real_port_id) == -1) return(-1); /* check that this bay is empty */ if (*rc != NULL) { if ((*rc)->card_flags & CISCO_CARD_FLAG_OVERRIDE) { vm_slot_remove_binding(vm,slot_id,port_id); } else { vm_error(vm,"a card already exists in slot %u/%u (%s)\n", slot_id,port_id,(*rc)->dev_type); return(-1); } } card = vm->slots[slot_id]; if (!card || (card == *rc)) { /* Main slot */ drv_array = vm->slots_drivers; card_type = vm->slots_type; card_id = slot_id; parent = NULL; } else { /* Subslot */ if (!card->driver->card_get_sub_info) { vm_error(vm,"no sub-slot possible for slot %u/%u.\n",slot_id,port_id); return(-1); } if (card->driver->card_get_sub_info(vm,card,port_id, &drv_array,&card_type) == -1) { vm_error(vm,"no sub-slot info for slot %u/%u.\n",slot_id,port_id); return(-1); } card_id = port_id; parent = card; } assert(drv_array != NULL); /* Find the card driver */ if (!(driver = cisco_card_find_driver(drv_array,dev_type))) { vm_error(vm,"unknown card type '%s' for slot %u/%u.\n", dev_type,slot_id,port_id); return(-1); } /* Allocate new card info */ if (!(nc = cisco_card_create(card_type))) return(-1); nc->slot_id = slot_id; nc->subslot_id = port_id; nc->card_id = card_id; nc->dev_type = driver->dev_type; nc->driver = driver; nc->parent = parent; *rc = nc; return(0); } /* Remove a slot binding */ int vm_slot_remove_binding(vm_instance_t *vm,u_int slot_id,u_int port_id) { struct cisco_card **rc,*sc; u_int i,real_port_id; if (vm_slot_get_info(vm,slot_id,port_id,&rc,&real_port_id) == -1) return(-1); if (*rc == NULL) return(-1); if ((*rc)->drv_info != NULL) { vm_error(vm,"slot %u/%u is still active\n",slot_id,port_id); return(-1); } for(i=0;isub_slots[i]) != NULL) { vm_error(vm,"sub-slot %u/%u is still active\n", slot_id,sc->subslot_id); return(-1); } } /* Remove all NIOs bindings */ vm_slot_remove_all_nio_bindings(vm,slot_id); /* Free the card info structure */ free(*rc); *rc = NULL; return(0); } /* Add a network IO binding */ int vm_slot_add_nio_binding(vm_instance_t *vm,u_int slot_id,u_int port_id, char *nio_name) { struct cisco_nio_binding *nb; struct cisco_card *card,*rc; u_int real_port_id; netio_desc_t *nio; if (!(card = vm_slot_get_card_ptr(vm,slot_id))) return(-1); /* Get the real card (in case this is a sub-slot) */ real_port_id = vm_slot_translate_port_id(vm,slot_id,port_id,&rc); if (rc == NULL) return(-1); /* check that a NIO is not already bound to this port */ if (cisco_card_find_nio_binding(rc,real_port_id) != NULL) { vm_error(vm,"a NIO already exists for interface %u/%u.\n", slot_id,port_id); return(-1); } /* acquire a reference on the NIO object */ if (!(nio = netio_acquire(nio_name))) { vm_error(vm,"unable to find NIO '%s'.\n",nio_name); return(-1); } /* create a new binding */ if (!(nb = malloc(sizeof(*nb)))) { vm_error(vm,"unable to create NIO binding for interface %u/%u.\n", slot_id,port_id); netio_release(nio_name); return(-1); } memset(nb,0,sizeof(*nb)); nb->nio = nio; nb->port_id = real_port_id; nb->orig_port_id = port_id; nb->next = rc->nio_list; if (nb->next) nb->next->prev = nb; rc->nio_list = nb; return(0); } /* Remove a NIO binding */ int vm_slot_remove_nio_binding(vm_instance_t *vm,u_int slot_id,u_int port_id) { struct cisco_nio_binding *nb; struct cisco_card *card,*rc; u_int real_port_id; if (!(card = vm_slot_get_card_ptr(vm,slot_id))) return(-1); /* Get the real card (in case this is a sub-slot) */ real_port_id = vm_slot_translate_port_id(vm,slot_id,port_id,&rc); if (rc == NULL) return(-1); /* no nio binding for this slot/port ? */ if (!(nb = cisco_card_find_nio_binding(rc,real_port_id))) return(-1); /* tell the NM driver to stop using this NIO */ if (rc->driver) rc->driver->card_unset_nio(vm,rc,port_id); /* remove this entry from the double linked list */ if (nb->next) nb->next->prev = nb->prev; if (nb->prev) { nb->prev->next = nb->next; } else { rc->nio_list = nb->next; } /* unreference NIO object */ netio_release(nb->nio->name); free(nb); return(0); } /* Remove all NIO bindings for the specified slot (sub-slots included) */ int vm_slot_remove_all_nio_bindings(vm_instance_t *vm,u_int slot_id) { struct cisco_card *card,*sc; int i; if (!(card = vm_slot_get_card_ptr(vm,slot_id))) return(-1); /* Remove NIO bindings for the main slot */ cisco_card_remove_all_nio_bindings(vm,card); /* Remove NIO bindings for all sub-slots */ for(i=0;isub_slots[i]) != NULL) cisco_card_remove_all_nio_bindings(vm,sc); } return(0); } /* Enable a Network IO descriptor for the specified slot */ int vm_slot_enable_nio(vm_instance_t *vm,u_int slot_id,u_int port_id) { struct cisco_nio_binding *nb; struct cisco_card *card,*rc; u_int real_port_id; if (!(card = vm_slot_get_card_ptr(vm,slot_id))) return(-1); /* Get the real card (in case this is a sub-slot) */ real_port_id = vm_slot_translate_port_id(vm,slot_id,port_id,&rc); if (rc == NULL) return(-1); /* no nio binding for this slot/port ? */ if (!(nb = cisco_card_find_nio_binding(rc,real_port_id))) return(-1); /* check that the driver is defined and successfully initialized */ if (!rc->driver || !rc->drv_info) return(-1); return(rc->driver->card_set_nio(vm,rc,real_port_id,nb->nio)); } /* Disable Network IO descriptor for the specified slot */ int vm_slot_disable_nio(vm_instance_t *vm,u_int slot_id,u_int port_id) { struct cisco_nio_binding *nb; struct cisco_card *card,*rc; u_int real_port_id; if (!(card = vm_slot_get_card_ptr(vm,slot_id))) return(-1); /* Get the real card (in case this is a sub-slot) */ real_port_id = vm_slot_translate_port_id(vm,slot_id,port_id,&rc); if (rc == NULL) return(-1); /* no nio binding for this slot/port ? */ if (!(nb = cisco_card_find_nio_binding(rc,real_port_id))) return(-1); /* check that the driver is defined and successfully initialized */ if (!rc->driver || !rc->drv_info) return(-1); return(rc->driver->card_unset_nio(vm,rc,real_port_id)); } /* Enable all NIO for the specified slot (sub-slots included) */ int vm_slot_enable_all_nio(vm_instance_t *vm,u_int slot_id) { struct cisco_card *card; int i; if (!(card = vm_slot_get_card_ptr(vm,slot_id))) return(-1); /* Enable slot NIOs */ cisco_card_enable_all_nio(vm,card); /* Enable NIO of sub-slots */ for(i=0;isub_slots[i]); return(0); } /* Disable all NIO for the specified slot (sub-slots included) */ int vm_slot_disable_all_nio(vm_instance_t *vm,u_int slot_id) { struct cisco_card *card; int i; if (!(card = vm_slot_get_card_ptr(vm,slot_id))) return(-1); /* Disable slot NIOs */ cisco_card_disable_all_nio(vm,card); /* Disable NIO of sub-slots */ for(i=0;isub_slots[i]); return(0); } /* Initialize the specified slot (sub-slots included) */ int vm_slot_init(vm_instance_t *vm,u_int slot_id) { struct cisco_card *card; int i; if (!(card = vm_slot_get_card_ptr(vm,slot_id))) return(0); /* Initialize card main module */ cisco_card_init(vm,card,slot_id); /* Initialize sub-slots */ for(i=0;isub_slots[i],slot_id); /* Enable all NIO */ vm_slot_enable_all_nio(vm,slot_id); return(0); } /* Initialize all slots of a VM */ int vm_slot_init_all(vm_instance_t *vm) { int i; for(i=0;inr_slots;i++) { if (vm_slot_init(vm,i) == -1) { vm_error(vm,"unable to initialize slot %u\n",i); return(-1); } } return(0); } /* Shutdown the specified slot (sub-slots included) */ int vm_slot_shutdown(vm_instance_t *vm,u_int slot_id) { struct cisco_card *card; int i; if (!(card = vm_slot_get_card_ptr(vm,slot_id))) return(-1); /* Disable all NIO */ vm_slot_disable_all_nio(vm,slot_id); /* Shutdown sub-slots */ for(i=0;isub_slots[i]); /* Shutdown card main module */ cisco_card_shutdown(vm,card); return(0); } /* Shutdown all slots of a VM */ int vm_slot_shutdown_all(vm_instance_t *vm) { int i; for(i=0;inr_slots;i++) vm_slot_shutdown(vm,i); return(0); } /* Show info about the specified slot (sub-slots included) */ int vm_slot_show_info(vm_instance_t *vm,u_int slot_id) { struct cisco_card *card; if (!(card = vm_slot_get_card_ptr(vm,slot_id))) return(-1); cisco_card_show_info(vm,card); return(0); } /* Show info about all slots */ int vm_slot_show_all_info(vm_instance_t *vm) { int i; for(i=0;inr_slots;i++) vm_slot_show_info(vm,i); return(0); } /* Check if the specified slot has a valid EEPROM defined */ int vm_slot_check_eeprom(vm_instance_t *vm,u_int slot_id,u_int port_id) { struct cisco_card *card,*rc; if (!(card = vm_slot_get_card_ptr(vm,slot_id))) return(FALSE); /* Get the real card (in case this is a sub-slot) */ vm_slot_translate_port_id(vm,slot_id,port_id,&rc); if (rc == NULL) return(FALSE); return(cisco_card_check_eeprom(rc)); } /* Returns the EEPROM data of the specified slot */ struct cisco_eeprom * vm_slot_get_eeprom(vm_instance_t *vm,u_int slot_id,u_int port_id) { struct cisco_card *card,*rc; if (!(card = vm_slot_get_card_ptr(vm,slot_id))) return NULL; /* Get the real card (in case this is a sub-slot) */ vm_slot_translate_port_id(vm,slot_id,port_id,&rc); if (rc == NULL) return NULL; return(&rc->eeprom); } /* Save config for the specified slot (sub-slots included) */ int vm_slot_save_config(vm_instance_t *vm,u_int slot_id,FILE *fd) { struct cisco_card *card; int i; if (!(card = vm_slot_get_card_ptr(vm,slot_id))) return(-1); /* Main slot info */ cisco_card_save_config(vm,card,fd); /* Shutdown sub-slots */ for(i=0;isub_slots[i],fd); return(0); } /* Save config for all slots */ int vm_slot_save_all_config(vm_instance_t *vm,FILE *fd) { int i; for(i=0;inr_slots;i++) vm_slot_save_config(vm,i,fd); return(0); } /* Show slot drivers */ int vm_slot_show_drivers(vm_instance_t *vm) { char *slot_type; int i; if (!vm->slots_drivers) return(-1); slot_type = cisco_card_get_type_desc(vm->slots_type); printf("Available %s %s drivers:\n",vm->platform->log_name,slot_type); for(i=0;vm->slots_drivers[i];i++) { printf(" * %s %s\n", vm->slots_drivers[i]->dev_type, !vm->slots_drivers[i]->supported ? "(NOT WORKING)" : ""); } printf("\n"); return(0); } /* Maximum number of tokens in a slot description */ #define SLOT_DESC_MAX_TOKENS 8 /* Create a Network Module (command line) */ int vm_slot_cmd_create(vm_instance_t *vm,char *str) { char *tokens[SLOT_DESC_MAX_TOKENS]; int i,count,res; u_int slot_id,port_id; /* A port adapter description is like "1:0:NM-1FE" */ count = m_strsplit(str,':',tokens,SLOT_DESC_MAX_TOKENS); if ((count < 2) || (count > 3)) { vm_error(vm,"unable to parse slot description '%s'.\n",str); return(-1); } /* Parse the slot id */ slot_id = atoi(tokens[0]); /* Parse the sub-slot id */ if (count == 3) port_id = atoi(tokens[1]); else port_id = 0; /* Add this new slot to the current slot list */ res = vm_slot_add_binding(vm,tokens[count-1],slot_id,port_id); /* The complete array was cleaned by strsplit */ for(i=0;iinstance_id,slot_id,port_id); /* Create the Network IO descriptor */ nio = NULL; nio_type = netio_get_type(tokens[2]); switch(nio_type) { case NETIO_TYPE_UNIX: if (count != 5) { vm_error(vm,"invalid number of arguments for UNIX NIO '%s'\n",str); goto done; } nio = netio_desc_create_unix(nio_name,tokens[3],tokens[4]); break; case NETIO_TYPE_VDE: if (count != 5) { vm_error(vm,"invalid number of arguments for VDE NIO '%s'\n",str); goto done; } nio = netio_desc_create_vde(nio_name,tokens[3],tokens[4]); break; case NETIO_TYPE_TAP: if (count != 4) { vm_error(vm,"invalid number of arguments for TAP NIO '%s'\n",str); goto done; } nio = netio_desc_create_tap(nio_name,tokens[3]); break; case NETIO_TYPE_UDP: if (count != 6) { vm_error(vm,"invalid number of arguments for UDP NIO '%s'\n",str); goto done; } nio = netio_desc_create_udp(nio_name,atoi(tokens[3]), tokens[4],atoi(tokens[5])); break; case NETIO_TYPE_MCAST: if (count != 5) { vm_error(vm,"invalid number of arguments for Multicast NIO '%s'\n", str); goto done; } nio = netio_desc_create_mcast(nio_name,tokens[3],atoi(tokens[4])); break; case NETIO_TYPE_TCP_CLI: if (count != 5) { vm_error(vm,"invalid number of arguments for TCP CLI NIO '%s'\n", str); goto done; } nio = netio_desc_create_tcp_cli(nio_name,tokens[3],tokens[4]); break; case NETIO_TYPE_TCP_SER: if (count != 4) { vm_error(vm,"invalid number of arguments for TCP SER NIO '%s'\n", str); goto done; } nio = netio_desc_create_tcp_ser(nio_name,tokens[3]); break; case NETIO_TYPE_NULL: nio = netio_desc_create_null(nio_name); break; #ifdef LINUX_ETH case NETIO_TYPE_LINUX_ETH: if (count != 4) { vm_error(vm,"invalid number of arguments for Linux Eth NIO '%s'\n", str); goto done; } nio = netio_desc_create_lnxeth(nio_name,tokens[3]); break; #endif #ifdef GEN_ETH case NETIO_TYPE_GEN_ETH: if (count != 4) { vm_error(vm, "invalid number of arguments for Generic Eth NIO '%s'\n", str); goto done; } nio = netio_desc_create_geneth(nio_name,tokens[3]); break; #endif default: vm_error(vm,"unknown NETIO type '%s'\n",tokens[2]); goto done; } if (!nio) { vm_error(vm,"unable to create NETIO descriptor for slot %u\n",slot_id); goto done; } if (vm_slot_add_nio_binding(vm,slot_id,port_id,nio_name) == -1) { vm_error(vm,"unable to add NETIO binding for slot %u\n",slot_id); netio_release(nio_name); netio_delete(nio_name); goto done; } netio_release(nio_name); res = 0; done: /* The complete array was cleaned by strsplit */ for(i=0;i #include "utils.h" #include "net.h" #include "net_io.h" #include "cisco_eeprom.h" #define CISCO_CARD_MAX_WIC 8 #define CISCO_CARD_MAX_SUBSLOTS 16 /* Card types */ enum { CISCO_CARD_TYPE_UNDEF = 0, CISCO_CARD_TYPE_PA, CISCO_CARD_TYPE_NM, CISCO_CARD_TYPE_WIC, }; /* Card flags */ enum { CISCO_CARD_FLAG_OVERRIDE = 1, }; /* Forward declarations */ struct cisco_card; struct cisco_card_driver; /* Prototype of card driver initialization function */ typedef int (*cisco_card_init_fn)(vm_instance_t *vm,struct cisco_card *card); /* Prototype of card driver shutdown function */ typedef int (*cisco_card_shutdown_fn)(vm_instance_t *vm, struct cisco_card *card); /* Prototype of card NIO get sub-slot info function */ typedef int (*cisco_card_get_sub_info_fn)(vm_instance_t *vm,struct cisco_card *card, u_int port_id, struct cisco_card_driver ***drv_array, u_int *subcard_type); /* Prototype of card NIO set function */ typedef int (*cisco_card_set_nio_fn)(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio); /* Prototype of card NIO unset function */ typedef int (*cisco_card_unset_nio_fn)(vm_instance_t *vm, struct cisco_card *card, u_int port_id); /* Prototype of card NIO show info function */ typedef int (*cisco_card_show_info_fn)(vm_instance_t *vm, struct cisco_card *card); /* Cisco NIO binding to a slot/port */ struct cisco_nio_binding { netio_desc_t *nio; u_int port_id,orig_port_id; struct cisco_nio_binding *prev,*next; }; /* Generic Cisco card driver */ struct cisco_card_driver { char *dev_type; int supported; int wic_slots; cisco_card_init_fn card_init; cisco_card_shutdown_fn card_shutdown; cisco_card_get_sub_info_fn card_get_sub_info; cisco_card_set_nio_fn card_set_nio; cisco_card_unset_nio_fn card_unset_nio; cisco_card_show_info_fn card_show_info; }; /* Generic Cisco card */ struct cisco_card { char *dev_name; /* Device name */ char *dev_type; /* Device Type */ u_int card_type; /* Card type (NM,PA,WIC,...) */ u_int card_flags; /* Card flags */ u_int card_id; /* Card ID (slot or sub-slot) */ u_int slot_id,subslot_id; /* Slot and Sub-slot ID */ struct cisco_eeprom eeprom; /* EEPROM */ struct pci_bus *pci_bus; /* PCI bus */ struct cisco_card_driver *driver; /* Driver */ void *drv_info; /* Private driver info */ struct cisco_nio_binding *nio_list; /* NIO bindings to ports */ struct cisco_card *parent; /* Parent card */ struct cisco_card *sub_slots[CISCO_CARD_MAX_SUBSLOTS]; /* Sub-slots */ }; /* Set EEPROM definition for the specified Cisco card */ int cisco_card_set_eeprom(vm_instance_t *vm,struct cisco_card *card, const struct cisco_eeprom *eeprom); /* Unset EEPROM definition */ int cisco_card_unset_eeprom(struct cisco_card *card); /* Check if a card has a valid EEPROM defined */ int cisco_card_check_eeprom(struct cisco_card *card); /* Get slot info */ struct cisco_card *vm_slot_get_card_ptr(vm_instance_t *vm,u_int slot_id); /* Check if a slot has an active card */ int vm_slot_active(vm_instance_t *vm,u_int slot_id,u_int port_id); /* Set a flag for a card */ int vm_slot_set_flag(vm_instance_t *vm,u_int slot_id,u_int port_id,u_int flag); /* Add a slot binding */ int vm_slot_add_binding(vm_instance_t *vm,char *dev_type, u_int slot_id,u_int port_id); /* Remove a slot binding */ int vm_slot_remove_binding(vm_instance_t *vm,u_int slot_id,u_int port_id); /* Add a network IO binding */ int vm_slot_add_nio_binding(vm_instance_t *vm,u_int slot_id,u_int port_id, char *nio_name); /* Remove a NIO binding */ int vm_slot_remove_nio_binding(vm_instance_t *vm,u_int slot_id,u_int port_id); /* Remove all NIO bindings for the specified slot (sub-slots included) */ int vm_slot_remove_all_nio_bindings(vm_instance_t *vm,u_int slot_id); /* Enable a Network IO descriptor for the specified slot */ int vm_slot_enable_nio(vm_instance_t *vm,u_int slot_id,u_int port_id); /* Disable Network IO descriptor for the specified slot */ int vm_slot_disable_nio(vm_instance_t *vm,u_int slot_id,u_int port_id); /* Enable all NIO for the specified slot (sub-slots included) */ int vm_slot_enable_all_nio(vm_instance_t *vm,u_int slot_id); /* Disable all NIO for the specified slot (sub-slots included) */ int vm_slot_disable_all_nio(vm_instance_t *vm,u_int slot_id); /* Initialize the specified slot (sub-slots included) */ int vm_slot_init(vm_instance_t *vm,u_int slot_id); /* Initialize all slots of a VM */ int vm_slot_init_all(vm_instance_t *vm); /* Shutdown the specified slot (sub-slots included) */ int vm_slot_shutdown(vm_instance_t *vm,u_int slot_id); /* Shutdown all slots of a VM */ int vm_slot_shutdown_all(vm_instance_t *vm); /* Show info about the specified slot (sub-slots included) */ int vm_slot_show_info(vm_instance_t *vm,u_int slot_id); /* Show info about all slots */ int vm_slot_show_all_info(vm_instance_t *vm); /* Check if the specified slot has a valid EEPROM defined */ int vm_slot_check_eeprom(vm_instance_t *vm,u_int slot_id,u_int port_id); /* Returns the EEPROM data of the specified slot */ struct cisco_eeprom * vm_slot_get_eeprom(vm_instance_t *vm,u_int slot_id,u_int port_id); /* Save config for the specified slot (sub-slots included) */ int vm_slot_save_config(vm_instance_t *vm,u_int slot_id,FILE *fd); /* Save config for all slots */ int vm_slot_save_all_config(vm_instance_t *vm,FILE *fd); /* Show slot drivers */ int vm_slot_show_drivers(vm_instance_t *vm); /* Create a Network Module (command line) */ int vm_slot_cmd_create(vm_instance_t *vm,char *str); /* Add a Network IO descriptor binding (command line) */ int vm_slot_cmd_add_nio(vm_instance_t *vm,char *str); #endif dynamips-0.2.14/common/cisco_eeprom.c000066400000000000000000000773211241034141600175330ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot. All rights reserved. * * Cisco EEPROM manipulation functions. */ #include #include #include #include #include #include #include "utils.h" #include "cisco_eeprom.h" /* ====================================================================== */ /* NM-1E: 1 Ethernet Port Network Module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_nm_1e_data[] = { 0x0143, 0x0100, 0x0075, 0xCD81, 0x500D, 0xA201, 0x0000, 0x0000, 0x5800, 0x0000, 0x9803, 0x2000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* NM-4E: 4 Ethernet Port Network Module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_nm_4e_data[] = { 0x0142, 0x0100, 0x0075, 0xCD81, 0x500D, 0xA201, 0x0000, 0x0000, 0x5800, 0x0000, 0x9803, 0x2000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* NM-1FE-TX: 1 FastEthernet Port Network Module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_nm_1fe_tx_data[] = { 0x0144, 0x0100, 0x0075, 0xCD81, 0x500D, 0xA201, 0x0000, 0x0000, 0x5800, 0x0000, 0x9803, 0x2000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* NM-16ESW: 16 FastEthernet Port Switch Network Module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_nm_16esw_data[] = { 0x04FF, 0x4002, 0xA941, 0x0100, 0xC046, 0x0320, 0x003B, 0x3401, 0x4245, 0x3080, 0x0000, 0x0000, 0x0203, 0xC18B, 0x3030, 0x3030, 0x3030, 0x3030, 0x3030, 0x3003, 0x0081, 0x0000, 0x0000, 0x0400, 0xCF06, 0x0013, 0x1A1D, 0x0BD1, 0x4300, 0x11FF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* NMD-36ESW: 36 FastEthernet Port Switch Network Module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_nmd_36esw_data[] = { 0x04FF, 0x4002, 0xB141, 0x0100, 0xC046, 0x0320, 0x003B, 0x3401, 0x4245, 0x3080, 0x0000, 0x0000, 0x0203, 0xC18B, 0x3030, 0x3030, 0x3030, 0x3030, 0x3030, 0x3003, 0x0081, 0x0000, 0x0000, 0x0400, 0xCF06, 0x0013, 0x1A1D, 0x0BD1, 0x4300, 0x26FF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* NM-4T: 4 Serial Network Module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_nm_4t_data[] = { 0x0154, 0x0101, 0x009D, 0x2D64, 0x5009, 0x0A02, 0x0000, 0x0000, 0x5800, 0x0000, 0x9811, 0x0300, 0x0005, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* NM-2E2W: 2 Ethernet ports with 2 WIC slots Module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_nm_2e2w_data[] = { 0x011E, 0x0102, 0x009A, 0xEBB1, 0x5004, 0x9305, 0x0000, 0x0000, 0x5000, 0x0000, 0x9808, 0x1217, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* NM-2W: 2 WIC slots Module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_nm_2w_data[] = { 0x04FF, 0x4000, 0xD641, 0x0100, 0xC046, 0x0320, 0x0012, 0xBF01, 0x4247, 0x3080, 0x0000, 0x0000, 0x0205, 0xC18B, 0x4A41, 0x4430, 0x3730, 0x3330, 0x375A, 0x3203, 0x0081, 0x0000, 0x0000, 0x0400, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* NM-1A-OC3MM: 1 ATM OC3 port Module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_nm_1a_oc3mm_data[] = { 0x019A, 0x0100, 0x015B, 0x41D9, 0x500E, 0x7402, 0x0000, 0x0000, 0x7800, 0x0000, 0x0011, 0x2117, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* NM-NAM: Network Analysis Module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_nm_nam_data[] = { 0x04FF, 0x4004, 0x6A41, 0x0100, 0xC046, 0x0320, 0x004F, 0x9E01, 0x4241, 0x3080, 0x0000, 0x0000, 0x0202, 0xC18B, 0x4A41, 0x4230, 0x3630, 0x3630, 0x3543, 0x3403, 0x0081, 0x0000, 0x0000, 0x0400, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* NM-CIDS: Network Analysis Module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_nm_cids_data[] = { 0x04FF, 0x4004, 0x2541, 0x0100, 0xC046, 0x0320, 0x004F, 0x9E01, 0x4241, 0x3080, 0x0000, 0x0000, 0x0202, 0xC18B, 0x4A41, 0x4230, 0x3630, 0x3630, 0x3543, 0x3403, 0x0081, 0x0000, 0x0000, 0x0400, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* NM EEPROMs */ /* ====================================================================== */ static const struct cisco_eeprom eeprom_nm_array[] = { { "NM-1E", eeprom_nm_1e_data, sizeof(eeprom_nm_1e_data)/2 }, { "NM-4E", eeprom_nm_4e_data, sizeof(eeprom_nm_4e_data)/2 }, { "NM-1FE-TX", eeprom_nm_1fe_tx_data, sizeof(eeprom_nm_1fe_tx_data)/2 }, { "NM-16ESW", eeprom_nm_16esw_data, sizeof(eeprom_nm_16esw_data)/2 }, { "NMD-36ESW", eeprom_nmd_36esw_data, sizeof(eeprom_nmd_36esw_data)/2 }, { "NM-4T", eeprom_nm_4t_data, sizeof(eeprom_nm_4t_data)/2 }, { "NM-2E2W", eeprom_nm_2e2w_data, sizeof(eeprom_nm_2e2w_data)/2 }, { "NM-2W", eeprom_nm_2w_data, sizeof(eeprom_nm_2w_data)/2 }, { "NM-1A-OC3MM", eeprom_nm_1a_oc3mm_data, sizeof(eeprom_nm_1a_oc3mm_data)/2 }, { "NM-NAM", eeprom_nm_nam_data, sizeof(eeprom_nm_nam_data)/2 }, { "NM-CIDS", eeprom_nm_cids_data, sizeof(eeprom_nm_cids_data)/2 }, { NULL, NULL, 0 }, }; /* Find a NM EEPROM */ const struct cisco_eeprom *cisco_eeprom_find_nm(char *name) { return(cisco_eeprom_find(eeprom_nm_array,name)); } /* ====================================================================== */ /* PA-FE-TX: 1 FastEthernet Port Adapter EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_pa_fe_tx_data[] = { 0x0111, 0x0102, 0xffff, 0xffff, 0x4906, 0x9804, 0x0000, 0x0000, 0x6000, 0x0000, 0x9812, 0x1700, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* PA-2FE-TX: 2 FastEthernet Port Adapter EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_pa_2fe_tx_data[] = { 0x04FF, 0x4002, 0x2441, 0x0100, 0xC18B, 0x5858, 0x5830, 0x3030, 0x3030, 0x3030, 0x3082, 0x4915, 0x2C04, 0x4241, 0x3003, 0x0081, 0x0000, 0x0000, 0x0400, 0x8000, 0x0000, 0x00CB, 0x9450, 0x412D, 0x3246, 0x452D, 0x4658, 0x2020, 0x2020, 0x2020, 0x2020, 0x2020, 0x20C0, 0x4603, 0x2000, 0x20A0, 0x04FF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* PA-GE: 1 GigabitEthernet Port Adapter EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_pa_ge_data[] = { 0x0198, 0x0100, 0x0000, 0x0000, 0x000C, 0x4803, 0x0000, 0x0000, 0x5000, 0x0000, 0x9906, 0x0300, 0x0001, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* PA-4E: 4 Ethernet Port Adapter EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_pa_4e_data[] = { 0x0102, 0x010E, 0xFFFF, 0xFFFF, 0x4906, 0x1404, 0x0000, 0x0000, 0x5000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* PA-8E: 8 Ethernet Port Adapter EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_pa_8e_data[] = { 0x0101, 0x010E, 0xFFFF, 0xFFFF, 0x4906, 0x1404, 0x0000, 0x0000, 0x5000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* PA-4T+: 4 Serial Port Adapter EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_pa_4t_data[] = { 0x010C, 0x010F, 0xffff, 0xffff, 0x4906, 0x2E07, 0x0000, 0x0000, 0x5000, 0x0000, 0x0010, 0x2400, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* PA-8T: 8 Serial Port Adapter EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_pa_8t_data[] = { 0x010E, 0x010F, 0xffff, 0xffff, 0x4906, 0x2E07, 0x0000, 0x0000, 0x5000, 0x0000, 0x0010, 0x2400, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* PA-A1: 1 ATM Port Adapter EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_pa_a1_data[] = { 0x0117, 0x010F, 0xffff, 0xffff, 0x4906, 0x2E07, 0x0000, 0x0000, 0x5000, 0x0000, 0x0010, 0x2400, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* PA-A3: 1 ATM Port Adapter EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_pa_a3_data[] = { 0x0159, 0x0200, 0xFFFF, 0xFFFF, 0x4909, 0x7E04, 0x0000, 0x0000, 0x5000, 0x0000, 0x0007, 0x1100, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* PA-POS-OC3: 1 POS Port Adapter EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_pa_pos_oc3_data[] = { 0x0196, 0x0202, 0xffff, 0xffff, 0x490C, 0x7806, 0x0000, 0x0000, 0x5000, 0x0000, 0x0208, 0x1900, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* PA-4B: 4 BRI Port Adapter EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_pa_4b_data[] = { 0x013D, 0x0202, 0xffff, 0xffff, 0x490C, 0x7806, 0x0000, 0x0000, 0x5000, 0x0000, 0x0208, 0x1900, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* PA-MC-8TE1 */ /* ====================================================================== */ static m_uint16_t eeprom_pa_mc8te1_data[] = { 0x04FF, 0x4003, 0x4E41, 0x0200, 0xC18B, 0x4A41, 0x4530, 0x3834, 0x3159, 0x3251, 0x3082, 0x491D, 0x7D02, 0x4241, 0x3003, 0x0081, 0x0000, 0x0000, 0x0400, 0x8000, 0x0127, 0x9BCB, 0x9450, 0x412D, 0x4D43, 0x2D38, 0x5445, 0x312B, 0x2020, 0x2020, 0x2020, 0x2020, 0x20C0, 0x4603, 0x2000, 0x4BBB, 0x02FF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* C7200-JC-PA */ /* ====================================================================== */ static m_uint16_t eeprom_c7200_jc_pa_data[] = { 0x04FF, 0x4005, 0x1141, 0x0101, 0x8744, 0x0A3B, 0x0382, 0x4928, 0xB003, 0x4241, 0x30C1, 0x8B58, 0x5858, 0x5858, 0x5858, 0x5858, 0x5858, 0x0400, 0x0203, 0x851C, 0x1DDA, 0x03CB, 0x8B43, 0x3732, 0x3030, 0x2D4A, 0x432D, 0x5041, 0x8800, 0x0145, 0xC589, 0x5630, 0x3120, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* PA EEPROMs */ /* ====================================================================== */ static const struct cisco_eeprom eeprom_pa_array[] = { { "PA-FE-TX", eeprom_pa_fe_tx_data, sizeof(eeprom_pa_fe_tx_data)/2 }, { "PA-2FE-TX", eeprom_pa_2fe_tx_data, sizeof(eeprom_pa_2fe_tx_data)/2 }, { "PA-GE", eeprom_pa_ge_data, sizeof(eeprom_pa_ge_data)/2 }, { "PA-4E", eeprom_pa_4e_data, sizeof(eeprom_pa_4e_data)/2 }, { "PA-8E", eeprom_pa_8e_data, sizeof(eeprom_pa_8e_data)/2 }, { "PA-4T+", eeprom_pa_4t_data, sizeof(eeprom_pa_4t_data)/2 }, { "PA-8T", eeprom_pa_8t_data, sizeof(eeprom_pa_8t_data)/2 }, { "PA-A1", eeprom_pa_a1_data, sizeof(eeprom_pa_a1_data)/2 }, { "PA-A3", eeprom_pa_a3_data, sizeof(eeprom_pa_a3_data)/2 }, { "PA-POS-OC3", eeprom_pa_pos_oc3_data, sizeof(eeprom_pa_pos_oc3_data)/2 }, { "PA-4B", eeprom_pa_4b_data, sizeof(eeprom_pa_4b_data)/2 }, { "PA-MC-8TE1", eeprom_pa_mc8te1_data, sizeof(eeprom_pa_mc8te1_data)/2 }, { "C7200-JC-PA", eeprom_c7200_jc_pa_data, sizeof(eeprom_c7200_jc_pa_data)/2 }, { NULL, NULL, 0 }, }; /* Find a PA EEPROM */ const struct cisco_eeprom *cisco_eeprom_find_pa(char *name) { return(cisco_eeprom_find(eeprom_pa_array,name)); } /* ====================================================================== */ /* WIC-1T: 1 Serial port Module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_wic_1t_data[] = { 0x0102, 0x0100, 0x0000, 0x0000, 0x5005, 0xEA01, 0x0000, 0x0000, 0xB000, 0x0000, 0x0303, 0x0401, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* WIC-2T: 2 Serial ports Module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_wic_2t_data[] = { 0x0112, 0x0100, 0x0000, 0x0000, 0x5005, 0xEA01, 0x0000, 0x0000, 0xB000, 0x0000, 0x0303, 0x0401, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* WIC-1B-S/T: 1 BRI port Module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_wic_1b_st_data[] = { 0x0107, 0x0100, 0x0000, 0x0000, 0x5005, 0xEA01, 0x0000, 0x0000, 0xB000, 0x0000, 0x0303, 0x0401, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* WIC-4ESW: 4 Ethernet port switch module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_wic_4esw_data[] = { 0x04FF, 0x4000, 0x6441, 0x0100, 0x8249, 0x22FE, 0x0142, 0x4430, 0x8000, 0x0000, 0x0002, 0x01C1, 0x8B46, 0x4F43, 0x3039, 0x3435, 0x344C, 0x5345, 0x0300, 0x8100, 0x0000, 0x0004, 0x00C0, 0x4603, 0x2000, 0x60F1, 0x0105, 0x01CF, 0x0600, 0x1646, 0x37F4, 0x6843, 0x0014, 0xCB88, 0x5749, 0x432D, 0x3445, 0x5357, 0xC68A, 0x4950, 0x4D45, 0x4430, 0x3042, 0x5241, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* WIC-1ENET: 1 Ethernet port module EEPROM */ /* ====================================================================== */ static m_uint16_t eeprom_wic_1enet_data[] = { 0x04FF, 0x4000, 0x3941, 0x0101, 0xC18B, 0x464F, 0x4330, 0x3830, 0x3832, 0x4330, 0x3682, 0x4923, 0x0901, 0x4242, 0x3002, 0x04CB, 0x8957, 0x4943, 0x2D31, 0x454E, 0x4554, 0x0700, 0x0300, 0x8100, 0x0000, 0x0005, 0x0104, 0x00CF, 0x0644, 0x5566, 0x7788, 0xAA43, 0x0001, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* ====================================================================== */ /* WIC EEPROMs */ /* ====================================================================== */ static const struct cisco_eeprom eeprom_wic_array[] = { { "WIC-1T", eeprom_wic_1t_data, sizeof(eeprom_wic_1t_data)/2 }, { "WIC-2T", eeprom_wic_2t_data, sizeof(eeprom_wic_2t_data)/2 }, { "WIC-1B", eeprom_wic_1b_st_data, sizeof(eeprom_wic_1b_st_data)/2 }, { "WIC-4ESW", eeprom_wic_4esw_data, sizeof(eeprom_wic_4esw_data)/2 }, { "WIC-1ENET", eeprom_wic_1enet_data, sizeof(eeprom_wic_1enet_data)/2 }, { NULL, NULL, 0 }, }; /* Find a WIC EEPROM */ const struct cisco_eeprom *cisco_eeprom_find_wic(char *name) { return(cisco_eeprom_find(eeprom_wic_array,name)); } /* ====================================================================== */ /* C6k EEPROMs */ /* ====================================================================== */ /* Chassis: 6509 */ static m_uint16_t eeprom_c6k_chassis_6509_data[] = { 0xABAB, 0x0190, 0x0F0D, 0x0100, 0x0002, 0x6001, 0x9002, 0x4369, 0x7363, 0x6F20, 0x5379, 0x7374, 0x656D, 0x7300, 0x0000, 0x0000, 0x0000, 0x5753, 0x2D43, 0x3635, 0x3039, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5343, 0x4130, 0x3333, 0x3730, 0x314A, 0x5500, 0x0000, 0x0000, 0x0000, 0x0000, 0x3733, 0x2D33, 0x3433, 0x382D, 0x3033, 0x0000, 0x0000, 0x0000, 0x4230, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0009, 0x0005, 0x0001, 0x0002, 0x0001, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6001, 0x0124, 0x01AD, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0016, 0x00D0, 0x000F, 0x2000, 0x0400, 0x0009, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; /* Supervisor: SUP1A-2GE */ static m_uint16_t eeprom_c6k_sup1a_2ge_data[] = { 0xABAB, 0x0190, 0x138F, 0x0100, 0x0002, 0x6003, 0x00DB, 0x4369, 0x7363, 0x6F20, 0x5379, 0x7374, 0x656D, 0x7300, 0x0000, 0x0000, 0x0000, 0x5753, 0x2D58, 0x364B, 0x2D53, 0x5550, 0x3141, 0x2D32, 0x4745, 0x0000, 0x0000, 0x5341, 0x4430, 0x3333, 0x3431, 0x3639, 0x3800, 0x0000, 0x0000, 0x0000, 0x0000, 0x3733, 0x2D34, 0x3336, 0x382D, 0x3031, 0x0000, 0x0000, 0x0000, 0x4130, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0009, 0x0005, 0x0001, 0x0003, 0x0001, 0x0001, 0x0002, 0x00DB, 0xFF56, 0x0000, 0x0000, 0x6003, 0x0162, 0x0B56, 0x0000, 0x0000, 0x0000, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000, 0x0014, 0x00D0, 0xBCEE, 0xB920, 0x0002, 0x0100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1F02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0200, 0x4132, 0x8181, 0x8181, 0x4B3C, 0x8080, 0x8080, 0x8080, 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; /* EARL: PFC1 (aka EARL5) */ static m_uint16_t eeprom_c6k_earl_pfc1_data[] = { 0xABAB, 0x0190, 0x117D, 0x0100, 0x0002, 0x6004, 0x0066, 0x4369, 0x7363, 0x6F20, 0x5379, 0x7374, 0x656D, 0x7300, 0x0000, 0x0000, 0x0000, 0x5753, 0x2D46, 0x364B, 0x2D50, 0x4643, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5341, 0x4430, 0x3334, 0x3333, 0x3637, 0x3800, 0x0000, 0x0000, 0x0000, 0x0000, 0x3733, 0x2D34, 0x3037, 0x352D, 0x3033, 0x0000, 0x0000, 0x0000, 0x4130, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0009, 0x0005, 0x0001, 0x0003, 0x0001, 0x0001, 0x0010, 0x0066, 0xFFB0, 0x0000, 0x0000, 0x6004, 0x0148, 0x07B7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x000E, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4B3C, 0x4132, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; /* Power Supply: 1000W */ static m_uint16_t eeprom_c6k_power_1000w_data[] = { 0xABAB, 0x0190, 0x121C, 0x0100, 0x0002, 0xAB01, 0x0003, 0x4369, 0x7363, 0x6F20, 0x5379, 0x7374, 0x656D, 0x732C, 0x2049, 0x6E63, 0x2E00, 0x5753, 0x2D43, 0x4143, 0x2D31, 0x3030, 0x3057, 0x0000, 0x0000, 0x0000, 0x0000, 0x534F, 0x4E30, 0x3430, 0x3930, 0x3036, 0x3600, 0x0000, 0x0000, 0x0000, 0x0000, 0x3334, 0x2D30, 0x3932, 0x332D, 0x3031, 0x0000, 0x0000, 0x0000, 0x4230, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0009, 0x000C, 0x0003, 0x0001, 0x0006, 0x0003, 0x0000, 0x0000, 0x07EE, 0x0000, 0x0000, 0xAB01, 0x0114, 0x02C0, 0x0000, 0x0000, 0x0000, 0x0000, 0x07EE, 0x07EE, 0x0015, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; /* VTT: Voltage Termination module */ static m_uint16_t eeprom_c6k_vtt_data[] = { 0xABAB, 0x0190, 0x0FC4, 0x0100, 0x0002, 0xAB02, 0x0001, 0x4369, 0X7363, 0x6F20, 0x5379, 0x7374, 0x656D, 0x7300, 0x0000, 0x0000, 0x0000, 0x5753, 0x2D43, 0x3630, 0x3030, 0x2D56, 0x5454, 0x0000, 0x0000, 0x0000, 0x0000, 0x534D, 0x5430, 0x3333, 0x3531, 0x3330, 0x3400, 0x0000, 0x0000, 0x0000, 0x0000, 0x3733, 0x2D33, 0x3230, 0x382D, 0x3034, 0x0000, 0x0000, 0x0000, 0x4130, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0009, 0x0005, 0x0001, 0x0002, 0x0012, 0x0001, 0x0002, 0x0003, 0x0000, 0x0000, 0x0000, 0xAB02, 0x0118, 0x00C9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x6455, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; /* Linecard: WS-X6248 */ static m_uint16_t eeprom_c6k_lc_wsx6248_data[] = { 0xABAB, 0x0190, 0x1339, 0x0100, 0x0002, 0x6003, 0x00CB, 0x4369, 0x7363, 0x6F20, 0x5379, 0x7374, 0x656D, 0x7300, 0x0000, 0x0000, 0x0000, 0x5753, 0x2D58, 0x3632, 0x3438, 0x2D52, 0x4A2D, 0x3435, 0x0000, 0x0000, 0x0000, 0x5341, 0x4430, 0x3333, 0x3436, 0x3834, 0x3200, 0x0000, 0x0000, 0x0000, 0x0000, 0x3733, 0x2D33, 0x3234, 0x342D, 0x3038, 0x0000, 0x0000, 0x0000, 0x4330, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0009, 0x0005, 0x0001, 0x0003, 0x0001, 0x0001, 0x0002, 0x00CB, 0xFEF3, 0x0000, 0x0000, 0x6003, 0x0162, 0x0B02, 0x0000, 0x0000, 0x0000, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0030, 0xB6CC, 0x3CC0, 0x0030, 0x0106, 0x0003, 0x0001, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1230, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0200, 0x4B3C, 0x4132, 0x8181, 0x8181, 0x8080, 0x8080, 0x8080, 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static const struct cisco_eeprom eeprom_c6k_array[] = { { "C6K-CHASSIS-6509", eeprom_c6k_chassis_6509_data, sizeof(eeprom_c6k_chassis_6509_data)/2 } , { "C6K-SUP-SUP1A-2GE", eeprom_c6k_sup1a_2ge_data, sizeof(eeprom_c6k_sup1a_2ge_data)/2 } , { "C6K-EARL-PFC1", eeprom_c6k_earl_pfc1_data, sizeof(eeprom_c6k_earl_pfc1_data)/2 } , { "C6K-POWER-1000W", eeprom_c6k_power_1000w_data, sizeof(eeprom_c6k_power_1000w_data)/2 } , { "C6K-VTT", eeprom_c6k_vtt_data, sizeof(eeprom_c6k_vtt_data) }, { "C6K-LC-WS-X6248", eeprom_c6k_lc_wsx6248_data, sizeof(eeprom_c6k_lc_wsx6248_data)/2 } , { NULL, NULL, 0 }, }; /* Find a C6k EEPROM */ const struct cisco_eeprom *cisco_eeprom_find_c6k(char *name) { return(cisco_eeprom_find(eeprom_c6k_array,name)); } /* ====================================================================== */ /* Utility functions */ /* ====================================================================== */ /* Find an EEPROM in the specified EEPROM array */ const struct cisco_eeprom * cisco_eeprom_find(const struct cisco_eeprom *eeproms,char *name) { int i; for(i=0;eeproms[i].name;i++) if (!strcmp(eeproms[i].name,name)) return(&eeproms[i]); return NULL; } /* Copy an EEPROM */ int cisco_eeprom_copy(struct cisco_eeprom *dst,const struct cisco_eeprom *src) { m_uint16_t *data; if (!src || !src) return(-1); cisco_eeprom_free(dst); if (!(data = malloc(src->len << 1))) return(-1); memcpy(data,src->data,src->len << 1); dst->name = src->name; dst->data = data; dst->len = src->len; return(0); } /* Free resources used by an EEPROM */ void cisco_eeprom_free(struct cisco_eeprom *eeprom) { if (eeprom && eeprom->data) { free(eeprom->data); eeprom->data = NULL; eeprom->len = 0; } } /* Return TRUE if the specified EEPROM contains usable data */ int cisco_eeprom_valid(struct cisco_eeprom *eeprom) { return((eeprom && eeprom->data) ? TRUE : FALSE); } /* Get a byte from an EEPROM */ int cisco_eeprom_get_byte(struct cisco_eeprom *eeprom, size_t offset,m_uint8_t *val) { m_uint16_t tmp; if (offset >= (eeprom->len << 1)) { *val = 0xFF; return(-1); } tmp = eeprom->data[offset >> 1]; if (!(offset & 1)) tmp >>= 8; *val = tmp & 0xFF; return(0); } /* Set a byte to an EEPROM */ int cisco_eeprom_set_byte(struct cisco_eeprom *eeprom, size_t offset,m_uint8_t val) { m_uint16_t tmp; if (offset >= (eeprom->len << 1)) return(-1); tmp = eeprom->data[offset >> 1]; if (offset & 1) tmp = (tmp & 0xFF00) | val; else tmp = (tmp & 0x00FF) | (val << 8); eeprom->data[offset >> 1] = tmp; return(0); } /* Get an EEPROM region */ int cisco_eeprom_get_region(struct cisco_eeprom *eeprom,size_t offset, m_uint8_t *data,size_t data_len) { size_t i; for(i=0;i> 6) & 0x03; if (tmp == 0x03) { /* Variable len */ if (cisco_eeprom_get_byte(eeprom,(*offset)++,&tmp) == -1) return(-1); *len = tmp & 0x0F; } else { /* Fixed len */ *len = 1 << tmp; } return(1); } /* Dump a Cisco EEPROM unformatted */ void cisco_eeprom_dump(struct cisco_eeprom *eeprom) { printf("Dumping EEPROM contents:\n"); size_t i = 0; m_uint8_t tmp; do { if (cisco_eeprom_get_byte(eeprom,i,&tmp) == -1) break; printf(" 0x%2.2x",tmp); i++; if (i%16 == 0) printf("\n") ; } while(1); printf("\n"); } /* Dump a Cisco EEPROM with format version 4 */ void cisco_eeprom_v4_dump(struct cisco_eeprom *eeprom) { m_uint8_t type,len,tmp; size_t i,offset=2; printf("Dumping EEPROM contents:\n"); do { /* Read field */ if (cisco_eeprom_v4_get_field(eeprom,&type,&len,&offset) < 1) break; printf(" Field 0x%2.2x: ",type); for(i=0;ilen << 1)); } /* Returns the offset of the specified field */ int cisco_eeprom_v4_find_field(struct cisco_eeprom *eeprom, m_uint8_t field_type, size_t *field_offset) { m_uint8_t type,len; size_t offset=2; do { /* Read field */ if (cisco_eeprom_v4_get_field(eeprom,&type,&len,&offset) < 1) break; if (type == field_type) { *field_offset = offset; return(0); } offset += len; }while(offset < (eeprom->len << 1)); return(-1); } dynamips-0.2.14/common/cisco_eeprom.h000066400000000000000000000112621241034141600175300ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot. All rights reserved. * * Cisco EEPROM manipulation functions. */ #ifndef __CISCO_EEPROM_H__ #define __CISCO_EEPROM_H__ #include "utils.h" /* CISCO EEPROM format version 1 (size=0x20?) 0x00: 01(version) 0x01: XX(product id) // TODO format might depend on the type or class of hardware 0x02: XX (.) XX(Hardware revision) 0x04: XX XX XX XX(Serial number) 0x08: XX (-) XX XX (-) XX(Part number) 0x0C: XX(Test history) 0x0D: XX (-) XX (-) XX(RMA number) 0x10: XX(Board Revision) 0x11: ...FF(padding?) 0x17: XX(Connector type)(0=PCI,1=Wan Module,other=PCI) 0x18: ...FF(padding?) // 0x20+ is optional? ignored if FF FF... 0x26: XX XX XX XX(Version Identifier)(4 chars) 0x2A: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX(FRU Part Number)(18 chars) 0x3C: ...FF(padding?) */ /* CISCO EEPROM format version 4 (size=0x80?) 0x00: 04(version) FF(padding?) 0x02: { // {00000000b}.* adds 0x100 to id? // {LLDDDDDDb} has length=2^LLb(1,2,4) and id=DDDDDDb // {11DDDDDDb TTLLLLLLb} has id=DDDDDDb, length=LLLLLLb and type=TTb(00b=hex,01b=number,10b=string,11b=hex or reserved?) : 01 XX(Number of Slots) : 02 XX(Fab Version) : 03 XX(RMA Test History) : 04 XX(RMA History) : 05 XX(Connector Type) : 06 XX(EHSA Preferred Master) : 07 XX(Vendor ID) : 09 XX(Processor type) : 0B XX(Power Supply Type: 0=AC, !0=DC) : 0C XX(ignored?) : 40 XX XX(product id) : 41 XX (.) XX (Hardware Revision) : 42 XX XX(Board Revision) : 43 XXXX(MAC Address block size) : 44 XX XX(Capabilities) : 45 XX XX(Self test result) : 4A XX XX(Radio Country Code) : 80 XXXX XXXX(Deviation Number) : 81 XX (-) XX (-) XX (-) XX(RMA Number) : 82 XX (-) XXXX (-) XX(Part Number) : 83 XXXXXXXX(Hardware date code) : 84 XX XX XX XX(Manufacturing Engineer) : 85 XX (-) XXXX (-) XX(Fab Part Number) : C0 46 XX XX (-) XX XX XX (-) XX(Part Number)(number) : C1 8B XX XX XX XX XX XX XX XX XX XX XX(PCB Serial Number)(string) : C2 8B XX XX XX XX XX XX XX XX XX XX XX(Chassis Serial Number)(string) : C3 06 XX XX XX XX XX XX(Chassis MAC Address) : C4 08 XX XX XX XX XX XX XX XX(Manufacturing Test Data) : C5 08 XX XX XX XX XX XX XX XX(Field Diagnostics Data) : C6 8A XX XX XX XX XX XX XX XX XX XX(CLEI Code)(string) : C7 ?? XX?(ignored?) : C8 09 XX[min dBmV] XX[max dBmV] XX[num_values=3] XXXX[value_0] XXXX[value_1] XXXX[value_2](Calibration Data) : C9 ?? XX?(Platform features)(hex) : CB 88 XX XX XX XX XX XX XX XX(Product (FRU) Number)(string) : CF 06 XXXX (.) XXXX (.) XXXX(Base MAC Address) }.* 0x??: ...FF(padding?) */ /* Cisco EEPROM */ struct cisco_eeprom { char *name; m_uint16_t *data; size_t len; }; /* Find a NM EEPROM */ const struct cisco_eeprom *cisco_eeprom_find_nm(char *name); /* Find a PA EEPROM */ const struct cisco_eeprom *cisco_eeprom_find_pa(char *name); /* Find a WIC EEPROM */ const struct cisco_eeprom *cisco_eeprom_find_wic(char *name); /* Find a C6k EEPROM */ const struct cisco_eeprom *cisco_eeprom_find_c6k(char *name); /* Find an EEPROM in the specified EEPROM array */ const struct cisco_eeprom * cisco_eeprom_find(const struct cisco_eeprom *eeproms,char *name); /* Copy an EEPROM */ int cisco_eeprom_copy(struct cisco_eeprom *dst,const struct cisco_eeprom *src); /* Free resources used by an EEPROM */ void cisco_eeprom_free(struct cisco_eeprom *eeprom); /* Return TRUE if the specified EEPROM contains usable data */ int cisco_eeprom_valid(struct cisco_eeprom *eeprom); /* Get a byte from an EEPROM */ int cisco_eeprom_get_byte(struct cisco_eeprom *eeprom, size_t offset,m_uint8_t *val); /* Set a byte to an EEPROM */ int cisco_eeprom_set_byte(struct cisco_eeprom *eeprom, size_t offset,m_uint8_t val); /* Get an EEPROM region */ int cisco_eeprom_get_region(struct cisco_eeprom *eeprom,size_t offset, m_uint8_t *data,size_t data_len); /* Set an EEPROM region */ int cisco_eeprom_set_region(struct cisco_eeprom *eeprom,size_t offset, m_uint8_t *data,size_t data_len); /* Get a field of a Cisco EEPROM v4 */ int cisco_eeprom_v4_get_field(struct cisco_eeprom *eeprom,m_uint8_t *type, m_uint8_t *len,size_t *offset); /* Dump a Cisco EEPROM with format version 4 */ void cisco_eeprom_v4_dump(struct cisco_eeprom *eeprom); /* Dump a Cisco EEPROM with unformatted */ void cisco_eeprom_dump(struct cisco_eeprom *eeprom); /* Returns the offset of the specified field */ int cisco_eeprom_v4_find_field(struct cisco_eeprom *eeprom, m_uint8_t field_type, size_t *field_offset); #endif dynamips-0.2.14/common/crc.c000066400000000000000000000027721241034141600156310ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * CRC functions. */ #include "dynamips_common.h" #define CRC12_POLY 0x0f01 #define CRC16_POLY 0xa001 #define CRC32_POLY 0xedb88320L /* CRC tables */ m_uint16_t crc12_array[256],crc16_array[256]; m_uint32_t crc32_array[256]; /* Initialize CRC-12 algorithm */ static void crc12_init(void) { m_uint16_t crc,c; int i,j; for(i=0;i<256;i++) { crc = 0; c = (m_uint16_t)i; for(j=0;j<8;j++) { if ((crc ^ c) & 0x0001) crc = (crc >> 1) ^ CRC12_POLY; else crc = crc >> 1; c = c >> 1; } crc12_array[i] = crc; } } /* Initialize CRC-16 algorithm */ static void crc16_init(void) { m_uint16_t crc,c; int i,j; for(i=0;i<256;i++) { crc = 0; c = (m_uint16_t)i; for(j=0;j<8;j++) { if ((crc ^ c) & 0x0001) crc = (crc >> 1) ^ CRC16_POLY; else crc = crc >> 1; c = c >> 1; } crc16_array[i] = crc; } } /* Initialize CRC-32 algorithm */ static void crc32_init(void) { unsigned long c; int n, k; for (n=0;n<256;n++) { c = (unsigned long) n; for (k = 0; k < 8; k++) { if (c & 1) c = CRC32_POLY ^ (c >> 1); else c = c >> 1; } crc32_array[n] = c; } } /* Initialize CRC algorithms */ void crc_init(void) { crc12_init(); crc16_init(); crc32_init(); } dynamips-0.2.14/common/crc.h000066400000000000000000000022541241034141600156310ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * CRC functions. */ #ifndef __CRC_H__ #define __CRC_H__ #include "dynamips_common.h" extern m_uint16_t crc12_array[],crc16_array[]; extern m_uint32_t crc32_array[]; /* Compute a CRC-12 hash on a 32-bit integer */ static forced_inline m_uint32_t crc12_hash_u32(m_uint32_t val) { register m_uint32_t crc=0; register int i; for(i=0;i<4;i++) { crc = (crc >> 8) ^ crc12_array[(crc^val) & 0xff]; val >>= 8; } return(crc); } /* Compute a CRC-16 hash on a 32-bit integer */ static forced_inline m_uint32_t crc16_hash_u32(m_uint32_t val) { register m_uint32_t crc=0; register int i; for(i=0;i<4;i++) { crc = (crc >> 8) ^ crc16_array[(crc^val) & 0xff]; val >>= 8; } return(crc); } /* Compute a CRC-32 on the specified block */ static forced_inline m_uint32_t crc32_compute(m_uint32_t crc_accum,m_uint8_t *ptr,int len) { register m_uint32_t c = crc_accum; int n; for (n = 0; n < len; n++) { c = crc32_array[(c ^ ptr[n]) & 0xff] ^ (c >> 8); } return(~c); } /* Initialize CRC algorithms */ void crc_init(void); #endif dynamips-0.2.14/common/dev_am79c971.c000066400000000000000000000766201241034141600171040ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (C) 2006 Christophe Fillot. All rights reserved. * * AMD Am79c971 FastEthernet chip emulation. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_am79c971.h" /* Debugging flags */ #define DEBUG_CSR_REGS 0 #define DEBUG_BCR_REGS 0 #define DEBUG_PCI_REGS 0 #define DEBUG_ACCESS 0 #define DEBUG_TRANSMIT 0 #define DEBUG_RECEIVE 0 #define DEBUG_UNKNOWN 0 /* AMD Am79c971 PCI vendor/product codes */ #define AM79C971_PCI_VENDOR_ID 0x1022 #define AM79C971_PCI_PRODUCT_ID 0x2000 /* Maximum packet size */ #define AM79C971_MAX_PKT_SIZE 2048 /* Send up to 16 packets in a TX ring scan pass */ #define AM79C971_TXRING_PASS_COUNT 16 /* CSR0: Controller Status and Control Register */ #define AM79C971_CSR0_ERR 0x00008000 /* Error (BABL,CERR,MISS,MERR) */ #define AM79C971_CSR0_BABL 0x00004000 /* Transmitter Timeout Error */ #define AM79C971_CSR0_CERR 0x00002000 /* Collision Error */ #define AM79C971_CSR0_MISS 0x00001000 /* Missed Frame */ #define AM79C971_CSR0_MERR 0x00000800 /* Memory Error */ #define AM79C971_CSR0_RINT 0x00000400 /* Receive Interrupt */ #define AM79C971_CSR0_TINT 0x00000200 /* Transmit Interrupt */ #define AM79C971_CSR0_IDON 0x00000100 /* Initialization Done */ #define AM79C971_CSR0_INTR 0x00000080 /* Interrupt Flag */ #define AM79C971_CSR0_IENA 0x00000040 /* Interrupt Enable */ #define AM79C971_CSR0_RXON 0x00000020 /* Receive On */ #define AM79C971_CSR0_TXON 0x00000010 /* Transmit On */ #define AM79C971_CSR0_TDMD 0x00000008 /* Transmit Demand */ #define AM79C971_CSR0_STOP 0x00000004 /* Stop */ #define AM79C971_CSR0_STRT 0x00000002 /* Start */ #define AM79C971_CSR0_INIT 0x00000001 /* Initialization */ /* CSR3: Interrupt Masks and Deferral Control */ #define AM79C971_CSR3_BABLM 0x00004000 /* Transmit. Timeout Int. Mask */ #define AM79C971_CSR3_CERRM 0x00002000 /* Collision Error Int. Mask*/ #define AM79C971_CSR3_MISSM 0x00001000 /* Missed Frame Interrupt Mask */ #define AM79C971_CSR3_MERRM 0x00000800 /* Memory Error Interrupt Mask */ #define AM79C971_CSR3_RINTM 0x00000400 /* Receive Interrupt Mask */ #define AM79C971_CSR3_TINTM 0x00000200 /* Transmit Interrupt Mask */ #define AM79C971_CSR3_IDONM 0x00000100 /* Initialization Done Mask */ #define AM79C971_CSR3_BSWP 0x00000004 /* Byte Swap */ #define AM79C971_CSR3_IM_MASK 0x00007F00 /* Interrupt Masks for CSR3 */ /* CSR5: Extended Control and Interrupt 1 */ #define AM79C971_CSR5_TOKINTD 0x00008000 /* Receive Interrupt Mask */ #define AM79C971_CSR5_SPND 0x00000001 /* Suspend */ /* CSR15: Mode */ #define AM79C971_CSR15_PROM 0x00008000 /* Promiscous Mode */ #define AM79C971_CSR15_DRCVBC 0x00004000 /* Disable Receive Broadcast */ #define AM79C971_CSR15_DRCVPA 0x00002000 /* Disable Receive PHY address */ #define AM79C971_CSR15_DTX 0x00000002 /* Disable Transmit */ #define AM79C971_CSR15_DRX 0x00000001 /* Disable Receive */ /* AMD 79C971 Initialization block length */ #define AM79C971_INIT_BLOCK_LEN 0x1c /* RX descriptors */ #define AM79C971_RMD1_OWN 0x80000000 /* OWN=1: owned by Am79c971 */ #define AM79C971_RMD1_ERR 0x40000000 /* Error */ #define AM79C971_RMD1_FRAM 0x20000000 /* Framing Error */ #define AM79C971_RMD1_OFLO 0x10000000 /* Overflow Error */ #define AM79C971_RMD1_CRC 0x08000000 /* Invalid CRC */ #define AM79C971_RMD1_BUFF 0x08000000 /* Buffer Error (chaining) */ #define AM79C971_RMD1_STP 0x02000000 /* Start of Packet */ #define AM79C971_RMD1_ENP 0x01000000 /* End of Packet */ #define AM79C971_RMD1_BPE 0x00800000 /* Bus Parity Error */ #define AM79C971_RMD1_PAM 0x00400000 /* Physical Address Match */ #define AM79C971_RMD1_LAFM 0x00200000 /* Logical Addr. Filter Match */ #define AM79C971_RMD1_BAM 0x00100000 /* Broadcast Address Match */ #define AM79C971_RMD1_LEN 0x00000FFF /* Buffer Length */ #define AM79C971_RMD2_LEN 0x00000FFF /* Received byte count */ /* TX descriptors */ #define AM79C971_TMD1_OWN 0x80000000 /* OWN=1: owned by Am79c971 */ #define AM79C971_TMD1_ERR 0x40000000 /* Error */ #define AM79C971_TMD1_ADD_FCS 0x20000000 /* FCS generation */ #define AM79C971_TMD1_STP 0x02000000 /* Start of Packet */ #define AM79C971_TMD1_ENP 0x01000000 /* End of Packet */ #define AM79C971_TMD1_LEN 0x00000FFF /* Buffer Length */ /* RX Descriptor */ struct rx_desc { m_uint32_t rmd[4]; }; /* TX Descriptor */ struct tx_desc { m_uint32_t tmd[4]; }; /* AMD 79C971 Data */ struct am79c971_data { char *name; /* Lock */ pthread_mutex_t lock; /* Interface type (10baseT or 100baseTX) */ int type; /* RX/TX clearing count */ int rx_tx_clear_count; /* Current RAP (Register Address Pointer) value */ m_uint8_t rap; /* CSR and BCR registers */ m_uint32_t csr[256],bcr[256]; /* RX/TX rings start addresses */ m_uint32_t rx_start,tx_start; /* RX/TX number of descriptors (log2) */ m_uint32_t rx_l2len,tx_l2len; /* RX/TX number of descriptors */ m_uint32_t rx_len,tx_len; /* RX/TX ring positions */ m_uint32_t rx_pos,tx_pos; /* MII registers */ m_uint16_t mii_regs[32][32]; /* Physical (MAC) address */ n_eth_addr_t mac_addr; /* Device information */ struct vdevice *dev; /* PCI device information */ struct pci_device *pci_dev; /* Virtual machine */ vm_instance_t *vm; /* NetIO descriptor */ netio_desc_t *nio; /* TX ring scanner task id */ ptask_id_t tx_tid; }; /* Log an am79c971 message */ #define AM79C971_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* Lock/Unlock primitives */ #define AM79C971_LOCK(d) pthread_mutex_lock(&(d)->lock) #define AM79C971_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) static m_uint16_t mii_reg_values[32] = { 0x1000, 0x782D, 0x0013, 0x78E2, 0x01E1, 0xC9E1, 0x000F, 0x2001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0104, 0x4780, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00C8, 0x0000, 0xFFFF, 0x0000, 0x0000, 0x0000, #if 0 0x1000, 0x782D, 0x0013, 0x78e2, 0x01E1, 0xC9E1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x8060, 0x8023, 0x0820, 0x0000, 0x3800, 0xA3B9, 0x0000, 0x0000, 0x0000, #endif }; /* Read a MII register */ static m_uint16_t mii_reg_read(struct am79c971_data *d,u_int phy,u_int reg) { if ((phy >= 32) || (reg >= 32)) return(0); return(d->mii_regs[phy][reg]); } /* Write a MII register */ _maybe_used static void mii_reg_write(struct am79c971_data *d,u_int phy,u_int reg, m_uint16_t value) { if ((phy < 32) && (reg < 32)) d->mii_regs[phy][reg] = value; } /* Check if a packet must be delivered to the emulated chip */ static inline int am79c971_handle_mac_addr(struct am79c971_data *d, m_uint8_t *pkt) { n_eth_hdr_t *hdr = (n_eth_hdr_t *)pkt; /* Accept systematically frames if we are running in promiscuous mode */ if (d->csr[15] & AM79C971_CSR15_PROM) return(TRUE); /* Accept systematically all multicast frames */ if (eth_addr_is_mcast(&hdr->daddr)) return(TRUE); /* Accept frames directly for us, discard others */ if (!memcmp(&d->mac_addr,&hdr->daddr,N_ETH_ALEN)) return(TRUE); return(FALSE); } /* Update the Interrupt Flag bit of csr0 */ static void am79c971_update_irq_status(struct am79c971_data *d) { m_uint32_t mask; /* Bits set in CR3 disable the specified interrupts */ mask = AM79C971_CSR3_IM_MASK & ~(d->csr[3] & AM79C971_CSR3_IM_MASK); if (d->csr[0] & mask) d->csr[0] |= AM79C971_CSR0_INTR; else d->csr[0] &= ~AM79C971_CSR0_INTR; if ((d->csr[0] & (AM79C971_CSR0_INTR|AM79C971_CSR0_IENA)) == (AM79C971_CSR0_INTR|AM79C971_CSR0_IENA)) { pci_dev_trigger_irq(d->vm,d->pci_dev); } else { pci_dev_clear_irq(d->vm,d->pci_dev); } } /* Update RX/TX ON bits of csr0 */ static void am79c971_update_rx_tx_on_bits(struct am79c971_data *d) { /* * Set RX ON if DRX in csr15 is cleared, and set TX on if DTX * in csr15 is cleared. The START bit must be set. */ d->csr[0] &= ~(AM79C971_CSR0_RXON|AM79C971_CSR0_TXON); if (d->csr[0] & AM79C971_CSR0_STRT) { if (!(d->csr[15] & AM79C971_CSR15_DRX)) d->csr[0] |= AM79C971_CSR0_RXON; if (!(d->csr[15] & AM79C971_CSR15_DTX)) d->csr[0] |= AM79C971_CSR0_TXON; } } /* Update RX/TX descriptor lengths */ static void am79c971_update_rx_tx_len(struct am79c971_data *d) { d->rx_len = 1 << d->rx_l2len; d->tx_len = 1 << d->tx_l2len; /* Normalize ring sizes */ if (d->rx_len > 512) d->rx_len = 512; if (d->tx_len > 512) d->tx_len = 512; } /* Fetch the initialization block from memory */ static int am79c971_fetch_init_block(struct am79c971_data *d) { m_uint32_t ib[AM79C971_INIT_BLOCK_LEN]; m_uint32_t ib_addr,ib_tmp; /* The init block address is contained in csr1 (low) and csr2 (high) */ ib_addr = (d->csr[2] << 16) | d->csr[1]; if (!ib_addr) { AM79C971_LOG(d,"trying to fetch init block at address 0...\n"); return(-1); } AM79C971_LOG(d,"fetching init block at address 0x%8.8x\n",ib_addr); physmem_copy_from_vm(d->vm,ib,ib_addr,sizeof(ib)); /* Extract RX/TX ring addresses */ d->rx_start = vmtoh32(ib[5]); d->tx_start = vmtoh32(ib[6]); /* Set csr15 from mode field */ ib_tmp = vmtoh32(ib[0]); d->csr[15] = ib_tmp & 0xffff; /* Extract RX/TX ring sizes */ d->rx_l2len = (ib_tmp >> 20) & 0x0F; d->tx_l2len = (ib_tmp >> 28) & 0x0F; am79c971_update_rx_tx_len(d); AM79C971_LOG(d,"rx_ring = 0x%8.8x (%u), tx_ring = 0x%8.8x (%u)\n", d->rx_start,d->rx_len,d->tx_start,d->tx_len); /* Get the physical MAC address */ ib_tmp = vmtoh32(ib[1]); d->csr[12] = ib_tmp & 0xFFFF; d->csr[13] = ib_tmp >> 16; d->mac_addr.eth_addr_byte[3] = (ib_tmp >> 24) & 0xFF; d->mac_addr.eth_addr_byte[2] = (ib_tmp >> 16) & 0xFF; d->mac_addr.eth_addr_byte[1] = (ib_tmp >> 8) & 0xFF; d->mac_addr.eth_addr_byte[0] = ib_tmp & 0xFF; ib_tmp = vmtoh32(ib[2]); d->csr[14] = ib_tmp & 0xFFFF; d->mac_addr.eth_addr_byte[5] = (ib_tmp >> 8) & 0xFF; d->mac_addr.eth_addr_byte[4] = ib_tmp & 0xFF; /* * Mark the initialization as done is csr0. */ d->csr[0] |= AM79C971_CSR0_IDON; /* Update RX/TX ON bits of csr0 since csr15 has been modified */ am79c971_update_rx_tx_on_bits(d); AM79C971_LOG(d,"CSR0 = 0x%4.4x\n",d->csr[0]); return(0); } /* RDP (Register Data Port) access */ static void am79c971_rdp_access(cpu_gen_t *cpu,struct am79c971_data *d, u_int op_type,m_uint64_t *data) { m_uint32_t mask; #if DEBUG_CSR_REGS if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to CSR %d\n",d->rap); } else { cpu_log(cpu,d->name,"write access to CSR %d, value=0x%x\n",d->rap,*data); } #endif switch(d->rap) { case 0: /* CSR0: Controller Status and Control Register */ if (op_type == MTS_READ) { //AM79C971_LOG(d,"reading CSR0 (val=0x%4.4x)\n",d->csr[0]); *data = d->csr[0]; } else { /* * The STOP bit clears other bits. * It has precedence over INIT and START bits. */ if (*data & AM79C971_CSR0_STOP) { //AM79C971_LOG(d,"stopping interface!\n"); d->csr[0] = AM79C971_CSR0_STOP; d->tx_pos = d->rx_pos = 0; am79c971_update_irq_status(d); break; } /* These bits are cleared when set to 1 */ mask = AM79C971_CSR0_BABL | AM79C971_CSR0_CERR; mask |= AM79C971_CSR0_MISS | AM79C971_CSR0_MERR; mask |= AM79C971_CSR0_IDON; if (++d->rx_tx_clear_count == 3) { mask |= AM79C971_CSR0_RINT | AM79C971_CSR0_TINT; d->rx_tx_clear_count = 0; } d->csr[0] &= ~(*data & mask); /* Save the Interrupt Enable bit */ d->csr[0] |= *data & AM79C971_CSR0_IENA; /* If INIT bit is set, fetch the initialization block */ if (*data & AM79C971_CSR0_INIT) { d->csr[0] |= AM79C971_CSR0_INIT; d->csr[0] &= ~AM79C971_CSR0_STOP; am79c971_fetch_init_block(d); } /* If STRT bit is set, clear the stop bit */ if (*data & AM79C971_CSR0_STRT) { //AM79C971_LOG(d,"enabling interface!\n"); d->csr[0] |= AM79C971_CSR0_STRT; d->csr[0] &= ~AM79C971_CSR0_STOP; am79c971_update_rx_tx_on_bits(d); } /* Update IRQ status */ am79c971_update_irq_status(d); } break; case 6: /* CSR6: RX/TX Descriptor Table Length */ if (op_type == MTS_WRITE) { d->rx_l2len = (*data >> 8) & 0x0F; d->tx_l2len = (*data >> 12) & 0x0F; am79c971_update_rx_tx_len(d); } else { *data = (d->tx_l2len << 12) | (d->rx_l2len << 8); } break; case 15: /* CSR15: Mode */ if (op_type == MTS_WRITE) { d->csr[15] = *data; am79c971_update_rx_tx_on_bits(d); } else { *data = d->csr[15]; } break; case 88: /* CSR88: Chip ID Register Lower (VER=0, PARTID=0x2623, MANFID=1, ONE=1) */ if (op_type == MTS_READ) { switch(d->type) { case AM79C971_TYPE_100BASE_TX: *data = 0x02623003; break; case AM79C971_TYPE_10BASE_T: *data = 0x02621003; // Am79C970A, "AMD Presidio", "AmdP2" break; default: *data = 0; break; } } break; default: if (op_type == MTS_READ) { *data = d->csr[d->rap]; } else { d->csr[d->rap] = *data; } #if DEBUG_UNKNOWN if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to unknown CSR %d\n",d->rap); } else { cpu_log(cpu,d->name,"write access to unknown CSR %d, value=0x%x\n", d->rap,*data); } #endif } } /* BDP (BCR Data Port) access */ static void am79c971_bdp_access(cpu_gen_t *cpu,struct am79c971_data *d, u_int op_type,m_uint64_t *data) { u_int mii_phy,mii_reg; #if DEBUG_BCR_REGS if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to BCR %d\n",d->rap); } else { cpu_log(cpu,d->name,"write access to BCR %d, value=0x%x\n",d->rap,*data); } #endif switch(d->rap) { case 9: if (op_type == MTS_READ) *data = 1; break; /* BCR32: MII Control and Status Register case 32: */ case 34: /* BCR34: MII Management Data Register */ mii_phy = (d->bcr[33] >> 5) & 0x1F; mii_reg = (d->bcr[33] >> 0) & 0x1F; if (op_type == MTS_READ) *data = mii_reg_read(d,mii_phy,mii_reg); //else //mii_reg_write(d,mii_phy,mii_reg,*data); break; default: if (op_type == MTS_READ) { *data = d->bcr[d->rap]; } else { d->bcr[d->rap] = *data; } #if DEBUG_UNKNOWN if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to unknown BCR %d\n",d->rap); } else { cpu_log(cpu,d->name, "write access to unknown BCR %d, value=0x%x\n", d->rap,*data); } #endif } } /* * dev_am79c971_access() */ void *dev_am79c971_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct am79c971_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to offset=0x%x, pc=0x%llx, size=%u\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name,"write access to offset=0x%x, pc=0x%llx, " "val=0x%llx, size=%u\n",offset,cpu_get_pc(cpu),*data,op_size); } #endif AM79C971_LOCK(d); switch(offset) { /* RAP (Register Address Pointer) (DWIO=0) case 0x12: */ case 0x14: /* RAP (Register Address Pointer) (DWIO=1) */ if (op_type == MTS_WRITE) { d->rap = *data & 0xFF; } else { *data = d->rap; } break; case 0x10: /* RDP (Register Data Port) */ am79c971_rdp_access(cpu,d,op_type,data); break; /* BDP (BCR Data Port) (DWIO=0) case 0x16: */ case 0x1c: /* BDP (BCR Data Port) (DWIO=1) */ am79c971_bdp_access(cpu,d,op_type,data); break; } AM79C971_UNLOCK(d); return NULL; } /* Read a RX descriptor */ static int rxdesc_read(struct am79c971_data *d,m_uint32_t rxd_addr, struct rx_desc *rxd) { m_uint32_t buf[4]; m_uint8_t sw_style; /* Get the software style */ sw_style = d->bcr[20]; /* Read the descriptor from VM physical RAM */ physmem_copy_from_vm(d->vm,&buf,rxd_addr,sizeof(struct rx_desc)); switch(sw_style) { case 2: rxd->rmd[0] = vmtoh32(buf[0]); /* rb addr */ rxd->rmd[1] = vmtoh32(buf[1]); /* own flag, ... */ rxd->rmd[2] = vmtoh32(buf[2]); /* rfrtag, mcnt, ... */ rxd->rmd[3] = vmtoh32(buf[3]); /* user */ break; case 3: rxd->rmd[0] = vmtoh32(buf[2]); /* rb addr */ rxd->rmd[1] = vmtoh32(buf[1]); /* own flag, ... */ rxd->rmd[2] = vmtoh32(buf[0]); /* rfrtag, mcnt, ... */ rxd->rmd[3] = vmtoh32(buf[3]); /* user */ break; default: AM79C971_LOG(d,"invalid software style %u!\n",sw_style); return(-1); } return(0); } /* Set the address of the next RX descriptor */ static inline void rxdesc_set_next(struct am79c971_data *d) { d->rx_pos++; if (d->rx_pos == d->rx_len) d->rx_pos = 0; } /* Compute the address of the current RX descriptor */ static inline m_uint32_t rxdesc_get_current(struct am79c971_data *d) { return(d->rx_start + (d->rx_pos * sizeof(struct rx_desc))); } /* Put a packet in buffer of a descriptor */ static void rxdesc_put_pkt(struct am79c971_data *d,struct rx_desc *rxd, u_char **pkt,ssize_t *pkt_len) { ssize_t len,cp_len; /* Compute the data length to copy */ len = ~((rxd->rmd[1] & AM79C971_RMD1_LEN) - 1); len &= AM79C971_RMD1_LEN; cp_len = m_min(len,*pkt_len); /* Copy packet data to the VM physical RAM */ #if DEBUG_RECEIVE AM79C971_LOG(d,"am79c971_handle_rxring: storing %u bytes at 0x%8.8x\n", cp_len, rxd->rmd[0]); #endif physmem_copy_to_vm(d->vm,*pkt,rxd->rmd[0],cp_len); *pkt += cp_len; *pkt_len -= cp_len; } /* * Put a packet in the RX ring. */ static int am79c971_receive_pkt(struct am79c971_data *d, u_char *pkt,ssize_t pkt_len) { m_uint32_t rx_start,rx_current,rx_next,rxdn_rmd1; struct rx_desc rxd0,rxdn,*rxdc; ssize_t tot_len = pkt_len; u_char *pkt_ptr = pkt; m_uint8_t sw_style; int i; /* Truncate the packet if it is too big */ pkt_len = m_min(pkt_len,AM79C971_MAX_PKT_SIZE); /* Copy the current rxring descriptor */ rx_start = rx_current = rxdesc_get_current(d); rxdesc_read(d,rx_start,&rxd0); /* We must have the first descriptor... */ if (!(rxd0.rmd[1] & AM79C971_RMD1_OWN)) return(FALSE); for(i=0,rxdc=&rxd0;;i++) { #if DEBUG_RECEIVE AM79C971_LOG(d,"am79c971_handle_rxring: i=%d, addr=0x%8.8x: " "rmd[0]=0x%x, rmd[1]=0x%x, rmd[2]=0x%x, rmd[3]=0x%x\n", i,rx_current, rxdc->rmd[0],rxdc->rmd[1],rxdc->rmd[2],rxdc->rmd[3]); #endif /* Put data into the descriptor buffer */ rxdesc_put_pkt(d,rxdc,&pkt_ptr,&tot_len); /* Go to the next descriptor */ rxdesc_set_next(d); /* If this is not the first descriptor, clear the OWN bit */ if (i != 0) rxdc->rmd[1] &= ~AM79C971_RMD1_OWN; /* If we have finished, mark the descriptor as end of packet */ if (tot_len == 0) { rxdc->rmd[1] |= AM79C971_RMD1_ENP; physmem_copy_u32_to_vm(d->vm,rx_current+4,rxdc->rmd[1]); /* Get the software style */ sw_style = d->bcr[20]; /* Update the message byte count field */ rxdc->rmd[2] &= ~AM79C971_RMD2_LEN; rxdc->rmd[2] |= pkt_len + 4; switch(sw_style) { case 2: physmem_copy_u32_to_vm(d->vm,rx_current+8,rxdc->rmd[2]); break; case 3: physmem_copy_u32_to_vm(d->vm,rx_current,rxdc->rmd[2]); break; default: AM79C971_LOG(d,"invalid software style %u!\n",sw_style); } break; } /* Try to acquire the next descriptor */ rx_next = rxdesc_get_current(d); rxdn_rmd1 = physmem_copy_u32_from_vm(d->vm,rx_next+4); if (!(rxdn_rmd1 & AM79C971_RMD1_OWN)) { rxdc->rmd[1] |= AM79C971_RMD1_ERR | AM79C971_RMD1_BUFF; rxdc->rmd[1] |= AM79C971_RMD1_ENP; physmem_copy_u32_to_vm(d->vm,rx_current+4,rxdc->rmd[1]); break; } /* Update rmd1 to store change of OWN bit */ physmem_copy_u32_to_vm(d->vm,rx_current+4,rxdc->rmd[1]); /* Read the next descriptor from VM physical RAM */ rxdesc_read(d,rx_next,&rxdn); rxdc = &rxdn; rx_current = rx_next; } /* Update the first RX descriptor */ rxd0.rmd[1] &= ~AM79C971_RMD1_OWN; rxd0.rmd[1] |= AM79C971_RMD1_STP; physmem_copy_u32_to_vm(d->vm,rx_start+4,rxd0.rmd[1]); d->csr[0] |= AM79C971_CSR0_RINT; am79c971_update_irq_status(d); return(TRUE); } /* Handle the RX ring */ static int am79c971_handle_rxring(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, struct am79c971_data *d) { /* * Don't start receive if the RX ring address has not been set * and if RX ON is not set. */ if ((d->rx_start == 0) || !(d->csr[0] & AM79C971_CSR0_RXON)) return(FALSE); #if DEBUG_RECEIVE AM79C971_LOG(d,"receiving a packet of %d bytes\n",pkt_len); mem_dump(log_file,pkt,pkt_len); #endif AM79C971_LOCK(d); /* * Receive only multicast/broadcast trafic + unicast traffic * for this virtual machine. */ if (am79c971_handle_mac_addr(d,pkt)) am79c971_receive_pkt(d,pkt,pkt_len); AM79C971_UNLOCK(d); return(TRUE); } /* Read a TX descriptor */ static int txdesc_read(struct am79c971_data *d,m_uint32_t txd_addr, struct tx_desc *txd) { m_uint32_t buf[4]; m_uint8_t sw_style; /* Get the software style */ sw_style = d->bcr[20]; /* Read the descriptor from VM physical RAM */ physmem_copy_from_vm(d->vm,&buf,txd_addr,sizeof(struct tx_desc)); switch(sw_style) { case 2: txd->tmd[0] = vmtoh32(buf[0]); /* tb addr */ txd->tmd[1] = vmtoh32(buf[1]); /* own flag, ... */ txd->tmd[2] = vmtoh32(buf[2]); /* buff, uflo, ... */ txd->tmd[3] = vmtoh32(buf[3]); /* user */ break; case 3: txd->tmd[0] = vmtoh32(buf[2]); /* tb addr */ txd->tmd[1] = vmtoh32(buf[1]); /* own flag, ... */ txd->tmd[2] = vmtoh32(buf[0]); /* buff, uflo, ... */ txd->tmd[3] = vmtoh32(buf[3]); /* user */ break; default: AM79C971_LOG(d,"invalid software style %u!\n",sw_style); return(-1); } return(0); } /* Set the address of the next TX descriptor */ static inline void txdesc_set_next(struct am79c971_data *d) { d->tx_pos++; if (d->tx_pos == d->tx_len) d->tx_pos = 0; } /* Compute the address of the current TX descriptor */ static inline m_uint32_t txdesc_get_current(struct am79c971_data *d) { return(d->tx_start + (d->tx_pos * sizeof(struct tx_desc))); } /* Handle the TX ring (single packet) */ static int am79c971_handle_txring_single(struct am79c971_data *d) { u_char pkt[AM79C971_MAX_PKT_SIZE],*pkt_ptr; struct tx_desc txd0,ctxd,ntxd,*ptxd; m_uint32_t tx_start,tx_current; m_uint32_t clen,tot_len; if ((d->tx_start == 0) || !(d->csr[0] & AM79C971_CSR0_TXON)) return(FALSE); /* Check if the NIO can transmit */ if (!netio_can_transmit(d->nio)) return(FALSE); /* Copy the current txring descriptor */ tx_start = tx_current = txdesc_get_current(d); ptxd = &txd0; txdesc_read(d,tx_start,ptxd); /* If we don't own the first descriptor, we cannot transmit */ if (!(ptxd->tmd[1] & AM79C971_TMD1_OWN)) return(FALSE); #if DEBUG_TRANSMIT AM79C971_LOG(d,"am79c971_handle_txring: 1st desc: " "tmd[0]=0x%x, tmd[1]=0x%x, tmd[2]=0x%x, tmd[3]=0x%x\n", ptxd->tmd[0],ptxd->tmd[1],ptxd->tmd[2],ptxd->tmd[3]); #endif /* Empty packet for now */ pkt_ptr = pkt; tot_len = 0; for(;;) { #if DEBUG_TRANSMIT AM79C971_LOG(d,"am79c971_handle_txring: loop: " "tmd[0]=0x%x, tmd[1]=0x%x, tmd[2]=0x%x, tmd[3]=0x%x\n", ptxd->tmd[0],ptxd->tmd[1],ptxd->tmd[2],ptxd->tmd[3]); #endif /* Copy packet data */ clen = ~((ptxd->tmd[1] & AM79C971_TMD1_LEN) - 1); clen &= AM79C971_TMD1_LEN; physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->tmd[0],clen); pkt_ptr += clen; tot_len += clen; /* Clear the OWN bit if this is not the first descriptor */ if (!(ptxd->tmd[1] & AM79C971_TMD1_STP)) { ptxd->tmd[1] &= ~AM79C971_TMD1_OWN; physmem_copy_u32_to_vm(d->vm,tx_current+4,ptxd->tmd[1]); } /* Set the next descriptor */ txdesc_set_next(d); /* Stop now if end of packet has been reached */ if (ptxd->tmd[1] & AM79C971_TMD1_ENP) break; /* Read the next descriptor and try to acquire it */ tx_current = txdesc_get_current(d); txdesc_read(d,tx_current,&ntxd); if (!(ntxd.tmd[1] & AM79C971_TMD1_OWN)) { AM79C971_LOG(d,"am79c971_handle_txring: UNDERFLOW!\n"); return(FALSE); } memcpy(&ctxd,&ntxd,sizeof(struct tx_desc)); ptxd = &ctxd; } if (tot_len != 0) { #if DEBUG_TRANSMIT AM79C971_LOG(d,"sending packet of %u bytes\n",tot_len); mem_dump(log_file,pkt,tot_len); #endif /* rewrite ISL header if required */ cisco_isl_rewrite(pkt,tot_len); /* send it on wire */ netio_send(d->nio,pkt,tot_len); } /* Clear the OWN flag of the first descriptor */ txd0.tmd[1] &= ~AM79C971_TMD1_OWN; physmem_copy_u32_to_vm(d->vm,tx_start+4,txd0.tmd[1]); /* Generate TX interrupt */ d->csr[0] |= AM79C971_CSR0_TINT; am79c971_update_irq_status(d); return(TRUE); } /* Handle the TX ring */ static int am79c971_handle_txring(struct am79c971_data *d) { int i; AM79C971_LOCK(d); for(i=0;inio); AM79C971_UNLOCK(d); return(TRUE); } /* * pci_am79c971_read() * * Read a PCI register. */ static m_uint32_t pci_am79c971_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { struct am79c971_data *d = dev->priv_data; #if DEBUG_PCI_REGS AM79C971_LOG(d,"read PCI register 0x%x\n",reg); #endif switch (reg) { case 0x00: return((AM79C971_PCI_PRODUCT_ID << 16) | AM79C971_PCI_VENDOR_ID); /* Status, Command case 0x04: */ /* Base-Class, Sub-Class, Programming IF, Revision ID (read-only, should be 0x02000021?) */ case 0x08: return(0x02000002); /* Reserved, Header Type, Latency Timer, Reserved case 0x0C: */ /* I/O Base Address case 0x10: */ case PCI_REG_BAR1: return(d->dev->phys_addr); /* Subsystem ID Subsystem Vendor ID case 0x2C: */ /* Expansion ROM Base Address case 0x30: */ /* MAX_LAT, MIN_GNT, Interrupt Pin, Interrupt Line case 0x3C: */ default: return(0); } } /* * pci_am79c971_write() * * Write a PCI register. */ static void pci_am79c971_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct am79c971_data *d = dev->priv_data; #if DEBUG_PCI_REGS AM79C971_LOG(d,"write PCI register 0x%x, value 0x%x\n",reg,value); #endif switch(reg) { case PCI_REG_BAR1: vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); AM79C971_LOG(d,"registers are mapped at 0x%x\n",value); break; } } /* * dev_am79c971_init() * * Generic AMD Am79c971 initialization code. */ struct am79c971_data * dev_am79c971_init(vm_instance_t *vm,char *name,int interface_type, struct pci_bus *pci_bus,int pci_device,int irq) { struct am79c971_data *d; struct pci_device *pci_dev; struct vdevice *dev; /* Allocate the private data structure for AM79C971 */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"%s (AM79C971): out of memory\n",name); return NULL; } memset(d,0,sizeof(*d)); memcpy(d->mii_regs[0],mii_reg_values,sizeof(mii_reg_values)); pthread_mutex_init(&d->lock,NULL); /* Add as PCI device */ pci_dev = pci_dev_add(pci_bus,name, AM79C971_PCI_VENDOR_ID,AM79C971_PCI_PRODUCT_ID, pci_device,0,irq, d,NULL,pci_am79c971_read,pci_am79c971_write); if (!pci_dev) { fprintf(stderr,"%s (AM79C971): unable to create PCI device.\n",name); goto err_pci_dev; } /* Create the device itself */ if (!(dev = dev_create(name))) { fprintf(stderr,"%s (AM79C971): unable to create device.\n",name); goto err_dev; } d->name = name; d->vm = vm; d->type = interface_type; d->pci_dev = pci_dev; d->dev = dev; dev->phys_addr = 0; dev->phys_len = 0x4000; dev->handler = dev_am79c971_access; dev->priv_data = d; return(d); err_dev: pci_dev_remove(pci_dev); err_pci_dev: free(d); return NULL; } /* Remove an AMD Am79c971 device */ void dev_am79c971_remove(struct am79c971_data *d) { if (d != NULL) { pci_dev_remove(d->pci_dev); vm_unbind_device(d->vm,d->dev); cpu_group_rebuild_mts(d->vm->cpu_group); free(d->dev); free(d); } } /* Bind a NIO to an AMD Am79c971 device */ int dev_am79c971_set_nio(struct am79c971_data *d,netio_desc_t *nio) { /* check that a NIO is not already bound */ if (d->nio != NULL) return(-1); d->nio = nio; d->tx_tid = ptask_add((ptask_callback)am79c971_handle_txring,d,NULL); netio_rxl_add(nio,(netio_rx_handler_t)am79c971_handle_rxring,d,NULL); return(0); } /* Unbind a NIO from an AMD Am79c971 device */ void dev_am79c971_unset_nio(struct am79c971_data *d) { if (d->nio != NULL) { ptask_remove(d->tx_tid); netio_rxl_remove(d->nio); d->nio = NULL; } } dynamips-0.2.14/common/dev_am79c971.h000066400000000000000000000016231241034141600171000ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __DEV_AM79C971_H__ #define __DEV_AM79C971_H__ #include #include "utils.h" #include "cpu.h" #include "vm.h" #include "device.h" #include "net_io.h" /* Interface type */ #define AM79C971_TYPE_100BASE_TX 1 /* 100baseTX */ #define AM79C971_TYPE_10BASE_T 2 /* 10baseT */ /* Generic AMD Am79c971 initialization code */ struct am79c971_data * dev_am79c971_init(vm_instance_t *vm,char *name,int interface_type, struct pci_bus *pci_bus,int pci_device,int irq); /* Remove an AMD Am79c971 device */ void dev_am79c971_remove(struct am79c971_data *d); /* Bind a NIO to an AMD Am79c971 device */ int dev_am79c971_set_nio(struct am79c971_data *d,netio_desc_t *nio); /* Unbind a NIO from an AMD Am79c971 device */ void dev_am79c971_unset_nio(struct am79c971_data *d); #endif dynamips-0.2.14/common/dev_ap1011.c000066400000000000000000000024531241034141600166170ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot. All rights reserved. * * AP1011 - Sturgeon HyperTransport-PCI Bridge. */ #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #define AP1011_PCI_VENDOR_ID 0x14D9 #define AP1011_PCI_PRODUCT_ID 0x0010 /* * pci_ap1011_read() * * Read a PCI register. */ static m_uint32_t pci_ap1011_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { switch (reg) { case 0x08: return(0x06040000); case 0x34: return(0x00000040); case 0x40: return(0x00210008); case 0x44: return(0x00000020); case 0x48: return(0x000000C0); default: return(0); } } /* Create an AP1011 Sturgeon HyperTransport-PCI Bridge */ int dev_ap1011_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus) { struct pci_device *dev; dev = pci_bridge_create_dev(pci_bus,"ap1011", AP1011_PCI_VENDOR_ID,AP1011_PCI_PRODUCT_ID, pci_device,0,sec_bus,pci_ap1011_read,NULL); return((dev != NULL) ? 0 : -1); } dynamips-0.2.14/common/dev_bootflash.c000066400000000000000000000325471241034141600177040ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot. All rights reserved. * * Intel Flash SIMM emulation. * * Intelligent ID Codes: * 28F008SA: 0x89A2 (1 Mb) * 28F016SA: 0x89A0 (2 Mb) * * Manuals: * http://www.ortodoxism.ro/datasheets/Intel/mXvsysv.pdf * * TODO: A lot of commands are lacking. Doesn't work with NPE-G2. */ #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #define DEBUG_ACCESS 0 #define DEBUG_WRITE 0 /* Flash command states */ enum { FLASH_CMD_READ_ARRAY = 0, FLASH_CMD_READ_ID, FLASH_CMD_READ_QUERY, FLASH_CMD_READ_STATUS, FLASH_CMD_WRITE_BUF_CNT, FLASH_CMD_WRITE_BUF_DATA, FLASH_CMD_WRITE_BUF_CONFIRM, FLASH_CMD_WB_PROG, FLASH_CMD_WB_PROG_DONE, FLASH_CMD_BLK_ERASE, FLASH_CMD_BLK_ERASE_DONE, FLASH_CMD_CONFIG, }; /* Flash access mode (byte or word) */ enum { FLASH_MODE_BYTE = 1, FLASH_MODE_WORD = 2, }; #define MAX_FLASH 4 #define FLASH_BUF_SIZE 32 /* Forward declarations */ struct flash_data; struct flashset_data; /* Flash model */ struct flash_model { char *name; u_int total_size; u_int mode; u_int nr_flash_bits; u_int blk_size; u_int id_manufacturer; u_int id_device; }; /* Flash internal data */ struct flash_data { u_int mode,offset_shift,state,blk_size; m_uint8_t id_manufacturer,id_device; m_uint8_t status_reg; struct flashset_data *flash_set; u_int flash_pos; /* Write buffer */ u_int wb_offset,wb_count,wb_remain; u_int wbuf[FLASH_BUF_SIZE]; }; /* Flashset private data */ struct flashset_data { vm_instance_t *vm; vm_obj_t vm_obj; struct vdevice dev; char *filename; u_int mode; u_int nr_flash_bits; u_int nr_flash_count; struct flash_data flash[MAX_FLASH]; }; /* Log a Flash message */ #define FLASH_LOG(d,msg...) vm_log((d)->flash_set->vm, \ (d)->flash_set->dev.name, \ msg) #define BPTR(d,offset) (((u_char *)(d)->dev.host_addr) + offset) /* Some Flash models */ static struct flash_model flash_models[] = { /* C1700 4 Mb bootflash: 1x28F320 in word mode */ { "c1700-bootflash-4mb",4 * 1048576,FLASH_MODE_WORD,0,0x10000,0x89,0x14 }, /* C1700 8 Mb bootflash: 1x28F640 in word mode */ { "c1700-bootflash-8mb",8 * 1048576,FLASH_MODE_WORD,0,0x10000,0x89,0x15 }, /* C3600 8 Mb bootflash: 4x28F016SA in byte mode */ { "c3600-bootflash-8mb",8 * 1048576,FLASH_MODE_BYTE,2,0x10000,0x89,0xA0 }, /* C7200 4 Mb bootflash: 4x28F008SA in byte mode */ { "c7200-bootflash-4mb",4 * 1048576,FLASH_MODE_BYTE,2,0x10000,0x89,0xA2 }, /* C7200 8 Mb bootflash: 4x28F016SA in byte mode */ { "c7200-bootflash-8mb",8 * 1048576,FLASH_MODE_BYTE,2,0x10000,0x89,0xA0 }, /* * C7200 64 Mb bootflash: 4x128 Mb Intel flash in byte mode * (for NPE-G2 but doesn't work now). */ { "c7200-bootflash-64mb",64 * 1048576,FLASH_MODE_BYTE,2,0x10000,0x89,0x18 }, /* C2600 8 Mb bootflash: 4x28F016SA in byte mode */ { "c2600-bootflash-8mb",8 * 1048576,FLASH_MODE_BYTE,2,0x10000,0x89,0xA0 }, { NULL, 0, 0, 0, 0, 0 }, }; /* Flash model lookup */ static struct flash_model *flash_model_find(char *name) { struct flash_model *fm; for(fm=&flash_models[0];fm->name!=NULL;fm++) if (!strcmp(fm->name,name)) return fm; return NULL; } /* Initialize a flashset */ static int flashset_init(struct flashset_data *d, u_int mode,u_int nr_flash_bits,u_int blk_size, m_uint8_t id_manufacturer,m_uint8_t id_device) { struct flash_data *flash; u_int i,offset_shift; d->mode = mode; d->nr_flash_bits = nr_flash_bits; d->nr_flash_count = 1 << d->nr_flash_bits; switch(mode) { case FLASH_MODE_BYTE: offset_shift = 0; break; case FLASH_MODE_WORD: offset_shift = 1; break; default: return(-1); } for(i=0;inr_flash_count;i++) { flash = &d->flash[i]; flash->mode = mode; flash->offset_shift = offset_shift; flash->state = FLASH_CMD_READ_ARRAY; flash->id_manufacturer = id_manufacturer; flash->id_device = id_device; flash->flash_set = d; flash->flash_pos = i; flash->blk_size = blk_size; } return(0); } /* Read a byte from a Flash */ static int flash_read(struct flash_data *d,u_int offset,u_int *data) { u_int real_offset; real_offset = (offset << (d->flash_set->nr_flash_bits)) + d->flash_pos; if (d->mode == FLASH_MODE_BYTE) { *data = *BPTR(d->flash_set,real_offset); } else { *data = *BPTR(d->flash_set,(real_offset << 1)) << 8; *data |= *BPTR(d->flash_set,(real_offset << 1)+1); } return(0); } /* Write a byte to a Flash */ static int flash_write(struct flash_data *d,u_int offset,u_int data) { u_int real_offset; real_offset = (offset << (d->flash_set->nr_flash_bits)) + d->flash_pos; if (d->mode == FLASH_MODE_BYTE) { *BPTR(d->flash_set,real_offset) = data; } else { *BPTR(d->flash_set,(real_offset << 1)) = data >> 8; *BPTR(d->flash_set,(real_offset << 1)+1) = data & 0xFF; } return(0); } /* Set machine state given a command */ static void flash_cmd(struct flash_data *d,u_int offset,u_int cmd) { cmd = cmd & 0xFF; switch(cmd) { case 0x40: case 0x10: d->state = FLASH_CMD_WB_PROG; break; case 0xe8: d->state = FLASH_CMD_WRITE_BUF_CNT; d->wb_offset = offset; d->wb_count = d->wb_remain = 0; break; case 0x70: d->state = FLASH_CMD_READ_STATUS; break; case 0x50: d->status_reg = 0; d->state = FLASH_CMD_READ_ARRAY; break; case 0x90: d->state = FLASH_CMD_READ_ID; break; case 0x20: d->state = FLASH_CMD_BLK_ERASE; break; case 0xff: d->state = FLASH_CMD_READ_ARRAY; break; default: FLASH_LOG(d,"flash_cmd(%u): command 0x%2.2x not implemented\n", d->flash_pos,(u_int)cmd); } } /* Generic Flash access */ static void flash_access(struct flash_data *d,m_uint32_t offset,u_int op_type, u_int *data) { u_int i; if (op_type == MTS_READ) *data = 0x00; #if DEBUG_ACCESS if (op_type == MTS_READ) { FLASH_LOG(d,"flash_access(%u): read access to offset 0x%8.8x " "(state=%u)\n",d->flash_pos,offset,d->state); } else { FLASH_LOG(d,"flash_access(%u): write access to offset 0x%8.8x, " "data=0x%4.4x (state=%u)\n", d->flash_pos,offset,*data,d->state); } #endif offset >>= d->offset_shift; /* State machine for Flash commands */ switch(d->state) { case FLASH_CMD_READ_ARRAY: if (op_type == MTS_READ) { flash_read(d,offset,data); return; } /* Command Write */ flash_cmd(d,offset,*data); break; /* Write byte/word */ case FLASH_CMD_WB_PROG: if (op_type == MTS_WRITE) { flash_write(d,offset,*data); d->state = FLASH_CMD_WB_PROG_DONE; } break; /* Write byte/word (done) */ case FLASH_CMD_WB_PROG_DONE: if (op_type == MTS_WRITE) { flash_cmd(d,offset,*data); } else { *data = 0x80; } break; /* Write buffer (count) */ case FLASH_CMD_WRITE_BUF_CNT: if (op_type == MTS_WRITE) { d->wb_count = d->wb_remain = (*data & 0x1F) + 1; d->state = FLASH_CMD_WRITE_BUF_DATA; } else { *data = 0x80; } break; /* Write buffer (data) */ case FLASH_CMD_WRITE_BUF_DATA: if (op_type == MTS_WRITE) { if ((offset >= d->wb_offset) && (offset < (d->wb_offset + d->wb_count))) { d->wbuf[offset - d->wb_offset] = *data; d->wb_remain--; if (!d->wb_remain) d->state = FLASH_CMD_WRITE_BUF_CONFIRM; } } else { *data = 0x80; } break; /* Write buffer (confirm) */ case FLASH_CMD_WRITE_BUF_CONFIRM: if (op_type == MTS_WRITE) { if ((*data & 0xFF) == 0xD0) { for(i=0;iwb_count;i++) flash_write(d,d->wb_offset+i,d->wbuf[i]); } else { /* XXX Error */ } d->state = FLASH_CMD_READ_ARRAY; } else { *data = 0x80; } break; /* Read status register */ case FLASH_CMD_READ_STATUS: if (op_type == MTS_READ) *data = 0x80; //d->status_reg; d->state = FLASH_CMD_READ_ARRAY; break; /* Read identifier codes */ case FLASH_CMD_READ_ID: if (op_type == MTS_READ) { switch(offset) { case 0x00: *data = d->id_manufacturer; break; case 0x01: *data = d->id_device; break; default: *data = 0x00; break; } } else { flash_cmd(d,offset,*data); } break; /* Block Erase */ case FLASH_CMD_BLK_ERASE: if (op_type == MTS_WRITE) { #if DEBUG_WRITE FLASH_LOG(d,"flash_access(%u): erasing block at offset 0x%8.8x\n" offset); #endif if ((*data & 0xFF) == 0xD0) { for(i=0;iblk_size;i++) flash_write(d,offset+i,0xFFFF); d->state = FLASH_CMD_BLK_ERASE_DONE; } } else { *data = 0x80; } break; /* Block Erase Done */ case FLASH_CMD_BLK_ERASE_DONE: if (op_type == MTS_WRITE) { flash_cmd(d,offset,*data); } else { *data = 0x80; } break; } } /* * dev_bootflash_access() */ void *dev_bootflash_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct flashset_data *d = dev->priv_data; u_int flash_data[8]; u_int i,fi,d_off; #if DEBUG_ACCESS if (op_type == MTS_READ) cpu_log(cpu,dev->name,"read access to offset = 0x%x, pc = 0x%llx\n", offset,cpu_get_pc(cpu)); else cpu_log(cpu,dev->name,"write access to vaddr = 0x%x, pc = 0x%llx, " "val = 0x%llx\n",offset,cpu_get_pc(cpu),*data); #endif if (op_type == MTS_READ) { *data = 0; for(i=0;imode) { fi = (offset+i) & (d->nr_flash_count-1); flash_access(&d->flash[fi],((offset+i) >> d->nr_flash_bits),op_type, &flash_data[i]); d_off = (op_size - i - d->mode) << 3; *data |= (m_uint64_t)flash_data[i] << d_off; } } else { for(i=0;imode) { fi = (offset+i) & (d->nr_flash_count-1); d_off = (op_size - i - d->mode) << 3; flash_data[i] = *data >> d_off; flash_access(&d->flash[fi],((offset+i) >> d->nr_flash_bits),op_type, &flash_data[i]); } } return NULL; } /* Shutdown a bootflash device */ void dev_bootflash_shutdown(vm_instance_t *vm,struct flashset_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* We don't remove the file, since it used as permanent storage */ if (d->filename) free(d->filename); /* Free the structure itself */ free(d); } } /* Create a 8 Mb bootflash */ int dev_bootflash_init(vm_instance_t *vm,char *name,char *model, m_uint64_t paddr) { struct flash_model *fm; struct flashset_data *d; u_char *ptr; /* Find the flash model */ if (!(fm = flash_model_find(model))) { vm_error(vm,"bootflash: unable to find model '%s'\n",model); return(-1); } /* Allocate the private data structure */ if (!(d = malloc(sizeof(*d)))) { vm_error(vm,"bootflash: unable to create device.\n"); return(-1); } memset(d,0,sizeof(*d)); d->vm = vm; /* Initialize flash based on model properties */ flashset_init(d,fm->mode,fm->nr_flash_bits,fm->blk_size, fm->id_manufacturer,fm->id_device); vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_bootflash_shutdown; if (!(d->filename = vm_build_filename(vm,name))) { vm_error(vm,"bootflash: unable to create filename.\n"); goto err_filename; } dev_init(&d->dev); d->dev.name = name; d->dev.priv_data = d; d->dev.phys_addr = paddr; d->dev.phys_len = fm->total_size; d->dev.handler = dev_bootflash_access; d->dev.fd = memzone_create_file(d->filename,d->dev.phys_len,&ptr); d->dev.host_addr = (m_iptr_t)ptr; d->dev.flags = VDEVICE_FLAG_NO_MTS_MMAP; if (d->dev.fd == -1) { vm_error(vm,"bootflash: unable to map file '%s'\n",d->filename); goto err_fd_create; } /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); err_fd_create: free(d->filename); err_filename: free(d); return(-1); } dynamips-0.2.14/common/dev_bswap.c000066400000000000000000000051171241034141600170300ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * Byte-swapping device. */ #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" struct bswap_data { /* VM object info */ vm_obj_t vm_obj; /* VM instance */ vm_instance_t *vm; /* Byte-swap device */ struct vdevice dev; /* Physical address base for rewrite */ m_uint64_t phys_base; }; /* * Byte swapped access. */ static void *dev_bswap_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct bswap_data *d = dev->priv_data; m_uint64_t paddr; paddr = d->phys_base + offset; switch(op_size) { case 1: if (op_type == MTS_READ) *data = physmem_copy_u8_from_vm(d->vm,paddr ^ 0x03); else physmem_copy_u8_to_vm(d->vm,paddr ^ 0x03,*data); break; case 2: if (op_type == MTS_READ) *data = swap16(physmem_copy_u16_from_vm(d->vm,paddr ^ 0x02)); else physmem_copy_u16_to_vm(d->vm,paddr ^ 0x02,swap16(*data)); break; case 4: if (op_type == MTS_READ) *data = swap32(physmem_copy_u32_from_vm(d->vm,paddr)); else physmem_copy_u32_to_vm(d->vm,paddr,swap32(*data)); break; } return NULL; } /* Shutdown an byte-swap device */ void dev_bswap_shutdown(vm_instance_t *vm,struct bswap_data *d) { if (d != NULL) { /* Remove the alias, the byte-swapped and the main device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* Initialized a byte-swap device */ int dev_bswap_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len, m_uint64_t remap_addr) { struct bswap_data *d; if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"BSWAP: unable to create device.\n"); return(-1); } vm_object_init(&d->vm_obj); d->vm = vm; d->phys_base = remap_addr; d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_bswap_shutdown; dev_init(&d->dev); d->dev.name = name; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_bswap_access; d->dev.priv_data = d; /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_c1700.c000066400000000000000000000532261241034141600164520ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * Patched by Jeremy Grossmann for the GNS3 project (www.gns3.net) * * Generic Cisco 1700 routines and definitions (EEPROM,...). */ #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "ppc32_mem.h" #include "pci_io.h" #include "cisco_eeprom.h" #include "dev_mpc860.h" #include "dev_rom.h" #include "dev_c1700.h" #include "dev_c1700_iofpga.h" #include "dev_vtty.h" #include "registry.h" #include "fs_nvram.h" /* ======================================================================== */ /* EEPROM definitions */ /* ======================================================================== */ /* Cisco 1700 mainboard EEPROM */ static m_uint16_t eeprom_c1700_mb_data[] = { 0x0101, 0x0404, 0x0000, 0x0000, 0x4320, 0x00FF, 0x00B2, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x4654, 0x5809, 0x4557, 0x304D, 0x5902, 0x0200, 0x0000, 0x0000, 0x00FF, 0xFFFF, 0x5006, 0x490B, 0x1709, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; struct c1700_mb_id { char *name; char *mb_driver; m_uint16_t id; int supported; }; struct c1700_mb_id c1700_mainboard_id[] = { { "1710" , "C1710-MB-1FE-1E" , 0x02F6, TRUE }, { "1720" , "C1700-MB-1ETH" , 0x00B2, TRUE }, { "1721" , "C1700-MB-1ETH" , 0x035A, TRUE }, { "1750" , "C1700-MB-1ETH" , 0x00C9, TRUE }, { "1751" , "C1700-MB-1ETH" , 0x024D, TRUE }, { "1760" , "C1700-MB-1ETH" , 0x0316, TRUE }, { NULL , NULL , 0x0000, 0 }, }; /* ======================================================================== */ /* Network Module Drivers */ /* ======================================================================== */ static struct cisco_card_driver *nm_drivers[] = { &dev_c1700_mb_eth_driver, &dev_c1710_mb_eth_driver, NULL, }; /* ======================================================================== */ /* Cisco 1700 router instances */ /* ======================================================================== */ /* Initialize default parameters for a C1700 */ static void c1700_init_defaults(c1700_t *router); /* Directly extract the configuration from the NVRAM device */ static int c1700_nvram_extract_config(vm_instance_t *vm,u_char **startup_config,size_t *startup_len,u_char **private_config,size_t *private_len) { int ret; ret = generic_nvram_extract_config(vm, "nvram", vm->nvram_rom_space, 0, 0, FS_NVRAM_FORMAT_DEFAULT, startup_config, startup_len, private_config, private_len); return(ret); } /* Directly push the IOS configuration to the NVRAM device */ static int c1700_nvram_push_config(vm_instance_t *vm,u_char *startup_config,size_t startup_len,u_char *private_config,size_t private_len) { int ret; ret = generic_nvram_push_config(vm, "nvram", vm->nvram_size*1024, vm->nvram_rom_space, 0, 0, FS_NVRAM_FORMAT_DEFAULT, startup_config, startup_len, private_config, private_len); return(ret); } /* Create a new router instance */ static int c1700_create_instance(vm_instance_t *vm) { c1700_t *router; if (!(router = malloc(sizeof(*router)))) { fprintf(stderr,"C1700 '%s': Unable to create new instance!\n",vm->name); return(-1); } memset(router,0,sizeof(*router)); router->vm = vm; vm->hw_data = router; c1700_init_defaults(router); return(0); } /* Free resources used by a router instance */ static int c1700_delete_instance(vm_instance_t *vm) { c1700_t *router = VM_C1700(vm); int i; /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(FALSE); } } /* Remove NIO bindings */ for(i=0;inr_slots;i++) vm_slot_remove_all_nio_bindings(vm,i); /* Shutdown all Network Modules */ vm_slot_shutdown_all(vm); /* Free mainboard EEPROM */ cisco_eeprom_free(&router->mb_eeprom); /* Free all resources used by VM */ vm_free(vm); /* Free the router structure */ free(router); return(TRUE); } /* Save configuration of a C1700 instance */ void c1700_save_config(vm_instance_t *vm,FILE *fd) { c1700_t *router = VM_C1700(vm); fprintf(fd,"c1700 set_chassis %s %s\n\n",vm->name,router->mainboard_type); } /* Get WIC device address for the specified onboard port */ int c1700_get_onboard_wic_addr(u_int slot,m_uint64_t *phys_addr) { if (slot >= C1700_MAX_WIC_BAYS) return(-1); *phys_addr = C1700_WIC_ADDR + (slot * C1700_WIC_SIZE); return(0); } /* Set EEPROM for the specified slot */ int c1700_set_slot_eeprom(c1700_t *router,u_int slot, struct cisco_eeprom *eeprom) { switch(slot) { case 1: router->nm_eeprom_group.eeprom[0] = eeprom; return(0); default: return(-1); } } /* Get slot/port corresponding to specified network IRQ */ static inline void c1700_net_irq_get_slot_port(u_int irq,u_int *slot,u_int *port) { irq -= C1700_NETIO_IRQ_BASE; *port = irq & C1700_NETIO_IRQ_PORT_MASK; *slot = irq >> C1700_NETIO_IRQ_PORT_BITS; } /* Get network IRQ for specified slot/port */ u_int c1700_net_irq_for_slot_port(u_int slot,u_int port) { u_int irq; irq = (slot << C1700_NETIO_IRQ_PORT_BITS) + port; irq += C1700_NETIO_IRQ_BASE; return(irq); } /* Find Cisco 1700 Mainboard info */ static struct c1700_mb_id *c1700_get_mb_info(char *mainboard_type) { int i; for(i=0;c1700_mainboard_id[i].name;i++) if (!strcmp(c1700_mainboard_id[i].name,mainboard_type)) return(&c1700_mainboard_id[i]); return NULL; } /* Show all available mainboards */ static void c1700_mainboard_show_drivers(void) { int i; printf("Available C1700 chassis drivers:\n"); for(i=0;c1700_mainboard_id[i].name;i++) printf(" * %s %s\n", c1700_mainboard_id[i].name, !c1700_mainboard_id[i].supported ? "(NOT WORKING)" : ""); printf("\n"); } /* Set the base MAC address of the chassis */ static int c1700_burn_mac_addr(c1700_t *router,n_eth_addr_t *addr) { int i; for(i=0;i<3;i++) { router->vm->chassis_cookie[i+1] = addr->eth_addr_byte[i*2] << 8; router->vm->chassis_cookie[i+1] |= addr->eth_addr_byte[(i*2)+1]; } return(0); } /* Set mainboard type */ int c1700_mainboard_set_type(c1700_t *router,char *mainboard_type) { struct c1700_mb_id *mb_info; if (router->vm->status == VM_STATUS_RUNNING) { vm_error(router->vm,"unable to change mainboard type when online.\n"); return(-1); } if (!(mb_info = c1700_get_mb_info(mainboard_type))) { vm_error(router->vm,"unknown mainboard '%s'\n",mainboard_type); return(-1); } router->mainboard_type = mainboard_type; /* Set the cookie */ memcpy(router->vm->chassis_cookie, eeprom_c1700_mb_data,sizeof(eeprom_c1700_mb_data)); router->vm->chassis_cookie[6] = mb_info->id; /* Set the chassis base MAC address */ c1700_burn_mac_addr(router,&router->mac_addr); /* Set the mainboard driver */ if (vm_slot_active(router->vm,0,0)) vm_slot_remove_binding(router->vm,0,0); vm_slot_add_binding(router->vm,mb_info->mb_driver,0,0); c1700_refresh_systemid(router); return(0); } /* Set the system id or processor board id in the eeprom */ int c1700_set_system_id(c1700_t *router,char *id) { /* 11 characters is enough. Array is 20 long */ strncpy(router->board_id,id,13); /* Make sure it is null terminated */ router->board_id[13] = 0x00; c1700_refresh_systemid(router); return 0; } int c1700_refresh_systemid(c1700_t *router) { if (router->board_id[0] == 0x00) return(0); m_uint8_t buf[11]; parse_board_id(buf,router->board_id,9); // Does not use the cisco_eeprom libraries.. do it by hand // cisco_eeprom_set_region(&router->mb_eeprom ,24,buf,9); int i; for(i=0;i<4;i++) { router->vm->chassis_cookie[i+12] = buf[i*2] << 8; router->vm->chassis_cookie[i+12] |= buf[(i*2)+1]; } router->vm->chassis_cookie[i+12] &= 0x00ff; router->vm->chassis_cookie[i+12] |= buf[(i*2)] << 8; return (0); } /* Set chassis MAC address */ int c1700_chassis_set_mac_addr(c1700_t *router,char *mac_addr) { if (parse_mac_addr(&router->mac_addr,mac_addr) == -1) { vm_error(router->vm,"unable to parse MAC address '%s'.\n",mac_addr); return(-1); } /* Set the chassis base MAC address */ c1700_burn_mac_addr(router,&router->mac_addr); return(0); } /* Initialize a Cisco 1700 */ static int c1700_init(c1700_t *router) { vm_instance_t *vm = router->vm; /* Create the PCI bus */ if (!(vm->pci_bus[0] = pci_bus_create("PCI0",0))) { vm_error(vm,"unable to create PCI data.\n"); return(-1); } /* Bind PCI bus to slots 0 and 1 */ vm->slots_pci_bus[0] = vm->pci_bus[0]; vm->slots_pci_bus[1] = vm->pci_bus[0]; vm->elf_machine_id = C1700_ELF_MACHINE_ID; return(0); } /* Show C1700 hardware info */ void c1700_show_hardware(c1700_t *router) { vm_instance_t *vm = router->vm; printf("C1700 instance '%s' (id %d):\n",vm->name,vm->instance_id); printf(" VM Status : %d\n",vm->status); printf(" RAM size : %u Mb\n",vm->ram_size); printf(" NVRAM size : %u Kb\n",vm->nvram_size); printf(" IOS image : %s\n\n",vm->ios_image); if (vm->debug_level > 0) { dev_show_list(vm); pci_dev_show_list(vm->pci_bus[0]); pci_dev_show_list(vm->pci_bus[1]); printf("\n"); } } /* Initialize default parameters for a C1700 */ static void c1700_init_defaults(c1700_t *router) { vm_instance_t *vm = router->vm; n_eth_addr_t *m; m_uint16_t pid; /* Set platform slots characteristics */ vm->nr_slots = C1700_MAX_NM_BAYS; vm->slots_type = CISCO_CARD_TYPE_NM; vm->slots_drivers = nm_drivers; pid = (m_uint16_t)getpid(); /* Generate a chassis MAC address based on the instance ID */ m = &router->mac_addr; m->eth_addr_byte[0] = vm_get_mac_addr_msb(vm); m->eth_addr_byte[1] = vm->instance_id & 0xFF; m->eth_addr_byte[2] = pid >> 8; m->eth_addr_byte[3] = pid & 0xFF; m->eth_addr_byte[4] = 0x00; m->eth_addr_byte[5] = 0x00; router->board_id[0] = 0x00; c1700_init_eeprom_groups(router); c1700_mainboard_set_type(router,C1700_DEFAULT_MAINBOARD); c1700_burn_mac_addr(router,&router->mac_addr); vm->ram_mmap = C1700_DEFAULT_RAM_MMAP; vm->ram_size = C1700_DEFAULT_RAM_SIZE; vm->rom_size = C1700_DEFAULT_ROM_SIZE; vm->nvram_size = C1700_DEFAULT_NVRAM_SIZE; vm->conf_reg_setup = C1700_DEFAULT_CONF_REG; vm->clock_divisor = C1700_DEFAULT_CLOCK_DIV; vm->nvram_rom_space = C1700_NVRAM_ROM_RES_SIZE; vm->nm_iomem_size = C1700_DEFAULT_IOMEM_SIZE; vm->pcmcia_disk_size[0] = C1700_DEFAULT_DISK0_SIZE; vm->pcmcia_disk_size[1] = C1700_DEFAULT_DISK1_SIZE; } /* Set an IRQ */ static void c1700_set_irq(vm_instance_t *vm,u_int irq) { c1700_t *router = VM_C1700(vm); cpu_ppc_t *cpu = CPU_PPC32(vm->boot_cpu); u_int slot,port; switch(irq) { case C1700_VTIMER_IRQ: mpc860_set_pending_irq(router->mpc_data,30); break; case C1700_DUART_IRQ: mpc860_set_pending_irq(router->mpc_data,29); break; case C1700_NETIO_IRQ: mpc860_set_pending_irq(router->mpc_data,25); break; case C1700_PA_MGMT_IRQ: mpc860_set_pending_irq(router->mpc_data,27); break; case C1700_NETIO_IRQ_BASE ... C1700_NETIO_IRQ_END: c1700_net_irq_get_slot_port(irq,&slot,&port); dev_c1700_iofpga_net_set_irq(router->iofpga_data,slot,port); break; /* IRQ test */ case 255: mpc860_set_pending_irq(router->mpc_data,24); break; } if (vm->irq_idle_preempt[irq]) cpu_idle_break_wait(cpu->gen); } /* Clear an IRQ */ static void c1700_clear_irq(vm_instance_t *vm,u_int irq) { c1700_t *router = VM_C1700(vm); u_int slot,port; switch(irq) { case C1700_VTIMER_IRQ: mpc860_clear_pending_irq(router->mpc_data,30); break; case C1700_DUART_IRQ: mpc860_clear_pending_irq(router->mpc_data,29); break; case C1700_NETIO_IRQ: mpc860_clear_pending_irq(router->mpc_data,25); break; case C1700_PA_MGMT_IRQ: mpc860_clear_pending_irq(router->mpc_data,27); break; case C1700_NETIO_IRQ_BASE ... C1700_NETIO_IRQ_END: c1700_net_irq_get_slot_port(irq,&slot,&port); dev_c1700_iofpga_net_clear_irq(router->iofpga_data,slot,port); break; /* IRQ test */ case 255: mpc860_clear_pending_irq(router->mpc_data,24); break; } } /* Initialize the C1700 Platform */ static int c1700_init_platform(c1700_t *router) { vm_instance_t *vm = router->vm; vm_obj_t *obj; cpu_ppc_t *cpu; cpu_gen_t *gen; /* Copy config register setup into "active" config register */ vm->conf_reg = vm->conf_reg_setup; /* Create Console and AUX ports */ vm_init_vtty(vm); /* Create a CPU group */ vm->cpu_group = cpu_group_create("System CPU"); /* Initialize the virtual PowerPC processor */ if (!(gen = cpu_create(vm,CPU_TYPE_PPC32,0))) { vm_error(vm,"unable to create CPU!\n"); return(-1); } cpu = CPU_PPC32(gen); /* Add this CPU to the system CPU group */ cpu_group_add(vm->cpu_group,gen); vm->boot_cpu = gen; /* Set processor ID */ ppc32_set_pvr(cpu,0x00500202); /* Mark the Network IO interrupt as high priority */ vm->irq_idle_preempt[C1700_NETIO_IRQ] = TRUE; vm->irq_idle_preempt[C1700_DUART_IRQ] = TRUE; /* Copy some parameters from VM to CPU (idle PC, ...) */ cpu->idle_pc = vm->idle_pc; if (vm->timer_irq_check_itv) cpu->timer_irq_check_itv = vm->timer_irq_check_itv; /* Remote emulator control */ dev_remote_control_init(vm,0xf6000000,0x1000); /* MPC860 */ cpu->mpc860_immr = C1700_MPC860_ADDR; if (dev_mpc860_init(vm,"MPC860",C1700_MPC860_ADDR,0x10000) == -1) return(-1); if (!(obj = vm_object_find(router->vm,"MPC860"))) return(-1); router->mpc_data = obj->data; /* IO FPGA */ if (dev_c1700_iofpga_init(router,C1700_IOFPGA_ADDR,0x10000) == -1) return(-1); if (!(obj = vm_object_find(router->vm,"io_fpga"))) return(-1); router->iofpga_data = obj->data; /* Initialize the chassis */ if (c1700_init(router) == -1) return(-1); /* Initialize RAM */ vm_ram_init(vm,0x00000000ULL); /* Initialize ROM */ if (!vm->rom_filename) { /* use embedded ROM */ dev_rom_init(vm,"rom",C1700_ROM_ADDR,512*1024, ppc32_microcode,ppc32_microcode_len); } else { /* use alternate ROM */ dev_ram_init(vm,"rom",TRUE,TRUE,NULL,FALSE,C1700_ROM_ADDR,512*1024); } /* RAM aliasing */ dev_create_ram_alias(vm,"ram_alias","ram",0x80000000,vm->ram_size*1048576); /* NVRAM */ dev_nvram_init(vm,"nvram", C1700_NVRAM_ADDR,vm->nvram_size*1024,&vm->conf_reg); /* Bootflash (4 Mb) */ dev_bootflash_init(vm,"bootflash","c1700-bootflash-4mb",C1700_FLASH_ADDR); /* Initialize the NS16552 DUART */ dev_ns16552_init(vm,C1700_DUART_ADDR,0x1000,0,C1700_DUART_IRQ, vm->vtty_con,vm->vtty_aux); /* Initialize Network Modules */ if (vm_slot_init_all(vm) == -1) return(-1); /* Show device list */ c1700_show_hardware(router); return(0); } static struct ppc32_bat_prog bat_array[] = { { PPC32_IBAT_IDX, 0, 0xfff0001e, 0xfff00001 }, { PPC32_IBAT_IDX, 1, 0x00001ffe, 0x00000001 }, { PPC32_IBAT_IDX, 2, 0x00000000, 0xee3e0072 }, { PPC32_IBAT_IDX, 3, 0x80001ffe, 0x00000001 }, { PPC32_DBAT_IDX, 0, 0x80001ffe, 0x00000042 }, { PPC32_DBAT_IDX, 1, 0x00001ffe, 0x0000002a }, { PPC32_DBAT_IDX, 2, 0x40007ffe, 0x4000002a }, { PPC32_DBAT_IDX, 3, 0xf0001ffe, 0xf000002a }, { -1, -1, 0, 0 }, }; /* Boot the IOS image */ static int c1700_boot_ios(c1700_t *router) { vm_instance_t *vm = router->vm; cpu_ppc_t *cpu; if (!vm->boot_cpu) return(-1); /* Suspend CPU activity since we will restart directly from ROM */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Reset the boot CPU */ cpu = CPU_PPC32(vm->boot_cpu); ppc32_reset(cpu); /* Adjust stack pointer */ cpu->gpr[1] |= 0x80000000; /* Load BAT registers */ printf("Loading BAT registers\n"); ppc32_load_bat_array(cpu,bat_array); cpu->msr |= PPC32_MSR_IR|PPC32_MSR_DR; /* IRQ routing */ vm->set_irq = c1700_set_irq; vm->clear_irq = c1700_clear_irq; /* Load IOS image */ if (ppc32_load_elf_image(cpu,vm->ios_image, (vm->ghost_status == VM_GHOST_RAM_USE), &vm->ios_entry_point) < 0) { vm_error(vm,"failed to load Cisco IOS image '%s'.\n",vm->ios_image); return(-1); } /* Launch the simulation */ printf("\nC1700 '%s': starting simulation (CPU0 IA=0x%8.8x), " "JIT %sabled.\n", vm->name,cpu->ia,vm->jit_use ? "en":"dis"); vm_log(vm,"C1700_BOOT", "starting instance (CPU0 PC=0x%8.8x,idle_pc=0x%8.8x,JIT %s)\n", cpu->ia,cpu->idle_pc,vm->jit_use ? "on":"off"); /* Start main CPU */ if (vm->ghost_status != VM_GHOST_RAM_GENERATE) { vm->status = VM_STATUS_RUNNING; cpu_start(vm->boot_cpu); } else { vm->status = VM_STATUS_SHUTDOWN; } return(0); } /* Initialize a Cisco 1700 instance */ static int c1700_init_instance(vm_instance_t *vm) { c1700_t *router = VM_C1700(vm); m_uint32_t rom_entry_point; cpu_ppc_t *cpu0; if (!vm->ios_image) { vm_error(vm,"no Cisco IOS image defined."); return(-1); } /* Initialize the C1700 platform */ if (c1700_init_platform(router) == -1) { vm_error(vm,"unable to initialize the platform hardware.\n"); return(-1); } /* Load IOS configuration files */ if (vm->ios_startup_config != NULL || vm->ios_private_config != NULL) { vm_nvram_push_config(vm,vm->ios_startup_config,vm->ios_private_config); vm->conf_reg &= ~0x40; } /* Load ROM (ELF image or embedded) */ cpu0 = CPU_PPC32(vm->boot_cpu); rom_entry_point = (m_uint32_t)PPC32_ROM_START; if ((vm->rom_filename != NULL) && (ppc32_load_elf_image(cpu0,vm->rom_filename,0,&rom_entry_point) < 0)) { vm_error(vm,"unable to load alternate ROM '%s', " "fallback to embedded ROM.\n\n",vm->rom_filename); vm->rom_filename = NULL; } return(c1700_boot_ios(router)); } /* Stop a Cisco 1700 instance */ static int c1700_stop_instance(vm_instance_t *vm) { printf("\nC1700 '%s': stopping simulation.\n",vm->name); vm_log(vm,"C1700_STOP","stopping simulation.\n"); /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } } /* Free resources that were used during execution to emulate hardware */ vm_slot_shutdown_all(vm); vm_hardware_shutdown(vm); /* Cleanup */ VM_C1700(vm)->iofpga_data = NULL; VM_C1700(vm)->mpc_data = NULL; return(0); } /* Get MAC address MSB */ static u_int c1700_get_mac_addr_msb(void) { return(0xD0); } /* Parse specific options for the Cisco 1700 platform */ static int c1700_cli_parse_options(vm_instance_t *vm,int option) { c1700_t *router = VM_C1700(vm); switch(option) { /* IO memory reserved for NMs (in percents!) */ case OPT_IOMEM_SIZE: vm->nm_iomem_size = 0x8000 | atoi(optarg); break; /* Mainboard type */ case 't': c1700_mainboard_set_type(router,optarg); break; /* Set the base MAC address */ case 'm': if (!c1700_chassis_set_mac_addr(router,optarg)) printf("MAC address set to '%s'.\n",optarg); break; /* Set the System ID */ case 'I': if (!c1700_set_system_id(router,optarg)) printf("System ID set to '%s'.\n",optarg); break; /* Unknown option */ default: return(-1); } return(0); } /* Show specific CLI options */ static void c1700_cli_show_options(vm_instance_t *vm) { printf(" --iomem-size : IO memory (in percents, default: %u)\n" " -t : Select Chassis type\n" " -p : Define a WIC Module\n" " -I : Set Processor Board Serial Number\n" " -s : Bind a Network IO interface to a WIC\n", vm->nm_iomem_size); } /* Platform definition */ static vm_platform_t c1700_platform = { "c1700", "C1700", "1700", c1700_create_instance, c1700_delete_instance, c1700_init_instance, c1700_stop_instance, NULL, NULL, c1700_nvram_extract_config, c1700_nvram_push_config, c1700_get_mac_addr_msb, c1700_save_config, c1700_cli_parse_options, c1700_cli_show_options, c1700_mainboard_show_drivers, }; /* Register the c1700 platform */ int c1700_platform_register(void) { if (vm_platform_register(&c1700_platform) == -1) return(-1); return(hypervisor_c1700_init(&c1700_platform)); } dynamips-0.2.14/common/dev_c1700.h000066400000000000000000000102471241034141600164530ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Generic Cisco 1700 routines and definitions (EEPROM,...). */ #ifndef __DEV_C1700_H__ #define __DEV_C1700_H__ #include #include "utils.h" #include "net.h" #include "device.h" #include "pci_dev.h" #include "nmc93cX6.h" #include "net_io.h" #include "dev_mpc860.h" #include "vm.h" /* Default C1700 parameters */ #define C1700_DEFAULT_MAINBOARD "1720" #define C1700_DEFAULT_RAM_SIZE 64 #define C1700_DEFAULT_ROM_SIZE 2 #define C1700_DEFAULT_NVRAM_SIZE 32 #define C1700_DEFAULT_CONF_REG 0x2102 #define C1700_DEFAULT_CLOCK_DIV 8 #define C1700_DEFAULT_RAM_MMAP 1 #define C1700_DEFAULT_DISK0_SIZE 0 #define C1700_DEFAULT_DISK1_SIZE 0 #define C1700_DEFAULT_IOMEM_SIZE 15 /* Percents! */ /* 1700 characteristics: only mainboard (considered as fake NM) */ #define C1700_MAX_NM_BAYS 1 #define C1700_MAX_WIC_BAYS 2 /* C1700 Virtual Timer Interrupt */ #define C1700_VTIMER_IRQ 0 /* C1700 DUART Interrupt */ #define C1700_DUART_IRQ 1 /* C1700 Network I/O Interrupt */ #define C1700_NETIO_IRQ 2 /* C1700 PA Management Interrupt */ #define C1700_PA_MGMT_IRQ 3 /* Network IRQ */ #define C1700_NETIO_IRQ_BASE 32 #define C1700_NETIO_IRQ_PORT_BITS 2 #define C1700_NETIO_IRQ_PORT_MASK ((1 << C1700_NETIO_IRQ_PORT_BITS) - 1) #define C1700_NETIO_IRQ_PER_SLOT (1 << C1700_NETIO_IRQ_PORT_BITS) #define C1700_NETIO_IRQ_END \ (C1700_NETIO_IRQ_BASE + (C1700_MAX_NM_BAYS * C1700_NETIO_IRQ_PER_SLOT) - 1) /* C1700 common device addresses */ #define C1700_FLASH_ADDR 0x60000000ULL #define C1700_NVRAM_ADDR 0x68000000ULL #define C1700_IOFPGA_ADDR 0x68020000ULL #define C1700_WIC_ADDR 0x68030000ULL #define C1700_DUART_ADDR 0x68050000ULL #define C1700_MPC860_ADDR 0xff000000ULL #define C1700_ROM_ADDR 0xfff00000ULL /* WIC interval in address space */ #define C1700_WIC_SIZE 0x1000 /* Reserved space for ROM in NVRAM */ #define C1700_NVRAM_ROM_RES_SIZE 2048 /* C1700 ELF Platform ID */ #define C1700_ELF_MACHINE_ID 0x33 #define VM_C1700(vm) ((c1700_t *)vm->hw_data) /* C1700 router */ typedef struct c1700_router c1700_t; /* C1700 router */ struct c1700_router { /* Mainboard type (2610, 2611, etc) */ char *mainboard_type; /* Chassis MAC address */ n_eth_addr_t mac_addr; char board_id[20]; /* Associated VM instance */ vm_instance_t *vm; /* I/O FPGA */ struct c1700_iofpga_data *iofpga_data; /* * Mainboard EEPROM. * It can be modified to change the chassis MAC address. */ struct cisco_eeprom mb_eeprom; struct nmc93cX6_group mb_eeprom_group; /* Network Module EEPROM */ struct nmc93cX6_group nm_eeprom_group; /* MPC860 device private data */ struct mpc860_data *mpc_data; }; /* Get WIC device address for the specified onboard port */ int c1700_get_onboard_wic_addr(u_int slot,m_uint64_t *phys_addr); /* Set EEPROM for the specified slot */ int c1700_set_slot_eeprom(c1700_t *router,u_int slot, struct cisco_eeprom *eeprom); /* Get network IRQ for specified slot/port */ u_int c1700_net_irq_for_slot_port(u_int slot,u_int port); /* Set mainboard type */ int c1700_mainboard_set_type(c1700_t *router,char *mainboard_type); /* Set chassis MAC address */ int c1700_chassis_set_mac_addr(c1700_t *router,char *mac_addr); /* Set the system id */ int c1700_set_system_id(c1700_t *router,char *id); /* Burn the system id into the appropriate eeprom if possible */ int c1700_refresh_systemid(c1700_t *router); /* Show C1700 hardware info */ void c1700_show_hardware(c1700_t *router); /* Initialize EEPROM groups */ void c1700_init_eeprom_groups(c1700_t *router); /* dev_c1700_iofpga_init() */ int dev_c1700_iofpga_init(c1700_t *router,m_uint64_t paddr,m_uint32_t len); /* Register the c1700 platform */ int c1700_platform_register(void); /* Hypervisor C1700 initialization */ extern int hypervisor_c1700_init(vm_platform_t *platform); /* c1700 Motherboard drivers */ extern struct cisco_card_driver dev_c1700_mb_eth_driver; extern struct cisco_card_driver dev_c1710_mb_eth_driver; /* WIC drivers */ extern struct cisco_card_driver *dev_c1700_mb_wic_drivers[]; #endif dynamips-0.2.14/common/dev_c1700_eth.c000066400000000000000000000076111241034141600173070ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * Cisco 1700 Mainboard Ethernet driver. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "vm.h" #include "dev_mpc860.h" #include "dev_c1700.h" /* Return sub-slot info for integrated WIC slots (on motherboard) */ static int dev_c1700_mb_get_sub_info(vm_instance_t *vm,struct cisco_card *card, u_int port_id, struct cisco_card_driver ***drv_array, u_int *subcard_type) { /* 2 integrated WIC slots */ if ((port_id & 0x0F) >= 2) return(-1); *drv_array = dev_c1700_mb_wic_drivers; *subcard_type = CISCO_CARD_TYPE_WIC; return(0); } /* ====================================================================== */ /* MPC860 - Integrated Ethernet port */ /* ====================================================================== */ /* Initialize Ethernet part of the MPC860 */ static int dev_c1700_mb_eth_init(vm_instance_t *vm,struct cisco_card *card) { if (card->slot_id != 0) { vm_error(vm,"dev_c1700_mb_eth_init: bad slot %u specified.\n", card->slot_id); return(-1); } /* Store device info into the router structure */ card->drv_info = VM_C1700(vm)->mpc_data; return(0); } /* Nothing to do, we never remove the system controller */ static int dev_c1700_mb_eth_shutdown(vm_instance_t *vm,struct cisco_card *card) { return(0); } /* Bind a Network IO descriptor */ static int dev_c1700_mb_eth_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct mpc860_data *d = card->drv_info; if (port_id != 0) return(-1); return(mpc860_fec_set_nio(d,nio)); } /* Unbind a Network IO descriptor */ static int dev_c1700_mb_eth_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct mpc860_data *d = card->drv_info; if (port_id != 0) return(-1); return(mpc860_fec_unset_nio(d)); } /* c1700 Motherboard driver */ struct cisco_card_driver dev_c1700_mb_eth_driver = { "C1700-MB-1ETH", 1, 2, dev_c1700_mb_eth_init, dev_c1700_mb_eth_shutdown, dev_c1700_mb_get_sub_info, dev_c1700_mb_eth_set_nio, dev_c1700_mb_eth_unset_nio, NULL, }; /* ====================================================================== */ /* C1710: 1 FastEthernet port + 1 Ethernet port as SCC channel 1. */ /* ====================================================================== */ /* Bind a Network IO descriptor */ static int dev_c1710_mb_eth_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct mpc860_data *d = card->drv_info; switch(port_id) { case 0: return(mpc860_fec_set_nio(d,nio)); case 1: return(mpc860_scc_set_nio(d,0,nio)); default: return(-1); } } /* Unbind a Network IO descriptor */ static int dev_c1710_mb_eth_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct mpc860_data *d = card->drv_info; switch(port_id) { case 0: return(mpc860_fec_unset_nio(d)); case 1: return(mpc860_scc_unset_nio(d,0)); default: return(-1); } } /* c1710 Motherboard driver */ struct cisco_card_driver dev_c1710_mb_eth_driver = { "C1710-MB-1FE-1E", 1, 0, dev_c1700_mb_eth_init, dev_c1700_mb_eth_shutdown, NULL, dev_c1710_mb_eth_set_nio, dev_c1710_mb_eth_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c1700_iofpga.c000066400000000000000000000207531241034141600177760ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) */ #include #include #include #include #include #include #include #include #include "ptask.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_vtty.h" #include "nmc93cX6.h" #include "dev_mpc860.h" #include "dev_c1700.h" /* Debugging flags */ #define DEBUG_UNKNOWN 1 #define DEBUG_ACCESS 0 #define DEBUG_WIC 0 #define DEBUG_NET_IRQ 0 /* Definitions for Mainboard EEPROM */ #define EEPROM_MB_DOUT 3 #define EEPROM_MB_DIN 2 #define EEPROM_MB_CLK 1 #define EEPROM_MB_CS 0 /* Network IRQ distribution */ static u_int net_irq_dist[C1700_MAX_NM_BAYS] = { 0, /* XXX: required/does exist ??? */ }; /* IO FPGA structure */ struct c1700_iofpga_data { vm_obj_t vm_obj; struct vdevice dev; c1700_t *router; /* Network Interrupt status */ m_uint8_t net_irq_status; /* Interrupt mask */ m_uint16_t intr_mask; /* WIC SPI selection */ m_uint8_t wic_select; }; /* Mainboard EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_mb_def = { EEPROM_MB_CLK, EEPROM_MB_CS, EEPROM_MB_DIN, EEPROM_MB_DOUT, }; /* Mainboard EEPROM */ static const struct nmc93cX6_group eeprom_mb_group = { EEPROM_TYPE_NMC93C46, 1, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "Mainboard EEPROM", { &eeprom_mb_def }, }; /* Update network interrupt status */ static inline void dev_c1700_iofpga_net_update_irq(struct c1700_iofpga_data *d) { if (d->net_irq_status) { vm_set_irq(d->router->vm,C1700_NETIO_IRQ); } else { vm_clear_irq(d->router->vm,C1700_NETIO_IRQ); } } /* Trigger a Network IRQ for the specified slot/port */ void dev_c1700_iofpga_net_set_irq(struct c1700_iofpga_data *d, u_int slot,u_int port) { #if DEBUG_NET_IRQ vm_log(d->router->vm,"IO_FPGA","setting NetIRQ for slot %u port %u\n", slot,port); #endif d->net_irq_status |= 1 << (net_irq_dist[slot] + port); dev_c1700_iofpga_net_update_irq(d); } /* Clear a Network IRQ for the specified slot/port */ void dev_c1700_iofpga_net_clear_irq(struct c1700_iofpga_data *d, u_int slot,u_int port) { #if DEBUG_NET_IRQ vm_log(d->router->vm,"IO_FPGA","clearing NetIRQ for slot %u port %u\n", slot,port); #endif d->net_irq_status &= ~(1 << (net_irq_dist[slot] + port)); dev_c1700_iofpga_net_update_irq(d); } /* Callback for MPC860 SPI Transmit */ static void dev_c1700_mpc860_spi_tx_callback(struct mpc860_data *mpc_data, u_char *buffer,u_int len, void *user_arg) { struct c1700_iofpga_data *d = user_arg; struct cisco_eeprom *eeprom; u_char reply_buf[4]; u_int wic_port; u_int eeprom_offset; if (d->wic_select & 0x20) wic_port = 0x10; else if (d->wic_select & 0x08) wic_port = 0x20; else { #if DEBUG_WIC vm_error(d->router->vm,"unknown value for wic_select (0x%8.8x)\n", d->wic_select); #endif wic_port = 0; } /* No WIC in slot or no EEPROM: fake an empty EEPROM */ if (!wic_port || !(eeprom = vm_slot_get_eeprom(d->router->vm,0,wic_port))) { memset(reply_buf,0xFF,sizeof(reply_buf)); mpc860_spi_receive(mpc_data,reply_buf,sizeof(reply_buf)); return; } /* Read request: 0x03 offset 0x00 0x00 */ eeprom_offset = buffer[1]; reply_buf[0] = 0; reply_buf[1] = 0; cisco_eeprom_get_byte(eeprom,eeprom_offset,&reply_buf[2]); cisco_eeprom_get_byte(eeprom,eeprom_offset+1,&reply_buf[3]); mpc860_spi_receive(mpc_data,reply_buf,sizeof(reply_buf)); } /* * dev_c1700_iofpga_access() */ static void * dev_c1700_iofpga_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct c1700_iofpga_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0x0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA","reading reg 0x%x at pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA", "writing reg 0x%x at pc=0x%llx, data=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),*data,op_size); } #endif switch(offset) { /* * Bits 0-2: motherboard model (change with caution, different log patterns) * 0=MPC860T * 1=MPC860T * 2=MPC860 * 3=MPC860P * 4=MPC862P * 5=MPC855T * 6=MPC860P * 7=(see iofpga[5]&0xf) * Bits 3-7: ??? */ case 0x04: break; /* (iopfga[4]&0x7 == 7) * Bits 0-3: extended motherboard model * 0-1=MPC860P * 2-15=MPC860 * Bits 4-7: ??? related to clock speed? case 0x05: */ /* (iopfga[4]&0x7 == 7 and iofpga[5]&0xF == 1) * Bits 0-7: ??? case 0x0A: */ /* * Bit 0: ??? * Bit 1: card present in slot 0 / WIC 0. * Bit 2: card present in slot 0 / WIC 1. * Bit 3: compression/VPN module ? (mention of "slot 3") * Bits 4-7: ??? */ case 0x10: if (op_type == MTS_READ) { *data = 0; /* check WIC 0 */ if (vm_slot_check_eeprom(d->router->vm,0,0x10)) *data |= 0x02; /* check WIC 1 */ if (vm_slot_check_eeprom(d->router->vm,0,0x20)) *data |= 0x04; } break; /* * Bit 0: ??? NVRAM write protection? * Bit 1: ??? * Bit 2: ??? related to syscalls? * Bits 3-7: ??? case 0x14: */ /* WIC card selection for EEPROM reading * Bits 0-2: ??? * Bit 3: wic_port 0x10 * Bit 4: ??? * Bit 5: wic_port 0x20 * Bits 6-7: ??? */ case 0x18: if (op_type == MTS_READ) *data = d->wic_select; else { d->wic_select = *data; } break; /* * Bit 0-7: ??? case 0x20: */ /* Unknown, read on 1760. Considering the activity pattern, * it's probably used to update the fpga. */ case 0x4c: if (op_type == MTS_READ) *data = 0xFF; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA", "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA", "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* Shutdown the IO FPGA device */ static void dev_c1700_iofpga_shutdown(vm_instance_t *vm,struct c1700_iofpga_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* * dev_c1700_iofpga_init() */ int dev_c1700_iofpga_init(c1700_t *router,m_uint64_t paddr,m_uint32_t len) { vm_instance_t *vm = router->vm; struct c1700_iofpga_data *d; /* Allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"IO_FPGA: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); d->router = router; vm_object_init(&d->vm_obj); d->vm_obj.name = "io_fpga"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_c1700_iofpga_shutdown; /* Set device properties */ dev_init(&d->dev); d->dev.name = "io_fpga"; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.priv_data = d; d->dev.handler = dev_c1700_iofpga_access; /* Initialize the MPC860 SPI TX callback to read mainboard WIC EEPROMs */ mpc860_spi_set_tx_callback(router->mpc_data, dev_c1700_mpc860_spi_tx_callback,d); /* Map this device to the VM */ vm_bind_device(router->vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } /* Initialize EEPROM groups */ void c1700_init_eeprom_groups(c1700_t *router) { /* Initialize Mainboard EEPROM */ router->mb_eeprom_group = eeprom_mb_group; router->mb_eeprom_group.eeprom[0] = &router->mb_eeprom; router->mb_eeprom.data = NULL; router->mb_eeprom.len = 0; } dynamips-0.2.14/common/dev_c1700_iofpga.h000066400000000000000000000013511241034141600177740ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005-2007 Christophe Fillot (cf@utc.fr) * * Cisco c1700 I/O FPGA. */ #ifndef __DEV_C1700_IOFPGA_H__ #define __DEV_C1700_IOFPGA_H__ /* Forward declaration for IO_FPGA private data */ struct c1700_iofpga_data; /* Trigger a Network IRQ for the specified slot/port */ void dev_c1700_iofpga_net_set_irq(struct c1700_iofpga_data *d, u_int slot,u_int port); /* Clear a Network IRQ for the specified slot/port */ void dev_c1700_iofpga_net_clear_irq(struct c1700_iofpga_data *d, u_int slot,u_int port); /* Create the c1700 I/O FPGA */ int dev_c1700_iofpga_init(c1700_t *router,m_uint64_t paddr,m_uint32_t len); #endif dynamips-0.2.14/common/dev_c1700_wic.c000066400000000000000000000212401241034141600173030ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * WIC Modules. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "vm.h" #include "dev_nm_16esw.h" #include "dev_mpc860.h" #include "dev_c1700.h" #include "dev_wic_serial.h" /* Get the SCC channel associated to a WIC sub-slot */ static int dev_c1700_mb_wic_get_scc_chan(struct cisco_card *card,u_int port_id, u_int *scc_chan) { u_int cid; cid = card->subslot_id + port_id; switch(cid) { /* WIC 0 port 0 mapped to MPC860 SCC1 */ case 0x10: *scc_chan = 0; break; /* WIC 0 port 1 mapped to MPC860 SCC4 */ case 0x11: *scc_chan = 3; break; /* WIC 1 port 0 mapped to MPC860 SCC2 */ case 0x20: *scc_chan = 1; break; /* WIC 1 port 1 mapped to MPC860 SCC3 */ case 0x21: *scc_chan = 2; break; default: return(-1); } return(0); } /* ======================================================================== */ /* WIC-1T */ /* ======================================================================== */ /* Initialize a WIC-1T in the specified slot */ static int dev_c1700_mb_wic1t_init(vm_instance_t *vm,struct cisco_card *card) { struct wic_serial_data *wic_data; m_uint64_t phys_addr; u_int wic_id; /* Create the WIC device */ wic_id = (card->subslot_id >> 4) - 1; if (c1700_get_onboard_wic_addr(wic_id,&phys_addr) == -1) { vm_error(vm,"WIC","invalid slot %u (subslot_id=%u)\n", wic_id,card->subslot_id); return(-1); } wic_data = dev_wic_serial_init(vm,card->dev_name,WIC_SERIAL_MODEL_1T, phys_addr,C1700_WIC_SIZE); if (!wic_data) return(-1); /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_wic("WIC-1T")); /* Store device info into the router structure */ card->drv_info = wic_data; return(0); } /* Remove a WIC-1T from the specified slot */ static int dev_c1700_mb_wic1t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the WIC device */ dev_wic_serial_remove(card->drv_info); /* Remove the WIC EEPROM */ cisco_card_unset_eeprom(card); return(0); } /* Bind a Network IO descriptor */ static int dev_c1700_mb_wic1t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { u_int scc_chan; if ((port_id > 0) || (dev_c1700_mb_wic_get_scc_chan(card,port_id,&scc_chan) == -1)) return(-1); return(mpc860_scc_set_nio(VM_C1700(vm)->mpc_data,scc_chan,nio)); } /* Unbind a Network IO descriptor */ static int dev_c1700_mb_wic1t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { u_int scc_chan; if ((port_id > 0) || (dev_c1700_mb_wic_get_scc_chan(card,port_id,&scc_chan) == -1)) return(-1); return(mpc860_scc_unset_nio(VM_C1700(vm)->mpc_data,scc_chan)); } /* ======================================================================== */ /* WIC-2T */ /* ======================================================================== */ /* Initialize a WIC-2T in the specified slot */ static int dev_c1700_mb_wic2t_init(vm_instance_t *vm,struct cisco_card *card) { struct wic_serial_data *wic_data; m_uint64_t phys_addr; u_int wic_id; /* Create the WIC device */ wic_id = (card->subslot_id >> 4) - 1; if (c1700_get_onboard_wic_addr(wic_id,&phys_addr) == -1) { vm_error(vm,"WIC","invalid slot %u (subslot_id=%u)\n", wic_id,card->subslot_id); return(-1); } wic_data = dev_wic_serial_init(vm,card->dev_name,WIC_SERIAL_MODEL_2T, phys_addr,C1700_WIC_SIZE); if (!wic_data) return(-1); /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_wic("WIC-2T")); /* Store device info into the router structure */ card->drv_info = wic_data; return(0); } /* Remove a WIC-2T from the specified slot */ static int dev_c1700_mb_wic2t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the WIC device */ dev_wic_serial_remove(card->drv_info); /* Remove the WIC EEPROM */ cisco_card_unset_eeprom(card); return(0); } /* Bind a Network IO descriptor */ static int dev_c1700_mb_wic2t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { u_int scc_chan; if ((port_id > 1) || (dev_c1700_mb_wic_get_scc_chan(card,port_id,&scc_chan) == -1)) return(-1); return(mpc860_scc_set_nio(VM_C1700(vm)->mpc_data,scc_chan,nio)); } /* Unbind a Network IO descriptor */ static int dev_c1700_mb_wic2t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { u_int scc_chan; if ((port_id > 1) || (dev_c1700_mb_wic_get_scc_chan(card,port_id,&scc_chan) == -1)) return(-1); return(mpc860_scc_unset_nio(VM_C1700(vm)->mpc_data,scc_chan)); } /* ======================================================================== */ /* WIC-1ENET */ /* ======================================================================== */ /* Initialize a WIC-1ENET in the specified slot */ static int dev_c1700_mb_wic1enet_init(vm_instance_t *vm,struct cisco_card *card) { m_uint8_t eeprom_ver; size_t offset; n_eth_addr_t addr; m_uint16_t pid; pid = (m_uint16_t)getpid(); /* Generate automatically the MAC address */ addr.eth_addr_byte[0] = vm_get_mac_addr_msb(vm); addr.eth_addr_byte[1] = vm->instance_id & 0xFF; addr.eth_addr_byte[2] = pid >> 8; addr.eth_addr_byte[3] = pid & 0xFF; addr.eth_addr_byte[4] = card->subslot_id; addr.eth_addr_byte[5] = 0x00; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_wic("WIC-1ENET")); /* Read EEPROM format version */ cisco_eeprom_get_byte(&card->eeprom,0,&eeprom_ver); if (eeprom_ver != 4) return(-1); if (cisco_eeprom_v4_find_field(&card->eeprom,0xCF,&offset) == -1) return(-1); cisco_eeprom_set_region(&card->eeprom,offset,addr.eth_addr_byte,6); /* Store device info into the router structure */ card->drv_info = VM_C1700(vm)->mpc_data; return(0); } /* Remove a WIC-1ENET from the specified slot */ static int dev_c1700_mb_wic1enet_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the WIC EEPROM */ cisco_card_unset_eeprom(card); return(0); } /* Bind a Network IO descriptor */ static int dev_c1700_mb_wic1enet_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct mpc860_data *mpc_data = card->drv_info; u_int scc_chan; if ((port_id > 0) || (dev_c1700_mb_wic_get_scc_chan(card,port_id,&scc_chan) == -1)) return(-1); return(mpc860_scc_set_nio(mpc_data,scc_chan,nio)); } /* Unbind a Network IO descriptor */ static int dev_c1700_mb_wic1enet_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct mpc860_data *mpc_data = card->drv_info; u_int scc_chan; if ((port_id > 0) || (dev_c1700_mb_wic_get_scc_chan(card,port_id,&scc_chan) == -1)) return(-1); return(mpc860_scc_unset_nio(mpc_data,scc_chan)); } /* ======================================================================== */ /* Cisco 1700 WIC-1T driver */ struct cisco_card_driver dev_c1700_mb_wic1t_driver = { "WIC-1T", 1, 0, dev_c1700_mb_wic1t_init, dev_c1700_mb_wic1t_shutdown, NULL, dev_c1700_mb_wic1t_set_nio, dev_c1700_mb_wic1t_unset_nio, NULL, }; /* Cisco 1700 WIC-2T driver */ struct cisco_card_driver dev_c1700_mb_wic2t_driver = { "WIC-2T", 1, 0, dev_c1700_mb_wic2t_init, dev_c1700_mb_wic2t_shutdown, NULL, dev_c1700_mb_wic2t_set_nio, dev_c1700_mb_wic2t_unset_nio, NULL, }; /* Cisco 1700 WIC-1ENET driver */ struct cisco_card_driver dev_c1700_mb_wic1enet_driver = { "WIC-1ENET", 1, 0, dev_c1700_mb_wic1enet_init, dev_c1700_mb_wic1enet_shutdown, NULL, dev_c1700_mb_wic1enet_set_nio, dev_c1700_mb_wic1enet_unset_nio, NULL, }; /* WIC drivers (mainbord slots) */ struct cisco_card_driver *dev_c1700_mb_wic_drivers[] = { &dev_c1700_mb_wic1t_driver, &dev_c1700_mb_wic2t_driver, &dev_c1700_mb_wic1enet_driver, NULL, }; dynamips-0.2.14/common/dev_c2600.c000066400000000000000000000566111241034141600164530ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * Patched by Jeremy Grossmann for the GNS3 project (www.gns3.net) * * Generic Cisco 2600 routines and definitions (EEPROM,...). */ #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "ppc32_mem.h" #include "pci_io.h" #include "cisco_eeprom.h" #include "dev_mpc860.h" #include "dev_rom.h" #include "dev_c2600.h" #include "dev_c2600_iofpga.h" #include "dev_vtty.h" #include "registry.h" #include "fs_nvram.h" /* ======================================================================== */ /* EEPROM definitions */ /* ======================================================================== */ /* Cisco 2600 mainboard EEPROM */ static m_uint16_t eeprom_c2600_mb_data[] = { 0x0101, 0x0404, 0x0000, 0x0000, 0x4320, 0x00FF, 0x0091, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x4654, 0x5809, 0x4557, 0x304D, 0x5902, 0x0200, 0x0000, 0x0000, 0x00FF, 0xFFFF, 0x5006, 0x490B, 0x1709, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; struct c2600_mb_id { char *name; char *mb_driver; m_uint16_t id; int xm_model; int supported; }; struct c2600_mb_id c2600_mainboard_id[] = { { "2610" , "CISCO2600-MB-1E" , 0x0091, FALSE, TRUE }, { "2611" , "CISCO2600-MB-2E" , 0x0092, FALSE, TRUE }, { "2620" , "CISCO2600-MB-1FE" , 0x0094, FALSE, TRUE }, { "2621" , "CISCO2600-MB-2FE" , 0x00a2, FALSE, TRUE }, { "2610XM" , "CISCO2600-MB-1FE" , 0x036a, TRUE, TRUE }, { "2611XM" , "CISCO2600-MB-2FE" , 0x036b, TRUE, FALSE }, { "2620XM" , "CISCO2600-MB-1FE" , 0x036c, TRUE, TRUE }, { "2621XM" , "CISCO2600-MB-2FE" , 0x036d, TRUE, FALSE }, { "2650XM" , "CISCO2600-MB-1FE" , 0x036e, TRUE, TRUE }, { "2651XM" , "CISCO2600-MB-2FE" , 0x036f, TRUE, FALSE }, { NULL , NULL , 0x0000, FALSE, FALSE }, }; /* ======================================================================== */ /* Network Module Drivers */ /* ======================================================================== */ static struct cisco_card_driver *nm_drivers[] = { &dev_c2600_mb1e_eth_driver, &dev_c2600_mb2e_eth_driver, &dev_c2600_mb1fe_eth_driver, &dev_c2600_mb2fe_eth_driver, &dev_c2600_nm_1e_driver, &dev_c2600_nm_4e_driver, &dev_c2600_nm_1fe_tx_driver, &dev_c2600_nm_16esw_driver, &dev_c2600_nm_nam_driver, &dev_c2600_nm_cids_driver, NULL, }; /* ======================================================================== */ /* Cisco 2600 router instances */ /* ======================================================================== */ /* Initialize default parameters for a C2600 */ static void c2600_init_defaults(c2600_t *router); /* Directly extract the configuration from the NVRAM device */ static int c2600_nvram_extract_config(vm_instance_t *vm,u_char **startup_config,size_t *startup_len,u_char **private_config,size_t *private_len) { int ret; ret = generic_nvram_extract_config(vm, "nvram", vm->nvram_rom_space*4, 0, 0, FS_NVRAM_FORMAT_SCALE_4, startup_config, startup_len, private_config, private_len); return(ret); } /* Directly push the IOS configuration to the NVRAM device */ static int c2600_nvram_push_config(vm_instance_t *vm,u_char *startup_config,size_t startup_len,u_char *private_config,size_t private_len) { int ret; ret = generic_nvram_push_config(vm, "nvram", vm->nvram_size*4096, vm->nvram_rom_space*4, 0, 0, FS_NVRAM_FORMAT_SCALE_4, startup_config, startup_len, private_config, private_len); return(ret); } /* Check for empty config */ int c2600_nvram_check_empty_config(vm_instance_t *vm) { struct vdevice *dev; m_uint64_t addr; m_uint32_t len; if (!(dev = dev_get_by_name(vm,"nvram"))) return(-1); addr = dev->phys_addr + (vm->nvram_rom_space << 2); len = dev->phys_len - (vm->nvram_rom_space << 2); while(len > 0) { if (physmem_copy_u32_from_vm(vm,addr) != 0) return(0); addr += sizeof(m_uint32_t); len -= sizeof(m_uint32_t); } /* Empty NVRAM */ vm->conf_reg |= 0x0040; printf("NVRAM is empty, setting config register to 0x%x\n",vm->conf_reg); return(0); } /* Create a new router instance */ static int c2600_create_instance(vm_instance_t *vm) { c2600_t *router; if (!(router = malloc(sizeof(*router)))) { fprintf(stderr,"C2600 '%s': Unable to create new instance!\n",vm->name); return(-1); } memset(router,0,sizeof(*router)); router->vm = vm; vm->hw_data = router; c2600_init_defaults(router); return(0); } /* Free resources used by a router instance */ static int c2600_delete_instance(vm_instance_t *vm) { c2600_t *router = VM_C2600(vm); int i; /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(FALSE); } } /* Remove NIO bindings */ for(i=0;inr_slots;i++) vm_slot_remove_all_nio_bindings(vm,i); /* Shutdown all Network Modules */ vm_slot_shutdown_all(vm); /* Free mainboard EEPROM */ cisco_eeprom_free(&router->mb_eeprom); /* Free all resources used by VM */ vm_free(vm); /* Free the router structure */ free(router); return(TRUE); } /* Save configuration of a C2600 instance */ void c2600_save_config(vm_instance_t *vm,FILE *fd) { c2600_t *router = VM_C2600(vm); fprintf(fd,"c2600 set_chassis %s %s\n\n",vm->name,router->mainboard_type); } /* Get WIC device address for the specified onboard port */ int c2600_get_onboard_wic_addr(u_int slot,m_uint64_t *phys_addr) { if (slot >= C2600_MAX_WIC_BAYS) return(-1); *phys_addr = C2600_WIC_ADDR + (slot * C2600_WIC_SIZE); return(0); } /* Set EEPROM for the specified slot */ int c2600_set_slot_eeprom(c2600_t *router,u_int slot, struct cisco_eeprom *eeprom) { switch(slot) { case 1: router->nm_eeprom_group.eeprom[0] = eeprom; return(0); default: return(-1); } } /* Get slot/port corresponding to specified network IRQ */ static inline void c2600_net_irq_get_slot_port(u_int irq,u_int *slot,u_int *port) { irq -= C2600_NETIO_IRQ_BASE; *port = irq & C2600_NETIO_IRQ_PORT_MASK; *slot = irq >> C2600_NETIO_IRQ_PORT_BITS; } /* Get network IRQ for specified slot/port */ u_int c2600_net_irq_for_slot_port(u_int slot,u_int port) { u_int irq; irq = (slot << C2600_NETIO_IRQ_PORT_BITS) + port; irq += C2600_NETIO_IRQ_BASE; return(irq); } /* Find Cisco 2600 Mainboard info */ static struct c2600_mb_id *c2600_get_mb_info(char *mainboard_type) { int i; for(i=0;c2600_mainboard_id[i].name;i++) if (!strcmp(c2600_mainboard_id[i].name,mainboard_type)) return(&c2600_mainboard_id[i]); return NULL; } /* Show all available mainboards */ void c2600_mainboard_show_drivers(void) { int i; printf("Available C2600 chassis drivers:\n"); for(i=0;c2600_mainboard_id[i].name;i++) printf(" * %s %s\n", c2600_mainboard_id[i].name, !c2600_mainboard_id[i].supported ? "(NOT WORKING)" : ""); printf("\n"); } /* Show the list of available NM drivers */ void c2600_nm_show_drivers(void) { int i; printf("Available C2600 Network Module drivers:\n"); for(i=0;nm_drivers[i];i++) { printf(" * %s %s\n", nm_drivers[i]->dev_type, !nm_drivers[i]->supported ? "(NOT WORKING)" : ""); } printf("\n"); } /* Set the system id or processor board id in the eeprom */ int c2600_set_system_id(c2600_t *router,char *id) { /* 11 characters is enough. Array is 20 long */ strncpy(router->board_id,id,13); /* Make sure it is null terminated */ router->board_id[13] = 0x00; c2600_refresh_systemid(router); return 0; } int c2600_refresh_systemid(c2600_t *router) { if (router->board_id[0] == 0x00) return(0); m_uint8_t buf[11]; parse_board_id(buf,router->board_id,9); // Does not use the cisco_eeprom libraries.. do it by hand int i; for(i=0;i<4;i++) { router->vm->chassis_cookie[i+12] = buf[i*2] << 8; router->vm->chassis_cookie[i+12] |= buf[(i*2)+1]; } router->vm->chassis_cookie[i+12] &= 0x00ff; router->vm->chassis_cookie[i+12] |= buf[(i*2)]<<8; return (0); } /* Set the base MAC address of the chassis */ static int c2600_burn_mac_addr(c2600_t *router,n_eth_addr_t *addr) { int i; for(i=0;i<3;i++) { router->vm->chassis_cookie[i+1] = addr->eth_addr_byte[i*2] << 8; router->vm->chassis_cookie[i+1] |= addr->eth_addr_byte[(i*2)+1]; } return(0); } /* Set mainboard type */ int c2600_mainboard_set_type(c2600_t *router,char *mainboard_type) { struct c2600_mb_id *mb_info; if (router->vm->status == VM_STATUS_RUNNING) { vm_error(router->vm,"unable to change mainboard type when online.\n"); return(-1); } if (!(mb_info = c2600_get_mb_info(mainboard_type))) { vm_error(router->vm,"unknown mainboard '%s'\n",mainboard_type); return(-1); } router->mainboard_type = mainboard_type; router->xm_model = mb_info->xm_model; /* Set the cookie */ memcpy(router->vm->chassis_cookie, eeprom_c2600_mb_data,sizeof(eeprom_c2600_mb_data)); router->vm->chassis_cookie[6] = mb_info->id; /* Set the chassis base MAC address */ c2600_burn_mac_addr(router,&router->mac_addr); /* Set the mainboard driver */ if (vm_slot_active(router->vm,0,0)) vm_slot_remove_binding(router->vm,0,0); vm_slot_add_binding(router->vm,mb_info->mb_driver,0,0); c2600_refresh_systemid(router); return(0); } /* Set chassis MAC address */ int c2600_chassis_set_mac_addr(c2600_t *router,char *mac_addr) { if (parse_mac_addr(&router->mac_addr,mac_addr) == -1) { vm_error(router->vm,"unable to parse MAC address '%s'.\n",mac_addr); return(-1); } /* Set the chassis base MAC address */ c2600_burn_mac_addr(router,&router->mac_addr); return(0); } /* Initialize a Cisco 2600 */ static int c2600_init(c2600_t *router) { vm_instance_t *vm = router->vm; /* Create the PCI bus */ if (!(vm->pci_bus[0] = pci_bus_create("PCI0",0))) { vm_error(vm,"unable to create PCI data.\n"); return(-1); } /* Create the PCI controller */ if (dev_c2600_pci_init(vm,"c2600_pci",C2600_PCICTRL_ADDR,0x10000, vm->pci_bus[0]) == -1) return(-1); /* Bind PCI bus to slots 0 and 1 */ vm->slots_pci_bus[0] = vm->pci_bus[0]; vm->slots_pci_bus[1] = vm->pci_bus[0]; vm->elf_machine_id = C2600_ELF_MACHINE_ID; return(0); } /* Show C2600 hardware info */ void c2600_show_hardware(c2600_t *router) { vm_instance_t *vm = router->vm; printf("C2600 instance '%s' (id %d):\n",vm->name,vm->instance_id); printf(" VM Status : %d\n",vm->status); printf(" RAM size : %u Mb\n",vm->ram_size); printf(" NVRAM size : %u Kb\n",vm->nvram_size); printf(" IOS image : %s\n\n",vm->ios_image); if (vm->debug_level > 0) { dev_show_list(vm); pci_dev_show_list(vm->pci_bus[0]); pci_dev_show_list(vm->pci_bus[1]); printf("\n"); } } /* Initialize default parameters for a C2600 */ static void c2600_init_defaults(c2600_t *router) { vm_instance_t *vm = router->vm; n_eth_addr_t *m; m_uint16_t pid; /* Set platform slots characteristics */ vm->nr_slots = C2600_MAX_NM_BAYS; vm->slots_type = CISCO_CARD_TYPE_NM; vm->slots_drivers = nm_drivers; pid = (m_uint16_t)getpid(); /* Generate a chassis MAC address based on the instance ID */ m = &router->mac_addr; m->eth_addr_byte[0] = vm_get_mac_addr_msb(vm); m->eth_addr_byte[1] = vm->instance_id & 0xFF; m->eth_addr_byte[2] = pid >> 8; m->eth_addr_byte[3] = pid & 0xFF; m->eth_addr_byte[4] = 0x00; m->eth_addr_byte[5] = 0x00; router->board_id[0] = 0x00; c2600_init_eeprom_groups(router); c2600_mainboard_set_type(router,C2600_DEFAULT_MAINBOARD); c2600_burn_mac_addr(router,&router->mac_addr); vm->ram_mmap = C2600_DEFAULT_RAM_MMAP; vm->ram_size = C2600_DEFAULT_RAM_SIZE; vm->rom_size = C2600_DEFAULT_ROM_SIZE; vm->nvram_size = C2600_DEFAULT_NVRAM_SIZE; vm->conf_reg_setup = C2600_DEFAULT_CONF_REG; vm->clock_divisor = C2600_DEFAULT_CLOCK_DIV; vm->nvram_rom_space = C2600_NVRAM_ROM_RES_SIZE; vm->nm_iomem_size = C2600_DEFAULT_IOMEM_SIZE; vm->pcmcia_disk_size[0] = C2600_DEFAULT_DISK0_SIZE; vm->pcmcia_disk_size[1] = C2600_DEFAULT_DISK1_SIZE; } /* Set an IRQ */ static void c2600_set_irq(vm_instance_t *vm,u_int irq) { c2600_t *router = VM_C2600(vm); cpu_ppc_t *cpu = CPU_PPC32(vm->boot_cpu); u_int slot,port; switch(irq) { case C2600_VTIMER_IRQ: mpc860_set_pending_irq(router->mpc_data,30); break; case C2600_DUART_IRQ: mpc860_set_pending_irq(router->mpc_data,29); break; case C2600_NETIO_IRQ: mpc860_set_pending_irq(router->mpc_data,25); break; case C2600_PA_MGMT_IRQ: mpc860_set_pending_irq(router->mpc_data,27); break; case C2600_NETIO_IRQ_BASE ... C2600_NETIO_IRQ_END: c2600_net_irq_get_slot_port(irq,&slot,&port); dev_c2600_iofpga_net_set_irq(router->iofpga_data,slot,port); break; /* IRQ test */ case 255: mpc860_set_pending_irq(router->mpc_data,24); break; } if (vm->irq_idle_preempt[irq]) cpu_idle_break_wait(cpu->gen); } /* Clear an IRQ */ static void c2600_clear_irq(vm_instance_t *vm,u_int irq) { c2600_t *router = VM_C2600(vm); u_int slot,port; switch(irq) { case C2600_VTIMER_IRQ: mpc860_clear_pending_irq(router->mpc_data,30); break; case C2600_DUART_IRQ: mpc860_clear_pending_irq(router->mpc_data,29); break; case C2600_NETIO_IRQ: mpc860_clear_pending_irq(router->mpc_data,25); break; case C2600_PA_MGMT_IRQ: mpc860_clear_pending_irq(router->mpc_data,27); break; case C2600_NETIO_IRQ_BASE ... C2600_NETIO_IRQ_END: c2600_net_irq_get_slot_port(irq,&slot,&port); dev_c2600_iofpga_net_clear_irq(router->iofpga_data,slot,port); break; /* IRQ test */ case 255: mpc860_clear_pending_irq(router->mpc_data,24); break; } } /* Initialize the C2600 Platform */ static int c2600_init_platform(c2600_t *router) { vm_instance_t *vm = router->vm; vm_obj_t *obj; cpu_ppc_t *cpu; cpu_gen_t *gen; /* Copy config register setup into "active" config register */ vm->conf_reg = vm->conf_reg_setup; /* Create Console and AUX ports */ vm_init_vtty(vm); /* Create a CPU group */ vm->cpu_group = cpu_group_create("System CPU"); /* Initialize the virtual PowerPC processor */ if (!(gen = cpu_create(vm,CPU_TYPE_PPC32,0))) { vm_error(vm,"unable to create CPU!\n"); return(-1); } cpu = CPU_PPC32(gen); /* Add this CPU to the system CPU group */ cpu_group_add(vm->cpu_group,gen); vm->boot_cpu = gen; /* Set processor ID */ ppc32_set_pvr(cpu,0x00500202); /* Mark the Network IO interrupt as high priority */ vm->irq_idle_preempt[C2600_NETIO_IRQ] = TRUE; vm->irq_idle_preempt[C2600_DUART_IRQ] = TRUE; /* Copy some parameters from VM to CPU (idle PC, ...) */ cpu->idle_pc = vm->idle_pc; if (vm->timer_irq_check_itv) cpu->timer_irq_check_itv = vm->timer_irq_check_itv; /* Remote emulator control */ dev_remote_control_init(vm,0xf6000000,0x1000); /* MPC860 */ cpu->mpc860_immr = C2600_MPC860_ADDR; if (dev_mpc860_init(vm,"MPC860",C2600_MPC860_ADDR,0x10000) == -1) return(-1); if (!(obj = vm_object_find(router->vm,"MPC860"))) return(-1); router->mpc_data = obj->data; /* IO FPGA */ if (dev_c2600_iofpga_init(router,C2600_IOFPGA_ADDR,0x10000) == -1) return(-1); if (!(obj = vm_object_find(router->vm,"io_fpga"))) return(-1); router->iofpga_data = obj->data; /* Initialize the chassis */ if (c2600_init(router) == -1) return(-1); /* Initialize RAM */ vm_ram_init(vm,0x00000000ULL); /* Initialize ROM */ if (!vm->rom_filename) { /* use embedded ROM */ dev_rom_init(vm,"rom",C2600_ROM_ADDR,512*1024, ppc32_microcode,ppc32_microcode_len); } else { /* use alternate ROM */ dev_ram_init(vm,"rom",TRUE,TRUE,NULL,FALSE,C2600_ROM_ADDR,512*1024); } /* RAM aliasing */ dev_create_ram_alias(vm,"ram_alias","ram",0x80000000,vm->ram_size*1048576); /* NVRAM */ dev_ram_init(vm,"nvram",TRUE,FALSE,NULL,FALSE, C2600_NVRAM_ADDR,vm->nvram_size*4096); c2600_nvram_check_empty_config(vm); /* Bootflash */ dev_bootflash_init(vm,"flash0","c2600-bootflash-8mb", C2600_FLASH_ADDR); dev_bootflash_init(vm,"flash1","c2600-bootflash-8mb", C2600_FLASH_ADDR+0x800000); /* Initialize the NS16552 DUART */ dev_ns16552_init(vm,C2600_DUART_ADDR,0x1000,0,C2600_DUART_IRQ, vm->vtty_con,vm->vtty_aux); /* Initialize Network Modules */ if (vm_slot_init_all(vm) == -1) return(-1); /* Show device list */ c2600_show_hardware(router); return(0); } static struct ppc32_bat_prog bat_array[] = { { PPC32_IBAT_IDX, 0, 0xfff0001e, 0xfff00001 }, { PPC32_IBAT_IDX, 1, 0x00001ffe, 0x00000001 }, { PPC32_IBAT_IDX, 2, 0x00000000, 0xee3e0072 }, { PPC32_IBAT_IDX, 3, 0x80001ffe, 0x00000001 }, { PPC32_DBAT_IDX, 0, 0x80001ffe, 0x00000042 }, { PPC32_DBAT_IDX, 1, 0x00001ffe, 0x0000002a }, { PPC32_DBAT_IDX, 2, 0x40007ffe, 0x4000002a }, { PPC32_DBAT_IDX, 3, 0xf0001ffe, 0xf000002a }, { -1, -1, 0, 0 }, }; /* Boot the IOS image */ int c2600_boot_ios(c2600_t *router) { vm_instance_t *vm = router->vm; cpu_ppc_t *cpu; if (!vm->boot_cpu) return(-1); /* Suspend CPU activity since we will restart directly from ROM */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Reset the boot CPU */ cpu = CPU_PPC32(vm->boot_cpu); ppc32_reset(cpu); /* Adjust stack pointer */ cpu->gpr[1] |= 0x80000000; /* Load BAT registers */ printf("Loading BAT registers\n"); ppc32_load_bat_array(cpu,bat_array); cpu->msr |= PPC32_MSR_IR|PPC32_MSR_DR; /* IRQ routing */ vm->set_irq = c2600_set_irq; vm->clear_irq = c2600_clear_irq; /* Load IOS image */ if (ppc32_load_elf_image(cpu,vm->ios_image, (vm->ghost_status == VM_GHOST_RAM_USE), &vm->ios_entry_point) < 0) { vm_error(vm,"failed to load Cisco IOS image '%s'.\n",vm->ios_image); return(-1); } /* Launch the simulation */ printf("\nC2600 '%s': starting simulation (CPU0 IA=0x%8.8x), " "JIT %sabled.\n", vm->name,cpu->ia,vm->jit_use ? "en":"dis"); vm_log(vm,"C2600_BOOT", "starting instance (CPU0 PC=0x%8.8x,idle_pc=0x%8.8x,JIT %s)\n", cpu->ia,cpu->idle_pc,vm->jit_use ? "on":"off"); /* Start main CPU */ if (vm->ghost_status != VM_GHOST_RAM_GENERATE) { vm->status = VM_STATUS_RUNNING; cpu_start(vm->boot_cpu); } else { vm->status = VM_STATUS_SHUTDOWN; } return(0); } /* Initialize a Cisco 2600 instance */ static int c2600_init_instance(vm_instance_t *vm) { c2600_t *router = VM_C2600(vm); m_uint32_t rom_entry_point; cpu_ppc_t *cpu0; if (!vm->ios_image) { vm_error(vm,"no Cisco IOS image defined."); return(-1); } /* Initialize the C2600 platform */ if (c2600_init_platform(router) == -1) { vm_error(vm,"unable to initialize the platform hardware.\n"); return(-1); } /* Load IOS configuration files */ if (vm->ios_startup_config != NULL || vm->ios_private_config != NULL) { vm_nvram_push_config(vm,vm->ios_startup_config,vm->ios_private_config); vm->conf_reg &= ~0x40; } /* Load ROM (ELF image or embedded) */ cpu0 = CPU_PPC32(vm->boot_cpu); rom_entry_point = (m_uint32_t)PPC32_ROM_START; if ((vm->rom_filename != NULL) && (ppc32_load_elf_image(cpu0,vm->rom_filename,0,&rom_entry_point) < 0)) { vm_error(vm,"unable to load alternate ROM '%s', " "fallback to embedded ROM.\n\n",vm->rom_filename); vm->rom_filename = NULL; } return(c2600_boot_ios(router)); } /* Stop a Cisco 2600 instance */ int c2600_stop_instance(vm_instance_t *vm) { printf("\nC2600 '%s': stopping simulation.\n",vm->name); vm_log(vm,"C2600_STOP","stopping simulation.\n"); /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } } /* Free resources that were used during execution to emulate hardware */ vm_slot_shutdown_all(vm); vm_hardware_shutdown(vm); /* Cleanup */ VM_C2600(vm)->iofpga_data = NULL; VM_C2600(vm)->mpc_data = NULL; return(0); } /* Get MAC address MSB */ static u_int c2600_get_mac_addr_msb(void) { return(0xC8); } /* Parse specific options for the Cisco 2600 platform */ static int c2600_cli_parse_options(vm_instance_t *vm,int option) { c2600_t *router = VM_C2600(vm); switch(option) { /* IO memory reserved for NMs (in percents!) */ case OPT_IOMEM_SIZE: vm->nm_iomem_size = 0x8000 | atoi(optarg); break; /* Mainboard type */ case 't': c2600_mainboard_set_type(router,optarg); break; /* Set the base MAC address */ case 'm': if (!c2600_chassis_set_mac_addr(router,optarg)) printf("MAC address set to '%s'.\n",optarg); break; /* Set the System ID */ case 'I': if (!c2600_set_system_id(router,optarg)) printf("System ID set to '%s'.\n",optarg); break; /* Unknown option */ default: return(-1); } return(0); } /* Show specific CLI options */ static void c2600_cli_show_options(vm_instance_t *vm) { printf(" --iomem-size : IO memory (in percents, default: %u)\n" " -p : Define a Network Module\n" " -I : Set Processor Board Serial Number\n" " -s : Bind a Network IO interface to a " "Network Module\n", vm->nm_iomem_size); } /* Platform definition */ static vm_platform_t c2600_platform = { "c2600", "C2600", "2600", c2600_create_instance, c2600_delete_instance, c2600_init_instance, c2600_stop_instance, NULL, NULL, c2600_nvram_extract_config, c2600_nvram_push_config, c2600_get_mac_addr_msb, c2600_save_config, c2600_cli_parse_options, c2600_cli_show_options, c2600_mainboard_show_drivers, }; /* Register the c2600 platform */ int c2600_platform_register(void) { if (vm_platform_register(&c2600_platform) == -1) return(-1); return(hypervisor_c2600_init(&c2600_platform)); } dynamips-0.2.14/common/dev_c2600.h000066400000000000000000000116311241034141600164510ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Generic Cisco 2600 routines and definitions (EEPROM,...). */ #ifndef __DEV_C2600_H__ #define __DEV_C2600_H__ #include #include "utils.h" #include "net.h" #include "device.h" #include "pci_dev.h" #include "nmc93cX6.h" #include "net_io.h" #include "dev_mpc860.h" #include "vm.h" /* Default C2600 parameters */ #define C2600_DEFAULT_MAINBOARD "2610" #define C2600_DEFAULT_RAM_SIZE 64 #define C2600_DEFAULT_ROM_SIZE 2 #define C2600_DEFAULT_NVRAM_SIZE 128 #define C2600_DEFAULT_CONF_REG 0x2102 #define C2600_DEFAULT_CLOCK_DIV 8 #define C2600_DEFAULT_RAM_MMAP 1 #define C2600_DEFAULT_DISK0_SIZE 0 #define C2600_DEFAULT_DISK1_SIZE 0 #define C2600_DEFAULT_IOMEM_SIZE 15 /* Percents! */ /* 2600 characteristics: 1 NM + mainboard, 2 onboard WIC slots */ #define C2600_MAX_NM_BAYS 2 #define C2600_MAX_WIC_BAYS 2 /* C2600 Virtual Timer Interrupt */ #define C2600_VTIMER_IRQ 0 /* C2600 DUART Interrupt */ #define C2600_DUART_IRQ 1 /* C2600 Network I/O Interrupt */ #define C2600_NETIO_IRQ 2 /* C2600 PA Management Interrupt */ #define C2600_PA_MGMT_IRQ 3 /* Network IRQ */ #define C2600_NETIO_IRQ_BASE 32 #define C2600_NETIO_IRQ_PORT_BITS 2 #define C2600_NETIO_IRQ_PORT_MASK ((1 << C2600_NETIO_IRQ_PORT_BITS) - 1) #define C2600_NETIO_IRQ_PER_SLOT (1 << C2600_NETIO_IRQ_PORT_BITS) #define C2600_NETIO_IRQ_END \ (C2600_NETIO_IRQ_BASE + (C2600_MAX_NM_BAYS * C2600_NETIO_IRQ_PER_SLOT) - 1) /* C2600 common device addresses */ #define C2600_FLASH_ADDR 0x60000000ULL #define C2600_WIC_ADDR 0x67000000ULL #define C2600_IOFPGA_ADDR 0x67400000ULL #define C2600_NVRAM_ADDR 0x67c00000ULL #define C2600_PCICTRL_ADDR 0x68000000ULL #define C2600_MPC860_ADDR 0x68010000ULL #define C2600_DUART_ADDR 0xffe00000ULL #define C2600_ROM_ADDR 0xfff00000ULL /* WIC interval in address space */ #define C2600_WIC_SIZE 0x400 /* Reserved space for ROM in NVRAM */ #define C2600_NVRAM_ROM_RES_SIZE 2048 /* C2600 ELF Platform ID */ #define C2600_ELF_MACHINE_ID 0x2b #define VM_C2600(vm) ((c2600_t *)vm->hw_data) /* C2600 router */ typedef struct c2600_router c2600_t; /* C2600 router */ struct c2600_router { /* Mainboard type (2610, 2611, etc) */ char *mainboard_type; /* Is the router a XM model ? */ int xm_model; /* Chassis MAC address */ n_eth_addr_t mac_addr; char board_id[20]; /* Associated VM instance */ vm_instance_t *vm; /* I/O FPGA */ struct c2600_iofpga_data *iofpga_data; /* * Mainboard EEPROM. * It can be modified to change the chassis MAC address. */ struct cisco_eeprom mb_eeprom; struct nmc93cX6_group mb_eeprom_group; /* Network Module EEPROM */ struct nmc93cX6_group nm_eeprom_group; /* MPC860 device private data */ struct mpc860_data *mpc_data; }; /* Get WIC device address for the specified onboard port */ int c2600_get_onboard_wic_addr(u_int slot,m_uint64_t *phys_addr); /* Set EEPROM for the specified slot */ int c2600_set_slot_eeprom(c2600_t *router,u_int slot, struct cisco_eeprom *eeprom); /* Get network IRQ for specified slot/port */ u_int c2600_net_irq_for_slot_port(u_int slot,u_int port); /* Set mainboard type */ int c2600_mainboard_set_type(c2600_t *router,char *mainboard_type); /* Set chassis MAC address */ int c2600_chassis_set_mac_addr(c2600_t *router,char *mac_addr); /* Set the system id */ int c2600_set_system_id(c2600_t *router,char *id); /* Burn the system id into the appropriate eeprom if possible */ int c2600_refresh_systemid(c2600_t *router); /* Show C2600 hardware info */ void c2600_show_hardware(c2600_t *router); /* Initialize EEPROM groups */ void c2600_init_eeprom_groups(c2600_t *router); /* Create the c2600 PCI controller device */ int dev_c2600_pci_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len, struct pci_bus *bus); /* dev_c2600_iofpga_init() */ int dev_c2600_iofpga_init(c2600_t *router,m_uint64_t paddr,m_uint32_t len); /* Register the c2600 platform */ int c2600_platform_register(void); /* Hypervisor C2600 initialization */ extern int hypervisor_c2600_init(vm_platform_t *platform); /* NM drivers */ extern struct cisco_card_driver dev_c2600_mb1e_eth_driver; extern struct cisco_card_driver dev_c2600_mb2e_eth_driver; extern struct cisco_card_driver dev_c2600_mb1fe_eth_driver; extern struct cisco_card_driver dev_c2600_mb2fe_eth_driver; extern struct cisco_card_driver dev_c2600_nm_1e_driver; extern struct cisco_card_driver dev_c2600_nm_4e_driver; extern struct cisco_card_driver dev_c2600_nm_1fe_tx_driver; extern struct cisco_card_driver dev_c2600_nm_16esw_driver; extern struct cisco_card_driver dev_c2600_nm_nam_driver; extern struct cisco_card_driver dev_c2600_nm_cids_driver; /* WIC drivers */ extern struct cisco_card_driver *dev_c2600_mb_wic_drivers[]; #endif dynamips-0.2.14/common/dev_c2600_eth.c000066400000000000000000000252661241034141600173150ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Ethernet Network Modules. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "vm.h" #include "dev_am79c971.h" #include "dev_nm_16esw.h" #include "dev_c2600.h" /* Multi-Ethernet NM with Am79c971 chips */ struct nm_eth_data { u_int nr_port; struct am79c971_data *port[8]; }; /* Return sub-slot info for integrated WIC slots (on motherboard) */ static int dev_c2600_mb_get_sub_info(vm_instance_t *vm,struct cisco_card *card, u_int port_id, struct cisco_card_driver ***drv_array, u_int *subcard_type) { /* 2 integrated WIC slots */ if ((port_id & 0x0F) >= 2) return(-1); *drv_array = dev_c2600_mb_wic_drivers; *subcard_type = CISCO_CARD_TYPE_WIC; return(0); } /* * dev_c2600_nm_eth_init() * * Add an Ethernet Network Module into specified slot. */ static int dev_c2600_nm_eth_init(vm_instance_t *vm,struct cisco_card *card, int nr_port,int interface_type, const struct cisco_eeprom *eeprom) { struct nm_eth_data *data; u_int slot = card->slot_id; int i; /* Allocate the private data structure */ if (!(data = malloc(sizeof(*data)))) { vm_error(vm,"%s: out of memory.\n",card->dev_name); return(-1); } memset(data,0,sizeof(*data)); data->nr_port = nr_port; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,eeprom); c2600_set_slot_eeprom(VM_C2600(vm),slot,&card->eeprom); /* Create the AMD Am971c971 chip(s) */ for(i=0;inr_port;i++) { data->port[i] = dev_am79c971_init(vm,card->dev_name,interface_type, card->pci_bus,i+(slot * 4), c2600_net_irq_for_slot_port(slot,i)); } /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove an Ethernet NM from the specified slot */ static int dev_c2600_nm_eth_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct nm_eth_data *data = card->drv_info; int i; /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c2600_set_slot_eeprom(VM_C2600(vm),card->slot_id,NULL); /* Remove the AMD Am79c971 chips */ for(i=0;inr_port;i++) dev_am79c971_remove(data->port[i]); free(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c2600_nm_eth_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct nm_eth_data *d = card->drv_info; if (!d || (port_id >= d->nr_port)) return(-1); dev_am79c971_set_nio(d->port[port_id],nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c2600_nm_eth_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct nm_eth_data *d = card->drv_info; if (!d || (port_id >= d->nr_port)) return(-1); dev_am79c971_unset_nio(d->port[port_id]); return(0); } /* ====================================================================== */ /* Cisco 2600 mainboard with 1 Ethernet port */ /* ====================================================================== */ static int dev_c2600_mb1e_eth_init(vm_instance_t *vm,struct cisco_card *card) { return(dev_c2600_nm_eth_init(vm,card,1,AM79C971_TYPE_10BASE_T,NULL)); } /* ====================================================================== */ /* Cisco 2600 mainboard with 1 Ethernet port */ /* ====================================================================== */ static int dev_c2600_mb2e_eth_init(vm_instance_t *vm,struct cisco_card *card) { return(dev_c2600_nm_eth_init(vm,card,2,AM79C971_TYPE_10BASE_T,NULL)); } /* ====================================================================== */ /* Cisco 2600 mainboard with 1 FastEthernet port */ /* ====================================================================== */ static int dev_c2600_mb1fe_eth_init(vm_instance_t *vm,struct cisco_card *card) { return(dev_c2600_nm_eth_init(vm,card,1,AM79C971_TYPE_100BASE_TX,NULL)); } /* ====================================================================== */ /* Cisco 2600 mainboard with 2 FastEthernet ports */ /* ====================================================================== */ static int dev_c2600_mb2fe_eth_init(vm_instance_t *vm,struct cisco_card *card) { return(dev_c2600_nm_eth_init(vm,card,2,AM79C971_TYPE_100BASE_TX,NULL)); } /* ====================================================================== */ /* NM-1E */ /* ====================================================================== */ /* * dev_c2600_nm_1e_init() * * Add a NM-1E Network Module into specified slot. */ static int dev_c2600_nm_1e_init(vm_instance_t *vm,struct cisco_card *card) { return(dev_c2600_nm_eth_init(vm,card,1,AM79C971_TYPE_10BASE_T, cisco_eeprom_find_nm("NM-1E"))); } /* ====================================================================== */ /* NM-4E */ /* ====================================================================== */ /* * dev_c2600_nm_4e_init() * * Add a NM-4E Network Module into specified slot. */ static int dev_c2600_nm_4e_init(vm_instance_t *vm,struct cisco_card *card) { return(dev_c2600_nm_eth_init(vm,card,4,AM79C971_TYPE_10BASE_T, cisco_eeprom_find_nm("NM-4E"))); } /* ====================================================================== */ /* NM-1FE-TX */ /* ====================================================================== */ /* * dev_c2600_nm_1fe_tx_init() * * Add a NM-1FE-TX Network Module into specified slot. */ static int dev_c2600_nm_1fe_tx_init(vm_instance_t *vm,struct cisco_card *card) { return(dev_c2600_nm_eth_init(vm,card,1,AM79C971_TYPE_100BASE_TX, cisco_eeprom_find_nm("NM-1FE-TX"))); } /* ====================================================================== */ /* NM-16ESW */ /* ====================================================================== */ /* Add a NM-16ESW */ static int dev_c2600_nm_16esw_init(vm_instance_t *vm,struct cisco_card *card) { struct nm_16esw_data *data; u_int slot = card->slot_id; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_nm("NM-16ESW")); dev_nm_16esw_burn_mac_addr(vm,slot,&card->eeprom); c2600_set_slot_eeprom(VM_C2600(vm),slot,&card->eeprom); /* Create the device */ data = dev_nm_16esw_init(vm,card->dev_name,slot,card->pci_bus,4, c2600_net_irq_for_slot_port(slot,0)); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a NM-16ESW from the specified slot */ static int dev_c2600_nm_16esw_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct nm_16esw_data *data = card->drv_info; /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c2600_set_slot_eeprom(VM_C2600(vm),card->slot_id,NULL); /* Remove the BCM5600 chip */ dev_nm_16esw_remove(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c2600_nm_16esw_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_set_nio(d,port_id,nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c2600_nm_16esw_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_unset_nio(d,port_id); return(0); } /* Show debug info */ static int dev_c2600_nm_16esw_show_info(vm_instance_t *vm,struct cisco_card *card) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_show_info(d); return(0); } /* ====================================================================== */ /* Cisco 2600 mainboard 1 Ethernet port driver */ struct cisco_card_driver dev_c2600_mb1e_eth_driver = { "CISCO2600-MB-1E", 1, 2, dev_c2600_mb1e_eth_init, dev_c2600_nm_eth_shutdown, dev_c2600_mb_get_sub_info, dev_c2600_nm_eth_set_nio, dev_c2600_nm_eth_unset_nio, NULL, }; /* Cisco 2600 mainboard 2 Ethernet port driver */ struct cisco_card_driver dev_c2600_mb2e_eth_driver = { "CISCO2600-MB-2E", 1, 2, dev_c2600_mb2e_eth_init, dev_c2600_nm_eth_shutdown, dev_c2600_mb_get_sub_info, dev_c2600_nm_eth_set_nio, dev_c2600_nm_eth_unset_nio, NULL, }; /* Cisco 2600 mainboard 1 FastEthernet port driver */ struct cisco_card_driver dev_c2600_mb1fe_eth_driver = { "CISCO2600-MB-1FE", 1, 2, dev_c2600_mb1fe_eth_init, dev_c2600_nm_eth_shutdown, dev_c2600_mb_get_sub_info, dev_c2600_nm_eth_set_nio, dev_c2600_nm_eth_unset_nio, NULL, }; /* Cisco 2600 mainboard 2 Ethernet port driver */ struct cisco_card_driver dev_c2600_mb2fe_eth_driver = { "CISCO2600-MB-2FE", 1, 2, dev_c2600_mb2fe_eth_init, dev_c2600_nm_eth_shutdown, dev_c2600_mb_get_sub_info, dev_c2600_nm_eth_set_nio, dev_c2600_nm_eth_unset_nio, NULL, }; /* NM-1FE-TX driver */ struct cisco_card_driver dev_c2600_nm_1fe_tx_driver = { "NM-1FE-TX", 1, 0, dev_c2600_nm_1fe_tx_init, dev_c2600_nm_eth_shutdown, NULL, dev_c2600_nm_eth_set_nio, dev_c2600_nm_eth_unset_nio, NULL, }; /* NM-1E driver */ struct cisco_card_driver dev_c2600_nm_1e_driver = { "NM-1E", 1, 0, dev_c2600_nm_1e_init, dev_c2600_nm_eth_shutdown, NULL, dev_c2600_nm_eth_set_nio, dev_c2600_nm_eth_unset_nio, NULL, }; /* NM-4E driver */ struct cisco_card_driver dev_c2600_nm_4e_driver = { "NM-4E", 1, 0, dev_c2600_nm_4e_init, dev_c2600_nm_eth_shutdown, NULL, dev_c2600_nm_eth_set_nio, dev_c2600_nm_eth_unset_nio, NULL, }; /* NM-16ESW driver */ struct cisco_card_driver dev_c2600_nm_16esw_driver = { "NM-16ESW", 1, 0, dev_c2600_nm_16esw_init, dev_c2600_nm_16esw_shutdown, NULL, dev_c2600_nm_16esw_set_nio, dev_c2600_nm_16esw_unset_nio, dev_c2600_nm_16esw_show_info, }; dynamips-0.2.14/common/dev_c2600_iofpga.c000066400000000000000000000210271241034141600177710ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) */ #include #include #include #include #include #include #include #include #include "ptask.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_vtty.h" #include "nmc93cX6.h" #include "dev_mpc860.h" #include "dev_c2600.h" /* Debugging flags */ #define DEBUG_UNKNOWN 1 #define DEBUG_ACCESS 0 #define DEBUG_NET_IRQ 0 /* Definitions for Mainboard EEPROM */ #define EEPROM_MB_DOUT 3 #define EEPROM_MB_DIN 2 #define EEPROM_MB_CLK 1 #define EEPROM_MB_CS 0 /* Definitions for Network Modules EEPROM */ #define EEPROM_NM_DOUT 7 #define EEPROM_NM_DIN 6 #define EEPROM_NM_CLK 2 #define EEPROM_NM_CS 4 /* Network IRQ distribution */ static u_int net_irq_dist[C2600_MAX_NM_BAYS] = { 4, /* reg 0x08, bits 4-5 */ 0, /* reg 0x08, bits 0-3 */ }; /* IO FPGA structure */ struct c2600_iofpga_data { vm_obj_t vm_obj; struct vdevice dev; c2600_t *router; /* Network Interrupt status */ m_uint8_t net_irq_status; /* Interrupt mask */ m_uint16_t intr_mask; /* WIC SPI selection */ m_uint8_t wic_select; }; /* Mainboard EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_mb_def = { EEPROM_MB_CLK, EEPROM_MB_CS, EEPROM_MB_DIN, EEPROM_MB_DOUT, }; /* Mainboard EEPROM */ static const struct nmc93cX6_group eeprom_mb_group = { EEPROM_TYPE_NMC93C46, 1, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "Mainboard EEPROM", { &eeprom_mb_def }, }; /* NM EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_nm_def = { EEPROM_NM_CLK, EEPROM_NM_CS, EEPROM_NM_DIN, EEPROM_NM_DOUT, }; /* NM EEPROM */ static const struct nmc93cX6_group eeprom_nm_group = { EEPROM_TYPE_NMC93C46, 1, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "NM EEPROM", { &eeprom_nm_def }, }; /* Update network interrupt status */ static inline void dev_c2600_iofpga_net_update_irq(struct c2600_iofpga_data *d) { if (d->net_irq_status) { vm_set_irq(d->router->vm,C2600_NETIO_IRQ); } else { vm_clear_irq(d->router->vm,C2600_NETIO_IRQ); } } /* Trigger a Network IRQ for the specified slot/port */ void dev_c2600_iofpga_net_set_irq(struct c2600_iofpga_data *d, u_int slot,u_int port) { #if DEBUG_NET_IRQ vm_log(d->router->vm,"IO_FPGA","setting NetIRQ for slot %u port %u\n", slot,port); #endif d->net_irq_status |= 1 << (net_irq_dist[slot] + port); dev_c2600_iofpga_net_update_irq(d); } /* Clear a Network IRQ for the specified slot/port */ void dev_c2600_iofpga_net_clear_irq(struct c2600_iofpga_data *d, u_int slot,u_int port) { #if DEBUG_NET_IRQ vm_log(d->router->vm,"IO_FPGA","clearing NetIRQ for slot %u port %u\n", slot,port); #endif d->net_irq_status &= ~(1 << (net_irq_dist[slot] + port)); dev_c2600_iofpga_net_update_irq(d); } /* Callback for MPC860 SPI Transmit */ static void dev_c2600_mpc860_spi_tx_callback(struct mpc860_data *mpc_data, u_char *buffer,u_int len, void *user_arg) { struct c2600_iofpga_data *d = user_arg; struct cisco_eeprom *eeprom; u_char reply_buf[4]; u_int wic_port; u_int eeprom_offset; if (d->wic_select & 0x20) wic_port = 0x10; else if (d->wic_select & 0x08) wic_port = 0x20; else { vm_error(d->router->vm,"unknown value for wic_select (0x%8.8x)\n", d->wic_select); wic_port = 0; } /* No WIC in slot or no EEPROM: fake an empty EEPROM */ if (!wic_port || !(eeprom = vm_slot_get_eeprom(d->router->vm,0,wic_port))) { memset(reply_buf,0xFF,sizeof(reply_buf)); mpc860_spi_receive(mpc_data,reply_buf,sizeof(reply_buf)); return; } /* Read request: 0x03 offset 0x00 0x00 */ eeprom_offset = buffer[1]; reply_buf[0] = 0; reply_buf[1] = 0; cisco_eeprom_get_byte(eeprom,eeprom_offset,&reply_buf[2]); cisco_eeprom_get_byte(eeprom,eeprom_offset+1,&reply_buf[3]); mpc860_spi_receive(mpc_data,reply_buf,sizeof(reply_buf)); } /* * dev_c2600_iofpga_access() */ static void * dev_c2600_iofpga_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct c2600_iofpga_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0x0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA","reading reg 0x%x at pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA", "writing reg 0x%x at pc=0x%llx, data=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),*data,op_size); } #endif switch(offset) { case 0x04: if (op_type == MTS_READ) *data = 0x00; break; /* * Network Interrupt. * * Bit 0-3: slot 1. * Bit 4: slot 0 (MB), port 0 * Bit 5: slot 0 (MB), port 1 * Other: AIM ? (error messages displayed) */ case 0x08: if (op_type == MTS_READ) *data = d->net_irq_status; break; case 0x10: if (op_type == MTS_READ) *data = d->wic_select; else { d->wic_select = *data; } break; case 0x14: if (op_type == MTS_READ) *data = 0; //0xFFFFFFFF; break; /* * Flash Related: 0x1y * * Bit 1: card present in slot 0 / WIC 0. * Bit 2: card present in slot 0 / WIC 1. * * Other bits unknown. */ case 0x0c: if (op_type == MTS_READ) { *data = 0x10; /* check WIC 0 */ if (vm_slot_check_eeprom(d->router->vm,0,0x10)) *data |= 0x02; /* check WIC 1 */ if (vm_slot_check_eeprom(d->router->vm,0,0x20)) *data |= 0x04; } break; /* NM EEPROM */ case 0x1c: if (op_type == MTS_WRITE) nmc93cX6_write(&d->router->nm_eeprom_group,(u_int)(*data)); else *data = nmc93cX6_read(&d->router->nm_eeprom_group); break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA", "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA", "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* Shutdown the IO FPGA device */ static void dev_c2600_iofpga_shutdown(vm_instance_t *vm,struct c2600_iofpga_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* * dev_c2600_iofpga_init() */ int dev_c2600_iofpga_init(c2600_t *router,m_uint64_t paddr,m_uint32_t len) { vm_instance_t *vm = router->vm; struct c2600_iofpga_data *d; /* Allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"IO_FPGA: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); d->router = router; vm_object_init(&d->vm_obj); d->vm_obj.name = "io_fpga"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_c2600_iofpga_shutdown; /* Set device properties */ dev_init(&d->dev); d->dev.name = "io_fpga"; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.priv_data = d; d->dev.handler = dev_c2600_iofpga_access; /* Initialize the MPC860 SPI TX callback to read mainboard WIC EEPROMs */ mpc860_spi_set_tx_callback(router->mpc_data, dev_c2600_mpc860_spi_tx_callback,d); /* Map this device to the VM */ vm_bind_device(router->vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } /* Initialize EEPROM groups */ void c2600_init_eeprom_groups(c2600_t *router) { /* Initialize Mainboard EEPROM */ router->mb_eeprom_group = eeprom_mb_group; router->mb_eeprom_group.eeprom[0] = &router->mb_eeprom; router->mb_eeprom.data = NULL; router->mb_eeprom.len = 0; /* EEPROM for NM slot 1 */ router->nm_eeprom_group = eeprom_nm_group; } dynamips-0.2.14/common/dev_c2600_iofpga.h000066400000000000000000000013511241034141600177740ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005-2007 Christophe Fillot (cf@utc.fr) * * Cisco c2600 I/O FPGA. */ #ifndef __DEV_C2600_IOFPGA_H__ #define __DEV_C2600_IOFPGA_H__ /* Forward declaration for IO_FPGA private data */ struct c2600_iofpga_data; /* Trigger a Network IRQ for the specified slot/port */ void dev_c2600_iofpga_net_set_irq(struct c2600_iofpga_data *d, u_int slot,u_int port); /* Clear a Network IRQ for the specified slot/port */ void dev_c2600_iofpga_net_clear_irq(struct c2600_iofpga_data *d, u_int slot,u_int port); /* Create the c2600 I/O FPGA */ int dev_c2600_iofpga_init(c2600_t *router,m_uint64_t paddr,m_uint32_t len); #endif dynamips-0.2.14/common/dev_c2600_pci.c000066400000000000000000000126701241034141600173030ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Cisco 2600 PCI controller. */ #include #include #include #include "utils.h" #include "net.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net_io.h" /* Debugging flags */ #define DEBUG_ACCESS 0 #define DEBUG_UNKNOWN 1 #define DEBUG_PCI 1 #define C2600_PCI_BRIDGE_VENDOR_ID 0x10ee #define C2600_PCI_BRIDGE_PRODUCT_ID 0x4013 /* C2600 PCI controller */ struct c2600_pci_data { char *name; vm_obj_t vm_obj; struct vdevice dev; struct pci_device *pci_dev; vm_instance_t *vm; struct pci_bus *bus; m_uint32_t bridge_bar0,bridge_bar1; }; /* * dev_c2600_pci_access() */ void *dev_c2600_pci_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct c2600_pci_data *d = dev->priv_data; struct pci_device *pci_dev; u_int bus,device,function,reg; if (op_type == MTS_READ) *data = 0x0; bus = 0; device = (offset >> 12) & 0x0F; function = (offset >> 8) & 0x07; reg = offset & 0xFF; /* Find the corresponding PCI device */ pci_dev = pci_dev_lookup(d->bus,bus,device,function); #if DEBUG_PCI if (op_type == MTS_READ) { cpu_log(cpu,"PCI","read request at pc=0x%llx: " "bus=%d,device=%d,function=%d,reg=0x%2.2x\n", cpu_get_pc(cpu), bus, device, function, reg); } else { cpu_log(cpu,"PCI","write request (data=0x%8.8llx) at pc=0x%llx: " "bus=%d,device=%d,function=%d,reg=0x%2.2x\n", *data, cpu_get_pc(cpu), bus, device, function, reg); } #endif if (!pci_dev) { if (op_type == MTS_READ) { cpu_log(cpu,"PCI","read request for unknown device at pc=0x%llx " "(bus=%d,device=%d,function=%d,reg=0x%2.2x).\n", cpu_get_pc(cpu), bus, device, function, reg); } else { cpu_log(cpu,"PCI","write request (data=0x%8.8llx) for unknown device " "at pc=0x%llx (bus=%d,device=%d,function=%d,reg=0x%2.2x).\n", *data, cpu_get_pc(cpu), bus, device, function, reg); } /* Returns an invalid device ID */ if ((op_type == MTS_READ) && (reg == PCI_REG_ID)) *data = 0xffffffff; } else { if (op_type == MTS_WRITE) { if (pci_dev->write_register != NULL) pci_dev->write_register(cpu,pci_dev,reg,*data); } else { if (reg == PCI_REG_ID) *data = (pci_dev->product_id << 16) | pci_dev->vendor_id; else { if (pci_dev->read_register != NULL) *data = pci_dev->read_register(cpu,pci_dev,reg); } } } return NULL; } /* Shutdown the c2600 PCI controller device */ void dev_c2600_pci_shutdown(vm_instance_t *vm,struct c2600_pci_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* PCI bridge read access */ static m_uint32_t dev_c2600_pci_bridge_read(cpu_gen_t *cpu, struct pci_device *dev, int reg) { struct c2600_pci_data *d = dev->priv_data; switch(reg) { case 0x10: return(d->bridge_bar0); case 0x14: return(d->bridge_bar1); default: return(0); } } /* PCI bridge read access */ static void dev_c2600_pci_bridge_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct c2600_pci_data *d = dev->priv_data; switch(reg) { case 0x10: /* BAR0 must be at 0x00000000 for correct RAM access */ if (value != 0x00000000) { vm_error(d->vm,"C2600_PCI", "Trying to set bridge BAR0 at 0x%8.8x!\n", value); } d->bridge_bar0 = value; break; case 0x14: /* BAR1 = byte swapped zone */ if (!d->bridge_bar1) { d->bridge_bar1 = value; /* XXX */ dev_bswap_init(d->vm,"pci_bswap",d->bridge_bar1,0x10000000, 0x00000000); } break; } } /* Create the c2600 PCI controller device */ int dev_c2600_pci_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len, struct pci_bus *bus) { struct c2600_pci_data *d; if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"c2600_pci: unable to create device data.\n"); return(-1); } memset(d,0,sizeof(*d)); d->name = name; d->vm = vm; d->bus = bus; vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_c2600_pci_shutdown; dev_init(&d->dev); d->dev.name = name; d->dev.priv_data = d; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_c2600_pci_access; pci_dev_add(d->bus,"pci_bridge", C2600_PCI_BRIDGE_VENDOR_ID,C2600_PCI_BRIDGE_PRODUCT_ID, 15,0,-1,d, NULL, dev_c2600_pci_bridge_read, dev_c2600_pci_bridge_write); /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_c2600_pcmod.c000066400000000000000000000056251241034141600176340ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * PC Modules NM (NM-NAM, NM-CIDS, ...) for c2600 platforms. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "vm.h" #include "dev_i8255x.h" #include "dev_c2600.h" /* Initialize a NM-NAM in the specified slot */ static int dev_c2600_pcmod_init(vm_instance_t *vm,struct cisco_card *card) { struct i8255x_data *data; u_int slot = card->slot_id; /* * Non-XM models don't have the capability to byte-swap through their * PCI host bridge (required for i82559 data transfers). */ if (!VM_C2600(vm)->xm_model) { vm_error(vm,"%s is not supported in C2600 non-XM models.\n", card->driver->dev_type); return(-1); } /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_nm(card->driver->dev_type)); c2600_set_slot_eeprom(VM_C2600(vm),slot,&card->eeprom); /* Create the Intel i8255x chip */ data = dev_i8255x_init(vm,card->dev_name,0, card->pci_bus,slot * 4, c2600_net_irq_for_slot_port(slot,0)); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a NM PC module from the specified slot */ static int dev_c2600_pcmod_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct i8255x_data *data = card->drv_info; /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c2600_set_slot_eeprom(VM_C2600(vm),card->slot_id,NULL); /* Remove the Intel i2855x chip */ dev_i8255x_remove(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c2600_pcmod_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct i8255x_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); dev_i8255x_set_nio(d,nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c2600_pcmod_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct i8255x_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); dev_i8255x_unset_nio(d); return(0); } /* NM-NAM driver */ struct cisco_card_driver dev_c2600_nm_nam_driver = { "NM-NAM", 0, 0, dev_c2600_pcmod_init, dev_c2600_pcmod_shutdown, NULL, dev_c2600_pcmod_set_nio, dev_c2600_pcmod_unset_nio, NULL, }; /* NM-CIDS driver */ struct cisco_card_driver dev_c2600_nm_cids_driver = { "NM-CIDS", 0, 0, dev_c2600_pcmod_init, dev_c2600_pcmod_shutdown, NULL, dev_c2600_pcmod_set_nio, dev_c2600_pcmod_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c2600_wic.c000066400000000000000000000130171241034141600173060ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * WIC Modules. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "vm.h" #include "dev_mpc860.h" #include "dev_c2600.h" #include "dev_wic_serial.h" /* Get the SCC channel associated to a WIC sub-slot */ static int dev_c2600_mb_wic_get_scc_chan(struct cisco_card *card,u_int port_id, u_int *scc_chan) { u_int cid; cid = card->subslot_id + port_id; switch(cid) { /* WIC 0 port 0 mapped to MPC860 SCC1 */ case 0x10: *scc_chan = 0; break; /* WIC 0 port 1 mapped to MPC860 SCC4 */ case 0x11: *scc_chan = 3; break; /* WIC 1 port 0 mapped to MPC860 SCC2 */ case 0x20: *scc_chan = 1; break; /* WIC 1 port 1 mapped to MPC860 SCC3 */ case 0x21: *scc_chan = 2; break; default: return(-1); } return(0); } /* Initialize a WIC-1T in the specified slot */ static int dev_c2600_mb_wic1t_init(vm_instance_t *vm,struct cisco_card *card) { struct wic_serial_data *wic_data; m_uint64_t phys_addr; u_int wic_id; /* Create the WIC device */ wic_id = (card->subslot_id >> 4) - 1; if (c2600_get_onboard_wic_addr(wic_id,&phys_addr) == -1) { vm_error(vm,"WIC","invalid slot %u (subslot_id=%u)\n", wic_id,card->subslot_id); return(-1); } wic_data = dev_wic_serial_init(vm,card->dev_name,WIC_SERIAL_MODEL_1T, phys_addr,C2600_WIC_SIZE); if (!wic_data) return(-1); /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_wic("WIC-1T")); /* Store device info into the router structure */ card->drv_info = wic_data; return(0); } /* Remove a WIC-1T from the specified slot */ static int dev_c2600_mb_wic1t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the WIC device */ dev_wic_serial_remove(card->drv_info); /* Remove the WIC EEPROM */ cisco_card_unset_eeprom(card); return(0); } /* Bind a Network IO descriptor */ static int dev_c2600_mb_wic1t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { u_int scc_chan; if ((port_id > 0) || (dev_c2600_mb_wic_get_scc_chan(card,port_id,&scc_chan) == -1)) return(-1); return(mpc860_scc_set_nio(VM_C2600(vm)->mpc_data,scc_chan,nio)); } /* Unbind a Network IO descriptor */ static int dev_c2600_mb_wic1t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { u_int scc_chan; if ((port_id > 0) || (dev_c2600_mb_wic_get_scc_chan(card,port_id,&scc_chan) == -1)) return(-1); return(mpc860_scc_unset_nio(VM_C2600(vm)->mpc_data,scc_chan)); } /* Initialize a WIC-2T in the specified slot */ static int dev_c2600_mb_wic2t_init(vm_instance_t *vm,struct cisco_card *card) { struct wic_serial_data *wic_data; m_uint64_t phys_addr; u_int wic_id; /* Create the WIC device */ wic_id = (card->subslot_id >> 4) - 1; if (c2600_get_onboard_wic_addr(wic_id,&phys_addr) == -1) { vm_error(vm,"WIC","invalid slot %u (subslot_id=%u)\n", wic_id,card->subslot_id); return(-1); } wic_data = dev_wic_serial_init(vm,card->dev_name,WIC_SERIAL_MODEL_2T, phys_addr,C2600_WIC_SIZE); if (!wic_data) return(-1); /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_wic("WIC-2T")); /* Store device info into the router structure */ card->drv_info = wic_data; return(0); } /* Remove a WIC-2T from the specified slot */ static int dev_c2600_mb_wic2t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the WIC device */ dev_wic_serial_remove(card->drv_info); /* Remove the WIC EEPROM */ cisco_card_unset_eeprom(card); return(0); } /* Bind a Network IO descriptor */ static int dev_c2600_mb_wic2t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { u_int scc_chan; if ((port_id > 1) || (dev_c2600_mb_wic_get_scc_chan(card,port_id,&scc_chan) == -1)) return(-1); return(mpc860_scc_set_nio(VM_C2600(vm)->mpc_data,scc_chan,nio)); } /* Unbind a Network IO descriptor */ static int dev_c2600_mb_wic2t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { u_int scc_chan; if ((port_id > 1) || (dev_c2600_mb_wic_get_scc_chan(card,port_id,&scc_chan) == -1)) return(-1); return(mpc860_scc_unset_nio(VM_C2600(vm)->mpc_data,scc_chan)); } /* Cisco 2600 WIC-1T driver (for mainboard) */ struct cisco_card_driver dev_c2600_mb_wic1t_driver = { "WIC-1T", 1, 0, dev_c2600_mb_wic1t_init, dev_c2600_mb_wic1t_shutdown, NULL, dev_c2600_mb_wic1t_set_nio, dev_c2600_mb_wic1t_unset_nio, NULL, }; /* Cisco 2600 WIC-2T driver (for mainboard) */ struct cisco_card_driver dev_c2600_mb_wic2t_driver = { "WIC-2T", 1, 0, dev_c2600_mb_wic2t_init, dev_c2600_mb_wic2t_shutdown, NULL, dev_c2600_mb_wic2t_set_nio, dev_c2600_mb_wic2t_unset_nio, NULL, }; /* WIC drivers (mainbord slots) */ struct cisco_card_driver *dev_c2600_mb_wic_drivers[] = { &dev_c2600_mb_wic1t_driver, &dev_c2600_mb_wic2t_driver, NULL, }; dynamips-0.2.14/common/dev_c2691.c000066400000000000000000000471011241034141600164570ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * Patched by Jeremy Grossmann for the GNS3 project (www.gns3.net) * * Generic Cisco 2691 routines and definitions (EEPROM,...). */ #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "pci_io.h" #include "dev_gt.h" #include "cisco_eeprom.h" #include "dev_rom.h" #include "dev_c2691.h" #include "dev_c2691_iofpga.h" #include "dev_vtty.h" #include "registry.h" #include "fs_nvram.h" /* ======================================================================== */ /* EEPROM definitions */ /* ======================================================================== */ /* Cisco 2691 mainboard EEPROM */ static m_uint16_t eeprom_c2691_mainboard_data[] = { 0x04FF, 0xC18B, 0x5858, 0x5858, 0x5858, 0x5858, 0x5858, 0x5809, 0x6640, 0x0258, 0xC046, 0x0320, 0x0025, 0x9002, 0x4246, 0x3085, 0x1C10, 0x8206, 0x80FF, 0xFFFF, 0xFFC4, 0x08FF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFF81, 0xFFFF, 0xFFFF, 0x03FF, 0x04FF, 0xC28B, 0x5858, 0x5858, 0x5858, 0x5858, 0x5858, 0x58C3, 0x0600, 0x1280, 0xD387, 0xC043, 0x0020, 0xC508, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x4100, 0x0101, 0x01FF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; struct cisco_eeprom eeprom_c2691_mainboard = { "C2691 Backplane", eeprom_c2691_mainboard_data, sizeof(eeprom_c2691_mainboard_data)/2, }; /* ======================================================================== */ /* Network Module Drivers */ /* ======================================================================== */ static struct cisco_card_driver *nm_drivers[] = { &dev_c2691_nm_1fe_tx_driver, &dev_c2691_nm_16esw_driver, &dev_c2691_gt96100_fe_driver, &dev_c2691_nm_4t_driver, &dev_c2691_nm_nam_driver, &dev_c2691_nm_cids_driver, NULL, }; /* ======================================================================== */ /* Cisco 2691 router instances */ /* ======================================================================== */ /* Initialize default parameters for a C2691 */ static void c2691_init_defaults(c2691_t *router); /* Directly extract the configuration from the NVRAM device */ static int c2691_nvram_extract_config(vm_instance_t *vm,u_char **startup_config,size_t *startup_len,u_char **private_config,size_t *private_len) { int ret; ret = generic_nvram_extract_config(vm, "rom", C2691_NVRAM_OFFSET, C2691_NVRAM_SIZE, 0, FS_NVRAM_FORMAT_WITH_BACKUP, startup_config, startup_len, private_config, private_len); return(ret); } /* Directly push the IOS configuration to the NVRAM device */ static int c2691_nvram_push_config(vm_instance_t *vm,u_char *startup_config,size_t startup_len,u_char *private_config,size_t private_len) { int ret; ret = generic_nvram_push_config(vm, "rom", vm->rom_size*1048576, C2691_NVRAM_OFFSET, C2691_NVRAM_SIZE, 0, FS_NVRAM_FORMAT_WITH_BACKUP, startup_config, startup_len, private_config, private_len); return(ret); } /* Check for empty config */ int c2691_nvram_check_empty_config(vm_instance_t *vm) { struct vdevice *rom_dev; m_uint64_t addr; size_t len; if (!(rom_dev = dev_get_by_name(vm,"rom"))) return(-1); addr = rom_dev->phys_addr + C2691_NVRAM_OFFSET; len = C2691_NVRAM_SIZE; while(len > 0) { if (physmem_copy_u32_from_vm(vm,addr) != 0) return(0); addr += sizeof(m_uint32_t); len -= sizeof(m_uint32_t); } /* Empty NVRAM */ vm->conf_reg |= 0x0040; printf("NVRAM is empty, setting config register to 0x%x\n",vm->conf_reg); return(0); } /* Create a new router instance */ static int c2691_create_instance(vm_instance_t *vm) { c2691_t *router; if (!(router = malloc(sizeof(*router)))) { fprintf(stderr,"C2691 '%s': Unable to create new instance!\n",vm->name); return(-1); } memset(router,0,sizeof(*router)); router->vm = vm; vm->hw_data = router; c2691_init_defaults(router); return(0); } /* Free resources used by a router instance */ static int c2691_delete_instance(vm_instance_t *vm) { c2691_t *router = VM_C2691(vm); int i; /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(FALSE); } } /* Remove NIO bindings */ for(i=0;inr_slots;i++) vm_slot_remove_all_nio_bindings(vm,i); /* Shutdown all Network Modules */ vm_slot_shutdown_all(vm); /* Free mainboard EEPROM */ cisco_eeprom_free(&router->mb_eeprom); /* Free all resources used by VM */ vm_free(vm); /* Free the router structure */ free(router); return(TRUE); } /* Get WIC device address for the specified onboard port */ int c2691_get_onboard_wic_addr(u_int slot,m_uint64_t *phys_addr) { if (slot >= C2691_MAX_WIC_BAYS) return(-1); *phys_addr = C2691_WIC_ADDR + (slot * C2691_WIC_SIZE); return(0); } /* Set EEPROM for the specified slot */ int c2691_set_slot_eeprom(c2691_t *router,u_int slot, struct cisco_eeprom *eeprom) { if (slot != 1) return(-1); router->nm_eeprom_group.eeprom[0] = eeprom; return(0); } /* Get slot/port corresponding to specified network IRQ */ static inline void c2691_net_irq_get_slot_port(u_int irq,u_int *slot,u_int *port) { irq -= C2691_NETIO_IRQ_BASE; *port = irq & C2691_NETIO_IRQ_PORT_MASK; *slot = irq >> C2691_NETIO_IRQ_PORT_BITS; } /* Get network IRQ for specified slot/port */ u_int c2691_net_irq_for_slot_port(u_int slot,u_int port) { u_int irq; irq = (slot << C2691_NETIO_IRQ_PORT_BITS) + port; irq += C2691_NETIO_IRQ_BASE; return(irq); } /* Set the base MAC address of the chassis */ static int c2691_burn_mac_addr(c2691_t *router,n_eth_addr_t *addr) { m_uint8_t eeprom_ver; size_t offset; /* Read EEPROM format version */ cisco_eeprom_get_byte(&router->mb_eeprom,0,&eeprom_ver); switch(eeprom_ver) { case 0: cisco_eeprom_set_region(&router->mb_eeprom,2,addr->eth_addr_byte,6); break; case 4: if (!cisco_eeprom_v4_find_field(&router->mb_eeprom,0xC3,&offset)) { cisco_eeprom_set_region(&router->mb_eeprom,offset, addr->eth_addr_byte,6); } break; default: vm_error(router->vm,"c2691_burn_mac_addr: unable to handle " "EEPROM version %u\n",eeprom_ver); return(-1); } return(0); } /* Set the system id or processor board id in the eeprom */ int c2691_set_system_id(c2691_t *router,char *id) { /* 11 characters is enough. Array is 20 long */ strncpy(router->board_id,id,13); /* Make sure it is null terminated */ router->board_id[13] = 0x00; c2691_refresh_systemid(router); return 0; } int c2691_refresh_systemid(c2691_t *router) { if (router->board_id[0] == 0x00) return(0); m_uint8_t buf[11]; parse_board_id(buf,router->board_id,11); cisco_eeprom_set_region(&router->mb_eeprom ,62,buf,11); return (0); } /* Set chassis MAC address */ int c2691_chassis_set_mac_addr(c2691_t *router,char *mac_addr) { if (parse_mac_addr(&router->mac_addr,mac_addr) == -1) { vm_error(router->vm,"unable to parse MAC address '%s'.\n",mac_addr); return(-1); } /* Set the chassis base MAC address */ c2691_burn_mac_addr(router,&router->mac_addr); return(0); } /* Create the two main PCI busses for a GT64120 based system */ static int c2691_init_gt96100(c2691_t *router) { vm_instance_t *vm = router->vm; vm_obj_t *obj; vm->pci_bus[0] = pci_bus_create("PCI bus #0",0); vm->pci_bus[1] = pci_bus_create("PCI bus #1",0); if (!vm->pci_bus[0] || !vm->pci_bus[1]) { vm_error(router->vm,"unable to create PCI data.\n"); return(-1); } if (dev_gt96100_init(vm,"gt96100",C2691_GT96K_ADDR,0x200000, C2691_GT96K_IRQ, C2691_EXT_IRQ, c2691_net_irq_for_slot_port(0,0), 255) == -1) return(-1); if (!(obj = vm_object_find(router->vm,"gt96100"))) return(-1); router->gt_data = obj->data; return(0); } /* Initialize a Cisco 2691 */ static int c2691_init(c2691_t *router) { vm_instance_t *vm = router->vm; /* Set the processor type: R7000 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R7000); /* Initialize the Galileo GT-96100 PCI controller */ if (c2691_init_gt96100(router) == -1) return(-1); /* Initialize PCI map (NM slot 1) */ vm->slots_pci_bus[1] = vm->pci_bus[1]; vm->elf_machine_id = C2691_ELF_MACHINE_ID; return(0); } /* Show C2691 hardware info */ void c2691_show_hardware(c2691_t *router) { vm_instance_t *vm = router->vm; printf("C2691 instance '%s' (id %d):\n",vm->name,vm->instance_id); printf(" VM Status : %d\n",vm->status); printf(" RAM size : %u Mb\n",vm->ram_size); printf(" NVRAM size : %u Kb\n",vm->nvram_size); printf(" IOS image : %s\n\n",vm->ios_image); if (vm->debug_level > 0) { dev_show_list(vm); pci_dev_show_list(vm->pci_bus[0]); pci_dev_show_list(vm->pci_bus[1]); printf("\n"); } } /* Initialize default parameters for a C2691 */ static void c2691_init_defaults(c2691_t *router) { vm_instance_t *vm = router->vm; n_eth_addr_t *m; m_uint16_t pid; /* Set platform slots characteristics */ vm->nr_slots = C2691_MAX_NM_BAYS; vm->slots_type = CISCO_CARD_TYPE_NM; vm->slots_drivers = nm_drivers; pid = (m_uint16_t)getpid(); /* Generate a chassis MAC address based on the instance ID */ m = &router->mac_addr; m->eth_addr_byte[0] = vm_get_mac_addr_msb(vm); m->eth_addr_byte[1] = vm->instance_id & 0xFF; m->eth_addr_byte[2] = pid >> 8; m->eth_addr_byte[3] = pid & 0xFF; m->eth_addr_byte[4] = 0x00; m->eth_addr_byte[5] = 0x00; c2691_init_eeprom_groups(router); cisco_eeprom_copy(&router->mb_eeprom,&eeprom_c2691_mainboard); c2691_burn_mac_addr(router,&router->mac_addr); /* The GT96100 system controller has 2 integrated FastEthernet ports */ vm_slot_add_binding(vm,"GT96100-FE",0,0); vm->ram_mmap = C2691_DEFAULT_RAM_MMAP; vm->ram_size = C2691_DEFAULT_RAM_SIZE; vm->rom_size = C2691_DEFAULT_ROM_SIZE; vm->nvram_size = C2691_DEFAULT_NVRAM_SIZE; vm->conf_reg_setup = C2691_DEFAULT_CONF_REG; vm->clock_divisor = C2691_DEFAULT_CLOCK_DIV; vm->nvram_rom_space = C2691_NVRAM_ROM_RES_SIZE; vm->nm_iomem_size = C2691_DEFAULT_IOMEM_SIZE; vm->pcmcia_disk_size[0] = C2691_DEFAULT_DISK0_SIZE; vm->pcmcia_disk_size[1] = C2691_DEFAULT_DISK1_SIZE; } /* Initialize the C2691 Platform */ static int c2691_init_platform(c2691_t *router) { vm_instance_t *vm = router->vm; cpu_mips_t *cpu; cpu_gen_t *gen; vm_obj_t *obj; /* Copy config register setup into "active" config register */ vm->conf_reg = vm->conf_reg_setup; /* Create Console and AUX ports */ vm_init_vtty(vm); /* Create a CPU group */ vm->cpu_group = cpu_group_create("System CPU"); /* Initialize the virtual MIPS processor */ if (!(gen = cpu_create(vm,CPU_TYPE_MIPS64,0))) { vm_error(vm,"unable to create CPU!\n"); return(-1); } cpu = CPU_MIPS64(gen); /* Add this CPU to the system CPU group */ cpu_group_add(vm->cpu_group,gen); vm->boot_cpu = gen; /* Initialize the IRQ routing vectors */ vm->set_irq = mips64_vm_set_irq; vm->clear_irq = mips64_vm_clear_irq; /* Mark the Network IO interrupt as high priority */ cpu->irq_idle_preempt[C2691_NETIO_IRQ] = TRUE; cpu->irq_idle_preempt[C2691_GT96K_IRQ] = TRUE; cpu->irq_idle_preempt[C2691_DUART_IRQ] = TRUE; /* Copy some parameters from VM to CPU (idle PC, ...) */ cpu->idle_pc = vm->idle_pc; if (vm->timer_irq_check_itv) cpu->timer_irq_check_itv = vm->timer_irq_check_itv; /* Remote emulator control */ dev_remote_control_init(vm,0x16000000,0x1000); /* Specific Storage Area (SSA) */ dev_ram_init(vm,"ssa",TRUE,FALSE,NULL,FALSE,0x16001000ULL,0x7000); /* IO FPGA */ if (dev_c2691_iofpga_init(router,C2691_IOFPGA_ADDR,0x40000) == -1) return(-1); if (!(obj = vm_object_find(router->vm,"io_fpga"))) return(-1); router->iofpga_data = obj->data; #if 0 /* PCI IO space */ if (!(vm->pci_io_space = pci_io_data_init(vm,C2691_PCI_IO_ADDR))) return(-1); #endif /* Initialize the chassis */ if (c2691_init(router) == -1) return(-1); /* Initialize RAM */ vm_ram_init(vm,0x00000000ULL); /* Initialize ROM (as a Flash) */ if (!(obj = dev_flash_init(vm,"rom",C2691_ROM_ADDR,vm->rom_size*1048576))) return(-1); dev_flash_copy_data(obj,0,mips64_microcode,mips64_microcode_len); c2691_nvram_check_empty_config(vm); /* Byte swapping */ dev_bswap_init(vm,"mem_bswap",C2691_BSWAP_ADDR,1024*1048576,0x00000000ULL); /* Initialize the NS16552 DUART */ dev_ns16552_init(vm,C2691_DUART_ADDR,0x1000,3,C2691_DUART_IRQ, vm->vtty_con,vm->vtty_aux); /* PCMCIA Slot 0 */ dev_pcmcia_disk_init(vm,"slot0",C2691_SLOT0_ADDR,0x200000, vm->pcmcia_disk_size[0],1); /* PCMCIA Slot 1 */ dev_pcmcia_disk_init(vm,"slot1",C2691_SLOT1_ADDR,0x200000, vm->pcmcia_disk_size[1],1); /* Initialize Network Modules */ if (vm_slot_init_all(vm) == -1) return(-1); /* Show device list */ c2691_show_hardware(router); return(0); } /* Boot the IOS image */ static int c2691_boot_ios(c2691_t *router) { vm_instance_t *vm = router->vm; cpu_mips_t *cpu; if (!vm->boot_cpu) return(-1); /* Suspend CPU activity since we will restart directly from ROM */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Reset the boot CPU */ cpu = CPU_MIPS64(vm->boot_cpu); mips64_reset(cpu); /* Load IOS image */ if (mips64_load_elf_image(cpu,vm->ios_image, (vm->ghost_status == VM_GHOST_RAM_USE), &vm->ios_entry_point) < 0) { vm_error(vm,"failed to load Cisco IOS image '%s'.\n",vm->ios_image); return(-1); } /* Launch the simulation */ printf("\nC2691 '%s': starting simulation (CPU0 PC=0x%llx), " "JIT %sabled.\n", vm->name,cpu->pc,vm->jit_use ? "en":"dis"); vm_log(vm,"C2691_BOOT", "starting instance (CPU0 PC=0x%llx,idle_pc=0x%llx,JIT %s)\n", cpu->pc,cpu->idle_pc,vm->jit_use ? "on":"off"); /* Start main CPU */ if (vm->ghost_status != VM_GHOST_RAM_GENERATE) { vm->status = VM_STATUS_RUNNING; cpu_start(vm->boot_cpu); } else { vm->status = VM_STATUS_SHUTDOWN; } return(0); } /* Set an IRQ */ static void c2691_set_irq(vm_instance_t *vm,u_int irq) { c2691_t *router = VM_C2691(vm); cpu_mips_t *cpu0 = CPU_MIPS64(vm->boot_cpu); u_int slot,port; switch(irq) { case 0 ... 7: mips64_set_irq(cpu0,irq); if (cpu0->irq_idle_preempt[irq]) cpu_idle_break_wait(cpu0->gen); break; case C2691_NETIO_IRQ_BASE ... C2691_NETIO_IRQ_END: c2691_net_irq_get_slot_port(irq,&slot,&port); dev_c2691_iofpga_net_set_irq(router->iofpga_data,slot,port); break; } } /* Clear an IRQ */ static void c2691_clear_irq(vm_instance_t *vm,u_int irq) { c2691_t *router = VM_C2691(vm); cpu_mips_t *cpu0 = CPU_MIPS64(vm->boot_cpu); u_int slot,port; switch(irq) { case 0 ... 7: mips64_clear_irq(cpu0,irq); break; case C2691_NETIO_IRQ_BASE ... C2691_NETIO_IRQ_END: c2691_net_irq_get_slot_port(irq,&slot,&port); dev_c2691_iofpga_net_clear_irq(router->iofpga_data,slot,port); break; } } /* Initialize a Cisco 2691 instance */ static int c2691_init_instance(vm_instance_t *vm) { c2691_t *router = VM_C2691(vm); m_uint32_t rom_entry_point; cpu_mips_t *cpu0; if (!vm->ios_image) { vm_error(vm,"no Cisco IOS image defined."); return(-1); } /* Initialize the C2691 platform */ if (c2691_init_platform(router) == -1) { vm_error(vm,"unable to initialize the platform hardware.\n"); return(-1); } /* IRQ routing */ vm->set_irq = c2691_set_irq; vm->clear_irq = c2691_clear_irq; /* Load IOS configuration files */ if (vm->ios_startup_config != NULL || vm->ios_private_config != NULL) { vm_nvram_push_config(vm,vm->ios_startup_config,vm->ios_private_config); vm->conf_reg &= ~0x40; } /* Load ROM (ELF image or embedded) */ cpu0 = CPU_MIPS64(vm->boot_cpu); rom_entry_point = (m_uint32_t)MIPS_ROM_PC; if ((vm->rom_filename != NULL) && (mips64_load_elf_image(cpu0,vm->rom_filename,0,&rom_entry_point) < 0)) { vm_error(vm,"unable to load alternate ROM '%s', " "fallback to embedded ROM.\n\n",vm->rom_filename); vm->rom_filename = NULL; } /* Load symbol file */ if (vm->sym_filename) { mips64_sym_load_file(cpu0,vm->sym_filename); cpu0->sym_trace = 1; } return(c2691_boot_ios(router)); } /* Stop a Cisco 2691 instance */ static int c2691_stop_instance(vm_instance_t *vm) { printf("\nC2691 '%s': stopping simulation.\n",vm->name); vm_log(vm,"C2691_STOP","stopping simulation.\n"); /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } } /* Free resources that were used during execution to emulate hardware */ vm_slot_shutdown_all(vm); vm_hardware_shutdown(vm); /* Cleanup */ VM_C2691(vm)->iofpga_data = NULL; VM_C2691(vm)->gt_data = NULL; return(0); } /* Get MAC address MSB */ static u_int c2691_get_mac_addr_msb(void) { return(0xC0); } /* Parse specific options for the Cisco 2691 platform */ static int c2691_cli_parse_options(vm_instance_t *vm,int option) { c2691_t *router = VM_C2691(vm); switch(option) { /* IO memory reserved for NMs (in percents!) */ case OPT_IOMEM_SIZE: vm->nm_iomem_size = 0x8000 | atoi(optarg); break; /* Set the base MAC address */ case 'm': if (!c2691_chassis_set_mac_addr(router,optarg)) printf("MAC address set to '%s'.\n",optarg); break; /* Unknown option */ default: return(-1); } return(0); } /* Show specific CLI options */ static void c2691_cli_show_options(vm_instance_t *vm) { printf(" --iomem-size : IO memory (in percents, default: %u)\n" " -p : Define a Network Module\n" " -s : Bind a Network IO interface to a " "Network Module\n", vm->nm_iomem_size); } /* Platform definition */ static vm_platform_t c2691_platform = { "c2691", "C2691", "2691", c2691_create_instance, c2691_delete_instance, c2691_init_instance, c2691_stop_instance, NULL, NULL, c2691_nvram_extract_config, c2691_nvram_push_config, c2691_get_mac_addr_msb, NULL, c2691_cli_parse_options, c2691_cli_show_options, NULL, }; /* Register the c2691 platform */ int c2691_platform_register(void) { if (vm_platform_register(&c2691_platform) == -1) return(-1); return(hypervisor_c2691_init(&c2691_platform)); } dynamips-0.2.14/common/dev_c2691.h000066400000000000000000000106631241034141600164670ustar00rootroot00000000000000/* * Cisco 2691 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Generic Cisco 2691 routines and definitions (EEPROM,...). */ #ifndef __DEV_C2691_H__ #define __DEV_C2691_H__ #include #include "utils.h" #include "net.h" #include "device.h" #include "pci_dev.h" #include "nmc93cX6.h" #include "net_io.h" #include "vm.h" /* Default C2691 parameters */ #define C2691_DEFAULT_RAM_SIZE 128 #define C2691_DEFAULT_ROM_SIZE 2 #define C2691_DEFAULT_NVRAM_SIZE 112 #define C2691_DEFAULT_CONF_REG 0x2102 #define C2691_DEFAULT_CLOCK_DIV 8 #define C2691_DEFAULT_RAM_MMAP 1 #define C2691_DEFAULT_DISK0_SIZE 16 #define C2691_DEFAULT_DISK1_SIZE 0 #define C2691_DEFAULT_IOMEM_SIZE 5 /* Percents! */ /* 2691 characteritics: 1 NM, 3 WIC, 2 AIM */ #define C2691_MAX_NM_BAYS 2 #define C2691_MAX_WIC_BAYS 3 /* C2691 DUART Interrupt */ #define C2691_DUART_IRQ 5 /* C2691 Network I/O Interrupt */ #define C2691_NETIO_IRQ 2 /* C2691 GT64k DMA/Timer Interrupt */ #define C2691_GT96K_IRQ 3 /* C2691 External Interrupt */ #define C2691_EXT_IRQ 6 /* Network IRQ */ #define C2691_NETIO_IRQ_BASE 32 #define C2691_NETIO_IRQ_PORT_BITS 3 #define C2691_NETIO_IRQ_PORT_MASK ((1 << C2691_NETIO_IRQ_PORT_BITS) - 1) #define C2691_NETIO_IRQ_PER_SLOT (1 << C2691_NETIO_IRQ_PORT_BITS) #define C2691_NETIO_IRQ_END \ (C2691_NETIO_IRQ_BASE + (C2691_MAX_NM_BAYS * C2691_NETIO_IRQ_PER_SLOT) - 1) /* C2691 common device addresses */ #define C2691_GT96K_ADDR 0x14000000ULL #define C2691_IOFPGA_ADDR 0x1e800000ULL #define C2691_BITBUCKET_ADDR 0x1ec00000ULL #define C2691_ROM_ADDR 0x1fc00000ULL #define C2691_SLOT0_ADDR 0x30000000ULL #define C2691_SLOT1_ADDR 0x32000000ULL #define C2691_DUART_ADDR 0x3c100000ULL #define C2691_WIC_ADDR 0x3c200000ULL #define C2691_BSWAP_ADDR 0xc0000000ULL #define C2691_PCI_IO_ADDR 0x100000000ULL /* WIC interval in address space */ #define C2691_WIC_SIZE 0x2000 /* Offset of simulated NVRAM in ROM flash */ #define C2691_NVRAM_OFFSET 0xE0000 #define C2691_NVRAM_SIZE 0x1C000 // with backup /* Reserved space for ROM in NVRAM */ #define C2691_NVRAM_ROM_RES_SIZE 0 /* C2691 ELF Platform ID */ #define C2691_ELF_MACHINE_ID 0x66 #define VM_C2691(vm) ((c2691_t *)vm->hw_data) /* C2691 router */ typedef struct c2691_router c2691_t; /* C2691 router */ struct c2691_router { /* Chassis MAC address */ n_eth_addr_t mac_addr; char board_id[20]; /* Associated VM instance */ vm_instance_t *vm; /* GT96100 data */ struct gt_data *gt_data; /* I/O FPGA */ struct c2691_iofpga_data *iofpga_data; /* Chassis information */ m_uint8_t oir_status; /* * Mainboard EEPROM. * It can be modified to change the chassis MAC address. */ struct cisco_eeprom mb_eeprom; struct nmc93cX6_group mb_eeprom_group; /* Network Module EEPROM */ struct nmc93cX6_group nm_eeprom_group; }; /* Get WIC device address for the specified onboard port */ int c2691_get_onboard_wic_addr(u_int slot,m_uint64_t *phys_addr); /* Set EEPROM for the specified slot */ int c2691_set_slot_eeprom(c2691_t *router,u_int slot, struct cisco_eeprom *eeprom); /* Get network IRQ for specified slot/port */ u_int c2691_net_irq_for_slot_port(u_int slot,u_int port); /* Set chassis MAC address */ int c2691_chassis_set_mac_addr(c2691_t *router,char *mac_addr); /* Set the system id */ int c2691_set_system_id(c2691_t *router,char *id); /* Burn the system id into the appropriate eeprom if possible */ int c2691_refresh_systemid(c2691_t *router); /* Show C2691 hardware info */ void c2691_show_hardware(c2691_t *router); /* Initialize EEPROM groups */ void c2691_init_eeprom_groups(c2691_t *router); /* dev_c2691_iofpga_init() */ int dev_c2691_iofpga_init(c2691_t *router,m_uint64_t paddr,m_uint32_t len); /* Register the c2691 platform */ int c2691_platform_register(void); /* Hypervisor C2691 initialization */ extern int hypervisor_c2691_init(vm_platform_t *platform); /* NM drivers */ extern struct cisco_card_driver dev_c2691_nm_1fe_tx_driver; extern struct cisco_card_driver dev_c2691_gt96100_fe_driver; extern struct cisco_card_driver dev_c2691_nm_4t_driver; extern struct cisco_card_driver dev_c2691_nm_16esw_driver; extern struct cisco_card_driver dev_c2691_nm_nam_driver; extern struct cisco_card_driver dev_c2691_nm_cids_driver; /* WIC drivers */ extern struct cisco_card_driver *dev_c2691_mb_wic_drivers[]; #endif dynamips-0.2.14/common/dev_c2691_eth.c000066400000000000000000000204171241034141600173200ustar00rootroot00000000000000/* * Cisco C2691 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Ethernet Network Modules. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_am79c971.h" #include "dev_nm_16esw.h" #include "dev_gt.h" #include "dev_c2691.h" /* Multi-Ethernet NM with Am79c971 chips */ struct nm_eth_data { u_int nr_port; struct am79c971_data *port[8]; }; /* Return sub-slot info for integrated WIC slots (on motherboard) */ static int dev_c2691_mb_get_sub_info(vm_instance_t *vm,struct cisco_card *card, u_int port_id, struct cisco_card_driver ***drv_array, u_int *subcard_type) { /* 3 integrated WIC slots */ if ((port_id & 0x0F) >= 3) return(-1); *drv_array = dev_c2691_mb_wic_drivers; *subcard_type = CISCO_CARD_TYPE_WIC; return(0); } /* * dev_c2691_nm_eth_init() * * Add an Ethernet Network Module into specified slot. */ static int dev_c2691_nm_eth_init(vm_instance_t *vm,struct cisco_card *card, int nr_port,int interface_type, const struct cisco_eeprom *eeprom) { struct nm_eth_data *data; u_int slot = card->slot_id; int i; /* Allocate the private data structure */ if (!(data = malloc(sizeof(*data)))) { vm_error(vm,"%s: out of memory.\n",card->dev_name); return(-1); } memset(data,0,sizeof(*data)); data->nr_port = nr_port; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,eeprom); c2691_set_slot_eeprom(VM_C2691(vm),slot,&card->eeprom); /* Create the AMD Am971c971 chip(s) */ for(i=0;inr_port;i++) { data->port[i] = dev_am79c971_init(vm,card->dev_name,interface_type, card->pci_bus,6+i, c2691_net_irq_for_slot_port(slot,i)); } /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove an Ethernet NM from the specified slot */ static int dev_c2691_nm_eth_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct nm_eth_data *data = card->drv_info; int i; /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c2691_set_slot_eeprom(VM_C2691(vm),card->slot_id,NULL); /* Remove the AMD Am79c971 chips */ for(i=0;inr_port;i++) dev_am79c971_remove(data->port[i]); free(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c2691_nm_eth_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct nm_eth_data *d = card->drv_info; if (!d || (port_id >= d->nr_port)) return(-1); dev_am79c971_set_nio(d->port[port_id],nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c2691_nm_eth_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct nm_eth_data *d = card->drv_info; if (!d || (port_id >= d->nr_port)) return(-1); dev_am79c971_unset_nio(d->port[port_id]); return(0); } /* ====================================================================== */ /* NM-1FE-TX */ /* ====================================================================== */ /* * dev_c2691_nm_1fe_tx_init() * * Add a NM-1FE-TX Network Module into specified slot. */ static int dev_c2691_nm_1fe_tx_init(vm_instance_t *vm,struct cisco_card *card) { return(dev_c2691_nm_eth_init(vm,card,1,AM79C971_TYPE_100BASE_TX, cisco_eeprom_find_nm("NM-1FE-TX"))); } /* ====================================================================== */ /* NM-16ESW */ /* ====================================================================== */ /* Add a NM-16ESW */ static int dev_c2691_nm_16esw_init(vm_instance_t *vm,struct cisco_card *card) { struct nm_16esw_data *data; u_int slot = card->slot_id; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_nm("NM-16ESW")); dev_nm_16esw_burn_mac_addr(vm,slot,&card->eeprom); c2691_set_slot_eeprom(VM_C2691(vm),slot,&card->eeprom); /* Create the device */ data = dev_nm_16esw_init(vm,card->dev_name,slot, card->pci_bus,6, c2691_net_irq_for_slot_port(slot,0)); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a NM-16ESW from the specified slot */ static int dev_c2691_nm_16esw_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct nm_16esw_data *data = card->drv_info; /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c2691_set_slot_eeprom(VM_C2691(vm),card->slot_id,NULL); /* Remove the BCM5600 chip */ dev_nm_16esw_remove(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c2691_nm_16esw_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_set_nio(d,port_id,nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c2691_nm_16esw_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_unset_nio(d,port_id); return(0); } /* Show debug info */ static int dev_c2691_nm_16esw_show_info(vm_instance_t *vm,struct cisco_card *card) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_show_info(d); return(0); } /* ====================================================================== */ /* GT96100 - Integrated Ethernet ports */ /* ====================================================================== */ /* Initialize Ethernet part of the GT96100 controller */ static int dev_c2691_gt96100_fe_init(vm_instance_t *vm,struct cisco_card *card) { if (card->slot_id != 0) { vm_error(vm,"dev_c2691_gt96100_fe_init: bad slot %u specified.\n", card->slot_id); return(-1); } /* Store device info into the router structure */ card->drv_info = VM_C2691(vm)->gt_data; return(0); } /* Nothing to do, we never remove the system controller */ static int dev_c2691_gt96100_fe_shutdown(vm_instance_t *vm,struct cisco_card *card) { return(0); } /* Bind a Network IO descriptor */ static int dev_c2691_gt96100_fe_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct gt_data *d = card->drv_info; dev_gt96100_eth_set_nio(d,port_id,nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c2691_gt96100_fe_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct gt_data *d = card->drv_info; dev_gt96100_eth_unset_nio(d,port_id); return(0); } /* ====================================================================== */ /* NM-1FE-TX driver */ struct cisco_card_driver dev_c2691_nm_1fe_tx_driver = { "NM-1FE-TX", 1, 0, dev_c2691_nm_1fe_tx_init, dev_c2691_nm_eth_shutdown, NULL, dev_c2691_nm_eth_set_nio, dev_c2691_nm_eth_unset_nio, NULL, }; /* NM-16ESW driver */ struct cisco_card_driver dev_c2691_nm_16esw_driver = { "NM-16ESW", 1, 0, dev_c2691_nm_16esw_init, dev_c2691_nm_16esw_shutdown, NULL, dev_c2691_nm_16esw_set_nio, dev_c2691_nm_16esw_unset_nio, dev_c2691_nm_16esw_show_info, }; /* GT96100 FastEthernet integrated ports */ struct cisco_card_driver dev_c2691_gt96100_fe_driver = { "GT96100-FE", 1, 3, dev_c2691_gt96100_fe_init, dev_c2691_gt96100_fe_shutdown, dev_c2691_mb_get_sub_info, dev_c2691_gt96100_fe_set_nio, dev_c2691_gt96100_fe_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c2691_iofpga.c000066400000000000000000000274731241034141600200160ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) */ #include #include #include #include #include #include #include #include #include "ptask.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_vtty.h" #include "nmc93cX6.h" #include "dev_c2691.h" /* Debugging flags */ #define DEBUG_UNKNOWN 1 #define DEBUG_ACCESS 0 #define DEBUG_NET_IRQ 0 /* Definitions for Mainboard EEPROM */ #define EEPROM_MB_DOUT 3 #define EEPROM_MB_DIN 2 #define EEPROM_MB_CLK 1 #define EEPROM_MB_CS 0 /* Definitions for Network Modules EEPROM */ #define EEPROM_NM_DOUT 7 #define EEPROM_NM_DIN 6 #define EEPROM_NM_CLK 2 #define EEPROM_NM_CS 4 /* Network IRQ distribution */ struct net_irq_distrib { u_int reg; u_int offset; }; static struct net_irq_distrib net_irq_dist[C2691_MAX_NM_BAYS] = { { 0, 0 }, /* Slot 0: reg 0x26, 0x000000XX */ { 1, 0 }, /* Slot 1: reg 0x28, 0x000000XX */ }; /* IO FPGA structure */ struct c2691_iofpga_data { vm_obj_t vm_obj; struct vdevice dev; c2691_t *router; /* Network IRQ status */ m_uint16_t net_irq_status[2]; /* Interrupt mask */ m_uint16_t intr_mask; /* WIC select */ u_int wic_select; u_int wic_cmd_pos; u_int wic_cmd_valid; m_uint16_t wic_cmd[2]; }; /* Mainboard EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_mb_def = { EEPROM_MB_CLK, EEPROM_MB_CS, EEPROM_MB_DIN, EEPROM_MB_DOUT, }; /* Mainboard EEPROM */ static const struct nmc93cX6_group eeprom_mb_group = { EEPROM_TYPE_NMC93C46, 1, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "Mainboard EEPROM", { &eeprom_mb_def }, }; /* NM EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_nm_def = { EEPROM_NM_CLK, EEPROM_NM_CS, EEPROM_NM_DIN, EEPROM_NM_DOUT, }; /* NM EEPROM */ static const struct nmc93cX6_group eeprom_nm_group = { EEPROM_TYPE_NMC93C46, 1, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "NM EEPROM", { &eeprom_nm_def }, }; /* Update network interrupt status */ static inline void dev_c2691_iofpga_net_update_irq(struct c2691_iofpga_data *d) { if ((d->net_irq_status[0] != 0xFFFF) || (d->net_irq_status[1] != 0xFFFF)) { vm_set_irq(d->router->vm,C2691_NETIO_IRQ); } else { vm_clear_irq(d->router->vm,C2691_NETIO_IRQ); } } /* Trigger a Network IRQ for the specified slot/port */ void dev_c2691_iofpga_net_set_irq(struct c2691_iofpga_data *d, u_int slot,u_int port) { struct net_irq_distrib *irq_dist; #if DEBUG_NET_IRQ vm_log(d->router->vm,"IO_FPGA","setting NetIRQ for slot %u port %u\n", slot,port); #endif irq_dist = &net_irq_dist[slot]; d->net_irq_status[irq_dist->reg] &= ~(1 << (irq_dist->offset + port)); dev_c2691_iofpga_net_update_irq(d); } /* Clear a Network IRQ for the specified slot/port */ void dev_c2691_iofpga_net_clear_irq(struct c2691_iofpga_data *d, u_int slot,u_int port) { struct net_irq_distrib *irq_dist; #if DEBUG_NET_IRQ vm_log(d->router->vm,"IO_FPGA","clearing NetIRQ for slot %u port %u\n", slot,port); #endif irq_dist = &net_irq_dist[slot]; d->net_irq_status[irq_dist->reg] |= (1 << (irq_dist->offset + port)); dev_c2691_iofpga_net_update_irq(d); } /* Read a WIC EEPROM */ static m_uint16_t dev_c2691_read_wic_eeprom(struct c2691_iofpga_data *d) { struct cisco_eeprom *eeprom; u_int wic_port; u_int eeprom_offset; m_uint8_t val[2]; switch(d->wic_select) { case 0x1700: wic_port = 0x10; break; case 0x1D00: wic_port = 0x20; break; case 0x3500: wic_port = 0x30; break; default: wic_port = 0; } /* No WIC in slot or no EEPROM: fake an empty EEPROM */ if (!wic_port || !(eeprom = vm_slot_get_eeprom(d->router->vm,0,wic_port))) return(0xFFFF); /* EEPROM offset is in the lowest 6 bits */ eeprom_offset = d->wic_cmd[0] & 0x3F; cisco_eeprom_get_byte(eeprom,eeprom_offset,&val[0]); cisco_eeprom_get_byte(eeprom,eeprom_offset+1,&val[1]); return(((m_uint16_t)val[0] << 8) | val[1]); } /* * dev_c2691_iofpga_access() */ static void * dev_c2691_iofpga_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct c2691_iofpga_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0x0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA","reading reg 0x%x at pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA", "writing reg 0x%x at pc=0x%llx, data=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),*data,op_size); } #endif switch(offset) { /* * Platform type ? * 0x04 and 0x05 seem to work. */ case 0x36: if (op_type == MTS_READ) *data = 0x04 << 5; break; /* Mainboard EEPROM */ case 0x0e: if (op_type == MTS_WRITE) nmc93cX6_write(&d->router->mb_eeprom_group,(u_int)(*data)); else *data = nmc93cX6_read(&d->router->mb_eeprom_group); break; case 0x12: /* * Bit 0: 1=No WIC in slot 0. * Bit 1: 1=No WIC in slot 1. * Bit 2: 1=No WIC in slot 2. */ if (op_type == MTS_READ) { *data = 0xFFFF; /* check WIC 0 */ if (vm_slot_check_eeprom(d->router->vm,0,0x10)) *data &= ~0x01; /* check WIC 1 */ if (vm_slot_check_eeprom(d->router->vm,0,0x20)) *data &= ~0x02; /* check WIC 2 */ if (vm_slot_check_eeprom(d->router->vm,0,0x30)) *data &= ~0x04; } else { d->wic_select = *data; } break; case 0x14: if (op_type == MTS_READ) *data = 0xFFFF; break; case 0x18: if (op_type == MTS_READ) *data = 0xFFFF; break; /* wic/vwic related */ case 0x40: if (op_type == MTS_READ) *data = 0x0004; break; /* WIC related: 16-bit data */ case 0x42: if (op_type == MTS_READ) { if (d->wic_cmd_valid) { *data = dev_c2691_read_wic_eeprom(d); d->wic_cmd_valid = FALSE; } else { *data = 0xFFFF; } } else { /* * Store the EEPROM command (in 2 words). * * For a read, we have: * Word 0: 0x180 (nmc93c46 READ) + offset (6-bits). * Word 1: 0 (no data). */ d->wic_cmd[d->wic_cmd_pos++] = *data; if (d->wic_cmd_pos == 2) { d->wic_cmd_pos = 0; d->wic_cmd_valid = TRUE; } } break; /* NM Slot 1 EEPROM */ case 0x44: if (op_type == MTS_WRITE) nmc93cX6_write(&d->router->nm_eeprom_group,(u_int)(*data)); else *data = nmc93cX6_read(&d->router->nm_eeprom_group); break; /* AIM EEPROM #0 */ case 0x48: if (op_type == MTS_READ) *data = 0xFFFF; break; /* AIM EEPROM #1 */ case 0x4a: if (op_type == MTS_READ) *data = 0xFFFF; break; /* * NM Presence. * * Bit 7: 0=NM present in slot 1. * Other bits unknown. */ case 0x20: if (op_type == MTS_READ) { *data = 0xFFFF; if (vm_slot_get_card_ptr(d->router->vm,1)) *data &= ~0x08; } break; /* ??? */ case 0x24: break; /* Intr Mask (sh platform) */ case 0x30: if (op_type == MTS_READ) *data = d->intr_mask; else d->intr_mask = *data; break; /* * Network interrupt status. * * Bit 0: 0 = GT96100 Ethernet ports. * Other bits unknown. */ case 0x26: if (op_type == MTS_READ) *data = d->net_irq_status[0]; break; /* * Network interrupt status. * * Bit 0: 0 = NM in Slot 1. * Other bits unknown. */ case 0x28: if (op_type == MTS_READ) *data = d->net_irq_status[1]; break; case 0x2c: if (op_type == MTS_READ) *data = 0xFFFF; break; /* OIR interrupt but not supported (IRQ 6) */ case 0x2e: if (op_type == MTS_READ) *data = 0xFFFF; break; /* * Environmental monitor, determined with "sh env all". * * Bit 0: 1 = Fan Error * Bit 1: 1 = Fan Error * Bit 2: 1 = Over-temperature * Bit 3: ??? * Bit 4: 0 = RPS present. * Bit 5: 0 = Input Voltage status failure. * Bit 6: 1 = Thermal status failure. * Bit 7: 1 = DC Output Voltage status failure. */ case 0x3a: if (op_type == MTS_READ) *data = 0x0020; break; /* * Bit 0: Slot0 Compact Flash presence. * Bit 1: System Compact Flash presence. */ case 0x3c: if (op_type == MTS_READ) { *data = 0xFFFF; /* System Flash ? */ if (cpu->vm->pcmcia_disk_size[0]) *data &= ~0x02; /* Slot0 Flash ? */ if (cpu->vm->pcmcia_disk_size[1]) *data &= ~0x01; } break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA", "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA", "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* Initialize EEPROM groups */ void c2691_init_eeprom_groups(c2691_t *router) { /* Initialize Mainboard EEPROM */ router->mb_eeprom_group = eeprom_mb_group; router->mb_eeprom_group.eeprom[0] = &router->mb_eeprom; router->mb_eeprom.data = NULL; router->mb_eeprom.len = 0; /* EEPROM for NM slot 1 */ router->nm_eeprom_group = eeprom_nm_group; router->nm_eeprom_group.eeprom[0] = NULL; } /* Shutdown the IO FPGA device */ static void dev_c2691_iofpga_shutdown(vm_instance_t *vm,struct c2691_iofpga_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* * dev_c2691_iofpga_init() */ int dev_c2691_iofpga_init(c2691_t *router,m_uint64_t paddr,m_uint32_t len) { vm_instance_t *vm = router->vm; struct c2691_iofpga_data *d; /* Allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"IO_FPGA: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); d->router = router; d->net_irq_status[0] = 0xFFFF; d->net_irq_status[1] = 0xFFFF; vm_object_init(&d->vm_obj); d->vm_obj.name = "io_fpga"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_c2691_iofpga_shutdown; /* Set device properties */ dev_init(&d->dev); d->dev.name = "io_fpga"; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.priv_data = d; d->dev.handler = dev_c2691_iofpga_access; /* Map this device to the VM */ vm_bind_device(router->vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_c2691_iofpga.h000066400000000000000000000013501241034141600200050ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005-2007 Christophe Fillot (cf@utc.fr) * * Cisco c2691 I/O FPGA. */ #ifndef __DEV_C2691_IOFPGA_H__ #define __DEV_C2691_IOFPGA_H__ /* Forward declaration for IO_FPGA private data */ struct c2691_iofpga_data; /* Trigger a Network IRQ for the specified slot/port */ void dev_c2691_iofpga_net_set_irq(struct c2691_iofpga_data *d, u_int slot,u_int port); /* Clear a Network IRQ for the specified slot/port */ void dev_c2691_iofpga_net_clear_irq(struct c2691_iofpga_data *d, u_int slot,u_int port); /* Create the c2691 I/O FPGA */ int dev_c2691_iofpga_init(c2691_t *router,m_uint64_t paddr,m_uint32_t len); #endif dynamips-0.2.14/common/dev_c2691_pcmod.c000066400000000000000000000051341241034141600176410ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * PC Modules NM (NM-NAM, NM-CIDS, ...) for c2691 platforms. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "vm.h" #include "dev_i8255x.h" #include "dev_c2691.h" /* Initialize a NM PC module in the specified slot */ static int dev_c2691_pcmod_init(vm_instance_t *vm,struct cisco_card *card) { struct i8255x_data *data; u_int slot = card->slot_id; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_nm(card->driver->dev_type)); c2691_set_slot_eeprom(VM_C2691(vm),slot,&card->eeprom); /* Create the Intel i8255x chip */ data = dev_i8255x_init(vm,card->dev_name,0, card->pci_bus,6, c2691_net_irq_for_slot_port(slot,0)); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a NM PC module from the specified slot */ static int dev_c2691_pcmod_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct i8255x_data *data = card->drv_info; /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c2691_set_slot_eeprom(VM_C2691(vm),card->slot_id,NULL); /* Remove the Intel i2855x chip */ dev_i8255x_remove(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c2691_pcmod_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct i8255x_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); dev_i8255x_set_nio(d,nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c2691_pcmod_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct i8255x_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); dev_i8255x_unset_nio(d); return(0); } /* NM-NAM driver */ struct cisco_card_driver dev_c2691_nm_nam_driver = { "NM-NAM", 0, 0, dev_c2691_pcmod_init, dev_c2691_pcmod_shutdown, NULL, dev_c2691_pcmod_set_nio, dev_c2691_pcmod_unset_nio, NULL, }; /* NM-CIDS driver */ struct cisco_card_driver dev_c2691_nm_cids_driver = { "NM-CIDS", 0, 0, dev_c2691_pcmod_init, dev_c2691_pcmod_shutdown, NULL, dev_c2691_pcmod_set_nio, dev_c2691_pcmod_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c2691_serial.c000066400000000000000000000050621241034141600200160ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Serial Network Modules. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_mueslix.h" #include "dev_c2691.h" /* ====================================================================== */ /* NM-4T */ /* ====================================================================== */ /* * dev_c2691_nm_4t_init() * * Add a NM-4T network module into specified slot. */ int dev_c2691_nm_4t_init(vm_instance_t *vm,struct cisco_card *card) { struct mueslix_data *data; u_int slot = card->slot_id; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_nm("NM-4T")); c2691_set_slot_eeprom(VM_C2691(vm),slot,&card->eeprom); /* Create the Mueslix chip */ data = dev_mueslix_init(vm,card->dev_name,0,card->pci_bus,6, c2691_net_irq_for_slot_port(slot,0)); if (!data) return(-1); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a NM-4T from the specified slot */ int dev_c2691_nm_4t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c2691_set_slot_eeprom(VM_C2691(vm),card->slot_id,NULL); /* Remove the mueslix driver */ dev_mueslix_remove(card->drv_info); return(0); } /* Bind a Network IO descriptor to a specific port */ int dev_c2691_nm_4t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct mueslix_data *d = card->drv_info; if (!d || (port_id >= MUESLIX_NR_CHANNELS)) return(-1); return(dev_mueslix_set_nio(d,port_id,nio)); } /* Unbind a Network IO descriptor to a specific port */ int dev_c2691_nm_4t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct mueslix_data *d = card->drv_info; if (!d || (port_id >= MUESLIX_NR_CHANNELS)) return(-1); return(dev_mueslix_unset_nio(d,port_id)); } /* NM-4T driver */ struct cisco_card_driver dev_c2691_nm_4t_driver = { "NM-4T", 1, 0, dev_c2691_nm_4t_init, dev_c2691_nm_4t_shutdown, NULL, dev_c2691_nm_4t_set_nio, dev_c2691_nm_4t_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c2691_wic.c000066400000000000000000000134761241034141600173310ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * WIC Modules. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "vm.h" #include "dev_gt.h" #include "dev_c2691.h" #include "dev_wic_serial.h" /* Get the MPSC channel associated to a WIC sub-slot */ static int dev_c2691_mb_wic_get_mpsc_chan(struct cisco_card *card, u_int port_id, u_int *mpsc_chan) { u_int cid; cid = card->subslot_id + port_id; switch(cid) { /* WIC 0 port 0 mapped to GT96100 MPSC1 */ case 0x10: *mpsc_chan = 1; break; /* WIC 0 port 1 mapped to GT96100 MPSC0 */ case 0x11: *mpsc_chan = 0; break; /* WIC 1 port 0 mapped to GT96100 MPSC4 */ case 0x20: *mpsc_chan = 4; break; /* WIC 1 port 1 mapped to GT96100 MPSC2 */ case 0x21: *mpsc_chan = 2; break; /* WIC 2 port 0 mapped to GT96100 MPSC5 */ case 0x30: *mpsc_chan = 5; break; /* WIC 2 port 1 mapped to GT96100 MPSC3 */ case 0x31: *mpsc_chan = 3; break; default: return(-1); } return(0); } /* Initialize a WIC-1T in the specified slot */ static int dev_c2691_mb_wic1t_init(vm_instance_t *vm,struct cisco_card *card) { struct wic_serial_data *wic_data; m_uint64_t phys_addr; u_int wic_id; /* Create the WIC device */ wic_id = (card->subslot_id >> 4) - 1; if (c2691_get_onboard_wic_addr(wic_id,&phys_addr) == -1) { vm_error(vm,"WIC","invalid slot %u (subslot_id=%u)\n", wic_id,card->subslot_id); return(-1); } wic_data = dev_wic_serial_init(vm,card->dev_name,WIC_SERIAL_MODEL_1T, phys_addr,C2691_WIC_SIZE); if (!wic_data) return(-1); /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_wic("WIC-1T")); /* Store device info into the router structure */ card->drv_info = wic_data; return(0); } /* Remove a WIC-1T from the specified slot */ static int dev_c2691_mb_wic1t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the WIC device */ dev_wic_serial_remove(card->drv_info); /* Remove the WIC EEPROM */ cisco_card_unset_eeprom(card); return(0); } /* Bind a Network IO descriptor */ static int dev_c2691_mb_wic1t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { u_int mpsc_chan; if ((port_id > 0) || (dev_c2691_mb_wic_get_mpsc_chan(card,port_id,&mpsc_chan) == -1)) return(-1); return(dev_gt96100_mpsc_set_nio(VM_C2691(vm)->gt_data,mpsc_chan,nio)); } /* Unbind a Network IO descriptor */ static int dev_c2691_mb_wic1t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { u_int mpsc_chan; if ((port_id > 0) || (dev_c2691_mb_wic_get_mpsc_chan(card,port_id,&mpsc_chan) == -1)) return(-1); return(dev_gt96100_mpsc_unset_nio(VM_C2691(vm)->gt_data,mpsc_chan)); } /* Initialize a WIC-2T in the specified slot */ static int dev_c2691_mb_wic2t_init(vm_instance_t *vm,struct cisco_card *card) { struct wic_serial_data *wic_data; m_uint64_t phys_addr; u_int wic_id; /* Create the WIC device */ wic_id = (card->subslot_id >> 4) - 1; if (c2691_get_onboard_wic_addr(wic_id,&phys_addr) == -1) { vm_error(vm,"WIC","invalid slot %u (subslot_id=%u)\n", wic_id,card->subslot_id); return(-1); } wic_data = dev_wic_serial_init(vm,card->dev_name,WIC_SERIAL_MODEL_2T, phys_addr,C2691_WIC_SIZE); if (!wic_data) return(-1); /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_wic("WIC-2T")); /* Store device info into the router structure */ card->drv_info = wic_data; return(0); } /* Remove a WIC-2T from the specified slot */ static int dev_c2691_mb_wic2t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the WIC device */ dev_wic_serial_remove(card->drv_info); /* Remove the WIC EEPROM */ cisco_card_unset_eeprom(card); return(0); } /* Bind a Network IO descriptor */ static int dev_c2691_mb_wic2t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { u_int mpsc_chan; if ((port_id > 1) || (dev_c2691_mb_wic_get_mpsc_chan(card,port_id,&mpsc_chan) == -1)) return(-1); return(dev_gt96100_mpsc_set_nio(VM_C2691(vm)->gt_data,mpsc_chan,nio)); } /* Unbind a Network IO descriptor */ static int dev_c2691_mb_wic2t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { u_int mpsc_chan; if ((port_id > 1) || (dev_c2691_mb_wic_get_mpsc_chan(card,port_id,&mpsc_chan) == -1)) return(-1); return(dev_gt96100_mpsc_unset_nio(VM_C2691(vm)->gt_data,mpsc_chan)); } /* Cisco 2691 WIC-1T driver (for mainboard) */ struct cisco_card_driver dev_c2691_mb_wic1t_driver = { "WIC-1T", 1, 0, dev_c2691_mb_wic1t_init, dev_c2691_mb_wic1t_shutdown, NULL, dev_c2691_mb_wic1t_set_nio, dev_c2691_mb_wic1t_unset_nio, NULL, }; /* Cisco 2691 WIC-2T driver (for mainboard) */ struct cisco_card_driver dev_c2691_mb_wic2t_driver = { "WIC-2T", 1, 0, dev_c2691_mb_wic2t_init, dev_c2691_mb_wic2t_shutdown, NULL, dev_c2691_mb_wic2t_set_nio, dev_c2691_mb_wic2t_unset_nio, NULL, }; /* WIC drivers (mainbord slots) */ struct cisco_card_driver *dev_c2691_mb_wic_drivers[] = { &dev_c2691_mb_wic1t_driver, &dev_c2691_mb_wic2t_driver, NULL, }; dynamips-0.2.14/common/dev_c3600.c000066400000000000000000000701661241034141600164550ustar00rootroot00000000000000/* * Cisco 3600 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * Patched by Jeremy Grossmann for the GNS3 project (www.gns3.net) * * Generic Cisco 3600 routines and definitions (EEPROM,...). */ #include #include #include #include #include #include #include "cpu.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "pci_io.h" #include "dev_gt.h" #include "cisco_eeprom.h" #include "dev_rom.h" #include "dev_c3600.h" #include "dev_c3600_iofpga.h" #include "dev_c3600_bay.h" #include "dev_vtty.h" #include "registry.h" #include "fs_nvram.h" /* ======================================================================== */ /* EEPROM definitions */ /* ======================================================================== */ /* Cisco 3620 mainboard EEPROM */ static m_uint16_t eeprom_c3620_mainboard_data[64] = { 0x0001, 0x0000, 0x0000, 0x0000, 0x0AFF, 0x7318, 0x5011, 0x0020, 0xFF10, 0x45C5, 0xA0FF, 0x9904, 0x19FF, 0xFFFF, 0xFFFF, 0x0002, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; struct cisco_eeprom eeprom_c3620_mainboard = { "C3620 Mainboard", eeprom_c3620_mainboard_data, sizeof(eeprom_c3620_mainboard_data)/2, }; /* Cisco 3640 mainboard EEPROM */ static m_uint16_t eeprom_c3640_mainboard_data[64] = { 0x0001, 0x0000, 0x0000, 0x0000, 0x0AFF, 0x7316, 0x8514, 0x0040, 0xFF10, 0x45C5, 0xA1FF, 0x0102, 0x22FF, 0xFFFF, 0xFFFF, 0x0002, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; struct cisco_eeprom eeprom_c3640_mainboard = { "C3640 Mainboard", eeprom_c3640_mainboard_data, sizeof(eeprom_c3640_mainboard_data)/2, }; /* Cisco 3660 backplane EEPROM */ static m_uint16_t eeprom_c3660_backplane_data[64] = { 0x04FF, 0x4000, 0xC841, 0x0100, 0xC046, 0x0320, 0x0012, 0x8402, 0x4243, 0x3080, 0x0000, 0x0000, 0x0202, 0xC18B, 0x4841, 0x4430, 0x3434, 0x3431, 0x3135, 0x4A03, 0x0081, 0x0000, 0x0000, 0x0400, 0xC28B, 0x4654, 0x5830, 0x3934, 0x3557, 0x304D, 0x59C3, 0x0600, 0x044D, 0x0EC2, 0xD043, 0x0070, 0xC408, 0x0000, 0x0000, 0x0000, 0x0000, 0x851C, 0x0A5B, 0x0201, 0x06FF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; struct cisco_eeprom eeprom_c3660_backplane = { "C3660 Backplane", eeprom_c3660_backplane_data, sizeof(eeprom_c3660_backplane_data)/2, }; /* ======================================================================== */ /* Chassis Drivers */ /* ======================================================================== */ static int c3620_init(c3600_t *router); static int c3640_init(c3600_t *router); static int c3660_init(c3600_t *router); static struct c3600_chassis_driver chassis_drivers[] = { { "3620" , 3620, 1, c3620_init, &eeprom_c3620_mainboard }, { "3640" , 3640, 1, c3640_init, &eeprom_c3640_mainboard }, { "3660" , 3660, 1, c3660_init, &eeprom_c3660_backplane }, { NULL , -1, 0, NULL, NULL }, }; /* ======================================================================== */ /* Network Module Drivers */ /* ======================================================================== */ static struct cisco_card_driver *nm_drivers[] = { &dev_c3600_nm_1e_driver, &dev_c3600_nm_4e_driver, &dev_c3600_nm_1fe_tx_driver, &dev_c3600_nm_4t_driver, &dev_c3600_leopard_2fe_driver, &dev_c3600_nm_16esw_driver, NULL, }; /* ======================================================================== */ /* Cisco 3600 router instances */ /* ======================================================================== */ /* Initialize default parameters for a C3600 */ static void c3600_init_defaults(c3600_t *router); /* Directly extract the configuration from the NVRAM device */ static int c3600_nvram_extract_config(vm_instance_t *vm,u_char **startup_config,size_t *startup_len,u_char **private_config,size_t *private_len) { int ret; ret = generic_nvram_extract_config(vm, "nvram", vm->nvram_rom_space, 0, 0, FS_NVRAM_FORMAT_DEFAULT, startup_config, startup_len, private_config, private_len); return(ret); } /* Directly push the IOS configuration to the NVRAM device */ static int c3600_nvram_push_config(vm_instance_t *vm,u_char *startup_config,size_t startup_len,u_char *private_config,size_t private_len) { int ret; ret = generic_nvram_push_config(vm, "nvram", vm->nvram_size*1024, vm->nvram_rom_space, 0, 0, FS_NVRAM_FORMAT_DEFAULT, startup_config, startup_len, private_config, private_len); return(ret); } /* Create a new router instance */ static int c3600_create_instance(vm_instance_t *vm) { c3600_t *router; if (!(router = malloc(sizeof(*router)))) { fprintf(stderr,"C3600 '%s': Unable to create new instance!\n",vm->name); return(-1); } memset(router,0,sizeof(*router)); router->vm = vm; vm->hw_data = router; c3600_init_defaults(router); return(0); } /* Free resources used by a router instance */ static int c3600_delete_instance(vm_instance_t *vm) { c3600_t *router = VM_C3600(vm); int i; /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(FALSE); } } /* Remove NIO bindings */ for(i=0;inr_slots;i++) vm_slot_remove_all_nio_bindings(vm,i); /* Shutdown all Network Modules */ vm_slot_shutdown_all(vm); /* Free mainboard EEPROM */ cisco_eeprom_free(&router->mb_eeprom); /* Free all resources used by VM */ vm_free(vm); /* Free the router structure */ free(router); return(TRUE); } /* Save configuration of a C3600 instance */ static void c3600_save_config(vm_instance_t *vm,FILE *fd) { c3600_t *router = VM_C3600(vm); fprintf(fd,"c3600 set_chassis %s %s\n\n", vm->name,router->chassis_driver->chassis_type); } /* Set EEPROM for the specified slot */ int c3600_set_slot_eeprom(c3600_t *router,u_int slot, struct cisco_eeprom *eeprom) { if (slot >= C3600_MAX_NM_BAYS) return(-1); router->c3660_nm_eeprom_group[slot].eeprom[0] = eeprom; return(0); } /* Get slot/port corresponding to specified network IRQ */ static inline void c3600_net_irq_get_slot_port(u_int irq,u_int *slot,u_int *port) { irq -= C3600_NETIO_IRQ_BASE; *port = irq & C3600_NETIO_IRQ_PORT_MASK; *slot = irq >> C3600_NETIO_IRQ_PORT_BITS; } /* Get network IRQ for specified slot/port */ u_int c3600_net_irq_for_slot_port(u_int slot,u_int port) { u_int irq; irq = (slot << C3600_NETIO_IRQ_PORT_BITS) + port; irq += C3600_NETIO_IRQ_BASE; return(irq); } /* Get a chassis driver */ struct c3600_chassis_driver *c3600_chassis_get_driver(char *chassis_type) { int i; for(i=0;chassis_drivers[i].chassis_type;i++) if (!strcmp(chassis_drivers[i].chassis_type,chassis_type)) return(&chassis_drivers[i]); return NULL; } /* Set the system id or processor board id in the eeprom */ int c3600_set_system_id(c3600_t *router,char *id) { /* 11 characters is enough. Array is 20 long */ strncpy(router->board_id,id,13); /* Make sure it is null terminated */ router->board_id[13] = 0x00; c3600_refresh_systemid(router); return 0; } int c3600_refresh_systemid(c3600_t *router) { if (router->board_id[0] == 0x00) return(0); m_uint8_t buf[11]; if (!strcmp("3660",router->chassis_driver->chassis_type)) { parse_board_id(buf,router->board_id,11); cisco_eeprom_set_region(&router->mb_eeprom ,50,buf,11); } else { parse_board_id(buf,router->board_id,4); cisco_eeprom_set_region(&router->mb_eeprom ,16,buf,4); } return (0); } /* Set the base MAC address of the chassis */ static int c3600_burn_mac_addr(c3600_t *router,n_eth_addr_t *addr) { m_uint8_t eeprom_ver; size_t offset; /* Read EEPROM format version */ cisco_eeprom_get_byte(&router->mb_eeprom,0,&eeprom_ver); switch(eeprom_ver) { case 0: cisco_eeprom_set_region(&router->mb_eeprom,2,addr->eth_addr_byte,6); break; case 4: if (!cisco_eeprom_v4_find_field(&router->mb_eeprom,0xC3,&offset)) { cisco_eeprom_set_region(&router->mb_eeprom,offset, addr->eth_addr_byte,6); } break; default: vm_error(router->vm,"c3600_burn_mac_addr: unable to handle " "EEPROM version %u\n",eeprom_ver); return(-1); } return(0); } /* Set chassis MAC address */ int c3600_chassis_set_mac_addr(c3600_t *router,char *mac_addr) { if (parse_mac_addr(&router->mac_addr,mac_addr) == -1) { vm_error(router->vm,"unable to parse MAC address '%s'.\n",mac_addr); return(-1); } /* Set the chassis base MAC address */ c3600_burn_mac_addr(router,&router->mac_addr); return(0); } /* Set the chassis type */ int c3600_chassis_set_type(c3600_t *router,char *chassis_type) { struct c3600_chassis_driver *driver; if (router->vm->status == VM_STATUS_RUNNING) { vm_error(router->vm,"unable to change chassis type when online.\n"); return(-1); } if (!(driver = c3600_chassis_get_driver(chassis_type))) { vm_error(router->vm,"unknown chassis type '%s'.\n",chassis_type); return(-1); } router->chassis_driver = driver; /* Copy the mainboard EEPROM */ if (cisco_eeprom_copy(&router->mb_eeprom,driver->eeprom) == -1) { vm_error(router->vm,"unable to set chassis EEPROM '%s'.\n",chassis_type); return(-1); } /* Set the chassis base MAC address */ c3600_burn_mac_addr(router,&router->mac_addr); /* The motherboard has 2 integrated FastEthernet ports on a 3660 */ if (driver->chassis_id == 3660) { vm_slot_remove_binding(router->vm,0,0); vm_slot_add_binding(router->vm,"Leopard-2FE",0,0); } c3600_refresh_systemid(router); return(0); } /* Get the chassis ID */ int c3600_chassis_get_id(c3600_t *router) { if (router->chassis_driver) return(router->chassis_driver->chassis_id); return(-1); } /* Show the list of available chassis drivers */ static void c3600_chassis_show_drivers(void) { int i; printf("Available C3600 chassis drivers:\n"); for(i=0;chassis_drivers[i].chassis_type;i++) { printf(" * %s %s\n", chassis_drivers[i].chassis_type, !chassis_drivers[i].supported ? "(NOT WORKING)" : ""); } printf("\n"); } /* Create the main PCI bus for a GT64010 based system */ static int c3600_init_gt64010(c3600_t *router) { if (!(router->vm->pci_bus[0] = pci_bus_create("PCI bus",0))) { vm_error(router->vm,"unable to create PCI data.\n"); return(-1); } return(dev_gt64010_init(router->vm,"gt64010",C3600_GT64K_ADDR,0x1000, C3600_GT64K_IRQ)); } /* Create the two main PCI busses for a GT64120 based system */ static int c3600_init_gt64120(c3600_t *router) { vm_instance_t *vm = router->vm; vm->pci_bus[0] = pci_bus_create("PCI bus #0",0); vm->pci_bus[1] = pci_bus_create("PCI bus #1",0); if (!vm->pci_bus[0] || !vm->pci_bus[1]) { vm_error(router->vm,"unable to create PCI data.\n"); return(-1); } return(dev_gt64120_init(vm,"gt64120",C3600_GT64K_ADDR,0x1000, C3600_GT64K_IRQ)); } /* Initialize a Cisco 3620 */ static int c3620_init(c3600_t *router) { vm_instance_t *vm = router->vm; int i; /* Set the processor type: R4700 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R4700); /* Initialize the Galileo GT-64010 PCI controller */ if (c3600_init_gt64010(router) == -1) return(-1); /* Initialize PCI map (no PCI bridge for this chassis) */ for(i=0;islots_pci_bus[i] = vm->pci_bus[0]; vm->elf_machine_id = C3620_ELF_MACHINE_ID; return(0); } /* Initialize a Cisco 3640 */ static int c3640_init(c3600_t *router) { vm_instance_t *vm = router->vm; struct nm_bay_info *bay; int i; /* Set the processor type: R4700 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R4700); /* Initialize the Galileo GT-64010 PCI controller */ if (c3600_init_gt64010(router) == -1) return(-1); /* Create the NM PCI busses */ vm->pci_bus_pool[0] = pci_bus_create("NM Slots 0,2",-1); vm->pci_bus_pool[1] = pci_bus_create("NM Slots 1,3",-1); /* Initialize PCI map and PCI bridges */ for(i=0;i<=3;i++) { bay = c3600_nm_get_bay_info(3640,i); /* Map the NM PCI bus */ vm->slots_pci_bus[i] = vm->pci_bus_pool[i & 1]; if (bay && (bay->pci_bridge_device != -1)) dev_dec21052_init(vm->pci_bus[0],bay->pci_bridge_device, vm->slots_pci_bus[i]); } vm->elf_machine_id = C3640_ELF_MACHINE_ID; return(0); } /* Initialize a Cisco 3660 */ static int c3660_init(c3600_t *router) { vm_instance_t *vm = router->vm; struct nm_bay_info *bay; char bus_name[128]; int i; /* Set the processor type: R5271 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R527x); /* Initialize the Galileo GT-64120 PCI controller */ if (c3600_init_gt64120(router) == -1) return(-1); /* Create the NM PCI busses */ for(i=1;i<=6;i++) { snprintf(bus_name,sizeof(bus_name),"NM Slot %d",i); vm->pci_bus_pool[i] = pci_bus_create(bus_name,-1); } /* Slot 0 is mapped to the first bus of GT64120 */ vm->slots_pci_bus[0] = vm->pci_bus[0]; /* Initialize PCI map and PCI bridges */ for(i=1;islots_pci_bus[i] = vm->pci_bus_pool[i]; /* Slots 1-6 are mapped to the second bus of GT64120 */ if (bay && (bay->pci_bridge_device != -1)) dev_dec21152_init(vm->pci_bus[1],bay->pci_bridge_device, vm->slots_pci_bus[i]); } vm->elf_machine_id = C3660_ELF_MACHINE_ID; return(0); } /* Show C3600 hardware info */ void c3600_show_hardware(c3600_t *router) { vm_instance_t *vm = router->vm; printf("C3600 instance '%s' (id %d):\n",vm->name,vm->instance_id); printf(" VM Status : %d\n",vm->status); printf(" RAM size : %u Mb\n",vm->ram_size); printf(" NVRAM size : %u Kb\n",vm->nvram_size); printf(" Chassis : %s\n",router->chassis_driver->chassis_type); printf(" IOS image : %s\n\n",vm->ios_image); if (vm->debug_level > 0) { dev_show_list(vm); pci_dev_show_list(vm->pci_bus[0]); pci_dev_show_list(vm->pci_bus[1]); printf("\n"); } } /* Initialize default parameters for a C3600 */ static void c3600_init_defaults(c3600_t *router) { vm_instance_t *vm = router->vm; n_eth_addr_t *m; m_uint16_t pid; /* Set platform slots characteristics */ vm->nr_slots = C3600_MAX_NM_BAYS; vm->slots_type = CISCO_CARD_TYPE_NM; vm->slots_drivers = nm_drivers; pid = (m_uint16_t)getpid(); /* Generate a chassis MAC address based on the instance ID */ m = &router->mac_addr; m->eth_addr_byte[0] = vm_get_mac_addr_msb(vm); m->eth_addr_byte[1] = vm->instance_id & 0xFF; m->eth_addr_byte[2] = pid >> 8; m->eth_addr_byte[3] = pid & 0xFF; m->eth_addr_byte[4] = 0x00; m->eth_addr_byte[5] = 0x00; router->board_id[0] = 0x00; c3600_init_eeprom_groups(router); c3600_chassis_set_type(router,C3600_DEFAULT_CHASSIS); vm->ram_mmap = C3600_DEFAULT_RAM_MMAP; vm->ram_size = C3600_DEFAULT_RAM_SIZE; vm->rom_size = C3600_DEFAULT_ROM_SIZE; vm->nvram_size = C3600_DEFAULT_NVRAM_SIZE; vm->conf_reg_setup = C3600_DEFAULT_CONF_REG; vm->clock_divisor = C3600_DEFAULT_CLOCK_DIV; vm->nvram_rom_space = C3600_NVRAM_ROM_RES_SIZE; vm->nm_iomem_size = C3600_DEFAULT_IOMEM_SIZE; vm->pcmcia_disk_size[0] = C3600_DEFAULT_DISK0_SIZE; vm->pcmcia_disk_size[1] = C3600_DEFAULT_DISK1_SIZE; } /* Initialize the C3600 Platform */ static int c3600_init_platform(c3600_t *router) { vm_instance_t *vm = router->vm; cpu_mips_t *cpu; cpu_gen_t *gen; vm_obj_t *obj; /* Copy config register setup into "active" config register */ vm->conf_reg = vm->conf_reg_setup; /* Create Console and AUX ports */ vm_init_vtty(vm); /* Create a CPU group */ vm->cpu_group = cpu_group_create("System CPU"); /* Initialize the virtual MIPS processor */ if (!(gen = cpu_create(vm,CPU_TYPE_MIPS64,0))) { vm_error(vm,"unable to create CPU!\n"); return(-1); } cpu = CPU_MIPS64(gen); /* Add this CPU to the system CPU group */ cpu_group_add(vm->cpu_group,gen); vm->boot_cpu = gen; /* Initialize the IRQ routing vectors */ vm->set_irq = mips64_vm_set_irq; vm->clear_irq = mips64_vm_clear_irq; /* Mark the Network IO interrupt as high priority */ cpu->irq_idle_preempt[C3600_NETIO_IRQ] = TRUE; cpu->irq_idle_preempt[C3600_GT64K_IRQ] = TRUE; cpu->irq_idle_preempt[C3600_DUART_IRQ] = TRUE; /* Copy some parameters from VM to CPU (idle PC, ...) */ cpu->idle_pc = vm->idle_pc; if (vm->timer_irq_check_itv) cpu->timer_irq_check_itv = vm->timer_irq_check_itv; /* Get chassis specific driver */ if (!router->chassis_driver) { vm_error(vm,"no chassis defined.\n"); return(-1); } /* Remote emulator control */ dev_remote_control_init(vm,0x16000000,0x1000); /* Bootflash (8 Mb) */ dev_bootflash_init(vm,"bootflash","c3600-bootflash-8mb", C3600_BOOTFLASH_ADDR); /* NVRAM and calendar */ dev_nvram_init(vm,"nvram", C3600_NVRAM_ADDR,vm->nvram_size*1024,&vm->conf_reg); /* Bit-bucket zone */ dev_zero_init(vm,"zero",C3600_BITBUCKET_ADDR,0xc00000); /* IO FPGA */ if (dev_c3600_iofpga_init(router,C3600_IOFPGA_ADDR,0x40000) == -1) return(-1); if (!(obj = vm_object_find(router->vm,"io_fpga"))) return(-1); router->iofpga_data = obj->data; /* PCI IO space */ if (!(vm->pci_io_space = pci_io_data_init(vm,C3600_PCI_IO_ADDR))) return(-1); /* Initialize the chassis */ if (router->chassis_driver->chassis_init(router) == -1) return(-1); /* Initialize RAM */ vm_ram_init(vm,0x00000000ULL); /* Initialize ROM */ if (!vm->rom_filename) { /* use embedded ROM */ dev_rom_init(vm,"rom",C3600_ROM_ADDR,vm->rom_size*1048576, mips64_microcode,mips64_microcode_len); } else { /* use alternate ROM */ dev_ram_init(vm,"rom",TRUE,TRUE,NULL,FALSE, C3600_ROM_ADDR,vm->rom_size*1048576); } /* Initialize the NS16552 DUART */ dev_ns16552_init(vm,C3600_DUART_ADDR,0x1000,3,C3600_DUART_IRQ, vm->vtty_con,vm->vtty_aux); /* Cirrus Logic PD6729 (PCI-to-PCMCIA host adapter) */ dev_clpd6729_init(vm,vm->pci_bus[0],20,vm->pci_io_space,0x4402,0x4403); /* Initialize Network Modules */ if (vm_slot_init_all(vm) == -1) return(-1); /* Show device list */ c3600_show_hardware(router); return(0); } /* Boot the IOS image */ static int c3600_boot_ios(c3600_t *router) { vm_instance_t *vm = router->vm; cpu_mips_t *cpu; if (!vm->boot_cpu) return(-1); /* Suspend CPU activity since we will restart directly from ROM */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Reset the boot CPU */ cpu = CPU_MIPS64(vm->boot_cpu); mips64_reset(cpu); /* Load IOS image */ if (mips64_load_elf_image(cpu,vm->ios_image, (vm->ghost_status == VM_GHOST_RAM_USE), &vm->ios_entry_point) < 0) { vm_error(vm,"failed to load Cisco IOS image '%s'.\n",vm->ios_image); return(-1); } /* Launch the simulation */ printf("\nC3600 '%s': starting simulation (CPU0 PC=0x%llx), " "JIT %sabled.\n", vm->name,cpu->pc,vm->jit_use ? "en":"dis"); vm_log(vm,"C3600_BOOT", "starting instance (CPU0 PC=0x%llx,idle_pc=0x%llx,JIT %s)\n", cpu->pc,cpu->idle_pc,vm->jit_use ? "on":"off"); /* Start main CPU */ if (vm->ghost_status != VM_GHOST_RAM_GENERATE) { vm->status = VM_STATUS_RUNNING; cpu_start(vm->boot_cpu); } else { vm->status = VM_STATUS_SHUTDOWN; } return(0); } /* Set an IRQ */ static void c3600_set_irq(vm_instance_t *vm,u_int irq) { c3600_t *router = VM_C3600(vm); cpu_mips_t *cpu0 = CPU_MIPS64(vm->boot_cpu); u_int slot,port; switch(irq) { case 0 ... 7: mips64_set_irq(cpu0,irq); if (cpu0->irq_idle_preempt[irq]) cpu_idle_break_wait(cpu0->gen); break; case C3600_NETIO_IRQ_BASE ... C3600_NETIO_IRQ_END: c3600_net_irq_get_slot_port(irq,&slot,&port); dev_c3600_iofpga_net_set_irq(router->iofpga_data,slot,port); break; } } /* Clear an IRQ */ static void c3600_clear_irq(vm_instance_t *vm,u_int irq) { c3600_t *router = VM_C3600(vm); cpu_mips_t *cpu0 = CPU_MIPS64(vm->boot_cpu); u_int slot,port; switch(irq) { case 0 ... 7: mips64_clear_irq(cpu0,irq); break; case C3600_NETIO_IRQ_BASE ... C3600_NETIO_IRQ_END: c3600_net_irq_get_slot_port(irq,&slot,&port); dev_c3600_iofpga_net_clear_irq(router->iofpga_data,slot,port); break; } } /* Initialize a Cisco 3600 instance */ static int c3600_init_instance(vm_instance_t *vm) { c3600_t *router = VM_C3600(vm); m_uint32_t rom_entry_point; cpu_mips_t *cpu0; if (!vm->ios_image) { vm_error(vm,"no Cisco IOS image defined."); return(-1); } /* Initialize the C3600 platform */ if (c3600_init_platform(router) == -1) { vm_error(vm,"unable to initialize the platform hardware.\n"); return(-1); } /* IRQ routing */ vm->set_irq = c3600_set_irq; vm->clear_irq = c3600_clear_irq; /* Load IOS configuration files */ if (vm->ios_startup_config != NULL || vm->ios_private_config != NULL) { vm_nvram_push_config(vm,vm->ios_startup_config,vm->ios_private_config); vm->conf_reg &= ~0x40; } /* Load ROM (ELF image or embedded) */ cpu0 = CPU_MIPS64(vm->boot_cpu); rom_entry_point = (m_uint32_t)MIPS_ROM_PC; if ((vm->rom_filename != NULL) && (mips64_load_elf_image(cpu0,vm->rom_filename,0,&rom_entry_point) < 0)) { vm_error(vm,"unable to load alternate ROM '%s', " "fallback to embedded ROM.\n\n",vm->rom_filename); vm->rom_filename = NULL; } /* Load symbol file */ if (vm->sym_filename) { mips64_sym_load_file(cpu0,vm->sym_filename); cpu0->sym_trace = 1; } return(c3600_boot_ios(router)); } /* Stop a Cisco 3600 instance */ static int c3600_stop_instance(vm_instance_t *vm) { printf("\nC3600 '%s': stopping simulation.\n",vm->name); vm_log(vm,"C3600_STOP","stopping simulation.\n"); /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } } /* Free resources that were used during execution to emulate hardware */ vm_slot_shutdown_all(vm); vm_hardware_shutdown(vm); return(0); } static m_uint16_t c3660_oir_masks[C3600_MAX_NM_BAYS] = { 0x0000, 0x0900, /* slot 1 */ 0x0009, /* slot 2 */ 0x0A00, /* slot 3 */ 0x000A, /* slot 4 */ 0x0C00, /* slot 5 */ 0x000C, /* slot 6 */ }; /* Trigger an OIR event (3660 only) */ static int c3600_trigger_oir_event(c3600_t *router,u_int nm_bay) { if (nm_bay >= C3600_MAX_NM_BAYS) return(-1); router->oir_status = c3660_oir_masks[nm_bay]; vm_set_irq(router->vm,C3600_EXT_IRQ); return(0); } /* Initialize a new NM while the virtual router is online (OIR) */ static int c3600_nm_init_online(vm_instance_t *vm,u_int slot,u_int subslot) { if (!slot) { vm_error(vm,"OIR not supported on slot 0.\n"); return(-1); } /* * Suspend CPU activity while adding new hardware (since we change the * memory maps). */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Add the new hardware elements */ if (vm_slot_init(vm,slot) == -1) return(-1); /* Resume normal operations */ vm_resume(vm); /* Now, we can safely trigger the OIR event */ c3600_trigger_oir_event(VM_C3600(vm),slot); return(0); } /* Stop a NM while the virtual router is online (OIR) */ static int c3600_nm_stop_online(vm_instance_t *vm,u_int slot,u_int subslot) { if (!slot) { vm_error(vm,"OIR not supported on slot 0.\n"); return(-1); } /* The NM driver must be initialized */ if (!vm_slot_get_card_ptr(vm,slot)) { vm_error(vm,"trying to shut down empty slot %u.\n",slot); return(-1); } /* Disable all NIOs to stop traffic forwarding */ vm_slot_disable_all_nio(vm,slot); /* We can safely trigger the OIR event */ c3600_trigger_oir_event(VM_C3600(vm),slot); /* * Suspend CPU activity while removing the hardware (since we change the * memory maps). */ vm_suspend(vm); /* Device removal */ if (vm_slot_shutdown(vm,slot) != 0) vm_error(vm,"unable to shutdown slot %u.\n",slot); /* Resume normal operations */ vm_resume(vm); return(0); } /* Get MAC address MSB */ static u_int c3600_get_mac_addr_msb(void) { return(0xCC); } /* Parse specific options for the Cisco 3600 platform */ static int c3600_cli_parse_options(vm_instance_t *vm,int option) { c3600_t *router = VM_C3600(vm); switch(option) { /* IO memory reserved for NMs (in percents!) */ case OPT_IOMEM_SIZE: vm->nm_iomem_size = 0x8000 | atoi(optarg); break; /* Chassis type */ case 't': c3600_chassis_set_type(router,optarg); break; /* Set the base MAC address */ case 'm': if (!c3600_chassis_set_mac_addr(router,optarg)) printf("MAC address set to '%s'.\n",optarg); break; /* Set the System ID */ case 'I': if (!c3600_set_system_id(router,optarg)) printf("System ID set to '%s'.\n",optarg); break; /* Unknown option */ default: return(-1); } return(0); } /* Show specific CLI options */ static void c3600_cli_show_options(vm_instance_t *vm) { printf(" -t : Select Chassis type (default: \"%s\")\n" " --iomem-size : IO memory (in percents, default: %u)\n" " -p : Define a Network Module\n" " -I : Set Processor Board Serial Number\n" " -s : Bind a Network IO interface to a " "Network Module\n", C3600_DEFAULT_CHASSIS,vm->nm_iomem_size); } /* Platform definition */ static vm_platform_t c3600_platform = { "c3600", "C3600", "3600", c3600_create_instance, c3600_delete_instance, c3600_init_instance, c3600_stop_instance, c3600_nm_init_online, c3600_nm_stop_online, c3600_nvram_extract_config, c3600_nvram_push_config, c3600_get_mac_addr_msb, c3600_save_config, c3600_cli_parse_options, c3600_cli_show_options, c3600_chassis_show_drivers, }; /* Register the c3600 platform */ int c3600_platform_register(void) { if (vm_platform_register(&c3600_platform) == -1) return(-1); return(hypervisor_c3600_init(&c3600_platform)); } dynamips-0.2.14/common/dev_c3600.h000066400000000000000000000114721241034141600164550ustar00rootroot00000000000000/* * Cisco 3600 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Generic Cisco 3600 routines and definitions (EEPROM,...). */ #ifndef __DEV_C3600_H__ #define __DEV_C3600_H__ #include #include "utils.h" #include "net.h" #include "device.h" #include "pci_dev.h" #include "nmc93cX6.h" #include "net_io.h" #include "vm.h" /* Default C3600 parameters */ #define C3600_DEFAULT_CHASSIS "3640" #define C3600_DEFAULT_RAM_SIZE 128 #define C3600_DEFAULT_ROM_SIZE 2 #define C3600_DEFAULT_NVRAM_SIZE 128 #define C3600_DEFAULT_CONF_REG 0x2102 #define C3600_DEFAULT_CLOCK_DIV 4 #define C3600_DEFAULT_RAM_MMAP 1 #define C3600_DEFAULT_DISK0_SIZE 0 #define C3600_DEFAULT_DISK1_SIZE 0 #define C3600_DEFAULT_IOMEM_SIZE 5 /* Percents! */ /* 6 NM slots for the 3660 + integrated FastEthernet ports */ #define C3600_MAX_NM_BAYS 7 /* C3600 DUART Interrupt */ #define C3600_DUART_IRQ 5 /* C3600 Network I/O Interrupt */ #define C3600_NETIO_IRQ 2 /* C3600 GT64k DMA/Timer Interrupt */ #define C3600_GT64K_IRQ 4 /* C3600 External Interrupt */ #define C3600_EXT_IRQ 6 /* C3600 NM Management Interrupt handler */ #define C3600_NM_MGMT_IRQ 3 /* Network IRQ */ #define C3600_NETIO_IRQ_BASE 32 #define C3600_NETIO_IRQ_PORT_BITS 2 #define C3600_NETIO_IRQ_PORT_MASK ((1 << C3600_NETIO_IRQ_PORT_BITS) - 1) #define C3600_NETIO_IRQ_PER_SLOT (1 << C3600_NETIO_IRQ_PORT_BITS) #define C3600_NETIO_IRQ_END \ (C3600_NETIO_IRQ_BASE + (C3600_MAX_NM_BAYS * C3600_NETIO_IRQ_PER_SLOT) - 1) /* C3600 common device addresses */ #define C3600_GT64K_ADDR 0x14000000ULL #define C3600_IOFPGA_ADDR 0x1e800000ULL #define C3600_DUART_ADDR 0x1e840000ULL #define C3600_BITBUCKET_ADDR 0x1ec00000ULL #define C3600_NVRAM_ADDR 0x1fe00000ULL #define C3600_ROM_ADDR 0x1fc00000ULL #define C3600_BOOTFLASH_ADDR 0x30000000ULL #define C3600_PCI_IO_ADDR 0x100000000ULL /* Reserved space for ROM in NVRAM */ #define C3600_NVRAM_ROM_RES_SIZE 2048 /* C3600 ELF Platform ID */ #define C3620_ELF_MACHINE_ID 0x1e #define C3640_ELF_MACHINE_ID 0x1e #define C3660_ELF_MACHINE_ID 0x34 #define VM_C3600(vm) ((c3600_t *)vm->hw_data) /* C3600 router */ typedef struct c3600_router c3600_t; /* Prototype of chassis driver initialization function */ typedef int (*c3600_chassis_init_fn)(c3600_t *router); /* C3600 Chassis Driver */ struct c3600_chassis_driver { char *chassis_type; int chassis_id; int supported; c3600_chassis_init_fn chassis_init; struct cisco_eeprom *eeprom; }; /* C3600 router */ struct c3600_router { /* Chassis MAC address */ n_eth_addr_t mac_addr; char board_id[20]; /* Associated VM instance */ vm_instance_t *vm; /* I/O FPGA */ struct c3600_iofpga_data *iofpga_data; /* Chassis information */ struct c3600_chassis_driver *chassis_driver; m_uint16_t oir_status; /* * Mainboard EEPROM. * It can be modified to change the chassis MAC address. */ struct cisco_eeprom mb_eeprom; struct nmc93cX6_group mb_eeprom_group; /* Network Module EEPROMs (3620/3640) */ struct nmc93cX6_group nm_eeprom_group; /* Cisco 3660 NM EEPROMs */ struct nmc93cX6_group c3660_nm_eeprom_group[C3600_MAX_NM_BAYS]; }; /* Set EEPROM for the specified slot */ int c3600_set_slot_eeprom(c3600_t *router,u_int slot, struct cisco_eeprom *eeprom); /* Get network IRQ for specified slot/port */ u_int c3600_net_irq_for_slot_port(u_int slot,u_int port); /* Show the list of available NM drivers */ void c3600_nm_show_drivers(void); /* Set chassis MAC address */ int c3600_chassis_set_mac_addr(c3600_t *router,char *mac_addr); /* Set the system id */ int c3600_set_system_id(c3600_t *router,char *id); /* Burn the system id into the appropriate eeprom if possible */ int c3600_refresh_systemid(c3600_t *router); /* Set the chassis type */ int c3600_chassis_set_type(c3600_t *router,char *chassis_type); /* Get the chassis ID */ int c3600_chassis_get_id(c3600_t *router); /* Show C3600 hardware info */ void c3600_show_hardware(c3600_t *router); /* Initialize EEPROM groups */ void c3600_init_eeprom_groups(c3600_t *router); /* dev_c3600_iofpga_init() */ int dev_c3600_iofpga_init(c3600_t *router,m_uint64_t paddr,m_uint32_t len); /* Register the c3600 platform */ int c3600_platform_register(void); /* Hypervisor C3600 initialization */ extern int hypervisor_c3600_init(vm_platform_t *platform); /* NM drivers */ extern struct cisco_card_driver dev_c3600_nm_1e_driver; extern struct cisco_card_driver dev_c3600_nm_4e_driver; extern struct cisco_card_driver dev_c3600_nm_1fe_tx_driver; extern struct cisco_card_driver dev_c3600_nm_4t_driver; extern struct cisco_card_driver dev_c3600_leopard_2fe_driver; extern struct cisco_card_driver dev_c3600_nm_16esw_driver; extern struct cisco_card_driver dev_c3600_nmd_36esw_driver; #endif dynamips-0.2.14/common/dev_c3600_bay.c000066400000000000000000000034361241034141600173040ustar00rootroot00000000000000/* * Cisco 3600 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Cisco 3600 Network Modules. Info are obtained with "show pci bridge". */ #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "device.h" #include "dev_c3600_bay.h" /* C3620 NM info */ static struct nm_bay_info c3620_nm_bays[2] = { { -1, 0x05 }, /* Slot 0: PCI bus 0, device 5 */ { -1, 0x0d }, /* Slot 1: PCI bus 0, device 13 */ }; /* C3640 NM info */ static struct nm_bay_info c3640_nm_bays[4] = { { 0x03, 0x00 }, /* Slot 0: PCI bus 2, device 0 */ { 0x02, 0x00 }, /* Slot 1: PCI bus 1, device 0 */ { -1, 0x08 }, /* Slot 2: PCI bus 2, device 8 */ { -1, 0x08 }, /* Slot 3: PCI bus 1, device 8 */ }; /* C3660 NM info */ static struct nm_bay_info c3660_nm_bays[7] = { { -1, 0x06 }, /* Slot 0: PCI bus 0, device 6 */ { 0x02, 0x00 }, /* Slot 1: PCI bus 2 */ { 0x07, 0x00 }, /* Slot 2: PCI bus 22 */ { 0x03, 0x00 }, /* Slot 3: PCI bus 6 */ { 0x06, 0x00 }, /* Slot 4: PCI bus 18 */ { 0x04, 0x00 }, /* Slot 5: PCI bus 10 */ { 0x05, 0x00 }, /* Slot 6: PCI bus 14 */ }; /* Get NM bay information */ struct nm_bay_info *c3600_nm_get_bay_info(u_int chassis,u_int nm_bay) { struct nm_bay_info *bay_info; u_int max_bays = 0; switch(chassis) { case 3620: bay_info = c3620_nm_bays; max_bays = 2; break; case 3640: bay_info = c3640_nm_bays; max_bays = 4; break; case 3660: bay_info = c3660_nm_bays; max_bays = 7; break; } if (nm_bay >= max_bays) return NULL; return(&bay_info[nm_bay]); } dynamips-0.2.14/common/dev_c3600_bay.h000066400000000000000000000006601241034141600173050ustar00rootroot00000000000000/* * Cisco 3600 simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Network Module helper. */ #ifndef __DEV_C3600_BAY_H__ #define __DEV_C3600_BAY_H__ #include "pci_dev.h" #include "dev_c3600.h" /* NM Information */ struct nm_bay_info { int pci_bridge_device; int pci_device; }; /* Get a NM bay information */ struct nm_bay_info *c3600_nm_get_bay_info(u_int chassis,u_int nm_bay); #endif dynamips-0.2.14/common/dev_c3600_eth.c000066400000000000000000000240441241034141600173070ustar00rootroot00000000000000/* * Cisco C3600 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Ethernet Network Modules. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_am79c971.h" #include "dev_nm_16esw.h" #include "dev_c3600.h" #include "dev_c3600_bay.h" /* Multi-Ethernet NM with Am79c971 chips */ struct nm_eth_data { u_int nr_port; struct am79c971_data *port[8]; }; /* * dev_c3600_nm_eth_init() * * Add an Ethernet Network Module into specified slot. */ static int dev_c3600_nm_eth_init(vm_instance_t *vm,struct cisco_card *card, int nr_port,int interface_type, const struct cisco_eeprom *eeprom) { struct nm_bay_info *bay_info; struct nm_eth_data *data; u_int slot = card->slot_id; u_int chassis_id; int i; /* Allocate the private data structure */ if (!(data = malloc(sizeof(*data)))) { vm_error(vm,"%s: out of memory.\n",card->dev_name); return(-1); } memset(data,0,sizeof(*data)); data->nr_port = nr_port; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,eeprom); c3600_set_slot_eeprom(VM_C3600(vm),slot,&card->eeprom); /* Get PCI bus info about this bay */ chassis_id = c3600_chassis_get_id(VM_C3600(vm)); bay_info = c3600_nm_get_bay_info(chassis_id,slot); if (!bay_info) { vm_error(vm,"unable to get info for NM bay %u\n",slot); return(-1); } /* Create the AMD Am971c971 chip(s) */ for(i=0;inr_port;i++) { data->port[i] = dev_am79c971_init(vm,card->dev_name,interface_type, card->pci_bus,bay_info->pci_device+i, c3600_net_irq_for_slot_port(slot,i)); } /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove an Ethernet NM from the specified slot */ static int dev_c3600_nm_eth_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct nm_eth_data *data = card->drv_info; int i; /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c3600_set_slot_eeprom(VM_C3600(vm),card->slot_id,NULL); /* Remove the AMD Am79c971 chips */ for(i=0;inr_port;i++) dev_am79c971_remove(data->port[i]); free(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c3600_nm_eth_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct nm_eth_data *d = card->drv_info; if (!d || (port_id >= d->nr_port)) return(-1); dev_am79c971_set_nio(d->port[port_id],nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c3600_nm_eth_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct nm_eth_data *d = card->drv_info; if (!d || (port_id >= d->nr_port)) return(-1); dev_am79c971_unset_nio(d->port[port_id]); return(0); } /* ====================================================================== */ /* NM-1E */ /* ====================================================================== */ /* * dev_c3600_nm_1e_init() * * Add a NM-1E Network Module into specified slot. */ static int dev_c3600_nm_1e_init(vm_instance_t *vm,struct cisco_card *card) { return(dev_c3600_nm_eth_init(vm,card,1,AM79C971_TYPE_10BASE_T, cisco_eeprom_find_nm("NM-1E"))); } /* ====================================================================== */ /* NM-4E */ /* ====================================================================== */ /* * dev_c3600_nm_4e_init() * * Add a NM-4E Network Module into specified slot. */ static int dev_c3600_nm_4e_init(vm_instance_t *vm,struct cisco_card *card) { return(dev_c3600_nm_eth_init(vm,card,4,AM79C971_TYPE_10BASE_T, cisco_eeprom_find_nm("NM-4E"))); } /* ====================================================================== */ /* NM-1FE-TX */ /* ====================================================================== */ /* * dev_c3600_nm_1fe_tx_init() * * Add a NM-1FE-TX Network Module into specified slot. */ static int dev_c3600_nm_1fe_tx_init(vm_instance_t *vm,struct cisco_card *card) { return(dev_c3600_nm_eth_init(vm,card,1,AM79C971_TYPE_100BASE_TX, cisco_eeprom_find_nm("NM-1FE-TX"))); } /* ====================================================================== */ /* NM-16ESW */ /* ====================================================================== */ /* Add a NM-16ESW */ static int dev_c3600_nm_16esw_init(vm_instance_t *vm,struct cisco_card *card) { struct nm_bay_info *bay_info; struct nm_16esw_data *data; u_int slot = card->slot_id; u_int chassis_id; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_nm("NM-16ESW")); dev_nm_16esw_burn_mac_addr(vm,slot,&card->eeprom); c3600_set_slot_eeprom(VM_C3600(vm),slot,&card->eeprom); /* Get PCI bus info about this bay */ chassis_id = c3600_chassis_get_id(VM_C3600(vm)); bay_info = c3600_nm_get_bay_info(chassis_id,slot); if (!bay_info) { vm_error(vm,"unable to get info for NM bay %u\n",slot); return(-1); } /* Create the device */ data = dev_nm_16esw_init(vm,card->dev_name,slot, card->pci_bus,bay_info->pci_device, c3600_net_irq_for_slot_port(slot,0)); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a NM-16ESW from the specified slot */ static int dev_c3600_nm_16esw_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct nm_16esw_data *data = card->drv_info; /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c3600_set_slot_eeprom(VM_C3600(vm),card->slot_id,NULL); /* Remove the BCM5600 chip */ dev_nm_16esw_remove(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c3600_nm_16esw_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_set_nio(d,port_id,nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c3600_nm_16esw_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_unset_nio(d,port_id); return(0); } /* Show debug info */ static int dev_c3600_nm_16esw_show_info(vm_instance_t *vm,struct cisco_card *card) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_show_info(d); return(0); } /* ====================================================================== */ /* Leopard-2FE */ /* ====================================================================== */ /* * Leopard-2FE: 2 FastEthernet ports on C3660 motherboard. * * Leopard-2FE is the FRU/Product Number displayed by "show diag". */ static m_uint16_t eeprom_c3600_leopard_2fe_data[] = { 0x04FF, 0xC18B, 0x4A41, 0x4230, 0x3530, 0x3330, 0x3454, 0x3809, 0x3440, 0x00B3, 0xC046, 0x0320, 0x0012, 0x8104, 0x4241, 0x3085, 0x1C0C, 0xA202, 0x80FF, 0xFFFF, 0xFFC4, 0x08FF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFA1, 0xFFFF, 0xFFFF, 0x03FF, 0x04FF, 0xC508, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFF00, }; static const struct cisco_eeprom eeprom_c3600_leopard_2fe = { "Leopard-2FE", (m_uint16_t *)eeprom_c3600_leopard_2fe_data, sizeof(eeprom_c3600_leopard_2fe_data)/2, }; /* * dev_c3600_leopard_2fe_init() * * Add Leopard-2FE (only Cisco 3660, in slot 0). */ static int dev_c3600_leopard_2fe_init(vm_instance_t *vm,struct cisco_card *card) { if (card->slot_id != 0) { vm_error(vm,"dev_c3600_leopard_2fe_init: bad slot %u specified.\n", card->slot_id); return(-1); } return(dev_c3600_nm_eth_init(vm,card,2,AM79C971_TYPE_100BASE_TX, &eeprom_c3600_leopard_2fe)); } /* ====================================================================== */ /* NM-1FE-TX driver */ struct cisco_card_driver dev_c3600_nm_1fe_tx_driver = { "NM-1FE-TX", 1, 0, dev_c3600_nm_1fe_tx_init, dev_c3600_nm_eth_shutdown, NULL, dev_c3600_nm_eth_set_nio, dev_c3600_nm_eth_unset_nio, NULL, }; /* NM-1E driver */ struct cisco_card_driver dev_c3600_nm_1e_driver = { "NM-1E", 1, 0, dev_c3600_nm_1e_init, dev_c3600_nm_eth_shutdown, NULL, dev_c3600_nm_eth_set_nio, dev_c3600_nm_eth_unset_nio, NULL, }; /* NM-4E driver */ struct cisco_card_driver dev_c3600_nm_4e_driver = { "NM-4E", 1, 0, dev_c3600_nm_4e_init, dev_c3600_nm_eth_shutdown, NULL, dev_c3600_nm_eth_set_nio, dev_c3600_nm_eth_unset_nio, NULL, }; /* NM-16ESW driver */ struct cisco_card_driver dev_c3600_nm_16esw_driver = { "NM-16ESW", 1, 0, dev_c3600_nm_16esw_init, dev_c3600_nm_16esw_shutdown, NULL, dev_c3600_nm_16esw_set_nio, dev_c3600_nm_16esw_unset_nio, dev_c3600_nm_16esw_show_info, }; /* Leopard-2FE driver */ struct cisco_card_driver dev_c3600_leopard_2fe_driver = { "Leopard-2FE", 1, 0, dev_c3600_leopard_2fe_init, dev_c3600_nm_eth_shutdown, NULL, dev_c3600_nm_eth_set_nio, dev_c3600_nm_eth_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c3600_iofpga.c000066400000000000000000000515301241034141600177740ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * TODO: Online Insertion/Removal (OIR). */ #include #include #include #include #include #include #include #include #include "ptask.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_vtty.h" #include "nmc93cX6.h" #include "dev_c3600.h" /* Debugging flags */ #define DEBUG_UNKNOWN 1 #define DEBUG_ACCESS 0 #define DEBUG_NET_IRQ 0 /* Definitions for Mainboard EEPROM */ #define EEPROM_MB_DOUT 3 #define EEPROM_MB_DIN 2 #define EEPROM_MB_CLK 1 #define EEPROM_MB_CS 0 /* Definitions for Network Modules EEPROM */ #define EEPROM_NM_DOUT 7 #define EEPROM_NM_DIN 6 #define EEPROM_NM_CLK 2 #define EEPROM_NM_CS 4 /* Network IRQ distribution */ struct net_irq_distrib { u_int c3620_c3640_offset; u_int c3660_reg; u_int c3660_offset; }; /* * Network IRQ distribution for c3620/c3640 * * Slot 0 | 3620/3640: reg 0x20001 | 3660: reg 0x20010, offset 0 * Slot 1 | 3620/3640: reg 0x20000 | 3660: reg 0x10010, offset 24 * Slot 2 | 3640 : reg 0x20003 | 3660: reg 0x10010, offset 16 * Slot 3 | 3640 : reg 0x20002 | 3660: reg 0x10010, offset 28 * Slot 4 | 3620/3640: N/A | 3660: reg 0x10010, offset 20 * Slot 5 | 3620/3640: N/A | 3660: reg 0x10010, offset 8 * Slot 6 | 3620/3640: N/A | 3660: reg 0x10010, offset 0 */ static struct net_irq_distrib net_irq_dist[C3600_MAX_NM_BAYS] = { { 16, 1, 0 }, { 24, 0, 24 }, { 0, 0, 16 }, { 8, 0, 28 }, { 32, 0, 20 }, { 32, 0, 8 }, { 32, 0, 0 }, }; /* IO FPGA structure */ struct c3600_iofpga_data { vm_obj_t vm_obj; struct vdevice dev; c3600_t *router; /* Network IRQ status */ m_uint32_t net_irq_status[2]; /* Slot select for EEPROM access */ u_int eeprom_slot; /* IO Mask. Don't know the meaning */ m_uint8_t io_mask; m_uint16_t sel; }; /* Mainboard EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_mb_def = { EEPROM_MB_CLK, EEPROM_MB_CS, EEPROM_MB_DIN, EEPROM_MB_DOUT, }; /* Mainboard EEPROM */ static const struct nmc93cX6_group eeprom_mb_group = { EEPROM_TYPE_NMC93C46, 1, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "Mainboard EEPROM", { &eeprom_mb_def }, }; /* NM EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_nm_def = { EEPROM_NM_CLK, EEPROM_NM_CS, EEPROM_NM_DIN, EEPROM_NM_DOUT, }; /* NM EEPROM */ static const struct nmc93cX6_group eeprom_nm_group = { EEPROM_TYPE_NMC93C46, 1, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "NM EEPROM", { &eeprom_nm_def }, }; /* C3660 NM presence masks */ static const m_uint16_t c3660_nm_masks[6] = { 0xF0FF, /* slot 1 */ 0xFFF0, /* slot 2 */ 0x0FFF, /* slot 3 */ 0xFF0F, /* slot 4 */ 0xF0FF, /* slot 5 */ 0xFFF0, /* slot 6 */ }; /* Select the current NM EEPROM */ static void nm_eeprom_select(struct c3600_iofpga_data *d,u_int slot) { struct cisco_eeprom *eeprom = NULL; struct cisco_card *card; card = vm_slot_get_card_ptr(d->router->vm,slot); if (card != NULL) eeprom = &card->eeprom; d->router->nm_eeprom_group.eeprom[0] = eeprom; } /* Return the NM status register given the detected EEPROM (3620/3640) */ static u_int nm_get_status_1(struct c3600_iofpga_data *d) { u_int res = 0xFFFF; int i; for(i=0;i<4;i++) { if (vm_slot_check_eeprom(d->router->vm,i,0)) res &= ~(0x1111 << i); } return(res); } /* Return the NM status register given the detected EEPROM (3660) */ static u_int nm_get_status_2(struct c3600_iofpga_data *d,u_int pos) { u_int res = 0xFFFF; u_int start,end; int i; switch(pos) { case 0: /* word 0: slot 1 - 4 */ start = 1; end = 4; break; case 1: /* word 1: slot 5 - 6 */ start = 5; end = 6; break; default: return(res); } for(i=start;i<=end;i++) { if (vm_slot_check_eeprom(d->router->vm,i,0)) res &= c3660_nm_masks[i-1]; } return(res); } /* Update network interrupt status */ static inline void dev_c3620_c3640_iofpga_net_update_irq(struct c3600_iofpga_data *d) { if (d->net_irq_status[0]) { vm_set_irq(d->router->vm,C3600_NETIO_IRQ); } else { vm_clear_irq(d->router->vm,C3600_NETIO_IRQ); } } static inline void dev_c3660_iofpga_net_update_irq(struct c3600_iofpga_data *d) { if (d->net_irq_status[0] || d->net_irq_status[1]) { vm_set_irq(d->router->vm,C3600_NETIO_IRQ); } else { vm_clear_irq(d->router->vm,C3600_NETIO_IRQ); } } /* Trigger a Network IRQ for the specified slot/port */ void dev_c3600_iofpga_net_set_irq(struct c3600_iofpga_data *d, u_int slot,u_int port) { struct net_irq_distrib *irq_dist; #if DEBUG_NET_IRQ vm_log(d->router->vm,"IO_FPGA","setting NetIRQ for slot %u port %u\n", slot,port); #endif irq_dist = &net_irq_dist[slot]; switch(c3600_chassis_get_id(d->router)) { case 3620: case 3640: d->net_irq_status[0] |= (1 << (irq_dist->c3620_c3640_offset + port)); dev_c3620_c3640_iofpga_net_update_irq(d); break; case 3660: d->net_irq_status[irq_dist->c3660_reg] |= (1 << (irq_dist->c3660_offset + port)); dev_c3660_iofpga_net_update_irq(d); break; } } /* Clear a Network IRQ for the specified slot/port */ void dev_c3600_iofpga_net_clear_irq(struct c3600_iofpga_data *d, u_int slot,u_int port) { struct net_irq_distrib *irq_dist; #if DEBUG_NET_IRQ vm_log(d->router->vm,"IO_FPGA","clearing NetIRQ for slot %u port %u\n", slot,port); #endif irq_dist = &net_irq_dist[slot]; switch(c3600_chassis_get_id(d->router)) { case 3620: case 3640: d->net_irq_status[0] &= ~(1 << (irq_dist->c3620_c3640_offset + port)); dev_c3620_c3640_iofpga_net_update_irq(d); break; case 3660: d->net_irq_status[irq_dist->c3660_reg] &= ~(1 << (irq_dist->c3660_offset + port)); dev_c3660_iofpga_net_update_irq(d); break; } } /* * dev_c3620_c3640_iofpga_access() */ static void * dev_c3620_c3640_iofpga_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct c3600_iofpga_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0x0; #if DEBUG_ACCESS if (offset != 0x0c) { if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA","reading reg 0x%x at pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA", "writing reg 0x%x at pc=0x%llx, data=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),*data,op_size); } } #endif switch(offset) { /* Probably flash protection (if 0, no write access allowed) */ case 0x00008: if (op_type == MTS_READ) *data = 0xFF; break; /* Bootflash of 8 Mb */ case 0x0000a: if (op_type == MTS_READ) *data = 0x1000; break; /* * 0x7d00 is written here regularly. * Some kind of hardware watchdog ? */ case 0x0000c: break; /* Mainboard EEPROM */ case 0x0000e: if (op_type == MTS_WRITE) nmc93cX6_write(&d->router->mb_eeprom_group,(u_int)(*data)); else *data = nmc93cX6_read(&d->router->mb_eeprom_group); break; /* * Network modules presence. * * Bit 0: 0 = NM in slot 0 is valid * Bit 1: 0 = NM in slot 1 is valid * Bit 2: 0 = NM in slot 2 is valid * Bit 3: 0 = NM in slot 3 is valid * * Very well explained on Cisco website: * http://www.cisco.com/en/US/customer/products/hw/routers/ps274/products_tech_note09186a0080109510.shtml */ case 0x10006: if (op_type == MTS_READ) *data = nm_get_status_1(d); break; /* * NM EEPROMs. */ case 0x10008: if (op_type == MTS_WRITE) { d->eeprom_slot = *data & 0x03; nm_eeprom_select(d,d->eeprom_slot); nmc93cX6_write(&d->router->nm_eeprom_group,*data); } else { *data = nmc93cX6_read(&d->router->nm_eeprom_group); } break; /* Network interrupt status */ case 0x20000: /* slot 1 */ if (op_type == MTS_READ) *data = d->net_irq_status[0] >> 24; break; case 0x20001: /* slot 0 */ if (op_type == MTS_READ) *data = d->net_irq_status[0] >> 16; break; case 0x20002: /* slot 3 */ if (op_type == MTS_READ) *data = d->net_irq_status[0] >> 8; break; case 0x20003: /* slot 2 */ if (op_type == MTS_READ) *data = d->net_irq_status[0]; break; /* * Read when a PA Management interrupt is triggered. * * If not 0, we get: * "Error: Unexpected NM Interrupt received from slot: x" */ case 0x20004: if (op_type == MTS_READ) *data = 0x00; vm_clear_irq(d->router->vm,C3600_NM_MGMT_IRQ); break; /* * Read when an external interrupt is triggered. * * Bit 4: 1 = %UNKNOWN-1-GT64010: Unknown fatal interrupt(s) * Bit 6: 1 = %OIRINT: OIR Event has occurred oir_ctrl 1000 oir_stat FFFF * * oir_ctrl = register 0x10004 * oir_stat = register 0x10006 */ case 0x20006: if (op_type == MTS_READ) *data = 0x00; vm_clear_irq(d->router->vm,C3600_EXT_IRQ); break; case 0x10004: /* ??? OIR control ??? */ if (op_type == MTS_READ) { *data = 0x0000; } break; /* IO Mask (displayed by "show c3600") */ case 0x20008: if (op_type == MTS_READ) *data = d->io_mask; else d->io_mask = *data; break; /* * Platform type ? * 0: 3640, 4 << 5: 3620, 3 << 5: 3660 */ case 0x30000: if (op_type == MTS_READ) { switch(c3600_chassis_get_id(d->router)) { case 3620: *data = 4 << 5; break; case 3640: *data = 0 << 5; break; case 3660: *data = 3 << 5; break; default: *data = 0; } } break; /* ??? */ case 0x30002: if (op_type == MTS_WRITE) { d->sel = *data; } else { //*data = d->sel; } break; /* * Environmental parameters, determined with "sh env all". * * Bit 0: 0 = overtemperature condition. * Bit 4: 0 = RPS present. * Bit 5: 0 = Input Voltage status failure. * Bit 6: 1 = Thermal status failure. * Bit 7: 1 = DC Output Voltage status failure. */ case 0x30004: if (op_type == MTS_READ) { *data = 32 + 1; } break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA", "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA", "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* * dev_c3660_iofpga_access() */ static void * dev_c3660_iofpga_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct c3600_iofpga_data *d = dev->priv_data; u_int slot; if (op_type == MTS_READ) *data = 0x0; #if DEBUG_ACCESS if (offset != 0x0c) { if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA","reading reg 0x%x at pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA", "writing reg 0x%x at pc=0x%llx, data=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),*data,op_size); } } #endif switch(offset) { /* * 0x7d00 is written here regularly. * Some kind of hardware watchdog ? */ case 0x0000c: break; /* Probably flash protection (if 0, no write access allowed) */ case 0x00008: if (op_type == MTS_READ) *data = 0xFF; break; /* Bootflash of 8 Mb */ case 0x0000a: if (op_type == MTS_READ) *data = 0x1000; break; /* NM presence - slots 1 to 4 */ case 0x10006: if (op_type == MTS_READ) *data = nm_get_status_2(d,0); break; /* NM presence - slot 5 to 6 */ case 0x10008: if (op_type == MTS_READ) *data = nm_get_status_2(d,1); break; /* Fan status, PS presence */ case 0x10018: if (op_type == MTS_READ) *data = 0x0000; break; /* unknown, read by env monitor */ case 0x1001a: if (op_type == MTS_READ) *data = 0x0000; break; /* board temperature */ case 0x30004: if (op_type == MTS_READ) { *data = 32 + 1; } break; /* sh c3600: Per Slot Intr Mask */ case 0x10016: if (op_type == MTS_READ) *data = 0x12; break; /* sh c3600: OIR fsm state slot's (12) */ case 0x10020: if (op_type == MTS_READ) *data = 0x00; break; /* sh c3600: OIR fsm state slot's (34) */ case 0x10022: if (op_type == MTS_READ) *data = 0x00; break; /* sh c3600: OIR fsm state slot's (56) */ case 0x10024: if (op_type == MTS_READ) *data = 0x00; break; /* * Backplane EEPROM. * * Bit 7: 0=Telco chassis, 1=Enterprise chassis. */ case 0x10000: if (op_type == MTS_WRITE) nmc93cX6_write(&d->router->mb_eeprom_group,(u_int)(*data)); else *data = nmc93cX6_read(&d->router->mb_eeprom_group) | 0x80; break; /* NM EEPROMs - slots 1 to 6 */ case 0x1000a: case 0x1000b: case 0x1000c: case 0x1000d: case 0x1000e: case 0x1000f: slot = (offset - 0x1000a) + 1; if (op_type == MTS_WRITE) { nmc93cX6_write(&d->router->c3660_nm_eeprom_group[slot], (u_int)(*data)); } else { *data = nmc93cX6_read(&d->router->c3660_nm_eeprom_group[slot]); } break; /* NM EEPROM - slot 0 */ case 0x20006: if (op_type == MTS_WRITE) { nmc93cX6_write(&d->router->c3660_nm_eeprom_group[0], (u_int)(*data)); } else { *data = nmc93cX6_read(&d->router->c3660_nm_eeprom_group[0]); } break; /* Unknown EEPROMs ? */ case 0x20000: case 0x20002: case 0x20004: if (op_type == MTS_READ) *data = 0xFFFF; break; /* IO Mask (displayed by "show c3600") */ case 0x20008: if (op_type == MTS_READ) *data = d->io_mask; else d->io_mask = *data; break; /* 0: 3640, 4 << 5: 3620, 3 << 5: 3660 */ case 0x30000: if (op_type == MTS_READ) *data = 3 << 5; break; /* ??? */ case 0x30008: if (op_type == MTS_READ) *data = 0xFF; break; /* * Read at net interrupt (size 4). * It seems that there are 4 lines per slot. * * Bit 24-27: slot 1 * Bit 16-19: slot 2 * Bit 28-31: slot 3 * Bit 20-23: slot 4 * Bit 08-11: slot 5 * Bit 00-03: slot 6 * * Other bits are unknown. */ case 0x10010: if (op_type == MTS_READ) *data = d->net_irq_status[0]; break; /* * Read at net interrupt (size 1) * * Bit 7-6: we get "Unexpected AIM interrupt on AIM slot 1". * Bit 5-4: we get "Unexpected AIM interrupt on AIM slot 0". * Bit 0-3: net interrupt for slot 0. */ case 0x20010: if (op_type == MTS_READ) *data = d->net_irq_status[1]; break; /* * Read when a PA Management interrupt is triggered. * * If not 0, we get: * "Error: Unexpected NM Interrupt received from slot: x" */ case 0x10014: if (op_type == MTS_READ) *data = 0x00; vm_clear_irq(d->router->vm,C3600_NM_MGMT_IRQ); break; /* * Read when an external interrupt is triggered. * * Bit 4: 1 = %UNKNOWN-1-GT64010: Unknown fatal interrupt(s) * Bit 6: 1 = %OIRINT: OIR Event has occurred oir_ctrl 1000 oir_stat FFFF * * oir_ctrl = register 0x10004 * oir_stat = register 0x10006 */ case 0x2000a: if (op_type == MTS_READ) { if (d->router->oir_status != 0) *data = 0x40; } vm_clear_irq(d->router->vm,C3600_EXT_IRQ); break; /* * oir_ctrl (seen with "debug oir") * * Bits 0-2 : OIR event for slots 2,4,6 * Bit 3 : Summary for slots 2,4,6 * Bits 4-6 : OIR watchdog (?) for slots 2,4,6 * Bit 7 : Unknown/unused ? * Bits 8-10 : OIR event for slots 1,3,5 * Bit 11 : Summary for slots 1,3,5 * Bits 12-14 : OIR watchdog (?) for slots 1,3,5 * Bit 15 : Unknown/unused ? */ case 0x10004: if (op_type == MTS_READ) *data = d->router->oir_status; else d->router->oir_status &= ~(*data); break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA", "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA", "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* Initialize EEPROM groups */ void c3600_init_eeprom_groups(c3600_t *router) { int i; /* Initialize Mainboard EEPROM */ router->mb_eeprom_group = eeprom_mb_group; router->mb_eeprom_group.eeprom[0] = &router->mb_eeprom; router->mb_eeprom.data = NULL; router->mb_eeprom.len = 0; /* Initialize NM EEPROM for 3620/3640 */ router->nm_eeprom_group = eeprom_nm_group; router->nm_eeprom_group.eeprom[0] = NULL; /* Initialize NM EEPROM for 3660 */ for(i=0;ic3660_nm_eeprom_group[i] = eeprom_nm_group; router->c3660_nm_eeprom_group[i].eeprom[0] = NULL; } } /* Shutdown the IO FPGA device */ static void dev_c3600_iofpga_shutdown(vm_instance_t *vm,struct c3600_iofpga_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* * dev_c3600_iofpga_init() */ int dev_c3600_iofpga_init(c3600_t *router,m_uint64_t paddr,m_uint32_t len) { vm_instance_t *vm = router->vm; struct c3600_iofpga_data *d; /* Allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"IO_FPGA: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); d->router = router; vm_object_init(&d->vm_obj); d->vm_obj.name = "io_fpga"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_c3600_iofpga_shutdown; /* Set device properties */ dev_init(&d->dev); d->dev.name = "io_fpga"; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.priv_data = d; switch(router->chassis_driver->chassis_id) { case 3620: case 3640: d->dev.handler = dev_c3620_c3640_iofpga_access; break; case 3660: d->dev.handler = dev_c3660_iofpga_access; break; default: fprintf(stderr,"C3600 '%s': invalid chassis ID %d\n", router->vm->name,router->chassis_driver->chassis_id); free(d); return(-1); } /* Map this device to the VM */ vm_bind_device(router->vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_c3600_iofpga.h000066400000000000000000000013511241034141600177750ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005-2007 Christophe Fillot (cf@utc.fr) * * Cisco c3600 I/O FPGA. */ #ifndef __DEV_C3600_IOFPGA_H__ #define __DEV_C3600_IOFPGA_H__ /* Forward declaration for IO_FPGA private data */ struct c3600_iofpga_data; /* Trigger a Network IRQ for the specified slot/port */ void dev_c3600_iofpga_net_set_irq(struct c3600_iofpga_data *d, u_int slot,u_int port); /* Clear a Network IRQ for the specified slot/port */ void dev_c3600_iofpga_net_clear_irq(struct c3600_iofpga_data *d, u_int slot,u_int port); /* Create the c3600 I/O FPGA */ int dev_c3600_iofpga_init(c3600_t *router,m_uint64_t paddr,m_uint32_t len); #endif dynamips-0.2.14/common/dev_c3600_serial.c000066400000000000000000000056571241034141600200170ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Serial Network Modules. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_mueslix.h" #include "dev_c3600.h" #include "dev_c3600_bay.h" /* ====================================================================== */ /* NM-4T */ /* ====================================================================== */ /* * dev_c3600_nm_4t_init() * * Add a NM-4T network module into specified slot. */ int dev_c3600_nm_4t_init(vm_instance_t *vm,struct cisco_card *card) { struct nm_bay_info *bay_info; struct mueslix_data *data; u_int slot = card->slot_id; u_int chassis_id; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_nm("NM-4T")); c3600_set_slot_eeprom(VM_C3600(vm),slot,&card->eeprom); /* Get PCI bus info about this bay */ chassis_id = c3600_chassis_get_id(VM_C3600(vm)); bay_info = c3600_nm_get_bay_info(chassis_id,slot); if (!bay_info) { vm_error(vm,"unable to get info for NM bay %u\n",slot); return(-1); } /* Create the Mueslix chip */ data = dev_mueslix_init(vm,card->dev_name,0, card->pci_bus,bay_info->pci_device, c3600_net_irq_for_slot_port(slot,0)); if (!data) return(-1); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a NM-4T from the specified slot */ int dev_c3600_nm_4t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c3600_set_slot_eeprom(VM_C3600(vm),card->slot_id,NULL); /* Remove the mueslix driver */ dev_mueslix_remove(card->drv_info); return(0); } /* Bind a Network IO descriptor to a specific port */ int dev_c3600_nm_4t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct mueslix_data *d = card->drv_info; if (!d || (port_id >= MUESLIX_NR_CHANNELS)) return(-1); return(dev_mueslix_set_nio(d,port_id,nio)); } /* Unbind a Network IO descriptor to a specific port */ int dev_c3600_nm_4t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct mueslix_data *d = card->drv_info; if (!d || (port_id >= MUESLIX_NR_CHANNELS)) return(-1); return(dev_mueslix_unset_nio(d,port_id)); } /* NM-4T driver */ struct cisco_card_driver dev_c3600_nm_4t_driver = { "NM-4T", 1, 0, dev_c3600_nm_4t_init, dev_c3600_nm_4t_shutdown, NULL, dev_c3600_nm_4t_set_nio, dev_c3600_nm_4t_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c3725.c000066400000000000000000000502241241034141600164560ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * Patched by Jeremy Grossmann for the GNS3 project (www.gns3.net) * * Generic Cisco 3725 routines and definitions (EEPROM,...). */ #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "pci_io.h" #include "dev_gt.h" #include "cisco_eeprom.h" #include "dev_rom.h" #include "dev_c3725.h" #include "dev_c3725_iofpga.h" #include "dev_vtty.h" #include "registry.h" #include "fs_nvram.h" /* ======================================================================== */ /* EEPROM definitions */ /* ======================================================================== */ /* Cisco 3725 mainboard EEPROM */ static m_uint16_t eeprom_c3725_mainboard_data[] = { 0x04FF, 0xC18B, 0x5858, 0x5858, 0x5858, 0x5858, 0x5858, 0x5809, 0x6140, 0x0259, 0xC046, 0x0320, 0x003F, 0x1302, 0x4244, 0x3085, 0x1C10, 0x8206, 0x80FF, 0xFFFF, 0xFFC4, 0x08FF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFF81, 0xFFFF, 0xFFFF, 0x03FF, 0x04FF, 0xC28B, 0x4654, 0x5830, 0x3934, 0x3557, 0x304D, 0x59C3, 0x0600, 0x1319, 0x5C6F, 0x7043, 0x0030, 0xC508, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x4100, 0x0101, 0x02FF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; struct cisco_eeprom eeprom_c3725_mainboard = { "C3725 Backplane", eeprom_c3725_mainboard_data, sizeof(eeprom_c3725_mainboard_data)/2, }; /* ======================================================================== */ /* Network Module Drivers */ /* ======================================================================== */ static struct cisco_card_driver *nm_drivers[] = { &dev_c3725_nm_1fe_tx_driver, &dev_c3725_nm_16esw_driver, &dev_c3725_gt96100_fe_driver, &dev_c3725_nm_4t_driver, &dev_c3725_nm_nam_driver, &dev_c3725_nm_cids_driver, NULL, }; /* ======================================================================== */ /* Cisco 3725 router instances */ /* ======================================================================== */ /* Initialize default parameters for a C3725 */ static void c3725_init_defaults(c3725_t *router); /* Directly extract the configuration from the NVRAM device */ static int c3725_nvram_extract_config(vm_instance_t *vm,u_char **startup_config,size_t *startup_len,u_char **private_config,size_t *private_len) { int ret; ret = generic_nvram_extract_config(vm, "rom", C3725_NVRAM_OFFSET, C3725_NVRAM_SIZE, 0, FS_NVRAM_FORMAT_WITH_BACKUP, startup_config, startup_len, private_config, private_len); return(ret); } /* Directly push the IOS configuration to the NVRAM device */ static int c3725_nvram_push_config(vm_instance_t *vm,u_char *startup_config,size_t startup_len,u_char *private_config,size_t private_len) { int ret; ret = generic_nvram_push_config(vm, "rom", vm->rom_size*1048576, C3725_NVRAM_OFFSET, C3725_NVRAM_SIZE, 0, FS_NVRAM_FORMAT_WITH_BACKUP, startup_config, startup_len, private_config, private_len); return(ret); } /* Check for empty config */ static int c3725_nvram_check_empty_config(vm_instance_t *vm) { struct vdevice *rom_dev; m_uint64_t addr; size_t len; if (!(rom_dev = dev_get_by_name(vm,"rom"))) return(-1); addr = rom_dev->phys_addr + C3725_NVRAM_OFFSET; len = C3725_NVRAM_SIZE; while(len > 0) { if (physmem_copy_u32_from_vm(vm,addr) != 0) return(0); addr += sizeof(m_uint32_t); len -= sizeof(m_uint32_t); } /* Empty NVRAM */ vm->conf_reg |= 0x0040; printf("NVRAM is empty, setting config register to 0x%x\n",vm->conf_reg); return(0); } /* Create a new router instance */ static int c3725_create_instance(vm_instance_t *vm) { c3725_t *router; if (!(router = malloc(sizeof(*router)))) { fprintf(stderr,"C3725 '%s': Unable to create new instance!\n",vm->name); return(-1); } memset(router,0,sizeof(*router)); router->vm = vm; vm->hw_data = router; c3725_init_defaults(router); return(0); } /* Free resources used by a router instance */ static int c3725_delete_instance(vm_instance_t *vm) { c3725_t *router = VM_C3725(vm); int i; /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(FALSE); } } /* Remove NIO bindings */ for(i=0;inr_slots;i++) vm_slot_remove_all_nio_bindings(vm,i); /* Shutdown all Network Modules */ vm_slot_shutdown_all(vm); /* Free mainboard EEPROM */ cisco_eeprom_free(&router->mb_eeprom); /* Free all resources used by VM */ vm_free(vm); /* Free the router structure */ free(router); return(TRUE); } /* Get WIC device address for the specified onboard port */ int c3725_get_onboard_wic_addr(u_int slot,m_uint64_t *phys_addr) { if (slot >= C3725_MAX_WIC_BAYS) return(-1); *phys_addr = C3725_WIC_ADDR + (slot * C3725_WIC_SIZE); return(0); } /* Set EEPROM for the specified slot */ int c3725_set_slot_eeprom(c3725_t *router,u_int slot, struct cisco_eeprom *eeprom) { if ((slot < 1) || (slot >= C3725_MAX_NM_BAYS)) return(-1); router->nm_eeprom_group[slot-1].eeprom[0] = eeprom; return(0); } /* Get slot/port corresponding to specified network IRQ */ static inline void c3725_net_irq_get_slot_port(u_int irq,u_int *slot,u_int *port) { irq -= C3725_NETIO_IRQ_BASE; *port = irq & C3725_NETIO_IRQ_PORT_MASK; *slot = irq >> C3725_NETIO_IRQ_PORT_BITS; } /* Get network IRQ for specified slot/port */ u_int c3725_net_irq_for_slot_port(u_int slot,u_int port) { u_int irq; irq = (slot << C3725_NETIO_IRQ_PORT_BITS) + port; irq += C3725_NETIO_IRQ_BASE; return(irq); } /* Get PCI device for the specified NM bay */ int c3725_nm_get_pci_device(u_int nm_bay) { switch(nm_bay) { case 1: return(0x06); case 2: return(0x0A); default: return(-1); } } /* Set the system id or processor board id in the eeprom */ int c3725_set_system_id(c3725_t *router,char *id) { /* 11 characters is enough. Array is 20 long */ strncpy(router->board_id,id,13); /* Make sure it is null terminated */ router->board_id[13] = 0x00; c3725_refresh_systemid(router); return 0; } int c3725_refresh_systemid(c3725_t *router) { if (router->board_id[0] == 0x00) return(0); m_uint8_t buf[11]; parse_board_id(buf,router->board_id,11); cisco_eeprom_set_region(&router->mb_eeprom ,62,buf,11); return (0); } /* Set the base MAC address of the chassis */ static int c3725_burn_mac_addr(c3725_t *router,n_eth_addr_t *addr) { m_uint8_t eeprom_ver; size_t offset; /* Read EEPROM format version */ cisco_eeprom_get_byte(&router->mb_eeprom,0,&eeprom_ver); switch(eeprom_ver) { case 0: cisco_eeprom_set_region(&router->mb_eeprom,2,addr->eth_addr_byte,6); break; case 4: if (!cisco_eeprom_v4_find_field(&router->mb_eeprom,0xC3,&offset)) { cisco_eeprom_set_region(&router->mb_eeprom,offset, addr->eth_addr_byte,6); } break; default: vm_error(router->vm,"c3725_burn_mac_addr: unable to handle " "EEPROM version %u\n",eeprom_ver); return(-1); } return(0); } /* Set chassis MAC address */ int c3725_chassis_set_mac_addr(c3725_t *router,char *mac_addr) { if (parse_mac_addr(&router->mac_addr,mac_addr) == -1) { vm_error(router->vm,"unable to parse MAC address '%s'.\n",mac_addr); return(-1); } /* Set the chassis base MAC address */ c3725_burn_mac_addr(router,&router->mac_addr); return(0); } /* Create the two main PCI busses for a GT64120 based system */ static int c3725_init_gt96100(c3725_t *router) { vm_instance_t *vm = router->vm; vm_obj_t *obj; vm->pci_bus[0] = pci_bus_create("PCI bus #0",0); vm->pci_bus[1] = pci_bus_create("PCI bus #1",0); if (!vm->pci_bus[0] || !vm->pci_bus[1]) { vm_error(router->vm,"unable to create PCI data.\n"); return(-1); } if (dev_gt96100_init(vm,"gt96100",C3725_GT96K_ADDR,0x200000, C3725_GT96K_IRQ, C3725_EXT_IRQ, c3725_net_irq_for_slot_port(0,0), 255) == -1) return(-1); if (!(obj = vm_object_find(router->vm,"gt96100"))) return(-1); router->gt_data = obj->data; return(0); } /* Initialize a Cisco 3725 */ static int c3725_init(c3725_t *router) { vm_instance_t *vm = router->vm; /* Set the processor type: R7000 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R7000); /* Initialize the Galileo GT-96100 PCI controller */ if (c3725_init_gt96100(router) == -1) return(-1); /* Initialize PCI map (NM slots 1 & 2) */ vm->slots_pci_bus[1] = vm->pci_bus[1]; vm->slots_pci_bus[2] = vm->pci_bus[1]; vm->elf_machine_id = C3725_ELF_MACHINE_ID; return(0); } /* Show C3725 hardware info */ void c3725_show_hardware(c3725_t *router) { vm_instance_t *vm = router->vm; printf("C3725 instance '%s' (id %d):\n",vm->name,vm->instance_id); printf(" VM Status : %d\n",vm->status); printf(" RAM size : %u Mb\n",vm->ram_size); printf(" NVRAM size : %u Kb\n",vm->nvram_size); printf(" IOS image : %s\n\n",vm->ios_image); if (vm->debug_level > 0) { dev_show_list(vm); pci_dev_show_list(vm->pci_bus[0]); pci_dev_show_list(vm->pci_bus[1]); printf("\n"); } } /* Initialize default parameters for a C3725 */ static void c3725_init_defaults(c3725_t *router) { vm_instance_t *vm = router->vm; n_eth_addr_t *m; m_uint16_t pid; /* Set platform slots characteristics */ vm->nr_slots = C3725_MAX_NM_BAYS; vm->slots_type = CISCO_CARD_TYPE_NM; vm->slots_drivers = nm_drivers; pid = (m_uint16_t)getpid(); /* Generate a chassis MAC address based on the instance ID */ m = &router->mac_addr; m->eth_addr_byte[0] = vm_get_mac_addr_msb(vm); m->eth_addr_byte[1] = vm->instance_id & 0xFF; m->eth_addr_byte[2] = pid >> 8; m->eth_addr_byte[3] = pid & 0xFF; m->eth_addr_byte[4] = 0x00; m->eth_addr_byte[5] = 0x00; router->board_id[0] = 0x00; c3725_init_eeprom_groups(router); cisco_eeprom_copy(&router->mb_eeprom,&eeprom_c3725_mainboard); c3725_burn_mac_addr(router,&router->mac_addr); /* The GT96100 system controller has 2 integrated FastEthernet ports */ vm_slot_add_binding(vm,"GT96100-FE",0,0); vm->ram_mmap = C3725_DEFAULT_RAM_MMAP; vm->ram_size = C3725_DEFAULT_RAM_SIZE; vm->rom_size = C3725_DEFAULT_ROM_SIZE; vm->nvram_size = C3725_DEFAULT_NVRAM_SIZE; vm->conf_reg_setup = C3725_DEFAULT_CONF_REG; vm->clock_divisor = C3725_DEFAULT_CLOCK_DIV; vm->nvram_rom_space = C3725_NVRAM_ROM_RES_SIZE; vm->nm_iomem_size = C3725_DEFAULT_IOMEM_SIZE; vm->pcmcia_disk_size[0] = C3725_DEFAULT_DISK0_SIZE; vm->pcmcia_disk_size[1] = C3725_DEFAULT_DISK1_SIZE; } /* Initialize the C3725 Platform */ static int c3725_init_platform(c3725_t *router) { vm_instance_t *vm = router->vm; cpu_mips_t *cpu; cpu_gen_t *gen; vm_obj_t *obj; /* Copy config register setup into "active" config register */ vm->conf_reg = vm->conf_reg_setup; /* Create Console and AUX ports */ vm_init_vtty(vm); /* Create a CPU group */ vm->cpu_group = cpu_group_create("System CPU"); /* Initialize the virtual MIPS processor */ if (!(gen = cpu_create(vm,CPU_TYPE_MIPS64,0))) { vm_error(vm,"unable to create CPU!\n"); return(-1); } cpu = CPU_MIPS64(gen); /* Add this CPU to the system CPU group */ cpu_group_add(vm->cpu_group,gen); vm->boot_cpu = gen; /* Initialize the IRQ routing vectors */ vm->set_irq = mips64_vm_set_irq; vm->clear_irq = mips64_vm_clear_irq; /* Mark the Network IO interrupt as high priority */ cpu->irq_idle_preempt[C3725_NETIO_IRQ] = TRUE; cpu->irq_idle_preempt[C3725_GT96K_IRQ] = TRUE; cpu->irq_idle_preempt[C3725_DUART_IRQ] = TRUE; /* Copy some parameters from VM to CPU (idle PC, ...) */ cpu->idle_pc = vm->idle_pc; if (vm->timer_irq_check_itv) cpu->timer_irq_check_itv = vm->timer_irq_check_itv; /* Remote emulator control */ dev_remote_control_init(vm,0x16000000,0x1000); /* Specific Storage Area (SSA) */ dev_ram_init(vm,"ssa",TRUE,FALSE,NULL,FALSE,0x16001000ULL,0x7000); /* IO FPGA */ if (dev_c3725_iofpga_init(router,C3725_IOFPGA_ADDR,0x40000) == -1) return(-1); if (!(obj = vm_object_find(router->vm,"io_fpga"))) return(-1); router->iofpga_data = obj->data; #if 0 /* PCI IO space */ if (!(vm->pci_io_space = pci_io_data_init(vm,C3725_PCI_IO_ADDR))) return(-1); #endif /* Initialize the chassis */ if (c3725_init(router) == -1) return(-1); /* Initialize RAM */ vm_ram_init(vm,0x00000000ULL); /* Initialize ROM (as a Flash) */ if (!(obj = dev_flash_init(vm,"rom",C3725_ROM_ADDR,vm->rom_size*1048576))) return(-1); dev_flash_copy_data(obj,0,mips64_microcode,mips64_microcode_len); c3725_nvram_check_empty_config(vm); /* Byte swapping */ dev_bswap_init(vm,"mem_bswap",C3725_BSWAP_ADDR,1024*1048576,0x00000000ULL); /* Initialize the NS16552 DUART */ dev_ns16552_init(vm,C3725_DUART_ADDR,0x1000,3,C3725_DUART_IRQ, vm->vtty_con,vm->vtty_aux); /* PCMCIA Slot 0 */ dev_pcmcia_disk_init(vm,"slot0",C3725_SLOT0_ADDR,0x200000, vm->pcmcia_disk_size[0],1); /* PCMCIA Slot 1 */ dev_pcmcia_disk_init(vm,"slot1",C3725_SLOT1_ADDR,0x200000, vm->pcmcia_disk_size[1],1); /* Initialize Network Modules */ if (vm_slot_init_all(vm) == -1) return(-1); /* Show device list */ c3725_show_hardware(router); return(0); } /* Boot the IOS image */ static int c3725_boot_ios(c3725_t *router) { vm_instance_t *vm = router->vm; cpu_mips_t *cpu; if (!vm->boot_cpu) return(-1); /* Suspend CPU activity since we will restart directly from ROM */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Reset the boot CPU */ cpu = CPU_MIPS64(vm->boot_cpu); mips64_reset(cpu); /* Load IOS image */ if (mips64_load_elf_image(cpu,vm->ios_image, (vm->ghost_status == VM_GHOST_RAM_USE), &vm->ios_entry_point) < 0) { vm_error(vm,"failed to load Cisco IOS image '%s'.\n",vm->ios_image); return(-1); } /* Launch the simulation */ printf("\nC3725 '%s': starting simulation (CPU0 PC=0x%llx), " "JIT %sabled.\n", vm->name,cpu->pc,vm->jit_use ? "en":"dis"); vm_log(vm,"C3725_BOOT", "starting instance (CPU0 PC=0x%llx,idle_pc=0x%llx,JIT %s)\n", cpu->pc,cpu->idle_pc,vm->jit_use ? "on":"off"); /* Start main CPU */ if (vm->ghost_status != VM_GHOST_RAM_GENERATE) { vm->status = VM_STATUS_RUNNING; cpu_start(vm->boot_cpu); } else { vm->status = VM_STATUS_SHUTDOWN; } return(0); } /* Set an IRQ */ static void c3725_set_irq(vm_instance_t *vm,u_int irq) { c3725_t *router = VM_C3725(vm); cpu_mips_t *cpu0 = CPU_MIPS64(vm->boot_cpu); u_int slot,port; switch(irq) { case 0 ... 7: mips64_set_irq(cpu0,irq); if (cpu0->irq_idle_preempt[irq]) cpu_idle_break_wait(cpu0->gen); break; case C3725_NETIO_IRQ_BASE ... C3725_NETIO_IRQ_END: c3725_net_irq_get_slot_port(irq,&slot,&port); dev_c3725_iofpga_net_set_irq(router->iofpga_data,slot,port); break; } } /* Clear an IRQ */ static void c3725_clear_irq(vm_instance_t *vm,u_int irq) { c3725_t *router = VM_C3725(vm); cpu_mips_t *cpu0 = CPU_MIPS64(vm->boot_cpu); u_int slot,port; switch(irq) { case 0 ... 7: mips64_clear_irq(cpu0,irq); break; case C3725_NETIO_IRQ_BASE ... C3725_NETIO_IRQ_END: c3725_net_irq_get_slot_port(irq,&slot,&port); dev_c3725_iofpga_net_clear_irq(router->iofpga_data,slot,port); break; } } /* Initialize a Cisco 3725 instance */ static int c3725_init_instance(vm_instance_t *vm) { c3725_t *router = VM_C3725(vm); m_uint32_t rom_entry_point; cpu_mips_t *cpu0; if (!vm->ios_image) { vm_error(vm,"no Cisco IOS image defined."); return(-1); } /* Initialize the C3725 platform */ if (c3725_init_platform(router) == -1) { vm_error(vm,"unable to initialize the platform hardware.\n"); return(-1); } /* IRQ routing */ vm->set_irq = c3725_set_irq; vm->clear_irq = c3725_clear_irq; /* Load IOS configuration files */ if (vm->ios_startup_config != NULL || vm->ios_private_config != NULL) { vm_nvram_push_config(vm,vm->ios_startup_config,vm->ios_private_config); vm->conf_reg &= ~0x40; } /* Load ROM (ELF image or embedded) */ cpu0 = CPU_MIPS64(vm->boot_cpu); rom_entry_point = (m_uint32_t)MIPS_ROM_PC; if ((vm->rom_filename != NULL) && (mips64_load_elf_image(cpu0,vm->rom_filename,0,&rom_entry_point) < 0)) { vm_error(vm,"unable to load alternate ROM '%s', " "fallback to embedded ROM.\n\n",vm->rom_filename); vm->rom_filename = NULL; } /* Load symbol file */ if (vm->sym_filename) { mips64_sym_load_file(cpu0,vm->sym_filename); cpu0->sym_trace = 1; } return(c3725_boot_ios(router)); } /* Stop a Cisco 3725 instance */ static int c3725_stop_instance(vm_instance_t *vm) { printf("\nC3725 '%s': stopping simulation.\n",vm->name); vm_log(vm,"C3725_STOP","stopping simulation.\n"); /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } } /* Free resources that were used during execution to emulate hardware */ vm_slot_shutdown_all(vm); vm_hardware_shutdown(vm); /* Cleanup */ VM_C3725(vm)->iofpga_data = NULL; VM_C3725(vm)->gt_data = NULL; return(0); } /* Get MAC address MSB */ static u_int c3725_get_mac_addr_msb(void) { return(0xC2); } /* Parse specific options for the Cisco 3725 platform */ static int c3725_cli_parse_options(vm_instance_t *vm,int option) { c3725_t *router = VM_C3725(vm); switch(option) { /* IO memory reserved for NMs (in percents!) */ case OPT_IOMEM_SIZE: vm->nm_iomem_size = 0x8000 | atoi(optarg); break; /* Set the base MAC address */ case 'm': if (!c3725_chassis_set_mac_addr(router,optarg)) printf("MAC address set to '%s'.\n",optarg); break; /* Set the System ID */ case 'I': if (!c3725_set_system_id(router,optarg)) printf("System ID set to '%s'.\n",optarg); break; /* Unknown option */ default: return(-1); } return(0); } /* Show specific CLI options */ static void c3725_cli_show_options(vm_instance_t *vm) { printf(" --iomem-size : IO memory (in percents, default: %u)\n" " -p : Define a Network Module\n" " -I : Set Processor Board Serial Number\n" " -s : Bind a Network IO interface to a " "Network Module\n", vm->nm_iomem_size); } /* Platform definition */ static vm_platform_t c3725_platform = { "c3725", "C3725", "3725", c3725_create_instance, c3725_delete_instance, c3725_init_instance, c3725_stop_instance, NULL, NULL, c3725_nvram_extract_config, c3725_nvram_push_config, c3725_get_mac_addr_msb, NULL, c3725_cli_parse_options, c3725_cli_show_options, NULL, }; /* Register the c3725 platform */ int c3725_platform_register(void) { if (vm_platform_register(&c3725_platform) == -1) return(-1); return(hypervisor_c3725_init(&c3725_platform)); } dynamips-0.2.14/common/dev_c3725.h000066400000000000000000000107441241034141600164660ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Generic Cisco 3725 routines and definitions (EEPROM,...). */ #ifndef __DEV_C3725_H__ #define __DEV_C3725_H__ #include #include "utils.h" #include "net.h" #include "device.h" #include "pci_dev.h" #include "nmc93cX6.h" #include "net_io.h" #include "vm.h" /* Default C3725 parameters */ #define C3725_DEFAULT_RAM_SIZE 128 #define C3725_DEFAULT_ROM_SIZE 2 #define C3725_DEFAULT_NVRAM_SIZE 112 #define C3725_DEFAULT_CONF_REG 0x2102 #define C3725_DEFAULT_CLOCK_DIV 8 #define C3725_DEFAULT_RAM_MMAP 1 #define C3725_DEFAULT_DISK0_SIZE 16 #define C3725_DEFAULT_DISK1_SIZE 0 #define C3725_DEFAULT_IOMEM_SIZE 5 /* Percents! */ /* 3725 characteritics: 2 NM, 3 WIC, 2 AIM */ #define C3725_MAX_NM_BAYS 3 #define C3725_MAX_WIC_BAYS 3 /* C3725 DUART Interrupt */ #define C3725_DUART_IRQ 5 /* C3725 Network I/O Interrupt */ #define C3725_NETIO_IRQ 2 /* C3725 GT64k DMA/Timer Interrupt */ #define C3725_GT96K_IRQ 3 /* C3725 External Interrupt */ #define C3725_EXT_IRQ 6 /* Network IRQ */ #define C3725_NETIO_IRQ_BASE 32 #define C3725_NETIO_IRQ_PORT_BITS 2 #define C3725_NETIO_IRQ_PORT_MASK ((1 << C3725_NETIO_IRQ_PORT_BITS) - 1) #define C3725_NETIO_IRQ_PER_SLOT (1 << C3725_NETIO_IRQ_PORT_BITS) #define C3725_NETIO_IRQ_END \ (C3725_NETIO_IRQ_BASE + (C3725_MAX_NM_BAYS * C3725_NETIO_IRQ_PER_SLOT) - 1) /* C3725 common device addresses */ #define C3725_GT96K_ADDR 0x14000000ULL #define C3725_IOFPGA_ADDR 0x1e800000ULL #define C3725_BITBUCKET_ADDR 0x1ec00000ULL #define C3725_ROM_ADDR 0x1fc00000ULL #define C3725_SLOT0_ADDR 0x30000000ULL #define C3725_SLOT1_ADDR 0x32000000ULL #define C3725_DUART_ADDR 0x3c100000ULL #define C3725_WIC_ADDR 0x3c200000ULL #define C3725_BSWAP_ADDR 0xc0000000ULL #define C3725_PCI_IO_ADDR 0x100000000ULL /* WIC interval in address space */ #define C3725_WIC_SIZE 0x2000 /* Offset of simulated NVRAM in ROM flash */ #define C3725_NVRAM_OFFSET 0xE0000 #define C3725_NVRAM_SIZE 0x1C000 // with backup /* Reserved space for ROM in NVRAM */ #define C3725_NVRAM_ROM_RES_SIZE 0 /* C3725 ELF Platform ID */ #define C3725_ELF_MACHINE_ID 0x61 #define VM_C3725(vm) ((c3725_t *)vm->hw_data) /* C3725 router */ typedef struct c3725_router c3725_t; /* C3725 router */ struct c3725_router { /* Chassis MAC address */ n_eth_addr_t mac_addr; char board_id[20]; /* Associated VM instance */ vm_instance_t *vm; /* GT96100 data */ struct gt_data *gt_data; /* I/O FPGA */ struct c3725_iofpga_data *iofpga_data; /* Chassis information */ m_uint8_t oir_status; /* * Mainboard EEPROM. * It can be modified to change the chassis MAC address. */ struct cisco_eeprom mb_eeprom; struct nmc93cX6_group mb_eeprom_group; /* Network Module EEPROMs */ struct nmc93cX6_group nm_eeprom_group[2]; }; /* Get WIC device address for the specified onboard port */ int c3725_get_onboard_wic_addr(u_int slot,m_uint64_t *phys_addr); /* Set EEPROM for the specified slot */ int c3725_set_slot_eeprom(c3725_t *router,u_int slot, struct cisco_eeprom *eeprom); /* Get network IRQ for specified slot/port */ u_int c3725_net_irq_for_slot_port(u_int slot,u_int port); /* Get PCI device for the specified NM bay */ int c3725_nm_get_pci_device(u_int nm_bay); /* Set chassis MAC address */ int c3725_chassis_set_mac_addr(c3725_t *router,char *mac_addr); /* Set the system id */ int c3725_set_system_id(c3725_t *router,char *id); /* Burn the system id into the appropriate eeprom if possible */ int c3725_refresh_systemid(c3725_t *router); /* Show C3725 hardware info */ void c3725_show_hardware(c3725_t *router); /* Initialize EEPROM groups */ void c3725_init_eeprom_groups(c3725_t *router); /* Register the c3725 platform */ int c3725_platform_register(void); /* Hypervisor C3725 initialization */ extern int hypervisor_c3725_init(vm_platform_t *platform); /* NM drivers */ extern struct cisco_card_driver dev_c3725_nm_1fe_tx_driver; extern struct cisco_card_driver dev_c3725_gt96100_fe_driver; extern struct cisco_card_driver dev_c3725_nm_4t_driver; extern struct cisco_card_driver dev_c3725_nm_16esw_driver; extern struct cisco_card_driver dev_c3725_nmd_36esw_driver; extern struct cisco_card_driver dev_c3725_nm_nam_driver; extern struct cisco_card_driver dev_c3725_nm_cids_driver; /* WIC drivers */ extern struct cisco_card_driver *dev_c3725_mb_wic_drivers[]; #endif dynamips-0.2.14/common/dev_c3725_eth.c000066400000000000000000000210421241034141600173120ustar00rootroot00000000000000/* * Cisco C3725 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Ethernet Network Modules. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_am79c971.h" #include "dev_nm_16esw.h" #include "dev_gt.h" #include "dev_c3725.h" /* Multi-Ethernet NM with Am79c971 chips */ struct nm_eth_data { u_int nr_port; struct am79c971_data *port[8]; }; /* Return sub-slot info for integrated WIC slots (on motherboard) */ static int dev_c3725_mb_get_sub_info(vm_instance_t *vm,struct cisco_card *card, u_int port_id, struct cisco_card_driver ***drv_array, u_int *subcard_type) { /* 3 integrated WIC slots */ if ((port_id & 0x0F) >= 3) return(-1); *drv_array = dev_c3725_mb_wic_drivers; *subcard_type = CISCO_CARD_TYPE_WIC; return(0); } /* * dev_c3725_nm_eth_init() * * Add an Ethernet Network Module into specified slot. */ static int dev_c3725_nm_eth_init(vm_instance_t *vm,struct cisco_card *card, int nr_port,int interface_type, const struct cisco_eeprom *eeprom) { struct nm_eth_data *data; u_int slot = card->slot_id; int i; /* Allocate the private data structure */ if (!(data = malloc(sizeof(*data)))) { vm_error(vm,"%s: out of memory.\n",card->dev_name); return(-1); } memset(data,0,sizeof(*data)); data->nr_port = nr_port; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,eeprom); c3725_set_slot_eeprom(VM_C3725(vm),slot,&card->eeprom); /* Create the AMD Am971c971 chip(s) */ for(i=0;inr_port;i++) { data->port[i] = dev_am79c971_init(vm,card->dev_name,interface_type, card->pci_bus, c3725_nm_get_pci_device(slot), c3725_net_irq_for_slot_port(slot,i)); } /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove an Ethernet NM from the specified slot */ static int dev_c3725_nm_eth_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct nm_eth_data *data = card->drv_info; int i; /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c3725_set_slot_eeprom(VM_C3725(vm),card->slot_id,NULL); /* Remove the AMD Am79c971 chips */ for(i=0;inr_port;i++) dev_am79c971_remove(data->port[i]); free(data); return(0); } static int dev_c3725_nm_eth_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct nm_eth_data *d = card->drv_info; if (!d || (port_id >= d->nr_port)) return(-1); dev_am79c971_set_nio(d->port[port_id],nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c3725_nm_eth_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct nm_eth_data *d = card->drv_info; if (!d || (port_id >= d->nr_port)) return(-1); dev_am79c971_unset_nio(d->port[port_id]); return(0); } /* ====================================================================== */ /* NM-1FE-TX */ /* ====================================================================== */ /* * dev_c3725_nm_1fe_tx_init() * * Add a NM-1FE-TX Network Module into specified slot. */ static int dev_c3725_nm_1fe_tx_init(vm_instance_t *vm,struct cisco_card *card) { return(dev_c3725_nm_eth_init(vm,card,1,AM79C971_TYPE_100BASE_TX, cisco_eeprom_find_nm("NM-1FE-TX"))); } /* ====================================================================== */ /* NM-16ESW */ /* ====================================================================== */ /* Add a NM-16ESW */ static int dev_c3725_nm_16esw_init(vm_instance_t *vm,struct cisco_card *card) { struct nm_16esw_data *data; u_int slot = card->slot_id; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_nm("NM-16ESW")); dev_nm_16esw_burn_mac_addr(vm,slot,&card->eeprom); c3725_set_slot_eeprom(VM_C3725(vm),slot,&card->eeprom); /* Create the device */ data = dev_nm_16esw_init(vm,card->dev_name,slot, card->pci_bus,c3725_nm_get_pci_device(slot), c3725_net_irq_for_slot_port(slot,0)); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a NM-16ESW from the specified slot */ static int dev_c3725_nm_16esw_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct nm_16esw_data *data = card->drv_info; /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c3725_set_slot_eeprom(VM_C3725(vm),card->slot_id,NULL); /* Remove the BCM5600 chip */ dev_nm_16esw_remove(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c3725_nm_16esw_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_set_nio(d,port_id,nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c3725_nm_16esw_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_unset_nio(d,port_id); return(0); } /* Show debug info */ static int dev_c3725_nm_16esw_show_info(vm_instance_t *vm,struct cisco_card *card) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_show_info(d); return(0); } /* ====================================================================== */ /* GT96100 - Integrated Ethernet ports */ /* ====================================================================== */ /* Initialize Ethernet part of the GT96100 controller */ static int dev_c3725_gt96100_fe_init(vm_instance_t *vm,struct cisco_card *card) { if (card->slot_id != 0) { vm_error(vm,"dev_c3725_gt96100_fe_init: bad slot %u specified.\n", card->slot_id); return(-1); } /* Store device info into the router structure */ card->drv_info = VM_C3725(vm)->gt_data; return(0); } /* Nothing to do, we never remove the system controller */ static int dev_c3725_gt96100_fe_shutdown(vm_instance_t *vm,struct cisco_card *card) { return(0); } /* Bind a Network IO descriptor */ static int dev_c3725_gt96100_fe_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct gt_data *d = card->drv_info; dev_gt96100_eth_set_nio(d,port_id,nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c3725_gt96100_fe_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct gt_data *d = card->drv_info; dev_gt96100_eth_unset_nio(d,port_id); return(0); } /* Show debug info */ static int dev_c3725_gt96100_show_info(vm_instance_t *vm,struct cisco_card *card) { struct gt_data *d = card->drv_info; dev_gt96100_show_info(d); return(0); } /* ====================================================================== */ /* NM-1FE-TX driver */ struct cisco_card_driver dev_c3725_nm_1fe_tx_driver = { "NM-1FE-TX", 1, 0, dev_c3725_nm_1fe_tx_init, dev_c3725_nm_eth_shutdown, NULL, dev_c3725_nm_eth_set_nio, dev_c3725_nm_eth_unset_nio, NULL, }; /* NM-16ESW driver */ struct cisco_card_driver dev_c3725_nm_16esw_driver = { "NM-16ESW", 1, 0, dev_c3725_nm_16esw_init, dev_c3725_nm_16esw_shutdown, NULL, dev_c3725_nm_16esw_set_nio, dev_c3725_nm_16esw_unset_nio, dev_c3725_nm_16esw_show_info, }; /* GT96100 FastEthernet integrated ports */ struct cisco_card_driver dev_c3725_gt96100_fe_driver = { "GT96100-FE", 1, 2, dev_c3725_gt96100_fe_init, dev_c3725_gt96100_fe_shutdown, dev_c3725_mb_get_sub_info, dev_c3725_gt96100_fe_set_nio, dev_c3725_gt96100_fe_unset_nio, dev_c3725_gt96100_show_info, }; dynamips-0.2.14/common/dev_c3725_iofpga.c000066400000000000000000000306771241034141600200150ustar00rootroot00000000000000/* * Cisco 3725 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * This is very similar to c2691. */ #include #include #include #include #include #include #include #include #include "ptask.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_vtty.h" #include "nmc93cX6.h" #include "dev_c3725.h" /* Debugging flags */ #define DEBUG_UNKNOWN 1 #define DEBUG_ACCESS 0 #define DEBUG_NET_IRQ 0 /* Definitions for Mainboard EEPROM */ #define EEPROM_MB_DOUT 3 #define EEPROM_MB_DIN 2 #define EEPROM_MB_CLK 1 #define EEPROM_MB_CS 0 /* Definitions for Network Modules EEPROM */ #define EEPROM_NM_DOUT 7 #define EEPROM_NM_DIN 6 #define EEPROM_NM_CLK 2 #define EEPROM_NM_CS 4 /* Network IRQ distribution */ struct net_irq_distrib { u_int reg; u_int offset; }; static struct net_irq_distrib net_irq_dist[C3725_MAX_NM_BAYS] = { { 0, 0 }, /* Slot 0: reg 0x26, 0x000000XX */ { 1, 0 }, /* Slot 1: reg 0x28, 0x0000000X */ { 1, 4 }, /* Slot 2: reg 0x28, 0x000000X0 */ }; /* IO FPGA structure */ struct c3725_iofpga_data { vm_obj_t vm_obj; struct vdevice dev; c3725_t *router; /* Network IRQ status */ m_uint16_t net_irq_status[2]; /* Interrupt mask */ m_uint16_t intr_mask; /* WIC select */ u_int wic_select; u_int wic_cmd_pos; u_int wic_cmd_valid; m_uint16_t wic_cmd[2]; }; /* Mainboard EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_mb_def = { EEPROM_MB_CLK, EEPROM_MB_CS, EEPROM_MB_DIN, EEPROM_MB_DOUT, }; /* Mainboard EEPROM */ static const struct nmc93cX6_group eeprom_mb_group = { EEPROM_TYPE_NMC93C46, 1, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "Mainboard EEPROM", { &eeprom_mb_def }, }; /* NM EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_nm_def = { EEPROM_NM_CLK, EEPROM_NM_CS, EEPROM_NM_DIN, EEPROM_NM_DOUT, }; /* NM EEPROM */ static const struct nmc93cX6_group eeprom_nm_group = { EEPROM_TYPE_NMC93C46, 1, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "NM EEPROM", { &eeprom_nm_def }, }; /* Update network interrupt status */ static inline void dev_c3725_iofpga_net_update_irq(struct c3725_iofpga_data *d) { if ((d->net_irq_status[0] != 0xFFFF) || (d->net_irq_status[1] != 0xFFFF)) { vm_set_irq(d->router->vm,C3725_NETIO_IRQ); } else { vm_clear_irq(d->router->vm,C3725_NETIO_IRQ); } } /* Trigger a Network IRQ for the specified slot/port */ void dev_c3725_iofpga_net_set_irq(struct c3725_iofpga_data *d, u_int slot,u_int port) { struct net_irq_distrib *irq_dist; #if DEBUG_NET_IRQ vm_log(d->router->vm,"IO_FPGA","setting NetIRQ for slot %u port %u\n", slot,port); #endif irq_dist = &net_irq_dist[slot]; d->net_irq_status[irq_dist->reg] &= ~(1 << (irq_dist->offset + port)); dev_c3725_iofpga_net_update_irq(d); } /* Clear a Network IRQ for the specified slot/port */ void dev_c3725_iofpga_net_clear_irq(struct c3725_iofpga_data *d, u_int slot,u_int port) { struct net_irq_distrib *irq_dist; #if DEBUG_NET_IRQ vm_log(d->router->vm,"IO_FPGA","clearing NetIRQ for slot %u port %u\n", slot,port); #endif irq_dist = &net_irq_dist[slot]; d->net_irq_status[irq_dist->reg] |= (1 << (irq_dist->offset + port)); dev_c3725_iofpga_net_update_irq(d); } /* Read a WIC EEPROM */ static m_uint16_t dev_c3725_read_wic_eeprom(struct c3725_iofpga_data *d) { struct cisco_eeprom *eeprom; u_int wic_port; u_int eeprom_offset; m_uint8_t val[2]; switch(d->wic_select) { case 0x1700: wic_port = 0x10; break; case 0x1D00: wic_port = 0x20; break; case 0x3500: wic_port = 0x30; break; default: wic_port = 0; } /* No WIC in slot or no EEPROM: fake an empty EEPROM */ if (!wic_port || !(eeprom = vm_slot_get_eeprom(d->router->vm,0,wic_port))) return(0xFFFF); /* EEPROM offset is in the lowest 6 bits */ eeprom_offset = d->wic_cmd[0] & 0x3F; cisco_eeprom_get_byte(eeprom,eeprom_offset,&val[0]); cisco_eeprom_get_byte(eeprom,eeprom_offset+1,&val[1]); return(((m_uint16_t)val[0] << 8) | val[1]); } /* * dev_c3725_iofpga_access() */ static void * dev_c3725_iofpga_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct c3725_iofpga_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0x0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA","reading reg 0x%x at pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA", "writing reg 0x%x at pc=0x%llx, data=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),*data,op_size); } #endif switch(offset) { /* * Platform type ? * 0x04 and 0x05 seem to work. */ case 0x36: if (op_type == MTS_READ) *data = 0x04 << 5; break; /* Mainboard EEPROM */ case 0x0e: if (op_type == MTS_WRITE) nmc93cX6_write(&d->router->mb_eeprom_group,(u_int)(*data)); else *data = nmc93cX6_read(&d->router->mb_eeprom_group); break; case 0x12: /* * Bit 0: 1=No WIC in slot 0. * Bit 1: 1=No WIC in slot 1. * Bit 2: 1=No WIC in slot 2. */ if (op_type == MTS_READ) { *data = 0xFFFF; /* check WIC 0 */ if (vm_slot_check_eeprom(d->router->vm,0,0x10)) *data &= ~0x01; /* check WIC 1 */ if (vm_slot_check_eeprom(d->router->vm,0,0x20)) *data &= ~0x02; /* check WIC 2 */ if (vm_slot_check_eeprom(d->router->vm,0,0x30)) *data &= ~0x04; } else { d->wic_select = *data; } break; case 0x14: if (op_type == MTS_READ) *data = 0xFFFF; break; case 0x18: if (op_type == MTS_READ) *data = 0xFFFF; break; /* wic/vwic related */ case 0x40: if (op_type == MTS_READ) *data = 0x0004; break; /* WIC related: 16-bit data */ case 0x42: if (op_type == MTS_READ) { if (d->wic_cmd_valid) { *data = dev_c3725_read_wic_eeprom(d); d->wic_cmd_valid = FALSE; } else { *data = 0xFFFF; } } else { /* * Store the EEPROM command (in 2 words). * * For a read, we have: * Word 0: 0x180 (nmc93c46 READ) + offset (6-bits). * Word 1: 0 (no data). */ d->wic_cmd[d->wic_cmd_pos++] = *data; if (d->wic_cmd_pos == 2) { d->wic_cmd_pos = 0; d->wic_cmd_valid = TRUE; } } break; /* NM Slot 1 EEPROM */ case 0x44: if (op_type == MTS_WRITE) nmc93cX6_write(&d->router->nm_eeprom_group[0],(u_int)(*data)); else *data = nmc93cX6_read(&d->router->nm_eeprom_group[0]); break; /* NM Slot 2 EEPROM */ case 0x46: if (op_type == MTS_WRITE) nmc93cX6_write(&d->router->nm_eeprom_group[1],(u_int)(*data)); else *data = nmc93cX6_read(&d->router->nm_eeprom_group[1]); break; /* AIM EEPROM #0 */ case 0x48: if (op_type == MTS_READ) *data = 0xFFFF; break; /* AIM EEPROM #1 */ case 0x4a: if (op_type == MTS_READ) *data = 0xFFFF; break; /* * NM Presence. * * Bit 7: 0=NM present in slot 1. * Bit 11: 0=NM present in slot 2. * Other bits unknown. */ case 0x20: if (op_type == MTS_READ) { *data = 0xFFFF; if (vm_slot_get_card_ptr(d->router->vm,1)) *data &= ~0x0008; if (vm_slot_get_card_ptr(d->router->vm,2)) *data &= ~0x0800; } break; /* ??? */ case 0x24: break; /* Intr Mask (sh platform) */ case 0x30: if (op_type == MTS_READ) *data = d->intr_mask; else d->intr_mask = *data; break; /* * Network interrupt status. * * Bit 0: 0 = GT96100 Ethernet ports. * Other bits unknown. */ case 0x26: if (op_type == MTS_READ) *data = d->net_irq_status[0]; break; /* * Network interrupt status. * * Bit 0: 0 = NM in Slot 1. * Bit 8: 0 = NM in Slot 2. * Other bits unknown. */ case 0x28: if (op_type == MTS_READ) *data = d->net_irq_status[1]; break; case 0x2c: if (op_type == MTS_READ) *data = 0xFFFF; break; /* OIR interrupt but not supported (IRQ 6) */ case 0x2e: if (op_type == MTS_READ) *data = 0xFFFF; break; /* * Environmental monitor, determined with "sh env all". * * Bit 0: 1 = Fan Error * Bit 1: 1 = Fan Error * Bit 2: 1 = Over-temperature * Bit 3: ??? * Bit 4: 0 = RPS present. * Bit 5: 0 = Input Voltage status failure. * Bit 6: 1 = Thermal status failure. * Bit 7: 1 = DC Output Voltage status failure. */ case 0x3a: if (op_type == MTS_READ) *data = 0x0020; break; /* * Bit 0: Slot0 Compact Flash presence. * Bit 1: System Compact Flash presence. */ case 0x3c: if (op_type == MTS_READ) { *data = 0xFFFF; /* System Flash ? */ if (cpu->vm->pcmcia_disk_size[0]) *data &= ~0x02; /* Slot0 Flash ? */ if (cpu->vm->pcmcia_disk_size[1]) *data &= ~0x01; } break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA", "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA", "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* Initialize EEPROM groups */ void c3725_init_eeprom_groups(c3725_t *router) { /* Initialize Mainboard EEPROM */ router->mb_eeprom_group = eeprom_mb_group; router->mb_eeprom_group.eeprom[0] = &router->mb_eeprom; router->mb_eeprom.data = NULL; router->mb_eeprom.len = 0; /* EEPROM for NM slot 1 */ router->nm_eeprom_group[0] = eeprom_nm_group; router->nm_eeprom_group[0].eeprom[0] = NULL; /* EEPROM for NM slot 2 */ router->nm_eeprom_group[1] = eeprom_nm_group; router->nm_eeprom_group[1].eeprom[0] = NULL; } /* Shutdown the IO FPGA device */ static void dev_c3725_iofpga_shutdown(vm_instance_t *vm,struct c3725_iofpga_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* * dev_c3725_iofpga_init() */ int dev_c3725_iofpga_init(c3725_t *router,m_uint64_t paddr,m_uint32_t len) { vm_instance_t *vm = router->vm; struct c3725_iofpga_data *d; /* Allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"IO_FPGA: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); d->router = router; d->net_irq_status[0] = 0xFFFF; d->net_irq_status[1] = 0xFFFF; vm_object_init(&d->vm_obj); d->vm_obj.name = "io_fpga"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_c3725_iofpga_shutdown; /* Set device properties */ dev_init(&d->dev); d->dev.name = "io_fpga"; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.priv_data = d; d->dev.handler = dev_c3725_iofpga_access; /* Map this device to the VM */ vm_bind_device(router->vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_c3725_iofpga.h000066400000000000000000000013501241034141600200040ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005-2007 Christophe Fillot (cf@utc.fr) * * Cisco c3725 I/O FPGA. */ #ifndef __DEV_C3725_IOFPGA_H__ #define __DEV_C3725_IOFPGA_H__ /* Forward declaration for IO_FPGA private data */ struct c3725_iofpga_data; /* Trigger a Network IRQ for the specified slot/port */ void dev_c3725_iofpga_net_set_irq(struct c3725_iofpga_data *d, u_int slot,u_int port); /* Clear a Network IRQ for the specified slot/port */ void dev_c3725_iofpga_net_clear_irq(struct c3725_iofpga_data *d, u_int slot,u_int port); /* Create the c3725 I/O FPGA */ int dev_c3725_iofpga_init(c3725_t *router,m_uint64_t paddr,m_uint32_t len); #endif dynamips-0.2.14/common/dev_c3725_pcmod.c000066400000000000000000000052231241034141600176370ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * PC Modules NM (NM-NAM, NM-CIDS, ...) for c3725 platforms. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "vm.h" #include "dev_i8255x.h" #include "dev_c3725.h" /* Initialize a NM PC module in the specified slot */ static int dev_c3725_pcmod_init(vm_instance_t *vm,struct cisco_card *card) { struct i8255x_data *data; u_int slot = card->slot_id; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_nm(card->driver->dev_type)); c3725_set_slot_eeprom(VM_C3725(vm),slot,&card->eeprom); /* Create the Intel i8255x chip */ data = dev_i8255x_init(vm,card->dev_name,0, card->pci_bus, c3725_nm_get_pci_device(slot), c3725_net_irq_for_slot_port(slot,0)); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a NM PC module from the specified slot */ static int dev_c3725_pcmod_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct i8255x_data *data = card->drv_info; /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c3725_set_slot_eeprom(VM_C3725(vm),card->slot_id,NULL); /* Remove the Intel i2855x chip */ dev_i8255x_remove(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c3725_pcmod_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct i8255x_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); dev_i8255x_set_nio(d,nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c3725_pcmod_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct i8255x_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); dev_i8255x_unset_nio(d); return(0); } /* NM-NAM driver */ struct cisco_card_driver dev_c3725_nm_nam_driver = { "NM-NAM", 0, 0, dev_c3725_pcmod_init, dev_c3725_pcmod_shutdown, NULL, dev_c3725_pcmod_set_nio, dev_c3725_pcmod_unset_nio, NULL, }; /* NM-CIDS driver */ struct cisco_card_driver dev_c3725_nm_cids_driver = { "NM-CIDS", 0, 0, dev_c3725_pcmod_init, dev_c3725_pcmod_shutdown, NULL, dev_c3725_pcmod_set_nio, dev_c3725_pcmod_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c3725_serial.c000066400000000000000000000051521241034141600200150ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Serial Network Modules. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_mueslix.h" #include "dev_c3725.h" /* ====================================================================== */ /* NM-4T */ /* ====================================================================== */ /* * dev_c3725_nm_4t_init() * * Add a NM-4T network module into specified slot. */ int dev_c3725_nm_4t_init(vm_instance_t *vm,struct cisco_card *card) { struct mueslix_data *data; u_int slot = card->slot_id; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_nm("NM-4T")); c3725_set_slot_eeprom(VM_C3725(vm),slot,&card->eeprom); /* Create the Mueslix chip */ data = dev_mueslix_init(vm,card->dev_name,0, card->pci_bus,c3725_nm_get_pci_device(slot), c3725_net_irq_for_slot_port(slot,0)); if (!data) return(-1); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a NM-4T from the specified slot */ int dev_c3725_nm_4t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c3725_set_slot_eeprom(VM_C3725(vm),card->slot_id,NULL); /* Remove the mueslix driver */ dev_mueslix_remove(card->drv_info); return(0); } /* Bind a Network IO descriptor to a specific port */ int dev_c3725_nm_4t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct mueslix_data *d = card->drv_info; if (!d || (port_id >= MUESLIX_NR_CHANNELS)) return(-1); return(dev_mueslix_set_nio(d,port_id,nio)); } /* Unbind a Network IO descriptor to a specific port */ int dev_c3725_nm_4t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct mueslix_data *d = card->drv_info; if (!d || (port_id >= MUESLIX_NR_CHANNELS)) return(-1); return(dev_mueslix_unset_nio(d,port_id)); } /* NM-4T driver */ struct cisco_card_driver dev_c3725_nm_4t_driver = { "NM-4T", 1, 0, dev_c3725_nm_4t_init, dev_c3725_nm_4t_shutdown, NULL, dev_c3725_nm_4t_set_nio, dev_c3725_nm_4t_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c3725_wic.c000066400000000000000000000134761241034141600173300ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * WIC Modules. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "vm.h" #include "dev_gt.h" #include "dev_c3725.h" #include "dev_wic_serial.h" /* Get the MPSC channel associated to a WIC sub-slot */ static int dev_c3725_mb_wic_get_mpsc_chan(struct cisco_card *card, u_int port_id, u_int *mpsc_chan) { u_int cid; cid = card->subslot_id + port_id; switch(cid) { /* WIC 0 port 0 mapped to GT96100 MPSC1 */ case 0x10: *mpsc_chan = 1; break; /* WIC 0 port 1 mapped to GT96100 MPSC0 */ case 0x11: *mpsc_chan = 0; break; /* WIC 1 port 0 mapped to GT96100 MPSC4 */ case 0x20: *mpsc_chan = 4; break; /* WIC 1 port 1 mapped to GT96100 MPSC2 */ case 0x21: *mpsc_chan = 2; break; /* WIC 2 port 0 mapped to GT96100 MPSC5 */ case 0x30: *mpsc_chan = 5; break; /* WIC 2 port 1 mapped to GT96100 MPSC3 */ case 0x31: *mpsc_chan = 3; break; default: return(-1); } return(0); } /* Initialize a WIC-1T in the specified slot */ static int dev_c3725_mb_wic1t_init(vm_instance_t *vm,struct cisco_card *card) { struct wic_serial_data *wic_data; m_uint64_t phys_addr; u_int wic_id; /* Create the WIC device */ wic_id = (card->subslot_id >> 4) - 1; if (c3725_get_onboard_wic_addr(wic_id,&phys_addr) == -1) { vm_error(vm,"WIC","invalid slot %u (subslot_id=%u)\n", wic_id,card->subslot_id); return(-1); } wic_data = dev_wic_serial_init(vm,card->dev_name,WIC_SERIAL_MODEL_1T, phys_addr,C3725_WIC_SIZE); if (!wic_data) return(-1); /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_wic("WIC-1T")); /* Store device info into the router structure */ card->drv_info = wic_data; return(0); } /* Remove a WIC-1T from the specified slot */ static int dev_c3725_mb_wic1t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the WIC device */ dev_wic_serial_remove(card->drv_info); /* Remove the WIC EEPROM */ cisco_card_unset_eeprom(card); return(0); } /* Bind a Network IO descriptor */ static int dev_c3725_mb_wic1t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { u_int mpsc_chan; if ((port_id > 0) || (dev_c3725_mb_wic_get_mpsc_chan(card,port_id,&mpsc_chan) == -1)) return(-1); return(dev_gt96100_mpsc_set_nio(VM_C3725(vm)->gt_data,mpsc_chan,nio)); } /* Unbind a Network IO descriptor */ static int dev_c3725_mb_wic1t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { u_int mpsc_chan; if ((port_id > 0) || (dev_c3725_mb_wic_get_mpsc_chan(card,port_id,&mpsc_chan) == -1)) return(-1); return(dev_gt96100_mpsc_unset_nio(VM_C3725(vm)->gt_data,mpsc_chan)); } /* Initialize a WIC-2T in the specified slot */ static int dev_c3725_mb_wic2t_init(vm_instance_t *vm,struct cisco_card *card) { struct wic_serial_data *wic_data; m_uint64_t phys_addr; u_int wic_id; /* Create the WIC device */ wic_id = (card->subslot_id >> 4) - 1; if (c3725_get_onboard_wic_addr(wic_id,&phys_addr) == -1) { vm_error(vm,"WIC","invalid slot %u (subslot_id=%u)\n", wic_id,card->subslot_id); return(-1); } wic_data = dev_wic_serial_init(vm,card->dev_name,WIC_SERIAL_MODEL_2T, phys_addr,C3725_WIC_SIZE); if (!wic_data) return(-1); /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_wic("WIC-2T")); /* Store device info into the router structure */ card->drv_info = wic_data; return(0); } /* Remove a WIC-2T from the specified slot */ static int dev_c3725_mb_wic2t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the WIC device */ dev_wic_serial_remove(card->drv_info); /* Remove the WIC EEPROM */ cisco_card_unset_eeprom(card); return(0); } /* Bind a Network IO descriptor */ static int dev_c3725_mb_wic2t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { u_int mpsc_chan; if ((port_id > 1) || (dev_c3725_mb_wic_get_mpsc_chan(card,port_id,&mpsc_chan) == -1)) return(-1); return(dev_gt96100_mpsc_set_nio(VM_C3725(vm)->gt_data,mpsc_chan,nio)); } /* Unbind a Network IO descriptor */ static int dev_c3725_mb_wic2t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { u_int mpsc_chan; if ((port_id > 1) || (dev_c3725_mb_wic_get_mpsc_chan(card,port_id,&mpsc_chan) == -1)) return(-1); return(dev_gt96100_mpsc_unset_nio(VM_C3725(vm)->gt_data,mpsc_chan)); } /* Cisco 3725 WIC-1T driver (for mainboard) */ struct cisco_card_driver dev_c3725_mb_wic1t_driver = { "WIC-1T", 1, 0, dev_c3725_mb_wic1t_init, dev_c3725_mb_wic1t_shutdown, NULL, dev_c3725_mb_wic1t_set_nio, dev_c3725_mb_wic1t_unset_nio, NULL, }; /* Cisco 3725 WIC-2T driver (for mainboard) */ struct cisco_card_driver dev_c3725_mb_wic2t_driver = { "WIC-2T", 1, 0, dev_c3725_mb_wic2t_init, dev_c3725_mb_wic2t_shutdown, NULL, dev_c3725_mb_wic2t_set_nio, dev_c3725_mb_wic2t_unset_nio, NULL, }; /* WIC drivers (mainbord slots) */ struct cisco_card_driver *dev_c3725_mb_wic_drivers[] = { &dev_c3725_mb_wic1t_driver, &dev_c3725_mb_wic2t_driver, NULL, }; dynamips-0.2.14/common/dev_c3745.c000066400000000000000000000576131241034141600164710ustar00rootroot00000000000000/* * Cisco 3745 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * Patched by Jeremy Grossmann for the GNS3 project (www.gns3.net) * * Generic Cisco 3745 routines and definitions (EEPROM,...). */ #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "pci_io.h" #include "dev_gt.h" #include "cisco_eeprom.h" #include "dev_rom.h" #include "dev_c3745.h" #include "dev_c3745_iofpga.h" #include "dev_vtty.h" #include "registry.h" #include "fs_nvram.h" /* ======================================================================== */ /* EEPROM definitions */ /* ======================================================================== */ /* Cisco 3745 motherboard EEPROM */ static m_uint16_t eeprom_c3745_motherboard_data[] = { 0x04FF, 0xC18B, 0x5858, 0x5858, 0x5858, 0x5858, 0x5858, 0x5809, 0x6940, 0x02F7, 0xC046, 0x0320, 0x003E, 0x3E03, 0x4241, 0x3085, 0x1C12, 0x4004, 0x80FF, 0xFFFF, 0xFFC4, 0x08FF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFF81, 0x0000, 0x0000, 0x0400, 0x0300, 0xC508, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x4102, 0x0002, 0x04C2, 0x8B58, 0x5858, 0x5858, 0x5858, 0x5858, 0x5858, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; struct cisco_eeprom eeprom_c3745_motherboard = { "C3745 Motherboard", eeprom_c3745_motherboard_data, sizeof(eeprom_c3745_motherboard_data)/2, }; /* Cisco 3745 I/O board EEPROM */ static m_uint16_t eeprom_c3745_ioboard_data[] = { 0x04FF, 0x4002, 0xF841, 0x0200, 0xC046, 0x0320, 0x0038, 0x7E01, 0x4242, 0x3080, 0x0000, 0x0000, 0x0203, 0xC18B, 0x5858, 0x5858, 0x5858, 0x5858, 0x5858, 0x5803, 0x0081, 0x0000, 0x0000, 0x0400, 0xC809, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFC2, 0x8B58, 0x5858, 0x5858, 0x5858, 0x5858, 0x5858, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; struct cisco_eeprom eeprom_c3745_ioboard = { "C3745 I/O board", eeprom_c3745_ioboard_data, sizeof(eeprom_c3745_ioboard_data)/2, }; /* Cisco 3745 midplane EEPROM */ static m_uint16_t eeprom_c3745_midplane_data[] = { 0x04FF, 0x4003, 0x3E41, 0x0200, 0xC046, 0x0320, 0x0030, 0x0101, 0x4241, 0x3080, 0x0000, 0x0000, 0x0205, 0xC18B, 0x5858, 0x5858, 0x5858, 0x5858, 0x5858, 0x5803, 0x0081, 0x0000, 0x0000, 0x0400, 0xC809, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFC3, 0x0600, 0x0DED, 0xCD7D, 0x8043, 0x0050, 0xC28B, 0x4654, 0x5830, 0x3934, 0x3557, 0x304D, 0x59FF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; struct cisco_eeprom eeprom_c3745_midplane = { "C3745 Midplane", eeprom_c3745_midplane_data, sizeof(eeprom_c3745_midplane_data)/2, }; /* ======================================================================== */ /* Network Module Drivers */ /* ======================================================================== */ static struct cisco_card_driver *nm_drivers[] = { &dev_c3745_nm_1fe_tx_driver, &dev_c3745_nm_16esw_driver, &dev_c3745_gt96100_fe_driver, &dev_c3745_nm_4t_driver, &dev_c3745_nm_nam_driver, &dev_c3745_nm_cids_driver, NULL, }; /* ======================================================================== */ /* Cisco 3745 router instances */ /* ======================================================================== */ /* Initialize default parameters for a C3745 */ static void c3745_init_defaults(c3745_t *router); /* Directly extract the configuration from the NVRAM device */ static int c3745_nvram_extract_config(vm_instance_t *vm,u_char **startup_config,size_t *startup_len,u_char **private_config,size_t *private_len) { int ret; ret = generic_nvram_extract_config(vm, "rom", C3745_NVRAM_OFFSET, C3745_NVRAM_SIZE, 0, FS_NVRAM_FORMAT_WITH_BACKUP, startup_config, startup_len, private_config, private_len); return(ret); } /* Directly push the IOS configuration to the NVRAM device */ static int c3745_nvram_push_config(vm_instance_t *vm,u_char *startup_config,size_t startup_len,u_char *private_config,size_t private_len) { int ret; ret = generic_nvram_push_config(vm, "rom", vm->rom_size*1048576, C3745_NVRAM_OFFSET, C3745_NVRAM_SIZE, 0, FS_NVRAM_FORMAT_WITH_BACKUP, startup_config, startup_len, private_config, private_len); return(ret); } /* Check for empty config */ static int c3745_nvram_check_empty_config(vm_instance_t *vm) { struct vdevice *rom_dev; m_uint64_t addr; size_t len; if (!(rom_dev = dev_get_by_name(vm,"rom"))) return(-1); addr = rom_dev->phys_addr + C3745_NVRAM_OFFSET; len = C3745_NVRAM_SIZE; while(len > 0) { if (physmem_copy_u32_from_vm(vm,addr) != 0) return(0); addr += sizeof(m_uint32_t); len -= sizeof(m_uint32_t); } /* Empty NVRAM */ vm->conf_reg |= 0x0040; printf("NVRAM is empty, setting config register to 0x%x\n",vm->conf_reg); return(0); } /* Create a new router instance */ static int c3745_create_instance(vm_instance_t *vm) { c3745_t *router; if (!(router = malloc(sizeof(*router)))) { fprintf(stderr,"C3745 '%s': Unable to create new instance!\n",vm->name); return(-1); } memset(router,0,sizeof(*router)); router->vm = vm; vm->hw_data = router; c3745_init_defaults(router); return(0); } /* Free resources used by a router instance */ static int c3745_delete_instance(vm_instance_t *vm) { c3745_t *router = VM_C3745(vm); int i; /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(FALSE); } } /* Remove NIO bindings */ for(i=0;inr_slots;i++) vm_slot_remove_all_nio_bindings(vm,i); /* Shutdown all Network Modules */ vm_slot_shutdown_all(vm); /* Free mainboard EEPROM */ for(i=0;i<3;i++) cisco_eeprom_free(&router->sys_eeprom[i]); /* Free all resources used by VM */ vm_free(vm); /* Free the router structure */ free(router); return(TRUE); } /* Get WIC device address for the specified onboard port */ int c3745_get_onboard_wic_addr(u_int slot,m_uint64_t *phys_addr) { if (slot >= C3745_MAX_WIC_BAYS) return(-1); *phys_addr = C3745_WIC_ADDR + (slot * C3745_WIC_SIZE); return(0); } /* Set EEPROM for the specified slot */ int c3745_set_slot_eeprom(c3745_t *router,u_int slot, struct cisco_eeprom *eeprom) { if ((slot < 1) || (slot >= C3745_MAX_NM_BAYS)) return(-1); router->nm_eeprom_group[slot-1].eeprom[0] = eeprom; return(0); } /* Get slot/port corresponding to specified network IRQ */ static inline void c3745_net_irq_get_slot_port(u_int irq,u_int *slot,u_int *port) { irq -= C3745_NETIO_IRQ_BASE; *port = irq & C3745_NETIO_IRQ_PORT_MASK; *slot = irq >> C3745_NETIO_IRQ_PORT_BITS; } /* Get network IRQ for specified slot/port */ u_int c3745_net_irq_for_slot_port(u_int slot,u_int port) { u_int irq; irq = (slot << C3745_NETIO_IRQ_PORT_BITS) + port; irq += C3745_NETIO_IRQ_BASE; return(irq); } /* Set the system id or processor board id in the eeprom */ int c3745_set_system_id(c3745_t *router,char *id) { /* 11 characters is enough. Array is 20 long */ strncpy(router->board_id,id,13); /* Make sure it is null terminated */ router->board_id[13] = 0x00; c3745_refresh_systemid(router); return 0; } int c3745_refresh_systemid(c3745_t *router) { if (router->board_id[0] == 0x00) return(0); m_uint8_t buf[11]; parse_board_id(buf,router->board_id,11); cisco_eeprom_set_region(&router->sys_eeprom[2] ,72,buf,11); return (0); } /* Set the base MAC address of the chassis */ static int c3745_burn_mac_addr(c3745_t *router,n_eth_addr_t *addr) { m_uint8_t eeprom_ver; size_t offset; /* Read EEPROM format version */ cisco_eeprom_get_byte(&router->sys_eeprom[2],0,&eeprom_ver); switch(eeprom_ver) { case 0: cisco_eeprom_set_region(&router->sys_eeprom[2],2, addr->eth_addr_byte,6); break; case 4: if (!cisco_eeprom_v4_find_field(&router->sys_eeprom[2], 0xC3,&offset)) { cisco_eeprom_set_region(&router->sys_eeprom[2],offset, addr->eth_addr_byte,6); } break; default: vm_error(router->vm,"c3745_burn_mac_addr: unable to handle " "EEPROM version %u\n",eeprom_ver); return(-1); } return(0); } /* Set chassis MAC address */ int c3745_chassis_set_mac_addr(c3745_t *router,char *mac_addr) { if (parse_mac_addr(&router->mac_addr,mac_addr) == -1) { vm_error(router->vm,"unable to parse MAC address '%s'.\n",mac_addr); return(-1); } /* Set the chassis base MAC address */ c3745_burn_mac_addr(router,&router->mac_addr); return(0); } /* Create the two main PCI busses for a GT64120 based system */ static int c3745_init_gt96100(c3745_t *router) { vm_instance_t *vm = router->vm; vm_obj_t *obj; vm->pci_bus[0] = pci_bus_create("PCI bus #0",0); vm->pci_bus[1] = pci_bus_create("PCI bus #1",0); if (!vm->pci_bus[0] || !vm->pci_bus[1]) { vm_error(router->vm,"unable to create PCI data.\n"); return(-1); } if (dev_gt96100_init(vm,"gt96100",C3745_GT96K_ADDR,0x200000, C3745_GT96K_IRQ, C3745_EXT_IRQ, c3745_net_irq_for_slot_port(0,0), 255) == -1) return(-1); if (!(obj = vm_object_find(router->vm,"gt96100"))) return(-1); router->gt_data = obj->data; return(0); } /* Initialize a Cisco 3745 */ static int c3745_init(c3745_t *router) { vm_instance_t *vm = router->vm; char bus_name[128]; int i; /* Set the processor type: R7000 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R7000); /* Initialize the Galileo GT-96100 PCI controller */ if (c3745_init_gt96100(router) == -1) return(-1); /* Create the NM PCI busses for slots 1-4 */ for(i=1;i<=4;i++) { snprintf(bus_name,sizeof(bus_name),"NM Slot %d",i); vm->pci_bus_pool[i] = pci_bus_create(bus_name,-1); /* Map the NM PCI bus */ vm->slots_pci_bus[i] = vm->pci_bus_pool[i]; /* Create the PCI bridge */ dev_ti2050b_init(vm->pci_bus[1],i,vm->slots_pci_bus[i]); } vm->elf_machine_id = C3745_ELF_MACHINE_ID; return(0); } /* Show C3745 hardware info */ void c3745_show_hardware(c3745_t *router) { vm_instance_t *vm = router->vm; printf("C3745 instance '%s' (id %d):\n",vm->name,vm->instance_id); printf(" VM Status : %d\n",vm->status); printf(" RAM size : %u Mb\n",vm->ram_size); printf(" NVRAM size : %u Kb\n",vm->nvram_size); printf(" IOS image : %s\n\n",vm->ios_image); if (vm->debug_level > 0) { dev_show_list(vm); pci_dev_show_list(vm->pci_bus[0]); pci_dev_show_list(vm->pci_bus[1]); printf("\n"); } } /* Initialize default parameters for a C3745 */ static void c3745_init_defaults(c3745_t *router) { vm_instance_t *vm = router->vm; n_eth_addr_t *m; m_uint16_t pid; /* Set platform slots characteristics */ vm->nr_slots = C3745_MAX_NM_BAYS; vm->slots_type = CISCO_CARD_TYPE_NM; vm->slots_drivers = nm_drivers; pid = (m_uint16_t)getpid(); /* Generate a chassis MAC address based on the instance ID */ m = &router->mac_addr; m->eth_addr_byte[0] = vm_get_mac_addr_msb(vm); m->eth_addr_byte[1] = vm->instance_id & 0xFF; m->eth_addr_byte[2] = pid >> 8; m->eth_addr_byte[3] = pid & 0xFF; m->eth_addr_byte[4] = 0x00; m->eth_addr_byte[5] = 0x00; router->board_id[0] = 0x00; c3745_init_eeprom_groups(router); cisco_eeprom_copy(&router->sys_eeprom[0],&eeprom_c3745_motherboard); cisco_eeprom_copy(&router->sys_eeprom[1],&eeprom_c3745_ioboard); cisco_eeprom_copy(&router->sys_eeprom[2],&eeprom_c3745_midplane); c3745_burn_mac_addr(router,&router->mac_addr); /* The GT96100 system controller has 2 integrated FastEthernet ports */ vm_slot_add_binding(vm,"GT96100-FE",0,0); vm->ram_mmap = C3745_DEFAULT_RAM_MMAP; vm->ram_size = C3745_DEFAULT_RAM_SIZE; vm->rom_size = C3745_DEFAULT_ROM_SIZE; vm->nvram_size = C3745_DEFAULT_NVRAM_SIZE; vm->conf_reg_setup = C3745_DEFAULT_CONF_REG; vm->clock_divisor = C3745_DEFAULT_CLOCK_DIV; vm->nvram_rom_space = C3745_NVRAM_ROM_RES_SIZE; vm->nm_iomem_size = C3745_DEFAULT_IOMEM_SIZE; vm->pcmcia_disk_size[0] = C3745_DEFAULT_DISK0_SIZE; vm->pcmcia_disk_size[1] = C3745_DEFAULT_DISK1_SIZE; } /* Initialize the C3745 Platform */ static int c3745_init_platform(c3745_t *router) { vm_instance_t *vm = router->vm; cpu_mips_t *cpu; cpu_gen_t *gen; vm_obj_t *obj; /* Copy config register setup into "active" config register */ vm->conf_reg = vm->conf_reg_setup; /* Create Console and AUX ports */ vm_init_vtty(vm); /* Create a CPU group */ vm->cpu_group = cpu_group_create("System CPU"); /* Initialize the virtual MIPS processor */ if (!(gen = cpu_create(vm,CPU_TYPE_MIPS64,0))) { vm_error(vm,"unable to create CPU!\n"); return(-1); } cpu = CPU_MIPS64(gen); /* Add this CPU to the system CPU group */ cpu_group_add(vm->cpu_group,gen); vm->boot_cpu = gen; /* Initialize the IRQ routing vectors */ vm->set_irq = mips64_vm_set_irq; vm->clear_irq = mips64_vm_clear_irq; /* Mark the Network IO interrupt as high priority */ cpu->irq_idle_preempt[C3745_NETIO_IRQ] = TRUE; cpu->irq_idle_preempt[C3745_GT96K_IRQ] = TRUE; cpu->irq_idle_preempt[C3745_DUART_IRQ] = TRUE; /* Copy some parameters from VM to CPU (idle PC, ...) */ cpu->idle_pc = vm->idle_pc; if (vm->timer_irq_check_itv) cpu->timer_irq_check_itv = vm->timer_irq_check_itv; /* Remote emulator control */ dev_remote_control_init(vm,0x16000000,0x1000); /* Specific Storage Area (SSA) */ dev_ram_init(vm,"ssa",TRUE,FALSE,NULL,FALSE,0x16001000ULL,0x7000); /* IO FPGA */ if (dev_c3745_iofpga_init(router,C3745_IOFPGA_ADDR,0x200000) == -1) return(-1); if (!(obj = vm_object_find(router->vm,"io_fpga"))) return(-1); router->iofpga_data = obj->data; #if 0 /* PCI IO space */ if (!(vm->pci_io_space = pci_io_data_init(vm,C3745_PCI_IO_ADDR))) return(-1); #endif /* Initialize the chassis */ if (c3745_init(router) == -1) return(-1); /* Initialize RAM */ vm_ram_init(vm,0x00000000ULL); /* Initialize ROM (as a Flash) */ if (!(obj = dev_flash_init(vm,"rom",C3745_ROM_ADDR,vm->rom_size*1048576))) return(-1); dev_flash_copy_data(obj,0,mips64_microcode,mips64_microcode_len); c3745_nvram_check_empty_config(vm); /* Byte swapping */ dev_bswap_init(vm,"mem_bswap",C3745_BSWAP_ADDR,1024*1048576,0x00000000ULL); /* Initialize the NS16552 DUART */ dev_ns16552_init(vm,C3745_DUART_ADDR,0x1000,3,C3745_DUART_IRQ, vm->vtty_con,vm->vtty_aux); /* PCMCIA Slot 0 */ dev_pcmcia_disk_init(vm,"slot0",C3745_SLOT0_ADDR,0x200000, vm->pcmcia_disk_size[0],1); /* PCMCIA Slot 1 */ dev_pcmcia_disk_init(vm,"slot1",C3745_SLOT1_ADDR,0x200000, vm->pcmcia_disk_size[1],1); /* Initialize Network Modules */ if (vm_slot_init_all(vm) == -1) return(-1); /* Show device list */ c3745_show_hardware(router); return(0); } /* Boot the IOS image */ static int c3745_boot_ios(c3745_t *router) { vm_instance_t *vm = router->vm; cpu_mips_t *cpu; if (!vm->boot_cpu) return(-1); /* Suspend CPU activity since we will restart directly from ROM */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Reset the boot CPU */ cpu = CPU_MIPS64(vm->boot_cpu); mips64_reset(cpu); /* Load IOS image */ if (mips64_load_elf_image(cpu,vm->ios_image, (vm->ghost_status == VM_GHOST_RAM_USE), &vm->ios_entry_point) < 0) { vm_error(vm,"failed to load Cisco IOS image '%s'.\n",vm->ios_image); return(-1); } /* Launch the simulation */ printf("\nC3745 '%s': starting simulation (CPU0 PC=0x%llx), " "JIT %sabled.\n", vm->name,cpu->pc,vm->jit_use ? "en":"dis"); vm_log(vm,"C3745_BOOT", "starting instance (CPU0 PC=0x%llx,idle_pc=0x%llx,JIT %s)\n", cpu->pc,cpu->idle_pc,vm->jit_use ? "on":"off"); /* Start main CPU */ if (vm->ghost_status != VM_GHOST_RAM_GENERATE) { vm->status = VM_STATUS_RUNNING; cpu_start(vm->boot_cpu); } else { vm->status = VM_STATUS_SHUTDOWN; } return(0); } /* Set an IRQ */ static void c3745_set_irq(vm_instance_t *vm,u_int irq) { c3745_t *router = VM_C3745(vm); cpu_mips_t *cpu0 = CPU_MIPS64(vm->boot_cpu); u_int slot,port; switch(irq) { case 0 ... 7: mips64_set_irq(cpu0,irq); if (cpu0->irq_idle_preempt[irq]) cpu_idle_break_wait(cpu0->gen); break; case C3745_NETIO_IRQ_BASE ... C3745_NETIO_IRQ_END: c3745_net_irq_get_slot_port(irq,&slot,&port); dev_c3745_iofpga_net_set_irq(router->iofpga_data,slot,port); break; } } /* Clear an IRQ */ static void c3745_clear_irq(vm_instance_t *vm,u_int irq) { c3745_t *router = VM_C3745(vm); cpu_mips_t *cpu0 = CPU_MIPS64(vm->boot_cpu); u_int slot,port; switch(irq) { case 0 ... 7: mips64_clear_irq(cpu0,irq); break; case C3745_NETIO_IRQ_BASE ... C3745_NETIO_IRQ_END: c3745_net_irq_get_slot_port(irq,&slot,&port); dev_c3745_iofpga_net_clear_irq(router->iofpga_data,slot,port); break; } } /* Initialize a Cisco 3745 instance */ static int c3745_init_instance(vm_instance_t *vm) { c3745_t *router = VM_C3745(vm); m_uint32_t rom_entry_point; cpu_mips_t *cpu0; if (!vm->ios_image) { vm_error(vm,"no Cisco IOS image defined."); return(-1); } /* Initialize the C3745 platform */ if (c3745_init_platform(router) == -1) { vm_error(vm,"unable to initialize the platform hardware.\n"); return(-1); } /* IRQ routing */ vm->set_irq = c3745_set_irq; vm->clear_irq = c3745_clear_irq; /* Load IOS configuration files */ if (vm->ios_startup_config != NULL || vm->ios_private_config != NULL) { vm_nvram_push_config(vm,vm->ios_startup_config,vm->ios_private_config); vm->conf_reg &= ~0x40; } /* Load ROM (ELF image or embedded) */ cpu0 = CPU_MIPS64(vm->boot_cpu); rom_entry_point = (m_uint32_t)MIPS_ROM_PC; if ((vm->rom_filename != NULL) && (mips64_load_elf_image(cpu0,vm->rom_filename,0,&rom_entry_point) < 0)) { vm_error(vm,"unable to load alternate ROM '%s', " "fallback to embedded ROM.\n\n",vm->rom_filename); vm->rom_filename = NULL; } /* Load symbol file */ if (vm->sym_filename) { mips64_sym_load_file(cpu0,vm->sym_filename); cpu0->sym_trace = 1; } return(c3745_boot_ios(router)); } /* Stop a Cisco 3745 instance */ static int c3745_stop_instance(vm_instance_t *vm) { printf("\nC3745 '%s': stopping simulation.\n",vm->name); vm_log(vm,"C3745_STOP","stopping simulation.\n"); /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } } /* Free resources that were used during execution to emulate hardware */ vm_slot_shutdown_all(vm); vm_hardware_shutdown(vm); /* Cleanup */ VM_C3745(vm)->iofpga_data = NULL; VM_C3745(vm)->gt_data = NULL; return(0); } /* Trigger an OIR event */ static int c3745_trigger_oir_event(c3745_t *router,u_int slot_mask) { router->oir_status = slot_mask; vm_set_irq(router->vm,C3745_EXT_IRQ); return(0); } /* Initialize a new NM while the virtual router is online (OIR) */ static int c3745_nm_init_online(vm_instance_t *vm,u_int slot,u_int subslot) { if (!slot) { vm_error(vm,"OIR not supported on slot 0.\n"); return(-1); } /* * Suspend CPU activity while adding new hardware (since we change the * memory maps). */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Add the new hardware elements */ if (vm_slot_init(vm,slot) == -1) return(-1); /* Resume normal operations */ vm_resume(vm); /* Now, we can safely trigger the OIR event */ c3745_trigger_oir_event(VM_C3745(vm),1 << (slot - 1)); return(0); } /* Stop a NM while the virtual router is online (OIR) */ static int c3745_nm_stop_online(vm_instance_t *vm,u_int slot,u_int subslot) { if (!slot) { vm_error(vm,"OIR not supported on slot 0.\n"); return(-1); } /* The NM driver must be initialized */ if (!vm_slot_get_card_ptr(vm,slot)) { vm_error(vm,"trying to shut down empty slot %u.\n",slot); return(-1); } /* Disable all NIOs to stop traffic forwarding */ vm_slot_disable_all_nio(vm,slot); /* We can safely trigger the OIR event */ c3745_trigger_oir_event(VM_C3745(vm),1 << (slot - 1)); /* * Suspend CPU activity while removing the hardware (since we change the * memory maps). */ vm_suspend(vm); /* Device removal */ if (vm_slot_shutdown(vm,slot) != 0) vm_error(vm,"unable to shutdown slot %u.\n",slot); /* Resume normal operations */ vm_resume(vm); return(0); } /* Get MAC address MSB */ static u_int c3745_get_mac_addr_msb(void) { return(0xC4); } /* Parse specific options for the Cisco 1700 platform */ static int c3745_cli_parse_options(vm_instance_t *vm,int option) { c3745_t *router = VM_C3745(vm); switch(option) { /* IO memory reserved for NMs (in percents!) */ case OPT_IOMEM_SIZE: vm->nm_iomem_size = 0x8000 | atoi(optarg); break; /* Set the base MAC address */ case 'm': if (!c3745_chassis_set_mac_addr(router,optarg)) printf("MAC address set to '%s'.\n",optarg); break; /* Set the System ID */ case 'I': if (!c3745_set_system_id(router,optarg)) printf("System ID set to '%s'.\n",optarg); break; /* Unknown option */ default: return(-1); } return(0); } /* Show specific CLI options */ static void c3745_cli_show_options(vm_instance_t *vm) { printf(" --iomem-size : IO memory (in percents, default: %u)\n" " -p : Define a Network Module\n" " -I : Set Processor Board Serial Number\n" " -s : Bind a Network IO interface to a " "Network Module\n", vm->nm_iomem_size); } /* Platform definition */ static vm_platform_t c3745_platform = { "c3745", "C3745", "3745", c3745_create_instance, c3745_delete_instance, c3745_init_instance, c3745_stop_instance, c3745_nm_init_online, c3745_nm_stop_online, c3745_nvram_extract_config, c3745_nvram_push_config, c3745_get_mac_addr_msb, NULL, c3745_cli_parse_options, c3745_cli_show_options, NULL, }; /* Register the c3745 platform */ int c3745_platform_register(void) { if (vm_platform_register(&c3745_platform) == -1) return(-1); return(hypervisor_c3745_init(&c3745_platform)); } dynamips-0.2.14/common/dev_c3745.h000066400000000000000000000110211241034141600164550ustar00rootroot00000000000000/* * Cisco 3745 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Generic Cisco 3745 routines and definitions (EEPROM,...). */ #ifndef __DEV_C3745_H__ #define __DEV_C3745_H__ #include #include "utils.h" #include "net.h" #include "device.h" #include "pci_dev.h" #include "nmc93cX6.h" #include "dev_gt.h" #include "net_io.h" #include "vm.h" /* Default C3745 parameters */ #define C3745_DEFAULT_RAM_SIZE 128 #define C3745_DEFAULT_ROM_SIZE 2 #define C3745_DEFAULT_NVRAM_SIZE 304 #define C3745_DEFAULT_CONF_REG 0x2102 #define C3745_DEFAULT_CLOCK_DIV 8 #define C3745_DEFAULT_RAM_MMAP 1 #define C3745_DEFAULT_DISK0_SIZE 16 #define C3745_DEFAULT_DISK1_SIZE 0 #define C3745_DEFAULT_IOMEM_SIZE 5 /* Percents! */ /* 3745 characteritics: 4 NM (+ motherboard), 3 WIC, 2 AIM */ #define C3745_MAX_NM_BAYS 5 #define C3745_MAX_WIC_BAYS 3 /* C3745 DUART Interrupt */ #define C3745_DUART_IRQ 5 /* C3745 Network I/O Interrupt */ #define C3745_NETIO_IRQ 2 /* C3745 GT64k DMA/Timer Interrupt */ #define C3745_GT96K_IRQ 3 /* C3745 External Interrupt */ #define C3745_EXT_IRQ 6 /* Network IRQ */ #define C3745_NETIO_IRQ_BASE 32 #define C3745_NETIO_IRQ_PORT_BITS 2 #define C3745_NETIO_IRQ_PORT_MASK ((1 << C3745_NETIO_IRQ_PORT_BITS) - 1) #define C3745_NETIO_IRQ_PER_SLOT (1 << C3745_NETIO_IRQ_PORT_BITS) #define C3745_NETIO_IRQ_END \ (C3745_NETIO_IRQ_BASE + (C3745_MAX_NM_BAYS * C3745_NETIO_IRQ_PER_SLOT) - 1) /* C3745 common device addresses */ #define C3745_BITBUCKET_ADDR 0x1ec00000ULL #define C3745_IOFPGA_ADDR 0x1fa00000ULL #define C3745_ROM_ADDR 0x1fc00000ULL #define C3745_GT96K_ADDR 0x24000000ULL #define C3745_SLOT0_ADDR 0x30000000ULL #define C3745_SLOT1_ADDR 0x32000000ULL #define C3745_DUART_ADDR 0x3c100000ULL #define C3745_WIC_ADDR 0x3c200000ULL #define C3745_BSWAP_ADDR 0xc0000000ULL #define C3745_PCI_IO_ADDR 0x100000000ULL /* WIC interval in address space */ #define C3745_WIC_SIZE 0x2000 /* Offset of simulated NVRAM in ROM flash */ #define C3745_NVRAM_OFFSET 0xB0000 #define C3745_NVRAM_SIZE 0x4C000 // with backup /* Reserved space for ROM in NVRAM */ #define C3745_NVRAM_ROM_RES_SIZE 0 /* C3745 ELF Platform ID */ #define C3745_ELF_MACHINE_ID 0x69 #define VM_C3745(vm) ((c3745_t *)vm->hw_data) /* C3745 router */ typedef struct c3745_router c3745_t; /* C3745 router */ struct c3745_router { /* Chassis MAC address */ n_eth_addr_t mac_addr; char board_id[20]; /* Associated VM instance */ vm_instance_t *vm; /* GT96100 data */ struct gt_data *gt_data; /* I/O FPGA */ struct c3745_iofpga_data *iofpga_data; /* OIR status */ m_uint8_t oir_status; /* * System EEPROMs. * It can be modified to change the chassis MAC address. */ struct cisco_eeprom sys_eeprom[3]; struct nmc93cX6_group sys_eeprom_group; /* Network Module EEPROMs */ struct nmc93cX6_group nm_eeprom_group[4]; }; /* Get WIC device address for the specified onboard port */ int c3745_get_onboard_wic_addr(u_int slot,m_uint64_t *phys_addr); /* Set EEPROM for the specified slot */ int c3745_set_slot_eeprom(c3745_t *router,u_int slot, struct cisco_eeprom *eeprom); /* Get network IRQ for specified slot/port */ u_int c3745_net_irq_for_slot_port(u_int slot,u_int port); /* Set chassis MAC address */ int c3745_chassis_set_mac_addr(c3745_t *router,char *mac_addr); /* Set the system id */ int c3745_set_system_id(c3745_t *router,char *id); /* Burn the system id into the appropriate eeprom if possible */ int c3745_refresh_systemid(c3745_t *router); /* Show C3745 hardware info */ void c3745_show_hardware(c3745_t *router); /* Initialize EEPROM groups */ void c3745_init_eeprom_groups(c3745_t *router); /* dev_c3745_iofpga_init() */ int dev_c3745_iofpga_init(c3745_t *router,m_uint64_t paddr,m_uint32_t len); /* Register the c3745 platform */ int c3745_platform_register(void); /* Hypervisor C3745 initialization */ extern int hypervisor_c3745_init(vm_platform_t *platform); /* NM drivers */ extern struct cisco_card_driver dev_c3745_nm_1fe_tx_driver; extern struct cisco_card_driver dev_c3745_gt96100_fe_driver; extern struct cisco_card_driver dev_c3745_nm_4t_driver; extern struct cisco_card_driver dev_c3745_nm_16esw_driver; extern struct cisco_card_driver dev_c3745_nmd_36esw_driver; extern struct cisco_card_driver dev_c3745_nm_nam_driver; extern struct cisco_card_driver dev_c3745_nm_cids_driver; /* WIC drivers */ extern struct cisco_card_driver *dev_c3745_mb_wic_drivers[]; #endif dynamips-0.2.14/common/dev_c3745_eth.c000066400000000000000000000203571241034141600173240ustar00rootroot00000000000000/* * Cisco C3745 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Ethernet Network Modules. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_am79c971.h" #include "dev_nm_16esw.h" #include "dev_gt.h" #include "dev_c3745.h" /* Multi-Ethernet NM with Am79c971 chips */ struct nm_eth_data { u_int nr_port; struct am79c971_data *port[8]; }; /* Return sub-slot info for integrated WIC slots (on motherboard) */ static int dev_c3745_mb_get_sub_info(vm_instance_t *vm,struct cisco_card *card, u_int port_id, struct cisco_card_driver ***drv_array, u_int *subcard_type) { /* 3 integrated WIC slots */ if ((port_id & 0x0F) >= 3) return(-1); *drv_array = dev_c3745_mb_wic_drivers; *subcard_type = CISCO_CARD_TYPE_WIC; return(0); } /* * dev_c3745_nm_eth_init() * * Add an Ethernet Network Module into specified slot. */ static int dev_c3745_nm_eth_init(vm_instance_t *vm,struct cisco_card *card, int nr_port,int interface_type, const struct cisco_eeprom *eeprom) { struct nm_eth_data *data; u_int slot = card->slot_id; int i; /* Allocate the private data structure */ if (!(data = malloc(sizeof(*data)))) { vm_error(vm,"%s: out of memory.\n",card->dev_name); return(-1); } memset(data,0,sizeof(*data)); data->nr_port = nr_port; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,eeprom); c3745_set_slot_eeprom(VM_C3745(vm),slot,&card->eeprom); /* Create the AMD Am971c971 chip(s) */ for(i=0;inr_port;i++) { data->port[i] = dev_am79c971_init(vm,card->dev_name,interface_type, card->pci_bus,0, c3745_net_irq_for_slot_port(slot,0)); } /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove an Ethernet NM from the specified slot */ static int dev_c3745_nm_eth_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct nm_eth_data *data = card->drv_info; int i; /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c3745_set_slot_eeprom(VM_C3745(vm),card->slot_id,NULL); /* Remove the AMD Am79c971 chips */ for(i=0;inr_port;i++) dev_am79c971_remove(data->port[i]); free(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c3745_nm_eth_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct nm_eth_data *d = card->drv_info; if (!d || (port_id >= d->nr_port)) return(-1); dev_am79c971_set_nio(d->port[port_id],nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c3745_nm_eth_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct nm_eth_data *d = card->drv_info; if (!d || (port_id >= d->nr_port)) return(-1); dev_am79c971_unset_nio(d->port[port_id]); return(0); } /* ====================================================================== */ /* NM-1FE-TX */ /* ====================================================================== */ /* * dev_c3745_nm_1fe_tx_init() * * Add a NM-1FE-TX Network Module into specified slot. */ static int dev_c3745_nm_1fe_tx_init(vm_instance_t *vm,struct cisco_card *card) { return(dev_c3745_nm_eth_init(vm,card,1,AM79C971_TYPE_100BASE_TX, cisco_eeprom_find_nm("NM-1FE-TX"))); } /* ====================================================================== */ /* NM-16ESW */ /* ====================================================================== */ /* Add a NM-16ESW */ static int dev_c3745_nm_16esw_init(vm_instance_t *vm,struct cisco_card *card) { struct nm_16esw_data *data; u_int slot = card->slot_id; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_nm("NM-16ESW")); dev_nm_16esw_burn_mac_addr(vm,slot,&card->eeprom); c3745_set_slot_eeprom(VM_C3745(vm),slot,&card->eeprom); /* Create the device */ data = dev_nm_16esw_init(vm,card->dev_name,slot,card->pci_bus,0, c3745_net_irq_for_slot_port(slot,0)); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a NM-16ESW from the specified slot */ static int dev_c3745_nm_16esw_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct nm_16esw_data *data = card->drv_info; /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c3745_set_slot_eeprom(VM_C3745(vm),card->slot_id,NULL); /* Remove the BCM5600 chip */ dev_nm_16esw_remove(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c3745_nm_16esw_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_set_nio(d,port_id,nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c3745_nm_16esw_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_unset_nio(d,port_id); return(0); } /* Show debug info */ static int dev_c3745_nm_16esw_show_info(vm_instance_t *vm,struct cisco_card *card) { struct nm_16esw_data *d = card->drv_info; dev_nm_16esw_show_info(d); return(0); } /* ====================================================================== */ /* GT96100 - Integrated Ethernet ports */ /* ====================================================================== */ /* Initialize Ethernet part of the GT96100 controller */ static int dev_c3745_gt96100_fe_init(vm_instance_t *vm,struct cisco_card *card) { if (card->slot_id != 0) { vm_error(vm,"dev_c3745_gt96100_fe_init: bad slot %u specified.\n", card->slot_id); return(-1); } /* Store device info into the router structure */ card->drv_info = VM_C3745(vm)->gt_data; return(0); } /* Nothing to do, we never remove the system controller */ static int dev_c3745_gt96100_fe_shutdown(vm_instance_t *vm,struct cisco_card *card) { return(0); } /* Bind a Network IO descriptor */ static int dev_c3745_gt96100_fe_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct gt_data *d = card->drv_info; dev_gt96100_eth_set_nio(d,port_id,nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c3745_gt96100_fe_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct gt_data *d = card->drv_info; dev_gt96100_eth_unset_nio(d,port_id); return(0); } /* ====================================================================== */ /* NM-1FE-TX driver */ struct cisco_card_driver dev_c3745_nm_1fe_tx_driver = { "NM-1FE-TX", 1, 0, dev_c3745_nm_1fe_tx_init, dev_c3745_nm_eth_shutdown, NULL, dev_c3745_nm_eth_set_nio, dev_c3745_nm_eth_unset_nio, NULL, }; /* NM-16ESW driver */ struct cisco_card_driver dev_c3745_nm_16esw_driver = { "NM-16ESW", 1, 0, dev_c3745_nm_16esw_init, dev_c3745_nm_16esw_shutdown, NULL, dev_c3745_nm_16esw_set_nio, dev_c3745_nm_16esw_unset_nio, dev_c3745_nm_16esw_show_info, }; /* GT96100 FastEthernet integrated ports */ struct cisco_card_driver dev_c3745_gt96100_fe_driver = { "GT96100-FE", 1, 3, dev_c3745_gt96100_fe_init, dev_c3745_gt96100_fe_shutdown, dev_c3745_mb_get_sub_info, dev_c3745_gt96100_fe_set_nio, dev_c3745_gt96100_fe_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c3745_iofpga.c000066400000000000000000000337611241034141600200140ustar00rootroot00000000000000/* * Cisco 3745 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) */ #include #include #include #include #include #include #include #include #include "ptask.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_vtty.h" #include "nmc93cX6.h" #include "dev_c3745.h" /* Debugging flags */ #define DEBUG_UNKNOWN 1 #define DEBUG_ACCESS 0 #define DEBUG_NET_IRQ 0 /* Definitions for Motherboard EEPROM (0x00) */ #define EEPROM_MB_DOUT 3 #define EEPROM_MB_DIN 2 #define EEPROM_MB_CLK 1 #define EEPROM_MB_CS 0 /* Definitions for I/O board EEPROM (0x01) */ #define EEPROM_IO_DOUT 3 #define EEPROM_IO_DIN 2 #define EEPROM_IO_CLK 1 #define EEPROM_IO_CS 8 /* Definitions for Midplane EEPROM (0x02) */ #define EEPROM_MP_DOUT 3 #define EEPROM_MP_DIN 2 #define EEPROM_MP_CLK 1 #define EEPROM_MP_CS 9 /* Definitions for Network Modules EEPROM */ #define EEPROM_NM_DOUT 7 #define EEPROM_NM_DIN 6 #define EEPROM_NM_CLK 2 #define EEPROM_NM_CS 4 /* Network IRQ distribution */ struct net_irq_distrib { u_int reg; u_int offset; }; static struct net_irq_distrib net_irq_dist[C3745_MAX_NM_BAYS] = { { 0, 0 }, /* Slot 0: reg 0x20, 0x00XX */ { 1, 0 }, /* Slot 1: reg 0x22, 0x000X */ { 1, 4 }, /* Slot 2: reg 0x22, 0x00X0 */ { 1, 8 }, /* Slot 3: reg 0x22, 0x0X00 */ { 1, 12 }, /* Slot 4: reg 0x22, 0xX000 */ }; /* IO FPGA structure */ struct c3745_iofpga_data { vm_obj_t vm_obj; struct vdevice dev; c3745_t *router; /* Network IRQ status */ m_uint16_t net_irq_status[2]; /* Interrupt mask */ m_uint16_t intr_mask,io_mask2; /* EEPROM select */ u_int eeprom_select; /* WIC select */ u_int wic_select; u_int wic_cmd_pos; u_int wic_cmd_valid; m_uint16_t wic_cmd[2]; }; /* Motherboard EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_mb_def = { EEPROM_MB_CLK, EEPROM_MB_CS, EEPROM_MB_DIN, EEPROM_MB_DOUT, }; /* I/O board EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_io_def = { EEPROM_IO_CLK, EEPROM_IO_CS, EEPROM_IO_DIN, EEPROM_IO_DOUT, }; /* Midplane EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_mp_def = { EEPROM_MP_CLK, EEPROM_MP_CS, EEPROM_MP_DIN, EEPROM_MP_DOUT, }; /* System EEPROM group */ static const struct nmc93cX6_group eeprom_sys_group = { EEPROM_TYPE_NMC93C46, 3, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "System EEPROM", { &eeprom_mb_def, &eeprom_io_def, &eeprom_mp_def }, }; /* NM EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_nm_def = { EEPROM_NM_CLK, EEPROM_NM_CS, EEPROM_NM_DIN, EEPROM_NM_DOUT, }; /* NM EEPROM */ static const struct nmc93cX6_group eeprom_nm_group = { EEPROM_TYPE_NMC93C46, 1, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "NM EEPROM", { &eeprom_nm_def }, }; /* Update network interrupt status */ static inline void dev_c3745_iofpga_net_update_irq(struct c3745_iofpga_data *d) { if ((d->net_irq_status[0] != 0xFFFF) || (d->net_irq_status[1] != 0xFFFF)) { vm_set_irq(d->router->vm,C3745_NETIO_IRQ); } else { vm_clear_irq(d->router->vm,C3745_NETIO_IRQ); } } /* Trigger a Network IRQ for the specified slot/port */ void dev_c3745_iofpga_net_set_irq(struct c3745_iofpga_data *d, u_int slot,u_int port) { struct net_irq_distrib *irq_dist; #if DEBUG_NET_IRQ vm_log(d->router->vm,"IO_FPGA","setting NetIRQ for slot %u port %u\n", slot,port); #endif irq_dist = &net_irq_dist[slot]; d->net_irq_status[irq_dist->reg] &= ~(1 << (irq_dist->offset + port)); dev_c3745_iofpga_net_update_irq(d); } /* Clear a Network IRQ for the specified slot/port */ void dev_c3745_iofpga_net_clear_irq(struct c3745_iofpga_data *d, u_int slot,u_int port) { struct net_irq_distrib *irq_dist; #if DEBUG_NET_IRQ vm_log(d->router->vm,"IO_FPGA","clearing NetIRQ for slot %u port %u\n", slot,port); #endif irq_dist = &net_irq_dist[slot]; d->net_irq_status[irq_dist->reg] |= (1 << (irq_dist->offset + port)); dev_c3745_iofpga_net_update_irq(d); } /* Read a WIC EEPROM */ static m_uint16_t dev_c3745_read_wic_eeprom(struct c3745_iofpga_data *d) { struct cisco_eeprom *eeprom; u_int wic_port; u_int eeprom_offset; m_uint8_t val[2]; switch(d->wic_select) { case 0x1700: wic_port = 0x10; break; case 0x1D00: wic_port = 0x20; break; case 0x3500: wic_port = 0x30; break; default: wic_port = 0; } /* No WIC in slot or no EEPROM: fake an empty EEPROM */ if (!wic_port || !(eeprom = vm_slot_get_eeprom(d->router->vm,0,wic_port))) return(0xFFFF); /* EEPROM offset is in the lowest 6 bits */ eeprom_offset = d->wic_cmd[0] & 0x3F; cisco_eeprom_get_byte(eeprom,eeprom_offset,&val[0]); cisco_eeprom_get_byte(eeprom,eeprom_offset+1,&val[1]); return(((m_uint16_t)val[0] << 8) | val[1]); } /* * dev_c3745_iofpga_access() */ static void * dev_c3745_iofpga_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct c3745_iofpga_data *d = dev->priv_data; u_int slot; if (op_type == MTS_READ) *data = 0x0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA","reading reg 0x%x at pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA", "writing reg 0x%x at pc=0x%llx, data=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),*data,op_size); } #endif switch(offset) { /* Unknown */ case 0x000000: if (op_type == MTS_READ) *data = 0xFFFF; break; /* Unknown */ case 0x000004: if (op_type == MTS_READ) *data = 0xFFFF; break; /* * CompactFlash. * * Bit 0: Slot0 Compact Flash presence. * Bit 1: System Compact Flash presence. */ case 0x000012: if (op_type == MTS_READ) { *data = 0xFFFF; /* System Flash ? */ if (cpu->vm->pcmcia_disk_size[0]) *data &= ~0x02; /* Slot0 Flash ? */ if (cpu->vm->pcmcia_disk_size[1]) *data &= ~0x01; } break; /* Suppress the "****TDM FPGA download failed.." message */ case 0x000014: if (op_type == MTS_READ) *data = 0x00FF; break; /* Power supply status */ case 0x00000a: if (op_type == MTS_READ) *data = 0x0000; break; /* Fan status */ case 0x00000c: if (op_type == MTS_READ) *data = 0x0000; break; /* System EEPROMs */ case 0x00000e: if (op_type == MTS_WRITE) nmc93cX6_write(&d->router->sys_eeprom_group,(u_int)(*data)); else *data = nmc93cX6_read(&d->router->sys_eeprom_group); break; /* * Network interrupt status. * * Bit 0: 0 = GT96100 Ethernet ports. * Bit 8: 0 = AIM slot 0. * Bit 9: 0 = AIM slot 1. */ case 0x000020: if (op_type == MTS_READ) *data = d->net_irq_status[0]; break; /* * Network interrupt status. * * Bit 0: 0 = Interrupt for slot 1 * Bit 4: 0 = Interrupt for slot 2 * Bit 8: 0 = Interrupt for slot 3 * Bit 12: 0 = Interrupt for slot 4 */ case 0x000022: if (op_type == MTS_READ) *data = d->net_irq_status[1]; break; /* * Read when PA Mgmt IRQ (4) is received. * Message: "Error: Unexpected NM Interrupt received from slot: X" * * Bits 0-1: 0 = Interrupt for slot 1 * Bits 2-3: 0 = Interrupt for slot 2 * Bits 4-5: 0 = Interrupt for slot 3 * Bits 6-7: 0 = Interrupt for slot 4 */ case 0x000026: if (op_type == MTS_READ) *data = 0x00FF; break; /* * Read when OIR IRQ (6) is received. * Bits 0-3: 1 = OIR for slots 1-4 * Bits 4-8: 1 = OIR "Watchdog" (???) for slots 1-4 */ case 0x000028: if (op_type == MTS_READ) { *data = d->router->oir_status; } else { d->router->oir_status &= ~(*data); vm_clear_irq(d->router->vm,C3745_EXT_IRQ); } break; /* * Per Slot Intr Mask (seen with "sh platform"). * IO Mask 1 is the lower 8-bits. */ case 0x00002a: if (op_type == MTS_READ) *data = d->intr_mask; else d->intr_mask = *data; break; /* IO Mask 2 (seen with "sh platform") */ case 0x00002c: if (op_type == MTS_READ) *data = d->io_mask2; else d->io_mask2 = *data; break; /* EEPROM in slots 1-4 */ case 0x000040: case 0x000042: case 0x000044: case 0x000046: slot = (offset - 0x000040) >> 1; if (op_type == MTS_WRITE) nmc93cX6_write(&d->router->nm_eeprom_group[slot],(u_int)(*data)); else *data = nmc93cX6_read(&d->router->nm_eeprom_group[slot]); break; /* AIM slot 0 EEPROM */ case 0x000048: if (op_type == MTS_READ) *data = 0xFFFF; break; /* AIM slot 1 EEPROM */ case 0x00004A: if (op_type == MTS_READ) *data = 0xFFFF; break; /* * NM presence. * * Bit 0: 0 = NM present in slot 2 (0x42) * Bit 4: 0 = NM present in slot 4 (0x46) * Bit 8: 0 = NM present in slot 1 (0x40) * Bit 12: 0 = NM present in slot 3 (0x44) */ case 0x00004e: if (op_type == MTS_READ) { *data = 0xFFFF; if (vm_slot_check_eeprom(d->router->vm,1,0)) *data &= ~0x0100; if (vm_slot_check_eeprom(d->router->vm,2,0)) *data &= ~0x0001; if (vm_slot_check_eeprom(d->router->vm,3,0)) *data &= ~0x1000; if (vm_slot_check_eeprom(d->router->vm,4,0)) *data &= ~0x0010; } break; /* * VWIC/WIC related * Bits 0-2: WIC presence */ case 0x100004: if (op_type == MTS_READ) { *data = 0xFFFF; /* check WIC 0 */ if (vm_slot_check_eeprom(d->router->vm,0,0x10)) *data &= ~0x01; /* check WIC 1 */ if (vm_slot_check_eeprom(d->router->vm,0,0x20)) *data &= ~0x02; /* check WIC 2 */ if (vm_slot_check_eeprom(d->router->vm,0,0x30)) *data &= ~0x04; } else { d->wic_select = *data; } break; case 0x100006: if (op_type == MTS_READ) *data = 0x0004; break; case 0x100008: if (op_type == MTS_READ) { if (d->wic_cmd_valid) { *data = dev_c3745_read_wic_eeprom(d); d->wic_cmd_valid = FALSE; } else { *data = 0xFFFF; } } else { /* * Store the EEPROM command (in 2 words). * * For a read, we have: * Word 0: 0x180 (nmc93c46 READ) + offset (6-bits). * Word 1: 0 (no data). */ d->wic_cmd[d->wic_cmd_pos++] = *data; if (d->wic_cmd_pos == 2) { d->wic_cmd_pos = 0; d->wic_cmd_valid = TRUE; } } break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA", "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA", "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* Initialize EEPROM groups */ void c3745_init_eeprom_groups(c3745_t *router) { int i; /* Initialize Mainboard EEPROM */ router->sys_eeprom_group = eeprom_sys_group; for(i=0;i<3;i++) { router->sys_eeprom_group.eeprom[i] = &router->sys_eeprom[i]; router->sys_eeprom[i].data = NULL; router->sys_eeprom[i].len = 0; } /* EEPROMs for Network Modules */ for(i=1;i<=4;i++) { router->nm_eeprom_group[i-1] = eeprom_nm_group; router->nm_eeprom_group[i-1].eeprom[0] = NULL; } } /* Shutdown the IO FPGA device */ static void dev_c3745_iofpga_shutdown(vm_instance_t *vm,struct c3745_iofpga_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* * dev_c3745_iofpga_init() */ int dev_c3745_iofpga_init(c3745_t *router,m_uint64_t paddr,m_uint32_t len) { vm_instance_t *vm = router->vm; struct c3745_iofpga_data *d; /* Allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"IO_FPGA: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); d->router = router; d->net_irq_status[0] = 0xFFFF; d->net_irq_status[1] = 0xFFFF; vm_object_init(&d->vm_obj); d->vm_obj.name = "io_fpga"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_c3745_iofpga_shutdown; /* Set device properties */ dev_init(&d->dev); d->dev.name = "io_fpga"; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.priv_data = d; d->dev.handler = dev_c3745_iofpga_access; /* Map this device to the VM */ vm_bind_device(router->vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_c3745_iofpga.h000066400000000000000000000013471241034141600200140ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005-2007 Christophe Fillot (cf@utc.fr) * * Cisco c3745 I/O FPGA. */ #ifndef __DEV_C3745_IOFPGA_H__ #define __DEV_C3745_IOFPGA_H__ /* Forward declaration for IO_FPGA private data */ struct c3745_iofpga_data; /* Trigger a Network IRQ for the specified slot/port */ void dev_c3745_iofpga_net_set_irq(struct c3745_iofpga_data *d, u_int slot,u_int port); /* Clear a Network IRQ for the specified slot/port */ void dev_c3745_iofpga_net_clear_irq(struct c3745_iofpga_data *d, u_int slot,u_int port); /* Create the c3745 I/O FPGA */ int dev_c3745_iofpga_init(c3745_t *router,m_uint64_t paddr,m_uint32_t len); #endif dynamips-0.2.14/common/dev_c3745_pcmod.c000066400000000000000000000051341241034141600176420ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * PC Modules NM (NM-NAM, NM-CIDS, ...) for c3745 platforms. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "vm.h" #include "dev_i8255x.h" #include "dev_c3745.h" /* Initialize a NM PC module in the specified slot */ static int dev_c3745_pcmod_init(vm_instance_t *vm,struct cisco_card *card) { struct i8255x_data *data; u_int slot = card->slot_id; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_nm(card->driver->dev_type)); c3745_set_slot_eeprom(VM_C3745(vm),slot,&card->eeprom); /* Create the Intel i8255x chip */ data = dev_i8255x_init(vm,card->dev_name,0, card->pci_bus,0, c3745_net_irq_for_slot_port(slot,0)); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a NM PC module from the specified slot */ static int dev_c3745_pcmod_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct i8255x_data *data = card->drv_info; /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c3745_set_slot_eeprom(VM_C3745(vm),card->slot_id,NULL); /* Remove the Intel i2855x chip */ dev_i8255x_remove(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c3745_pcmod_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct i8255x_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); dev_i8255x_set_nio(d,nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c3745_pcmod_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct i8255x_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); dev_i8255x_unset_nio(d); return(0); } /* NM-NAM driver */ struct cisco_card_driver dev_c3745_nm_nam_driver = { "NM-NAM", 0, 0, dev_c3745_pcmod_init, dev_c3745_pcmod_shutdown, NULL, dev_c3745_pcmod_set_nio, dev_c3745_pcmod_unset_nio, NULL, }; /* NM-CIDS driver */ struct cisco_card_driver dev_c3745_nm_cids_driver = { "NM-CIDS", 0, 0, dev_c3745_pcmod_init, dev_c3745_pcmod_shutdown, NULL, dev_c3745_pcmod_set_nio, dev_c3745_pcmod_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c3745_serial.c000066400000000000000000000050621241034141600200170ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Serial Network Modules. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_mueslix.h" #include "dev_c3745.h" /* ====================================================================== */ /* NM-4T */ /* ====================================================================== */ /* * dev_c3745_nm_4t_init() * * Add a NM-4T network module into specified slot. */ int dev_c3745_nm_4t_init(vm_instance_t *vm,struct cisco_card *card) { struct mueslix_data *data; u_int slot = card->slot_id; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_nm("NM-4T")); c3745_set_slot_eeprom(VM_C3745(vm),slot,&card->eeprom); /* Create the Mueslix chip */ data = dev_mueslix_init(vm,card->dev_name,0,card->pci_bus,0, c3745_net_irq_for_slot_port(slot,0)); if (!data) return(-1); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a NM-4T from the specified slot */ int dev_c3745_nm_4t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the NM EEPROM */ cisco_card_unset_eeprom(card); c3745_set_slot_eeprom(VM_C3745(vm),card->slot_id,NULL); /* Remove the mueslix driver */ dev_mueslix_remove(card->drv_info); return(0); } /* Bind a Network IO descriptor to a specific port */ int dev_c3745_nm_4t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct mueslix_data *d = card->drv_info; if (!d || (port_id >= MUESLIX_NR_CHANNELS)) return(-1); return(dev_mueslix_set_nio(d,port_id,nio)); } /* Unbind a Network IO descriptor to a specific port */ int dev_c3745_nm_4t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct mueslix_data *d = card->drv_info; if (!d || (port_id >= MUESLIX_NR_CHANNELS)) return(-1); return(dev_mueslix_unset_nio(d,port_id)); } /* NM-4T driver */ struct cisco_card_driver dev_c3745_nm_4t_driver = { "NM-4T", 1, 0, dev_c3745_nm_4t_init, dev_c3745_nm_4t_shutdown, NULL, dev_c3745_nm_4t_set_nio, dev_c3745_nm_4t_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c3745_wic.c000066400000000000000000000135001241034141600173160ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * WIC Modules. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "vm.h" #include "dev_gt.h" #include "dev_c3745.h" #include "dev_wic_serial.h" /* Get the MPSC channel associated to a WIC sub-slot */ static int dev_c3745_mb_wic_get_mpsc_chan(struct cisco_card *card, u_int port_id, u_int *mpsc_chan) { u_int cid; cid = card->subslot_id + port_id; switch(cid) { /* WIC 0 port 0 mapped to GT96100 MPSC1 */ case 0x10: *mpsc_chan = 1; break; /* WIC 0 port 1 mapped to GT96100 MPSC0 */ case 0x11: *mpsc_chan = 0; break; /* WIC 1 port 0 mapped to GT96100 MPSC4 */ case 0x20: *mpsc_chan = 4; break; /* WIC 1 port 1 mapped to GT96100 MPSC2 */ case 0x21: *mpsc_chan = 2; break; /* WIC 2 port 0 mapped to GT96100 MPSC5 */ case 0x30: *mpsc_chan = 5; break; /* WIC 2 port 1 mapped to GT96100 MPSC3 */ case 0x31: *mpsc_chan = 3; break; default: return(-1); } return(0); } /* Initialize a WIC-1T in the specified slot */ static int dev_c3745_mb_wic1t_init(vm_instance_t *vm,struct cisco_card *card) { struct wic_serial_data *wic_data; m_uint64_t phys_addr; u_int wic_id; /* Create the WIC device */ wic_id = (card->subslot_id >> 4) - 1; if (c3745_get_onboard_wic_addr(wic_id,&phys_addr) == -1) { vm_error(vm,"WIC","invalid slot %u (subslot_id=%u)\n", wic_id,card->subslot_id); return(-1); } wic_data = dev_wic_serial_init(vm,card->dev_name,WIC_SERIAL_MODEL_1T, phys_addr,C3745_WIC_SIZE); if (!wic_data) return(-1); /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_wic("WIC-1T")); /* Store device info into the router structure */ card->drv_info = wic_data; return(0); } /* Remove a WIC-1T from the specified slot */ static int dev_c3745_mb_wic1t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the WIC device */ dev_wic_serial_remove(card->drv_info); /* Remove the WIC EEPROM */ cisco_card_unset_eeprom(card); return(0); } /* Bind a Network IO descriptor */ static int dev_c3745_mb_wic1t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { u_int mpsc_chan; if ((port_id > 0) || (dev_c3745_mb_wic_get_mpsc_chan(card,port_id,&mpsc_chan) == -1)) return(-1); return(dev_gt96100_mpsc_set_nio(VM_C3745(vm)->gt_data,mpsc_chan,nio)); } /* Unbind a Network IO descriptor */ static int dev_c3745_mb_wic1t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { u_int mpsc_chan; if ((port_id > 0) || (dev_c3745_mb_wic_get_mpsc_chan(card,port_id,&mpsc_chan) == -1)) return(-1); return(dev_gt96100_mpsc_unset_nio(VM_C3745(vm)->gt_data,mpsc_chan)); } /* Initialize a WIC-2T in the specified slot */ static int dev_c3745_mb_wic2t_init(vm_instance_t *vm,struct cisco_card *card) { struct wic_serial_data *wic_data; m_uint64_t phys_addr; u_int wic_id; /* Create the WIC device */ wic_id = (card->subslot_id >> 4) - 1; if (c3745_get_onboard_wic_addr(wic_id,&phys_addr) == -1) { vm_error(vm,"WIC","invalid slot %u (subslot_id=%u)\n", wic_id,card->subslot_id); return(-1); } wic_data = dev_wic_serial_init(vm,card->dev_name,WIC_SERIAL_MODEL_2T, phys_addr,C3745_WIC_SIZE); if (!wic_data) return(-1); /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_wic("WIC-2T")); /* Store device info into the router structure */ card->drv_info = wic_data; return(0); } /* Remove a WIC-2T from the specified slot */ static int dev_c3745_mb_wic2t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the WIC device */ dev_wic_serial_remove(card->drv_info); /* Remove the WIC EEPROM */ cisco_card_unset_eeprom(card); return(0); } /* Bind a Network IO descriptor */ static int dev_c3745_mb_wic2t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { u_int mpsc_chan; if ((port_id > 1) || (dev_c3745_mb_wic_get_mpsc_chan(card,port_id,&mpsc_chan) == -1)) return(-1); return(dev_gt96100_mpsc_set_nio(VM_C3745(vm)->gt_data,mpsc_chan,nio)); } /* Unbind a Network IO descriptor */ static int dev_c3745_mb_wic2t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { u_int mpsc_chan; if ((port_id > 1) || (dev_c3745_mb_wic_get_mpsc_chan(card,port_id,&mpsc_chan) == -1)) return(-1); return(dev_gt96100_mpsc_unset_nio(VM_C3745(vm)->gt_data,mpsc_chan)); } /* Cisco 3745 WIC-1T driver (for mainboard) */ struct cisco_card_driver dev_c3745_mb_wic1t_driver = { "WIC-1T", 1, 0, dev_c3745_mb_wic1t_init, dev_c3745_mb_wic1t_shutdown, NULL, dev_c3745_mb_wic1t_set_nio, dev_c3745_mb_wic1t_unset_nio, NULL, }; /* Cisco 3745 WIC-2T driver (for mainboard) */ struct cisco_card_driver dev_c3745_mb_wic2t_driver = { "WIC-2T", 1, 0, dev_c3745_mb_wic2t_init, dev_c3745_mb_wic2t_shutdown, NULL, dev_c3745_mb_wic2t_set_nio, dev_c3745_mb_wic2t_unset_nio, NULL, }; /* WIC drivers (mainbord slots) */ struct cisco_card_driver *dev_c3745_mb_wic_drivers[] = { &dev_c3745_mb_wic1t_driver, &dev_c3745_mb_wic2t_driver, NULL, }; dynamips-0.2.14/common/dev_c6msfc1.c000066400000000000000000000521001241034141600171500ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * Generic MSFC1 routines and definitions (EEPROM,...). * * This is not a working platform! I only added it to play, since it is very * similar to an NPE-200. I think that could work with a functional CatOS SP. */ #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "pci_io.h" #include "dev_gt.h" #include "cisco_eeprom.h" #include "dev_rom.h" #include "dev_dec21140.h" #include "dev_i8254x.h" #include "dev_c6msfc1.h" #include "dev_c6msfc1_mpfpga.h" #include "dev_vtty.h" #include "registry.h" #include "net.h" #include "fs_nvram.h" /* MSFC1 EEPROM */ static m_uint16_t eeprom_msfc1_data[128] = { 0xabab, 0x0190, 0x1262, 0x0100, 0x0002, 0x6003, 0x00cf, 0x4369, 0x7363, 0x6f20, 0x5379, 0x7374, 0x656d, 0x732c, 0x2049, 0x6e63, 0x2e00, 0x5753, 0x2d46, 0x3630, 0x3031, 0x2d52, 0x5346, 0x4300, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3733, 0x2d37, 0x3135, 0x302d, 0x3036, 0x0000, 0x0000, 0x0000, 0x4130, 0x3100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x012d, 0x0000, 0x0000, 0x0009, 0x0005, 0x0001, 0x0003, 0x0001, 0x0001, 0x0002, 0x00cf, 0xffbf, 0x0000, 0x0000, 0x6003, 0x0162, 0x0afd, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0005, 0x00e0, 0xaabb, 0xcc00, 0x0100, 0x0100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1401, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x4b3c, 0x4132, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, }; static struct cisco_eeprom msfc1_eeprom = { "msfc1", eeprom_msfc1_data, sizeof(eeprom_msfc1_data)/2, }; /* ====================================================================== */ /* EOBC - Ethernet Out of Band Channel */ /* ====================================================================== */ static int dev_c6msfc1_eobc_init(vm_instance_t *vm,struct cisco_card *card) { struct dec21140_data *data; /* Create the DEC21140 chip */ data = dev_dec21140_init(vm,card->dev_name,vm->pci_bus[0],6, c6msfc1_net_irq_for_slot_port(0,0)); if (!data) return(-1); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove EOBC */ static int dev_c6msfc1_eobc_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct dec21140_data *data = card->drv_info; dev_dec21140_remove(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c6msfc1_eobc_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct dec21140_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); return(dev_dec21140_set_nio(d,nio)); } /* Unbind a Network IO descriptor */ static int dev_c6msfc1_eobc_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct dec21140_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); dev_dec21140_unset_nio(d); return(0); } /* EOBC driver */ struct cisco_card_driver dev_c6msfc1_eobc = { "C6MSFC1_EOBC", 0, 0, dev_c6msfc1_eobc_init, dev_c6msfc1_eobc_shutdown, NULL, dev_c6msfc1_eobc_set_nio, dev_c6msfc1_eobc_unset_nio, NULL, }; /* ====================================================================== */ /* IBC - InBand Channel */ /* ====================================================================== */ static int dev_c6msfc1_ibc_init(vm_instance_t *vm,struct cisco_card *card) { struct i8254x_data *data; /* Create the Intel Wiseman/Livengood chip */ data = dev_i8254x_init(vm,card->dev_name,0,vm->pci_bus_pool[24],1, c6msfc1_net_irq_for_slot_port(1,0)); if (!data) return(-1); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove EOBC */ static int dev_c6msfc1_ibc_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct i8254x_data *data = card->drv_info; dev_i8254x_remove(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c6msfc1_ibc_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct i8254x_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); return(dev_i8254x_set_nio(d,nio)); } /* Unbind a Network IO descriptor */ static int dev_c6msfc1_ibc_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct i8254x_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); dev_i8254x_unset_nio(d); return(0); } /* IBC driver */ struct cisco_card_driver dev_c6msfc1_ibc = { "C6MSFC1_IBC", 0, 0, dev_c6msfc1_ibc_init, dev_c6msfc1_ibc_shutdown, NULL, dev_c6msfc1_ibc_set_nio, dev_c6msfc1_ibc_unset_nio, NULL, }; /* ======================================================================== */ /* Port Adapter Drivers */ /* ======================================================================== */ static struct cisco_card_driver *pa_drivers[] = { &dev_c6msfc1_eobc, &dev_c6msfc1_ibc, NULL, }; /* ======================================================================== */ /* C6MSFC1 router instances */ /* ======================================================================== */ /* Initialize default parameters for a MSFC1 */ static void c6msfc1_init_defaults(c6msfc1_t *router); /* Directly extract the configuration from the NVRAM device */ static int c6msfc1_nvram_extract_config(vm_instance_t *vm,u_char **startup_config,size_t *startup_len,u_char **private_config,size_t *private_len) { int ret; ret = generic_nvram_extract_config(vm, "nvram", vm->nvram_rom_space, 0, C6MSFC1_NVRAM_ADDR + vm->nvram_rom_space, FS_NVRAM_FORMAT_ABSOLUTE_C6, startup_config, startup_len, private_config, private_len); return(ret); } /* Directly push the IOS configuration to the NVRAM device */ static int c6msfc1_nvram_push_config(vm_instance_t *vm,u_char *startup_config,size_t startup_len,u_char *private_config,size_t private_len) { int ret; ret = generic_nvram_push_config(vm, "nvram", vm->nvram_size*1024, vm->nvram_rom_space, 0, C6MSFC1_NVRAM_ADDR + vm->nvram_rom_space, FS_NVRAM_FORMAT_ABSOLUTE_C6, startup_config, startup_len, private_config, private_len); return(ret); } /* Get slot/port corresponding to specified network IRQ */ static inline void c6msfc1_net_irq_get_slot_port(u_int irq,u_int *slot,u_int *port) { *slot = irq - C6MSFC1_NETIO_IRQ_BASE; *port = 0; } /* Get network IRQ for specified slot/port */ u_int c6msfc1_net_irq_for_slot_port(u_int slot,u_int port) { u_int irq; irq = C6MSFC1_NETIO_IRQ_BASE + slot; return(irq); } /* Set MSFC eeprom definition */ static int c6msfc1_set_eeprom(c6msfc1_t *router) { if (cisco_eeprom_copy(&router->cpu_eeprom,&msfc1_eeprom) == -1) { vm_error(router->vm,"unable to set NPE EEPROM.\n"); return(-1); } return(0); } /* Set the base MAC address of the chassis */ _unused static int c6msfc1_burn_mac_addr(c6msfc1_t *router,n_eth_addr_t *addr) { m_uint8_t eeprom_ver; /* Read EEPROM format version */ cisco_eeprom_get_byte(&router->mp_eeprom,0,&eeprom_ver); if (eeprom_ver != 1) { vm_error(router->vm,"c6msfc1_burn_mac_addr: unable to handle " "EEPROM version %u\n",eeprom_ver); return(-1); } cisco_eeprom_set_region(&router->mp_eeprom,12,addr->eth_addr_byte,6); return(0); } /* Create a new router instance */ static int c6msfc1_create_instance(vm_instance_t *vm) { c6msfc1_t *router; if (!(router = malloc(sizeof(*router)))) { fprintf(stderr,"C6MFC1 '%s': Unable to create new instance!\n",vm->name); return(-1); } memset(router,0,sizeof(*router)); router->vm = vm; vm->hw_data = router; vm->elf_machine_id = C6MSFC1_ELF_MACHINE_ID; c6msfc1_init_defaults(router); return(0); } /* Free resources used by a router instance */ static int c6msfc1_delete_instance(vm_instance_t *vm) { c6msfc1_t *router = VM_C6MSFC1(vm); int i; /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(FALSE); } } /* Remove NIO bindings */ for(i=0;icpu_eeprom); cisco_eeprom_free(&router->mp_eeprom); /* Free all resources used by VM */ vm_free(vm); /* Free the router structure */ free(router); return(TRUE); } /* Create the main PCI bus for a GT64010 based system */ static int c6msfc1_init_gt64010(c6msfc1_t *router) { vm_instance_t *vm = router->vm; if (!(vm->pci_bus[0] = pci_bus_create("PCI Bus 0",0))) { vm_error(vm,"unable to create PCI data.\n"); return(-1); } return(dev_gt64010_init(vm,"gt64010",C6MSFC1_GT64K_ADDR,0x1000, C6MSFC1_GT64K_IRQ)); } /* Initialize a MSFC1 board */ static int c6msfc1_init_hw(c6msfc1_t *router) { vm_instance_t *vm = router->vm; /* Set the processor type: R5000 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R5000); /* Initialize the Galileo GT-64010 PCI controller */ if (c6msfc1_init_gt64010(router) == -1) return(-1); /* Create PCI bus 1 */ vm->pci_bus_pool[24] = pci_bus_create("PCI Bus 1",-1); dev_dec21154_init(vm->pci_bus[0],1,vm->pci_bus_pool[24]); /* Initialize SRAM (4Mb) */ dev_c7200_sram_init(vm,"sram",C6MSFC1_SRAM_ADDR,C6MSFC1_SRAM_SIZE, vm->pci_bus_pool[24],0); /* PCI IO space */ if (!(vm->pci_io_space = pci_io_data_init(vm,C6MSFC1_PCI_IO_ADDR))) return(-1); /* Cirrus Logic PD6729 (PCI-to-PCMCIA host adapter) */ dev_clpd6729_init(vm,vm->pci_bus[0],5,vm->pci_io_space,0x402,0x403); return(0); } /* Show MSFC1 hardware info */ void c6msfc1_show_hardware(c6msfc1_t *router) { vm_instance_t *vm = router->vm; printf("C6MSFC1 instance '%s' (id %d):\n",vm->name,vm->instance_id); printf(" VM Status : %d\n",vm->status); printf(" RAM size : %u Mb\n",vm->ram_size); printf(" IOMEM size : %u Mb\n",vm->iomem_size); printf(" NVRAM size : %u Kb\n",vm->nvram_size); printf(" IOS image : %s\n\n",vm->ios_image); if (vm->debug_level > 0) { dev_show_list(vm); pci_dev_show_list(vm->pci_bus[0]); pci_dev_show_list(vm->pci_bus[1]); printf("\n"); } } /* Initialize default parameters for a MSFC1 */ static void c6msfc1_init_defaults(c6msfc1_t *router) { vm_instance_t *vm = router->vm; n_eth_addr_t *m; m_uint16_t pid; /* Set platform slots characteristics */ vm->nr_slots = C6MSFC1_MAX_PA_BAYS; vm->slots_type = CISCO_CARD_TYPE_PA; vm->slots_drivers = pa_drivers; pid = (m_uint16_t)getpid(); /* Generate a chassis MAC address based on the instance ID */ m = &router->mac_addr; m->eth_addr_byte[0] = vm_get_mac_addr_msb(vm); m->eth_addr_byte[1] = vm->instance_id & 0xFF; m->eth_addr_byte[2] = pid >> 8; m->eth_addr_byte[3] = pid & 0xFF; m->eth_addr_byte[4] = 0x00; m->eth_addr_byte[5] = 0x00; /* Default slot: 1 */ router->msfc_slot = 1; c6msfc1_set_eeprom(router); c6msfc1_init_eeprom_groups(router); /* Create EOBC and IBC interfaces */ vm_slot_add_binding(vm,"C6MSFC1_EOBC",0,0); vm_slot_add_binding(vm,"C6MSFC1_IBC",1,0); vm->ram_mmap = C6MSFC1_DEFAULT_RAM_MMAP; vm->ram_size = C6MSFC1_DEFAULT_RAM_SIZE; vm->rom_size = C6MSFC1_DEFAULT_ROM_SIZE; vm->nvram_size = C6MSFC1_DEFAULT_NVRAM_SIZE; vm->iomem_size = 0; vm->conf_reg_setup = C6MSFC1_DEFAULT_CONF_REG; vm->clock_divisor = C6MSFC1_DEFAULT_CLOCK_DIV; vm->nvram_rom_space = C6MSFC1_NVRAM_ROM_RES_SIZE; } /* Run the checklist */ static int c6msfc1_checklist(c6msfc1_t *router) { struct vm_instance *vm = router->vm; int res = 0; res += vm_object_check(vm,"ram"); res += vm_object_check(vm,"rom"); res += vm_object_check(vm,"nvram"); res += vm_object_check(vm,"zero"); if (res < 0) vm_error(vm,"incomplete initialization (no memory?)\n"); return(res); } /* Initialize Port Adapters */ static int c6msfc1_init_platform_pa(c6msfc1_t *router) { return(vm_slot_init_all(router->vm)); } /* Initialize the MSFC1 Platform */ static int c6msfc1_init_platform(c6msfc1_t *router) { struct vm_instance *vm = router->vm; cpu_mips_t *cpu0; cpu_gen_t *gen0; vm_obj_t *obj; /* Copy config register setup into "active" config register */ vm->conf_reg = vm->conf_reg_setup; /* Create Console and AUX ports */ vm_init_vtty(vm); /* Create a CPU group */ vm->cpu_group = cpu_group_create("System CPU"); /* Initialize the virtual MIPS processor */ if (!(gen0 = cpu_create(vm,CPU_TYPE_MIPS64,0))) { vm_error(vm,"unable to create CPU0!\n"); return(-1); } cpu0 = CPU_MIPS64(gen0); /* Add this CPU to the system CPU group */ cpu_group_add(vm->cpu_group,gen0); vm->boot_cpu = gen0; /* Initialize the IRQ routing vectors */ vm->set_irq = mips64_vm_set_irq; vm->clear_irq = mips64_vm_clear_irq; /* Mark the Network IO interrupt as high priority */ cpu0->irq_idle_preempt[C6MSFC1_NETIO_IRQ] = TRUE; cpu0->irq_idle_preempt[C6MSFC1_GT64K_IRQ] = TRUE; /* Copy some parameters from VM to CPU0 (idle PC, ...) */ cpu0->idle_pc = vm->idle_pc; if (vm->timer_irq_check_itv) cpu0->timer_irq_check_itv = vm->timer_irq_check_itv; /* * On the MSFC1, bit 33 of physical addresses is used to bypass L2 cache. * We clear it systematically. */ cpu0->addr_bus_mask = C6MSFC1_ADDR_BUS_MASK; /* Remote emulator control */ dev_remote_control_init(vm,0x16000000,0x1000); /* Bootflash (8 Mb) */ dev_bootflash_init(vm,"bootflash","c7200-bootflash-8mb", C6MSFC1_BOOTFLASH_ADDR); /* NVRAM and calendar */ dev_nvram_init(vm,"nvram",C6MSFC1_NVRAM_ADDR, vm->nvram_size*1024,&vm->conf_reg); /* Bit-bucket zone */ dev_zero_init(vm,"zero",C6MSFC1_BITBUCKET_ADDR,0xc00000); /* Initialize the NPE board */ if (c6msfc1_init_hw(router) == -1) return(-1); /* Initialize RAM */ vm_ram_init(vm,0x00000000ULL); /* Initialize ROM */ if (!vm->rom_filename) { /* use embedded ROM */ dev_rom_init(vm,"rom",C6MSFC1_ROM_ADDR,vm->rom_size*1048576, mips64_microcode,mips64_microcode_len); } else { /* use alternate ROM */ dev_ram_init(vm,"rom",TRUE,TRUE,NULL,FALSE, C6MSFC1_ROM_ADDR,vm->rom_size*1048576); } /* Byte swapping */ dev_bswap_init(vm,"mem_bswap",C6MSFC1_BSWAP_ADDR,1024*1048576,0x00000000ULL); /* PCI IO space */ if (!(vm->pci_io_space = pci_io_data_init(vm,C6MSFC1_PCI_IO_ADDR))) return(-1); /* Initialize the Port Adapters */ if (c6msfc1_init_platform_pa(router) == -1) return(-1); /* Verify the check list */ if (c6msfc1_checklist(router) == -1) return(-1); /* Midplane FPGA */ if (dev_c6msfc1_mpfpga_init(router,C6MSFC1_MPFPGA_ADDR,0x1000) == -1) return(-1); if (!(obj = vm_object_find(router->vm,"mp_fpga"))) return(-1); router->mpfpga_data = obj->data; /* IO FPGA */ if (dev_c6msfc1_iofpga_init(router,C6MSFC1_IOFPGA_ADDR,0x1000) == -1) return(-1); /* Show device list */ c6msfc1_show_hardware(router); return(0); } /* Boot the IOS image */ static int c6msfc1_boot_ios(c6msfc1_t *router) { vm_instance_t *vm = router->vm; cpu_mips_t *cpu; if (!vm->boot_cpu) return(-1); /* Suspend CPU activity since we will restart directly from ROM */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Reset the boot CPU */ cpu = CPU_MIPS64(vm->boot_cpu); mips64_reset(cpu); /* Load IOS image */ if (mips64_load_elf_image(cpu,vm->ios_image, (vm->ghost_status == VM_GHOST_RAM_USE), &vm->ios_entry_point) < 0) { vm_error(vm,"failed to load Cisco IOS image '%s'.\n",vm->ios_image); return(-1); } /* Launch the simulation */ printf("\nC6MSFC1 '%s': starting simulation (CPU0 PC=0x%llx), " "JIT %sabled.\n", vm->name,cpu->pc,vm->jit_use ? "en":"dis"); vm_log(vm,"C6MSFC1_BOOT", "starting instance (CPU0 PC=0x%llx,idle_pc=0x%llx,JIT %s)\n", cpu->pc,cpu->idle_pc,vm->jit_use ? "on":"off"); /* Start main CPU */ if (vm->ghost_status != VM_GHOST_RAM_GENERATE) { vm->status = VM_STATUS_RUNNING; cpu_start(vm->boot_cpu); } else { vm->status = VM_STATUS_SHUTDOWN; } return(0); } /* Set an IRQ */ static void c6msfc1_set_irq(vm_instance_t *vm,u_int irq) { c6msfc1_t *router = VM_C6MSFC1(vm); cpu_mips_t *cpu0 = CPU_MIPS64(vm->boot_cpu); u_int slot,port; switch(irq) { case 0 ... 7: mips64_set_irq(cpu0,irq); if (cpu0->irq_idle_preempt[irq]) cpu_idle_break_wait(cpu0->gen); break; case C6MSFC1_NETIO_IRQ_BASE ... C6MSFC1_NETIO_IRQ_END: c6msfc1_net_irq_get_slot_port(irq,&slot,&port); dev_c6msfc1_mpfpga_net_set_irq(router->mpfpga_data,slot,port); break; } } /* Clear an IRQ */ static void c6msfc1_clear_irq(vm_instance_t *vm,u_int irq) { c6msfc1_t *router = VM_C6MSFC1(vm); cpu_mips_t *cpu0 = CPU_MIPS64(vm->boot_cpu); u_int slot,port; switch(irq) { case 0 ... 7: mips64_clear_irq(cpu0,irq); break; case C6MSFC1_NETIO_IRQ_BASE ... C6MSFC1_NETIO_IRQ_END: c6msfc1_net_irq_get_slot_port(irq,&slot,&port); dev_c6msfc1_mpfpga_net_clear_irq(router->mpfpga_data,slot,port); break; } } /* Initialize a MSFC1 instance */ static int c6msfc1_init_instance(vm_instance_t *vm) { c6msfc1_t *router = VM_C6MSFC1(vm); m_uint32_t rom_entry_point; cpu_mips_t *cpu0; /* Initialize the MSFC1 platform */ if (c6msfc1_init_platform(router) == -1) { vm_error(vm,"unable to initialize the platform hardware.\n"); return(-1); } /* IRQ routing */ vm->set_irq = c6msfc1_set_irq; vm->clear_irq = c6msfc1_clear_irq; /* Load IOS configuration files */ if (vm->ios_startup_config != NULL || vm->ios_private_config != NULL) { vm_nvram_push_config(vm,vm->ios_startup_config,vm->ios_private_config); vm->conf_reg &= ~0x40; } /* Load ROM (ELF image or embedded) */ cpu0 = CPU_MIPS64(vm->boot_cpu); rom_entry_point = (m_uint32_t)MIPS_ROM_PC; if ((vm->rom_filename != NULL) && (mips64_load_elf_image(cpu0,vm->rom_filename,0,&rom_entry_point) < 0)) { vm_error(vm,"unable to load alternate ROM '%s', " "fallback to embedded ROM.\n\n",vm->rom_filename); vm->rom_filename = NULL; } /* Load symbol file */ if (vm->sym_filename) { mips64_sym_load_file(cpu0,vm->sym_filename); cpu0->sym_trace = 1; } return(c6msfc1_boot_ios(router)); } /* Stop a MSFC1 instance */ static int c6msfc1_stop_instance(vm_instance_t *vm) { printf("\nC6MSFC1 '%s': stopping simulation.\n",vm->name); vm_log(vm,"C6MSFC1_STOP","stopping simulation.\n"); /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } } /* Free resources that were used during execution to emulate hardware */ vm_slot_shutdown_all(vm); vm_hardware_shutdown(vm); return(0); } /* Get MAC address MSB */ static u_int c6msfc1_get_mac_addr_msb(void) { return(0xC6); } /* Show specific CLI options */ static void c6msfc1_cli_show_options(vm_instance_t *vm) { printf(" -s : Bind a Network IO interface to a " "Port Adapter\n"); } /* Platform definition */ static vm_platform_t c6msfc1_platform = { "c6msfc1", "C6MSFC1", "C6MSFC1", c6msfc1_create_instance, c6msfc1_delete_instance, c6msfc1_init_instance, c6msfc1_stop_instance, NULL, NULL, c6msfc1_nvram_extract_config, c6msfc1_nvram_push_config, c6msfc1_get_mac_addr_msb, NULL, NULL, c6msfc1_cli_show_options, NULL, }; /* Register the C6-MSFC1 platform */ int c6msfc1_platform_register(void) { return(vm_platform_register(&c6msfc1_platform)); } dynamips-0.2.14/common/dev_c6msfc1.h000066400000000000000000000073431241034141600171660ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Generic Cisco MSFC1 routines and definitions (EEPROM,...). */ #ifndef __DEV_C6MSFC1_H__ #define __DEV_C6MSFC1_H__ #include #include "utils.h" #include "net.h" #include "device.h" #include "pci_dev.h" #include "nmc93cX6.h" #include "dev_ds1620.h" #include "net_io.h" #include "vm.h" /* Default MSFC1 parameters */ #define C6MSFC1_DEFAULT_RAM_SIZE 256 #define C6MSFC1_DEFAULT_ROM_SIZE 4 #define C6MSFC1_DEFAULT_NVRAM_SIZE 128 #define C6MSFC1_DEFAULT_CONF_REG 0x2102 #define C6MSFC1_DEFAULT_CLOCK_DIV 4 #define C6MSFC1_DEFAULT_RAM_MMAP 1 /* EOBC + IBC */ #define C6MSFC1_MAX_PA_BAYS 2 /* MSFC1 Timer IRQ (virtual) */ #define C6MSFC1_VTIMER_IRQ 0 /* MSFC1 DUART Interrupt */ #define C6MSFC1_DUART_IRQ 5 /* MSFC1 Network I/O Interrupt */ #define C6MSFC1_NETIO_IRQ 2 /* MSFC1 PA Management Interrupt handler */ #define C6MSFC1_PA_MGMT_IRQ 3 /* MSFC1 GT64k DMA/Timer Interrupt */ #define C6MSFC1_GT64K_IRQ 4 /* MSFC1 Error/OIR Interrupt */ #define C6MSFC1_OIR_IRQ 6 /* Network IRQ */ #define C6MSFC1_NETIO_IRQ_BASE 32 #define C6MSFC1_NETIO_IRQ_END \ (C6MSFC1_NETIO_IRQ_BASE + C6MSFC1_MAX_PA_BAYS - 1) /* MSFC1 base ram limit (256 Mb) */ #define C6MSFC1_BASE_RAM_LIMIT 256 /* MSFC1 common device addresses */ #define C6MSFC1_GT64K_ADDR 0x14000000ULL #define C6MSFC1_GT64K_SEC_ADDR 0x15000000ULL #define C6MSFC1_BOOTFLASH_ADDR 0x1a000000ULL #define C6MSFC1_NVRAM_ADDR 0x1e000000ULL #define C6MSFC1_MPFPGA_ADDR 0x1e800000ULL #define C6MSFC1_IOFPGA_ADDR 0x1e840000ULL #define C6MSFC1_BITBUCKET_ADDR 0x1f000000ULL #define C6MSFC1_ROM_ADDR 0x1fc00000ULL #define C6MSFC1_IOMEM_ADDR 0x20000000ULL #define C6MSFC1_SRAM_ADDR 0x4b000000ULL #define C6MSFC1_BSWAP_ADDR 0xc0000000ULL #define C6MSFC1_PCI_IO_ADDR 0x100000000ULL /* SRAM size */ #define C6MSFC1_SRAM_SIZE (4096*1024) /* Reserved space for ROM in NVRAM */ #define C6MSFC1_NVRAM_ROM_RES_SIZE 2048 /* MSFC1 physical address bus mask: keep only the lower 33 bits */ #define C6MSFC1_ADDR_BUS_MASK 0x1ffffffffULL /* MSFC1 ELF Platform ID */ #define C6MSFC1_ELF_MACHINE_ID 0x19 /* 2 temperature sensors in a MSFC1: chassis inlet and oulet */ #define C6MSFC1_TEMP_SENSORS 2 #define VM_C6MSFC1(vm) ((c6msfc1_t *)vm->hw_data) /* MSFC1 router */ typedef struct c6msfc1_router c6msfc1_t; /* MSFC1 router */ struct c6msfc1_router { /* Chassis MAC address */ n_eth_addr_t mac_addr; /* Associated VM instance */ vm_instance_t *vm; /* Midplane FPGA */ struct c6msfc1_mpfpga_data *mpfpga_data; /* Midplane EEPROM can be modified to change the chassis MAC address... */ struct cisco_eeprom cpu_eeprom,mp_eeprom; /* EEPROMs for CPU and Midplane */ struct nmc93cX6_group sys_eeprom_g1; /* Temperature sensors */ struct ds1620_data ds1620_sensors[C6MSFC1_TEMP_SENSORS]; /* Slot of this MSFC */ u_int msfc_slot; }; /* Initialize EEPROM groups */ void c6msfc1_init_eeprom_groups(c6msfc1_t *router); /* Get network IRQ for specified slot/port */ u_int c6msfc1_net_irq_for_slot_port(u_int slot,u_int port); /* Show the list of available PA drivers */ void c6msfc1_pa_show_drivers(void); /* Set chassis MAC address */ int c6msfc1_midplane_set_mac_addr(c6msfc1_t *router,char *mac_addr); /* Show MSFC1 hardware info */ void c6msfc1_show_hardware(c6msfc1_t *router); /* dev_c6msfc1_iofpga_init() */ int dev_c6msfc1_iofpga_init(c6msfc1_t *router,m_uint64_t paddr,m_uint32_t len); /* dev_mpfpga_init() */ int dev_c6msfc1_mpfpga_init(c6msfc1_t *router,m_uint64_t paddr,m_uint32_t len); /* Register the c6msfc1 platform */ int c6msfc1_platform_register(void); #endif dynamips-0.2.14/common/dev_c6msfc1_iofpga.c000066400000000000000000000300361241034141600205010ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * Cisco C6k-MSFC1 I/O FPGA: * - Simulates a NMC93C56 Serial EEPROM. * - Simulates a DALLAS DS1620 for Temperature Sensors. * - Simulates console and AUX ports (SCN2681). * * This is very similar to c7200 platform. */ #include #include #include #include #include #include #include #include #include "ptask.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_vtty.h" #include "nmc93cX6.h" #include "dev_ds1620.h" #include "dev_c6msfc1.h" /* Debugging flags */ #define DEBUG_UNKNOWN 1 #define DEBUG_ACCESS 0 #define DEBUG_LED 0 #define DEBUG_IO_CTL 0 #define DEBUG_ENVM 0 /* DUART RX/TX status (SRA/SRB) */ #define DUART_RX_READY 0x01 #define DUART_TX_READY 0x04 /* DUART RX/TX Interrupt Status/Mask */ #define DUART_TXRDYA 0x01 #define DUART_RXRDYA 0x02 #define DUART_TXRDYB 0x10 #define DUART_RXRDYB 0x20 /* Definitions for CPU and Midplane Serial EEPROMs */ #define EEPROM_CPU_DOUT 6 #define EEPROM_CPU_DIN 0 #define EEPROM_CPU_CLK 1 #define EEPROM_CPU_CS 2 #define EEPROM_MP_DOUT 7 #define EEPROM_MP_DIN 3 #define EEPROM_MP_CLK 4 #define EEPROM_MP_CS 5 /* Pack the NVRAM */ #define NVRAM_PACKED 0x04 /* Temperature: 22°C as default value */ #define C6MSFC1_DEFAULT_TEMP 22 #define DS1620_CHIP(d,id) (&(d)->router->ds1620_sensors[(id)]) /* IO FPGA structure */ struct iofpga_data { vm_obj_t vm_obj; struct vdevice dev; c6msfc1_t *router; /* Lock test */ pthread_mutex_t lock; /* Periodic task to trigger dummy DUART IRQ */ ptask_id_t duart_irq_tid; /* DUART & Console Management */ u_int duart_isr,duart_imr,duart_irq_seq; /* IO control register */ u_int io_ctrl_reg; /* Voltages */ u_int mux; }; #define IOFPGA_LOCK(d) pthread_mutex_lock(&(d)->lock) #define IOFPGA_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) /* CPU EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_cpu_def = { EEPROM_CPU_CLK, EEPROM_CPU_CS, EEPROM_CPU_DIN, EEPROM_CPU_DOUT, }; /* Midplane EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_midplane_def = { EEPROM_MP_CLK, EEPROM_MP_CS, EEPROM_MP_DIN, EEPROM_MP_DOUT, }; /* IOFPGA manages simultaneously CPU and Midplane EEPROM */ static const struct nmc93cX6_group eeprom_cpu_midplane = { EEPROM_TYPE_NMC93C56, 2, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "CPU and Midplane EEPROM", { &eeprom_cpu_def, &eeprom_midplane_def }, }; /* Console port input */ static void tty_con_input(vtty_t *vtty) { struct iofpga_data *d = vtty->priv_data; IOFPGA_LOCK(d); if (d->duart_imr & DUART_RXRDYA) { d->duart_isr |= DUART_RXRDYA; vm_set_irq(d->router->vm,C6MSFC1_DUART_IRQ); } IOFPGA_UNLOCK(d); } /* AUX port input */ static void tty_aux_input(vtty_t *vtty) { struct iofpga_data *d = vtty->priv_data; IOFPGA_LOCK(d); if (d->duart_imr & DUART_RXRDYB) { d->duart_isr |= DUART_RXRDYB; vm_set_irq(d->router->vm,C6MSFC1_DUART_IRQ); } IOFPGA_UNLOCK(d); } /* IRQ trickery for Console and AUX ports */ static int tty_trigger_dummy_irq(struct iofpga_data *d,void *arg) { u_int mask; IOFPGA_LOCK(d); d->duart_irq_seq++; if (d->duart_irq_seq == 2) { mask = DUART_TXRDYA|DUART_TXRDYB; if (d->duart_imr & mask) { d->duart_isr |= DUART_TXRDYA|DUART_TXRDYB; vm_set_irq(d->router->vm,C6MSFC1_DUART_IRQ); } d->duart_irq_seq = 0; } IOFPGA_UNLOCK(d); return(0); } /* * dev_c6msfc1_iofpga_access() */ void *dev_c6msfc1_iofpga_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct iofpga_data *d = dev->priv_data; vm_instance_t *vm = d->router->vm; u_char odata; int i; if (op_type == MTS_READ) *data = 0x0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA","reading reg 0x%x at pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"IO_FPGA","writing reg 0x%x at pc=0x%llx, data=0x%llx\n", offset,cpu_get_pc(cpu),*data); } #endif IOFPGA_LOCK(d); switch(offset) { /* I/O control register */ case 0x204: if (op_type == MTS_WRITE) { #if DEBUG_IO_CTL vm_log(vm,"IO_FPGA","setting value 0x%llx in io_ctrl_reg\n",*data); #endif d->io_ctrl_reg = *data; } else { *data = d->io_ctrl_reg; *data |= NVRAM_PACKED; /* Packed NVRAM */ } break; /* CPU/Midplane EEPROMs */ case 0x21c: if (op_type == MTS_WRITE) nmc93cX6_write(&d->router->sys_eeprom_g1,(u_int)(*data)); else *data = nmc93cX6_read(&d->router->sys_eeprom_g1); break; /* Watchdog */ case 0x234: break; /* * FPGA release/presence ? Flash SIMM size: * 0x0001: 2048K Flash (2 banks) * 0x0504: 8192K Flash (2 banks) * 0x0704: 16384K Flash (2 banks) * 0x0904: 32768K Flash (2 banks) * 0x0B04: 65536K Flash (2 banks) * 0x2001: 1024K Flash (1 bank) * 0x2504: 4096K Flash (1 bank) * 0x2704: 8192K Flash (1 bank) * 0x2904: 16384K Flash (1 bank) * 0x2B04: 32768K Flash (1 bank) * * Number of Flash SIMM banks + size. * Touching some lower bits causes problems with environmental monitor. * * It is displayed by command "sh bootflash: chips" */ case 0x23c: if (op_type == MTS_READ) *data = 0x2704; break; /* LEDs */ case 0x244: #if DEBUG_LED vm_log(vm,"IO_FPGA","LED register is now 0x%x (0x%x)\n", *data,(~*data) & 0x0F); #endif break; /* ==== DUART SCN2681 (console/aux) ==== */ case 0x404: /* Mode Register A (MRA) */ break; case 0x40c: /* Status Register A (SRA) */ if (op_type == MTS_READ) { odata = 0; if (vtty_is_char_avail(vm->vtty_con)) odata |= DUART_RX_READY; odata |= DUART_TX_READY; vm_clear_irq(vm,C6MSFC1_DUART_IRQ); *data = odata; } break; case 0x414: /* Command Register A (CRA) */ /* Disable TX = High */ if ((op_type == MTS_WRITE) && (*data & 0x8)) { vm->vtty_con->managed_flush = TRUE; vtty_flush(vm->vtty_con); } break; case 0x41c: /* RX/TX Holding Register A (RHRA/THRA) */ if (op_type == MTS_WRITE) { vtty_put_char(vm->vtty_con,(char)*data); d->duart_isr &= ~DUART_TXRDYA; } else { *data = vtty_get_char(vm->vtty_con); d->duart_isr &= ~DUART_RXRDYA; } break; case 0x424: /* WRITE: Aux Control Register (ACR) */ break; case 0x42c: /* Interrupt Status/Mask Register (ISR/IMR) */ if (op_type == MTS_WRITE) { d->duart_imr = *data; } else *data = d->duart_isr; break; case 0x434: /* Counter/Timer Upper Value (CTU) */ case 0x43c: /* Counter/Timer Lower Value (CTL) */ case 0x444: /* Mode Register B (MRB) */ break; case 0x44c: /* Status Register B (SRB) */ if (op_type == MTS_READ) { odata = 0; if (vtty_is_char_avail(vm->vtty_aux)) odata |= DUART_RX_READY; odata |= DUART_TX_READY; //vm_clear_irq(vm,C6MSFC1_DUART_IRQ); *data = odata; } break; case 0x454: /* Command Register B (CRB) */ /* Disable TX = High */ if ((op_type == MTS_WRITE) && (*data & 0x8)) { vm->vtty_aux->managed_flush = TRUE; vtty_flush(vm->vtty_aux); } break; case 0x45c: /* RX/TX Holding Register B (RHRB/THRB) */ if (op_type == MTS_WRITE) { vtty_put_char(vm->vtty_aux,(char)*data); d->duart_isr &= ~DUART_TXRDYA; } else { *data = vtty_get_char(vm->vtty_aux); d->duart_isr &= ~DUART_RXRDYB; } break; case 0x46c: /* WRITE: Output Port Configuration Register (OPCR) */ case 0x474: /* READ: Start Counter Command; */ /* WRITE: Set Output Port Bits Command */ case 0x47c: /* WRITE: Reset Output Port Bits Command */ break; /* ==== DS 1620 (temp sensors) ==== */ case 0x20c: /* Temperature Control */ if (op_type == MTS_WRITE) { for(i=0;i> i) & 0x01); ds1620_set_clk_bit(DS1620_CHIP(d,i),(*data >> 4) & 0x01); } } break; case 0x214: /* Temperature data write */ if (op_type == MTS_WRITE) { d->mux = *data; for(i=0;isys_eeprom_g1 = eeprom_cpu_midplane; router->sys_eeprom_g1.eeprom[0] = &router->cpu_eeprom; router->sys_eeprom_g1.eeprom[1] = &router->mp_eeprom; } /* Shutdown the IO FPGA device */ void dev_c6msfc1_iofpga_shutdown(vm_instance_t *vm,struct iofpga_data *d) { if (d != NULL) { IOFPGA_LOCK(d); vm->vtty_con->read_notifier = NULL; vm->vtty_aux->read_notifier = NULL; IOFPGA_UNLOCK(d); /* Remove the dummy IRQ periodic task */ ptask_remove(d->duart_irq_tid); /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* * dev_c6msfc1_iofpga_init() */ int dev_c6msfc1_iofpga_init(c6msfc1_t *router,m_uint64_t paddr,m_uint32_t len) { vm_instance_t *vm = router->vm; struct iofpga_data *d; u_int i; /* Allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"IO_FPGA: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); pthread_mutex_init(&d->lock,NULL); d->router = router; for(i=0;ivm_obj); d->vm_obj.name = "io_fpga"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_c6msfc1_iofpga_shutdown; /* Set device properties */ dev_init(&d->dev); d->dev.name = "io_fpga"; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_c6msfc1_iofpga_access; d->dev.priv_data = d; /* Set console and AUX port notifying functions */ vm->vtty_con->priv_data = d; vm->vtty_aux->priv_data = d; vm->vtty_con->read_notifier = tty_con_input; vm->vtty_aux->read_notifier = tty_aux_input; /* Trigger periodically a dummy IRQ to flush buffers */ d->duart_irq_tid = ptask_add((ptask_callback)tty_trigger_dummy_irq, d,NULL); /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_c6msfc1_mpfpga.c000066400000000000000000000110671241034141600205110ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * MSFC1 Midplane FPGA. */ #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "nmc93cX6.h" #include "dev_c6msfc1.h" #define DEBUG_UNKNOWN 1 #define DEBUG_ACCESS 1 #define DEBUG_NET_IRQ 1 /* Midplane FPGA private data */ struct c6msfc1_mpfpga_data { vm_obj_t vm_obj; struct vdevice dev; c6msfc1_t *router; m_uint32_t irq_status; m_uint32_t intr_enable; }; /* Update network interrupt status */ static inline void dev_c6msfc1_mpfpga_net_update_irq(struct c6msfc1_mpfpga_data *d) { if (d->irq_status) { vm_set_irq(d->router->vm,C6MSFC1_NETIO_IRQ); } else { vm_clear_irq(d->router->vm,C6MSFC1_NETIO_IRQ); } } /* Trigger a Network IRQ for the specified slot/port */ void dev_c6msfc1_mpfpga_net_set_irq(struct c6msfc1_mpfpga_data *d, u_int slot,u_int port) { #if DEBUG_NET_IRQ vm_log(d->router->vm,"MP_FPGA","setting NetIRQ for slot %u port %u\n", slot,port); #endif d->irq_status |= 1 << slot; dev_c6msfc1_mpfpga_net_update_irq(d); } /* Clear a Network IRQ for the specified slot/port */ void dev_c6msfc1_mpfpga_net_clear_irq(struct c6msfc1_mpfpga_data *d, u_int slot,u_int port) { #if DEBUG_NET_IRQ vm_log(d->router->vm,"MP_FPGA","clearing NetIRQ for slot %u port %u\n", slot,port); #endif d->irq_status &= ~(1 << slot); dev_c6msfc1_mpfpga_net_update_irq(d); } /* * dev_c6msfc1_access() */ void *dev_c6msfc1_mpfpga_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct c6msfc1_mpfpga_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0x0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"MP_FPGA","reading reg 0x%x at pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"MP_FPGA", "writing reg 0x%x at pc=0x%llx, data=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),*data,op_size); } #endif switch(offset) { /* * Revision + Slot: just tell we're in slot 1 (and chip rev 2) * Other bits are unknown. */ case 0x00: if (op_type == MTS_READ) *data = 0x12; break; /* Interrupt Control ("sh msfc") - unknown */ case 0x08: if (op_type == MTS_READ) *data = 0x1c; break; /* Interrupt Enable ("sh msfc") */ case 0x10: if (op_type == MTS_READ) *data = d->intr_enable; else d->intr_enable = *data; break; /* * Read when a Network Interrupt is triggered. * Bit 0: EOBC * Bit 1: IBC */ case 0x18: case 0x1b: if (op_type == MTS_READ) *data = d->irq_status; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"MP_FPGA","read from unknown addr 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"MP_FPGA","write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx\n",offset,*data,cpu_get_pc(cpu)); } #endif } return NULL; } /* Shutdown the MP FPGA device */ static void dev_c6msfc1_mpfpga_shutdown(vm_instance_t *vm,struct c6msfc1_mpfpga_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* * dev_c6msfc1_mpfpga_init() */ int dev_c6msfc1_mpfpga_init(c6msfc1_t *router,m_uint64_t paddr,m_uint32_t len) { struct c6msfc1_mpfpga_data *d; /* Allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"MP_FPGA: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); d->router = router; vm_object_init(&d->vm_obj); d->vm_obj.name = "mp_fpga"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_c6msfc1_mpfpga_shutdown; /* Set device properties */ dev_init(&d->dev); d->dev.name = "mp_fpga"; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_c6msfc1_mpfpga_access; d->dev.priv_data = d; /* Map this device to the VM */ vm_bind_device(router->vm,&d->dev); vm_object_add(router->vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_c6msfc1_mpfpga.h000066400000000000000000000014121241034141600205070ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005-2007 Christophe Fillot (cf@utc.fr) * * Cisco MSFC1 Midplane FPGA. */ #ifndef __DEV_C6MSFC1_MPFPGA_H__ #define __DEV_C6MSFC1_MPFPGA_H__ /* Forward declaration for MP_FPGA private data */ struct c6msfc1_mpfpga_data; /* Trigger a Network IRQ for the specified slot/port */ void dev_c6msfc1_mpfpga_net_set_irq(struct c6msfc1_mpfpga_data *d, u_int slot,u_int port); /* Clear a Network IRQ for the specified slot/port */ void dev_c6msfc1_mpfpga_net_clear_irq(struct c6msfc1_mpfpga_data *d, u_int slot,u_int port); /* Create the MSFC1 Midplane FPGA */ int dev_c6msfc1_mpfpga_init(c6msfc1_t *router,m_uint64_t paddr,m_uint32_t len); #endif dynamips-0.2.14/common/dev_c6sup1.c000066400000000000000000000544551241034141600170460ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * Generic C6k-SUP1 routines and definitions (EEPROM,...). * * This is not a working platform! I only added it to play. */ #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "pci_io.h" #include "dev_gt.h" #include "cisco_eeprom.h" #include "dev_rom.h" #include "dev_am79c971.h" #include "dev_i8254x.h" #include "dev_c6sup1.h" #include "dev_c6sup1_mpfpga.h" #include "dev_vtty.h" #include "registry.h" #include "net.h" /* ====================================================================== */ /* EOBC - Ethernet Out of Band Channel */ /* ====================================================================== */ static int dev_c6sup1_eobc_init(vm_instance_t *vm,struct cisco_card *card) { struct am79c971_data *data; /* Create the AMD 79c970 chip */ data = dev_am79c971_init(vm,card->dev_name,AM79C971_TYPE_100BASE_TX, vm->pci_bus[0],6, 3/*c6sup1_net_irq_for_slot_port(0,0)*/); if (!data) return(-1); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove EOBC */ static int dev_c6sup1_eobc_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct am79c971_data *data = card->drv_info; dev_am79c971_remove(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c6sup1_eobc_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct am79c971_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); return(dev_am79c971_set_nio(d,nio)); } /* Unbind a Network IO descriptor */ static int dev_c6sup1_eobc_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct am79c971_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); dev_am79c971_unset_nio(d); return(0); } /* EOBC driver */ struct cisco_card_driver dev_c6sup1_eobc = { "C6SUP1_EOBC", 0, 0, dev_c6sup1_eobc_init, dev_c6sup1_eobc_shutdown, NULL, dev_c6sup1_eobc_set_nio, dev_c6sup1_eobc_unset_nio, NULL, }; /* ====================================================================== */ /* IBC - InBand Channel */ /* ====================================================================== */ static int dev_c6sup1_ibc_init(vm_instance_t *vm,struct cisco_card *card) { struct i8254x_data *data; /* Create the Intel Wiseman/Livengood chip */ data = dev_i8254x_init(vm,card->dev_name,0,vm->pci_bus[0],7, 2/*c6sup1_net_irq_for_slot_port(1,0)*/); if (!data) return(-1); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove EOBC */ static int dev_c6sup1_ibc_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct i8254x_data *data = card->drv_info; dev_i8254x_remove(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c6sup1_ibc_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct i8254x_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); return(dev_i8254x_set_nio(d,nio)); } /* Unbind a Network IO descriptor */ static int dev_c6sup1_ibc_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct i8254x_data *d = card->drv_info; if (!d || (port_id != 0)) return(-1); dev_i8254x_unset_nio(d); return(0); } /* IBC driver */ struct cisco_card_driver dev_c6sup1_ibc = { "C6SUP1_IBC", 0, 0, dev_c6sup1_ibc_init, dev_c6sup1_ibc_shutdown, NULL, dev_c6sup1_ibc_set_nio, dev_c6sup1_ibc_unset_nio, NULL, }; /* ======================================================================== */ /* Port Adapter Drivers */ /* ======================================================================== */ static struct cisco_card_driver *pa_drivers[] = { &dev_c6sup1_eobc, &dev_c6sup1_ibc, NULL, }; /* ======================================================================== */ /* C6SUP1 router instances */ /* ======================================================================== */ /* Initialize default parameters for a SUP1 */ static void c6sup1_init_defaults(c6sup1_t *router); /* Directly extract the configuration from the NVRAM device */ static int c6sup1_nvram_extract_config(vm_instance_t *vm,u_char **startup_config,size_t *startup_len,u_char **private_config,size_t *private_len) { u_char *base_ptr,*ios_ptr,*cfg_ptr,*end_ptr; m_uint32_t start,end,nvlen,clen; m_uint16_t magic1,magic2; struct vdevice *nvram_dev; m_uint64_t nvram_addr; off_t nvram_size; int fd; if ((nvram_dev = dev_get_by_name(vm,"nvram"))) dev_sync(nvram_dev); fd = vm_mmap_open_file(vm,"nvram",&base_ptr,&nvram_size); if (fd == -1) return(-1); nvram_addr = C6SUP1_NVRAM_ADDR; ios_ptr = base_ptr + vm->nvram_rom_space; end_ptr = base_ptr + nvram_size; if ((ios_ptr + 0x30) >= end_ptr) { vm_error(vm,"NVRAM file too small\n"); vm_mmap_close_file(fd,base_ptr,nvram_size); return(-1); } magic1 = ntohs(*PTR_ADJUST(m_uint16_t *,ios_ptr,0x06)); magic2 = ntohs(*PTR_ADJUST(m_uint16_t *,ios_ptr,0x08)); if ((magic1 != 0xF0A5) || (magic2 != 0xABCD)) { vm_error(vm,"unable to find IOS magic numbers (0x%x,0x%x)!\n", magic1,magic2); vm_mmap_close_file(fd,base_ptr,nvram_size); return(-1); } start = ntohl(*PTR_ADJUST(m_uint32_t *,ios_ptr,0x10)) + 1; end = ntohl(*PTR_ADJUST(m_uint32_t *,ios_ptr,0x14)); nvlen = ntohl(*PTR_ADJUST(m_uint32_t *,ios_ptr,0x18)); clen = end - start; if ((clen + 1) != nvlen) { vm_error(vm,"invalid configuration size (0x%x)\n",nvlen); vm_mmap_close_file(fd,base_ptr,nvram_size); return(-1); } cfg_ptr = base_ptr + (start - nvram_addr); if ((start < nvram_addr) || ((cfg_ptr + clen) > end_ptr)) { vm_error(vm,"NVRAM file too small\n"); vm_mmap_close_file(fd,base_ptr,nvram_size); return(-1); } if (startup_config != NULL) { if (!(*startup_config = malloc(clen+1))) { vm_error(vm,"unable to allocate config buffer (%u bytes)\n",clen); vm_mmap_close_file(fd,base_ptr,nvram_size); return(-1); } memcpy(*startup_config,cfg_ptr,clen); (*startup_config)[clen] = 0; } if (startup_len != NULL) { *startup_len = clen; } if (private_config != NULL) { *private_config = NULL; } if (private_len != NULL) { *private_len = 0; } vm_mmap_close_file(fd,base_ptr,nvram_size); return(0); } /* Directly push the IOS configuration to the NVRAM device */ static int c6sup1_nvram_push_config(vm_instance_t *vm,u_char *startup_config,size_t startup_len,u_char *private_config,size_t private_len) { u_char *base_ptr,*ios_ptr,*cfg_ptr; m_uint32_t cfg_addr,cfg_offset; m_uint32_t nvram_addr,cklen; m_uint16_t cksum; int fd; fd = vm_mmap_create_file(vm,"nvram",vm->nvram_size*1024,&base_ptr); if (fd == -1) return(-1); cfg_offset = 0x2c; ios_ptr = base_ptr + vm->nvram_rom_space; cfg_ptr = ios_ptr + cfg_offset; nvram_addr = C6SUP1_NVRAM_ADDR; cfg_addr = nvram_addr + vm->nvram_rom_space + cfg_offset; /* Write IOS tag, uncompressed config... */ *PTR_ADJUST(m_uint16_t *,ios_ptr,0x06) = htons(0xF0A5); *PTR_ADJUST(m_uint16_t *,ios_ptr,0x08) = htons(0xABCD); *PTR_ADJUST(m_uint16_t *,ios_ptr,0x0a) = htons(0x0001); *PTR_ADJUST(m_uint16_t *,ios_ptr,0x0c) = htons(0x0000); *PTR_ADJUST(m_uint16_t *,ios_ptr,0x0e) = htons(0x0000); /* Store file contents to NVRAM */ memcpy(cfg_ptr,startup_config,startup_len); /* Write config addresses + size */ *PTR_ADJUST(m_uint32_t *,ios_ptr,0x10) = htonl(cfg_addr); *PTR_ADJUST(m_uint32_t *,ios_ptr,0x14) = htonl(cfg_addr + startup_len); *PTR_ADJUST(m_uint32_t *,ios_ptr,0x18) = htonl(startup_len); /* Compute the checksum */ cklen = (vm->nvram_size*1024) - (vm->nvram_rom_space + 0x08); cksum = nvram_cksum((m_uint16_t *)(ios_ptr+0x08),cklen); *PTR_ADJUST(m_uint16_t *,ios_ptr,0x0c) = htons(cksum); vm_mmap_close_file(fd,base_ptr,vm->nvram_size*1024); return(0); } /* Get slot/port corresponding to specified network IRQ */ static inline void c6sup1_net_irq_get_slot_port(u_int irq,u_int *slot,u_int *port) { *slot = irq - C6SUP1_NETIO_IRQ_BASE; *port = 0; } /* Get network IRQ for specified slot/port */ u_int c6sup1_net_irq_for_slot_port(u_int slot,u_int port) { u_int irq; irq = C6SUP1_NETIO_IRQ_BASE + slot; return(irq); } /* Free specific hardware resources used by a SUP1 */ static void c6sup1_free_hw_ressources(c6sup1_t *router) { /* Shutdown all Port Adapters */ vm_slot_shutdown_all(router->vm); } /* Create a new router instance */ static int c6sup1_create_instance(vm_instance_t *vm) { c6sup1_t *router; if (!(router = malloc(sizeof(*router)))) { fprintf(stderr,"C6SUP1 '%s': Unable to create new instance!\n",vm->name); return(-1); } memset(router,0,sizeof(*router)); router->vm = vm; vm->hw_data = router; vm->elf_machine_id = C6SUP1_ELF_MACHINE_ID; c6sup1_init_defaults(router); return(0); } /* Free resources used by a router instance */ static int c6sup1_delete_instance(vm_instance_t *vm) { c6sup1_t *router = VM_C6SUP1(vm); int i; /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(FALSE); } } /* Remove NIO bindings */ for(i=0;ibp_eeprom[i]); for (i = 0; i < NMC93CX6_MAX_EEPROM_PER_GROUP; i++) cisco_eeprom_free(&router->sup_eeprom[i]); for (i = 0; i < C6SUP1_MAX_SLOTS; i++) cisco_eeprom_free(&router->slot_eeprom[i]); /* Free all resources used by VM */ vm_free(vm); /* Free the router structure */ free(router); return(TRUE); } /* Create the main PCI bus for a GT64010 based system */ static int c6sup1_init_gt64010(c6sup1_t *router) { vm_instance_t *vm = router->vm; if (!(vm->pci_bus[0] = pci_bus_create("PCI Bus 0",0))) { vm_error(vm,"unable to create PCI data.\n"); return(-1); } return(dev_gt64010_init(vm,"gt64010",C6SUP1_GT64K_ADDR,0x1000, C6SUP1_GT64K_IRQ)); } /* Initialize a SUP1 board */ static int c6sup1_init_hw(c6sup1_t *router) { vm_instance_t *vm = router->vm; /* Set the processor type: R5000 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R5000); /* Initialize the Galileo GT-64010 PCI controller */ if (c6sup1_init_gt64010(router) == -1) return(-1); /* Create PCI bus 1 */ vm->pci_bus_pool[24] = pci_bus_create("PCI Bus 1",-1); dev_dec21154_init(vm->pci_bus[0],1,vm->pci_bus_pool[24]); /* PCI IO space */ if (!(vm->pci_io_space = pci_io_data_init(vm,C6SUP1_PCI_IO_ADDR))) return(-1); /* AGRAM ? */ dev_ram_init(vm,"agram",FALSE,FALSE,NULL,FALSE,0x1EA00000,0x40000); /* Cirrus Logic PD6729 (PCI-to-PCMCIA host adapter) */ dev_clpd6729_init(vm,vm->pci_bus[0],5,vm->pci_io_space,0x402,0x403); return(0); } /* Show SUP1 hardware info */ void c6sup1_show_hardware(c6sup1_t *router) { vm_instance_t *vm = router->vm; printf("C6SUP1 instance '%s' (id %d):\n",vm->name,vm->instance_id); printf(" VM Status : %d\n",vm->status); printf(" RAM size : %u Mb\n",vm->ram_size); printf(" IOMEM size : %u Mb\n",vm->iomem_size); printf(" NVRAM size : %u Kb\n",vm->nvram_size); printf(" IOS image : %s\n\n",vm->ios_image); if (vm->debug_level > 0) { dev_show_list(vm); pci_dev_show_list(vm->pci_bus[0]); pci_dev_show_list(vm->pci_bus[1]); printf("\n"); } } /* Initialize default parameters for a SUP1 */ static void c6sup1_init_defaults(c6sup1_t *router) { vm_instance_t *vm = router->vm; n_eth_addr_t *m; m_uint16_t pid; /* Set platform slots characteristics */ vm->nr_slots = C6SUP1_MAX_PA_BAYS; vm->slots_type = CISCO_CARD_TYPE_PA; vm->slots_drivers = pa_drivers; pid = (m_uint16_t)getpid(); /* Generate a chassis MAC address based on the instance ID */ m = &router->mac_addr; m->eth_addr_byte[0] = vm_get_mac_addr_msb(vm); m->eth_addr_byte[1] = vm->instance_id & 0xFF; m->eth_addr_byte[2] = pid >> 8; m->eth_addr_byte[3] = pid & 0xFF; m->eth_addr_byte[4] = 0x00; m->eth_addr_byte[5] = 0x00; /* Default slot: 1 */ router->sup_slot = 1; c6sup1_init_eeprom_groups(router); /* Create EOBC and IBC interfaces */ vm_slot_add_binding(vm,"C6SUP1_EOBC",0,0); vm_slot_add_binding(vm,"C6SUP1_IBC",1,0); vm->ram_mmap = C6SUP1_DEFAULT_RAM_MMAP; vm->ram_size = C6SUP1_DEFAULT_RAM_SIZE; vm->rom_size = C6SUP1_DEFAULT_ROM_SIZE; vm->nvram_size = C6SUP1_DEFAULT_NVRAM_SIZE; vm->iomem_size = 0; vm->conf_reg_setup = C6SUP1_DEFAULT_CONF_REG; vm->clock_divisor = C6SUP1_DEFAULT_CLOCK_DIV; vm->nvram_rom_space = C6SUP1_NVRAM_ROM_RES_SIZE; } /* Run the checklist */ static int c6sup1_checklist(c6sup1_t *router) { struct vm_instance *vm = router->vm; int res = 0; res += vm_object_check(vm,"ram"); res += vm_object_check(vm,"rom"); res += vm_object_check(vm,"nvram"); res += vm_object_check(vm,"zero"); if (res < 0) vm_error(vm,"incomplete initialization (no memory?)\n"); return(res); } /* Initialize Port Adapters */ static int c6sup1_init_platform_pa(c6sup1_t *router) { return(vm_slot_init_all(router->vm)); } /* Initialize the SUP1 Platform */ static int c6sup1_init_platform(c6sup1_t *router) { struct vm_instance *vm = router->vm; cpu_mips_t *cpu0; cpu_gen_t *gen0; vm_obj_t *obj; /* Copy config register setup into "active" config register */ vm->conf_reg = vm->conf_reg_setup; /* Create Console and AUX ports */ vm_init_vtty(vm); /* Create a CPU group */ vm->cpu_group = cpu_group_create("System CPU"); /* Initialize the virtual MIPS processor */ if (!(gen0 = cpu_create(vm,CPU_TYPE_MIPS64,0))) { vm_error(vm,"unable to create CPU0!\n"); return(-1); } cpu0 = CPU_MIPS64(gen0); /* Add this CPU to the system CPU group */ cpu_group_add(vm->cpu_group,gen0); vm->boot_cpu = gen0; /* Initialize the IRQ routing vectors */ vm->set_irq = mips64_vm_set_irq; vm->clear_irq = mips64_vm_clear_irq; /* Mark the Network IO interrupt as high priority */ cpu0->irq_idle_preempt[C6SUP1_NETIO_IRQ] = TRUE; cpu0->irq_idle_preempt[C6SUP1_GT64K_IRQ] = TRUE; /* Copy some parameters from VM to CPU0 (idle PC, ...) */ cpu0->idle_pc = vm->idle_pc; if (vm->timer_irq_check_itv) cpu0->timer_irq_check_itv = vm->timer_irq_check_itv; /* * On the SUP1, bit 33 of physical addresses is used to bypass L2 cache. * We clear it systematically. */ cpu0->addr_bus_mask = C6SUP1_ADDR_BUS_MASK; /* Remote emulator control */ dev_remote_control_init(vm,0x16000000,0x1000); /* Bootflash (8 Mb) */ dev_bootflash_init(vm,"bootflash","c7200-bootflash-8mb", C6SUP1_BOOTFLASH_ADDR); /* NVRAM and calendar */ dev_nvram_init(vm,"nvram",C6SUP1_NVRAM_ADDR, vm->nvram_size*1024,&vm->conf_reg); /* Bit-bucket zone */ dev_zero_init(vm,"zero",C6SUP1_BITBUCKET_ADDR,0xc00000); /* Initialize the NPE board */ if (c6sup1_init_hw(router) == -1) return(-1); /* Initialize RAM */ vm_ram_init(vm,0x00000000ULL); /* Initialize ROM */ if (!vm->rom_filename) { /* use embedded ROM */ dev_rom_init(vm,"rom",C6SUP1_ROM_ADDR,vm->rom_size*1048576, mips64_microcode,mips64_microcode_len); } else { /* use alternate ROM */ dev_ram_init(vm,"rom",TRUE,TRUE,NULL,FALSE, C6SUP1_ROM_ADDR,vm->rom_size*1048576); } /* Byte swapping */ dev_bswap_init(vm,"mem_bswap",C6SUP1_BSWAP_ADDR,1024*1048576,0x00000000ULL); /* PCI IO space */ if (!(vm->pci_io_space = pci_io_data_init(vm,C6SUP1_PCI_IO_ADDR))) return(-1); /* Initialize the Port Adapters */ if (c6sup1_init_platform_pa(router) == -1) return(-1); /* Verify the check list */ if (c6sup1_checklist(router) == -1) return(-1); /* Midplane FPGA */ if (dev_c6sup1_mpfpga_init(router,C6SUP1_MPFPGA_ADDR,0x40000) == -1) return(-1); if (!(obj = vm_object_find(router->vm,"mp_fpga"))) return(-1); router->mpfpga_data = obj->data; /* IO FPGA */ if (dev_c6sup1_iofpga_init(router,C6SUP1_IOFPGA_ADDR,0x1000) == -1) return(-1); /* Show device list */ c6sup1_show_hardware(router); return(0); } /* Boot the IOS image */ static int c6sup1_boot_ios(c6sup1_t *router) { vm_instance_t *vm = router->vm; cpu_mips_t *cpu; if (!vm->boot_cpu) return(-1); /* Suspend CPU activity since we will restart directly from ROM */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Reset the boot CPU */ cpu = CPU_MIPS64(vm->boot_cpu); mips64_reset(cpu); /* Load IOS image */ if (mips64_load_elf_image(cpu,vm->ios_image, (vm->ghost_status == VM_GHOST_RAM_USE), &vm->ios_entry_point) < 0) { vm_error(vm,"failed to load Cisco IOS image '%s'.\n",vm->ios_image); return(-1); } /* Launch the simulation */ printf("\nC6SUP1 '%s': starting simulation (CPU0 PC=0x%llx), " "JIT %sabled.\n", vm->name,cpu->pc,vm->jit_use ? "en":"dis"); vm_log(vm,"C6SUP1_BOOT", "starting instance (CPU0 PC=0x%llx,idle_pc=0x%llx,JIT %s)\n", cpu->pc,cpu->idle_pc,vm->jit_use ? "on":"off"); /* Start main CPU */ if (vm->ghost_status != VM_GHOST_RAM_GENERATE) { vm->status = VM_STATUS_RUNNING; cpu_start(vm->boot_cpu); } else { vm->status = VM_STATUS_SHUTDOWN; } return(0); } /* Set an IRQ */ static void c6sup1_set_irq(vm_instance_t *vm,u_int irq) { _maybe_used c6sup1_t *router = VM_C6SUP1(vm); cpu_mips_t *cpu0 = CPU_MIPS64(vm->boot_cpu); u_int slot,port; switch(irq) { case 0 ... 7: mips64_set_irq(cpu0,irq); if (cpu0->irq_idle_preempt[irq]) cpu_idle_break_wait(cpu0->gen); break; case C6SUP1_NETIO_IRQ_BASE ... C6SUP1_NETIO_IRQ_END: c6sup1_net_irq_get_slot_port(irq,&slot,&port); //dev_c6sup1_mpfpga_net_set_irq(router->mpfpga_data,slot,port); break; } } /* Clear an IRQ */ static void c6sup1_clear_irq(vm_instance_t *vm,u_int irq) { _maybe_used c6sup1_t *router = VM_C6SUP1(vm); cpu_mips_t *cpu0 = CPU_MIPS64(vm->boot_cpu); u_int slot,port; switch(irq) { case 0 ... 7: mips64_clear_irq(cpu0,irq); break; case C6SUP1_NETIO_IRQ_BASE ... C6SUP1_NETIO_IRQ_END: c6sup1_net_irq_get_slot_port(irq,&slot,&port); //dev_c6sup1_mpfpga_net_clear_irq(router->mpfpga_data,slot,port); break; } } /* Initialize a SUP1 instance */ static int c6sup1_init_instance(vm_instance_t *vm) { c6sup1_t *router = VM_C6SUP1(vm); m_uint32_t rom_entry_point; cpu_mips_t *cpu0; /* Initialize the SUP1 platform */ if (c6sup1_init_platform(router) == -1) { vm_error(vm,"unable to initialize the platform hardware.\n"); return(-1); } /* IRQ routing */ vm->set_irq = c6sup1_set_irq; vm->clear_irq = c6sup1_clear_irq; /* Load IOS configuration files */ if (vm->ios_startup_config != NULL || vm->ios_private_config != NULL) { vm_nvram_push_config(vm,vm->ios_startup_config,vm->ios_private_config); vm->conf_reg &= ~0x40; } /* Load ROM (ELF image or embedded) */ cpu0 = CPU_MIPS64(vm->boot_cpu); rom_entry_point = (m_uint32_t)MIPS_ROM_PC; if ((vm->rom_filename != NULL) && (mips64_load_elf_image(cpu0,vm->rom_filename,0,&rom_entry_point) < 0)) { vm_error(vm,"unable to load alternate ROM '%s', " "fallback to embedded ROM.\n\n",vm->rom_filename); vm->rom_filename = NULL; } /* Load symbol file */ if (vm->sym_filename) { mips64_sym_load_file(cpu0,vm->sym_filename); cpu0->sym_trace = 1; } return(c6sup1_boot_ios(router)); } /* Stop a SUP1 instance */ static int c6sup1_stop_instance(vm_instance_t *vm) { printf("\nC6SUP1 '%s': stopping simulation.\n",vm->name); vm_log(vm,"C6SUP1_STOP","stopping simulation.\n"); /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } } /* Free resources that were used during execution to emulate hardware */ c6sup1_free_hw_ressources(VM_C6SUP1(vm)); vm_hardware_shutdown(vm); return(0); } /* Get MAC address MSB */ static u_int c6sup1_get_mac_addr_msb(void) { return(0xD0); } /* Show specific CLI options */ static void c6sup1_cli_show_options(vm_instance_t *vm) { printf(" -s : Bind a Network IO interface to a " "Port Adapter\n"); } /* Platform definition */ static vm_platform_t c6sup1_platform = { "c6sup1", "C6SUP1", "C6SUP1", c6sup1_create_instance, c6sup1_delete_instance, c6sup1_init_instance, c6sup1_stop_instance, NULL, NULL, c6sup1_nvram_extract_config, c6sup1_nvram_push_config, c6sup1_get_mac_addr_msb, NULL, NULL, c6sup1_cli_show_options, NULL, }; /* Register the C6-SUP1 platform */ int c6sup1_platform_register(void) { return(vm_platform_register(&c6sup1_platform)); } dynamips-0.2.14/common/dev_c6sup1.h000066400000000000000000000077471241034141600170550ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * Generic Cisco C6k-SUP1 routines and definitions (EEPROM,...). */ #ifndef __DEV_C6SUP1_H__ #define __DEV_C6SUP1_H__ #include #include "utils.h" #include "net.h" #include "device.h" #include "pci_dev.h" #include "nmc93cX6.h" #include "dev_ds1620.h" #include "net_io.h" #include "vm.h" /* Default MSFC1 parameters */ #define C6SUP1_DEFAULT_RAM_SIZE 128 #define C6SUP1_DEFAULT_ROM_SIZE 4 #define C6SUP1_DEFAULT_NVRAM_SIZE 512 #define C6SUP1_DEFAULT_CONF_REG 0x2102 #define C6SUP1_DEFAULT_CLOCK_DIV 4 #define C6SUP1_DEFAULT_RAM_MMAP 1 /* EOBC + IBC */ #define C6SUP1_MAX_PA_BAYS 2 /* Maximum slots on 6513 */ #define C6SUP1_MAX_SLOTS 13 /* MSFC1 Timer IRQ (virtual) */ #define C6SUP1_VTIMER_IRQ 0 /* MSFC1 DUART Interrupt */ #define C6SUP1_DUART_IRQ 5 /* MSFC1 Network I/O Interrupt */ #define C6SUP1_NETIO_IRQ 2 /* MSFC1 PA Management Interrupt handler */ #define C6SUP1_PA_MGMT_IRQ 3 /* MSFC1 GT64k DMA/Timer Interrupt */ #define C6SUP1_GT64K_IRQ 4 /* MSFC1 Error/OIR Interrupt */ #define C6SUP1_OIR_IRQ 6 /* Network IRQ */ #define C6SUP1_NETIO_IRQ_BASE 32 #define C6SUP1_NETIO_IRQ_END \ (C6SUP1_NETIO_IRQ_BASE + C6SUP1_MAX_PA_BAYS - 1) /* MSFC1 base ram limit (256 Mb) */ #define C6SUP1_BASE_RAM_LIMIT 256 /* MSFC1 common device addresses */ #define C6SUP1_GT64K_ADDR 0x14000000ULL #define C6SUP1_GT64K_SEC_ADDR 0x15000000ULL #define C6SUP1_BOOTFLASH_ADDR 0x1a000000ULL #define C6SUP1_NVRAM_ADDR 0x1e000000ULL #define C6SUP1_IOFPGA_ADDR 0x1e840000ULL #define C6SUP1_MPFPGA_ADDR 0x1e880000ULL #define C6SUP1_BITBUCKET_ADDR 0x1f000000ULL #define C6SUP1_ROM_ADDR 0x1fc00000ULL #define C6SUP1_IOMEM_ADDR 0x20000000ULL #define C6SUP1_SRAM_ADDR 0x4b000000ULL #define C6SUP1_BSWAP_ADDR 0xc0000000ULL #define C6SUP1_PCI_IO_ADDR 0x100000000ULL /* SRAM size */ #define C6SUP1_SRAM_SIZE (4096*1024) /* Reserved space for ROM in NVRAM */ #define C6SUP1_NVRAM_ROM_RES_SIZE 2048 /* MSFC1 physical address bus mask: keep only the lower 33 bits */ #define C6SUP1_ADDR_BUS_MASK 0x1ffffffffULL /* MSFC1 ELF Platform ID */ #define C6SUP1_ELF_MACHINE_ID 0x19 /* 2 temperature sensors in a SUP1: chassis inlet and oulet */ #define C6SUP1_TEMP_SENSORS 2 #define VM_C6SUP1(vm) ((c6sup1_t *)vm->hw_data) /* MSFC1 router */ typedef struct c6sup1_router c6sup1_t; /* MSFC1 router */ struct c6sup1_router { /* Chassis MAC address */ n_eth_addr_t mac_addr; /* Associated VM instance */ vm_instance_t *vm; /* Midplane FPGA */ struct c6sup1_mpfpga_data *mpfpga_data; /* Backplane EEPROM data */ struct cisco_eeprom bp_eeprom[NMC93CX6_MAX_EEPROM_PER_GROUP]; /* Supervisor EEPROM data */ struct cisco_eeprom sup_eeprom[NMC93CX6_MAX_EEPROM_PER_GROUP]; /* Slots EEPROM data */ struct cisco_eeprom slot_eeprom[C6SUP1_MAX_SLOTS]; /* Backplane, Supervisor and Slot EEPROM groups */ struct nmc93cX6_group bp_eeprom_group; struct nmc93cX6_group sup_eeprom_group; struct nmc93cX6_group slot_eeprom_group; /* Temperature sensors */ struct ds1620_data ds1620_sensors[C6SUP1_TEMP_SENSORS]; /* Slot of this supervisor */ u_int sup_slot; }; /* Initialize EEPROM groups */ void c6sup1_init_eeprom_groups(c6sup1_t *router); /* Get network IRQ for specified slot/port */ u_int c6sup1_net_irq_for_slot_port(u_int slot,u_int port); /* Show the list of available PA drivers */ void c6sup1_pa_show_drivers(void); /* Set chassis MAC address */ int c6sup1_midplane_set_mac_addr(c6sup1_t *router,char *mac_addr); /* Show MSFC1 hardware info */ void c6sup1_show_hardware(c6sup1_t *router); /* dev_c6sup1_iofpga_init() */ int dev_c6sup1_iofpga_init(c6sup1_t *router,m_uint64_t paddr,m_uint32_t len); /* dev_mpfpga_init() */ int dev_c6sup1_mpfpga_init(c6sup1_t *router,m_uint64_t paddr,m_uint32_t len); /* Register the c6sup1 platform */ int c6sup1_platform_register(void); #endif dynamips-0.2.14/common/dev_c6sup1_iofpga.c000066400000000000000000000260341241034141600203630ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * Cisco C6k-SUP1 I/O FPGA: * - Simulates a NMC93C56 Serial EEPROM. * - Simulates a DALLAS DS1620 for Temperature Sensors. * - Simulates console and AUX ports (SCN2681). * * This is very similar to c7200 platform. */ #include #include #include #include #include #include #include #include #include "ptask.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_vtty.h" #include "nmc93cX6.h" #include "dev_ds1620.h" #include "dev_c6sup1.h" /* Debugging flags */ #define DEBUG_UNKNOWN 1 #define DEBUG_ACCESS 0 #define DEBUG_LED 0 #define DEBUG_IO_CTL 0 #define DEBUG_ENVM 0 /* DUART RX/TX status (SRA/SRB) */ #define DUART_RX_READY 0x01 #define DUART_TX_READY 0x04 /* DUART RX/TX Interrupt Status/Mask */ #define DUART_TXRDYA 0x01 #define DUART_RXRDYA 0x02 #define DUART_TXRDYB 0x10 #define DUART_RXRDYB 0x20 /* Pack the NVRAM */ #define NVRAM_PACKED 0x04 /* Temperature: 22°C as default value */ #define C6SUP1_DEFAULT_TEMP 22 #define DS1620_CHIP(d,id) (&(d)->router->ds1620_sensors[(id)]) /* 2 temperature sensors in a MSFC1: chassis inlet and oulet */ #define C6SUP1_TEMP_SENSORS 2 #define C6SUP1_DEFAULT_TEMP 22 /* default temperature: 22°C */ /* IO FPGA structure */ struct iofpga_data { vm_obj_t vm_obj; struct vdevice dev; c6sup1_t *router; /* Lock test */ pthread_mutex_t lock; /* Periodic task to trigger dummy DUART IRQ */ ptask_id_t duart_irq_tid; /* DUART & Console Management */ u_int duart_isr,duart_imr,duart_irq_seq; /* IO control register */ u_int io_ctrl_reg; /* Temperature Control */ u_int temp_cfg_reg[C6SUP1_TEMP_SENSORS]; u_int temp_deg_reg[C6SUP1_TEMP_SENSORS]; u_int temp_clk_low; u_int temp_cmd; u_int temp_cmd_pos; u_int temp_data; u_int temp_data_pos; /* Voltages */ u_int mux; }; #define IOFPGA_LOCK(d) pthread_mutex_lock(&(d)->lock) #define IOFPGA_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) /* Console port input */ static void tty_con_input(vtty_t *vtty) { struct iofpga_data *d = vtty->priv_data; IOFPGA_LOCK(d); if (d->duart_imr & DUART_RXRDYA) { d->duart_isr |= DUART_RXRDYA; vm_set_irq(d->router->vm,C6SUP1_DUART_IRQ); } IOFPGA_UNLOCK(d); } /* AUX port input */ static void tty_aux_input(vtty_t *vtty) { struct iofpga_data *d = vtty->priv_data; IOFPGA_LOCK(d); if (d->duart_imr & DUART_RXRDYB) { d->duart_isr |= DUART_RXRDYB; vm_set_irq(d->router->vm,C6SUP1_DUART_IRQ); } IOFPGA_UNLOCK(d); } /* IRQ trickery for Console and AUX ports */ static int tty_trigger_dummy_irq(struct iofpga_data *d,void *arg) { u_int mask; IOFPGA_LOCK(d); d->duart_irq_seq++; if (d->duart_irq_seq == 2) { mask = DUART_TXRDYA|DUART_TXRDYB; if (d->duart_imr & mask) { d->duart_isr |= DUART_TXRDYA|DUART_TXRDYB; vm_set_irq(d->router->vm,C6SUP1_DUART_IRQ); } d->duart_irq_seq = 0; } IOFPGA_UNLOCK(d); return(0); } /* * dev_c6sup1_iofpga_access() */ void *dev_c6sup1_iofpga_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct iofpga_data *d = dev->priv_data; vm_instance_t *vm = d->router->vm; u_char odata; int i; if (op_type == MTS_READ) *data = 0x0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA","reading reg 0x%x at pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"IO_FPGA","writing reg 0x%x at pc=0x%llx, data=0x%llx\n", offset,cpu_get_pc(cpu),*data); } #endif IOFPGA_LOCK(d); switch(offset) { /* I/O control register */ case 0x204: if (op_type == MTS_WRITE) { #if DEBUG_IO_CTL vm_log(vm,"IO_FPGA","setting value 0x%llx in io_ctrl_reg\n",*data); #endif d->io_ctrl_reg = *data; } else { *data = d->io_ctrl_reg; *data |= NVRAM_PACKED; /* Packed NVRAM */ } break; /* Watchdog */ case 0x234: break; /* * FPGA release/presence ? Flash SIMM size: * 0x0001: 2048K Flash (2 banks) * 0x0504: 8192K Flash (2 banks) * 0x0704: 16384K Flash (2 banks) * 0x0904: 32768K Flash (2 banks) * 0x0B04: 65536K Flash (2 banks) * 0x2001: 1024K Flash (1 bank) * 0x2504: 4096K Flash (1 bank) * 0x2704: 8192K Flash (1 bank) * 0x2904: 16384K Flash (1 bank) * 0x2B04: 32768K Flash (1 bank) * * Number of Flash SIMM banks + size. * Touching some lower bits causes problems with environmental monitor. * * It is displayed by command "sh bootflash: chips" */ case 0x23c: if (op_type == MTS_READ) *data = 0x2704; break; /* LEDs */ case 0x244: #if DEBUG_LED vm_log(vm,"IO_FPGA","LED register is now 0x%x (0x%x)\n", *data,(~*data) & 0x0F); #endif break; /* ==== DUART SCN2681 (console/aux) ==== */ case 0x404: /* Mode Register A (MRA) */ break; case 0x40c: /* Status Register A (SRA) */ if (op_type == MTS_READ) { odata = 0; if (vtty_is_char_avail(vm->vtty_con)) odata |= DUART_RX_READY; odata |= DUART_TX_READY; vm_clear_irq(vm,C6SUP1_DUART_IRQ); *data = odata; } break; case 0x414: /* Command Register A (CRA) */ /* Disable TX = High */ if ((op_type == MTS_WRITE) && (*data & 0x8)) { vm->vtty_con->managed_flush = TRUE; vtty_flush(vm->vtty_con); } break; case 0x41c: /* RX/TX Holding Register A (RHRA/THRA) */ if (op_type == MTS_WRITE) { vtty_put_char(vm->vtty_con,(char)*data); d->duart_isr &= ~DUART_TXRDYA; } else { *data = vtty_get_char(vm->vtty_con); d->duart_isr &= ~DUART_RXRDYA; } break; case 0x424: /* WRITE: Aux Control Register (ACR) */ break; case 0x42c: /* Interrupt Status/Mask Register (ISR/IMR) */ if (op_type == MTS_WRITE) { d->duart_imr = *data; } else *data = d->duart_isr; break; case 0x434: /* Counter/Timer Upper Value (CTU) */ case 0x43c: /* Counter/Timer Lower Value (CTL) */ case 0x444: /* Mode Register B (MRB) */ break; case 0x44c: /* Status Register B (SRB) */ if (op_type == MTS_READ) { odata = 0; if (vtty_is_char_avail(vm->vtty_aux)) odata |= DUART_RX_READY; odata |= DUART_TX_READY; //vm_clear_irq(vm,C6SUP1_DUART_IRQ); *data = odata; } break; case 0x454: /* Command Register B (CRB) */ /* Disable TX = High */ if ((op_type == MTS_WRITE) && (*data & 0x8)) { vm->vtty_aux->managed_flush = TRUE; vtty_flush(vm->vtty_aux); } break; case 0x45c: /* RX/TX Holding Register B (RHRB/THRB) */ if (op_type == MTS_WRITE) { vtty_put_char(vm->vtty_aux,(char)*data); d->duart_isr &= ~DUART_TXRDYA; } else { *data = vtty_get_char(vm->vtty_aux); d->duart_isr &= ~DUART_RXRDYB; } break; case 0x46c: /* WRITE: Output Port Configuration Register (OPCR) */ case 0x474: /* READ: Start Counter Command; */ /* WRITE: Set Output Port Bits Command */ case 0x47c: /* WRITE: Reset Output Port Bits Command */ break; /* ==== DS 1620 (temp sensors) ==== */ case 0x20c: /* Temperature Control */ if (op_type == MTS_WRITE) { for(i=0;i> i) & 0x01); ds1620_set_clk_bit(DS1620_CHIP(d,i),(*data >> 4) & 0x01); } } break; case 0x214: /* Temperature data write */ if (op_type == MTS_WRITE) { d->mux = *data; for(i=0;ivtty_con->read_notifier = NULL; vm->vtty_aux->read_notifier = NULL; IOFPGA_UNLOCK(d); /* Remove the dummy IRQ periodic task */ ptask_remove(d->duart_irq_tid); /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* * dev_c6sup1_iofpga_init() */ int dev_c6sup1_iofpga_init(c6sup1_t *router,m_uint64_t paddr,m_uint32_t len) { vm_instance_t *vm = router->vm; struct iofpga_data *d; u_int i; /* Allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"IO_FPGA: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); pthread_mutex_init(&d->lock,NULL); d->router = router; for(i=0;ivm_obj); d->vm_obj.name = "io_fpga"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_c6sup1_iofpga_shutdown; /* Set device properties */ dev_init(&d->dev); d->dev.name = "io_fpga"; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_c6sup1_iofpga_access; d->dev.priv_data = d; /* Set console and AUX port notifying functions */ vm->vtty_con->priv_data = d; vm->vtty_aux->priv_data = d; vm->vtty_con->read_notifier = tty_con_input; vm->vtty_aux->read_notifier = tty_aux_input; /* Trigger periodically a dummy IRQ to flush buffers */ d->duart_irq_tid = ptask_add((ptask_callback)tty_trigger_dummy_irq, d,NULL); /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_c6sup1_mpfpga.c000066400000000000000000000337251241034141600203750ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * C6k-Sup1a Midplane FPGA. */ #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "nmc93cX6.h" #include "dev_c6sup1.h" #define DEBUG_UNKNOWN 1 #define DEBUG_ACCESS 1 #define DEBUG_NET_IRQ 1 /* * Function 0xX000: * bit 0: 0:present, 1:absent. * bit 1: power ok (?) */ #define SLOT_NOT_PRESENT 0x01 #define SLOT_POWER_OK 0x02 /* * Function 0xX200: requires bit 3 to be set to avoid error about power * convertor failure. */ #define SLOT_POWER_CONVERTOR 0x08 /* Midplane FPGA private data */ struct c6sup1_mpfpga_data { vm_obj_t vm_obj; struct vdevice dev; c6sup1_t *router; m_uint32_t irq_status; m_uint32_t intr_enable; /* Slot/function selector */ u_int slot_sel; /* Slot status (up/down) */ u_int slot_status[C6SUP1_MAX_SLOTS]; }; /* === Definitions for "Backplane" EEPROM (Chassis Clock, VTT, ...) ======= */ #define EEPROM_BP_DOUT 0 /* reg 0x3c */ #define EEPROM_BP_DIN 0 /* reg 0x20 */ #define EEPROM_BP_CLK 1 /* Chip select (CS) bits */ #define EEPROM_BP_CS_CHASSIS 3 /* Chassis (6509,...) */ #define EEPROM_BP_CS_CHASSIS2 4 /* Chassis redundant EEPROM ? */ #define EEPROM_BP_CS_PS1 5 /* Power Supply #1 */ #define EEPROM_BP_CS_PS2 6 /* Power Supply #2 */ #define EEPROM_BP_CS_CLK1 7 /* Clock card #1 */ #define EEPROM_BP_CS_CLK2 8 /* Clock card #2 */ #define EEPROM_BP_CS_VTT1 9 /* VTT #1 */ #define EEPROM_BP_CS_VTT2 10 /* VTT #2 */ #define EEPROM_BP_CS_VTT3 11 /* VTT #3 */ static const struct nmc93cX6_eeprom_def eeprom_bp_def_chassis = { EEPROM_BP_CLK, EEPROM_BP_CS_CHASSIS, EEPROM_BP_DIN, EEPROM_BP_DOUT, }; static const struct nmc93cX6_eeprom_def eeprom_bp_def_chassis2 = { EEPROM_BP_CLK, EEPROM_BP_CS_CHASSIS2, EEPROM_BP_DIN, EEPROM_BP_DOUT, }; static const struct nmc93cX6_eeprom_def eeprom_bp_def_ps1 = { EEPROM_BP_CLK, EEPROM_BP_CS_PS1, EEPROM_BP_DIN, EEPROM_BP_DOUT, }; static const struct nmc93cX6_eeprom_def eeprom_bp_def_ps2 = { EEPROM_BP_CLK, EEPROM_BP_CS_PS2, EEPROM_BP_DIN, EEPROM_BP_DOUT, }; static const struct nmc93cX6_eeprom_def eeprom_bp_def_clk1 = { EEPROM_BP_CLK, EEPROM_BP_CS_CLK1, EEPROM_BP_DIN, EEPROM_BP_DOUT, }; static const struct nmc93cX6_eeprom_def eeprom_bp_def_clk2 = { EEPROM_BP_CLK, EEPROM_BP_CS_CLK2, EEPROM_BP_DIN, EEPROM_BP_DOUT, }; static const struct nmc93cX6_eeprom_def eeprom_bp_def_vtt1 = { EEPROM_BP_CLK, EEPROM_BP_CS_VTT1, EEPROM_BP_DIN, EEPROM_BP_DOUT, }; static const struct nmc93cX6_eeprom_def eeprom_bp_def_vtt2 = { EEPROM_BP_CLK, EEPROM_BP_CS_VTT2, EEPROM_BP_DIN, EEPROM_BP_DOUT, }; static const struct nmc93cX6_eeprom_def eeprom_bp_def_vtt3 = { EEPROM_BP_CLK, EEPROM_BP_CS_VTT3, EEPROM_BP_DIN, EEPROM_BP_DOUT, }; /* Backplane EEPROMs */ static const struct nmc93cX6_group eeprom_bp_group = { EEPROM_TYPE_NMC93C56, 9, 0, EEPROM_DORD_REVERSED, EEPROM_DOUT_KEEP, EEPROM_DEBUG_DISABLED, "Backplane EEPROMs", { &eeprom_bp_def_chassis, &eeprom_bp_def_chassis2, &eeprom_bp_def_ps1, &eeprom_bp_def_ps2, &eeprom_bp_def_clk1, &eeprom_bp_def_clk2, &eeprom_bp_def_vtt1, &eeprom_bp_def_vtt2, &eeprom_bp_def_vtt3, }, }; /* === Definitions for "Supervisor" EEPROMs (Sup1A,PFC/EARL) ============== */ #define EEPROM_SUP_DOUT 0 /* XXX */ #define EEPROM_SUP_DIN 2 #define EEPROM_SUP_CLK 1 #define EEPROM_SUP_CS 3 #define EEPROM_EARL_DOUT 2 /* XXX */ #define EEPROM_EARL_DIN 9 #define EEPROM_EARL_CLK 10 #define EEPROM_EARL_CS 8 static const struct nmc93cX6_eeprom_def eeprom_sup_def = { EEPROM_SUP_CLK, EEPROM_SUP_CS, EEPROM_SUP_DIN, EEPROM_SUP_DOUT, }; static const struct nmc93cX6_eeprom_def eeprom_earl_def = { EEPROM_EARL_CLK, EEPROM_EARL_CS, EEPROM_EARL_DIN, EEPROM_EARL_DOUT, }; /* Supervisor EEPROMs */ static const struct nmc93cX6_group eeprom_sup_group = { EEPROM_TYPE_NMC93C56, 2, 0, EEPROM_DORD_REVERSED, EEPROM_DOUT_KEEP, EEPROM_DEBUG_DISABLED, "Supervisor EEPROMs", { &eeprom_sup_def, &eeprom_earl_def }, }; /* === Definitions for "Slot" EEPROM ====================================== */ #define EEPROM_SLOT_DOUT 0 /* reg 0x4c */ #define EEPROM_SLOT_DIN 0 /* reg 0x48 */ #define EEPROM_SLOT_CLK 1 #define EEPROM_SLOT_CS 3 static const struct nmc93cX6_eeprom_def eeprom_slot_def = { EEPROM_SLOT_CLK, EEPROM_SLOT_CS, EEPROM_SLOT_DIN, EEPROM_SLOT_DOUT, }; static const struct nmc93cX6_group eeprom_slot_group = { EEPROM_TYPE_NMC93C56, 1, 0, EEPROM_DORD_REVERSED, EEPROM_DOUT_KEEP, EEPROM_DEBUG_DISABLED, "Slot EEPROMs", { &eeprom_slot_def }, }; /* ------------------------------------------------------------------------ */ /* Update network interrupt status */ static inline void dev_c6sup1_mpfpga_net_update_irq(struct c6sup1_mpfpga_data *d) { if (d->irq_status) { vm_set_irq(d->router->vm,C6SUP1_NETIO_IRQ); } else { vm_clear_irq(d->router->vm,C6SUP1_NETIO_IRQ); } } /* Trigger a Network IRQ for the specified slot/port */ void dev_c6sup1_mpfpga_net_set_irq(struct c6sup1_mpfpga_data *d, u_int slot,u_int port) { #if DEBUG_NET_IRQ vm_log(d->router->vm,"MP_FPGA","setting NetIRQ for slot %u port %u\n", slot,port); #endif d->irq_status |= 1 << slot; dev_c6sup1_mpfpga_net_update_irq(d); } /* Clear a Network IRQ for the specified slot/port */ void dev_c6sup1_mpfpga_net_clear_irq(struct c6sup1_mpfpga_data *d, u_int slot,u_int port) { #if DEBUG_NET_IRQ vm_log(d->router->vm,"MP_FPGA","clearing NetIRQ for slot %u port %u\n", slot,port); #endif d->irq_status &= ~(1 << slot); dev_c6sup1_mpfpga_net_update_irq(d); } /* * dev_c6sup1_access() */ void *dev_c6sup1_mpfpga_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct c6sup1_mpfpga_data *d = dev->priv_data; struct nmc93cX6_group *grp; u_int i,slot,func; if (op_type == MTS_READ) *data = 0xFFFFFFFF; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"MP_FPGA", "reading reg 0x%x at pc=0x%llx, ra=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),CPU_MIPS64(cpu)->gpr[MIPS_GPR_RA], op_size); } else { cpu_log(cpu,"MP_FPGA", "writing reg 0x%x at pc=0x%llx, ra=0x%llx " "data=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),CPU_MIPS64(cpu)->gpr[MIPS_GPR_RA], *data,op_size); } #endif switch(offset) { case 0x0c: case 0x14: case 0x1c: if (op_type == MTS_READ) *data = 0; break; case 0x18: if (op_type == MTS_READ) *data = 0x8000; break; /* 0x3E80 is written regularly here (watchdog ?) */ case 0x20004: break; /* Backplane EEPROMs */ case 0x000020: if (op_type == MTS_WRITE) { //m_log("EEPROM","write access(BP): data=0x%4.4llx\n",*data); nmc93cX6_write(&d->router->bp_eeprom_group,(u_int)(*data)); } break; /* Supervisor EEPROMs */ case 0x000024: if (op_type == MTS_WRITE) { //m_log("EEPROM","write access(SUP): data=0x%4.4llx\n",*data); nmc93cX6_write(&d->router->sup_eeprom_group,(u_int)(*data)); } break; /* Backplane/Supervisor EEPROMs read access */ case 0x00003C: if (op_type == MTS_READ) { *data = 0x0000; /* Backplane EEPROMs */ grp = &d->router->bp_eeprom_group; for(i=0;inr_eeprom;i++) { if (nmc93cX6_is_active(grp,i)) *data |= nmc93cX6_get_dout(grp,i); } /* Supervisor EEPROMs */ grp = &d->router->sup_eeprom_group; for(i=0;inr_eeprom;i++) { if (nmc93cX6_is_active(grp,i)) if (nmc93cX6_get_dout(grp,i)) *data |= 0xFFFF; //nmc93cX6_get_dout(grp,i); } } break; /* Slot selection */ case 0x000044: if (op_type == MTS_WRITE) { d->slot_sel = *data; slot = (d->slot_sel & 0xF000) >> 12; func = (d->slot_sel & 0x0F00) >> 8; if (slot <= C6SUP1_MAX_SLOTS) { grp = &d->router->slot_eeprom_group; grp->eeprom[0] = &d->router->slot_eeprom[slot-1]; /* mark the slot as powered on */ if (func == 0x02) { //printf("Marking slot %u as powered ON\n",slot); d->slot_status[slot-1] = TRUE; } } } break; /* Slot EEPROM write */ case 0x000048: if (op_type == MTS_WRITE) nmc93cX6_write(&d->router->slot_eeprom_group,(u_int)(*data)); break; /* Slot EEPROM read */ case 0x00004c: if (op_type == MTS_READ) { grp = &d->router->slot_eeprom_group; slot = (d->slot_sel & 0xF000) >> 12; func = (d->slot_sel & 0x0F00) >> 8; *data = 0; switch(func) { /* Presence + power ? */ case 0x00: *data = SLOT_NOT_PRESENT; if (grp->eeprom[0] && grp->eeprom[0]->data) { *data = 0; /* The SUP slot is always powered */ if (d->slot_status[slot-1] || (slot == d->router->sup_slot)) *data |= SLOT_POWER_OK; } break; case 0x01: *data = 0x0001; if (grp->eeprom[0] && grp->eeprom[0]->data) { *data = 0x0000; } break; /* Power-related */ case 0x02: *data = SLOT_POWER_CONVERTOR; break; /* EEPROM reading */ case 0x05: if (nmc93cX6_is_active(grp,0)) *data |= nmc93cX6_get_dout(grp,0); break; default: cpu_log(cpu,"MP_FPGA","slot control: unknown func 0x%2.2x\n", func); } } break; /* Slot Identification */ case 0x000004: if (op_type == MTS_READ) *data = (d->router->sup_slot << 8) | 0x80; break; /* Unknown: EARL interrupt ? */ /* 00:00:27: %CPU_MONITOR-3-PEER_EXCEPTION: CPU_MONITOR peer has failed due to exception , resetting [0/1] */ case 0x000050: if (op_type == MTS_READ) *data = 0; //0xFFFF; break; case 0x000074: if (op_type == MTS_READ) *data = 0x0000; //0x3FFF; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"MP_FPGA", "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"MP_FPGA", "write to unknown addr 0x%x, value=0x%llx, pc=0x%llx " "(op_size=%u)\n",offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* Shutdown the MP FPGA device */ static void dev_c6sup1_mpfpga_shutdown(vm_instance_t *vm,struct c6sup1_mpfpga_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* Initialize EEPROM groups */ void c6sup1_init_eeprom_groups(c6sup1_t *router) { struct nmc93cX6_group *grp; struct cisco_eeprom *buf; router->bp_eeprom_group = eeprom_bp_group; router->sup_eeprom_group = eeprom_sup_group; router->slot_eeprom_group = eeprom_slot_group; /* XXX */ buf = &router->bp_eeprom[0]; grp = &router->bp_eeprom_group; grp->eeprom[0] = buf; cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-CHASSIS-6509")); grp->eeprom[2] = buf; cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-POWER-1000W")); grp->eeprom[3] = buf; cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-POWER-1000W")); grp->eeprom[6] = buf; cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-VTT")); grp->eeprom[7] = buf; cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-VTT")); grp->eeprom[8] = buf; cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-VTT")); buf = &router->sup_eeprom[0]; grp = &router->sup_eeprom_group; grp->eeprom[0] = buf; cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-SUP-SUP1A-2GE")); grp->eeprom[1] = buf; cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-EARL-PFC1")); cisco_eeprom_copy(&router->slot_eeprom[0], cisco_eeprom_find_c6k("C6K-SUP-SUP1A-2GE")); cisco_eeprom_copy(&router->slot_eeprom[8], cisco_eeprom_find_c6k("C6K-LC-WS-X6248")); } /* * dev_c6sup1_mpfpga_init() */ int dev_c6sup1_mpfpga_init(c6sup1_t *router,m_uint64_t paddr,m_uint32_t len) { struct c6sup1_mpfpga_data *d; /* Allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"MP_FPGA: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); d->router = router; vm_object_init(&d->vm_obj); d->vm_obj.name = "mp_fpga"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_c6sup1_mpfpga_shutdown; /* Set device properties */ dev_init(&d->dev); d->dev.name = "mp_fpga"; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_c6sup1_mpfpga_access; d->dev.priv_data = d; /* Map this device to the VM */ vm_bind_device(router->vm,&d->dev); vm_object_add(router->vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_c6sup1_mpfpga.h000066400000000000000000000014011241034141600203640ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005-2007 Christophe Fillot (cf@utc.fr) * * Cisco C6k-SUP1 Midplane FPGA. */ #ifndef __DEV_C6SUP1_MPFPGA_H__ #define __DEV_C6SUP1_MPFPGA_H__ /* Forward declaration for MP_FPGA private data */ struct c6sup1_mpfpga_data; /* Trigger a Network IRQ for the specified slot/port */ void dev_c6sup1_mpfpga_net_set_irq(struct c6sup1_mpfpga_data *d, u_int slot,u_int port); /* Clear a Network IRQ for the specified slot/port */ void dev_c6sup1_mpfpga_net_clear_irq(struct c6sup1_mpfpga_data *d, u_int slot,u_int port); /* Create the SUP1 Midplane FPGA */ int dev_c6sup1_mpfpga_init(c6sup1_t *router,m_uint64_t paddr,m_uint32_t len); #endif dynamips-0.2.14/common/dev_c7200.c000066400000000000000000002040471241034141600164520ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * Patched by Jeremy Grossmann for the GNS3 project (www.gns3.net) * * Generic Cisco 7200 routines and definitions (EEPROM,...). */ #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "ppc32_mem.h" #include "device.h" #include "pci_io.h" #include "dev_gt.h" #include "dev_mv64460.h" #include "cisco_eeprom.h" #include "dev_rom.h" #include "dev_c7200.h" #include "dev_c7200_mpfpga.h" #include "dev_vtty.h" #include "registry.h" #include "net.h" #include "fs_nvram.h" /* ======================================================================== */ /* CPU EEPROM definitions */ /* ======================================================================== */ /* NPE-100 */ static m_uint16_t eeprom_cpu_npe100_data[16] = { 0x0135, 0x0203, 0xff10, 0x45C5, 0x4906, 0x0004, 0x0000, 0x0000, 0x6000, 0x0000, 0x9901, 0x0600, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* NPE-150 */ static m_uint16_t eeprom_cpu_npe150_data[16] = { 0x0115, 0x0203, 0xff10, 0x45C5, 0x4906, 0x0004, 0x0000, 0x0000, 0x6000, 0x0000, 0x9901, 0x0600, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* NPE-175 */ static m_uint16_t eeprom_cpu_npe175_data[16] = { 0x01C2, 0x0203, 0xff10, 0x45C5, 0x4906, 0x0004, 0x0000, 0x0000, 0x6000, 0x0000, 0x9901, 0x0600, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* NPE-200 */ static m_uint16_t eeprom_cpu_npe200_data[16] = { 0x0169, 0x0200, 0xff10, 0x45C5, 0x4909, 0x8902, 0x0000, 0x0000, 0x6800, 0x0000, 0x9710, 0x2200, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* NPE-225 (same as NPE-175) */ static m_uint16_t eeprom_cpu_npe225_data[16] = { 0x01C2, 0x0203, 0xff10, 0x45C5, 0x4906, 0x0004, 0x0000, 0x0000, 0x6000, 0x0000, 0x9901, 0x0600, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* NPE-300 */ static m_uint16_t eeprom_cpu_npe300_data[16] = { 0x01AE, 0x0402, 0xff10, 0x45C5, 0x490D, 0x5108, 0x0000, 0x0000, 0x5000, 0x0000, 0x0012, 0x1000, 0x0000, 0xFFFF, 0xFFFF, 0xFF00, }; /* NPE-400 */ static m_uint16_t eeprom_cpu_npe400_data[64] = { 0x04FF, 0x4001, 0xF841, 0x0100, 0xC046, 0x0320, 0x001F, 0xC802, 0x8249, 0x14BC, 0x0242, 0x4230, 0xC18B, 0x3131, 0x3131, 0x3131, 0x3131, 0x0000, 0x0004, 0x0002, 0x0285, 0x1C0F, 0xF602, 0xCB87, 0x4E50, 0x452D, 0x3430, 0x3080, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* NPE-G1 */ static m_uint16_t eeprom_cpu_npeg1_data[64] = { 0x04FF, 0x4003, 0x5B41, 0x0200, 0xC046, 0x0320, 0x0049, 0xD00B, 0x8249, 0x1B4C, 0x0B42, 0x4130, 0xC18B, 0x3131, 0x3131, 0x3131, 0x3131, 0x0000, 0x0004, 0x0002, 0x0985, 0x1C13, 0xDA09, 0xCB86, 0x4E50, 0x452D, 0x4731, 0x8000, 0x0000, 0x00FF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; /* NPE-G2 */ static m_uint16_t eeprom_cpu_npeg2_data[64] = { 0x04FF, 0x4004, 0xCA41, 0x0201, 0x8744, 0x19BC, 0x0182, 0x4928, 0x5901, 0x42FF, 0xFFC1, 0x8B43, 0x534A, 0x3039, 0x3435, 0x3239, 0x3237, 0x0400, 0x0201, 0x851C, 0x1DA2, 0x01CB, 0x864E, 0x5045, 0x2D47, 0x3280, 0x0000, 0x0000, 0x8956, 0x3031, 0x2DFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x15FF, }; /* * CPU EEPROM array. */ static struct cisco_eeprom c7200_cpu_eeprom[] = { { "npe-100", eeprom_cpu_npe100_data, sizeof(eeprom_cpu_npe100_data)/2 }, { "npe-150", eeprom_cpu_npe150_data, sizeof(eeprom_cpu_npe150_data)/2 }, { "npe-175", eeprom_cpu_npe175_data, sizeof(eeprom_cpu_npe175_data)/2 }, { "npe-200", eeprom_cpu_npe200_data, sizeof(eeprom_cpu_npe200_data)/2 }, { "npe-225", eeprom_cpu_npe225_data, sizeof(eeprom_cpu_npe225_data)/2 }, { "npe-300", eeprom_cpu_npe300_data, sizeof(eeprom_cpu_npe300_data)/2 }, { "npe-400", eeprom_cpu_npe400_data, sizeof(eeprom_cpu_npe400_data)/2 }, { "npe-g1" , eeprom_cpu_npeg1_data , sizeof(eeprom_cpu_npeg1_data)/2 }, { "npe-g2" , eeprom_cpu_npeg2_data , sizeof(eeprom_cpu_npeg2_data)/2 }, { NULL, NULL, 0 }, }; /* ======================================================================== */ /* Midplane EEPROM definitions */ /* ======================================================================== */ /* Standard Midplane EEPROM contents */ static m_uint16_t eeprom_midplane_data[32] = { 0x0106, 0x0101, 0xff10, 0x45C5, 0x4906, 0x0303, 0xFFFF, 0xFFFF, 0xFFFF, 0x0400, 0x0000, 0x0000, 0x4C09, 0x10B0, 0xFFFF, 0x00FF, 0x0000, 0x0000, 0x6335, 0x8B28, 0x631D, 0x0000, 0x608E, 0x6D1C, 0x62BB, 0x0000, 0x6335, 0x8B28, 0x0000, 0x0000, 0x6335, 0x8B28, }; /* VXR Midplane EEPROM contents */ static m_uint16_t eeprom_vxr_midplane_data[32] = { 0x0106, 0x0201, 0xff10, 0x45C5, 0x4906, 0x0303, 0xFFFF, 0xFFFF, 0xFFFF, 0x0400, 0x0000, 0x0000, 0x4C09, 0x10B0, 0xFFFF, 0x00FF, 0x0000, 0x0000, 0x6335, 0x8B28, 0x631D, 0x0000, 0x608E, 0x6D1C, 0x62BB, 0x0000, 0x6335, 0x8B28, 0x0000, 0x0000, 0x6335, 0x8B28, }; /* * Midplane EEPROM array. */ static struct cisco_eeprom c7200_midplane_eeprom[] = { { "std", eeprom_midplane_data, sizeof(eeprom_midplane_data)/2 }, { "vxr", eeprom_vxr_midplane_data, sizeof(eeprom_vxr_midplane_data)/2 }, { NULL, NULL, 0 }, }; /* ======================================================================== */ /* PEM EEPROM definitions (for NPE-175 and NPE-225) */ /* ======================================================================== */ /* NPE-175 */ static m_uint16_t eeprom_pem_npe175_data[16] = { 0x01C3, 0x0100, 0xFFFF, 0xFFFF, 0x490D, 0x8A04, 0x0000, 0x0000, 0x5000, 0x0000, 0x9906, 0x0400, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, }; /* NPE-225 */ static m_uint16_t eeprom_pem_npe225_data[16] = { 0x01D5, 0x0100, 0xFFFF, 0xFFFF, 0x490D, 0x8A04, 0x0000, 0x0000, 0x5000, 0x0000, 0x9906, 0x0400, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, }; /* * PEM EEPROM array. */ static struct cisco_eeprom c7200_pem_eeprom[] = { { "npe-175", eeprom_pem_npe175_data, sizeof(eeprom_pem_npe175_data)/2 }, { "npe-225", eeprom_pem_npe225_data, sizeof(eeprom_pem_npe225_data)/2 }, { NULL, NULL, 0 }, }; /* ======================================================================== */ /* Port Adapter Drivers */ /* ======================================================================== */ static struct cisco_card_driver *pa_drivers[] = { &dev_c7200_npeg2_driver, &dev_c7200_iocard_fe_driver, &dev_c7200_iocard_2fe_driver, &dev_c7200_iocard_ge_e_driver, &dev_c7200_pa_fe_tx_driver, &dev_c7200_pa_2fe_tx_driver, &dev_c7200_pa_ge_driver, &dev_c7200_pa_4e_driver, &dev_c7200_pa_8e_driver, &dev_c7200_pa_4t_driver, &dev_c7200_pa_8t_driver, &dev_c7200_pa_a1_driver, &dev_c7200_pa_pos_oc3_driver, &dev_c7200_pa_4b_driver, &dev_c7200_pa_mc8te1_driver, &dev_c7200_jcpa_driver, NULL, }; /* ======================================================================== */ /* NPE Drivers */ /* ======================================================================== */ #define DECLARE_NPE(type) \ int (c7200_init_##type)(c7200_t *router) DECLARE_NPE(npe100); DECLARE_NPE(npe150); DECLARE_NPE(npe175); DECLARE_NPE(npe200); DECLARE_NPE(npe225); DECLARE_NPE(npe300); DECLARE_NPE(npe400); DECLARE_NPE(npeg1); DECLARE_NPE(npeg2); static struct c7200_npe_driver npe_drivers[] = { { "npe-100" , C7200_NPE_FAMILY_MIPS, c7200_init_npe100, 256, 1, C7200_NVRAM_ADDR, TRUE, 0, 5, 0, 6 }, { "npe-150" , C7200_NPE_FAMILY_MIPS, c7200_init_npe150, 256, 1, C7200_NVRAM_ADDR, TRUE, 0, 5, 0, 6 }, { "npe-175" , C7200_NPE_FAMILY_MIPS, c7200_init_npe175, 256, 1, C7200_NVRAM_ADDR, TRUE, 2, 16, 1, 0 }, { "npe-200" , C7200_NPE_FAMILY_MIPS, c7200_init_npe200, 256, 1, C7200_NVRAM_ADDR, TRUE, 0, 5, 0, 6 }, { "npe-225" , C7200_NPE_FAMILY_MIPS, c7200_init_npe225, 256, 1, C7200_NVRAM_ADDR, TRUE, 2, 16, 1, 0 }, { "npe-300" , C7200_NPE_FAMILY_MIPS, c7200_init_npe300, 256, 1, C7200_NVRAM_ADDR, TRUE, 2, 16, 1, 0 }, { "npe-400" , C7200_NPE_FAMILY_MIPS, c7200_init_npe400, 512, 1, C7200_NVRAM_ADDR, TRUE, 2, 16, 1, 0 }, { "npe-g1" , C7200_NPE_FAMILY_MIPS, c7200_init_npeg1, 1024, 0, C7200_G1_NVRAM_ADDR, FALSE, 17, 16, 16, 0 }, { "npe-g2" , C7200_NPE_FAMILY_PPC , c7200_init_npeg2, 1024, 0, C7200_G2_NVRAM_ADDR, FALSE, 17, 16, 16, 0 }, { NULL, -1, NULL, -1, -1, 0, -1, -1, -1, -1 }, }; /* ======================================================================== */ /* Cisco 7200 router instances */ /* ======================================================================== */ /* Initialize default parameters for a C7200 */ static void c7200_init_defaults(c7200_t *router); /* Directly extract the configuration from the NVRAM device */ static int c7200_nvram_extract_config(vm_instance_t *vm,u_char **startup_config,size_t *startup_len,u_char **private_config,size_t *private_len) { int ret; ret = generic_nvram_extract_config(vm, "nvram", vm->nvram_rom_space, 0, VM_C7200(vm)->npe_driver->nvram_addr + vm->nvram_rom_space, FS_NVRAM_FORMAT_ABSOLUTE, startup_config, startup_len, private_config, private_len); return(ret); } /* Directly push the IOS configuration to the NVRAM device */ static int c7200_nvram_push_config(vm_instance_t *vm,u_char *startup_config,size_t startup_len,u_char *private_config,size_t private_len) { int ret; ret = generic_nvram_push_config(vm, "nvram", vm->nvram_size*1024, vm->nvram_rom_space, 0, VM_C7200(vm)->npe_driver->nvram_addr + vm->nvram_rom_space, FS_NVRAM_FORMAT_ABSOLUTE, startup_config, startup_len, private_config, private_len); return(ret); } /* Get an EEPROM for a given NPE model */ static const struct cisco_eeprom *c7200_get_cpu_eeprom(char *npe_name) { return(cisco_eeprom_find(c7200_cpu_eeprom,npe_name)); } /* Get an EEPROM for a given midplane model */ static const struct cisco_eeprom * c7200_get_midplane_eeprom(char *midplane_name) { return(cisco_eeprom_find(c7200_midplane_eeprom,midplane_name)); } /* Get a PEM EEPROM for a given NPE model */ static const struct cisco_eeprom *c7200_get_pem_eeprom(char *npe_name) { return(cisco_eeprom_find(c7200_pem_eeprom,npe_name)); } /* Set the base MAC address of the chassis */ static int c7200_burn_mac_addr(c7200_t *router,n_eth_addr_t *addr) { m_uint8_t eeprom_ver; /* Read EEPROM format version */ cisco_eeprom_get_byte(&router->mp_eeprom,0,&eeprom_ver); if (eeprom_ver != 1) { vm_error(router->vm,"c7200_burn_mac_addr: unable to handle " "EEPROM version %u\n",eeprom_ver); return(-1); } cisco_eeprom_set_region(&router->mp_eeprom,12,addr->eth_addr_byte,6); return(0); } /* Free specific hardware resources used by C7200 */ static void c7200_free_hw_ressources(c7200_t *router) { /* Shutdown all Port Adapters */ vm_slot_shutdown_all(router->vm); /* Inactivate the PCMCIA bus */ router->pcmcia_bus = NULL; /* Remove the hidden I/O bridge */ if (router->io_pci_bridge != NULL) { pci_bridge_remove(router->io_pci_bridge); router->io_pci_bridge = NULL; } } /* Create a new router instance */ static int c7200_create_instance(vm_instance_t *vm) { c7200_t *router; if (!(router = malloc(sizeof(*router)))) { fprintf(stderr,"C7200 '%s': Unable to create new instance!\n",vm->name); return(-1); } memset(router,0,sizeof(*router)); router->vm = vm; router->npe400_ram_size = C7200_DEFAULT_RAM_SIZE; vm->hw_data = router; vm->elf_machine_id = C7200_ELF_MACHINE_ID; c7200_init_defaults(router); return(0); } /* Free resources used by a router instance */ static int c7200_delete_instance(vm_instance_t *vm) { c7200_t *router = VM_C7200(vm); int i; /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(FALSE); } } /* Remove NIO bindings */ for(i=0;inr_slots;i++) vm_slot_remove_all_nio_bindings(vm,i); /* Free specific HW resources */ c7200_free_hw_ressources(router); /* Free EEPROMs */ cisco_eeprom_free(&router->cpu_eeprom); cisco_eeprom_free(&router->mp_eeprom); cisco_eeprom_free(&router->pem_eeprom); /* Free all resources used by VM */ vm_free(vm); /* Free the router structure */ free(router); return(TRUE); } /* Save configuration of a C7200 instance */ static void c7200_save_config(vm_instance_t *vm,FILE *fd) { c7200_t *router = VM_C7200(vm); fprintf(fd,"c7200 set_npe %s %s\n",vm->name,router->npe_driver->npe_type); fprintf(fd,"c7200 set_midplane %s %s\n\n",vm->name,router->midplane_type); } /* Returns TRUE if the specified card in slot 0 is an I/O card */ int c7200_slot0_iocard_present(c7200_t *router) { struct cisco_eeprom *eeprom; m_uint8_t eeprom_ver,data[2]; m_uint16_t card_type; size_t offset; if (!(eeprom = router->pa_eeprom_g1.eeprom[0])) return(FALSE); /* Read EEPROM format version */ card_type = 0; cisco_eeprom_get_byte(eeprom,0,&eeprom_ver); switch(eeprom_ver) { case 1: cisco_eeprom_get_byte(eeprom,0,&data[0]); cisco_eeprom_get_byte(eeprom,1,&data[1]); card_type = ((m_uint16_t)data[0] << 8) | data[1]; break; case 4: if (!cisco_eeprom_v4_find_field(eeprom,0x40,&offset)) { cisco_eeprom_get_byte(eeprom,offset,&data[0]); cisco_eeprom_get_byte(eeprom,offset+1,&data[1]); card_type = ((m_uint16_t)data[0] << 8) | data[1]; } break; } /* jacket card is not an i/o card */ if (card_type == 0x0511) return(FALSE); /* by default, if there is something, consider it as an i/o card */ return(TRUE); } /* Set EEPROM for the specified slot */ int c7200_set_slot_eeprom(c7200_t *router,u_int slot, struct cisco_eeprom *eeprom) { if (slot >= C7200_MAX_PA_BAYS) return(-1); switch(slot) { /* Group 1: bays 0, 1, 3, 4 */ case 0: router->pa_eeprom_g1.eeprom[0] = eeprom; break; case 1: router->pa_eeprom_g1.eeprom[1] = eeprom; break; case 3: router->pa_eeprom_g1.eeprom[2] = eeprom; break; case 4: router->pa_eeprom_g1.eeprom[3] = eeprom; break; /* Group 2: bays 2, 5, 6 */ case 2: router->pa_eeprom_g2.eeprom[0] = eeprom; break; case 5: router->pa_eeprom_g2.eeprom[1] = eeprom; break; case 6: router->pa_eeprom_g2.eeprom[2] = eeprom; break; /* Group 3: bay 7 */ case 7: router->pa_eeprom_g3.eeprom[0] = eeprom; break; } return(0); } /* Network IRQ distribution */ struct net_irq_distrib { u_int reg; u_int offset; }; static struct net_irq_distrib net_irq_dist[C7200_MAX_PA_BAYS] = { { 0, 0 }, /* Slot 0: reg 0x10, 0x000000XX */ { 0, 8 }, /* Slot 1: reg 0x10, 0x0000XX00 */ { 1, 8 }, /* Slot 2: reg 0x18, 0x0000XX00 */ { 0, 24 }, /* Slot 3: reg 0x10, 0xXX000000 */ { 0, 16 }, /* Slot 4: reg 0x10, 0x00XX0000 */ { 1, 24 }, /* Slot 5: reg 0x18, 0xXX000000 */ { 1, 16 }, /* Slot 6: reg 0x18, 0x00XX0000 */ { 2, 24 }, /* Slot 7: reg 0x294, 0xXX000000 */ }; /* Get register offset for the specified slot */ u_int dev_c7200_net_get_reg_offset(u_int slot) { return(net_irq_dist[slot].offset); } /* Update network interrupt status */ void dev_c7200_net_update_irq(c7200_t *router) { int i,status = 0; for(i=0;i<3;i++) status |= router->net_irq_status[i] & router->net_irq_mask[i]; if (status) { vm_set_irq(router->vm,C7200_NETIO_IRQ); } else { vm_clear_irq(router->vm,C7200_NETIO_IRQ); } } /* Trigger a Network IRQ for the specified slot/port */ void dev_c7200_net_set_irq(c7200_t *router,u_int slot,u_int port) { struct net_irq_distrib *irq_dist; #if DEBUG_NET_IRQ vm_log(router->vm,"C7200","setting NetIRQ for slot %u port %u\n", slot,port); #endif irq_dist = &net_irq_dist[slot]; router->net_irq_status[irq_dist->reg] |= 1 << (irq_dist->offset + port); dev_c7200_net_update_irq(router); } /* Clear a Network IRQ for the specified slot/port */ void dev_c7200_net_clear_irq(c7200_t *router,u_int slot,u_int port) { struct net_irq_distrib *irq_dist; #if DEBUG_NET_IRQ vm_log(router->vm,"C7200","clearing NetIRQ for slot %u port %u\n", slot,port); #endif irq_dist = &net_irq_dist[slot]; router->net_irq_status[irq_dist->reg] &= ~(1 << (irq_dist->offset + port)); dev_c7200_net_update_irq(router); } /* Get slot/port corresponding to specified network IRQ */ static inline void c7200_net_irq_get_slot_port(u_int irq,u_int *slot,u_int *port) { irq -= C7200_NETIO_IRQ_BASE; *port = irq & C7200_NETIO_IRQ_PORT_MASK; *slot = irq >> C7200_NETIO_IRQ_PORT_BITS; } /* Get network IRQ for specified slot/port */ u_int c7200_net_irq_for_slot_port(u_int slot,u_int port) { u_int irq; irq = (slot << C7200_NETIO_IRQ_PORT_BITS) + port; irq += C7200_NETIO_IRQ_BASE; return(irq); } /* Set NPE eeprom definition */ static int c7200_npe_set_eeprom(c7200_t *router) { const struct cisco_eeprom *eeprom; if (!(eeprom = c7200_get_cpu_eeprom(router->npe_driver->npe_type))) { vm_error(router->vm,"unknown NPE \"%s\" (internal error)!\n", router->npe_driver->npe_type); return(-1); } if (cisco_eeprom_copy(&router->cpu_eeprom,eeprom) == -1) { vm_error(router->vm,"unable to set NPE EEPROM.\n"); return(-1); } return(0); } /* Set PEM eeprom definition */ static int c7200_pem_set_eeprom(c7200_t *router) { const struct cisco_eeprom *eeprom; if (!(eeprom = c7200_get_pem_eeprom(router->npe_driver->npe_type))) { vm_error(router->vm,"no PEM EEPROM found for NPE type \"%s\"!\n", router->npe_driver->npe_type); return(-1); } if (cisco_eeprom_copy(&router->pem_eeprom,eeprom) == -1) { vm_error(router->vm,"unable to set PEM EEPROM.\n"); return(-1); } return(0); } /* Get an NPE driver */ struct c7200_npe_driver *c7200_npe_get_driver(char *npe_type) { int i; for(i=0;npe_drivers[i].npe_type;i++) if (!strcmp(npe_drivers[i].npe_type,npe_type)) return(&npe_drivers[i]); return NULL; } /* Set the NPE type */ int c7200_npe_set_type(c7200_t *router,char *npe_type) { struct c7200_npe_driver *driver; if (router->vm->status == VM_STATUS_RUNNING) { vm_error(router->vm,"unable to change NPE type when online.\n"); return(-1); } if (!(driver = c7200_npe_get_driver(npe_type))) { vm_error(router->vm,"unknown NPE type '%s'.\n",npe_type); return(-1); } router->npe_driver = driver; if (c7200_npe_set_eeprom(router) == -1) { vm_error(router->vm,"unable to find NPE '%s' EEPROM!\n", router->npe_driver->npe_type); return(-1); } #if 0 /* FIXME - for a later release */ /* Use a C7200-IO-FE by default in slot 0 if an I/O card is required */ if (driver->iocard_required) { vm_slot_add_binding(router->vm,"C7200-IO-FE",0,0); vm_slot_set_flag(router->vm,0,0,CISCO_CARD_FLAG_OVERRIDE); } #endif c7200_refresh_systemid(router); return(0); } /* Show the list of available NPE drivers */ static void c7200_npe_show_drivers(void) { int i; printf("Available C7200 NPE drivers:\n"); for(i=0;npe_drivers[i].npe_type;i++) { printf(" * %s %s\n", npe_drivers[i].npe_type, !npe_drivers[i].supported ? "(NOT WORKING)" : ""); } printf("\n"); } /* Set Midplane type */ int c7200_midplane_set_type(c7200_t *router,char *midplane_type) { const struct cisco_eeprom *eeprom; m_uint8_t version; if (router->vm->status == VM_STATUS_RUNNING) { vm_error(router->vm,"unable to change Midplane type when online.\n"); return(-1); } /* Set EEPROM */ if (!(eeprom = c7200_get_midplane_eeprom(midplane_type))) { vm_error(router->vm,"unknown Midplane \"%s\"!\n",midplane_type); return(-1); } /* Copy the midplane EEPROM */ if (cisco_eeprom_copy(&router->mp_eeprom,eeprom) == -1) { vm_error(router->vm,"unable to set midplane EEPROM.\n"); return(-1); } /* Set the chassis base MAC address */ c7200_burn_mac_addr(router,&router->mac_addr); /* Get the midplane version */ cisco_eeprom_get_byte(&router->mp_eeprom,2,&version); router->midplane_version = version; router->midplane_type = eeprom->name; c7200_refresh_systemid(router); return(0); } /* Set the system id or processor board id in the eeprom */ int c7200_set_system_id(c7200_t *router,char *id) { // 11 characters is enough. Array is 20 long strncpy(router->board_id,id,13); // Make sure it is null terminated router->board_id[13] = 0x00; c7200_refresh_systemid(router); return 0; } int c7200_refresh_systemid(c7200_t *router) { //fprintf(stderr,"Starting mp dump\n"); //cisco_eeprom_dump(&router->mp_eeprom); //fprintf(stderr,"Starting cpu dump\n"); //cisco_eeprom_dump(&router->cpu_eeprom); if (router->board_id[0] == 0x00) return(0); m_uint8_t buf[11]; if ( (!strcmp("npe-100",router->npe_driver->npe_type)) ||(!strcmp("npe-140",router->npe_driver->npe_type)) ||(!strcmp("npe-175",router->npe_driver->npe_type)) ||(!strcmp("npe-200",router->npe_driver->npe_type)) ||(!strcmp("npe-225",router->npe_driver->npe_type)) ||(!strcmp("npe-300",router->npe_driver->npe_type))) { //parse_board_id(buf,"4279256517",4); parse_board_id(buf,router->board_id,4); cisco_eeprom_set_region(&router->mp_eeprom ,4,buf,4); cisco_eeprom_set_region(&router->cpu_eeprom,4,buf,4); //fprintf(stderr,"Starting post mp dump\n"); //cisco_eeprom_dump(&router->mp_eeprom); //fprintf(stderr,"Starting post cpu dump\n"); //cisco_eeprom_dump(&router->cpu_eeprom); return (0); } return (-1); } /* Set chassis MAC address */ int c7200_midplane_set_mac_addr(c7200_t *router,char *mac_addr) { if (parse_mac_addr(&router->mac_addr,mac_addr) == -1) { vm_error(router->vm,"unable to parse MAC address '%s'.\n",mac_addr); return(-1); } /* Set the chassis base MAC address */ c7200_burn_mac_addr(router,&router->mac_addr); return(0); } /* Create the main PCI bus for a GT64010 based system */ static int c7200_init_gt64010(c7200_t *router) { vm_instance_t *vm = router->vm; if (!(vm->pci_bus[0] = pci_bus_create("MB0/MB1/MB2",0))) { vm_error(vm,"unable to create PCI data.\n"); return(-1); } return(dev_gt64010_init(vm,"gt64010",C7200_GT64K_ADDR,0x1000, C7200_GT64K_IRQ)); } /* Create the two main PCI busses for a GT64120 based system */ static int c7200_init_gt64120(c7200_t *router) { vm_instance_t *vm = router->vm; vm->pci_bus[0] = pci_bus_create("MB0/MB1",0); vm->pci_bus[1] = pci_bus_create("MB2",0); if (!vm->pci_bus[0] || !vm->pci_bus[1]) { vm_error(vm,"unable to create PCI data.\n"); return(-1); } return(dev_gt64120_init(vm,"gt64120",C7200_GT64K_ADDR,0x1000, C7200_GT64K_IRQ)); } /* Create the two main PCI busses for a dual GT64120 system */ static int c7200_init_dual_gt64120(c7200_t *router) { vm_instance_t *vm = router->vm; vm->pci_bus[0] = pci_bus_create("MB0/MB1",0); vm->pci_bus[1] = pci_bus_create("MB2",0); if (!vm->pci_bus[0] || !vm->pci_bus[1]) { vm_error(vm,"unable to create PCI data.\n",vm->name); return(-1); } /* Initialize the first GT64120 at 0x14000000 */ if (dev_gt64120_init(vm,"gt64120(1)",C7200_GT64K_ADDR,0x1000, C7200_GT64K_IRQ) == -1) return(-1); /* Initialize the second GT64120 at 0x15000000 */ if (dev_gt64120_init(vm,"gt64120(2)",C7200_GT64K_SEC_ADDR,0x1000, C7200_GT64K_IRQ) == -1) return(-1); return(0); } /* Create the two main PCI busses for a MV64460 based system */ static int c7200_init_mv64460(c7200_t *router) { vm_instance_t *vm = router->vm; vm->pci_bus[0] = pci_bus_create("MB0/MB1",3); vm->pci_bus[1] = pci_bus_create("MB2",0); if (!vm->pci_bus[0] || !vm->pci_bus[1]) { vm_error(vm,"unable to create PCI data.\n"); return(-1); } return(dev_mv64460_init(vm,"mv64460",C7200_G2_MV64460_ADDR,0x10000)); } /* Create the PA PCI busses */ static int c7200_pa_create_pci_busses(c7200_t *router) { vm_instance_t *vm = router->vm; char bus_name[128]; int i; for(i=1;ipci_bus_pool[i] = pci_bus_create(bus_name,-1); if (!vm->pci_bus_pool[i]) return(-1); } return(0); } /* Create a PA bridge, depending on the midplane */ static int c7200_pa_init_pci_bridge(c7200_t *router,u_int pa_bay, struct pci_bus *pci_bus,int pci_device) { struct pci_bus *pa_bus; pa_bus = router->vm->slots_pci_bus[pa_bay]; switch(router->midplane_version) { case 0: case 1: dev_dec21050_init(pci_bus,pci_device,pa_bus); break; default: dev_dec21150_init(pci_bus,pci_device,pa_bus); } return(0); } /* * Hidden "I/O" PCI bridge hack for PCMCIA controller. * * On NPE-175, NPE-225, NPE-300 and NPE-400, PCMCIA controller is * identified on PCI as Bus=2,Device=16. On NPE-G1, this is Bus=17,Device=16. * * However, I don't understand how the bridging between PCI bus 1 and 2 * is done (16 and 17 on NPE-G1). * * Maybe I'm missing something about PCI-to-PCI bridge mechanism, or there * is a special hidden device that does the job silently (it should be * visible on the PCI bus...) * * BTW, it works. */ static int c7200_create_io_pci_bridge(c7200_t *router,struct pci_bus *parent_bus) { vm_instance_t *vm = router->vm; /* Create the PCI bus where the PCMCIA controller will seat */ if (!(vm->pci_bus_pool[16] = pci_bus_create("I/O secondary bus",-1))) return(-1); /* Create the hidden bridge with "special" handling... */ if (!(router->io_pci_bridge = pci_bridge_add(parent_bus))) return(-1); router->io_pci_bridge->skip_bus_check = TRUE; pci_bridge_map_bus(router->io_pci_bridge,vm->pci_bus_pool[16]); router->pcmcia_bus = vm->pci_bus_pool[16]; return(0); } /* Initialize an NPE-100 board */ int c7200_init_npe100(c7200_t *router) { vm_instance_t *vm = router->vm; int i; /* Set the processor type: R4600 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R4600); /* Initialize the Galileo GT-64010 system controller */ if (c7200_init_gt64010(router) == -1) return(-1); /* PCMCIA controller is on bus 0 */ router->pcmcia_bus = vm->pci_bus[0]; /* Initialize the PA PCI busses */ if (c7200_pa_create_pci_busses(router) == -1) return(-1); /* Create PCI busses for PA Bays 1,3,5 and PA Bays 2,4,6 */ vm->pci_bus_pool[24] = pci_bus_create("PA Slots 1,3,5",-1); vm->pci_bus_pool[25] = pci_bus_create("PA Slots 2,4,6",-1); /* PCI bridges (MB0/MB1, MB0/MB2) */ dev_dec21050_init(vm->pci_bus[0],1,NULL); dev_dec21050_init(vm->pci_bus[0],2,vm->pci_bus_pool[24]); dev_dec21050_init(vm->pci_bus[0],3,NULL); dev_dec21050_init(vm->pci_bus[0],4,vm->pci_bus_pool[25]); /* Map the PA PCI busses */ vm->slots_pci_bus[0] = vm->pci_bus[0]; for(i=1;islots_pci_bus[i] = vm->pci_bus_pool[i]; /* PCI bridges for PA Bays 1 to 6 */ c7200_pa_init_pci_bridge(router,1,vm->pci_bus_pool[24],1); c7200_pa_init_pci_bridge(router,3,vm->pci_bus_pool[24],2); c7200_pa_init_pci_bridge(router,5,vm->pci_bus_pool[24],3); c7200_pa_init_pci_bridge(router,2,vm->pci_bus_pool[25],1); c7200_pa_init_pci_bridge(router,4,vm->pci_bus_pool[25],2); c7200_pa_init_pci_bridge(router,6,vm->pci_bus_pool[25],3); return(0); } /* Initialize an NPE-150 board */ int c7200_init_npe150(c7200_t *router) { vm_instance_t *vm = router->vm; m_uint32_t bank_size; int i; /* Set the processor type: R4700 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R4700); /* Initialize the Galileo GT-64010 system controller */ if (c7200_init_gt64010(router) == -1) return(-1); /* PCMCIA controller is on bus 0 */ router->pcmcia_bus = vm->pci_bus[0]; /* Initialize the PA PCI busses */ if (c7200_pa_create_pci_busses(router) == -1) return(-1); /* Create PCI busses for PA Bays 1,3,5 and PA Bays 2,4,6 */ vm->pci_bus_pool[24] = pci_bus_create("PA Slots 1,3,5",-1); vm->pci_bus_pool[25] = pci_bus_create("PA Slots 2,4,6",-1); /* PCI bridges (MB0/MB1, MB0/MB2) */ dev_dec21050_init(vm->pci_bus[0],1,NULL); dev_dec21050_init(vm->pci_bus[0],2,vm->pci_bus_pool[24]); dev_dec21050_init(vm->pci_bus[0],3,NULL); dev_dec21050_init(vm->pci_bus[0],4,vm->pci_bus_pool[25]); /* Map the PA PCI busses */ vm->slots_pci_bus[0] = vm->pci_bus[0]; for(i=1;islots_pci_bus[i] = vm->pci_bus_pool[i]; /* PCI bridges for PA Bays 1 to 6 */ c7200_pa_init_pci_bridge(router,1,vm->pci_bus_pool[24],1); c7200_pa_init_pci_bridge(router,3,vm->pci_bus_pool[24],2); c7200_pa_init_pci_bridge(router,5,vm->pci_bus_pool[24],3); c7200_pa_init_pci_bridge(router,2,vm->pci_bus_pool[25],1); c7200_pa_init_pci_bridge(router,4,vm->pci_bus_pool[25],2); c7200_pa_init_pci_bridge(router,6,vm->pci_bus_pool[25],3); /* Packet SRAM: 1 Mb */ bank_size = 0x80000; dev_c7200_sram_init(vm,"sram0",C7200_SRAM_ADDR,bank_size, vm->pci_bus_pool[24],0); dev_c7200_sram_init(vm,"sram1",C7200_SRAM_ADDR+bank_size,bank_size, vm->pci_bus_pool[25],0); return(0); } /* Initialize an NPE-175 board */ int c7200_init_npe175(c7200_t *router) { vm_instance_t *vm = router->vm; int i; /* Set the processor type: R5271 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R527x); /* Initialize the Galileo GT-64120 PCI controller */ if (c7200_init_gt64120(router) == -1) return(-1); /* Initialize the PA PCI busses */ if (c7200_pa_create_pci_busses(router) == -1) return(-1); /* Create PCI bus for PA Bay 0 (I/O Card, PCMCIA, Interfaces) */ vm->pci_bus_pool[0] = pci_bus_create("PA Slot 0",-1); /* PCI bridge for I/O card device on MB0 */ dev_dec21150_init(vm->pci_bus[0],1,vm->pci_bus_pool[0]); /* Create the hidden "I/O" PCI bridge for PCMCIA controller */ c7200_create_io_pci_bridge(router,vm->pci_bus_pool[0]); /* Map the PA PCI busses */ for(i=0;islots_pci_bus[i] = vm->pci_bus_pool[i]; /* PCI bridges for PA Bays 1 to 6 */ c7200_pa_init_pci_bridge(router,1,vm->pci_bus[0],7); c7200_pa_init_pci_bridge(router,3,vm->pci_bus[0],8); c7200_pa_init_pci_bridge(router,5,vm->pci_bus[0],9); c7200_pa_init_pci_bridge(router,2,vm->pci_bus[1],7); c7200_pa_init_pci_bridge(router,4,vm->pci_bus[1],8); c7200_pa_init_pci_bridge(router,6,vm->pci_bus[1],9); /* Enable PEM EEPROM */ c7200_pem_set_eeprom(router); return(0); } /* Initialize an NPE-200 board */ int c7200_init_npe200(c7200_t *router) { vm_instance_t *vm = router->vm; m_uint32_t bank_size; int i; /* Set the processor type: R5000 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R5000); /* Initialize the Galileo GT-64010 PCI controller */ if (c7200_init_gt64010(router) == -1) return(-1); /* PCMCIA controller is on bus 0 */ router->pcmcia_bus = vm->pci_bus[0]; /* Initialize the PA PCI busses */ if (c7200_pa_create_pci_busses(router) == -1) return(-1); /* Create PCI busses for PA Bays 1,3,5 and PA Bays 2,4,6 */ vm->pci_bus_pool[24] = pci_bus_create("PA Slots 1,3,5",-1); vm->pci_bus_pool[25] = pci_bus_create("PA Slots 2,4,6",-1); /* PCI bridges (MB0/MB1, MB0/MB2) */ dev_dec21050_init(vm->pci_bus[0],1,NULL); dev_dec21050_init(vm->pci_bus[0],2,vm->pci_bus_pool[24]); dev_dec21050_init(vm->pci_bus[0],3,NULL); dev_dec21050_init(vm->pci_bus[0],4,vm->pci_bus_pool[25]); /* Map the PA PCI busses */ vm->slots_pci_bus[0] = vm->pci_bus[0]; for(i=1;islots_pci_bus[i] = vm->pci_bus_pool[i]; /* PCI bridges for PA Bays 1 to 6 */ c7200_pa_init_pci_bridge(router,1,vm->pci_bus_pool[24],1); c7200_pa_init_pci_bridge(router,3,vm->pci_bus_pool[24],2); c7200_pa_init_pci_bridge(router,5,vm->pci_bus_pool[24],3); c7200_pa_init_pci_bridge(router,2,vm->pci_bus_pool[25],1); c7200_pa_init_pci_bridge(router,4,vm->pci_bus_pool[25],2); c7200_pa_init_pci_bridge(router,6,vm->pci_bus_pool[25],3); /* Packet SRAM: 4 Mb */ bank_size = 0x200000; dev_c7200_sram_init(vm,"sram0",C7200_SRAM_ADDR,bank_size, vm->pci_bus_pool[24],0); dev_c7200_sram_init(vm,"sram1",C7200_SRAM_ADDR+bank_size,bank_size, vm->pci_bus_pool[25],0); return(0); } /* Initialize an NPE-225 board */ int c7200_init_npe225(c7200_t *router) { vm_instance_t *vm = router->vm; int i; /* Set the processor type: R5271 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R527x); /* Initialize the Galileo GT-64120 PCI controller */ if (c7200_init_gt64120(router) == -1) return(-1); /* Initialize the PA PCI busses */ if (c7200_pa_create_pci_busses(router) == -1) return(-1); /* Create PCI bus for PA Bay 0 (I/O Card, PCMCIA, Interfaces) */ vm->pci_bus_pool[0] = pci_bus_create("PA Slot 0",-1); /* PCI bridge for I/O card device on MB0 */ dev_dec21150_init(vm->pci_bus[0],1,vm->pci_bus_pool[0]); /* Create the hidden "I/O" PCI bridge for PCMCIA controller */ c7200_create_io_pci_bridge(router,vm->pci_bus_pool[0]); /* Map the PA PCI busses */ for(i=0;islots_pci_bus[i] = vm->pci_bus_pool[i]; /* PCI bridges for PA Bays 1 to 6 */ c7200_pa_init_pci_bridge(router,1,vm->pci_bus[0],7); c7200_pa_init_pci_bridge(router,3,vm->pci_bus[0],8); c7200_pa_init_pci_bridge(router,5,vm->pci_bus[0],9); c7200_pa_init_pci_bridge(router,2,vm->pci_bus[1],7); c7200_pa_init_pci_bridge(router,4,vm->pci_bus[1],8); c7200_pa_init_pci_bridge(router,6,vm->pci_bus[1],9); /* Enable PEM EEPROM */ c7200_pem_set_eeprom(router); return(0); } /* Initialize an NPE-300 board */ int c7200_init_npe300(c7200_t *router) { vm_instance_t *vm = router->vm; int i; /* Set the processor type: R7000 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R7000); /* 32 Mb of I/O memory */ vm->iomem_size = 32; dev_ram_init(vm,"iomem",vm->ram_mmap,TRUE,NULL,vm->sparse_mem, C7200_IOMEM_ADDR,32*1048576); /* Initialize the two Galileo GT-64120 system controllers */ if (c7200_init_dual_gt64120(router) == -1) return(-1); /* Initialize the PA PCI busses */ if (c7200_pa_create_pci_busses(router) == -1) return(-1); /* Create PCI bus for PA Bay 0 (I/O Card, PCMCIA, Interfaces) */ vm->pci_bus_pool[0] = pci_bus_create("PA Slot 0",-1); /* Create PCI busses for PA Bays 1,3,5 and PA Bays 2,4,6 */ vm->pci_bus_pool[24] = pci_bus_create("PA Slots 1,3,5",-1); vm->pci_bus_pool[25] = pci_bus_create("PA Slots 2,4,6",-1); /* PCI bridge for I/O card device on MB0 */ dev_dec21150_init(vm->pci_bus[0],1,vm->pci_bus_pool[0]); /* Create the hidden "I/O" PCI bridge for PCMCIA controller */ c7200_create_io_pci_bridge(router,vm->pci_bus_pool[0]); /* PCI bridges for PA PCI "Head" Busses */ dev_dec21150_init(vm->pci_bus[0],2,vm->pci_bus_pool[24]); dev_dec21150_init(vm->pci_bus[1],1,vm->pci_bus_pool[25]); /* Map the PA PCI busses */ for(i=0;islots_pci_bus[i] = vm->pci_bus_pool[i]; /* PCI bridges for PA Bays 1 to 6 */ c7200_pa_init_pci_bridge(router,1,vm->pci_bus_pool[24],1); c7200_pa_init_pci_bridge(router,3,vm->pci_bus_pool[24],2); c7200_pa_init_pci_bridge(router,5,vm->pci_bus_pool[24],3); c7200_pa_init_pci_bridge(router,2,vm->pci_bus_pool[25],1); c7200_pa_init_pci_bridge(router,4,vm->pci_bus_pool[25],2); c7200_pa_init_pci_bridge(router,6,vm->pci_bus_pool[25],3); return(0); } /* Initialize an NPE-400 board */ int c7200_init_npe400(c7200_t *router) { vm_instance_t *vm = router->vm; int i; /* Set the processor type: R7000 */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_R7000); /* * Add supplemental memory (as "iomem") if we have more than 256 Mb. */ if (VM_C7200(vm)->npe400_ram_size > C7200_BASE_RAM_LIMIT) { vm->iomem_size = VM_C7200(vm)->npe400_ram_size - C7200_BASE_RAM_LIMIT; vm->ram_size = C7200_BASE_RAM_LIMIT; dev_ram_init(vm,"iomem",vm->ram_mmap,TRUE,NULL,vm->sparse_mem, C7200_IOMEM_ADDR,vm->iomem_size*1048576); } else { vm->iomem_size = 0; } /* Initialize the Galileo GT-64120 system controller */ if (c7200_init_gt64120(router) == -1) return(-1); /* Initialize the PA PCI busses */ if (c7200_pa_create_pci_busses(router) == -1) return(-1); /* Create PCI bus for PA Bay 0 (I/O Card, PCMCIA, Interfaces) */ vm->pci_bus_pool[0] = pci_bus_create("PA Slot 0",-1); /* PCI bridge for I/O card device on MB0 */ dev_dec21050_init(vm->pci_bus[0],1,vm->pci_bus_pool[0]); /* Create the hidden "I/O" PCI bridge for PCMCIA controller */ c7200_create_io_pci_bridge(router,vm->pci_bus_pool[0]); /* Map the PA PCI busses */ for(i=0;islots_pci_bus[i] = vm->pci_bus_pool[i]; /* PCI bridges for PA Bays 1 to 6 */ c7200_pa_init_pci_bridge(router,1,vm->pci_bus[0],7); c7200_pa_init_pci_bridge(router,3,vm->pci_bus[0],8); c7200_pa_init_pci_bridge(router,5,vm->pci_bus[0],9); c7200_pa_init_pci_bridge(router,2,vm->pci_bus[1],7); c7200_pa_init_pci_bridge(router,4,vm->pci_bus[1],8); c7200_pa_init_pci_bridge(router,6,vm->pci_bus[1],9); return(0); } /* Initialize an NPE-G1 board (XXX not working) */ int c7200_init_npeg1(c7200_t *router) { vm_instance_t *vm = router->vm; int i; /* Just some tests */ mips64_set_prid(CPU_MIPS64(vm->boot_cpu),MIPS_PRID_BCM1250); vm->pci_bus[0] = pci_bus_create("HT/PCI bus",0); /* SB-1 System control devices */ dev_sb1_init(vm); /* SB-1 I/O devices */ dev_sb1_io_init(vm,C7200_DUART_IRQ); /* SB-1 PCI bus configuration zone */ dev_sb1_pci_init(vm,"pci_cfg",0xFE000000ULL); /* Initialize the PA PCI busses */ if (c7200_pa_create_pci_busses(router) == -1) return(-1); /* Create PCI bus for PA Bay 0 (I/O Card, PCMCIA, Interfaces) */ vm->pci_bus_pool[0] = pci_bus_create("PA Slot 0",-1); /* Create PCI busses for PA Bays 1,3,5 and PA Bays 2,4,6 */ vm->pci_bus_pool[24] = pci_bus_create("PA Slots 1,3,5",-1); vm->pci_bus_pool[25] = pci_bus_create("PA Slots 2,4,6",-1); /* HyperTransport/PCI bridges */ dev_ap1011_init(vm->pci_bus_pool[28],0,NULL); dev_ap1011_init(vm->pci_bus_pool[28],1,vm->pci_bus_pool[24]); dev_ap1011_init(vm->pci_bus_pool[28],2,vm->pci_bus_pool[25]); /* PCI bridge for I/O card device on MB0 */ dev_dec21150_init(vm->pci_bus[0],3,vm->pci_bus_pool[0]); /* Create the hidden "I/O" PCI bridge for PCMCIA controller */ c7200_create_io_pci_bridge(router,vm->pci_bus_pool[0]); /* Map the PA PCI busses */ vm->slots_pci_bus[0] = vm->pci_bus[0]; for(i=1;islots_pci_bus[i] = vm->pci_bus_pool[i]; /* PCI bridges for PA Bays 1 to 6 */ c7200_pa_init_pci_bridge(router,1,vm->pci_bus_pool[24],1); c7200_pa_init_pci_bridge(router,3,vm->pci_bus_pool[24],2); c7200_pa_init_pci_bridge(router,5,vm->pci_bus_pool[24],3); c7200_pa_init_pci_bridge(router,2,vm->pci_bus_pool[25],1); c7200_pa_init_pci_bridge(router,4,vm->pci_bus_pool[25],2); c7200_pa_init_pci_bridge(router,6,vm->pci_bus_pool[25],3); return(0); } /* Initialize an NPE-G2 board (XXX not working) */ int c7200_init_npeg2(c7200_t *router) { vm_instance_t *vm = router->vm; int i; /* Set the processor type: PowerPC G4 */ ppc32_set_pvr(CPU_PPC32(vm->boot_cpu),0x80040201); /* Initialize the PA PCI busses */ if (c7200_pa_create_pci_busses(router) == -1) return(-1); /* Create PCI bus for PA Bay 0 (I/O Card, PCMCIA, Interfaces) */ vm->pci_bus_pool[0] = pci_bus_create("PA Slot 0",-1); /* PCI bridge for I/O card device on MB0 */ dev_plx6520cb_init(vm->pci_bus[1],3,vm->pci_bus_pool[0]); /* Create PCI busses for PA Bays 1,3,5 and PA Bays 2,4,6 */ vm->pci_bus_pool[24] = pci_bus_create("PA Slots 1,3,5",-1); vm->pci_bus_pool[25] = pci_bus_create("PA Slots 2,4,6",-1); dev_plx6520cb_init(vm->pci_bus[0],1,vm->pci_bus_pool[24]); dev_plx6520cb_init(vm->pci_bus[0],2,vm->pci_bus_pool[25]); /* Create the hidden "I/O" PCI bridge for PCMCIA controller */ c7200_create_io_pci_bridge(router,vm->pci_bus_pool[0]); /* Map the PA PCI busses */ vm->slots_pci_bus[0] = vm->pci_bus_pool[0]; for(i=1;islots_pci_bus[i] = vm->pci_bus_pool[i]; /* PCI bridges for PA Bays 1 to 7 */ c7200_pa_init_pci_bridge(router,1,vm->pci_bus_pool[24],1); c7200_pa_init_pci_bridge(router,3,vm->pci_bus_pool[24],2); c7200_pa_init_pci_bridge(router,5,vm->pci_bus_pool[24],3); c7200_pa_init_pci_bridge(router,2,vm->pci_bus_pool[25],1); c7200_pa_init_pci_bridge(router,4,vm->pci_bus_pool[25],2); c7200_pa_init_pci_bridge(router,6,vm->pci_bus_pool[25],3); c7200_pa_init_pci_bridge(router,7,vm->pci_bus_pool[0],8); #if 0 /* too late at this stage... */ /* Add a fake slot (8) for NPE-G2 Ethernet ports */ if (vm_slot_add_binding(router->vm,"NPE-G2",8,0) == -1) printf("unable to set slot 8\n"); #endif return(0); } /* Show C7200 hardware info */ void c7200_show_hardware(c7200_t *router) { vm_instance_t *vm = router->vm; printf("C7200 instance '%s' (id %d):\n",vm->name,vm->instance_id); printf(" VM Status : %d\n",vm->status); printf(" RAM size : %u Mb\n",vm->ram_size); printf(" IOMEM size : %u Mb\n",vm->iomem_size); printf(" NVRAM size : %u Kb\n",vm->nvram_size); printf(" NPE model : %s\n",router->npe_driver->npe_type); printf(" Midplane : %s\n",router->midplane_type); printf(" IOS image : %s\n\n",vm->ios_image); if (vm->debug_level > 0) { dev_show_list(vm); pci_dev_show_list(vm->pci_bus[0]); pci_dev_show_list(vm->pci_bus[1]); printf("\n"); } } /* Initialize default parameters for a C7200 */ static void c7200_init_defaults(c7200_t *router) { vm_instance_t *vm = router->vm; n_eth_addr_t *m; m_uint16_t pid; /* Set platform slots characteristics */ vm->nr_slots = C7200_MAX_PA_BAYS; vm->slots_type = CISCO_CARD_TYPE_PA; vm->slots_drivers = pa_drivers; pid = (m_uint16_t)getpid(); /* Generate a chassis MAC address based on the instance ID */ m = &router->mac_addr; m->eth_addr_byte[0] = vm_get_mac_addr_msb(vm); m->eth_addr_byte[1] = vm->instance_id & 0xFF; m->eth_addr_byte[2] = pid >> 8; m->eth_addr_byte[3] = pid & 0xFF; m->eth_addr_byte[4] = 0x00; m->eth_addr_byte[5] = 0x00; router->board_id[0] = 0x00; c7200_init_sys_eeprom_groups(router); c7200_init_mp_eeprom_groups(router); c7200_npe_set_type(router,C7200_DEFAULT_NPE_TYPE); c7200_midplane_set_type(router,C7200_DEFAULT_MIDPLANE); vm->ram_mmap = C7200_DEFAULT_RAM_MMAP; vm->ram_size = C7200_DEFAULT_RAM_SIZE; vm->rom_size = C7200_DEFAULT_ROM_SIZE; vm->nvram_size = C7200_DEFAULT_NVRAM_SIZE; vm->iomem_size = 0; vm->conf_reg_setup = C7200_DEFAULT_CONF_REG; vm->clock_divisor = C7200_DEFAULT_CLOCK_DIV; vm->nvram_rom_space = C7200_NVRAM_ROM_RES_SIZE; vm->pcmcia_disk_size[0] = C7200_DEFAULT_DISK0_SIZE; vm->pcmcia_disk_size[1] = C7200_DEFAULT_DISK1_SIZE; } /* Run the checklist */ static int c7200_checklist(c7200_t *router) { struct vm_instance *vm = router->vm; int res = 0; res += vm_object_check(vm,"ram"); res += vm_object_check(vm,"rom"); res += vm_object_check(vm,"nvram"); res += vm_object_check(vm,"zero"); if (res < 0) vm_error(vm,"incomplete initialization (no memory?)\n"); return(res); } /* Initialize Port Adapters */ static int c7200_init_platform_pa(c7200_t *router) { #if 1 /* FIXME - for a later release */ /* Use a C7200-IO-FE by default in slot 0 if an I/O card is required */ if (router->npe_driver->iocard_required && !vm_slot_active(router->vm,0,0)) vm_slot_add_binding(router->vm,"C7200-IO-FE",0,0); #endif return(vm_slot_init_all(router->vm)); } /* Initialize the C7200 Platform (MIPS) */ static int c7200m_init_platform(c7200_t *router) { struct vm_instance *vm = router->vm; cpu_mips_t *cpu0; cpu_gen_t *gen0; /* Copy config register setup into "active" config register */ vm->conf_reg = vm->conf_reg_setup; /* Create Console and AUX ports */ vm_init_vtty(vm); /* Check that the amount of RAM is valid */ if (vm->ram_size > router->npe_driver->max_ram_size) { vm_error(vm,"%u is not a valid RAM size for this NPE. " "Fallback to %u Mb.\n\n", vm->ram_size,router->npe_driver->max_ram_size); vm->ram_size = router->npe_driver->max_ram_size; } /* Create a CPU group */ vm->cpu_group = cpu_group_create("System CPU"); /* Initialize the virtual MIPS processor */ if (!(gen0 = cpu_create(vm,CPU_TYPE_MIPS64,0))) { vm_error(vm,"unable to create CPU0!\n"); return(-1); } cpu0 = CPU_MIPS64(gen0); /* Add this CPU to the system CPU group */ cpu_group_add(vm->cpu_group,gen0); vm->boot_cpu = gen0; /* Initialize the IRQ routing vectors */ vm->set_irq = mips64_vm_set_irq; vm->clear_irq = mips64_vm_clear_irq; /* Mark the Network IO interrupt as high priority */ cpu0->irq_idle_preempt[C7200_NETIO_IRQ] = TRUE; cpu0->irq_idle_preempt[C7200_GT64K_IRQ] = TRUE; /* Copy some parameters from VM to CPU0 (idle PC, ...) */ cpu0->idle_pc = vm->idle_pc; if (vm->timer_irq_check_itv) cpu0->timer_irq_check_itv = vm->timer_irq_check_itv; /* * On the C7200, bit 33 of physical addresses is used to bypass L2 cache. * We clear it systematically. */ cpu0->addr_bus_mask = C7200_ADDR_BUS_MASK; /* Remote emulator control */ dev_remote_control_init(vm,0x16000000,0x1000); /* Bootflash (8 Mb) */ dev_bootflash_init(vm,"bootflash","c7200-bootflash-8mb", C7200_BOOTFLASH_ADDR); /* NVRAM and calendar */ dev_nvram_init(vm,"nvram",router->npe_driver->nvram_addr, vm->nvram_size*1024,&vm->conf_reg); /* Bit-bucket zone */ dev_zero_init(vm,"zero",C7200_BITBUCKET_ADDR,0xc00000); /* Initialize the NPE board */ if (router->npe_driver->npe_init(router) == -1) return(-1); /* Initialize RAM */ vm_ram_init(vm,0x00000000ULL); /* Initialize ROM */ if (!vm->rom_filename) { /* use embedded ROM */ dev_rom_init(vm,"rom",C7200_ROM_ADDR,vm->rom_size*1048576, mips64_microcode,mips64_microcode_len); } else { /* use alternate ROM */ dev_ram_init(vm,"rom",TRUE,TRUE,NULL,FALSE, C7200_ROM_ADDR,vm->rom_size*1048576); } /* Byte swapping */ dev_bswap_init(vm,"mem_bswap",C7200_BSWAP_ADDR,1024*1048576,0x00000000ULL); /* PCI IO space */ if (!(vm->pci_io_space = pci_io_data_init(vm,C7200_PCI_IO_ADDR))) return(-1); /* Cirrus Logic PD6729 (PCI-to-PCMCIA host adapter) */ dev_clpd6729_init(vm,router->pcmcia_bus, router->npe_driver->clpd6729_pci_dev, vm->pci_io_space,0x402,0x403); /* Initialize the Port Adapters */ if (c7200_init_platform_pa(router) == -1) return(-1); /* Verify the check list */ if (c7200_checklist(router) == -1) return(-1); /* Midplane FPGA */ dev_c7200_mpfpga_init(router,C7200_MPFPGA_ADDR,0x1000); /* IO FPGA */ if (dev_c7200_iofpga_init(router,C7200_IOFPGA_ADDR,0x1000) == -1) return(-1); /* Show device list */ c7200_show_hardware(router); return(0); } /* Initialize the C7200 Platform (PowerPC) */ static int c7200p_init_platform(c7200_t *router) { struct vm_instance *vm = router->vm; cpu_ppc_t *cpu0; cpu_gen_t *gen0; vm_obj_t *obj; /* Copy config register setup into "active" config register */ vm->conf_reg = vm->conf_reg_setup; /* Create Console and AUX ports */ vm_init_vtty(vm); /* Check that the amount of RAM is valid */ if (vm->ram_size > router->npe_driver->max_ram_size) { vm_error(vm,"%u is not a valid RAM size for this NPE. " "Fallback to %u Mb.\n\n", vm->ram_size,router->npe_driver->max_ram_size); vm->ram_size = router->npe_driver->max_ram_size; } /* Create a CPU group */ vm->cpu_group = cpu_group_create("System CPU"); /* Initialize the virtual PowerPC processor */ if (!(gen0 = cpu_create(vm,CPU_TYPE_PPC32,0))) { vm_error(vm,"unable to create CPU0!\n"); return(-1); } cpu0 = CPU_PPC32(gen0); /* Add this CPU to the system CPU group */ cpu_group_add(vm->cpu_group,gen0); vm->boot_cpu = gen0; /* Mark the Network IO interrupt as high priority */ vm->irq_idle_preempt[C7200_NETIO_IRQ] = TRUE; /* Copy some parameters from VM to CPU0 (idle PC, ...) */ cpu0->idle_pc = vm->idle_pc; if (vm->timer_irq_check_itv) cpu0->timer_irq_check_itv = vm->timer_irq_check_itv; /* Initialize the Marvell MV-64460 system controller */ if (c7200_init_mv64460(router) == -1) return(-1); if (!(obj = vm_object_find(router->vm,"mv64460"))) return(-1); router->mv64460_sysctr = obj->data; /* Remote emulator control */ dev_remote_control_init(vm,0xf6000000,0x1000); /* Bootflash (64 Mb) */ dev_bootflash_init(vm,"bootflash","c7200-bootflash-64mb", C7200_G2_BOOTFLASH_ADDR); /* NVRAM and calendar */ vm->nvram_size = C7200_G2_NVRAM_SIZE / 1024; dev_nvram_init(vm,"nvram",router->npe_driver->nvram_addr, C7200_G2_NVRAM_SIZE,&vm->conf_reg); /* Initialize the NPE board */ if (router->npe_driver->npe_init(router) == -1) return(-1); /* Initialize RAM */ vm_ram_init(vm,0x00000000ULL); /* Initialize ROM */ if (!vm->rom_filename) { /* use embedded ROM */ dev_rom_init(vm,"rom",C7200_G2_ROM_ADDR,vm->rom_size*1048576, ppc32_microcode,ppc32_microcode_len); } else { /* use alternate ROM */ dev_ram_init(vm,"rom",TRUE,TRUE,NULL,FALSE, C7200_G2_ROM_ADDR,vm->rom_size*1048576); } /* Byte swapping - FIXME */ dev_bswap_init(vm,"mem_bswap0",C7200_G2_BSWAP_ADDR,32*1048576, 0x00000000ULL); //dev_bswap_init(vm,"mem_bswap0",C7200_G2_BSWAP_ADDR+0x10000000,32*1048576, // 0x00000000ULL); /* PCI IO space */ if (!(vm->pci_io_space = pci_io_data_init(vm,C7200_G2_PCI_IO_ADDR))) return(-1); /* Cirrus Logic PD6729 (PCI-to-PCMCIA host adapter) */ dev_clpd6729_init(vm,router->pcmcia_bus, router->npe_driver->clpd6729_pci_dev, vm->pci_io_space,0x402,0x403); /* Initialize the Port Adapters */ if (c7200_init_platform_pa(router) == -1) return(-1); /* IO FPGA */ if (dev_c7200_iofpga_init(router,C7200_G2_IOFPGA_ADDR,0x1000) == -1) return(-1); /* MP FPGA */ if (dev_c7200_mpfpga_init(router,C7200_G2_MPFPGA_ADDR,0x10000) == -1) return(-1); /* * If we have no i/o card in slot 0, the console is handled by * the MV64460. */ if (!c7200_slot0_iocard_present(router)) { vm_log(vm,"CONSOLE","console managed by NPE-G2 board\n"); mv64460_sdma_bind_vtty(router->mv64460_sysctr,0,vm->vtty_con); mv64460_sdma_bind_vtty(router->mv64460_sysctr,1,vm->vtty_aux); } /* Show device list */ c7200_show_hardware(router); return(0); } /* Boot the IOS image (MIPS) */ static int c7200m_boot_ios(c7200_t *router) { vm_instance_t *vm = router->vm; cpu_mips_t *cpu; if (!vm->boot_cpu) return(-1); /* Suspend CPU activity since we will restart directly from ROM */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Reset the boot CPU */ cpu = CPU_MIPS64(vm->boot_cpu); mips64_reset(cpu); /* Load IOS image */ if (mips64_load_elf_image(cpu,vm->ios_image, (vm->ghost_status == VM_GHOST_RAM_USE), &vm->ios_entry_point) < 0) { vm_error(vm,"failed to load Cisco IOS image '%s'.\n",vm->ios_image); return(-1); } /* Launch the simulation */ printf("\nC7200 '%s': starting simulation (CPU0 PC=0x%llx), " "JIT %sabled.\n", vm->name,cpu->pc,vm->jit_use ? "en":"dis"); vm_log(vm,"C7200_BOOT", "starting instance (CPU0 PC=0x%llx,idle_pc=0x%llx,JIT %s)\n", cpu->pc,cpu->idle_pc,vm->jit_use ? "on":"off"); /* Start main CPU */ if (vm->ghost_status != VM_GHOST_RAM_GENERATE) { vm->status = VM_STATUS_RUNNING; cpu_start(vm->boot_cpu); } else { vm->status = VM_STATUS_SHUTDOWN; } return(0); } /* Boot the IOS image (PowerPC) */ static int c7200p_boot_ios(c7200_t *router) { vm_instance_t *vm = router->vm; cpu_ppc_t *cpu; if (!vm->boot_cpu) return(-1); /* Suspend CPU activity since we will restart directly from ROM */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Reset the boot CPU */ cpu = CPU_PPC32(vm->boot_cpu); ppc32_reset(cpu); /* Load IOS image */ if (ppc32_load_elf_image(cpu,vm->ios_image, (vm->ghost_status == VM_GHOST_RAM_USE), &vm->ios_entry_point) < 0) { vm_error(vm,"failed to load Cisco IOS image '%s'.\n",vm->ios_image); return(-1); } /* Launch the simulation */ printf("\nC7200P '%s': starting simulation (CPU0 IA=0x%8.8x), " "JIT %sabled.\n", vm->name,cpu->ia,vm->jit_use ? "en":"dis"); vm_log(vm,"C7200P_BOOT", "starting instance (CPU0 IA=0x%8.8x,idle_pc=0x%8.8x,JIT %s)\n", cpu->ia,cpu->idle_pc,vm->jit_use ? "on":"off"); /* Start main CPU */ if (vm->ghost_status != VM_GHOST_RAM_GENERATE) { vm->status = VM_STATUS_RUNNING; cpu_start(vm->boot_cpu); } else { vm->status = VM_STATUS_SHUTDOWN; } return(0); } /* Set an IRQ */ static void c7200m_set_irq(vm_instance_t *vm,u_int irq) { c7200_t *router = VM_C7200(vm); cpu_mips_t *cpu0 = CPU_MIPS64(vm->boot_cpu); u_int slot,port; switch(irq) { case 0 ... 7: mips64_set_irq(cpu0,irq); if (cpu0->irq_idle_preempt[irq]) cpu_idle_break_wait(cpu0->gen); break; case C7200_NETIO_IRQ_BASE ... C7200_NETIO_IRQ_END: c7200_net_irq_get_slot_port(irq,&slot,&port); dev_c7200_net_set_irq(router,slot,port); break; } } /* Clear an IRQ */ static void c7200m_clear_irq(vm_instance_t *vm,u_int irq) { c7200_t *router = VM_C7200(vm); cpu_mips_t *cpu0 = CPU_MIPS64(vm->boot_cpu); u_int slot,port; switch(irq) { case 0 ... 7: mips64_clear_irq(cpu0,irq); break; case C7200_NETIO_IRQ_BASE ... C7200_NETIO_IRQ_END: c7200_net_irq_get_slot_port(irq,&slot,&port); dev_c7200_net_clear_irq(router,slot,port); break; } } /* Initialize a Cisco 7200 instance (MIPS) */ static int c7200m_init_instance(c7200_t *router) { vm_instance_t *vm = router->vm; m_uint32_t rom_entry_point; cpu_mips_t *cpu0; /* Initialize the C7200 platform */ if (c7200m_init_platform(router) == -1) { vm_error(vm,"unable to initialize the platform hardware.\n"); return(-1); } /* IRQ routing */ vm->set_irq = c7200m_set_irq; vm->clear_irq = c7200m_clear_irq; /* Load IOS configuration files */ if (vm->ios_startup_config != NULL || vm->ios_private_config != NULL) { vm_nvram_push_config(vm,vm->ios_startup_config,vm->ios_private_config); vm->conf_reg &= ~0x40; } /* Load ROM (ELF image or embedded) */ cpu0 = CPU_MIPS64(vm->boot_cpu); rom_entry_point = (m_uint32_t)MIPS_ROM_PC; if ((vm->rom_filename != NULL) && (mips64_load_elf_image(cpu0,vm->rom_filename,0,&rom_entry_point) < 0)) { vm_error(vm,"unable to load alternate ROM '%s', " "fallback to embedded ROM.\n\n",vm->rom_filename); vm->rom_filename = NULL; } /* Load symbol file */ if (vm->sym_filename) { mips64_sym_load_file(cpu0,vm->sym_filename); cpu0->sym_trace = 1; } return(c7200m_boot_ios(router)); } /* Set an IRQ */ static void c7200p_set_irq(vm_instance_t *vm,u_int irq) { c7200_t *router = VM_C7200(vm); cpu_ppc_t *cpu0 = CPU_PPC32(vm->boot_cpu); u_int slot,port; switch(irq) { case C7200_VTIMER_IRQ: ppc32_trigger_timer_irq(cpu0); break; case C7200_DUART_IRQ: dev_mv64460_set_gpp_intr(router->mv64460_sysctr,10); break; case C7200_NETIO_IRQ: dev_mv64460_set_gpp_intr(router->mv64460_sysctr,24); break; case C7200_PA_MGMT_IRQ: dev_mv64460_set_gpp_intr(router->mv64460_sysctr,20); break; case C7200_OIR_IRQ: dev_mv64460_set_gpp_intr(router->mv64460_sysctr,0); break; case C7200_NETIO_IRQ_BASE ... C7200_NETIO_IRQ_END: c7200_net_irq_get_slot_port(irq,&slot,&port); dev_c7200_net_set_irq(router,slot,port); break; } if (vm->irq_idle_preempt[irq]) cpu_idle_break_wait(cpu0->gen); } /* Clear an IRQ */ static void c7200p_clear_irq(vm_instance_t *vm,u_int irq) { c7200_t *router = VM_C7200(vm); u_int slot,port; switch(irq) { case C7200_DUART_IRQ: dev_mv64460_clear_gpp_intr(router->mv64460_sysctr,10); break; case C7200_NETIO_IRQ: dev_mv64460_clear_gpp_intr(router->mv64460_sysctr,24); break; case C7200_PA_MGMT_IRQ: dev_mv64460_clear_gpp_intr(router->mv64460_sysctr,20); break; case C7200_OIR_IRQ: dev_mv64460_clear_gpp_intr(router->mv64460_sysctr,0); break; case C7200_NETIO_IRQ_BASE ... C7200_NETIO_IRQ_END: c7200_net_irq_get_slot_port(irq,&slot,&port); dev_c7200_net_clear_irq(router,slot,port); break; } } /* Initialize a Cisco 7200 instance (PowerPC) */ static int c7200p_init_instance(c7200_t *router) { vm_instance_t *vm = router->vm; m_uint32_t rom_entry_point; cpu_ppc_t *cpu0; int i; /* Initialize the C7200 platform */ if (c7200p_init_platform(router) == -1) { vm_error(vm,"unable to initialize the platform hardware.\n"); return(-1); } /* IRQ routing */ vm->set_irq = c7200p_set_irq; vm->clear_irq = c7200p_clear_irq; /* Load ROM (ELF image or embedded) */ cpu0 = CPU_PPC32(vm->boot_cpu); rom_entry_point = (m_uint32_t)PPC32_ROM_START; if ((vm->rom_filename != NULL) && (ppc32_load_elf_image(cpu0,vm->rom_filename,0,&rom_entry_point) < 0)) { vm_error(vm,"unable to load alternate ROM '%s', " "fallback to embedded ROM.\n\n",vm->rom_filename); vm->rom_filename = NULL; } /* Initialize the MMU (TEST) */ for(i=0;isr[i] = i << 16; /* The page table takes 2 Mb of memory */ vm->ram_res_size = 2; ppc32_set_sdr1(cpu0,((vm->ram_size - 2) * 1048576) + 0x1F); ppc32_init_page_table(cpu0); ppc32_map_zone(cpu0,cpu0->sr[C7200_G2_BOOTFLASH_ADDR >> 28], C7200_G2_BOOTFLASH_ADDR,C7200_G2_BOOTFLASH_ADDR, 64*1048576,0,0x02); ppc32_map_zone(cpu0,cpu0->sr[0xD8000000 >> 28], 0xD8000000,0xD8000000,0x400000,0,0x02); ppc32_map_zone(cpu0,cpu0->sr[0xDC000000 >> 28], 0xDC000000,0xDC000000,0x400000,0,0x02); /* FIXME */ ppc32_map_zone(cpu0,cpu0->sr[0xDF000000 >> 28], 0xDF000000,0xDF000000,0x400000,0,0x02); /* INST */ cpu0->bat[PPC32_IBAT_IDX][0].reg[0] = 0x00007FFE; cpu0->bat[PPC32_IBAT_IDX][0].reg[1] = 0x00000003; cpu0->bat[PPC32_IBAT_IDX][3].reg[0] = 0xF0001FFE; cpu0->bat[PPC32_IBAT_IDX][3].reg[1] = 0xF0000003; /* DATA */ cpu0->bat[PPC32_DBAT_IDX][0].reg[0] = 0x00007FFE; cpu0->bat[PPC32_DBAT_IDX][0].reg[1] = 0x00000003; cpu0->bat[PPC32_DBAT_IDX][3].reg[0] = 0xF0001FFE; cpu0->bat[PPC32_DBAT_IDX][3].reg[1] = 0xF0000003; return(c7200p_boot_ios(router)); } /* Initialize a Cisco 7200 instance */ static int c7200_init_instance(vm_instance_t *vm) { c7200_t *router = VM_C7200(vm); switch(router->npe_driver->npe_family) { case C7200_NPE_FAMILY_MIPS: return(c7200m_init_instance(router)); case C7200_NPE_FAMILY_PPC: return(c7200p_init_instance(router)); default: vm_error(router->vm,"unsupported NPE family %d", router->npe_driver->npe_family); return(-1); } } /* Stop a Cisco 7200 instance */ static int c7200_stop_instance(vm_instance_t *vm) { printf("\nC7200 '%s': stopping simulation.\n",vm->name); vm_log(vm,"C7200_STOP","stopping simulation.\n"); /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } } /* Free resources that were used during execution to emulate hardware */ c7200_free_hw_ressources(VM_C7200(vm)); vm_hardware_shutdown(vm); return(0); } /* Trigger an OIR event */ static int c7200_trigger_oir_event(c7200_t *router,u_int slot) { switch(slot) { case 1 ... 6: router->oir_status[0] = 1 << slot; break; case 7: /* signal the OIR on slot 0, and set the "extended status" */ router->oir_status[0] = 1 << 0; router->oir_status[1] = 1 << 24; break; } vm_set_irq(router->vm,C7200_OIR_IRQ); return(0); } /* Initialize a new PA while the virtual router is online (OIR) */ static int c7200_pa_init_online(vm_instance_t *vm,u_int slot,u_int subslot) { if (!slot) { vm_error(vm,"OIR not supported on slot 0.\n"); return(-1); } /* * Suspend CPU activity while adding new hardware (since we change the * memory maps). */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Add the new hardware elements */ if (vm_slot_init(vm,slot) == -1) return(-1); /* Resume normal operations */ vm_resume(vm); /* Now, we can safely trigger the OIR event */ c7200_trigger_oir_event(VM_C7200(vm),slot); return(0); } /* Stop a PA while the virtual router is online (OIR) */ static int c7200_pa_stop_online(vm_instance_t *vm,u_int slot,u_int subslot) { if (!slot) { vm_error(vm,"OIR not supported on slot 0.\n"); return(-1); } /* The PA driver must be initialized */ if (!vm_slot_get_card_ptr(vm,slot)) { vm_error(vm,"trying to shut down empty slot %u.\n",slot); return(-1); } /* Disable all NIOs to stop traffic forwarding */ vm_slot_disable_all_nio(vm,slot); /* We can safely trigger the OIR event */ c7200_trigger_oir_event(VM_C7200(vm),slot); /* * Suspend CPU activity while removing the hardware (since we change the * memory maps). */ vm_suspend(vm); /* Device removal */ if (vm_slot_shutdown(vm,slot) != 0) vm_error(vm,"unable to shutdown slot %u.\n",slot); /* Resume normal operations */ vm_resume(vm); return(0); } /* Get MAC address MSB */ static u_int c7200_get_mac_addr_msb(void) { return(0xCA); } /* Parse specific options for the Cisco 7200 platform */ static int c7200_cli_parse_options(vm_instance_t *vm,int option) { c7200_t *router = VM_C7200(vm); switch(option) { /* NPE type */ case 't': c7200_npe_set_type(router,optarg); break; /* Midplane type */ case 'M': c7200_midplane_set_type(router,optarg); break; /* Set the base MAC address */ case 'm': if (!c7200_midplane_set_mac_addr(router,optarg)) printf("MAC address set to '%s'.\n",optarg); break; /* Set the System ID */ case 'I': if (!c7200_set_system_id(router,optarg)) printf("System ID set to '%s'.\n",optarg); break; /* Unknown option */ default: return(-1); } return(0); } /* Show specific CLI options */ static void c7200_cli_show_options(vm_instance_t *vm) { printf(" -t : Select NPE type (default: \"%s\")\n" " -M : Select Midplane (\"std\" or \"vxr\")\n" " -p : Define a Port Adapter\n" " -s : Bind a Network IO interface to a " "Port Adapter\n" " -I : Set Processor Board Serial Number\n", C7200_DEFAULT_NPE_TYPE); } /* Platform definition */ static vm_platform_t c7200_platform = { "c7200", "C7200", "7200", c7200_create_instance, c7200_delete_instance, c7200_init_instance, c7200_stop_instance, c7200_pa_init_online, c7200_pa_stop_online, c7200_nvram_extract_config, c7200_nvram_push_config, c7200_get_mac_addr_msb, c7200_save_config, c7200_cli_parse_options, c7200_cli_show_options, c7200_npe_show_drivers, }; /* Register the c7200 platform */ int c7200_platform_register(void) { if (vm_platform_register(&c7200_platform) == -1) return(-1); return(hypervisor_c7200_init(&c7200_platform)); } dynamips-0.2.14/common/dev_c7200.h000066400000000000000000000205041241034141600164510ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Generic Cisco 7200 routines and definitions (EEPROM,...). * * Notes on IRQs (see "show stack"): * * - triggering IRQ 3: we get indefinitely (for each slot): * "Error: Unexpected NM Interrupt received from slot: 6" * * - triggering IRQ 4: GT64010 reg access: probably "DMA/Timer Interrupt" * * - triggering IRQ 6: we get (probably "OIR/Error Interrupt") * %ERR-1-PERR: PCI bus parity error * %ERR-1-SERR: PCI bus system/parity error * %ERR-1-FATAL: Fatal error interrupt, No reloading * err_stat=0x0, err_enable=0x0, mgmt_event=0xFFFFFFFF * */ #ifndef __DEV_C7200_H__ #define __DEV_C7200_H__ #include #include "utils.h" #include "net.h" #include "device.h" #include "pci_dev.h" #include "nmc93cX6.h" #include "dev_mv64460.h" #include "dev_ds1620.h" #include "net_io.h" #include "vm.h" /* Default C7200 parameters */ #define C7200_DEFAULT_NPE_TYPE "npe-400" #define C7200_DEFAULT_MIDPLANE "vxr" #define C7200_DEFAULT_RAM_SIZE 256 #define C7200_DEFAULT_ROM_SIZE 4 #define C7200_DEFAULT_NVRAM_SIZE 128 #define C7200_DEFAULT_CONF_REG 0x2102 #define C7200_DEFAULT_CLOCK_DIV 4 #define C7200_DEFAULT_RAM_MMAP 1 #define C7200_DEFAULT_DISK0_SIZE 64 #define C7200_DEFAULT_DISK1_SIZE 0 /* * 6 slots + 1 I/O card. * Slot 8 is special: it is for the NPE-G2 ethernet ports, but doesn't * represent something real. */ #define C7200_MAX_PA_BAYS 9 /* C7200 Timer IRQ (virtual) */ #define C7200_VTIMER_IRQ 0 /* C7200 DUART Interrupt */ #define C7200_DUART_IRQ 5 /* C7200 Network I/O Interrupt */ #define C7200_NETIO_IRQ 2 /* C7200 PA Management Interrupt handler */ #define C7200_PA_MGMT_IRQ 3 /* C7200 GT64k DMA/Timer Interrupt */ #define C7200_GT64K_IRQ 4 /* C7200 Error/OIR Interrupt */ #define C7200_OIR_IRQ 6 /* Network IRQ */ #define C7200_NETIO_IRQ_BASE 32 #define C7200_NETIO_IRQ_PORT_BITS 3 #define C7200_NETIO_IRQ_PORT_MASK ((1 << C7200_NETIO_IRQ_PORT_BITS) - 1) #define C7200_NETIO_IRQ_PER_SLOT (1 << C7200_NETIO_IRQ_PORT_BITS) #define C7200_NETIO_IRQ_END \ (C7200_NETIO_IRQ_BASE + (C7200_MAX_PA_BAYS * C7200_NETIO_IRQ_PER_SLOT) - 1) /* C7200 base ram limit (256 Mb) */ #define C7200_BASE_RAM_LIMIT 256 /* C7200 common device addresses */ #define C7200_GT64K_ADDR 0x14000000ULL #define C7200_GT64K_SEC_ADDR 0x15000000ULL #define C7200_BOOTFLASH_ADDR 0x1a000000ULL #define C7200_NVRAM_ADDR 0x1e000000ULL #define C7200_MPFPGA_ADDR 0x1e800000ULL #define C7200_IOFPGA_ADDR 0x1e840000ULL #define C7200_BITBUCKET_ADDR 0x1f000000ULL #define C7200_ROM_ADDR 0x1fc00000ULL #define C7200_IOMEM_ADDR 0x20000000ULL #define C7200_SRAM_ADDR 0x4b000000ULL #define C7200_BSWAP_ADDR 0xc0000000ULL #define C7200_PCI_IO_ADDR 0x100000000ULL /* NPE-G1 specific info */ #define C7200_G1_NVRAM_ADDR 0x1e400000ULL /* NPE-G2 specific info */ #define C7200_G2_BSWAP_ADDR 0xce000000ULL #define C7200_G2_BOOTFLASH_ADDR 0xe8000000ULL #define C7200_G2_PCI_IO_ADDR 0xf0000000ULL #define C7200_G2_MV64460_ADDR 0xf1000000ULL #define C7200_G2_MPFPGA_ADDR 0xfe000000ULL #define C7200_G2_IOFPGA_ADDR 0xfe040000ULL #define C7200_G2_NVRAM_ADDR 0xff000000ULL #define C7200_G2_ROM_ADDR 0xfff00000ULL /* NVRAM size for NPE-G2: 2 Mb */ #define C7200_G2_NVRAM_SIZE (2 * 1048576) /* Reserved space for ROM in NVRAM */ #define C7200_NVRAM_ROM_RES_SIZE 2048 /* C7200 physical address bus mask: keep only the lower 33 bits */ #define C7200_ADDR_BUS_MASK 0x1ffffffffULL /* C7200 ELF Platform ID */ #define C7200_ELF_MACHINE_ID 0x19 /* NPE families */ enum { C7200_NPE_FAMILY_MIPS = 0, C7200_NPE_FAMILY_PPC, }; /* 4 temperature sensors in a C7200 */ #define C7200_TEMP_SENSORS 4 #define VM_C7200(vm) ((c7200_t *)vm->hw_data) /* C7200 router */ typedef struct c7200_router c7200_t; /* Prototype of NPE driver initialization function */ typedef int (*c7200_npe_init_fn)(c7200_t *router); /* C7200 NPE Driver */ struct c7200_npe_driver { char *npe_type; int npe_family; c7200_npe_init_fn npe_init; int max_ram_size; int supported; m_uint64_t nvram_addr; int iocard_required; int clpd6729_pci_bus; int clpd6729_pci_dev; int dec21140_pci_bus; int dec21140_pci_dev; }; /* C7200 router */ struct c7200_router { /* Midplane type (standard,VXR) and chassis MAC address */ char *midplane_type; int midplane_version; n_eth_addr_t mac_addr; char board_id[20]; /* Associated VM instance */ vm_instance_t *vm; /* RAM size for npe-400 */ m_uint32_t npe400_ram_size; /* MV64460 device for NPE-G2 */ struct mv64460_data *mv64460_sysctr; /* NPE and OIR status */ struct c7200_npe_driver *npe_driver; m_uint32_t oir_status[2]; /* Hidden I/O bridge hack to support PCMCIA */ struct pci_bridge *io_pci_bridge; struct pci_bus *pcmcia_bus; /* PA and Network IRQ registers */ m_uint32_t pa_status_reg[2]; m_uint32_t pa_ctrl_reg[2]; m_uint32_t net_irq_status[3]; m_uint32_t net_irq_mask[3]; /* Temperature sensors */ struct ds1620_data ds1620_sensors[C7200_TEMP_SENSORS]; /* Power supply status */ u_int ps_status; /* Midplane EEPROM can be modified to change the chassis MAC address... */ struct cisco_eeprom cpu_eeprom,mp_eeprom,pem_eeprom; struct nmc93cX6_group sys_eeprom_g1; /* EEPROMs for CPU and Midplane */ struct nmc93cX6_group sys_eeprom_g2; /* EEPROM for PEM */ struct nmc93cX6_group pa_eeprom_g1; /* EEPROMs for bays 0, 1, 3, 4 */ struct nmc93cX6_group pa_eeprom_g2; /* EEPROMs for bays 2, 5, 6 */ struct nmc93cX6_group pa_eeprom_g3; /* EEPROM for bay 7 */ }; /* Initialize system EEPROM groups */ void c7200_init_sys_eeprom_groups(c7200_t *router); /* Initialize midplane EEPROM groups */ void c7200_init_mp_eeprom_groups(c7200_t *router); /* Returns TRUE if the specified card in slot 0 is an I/O card */ int c7200_slot0_iocard_present(c7200_t *router); /* Set EEPROM for the specified slot */ int c7200_set_slot_eeprom(c7200_t *router,u_int slot, struct cisco_eeprom *eeprom); /* Get network IRQ for specified slot/port */ u_int c7200_net_irq_for_slot_port(u_int slot,u_int port); /* Get register offset for the specified slot */ u_int dev_c7200_net_get_reg_offset(u_int slot); /* Update network interrupt status */ void dev_c7200_net_update_irq(c7200_t *router); /* Show the list of available PA drivers */ void c7200_pa_show_drivers(void); /* Get an NPE driver */ struct c7200_npe_driver *c7200_npe_get_driver(char *npe_type); /* Set the NPE type */ int c7200_npe_set_type(c7200_t *router,char *npe_type); /* Set Midplane type */ int c7200_midplane_set_type(c7200_t *router,char *midplane_type); /* Set chassis MAC address */ int c7200_midplane_set_mac_addr(c7200_t *router,char *mac_addr); /* Show C7200 hardware info */ void c7200_show_hardware(c7200_t *router); /* dev_c7200_iofpga_init() */ int dev_c7200_iofpga_init(c7200_t *router,m_uint64_t paddr,m_uint32_t len); /* Register the c7200 platform */ int c7200_platform_register(void); /* Set the system id */ int c7200_set_system_id(c7200_t *router,char *id); /* Burn the system id into the appropriate eeprom if possible */ int c7200_refresh_systemid(c7200_t *router); /* Hypervisor C7200 initialization */ extern int hypervisor_c7200_init(vm_platform_t *platform); /* PA drivers */ extern struct cisco_card_driver dev_c7200_npeg2_driver; extern struct cisco_card_driver dev_c7200_iocard_fe_driver; extern struct cisco_card_driver dev_c7200_iocard_2fe_driver; extern struct cisco_card_driver dev_c7200_iocard_ge_e_driver; extern struct cisco_card_driver dev_c7200_pa_fe_tx_driver; extern struct cisco_card_driver dev_c7200_pa_2fe_tx_driver; extern struct cisco_card_driver dev_c7200_pa_ge_driver; extern struct cisco_card_driver dev_c7200_pa_4e_driver; extern struct cisco_card_driver dev_c7200_pa_8e_driver; extern struct cisco_card_driver dev_c7200_pa_4t_driver; extern struct cisco_card_driver dev_c7200_pa_8t_driver; extern struct cisco_card_driver dev_c7200_pa_a1_driver; extern struct cisco_card_driver dev_c7200_pa_pos_oc3_driver; extern struct cisco_card_driver dev_c7200_pa_4b_driver; extern struct cisco_card_driver dev_c7200_pa_mc8te1_driver; extern struct cisco_card_driver dev_c7200_jcpa_driver; #endif dynamips-0.2.14/common/dev_c7200_bri.c000066400000000000000000000636141241034141600173110ustar00rootroot00000000000000/* * Cisco router Simulation Platform. * Copyright (C) 2005-2006 Christophe Fillot. All rights reserved. * * EEPROM types: * - 0x3d: PA-4B * - 0x3e: PA-8B * * Vernon Missouri offered a PA-4B. * * It is based on the Munich32 chip: * http://www.infineon.com//upload/Document/cmc_upload/migrated_files/document_files/Datasheet/m32_34m.pdf * * There is also one TP3420A per BRI port. */ #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_c7200.h" /* Debugging flags */ #define DEBUG_ACCESS 0 #define DEBUG_TRANSMIT 0 #define DEBUG_RECEIVE 0 /* PCI vendor/product codes */ #define BRI_PCI_VENDOR_ID 0x10ee #define BRI_PCI_PRODUCT_ID 0x4013 /* Memory used by the munich32 chip */ #define MUNICH32_MEM_SIZE 0x40000 /* Maximum packet size */ #define M32_MAX_PKT_SIZE 8192 /* 32 timeslots and 32 channels for a Munich32 chip */ #define M32_NR_TIMESLOTS 32 #define M32_NR_CHANNELS 32 /* Offsets */ #define M32_OFFSET_TS 0x0c /* Timeslots */ #define M32_OFFSET_CHAN 0x8c /* Channel specification */ #define M32_OFFSET_CRDA 0x28c /* Current RX descriptor address */ #define M32_OFFSET_CTDA 0x30c /* Current TX descriptor address */ /* Action Specification */ #define M32_AS_PCM_MASK 0xE0000000 /* PCM Highway Format */ #define M32_AS_PCM_SHIFT 29 #define M32_AS_MFL_MASK 0x1FFF0000 /* Maximum Frame Length */ #define M32_AS_MFL_SHIFT 16 #define M32_AS_IN 0x00008000 /* Initialization Procedure */ #define M32_AS_ICO 0x00004000 /* Initialize Channel Only */ #define M32_AS_CHAN_MASK 0x00001F00 /* Channel Number */ #define M32_AS_CHAN_SHIFT 8 #define M32_AS_IM 0x00000080 /* Interrupt Mask */ #define M32_AS_RES 0x00000040 /* Reset */ #define M32_AS_LOOPS_MASK 0x00000038 /* Loops (LOC,LOOP,LOOPI) */ #define M32_AS_LOOPS_SHIFT 3 #define M32_AS_IA 0x00000004 /* Interrupt Attention */ /* Interrupt Information */ #define M32_II_INT 0x80000000 /* Interrupt */ #define M32_II_VN3 0x20000000 /* Silicon version number */ #define M32_II_VN2 0x10000000 #define M32_II_VN1 0x08000000 #define M32_II_FRC 0x04000000 /* Framing bits changed */ #define M32_II_ARACK 0x00008000 /* Action Request Acknowledge */ #define M32_II_ARF 0x00004000 /* Action Request Failed */ #define M32_II_HI 0x00002000 /* Host Initiated Interrupt */ #define M32_II_FI 0x00001000 /* Frame Indication */ #define M32_II_IFC 0x00000800 /* Idle Flag Change */ #define M32_II_SF 0x00000400 /* Short Frame */ #define M32_II_ERR 0x00000200 /* Error condition */ #define M32_II_FO 0x00000100 /* Overflow/Underflow */ #define M32_II_RT 0x00000020 /* Direction (Transmit/Receive Int) */ /* Timeslot Assignment */ #define M32_TS_TTI 0x20000000 /* Transmit Timeslot Inhibit */ #define M32_TS_TCN_MASK 0x1F000000 /* Transmit Channel Number Mask */ #define M32_TS_TCN_SHIFT 24 #define M32_TS_TFM_MASK 0x00FF0000 /* Transmit Fill Mask */ #define M32_TS_TFM_SHIFT 16 #define M32_TS_RTI 0x00002000 /* Receive Timeslot Inhibit */ #define M32_TS_RCN_MASK 0x00001F00 /* Receive Channel Number Mask */ #define M32_TS_RCN_SHIFT 8 #define M32_TS_RFM_MASK 0x000000FF /* Receive Fill Mask */ #define M32_TS_RFM_SHIFT 0 /* Transmit Descriptor */ #define M32_TXDESC_FE 0x80000000 /* Frame End */ #define M32_TXDESC_HOLD 0x40000000 /* Hold=0: usable by Munich */ #define M32_TXDESC_HI 0x20000000 /* Host Initiated Interrupt */ #define M32_TXDESC_NO_MASK 0x1FFF0000 /* Number of bytes */ #define M32_TXDESC_NO_SHIFT 16 #define M32_TXDESC_V110 0x00008000 /* V.110/X.30 frame */ #define M32_TXDESC_CSM 0x00000800 /* CRC Select per Message */ #define M32_TXDESC_FNUM 0x000001FF /* Inter-Frame Time-Fill chars */ /* Munich32 TX descriptor */ struct m32_tx_desc { m_uint32_t params; /* Size + Flags */ m_uint32_t tdp; /* Transmit Data Pointer */ m_uint32_t ntdp; /* Next Transmit Descriptor Pointer */ }; /* Receive Descriptor (parameters) */ #define M32_RXDESC_HOLD 0x40000000 /* Hold */ #define M32_RXDESC_HI 0x20000000 /* Host Initiated Interrupt */ #define M32_RXDESC_NO_MASK 0x1FFF0000 /* Size of receive data section */ #define M32_RXDESC_NO_SHIFT 16 /* Receive Descriptor (status) */ #define M32_RXDESC_FE 0x80000000 /* Frame End */ #define M32_RXDESC_C 0x40000000 #define M32_RXDESC_BNO_MASK 0x1FFF0000 /* Bytes stored in data section */ #define M32_RXDESC_BNO_SHIFT 16 #define M32_RXDESC_SF 0x00004000 #define M32_RXDESC_LOSS 0x00002000 /* Error in sync pattern */ #define M32_RXDESC_CRCO 0x00001000 /* CRC error */ #define M32_RXDESC_NOB 0x00000800 /* Bit content not divisible by 8 */ #define M32_RXDESC_LFD 0x00000400 /* Long Frame Detected */ #define M32_RXDESC_RA 0x00000200 /* Receive Abort */ #define M32_RXDESC_ROF 0x00000100 /* Overflow of internal buffer */ /* Munich32 RX descriptor */ struct m32_rx_desc { m_uint32_t params; /* RX parameters (hold, hi, ...) */ m_uint32_t status; /* Status */ m_uint32_t rdp; /* Receive Data Pointer */ m_uint32_t nrdp; /* Next Receive Descriptor Pointer */ }; /* Munich32 channel */ struct m32_channel { m_uint32_t status; m_uint32_t frda; m_uint32_t ftda; m_uint32_t itbs; /* Physical addresses of current RX and TX descriptors */ m_uint32_t rx_current,tx_current; /* Poll mode */ u_int poll_mode; }; /* Munich32 chip data */ struct m32_data { /* Virtual machine */ vm_instance_t *vm; /* TX ring scanner task id */ ptask_id_t tx_tid; /* Interrupt Queue */ m_uint32_t iq_base_addr; m_uint32_t iq_cur_addr; u_int iq_size; /* Timeslots */ m_uint32_t timeslots[M32_NR_TIMESLOTS]; /* Channels */ struct m32_channel channels[M32_NR_CHANNELS]; /* Embedded config memory */ m_uint32_t cfg_mem[MUNICH32_MEM_SIZE/4]; }; /* === TP3420 SID === */ /* Activation / Desactivation */ #define TP3420_SID_NOP 0xFF /* No Operation */ #define TP3420_SID_PDN 0x00 /* Power Down */ #define TP3420_SID_PUP 0x20 /* Power Up */ #define TP3420_SID_DR 0x01 /* Deactivation Request */ #define TP3420_SID_FI2 0x02 /* Force Info 2 (NT Only) */ #define TP3420_SID_MMA 0x1F /* Monitor Mode Activation */ /* Device Modes */ #define TP3420_SID_NTA 0x04 /* NT Mode, Adaptive Sampling */ #define TP3420_SID_NTF 0x05 /* NT Mode, Fixed Sampling */ #define TP3420_SID_TES 0x06 /* TE Mode, Digital System Interface Slave */ #define TP3420_SID_TEM 0x07 /* TE Mode, Digital System Interface Master */ /* Digital Interface Formats */ #define TP3420_SID_DIF1 0x08 /* Digital System Interface Format 1 */ #define TP3420_SID_DIF2 0x09 /* Digital System Interface Format 2 */ #define TP3420_SID_DIF3 0x0A /* Digital System Interface Format 3 */ #define TP3420_SID_DIF4 0x0B /* Digital System Interface Format 4 */ /* BCLK Frequency Settings */ #define TP3420_SID_BCLK1 0x98 /* Set BCLK to 2.048 Mhz */ #define TP3420_SID_BCLK2 0x99 /* Set BCLK to 256 Khz */ #define TP3420_SID_BCLK3 0x9A /* Set BCLK to 512 Khz */ #define TP3420_SID_BCLK4 0x9B /* Set BCLK to 2.56 Mhz */ /* B Channel Exchange */ #define TP3420_SID_BDIR 0x0C /* B Channels Mapped Direct (B1->B1,B2->B2) */ #define TP3420_SID_BEX 0x0D /* B Channels Exchanged (B1->B2,B2->B1) */ /* D Channel Access */ #define TP3420_SID_DREQ1 0x0E /* D Channel Request, Class 1 Message */ #define TP3420_SID_DREQ2 0x0F /* D Channel Request, Class 2 Message */ /* D Channel Access Control */ #define TP3420_SID_DACCE 0x90 /* Enable D-Channel Access Mechanism */ #define TP3420_SID_DACCD 0x91 /* Disable D-Channel Access Mechanism */ #define TP3420_SID_EBIT0 0x96 /* Force Echo Bit to 0 */ #define TP3420_SID_EBITI 0x97 /* Force Echo Bit to Inverted Received D bit */ #define TP3420_SID_EBITN 0x9C /* Reset EBITI and EBIT0 to Normal Condition */ #define TP3420_SID_DCKE 0xF1 /* D Channel Clock Enable */ /* End Of Message (EOM) Interrupt */ #define TP3420_SID_EIE 0x10 /* EOM Interrupt Enabled */ #define TP3420_SID_EID 0x11 /* EOM Interrupt Disabled */ /* B1 Channel Enable/Disable */ #define TP3420_SID_B1E 0x14 /* B1 Channel Enabled */ #define TP3420_SID_B1D 0x15 /* B1 Channel Disabled */ /* B2 Channel Enable/Disable */ #define TP3420_SID_B2E 0x16 /* B2 Channel Enabled */ #define TP3420_SID_B2D 0x17 /* B2 Channel Disabled */ /* Loopback Tests Modes */ #define TP3420_SID_CAL 0x1B /* Clear All Loopbacks */ /* Control Device State Reading */ #define TP3420_SID_ENST 0x92 /* Enable the Device State Output on NOCST */ #define TP3420_SID_DISST 0x93 /* Disable the Device State Output on NOCST */ /* PIN Signal Selection */ #define TP3420_SID_PINDEF 0xE0 /* Redefine PIN signals */ /* TP3420 Status Register */ #define TP3420_SR_LSD 0x02 /* Line Signal Detected Far-End */ #define TP3420_SR_AP 0x03 /* Activation Pending */ #define TP3420_SR_AI 0x0C /* Activation Indication */ #define TP3420_SR_EI 0x0E /* Error Indication */ #define TP3420_SR_DI 0x0F /* Deactivation Indication */ #define TP3420_SR_EOM 0x06 /* End of D-channel TX message */ #define TP3420_SR_CON 0x07 /* Lost Contention for D channel */ /* NO Change Return status */ #define TP3420_SR_NOC 0x00 /* NOC Status after DISST command */ #define TP3420_SR_NOCST 0x80 /* NOC Status after ENST command */ /* BRI Channel Index */ #define BRI_CHAN_INDEX_B1 0 #define BRI_CHAN_INDEX_B2 1 #define BRI_CHAN_INDEX_D 2 /* PA-4B Data */ struct pa_4b_data { char *name; /* Virtual machine */ vm_instance_t *vm; /* Virtual device */ struct vdevice *dev; /* PCI device information */ struct pci_device *pci_dev; /* NetIO descriptor */ netio_desc_t *nio; /* Munich32 data and base offset */ struct m32_data m32_data; u_int m32_offset; }; /* Log a PA-4B/PA-8B message */ #define BRI_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* Read a configuration word */ static inline m_uint32_t m32_get_cfgw(struct m32_data *d,m_uint32_t offset) { return(d->cfg_mem[offset >> 2]); } /* Write a configuration word */ static inline void m32_set_cfgw(struct m32_data *d,m_uint32_t offset, m_uint32_t val) { d->cfg_mem[offset >> 2] = val; } /* Post an interrupt into the interrupt queue */ static int m32_post_interrupt(struct m32_data *d,m_uint32_t iq_value) { if (!d->iq_base_addr) return(-1); /* The INT bit is mandatory */ iq_value |= M32_II_INT; #if 0 printf("M32: Posting interrupt iq_val=0x%8.8x at 0x%8.8x\n", iq_value,d->iq_cur_addr); #endif physmem_copy_u32_to_vm(d->vm,d->iq_cur_addr,iq_value); d->iq_cur_addr += sizeof(m_uint32_t); if (d->iq_cur_addr >= (d->iq_base_addr + d->iq_size)) d->iq_cur_addr = d->iq_base_addr; return(0); } /* Fetch a timeslot assignment */ _unused static int m32_fetch_ts_assign(struct m32_data *d,u_int ts_id) { m_uint32_t offset; offset = M32_OFFSET_TS + (ts_id * sizeof(m_uint32_t)); d->timeslots[ts_id] = m32_get_cfgw(d,offset); return(0); } /* Fetch all timeslot assignments */ static int m32_fetch_all_ts(struct m32_data *d) { m_uint32_t offset = M32_OFFSET_TS; u_int i; for(i=0;itimeslots[i] = m32_get_cfgw(d,offset); return(0); } /* Show timeslots assignments (debugging) */ _maybe_used static void m32_show_ts_assign(struct m32_data *d) { m_uint32_t ts; u_int i; printf("MUNICH32 timeslots:\n"); for(i=0;itimeslots[i]; if ((ts & (M32_TS_TTI|M32_TS_RTI)) != (M32_TS_TTI|M32_TS_RTI)) { printf(" Timeslot %2u: ",i); if (!(ts & M32_TS_TTI)) { printf("TCN=%2u TFM=0x%2.2x ", (ts & M32_TS_TCN_MASK) >> M32_TS_TCN_SHIFT, (ts & M32_TS_TFM_MASK) >> M32_TS_TFM_SHIFT); } if (!(ts & M32_TS_RTI)) { printf("RCN=%2u RFM=0x%2.2x", (ts & M32_TS_RCN_MASK) >> M32_TS_RCN_SHIFT, (ts & M32_TS_RFM_MASK) >> M32_TS_RFM_SHIFT); } printf("\n"); } } printf("\n"); } /* Show info about a channels (debugging) */ static void m32_show_channel(struct m32_data *d,u_int chan_id) { struct m32_channel *chan; chan = &d->channels[chan_id]; printf("M32 Channel %u:\n",chan_id); printf(" Status : 0x%8.8x\n",chan->status); printf(" FRDA : 0x%8.8x\n",chan->frda); printf(" FTDA : 0x%8.8x\n",chan->ftda); printf(" ITBS : 0x%8.8x\n",chan->itbs); } /* Fetch a channel specification */ static int m32_fetch_chan_spec(struct m32_data *d,u_int chan_id) { struct m32_channel *chan; m_uint32_t offset; offset = M32_OFFSET_CHAN + (chan_id * 4 * sizeof(m_uint32_t)); chan = &d->channels[chan_id]; chan->status = m32_get_cfgw(d,offset); chan->frda = m32_get_cfgw(d,offset+4); chan->ftda = m32_get_cfgw(d,offset+8); chan->itbs = m32_get_cfgw(d,offset+12); chan->poll_mode = 0; chan->rx_current = chan->frda; chan->tx_current = chan->ftda; m32_set_cfgw(d,M32_OFFSET_CTDA+(chan_id*4),chan->tx_current); #if 1 if (chan_id == 2) { printf("M32: Fetched channel %u\n",chan_id); //m32_show_ts_assign(d); m32_show_channel(d,chan_id); } #endif return(0); } /* Fetch all channel specifications */ static void m32_fetch_all_chan_spec(struct m32_data *d) { u_int i; for(i=0;ivm,txd_addr); if (!(params & M32_TXDESC_HOLD)) return(FALSE); txd->params = params; txd->tdp = physmem_copy_u32_from_vm(d->vm,txd_addr+4); txd->ntdp = physmem_copy_u32_from_vm(d->vm,txd_addr+8); return(TRUE); } /* Try to acquire the next TX descriptor */ static int m32_tx_acquire_next(struct m32_data *d,m_uint32_t *txd_addr) { m_uint32_t params; /* HOLD bit must be reset */ params = physmem_copy_u32_from_vm(d->vm,*txd_addr); if ((params & M32_TXDESC_HOLD)) return(FALSE); *txd_addr = physmem_copy_u32_from_vm(d->vm,(*txd_addr)+8); return(TRUE); } /* Scan a channel TX ring */ static inline int m32_tx_scan(struct m32_data *d,u_int chan_id) { struct m32_channel *chan = &d->channels[chan_id]; m_uint8_t pkt[M32_MAX_PKT_SIZE]; struct m32_tx_desc txd; m_uint32_t pkt_len; if (!chan->tx_current) return(FALSE); switch(chan->poll_mode) { case 0: m32_set_cfgw(d,M32_OFFSET_CTDA+(chan_id*4),chan->tx_current); /* Try to transmit data */ if (!m32_tx_acquire(d,chan->tx_current,&txd)) return(FALSE); printf("M32: TX scanner for channel %u (tx_current=0x%8.8x)\n", chan_id,chan->tx_current); printf("M32: params=0x%8.8x, next=0x%8.8x.\n",txd.params,txd.ntdp); /* The descriptor has been acquired */ pkt_len = (txd.params & M32_TXDESC_NO_MASK) >> M32_TXDESC_NO_SHIFT; physmem_copy_from_vm(d->vm,pkt,txd.tdp,pkt_len); printf("M32: data_ptr=0x%x, len=%u\n",txd.tdp,pkt_len); mem_dump(stdout,pkt,pkt_len); /* Poll the next descriptor (wait for HOLD bit to be reset) */ chan->poll_mode = 1; if (txd.params & M32_TXDESC_FE) { m32_post_interrupt(d,M32_II_FI | chan_id); vm_set_irq(d->vm,2); } break; case 1: if (!m32_tx_acquire_next(d,&chan->tx_current)) return(FALSE); printf("M32: branching on next descriptor 0x%x\n",chan->tx_current); chan->poll_mode = 0; break; } return(TRUE); } /* Scan the all channel TX rings */ static void m32_tx_scan_all_channels(struct m32_data *d) { u_int i; for(i=0;iiq_base_addr = d->iq_cur_addr = m32_get_cfgw(d,4); d->iq_size = ((m32_get_cfgw(d,8) & 0xFF) + 1) * 16 * sizeof(m_uint32_t); } /* Initialization Procedure */ if (action & M32_AS_IN) { /* Fetch all timeslots assignments */ m32_fetch_all_ts(d); /* Fetch specification of the specified channel */ chan_id = (action & M32_AS_CHAN_MASK) >> M32_AS_CHAN_SHIFT; m32_fetch_chan_spec(d,chan_id); /* Generate acknowledge */ if (!(action & M32_AS_IM)) m32_post_interrupt(d,M32_II_ARACK); } /* Initialize Channel Only */ if (action & M32_AS_ICO) { /* Fetch specification of the specified channel */ chan_id = (action & M32_AS_CHAN_MASK) >> M32_AS_CHAN_SHIFT; m32_fetch_chan_spec(d,chan_id); /* Generate acknowledge */ if (!(action & M32_AS_IM)) m32_post_interrupt(d,M32_II_ARACK); } /* Reset */ if (action & M32_AS_RES) { /* Fetch all timeslots assignments */ m32_fetch_all_ts(d); /* Fetch all channel specifications */ m32_fetch_all_chan_spec(d); /* Generate acknowledge */ if (!(action & M32_AS_IM)) m32_post_interrupt(d,M32_II_ARACK); } return(0); } /* Munich32 general access function */ static void *m32_gen_access(struct m32_data *d,cpu_gen_t *cpu, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { switch(offset) { /* Action Specification */ case 0x0: if (op_type == MTS_WRITE) m32_action_req(d,*data); return NULL; /* Configuration memory */ default: switch(op_size) { case 4: if (op_type == MTS_READ) *data = m32_get_cfgw(d,offset); else m32_set_cfgw(d,offset,*data); break; case 1: if (op_type == MTS_READ) { *data = m32_get_cfgw(d,offset & ~0x03); *data >>= (24 - ((offset & 0x03) << 3)); *data &= 0xFF; } else { printf("UNSUPPORTED(1)!!!!\n"); } break; case 2: if (op_type == MTS_READ) { *data = m32_get_cfgw(d,offset & ~0x03); *data >>= (16 - ((offset & 0x03) << 3)); *data &= 0xFFFF; } else { printf("UNSUPPORTED(2)!!!!\n"); } break; case 8: if (op_type == MTS_READ) { *data = (m_uint64_t)m32_get_cfgw(d,offset) << 32; *data |= m32_get_cfgw(d,offset+4); } else { printf("UNSUPPORTED(8)!!!!\n"); } break; default: printf("UNSUPPORTED (size=%u)!!!\n",op_size); } } return NULL; } /* * pa_4b_access() */ void *pa_4b_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct pa_4b_data *d = dev->priv_data; _maybe_used static m_uint32_t test1,test2,test3; if (op_type == MTS_READ) *data = 0xFFFFFFFF; #if DEBUG_ACCESS if (offset >= MUNICH32_MEM_SIZE) { if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to offset = 0x%x, pc = 0x%llx " "(op_size=%u)\n",offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name,"write access to vaddr = 0x%x, pc = 0x%llx, " "val = 0x%llx (op_size=%u)\n", offset,cpu_get_pc(cpu),*data,op_size); } } #endif /* Specific cases */ switch(offset) { case 0x40008: if (op_type == MTS_READ) *data = 0xFF; break; case 0x40030: if (op_type == MTS_READ) *data = 0xFF; break; case 0x40000: if (op_type == MTS_READ) *data = 0xFFFF; break; case 0x40020: if (op_type == MTS_READ) *data = 0xFFFFFFFF; //test2; else test2 = *data; break; case 0x40021: if (op_type == MTS_READ) *data = 0xFF; //test3; else test3 = *data; break; case 0x40023: if (op_type == MTS_READ) *data = 0xFF; break; case 0x40040: if (op_type == MTS_READ) *data = 0x04; break; /* Channels enabled ? */ case 0x40044: if (op_type == MTS_READ) *data = 0xFF; /* 0x02 */ break; /* SID */ case 0x40050: if (op_type == MTS_WRITE) { test1 = *data; } else { switch(test1) { case TP3420_SID_PUP: *data = TP3420_SR_AI; vm_set_irq(d->vm,C7200_PA_MGMT_IRQ); break; case TP3420_SID_ENST: *data = 0xB0; break; default: *data = 0x03; break; } } break; default: if (offset < MUNICH32_MEM_SIZE) return(m32_gen_access(&d->m32_data,cpu,offset - d->m32_offset, op_size,op_type,data)); } return NULL; } /* * pci_munich32_read() */ static m_uint32_t pci_munich32_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { struct pa_4b_data *d = dev->priv_data; #if DEBUG_ACCESS BRI_LOG(d,"read PCI register 0x%x\n",reg); #endif switch(reg) { case PCI_REG_BAR0: return(d->dev->phys_addr); default: return(0); } } /* * pci_munich32_write() */ static void pci_munich32_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct pa_4b_data *d = dev->priv_data; #if DEBUG_ACCESS BRI_LOG(d,"write 0x%x to PCI register 0x%x\n",value,reg); #endif switch(reg) { case PCI_REG_BAR0: vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); BRI_LOG(d,"registers are mapped at 0x%x\n",value); break; } } /* * dev_c7200_bri_init() * * Add a PA-4B/PA-8B port adapter into specified slot. */ int dev_c7200_pa_bri_init(vm_instance_t *vm,struct cisco_card *card) { u_int slot = card->slot_id; struct pci_device *pci_dev; struct pa_4b_data *d; struct vdevice *dev; /* Allocate the private data structure for PA-4B chip */ if (!(d = malloc(sizeof(*d)))) { vm_error(vm,"%s: out of memory\n",card->dev_name); return(-1); } memset(d,0,sizeof(*d)); d->m32_offset = 0x08; d->m32_data.vm = vm; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_pa("PA-4B")); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Add as PCI device PA-4B */ pci_dev = pci_dev_add(card->pci_bus,card->dev_name, BRI_PCI_VENDOR_ID,BRI_PCI_PRODUCT_ID, 0,0,C7200_NETIO_IRQ,d, NULL,pci_munich32_read,pci_munich32_write); if (!pci_dev) { vm_error(vm,"%s: unable to create PCI device.\n",card->dev_name); return(-1); } /* Create the PA-4B structure */ d->name = card->dev_name; d->pci_dev = pci_dev; d->vm = vm; /* Create the device itself */ if (!(dev = dev_create(card->dev_name))) { vm_error(vm,"%s: unable to create device.\n",card->dev_name); return(-1); } dev->phys_len = 0x800000; dev->handler = pa_4b_access; /* Store device info */ dev->priv_data = d; d->dev = dev; /* Store device info into the router structure */ card->drv_info = d; return(0); } /* Remove a PA-4B from the specified slot */ int dev_c7200_pa_bri_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct pa_4b_data *d = card->drv_info; /* Remove the PA EEPROM */ cisco_card_unset_eeprom(card); c7200_set_slot_eeprom(VM_C7200(vm),card->slot_id,NULL); /* Remove the PCI device */ pci_dev_remove(d->pci_dev); /* Remove the device from the CPU address space */ vm_unbind_device(vm,d->dev); cpu_group_rebuild_mts(vm->cpu_group); /* Free the device structure itself */ free(d->dev); free(d); return(0); } /* Bind a Network IO descriptor to a specific port */ int dev_c7200_pa_bri_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct pa_4b_data *d = card->drv_info; if (!d || (port_id > 0)) return(-1); if (d->nio != NULL) return(-1); d->nio = nio; /* TEST */ d->m32_data.tx_tid = ptask_add((ptask_callback)m32_tx_scan_all_channels, &d->m32_data,NULL); //netio_rxl_add(nio,(netio_rx_handler_t)dev_pa_4b_handle_rxring,d,NULL); return(0); } /* Bind a Network IO descriptor to a specific port */ int dev_c7200_pa_bri_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct pa_4b_data *d = card->drv_info; if (!d || (port_id > 0)) return(-1); if (d->nio) { /* TEST */ ptask_remove(d->m32_data.tx_tid); //netio_rxl_remove(d->nio); d->nio = NULL; } return(0); } /* PA-4B driver */ struct cisco_card_driver dev_c7200_pa_4b_driver = { "PA-4B", 0, 0, dev_c7200_pa_bri_init, dev_c7200_pa_bri_shutdown, NULL, dev_c7200_pa_bri_set_nio, dev_c7200_pa_bri_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c7200_eth.c000066400000000000000000000507431241034141600173140ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Ethernet Port Adapters. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_am79c971.h" #include "dev_dec21140.h" #include "dev_i8254x.h" #include "dev_mv64460.h" #include "dev_c7200.h" /* ====================================================================== */ /* C7200-IO-FE EEPROM */ /* ====================================================================== */ /* C7200-IO-FE: C7200 IOCard with one FastEthernet port EEPROM */ static const m_uint16_t eeprom_c7200_io_fe_data[] = { 0x0183, 0x010E, 0xffff, 0xffff, 0x490B, 0x8C02, 0x0000, 0x0000, 0x5000, 0x0000, 0x9812, 0x2800, 0x00FF, 0xFFFF, 0xFFFF, 0xFFFF, }; static const struct cisco_eeprom eeprom_c7200_io_fe = { "C7200-IO-FE", (m_uint16_t *)eeprom_c7200_io_fe_data, sizeof(eeprom_c7200_io_fe_data)/2, }; /* * dev_c7200_iocard_init() * * Add an IOcard into slot 0. */ static int dev_c7200_iocard_init(vm_instance_t *vm,struct cisco_card *card) { struct dec21140_data *data; u_int slot = card->slot_id; if (slot != 0) { vm_error(vm,"cannot put IOCARD in PA bay %u!\n",slot); return(-1); } /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,&eeprom_c7200_io_fe); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Create the DEC21140 chip */ data = dev_dec21140_init(vm,card->dev_name, card->pci_bus, VM_C7200(vm)->npe_driver->dec21140_pci_dev, c7200_net_irq_for_slot_port(slot,0)); if (!data) return(-1); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove an IOcard from slot 0 */ static int dev_c7200_iocard_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the PA EEPROM */ cisco_card_unset_eeprom(card); c7200_set_slot_eeprom(VM_C7200(vm),card->slot_id,NULL); /* Shutdown the DEC21140 */ dev_dec21140_remove(card->drv_info); return(0); } /* Bind a Network IO descriptor */ static int dev_c7200_iocard_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct dec21140_data *d = card->drv_info; if (!d || (port_id > 0)) return(-1); return(dev_dec21140_set_nio(d,nio)); } /* Unbind a Network IO descriptor */ static int dev_c7200_iocard_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { struct dec21140_data *d = card->drv_info; if (!d || (port_id > 0)) return(-1); dev_dec21140_unset_nio(d); return(0); } /* * dev_c7200_pa_fe_tx_init() * * Add a PA-FE-TX port adapter into specified slot. */ static int dev_c7200_pa_fe_tx_init(vm_instance_t *vm,struct cisco_card *card) { struct dec21140_data *data; u_int slot = card->slot_id; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_pa("PA-FE-TX")); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Create the DEC21140 chip */ data = dev_dec21140_init(vm,card->dev_name,card->pci_bus,0, c7200_net_irq_for_slot_port(slot,0)); if (!data) return(-1); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a PA-FE-TX from the specified slot */ static int dev_c7200_pa_fe_tx_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the PA EEPROM */ cisco_card_unset_eeprom(card); c7200_set_slot_eeprom(VM_C7200(vm),card->slot_id,NULL); /* Shutdown the DEC21140 */ dev_dec21140_remove(card->drv_info); return(0); } /* Bind a Network IO descriptor */ static int dev_c7200_pa_fe_tx_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct dec21140_data *d = card->drv_info; if (!d || (port_id > 0)) return(-1); return(dev_dec21140_set_nio(d,nio)); } /* Unbind a Network IO descriptor */ static int dev_c7200_pa_fe_tx_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct dec21140_data *d = card->drv_info; if (!d || (port_id > 0)) return(-1); dev_dec21140_unset_nio(d); return(0); } /* C7200-IO-FE driver */ struct cisco_card_driver dev_c7200_iocard_fe_driver = { "C7200-IO-FE", 1, 0, dev_c7200_iocard_init, dev_c7200_iocard_shutdown, NULL, dev_c7200_iocard_set_nio, dev_c7200_iocard_unset_nio, NULL, }; /* PA-FE-TX driver */ struct cisco_card_driver dev_c7200_pa_fe_tx_driver = { "PA-FE-TX", 1, 0, dev_c7200_pa_fe_tx_init, dev_c7200_pa_fe_tx_shutdown, NULL, dev_c7200_pa_fe_tx_set_nio, dev_c7200_pa_fe_tx_unset_nio, NULL, }; /* ====================================================================== */ /* PA based on Intel i8254x chips */ /* ====================================================================== */ struct pa_i8254x_data { u_int nr_port; struct i8254x_data *port[2]; }; /* Remove a PA-2FE-TX from the specified slot */ static int dev_c7200_pa_i8254x_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct pa_i8254x_data *data = card->drv_info; int i; /* Remove the PA EEPROM */ cisco_card_unset_eeprom(card); c7200_set_slot_eeprom(VM_C7200(vm),card->slot_id,NULL); /* Remove the Intel i2854x chips */ for(i=0;inr_port;i++) dev_i8254x_remove(data->port[i]); free(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c7200_pa_i8254x_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct pa_i8254x_data *d = card->drv_info; if (!d || (port_id >= d->nr_port)) return(-1); dev_i8254x_set_nio(d->port[port_id],nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c7200_pa_i8254x_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct pa_i8254x_data *d = card->drv_info; if (!d || (port_id >= d->nr_port)) return(-1); dev_i8254x_unset_nio(d->port[port_id]); return(0); } /* ====================================================================== */ /* PA-2FE-TX */ /* ====================================================================== */ /* * dev_c7200_pa_2fe_tx_init() * * Add a PA-2FE-TX port adapter into specified slot. */ static int dev_c7200_pa_2fe_tx_init(vm_instance_t *vm,struct cisco_card *card) { struct pa_i8254x_data *data; u_int slot = card->slot_id; int i; /* Allocate the private data structure for the PA-2FE-TX */ if (!(data = malloc(sizeof(*data)))) { vm_error(vm,"%s: out of memory\n",card->dev_name); return(-1); } /* 2 Ethernet ports */ memset(data,0,sizeof(*data)); data->nr_port = 2; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_pa("PA-2FE-TX")); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Create the Intel i8254x chips */ for(i=0;inr_port;i++) { data->port[i] = dev_i8254x_init(vm,card->dev_name,0, card->pci_bus,i, c7200_net_irq_for_slot_port(slot,i)); } /* Store device info into the router structure */ card->drv_info = data; return(0); } /* PA-2FE-TX driver */ struct cisco_card_driver dev_c7200_pa_2fe_tx_driver = { "PA-2FE-TX", 0, 0, dev_c7200_pa_2fe_tx_init, dev_c7200_pa_i8254x_shutdown, NULL, dev_c7200_pa_i8254x_set_nio, dev_c7200_pa_i8254x_unset_nio, NULL, }; /* ====================================================================== */ /* PA-GE */ /* ====================================================================== */ /* * dev_c7200_pa_ge_init() * * Add a PA-GE port adapter into specified slot. */ static int dev_c7200_pa_ge_init(vm_instance_t *vm,struct cisco_card *card) { struct pa_i8254x_data *data; u_int slot = card->slot_id; /* Allocate the private data structure for the PA-2FE-TX */ if (!(data = malloc(sizeof(*data)))) { vm_error(vm,"%s: out of memory\n",card->dev_name); return(-1); } /* 2 Ethernet ports */ memset(data,0,sizeof(*data)); data->nr_port = 1; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_pa("PA-GE")); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Create the Intel i8254x chip */ data->port[0] = dev_i8254x_init(vm,card->dev_name,0, card->pci_bus,0, c7200_net_irq_for_slot_port(slot,0)); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* PA-GE driver */ struct cisco_card_driver dev_c7200_pa_ge_driver = { "PA-GE", 0, 0, dev_c7200_pa_ge_init, dev_c7200_pa_i8254x_shutdown, NULL, dev_c7200_pa_i8254x_set_nio, dev_c7200_pa_i8254x_unset_nio, NULL, }; /* ====================================================================== */ /* C7200-IO-2FE */ /* ====================================================================== */ /* C7200-IO-2FE/E: C7200 IOCard with two FastEthernet ports EEPROM */ static const m_uint16_t eeprom_c7200_io_2fe_data[] = { 0x04FF, 0x4002, 0x1541, 0x0201, 0xC046, 0x0320, 0x001B, 0xCA06, 0x8249, 0x138B, 0x0642, 0x4230, 0xC18B, 0x3030, 0x3030, 0x3030, 0x3030, 0x0000, 0x0004, 0x0002, 0x0385, 0x1C0D, 0x7F03, 0xCB8F, 0x4337, 0x3230, 0x302D, 0x492F, 0x4F2D, 0x3246, 0x452F, 0x4580, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; static const struct cisco_eeprom eeprom_c7200_io_2fe = { "C7200-IO-2FE", (m_uint16_t *)eeprom_c7200_io_2fe_data, sizeof(eeprom_c7200_io_2fe_data)/2, }; /* * dev_c7200_pa_2fe_tx_init() * * Add a C7200-IO-2FE/E port adapter into specified slot. */ static int dev_c7200_iocard_2fe_init(vm_instance_t *vm,struct cisco_card *card) { struct pa_i8254x_data *data; u_int slot = card->slot_id; /* Allocate the private data structure for the iocard */ if (!(data = malloc(sizeof(*data)))) { vm_error(vm,"%s: out of memory\n",card->dev_name); return(-1); } /* 2 Ethernet ports */ memset(data,0,sizeof(*data)); data->nr_port = 2; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,&eeprom_c7200_io_2fe); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Port Fa0/0 is on PCI Device 1 */ data->port[0] = dev_i8254x_init(vm,card->dev_name,0, card->pci_bus,1, c7200_net_irq_for_slot_port(slot,1)); /* Port Fa0/1 is on PCI Device 0 */ data->port[1] = dev_i8254x_init(vm,card->dev_name,0, card->pci_bus,0, c7200_net_irq_for_slot_port(slot,0)); if (!data->port[0] || !data->port[1]) { dev_i8254x_remove(data->port[0]); dev_i8254x_remove(data->port[1]); free(data); return(-1); } /* Store device info into the router structure */ card->drv_info = data; return(0); } /* C7200-IO-2FE driver */ struct cisco_card_driver dev_c7200_iocard_2fe_driver = { "C7200-IO-2FE", 0, 0, dev_c7200_iocard_2fe_init, dev_c7200_pa_i8254x_shutdown, NULL, dev_c7200_pa_i8254x_set_nio, dev_c7200_pa_i8254x_unset_nio, NULL, }; /* ====================================================================== */ /* C7200-IO-GE-E */ /* ====================================================================== */ /* * C7200-IO-GE+E: C7200 IOCard with 1 GigatEthernet ports * and 1 Ethernet port EEPROM. */ static const m_uint16_t eeprom_c7200_io_ge_e_data[] = { 0x04FF, 0x4002, 0x1641, 0x0201, 0xC046, 0x0320, 0x001B, 0xCA06, 0x8249, 0x138B, 0x0642, 0x4230, 0xC18B, 0x3030, 0x3030, 0x3030, 0x3030, 0x0000, 0x0004, 0x0002, 0x0385, 0x1C0D, 0x7F03, 0xCB8F, 0x4337, 0x3230, 0x302D, 0x492F, 0x4F2D, 0x3246, 0x452F, 0x4580, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; static const struct cisco_eeprom eeprom_c7200_io_ge_e = { "C7200-IO-GE-E", (m_uint16_t *)eeprom_c7200_io_ge_e_data, sizeof(eeprom_c7200_io_ge_e_data)/2, }; /* * dev_c7200_pa_ge_e_tx_init() * * Add a C7200-I/O-GE+E port adapter into specified slot. */ static int dev_c7200_iocard_ge_e_init(vm_instance_t *vm,struct cisco_card *card) { struct pa_i8254x_data *data; u_int slot = card->slot_id; /* Allocate the private data structure for the iocard */ if (!(data = malloc(sizeof(*data)))) { vm_error(vm,"%s: out of memory\n",card->dev_name); return(-1); } /* 2 Ethernet ports */ memset(data,0,sizeof(*data)); data->nr_port = 2; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,&eeprom_c7200_io_ge_e); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Port Gi0/0 is on PCI Device 1 */ data->port[0] = dev_i8254x_init(vm,card->dev_name,0, card->pci_bus,1, c7200_net_irq_for_slot_port(slot,1)); /* Port e0/0 is on PCI Device 0 */ data->port[1] = dev_i8254x_init(vm,card->dev_name,0, card->pci_bus,0, c7200_net_irq_for_slot_port(slot,0)); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* C7200-IO-GE-E driver */ struct cisco_card_driver dev_c7200_iocard_ge_e_driver = { "C7200-IO-GE-E", 0, 0, dev_c7200_iocard_ge_e_init, dev_c7200_pa_i8254x_shutdown, NULL, dev_c7200_pa_i8254x_set_nio, dev_c7200_pa_i8254x_unset_nio, NULL, }; /* ====================================================================== */ /* PA-4E / PA-8E */ /* ====================================================================== */ /* PA-4E/PA-8E data */ struct pa_4e8e_data { u_int nr_port; struct am79c971_data *port[8]; }; /* * dev_c7200_pa_4e_init() * * Add a PA-4E port adapter into specified slot. */ static int dev_c7200_pa_4e_init(vm_instance_t *vm,struct cisco_card *card) { struct pa_4e8e_data *data; u_int slot = card->slot_id; int i; /* Allocate the private data structure for the PA-4E */ if (!(data = malloc(sizeof(*data)))) { vm_error(vm,"%s: out of memory\n",card->dev_name); return(-1); } /* 4 Ethernet ports */ memset(data,0,sizeof(*data)); data->nr_port = 4; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_pa("PA-4E")); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Create the AMD Am79c971 chips */ for(i=0;inr_port;i++) { data->port[i] = dev_am79c971_init(vm,card->dev_name, AM79C971_TYPE_10BASE_T, card->pci_bus,i, c7200_net_irq_for_slot_port(slot,i)); } /* Store device info into the router structure */ card->drv_info = data; return(0); } /* * dev_c7200_pa_8e_init() * * Add a PA-8E port adapter into specified slot. */ static int dev_c7200_pa_8e_init(vm_instance_t *vm,struct cisco_card *card) { struct pa_4e8e_data *data; u_int slot = card->slot_id; int i; /* Allocate the private data structure for the PA-8E */ if (!(data = malloc(sizeof(*data)))) { vm_error(vm,"%s: out of memory\n",card->dev_name); return(-1); } /* 4 Ethernet ports */ memset(data,0,sizeof(*data)); data->nr_port = 8; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_pa("PA-8E")); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Create the AMD Am79c971 chips */ for(i=0;inr_port;i++) { data->port[i] = dev_am79c971_init(vm,card->dev_name, AM79C971_TYPE_10BASE_T, card->pci_bus,i, c7200_net_irq_for_slot_port(slot,i)); } /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a PA-4E/PA-8E from the specified slot */ static int dev_c7200_pa_4e8e_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct pa_4e8e_data *data = card->drv_info; int i; /* Remove the PA EEPROM */ cisco_card_unset_eeprom(card); c7200_set_slot_eeprom(VM_C7200(vm),card->slot_id,NULL); /* Remove the AMD Am79c971 chips */ for(i=0;inr_port;i++) dev_am79c971_remove(data->port[i]); free(data); return(0); } /* Bind a Network IO descriptor */ static int dev_c7200_pa_4e8e_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct pa_4e8e_data *d = card->drv_info; if (!d || (port_id >= d->nr_port)) return(-1); dev_am79c971_set_nio(d->port[port_id],nio); return(0); } /* Unbind a Network IO descriptor */ static int dev_c7200_pa_4e8e_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct pa_4e8e_data *d = card->drv_info; if (!d || (port_id >= d->nr_port)) return(-1); dev_am79c971_unset_nio(d->port[port_id]); return(0); } /* PA-4E driver */ struct cisco_card_driver dev_c7200_pa_4e_driver = { "PA-4E", 1, 0, dev_c7200_pa_4e_init, dev_c7200_pa_4e8e_shutdown, NULL, dev_c7200_pa_4e8e_set_nio, dev_c7200_pa_4e8e_unset_nio, NULL, }; /* PA-8E driver */ struct cisco_card_driver dev_c7200_pa_8e_driver = { "PA-8E", 1, 0, dev_c7200_pa_8e_init, dev_c7200_pa_4e8e_shutdown, NULL, dev_c7200_pa_4e8e_set_nio, dev_c7200_pa_4e8e_unset_nio, NULL, }; /* ====================================================================== */ /* NPE-G2 */ /* ====================================================================== */ /* Initialize NPE-G2 Ethernet ports */ static int dev_c7200_npeg2_init(vm_instance_t *vm,struct cisco_card *card) { /* Nothing to do */ card->drv_info = VM_C7200(vm)->mv64460_sysctr; return(0); } /* Shutdown NPE-G2 Ethernet ports */ static int dev_c7200_npeg2_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Nothing to do */ return(0); } /* Set a NIO descriptor */ static int dev_c7200_npeg2_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { c7200_t *router = VM_C7200(vm); struct mv64460_data *mv_data = router->mv64460_sysctr; if (!mv_data) return(-1); dev_mv64460_eth_set_nio(mv_data,port_id,nio); return(0); } /* Unbind a NIO descriptor */ static int dev_c7200_npeg2_unset_nio(vm_instance_t *vm, struct cisco_card *card, u_int port_id) { c7200_t *router = VM_C7200(vm); struct mv64460_data *mv_data = router->mv64460_sysctr; if (!mv_data) return(-1); dev_mv64460_eth_unset_nio(mv_data,port_id); return(0); } /* NPE-G2 driver */ struct cisco_card_driver dev_c7200_npeg2_driver = { "NPE-G2", 1, 0, dev_c7200_npeg2_init, dev_c7200_npeg2_shutdown, NULL, dev_c7200_npeg2_set_nio, dev_c7200_npeg2_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c7200_iofpga.c000066400000000000000000000464601241034141600200020ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Cisco 7200 I/O FPGA: * - Simulates a NMC93C46 Serial EEPROM as CPU and Midplane EEPROM. * - Simulates a DALLAS DS1620 for Temperature Sensors. * - Simulates voltage sensors. * - Simulates console and AUX ports (SCN2681). */ #include #include #include #include #include #include #include #include #include "ptask.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_vtty.h" #include "nmc93cX6.h" #include "dev_ds1620.h" #include "dev_c7200.h" /* Debugging flags */ #define DEBUG_UNKNOWN 1 #define DEBUG_ACCESS 0 #define DEBUG_LED 0 #define DEBUG_IO_CTL 0 #define DEBUG_ENVM 0 /* DUART RX/TX status (SRA/SRB) */ #define DUART_RX_READY 0x01 #define DUART_TX_READY 0x04 /* DUART RX/TX Interrupt Status/Mask */ #define DUART_TXRDYA 0x01 #define DUART_RXRDYA 0x02 #define DUART_TXRDYB 0x10 #define DUART_RXRDYB 0x20 /* Definitions for CPU and Midplane Serial EEPROMs */ #define EEPROM_CPU_DOUT 6 #define EEPROM_CPU_DIN 0 #define EEPROM_CPU_CLK 1 #define EEPROM_CPU_CS 2 #define EEPROM_MP_DOUT 7 #define EEPROM_MP_DIN 3 #define EEPROM_MP_CLK 4 #define EEPROM_MP_CS 5 /* Definitions for PEM (NPE-B) Serial EEPROM */ #define EEPROM_PEM_DOUT 3 #define EEPROM_PEM_DIN 2 #define EEPROM_PEM_CS 1 #define EEPROM_PEM_CLK 0 /* Pack the NVRAM */ #define NVRAM_PACKED 0x04 /* Temperature: 22°C as default value */ #define C7200_DEFAULT_TEMP 22 #define DS1620_CHIP(d,id) (&(d)->router->ds1620_sensors[(id)]) /* Voltages */ #define C7200_A2D_SAMPLES 9 /* * A2D MUX Select definitions. */ #define C7200_MUX_PS0 0x00 /* Power Supply 0 */ #define C7200_MUX_PS1 0x02 /* Power Supply 1 */ #define C7200_MUX_P3V 0x04 /* +3V */ #define C7200_MUX_P12V 0x08 /* +12V */ #define C7200_MUX_P5V 0x0a /* +5V */ #define C7200_MUX_N12V 0x0c /* -12V */ /* Analog To Digital Converters samples */ #define C7200_A2D_PS0 1150 #define C7200_A2D_PS1 1150 /* Voltage Samples */ #define C7200_A2D_P3V 1150 #define C7200_A2D_P12V 1150 #define C7200_A2D_P5V 1150 #define C7200_A2D_N12V 1150 /* IO FPGA structure */ struct iofpga_data { vm_obj_t vm_obj; struct vdevice dev; c7200_t *router; /* Lock test */ pthread_mutex_t lock; /* Periodic task to trigger dummy DUART IRQ */ ptask_id_t duart_irq_tid; /* DUART & Console Management */ u_int duart_isr,duart_imr,duart_irq_seq; /* IO control register */ u_int io_ctrl_reg; /* Voltages */ u_int mux; /* NPE-G2 environmental part */ m_uint32_t envm_r0,envm_r1,envm_r2; }; #define IOFPGA_LOCK(d) pthread_mutex_lock(&(d)->lock) #define IOFPGA_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) /* CPU EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_cpu_def = { EEPROM_CPU_CLK, EEPROM_CPU_CS, EEPROM_CPU_DIN, EEPROM_CPU_DOUT, }; /* Midplane EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_midplane_def = { EEPROM_MP_CLK, EEPROM_MP_CS, EEPROM_MP_DIN, EEPROM_MP_DOUT, }; /* PEM (NPE-B) EEPROM definition */ static const struct nmc93cX6_eeprom_def eeprom_pem_def = { EEPROM_PEM_CLK, EEPROM_PEM_CS, EEPROM_PEM_DIN, EEPROM_PEM_DOUT, }; /* IOFPGA manages simultaneously CPU and Midplane EEPROM */ static const struct nmc93cX6_group eeprom_cpu_midplane = { EEPROM_TYPE_NMC93C46, 2, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "CPU and Midplane EEPROM", { &eeprom_cpu_def, &eeprom_midplane_def }, }; /* * IOFPGA manages also PEM EEPROM (for NPE-B) * PEM stands for "Power Entry Module": * http://www.cisco.com/en/US/products/hw/routers/ps341/products_field_notice09186a00801cb26d.shtml */ static const struct nmc93cX6_group eeprom_pem_npeb = { EEPROM_TYPE_NMC93C46, 1, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "PEM (NPE-B) EEPROM", { &eeprom_pem_def }, }; /* NPE-G2 environmental monitor reading */ static m_uint32_t g2_envm_read(struct iofpga_data *d) { m_uint32_t val = 0; m_uint32_t p1; p1 = ((d->envm_r2 & 0xFF) << 8) | d->envm_r0 >> 3; switch(p1) { case 0x2a00: /* CPU Die Temperature */ val = 0x3000; break; case 0x4c00: /* +3.30V */ val = 0x2a9; break; case 0x4c01: /* +1.50V */ val = 0x135; break; case 0x4c02: /* +2.50V */ val = 0x204; break; case 0x4c03: /* +1.80V */ val = 0x173; break; case 0x4c04: /* +1.20V */ val = 0xF7; break; case 0x4c05: /* VDD_CPU */ val = 0x108; break; case 0x4800: /* VDD_MEM */ val = 0x204; break; case 0x4801: /* VTT */ val = 0xF9; break; case 0x4802: /* +3.45V */ val = 0x2c8; break; case 0x4803: /* -11.95V */ val = 0x260; break; case 0x4804: /* ? */ val = 0x111; break; case 0x4805: /* ? */ val = 0x111; break; case 0x4806: /* +5.15V */ val = 0x3F8; break; case 0x4807: /* +12.15V */ val = 0x33D; break; #if DEBUG_UNKNOWN default: vm_log(d->router->vm,"IO_FPGA","p1 = 0x%8.8x\n",p1); #endif } return(swap32(val)); } /* Console port input */ static void tty_con_input(vtty_t *vtty) { struct iofpga_data *d = vtty->priv_data; IOFPGA_LOCK(d); if (d->duart_imr & DUART_RXRDYA) { d->duart_isr |= DUART_RXRDYA; vm_set_irq(d->router->vm,C7200_DUART_IRQ); } IOFPGA_UNLOCK(d); } /* AUX port input */ static void tty_aux_input(vtty_t *vtty) { struct iofpga_data *d = vtty->priv_data; IOFPGA_LOCK(d); if (d->duart_imr & DUART_RXRDYB) { d->duart_isr |= DUART_RXRDYB; vm_set_irq(d->router->vm,C7200_DUART_IRQ); } IOFPGA_UNLOCK(d); } /* IRQ trickery for Console and AUX ports */ static int tty_trigger_dummy_irq(struct iofpga_data *d,void *arg) { u_int mask; IOFPGA_LOCK(d); d->duart_irq_seq++; if (d->duart_irq_seq == 2) { mask = DUART_TXRDYA|DUART_TXRDYB; if (d->duart_imr & mask) { d->duart_isr |= DUART_TXRDYA|DUART_TXRDYB; vm_set_irq(d->router->vm,C7200_DUART_IRQ); } d->duart_irq_seq = 0; } IOFPGA_UNLOCK(d); return(0); } /* * dev_c7200_iofpga_access() */ void *dev_c7200_iofpga_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct iofpga_data *d = dev->priv_data; c7200_t *router = d->router; vm_instance_t *vm = router->vm; u_char odata; int i; if (op_type == MTS_READ) *data = 0x0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA","reading reg 0x%x at pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"IO_FPGA","writing reg 0x%x at pc=0x%llx, data=0x%llx\n", offset,cpu_get_pc(cpu),*data); } #endif IOFPGA_LOCK(d); switch(offset) { /* PA Status Reg #2 (for slot 7) */ case 0x2d4: if (op_type == MTS_READ) *data = router->pa_status_reg[1]; break; /* PA Control Reg #2 (for slot 7) */ case 0x2dc: if (op_type == MTS_READ) router->pa_ctrl_reg[1] = *data; else *data = router->pa_ctrl_reg[1]; break; /* EEPROM for PA in slot 7 */ case 0x2e4: if (op_type == MTS_WRITE) nmc93cX6_write(&router->pa_eeprom_g3,*data); else *data = nmc93cX6_read(&router->pa_eeprom_g3); break; /* Network Interrupt for slot 7 */ case 0x294: if (op_type == MTS_READ) *data = router->net_irq_status[2]; break; /* Interrupt mask for slot 7 */ case 0x2a4: if (op_type == MTS_READ) { *data = router->net_irq_mask[2]; } else { router->net_irq_mask[2] = *data; dev_c7200_net_update_irq(router); } break; /* OIR status for slot 7 (bit 24, other bits unknown) */ case 0x2f4: if (op_type == MTS_READ) { *data = router->oir_status[1]; } else { router->oir_status[1] &= ~(*data); } break; /* NPE-G1 test - unknown (value written: 0x01) */ case 0x338: break; /* * NPE-G1/NPE-G2 - has influence on slot 0 / flash / pcmcia ... * Bit 24: 1=I/O slot present * Lower 16 bits: FPGA version (displayed by "sh c7200") */ case 0x390: if (op_type == MTS_READ) { *data = 0x0102; /* If we have an I/O slot, we use the I/O slot DUART */ if (c7200_slot0_iocard_present(router)) *data |= 0x01000000; } break; /* I/O control register */ case 0x204: if (op_type == MTS_WRITE) { #if DEBUG_IO_CTL vm_log(vm,"IO_FPGA","setting value 0x%llx in io_ctrl_reg\n",*data); #endif d->io_ctrl_reg = *data; } else { *data = d->io_ctrl_reg; *data |= NVRAM_PACKED; /* Packed NVRAM */ } break; /* CPU/Midplane EEPROMs */ case 0x21c: if (op_type == MTS_WRITE) nmc93cX6_write(&d->router->sys_eeprom_g1,(u_int)(*data)); else *data = nmc93cX6_read(&d->router->sys_eeprom_g1); break; /* PEM (NPE-B) EEPROM */ case 0x388: if (op_type == MTS_WRITE) nmc93cX6_write(&d->router->sys_eeprom_g2,(u_int)(*data)); else *data = nmc93cX6_read(&d->router->sys_eeprom_g2); break; /* Watchdog */ case 0x234: break; /* * FPGA release/presence ? Flash SIMM size: * 0x0001: 2048K Flash (2 banks) * 0x0504: 8192K Flash (2 banks) * 0x0704: 16384K Flash (2 banks) * 0x0904: 32768K Flash (2 banks) * 0x0B04: 65536K Flash (2 banks) * 0x2001: 1024K Flash (1 bank) * 0x2504: 4096K Flash (1 bank) * 0x2704: 8192K Flash (1 bank) * 0x2904: 16384K Flash (1 bank) * 0x2B04: 32768K Flash (1 bank) * * Number of Flash SIMM banks + size. * Touching some lower bits causes problems with environmental monitor. * * It is displayed by command "sh bootflash: chips" */ case 0x23c: if (op_type == MTS_READ) *data = 0x2704; break; /* LEDs */ case 0x244: #if DEBUG_LED vm_log(vm,"IO_FPGA","LED register is now 0x%x (0x%x)\n", *data,(~*data) & 0x0F); #endif break; /* ==== DUART SCN2681 (console/aux) ==== */ case 0x404: /* Mode Register A (MRA) */ break; case 0x40c: /* Status Register A (SRA) */ if (op_type == MTS_READ) { odata = 0; if (vtty_is_char_avail(vm->vtty_con)) odata |= DUART_RX_READY; odata |= DUART_TX_READY; vm_clear_irq(vm,C7200_DUART_IRQ); *data = odata; } break; case 0x414: /* Command Register A (CRA) */ /* Disable TX = High */ if ((op_type == MTS_WRITE) && (*data & 0x8)) { vm->vtty_con->managed_flush = TRUE; vtty_flush(vm->vtty_con); } break; case 0x41c: /* RX/TX Holding Register A (RHRA/THRA) */ if (op_type == MTS_WRITE) { vtty_put_char(vm->vtty_con,(char)*data); d->duart_isr &= ~DUART_TXRDYA; } else { *data = vtty_get_char(vm->vtty_con); d->duart_isr &= ~DUART_RXRDYA; } break; case 0x424: /* WRITE: Aux Control Register (ACR) */ break; case 0x42c: /* Interrupt Status/Mask Register (ISR/IMR) */ if (op_type == MTS_WRITE) { d->duart_imr = *data; } else *data = d->duart_isr; break; case 0x434: /* Counter/Timer Upper Value (CTU) */ case 0x43c: /* Counter/Timer Lower Value (CTL) */ case 0x444: /* Mode Register B (MRB) */ break; case 0x44c: /* Status Register B (SRB) */ if (op_type == MTS_READ) { odata = 0; if (vtty_is_char_avail(vm->vtty_aux)) odata |= DUART_RX_READY; odata |= DUART_TX_READY; //vm_clear_irq(vm,C7200_DUART_IRQ); *data = odata; } break; case 0x454: /* Command Register B (CRB) */ /* Disable TX = High */ if ((op_type == MTS_WRITE) && (*data & 0x8)) { vm->vtty_aux->managed_flush = TRUE; vtty_flush(vm->vtty_aux); } break; case 0x45c: /* RX/TX Holding Register B (RHRB/THRB) */ if (op_type == MTS_WRITE) { vtty_put_char(vm->vtty_aux,(char)*data); d->duart_isr &= ~DUART_TXRDYA; } else { *data = vtty_get_char(vm->vtty_aux); d->duart_isr &= ~DUART_RXRDYB; } break; case 0x46c: /* WRITE: Output Port Configuration Register (OPCR) */ case 0x474: /* READ: Start Counter Command; */ /* WRITE: Set Output Port Bits Command */ case 0x47c: /* WRITE: Reset Output Port Bits Command */ break; /* ==== DS 1620 (temp sensors) ==== */ case 0x20c: /* Temperature Control */ if (op_type == MTS_WRITE) { for(i=0;i> i) & 0x01); ds1620_set_clk_bit(DS1620_CHIP(d,i),(*data >> 4) & 0x01); } } break; case 0x214: /* Temperature data write */ if (op_type == MTS_WRITE) { d->mux = *data; for(i=0;imux); #endif if (op_type == MTS_READ) *data = 0xFFFFFFFF; break; case 0x257: /* ENVM A/D Converter */ #if DEBUG_ENVM vm_log(vm,"ENVM","access to envm a/d converter - mux = %u\n",d->mux); #endif if (op_type == MTS_READ) { switch(d->mux) { case C7200_MUX_PS0: if (router->ps_status & 0x1) *data = C7200_A2D_PS0; break; case C7200_MUX_PS1: if (router->ps_status & 0x2) *data = C7200_A2D_PS1; break; case C7200_MUX_P3V: *data = C7200_A2D_P3V; break; case C7200_MUX_P12V: *data = C7200_A2D_P12V; break; case C7200_MUX_P5V: *data = C7200_A2D_P5V; break; case C7200_MUX_N12V: *data = C7200_A2D_N12V; break; default: *data = 0; } *data = *data / C7200_A2D_SAMPLES; } break; /* NPE-G2 environmental monitor reading */ case 0x3c0: if (op_type == MTS_READ) *data = 0; break; case 0x3c4: if (op_type == MTS_WRITE) d->envm_r0 = swap32(*data); break; case 0x3c8: if (op_type == MTS_WRITE) { d->envm_r1 = swap32(*data); } else { *data = g2_envm_read(d); } break; case 0x3cc: if (op_type == MTS_WRITE) d->envm_r2 = swap32(*data); break; /* PCMCIA status ? */ case 0x3d6: if (op_type == MTS_READ) *data = 0x33; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"IO_FPGA","read from addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"IO_FPGA","write to addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } IOFPGA_UNLOCK(d); return NULL; } /* Initialize system EEPROM groups */ void c7200_init_sys_eeprom_groups(c7200_t *router) { router->sys_eeprom_g1 = eeprom_cpu_midplane; router->sys_eeprom_g2 = eeprom_pem_npeb; router->sys_eeprom_g1.eeprom[0] = &router->cpu_eeprom; router->sys_eeprom_g1.eeprom[1] = &router->mp_eeprom; router->sys_eeprom_g2.eeprom[0] = &router->pem_eeprom; } /* Shutdown the IO FPGA device */ void dev_c7200_iofpga_shutdown(vm_instance_t *vm,struct iofpga_data *d) { if (d != NULL) { IOFPGA_LOCK(d); vm->vtty_con->read_notifier = NULL; vm->vtty_aux->read_notifier = NULL; IOFPGA_UNLOCK(d); /* Remove the dummy IRQ periodic task */ ptask_remove(d->duart_irq_tid); /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* * dev_c7200_iofpga_init() */ int dev_c7200_iofpga_init(c7200_t *router,m_uint64_t paddr,m_uint32_t len) { vm_instance_t *vm = router->vm; struct iofpga_data *d; u_int i; /* Allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"IO_FPGA: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); pthread_mutex_init(&d->lock,NULL); d->router = router; for(i=0;ivm_obj); d->vm_obj.name = "io_fpga"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_c7200_iofpga_shutdown; /* Set device properties */ dev_init(&d->dev); d->dev.name = "io_fpga"; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_c7200_iofpga_access; d->dev.priv_data = d; /* If we have an I/O slot, we use the I/O slot DUART */ if (c7200_slot0_iocard_present(router)) { vm_log(vm,"CONSOLE","console managed by I/O board\n"); /* Set console and AUX port notifying functions */ vm->vtty_con->priv_data = d; vm->vtty_aux->priv_data = d; vm->vtty_con->read_notifier = tty_con_input; vm->vtty_aux->read_notifier = tty_aux_input; /* Trigger periodically a dummy IRQ to flush buffers */ d->duart_irq_tid = ptask_add((ptask_callback)tty_trigger_dummy_irq, d,NULL); } /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_c7200_jcpa.c000066400000000000000000000035411241034141600174430ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * Dummy module for c7200 jacket card. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_c7200.h" /* * dev_c7200_jcpa_init() * * Add a c7200 jacket card into slot 0. */ static int dev_c7200_jcpa_init(vm_instance_t *vm,struct cisco_card *card) { u_int slot = card->slot_id; if (slot != 0) { vm_error(vm,"cannot put C7200-JC-PA in PA bay %u!\n",slot); return(-1); } /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_pa("C7200-JC-PA")); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Store dummy info... */ card->drv_info = card; return(0); } /* Remove a jacket card from the specified slot */ static int dev_c7200_jcpa_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the PA EEPROM */ cisco_card_unset_eeprom(card); c7200_set_slot_eeprom(VM_C7200(vm),card->slot_id,NULL); return(0); } /* Bind a Network IO descriptor */ static int dev_c7200_jcpa_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { /* network handling is done by slot 7 */ return(0); } /* Unbind a Network IO descriptor */ static int dev_c7200_jcpa_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { /* network handling is done by slot 7 */ return(0); } /* Jacket card driver */ struct cisco_card_driver dev_c7200_jcpa_driver = { "C7200-JC-PA", 1, 0, dev_c7200_jcpa_init, dev_c7200_jcpa_shutdown, NULL, dev_c7200_jcpa_set_nio, dev_c7200_jcpa_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c7200_mpfpga.c000066400000000000000000000311561241034141600200030ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005-2007 Christophe Fillot (cf@utc.fr) * * Cisco c7200 Midplane FPGA. */ #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "nmc93cX6.h" #include "dev_c7200.h" #define DEBUG_UNKNOWN 1 #define DEBUG_ACCESS 0 #define DEBUG_NET_IRQ 0 #define DEBUG_OIR 1 /* * Definitions for Port Adapter Status. */ #define PCI_BAY_3V 0x00000002 /* 3.3V */ #define PCI_BAY_5V 0x00000004 /* 5V */ #define PCI_BAY_POWER_OK (PCI_BAY_3V | PCI_BAY_5V) /* Bay power */ struct bay_power { u_int reg; u_int offset; }; static struct bay_power bay_power_info[C7200_MAX_PA_BAYS] = { { 0, 0 }, /* I/O card */ { 0, 8 }, /* Slot 1 */ { 0, 12 }, /* Slot 2 */ { 0, 24 }, /* Slot 3 */ { 0, 16 }, /* Slot 4 */ { 0, 28 }, /* Slot 5 */ { 0, 20 }, /* Slot 6 */ { 1, 24 }, /* Slot 7 */ }; /* * Definitions for EEPROM access (slots 0,1,3,4) (0x60) */ #define BAY0_EEPROM_SELECT_BIT 1 #define BAY0_EEPROM_CLOCK_BIT 3 #define BAY0_EEPROM_DIN_BIT 4 #define BAY0_EEPROM_DOUT_BIT 6 #define BAY1_EEPROM_SELECT_BIT 9 #define BAY1_EEPROM_CLOCK_BIT 11 #define BAY1_EEPROM_DIN_BIT 12 #define BAY1_EEPROM_DOUT_BIT 14 #define BAY3_EEPROM_SELECT_BIT 25 #define BAY3_EEPROM_CLOCK_BIT 27 #define BAY3_EEPROM_DIN_BIT 28 #define BAY3_EEPROM_DOUT_BIT 30 #define BAY4_EEPROM_SELECT_BIT 17 #define BAY4_EEPROM_CLOCK_BIT 19 #define BAY4_EEPROM_DIN_BIT 20 #define BAY4_EEPROM_DOUT_BIT 22 /* * Definitions for EEPROM access (slots 2,5,6) (0x68) */ #define BAY2_EEPROM_SELECT_BIT 9 #define BAY2_EEPROM_CLOCK_BIT 11 #define BAY2_EEPROM_DIN_BIT 12 #define BAY2_EEPROM_DOUT_BIT 14 #define BAY5_EEPROM_SELECT_BIT 25 #define BAY5_EEPROM_CLOCK_BIT 27 #define BAY5_EEPROM_DIN_BIT 28 #define BAY5_EEPROM_DOUT_BIT 30 #define BAY6_EEPROM_SELECT_BIT 17 #define BAY6_EEPROM_CLOCK_BIT 19 #define BAY6_EEPROM_DIN_BIT 20 #define BAY6_EEPROM_DOUT_BIT 22 /* Definitions for EEPROM access (slot 7) (0x2e4, in iofpga) */ #define BAY7_EEPROM_SELECT_BIT 25 #define BAY7_EEPROM_CLOCK_BIT 27 #define BAY7_EEPROM_DIN_BIT 28 #define BAY7_EEPROM_DOUT_BIT 30 /* PA Bay EEPROM definitions */ static const struct nmc93cX6_eeprom_def eeprom_bay_def[C7200_MAX_PA_BAYS] = { /* Bay 0 */ { BAY0_EEPROM_CLOCK_BIT , BAY0_EEPROM_SELECT_BIT, BAY0_EEPROM_DIN_BIT , BAY0_EEPROM_DOUT_BIT, }, /* Bay 1 */ { BAY1_EEPROM_CLOCK_BIT , BAY1_EEPROM_SELECT_BIT, BAY1_EEPROM_DIN_BIT , BAY1_EEPROM_DOUT_BIT, }, /* Bay 2 */ { BAY2_EEPROM_CLOCK_BIT , BAY2_EEPROM_SELECT_BIT, BAY2_EEPROM_DIN_BIT , BAY2_EEPROM_DOUT_BIT, }, /* Bay 3 */ { BAY3_EEPROM_CLOCK_BIT , BAY3_EEPROM_SELECT_BIT, BAY3_EEPROM_DIN_BIT , BAY3_EEPROM_DOUT_BIT, }, /* Bay 4 */ { BAY4_EEPROM_CLOCK_BIT , BAY4_EEPROM_SELECT_BIT, BAY4_EEPROM_DIN_BIT , BAY4_EEPROM_DOUT_BIT, }, /* Bay 5 */ { BAY5_EEPROM_CLOCK_BIT , BAY5_EEPROM_SELECT_BIT, BAY5_EEPROM_DIN_BIT , BAY5_EEPROM_DOUT_BIT, }, /* Bay 6 */ { BAY6_EEPROM_CLOCK_BIT , BAY6_EEPROM_SELECT_BIT, BAY6_EEPROM_DIN_BIT , BAY6_EEPROM_DOUT_BIT, }, /* Bay 7 */ { BAY7_EEPROM_CLOCK_BIT , BAY7_EEPROM_SELECT_BIT, BAY7_EEPROM_DIN_BIT , BAY7_EEPROM_DOUT_BIT, }, }; /* EEPROM group #1 (Bays 0, 1, 3, 4) */ static const struct nmc93cX6_group eeprom_bays_g1 = { EEPROM_TYPE_NMC93C46, 4, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "PA Bays (Group #1) EEPROM", { &eeprom_bay_def[0], &eeprom_bay_def[1], &eeprom_bay_def[3], &eeprom_bay_def[4], }, }; /* EEPROM group #2 (Bays 2, 5, 6) */ static const struct nmc93cX6_group eeprom_bays_g2 = { EEPROM_TYPE_NMC93C46, 3, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "PA Bays (Group #2) EEPROM", { &eeprom_bay_def[2], &eeprom_bay_def[5], &eeprom_bay_def[6] }, }; /* EEPROM group #3 (Bay 7) */ static const struct nmc93cX6_group eeprom_bays_g3 = { EEPROM_TYPE_NMC93C46, 1, 0, EEPROM_DORD_NORMAL, EEPROM_DOUT_HIGH, EEPROM_DEBUG_DISABLED, "PA Bay (Group #3) EEPROM", { &eeprom_bay_def[7] }, }; /* Midplane FPGA private data */ struct c7200_mpfpga_data { vm_obj_t vm_obj; struct vdevice dev; c7200_t *router; }; /* Returns TRUE if a slot has to be powered */ static int pa_get_power_status(c7200_t *router,u_int slot) { if (slot == 0) return(TRUE); return(vm_slot_check_eeprom(router->vm,slot,0)); } /* Update Port Adapter Status */ static void pa_update_status_reg(struct c7200_mpfpga_data *d) { c7200_t *router = d->router; struct bay_power *pw; u_int i; router->pa_status_reg[0] = router->pa_status_reg[1] = 0; for(i=0;ipa_status_reg[pw->reg] |= (PCI_BAY_POWER_OK << pw->offset); } } } /* * dev_mpfpga_access() */ void *dev_c7200_mpfpga_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct c7200_mpfpga_data *d = dev->priv_data; c7200_t *router = d->router; if (op_type == MTS_READ) *data = 0x0; /* Optimization: this is written regularly */ if (offset == 0x7b) return NULL; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"MP_FPGA","reading reg 0x%x at pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,"MP_FPGA", "writing reg 0x%x at pc=0x%llx, data=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),*data,op_size); } #endif switch(offset) { /* Interrupt status for slots 0, 1, 3, 4 */ case 0x10: case 0x11: case 0x12: case 0x13: if (op_type == MTS_READ) { *data = router->net_irq_status[0]; /* if we have a card in slot 7, report status as for slot 0 */ if (vm_slot_active(router->vm,7,0)) { u_int offset; offset = dev_c7200_net_get_reg_offset(0); if (router->net_irq_status[2]) { *data |= 0xFF << offset; } else { *data &= ~(0xFF << offset); } } } break; /* Interrupt status for slots 2, 5, 6 */ case 0x18: case 0x19: case 0x1a: case 0x1b: if (op_type == MTS_READ) *data = router->net_irq_status[1]; break; /* Interrupt mask for slots 0, 1, 3, 4 */ case 0x20: if (op_type == MTS_READ) { *data = router->net_irq_mask[0]; } else { router->net_irq_mask[0] = *data; dev_c7200_net_update_irq(router); } break; /* Interrupt mask for slots 2, 5, 6 */ case 0x28: if (op_type == MTS_READ) { *data = router->net_irq_mask[1]; } else { router->net_irq_mask[1] = *data; dev_c7200_net_update_irq(router); } break; /* * - PCI errors (seen with IRQ 6) * - Used when PA Mgmt IRQ is triggered. * * If the PA Mgmt IRQ is triggered for an undefined slot, a crash * occurs with "Error: Unexpected NM Interrupt received from slot: 6" * So, we use the PA status reg as mask to return something safe * (slot order is identical). */ case 0x40: if (op_type == MTS_READ) *data = 0x66666600 & router->pa_status_reg[0]; vm_clear_irq(router->vm,C7200_PA_MGMT_IRQ); break; case 0x48: /* ??? (test) */ if (op_type == MTS_READ) *data = 0xFFFFFFFF; break; /* * This corresponds to err_stat in error message when IRQ 6 is * triggered. * * Bit 7 => SRAM error. * Bits 1-6 => OIR on slot 1-6 */ case 0x70: if (op_type == MTS_READ) { #if DEBUG_OIR cpu_log(cpu,"MP_FPGA","reading reg 0x%x at pc=0x%llx, val=0x%x\n", offset,cpu_get_pc(cpu),router->oir_status[0]); #endif *data = router->oir_status[0]; vm_clear_irq(router->vm,C7200_OIR_IRQ); } else { #if DEBUG_OIR cpu_log(cpu,"MP_FPGA","writing reg 0x%x at pc=0x%llx " "(data=0x%llx)\n",offset,cpu_get_pc(cpu),*data); #endif router->oir_status[0] &= ~(*data); vm_clear_irq(router->vm,C7200_OIR_IRQ); } break; /* * This corresponds to err_enable in error message when IRQ 6 is * triggered. No idea of what it really means. */ case 0x78: if (op_type == MTS_READ) { #if DEBUG_OIR cpu_log(cpu,"MP_FPGA","reading 0x78 at pc=0x%llx\n", cpu_get_pc(cpu)); #endif *data = 0x00; } else { #if DEBUG_OIR cpu_log(cpu,"MP_FPGA","writing reg 0x78 at pc=0x%llx " "(data=0x%llx)\n",cpu_get_pc(cpu),*data); #endif } break; case 0x38: /* TDM status */ break; case 0x50: /* Port Adapter Status */ if (op_type == MTS_READ) { pa_update_status_reg(d); *data = router->pa_status_reg[0]; } break; case 0x58: /* Port Adapter Control */ if (op_type == MTS_WRITE) router->pa_ctrl_reg[0] = *data; else *data = router->pa_ctrl_reg[0]; break; case 0x60: /* EEPROM for PA in slots 0,1,3,4 */ if (op_type == MTS_WRITE) nmc93cX6_write(&router->pa_eeprom_g1,*data); else *data = nmc93cX6_read(&router->pa_eeprom_g1); break; case 0x68: /* EEPROM for PA in slots 2,5,6 */ if (op_type == MTS_WRITE) { nmc93cX6_write(&router->pa_eeprom_g2,*data); router->ps_status = *data & 0x03; if (router->ps_status == 0) { printf("\n\n\nVM %s: power shutdown - halting...\n\n\n", router->vm->name); vm_log(router->vm,"MP_FPGA","shutdown of power supplies\n"); vm_stop(router->vm); } } else { *data = nmc93cX6_read(&router->pa_eeprom_g2); *data |= router->ps_status & 0x03; } break; case 0x7b: /* ??? */ break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"MP_FPGA","read from addr 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"MP_FPGA","write to addr 0x%x, value=0x%llx, " "pc=0x%llx\n",offset,*data,cpu_get_pc(cpu)); } #endif } return NULL; } /* Initialize EEPROM groups */ void c7200_init_mp_eeprom_groups(c7200_t *router) { /* Group 1: bays 0, 1, 3, 4 */ router->pa_eeprom_g1 = eeprom_bays_g1; router->pa_eeprom_g1.eeprom[0] = NULL; router->pa_eeprom_g1.eeprom[1] = NULL; router->pa_eeprom_g1.eeprom[2] = NULL; router->pa_eeprom_g1.eeprom[3] = NULL; /* Group 2: bays 2, 5, 6 */ router->pa_eeprom_g2 = eeprom_bays_g2; router->pa_eeprom_g2.eeprom[0] = NULL; router->pa_eeprom_g2.eeprom[1] = NULL; router->pa_eeprom_g2.eeprom[2] = NULL; /* Group 3: bay 7 */ router->pa_eeprom_g3 = eeprom_bays_g3; router->pa_eeprom_g3.eeprom[0] = NULL; } /* Shutdown the MP FPGA device */ static void dev_c7200_mpfpga_shutdown(vm_instance_t *vm,struct c7200_mpfpga_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* Create the c7200 Midplane FPGA */ int dev_c7200_mpfpga_init(c7200_t *router,m_uint64_t paddr,m_uint32_t len) { struct c7200_mpfpga_data *d; /* Allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"MP_FPGA: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); d->router = router; router->ps_status = 0x03; vm_object_init(&d->vm_obj); d->vm_obj.name = "mp_fpga"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_c7200_mpfpga_shutdown; /* Set device properties */ dev_init(&d->dev); d->dev.name = "mp_fpga"; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_c7200_mpfpga_access; d->dev.priv_data = d; /* Map this device to the VM */ vm_bind_device(router->vm,&d->dev); vm_object_add(router->vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_c7200_mpfpga.h000066400000000000000000000005001241034141600177750ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005-2007 Christophe Fillot (cf@utc.fr) * * Cisco c7200 Midplane FPGA. */ #ifndef __DEV_C7200_MPFPGA_H__ #define __DEV_C7200_MPFPGA_H__ /* Create the c7200 Midplane FPGA */ int dev_c7200_mpfpga_init(c7200_t *router,m_uint64_t paddr,m_uint32_t len); #endif dynamips-0.2.14/common/dev_c7200_pos.c000066400000000000000000000520171241034141600173310ustar00rootroot00000000000000/* * Cisco router Simulation Platform. * Copyright (c) 2005-2007 Christophe Fillot. All rights reserved. * * EEPROM types: * - 0x95: PA-POS-OC3SMI * - 0x96: PA-POS-OC3MM * * Just an experimentation (I don't have any PA-POS-OC3). */ #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_c7200.h" #include "dev_plx.h" /* Debugging flags */ #define DEBUG_ACCESS 0 #define DEBUG_UNKNOWN 0 #define DEBUG_TRANSMIT 0 #define DEBUG_RECEIVE 0 /* PCI vendor/product codes */ #define POS_OC3_PCI_VENDOR_ID 0x10b5 #define POS_OC3_PCI_PRODUCT_ID 0x9060 /* Maximum packet size */ #define POS_OC3_MAX_PKT_SIZE 8192 /* RX descriptors */ #define POS_OC3_RXDESC_OWN 0x80000000 /* Ownership */ #define POS_OC3_RXDESC_WRAP 0x40000000 /* Wrap ring */ #define POS_OC3_RXDESC_CONT 0x08000000 /* Packet continues */ #define POS_OC3_RXDESC_LEN_MASK 0x1fff /* TX descriptors */ #define POS_OC3_TXDESC_OWN 0x80000000 /* Ownership */ #define POS_OC3_TXDESC_WRAP 0x40000000 /* Wrap ring */ #define POS_OC3_TXDESC_CONT 0x08000000 /* Packet continues */ #define POS_OC3_TXDESC_LEN_MASK 0x1fff /* RX Descriptor */ struct rx_desc { m_uint32_t rdes[2]; }; /* TX Descriptor */ struct tx_desc { m_uint32_t tdes[2]; }; /* PA-POS-OC3 Data */ struct pos_oc3_data { char *name; /* IRQ clearing count */ u_int irq_clearing_count; /* Control register #1 */ m_uint16_t ctrl_reg1; /* CRC size */ u_int crc_size; /* physical addresses for start and end of RX/TX rings */ m_uint32_t rx_start,rx_end,tx_start,tx_end; /* physical addresses of current RX and TX descriptors */ m_uint32_t rx_current,tx_current; /* Virtual machine */ vm_instance_t *vm; /* Virtual devices */ char *rx_name,*tx_name,*cs_name; vm_obj_t *rx_obj,*tx_obj,*cs_obj; struct vdevice rx_dev,tx_dev,cs_dev; /* PCI device information */ struct vdevice dev; struct pci_device *pci_dev; /* NetIO descriptor */ netio_desc_t *nio; /* TX ring scanner task id */ ptask_id_t tx_tid; }; /* Log a PA-POS-OC3 message */ #define POS_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* * pos_access() */ static void *dev_pos_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { _maybe_used struct pos_oc3_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to offset = 0x%x, pc = 0x%llx\n", offset,cpu_get_pc(cpu)); } else { if (offset != 0x404) cpu_log(cpu,d->name,"write access to vaddr = 0x%x, pc = 0x%llx, " "val = 0x%llx\n",offset,cpu_get_pc(cpu),*data); } #endif switch(offset) { case 0x404: if (op_type == MTS_READ) *data = 0xFFFFFFFF; break; case 0x406: if (op_type == MTS_READ) *data = 0xFFFFFFFF; break; case 0x407: if (op_type == MTS_READ) *data = 0xFFFFFFFF; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,d->name, "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name, "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* * pos_rx_access() */ static void *dev_pos_rx_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct pos_oc3_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to offset = 0x%x, pc = 0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,d->name,"write access to vaddr = 0x%x, pc = 0x%llx, " "val = 0x%llx\n",offset,cpu_get_pc(cpu),*data); } #endif switch(offset) { case 0x04: if (op_type == MTS_READ) *data = d->rx_start; else d->rx_start = *data; break; case 0x08: if (op_type == MTS_READ) *data = d->rx_current; else d->rx_current = *data; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,d->rx_name, "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->rx_name, "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* * pos_tx_access() */ static void *dev_pos_tx_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct pos_oc3_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,d->tx_name,"read access to offset = 0x%x, pc = 0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,d->tx_name,"write access to vaddr = 0x%x, pc = 0x%llx, " "val = 0x%llx\n",offset,cpu_get_pc(cpu),*data); } #endif switch(offset) { case 0x04: if (op_type == MTS_READ) *data = d->tx_start; else d->tx_start = *data; break; case 0x08: if (op_type == MTS_READ) *data = d->tx_current; else d->tx_current = *data; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,d->tx_name, "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->tx_name, "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* * pos_cs_access() */ static void *dev_pos_cs_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct pos_oc3_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,d->cs_name,"read access to offset = 0x%x, pc = 0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,d->cs_name,"write access to vaddr = 0x%x, pc = 0x%llx, " "val = 0x%llx\n",offset,cpu_get_pc(cpu),*data); } #endif switch(offset) { case 0x300000: case 0x300004: case 0x30001c: if (op_type == MTS_READ) { *data = 0x00000FFF; /* Add a delay before clearing the IRQ */ if (++d->irq_clearing_count == 20) { pci_dev_clear_irq(d->vm,d->pci_dev); d->irq_clearing_count = 0; } } break; case 0x300008: if (op_type == MTS_READ) *data = 0x000007F; break; case 0x300028: if (op_type == MTS_READ) { *data = d->ctrl_reg1; } else { d->ctrl_reg1 = *data; switch(*data) { case 0x06: d->crc_size = 2; break; case 0x07: d->crc_size = 4; break; default: d->crc_size = 2; cpu_log(cpu,d->cs_name, "unknown value 0x%4.4llx written in ctrl_reg1\n", *data); } cpu_log(cpu,d->cs_name,"CRC size set to 0x%4.4x\n",d->crc_size); } break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,d->cs_name, "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->cs_name, "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* * Get the address of the next RX descriptor. */ static m_uint32_t rxdesc_get_next(struct pos_oc3_data *d,m_uint32_t rxd_addr, struct rx_desc *rxd) { m_uint32_t nrxd_addr; if (rxd->rdes[0] & POS_OC3_RXDESC_WRAP) nrxd_addr = d->rx_start; else nrxd_addr = rxd_addr + sizeof(struct rx_desc); return(nrxd_addr); } /* Read an RX descriptor */ static void rxdesc_read(struct pos_oc3_data *d,m_uint32_t rxd_addr, struct rx_desc *rxd) { #if DEBUG_RECEIVE POS_LOG(d,"reading RX descriptor at address 0x%x\n",rxd_addr); #endif /* get the next descriptor from VM physical RAM */ physmem_copy_from_vm(d->vm,rxd,rxd_addr,sizeof(struct rx_desc)); /* byte-swapping */ rxd->rdes[0] = vmtoh32(rxd->rdes[0]); rxd->rdes[1] = vmtoh32(rxd->rdes[1]); } /* * Try to acquire the specified RX descriptor. Returns TRUE if we have it. * It assumes that the byte-swapping is done. */ static inline int rxdesc_acquire(m_uint32_t rdes0) { return(rdes0 & POS_OC3_RXDESC_OWN); } /* Put a packet in buffer of a descriptor */ static ssize_t rxdesc_put_pkt(struct pos_oc3_data *d,struct rx_desc *rxd, u_char **pkt,ssize_t *pkt_len) { ssize_t len,cp_len; len = rxd->rdes[0] & POS_OC3_RXDESC_LEN_MASK; /* compute the data length to copy */ cp_len = m_min(len,*pkt_len); #if DEBUG_RECEIVE POS_LOG(d,"copying %d bytes at 0x%x\n",cp_len,rxd->rdes[1]); #endif /* copy packet data to the VM physical RAM */ physmem_copy_to_vm(d->vm,*pkt,rxd->rdes[1],cp_len); *pkt += cp_len; *pkt_len -= cp_len; return(cp_len); } /* * Put a packet in the RX ring. */ static void dev_pos_oc3_receive_pkt(struct pos_oc3_data *d, u_char *pkt,ssize_t pkt_len) { m_uint32_t rx_start,rxdn_addr,rxdn_rdes0; struct rx_desc rxd0,rxdn,*rxdc; ssize_t cp_len,tot_len = pkt_len; u_char *pkt_ptr = pkt; int i; if (d->rx_start == 0) return; /* Truncate the packet if it is too big */ pkt_len = m_min(pkt_len,POS_OC3_MAX_PKT_SIZE); /* Copy the current rxring descriptor */ rxdesc_read(d,d->rx_current,&rxd0); /* We must have the first descriptor... */ if (!rxdesc_acquire(rxd0.rdes[0])) return; /* Remember the first RX descriptor address */ rx_start = d->rx_current; for(i=0,rxdc=&rxd0;tot_len>0;i++) { /* Put data into the descriptor buffers */ cp_len = rxdesc_put_pkt(d,rxdc,&pkt_ptr,&tot_len); /* Get address of the next descriptor */ rxdn_addr = rxdesc_get_next(d,d->rx_current,rxdc); /* We have finished if the complete packet has been stored */ if (tot_len == 0) { rxdc->rdes[0] = (cp_len + d->crc_size); if (i != 0) physmem_copy_u32_to_vm(d->vm,d->rx_current,rxdc->rdes[0]); d->rx_current = rxdn_addr; break; } #if DEBUG_RECEIVE POS_LOG(d,"trying to acquire new descriptor at 0x%x\n",rxdn_addr); #endif /* Get status of the next descriptor to see if we can acquire it */ rxdn_rdes0 = physmem_copy_u32_from_vm(d->vm,rxdn_addr); if (!rxdesc_acquire(rxdn_rdes0)) rxdc->rdes[0] = 0; /* error, no buf available (special flag?) */ else rxdc->rdes[0] = POS_OC3_RXDESC_CONT; /* packet continues */ rxdc->rdes[0] |= cp_len; /* Update the new status (only if we are not on the first desc) */ if (i != 0) physmem_copy_u32_to_vm(d->vm,d->rx_current,rxdc->rdes[0]); /* Update the RX pointer */ d->rx_current = rxdn_addr; if (!(rxdc->rdes[0] & POS_OC3_RXDESC_CONT)) break; /* Read the next descriptor from VM physical RAM */ rxdesc_read(d,rxdn_addr,&rxdn); rxdc = &rxdn; } /* Update the first RX descriptor */ physmem_copy_u32_to_vm(d->vm,rx_start,rxd0.rdes[0]); /* Generate IRQ on CPU */ pci_dev_trigger_irq(d->vm,d->pci_dev); } /* Handle the RX ring */ static int dev_pos_oc3_handle_rxring(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, struct pos_oc3_data *d) { #if DEBUG_RECEIVE POS_LOG(d,"receiving a packet of %d bytes\n",pkt_len); mem_dump(log_file,pkt,pkt_len); #endif dev_pos_oc3_receive_pkt(d,pkt,pkt_len); return(TRUE); } /* Read a TX descriptor */ static void txdesc_read(struct pos_oc3_data *d,m_uint32_t txd_addr, struct tx_desc *txd) { /* get the next descriptor from VM physical RAM */ physmem_copy_from_vm(d->vm,txd,txd_addr,sizeof(struct tx_desc)); /* byte-swapping */ txd->tdes[0] = vmtoh32(txd->tdes[0]); txd->tdes[1] = vmtoh32(txd->tdes[1]); } /* Set the address of the next TX descriptor */ static void txdesc_set_next(struct pos_oc3_data *d,struct tx_desc *txd) { if (txd->tdes[0] & POS_OC3_TXDESC_WRAP) d->tx_current = d->tx_start; else d->tx_current += sizeof(struct tx_desc); } /* Handle the TX ring */ static int dev_pos_oc3_handle_txring(struct pos_oc3_data *d) { u_char pkt[POS_OC3_MAX_PKT_SIZE],*pkt_ptr; m_uint32_t clen,tot_len,norm_len; m_uint32_t tx_start,addr; struct tx_desc txd0,ctxd,*ptxd; int i,done = FALSE; if ((d->tx_start == 0) || (d->nio == NULL)) return(FALSE); /* Copy the current txring descriptor */ tx_start = d->tx_current; ptxd = &txd0; txdesc_read(d,d->tx_current,ptxd); /* If we don't own the descriptor, we cannot transmit */ if (!(txd0.tdes[0] & POS_OC3_TXDESC_OWN)) return(FALSE); #if DEBUG_TRANSMIT POS_LOG(d,"pos_oc3_handle_txring: 1st desc: tdes[0]=0x%x, tdes[1]=0x%x\n", ptxd->tdes[0],ptxd->tdes[1]); #endif pkt_ptr = pkt; tot_len = 0; i = 0; do { #if DEBUG_TRANSMIT POS_LOG(d,"pos_oc3_handle_txring: loop: tdes[0]=0x%x, tdes[1]=0x%x\n", ptxd->tdes[0],ptxd->tdes[1]); #endif if (!(ptxd->tdes[0] & POS_OC3_TXDESC_OWN)) { POS_LOG(d,"pos_oc3_handle_txring: descriptor not owned!\n"); return(FALSE); } clen = ptxd->tdes[0] & POS_OC3_TXDESC_LEN_MASK; /* Be sure that we have length not null */ if (clen != 0) { addr = ptxd->tdes[1]; norm_len = normalize_size(clen,4,0); physmem_copy_from_vm(d->vm,pkt_ptr,addr,norm_len); mem_bswap32(pkt_ptr,norm_len); } pkt_ptr += clen; tot_len += clen; /* Clear the OWN bit if this is not the first descriptor */ if (i != 0) physmem_copy_u32_to_vm(d->vm,d->tx_current,0); /* Go to the next descriptor */ txdesc_set_next(d,ptxd); /* Copy the next txring descriptor */ if (ptxd->tdes[0] & POS_OC3_TXDESC_CONT) { txdesc_read(d,d->tx_current,&ctxd); ptxd = &ctxd; i++; } else done = TRUE; }while(!done); if (tot_len != 0) { #if DEBUG_TRANSMIT POS_LOG(d,"sending packet of %u bytes (flags=0x%4.4x)\n", tot_len,txd0.tdes[0]); mem_dump(log_file,pkt,tot_len); #endif /* send it on wire */ netio_send(d->nio,pkt,tot_len); } /* Clear the OWN flag of the first descriptor */ txd0.tdes[0] &= ~POS_OC3_TXDESC_OWN; physmem_copy_u32_to_vm(d->vm,tx_start,txd0.tdes[0]); /* Interrupt on completion */ pci_dev_trigger_irq(d->vm,d->pci_dev); return(TRUE); } /* * pci_pos_read() */ static m_uint32_t pci_pos_read(cpu_gen_t *cpu,struct pci_device *dev,int reg) { struct pos_oc3_data *d = dev->priv_data; #if DEBUG_ACCESS POS_LOG(d,"read PCI register 0x%x\n",reg); #endif switch(reg) { case PCI_REG_BAR0: return(d->dev.phys_addr); default: return(0); } } /* * pci_pos_write() */ static void pci_pos_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct pos_oc3_data *d = dev->priv_data; #if DEBUG_ACCESS POS_LOG(d,"write 0x%x to PCI register 0x%x\n",value,reg); #endif switch(reg) { case PCI_REG_BAR0: vm_map_device(cpu->vm,&d->dev,(m_uint64_t)value); POS_LOG(d,"registers are mapped at 0x%x\n",value); break; } } /* * dev_c7200_pa_pos_init() * * Add a PA-POS port adapter into specified slot. */ int dev_c7200_pa_pos_init(vm_instance_t *vm,struct cisco_card *card) { struct pos_oc3_data *d; u_int slot = card->slot_id; /* Allocate the private data structure for PA-POS-OC3 chip */ if (!(d = malloc(sizeof(*d)))) { vm_error(vm,"%s: out of memory\n",card->dev_name); return(-1); } memset(d,0,sizeof(*d)); d->name = card->dev_name; d->vm = vm; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_pa("PA-POS-OC3")); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Initialize RX device */ d->rx_name = dyn_sprintf("%s_RX",card->dev_name); dev_init(&d->rx_dev); d->rx_dev.name = d->rx_name; d->rx_dev.priv_data = d; d->rx_dev.handler = dev_pos_rx_access; /* Initialize TX device */ d->tx_name = dyn_sprintf("%s_TX",card->dev_name); dev_init(&d->tx_dev); d->tx_dev.name = d->tx_name; d->tx_dev.priv_data = d; d->tx_dev.handler = dev_pos_tx_access; /* Initialize CS device */ d->cs_name = dyn_sprintf("%s_CS",card->dev_name); dev_init(&d->cs_dev); d->cs_dev.name = d->cs_name; d->cs_dev.priv_data = d; d->cs_dev.handler = dev_pos_cs_access; /* Initialize PLX9060 for RX part */ d->rx_obj = dev_plx9060_init(vm,d->rx_name,card->pci_bus,0,&d->rx_dev); /* Initialize PLX9060 for TX part */ d->tx_obj = dev_plx9060_init(vm,d->tx_name,card->pci_bus,1,&d->tx_dev); /* Initialize PLX9060 for CS part (CS=card status, chip status, ... ?) */ d->cs_obj = dev_plx9060_init(vm,d->cs_name,card->pci_bus,2,&d->cs_dev); /* Unknown PCI device here (will be mapped at 0x30000) */ dev_init(&d->dev); d->dev.name = card->dev_name; d->dev.priv_data = d; d->dev.phys_len = 0x10000; d->dev.handler = dev_pos_access; d->pci_dev = pci_dev_add(card->pci_bus,card->dev_name,0,0,3,0, c7200_net_irq_for_slot_port(slot,0), d,NULL,pci_pos_read,pci_pos_write); /* Store device info into the router structure */ card->drv_info = d; return(0); } /* Remove a PA-POS-OC3 from the specified slot */ int dev_c7200_pa_pos_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct pos_oc3_data *d = card->drv_info; /* Remove the PA EEPROM */ cisco_card_unset_eeprom(card); c7200_set_slot_eeprom(VM_C7200(vm),card->slot_id,NULL); /* Remove the PCI device */ pci_dev_remove(d->pci_dev); /* Remove the PLX9060 chips */ vm_object_remove(vm,d->rx_obj); vm_object_remove(vm,d->tx_obj); vm_object_remove(vm,d->cs_obj); /* Remove the devices from the CPU address space */ vm_unbind_device(vm,&d->rx_dev); vm_unbind_device(vm,&d->tx_dev); vm_unbind_device(vm,&d->cs_dev); vm_unbind_device(vm,&d->dev); cpu_group_rebuild_mts(vm->cpu_group); /* Free the device structure itself */ free(d); return(0); } /* Bind a Network IO descriptor to a specific port */ int dev_c7200_pa_pos_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct pos_oc3_data *d = card->drv_info; if (!d || (port_id > 0)) return(-1); if (d->nio != NULL) return(-1); d->nio = nio; d->tx_tid = ptask_add((ptask_callback)dev_pos_oc3_handle_txring,d,NULL); netio_rxl_add(nio,(netio_rx_handler_t)dev_pos_oc3_handle_rxring,d,NULL); return(0); } /* Bind a Network IO descriptor to a specific port */ int dev_c7200_pa_pos_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct pos_oc3_data *d = card->drv_info; if (!d || (port_id > 0)) return(-1); if (d->nio) { ptask_remove(d->tx_tid); netio_rxl_remove(d->nio); d->nio = NULL; } return(0); } /* PA-POS-OC3 driver */ struct cisco_card_driver dev_c7200_pa_pos_oc3_driver = { "PA-POS-OC3", 1, 0, dev_c7200_pa_pos_init, dev_c7200_pa_pos_shutdown, NULL, dev_c7200_pa_pos_set_nio, dev_c7200_pa_pos_unset_nio, NULL, }; dynamips-0.2.14/common/dev_c7200_serial.c000066400000000000000000000136131241034141600200060ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (C) 2005,2006 Christophe Fillot. All rights reserved. * * Serial Interfaces (Mueslix). * * EEPROM types: * - 0x0C: PA-4T+ * - 0x0D: PA-8T-V35 * - 0x0E: PA-8T-X21 * - 0x0F: PA-8T-232 * - 0x10: PA-2H (HSSI) * - 0x40: PA-4E1G/120 * * It seems that the PA-8T is a combination of two PA-4T+. * * Note: "debug serial mueslix" gives more technical info. */ #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_mueslix.h" #include "dev_c7200.h" /* ====================================================================== */ /* PA-4T+ */ /* ====================================================================== */ /* * dev_c7200_pa_4t_init() * * Add a PA-4T port adapter into specified slot. */ static int dev_c7200_pa_4t_init(vm_instance_t *vm,struct cisco_card *card) { struct mueslix_data *data; u_int slot = card->slot_id; /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_pa("PA-4T+")); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Create the Mueslix chip */ data = dev_mueslix_init(vm,card->dev_name,1, card->pci_bus,0, c7200_net_irq_for_slot_port(slot,0)); if (!data) return(-1); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a PA-4T+ from the specified slot */ static int dev_c7200_pa_4t_shutdown(vm_instance_t *vm,struct cisco_card *card) { /* Remove the PA EEPROM */ cisco_card_unset_eeprom(card); c7200_set_slot_eeprom(VM_C7200(vm),card->slot_id,NULL); /* Remove the Mueslix chip */ dev_mueslix_remove(card->drv_info); return(0); } /* Bind a Network IO descriptor to a specific port */ static int dev_c7200_pa_4t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct mueslix_data *data = card->drv_info; if (!data || (port_id >= MUESLIX_NR_CHANNELS)) return(-1); return(dev_mueslix_set_nio(data,port_id,nio)); } /* Unbind a Network IO descriptor to a specific port */ static int dev_c7200_pa_4t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct mueslix_data *data = card->drv_info; if (!data || (port_id >= MUESLIX_NR_CHANNELS)) return(-1); return(dev_mueslix_unset_nio(data,port_id)); } /* PA-4T+ driver */ struct cisco_card_driver dev_c7200_pa_4t_driver = { "PA-4T+", 1, 0, dev_c7200_pa_4t_init, dev_c7200_pa_4t_shutdown, NULL, dev_c7200_pa_4t_set_nio, dev_c7200_pa_4t_unset_nio, }; /* ====================================================================== */ /* PA-8T */ /* ====================================================================== */ /* PA-8T data */ struct pa8t_data { struct mueslix_data *mueslix[2]; }; /* * dev_c7200_pa_8t_init() * * Add a PA-8T port adapter into specified slot. */ static int dev_c7200_pa_8t_init(vm_instance_t *vm,struct cisco_card *card) { struct pa8t_data *data; u_int slot = card->slot_id; /* Allocate the private data structure for the PA-8T */ if (!(data = malloc(sizeof(*data)))) { vm_log(vm,"%s: out of memory\n",card->dev_name); return(-1); } /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_pa("PA-8T")); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Create the 1st Mueslix chip */ data->mueslix[0] = dev_mueslix_init(vm,card->dev_name,1, card->pci_bus,0, c7200_net_irq_for_slot_port(slot,0)); if (!data->mueslix[0]) return(-1); /* Create the 2nd Mueslix chip */ data->mueslix[1] = dev_mueslix_init(vm,card->dev_name,1, card->pci_bus,1, c7200_net_irq_for_slot_port(slot,1)); if (!data->mueslix[1]) return(-1); /* Store device info into the router structure */ card->drv_info = data; return(0); } /* Remove a PA-8T from the specified slot */ static int dev_c7200_pa_8t_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct pa8t_data *data = card->drv_info; /* Remove the PA EEPROM */ cisco_card_unset_eeprom(card); c7200_set_slot_eeprom(VM_C7200(vm),card->slot_id,NULL); /* Remove the two Mueslix chips */ dev_mueslix_remove(data->mueslix[0]); dev_mueslix_remove(data->mueslix[1]); free(data); return(0); } /* Bind a Network IO descriptor to a specific port */ static int dev_c7200_pa_8t_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct pa8t_data *d = card->drv_info; if (!d || (port_id >= (MUESLIX_NR_CHANNELS*2))) return(-1); return(dev_mueslix_set_nio(d->mueslix[port_id>>2],(port_id&0x03),nio)); } /* Bind a Network IO descriptor to a specific port */ static int dev_c7200_pa_8t_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct pa8t_data *d = card->drv_info; if (!d || (port_id >= (MUESLIX_NR_CHANNELS*2))) return(-1); return(dev_mueslix_unset_nio(d->mueslix[port_id>>2],port_id&0x03)); } /* PA-8T driver */ struct cisco_card_driver dev_c7200_pa_8t_driver = { "PA-8T", 1, 0, dev_c7200_pa_8t_init, dev_c7200_pa_8t_shutdown, NULL, dev_c7200_pa_8t_set_nio, dev_c7200_pa_8t_unset_nio, }; dynamips-0.2.14/common/dev_c7200_sram.c000066400000000000000000000071511241034141600174710ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Packet SRAM. This is a fast memory zone for packets on NPE150/NPE200. */ #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #define PCI_VENDOR_SRAM 0x1137 #define PCI_PRODUCT_SRAM 0x0005 /* SRAM structure */ struct sram_data { /* VM object info */ vm_obj_t vm_obj; /* SRAM main device */ struct vdevice *dev; /* Aliased device */ char *alias_dev_name; struct vdevice *alias_dev; /* Byte-swapped device */ char *bs_dev_name; vm_obj_t *bs_obj; /* PCI device */ struct pci_device *pci_dev; /* Filename used to virtualize SRAM */ char *filename; }; /* Shutdown an SRAM device */ void dev_c7200_sram_shutdown(vm_instance_t *vm,struct sram_data *d) { if (d != NULL) { /* Remove the PCI device */ pci_dev_remove(d->pci_dev); /* Remove the byte-swapped device */ vm_object_remove(vm,d->bs_obj); /* Remove the alias and the main device */ dev_remove(vm,d->alias_dev); dev_remove(vm,d->dev); /* Free devices */ free(d->alias_dev); free(d->dev); /* Free device names */ free(d->alias_dev_name); free(d->bs_dev_name); /* Remove filename used to virtualize SRAM */ if (d->filename) { unlink(d->filename); free(d->filename); } /* Free the structure itself */ free(d); } } /* Initialize an SRAM device */ int dev_c7200_sram_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len, struct pci_bus *pci_bus,int pci_device) { m_uint64_t alias_paddr; struct sram_data *d; /* Allocate the private data structure for SRAM */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"dev_c7200_sram_init (%s): out of memory\n",name); return(-1); } memset(d,0,sizeof(*d)); vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_c7200_sram_shutdown; if (!(d->filename = vm_build_filename(vm,name))) return(-1); /* add as a pci device */ d->pci_dev = pci_dev_add_basic(pci_bus,name, PCI_VENDOR_SRAM,PCI_PRODUCT_SRAM, pci_device,0); alias_paddr = 0x100000000ULL + paddr; /* create the standard RAM zone */ if (!(d->dev = dev_create_ram(vm,name,FALSE,d->filename,paddr,len))) { fprintf(stderr,"dev_c7200_sram_init: unable to create '%s' file.\n", d->filename); return(-1); } /* create the RAM alias */ if (!(d->alias_dev_name = dyn_sprintf("%s_alias",name))) { fprintf(stderr,"dev_c7200_sram_init: unable to create alias name.\n"); return(-1); } d->alias_dev = dev_create_ram_alias(vm,d->alias_dev_name,name, alias_paddr,len); if (!d->alias_dev) { fprintf(stderr,"dev_c7200_sram_init: unable to create alias device.\n"); return(-1); } /* create the byte-swapped zone (used with Galileo DMA) */ if (!(d->bs_dev_name = dyn_sprintf("%s_bswap",name))) { fprintf(stderr,"dev_c7200_sram_init: unable to create BS name.\n"); return(-1); } if (dev_bswap_init(vm,d->bs_dev_name,paddr+0x800000,len,paddr) == -1) { fprintf(stderr,"dev_c7200_sram_init: unable to create BS device.\n"); return(-1); } d->bs_obj = vm_object_find(vm,d->bs_dev_name); return(0); } dynamips-0.2.14/common/dev_clpd6729.c000066400000000000000000000147061241034141600171720ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot. All rights reserved. * * Cirrus Logic PD6729 PCI-to-PCMCIA host adapter. * * TODO: finish the code! (especially extended registers) */ #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "pci_dev.h" #include "pci_io.h" #define DEBUG_ACCESS 0 /* Cirrus Logic PD6729 PCI vendor/product codes */ #define CLPD6729_PCI_VENDOR_ID 0x1013 #define CLPD6729_PCI_PRODUCT_ID 0x1100 #define CLPD6729_REG_CHIP_REV 0x00 /* Chip Revision */ #define CLPD6729_REG_INT_STATUS 0x01 /* Interface Status */ #define CLPD6729_REG_POWER_CTRL 0x02 /* Power Control */ #define CLPD6729_REG_INTGEN_CTRL 0x03 /* Interrupt & General Control */ #define CLPD6729_REG_CARD_STATUS 0x04 /* Card Status Change */ #define CLPD6729_REG_FIFO_CTRL 0x17 /* FIFO Control */ #define CLPD6729_REG_EXT_INDEX 0x2E /* Extended Index */ /* CLPD6729 private data */ struct clpd6729_data { vm_obj_t vm_obj; struct vdevice dev; struct pci_device *pci_dev; struct pci_io_device *pci_io_dev; /* VM objects present in slots (typically, PCMCIA disks...) */ vm_obj_t *slot_obj[2]; /* Base registers */ m_uint8_t base_index; m_uint8_t base_regs[256]; }; /* Handle access to a base register */ static void clpd6729_base_reg_access(cpu_gen_t *cpu,struct clpd6729_data *d, u_int op_type,m_uint64_t *data) { u_int slot_id,reg; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"CLPD6729","reading reg 0x%2.2x at pc=0x%llx\n", d->base_index,cpu_get_pc(cpu)); } else { cpu_log(cpu,"CLPD6729","writing reg 0x%2.2x, data=0x%llx at pc=0x%llx\n", d->base_index,*data,cpu_get_pc(cpu)); } #endif if (op_type == MTS_READ) *data = 0; /* Reserved registers */ if (d->base_index >= 0x80) return; /* * Socket A regs: 0x00 to 0x3f * Socket B regs: 0x40 to 0x7f */ if (d->base_index >= 0x40) { slot_id = 1; reg = d->base_index - 0x40; } else { slot_id = 0; reg = d->base_index; } switch(reg) { case CLPD6729_REG_CHIP_REV: if (op_type == MTS_READ) *data = 0x48; break; case CLPD6729_REG_INT_STATUS: if (op_type == MTS_READ) { if (d->slot_obj[slot_id]) *data = 0xEF; else *data = 0x80; } break; case CLPD6729_REG_INTGEN_CTRL: if (op_type == MTS_READ) *data = 0x40; break; case CLPD6729_REG_EXT_INDEX: if (op_type == MTS_WRITE) { cpu_log(cpu,"CLPD6729","ext reg index 0x%2.2llx at pc=0x%llx\n", *data,cpu_get_pc(cpu)); } break; case CLPD6729_REG_FIFO_CTRL: if (op_type == MTS_READ) *data = 0x80; /* FIFO is empty */ break; default: if (op_type == MTS_READ) *data = d->base_regs[d->base_index]; else d->base_regs[d->base_index] = (m_uint8_t)(*data); } } /* * dev_clpd6729_io_access() */ static void *dev_clpd6729_io_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size, u_int op_type,m_uint64_t *data) { struct clpd6729_data *d = dev->priv_data; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,dev->name,"reading at offset 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,dev->name,"writing at offset 0x%x, pc=0x%llx, data=0x%llx\n", offset,cpu_get_pc(cpu),*data); } #endif switch(offset) { case 0: /* Data register */ clpd6729_base_reg_access(cpu,d,op_type,data); break; case 1: /* Index register */ if (op_type == MTS_READ) *data = d->base_index; else d->base_index = *data; break; } return NULL; } /* Shutdown a CLPD6729 device */ void dev_clpd6729_shutdown(vm_instance_t *vm,struct clpd6729_data *d) { if (d != NULL) { /* Remove the PCI device */ pci_dev_remove(d->pci_dev); /* Remove the PCI I/O device */ pci_io_remove(d->pci_io_dev); /* Free the structure itself */ free(d); } } /* * dev_clpd6729_init() */ int dev_clpd6729_init(vm_instance_t *vm, struct pci_bus *pci_bus,int pci_device, struct pci_io_data *pci_io_data, m_uint32_t io_start,m_uint32_t io_end) { struct clpd6729_data *d; /* Allocate the private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"CLPD6729: unable to create device.\n"); return(-1); } memset(d,0,sizeof(*d)); vm_object_init(&d->vm_obj); d->vm_obj.name = "clpd6729"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_clpd6729_shutdown; dev_init(&d->dev); d->dev.name = "clpd6729"; d->dev.priv_data = d; d->pci_io_dev = pci_io_add(pci_io_data,io_start,io_end,&d->dev, dev_clpd6729_io_access); d->pci_dev = pci_dev_add(pci_bus,"clpd6729", CLPD6729_PCI_VENDOR_ID,CLPD6729_PCI_PRODUCT_ID, pci_device,0,-1,&d->dev,NULL,NULL,NULL); if (!d->pci_io_dev || !d->pci_dev) { fprintf(stderr,"CLPD6729: unable to create PCI devices.\n"); dev_clpd6729_shutdown(vm,d); return(-1); } vm_object_add(vm,&d->vm_obj); #if 1 /* PCMCIA disk test */ if (vm->pcmcia_disk_size[0]) d->slot_obj[0] = dev_pcmcia_disk_init(vm,"disk0",0x40000000ULL,0x200000, vm->pcmcia_disk_size[0],0); if (vm->pcmcia_disk_size[1]) d->slot_obj[1] = dev_pcmcia_disk_init(vm,"disk1",0x44000000ULL,0x200000, vm->pcmcia_disk_size[1],0); #endif #if 0 /* PCMCIA disk test */ if (vm->pcmcia_disk_size[0]) d->slot_obj[0] = dev_pcmcia_disk_init(vm,"disk0",0xd8000000ULL,0x200000, vm->pcmcia_disk_size[0],0); if (vm->pcmcia_disk_size[1]) d->slot_obj[1] = dev_pcmcia_disk_init(vm,"disk1",0xdc000000ULL,0x200000, vm->pcmcia_disk_size[1],0); #endif return(0); } dynamips-0.2.14/common/dev_dec21140.c000066400000000000000000000706361241034141600170470ustar00rootroot00000000000000/* * Cisco router simlation platform. * Copyright (C) 2005,2006 Christophe Fillot. All rights reserved. * * DEC21140 FastEthernet chip emulation. * * It allows to emulate a C7200-IO-FE card with 1 port and PA-FE-TX cards. * * Many many thanks to mtve (aka "Mtv Europe") for his great work on * this stuff. * * Manuals: * * DECchip 21140 PCI fast Ethernet LAN controller Hardware reference manual * http://ftp.nluug.nl/NetBSD/misc/dec-docs/ec-qc0cb-te.ps.gz * * National DP83840 PHY * http://www.rezrov.net/docs/DP83840A.pdf * * Remark: only Big-endian mode is supported. */ #include #include #include #include #include #include #include #include #include "crc.h" #include "utils.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_dec21140.h" /* Debugging flags */ #define DEBUG_MII_REGS 0 #define DEBUG_CSR_REGS 0 #define DEBUG_PCI_REGS 0 #define DEBUG_TRANSMIT 0 #define DEBUG_RECEIVE 0 /* DEC21140 PCI vendor/product codes */ #define DEC21140_PCI_VENDOR_ID 0x1011 #define DEC21140_PCI_PRODUCT_ID 0x0009 /* DEC21140 PCI registers */ #define DEC21140_PCI_CFID_REG_OFFSET 0x00 #define DEC21140_PCI_CFCS_REG_OFFSET 0x04 #define DEC21140_PCI_CFRV_REG_OFFSET 0x08 #define DEC21140_PCI_CFLT_REG_OFFSET 0x0C #define DEC21140_PCI_CBIO_REG_OFFSET 0x10 #define DEC21140_PCI_CBMA_REG_OFFSET 0x14 #define DEC21140_PCI_CFIT_REG_OFFSET 0x3C #define DEC21140_PCI_CFDA_REG_OFFSET 0x40 /* Number of CSR registers */ #define DEC21140_CSR_NR 16 /* CSR5: Status Register */ #define DEC21140_CSR5_TI 0x00000001 /* TX Interrupt */ #define DEC21140_CSR5_TPS 0x00000002 /* TX Process Stopped */ #define DEC21140_CSR5_TU 0x00000004 /* TX Buffer Unavailable */ #define DEC21140_CSR5_TJT 0x00000008 /* TX Jabber Timeout */ #define DEC21140_CSR5_UNF 0x00000020 /* TX Underflow */ #define DEC21140_CSR5_RI 0x00000040 /* RX Interrupt */ #define DEC21140_CSR5_RU 0x00000080 /* RX Buffer Unavailable */ #define DEC21140_CSR5_RPS 0x00000100 /* RX Process Stopped */ #define DEC21140_CSR5_RWT 0x00000200 /* RX Watchdog Timeout */ #define DEC21140_CSR5_GTE 0x00000800 /* Gen Purpose Timer Expired */ #define DEC21140_CSR5_FBE 0x00002000 /* Fatal Bus Error */ #define DEC21140_CSR5_AIS 0x00008000 /* Abnormal Interrupt Summary */ #define DEC21140_CSR5_NIS 0x00010000 /* Normal Interrupt Summary */ #define DEC21140_NIS_BITS \ (DEC21140_CSR5_TI|DEC21140_CSR5_RI|DEC21140_CSR5_TU) #define DEC21140_AIS_BITS \ (DEC21140_CSR5_TPS|DEC21140_CSR5_TJT|DEC21140_CSR5_UNF| \ DEC21140_CSR5_RU|DEC21140_CSR5_RPS|DEC21140_CSR5_RWT| \ DEC21140_CSR5_GTE|DEC21140_CSR5_FBE) #define DEC21140_CSR5_RS_SHIFT 17 #define DEC21140_CSR5_TS_SHIFT 20 /* CSR6: Operating Mode Register */ #define DEC21140_CSR6_START_RX 0x00000002 #define DEC21140_CSR6_START_TX 0x00002000 #define DEC21140_CSR6_PROMISC 0x00000040 /* CSR9: Serial EEPROM and MII */ #define DEC21140_CSR9_RX_BIT 0x00080000 #define DEC21140_CSR9_MII_READ 0x00040000 #define DEC21140_CSR9_TX_BIT 0x00020000 #define DEC21140_CSR9_MDC_CLOCK 0x00010000 #define DEC21140_CSR9_READ 0x00004000 #define DEC21140_CSR9_WRITE 0x00002000 /* Maximum packet size */ #define DEC21140_MAX_PKT_SIZE 2048 /* Send up to 32 packets in a TX ring scan pass */ #define DEC21140_TXRING_PASS_COUNT 32 /* Setup frame size */ #define DEC21140_SETUP_FRAME_SIZE 192 /* RX descriptors */ #define DEC21140_RXDESC_OWN 0x80000000 /* Ownership */ #define DEC21140_RXDESC_LS 0x00000100 /* Last Segment */ #define DEC21140_RXDESC_FS 0x00000200 /* First Segment */ #define DEC21140_RXDESC_MF 0x00000400 /* Multicast Frame */ #define DEC21140_RXDESC_DE 0x00004000 /* Descriptor Error */ #define DEC21140_RXDESC_RCH 0x01000000 /* Sec. Addr. Chained */ #define DEC21140_RXDESC_RER 0x02000000 /* Receive End of Ring */ #define DEC21140_RXDESC_FL_SHIFT 16 #define DEC21140_RXDESC_LEN_MASK 0x7ff /* TX descriptors */ #define DEC21140_TXDESC_OWN 0x80000000 /* Ownership */ #define DEC21140_TXDESC_TCH 0x01000000 /* Sec. Addr. Chained */ #define DEC21140_TXDESC_TER 0x02000000 /* Transmit End of Ring */ #define DEC21140_TXDESC_SET 0x08000000 /* Setup frame */ #define DEC21140_TXDESC_FS 0x20000000 /* First Segment */ #define DEC21140_TXDESC_LS 0x40000000 /* Last Segment */ #define DEC21140_TXDESC_IC 0x80000000 /* IRQ on completion */ #define DEC21140_TXDESC_LEN_MASK 0x7ff /* RX Descriptor */ struct rx_desc { m_uint32_t rdes[4]; }; /* TX Descriptor */ struct tx_desc { m_uint32_t tdes[4]; }; /* DEC21140 Data */ struct dec21140_data { char *name; /* Physical addresses of current RX and TX descriptors */ m_uint32_t rx_current; m_uint32_t tx_current; /* CSR registers */ m_uint32_t csr[DEC21140_CSR_NR]; /* MII registers */ m_uint32_t mii_state; m_uint32_t mii_phy; m_uint32_t mii_reg; m_uint32_t mii_data; m_uint32_t mii_outbits; m_uint16_t mii_regs[32][32]; /* Ethernet unicast addresses */ n_eth_addr_t mac_addr[16]; u_int mac_addr_count; /* Device information */ struct vdevice *dev; /* PCI device information */ struct pci_device *pci_dev; /* Virtual machine */ vm_instance_t *vm; /* NetIO descriptor */ netio_desc_t *nio; /* TX ring scanner task id */ ptask_id_t tx_tid; }; /* Log a dec21140 message */ #define DEC21140_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* Check if a packet must be delivered to the emulated chip */ static inline int dec21140_handle_mac_addr(struct dec21140_data *d, m_uint8_t *pkt) { n_eth_hdr_t *hdr = (n_eth_hdr_t *)pkt; int i; /* Accept systematically frames if we are running is promiscuous mode */ if (d->csr[6] & DEC21140_CSR6_PROMISC) return(TRUE); /* Accept systematically all multicast frames */ if (eth_addr_is_mcast(&hdr->daddr)) return(TRUE); /* Accept frames directly for us, discard others */ for(i=0;imac_addr_count;i++) if (!memcmp(&d->mac_addr[i],&hdr->daddr,N_ETH_ALEN)) return(TRUE); return(FALSE); } /* Update MAC addresses */ static void dec21140_update_mac_addr(struct dec21140_data *d, u_char *setup_frame) { n_eth_addr_t addr; int i,nb_addr,addr_size; d->mac_addr_count = 0; addr_size = N_ETH_ALEN * 2; nb_addr = DEC21140_SETUP_FRAME_SIZE / addr_size; for(i=0;imac_addr[d->mac_addr_count],&addr,N_ETH_ALEN); DEC21140_LOG(d,"unicast MAC address: " "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", addr.eth_addr_byte[0],addr.eth_addr_byte[1], addr.eth_addr_byte[2],addr.eth_addr_byte[3], addr.eth_addr_byte[4],addr.eth_addr_byte[5]); d->mac_addr_count++; } } } /* Get a PCI register name */ _maybe_used static char *pci_cfgreg_name(int reg) { static char *name[] = { "FID", "FCS", "FRV", "FLT", "BIO", "BMA", "?", "?", "?", "?", "?", "?", "?", "?", "?", "FIT", "FDA" }; return((reg>=0) && (reg<=DEC21140_CSR_NR*4) && ((reg&3)==0) ? name[reg>>2] : "?"); } /* * read from register of DP83840A PHY */ static m_uint16_t mii_reg_read(struct dec21140_data *d) { #if DEBUG_MII_REGS DEC21140_LOG(d,"MII PHY read %d reg %d\n",d->mii_phy,d->mii_reg); #endif /* * if it's BASIC MODE STATUS REGISTER (BMSR) at address 0x1 * then tell them that "Link Status" is up and no troubles. */ if (d->mii_reg == 1) { if (d->nio != NULL) return(0x04); else return(0x00); } return(d->mii_regs[d->mii_phy][d->mii_reg]); } /* * write to register of DP83840A PHY */ static void mii_reg_write(struct dec21140_data *d) { #if DEBUG_MII_REGS DEC21140_LOG(d,"MII PHY write %d reg %d value %04x\n", d->mii_phy,d->mii_reg,d->mii_data); #endif assert(d->mii_phy < 32); assert(d->mii_reg < 32); d->mii_regs[d->mii_phy][d->mii_reg] = d->mii_data; } /* * process new bit sent by IOS to PHY. */ static void mii_newbit(struct dec21140_data *d,int newbit) { #if DEBUG_MII_REGS DEC21140_LOG(d,"MII state was %d\n",d->mii_state); #endif switch (d->mii_state) { case 0: /* init */ d->mii_state = newbit ? 0 : 1; d->mii_phy = 0; d->mii_reg = 0; d->mii_data = 0; break; case 1: /* already got 0 */ d->mii_state = newbit ? 2 : 0; break; case 2: /* already got attention */ d->mii_state = newbit ? 3 : 4; break; case 3: /* probably it's read */ d->mii_state = newbit ? 0 : 10; break; case 4: /* probably it's write */ d->mii_state = newbit ? 20 : 0; break; case 10: case 11: case 12: case 13: case 14: case 20: case 21: case 22: case 23: case 24: /* read or write state, read 5 bits of phy */ d->mii_phy <<= 1; d->mii_phy |= newbit; d->mii_state++; break; case 15: case 16: case 17: case 18: case 19: case 25: case 26: case 27: case 28: case 29: /* read or write state, read 5 bits of reg */ d->mii_reg <<= 1; d->mii_reg |= newbit; d->mii_state++; if (d->mii_state == 20) { /* read state, got everything */ d->mii_outbits = mii_reg_read (d) << 15; /* first bit will * be thrown away! */ d->mii_state = 0; } break; case 30: /* write state, read first waiting bit */ d->mii_state = newbit ? 31 : 0; break; case 31: /* write state, read second waiting bit */ d->mii_state = newbit ? 0 : 32; break; case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: /* write state, read 16 bits of data */ d->mii_data <<= 1; d->mii_data |= newbit; d->mii_state++; if (d->mii_state == 48) { /* write state, got everything */ mii_reg_write (d); d->mii_state = 0; } break; default: DEC21140_LOG(d,"MII impossible state\n"); } #if DEBUG_MII_REGS DEC21140_LOG(d,"MII state now %d\n",d->mii_state); #endif } /* Update the interrupt status */ static inline void dev_dec21140_update_irq_status(struct dec21140_data *d) { int trigger = FALSE; m_uint32_t csr5; /* Work on a temporary copy of csr5 */ csr5 = d->csr[5]; /* Compute Interrupt Summary */ csr5 &= ~(DEC21140_CSR5_AIS|DEC21140_CSR5_NIS); if (csr5 & DEC21140_NIS_BITS) { csr5 |= DEC21140_CSR5_NIS; trigger = TRUE; } if (csr5 & DEC21140_AIS_BITS) { csr5 |= DEC21140_CSR5_AIS; trigger = TRUE; } d->csr[5] = csr5; if (trigger) pci_dev_trigger_irq(d->vm,d->pci_dev); else { pci_dev_clear_irq(d->vm,d->pci_dev); } } /* * dev_dec21140_access() */ void *dev_dec21140_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct dec21140_data *d = dev->priv_data; u_int reg; /* which CSR register ? */ reg = offset / 8; if ((reg >= DEC21140_CSR_NR) || (offset % 8) != 0) { cpu_log(cpu,d->name,"invalid access to offset 0x%x\n",offset); return NULL; } if (op_type == MTS_READ) { #if DEBUG_CSR_REGS cpu_log(cpu,d->name,"read CSR%u value 0x%x\n",reg,d->csr[reg]); #endif switch(reg) { case 5: /* Dynamically construct CSR5 */ *data = 0; if (d->csr[6] & DEC21140_CSR6_START_RX) *data |= 0x03 << DEC21140_CSR5_RS_SHIFT; if (d->csr[6] & DEC21140_CSR6_START_TX) *data |= 0x03 << DEC21140_CSR5_TS_SHIFT; *data |= d->csr[5]; break; case 8: /* CSR8 is cleared when read (missed frame counter) */ d->csr[reg] = 0; *data = 0; break; default: *data = d->csr[reg]; } } else { #if DEBUG_CSR_REGS cpu_log(cpu,d->name,"write CSR%u value 0x%x\n",reg,(m_uint32_t)*data); #endif switch(reg) { case 3: d->csr[reg] = *data; d->rx_current = d->csr[reg]; break; case 4: d->csr[reg] = *data; d->tx_current = d->csr[reg]; break; case 5: d->csr[reg] &= ~(*data); dev_dec21140_update_irq_status(d); break; case 9: /* * CSR9, probably they want to mess with MII PHY * The protocol to PHY is like serial over one bit. * We will ignore clock 0 of read or write. * * This whole code is needed only to tell IOS that "Link Status" * bit in BMSR register of DP83840A PHY is set. * * Also it makes "sh contr f0/0" happy. */ d->csr[reg] = *data; if ((*data & ~DEC21140_CSR9_TX_BIT) == (DEC21140_CSR9_MII_READ| DEC21140_CSR9_READ|DEC21140_CSR9_MDC_CLOCK)) { /* * read, pop one bit from mii_outbits */ if (d->mii_outbits & (1<<31)) d->csr[9] |= DEC21140_CSR9_RX_BIT; else d->csr[9] &= ~DEC21140_CSR9_RX_BIT; d->mii_outbits <<= 1; } else if((*data&~DEC21140_CSR9_TX_BIT) == (DEC21140_CSR9_WRITE|DEC21140_CSR9_MDC_CLOCK)) { /* * write, we've got input, do state machine */ mii_newbit(d,(*data&DEC21140_CSR9_TX_BIT) ? 1 : 0); } break; default: d->csr[reg] = *data; } } return NULL; } /* * Get the address of the next RX descriptor. */ static m_uint32_t rxdesc_get_next(struct dec21140_data *d,m_uint32_t rxd_addr, struct rx_desc *rxd) { m_uint32_t nrxd_addr; /* go to the next descriptor */ if (rxd->rdes[1] & DEC21140_RXDESC_RER) nrxd_addr = d->csr[3]; else { if (rxd->rdes[1] & DEC21140_RXDESC_RCH) nrxd_addr = rxd->rdes[3]; else nrxd_addr = rxd_addr + sizeof(struct rx_desc); } return(nrxd_addr); } /* Read a RX descriptor */ static void rxdesc_read(struct dec21140_data *d,m_uint32_t rxd_addr, struct rx_desc *rxd) { /* get the next descriptor from VM physical RAM */ physmem_copy_from_vm(d->vm,rxd,rxd_addr,sizeof(struct rx_desc)); /* byte-swapping */ rxd->rdes[0] = vmtoh32(rxd->rdes[0]); rxd->rdes[1] = vmtoh32(rxd->rdes[1]); rxd->rdes[2] = vmtoh32(rxd->rdes[2]); rxd->rdes[3] = vmtoh32(rxd->rdes[3]); } /* * Try to acquire the specified RX descriptor. Returns TRUE if we have it. * It assumes that the byte-swapping is done. */ static inline int rxdesc_acquire(m_uint32_t rdes0) { return(rdes0 & DEC21140_RXDESC_OWN); } /* Put a packet in buffer(s) of a descriptor */ static void rxdesc_put_pkt(struct dec21140_data *d,struct rx_desc *rxd, u_char **pkt,ssize_t *pkt_len) { ssize_t len1,len2,cp_len; /* get rbs1 and rbs2 */ len1 = rxd->rdes[1] & DEC21140_RXDESC_LEN_MASK; len2 = (rxd->rdes[1] >> 10) & DEC21140_RXDESC_LEN_MASK; /* try with buffer #1 */ if (len1 != 0) { /* compute the data length to copy */ cp_len = m_min(len1,*pkt_len); /* copy packet data to the VM physical RAM */ physmem_copy_to_vm(d->vm,*pkt,rxd->rdes[2],cp_len); *pkt += cp_len; *pkt_len -= cp_len; } /* try with buffer #2 */ if ((len2 != 0) && !(rxd->rdes[1] & DEC21140_RXDESC_RCH)) { /* compute the data length to copy */ cp_len = m_min(len2,*pkt_len); /* copy packet data to the VM physical RAM */ physmem_copy_to_vm(d->vm,*pkt,rxd->rdes[3],cp_len); *pkt += cp_len; *pkt_len -= cp_len; } } /* * Put a packet in the RX ring of the DEC21140. */ static int dev_dec21140_receive_pkt(struct dec21140_data *d, u_char *pkt,ssize_t pkt_len) { m_uint32_t rx_start,rxdn_addr,rxdn_rdes0; struct rx_desc rxd0,rxdn,*rxdc; ssize_t tot_len = pkt_len; u_char *pkt_ptr = pkt; n_eth_hdr_t *hdr; int i; /* Truncate the packet if it is too big */ pkt_len = m_min(pkt_len,DEC21140_MAX_PKT_SIZE); /* Copy the current rxring descriptor */ rxdesc_read(d,d->rx_current,&rxd0); /* We must have the first descriptor... */ if (!rxdesc_acquire(rxd0.rdes[0])) return(FALSE); /* Remember the first RX descriptor address */ rx_start = d->rx_current; for(i=0,rxdc=&rxd0;tot_len>0;i++) { /* Put data into the descriptor buffers */ rxdesc_put_pkt(d,rxdc,&pkt_ptr,&tot_len); /* Get address of the next descriptor */ rxdn_addr = rxdesc_get_next(d,d->rx_current,rxdc); /* We have finished if the complete packet has been stored */ if (tot_len == 0) { rxdc->rdes[0] = DEC21140_RXDESC_LS; rxdc->rdes[0] |= (pkt_len + 4) << DEC21140_RXDESC_FL_SHIFT; /* if this is a multicast frame, set the appropriate bit */ hdr = (n_eth_hdr_t *)pkt; if (eth_addr_is_mcast(&hdr->daddr)) rxdc->rdes[0] |= DEC21140_RXDESC_MF; if (i != 0) physmem_copy_u32_to_vm(d->vm,d->rx_current,rxdc->rdes[0]); d->rx_current = rxdn_addr; break; } /* Get status of the next descriptor to see if we can acquire it */ rxdn_rdes0 = physmem_copy_u32_from_vm(d->vm,rxdn_addr); if (!rxdesc_acquire(rxdn_rdes0)) rxdc->rdes[0] = DEC21140_RXDESC_LS | DEC21140_RXDESC_DE; else rxdc->rdes[0] = 0; /* ok, no special flag */ /* Update the new status (only if we are not on the first desc) */ if (i != 0) physmem_copy_u32_to_vm(d->vm,d->rx_current,rxdc->rdes[0]); /* Update the RX pointer */ d->rx_current = rxdn_addr; if (rxdc->rdes[0] != 0) break; /* Read the next descriptor from VM physical RAM */ rxdesc_read(d,rxdn_addr,&rxdn); rxdc = &rxdn; } /* Update the first RX descriptor */ rxd0.rdes[0] |= DEC21140_RXDESC_FS; physmem_copy_u32_to_vm(d->vm,rx_start,rxd0.rdes[0]); /* Indicate that we have a frame ready */ d->csr[5] |= DEC21140_CSR5_RI; dev_dec21140_update_irq_status(d); return(TRUE); } /* Handle the DEC21140 RX ring */ static int dev_dec21140_handle_rxring(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, struct dec21140_data *d) { /* * Don't start receive if the RX ring address has not been set * and if the SR bit in CSR6 is not set yet. */ if ((d->csr[3] == 0) || !(d->csr[6] & DEC21140_CSR6_START_RX)) return(FALSE); #if DEBUG_RECEIVE DEC21140_LOG(d,"receiving a packet of %d bytes\n",pkt_len); mem_dump(log_file,pkt,pkt_len); #endif /* * Receive only multicast/broadcast trafic + unicast traffic * for this virtual machine. */ if (dec21140_handle_mac_addr(d,pkt)) return(dev_dec21140_receive_pkt(d,pkt,pkt_len)); return(FALSE); } /* Read a TX descriptor */ static void txdesc_read(struct dec21140_data *d,m_uint32_t txd_addr, struct tx_desc *txd) { /* get the descriptor from VM physical RAM */ physmem_copy_from_vm(d->vm,txd,txd_addr,sizeof(struct tx_desc)); /* byte-swapping */ txd->tdes[0] = vmtoh32(txd->tdes[0]); txd->tdes[1] = vmtoh32(txd->tdes[1]); txd->tdes[2] = vmtoh32(txd->tdes[2]); txd->tdes[3] = vmtoh32(txd->tdes[3]); } /* Set the address of the next TX descriptor */ static void txdesc_set_next(struct dec21140_data *d,struct tx_desc *txd) { if (txd->tdes[1] & DEC21140_TXDESC_TER) d->tx_current = d->csr[4]; else { if (txd->tdes[1] & DEC21140_TXDESC_TCH) d->tx_current = txd->tdes[3]; else d->tx_current += sizeof(struct tx_desc); } } /* Handle the TX ring (single packet) */ static int dev_dec21140_handle_txring_single(struct dec21140_data *d) { u_char pkt[DEC21140_MAX_PKT_SIZE],*pkt_ptr; u_char setup_frame[DEC21140_SETUP_FRAME_SIZE]; m_uint32_t tx_start,len1,len2,clen,tot_len; struct tx_desc txd0,ctxd,*ptxd; int done = FALSE; /* * Don't start transmit if the txring address has not been set * and if the ST bit in CSR6 is not set yet. */ if ((d->csr[4] == 0) || (!(d->csr[6] & DEC21140_CSR6_START_TX))) return(FALSE); /* Check if the NIO can transmit */ if (!netio_can_transmit(d->nio)) return(FALSE); /* Copy the current txring descriptor */ tx_start = d->tx_current; ptxd = &txd0; txdesc_read(d,tx_start,ptxd); /* If we don't own the first descriptor, we cannot transmit */ if (!(txd0.tdes[0] & DEC21140_TXDESC_OWN)) return(FALSE); /* * Ignore setup frames (clear the own bit and skip). * We extract unicast MAC addresses to allow only appropriate traffic * to pass. */ if (!(txd0.tdes[1] & (DEC21140_TXDESC_FS|DEC21140_TXDESC_LS))) { len1 = ptxd->tdes[1] & DEC21140_TXDESC_LEN_MASK; len2 = (ptxd->tdes[1] >> 11) & DEC21140_TXDESC_LEN_MASK; if (txd0.tdes[1] & DEC21140_TXDESC_SET) { physmem_copy_from_vm(d->vm,setup_frame,ptxd->tdes[2], sizeof(setup_frame)); dec21140_update_mac_addr(d,setup_frame); } txdesc_set_next(d,ptxd); goto clear_txd0_own_bit; } #if DEBUG_TRANSMIT DEC21140_LOG(d,"dec21140_handle_txring: 1st desc: " "tdes[0]=0x%x, tdes[1]=0x%x, tdes[2]=0x%x, tdes[3]=0x%x\n", ptxd->tdes[0],ptxd->tdes[1],ptxd->tdes[2],ptxd->tdes[3]); #endif /* Empty packet for now */ pkt_ptr = pkt; tot_len = 0; do { #if DEBUG_TRANSMIT DEC21140_LOG(d,"dec21140_handle_txring: loop: " "tdes[0]=0x%x, tdes[1]=0x%x, tdes[2]=0x%x, tdes[3]=0x%x\n", ptxd->tdes[0],ptxd->tdes[1],ptxd->tdes[2],ptxd->tdes[3]); #endif if (!(ptxd->tdes[0] & DEC21140_TXDESC_OWN)) { DEC21140_LOG(d,"dec21140_handle_txring: descriptor not owned!\n"); return(FALSE); } len1 = ptxd->tdes[1] & DEC21140_TXDESC_LEN_MASK; len2 = (ptxd->tdes[1] >> 11) & DEC21140_TXDESC_LEN_MASK; clen = len1 + len2; /* Be sure that we have either len1 or len2 not null */ if (clen != 0) { if (len1 != 0) physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->tdes[2],len1); if ((len2 != 0) && !(ptxd->tdes[1] & DEC21140_TXDESC_TCH)) physmem_copy_from_vm(d->vm,pkt_ptr+len1,ptxd->tdes[3],len2); } pkt_ptr += clen; tot_len += clen; /* Clear the OWN bit if this is not the first descriptor */ if (!(ptxd->tdes[1] & DEC21140_TXDESC_FS)) physmem_copy_u32_to_vm(d->vm,d->tx_current,0); /* Go to the next descriptor */ txdesc_set_next(d,ptxd); /* * Copy the next txring descriptor (ignore setup frames that * have both FS and LS bit cleared). */ if (!(ptxd->tdes[1] & (DEC21140_TXDESC_LS|DEC21140_TXDESC_SET))) { txdesc_read(d,d->tx_current,&ctxd); ptxd = &ctxd; } else done = TRUE; }while(!done); if (tot_len != 0) { #if DEBUG_TRANSMIT DEC21140_LOG(d,"sending packet of %u bytes\n",tot_len); mem_dump(log_file,pkt,tot_len); #endif /* rewrite ISL header if required */ cisco_isl_rewrite(pkt,tot_len); /* send it on wire */ netio_send(d->nio,pkt,tot_len); } clear_txd0_own_bit: /* Clear the OWN flag of the first descriptor */ physmem_copy_u32_to_vm(d->vm,tx_start,0); /* Interrupt on completion ? */ if (txd0.tdes[1] & DEC21140_TXDESC_IC) { d->csr[5] |= DEC21140_CSR5_TI; dev_dec21140_update_irq_status(d); } return(TRUE); } /* Handle the TX ring */ static int dev_dec21140_handle_txring(struct dec21140_data *d) { int i; for(i=0;inio); return(TRUE); } /* * pci_dec21140_read() * * Read a PCI register. */ static m_uint32_t pci_dec21140_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { struct dec21140_data *d = dev->priv_data; #if DEBUG_PCI_REGS DEC21140_LOG(d,"read C%s(%u)\n",pci_cfgreg_name(reg),reg); #endif switch (reg) { case DEC21140_PCI_CFID_REG_OFFSET: return(0x00091011); case DEC21140_PCI_CFRV_REG_OFFSET: return(0x02000011); case DEC21140_PCI_CBMA_REG_OFFSET: return(d->dev->phys_addr); default: return(0); } } /* * pci_dec21140_write() * * Write a PCI register. */ static void pci_dec21140_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct dec21140_data *d = dev->priv_data; #if DEBUG_PCI_REGS DEC21140_LOG(d,"write C%s(%u) value 0x%x\n",pci_cfgreg_name(reg),reg,value); #endif switch(reg) { case DEC21140_PCI_CBMA_REG_OFFSET: vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); DEC21140_LOG(d,"registers are mapped at 0x%x\n",value); break; } } /* * dev_dec21140_init() * * Generic DEC21140 initialization code. */ struct dec21140_data *dev_dec21140_init(vm_instance_t *vm,char *name, struct pci_bus *pci_bus,int pci_device, int irq) { struct dec21140_data *d; struct pci_device *pci_dev; struct vdevice *dev; /* Allocate the private data structure for DEC21140 */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"%s (DEC21140): out of memory\n",name); return NULL; } memset(d,0,sizeof(*d)); /* Add as PCI device */ pci_dev = pci_dev_add(pci_bus,name, DEC21140_PCI_VENDOR_ID,DEC21140_PCI_PRODUCT_ID, pci_device,0,irq, d,NULL,pci_dec21140_read,pci_dec21140_write); if (!pci_dev) { fprintf(stderr,"%s (DEC21140): unable to create PCI device.\n",name); goto err_pci_dev; } /* Create the device itself */ if (!(dev = dev_create(name))) { fprintf(stderr,"%s (DEC21140): unable to create device.\n",name); goto err_dev; } d->name = name; d->vm = vm; d->pci_dev = pci_dev; d->dev = dev; /* Basic register setup */ d->csr[0] = 0xfff80000; d->csr[5] = 0xfc000000; d->csr[8] = 0xfffe0000; dev->phys_addr = 0; dev->phys_len = 0x20000; dev->handler = dev_dec21140_access; dev->priv_data = d; return(d); err_dev: pci_dev_remove(pci_dev); err_pci_dev: free(d); return NULL; } /* Remove a DEC21140 device */ void dev_dec21140_remove(struct dec21140_data *d) { if (d != NULL) { pci_dev_remove(d->pci_dev); vm_unbind_device(d->vm,d->dev); cpu_group_rebuild_mts(d->vm->cpu_group); free(d->dev); free(d); } } /* Bind a NIO to DEC21140 device */ int dev_dec21140_set_nio(struct dec21140_data *d,netio_desc_t *nio) { /* check that a NIO is not already bound */ if (d->nio != NULL) return(-1); d->nio = nio; d->tx_tid = ptask_add((ptask_callback)dev_dec21140_handle_txring,d,NULL); netio_rxl_add(nio,(netio_rx_handler_t)dev_dec21140_handle_rxring,d,NULL); return(0); } /* Unbind a NIO from a DEC21140 device */ void dev_dec21140_unset_nio(struct dec21140_data *d) { if (d->nio != NULL) { ptask_remove(d->tx_tid); netio_rxl_remove(d->nio); d->nio = NULL; } } dynamips-0.2.14/common/dev_dec21140.h000066400000000000000000000014531241034141600170430ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __DEV_DEC21140_H__ #define __DEV_DEC21140_H__ #include #include "utils.h" #include "vm.h" #include "cpu.h" #include "device.h" #include "net_io.h" /* Generic DEC21140 initialization code */ struct dec21140_data *dev_dec21140_init(vm_instance_t *vm,char *name, struct pci_bus *pci_bus,int pci_device, int irq); /* Remove a DEC21140 device */ void dev_dec21140_remove(struct dec21140_data *d); /* Bind a NIO to DEC21140 device */ int dev_dec21140_set_nio(struct dec21140_data *d,netio_desc_t *nio); /* Unbind a NIO from a DEC21140 device */ void dev_dec21140_unset_nio(struct dec21140_data *d); #endif dynamips-0.2.14/common/dev_dec21x50.c000066400000000000000000000047631241034141600171550ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * DEC21050/DEC21150 PCI bridges. * This is just a fake device. */ #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "pci_dev.h" #define PCI_VENDOR_DEC 0x1011 #define PCI_PRODUCT_DEC_21050 0x0001 #define PCI_PRODUCT_DEC_21052 0x0021 #define PCI_PRODUCT_DEC_21150 0x0023 #define PCI_PRODUCT_DEC_21152 0x0024 #define PCI_PRODUCT_DEC_21154 0x0026 /* * dev_dec21050_init() */ int dev_dec21050_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus) { struct pci_device *dev; dev = pci_bridge_create_dev(pci_bus,"dec21050", PCI_VENDOR_DEC,PCI_PRODUCT_DEC_21050, pci_device,0,sec_bus,NULL,NULL); return((dev != NULL) ? 0 : -1); } /* * dev_dec21052_init() */ int dev_dec21052_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus) { struct pci_device *dev; dev = pci_bridge_create_dev(pci_bus,"dec21052", PCI_VENDOR_DEC,PCI_PRODUCT_DEC_21052, pci_device,0,sec_bus,NULL,NULL); return((dev != NULL) ? 0 : -1); } /* * dev_dec21150_init() */ int dev_dec21150_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus) { struct pci_device *dev; dev = pci_bridge_create_dev(pci_bus,"dec21150", PCI_VENDOR_DEC,PCI_PRODUCT_DEC_21150, pci_device,0,sec_bus,NULL,NULL); return((dev != NULL) ? 0 : -1); } /* * dev_dec21152_init() */ int dev_dec21152_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus) { struct pci_device *dev; dev = pci_bridge_create_dev(pci_bus,"dec21152", PCI_VENDOR_DEC,PCI_PRODUCT_DEC_21152, pci_device,0,sec_bus,NULL,NULL); return((dev != NULL) ? 0 : -1); } /* * dev_dec21154_init() */ int dev_dec21154_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus) { struct pci_device *dev; dev = pci_bridge_create_dev(pci_bus,"dec21154", PCI_VENDOR_DEC,PCI_PRODUCT_DEC_21154, pci_device,0,sec_bus,NULL,NULL); return((dev != NULL) ? 0 : -1); } dynamips-0.2.14/common/dev_ds1620.c000066400000000000000000000113611241034141600166310ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Dallas DS1620 Temperature sensors. */ #include "dev_ds1620.h" /* DS1620 commands */ #define DS1620_READ_TEMP 0xAA #define DS1620_READ_COUNTER 0xA0 #define DS1620_READ_SLOPE 0xA9 #define DS1620_WRITE_TH 0x01 #define DS1620_WRITE_TL 0x02 #define DS1620_READ_TH 0xA1 #define DS1620_READ_TL 0xA2 #define DS1620_START_CONVT 0xEE #define DS1620_STOP_CONVT 0x22 #define DS1620_WRITE_CONFIG 0x0C #define DS1620_READ_CONFIG 0xAC /* DS1620 config register */ #define DS1620_CONFIG_STATUS_DONE 0x80 #define DS1620_CONFIG_STATUS_THF 0x40 #define DS1620_CONFIG_STATUS_TLF 0x20 #define DS1620_CONFIG_STATUS_CPU 0x02 #define DS1620_CONFIG_STATUS_1SHOT 0x01 /* Size of various operations in bits (command, config and temp data) */ #define DS1620_CMD_SIZE 8 #define DS1620_CONFIG_SIZE 8 #define DS1620_TEMP_SIZE 9 /* Internal states */ enum { DS1620_STATE_CMD_IN, DS1620_STATE_DATA_IN, DS1620_STATE_DATA_OUT, }; /* Update status register (TH/TL values) */ static void ds1620_update_status(struct ds1620_data *d) { if (d->temp >= d->reg_th) d->reg_config |= DS1620_CONFIG_STATUS_THF; if (d->temp <= d->reg_tl) d->reg_config |= DS1620_CONFIG_STATUS_TLF; } /* Set temperature */ void ds1620_set_temp(struct ds1620_data *d,int temp) { d->temp = temp << 1; ds1620_update_status(d); } /* Set reset bit */ void ds1620_set_rst_bit(struct ds1620_data *d,u_int rst_bit) { if (!rst_bit) { d->state = DS1620_STATE_CMD_IN; d->cmd_pos = 0; d->cmd = 0; d->data = 0; d->data_pos = 0; d->data_len = 0; } } /* Set state after command */ static void ds1620_cmd_set_state(struct ds1620_data *d) { d->data = 0; d->data_pos = 0; switch(d->cmd) { case DS1620_READ_TEMP: d->state = DS1620_STATE_DATA_OUT; d->data_len = DS1620_TEMP_SIZE; d->data = d->temp; break; case DS1620_READ_COUNTER: case DS1620_READ_SLOPE: d->state = DS1620_STATE_DATA_OUT; d->data_len = DS1620_TEMP_SIZE; d->data = 0; break; case DS1620_WRITE_TH: case DS1620_WRITE_TL: d->state = DS1620_STATE_DATA_IN; d->data_len = DS1620_TEMP_SIZE; break; case DS1620_READ_TH: d->state = DS1620_STATE_DATA_OUT; d->data_len = DS1620_TEMP_SIZE; d->data = d->reg_th; break; case DS1620_READ_TL: d->state = DS1620_STATE_DATA_OUT; d->data_len = DS1620_TEMP_SIZE; d->data = d->reg_tl; break; case DS1620_START_CONVT: case DS1620_STOP_CONVT: d->state = DS1620_STATE_CMD_IN; break; case DS1620_WRITE_CONFIG: d->state = DS1620_STATE_DATA_IN; d->data_len = DS1620_CONFIG_SIZE; break; case DS1620_READ_CONFIG: d->state = DS1620_STATE_DATA_OUT; d->data_len = DS1620_CONFIG_SIZE; d->data = d->reg_config; break; } } /* Execute command */ static void ds1620_exec_cmd(struct ds1620_data *d) { switch(d->cmd) { case DS1620_WRITE_TH: d->reg_th = d->data; break; case DS1620_WRITE_TL: d->reg_tl = d->data; break; case DS1620_WRITE_CONFIG: d->reg_config = d->data; break; } /* return in command input state */ d->state = DS1620_STATE_CMD_IN; } /* Write data bit */ void ds1620_write_data_bit(struct ds1620_data *d,u_int data_bit) { /* CLK must be low */ if (d->clk_bit != 0) return; switch(d->state) { case DS1620_STATE_CMD_IN: if (data_bit) d->cmd |= 1 << d->cmd_pos; if (++d->cmd_pos == DS1620_CMD_SIZE) ds1620_cmd_set_state(d); break; case DS1620_STATE_DATA_OUT: /* ignore input since it shouldn't happen */ break; case DS1620_STATE_DATA_IN: if (data_bit) d->data |= 1 << d->data_pos; if (++d->data_pos == d->data_len) ds1620_exec_cmd(d); break; } } /* Read data bit */ u_int ds1620_read_data_bit(struct ds1620_data *d) { u_int val; if (d->state != DS1620_STATE_DATA_OUT) return(1); val = (d->data >> d->data_pos) & 0x1; if (++d->data_pos == d->data_len) { /* return in command input state */ d->state = DS1620_STATE_CMD_IN; } return(val); } /* Initialize a DS1620 */ void ds1620_init(struct ds1620_data *d,int temp) { memset(d,0,sizeof(*d)); /* reset state */ ds1620_set_rst_bit(d,0); /* set initial temperature */ ds1620_set_temp(d,temp); /* chip in CPU mode (3-wire communications) */ d->reg_config = DS1620_CONFIG_STATUS_CPU; } dynamips-0.2.14/common/dev_ds1620.h000066400000000000000000000020271241034141600166350ustar00rootroot00000000000000 /* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Dallas DS1620 Temperature sensors. */ #ifndef __DEV_DS1620_H__ #define __DEV_DS1620_H__ #include "dynamips_common.h" struct ds1620_data { u_int state; u_int clk_bit; int temp; /* command input */ m_uint8_t cmd; u_int cmd_pos; /* data input/output */ m_uint16_t data; u_int data_pos; u_int data_len; /* registers */ m_uint8_t reg_config; m_uint16_t reg_th,reg_tl; }; /* Set CLK bit */ static inline void ds1620_set_clk_bit(struct ds1620_data *d,u_int clk_bit) { d->clk_bit = clk_bit; } /* Set temperature */ void ds1620_set_temp(struct ds1620_data *d,int temp); /* Set reset bit */ void ds1620_set_rst_bit(struct ds1620_data *d,u_int rst_bit); /* Write data bit */ void ds1620_write_data_bit(struct ds1620_data *d,u_int data_bit); /* Read data bit */ u_int ds1620_read_data_bit(struct ds1620_data *d); /* Initialize a DS1620 */ void ds1620_init(struct ds1620_data *d,int temp); #endif dynamips-0.2.14/common/dev_flash.c000066400000000000000000000157161241034141600170170ustar00rootroot00000000000000/* * Cisco Simulation Platform. * Copyright (c) 2006 Christophe Fillot. All rights reserved. * * 23-Oct-2006: only basic code at this time. * * Considering the access pattern, this might be emulating SST39VF1681/SST39VF1682. */ #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #define DEBUG_ACCESS 0 #define DEBUG_WRITE 0 /* Flash private data */ struct flash_data { vm_obj_t vm_obj; struct vdevice dev; m_uint32_t state; u_int sector_size; char *filename; }; #define BPTR(d,offset) (((char *)d->dev.host_addr) + offset) /* * dev_bootflash_access() */ void *dev_flash_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct flash_data *d = dev->priv_data; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,dev->name, "read access to offset=0x%x, pc=0x%llx (state=%u)\n", offset,cpu_get_pc(cpu),d->state); } else { cpu_log(cpu,dev->name, "write access to vaddr=0x%x, pc=0x%llx, val=0x%llx (state=%d)\n", offset,cpu_get_pc(cpu),*data,d->state); } #endif if (op_type == MTS_READ) { switch(d->state) { case 0: return(BPTR(d,offset)); default: cpu_log(cpu,dev->name,"read: unhandled state %d\n",d->state); } return NULL; } /* Write mode */ #if DEBUG_WRITE cpu_log(cpu,dev->name,"write to offset 0x%x, data=0x%llx\n",offset,*data); #endif switch(d->state) { /* Initial Cycle */ case 0: switch(offset) { case 0xAAA: if (*data == 0xAA) d->state = 1; break; default: switch(*data) { case 0xB0: /* Erase/Program Suspend */ d->state = 0; break; case 0x30: /* Erase/Program Resume */ d->state = 0; break; case 0xF0: /* Product ID Exit */ break; } } break; /* Cycle 1 was: 0xAAA, 0xAA */ case 1: if ((offset != 0x555) && (*data != 0x55)) d->state = 0; else d->state = 2; break; /* Cycle 1 was: 0xAAA, 0xAA, Cycle 2 was: 0x555, 0x55 */ case 2: d->state = 0; if (offset == 0xAAA) { switch(*data) { case 0x80: d->state = 3; break; case 0xA0: /* Byte/Word program */ d->state = 4; break; case 0xF0: /* Product ID Exit */ break; case 0xC0: /* Program Protection Register / Lock Protection Register */ d->state = 5; break; case 0x90: /* Product ID Entry / Status of Block B protection */ d->state = 6; break; case 0xD0: /* Set configuration register */ d->state = 7; break; } } break; /* * Cycle 1 was 0xAAA, 0xAA * Cycle 2 was 0x555, 0x55 * Cycle 3 was 0xAAA, 0x80 */ case 3: if ((offset != 0xAAA) && (*data != 0xAA)) d->state = 0; else d->state = 8; break; /* * Cycle 1 was 0xAAA, 0xAA * Cycle 2 was 0x555, 0x55 * Cycle 3 was 0xAAA, 0x80 * Cycle 4 was 0xAAA, 0xAA */ case 8: if ((offset != 0x555) && (*data != 0x55)) d->state = 0; else d->state = 9; break; /* * Cycle 1 was 0xAAA, 0xAA * Cycle 2 was 0x555, 0x55 * Cycle 3 was 0xAAA, 0x80 * Cycle 4 was 0xAAA, 0xAA * Cycle 5 was 0x555, 0x55 */ case 9: d->state = 0; switch(*data) { case 0x10: /* Chip Erase */ memset(BPTR(d,offset),0,d->dev.phys_len); break; case 0x30: /* Sector Erase */ memset(BPTR(d,offset),0,d->sector_size); break; case 0xA0: /* Enter Single Pulse Program Mode */ break; case 0x60: /* Sector Lockdown */ break; } break; /* Byte/Word Program */ case 4: d->state = 0; *(m_uint8_t *)(BPTR(d,offset)) = *data; break; default: cpu_log(cpu,dev->name,"write: unhandled state %d\n",d->state); } return NULL; } /* Copy data directly to a flash device */ int dev_flash_copy_data(vm_obj_t *obj,m_uint32_t offset, u_char *ptr,ssize_t len) { struct flash_data *d = obj->data; u_char *p; if (!d || !d->dev.host_addr) return(-1); p = (u_char *)d->dev.host_addr + offset; memcpy(p,ptr,len); return(0); } /* Shutdown a flash device */ void dev_flash_shutdown(vm_instance_t *vm,struct flash_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* We don't remove the file, since it used as permanent storage */ if (d->filename) free(d->filename); /* Free the structure itself */ free(d); } } /* Create a Flash device */ vm_obj_t *dev_flash_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len) { struct flash_data *d; u_char *ptr; /* Allocate the private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"Flash: unable to create device.\n"); return NULL; } memset(d,0,sizeof(*d)); d->sector_size = 0x4000; vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_flash_shutdown; if (!(d->filename = vm_build_filename(vm,name))) { fprintf(stderr,"Flash: unable to create filename.\n"); goto err_filename; } dev_init(&d->dev); d->dev.name = name; d->dev.priv_data = d; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_flash_access; d->dev.fd = memzone_create_file(d->filename,d->dev.phys_len,&ptr); d->dev.host_addr = (m_iptr_t)ptr; d->dev.flags = VDEVICE_FLAG_NO_MTS_MMAP; if (d->dev.fd == -1) { fprintf(stderr,"Flash: unable to map file '%s'\n",d->filename); goto err_fd_create; } /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(&d->vm_obj); err_fd_create: free(d->filename); err_filename: free(d); return NULL; } dynamips-0.2.14/common/dev_gt.c000066400000000000000000002442111241034141600163260ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Galileo GT64010/GT64120A/GT96100A system controller. * * The DMA stuff is not complete, only "normal" transfers are working * (source and destination addresses incrementing). * * Also, these transfers are "instantaneous" from a CPU point-of-view: when * a channel is enabled, the transfer is immediately done. So, this is not * very realistic. */ #include #include #include #include #include "utils.h" #include "net.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net_io.h" #include "ptask.h" #include "dev_gt.h" /* Debugging flags */ #define DEBUG_UNKNOWN 0 #define DEBUG_DMA 0 #define DEBUG_SDMA 0 #define DEBUG_MPSC 0 #define DEBUG_MII 0 #define DEBUG_ETH 0 #define DEBUG_ETH_TX 0 #define DEBUG_ETH_RX 0 #define DEBUG_ETH_HASH 0 /* PCI identification */ #define PCI_VENDOR_GALILEO 0x11ab /* Galileo Technology */ #define PCI_PRODUCT_GALILEO_GT64010 0x0146 /* GT-64010 */ #define PCI_PRODUCT_GALILEO_GT64011 0x4146 /* GT-64011 */ #define PCI_PRODUCT_GALILEO_GT64120 0x4620 /* GT-64120 */ #define PCI_PRODUCT_GALILEO_GT96100 0x9653 /* GT-96100 */ /* === Global definitions ================================================= */ /* Interrupt High Cause Register */ #define GT_IHCR_ETH0_SUM 0x00000001 #define GT_IHCR_ETH1_SUM 0x00000002 #define GT_IHCR_SDMA_SUM 0x00000010 /* Serial Cause Register */ #define GT_SCR_ETH0_SUM 0x00000001 #define GT_SCR_ETH1_SUM 0x00000002 #define GT_SCR_SDMA_SUM 0x00000010 #define GT_SCR_SDMA0_SUM 0x00000100 #define GT_SCR_MPSC0_SUM 0x00000200 /* === DMA definitions ==================================================== */ #define GT_DMA_CHANNELS 4 #define GT_DMA_FLYBY_ENABLE 0x00000001 /* FlyBy Enable */ #define GT_DMA_FLYBY_RDWR 0x00000002 /* SDRAM Read/Write (FlyBy) */ #define GT_DMA_SRC_DIR 0x0000000c /* Source Direction */ #define GT_DMA_DST_DIR 0x00000030 /* Destination Direction */ #define GT_DMA_DATA_LIMIT 0x000001c0 /* Data Transfer Limit */ #define GT_DMA_CHAIN_MODE 0x00000200 /* Chained Mode */ #define GT_DMA_INT_MODE 0x00000400 /* Interrupt Mode */ #define GT_DMA_TRANS_MODE 0x00000800 /* Transfer Mode */ #define GT_DMA_CHAN_ENABLE 0x00001000 /* Channel Enable */ #define GT_DMA_FETCH_NEXT 0x00002000 /* Fetch Next Record */ #define GT_DMA_ACT_STATUS 0x00004000 /* DMA Activity Status */ #define GT_DMA_SDA 0x00008000 /* Source/Destination Alignment */ #define GT_DMA_MDREQ 0x00010000 /* Mask DMA Requests */ #define GT_DMA_CDE 0x00020000 /* Close Descriptor Enable */ #define GT_DMA_EOTE 0x00040000 /* End-of-Transfer (EOT) Enable */ #define GT_DMA_EOTIE 0x00080000 /* EOT Interrupt Enable */ #define GT_DMA_ABORT 0x00100000 /* Abort DMA Transfer */ #define GT_DMA_SLP 0x00600000 /* Override Source Address */ #define GT_DMA_DLP 0x01800000 /* Override Dest Address */ #define GT_DMA_RLP 0x06000000 /* Override Record Address */ #define GT_DMA_REQ_SRC 0x10000000 /* DMA Request Source */ /* Galileo DMA channel */ struct dma_channel { m_uint32_t byte_count; m_uint32_t src_addr; m_uint32_t dst_addr; m_uint32_t cdptr; m_uint32_t nrptr; m_uint32_t ctrl; }; /* === Serial DMA (SDMA) ================================================== */ /* SDMA: 2 groups of 8 channels */ #define GT_SDMA_CHANNELS 8 #define GT_SDMA_GROUPS 2 /* SDMA channel */ struct sdma_channel { u_int id; m_uint32_t sdc; m_uint32_t sdcm; m_uint32_t rx_desc; m_uint32_t rx_buf_ptr; m_uint32_t scrdp; m_uint32_t tx_desc; m_uint32_t sctdp; m_uint32_t sftdp; }; /* SGCR: SDMA Group Register */ #define GT_REG_SGC 0x101af0 /* SDMA cause register: 8 fields (1 for each channel) of 4 bits */ #define GT_SDMA_CAUSE_RXBUF0 0x01 #define GT_SDMA_CAUSE_RXERR0 0x02 #define GT_SDMA_CAUSE_TXBUF0 0x04 #define GT_SDMA_CAUSE_TXEND0 0x08 /* SDMA channel register offsets */ #define GT_SDMA_SDC 0x000900 /* Configuration Register */ #define GT_SDMA_SDCM 0x000908 /* Command Register */ #define GT_SDMA_RX_DESC 0x008900 /* RX descriptor */ #define GT_SDMA_SCRDP 0x008910 /* Current RX descriptor */ #define GT_SDMA_TX_DESC 0x00c900 /* TX descriptor */ #define GT_SDMA_SCTDP 0x00c910 /* Current TX desc. pointer */ #define GT_SDMA_SFTDP 0x00c914 /* First TX desc. pointer */ /* SDCR: SDMA Configuration Register */ #define GT_SDCR_RFT 0x00000001 /* Receive FIFO Threshold */ #define GT_SDCR_SFM 0x00000002 /* Single Frame Mode */ #define GT_SDCR_RC 0x0000003c /* Retransmit count */ #define GT_SDCR_BLMR 0x00000040 /* Big/Little Endian RX mode */ #define GT_SDCR_BLMT 0x00000080 /* Big/Litlle Endian TX mode */ #define GT_SDCR_POVR 0x00000100 /* PCI override */ #define GT_SDCR_RIFB 0x00000200 /* RX IRQ on frame boundary */ #define GT_SDCR_BSZ 0x00003000 /* Burst size */ /* SDCMR: SDMA Command Register */ #define GT_SDCMR_ERD 0x00000080 /* Enable RX DMA */ #define GT_SDCMR_AR 0x00008000 /* Abort Receive */ #define GT_SDCMR_STD 0x00010000 /* Stop TX */ #define GT_SDCMR_STDH GT_SDCMR_STD /* Stop TX High */ #define GT_SDCMR_STDL 0x00020000 /* Stop TX Low */ #define GT_SDCMR_TXD 0x00800000 /* TX Demand */ #define GT_SDCMR_TXDH GT_SDCMR_TXD /* Start TX High */ #define GT_SDCMR_TXDL 0x01000000 /* Start TX Low */ #define GT_SDCMR_AT 0x80000000 /* Abort Transmit */ /* SDMA RX/TX descriptor */ struct sdma_desc { m_uint32_t buf_size; m_uint32_t cmd_stat; m_uint32_t next_ptr; m_uint32_t buf_ptr; }; /* SDMA Descriptor Command/Status word */ #define GT_SDMA_CMD_O 0x80000000 /* Owner bit */ #define GT_SDMA_CMD_AM 0x40000000 /* Auto-mode */ #define GT_SDMA_CMD_EI 0x00800000 /* Enable Interrupt */ #define GT_SDMA_CMD_F 0x00020000 /* First buffer */ #define GT_SDMA_CMD_L 0x00010000 /* Last buffer */ #define GT_SDMA_CMD_OFFSET offsetof(struct sdma_desc,cmd_stat) /* Offset of the Command/Status word */ /* === MultiProtocol Serial Controller (MPSC) ============================= */ /* 8 MPSC channels */ #define GT_MPSC_CHANNELS 8 /* MPSC channel */ struct mpsc_channel { m_uint32_t mmcrl; m_uint32_t mmcrh; m_uint32_t mpcr; m_uint32_t chr[10]; vtty_t *vtty; netio_desc_t *nio; }; #define GT_MPSC_MMCRL 0x000A00 /* Main Config Register Low */ #define GT_MPSC_MMCRH 0x000A04 /* Main Config Register High */ #define GT_MPSC_MPCR 0x000A08 /* Protocol Config Register */ #define GT_MPSC_CHR1 0x000A0C #define GT_MPSC_CHR2 0x000A10 #define GT_MPSC_CHR3 0x000A14 #define GT_MPSC_CHR4 0x000A18 #define GT_MPSC_CHR5 0x000A1C #define GT_MPSC_CHR6 0x000A20 #define GT_MPSC_CHR7 0x000A24 #define GT_MPSC_CHR8 0x000A28 #define GT_MPSC_CHR9 0x000A2C #define GT_MPSC_CHR10 0x000A30 #define GT_MMCRL_MODE_MASK 0x0000007 #define GT_MPSC_MODE_HDLC 0 #define GT_MPSC_MODE_UART 4 #define GT_MPSC_MODE_BISYNC 5 /* === Ethernet definitions =============================================== */ #define GT_ETH_PORTS 2 #define GT_MAX_PKT_SIZE 2048 /* SMI register */ #define GT_SMIR_DATA_MASK 0x0000FFFF #define GT_SMIR_PHYAD_MASK 0x001F0000 /* PHY Device Address */ #define GT_SMIR_PHYAD_SHIFT 16 #define GT_SMIR_REGAD_MASK 0x03e00000 /* PHY Device Register Address */ #define GT_SMIR_REGAD_SHIFT 21 #define GT_SMIR_OPCODE_MASK 0x04000000 /* Opcode (0: write, 1: read) */ #define GT_SMIR_OPCODE_READ 0x04000000 #define GT_SMIR_RVALID_FLAG 0x08000000 /* Read Valid */ #define GT_SMIR_BUSY_FLAG 0x10000000 /* Busy: 1=op in progress */ /* PCR: Port Configuration Register */ #define GT_PCR_PM 0x00000001 /* Promiscuous mode */ #define GT_PCR_RBM 0x00000002 /* Reject broadcast mode */ #define GT_PCR_PBF 0x00000004 /* Pass bad frames */ #define GT_PCR_EN 0x00000080 /* Port Enabled/Disabled */ #define GT_PCR_LPBK 0x00000300 /* Loopback mode */ #define GT_PCR_FC 0x00000400 /* Force collision */ #define GT_PCR_HS 0x00001000 /* Hash size */ #define GT_PCR_HM 0x00002000 /* Hash mode */ #define GT_PCR_HDM 0x00004000 /* Hash default mode */ #define GT_PCR_HD 0x00008000 /* Duplex Mode */ #define GT_PCR_ISL 0x70000000 /* ISL enabled (0x06) */ #define GT_PCR_ACCS 0x80000000 /* Accelerate Slot Time */ /* PCXR: Port Configuration Extend Register */ #define GT_PCXR_IGMP 0x00000001 /* IGMP packet capture */ #define GT_PCXR_SPAN 0x00000002 /* BPDU packet capture */ #define GT_PCXR_PAR 0x00000004 /* Partition Enable */ #define GT_PCXR_PRIOTX 0x00000038 /* Priority weight for TX */ #define GT_PCXR_PRIORX 0x000000C0 /* Priority weight for RX */ #define GT_PCXR_PRIORX_OV 0x00000100 /* Prio RX override */ #define GT_PCXR_DPLX_EN 0x00000200 /* Autoneg for Duplex */ #define GT_PCXR_FCTL_EN 0x00000400 /* Autoneg for 802.3x */ #define GT_PCXR_FLP 0x00000800 /* Force Link Pass */ #define GT_PCXR_FCTL 0x00001000 /* Flow Control Mode */ #define GT_PCXR_MFL 0x0000C000 /* Maximum Frame Length */ #define GT_PCXR_MIB_CLR_MODE 0x00010000 /* MIB counters clear mode */ #define GT_PCXR_SPEED 0x00040000 /* Port Speed */ #define GT_PCXR_SPEED_EN 0x00080000 /* Autoneg for Speed */ #define GT_PCXR_RMII_EN 0x00100000 /* RMII Enable */ #define GT_PCXR_DSCP_EN 0x00200000 /* DSCP decoding enable */ /* PCMR: Port Command Register */ #define GT_PCMR_FJ 0x00008000 /* Force Jam / Flow Control */ /* PSR: Port Status Register */ #define GT_PSR_SPEED 0x00000001 /* Speed: 10/100 Mb/s (100=>1)*/ #define GT_PSR_DUPLEX 0x00000002 /* Duplex (1: full) */ #define GT_PSR_FCTL 0x00000004 /* Flow Control Mode */ #define GT_PSR_LINK 0x00000008 /* Link Up/Down */ #define GT_PSR_PAUSE 0x00000010 /* Flow-control disabled state */ #define GT_PSR_TXLOW 0x00000020 /* TX Low priority status */ #define GT_PSR_TXHIGH 0x00000040 /* TX High priority status */ #define GT_PSR_TXINP 0x00000080 /* TX in Progress */ /* ICR: Interrupt Cause Register */ #define GT_ICR_RXBUF 0x00000001 /* RX Buffer returned to host */ #define GT_ICR_TXBUFH 0x00000004 /* TX Buffer High */ #define GT_ICR_TXBUFL 0x00000008 /* TX Buffer Low */ #define GT_ICR_TXENDH 0x00000040 /* TX End High */ #define GT_ICR_TXENDL 0x00000080 /* TX End Low */ #define GT_ICR_RXERR 0x00000100 /* RX Error */ #define GT_ICR_TXERRH 0x00000400 /* TX Error High */ #define GT_ICR_TXERRL 0x00000800 /* TX Error Low */ #define GT_ICR_RXOVR 0x00001000 /* RX Overrun */ #define GT_ICR_TXUDR 0x00002000 /* TX Underrun */ #define GT_ICR_RXBUFQ0 0x00010000 /* RX Buffer in Prio Queue 0 */ #define GT_ICR_RXBUFQ1 0x00020000 /* RX Buffer in Prio Queue 1 */ #define GT_ICR_RXBUFQ2 0x00040000 /* RX Buffer in Prio Queue 2 */ #define GT_ICR_RXBUFQ3 0x00080000 /* RX Buffer in Prio Queue 3 */ #define GT_ICR_RXERRQ0 0x00010000 /* RX Error in Prio Queue 0 */ #define GT_ICR_RXERRQ1 0x00020000 /* RX Error in Prio Queue 1 */ #define GT_ICR_RXERRQ2 0x00040000 /* RX Error in Prio Queue 2 */ #define GT_ICR_RXERRQ3 0x00080000 /* RX Error in Prio Queue 3 */ #define GT_ICR_MII_STC 0x10000000 /* MII PHY Status Change */ #define GT_ICR_SMI_DONE 0x20000000 /* SMI Command Done */ #define GT_ICR_INT_SUM 0x80000000 /* Ethernet Interrupt Summary */ #define GT_ICR_MASK 0x7FFFFFFF /* Ethernet hash entry */ #define GT_HTE_VALID 0x00000001 /* Valid entry */ #define GT_HTE_SKIP 0x00000002 /* Skip entry in a chain */ #define GT_HTE_RD 0x00000004 /* 0: Discard, 1: Receive */ #define GT_HTE_ADDR_MASK 0x7fffffffffff8ULL #define GT_HTE_HOPNUM 12 /* Hash Table Hop Number */ enum { GT_HTLOOKUP_MISS, GT_HTLOOKUP_MATCH, GT_HTLOOKUP_HOP_EXCEEDED, }; /* TX Descriptor */ #define GT_TXDESC_OWN 0x80000000 /* Ownership */ #define GT_TXDESC_AM 0x40000000 /* Auto-mode */ #define GT_TXDESC_EI 0x00800000 /* Enable Interrupt */ #define GT_TXDESC_GC 0x00400000 /* Generate CRC */ #define GT_TXDESC_P 0x00040000 /* Padding */ #define GT_TXDESC_F 0x00020000 /* First buffer of packet */ #define GT_TXDESC_L 0x00010000 /* Last buffer of packet */ #define GT_TXDESC_ES 0x00008000 /* Error Summary */ #define GT_TXDESC_RC 0x00003c00 /* Retransmit Count */ #define GT_TXDESC_COL 0x00000200 /* Collision */ #define GT_TXDESC_RL 0x00000100 /* Retransmit Limit Error */ #define GT_TXDESC_UR 0x00000040 /* Underrun Error */ #define GT_TXDESC_LC 0x00000020 /* Late Collision Error */ #define GT_TXDESC_BC_MASK 0xFFFF0000 /* Number of bytes to transmit */ #define GT_TXDESC_BC_SHIFT 16 /* RX Descriptor */ #define GT_RXDESC_OWN 0x80000000 /* Ownership */ #define GT_RXDESC_AM 0x40000000 /* Auto-mode */ #define GT_RXDESC_EI 0x00800000 /* Enable Interrupt */ #define GT_RXDESC_F 0x00020000 /* First buffer of packet */ #define GT_RXDESC_L 0x00010000 /* Last buffer of packet */ #define GT_RXDESC_ES 0x00008000 /* Error Summary */ #define GT_RXDESC_IGMP 0x00004000 /* IGMP packet detected */ #define GT_RXDESC_HE 0x00002000 /* Hash Table Expired */ #define GT_RXDESC_M 0x00001000 /* Dst MAC Miss in Hash Table */ #define GT_RXDESC_FT 0x00000800 /* Frame Type (802.3/Ethernet) */ #define GT_RXDESC_SF 0x00000100 /* Short Frame Error */ #define GT_RXDESC_MFL 0x00000080 /* Maximum Frame Length Error */ #define GT_RXDESC_OR 0x00000040 /* Overrun Error */ #define GT_RXDESC_COL 0x00000010 /* Collision */ #define GT_RXDESC_CE 0x00000001 /* CRC Error */ #define GT_RXDESC_BC_MASK 0x0000FFFF /* Byte count */ #define GT_RXDESC_BS_MASK 0xFFFF0000 /* Buffer size */ #define GT_RXDESC_BS_SHIFT 16 /* Galileo Ethernet port */ struct eth_port { netio_desc_t *nio; /* First and Current RX descriptors (4 queues) */ m_uint32_t rx_start[4],rx_current[4]; /* Current TX descriptors (2 queues) */ m_uint32_t tx_current[2]; /* Port registers */ m_uint32_t pcr,pcxr,pcmr,psr; /* SDMA registers */ m_uint32_t sdcr,sdcmr; /* Interrupt registers */ m_uint32_t icr,imr; /* Hash Table pointer */ m_uint32_t ht_addr; /* Ethernet MIB counters */ m_uint32_t rx_bytes,tx_bytes,rx_frames,tx_frames; }; /* ======================================================================== */ /* Galileo GT64xxx/GT96xxx system controller */ struct gt_data { char *name; vm_obj_t vm_obj; struct vdevice dev; struct pci_device *pci_dev; vm_instance_t *vm; pthread_mutex_t lock; struct pci_bus *bus[2]; struct dma_channel dma[GT_DMA_CHANNELS]; /* Interrupts (common) */ m_uint32_t int_cause_reg; m_uint32_t int_high_cause_reg; m_uint32_t int_mask_reg; /* Interrupts (GT96100) */ m_uint32_t int0_main_mask_reg,int0_high_mask_reg; m_uint32_t int1_main_mask_reg,int1_high_mask_reg; m_uint32_t ser_cause_reg; m_uint32_t serint0_mask_reg,serint1_mask_reg; u_int int0_irq,int1_irq,serint0_irq,serint1_irq; /* SDMA - Serial DMA (GT96100) */ m_uint32_t sgcr; m_uint32_t sdma_cause_reg,sdma_mask_reg; struct sdma_channel sdma[GT_SDMA_GROUPS][GT_SDMA_CHANNELS]; /* MPSC - MultiProtocol Serial Controller (GT96100) */ struct mpsc_channel mpsc[GT_MPSC_CHANNELS]; /* Ethernet ports (GT96100) */ u_int eth_irq; ptask_id_t eth_tx_tid; struct eth_port eth_ports[GT_ETH_PORTS]; m_uint32_t smi_reg; m_uint16_t mii_regs[32][32]; /* IRQ status update */ void (*gt_update_irq_status)(struct gt_data *gt_data); }; #define GT_LOCK(d) pthread_mutex_lock(&(d)->lock) #define GT_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) /* Log a GT message */ #define GT_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* Update the interrupt status */ static void gt64k_update_irq_status(struct gt_data *gt_data) { if (gt_data->pci_dev) { if (gt_data->int_cause_reg & gt_data->int_mask_reg) pci_dev_trigger_irq(gt_data->vm,gt_data->pci_dev); else pci_dev_clear_irq(gt_data->vm,gt_data->pci_dev); } } /* Fetch a DMA record (chained mode) */ static void gt_dma_fetch_rec(vm_instance_t *vm,struct dma_channel *channel) { m_uint32_t ptr; #if DEBUG_DMA vm_log(vm,"GT_DMA","fetching record at address 0x%x\n",channel->nrptr); #endif /* fetch the record from RAM */ ptr = channel->nrptr; channel->byte_count = swap32(physmem_copy_u32_from_vm(vm,ptr)); channel->src_addr = swap32(physmem_copy_u32_from_vm(vm,ptr+0x04)); channel->dst_addr = swap32(physmem_copy_u32_from_vm(vm,ptr+0x08)); channel->nrptr = swap32(physmem_copy_u32_from_vm(vm,ptr+0x0c)); /* clear the "fetch next record bit" */ channel->ctrl &= ~GT_DMA_FETCH_NEXT; } /* Handle control register of a DMA channel */ static void gt_dma_handle_ctrl(struct gt_data *gt_data,int chan_id) { struct dma_channel *channel = >_data->dma[chan_id]; vm_instance_t *vm = gt_data->vm; int done; if (channel->ctrl & GT_DMA_FETCH_NEXT) { if (channel->nrptr == 0) { vm_log(vm,"GT_DMA","trying to load a NULL DMA record...\n"); return; } gt_dma_fetch_rec(vm,channel); } if (channel->ctrl & GT_DMA_CHAN_ENABLE) { do { done = TRUE; #if DEBUG_DMA vm_log(vm,"GT_DMA", "starting transfer from 0x%x to 0x%x (size=%u bytes)\n", channel->src_addr,channel->dst_addr, channel->byte_count & 0xFFFF); #endif physmem_dma_transfer(vm,channel->src_addr,channel->dst_addr, channel->byte_count & 0xFFFF); /* chained mode */ if (!(channel->ctrl & GT_DMA_CHAIN_MODE)) { if (channel->nrptr) { gt_dma_fetch_rec(vm,channel); done = FALSE; } } }while(!done); #if DEBUG_DMA vm_log(vm,"GT_DMA","finished transfer.\n"); #endif /* Trigger DMA interrupt */ gt_data->int_cause_reg |= 1 << (4 + chan_id); gt_data->gt_update_irq_status(gt_data); } } #define DMA_REG(ch,reg_name) \ if (op_type == MTS_WRITE) \ gt_data->dma[ch].reg_name = *data; \ else \ *data = gt_data->dma[ch].reg_name; /* Handle a DMA channel */ static int gt_dma_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct gt_data *gt_data = dev->priv_data; switch(offset) { /* DMA Source Address */ case 0x810: DMA_REG(0,src_addr); return(1); case 0x814: DMA_REG(1,src_addr); return(1); case 0x818: DMA_REG(2,src_addr); return(1); case 0x81c: DMA_REG(3,src_addr); return(1); /* DMA Destination Address */ case 0x820: DMA_REG(0,dst_addr); return(1); case 0x824: DMA_REG(1,dst_addr); return(1); case 0x828: DMA_REG(2,dst_addr); return(1); case 0x82c: DMA_REG(3,dst_addr); return(1); /* DMA Next Record Pointer */ case 0x830: gt_data->dma[0].cdptr = *data; DMA_REG(0,nrptr); return(1); case 0x834: gt_data->dma[1].cdptr = *data; DMA_REG(1,nrptr); return(1); case 0x838: gt_data->dma[2].cdptr = *data; DMA_REG(2,nrptr); return(1); case 0x83c: gt_data->dma[3].cdptr = *data; DMA_REG(3,nrptr); return(1); /* DMA Channel Control */ case 0x840: DMA_REG(0,ctrl); if (op_type == MTS_WRITE) gt_dma_handle_ctrl(gt_data,0); return(1); case 0x844: DMA_REG(1,ctrl); if (op_type == MTS_WRITE) gt_dma_handle_ctrl(gt_data,1); return(1); case 0x848: DMA_REG(2,ctrl); if (op_type == MTS_WRITE) gt_dma_handle_ctrl(gt_data,2); return(1); case 0x84c: DMA_REG(3,ctrl); if (op_type == MTS_WRITE) gt_dma_handle_ctrl(gt_data,3); return(1); } return(0); } /* * dev_gt64010_access() */ void *dev_gt64010_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct gt_data *gt_data = dev->priv_data; if (op_type == MTS_READ) { *data = 0; } else { *data = swap32(*data); } if (gt_dma_access(cpu,dev,offset,op_size,op_type,data) != 0) goto done; switch(offset) { /* ===== DRAM Settings (completely faked, 128 Mb) ===== */ case 0x008: /* ras10_low */ if (op_type == MTS_READ) *data = 0x000; break; case 0x010: /* ras10_high */ if (op_type == MTS_READ) *data = 0x7F; break; case 0x018: /* ras32_low */ if (op_type == MTS_READ) *data = 0x080; break; case 0x020: /* ras32_high */ if (op_type == MTS_READ) *data = 0x7F; break; case 0x400: /* ras0_low */ if (op_type == MTS_READ) *data = 0x00; break; case 0x404: /* ras0_high */ if (op_type == MTS_READ) *data = 0xFF; break; case 0x408: /* ras1_low */ if (op_type == MTS_READ) *data = 0x7F; break; case 0x40c: /* ras1_high */ if (op_type == MTS_READ) *data = 0x00; break; case 0x410: /* ras2_low */ if (op_type == MTS_READ) *data = 0x00; break; case 0x414: /* ras2_high */ if (op_type == MTS_READ) *data = 0xFF; break; case 0x418: /* ras3_low */ if (op_type == MTS_READ) *data = 0x7F; break; case 0x41c: /* ras3_high */ if (op_type == MTS_READ) *data = 0x00; break; case 0xc08: /* pci0_cs10 */ if (op_type == MTS_READ) *data = 0xFFF; break; case 0xc0c: /* pci0_cs32 */ if (op_type == MTS_READ) *data = 0xFFF; break; case 0xc00: /* pci_cmd */ if (op_type == MTS_READ) *data = 0x00008001; break; /* ===== Interrupt Cause Register ===== */ case 0xc18: if (op_type == MTS_READ) { *data = gt_data->int_cause_reg; } else { gt_data->int_cause_reg &= *data; gt64k_update_irq_status(gt_data); } break; /* ===== Interrupt Mask Register ===== */ case 0xc1c: if (op_type == MTS_READ) *data = gt_data->int_mask_reg; else { gt_data->int_mask_reg = *data; gt64k_update_irq_status(gt_data); } break; /* ===== PCI Configuration ===== */ case PCI_BUS_ADDR: /* pci configuration address (0xcf8) */ pci_dev_addr_handler(cpu,gt_data->bus[0],op_type,FALSE,data); break; case PCI_BUS_DATA: /* pci data address (0xcfc) */ pci_dev_data_handler(cpu,gt_data->bus[0],op_type,FALSE,data); break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"GT64010","read from unknown addr 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"GT64010","write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx\n",offset,*data,cpu_get_pc(cpu)); } #endif } done: if (op_type == MTS_READ) *data = swap32(*data); return NULL; } /* * dev_gt64120_access() */ void *dev_gt64120_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct gt_data *gt_data = dev->priv_data; if (op_type == MTS_READ) { *data = 0; } else { *data = swap32(*data); } if (gt_dma_access(cpu,dev,offset,op_size,op_type,data) != 0) goto done; switch(offset) { case 0x008: /* ras10_low */ if (op_type == MTS_READ) *data = 0x000; break; case 0x010: /* ras10_high */ if (op_type == MTS_READ) *data = 0x7F; break; case 0x018: /* ras32_low */ if (op_type == MTS_READ) *data = 0x100; break; case 0x020: /* ras32_high */ if (op_type == MTS_READ) *data = 0x7F; break; case 0x400: /* ras0_low */ if (op_type == MTS_READ) *data = 0x00; break; case 0x404: /* ras0_high */ if (op_type == MTS_READ) *data = 0xFF; break; case 0x408: /* ras1_low */ if (op_type == MTS_READ) *data = 0x7F; break; case 0x40c: /* ras1_high */ if (op_type == MTS_READ) *data = 0x00; break; case 0x410: /* ras2_low */ if (op_type == MTS_READ) *data = 0x00; break; case 0x414: /* ras2_high */ if (op_type == MTS_READ) *data = 0xFF; break; case 0x418: /* ras3_low */ if (op_type == MTS_READ) *data = 0x7F; break; case 0x41c: /* ras3_high */ if (op_type == MTS_READ) *data = 0x00; break; case 0xc08: /* pci0_cs10 */ if (op_type == MTS_READ) *data = 0xFFF; break; case 0xc0c: /* pci0_cs32 */ if (op_type == MTS_READ) *data = 0xFFF; break; case 0xc00: /* pci_cmd */ if (op_type == MTS_READ) *data = 0x00008001; break; /* ===== Interrupt Cause Register ===== */ case 0xc18: if (op_type == MTS_READ) *data = gt_data->int_cause_reg; else { gt_data->int_cause_reg &= *data; gt64k_update_irq_status(gt_data); } break; /* ===== Interrupt Mask Register ===== */ case 0xc1c: if (op_type == MTS_READ) { *data = gt_data->int_mask_reg; } else { gt_data->int_mask_reg = *data; gt64k_update_irq_status(gt_data); } break; /* ===== PCI Bus 1 ===== */ case 0xcf0: pci_dev_addr_handler(cpu,gt_data->bus[1],op_type,FALSE,data); break; case 0xcf4: pci_dev_data_handler(cpu,gt_data->bus[1],op_type,FALSE,data); break; /* ===== PCI Bus 0 ===== */ case PCI_BUS_ADDR: /* pci configuration address (0xcf8) */ pci_dev_addr_handler(cpu,gt_data->bus[0],op_type,FALSE,data); break; case PCI_BUS_DATA: /* pci data address (0xcfc) */ pci_dev_data_handler(cpu,gt_data->bus[0],op_type,FALSE,data); break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"GT64120","read from unknown addr 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"GT64120","write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx\n",offset,*data,cpu_get_pc(cpu)); } #endif } done: if (op_type == MTS_READ) *data = swap32(*data); return NULL; } /* ======================================================================== */ /* GT96k Interrupts */ /* ======================================================================== */ static void gt96k_update_irq_status(struct gt_data *d) { /* Interrupt0* active ? */ if ((d->int_cause_reg & d->int0_main_mask_reg) || (d->int_high_cause_reg & d->int0_high_mask_reg)) { d->int_cause_reg |= 1 << 30; vm_set_irq(d->vm,d->int0_irq); } else { d->int_cause_reg &= ~(1 << 30); vm_clear_irq(d->vm,d->int0_irq); } /* Interrupt1* active ? */ if ((d->int_cause_reg & d->int1_main_mask_reg) || (d->int_high_cause_reg & d->int1_high_mask_reg)) { d->int_cause_reg |= 1 << 31; vm_set_irq(d->vm,d->int1_irq); } else { d->int_cause_reg &= ~(1 << 31); vm_clear_irq(d->vm,d->int1_irq); } /* SerInt0* active ? */ if (d->ser_cause_reg & d->serint0_mask_reg) { vm_set_irq(d->vm,d->serint0_irq); } else { vm_clear_irq(d->vm,d->serint0_irq); } /* SerInt1* active ? */ if (d->ser_cause_reg & d->serint1_mask_reg) { vm_set_irq(d->vm,d->serint1_irq); } else { vm_clear_irq(d->vm,d->serint1_irq); } } /* ======================================================================== */ /* SDMA (Serial DMA) */ /* ======================================================================== */ /* Update SDMA interrupt status */ static void gt_sdma_update_int_status(struct gt_data *d) { /* Update general SDMA status */ if (d->sdma_cause_reg & d->sdma_mask_reg) { d->ser_cause_reg |= GT_SCR_SDMA_SUM; d->int_high_cause_reg |= GT_IHCR_SDMA_SUM; } else { d->ser_cause_reg &= ~GT_SCR_SDMA_SUM; d->int_high_cause_reg &= ~GT_IHCR_SDMA_SUM; } gt96k_update_irq_status(d); } /* Update SDMA interrupt status for the specified channel */ static void gt_sdma_update_channel_int_status(struct gt_data *d,u_int chan_id) { m_uint32_t ch_st; /* Get the status of the specified SDMA channel */ ch_st = d->sdma_cause_reg & (0x0000000F << (chan_id << 2)); if (ch_st) d->ser_cause_reg |= GT_SCR_SDMA0_SUM << (chan_id << 1); else d->ser_cause_reg &= ~(GT_SCR_SDMA0_SUM << (chan_id << 1)); gt_sdma_update_int_status(d); } /* Set SDMA cause register for a channel */ static inline void gt_sdma_set_cause(struct gt_data *d,u_int chan_id, u_int value) { d->sdma_cause_reg |= value << (chan_id << 2); } /* Read a SDMA descriptor from memory */ static void gt_sdma_desc_read(struct gt_data *d,m_uint32_t addr, struct sdma_desc *desc) { physmem_copy_from_vm(d->vm,desc,addr,sizeof(struct sdma_desc)); /* byte-swapping */ desc->buf_size = vmtoh32(desc->buf_size); desc->cmd_stat = vmtoh32(desc->cmd_stat); desc->next_ptr = vmtoh32(desc->next_ptr); desc->buf_ptr = vmtoh32(desc->buf_ptr); } /* Write a SDMA descriptor to memory */ static void gt_sdma_desc_write(struct gt_data *d,m_uint32_t addr, struct sdma_desc *desc) { struct sdma_desc tmp; /* byte-swapping */ tmp.cmd_stat = vmtoh32(desc->cmd_stat); tmp.buf_size = vmtoh32(desc->buf_size); tmp.next_ptr = vmtoh32(desc->next_ptr); tmp.buf_ptr = vmtoh32(desc->buf_ptr); physmem_copy_to_vm(d->vm,&tmp,addr,sizeof(struct sdma_desc)); } /* Send contents of a SDMA buffer */ static void gt_sdma_send_buffer(struct gt_data *d,u_int chan_id, u_char *buffer,m_uint32_t len) { struct mpsc_channel *channel; u_int mode; channel = &d->mpsc[chan_id]; mode = channel->mmcrl & GT_MMCRL_MODE_MASK; switch(mode) { case GT_MPSC_MODE_HDLC: if (channel->nio != NULL) netio_send(channel->nio,buffer,len); break; case GT_MPSC_MODE_UART: if (channel->vtty != NULL) vtty_put_buffer(channel->vtty,(char *)buffer,len); break; } } /* Start TX DMA process */ static int gt_sdma_tx_start(struct gt_data *d,struct sdma_channel *chan) { u_char pkt[GT_MAX_PKT_SIZE],*pkt_ptr; struct sdma_desc txd0,ctxd,*ptxd; m_uint32_t tx_start,tx_current; m_uint32_t len,tot_len; int abort = FALSE; tx_start = tx_current = chan->sctdp; if (!tx_start) return(FALSE); ptxd = &txd0; gt_sdma_desc_read(d,tx_start,ptxd); /* If we don't own the first descriptor, we cannot transmit */ if (!(txd0.cmd_stat & GT_TXDESC_OWN)) return(FALSE); /* Empty packet for now */ pkt_ptr = pkt; tot_len = 0; for(;;) { /* Copy packet data to the buffer */ len = (ptxd->buf_size & GT_TXDESC_BC_MASK) >> GT_TXDESC_BC_SHIFT; physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->buf_ptr,len); pkt_ptr += len; tot_len += len; /* Clear the OWN bit if this is not the first descriptor */ if (!(ptxd->cmd_stat & GT_TXDESC_F)) { ptxd->cmd_stat &= ~GT_TXDESC_OWN; physmem_copy_u32_to_vm(d->vm,tx_current+GT_SDMA_CMD_OFFSET,ptxd->cmd_stat); } tx_current = ptxd->next_ptr; /* Last descriptor or no more desc available ? */ if (ptxd->cmd_stat & GT_TXDESC_L) break; if (!tx_current) { abort = TRUE; break; } /* Fetch the next descriptor */ gt_sdma_desc_read(d,tx_current,&ctxd); ptxd = &ctxd; } if ((tot_len != 0) && !abort) { #if DEBUG_SDMA GT_LOG(d,"SDMA%u: sending packet of %u bytes\n",tot_len); mem_dump(d->vm->log_fd,pkt,tot_len); #endif /* send it on wire */ gt_sdma_send_buffer(d,chan->id,pkt,tot_len); /* Signal that a TX buffer has been transmitted */ gt_sdma_set_cause(d,chan->id,GT_SDMA_CAUSE_TXBUF0); } /* Clear the OWN flag of the first descriptor */ txd0.cmd_stat &= ~GT_TXDESC_OWN; physmem_copy_u32_to_vm(d->vm,tx_start+GT_SDMA_CMD_OFFSET,txd0.cmd_stat); chan->sctdp = tx_current; if (abort || !tx_current) { gt_sdma_set_cause(d,chan->id,GT_SDMA_CAUSE_TXEND0); chan->sdcm &= ~GT_SDCMR_TXD; } /* Update interrupt status */ gt_sdma_update_channel_int_status(d,chan->id); return(TRUE); } /* Put a packet in buffer of a descriptor */ static void gt_sdma_rxdesc_put_pkt(struct gt_data *d,struct sdma_desc *rxd, u_char **pkt,ssize_t *pkt_len) { ssize_t len,cp_len; len = (rxd->buf_size & GT_RXDESC_BS_MASK) >> GT_RXDESC_BS_SHIFT; /* compute the data length to copy */ cp_len = m_min(len,*pkt_len); /* copy packet data to the VM physical RAM */ physmem_copy_to_vm(d->vm,*pkt,rxd->buf_ptr,cp_len); /* set the byte count in descriptor */ rxd->buf_size |= cp_len; *pkt += cp_len; *pkt_len -= cp_len; } /* Put a packet into SDMA buffers */ static int gt_sdma_handle_rxqueue(struct gt_data *d, struct sdma_channel *channel, u_char *pkt,ssize_t pkt_len) { m_uint32_t rx_start,rx_current; struct sdma_desc rxd0,rxdn,*rxdc; ssize_t tot_len = pkt_len; u_char *pkt_ptr = pkt; int i; /* Truncate the packet if it is too big */ pkt_len = m_min(pkt_len,GT_MAX_PKT_SIZE); /* Copy the first RX descriptor */ if (!(rx_start = rx_current = channel->scrdp)) goto dma_error; /* Load the first RX descriptor */ gt_sdma_desc_read(d,rx_start,&rxd0); #if DEBUG_SDMA GT_LOG(d,"SDMA channel %u: reading desc at 0x%8.8x " "[buf_size=0x%8.8x,cmd_stat=0x%8.8x," "next_ptr=0x%8.8x,buf_ptr=0x%8.8x]\n", channel->id,rx_start,rxd0.buf_size,rxd0.cmd_stat, rxd0.next_ptr,rxd0.buf_ptr); #endif for(i=0,rxdc=&rxd0;tot_len>0;i++) { /* We must own the descriptor */ if (!(rxdc->cmd_stat & GT_RXDESC_OWN)) goto dma_error; /* Put data into the descriptor buffer */ gt_sdma_rxdesc_put_pkt(d,rxdc,&pkt_ptr,&tot_len); /* Clear the OWN bit */ rxdc->cmd_stat &= ~GT_RXDESC_OWN; /* We have finished if the complete packet has been stored */ if (tot_len == 0) { rxdc->cmd_stat |= GT_RXDESC_L; rxdc->buf_size += 2; /* Add 2 bytes for CRC */ } /* Update the descriptor in host memory (but not the 1st) */ if (i != 0) gt_sdma_desc_write(d,rx_current,rxdc); /* Get address of the next descriptor */ rx_current = rxdc->next_ptr; if (tot_len == 0) break; if (!rx_current) goto dma_error; /* Read the next descriptor from VM physical RAM */ gt_sdma_desc_read(d,rx_current,&rxdn); rxdc = &rxdn; } /* Update the RX pointers */ channel->scrdp = rx_current; /* Update the first RX descriptor */ rxd0.cmd_stat |= GT_RXDESC_F; gt_sdma_desc_write(d,rx_start,&rxd0); /* Indicate that we have a frame ready */ gt_sdma_set_cause(d,channel->id,GT_SDMA_CAUSE_RXBUF0); gt_sdma_update_channel_int_status(d,channel->id); return(TRUE); dma_error: gt_sdma_set_cause(d,channel->id,GT_SDMA_CAUSE_RXERR0); gt_sdma_update_channel_int_status(d,channel->id); return(FALSE); } /* Handle RX packet for a SDMA channel */ static int gt_sdma_handle_rx_pkt(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, struct gt_data *d,void *arg) { struct sdma_channel *channel; u_int chan_id = (u_int)(u_long)arg; u_int group_id; GT_LOCK(d); /* Find the SDMA group associated to the MPSC channel for receiving */ group_id = (d->sgcr >> chan_id) & 0x01; channel = &d->sdma[group_id][chan_id]; gt_sdma_handle_rxqueue(d,channel,pkt,pkt_len); GT_UNLOCK(d); return(TRUE); } /* Handle a SDMA channel */ static int gt_sdma_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct gt_data *gt_data = dev->priv_data; struct sdma_channel *channel; u_int group,chan_id,reg; if ((offset & 0x000F00) != 0x000900) return(FALSE); /* Decode group, channel and register */ group = (offset >> 20) & 0x0F; chan_id = (offset >> 16) & 0x0F; reg = offset & 0xFFFF; if ((group >= GT_SDMA_GROUPS) || (chan_id >= GT_SDMA_CHANNELS)) { cpu_log(cpu,"GT96100","invalid SDMA register 0x%8.8x\n",offset); return(TRUE); } channel = >_data->sdma[group][chan_id]; #if 0 printf("SDMA: access to reg 0x%6.6x (group=%u, channel=%u)\n", offset, group, chan_id); #endif switch(reg) { /* Configuration Register */ case GT_SDMA_SDC: break; /* Command Register */ case GT_SDMA_SDCM: if (op_type == MTS_WRITE) { channel->sdcm = *data; if (channel->sdcm & GT_SDCMR_TXD) { #if DEBUG_SDMA cpu_log(cpu,"GT96100-SDMA","starting TX transfer (%u/%u)\n", group,chan_id); #endif while(gt_sdma_tx_start(gt_data,channel)) ; } } else { *data = 0xFF; //0xFFFFFFFF; } break; /* Current RX descriptor */ case GT_SDMA_SCRDP: if (op_type == MTS_READ) *data = channel->scrdp; else channel->scrdp = *data; break; /* Current TX desc. pointer */ case GT_SDMA_SCTDP: if (op_type == MTS_READ) *data = channel->sctdp; else channel->sctdp = *data; break; /* First TX desc. pointer */ case GT_SDMA_SFTDP: if (op_type == MTS_READ) *data = channel->sftdp; else channel->sftdp = *data; break; default: /* unknown/unmanaged register */ return(FALSE); } return(TRUE); } /* ======================================================================== */ /* MPSC (MultiProtocol Serial Controller) */ /* ======================================================================== */ /* Handle a MPSC channel */ static int gt_mpsc_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct gt_data *gt_data = dev->priv_data; struct mpsc_channel *channel; u_int chan_id,reg,reg2; if ((offset & 0x000F00) != 0x000A00) return(FALSE); /* Decode channel ID and register */ chan_id = offset >> 15; reg = offset & 0xFFF; if (chan_id >= GT_MPSC_CHANNELS) return(FALSE); channel = >_data->mpsc[chan_id]; switch(reg) { /* Main Config Register Low */ case GT_MPSC_MMCRL: if (op_type == MTS_READ) { *data = channel->mmcrl; } else { #if DEBUG_MPSC GT_LOG(gt_data,"MPSC channel %u set in mode %llu\n", chan_id,*data & 0x07); #endif channel->mmcrl = *data; } break; /* Main Config Register High */ case GT_MPSC_MMCRH: if (op_type == MTS_READ) *data = channel->mmcrh; else channel->mmcrh = *data; break; /* Protocol Config Register */ case GT_MPSC_MPCR: if (op_type == MTS_READ) *data = channel->mpcr; else channel->mpcr = *data; break; /* Channel registers */ case GT_MPSC_CHR1: case GT_MPSC_CHR2: case GT_MPSC_CHR3: case GT_MPSC_CHR4: case GT_MPSC_CHR5: case GT_MPSC_CHR6: case GT_MPSC_CHR7: case GT_MPSC_CHR8: case GT_MPSC_CHR9: //case GT_MPSC_CHR10: reg2 = (reg - GT_MPSC_CHR1) >> 2; if (op_type == MTS_READ) *data = channel->chr[reg2]; else channel->chr[reg2] = *data; break; case GT_MPSC_CHR10: if (op_type == MTS_READ) *data = channel->chr[9] | 0x20; else channel->chr[9] = *data; break; default: /* unknown/unmanaged register */ return(FALSE); } return(TRUE); } /* Set NIO for a MPSC channel */ int dev_gt96100_mpsc_set_nio(struct gt_data *d,u_int chan_id, netio_desc_t *nio) { struct mpsc_channel *channel; if (chan_id >= GT_MPSC_CHANNELS) return(-1); channel = &d->mpsc[chan_id]; if (channel->nio != NULL) return(-1); channel->nio = nio; netio_rxl_add(nio,(netio_rx_handler_t)gt_sdma_handle_rx_pkt, d,(void *)(u_long)chan_id); return(0); } /* Unset NIO for a MPSC channel */ int dev_gt96100_mpsc_unset_nio(struct gt_data *d,u_int chan_id) { struct mpsc_channel *channel; if (chan_id >= GT_MPSC_CHANNELS) return(-1); if (d == NULL) return(0); channel = &d->mpsc[chan_id]; if (channel->nio != NULL) { netio_rxl_remove(channel->nio); channel->nio = NULL; } return(0); } /* Set a VTTY for a MPSC channel */ int dev_gt96100_mpsc_set_vtty(struct gt_data *d,u_int chan_id,vtty_t *vtty) { struct mpsc_channel *channel; if (chan_id >= GT_MPSC_CHANNELS) return(-1); channel = &d->mpsc[chan_id]; if (channel->vtty != NULL) return(-1); channel->vtty = vtty; return(0); } /* Unset a VTTY for a MPSC channel */ int dev_gt96100_mpsc_unset_vtty(struct gt_data *d,u_int chan_id) { struct mpsc_channel *channel; if (chan_id >= GT_MPSC_CHANNELS) return(-1); channel = &d->mpsc[chan_id]; if (channel->vtty != NULL) { channel->vtty = NULL; } return(0); } /* ======================================================================== */ /* Ethernet */ /* ======================================================================== */ /* Trigger/clear Ethernet interrupt if one or both port have pending events */ static void gt_eth_set_int_status(struct gt_data *d) { /* Compute Ether0 summary */ if (d->eth_ports[0].icr & GT_ICR_INT_SUM) { d->ser_cause_reg |= GT_SCR_ETH0_SUM; d->int_high_cause_reg |= GT_IHCR_ETH0_SUM; } else { d->ser_cause_reg &= ~GT_SCR_ETH0_SUM; d->int_high_cause_reg &= ~GT_IHCR_ETH0_SUM; } /* Compute Ether1 summary */ if (d->eth_ports[1].icr & GT_ICR_INT_SUM) { d->ser_cause_reg |= GT_SCR_ETH1_SUM; d->int_high_cause_reg |= GT_IHCR_ETH1_SUM; } else { d->ser_cause_reg &= ~GT_SCR_ETH1_SUM; d->int_high_cause_reg &= ~GT_IHCR_ETH1_SUM; } gt96k_update_irq_status(d); } /* Update the Ethernet port interrupt status */ static void gt_eth_update_int_status(struct gt_data *d,struct eth_port *port) { if (port->icr & port->imr & GT_ICR_MASK) { port->icr |= GT_ICR_INT_SUM; } else { port->icr &= ~GT_ICR_INT_SUM; } gt_eth_set_int_status(d); } /* Read a MII register */ static m_uint32_t gt_mii_read(struct gt_data *d) { m_uint8_t port,reg; m_uint32_t res = 0; port = (d->smi_reg & GT_SMIR_PHYAD_MASK) >> GT_SMIR_PHYAD_SHIFT; reg = (d->smi_reg & GT_SMIR_REGAD_MASK) >> GT_SMIR_REGAD_SHIFT; #if DEBUG_MII GT_LOG(d,"MII: port 0x%4.4x, reg 0x%2.2x: reading.\n",port,reg); #endif if ((port < GT_ETH_PORTS) && (reg < 32)) { res = d->mii_regs[port][reg]; switch(reg) { case 0x00: res &= ~0x8200; /* clear reset bit and autoneg restart */ break; case 0x01: #if 0 if (d->ports[port].nio && bcm5600_mii_port_status(d,port)) d->mii_output = 0x782C; else d->mii_output = 0; #endif res = 0x782c; break; case 0x02: res = 0x40; break; case 0x03: res = 0x61d4; break; case 0x04: res = 0x1E1; break; case 0x05: res = 0x41E1; break; default: res = 0; } } /* Mark the data as ready */ res |= GT_SMIR_RVALID_FLAG; return(res); } /* Write a MII register */ static void gt_mii_write(struct gt_data *d) { m_uint8_t port,reg; m_uint16_t isolation; port = (d->smi_reg & GT_SMIR_PHYAD_MASK) >> GT_SMIR_PHYAD_SHIFT; reg = (d->smi_reg & GT_SMIR_REGAD_MASK) >> GT_SMIR_REGAD_SHIFT; if ((port < GT_ETH_PORTS) && (reg < 32)) { #if DEBUG_MII GT_LOG(d,"MII: port 0x%4.4x, reg 0x%2.2x: writing 0x%4.4x\n", port,reg,d->smi_reg & GT_SMIR_DATA_MASK); #endif /* Check if PHY isolation status is changing */ if (reg == 0) { isolation = (d->smi_reg ^ d->mii_regs[port][reg]) & 0x400; if (isolation) { #if DEBUG_MII GT_LOG(d,"MII: port 0x%4.4x: generating IRQ\n",port); #endif d->eth_ports[port].icr |= GT_ICR_MII_STC; gt_eth_update_int_status(d,&d->eth_ports[port]); } } d->mii_regs[port][reg] = d->smi_reg & GT_SMIR_DATA_MASK; } } /* Handle registers of Ethernet ports */ static int gt_eth_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct gt_data *d = dev->priv_data; struct eth_port *port = NULL; u_int queue; #if DEBUG_ETH const char* const access = (op_type == MTS_READ)? "read": "write"; #endif if ((offset < 0x80000) || (offset >= 0x90000)) return(FALSE); /* Determine the Ethernet port */ if ((offset >= 0x84800) && (offset < 0x88800)) port = &d->eth_ports[0]; else if ((offset >= 0x88800) && (offset < 0x8c800)) port = &d->eth_ports[1]; switch(offset) { /* SMI register */ case 0x80810: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","SMI register\n"); #endif if (op_type == MTS_WRITE) { d->smi_reg = *data; if (!(d->smi_reg & GT_SMIR_OPCODE_READ)) gt_mii_write(d); } else { *data = 0; if (d->smi_reg & GT_SMIR_OPCODE_READ) *data = gt_mii_read(d); } break; /* ICR: Interrupt Cause Register */ case 0x84850: case 0x88850: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","ICR: Interrupt Cause Register [%s]\n",access); #endif if (op_type == MTS_READ) { *data = port->icr; } else { port->icr &= *data; gt_eth_update_int_status(d,port); } break; /* IMR: Interrupt Mask Register */ case 0x84858: case 0x88858: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","IMR: Interrupt Mask Register [%s]\n",access); #endif if (op_type == MTS_READ) { *data = port->imr; } else { port->imr = *data; gt_eth_update_int_status(d,port); } break; /* PCR: Port Configuration Register */ case 0x84800: case 0x88800: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","PCR: Port Configuration Register [%s]\n",access); #endif if (op_type == MTS_READ) *data = port->pcr; else port->pcr = *data; break; /* PCXR: Port Configuration Extend Register */ case 0x84808: case 0x88808: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","PCXR: Port Configuration Extend Register [%s]\n",access); #endif if (op_type == MTS_READ) { *data = port->pcxr; *data |= GT_PCXR_SPEED; } else port->pcxr = *data; break; /* PCMR: Port Command Register */ case 0x84810: case 0x88810: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","PCMR: Port Command Register [%s]\n",access); #endif if (op_type == MTS_READ) *data = port->pcmr; else port->pcmr = *data; break; /* Port Status Register */ case 0x84818: case 0x88818: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","Port Status Register [%s]\n",access); #endif if (op_type == MTS_READ) *data = 0x0F; break; /* First RX descriptor */ case 0x84880: case 0x88880: case 0x84884: case 0x88884: case 0x84888: case 0x88888: case 0x8488C: case 0x8888C: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","First RX descriptor [%s]\n",access); #endif queue = (offset >> 2) & 0x03; if (op_type == MTS_READ) *data = port->rx_start[queue]; else port->rx_start[queue] = *data; break; /* Current RX descriptor */ case 0x848A0: case 0x888A0: case 0x848A4: case 0x888A4: case 0x848A8: case 0x888A8: case 0x848AC: case 0x888AC: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","Current RX descriptor [%s]\n",access); #endif queue = (offset >> 2) & 0x03; if (op_type == MTS_READ) *data = port->rx_current[queue]; else port->rx_current[queue] = *data; break; /* Current TX descriptor */ case 0x848E0: case 0x888E0: case 0x848E4: case 0x888E4: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","Current RX descriptor [%s]\n",access); #endif queue = (offset >> 2) & 0x01; if (op_type == MTS_READ) *data = port->tx_current[queue]; else port->tx_current[queue] = *data; break; /* Hash Table Pointer */ case 0x84828: case 0x88828: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","Hash Table Pointer [%s]\n",access); #endif if (op_type == MTS_READ) *data = port->ht_addr; else port->ht_addr = *data; break; /* SDCR: SDMA Configuration Register */ case 0x84840: case 0x88840: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","SDCR: SDMA Configuration Register [%s]\n",access); #endif if (op_type == MTS_READ) *data = port->sdcr; else port->sdcr = *data; break; /* SDCMR: SDMA Command Register */ case 0x84848: case 0x88848: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","SDCMR: SDMA Command Register [%s]\n",access); #endif if (op_type == MTS_WRITE) { /* Start RX DMA */ if (*data & GT_SDCMR_ERD) { port->sdcmr |= GT_SDCMR_ERD; port->sdcmr &= ~GT_SDCMR_AR; } /* Abort RX DMA */ if (*data & GT_SDCMR_AR) port->sdcmr &= ~GT_SDCMR_ERD; /* Start TX High */ if (*data & GT_SDCMR_TXDH) { port->sdcmr |= GT_SDCMR_TXDH; port->sdcmr &= ~GT_SDCMR_STDH; } /* Start TX Low */ if (*data & GT_SDCMR_TXDL) { port->sdcmr |= GT_SDCMR_TXDL; port->sdcmr &= ~GT_SDCMR_STDL; } /* Stop TX High */ if (*data & GT_SDCMR_STDH) { port->sdcmr &= ~GT_SDCMR_TXDH; port->sdcmr |= GT_SDCMR_STDH; } /* Stop TX Low */ if (*data & GT_SDCMR_STDL) { port->sdcmr &= ~GT_SDCMR_TXDL; port->sdcmr |= GT_SDCMR_STDL; } } else { *data = port->sdcmr; } break; /* Ethernet MIB Counters */ case 0x85800: case 0x89800: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","Ethernet MIB Counters - Bytes Received [%s]\n",access); #endif if (op_type == MTS_READ) { *data = port->rx_bytes; port->rx_bytes = 0; } break; case 0x85804: case 0x89804: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","Ethernet MIB Counters - Bytes Sent [%s]\n",access); #endif if (op_type == MTS_READ) { *data = port->tx_bytes; port->tx_bytes = 0; } break; case 0x85808: case 0x89808: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","Ethernet MIB Counters - Frames Received [%s]\n",access); #endif if (op_type == MTS_READ) { *data = port->rx_frames; port->rx_frames = 0; } break; case 0x8580C: case 0x8980C: #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH","Ethernet MIB Counters - Frames Sent [%s]\n",access); #endif if (op_type == MTS_READ) { *data = port->tx_frames; port->tx_frames = 0; } break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"GT96100/ETH", "read access to unknown register 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"GT96100/ETH", "write access to unknown register 0x%x, value=0x%llx, " "pc=0x%llx\n",offset,*data,cpu_get_pc(cpu)); } #endif } #if DEBUG_ETH cpu_log(cpu,"GT96100/ETH", "DONE register 0x%x, value=0x%llx, pc=0x%llx [%s]\n", offset,*data,cpu_get_pc(cpu),access); #endif return(TRUE); } /* * dev_gt96100_access() */ void *dev_gt96100_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct gt_data *gt_data = dev->priv_data; GT_LOCK(gt_data); if (op_type == MTS_READ) { *data = 0; } else { if (op_size == 4) *data = swap32(*data); } #if 0 /* DEBUG */ if (offset != 0x101a80) { if (op_type == MTS_READ) { cpu_log(cpu,"GT96100","READ OFFSET 0x%6.6x\n",offset); } else { cpu_log(cpu,"GT96100","WRITE OFFSET 0x%6.6x, DATA=0x%8.8llx\n", offset,*data); } } #endif /* DMA registers */ if (gt_dma_access(cpu,dev,offset,op_size,op_type,data) != 0) goto done; /* Serial DMA channel registers */ if (gt_sdma_access(cpu,dev,offset,op_size,op_type,data) != 0) goto done; /* MPSC registers */ if (gt_mpsc_access(cpu,dev,offset,op_size,op_type,data) != 0) goto done; /* Ethernet registers */ if (gt_eth_access(cpu,dev,offset,op_size,op_type,data) != 0) goto done; switch(offset) { /* Watchdog configuration register */ case 0x101a80: break; /* Watchdog value register */ case 0x101a84: break; case 0x008: /* ras10_low */ if (op_type == MTS_READ) *data = 0x000; break; case 0x010: /* ras10_high */ if (op_type == MTS_READ) *data = 0x7F; break; case 0x018: /* ras32_low */ if (op_type == MTS_READ) *data = 0x100; break; case 0x020: /* ras32_high */ if (op_type == MTS_READ) *data = 0x7F; break; case 0x400: /* ras0_low */ if (op_type == MTS_READ) *data = 0x00; break; case 0x404: /* ras0_high */ if (op_type == MTS_READ) *data = 0xFF; break; case 0x408: /* ras1_low */ if (op_type == MTS_READ) *data = 0x7F; break; case 0x40c: /* ras1_high */ if (op_type == MTS_READ) *data = 0x00; break; case 0x410: /* ras2_low */ if (op_type == MTS_READ) *data = 0x00; break; case 0x414: /* ras2_high */ if (op_type == MTS_READ) *data = 0xFF; break; case 0x418: /* ras3_low */ if (op_type == MTS_READ) *data = 0x7F; break; case 0x41c: /* ras3_high */ if (op_type == MTS_READ) *data = 0x00; break; case 0xc08: /* pci0_cs10 */ if (op_type == MTS_READ) *data = 0xFFF; break; case 0xc0c: /* pci0_cs32 */ if (op_type == MTS_READ) *data = 0xFFF; break; case 0xc00: /* pci_cmd */ if (op_type == MTS_READ) *data = 0x00008001; break; /* ===== Interrupt Main Cause Register ===== */ case 0xc18: if (op_type == MTS_READ) { *data = gt_data->int_cause_reg; } else { /* Don't touch bit 0, 30 and 31 which are read-only */ gt_data->int_cause_reg &= (*data | 0xC0000001); gt96k_update_irq_status(gt_data); } break; /* ===== Interrupt High Cause Register ===== */ case 0xc98: if (op_type == MTS_READ) *data = gt_data->int_high_cause_reg; break; /* ===== Interrupt0 Main Mask Register ===== */ case 0xc1c: if (op_type == MTS_READ) { *data = gt_data->int0_main_mask_reg; } else { gt_data->int0_main_mask_reg = *data; gt96k_update_irq_status(gt_data); } break; /* ===== Interrupt0 High Mask Register ===== */ case 0xc9c: if (op_type == MTS_READ) { *data = gt_data->int0_high_mask_reg; } else { gt_data->int0_high_mask_reg = *data; gt96k_update_irq_status(gt_data); } break; /* ===== Interrupt1 Main Mask Register ===== */ case 0xc24: if (op_type == MTS_READ) { *data = gt_data->int1_main_mask_reg; } else { gt_data->int1_main_mask_reg = *data; gt96k_update_irq_status(gt_data); } break; /* ===== Interrupt1 High Mask Register ===== */ case 0xca4: if (op_type == MTS_READ) { *data = gt_data->int1_high_mask_reg; } else { gt_data->int1_high_mask_reg = *data; gt96k_update_irq_status(gt_data); } break; /* ===== Serial Cause Register (read-only) ===== */ case 0x103a00: if (op_type == MTS_READ) *data = gt_data->ser_cause_reg; break; /* ===== SerInt0 Mask Register ===== */ case 0x103a80: if (op_type == MTS_READ) { *data = gt_data->serint0_mask_reg; } else { gt_data->serint0_mask_reg = *data; gt96k_update_irq_status(gt_data); } break; /* ===== SerInt1 Mask Register ===== */ case 0x103a88: if (op_type == MTS_READ) { *data = gt_data->serint1_mask_reg; } else { gt_data->serint1_mask_reg = *data; gt96k_update_irq_status(gt_data); } break; /* ===== SDMA cause register ===== */ case 0x103a10: if (op_type == MTS_READ) { *data = gt_data->sdma_cause_reg; } else { gt_data->sdma_cause_reg &= *data; gt_sdma_update_int_status(gt_data); } break; case 0x103a13: if (op_type == MTS_WRITE) { //printf("Writing 0x103a13, *data = 0x%8.8llx, " // "sdma_cause_reg=0x%8.8x\n", // *data, gt_data->sdma_cause_reg); gt_data->sdma_cause_reg = 0; gt_sdma_update_channel_int_status(gt_data,6); gt_sdma_update_channel_int_status(gt_data,7); } break; /* ==== SDMA mask register */ case 0x103a90: if (op_type == MTS_READ) { *data = gt_data->sdma_mask_reg; } else { gt_data->sdma_mask_reg = *data; gt_sdma_update_int_status(gt_data); } break; case 0x103a38: case 0x103a3c: case 0x100A48: if (op_type == MTS_READ) { //*data = 0xFFFFFFFF; } break; /* CIU Arbiter Configuration Register */ case 0x101ac0: if (op_type == MTS_READ) *data = 0x80000000; break; /* SGCR - SDMA Global Configuration Register */ case GT_REG_SGC: if (op_type == MTS_READ) *data = gt_data->sgcr; else gt_data->sgcr = *data; break; /* ===== PCI Bus 1 ===== */ case 0xcf0: pci_dev_addr_handler(cpu,gt_data->bus[1],op_type,FALSE,data); break; case 0xcf4: pci_dev_data_handler(cpu,gt_data->bus[1],op_type,FALSE,data); break; /* ===== PCI Bus 0 ===== */ case PCI_BUS_ADDR: /* pci configuration address (0xcf8) */ pci_dev_addr_handler(cpu,gt_data->bus[0],op_type,FALSE,data); break; case PCI_BUS_DATA: /* pci data address (0xcfc) */ pci_dev_data_handler(cpu,gt_data->bus[0],op_type,FALSE,data); break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"GT96100","read from unknown addr 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"GT96100","write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx\n",offset,*data,cpu_get_pc(cpu)); } #endif } done: GT_UNLOCK(gt_data); if ((op_type == MTS_READ) && (op_size == 4)) *data = swap32(*data); return NULL; } /* Handle a TX queue (single packet) */ static int gt_eth_handle_port_txqueue(struct gt_data *d,struct eth_port *port, int queue) { u_char pkt[GT_MAX_PKT_SIZE],*pkt_ptr; struct sdma_desc ctxd; m_uint32_t tx_current; m_uint32_t len,tot_len; int abort = FALSE; /* Check if this TX queue is active */ if ((queue == 0) && (port->sdcmr & GT_SDCMR_STDL)) return(FALSE); if ((queue == 1) && (port->sdcmr & GT_SDCMR_STDH)) return(FALSE); /* Copy the current txring descriptor */ tx_current = port->tx_current[queue]; if (!tx_current) return(FALSE); gt_sdma_desc_read(d,tx_current,&ctxd); /* If we don't own the first descriptor, we cannot transmit */ if (!(ctxd.cmd_stat & GT_TXDESC_OWN)) { if (queue == 0) { port->icr |= GT_ICR_TXENDL; port->sdcmr |= GT_SDCMR_STDL; port->sdcmr &= ~GT_SDCMR_TXDL; } else { port->icr |= GT_ICR_TXENDH; port->sdcmr |= GT_SDCMR_STDH; port->sdcmr &= ~GT_SDCMR_TXDH; } gt_eth_update_int_status(d,port); return(FALSE); } /* Empty packet for now */ pkt_ptr = pkt; tot_len = 0; for(;;) { #if DEBUG_ETH_TX GT_LOG(d,"gt_eth_handle_txqueue: loop: " "tx_current=0x%08x, cmd_stat=0x%08x, buf_size=0x%08x, next_ptr=0x%08x, buf_ptr=0x%08x\n", tx_current, ctxd.cmd_stat, ctxd.buf_size, ctxd.next_ptr, ctxd.buf_ptr); #endif if (!(ctxd.cmd_stat & GT_TXDESC_OWN)) { #if DEBUG_ETH_TX GT_LOG(d,"gt_eth_handle_txqueue: descriptor not owned!\n"); #endif abort = TRUE; break; } /* Copy packet data to the buffer */ len = (ctxd.buf_size & GT_TXDESC_BC_MASK) >> GT_TXDESC_BC_SHIFT; physmem_copy_from_vm(d->vm,pkt_ptr,ctxd.buf_ptr,len); pkt_ptr += len; tot_len += len; /* Clear the OWN bit if this is not the last descriptor */ if (!(ctxd.cmd_stat & GT_TXDESC_L)) { ctxd.cmd_stat &= ~GT_TXDESC_OWN; physmem_copy_u32_to_vm(d->vm,tx_current+GT_SDMA_CMD_OFFSET,ctxd.cmd_stat); } /* Last descriptor or no more desc available ? */ if (ctxd.cmd_stat & GT_TXDESC_L) break; if (!(ctxd.next_ptr)) { abort = TRUE; break; } /* Fetch the next descriptor */ tx_current = ctxd.next_ptr; gt_sdma_desc_read(d,tx_current,&ctxd); } if ((tot_len != 0) && !abort) { #if DEBUG_ETH_TX GT_LOG(d,"Ethernet: sending packet of %u bytes\n",tot_len); mem_dump(d->vm->log_fd,pkt,tot_len); #endif /* rewrite ISL header if required */ cisco_isl_rewrite(pkt,tot_len); /* send it on wire */ netio_send(port->nio,pkt,tot_len); /* Update MIB counters */ port->tx_bytes += tot_len; port->tx_frames++; } /* Clear the OWN flag of the last descriptor */ ctxd.cmd_stat &= ~GT_TXDESC_OWN; physmem_copy_u32_to_vm(d->vm,tx_current+GT_SDMA_CMD_OFFSET,ctxd.cmd_stat); port->tx_current[queue] = tx_current = ctxd.next_ptr; /* Notify host about transmitted packet */ if (queue == 0) port->icr |= GT_ICR_TXBUFL; else port->icr |= GT_ICR_TXBUFH; if (abort) { /* TX underrun */ port->icr |= GT_ICR_TXUDR; if (queue == 0) port->icr |= GT_ICR_TXERRL; else port->icr |= GT_ICR_TXERRH; } else { /* End of queue has been reached */ if (!tx_current) { if (queue == 0) port->icr |= GT_ICR_TXENDL; else port->icr |= GT_ICR_TXENDH; } } /* Update the interrupt status */ gt_eth_update_int_status(d,port); return(TRUE); } /* Handle TX ring of the specified port */ static void gt_eth_handle_port_txqueues(struct gt_data *d,u_int port) { gt_eth_handle_port_txqueue(d,&d->eth_ports[port],0); /* TX Low */ gt_eth_handle_port_txqueue(d,&d->eth_ports[port],1); /* TX High */ } /* Handle all TX rings of all Ethernet ports */ static int gt_eth_handle_txqueues(struct gt_data *d) { int i; GT_LOCK(d); for(i=0;i> 4] << 1; res |= (val & 0x100) >> 8; return(res); } /* * Compute hash value for Ethernet address filtering. * Two modes are available (p.271 of the GT96100 doc). */ static u_int gt_eth_hash_value(n_eth_addr_t *addr,int mode) { m_uint64_t tmp; u_int res; int i; /* Swap the nibbles */ for(i=0,tmp=0;ieth_addr_byte[i] & 0x0F]) << 4; tmp |= inv_nibble[(addr->eth_addr_byte[i] & 0xF0) >> 4]; } if (mode == 0) { /* Fill bits 0:8 */ res = (tmp & 0x00000003) | ((tmp & 0x00007f00) >> 6); res ^= (tmp & 0x00ff8000) >> 15; res ^= (tmp & 0x1ff000000ULL) >> 24; /* Fill bits 9:14 */ res |= (tmp & 0xfc) << 7; } else { /* Fill bits 0:8 */ res = gt_hash_inv_9bit((tmp & 0x00007fc0) >> 6); res ^= gt_hash_inv_9bit((tmp & 0x00ff8000) >> 15); res ^= gt_hash_inv_9bit((tmp & 0x1ff000000ULL) >> 24); /* Fill bits 9:14 */ res |= (tmp & 0x3f) << 9; } return(res); } /* * Walk through the Ethernet hash table. */ static int gt_eth_hash_lookup(struct gt_data *d,struct eth_port *port, n_eth_addr_t *addr,m_uint64_t *entry) { m_uint64_t eth_val; m_uint32_t hte_addr; u_int hash_val; int i; eth_val = (m_uint64_t)addr->eth_addr_byte[0] << 3; eth_val |= (m_uint64_t)addr->eth_addr_byte[1] << 11; eth_val |= (m_uint64_t)addr->eth_addr_byte[2] << 19; eth_val |= (m_uint64_t)addr->eth_addr_byte[3] << 27; eth_val |= (m_uint64_t)addr->eth_addr_byte[4] << 35; eth_val |= (m_uint64_t)addr->eth_addr_byte[5] << 43; /* Compute hash value for Ethernet address filtering */ hash_val = gt_eth_hash_value(addr,port->pcr & GT_PCR_HM); if (port->pcr & GT_PCR_HS) { /* 1/2K address filtering */ hte_addr = port->ht_addr + ((hash_val & 0x7ff) << 3); } else { /* 8K address filtering */ hte_addr = port->ht_addr + (hash_val << 3); } #if DEBUG_ETH_HASH GT_LOG(d,"Hash Lookup for Ethernet address " "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x: addr=0x%x\n", addr->eth_addr_byte[0], addr->eth_addr_byte[1], addr->eth_addr_byte[2], addr->eth_addr_byte[3], addr->eth_addr_byte[4], addr->eth_addr_byte[5], hte_addr); #endif for(i=0;ivm,hte_addr)) << 32; *entry |= physmem_copy_u32_from_vm(d->vm,hte_addr+4); /* Empty entry ? */ if (!(*entry & GT_HTE_VALID)) return(GT_HTLOOKUP_MISS); /* Skip flag or different Ethernet address: jump to next entry */ if ((*entry & GT_HTE_SKIP) || ((*entry & GT_HTE_ADDR_MASK) != eth_val)) continue; /* We have the good MAC address in this entry */ return(GT_HTLOOKUP_MATCH); } return(GT_HTLOOKUP_HOP_EXCEEDED); } /* * Check if a packet (given its destination address) must be handled * at RX path. * * Return values: * - 0: Discard packet ; * - 1: Receive packet but set "M" bit in RX descriptor; * - 2: Receive packet. * * The documentation is not clear about the M bit in RX descriptor. * It is described as "Miss" or "Match" depending on the section. * However, it turns out that IOS treats the bit as "Miss" bit. * If the bit is set, the destination MAC address has not been found * in the hash table, and the frame may be subject to software MAC * address filter associated by IOS with the interface. If the bit * is clear, the destination MAC address has been found in the hash * table and the frame will be accepted by IOS unconditionally. * The M bit is required to correctly handle unicast frames destined * to other MAC addresses when the interface works in promiscuous mode. * IOS puts an interface into promiscuous mode when multicast routing * or bridging has been configured on it. */ static inline int gt_eth_handle_rx_daddr(struct gt_data *d, struct eth_port *port, u_int hash_res, m_uint64_t hash_entry) { /* Hop Number exceeded */ if (hash_res == GT_HTLOOKUP_HOP_EXCEEDED) return(1); /* Match and hash entry marked as "Receive" */ if ((hash_res == GT_HTLOOKUP_MATCH) && (hash_entry & GT_HTE_RD)) return(2); /* Miss but hash table default mode to forward ? */ if ((hash_res == GT_HTLOOKUP_MISS) && (port->pcr & GT_PCR_HDM)) return(2); /* Promiscous Mode */ if (port->pcr & GT_PCR_PM) return(1); /* Drop packet for other cases */ return(0); } /* Put a packet in the specified RX queue */ static int gt_eth_handle_rxqueue(struct gt_data *d,u_int port_id,u_int queue, u_char *pkt,ssize_t pkt_len) { struct eth_port *port = &d->eth_ports[port_id]; m_uint32_t rx_start,rx_current; struct sdma_desc rxd0,rxdn,*rxdc; ssize_t tot_len = pkt_len; u_char *pkt_ptr = pkt; n_eth_dot1q_hdr_t *hdr; m_uint64_t hash_entry; int i,hash_res,addr_action; /* Truncate the packet if it is too big */ pkt_len = m_min(pkt_len,GT_MAX_PKT_SIZE); /* Copy the first RX descriptor */ if (!(rx_start = rx_current = port->rx_start[queue])) goto dma_error; /* Analyze the Ethernet header */ hdr = (n_eth_dot1q_hdr_t *)pkt; /* Hash table lookup for address filtering */ hash_res = gt_eth_hash_lookup(d,port,&hdr->daddr,&hash_entry); #if DEBUG_ETH_HASH GT_LOG(d,"Hash result: %d, hash_entry=0x%llx\n",hash_res,hash_entry); #endif if (!(addr_action = gt_eth_handle_rx_daddr(d,port,hash_res,hash_entry))) return(FALSE); /* Load the first RX descriptor */ gt_sdma_desc_read(d,rx_start,&rxd0); #if DEBUG_ETH_RX GT_LOG(d,"port %u/queue %u: reading desc at 0x%8.8x " "[buf_size=0x%8.8x,cmd_stat=0x%8.8x," "next_ptr=0x%8.8x,buf_ptr=0x%8.8x]\n", port_id,queue,rx_start, rxd0.buf_size,rxd0.cmd_stat,rxd0.next_ptr,rxd0.buf_ptr); #endif for(i=0,rxdc=&rxd0;tot_len>0;i++) { /* We must own the descriptor */ if (!(rxdc->cmd_stat & GT_RXDESC_OWN)) goto dma_error; /* Put data into the descriptor buffer */ gt_sdma_rxdesc_put_pkt(d,rxdc,&pkt_ptr,&tot_len); /* Clear the OWN bit */ rxdc->cmd_stat &= ~GT_RXDESC_OWN; /* We have finished if the complete packet has been stored */ if (tot_len == 0) { rxdc->cmd_stat |= GT_RXDESC_L; rxdc->buf_size += 4; /* Add 4 bytes for CRC */ } /* Update the descriptor in host memory (but not the 1st) */ if (i != 0) gt_sdma_desc_write(d,rx_current,rxdc); /* Get address of the next descriptor */ rx_current = rxdc->next_ptr; if (tot_len == 0) break; if (!rx_current) goto dma_error; /* Read the next descriptor from VM physical RAM */ gt_sdma_desc_read(d,rx_current,&rxdn); rxdc = &rxdn; } /* Update the RX pointers */ port->rx_start[queue] = port->rx_current[queue] = rx_current; /* Update the first RX descriptor */ rxd0.cmd_stat |= GT_RXDESC_F; if (hash_res == GT_HTLOOKUP_HOP_EXCEEDED) rxd0.cmd_stat |= GT_RXDESC_HE; if (addr_action == 1) rxd0.cmd_stat |= GT_RXDESC_M; if (ntohs(hdr->type) <= N_ETH_MTU) /* 802.3 frame */ rxd0.cmd_stat |= GT_RXDESC_FT; gt_sdma_desc_write(d,rx_start,&rxd0); /* Update MIB counters */ port->rx_bytes += pkt_len; port->rx_frames++; /* Indicate that we have a frame ready */ port->icr |= (GT_ICR_RXBUFQ0 << queue) | GT_ICR_RXBUF; gt_eth_update_int_status(d,port); return(TRUE); dma_error: port->icr |= (GT_ICR_RXERRQ0 << queue) | GT_ICR_RXERR; gt_eth_update_int_status(d,port); return(FALSE); } /* Handle RX packet for an Ethernet port */ static int gt_eth_handle_rx_pkt(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, struct gt_data *d,void *arg) { u_int queue,port_id = (u_int)(u_long)arg; struct eth_port *port; port = &d->eth_ports[port_id]; GT_LOCK(d); /* Check if RX DMA is active */ if (!(port->sdcmr & GT_SDCMR_ERD)) { GT_UNLOCK(d); return(FALSE); } queue = 0; /* At this time, only put packet in queue 0 */ gt_eth_handle_rxqueue(d,port_id,queue,pkt,pkt_len); GT_UNLOCK(d); return(TRUE); } /* Shutdown a GT system controller */ void dev_gt_shutdown(vm_instance_t *vm,struct gt_data *d) { if (d != NULL) { /* Stop the Ethernet TX ring scanner */ ptask_remove(d->eth_tx_tid); /* Remove the device */ dev_remove(vm,&d->dev); /* Remove the PCI device */ pci_dev_remove(d->pci_dev); /* Free the structure itself */ free(d); } } /* Create a new GT64010 controller */ int dev_gt64010_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len,u_int irq) { struct gt_data *d; if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"gt64010: unable to create device data.\n"); return(-1); } memset(d,0,sizeof(*d)); pthread_mutex_init(&d->lock,NULL); d->vm = vm; d->bus[0] = vm->pci_bus[0]; d->gt_update_irq_status = gt64k_update_irq_status; vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_gt_shutdown; dev_init(&d->dev); d->dev.name = name; d->dev.priv_data = d; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_gt64010_access; /* Add the controller as a PCI device */ if (!pci_dev_lookup(d->bus[0],0,0,0)) { d->pci_dev = pci_dev_add(d->bus[0],name, PCI_VENDOR_GALILEO,PCI_PRODUCT_GALILEO_GT64010, 0,0,irq,d,NULL,NULL,NULL); if (!d->pci_dev) { fprintf(stderr,"gt64010: unable to create PCI device.\n"); return(-1); } } /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } /* * pci_gt64120_read() * * Read a PCI register. */ static m_uint32_t pci_gt64120_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { switch (reg) { case 0x08: return(0x03008005); default: return(0); } } /* Create a new GT64120 controller */ int dev_gt64120_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len,u_int irq) { struct gt_data *d; if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"gt64120: unable to create device data.\n"); return(-1); } memset(d,0,sizeof(*d)); pthread_mutex_init(&d->lock,NULL); d->vm = vm; d->bus[0] = vm->pci_bus[0]; d->bus[1] = vm->pci_bus[1]; d->gt_update_irq_status = gt64k_update_irq_status; vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_gt_shutdown; dev_init(&d->dev); d->dev.name = name; d->dev.priv_data = d; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_gt64120_access; /* Add the controller as a PCI device */ if (!pci_dev_lookup(d->bus[0],0,0,0)) { d->pci_dev = pci_dev_add(d->bus[0],name, PCI_VENDOR_GALILEO,PCI_PRODUCT_GALILEO_GT64120, 0,0,irq,d,NULL,pci_gt64120_read,NULL); if (!d->pci_dev) { fprintf(stderr,"gt64120: unable to create PCI device.\n"); return(-1); } } /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } /* * pci_gt96100_read() * * Read a PCI register. */ static m_uint32_t pci_gt96100_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { switch (reg) { case 0x08: return(0x03008005); default: return(0); } } /* Create a new GT96100 controller */ int dev_gt96100_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len, u_int int0_irq,u_int int1_irq, u_int serint0_irq,u_int serint1_irq) { struct gt_data *d; u_int i; if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"gt96100: unable to create device data.\n"); return(-1); } memset(d,0,sizeof(*d)); pthread_mutex_init(&d->lock,NULL); d->name = name; d->vm = vm; d->gt_update_irq_status = gt96k_update_irq_status; for(i=0;isdma[0][i].id = i; d->sdma[1][i].id = i; } /* IRQ setup */ d->int0_irq = int0_irq; d->int1_irq = int1_irq; d->serint0_irq = serint0_irq; d->serint1_irq = serint1_irq; d->bus[0] = vm->pci_bus[0]; d->bus[1] = vm->pci_bus[1]; vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_gt_shutdown; dev_init(&d->dev); d->dev.name = name; d->dev.priv_data = d; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_gt96100_access; /* Add the controller as a PCI device */ if (!pci_dev_lookup(d->bus[0],0,0,0)) { d->pci_dev = pci_dev_add(d->bus[0],name, PCI_VENDOR_GALILEO,PCI_PRODUCT_GALILEO_GT96100, 0,0,-1,d,NULL,pci_gt96100_read,NULL); if (!d->pci_dev) { fprintf(stderr,"gt96100: unable to create PCI device.\n"); return(-1); } } /* Start the Ethernet TX ring scanner */ d->eth_tx_tid = ptask_add((ptask_callback)gt_eth_handle_txqueues,d,NULL); /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } /* Bind a NIO to GT96100 Ethernet device */ int dev_gt96100_eth_set_nio(struct gt_data *d,u_int port_id,netio_desc_t *nio) { struct eth_port *port; if (!d || (port_id >= GT_ETH_PORTS)) return(-1); port = &d->eth_ports[port_id]; /* check that a NIO is not already bound */ if (port->nio != NULL) return(-1); port->nio = nio; netio_rxl_add(nio,(netio_rx_handler_t)gt_eth_handle_rx_pkt, d,(void *)(u_long)port_id); return(0); } /* Unbind a NIO from a GT96100 device */ int dev_gt96100_eth_unset_nio(struct gt_data *d,u_int port_id) { struct eth_port *port; if (!d || (port_id >= GT_ETH_PORTS)) return(-1); port = &d->eth_ports[port_id]; if (port->nio != NULL) { netio_rxl_remove(port->nio); port->nio = NULL; } return(0); } /* Show debugging information */ static void dev_gt96100_show_eth_info(struct gt_data *d,u_int port_id) { struct eth_port *port; port = &d->eth_ports[port_id]; printf("GT96100 Ethernet port %u:\n",port_id); printf(" PCR = 0x%8.8x\n",port->pcr); printf(" PCXR = 0x%8.8x\n",port->pcxr); printf(" PCMR = 0x%8.8x\n",port->pcmr); printf(" PSR = 0x%8.8x\n",port->psr); printf(" ICR = 0x%8.8x\n",port->icr); printf(" IMR = 0x%8.8x\n",port->imr); printf("\n"); } /* Show debugging information */ int dev_gt96100_show_info(struct gt_data *d) { GT_LOCK(d); dev_gt96100_show_eth_info(d,0); dev_gt96100_show_eth_info(d,1); GT_UNLOCK(d); return(0); } dynamips-0.2.14/common/dev_gt.h000066400000000000000000000032641241034141600163340ustar00rootroot00000000000000/* * Cisco Router Simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __DEV_GT_H__ #define __DEV_GT_H__ #include #include "utils.h" #include "mips64.h" #include "cpu.h" #include "device.h" #include "net_io.h" #include "vm.h" /* Forward declaration for Galilo controller private data */ struct gt_data; /* Create a new GT64010 controller */ int dev_gt64010_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len,u_int irq); /* Create a new GT64120 controller */ int dev_gt64120_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len,u_int irq); /* Create a new GT96100 controller */ int dev_gt96100_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len, u_int int0_irq,u_int int1_irq, u_int serint0_irq,u_int serint1_irq); /* Set NIO for a MPSC channel */ int dev_gt96100_mpsc_set_nio(struct gt_data *d,u_int chan_id, netio_desc_t *nio); /* Unset NIO for a MPSC channel */ int dev_gt96100_mpsc_unset_nio(struct gt_data *d,u_int chan_id); /* Set a VTTY for a MPSC channel */ int dev_gt96100_mpsc_set_vtty(struct gt_data *d,u_int chan_id,vtty_t *vtty); /* Unset a VTTY for a MPSC channel */ int dev_gt96100_mpsc_unset_vtty(struct gt_data *d,u_int chan_id); /* Bind a NIO to GT96100 Ethernet device */ int dev_gt96100_eth_set_nio(struct gt_data *d,u_int port_id,netio_desc_t *nio); /* Unbind a NIO from a GT96100 Ethernet device */ int dev_gt96100_eth_unset_nio(struct gt_data *d,u_int port_id); /* Show debugging information */ int dev_gt96100_show_info(struct gt_data *d); #endif dynamips-0.2.14/common/dev_i8254x.c000066400000000000000000001051421241034141600166560ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (C) 2007 Christophe Fillot. All rights reserved. * * Intel i8254x (Wiseman/Livengood) Ethernet chip emulation. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_i8254x.h" /* Debugging flags */ #define DEBUG_MII_REGS 0 #define DEBUG_ACCESS 0 #define DEBUG_TRANSMIT 0 #define DEBUG_RECEIVE 0 #define DEBUG_UNKNOWN 0 /* Intel i8254x PCI vendor/product codes */ #define I8254X_PCI_VENDOR_ID 0x8086 #define I8254X_PCI_PRODUCT_ID 0x1001 /* Maximum packet size */ #define I8254X_MAX_PKT_SIZE 16384 /* Send up to 16 packets in a TX ring scan pass */ #define I8254X_TXRING_PASS_COUNT 16 /* Register list */ #define I8254X_REG_CTRL 0x0000 /* Control Register */ #define I8254X_REG_STATUS 0x0008 /* Device Status Register */ #define I8254X_REG_CTRLEXT 0x0018 /* Extended Control Register */ #define I8254X_REG_MDIC 0x0020 /* MDI Control Register */ #define I8254X_REG_FCAL 0x0028 /* Flow Control Address Low */ #define I8254X_REG_FCAH 0x002c /* Flow Control Address High */ #define I8254X_REG_FCT 0x0030 /* Flow Control Type */ #define I8254X_REG_VET 0x0038 /* VLAN Ether Type */ #define I8254X_REG_ICR 0x00c0 /* Interrupt Cause Read */ #define I8254X_REG_ITR 0x00c4 /* Interrupt Throttling Register */ #define I8254X_REG_ICS 0x00c8 /* Interrupt Cause Set Register */ #define I8254X_REG_IMS 0x00d0 /* Interrupt Mask Set/Read Register */ #define I8254X_REG_IMC 0x00d8 /* Interrupt Mask Clear Register */ #define I8254X_REG_RCTL 0x0100 /* Receive Control Register */ #define I8254X_REG_FCTTV 0x0170 /* Flow Control Transmit Timer Value */ #define I8254X_REG_TXCW 0x0178 /* Transmit Configuration Word */ #define I8254X_REG_RXCW 0x0180 /* Receive Configuration Word */ #define I8254X_REG_TCTL 0x0400 /* Transmit Control Register */ #define I8254X_REG_TIPG 0x0410 /* Transmit Inter Packet Gap */ #define I8254X_REG_LEDCTL 0x0E00 /* LED Control */ #define I8254X_REG_PBA 0x1000 /* Packet Buffer Allocation */ #define I8254X_REG_RDBAL 0x2800 /* RX Descriptor Base Address Low */ #define I8254X_REG_RDBAH 0x2804 /* RX Descriptor Base Address High */ #define I8254X_REG_RDLEN 0x2808 /* RX Descriptor Length */ #define I8254X_REG_RDH 0x2810 /* RX Descriptor Head */ #define I8254X_REG_RDT 0x2818 /* RX Descriptor Tail */ #define I8254X_REG_RDTR 0x2820 /* RX Delay Timer Register */ #define I8254X_REG_RXDCTL 0x3828 /* RX Descriptor Control */ #define I8254X_REG_RADV 0x282c /* RX Int. Absolute Delay Timer */ #define I8254X_REG_RSRPD 0x2c00 /* RX Small Packet Detect Interrupt */ #define I8254X_REG_TXDMAC 0x3000 /* TX DMA Control */ #define I8254X_REG_TDBAL 0x3800 /* TX Descriptor Base Address Low */ #define I8254X_REG_TDBAH 0x3804 /* TX Descriptor Base Address Low */ #define I8254X_REG_TDLEN 0x3808 /* TX Descriptor Length */ #define I8254X_REG_TDH 0x3810 /* TX Descriptor Head */ #define I8254X_REG_TDT 0x3818 /* TX Descriptor Tail */ #define I8254X_REG_TIDV 0x3820 /* TX Interrupt Delay Value */ #define I8254X_REG_TXDCTL 0x3828 /* TX Descriptor Control */ #define I8254X_REG_TADV 0x382c /* TX Absolute Interrupt Delay Value */ #define I8254X_REG_TSPMT 0x3830 /* TCP Segmentation Pad & Min Threshold */ #define I8254X_REG_RXCSUM 0x5000 /* RX Checksum Control */ /* Register list for i8254x */ #define I82542_REG_RDTR 0x0108 /* RX Delay Timer Register */ #define I82542_REG_RDBAL 0x0110 /* RX Descriptor Base Address Low */ #define I82542_REG_RDBAH 0x0114 /* RX Descriptor Base Address High */ #define I82542_REG_RDLEN 0x0118 /* RX Descriptor Length */ #define I82542_REG_RDH 0x0120 /* RDH for i82542 */ #define I82542_REG_RDT 0x0128 /* RDT for i82542 */ #define I82542_REG_TDBAL 0x0420 /* TX Descriptor Base Address Low */ #define I82542_REG_TDBAH 0x0424 /* TX Descriptor Base Address Low */ #define I82542_REG_TDLEN 0x0428 /* TX Descriptor Length */ #define I82542_REG_TDH 0x0430 /* TDH for i82542 */ #define I82542_REG_TDT 0x0438 /* TDT for i82542 */ /* CTRL - Control Register (0x0000) */ #define I8254X_CTRL_FD 0x00000001 /* Full Duplex */ #define I8254X_CTRL_LRST 0x00000008 /* Link Reset */ #define I8254X_CTRL_ASDE 0x00000020 /* Auto-speed detection */ #define I8254X_CTRL_SLU 0x00000040 /* Set Link Up */ #define I8254X_CTRL_ILOS 0x00000080 /* Invert Loss of Signal */ #define I8254X_CTRL_SPEED_MASK 0x00000300 /* Speed selection */ #define I8254X_CTRL_SPEED_SHIFT 8 #define I8254X_CTRL_FRCSPD 0x00000800 /* Force Speed */ #define I8254X_CTRL_FRCDPLX 0x00001000 /* Force Duplex */ #define I8254X_CTRL_SDP0_DATA 0x00040000 /* SDP0 data */ #define I8254X_CTRL_SDP1_DATA 0x00080000 /* SDP1 data */ #define I8254X_CTRL_SDP0_IODIR 0x00400000 /* SDP0 direction */ #define I8254X_CTRL_SDP1_IODIR 0x00800000 /* SDP1 direction */ #define I8254X_CTRL_RST 0x04000000 /* Device Reset */ #define I8254X_CTRL_RFCE 0x08000000 /* RX Flow Ctrl Enable */ #define I8254X_CTRL_TFCE 0x10000000 /* TX Flow Ctrl Enable */ #define I8254X_CTRL_VME 0x40000000 /* VLAN Mode Enable */ #define I8254X_CTRL_PHY_RST 0x80000000 /* PHY reset */ /* STATUS - Device Status Register (0x0008) */ #define I8254X_STATUS_FD 0x00000001 /* Full Duplex */ #define I8254X_STATUS_LU 0x00000002 /* Link Up */ #define I8254X_STATUS_TXOFF 0x00000010 /* Transmit paused */ #define I8254X_STATUS_TBIMODE 0x00000020 /* TBI Mode */ #define I8254X_STATUS_SPEED_MASK 0x000000C0 /* Link Speed setting */ #define I8254X_STATUS_SPEED_SHIFT 6 #define I8254X_STATUS_ASDV_MASK 0x00000300 /* Auto Speed Detection */ #define I8254X_STATUS_ASDV_SHIFT 8 #define I8254X_STATUS_PCI66 0x00000800 /* PCI bus speed */ #define I8254X_STATUS_BUS64 0x00001000 /* PCI bus width */ #define I8254X_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ #define I8254X_STATUS_PCIXSPD_MASK 0x0000C000 /* PCI-X speed */ #define I8254X_STATUS_PCIXSPD_SHIFT 14 /* CTRL_EXT - Extended Device Control Register (0x0018) */ #define I8254X_CTRLEXT_PHY_INT 0x00000020 /* PHY interrupt */ #define I8254X_CTRLEXT_SDP6_DATA 0x00000040 /* SDP6 data */ #define I8254X_CTRLEXT_SDP7_DATA 0x00000080 /* SDP7 data */ #define I8254X_CTRLEXT_SDP6_IODIR 0x00000400 /* SDP6 direction */ #define I8254X_CTRLEXT_SDP7_IODIR 0x00000800 /* SDP7 direction */ #define I8254X_CTRLEXT_ASDCHK 0x00001000 /* Auto-Speed Detect Chk */ #define I8254X_CTRLEXT_EE_RST 0x00002000 /* EEPROM reset */ #define I8254X_CTRLEXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ #define I8254X_CTRLEXT_RO_DIS 0x00020000 /* Relaxed Ordering Dis. */ #define I8254X_CTRLEXT_LNKMOD_MASK 0x00C00000 /* Link Mode */ #define I8254X_CTRLEXT_LNKMOD_SHIFT 22 /* MDIC - MDI Control Register (0x0020) */ #define I8254X_MDIC_DATA_MASK 0x0000FFFF /* Data */ #define I8254X_MDIC_REG_MASK 0x001F0000 /* PHY Register */ #define I8254X_MDIC_REG_SHIFT 16 #define I8254X_MDIC_PHY_MASK 0x03E00000 /* PHY Address */ #define I8254X_MDIC_PHY_SHIFT 21 #define I8254X_MDIC_OP_MASK 0x0C000000 /* Opcode */ #define I8254X_MDIC_OP_SHIFT 26 #define I8254X_MDIC_R 0x10000000 /* Ready */ #define I8254X_MDIC_I 0x20000000 /* Interrupt Enable */ #define I8254X_MDIC_E 0x40000000 /* Error */ /* ICR - Interrupt Cause Read (0x00c0) */ #define I8254X_ICR_TXDW 0x00000001 /* TX Desc Written back */ #define I8254X_ICR_TXQE 0x00000002 /* TX Queue Empty */ #define I8254X_ICR_LSC 0x00000004 /* Link Status Change */ #define I8254X_ICR_RXSEQ 0x00000008 /* RX Sequence Error */ #define I8254X_ICR_RXDMT0 0x00000010 /* RX Desc min threshold reached */ #define I8254X_ICR_RXO 0x00000040 /* RX Overrun */ #define I8254X_ICR_RXT0 0x00000080 /* RX Timer Interrupt */ #define I8254X_ICR_MDAC 0x00000200 /* MDIO Access Complete */ #define I8254X_ICR_RXCFG 0x00000400 #define I8254X_ICR_PHY_INT 0x00001000 /* PHY Interrupt */ #define I8254X_ICR_GPI_SDP6 0x00002000 /* GPI on SDP6 */ #define I8254X_ICR_GPI_SDP7 0x00004000 /* GPI on SDP7 */ #define I8254X_ICR_TXD_LOW 0x00008000 /* TX Desc low threshold hit */ #define I8254X_ICR_SRPD 0x00010000 /* Small RX packet detected */ /* RCTL - Receive Control Register (0x0100) */ #define I8254X_RCTL_EN 0x00000002 /* Receiver Enable */ #define I8254X_RCTL_SBP 0x00000004 /* Store Bad Packets */ #define I8254X_RCTL_UPE 0x00000008 /* Unicast Promiscuous Enabled */ #define I8254X_RCTL_MPE 0x00000010 /* Xcast Promiscuous Enabled */ #define I8254X_RCTL_LPE 0x00000020 /* Long Packet Reception Enable */ #define I8254X_RCTL_LBM_MASK 0x000000C0 /* Loopback Mode */ #define I8254X_RCTL_LBM_SHIFT 6 #define I8254X_RCTL_RDMTS_MASK 0x00000300 /* RX Desc Min Threshold Size */ #define I8254X_RCTL_RDMTS_SHIFT 8 #define I8254X_RCTL_MO_MASK 0x00003000 /* Multicast Offset */ #define I8254X_RCTL_MO_SHIFT 12 #define I8254X_RCTL_BAM 0x00008000 /* Broadcast Accept Mode */ #define I8254X_RCTL_BSIZE_MASK 0x00030000 /* RX Buffer Size */ #define I8254X_RCTL_BSIZE_SHIFT 16 #define I8254X_RCTL_VFE 0x00040000 /* VLAN Filter Enable */ #define I8254X_RCTL_CFIEN 0x00080000 /* CFI Enable */ #define I8254X_RCTL_CFI 0x00100000 /* Canonical Form Indicator Bit */ #define I8254X_RCTL_DPF 0x00400000 /* Discard Pause Frames */ #define I8254X_RCTL_PMCF 0x00800000 /* Pass MAC Control Frames */ #define I8254X_RCTL_BSEX 0x02000000 /* Buffer Size Extension */ #define I8254X_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ /* TCTL - Transmit Control Register (0x0400) */ #define I8254X_TCTL_EN 0x00000002 /* Transmit Enable */ #define I8254X_TCTL_PSP 0x00000008 /* Pad short packets */ #define I8254X_TCTL_SWXOFF 0x00400000 /* Software XOFF Transmission */ /* PBA - Packet Buffer Allocation (0x1000) */ #define I8254X_PBA_RXA_MASK 0x0000FFFF /* RX Packet Buffer */ #define I8254X_PBA_RXA_SHIFT 0 #define I8254X_PBA_TXA_MASK 0xFFFF0000 /* TX Packet Buffer */ #define I8254X_PBA_TXA_SHIFT 16 /* Flow Control Type */ #define I8254X_FCT_TYPE_DEFAULT 0x8808 /* === TX Descriptor fields === */ /* TX Packet Length (word 2) */ #define I8254X_TXDESC_LEN_MASK 0x0000ffff /* TX Descriptor CMD field (word 2) */ #define I8254X_TXDESC_IDE 0x80000000 /* Interrupt Delay Enable */ #define I8254X_TXDESC_VLE 0x40000000 /* VLAN Packet Enable */ #define I8254X_TXDESC_DEXT 0x20000000 /* Extension */ #define I8254X_TXDESC_RPS 0x10000000 /* Report Packet Sent */ #define I8254X_TXDESC_RS 0x08000000 /* Report Status */ #define I8254X_TXDESC_IC 0x04000000 /* Insert Checksum */ #define I8254X_TXDESC_IFCS 0x02000000 /* Insert FCS */ #define I8254X_TXDESC_EOP 0x01000000 /* End Of Packet */ /* TX Descriptor STA field (word 3) */ #define I8254X_TXDESC_TU 0x00000008 /* Transmit Underrun */ #define I8254X_TXDESC_LC 0x00000004 /* Late Collision */ #define I8254X_TXDESC_EC 0x00000002 /* Excess Collisions */ #define I8254X_TXDESC_DD 0x00000001 /* Descriptor Done */ /* === RX Descriptor fields === */ /* RX Packet Length (word 2) */ #define I8254X_RXDESC_LEN_MASK 0x0000ffff /* RX Descriptor STA field (word 3) */ #define I8254X_RXDESC_PIF 0x00000080 /* Passed In-exact Filter */ #define I8254X_RXDESC_IPCS 0x00000040 /* IP cksum calculated */ #define I8254X_RXDESC_TCPCS 0x00000020 /* TCP cksum calculated */ #define I8254X_RXDESC_VP 0x00000008 /* Packet is 802.1Q */ #define I8254X_RXDESC_IXSM 0x00000004 /* Ignore cksum indication */ #define I8254X_RXDESC_EOP 0x00000002 /* End Of Packet */ #define I8254X_RXDESC_DD 0x00000001 /* Descriptor Done */ /* Intel i8254x private data */ struct i8254x_data { char *name; /* Lock test */ pthread_mutex_t lock; /* Physical (MAC) address */ n_eth_addr_t mac_addr; /* Device information */ struct vdevice *dev; /* PCI device information */ struct pci_device *pci_dev; /* Virtual machine */ vm_instance_t *vm; /* NetIO descriptor */ netio_desc_t *nio; /* TX ring scanner task id */ ptask_id_t tx_tid; /* Interrupt registers */ m_uint32_t icr,imr; /* Device Control Register */ m_uint32_t ctrl; /* Extended Control Register */ m_uint32_t ctrl_ext; /* Flow Control registers */ m_uint32_t fcal,fcah,fct; /* RX Delay Timer */ m_uint32_t rdtr; /* RX/TX Control Registers */ m_uint32_t rctl,tctl; /* RX buffer size (computed from RX control register */ m_uint32_t rx_buf_size; /* RX/TX ring base addresses */ m_uint64_t rx_addr,tx_addr; /* RX/TX descriptor length */ m_uint32_t rdlen,tdlen; /* RX/TX descriptor head and tail */ m_uint32_t rdh,rdt,tdh,tdt; /* TX packet buffer */ m_uint8_t tx_buffer[I8254X_MAX_PKT_SIZE]; /* RX IRQ count */ m_uint32_t rx_irq_cnt; /* MII/PHY handling */ u_int mii_state; u_int mii_bit; u_int mii_opcode; u_int mii_phy; u_int mii_reg; u_int mii_data_pos; u_int mii_data; u_int mii_regs[32][32]; }; /* TX descriptor */ struct tx_desc { m_uint32_t tdes[4]; }; /* RX descriptor */ struct rx_desc { m_uint32_t rdes[4]; }; #define LVG_LOCK(d) pthread_mutex_lock(&(d)->lock) #define LVG_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) /* Log an message */ #define LVG_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* Read a MII register */ static m_uint16_t mii_reg_read(struct i8254x_data *d) { #if DEBUG_MII_REGS LVG_LOG(d,"MII PHY read %d reg %d\n",d->mii_phy,d->mii_reg); #endif switch(d->mii_reg) { case 0x00: return((d->mii_regs[d->mii_phy][d->mii_reg] & ~0x8200) | 0x2000); case 0x01: return(0x782c); case 0x02: return(0x0013); case 0x03: return(0x61d4); case 0x05: return(0x41e1); case 0x06: return(0x0001); case 0x11: return(0x4700); default: return(d->mii_regs[d->mii_phy][d->mii_reg]); } } /* Write a MII register */ static void mii_reg_write(struct i8254x_data *d) { #if DEBUG_MII_REGS LVG_LOG(d,"MII PHY write %d reg %d value %04x\n", d->mii_phy,d->mii_reg,d->mii_data); #endif assert(d->mii_phy < 32); assert(d->mii_reg < 32); d->mii_regs[d->mii_phy][d->mii_reg] = d->mii_data; } enum { MII_OPCODE_READ = 1, MII_OPCODE_WRITE, }; /* MII Finite State Machine */ static void mii_access(struct i8254x_data *d) { switch(d->mii_state) { case 0: /* reset */ d->mii_phy = 0; d->mii_reg = 0; d->mii_data_pos = 15; d->mii_data = 0; case 1: /* idle */ if (!d->mii_bit) d->mii_state = 2; else d->mii_state = 1; break; case 2: /* start */ d->mii_state = d->mii_bit ? 3 : 0; break; case 3: /* opcode */ d->mii_state = d->mii_bit ? 4 : 5; break; case 4: /* read: opcode "10" */ if (!d->mii_bit) { d->mii_opcode = MII_OPCODE_READ; d->mii_state = 6; } else { d->mii_state = 0; } break; case 5: /* write: opcode "01" */ if (d->mii_bit) { d->mii_opcode = MII_OPCODE_WRITE; d->mii_state = 6; } else { d->mii_state = 0; } break; case 6 ... 10: /* phy */ d->mii_phy <<= 1; d->mii_phy |= d->mii_bit; d->mii_state++; break; case 11 ... 15: /* reg */ d->mii_reg <<= 1; d->mii_reg |= d->mii_bit; d->mii_state++; break; case 16 ... 17: /* ta */ if (d->mii_opcode == MII_OPCODE_READ) d->mii_state = 18; else d->mii_state++; break; case 18: if (d->mii_opcode == MII_OPCODE_READ) { d->mii_data = mii_reg_read(d); d->mii_state++; } case 19 ... 35: if (d->mii_opcode == MII_OPCODE_READ) { d->mii_bit = (d->mii_data >> d->mii_data_pos) & 0x1; } else { d->mii_data |= d->mii_bit << d->mii_data_pos; } if (!d->mii_data_pos) { if (d->mii_opcode == MII_OPCODE_WRITE) mii_reg_write(d); d->mii_state = 0; } else { d->mii_state++; } d->mii_data_pos--; break; default: printf("MII: impossible state %u!\n",d->mii_state); } } /* Update the interrupt status */ static inline void dev_i8254x_update_irq_status(struct i8254x_data *d) { if (d->icr & d->imr) pci_dev_trigger_irq(d->vm,d->pci_dev); else pci_dev_clear_irq(d->vm,d->pci_dev); } /* Compute RX buffer size */ static inline void dev_i8254x_set_rx_buf_size(struct i8254x_data *d) { m_uint32_t bsize; bsize = (d->rctl & I8254X_RCTL_BSIZE_MASK) >> I8254X_RCTL_BSIZE_SHIFT; if (!(d->rctl & I8254X_RCTL_BSEX)) { /* Standard buffer sizes */ switch(bsize) { case 0: d->rx_buf_size = 2048; break; case 1: d->rx_buf_size = 1024; break; case 2: d->rx_buf_size = 512; break; case 3: d->rx_buf_size = 256; break; } } else { /* Extended buffer sizes */ switch(bsize) { case 0: d->rx_buf_size = 0; /* invalid */ break; case 1: d->rx_buf_size = 16384; break; case 2: d->rx_buf_size = 8192; break; case 3: d->rx_buf_size = 4096; break; } } } /* * dev_i8254x_access() */ void *dev_i8254x_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct i8254x_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0x0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to offset=0x%x, pc=0x%llx, size=%u\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name,"write access to offset=0x%x, pc=0x%llx, " "val=0x%llx, size=%u\n",offset,cpu_get_pc(cpu),*data,op_size); } #endif LVG_LOCK(d); switch(offset) { #if 0 /* TODO */ case 0x180: if (op_type == MTS_READ) *data = 0xFFFFFFFF; //0xDC004020; //1 << 31; break; #endif /* Link is Up and Full Duplex */ case I8254X_REG_STATUS: if (op_type == MTS_READ) *data = I8254X_STATUS_LU | I8254X_STATUS_FD; break; /* Device Control Register */ case I8254X_REG_CTRL: if (op_type == MTS_WRITE) d->ctrl = *data; else *data = d->ctrl; break; /* Extended Device Control Register */ case I8254X_REG_CTRLEXT: if (op_type == MTS_WRITE) { /* MDIO clock set ? */ if (!(d->ctrl_ext & I8254X_CTRLEXT_SDP6_DATA) && (*data & I8254X_CTRLEXT_SDP6_DATA)) { if (*data & I8254X_CTRLEXT_SDP7_IODIR) d->mii_bit = (*data & I8254X_CTRLEXT_SDP7_DATA) ? 1 : 0; mii_access(d); } d->ctrl_ext = *data; } else { *data = d->ctrl_ext; if (!(d->ctrl_ext & I8254X_CTRLEXT_SDP7_IODIR)) { if (d->mii_bit) *data |= I8254X_CTRLEXT_SDP7_DATA; else *data &= ~I8254X_CTRLEXT_SDP7_DATA; } } break; /* XXX */ case I8254X_REG_MDIC: if (op_type == MTS_READ) *data = 1 << 28; break; /* * Interrupt Cause Read Register. * * Notice: a read clears all interrupt bits. */ case I8254X_REG_ICR: if (op_type == MTS_READ) { *data = d->icr; d->icr = 0; if (d->rx_irq_cnt > 0) { d->icr |= I8254X_ICR_RXT0; d->rx_irq_cnt--; } dev_i8254x_update_irq_status(d); } break; /* Interrupt Cause Set Register */ case I8254X_REG_ICS: if (op_type == MTS_WRITE) { d->icr |= *data; dev_i8254x_update_irq_status(d); } break; /* Interrupt Mask Set/Read Register */ case I8254X_REG_IMS: if (op_type == MTS_WRITE) { d->imr |= *data; dev_i8254x_update_irq_status(d); } else { *data = d->imr; } break; /* Interrupt Mask Clear Register */ case I8254X_REG_IMC: if (op_type == MTS_WRITE) { d->imr &= ~(*data); dev_i8254x_update_irq_status(d); } break; /* Receive Control Register */ case I8254X_REG_RCTL: if (op_type == MTS_READ) { *data = d->rctl; } else { d->rctl = *data; dev_i8254x_set_rx_buf_size(d); } break; /* Transmit Control Register */ case I8254X_REG_TCTL: if (op_type == MTS_READ) *data = d->tctl; else d->tctl = *data; break; /* RX Descriptor Base Address Low */ case I8254X_REG_RDBAL: case I82542_REG_RDBAL: if (op_type == MTS_WRITE) { d->rx_addr &= 0xFFFFFFFF00000000ULL; d->rx_addr |= (m_uint32_t)(*data); } else { *data = (m_uint32_t)d->rx_addr; } break; /* RX Descriptor Base Address High */ case I8254X_REG_RDBAH: case I82542_REG_RDBAH: if (op_type == MTS_WRITE) { d->rx_addr &= 0x00000000FFFFFFFFULL; d->rx_addr |= *data << 32; } else { *data = d->rx_addr >> 32; } break; /* TX Descriptor Base Address Low */ case I8254X_REG_TDBAL: case I82542_REG_TDBAL: if (op_type == MTS_WRITE) { d->tx_addr &= 0xFFFFFFFF00000000ULL; d->tx_addr |= (m_uint32_t)(*data); } else { *data = (m_uint32_t)d->tx_addr; } break; /* TX Descriptor Base Address High */ case I8254X_REG_TDBAH: case I82542_REG_TDBAH: if (op_type == MTS_WRITE) { d->tx_addr &= 0x00000000FFFFFFFFULL; d->tx_addr |= *data << 32; } else { *data = d->tx_addr >> 32; } break; /* RX Descriptor Length */ case I8254X_REG_RDLEN: case I82542_REG_RDLEN: if (op_type == MTS_WRITE) d->rdlen = *data & 0xFFF80; else *data = d->rdlen; break; /* TX Descriptor Length */ case I8254X_REG_TDLEN: case I82542_REG_TDLEN: if (op_type == MTS_WRITE) d->tdlen = *data & 0xFFF80; else *data = d->tdlen; break; /* RX Descriptor Head */ case I82542_REG_RDH: case I8254X_REG_RDH: if (op_type == MTS_WRITE) d->rdh = *data & 0xFFFF; else *data = d->rdh; break; /* RX Descriptor Tail */ case I8254X_REG_RDT: case I82542_REG_RDT: if (op_type == MTS_WRITE) d->rdt = *data & 0xFFFF; else *data = d->rdt; break; /* TX Descriptor Head */ case I82542_REG_TDH: case I8254X_REG_TDH: if (op_type == MTS_WRITE) d->tdh = *data & 0xFFFF; else *data = d->tdh; break; /* TX Descriptor Tail */ case I82542_REG_TDT: case I8254X_REG_TDT: if (op_type == MTS_WRITE) d->tdt = *data & 0xFFFF; else *data = d->tdt; break; /* Flow Control Address Low */ case I8254X_REG_FCAL: if (op_type == MTS_WRITE) d->fcal = *data; else *data = d->fcal; break; /* Flow Control Address High */ case I8254X_REG_FCAH: if (op_type == MTS_WRITE) d->fcah = *data & 0xFFFF; else *data = d->fcah; break; /* Flow Control Type */ case I8254X_REG_FCT: if (op_type == MTS_WRITE) d->fct = *data & 0xFFFF; else *data = d->fct; break; /* RX Delay Timer */ case I8254X_REG_RDTR: case I82542_REG_RDTR: if (op_type == MTS_WRITE) d->rdtr = *data & 0xFFFF; else *data = d->rdtr; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,d->name, "read access to unknown offset=0x%x, " "pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name, "write access to unknown offset=0x%x, pc=0x%llx, " "val=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),*data,op_size); } #endif } LVG_UNLOCK(d); return NULL; } /* Read a TX descriptor */ static void txdesc_read(struct i8254x_data *d,m_uint64_t txd_addr, struct tx_desc *txd) { /* Get the descriptor from VM physical RAM */ physmem_copy_from_vm(d->vm,txd,txd_addr,sizeof(struct tx_desc)); /* byte-swapping */ txd->tdes[0] = vmtoh32(txd->tdes[0]); txd->tdes[1] = vmtoh32(txd->tdes[1]); txd->tdes[2] = vmtoh32(txd->tdes[2]); txd->tdes[3] = vmtoh32(txd->tdes[3]); } /* Handle the TX ring (single packet) */ static int dev_i8254x_handle_txring_single(struct i8254x_data *d) { m_uint64_t txd_addr,buf_addr; m_uint32_t buf_len,tot_len; m_uint32_t norm_len,icr; struct tx_desc txd; m_uint8_t *pkt_ptr; /* If Head is at same position than Tail, the ring is empty */ if (d->tdh == d->tdt) return(FALSE); /* Check if the NIO can transmit */ if (!netio_can_transmit(d->nio)) return(FALSE); /* Empty packet for now */ pkt_ptr = d->tx_buffer; tot_len = 0; icr = 0; while(d->tdh != d->tdt) { txd_addr = d->tx_addr + (d->tdh * sizeof(struct tx_desc)); txdesc_read(d,txd_addr,&txd); /* Copy the packet buffer */ buf_addr = ((m_uint64_t)txd.tdes[1] << 32) | txd.tdes[0]; buf_len = txd.tdes[2] & I8254X_TXDESC_LEN_MASK; #if DEBUG_TRANSMIT LVG_LOG(d,"copying data from 0x%8.8llx (buf_len=%u)\n",buf_addr,buf_len); #endif norm_len = normalize_size(buf_len,4,0); physmem_copy_from_vm(d->vm,pkt_ptr,buf_addr,norm_len); mem_bswap32(pkt_ptr,norm_len); pkt_ptr += buf_len; tot_len += buf_len; /* Write the descriptor done bit if required */ if (txd.tdes[2] & I8254X_TXDESC_RS) { txd.tdes[3] |= I8254X_TXDESC_DD; icr |= I8254X_ICR_TXDW; physmem_copy_u32_to_vm(d->vm,txd_addr+0x0c,txd.tdes[3]); } /* Go to the next descriptor. Wrap ring if we are at end */ if (++d->tdh == (d->tdlen / sizeof(struct tx_desc))) d->tdh = 0; /* End of packet ? */ if (txd.tdes[2] & I8254X_TXDESC_EOP) { #if DEBUG_TRANSMIT LVG_LOG(d,"sending packet of %u bytes\n",tot_len); mem_dump(log_file,d->tx_buffer,tot_len); #endif netio_send(d->nio,d->tx_buffer,tot_len); break; } } if (d->tdh == d->tdt) icr |= I8254X_ICR_TXQE; /* Update the interrupt cause register and trigger IRQ if needed */ d->icr |= icr; dev_i8254x_update_irq_status(d); return(TRUE); } /* Handle the TX ring */ static int dev_i8254x_handle_txring(struct i8254x_data *d) { int res,i; /* Transmit Enabled ? */ if (!(d->tctl & I8254X_TCTL_EN)) return(FALSE); for(i=0;inio); return(TRUE); } /* Read a RX descriptor */ static void rxdesc_read(struct i8254x_data *d,m_uint64_t rxd_addr, struct rx_desc *rxd) { /* Get the descriptor from VM physical RAM */ physmem_copy_from_vm(d->vm,rxd,rxd_addr,sizeof(struct rx_desc)); /* byte-swapping */ rxd->rdes[0] = vmtoh32(rxd->rdes[0]); rxd->rdes[1] = vmtoh32(rxd->rdes[1]); rxd->rdes[2] = vmtoh32(rxd->rdes[2]); rxd->rdes[3] = vmtoh32(rxd->rdes[3]); } /* * Put a packet in the RX ring. */ static int dev_i8254x_receive_pkt(struct i8254x_data *d, u_char *pkt,ssize_t pkt_len) { m_uint64_t rxd_addr,buf_addr; m_uint32_t cur_len,norm_len,tot_len; struct rx_desc rxd; m_uint32_t icr; u_char *pkt_ptr; if (!d->rx_buf_size) return(FALSE); LVG_LOCK(d); pkt_ptr = pkt; tot_len = pkt_len; icr = 0; while(tot_len > 0) { /* No descriptor available: RX overrun condition */ if (d->rdh == d->rdt) { icr |= I8254X_ICR_RXO; break; } rxd_addr = d->rx_addr + (d->rdh * sizeof(struct rx_desc)); rxdesc_read(d,rxd_addr,&rxd); cur_len = (tot_len > d->rx_buf_size) ? d->rx_buf_size : tot_len; /* Copy the packet data into the RX buffer */ buf_addr = ((m_uint64_t)rxd.rdes[1] << 32) | rxd.rdes[0]; norm_len = normalize_size(cur_len,4,0); mem_bswap32(pkt_ptr,norm_len); physmem_copy_to_vm(d->vm,pkt_ptr,buf_addr,norm_len); tot_len -= cur_len; pkt_ptr += cur_len; /* Set length field */ rxd.rdes[2] = cur_len; /* Set the status */ rxd.rdes[3] = I8254X_RXDESC_IXSM|I8254X_RXDESC_DD; if (!tot_len) { rxd.rdes[3] |= I8254X_RXDESC_EOP; icr |= I8254X_ICR_RXT0; d->rx_irq_cnt++; rxd.rdes[2] += 4; /* FCS */ } /* Write back updated descriptor */ physmem_copy_u32_to_vm(d->vm,rxd_addr+0x08,rxd.rdes[2]); physmem_copy_u32_to_vm(d->vm,rxd_addr+0x0c,rxd.rdes[3]); /* Goto to the next descriptor, and wrap if necessary */ if (++d->rdh == (d->rdlen / sizeof(struct rx_desc))) d->rdh = 0; } /* Update the interrupt cause register and trigger IRQ if needed */ d->icr |= icr; dev_i8254x_update_irq_status(d); LVG_UNLOCK(d); return(TRUE); } /* Handle the RX ring */ static int dev_i8254x_handle_rxring(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, struct i8254x_data *d) { /* * Don't start receive if RX has not been enabled in RCTL register. */ if (!(d->rctl & I8254X_RCTL_EN)) return(FALSE); #if DEBUG_RECEIVE LVG_LOG(d,"receiving a packet of %d bytes\n",pkt_len); mem_dump(log_file,pkt,pkt_len); #endif /* * Receive only multicast/broadcast trafic + unicast traffic * for this virtual machine. */ //if (dec21140_handle_mac_addr(d,pkt)) return(dev_i8254x_receive_pkt(d,pkt,pkt_len)); return(FALSE); } /* * pci_i8254x_read() * * Read a PCI register. */ static m_uint32_t pci_i8254x_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { struct i8254x_data *d = dev->priv_data; #if DEBUG_PCI_REGS I8254X_LOG(d,"read PCI register 0x%x\n",reg); #endif switch (reg) { case 0x00: return((I8254X_PCI_PRODUCT_ID << 16) | I8254X_PCI_VENDOR_ID); case 0x08: return(0x02000003); case PCI_REG_BAR0: return(d->dev->phys_addr); default: return(0); } } /* * pci_i8254x_write() * * Write a PCI register. */ static void pci_i8254x_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct i8254x_data *d = dev->priv_data; #if DEBUG_PCI_REGS LVG_LOG(d,"write PCI register 0x%x, value 0x%x\n",reg,value); #endif switch(reg) { case PCI_REG_BAR0: vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); LVG_LOG(d,"registers are mapped at 0x%x\n",value); break; } } /* * dev_i8254x_init() */ struct i8254x_data * dev_i8254x_init(vm_instance_t *vm,char *name,int interface_type, struct pci_bus *pci_bus,int pci_device,int irq) { struct i8254x_data *d; struct pci_device *pci_dev; struct vdevice *dev; /* Allocate the private data structure for I8254X */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"%s (i8254x): out of memory\n",name); return NULL; } memset(d,0,sizeof(*d)); pthread_mutex_init(&d->lock,NULL); /* Add as PCI device */ pci_dev = pci_dev_add(pci_bus,name, I8254X_PCI_VENDOR_ID,I8254X_PCI_PRODUCT_ID, pci_device,0,irq, d,NULL,pci_i8254x_read,pci_i8254x_write); if (!pci_dev) { fprintf(stderr,"%s (i8254x): unable to create PCI device.\n",name); goto err_pci_dev; } /* Create the device itself */ if (!(dev = dev_create(name))) { fprintf(stderr,"%s (i8254x): unable to create device.\n",name); goto err_dev; } d->name = name; d->vm = vm; d->pci_dev = pci_dev; d->dev = dev; dev->phys_addr = 0; dev->phys_len = 0x10000; dev->handler = dev_i8254x_access; dev->priv_data = d; return(d); err_dev: pci_dev_remove(pci_dev); err_pci_dev: free(d); return NULL; } /* Remove an Intel i8254x device */ void dev_i8254x_remove(struct i8254x_data *d) { if (d != NULL) { pci_dev_remove(d->pci_dev); vm_unbind_device(d->vm,d->dev); cpu_group_rebuild_mts(d->vm->cpu_group); free(d->dev); free(d); } } /* Bind a NIO to an Intel i8254x device */ int dev_i8254x_set_nio(struct i8254x_data *d,netio_desc_t *nio) { /* check that a NIO is not already bound */ if (d->nio != NULL) return(-1); d->nio = nio; d->tx_tid = ptask_add((ptask_callback)dev_i8254x_handle_txring,d,NULL); netio_rxl_add(nio,(netio_rx_handler_t)dev_i8254x_handle_rxring,d,NULL); return(0); } /* Unbind a NIO from an Intel i8254x device */ void dev_i8254x_unset_nio(struct i8254x_data *d) { if (d->nio != NULL) { ptask_remove(d->tx_tid); netio_rxl_remove(d->nio); d->nio = NULL; } } dynamips-0.2.14/common/dev_i8254x.h000066400000000000000000000013771241034141600166700ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __DEV_I8254X_H__ #define __DEV_I8254X_H__ #include #include "utils.h" #include "cpu.h" #include "vm.h" #include "device.h" #include "net_io.h" /* Generic Intel i8254x initialization code */ struct i8254x_data * dev_i8254x_init(vm_instance_t *vm,char *name,int interface_type, struct pci_bus *pci_bus,int pci_device,int irq); /* Remove an Intel i8254x device */ void dev_i8254x_remove(struct i8254x_data *d); /* Bind a NIO to an Intel i8254x device */ int dev_i8254x_set_nio(struct i8254x_data *d,netio_desc_t *nio); /* Unbind a NIO from an Intel i8254x device */ void dev_i8254x_unset_nio(struct i8254x_data *d); #endif dynamips-0.2.14/common/dev_i8255x.c000066400000000000000000000727071241034141600166710ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot. * * Intel i8255x (eepro100) Ethernet chip emulation. */ #include #include #include #include #include #include #include #include #include "utils.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_i8255x.h" /* Debugging flags */ #define DEBUG_MII_REGS 0 #define DEBUG_PCI_REGS 0 #define DEBUG_ACCESS 0 #define DEBUG_TRANSMIT 0 #define DEBUG_RECEIVE 0 #define DEBUG_UNKNOWN 0 /* Intel i8255x PCI vendor/product codes */ #define I8255X_PCI_VENDOR_ID 0x8086 #define I8255X_PCI_PRODUCT_ID 0x1229 /* Maximum packet size */ #define I8255X_MAX_PKT_SIZE 4096 /* MDI Control Register */ #define I8255X_MDI_IE 0x20000000 #define I8255X_MDI_R 0x10000000 #define I8255X_MDI_OP_MASK 0x0C000000 #define I8255X_MDI_OP_SHIFT 26 #define I8255X_MDI_PHY_MASK 0x03E00000 #define I8255X_MDI_PHY_SHIFT 21 #define I8255X_MDI_REG_MASK 0x001F0000 #define I8255X_MDI_REG_SHIFT 16 #define I8255X_MDI_DATA_MASK 0x0000FFFF /* Microcode size (in dwords) */ #define I8255X_UCODE_SIZE 64 /* Size of configuration block (in dwords) */ #define I8255X_CONFIG_SIZE 6 /* Size of statistical counters (in dwords) */ #define I8255X_STAT_CNT_SIZE 20 /* SCB Command Register (Command byte) */ #define SCB_CMD_RUC_MASK 0x07 /* RU Command */ #define SCB_CMD_RUC_SHIFT 0 #define SCB_CMD_CUC_MASK 0xF0 /* CU Command */ #define SCB_CMD_CUC_SHIFT 4 /* SCB Command Register (Interrupt Control Byte) */ #define SCB_CMD_CX 0x80 /* CX Mask */ #define SCB_CMD_FR 0x40 /* FR Mask */ #define SCB_CMD_CNA 0x20 /* CNA Mask */ #define SCB_CMD_RNR 0x10 /* RNR Mask */ #define SCB_CMD_ER 0x08 /* ER Mask */ #define SCB_CMD_FCP 0x04 /* FCP Mask */ #define SCB_CMD_SI 0x02 /* Software generated interrupt */ #define SCB_CMD_M 0x01 /* Mask Interrupt */ /* SCB Interrupt Mask */ #define SCB_INT_MASK 0xF0 /* SCB Status Word (Stat/ACK byte) */ #define SCB_STAT_CX 0x80 /* CU finished command execution */ #define SCB_STAT_FR 0x40 /* RU finished Frame Reception */ #define SCB_STAT_CNA 0x20 /* CU left active state or entered idle state */ #define SCB_STAT_RNR 0x10 /* RU left ready state */ #define SCB_STAT_MDI 0x08 /* MDI read/write cycle completed */ #define SCB_STAT_SWI 0x04 /* Software generated interrupt */ #define SCB_STAT_FCP 0x01 /* Flow Control Pause interrupt */ /* CU states */ #define CU_STATE_IDLE 0x00 /* Idle */ #define CU_STATE_SUSPEND 0x01 /* Suspended */ #define CU_STATE_LPQ_ACT 0x02 /* LPQ Active */ #define CU_STATE_HQP_ACT 0x03 /* HQP Active */ /* RU states */ #define RU_STATE_IDLE 0x00 /* Idle */ #define RU_STATE_SUSPEND 0x01 /* Suspended */ #define RU_STATE_NO_RES 0x02 /* No RX ressources available */ #define RU_STATE_READY 0x04 /* Ready */ /* CU (Command Unit) commands */ #define CU_CMD_NOP 0x00 /* No Operation */ #define CU_CMD_START 0x01 /* Start */ #define CU_CMD_RESUME 0x02 /* Resume */ #define CU_CMD_LOAD_DUMP_CNT 0x04 /* Load Dump Counters Address */ #define CU_CMD_DUMP_STAT_CNT 0x05 /* Dump Statistical Counters */ #define CU_CMD_LOAD_CU_BASE 0x06 /* Load CU Base */ #define CU_CMD_DUMP_RST_STAT_CNT 0x07 /* Dump & Reset Stat Counters */ #define CU_CMD_STAT_RESUME 0x0a /* Static Resume */ /* RU (Receive Unit) commands */ #define RU_CMD_NOP 0x00 /* No Operation */ #define RU_CMD_START 0x01 /* Start */ #define RU_CMD_RESUME 0x02 /* Resume */ #define RU_CMD_RX_DMA_REDIRECT 0x03 /* Receive DMA redirect */ #define RU_CMD_ABORT 0x04 /* Abort */ #define RU_CMD_LOAD_HDS 0x05 /* Load Header Data Size */ #define RU_CMD_LOAD_RU_BASE 0x06 /* Load RU Base */ /* CB (Command Block) commands */ #define CB_CMD_NOP 0x00 /* No Operation */ #define CB_CMD_IADDR_SETUP 0x01 /* Individual Address Setup */ #define CB_CMD_CONFIGURE 0x02 /* Configure Device Parameters */ #define CB_CMD_XCAST_SETUP 0x03 /* Multicast Address Setup */ #define CB_CMD_TRANSMIT 0x04 /* Transmit a single frame */ #define CB_CMD_LOAD_UCODE 0x05 /* Load Microcode */ #define CB_CMD_DUMP 0x06 /* Dump Internal Registers */ #define CB_CMD_DIAGNOSE 0x07 /* Diagnostics */ /* CB (Command Block) control/status word */ #define CB_CTRL_EL 0x80000000 /* Last command in CBL */ #define CB_CTRL_S 0x40000000 /* Suspend CU after completion */ #define CB_CTRL_I 0x20000000 /* Interrupt at end of exec (CX) */ #define CB_CTRL_SF 0x00080000 /* Mode: 0=simplified,1=flexible */ #define CB_CTRL_CMD_MASK 0x00070000 /* Command */ #define CB_CTRL_CMD_SHIFT 16 #define CB_CTRL_C 0x00008000 /* Execution status (1=completed) */ #define CB_CTRL_OK 0x00002000 /* Command success */ /* CB Transmit Command */ #define TXCB_NUM_MASK 0xFF000000 /* TBD Number */ #define TXCB_NUM_SHIFT 24 #define TXCB_EOF 0x00008000 /* Whole frame in TxCB */ #define TXCB_BLK_SIZE 0x00003FFF /* TxCB Byte count */ /* Receive Frame Descriptor (RxFD) control status/word */ #define RXFD_CTRL_EL 0x80000000 /* Last RXFD in RFA */ #define RXFD_CTRL_S 0x40000000 /* Suspend RU after completion */ #define RXFD_CTRL_H 0x00100000 /* Header RXFD */ #define RXFD_CTRL_SF 0x00080000 /* Mode: 0=simplified,1=flexible */ #define RXFD_CTRL_C 0x00008000 /* Execution status (1=completed) */ #define RXFD_CTRL_OK 0x00002000 /* Packet OK */ #define RXFD_CTRL_CRC_ERR 0x00000800 /* CRC Error */ #define RXFD_CTRL_CRC_AL 0x00000400 /* Alignment Error */ #define RXFD_CTRL_NO_RES 0x00000200 /* No Ressources */ #define RXFD_CTRL_DMA_OV 0x00000100 /* DMA Overrun */ #define RXFD_CTRL_FTS 0x00000080 /* Frame Too Short */ #define RXFD_CTRL_TL 0x00000020 /* Type/Length */ #define RXFD_CTRL_ERR 0x00000010 /* RX Error */ #define RXFD_CTRL_NAM 0x00000004 /* No Address Match */ #define RXFD_CTRL_IAM 0x00000002 /* Individual Address Match */ #define RXFD_CTRL_COLL 0x00000001 /* RX Collision */ #define RXFD_EOF 0x00008000 /* End Of Frame */ #define RXFD_SIZE_MASK 0x00003FFF /* Size mask */ #define RXBD_CTRL_EOF 0x00008000 /* End Of Frame */ #define RXBD_CTRL_F 0x00004000 /* Buffer used */ /* Tx Buffer Descriptor */ struct i8255x_txbd { m_uint32_t buf_addr; m_uint32_t buf_size; }; /* CU (Command Unit) Action */ struct i8255x_cu_action { m_uint32_t ctrl; m_uint32_t link_offset; m_uint32_t txbd_addr; m_uint32_t txbd_count; }; /* RX Buffer Descriptor */ struct i8255x_rxbd { m_uint32_t ctrl; m_uint32_t rxbd_next; m_uint32_t buf_addr; m_uint32_t buf_size; }; /* RX Frame Descriptor */ struct i8255x_rxfd { m_uint32_t ctrl; m_uint32_t link_offset; m_uint32_t rxbd_addr; m_uint32_t rxbd_size; }; /* Statistical counters indexes */ #define STAT_CNT_TX_GOOD 0 /* Transmit good frames */ #define STAT_CNT_RX_GOOD 9 /* Receive good frames */ #define STAT_CNT_RX_RES_ERR 12 /* Receive resource errors */ /* Intel i8255x private data */ struct i8255x_data { char *name; /* Lock test */ pthread_mutex_t lock; /* Physical (MAC) address */ n_eth_addr_t mac_addr; /* Device information */ struct vdevice *dev; /* PCI device information */ struct pci_device *pci_dev; /* Virtual machine */ vm_instance_t *vm; /* NetIO descriptor */ netio_desc_t *nio; /* CU and RU current states */ u_int cu_state,ru_state; /* CU/RU bases + current offsets */ m_uint32_t cu_base,ru_base; m_uint32_t cu_offset,ru_offset; /* SCB general pointer */ m_uint32_t scb_gptr; /* SCB Interrupt Control */ m_uint8_t scb_ic; /* SCB Status Acknowledge (for interrupts) */ m_uint8_t scb_stat_ack; /* Statistical counters address */ m_uint32_t stat_cnt_addr; /* MII registers */ m_uint32_t mii_ctrl; u_int mii_regs[32][32]; /* MAC Individual Address */ n_eth_addr_t iaddr; /* Configuration data */ m_uint32_t config_data[I8255X_CONFIG_SIZE]; /* Microcode */ m_uint32_t microcode[I8255X_UCODE_SIZE]; /* Statistical counters */ m_uint32_t stat_counters[I8255X_STAT_CNT_SIZE]; /* TX packet buffer */ m_uint8_t tx_buffer[I8255X_MAX_PKT_SIZE]; }; #define EEPRO_LOCK(d) pthread_mutex_lock(&(d)->lock) #define EEPRO_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) /* Log an message */ #define EEPRO_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) enum { MII_OPCODE_WRITE = 1, MII_OPCODE_READ, }; /* Read a MII register */ static m_uint16_t mii_reg_read(struct i8255x_data *d) { u_int mii_phy,mii_reg; mii_phy = (d->mii_ctrl & I8255X_MDI_PHY_MASK) >> I8255X_MDI_PHY_SHIFT; mii_reg = (d->mii_ctrl & I8255X_MDI_REG_MASK) >> I8255X_MDI_REG_SHIFT; #if DEBUG_MII_REGS EEPRO_LOG(d,"MII PHY read %d reg %d\n",mii_phy,mii_reg); #endif switch(mii_reg) { case 0x00: return((d->mii_regs[mii_phy][mii_reg] & ~0x8200) | 0x2000); case 0x01: return(0x782c); case 0x02: return(0x0013); case 0x03: return(0x61d4); case 0x05: return(0x41e1); case 0x06: return(0x0001); case 0x11: return(0x4700); default: return(d->mii_regs[mii_phy][mii_reg]); } } /* Write a MII register */ static void mii_reg_write(struct i8255x_data *d) { u_int mii_phy,mii_reg,mii_data; mii_phy = (d->mii_ctrl & I8255X_MDI_PHY_MASK) >> I8255X_MDI_PHY_SHIFT; mii_reg = (d->mii_ctrl & I8255X_MDI_REG_MASK) >> I8255X_MDI_REG_SHIFT; mii_data = d->mii_ctrl & I8255X_MDI_DATA_MASK; #if DEBUG_MII_REGS EEPRO_LOG(d,"MII PHY write %d reg %d value %04x\n", mii_phy,mii_reg,mii_data); #endif d->mii_regs[mii_phy][mii_reg] = mii_data; } /* Update interrupt status */ static void dev_i8255x_update_irq_status(struct i8255x_data *d) { /* If interrupts are masked, clear IRQ */ if (d->scb_ic & SCB_CMD_M) { pci_dev_clear_irq(d->vm,d->pci_dev); return; } /* Software generated interrupt ? */ if (d->scb_ic & SCB_CMD_SI) { pci_dev_trigger_irq(d->vm,d->pci_dev); return; } /* Hardware interrupt ? */ if (d->scb_stat_ack & (~d->scb_ic & SCB_INT_MASK)) pci_dev_trigger_irq(d->vm,d->pci_dev); else pci_dev_clear_irq(d->vm,d->pci_dev); } /* Fetch a CB (Command Block) */ static void dev_i8255x_fetch_cb(struct i8255x_data *d,m_uint32_t addr, struct i8255x_cu_action *action) { physmem_copy_from_vm(d->vm,action,addr,sizeof(*action)); action->ctrl = vmtoh32(action->ctrl); action->link_offset = vmtoh32(action->link_offset); action->txbd_addr = vmtoh32(action->txbd_addr); action->txbd_count = vmtoh32(action->txbd_count); } /* Fetch a TX buffer descriptor */ static void dev_i8255x_fetch_txbd(struct i8255x_data *d,m_uint32_t addr, struct i8255x_txbd *bd) { physmem_copy_from_vm(d->vm,bd,addr,sizeof(*bd)); bd->buf_addr = vmtoh32(bd->buf_addr); bd->buf_size = vmtoh32(bd->buf_size); } /* Transmit a frame */ static int dev_i8255x_send_tx_pkt(struct i8255x_data *d,m_uint32_t cb_addr, struct i8255x_cu_action *action) { m_uint32_t i,blk_size,tx_size,txbd_addr,txbd_cnt; struct i8255x_txbd txbd; m_uint8_t *tx_ptr; m_uint32_t norm_len; /* === Simplified mode: copy the data directly from the TxCB === */ if (!(action->ctrl & CB_CTRL_SF)) { tx_size = action->txbd_count & TXCB_BLK_SIZE; norm_len = normalize_size(tx_size,4,0); physmem_copy_from_vm(d->vm,d->tx_buffer,cb_addr+0x10,norm_len); mem_bswap32(d->tx_buffer,norm_len); goto do_transmit; } /* === Flexible mode === */ tx_ptr = d->tx_buffer; tx_size = 0; if (action->txbd_addr == 0xFFFFFFFF) { txbd_addr = cb_addr + 0x10; } else { /* copy the data directly from the TxCB if present */ blk_size = action->txbd_count & TXCB_BLK_SIZE; if (blk_size > 0) { tx_size = action->txbd_count & TXCB_BLK_SIZE; norm_len = normalize_size(tx_size,4,0); physmem_copy_from_vm(d->vm,tx_ptr,cb_addr+0x10,norm_len); mem_bswap32(tx_ptr,norm_len); tx_ptr += tx_size; } txbd_addr = action->txbd_addr; } txbd_cnt = (action->txbd_count & TXCB_NUM_MASK) >> TXCB_NUM_SHIFT; /* * Fetch all Tx buffer descriptors and copy data from each separate buffer. */ for(i=0;ivm,tx_ptr,txbd.buf_addr,norm_len); mem_bswap32(tx_ptr,norm_len); tx_ptr += txbd.buf_size; tx_size += txbd.buf_size; txbd_addr += sizeof(txbd); } do_transmit: d->stat_counters[STAT_CNT_TX_GOOD]++; #if DEBUG_TRANSMIT EEPRO_LOG(d,"sending packet of %u bytes\n",tx_size); mem_dump(log_file,d->tx_buffer,tx_size); #endif netio_send(d->nio,d->tx_buffer,tx_size); return(TRUE); } /* Process an indidual CB (Command Block) */ static void dev_i8255x_process_cb(struct i8255x_data *d,m_uint32_t cb_addr, struct i8255x_cu_action *action) { m_uint32_t tmp[2]; u_int cmd,res; cmd = (action->ctrl & CB_CTRL_CMD_MASK) >> CB_CTRL_CMD_SHIFT; switch(cmd) { /* No Operation */ case CB_CMD_NOP: res = TRUE; break; /* Transmit a frame */ case CB_CMD_TRANSMIT: res = dev_i8255x_send_tx_pkt(d,cb_addr,action); break; /* Configure */ case CB_CMD_CONFIGURE: physmem_copy_from_vm(d->vm,d->config_data,cb_addr+0x08, I8255X_CONFIG_SIZE * sizeof(m_uint32_t)); mem_bswap32(d->config_data,I8255X_CONFIG_SIZE * sizeof(m_uint32_t)); res = TRUE; break; /* Individual address setup */ case CB_CMD_IADDR_SETUP: tmp[0] = physmem_copy_u32_from_vm(d->vm,cb_addr+0x08); tmp[1] = physmem_copy_u32_from_vm(d->vm,cb_addr+0x0c); d->iaddr.eth_addr_byte[0] = tmp[0]; d->iaddr.eth_addr_byte[1] = tmp[0] >> 8; d->iaddr.eth_addr_byte[2] = tmp[0] >> 16; d->iaddr.eth_addr_byte[3] = tmp[0] >> 24; d->iaddr.eth_addr_byte[4] = tmp[1]; d->iaddr.eth_addr_byte[5] = tmp[1] >> 8; EEPRO_LOG(d,"iaddr set to: %2.2x%2.2x.%2.2x%2.2x.%2.2x%2.2x\n", d->iaddr.eth_addr_byte[0],d->iaddr.eth_addr_byte[1], d->iaddr.eth_addr_byte[2],d->iaddr.eth_addr_byte[3], d->iaddr.eth_addr_byte[4],d->iaddr.eth_addr_byte[5]); res = TRUE; break; /* Load Microcode */ case CB_CMD_LOAD_UCODE: physmem_copy_from_vm(d->vm,d->microcode,cb_addr+0x08, I8255X_UCODE_SIZE * sizeof(m_uint32_t)); mem_bswap32(d->microcode,I8255X_UCODE_SIZE * sizeof(m_uint32_t)); EEPRO_LOG(d,"microcode loaded\n"); res = TRUE; break; /* Unsupported command */ default: EEPRO_LOG(d,"unsupported CB command 0x%2.2x (cb_addr=0x%8.8x)\n", cmd,cb_addr); res = TRUE; } /* Set the completed bit with the result */ action->ctrl |= CB_CTRL_C; if (res) action->ctrl |= CB_CTRL_OK; /* Update control word */ physmem_copy_u32_to_vm(d->vm,cb_addr,action->ctrl); } /* Process a CBL (Command Block List) */ static void dev_i8255x_process_cbl(struct i8255x_data *d) { struct i8255x_cu_action action; m_uint32_t cb_addr; for(;;) { cb_addr = d->cu_base + d->cu_offset; dev_i8255x_fetch_cb(d,cb_addr,&action); /* Execute command */ dev_i8255x_process_cb(d,cb_addr,&action); /* Interrupt at end of execution ? */ if (action.ctrl & CB_CTRL_I) d->scb_stat_ack |= SCB_STAT_CX; /* Return to idle state ? */ if (action.ctrl & CB_CTRL_EL) { d->cu_state = CU_STATE_IDLE; d->scb_stat_ack |= SCB_STAT_CNA; break; } else { /* Enter suspended state ? */ if (action.ctrl & CB_CTRL_S) { d->cu_state = CU_STATE_SUSPEND; d->scb_stat_ack |= SCB_STAT_CNA; break; } } /* Go to next descriptor */ d->cu_offset = action.link_offset; } /* Update interrupt status */ dev_i8255x_update_irq_status(d); } /* Resume a Command Block List */ static int dev_i8255x_cu_resume(struct i8255x_data *d) { struct i8255x_cu_action action; m_uint32_t cu_addr; /* If we are in idle state, ignore the command */ if (d->cu_state == CU_STATE_IDLE) return(FALSE); cu_addr = d->cu_base + d->cu_offset; /* Check if the previous block has still the S bit set */ dev_i8255x_fetch_cb(d,cu_addr,&action); if (action.ctrl & CB_CTRL_S) return(FALSE); d->cu_offset = action.link_offset; d->cu_state = CU_STATE_LPQ_ACT; dev_i8255x_process_cbl(d); return(TRUE); } /* Dump Statistical counters */ static void dev_i8255x_dump_stat_cnt(struct i8255x_data *d) { m_uint32_t counters[I8255X_STAT_CNT_SIZE]; memcpy(counters,d->stat_counters,sizeof(counters)); mem_bswap32(counters,sizeof(counters)); physmem_copy_to_vm(d->vm,counters,d->stat_cnt_addr,sizeof(counters)); } /* Process a CU command */ static void dev_i8255x_process_cu_cmd(struct i8255x_data *d,u_int cuc) { switch(cuc) { /* No Operation */ case CU_CMD_NOP: break; /* Start */ case CU_CMD_START: d->cu_offset = d->scb_gptr; d->cu_state = CU_STATE_LPQ_ACT; dev_i8255x_process_cbl(d); break; /* Resume */ case CU_CMD_RESUME: dev_i8255x_cu_resume(d); break; /* Load CU base */ case CU_CMD_LOAD_CU_BASE: d->cu_base = d->scb_gptr; break; /* Load Dump Counters Address */ case CU_CMD_LOAD_DUMP_CNT: d->stat_cnt_addr = d->scb_gptr; break; /* Dump Statistical Counters */ case CU_CMD_DUMP_STAT_CNT: dev_i8255x_dump_stat_cnt(d); break; /* Dump Statistical Counters and reset them */ case CU_CMD_DUMP_RST_STAT_CNT: dev_i8255x_dump_stat_cnt(d); memset(d->stat_counters,0,sizeof(d->stat_counters)); break; default: EEPRO_LOG(d,"unsupported CU command 0x%2.2x\n",cuc); } } /* Fetch an RxFD (RX Frame Descriptor) */ static void dev_i8255x_fetch_rxfd(struct i8255x_data *d,m_uint32_t addr, struct i8255x_rxfd *rxfd) { physmem_copy_from_vm(d->vm,rxfd,addr,sizeof(*rxfd)); rxfd->ctrl = vmtoh32(rxfd->ctrl); rxfd->link_offset = vmtoh32(rxfd->link_offset); rxfd->rxbd_addr = vmtoh32(rxfd->rxbd_addr); rxfd->rxbd_size = vmtoh32(rxfd->rxbd_size); } /* Fetch an RxBD (Rx Buffer Descriptor) */ static void dev_i8255x_fetch_rxbd(struct i8255x_data *d,m_uint32_t addr, struct i8255x_rxbd *rxbd) { physmem_copy_from_vm(d->vm,rxbd,addr,sizeof(*rxbd)); rxbd->ctrl = vmtoh32(rxbd->ctrl); rxbd->rxbd_next = vmtoh32(rxbd->rxbd_next); rxbd->buf_addr = vmtoh32(rxbd->buf_addr); rxbd->buf_size = vmtoh32(rxbd->buf_size); } /* Store a packet */ static int dev_i8255x_store_rx_pkt(struct i8255x_data *d, m_uint8_t *pkt,ssize_t pkt_len) { m_uint32_t rxfd_addr,rxbd_addr; m_uint32_t rxfd_next,rxbd_next; m_uint32_t clen,buf_size,norm_len; struct i8255x_rxfd rxfd; struct i8255x_rxbd rxbd; m_uint8_t *pkt_ptr; ssize_t tot_len; /* Fetch the RX Frame descriptor */ rxfd_addr = d->ru_base + d->ru_offset; dev_i8255x_fetch_rxfd(d,rxfd_addr,&rxfd); /* === Simplified mode === */ if (!(rxfd.ctrl & RXFD_CTRL_SF)) { /* Copy the packet data directly into the frame descriptor */ norm_len = normalize_size(pkt_len,4,0); mem_bswap32(pkt,norm_len); physmem_copy_to_vm(d->vm,pkt,rxfd_addr+0x10,norm_len); /* Update the RxFD and generate the appropriate interrupt */ goto update_rxfd; } /* === Flexible mode === */ rxbd_addr = d->ru_base + rxfd.rxbd_addr; pkt_ptr = pkt; tot_len = pkt_len; do { /* Fetch the RX buffer */ dev_i8255x_fetch_rxbd(d,rxbd_addr,&rxbd); rxbd_next = rxbd.rxbd_next; /* Get the current buffer size */ buf_size = rxbd.buf_size & RXFD_SIZE_MASK; clen = m_min(tot_len,buf_size); /* Copy the data into the buffer */ norm_len = normalize_size(clen,4,0); mem_bswap32(pkt_ptr,norm_len); physmem_copy_to_vm(d->vm,pkt_ptr,rxbd.buf_addr,norm_len); pkt_ptr += clen; tot_len -= clen; /* Update RX buffer info */ if (!tot_len) { rxbd.ctrl |= RXBD_CTRL_EOF; clen += 4; /* Add CRC */ } rxbd.ctrl |= RXBD_CTRL_F | clen; physmem_copy_u32_to_vm(d->vm,rxbd_addr+0x00,rxbd.ctrl); }while(tot_len > 0); /* Set the next available RxBD in next RxFD */ rxbd_next = d->ru_base + rxbd.rxbd_next; rxfd_next = d->ru_base + rxfd.link_offset; physmem_copy_u32_to_vm(d->vm,rxfd_next+0x08,rxbd_next); /* Update the RxFD */ update_rxfd: rxfd.ctrl |= RXFD_CTRL_C | RXFD_CTRL_OK; rxfd.rxbd_size &= ~0xFFFF; rxfd.rxbd_size |= RXFD_EOF | (pkt_len + 4); physmem_copy_u32_to_vm(d->vm,rxfd_addr+0x00,rxfd.ctrl); physmem_copy_u32_to_vm(d->vm,rxfd_addr+0x0c,rxfd.rxbd_size); d->stat_counters[STAT_CNT_RX_GOOD]++; /* A frame has been received: generate an IRQ */ d->scb_stat_ack |= SCB_STAT_FR; if (rxfd.ctrl & RXFD_CTRL_EL) { d->ru_state = RU_STATE_NO_RES; d->scb_stat_ack |= SCB_STAT_RNR; } else { if (rxfd.ctrl & RXFD_CTRL_S) { d->ru_state = RU_STATE_SUSPEND; d->scb_stat_ack |= SCB_STAT_RNR; } else { d->ru_offset = rxfd.link_offset; } } dev_i8255x_update_irq_status(d); return(TRUE); } /* Resume reception */ static int dev_i8255x_ru_resume(struct i8255x_data *d) { struct i8255x_rxfd rxfd; m_uint32_t rxfd_addr; /* If we are not in ready state, ignore the command */ if (d->ru_state != RU_STATE_READY) return(FALSE); /* Fetch the RX Frame descriptor */ rxfd_addr = d->ru_base + d->ru_offset; dev_i8255x_fetch_rxfd(d,rxfd_addr,&rxfd); /* Check if the previous frame descriptor has still the S bit set */ if (rxfd.ctrl & RXFD_CTRL_S) return(FALSE); d->ru_offset = rxfd.link_offset; d->ru_state = RU_STATE_READY; return(TRUE); } /* Process a RU command */ static void dev_i8255x_process_ru_cmd(struct i8255x_data *d,u_int ruc) { switch(ruc) { /* No Operation */ case RU_CMD_NOP: break; /* Start */ case RU_CMD_START: d->ru_offset = d->scb_gptr; d->ru_state = RU_STATE_READY; break; /* Resume */ case RU_CMD_RESUME: dev_i8255x_ru_resume(d); break; /* Load RU base */ case RU_CMD_LOAD_RU_BASE: d->ru_base = d->scb_gptr; break; default: EEPRO_LOG(d,"unsupported RU command 0x%2.2x\n",ruc); } } /* * dev_i8255x_access() */ void *dev_i8255x_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct i8255x_data *d = dev->priv_data; u_int cuc,ruc,mii_op; if (op_type == MTS_READ) *data = 0x0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to offset=0x%x, pc=0x%llx, size=%u\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name,"write access to offset=0x%x, pc=0x%llx, " "val=0x%llx, size=%u\n",offset,cpu_get_pc(cpu),*data,op_size); } #endif EEPRO_LOCK(d); switch(offset) { /* SCB Command Word (interrupt control byte) */ case 0x00: if (op_type == MTS_WRITE) d->scb_ic = *data; break; /* SCB Command Word (command byte) */ case 0x01: if (op_type == MTS_WRITE) { cuc = (*data & SCB_CMD_CUC_MASK) >> SCB_CMD_CUC_SHIFT; ruc = (*data & SCB_CMD_RUC_MASK) >> SCB_CMD_RUC_SHIFT; /* Process CU and RU commands */ dev_i8255x_process_cu_cmd(d,cuc); dev_i8255x_process_ru_cmd(d,ruc); } break; /* SCB Status Word */ case 0x02: if (op_type == MTS_READ) { *data = d->scb_stat_ack << 8; } else { d->scb_stat_ack &= ~(*data >> 8); dev_i8255x_update_irq_status(d); } break; /* SCB General Pointer */ case 0x04: if (op_type == MTS_WRITE) d->scb_gptr = *data; else *data = d->scb_gptr; break; /* MDI control register */ case 0x10: if (op_type == MTS_READ) { mii_op = (d->mii_ctrl & I8255X_MDI_OP_MASK) >> I8255X_MDI_OP_SHIFT; if (mii_op == MII_OPCODE_READ) { d->mii_ctrl &= ~I8255X_MDI_DATA_MASK; d->mii_ctrl |= mii_reg_read(d); } *data = d->mii_ctrl | I8255X_MDI_R; } else { d->mii_ctrl = *data; mii_op = (d->mii_ctrl & I8255X_MDI_OP_MASK) >> I8255X_MDI_OP_SHIFT; if (mii_op == MII_OPCODE_WRITE) mii_reg_write(d); } break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,d->name, "read access to unknown offset=0x%x, " "pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name, "write access to unknown offset=0x%x, pc=0x%llx, " "val=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),*data,op_size); } #endif } EEPRO_UNLOCK(d); return NULL; } /* Handle the RX ring */ static int dev_i8255x_handle_rxring(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, struct i8255x_data *d) { int res = FALSE; EEPRO_LOCK(d); if (d->ru_state == RU_STATE_READY) res = dev_i8255x_store_rx_pkt(d,pkt,pkt_len); EEPRO_UNLOCK(d); return(res); } /* * pci_i8255x_read() * * Read a PCI register. */ static m_uint32_t pci_i8255x_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { struct i8255x_data *d = dev->priv_data; #if DEBUG_PCI_REGS EEPRO_LOG(d,"read PCI register 0x%x\n",reg); #endif switch (reg) { case 0x00: return((I8255X_PCI_PRODUCT_ID << 16) | I8255X_PCI_VENDOR_ID); case PCI_REG_BAR0: return(d->dev->phys_addr); case 0x0c: return(0x4000); default: return(0); } } /* * pci_i8255x_write() * * Write a PCI register. */ static void pci_i8255x_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct i8255x_data *d = dev->priv_data; #if DEBUG_PCI_REGS EEPRO_LOG(d,"write PCI register 0x%x, value 0x%x\n",reg,value); #endif switch(reg) { case PCI_REG_BAR0: vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); EEPRO_LOG(d,"registers are mapped at 0x%x\n",value); break; } } /* * dev_i8255x_init() */ struct i8255x_data * dev_i8255x_init(vm_instance_t *vm,char *name,int interface_type, struct pci_bus *pci_bus,int pci_device,int irq) { struct i8255x_data *d; struct pci_device *pci_dev; struct vdevice *dev; /* Allocate the private data structure for I8255X */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"%s (i8255x): out of memory\n",name); return NULL; } memset(d,0,sizeof(*d)); pthread_mutex_init(&d->lock,NULL); /* Add as PCI device */ pci_dev = pci_dev_add(pci_bus,name, I8255X_PCI_VENDOR_ID,I8255X_PCI_PRODUCT_ID, pci_device,0,irq, d,NULL,pci_i8255x_read,pci_i8255x_write); if (!pci_dev) { fprintf(stderr,"%s (i8255x): unable to create PCI device.\n",name); goto err_pci_dev; } /* Create the device itself */ if (!(dev = dev_create(name))) { fprintf(stderr,"%s (i8255x): unable to create device.\n",name); goto err_dev; } d->name = name; d->vm = vm; d->pci_dev = pci_dev; d->dev = dev; dev->phys_addr = 0; dev->phys_len = 0x10000; dev->handler = dev_i8255x_access; dev->priv_data = d; return(d); err_dev: pci_dev_remove(pci_dev); err_pci_dev: free(d); return NULL; } /* Remove an Intel i8255x device */ void dev_i8255x_remove(struct i8255x_data *d) { if (d != NULL) { pci_dev_remove(d->pci_dev); vm_unbind_device(d->vm,d->dev); cpu_group_rebuild_mts(d->vm->cpu_group); free(d->dev); free(d); } } /* Bind a NIO to an Intel i8255x device */ int dev_i8255x_set_nio(struct i8255x_data *d,netio_desc_t *nio) { /* check that a NIO is not already bound */ if (d->nio != NULL) return(-1); d->nio = nio; netio_rxl_add(nio,(netio_rx_handler_t)dev_i8255x_handle_rxring,d,NULL); return(0); } /* Unbind a NIO from an Intel i8255x device */ void dev_i8255x_unset_nio(struct i8255x_data *d) { if (d->nio != NULL) { netio_rxl_remove(d->nio); d->nio = NULL; } } dynamips-0.2.14/common/dev_i8255x.h000066400000000000000000000013771241034141600166710ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __DEV_I8255X_H__ #define __DEV_I8255X_H__ #include #include "utils.h" #include "cpu.h" #include "vm.h" #include "device.h" #include "net_io.h" /* Generic Intel i8255x initialization code */ struct i8255x_data * dev_i8255x_init(vm_instance_t *vm,char *name,int interface_type, struct pci_bus *pci_bus,int pci_device,int irq); /* Remove an Intel i8255x device */ void dev_i8255x_remove(struct i8255x_data *d); /* Bind a NIO to an Intel i8255x device */ int dev_i8255x_set_nio(struct i8255x_data *d,netio_desc_t *nio); /* Unbind a NIO from an Intel i8255x device */ void dev_i8255x_unset_nio(struct i8255x_data *d); #endif dynamips-0.2.14/common/dev_lxt970a.c000066400000000000000000000003521241034141600171200ustar00rootroot00000000000000/* * 10/100 Mbps Ethernet PHY. * * Based on: * Intel® LXT970A * Dual-Speed Fast Ethernet Transceiver * Order Number: 249099-001 * January 2001 * Based on: * IEEE Std 802.3-2008, Section 2 */ #include "dev_lxt970a.h" dynamips-0.2.14/common/dev_lxt970a.h000066400000000000000000000265251241034141600171370ustar00rootroot00000000000000/* * 10/100 Mbps Ethernet PHY. * * Based on: * Intel® LXT970A * Dual-Speed Fast Ethernet Transceiver * Order Number: 249099-001 * January 2001 * Based on: * IEEE Std 802.3-2008, Section 2 */ #ifndef __DEV_LXT970A_H__ #define __DEV_LXT970A_H__ /* Abreviations: RW=x - Read/Write with default value x RO=x - Read Only with default value x [xx] - default value determined by pins xx SC - Self Clearing LH - Latching High (remains High until read, and then returns to Low) LL - Latching Low (remains Low until read, and then returns to High) IGN - IGNored on certain conditions RTOR - Related To Other Register Pin states used for the default values: MF0 = VMF2 or VMF3 (Enabled) MF1 = VMF1 or VMF4 (Disabled) MF2 = VMF2 or VMF3 (Enabled) MF3 = VMF2 or VMF3 (Enabled) MF4 = VMF2 or VMF3 (Enabled) CFG0 = High CFG1 = High TRSTE = Low FDE = High MDDIS = Low, RESET = High, PWERDWN = Low (2.4.1.2, MDIO Control) */ /* Standard MII Registers */ #define LX970A_CR 0x00 /* Table 45. Control Register (Address 0) */ #define LX970A_CR_RESET 0x8000 /* Reset (RW=0,SC) */ #define LX970A_CR_LOOP 0x4000 /* Loopback (RW=0) */ #define LX970A_CR_SPEEDSELECT 0x2000 /* Speed Selection (RW=1[CFG0],IGN) */ #define LX970A_CR_ANENABLE 0x1000 /* Auto-Negotiation Enable (RW=1[MF0]) */ #define LX970A_CR_POWERDOWN 0x0800 /* Power Down (RW=0?) */ #define LX970A_CR_ISOLATE 0x0400 /* Isolate (RW=0[TRSTE]) */ #define LX970A_CR_ANRESTART 0x0200 /* Restart Auto-Negotiation (RW=1[CFG0],SC) */ #define LX970A_CR_DUPLEXMODE 0x0100 /* Duplex Mode (RW=1[FDE],IGN) */ #define LX970A_CR_COLLISIONTEST 0x0080 /* Collision Test (RW=0,IGN) */ #define LX970A_CR_TTM 0x0070 /* Transceiver Test Mode (RO=0) */ #define LX970A_CR_MSENABLE 0x0008 /* Master-Slave Enable (RO=0) */ #define LX970A_CR_MSVALUE 0x0004 /* Master-Slave Value (RO=0) */ #define LX970A_CR_RESERVED 0x0003 /* Reserved (RW=0) */ #define LX970A_CR_RO_MASK 0x007C #define LX970A_CR_RW_MASK 0xFF83 #define LX970A_CR_DEFAULT 0x3300 #define LX970A_SR 0x01 /* Table 46. Status Register (Address 1) */ #define LX970A_SR_100T4 0x8000 /* 100BASE-T4 (RO=0) */ #define LX970A_SR_100TX_FD 0x4000 /* 100BASE-X full-duplex (RO=1) */ #define LX970A_SR_100TX_HD 0x2000 /* 100BASE-X hald-duplex (RO=1) */ #define LX970A_SR_10T_FD 0x1000 /* 10 Mb/s full-duplex (RO=1) */ #define LX970A_SR_10T_HD 0x0800 /* 10 Mb/s half-duplex (RO=1) */ #define LX970A_SR_100T2_FD 0x0400 /* 100BASE-T2 full-duplex (RO=0) */ #define LX970A_SR_100T2_HD 0x0200 /* 100BASE-T2 half-duplex (RO=0) */ #define LX970A_SR_RESERVED 0x0100 /* Reserved (RO=0) */ #define LX970A_SR_MSCFGFAULT 0x0080 /* Master-Slave Configuration Fault (RO=0) */ #define LX970A_SR_MFPS 0x0040 /* MF Preamble Suppression (RO=0) */ #define LX970A_SR_ANCOMPLETE 0x0020 /* Auto-Neg. Complete (RO=0) */ #define LX970A_SR_REMOTEFAULT 0x0010 /* Remote Fault (RO=0,LH) */ #define LX970A_SR_ANABILITY 0x0008 /* Auto-Neg. Ability (RO=1) */ #define LX970A_SR_LINKSTATUS 0x0004 /* Link Status (RO=0,LL) */ #define LX970A_SR_JABBERDETECT 0x0002 /* Jabber Detect (10BASE-T Only) (RO=0,LH) */ #define LX970A_SR_EXTCAPABILITY 0x0001 /* Extended Capability (RO=1) */ #define LX970A_SR_RO_MASK 0xFFFF #define LX970A_SR_RW_MASK 0x0000 #define LX970A_SR_DEFAULT 0x7809 /* PHY ID number: Intel has OUI=00207Bh and ROUI=DE0400h (OUI with bits reversed) (ROUI << 10) & FFFFFFFFh = 78100000h */ #define LX970A_PIR1 0x02 /* Table 47. PHY Identification Register 1 (Address 2) */ #define LX970A_PIR1_PIN 0xFFFF /* PHY ID Number (RO=7810h) */ #define LX970A_PIR1_RO_MASK 0xFFFF #define LX970A_PIR1_RW_MASK 0x0000 #define LX970A_PIR1_DEFAULT 0x7810 #define LX970A_PIR2 0x03 /* Table 48. PHY Identification Register 2 (Address 3) */ #define LX970A_PIR2_PIN 0xFC00 /* PHY ID number (RO=000000b) */ #define LX970A_PIR2_MANMODELNUM 0x03F0 /* Manufacturer’s model number (RO=000000b) */ #define LX970A_PIR2_MANREVNUM 0x000F /* Manufacturer’s revision number (RO=0011b) */ #define LX970A_PIR2_RO_MASK 0xFFFF #define LX970A_PIR2_RW_MASK 0x0000 #define LX970A_PIR2_DEFAULT 0x0003 #define LX970A_ANAR 0x04 /* Table 49. Auto Negotiation Advertisement Register (Address 4) */ #define LX970A_ANAR_NEXTPAGE 0x8000 /* Next Page (RO=0) */ #define LX970A_ANAR_RESERVED_1 0x4000 /* Reserved (RO=0) */ #define LX970A_ANAR_REMOTEFAULT 0x2000 /* Remote Fault (RW=0) */ #define LX970A_ANAR_RESERVED_2 0x1800 /* Reserved (RW=0) */ #define LX970A_ANAR_PAUSE 0x0400 /* Pause (RW=0) */ #define LX970A_ANAR_100T4 0x0200 /* 100BASE-T4 (RW=0) */ #define LX970A_ANAR_100TX_FD 0x0100 /* 100BASE-TX (RW=1[FDE,MF4]) */ #define LX970A_ANAR_100TX_HD 0x0080 /* 100BASE-TX (RW=1[MF4]) */ #define LX970A_ANAR_10T_FD 0x0040 /* 10BASE-T full-duplex (RW=1[FDE,CFG1]) */ #define LX970A_ANAR_10T_HD 0x0020 /* 10BASE-T (RW=1[CFG1]) */ #define LX970A_ANAR_SF 0x001F /* Selector Field, S<4:0> (RW=00001b) */ #define LX970A_ANAR_RO_MASK 0xC000 #define LX970A_ANAR_RW_MASK 0x3FFF #define LX970A_ANAR_DEFAULT 0x01E1 /* auxiliary */ #define LX970A_ANAR_SF_IEEE8023 0x0001 /* <00001> = IEEE 802.3 */ #define LX970A_ANLPAR 0x05 /* Table 50. Auto Negotiation Link Partner Ability Register (Address 5) */ #define LX970A_ANLPAR_NEXTPAGE 0x8000 /* Next Page (RO) */ #define LX970A_ANLPAR_ACKNOWLEDGE 0x4000 /* Acknowledge (RO) */ #define LX970A_ANLPAR_REMOTEFAULT 0x2000 /* Remote Fault (RO) */ #define LX970A_ANLPAR_RESERVED 0x1800 /* Reserved (RO) */ #define LX970A_ANLPAR_PAUSE 0x0400 /* Pause (RO) */ #define LX970A_ANLPAR_100T4 0x0200 /* 100BASE-T4 (RO) */ #define LX970A_ANLPAR_100TX_FD 0x0100 /* 100BASE-TX full-duplex (RO) */ #define LX970A_ANLPAR_100TX_HD 0x0080 /* 100BASE-TX (RO) */ #define LX970A_ANLPAR_10T_FD 0x0040 /* 10BASE-T full-duplex (RO) */ #define LX970A_ANLPAR_10T_HD 0x0020 /* 10BASE-T (RO) */ #define LX970A_ANLPAR_SF 0x001F /* Selector Field S[4:0] (RO) */ #define LX970A_ANLPAR_RO_MASK 0xFFFF #define LX970A_ANLPAR_RW_MASK 0x0000 #define LX970A_ANLPAR_DEFAULT 0x0000 /* auxiliary */ #define LX970A_ANLPAR_SF_IEEE8023 0x0001 /* <00001> = IEEE 802.3 */ #define LX970A_ANE 0x06 /* Table 51. Auto Negotiation Expansion (Address 6) */ #define LX970A_ANE_RESERVED 0xFFE0 /* Reserved (RO=0) */ #define LX970A_ANE_PDETECTFAULT 0x0010 /* Parallel Detection Fault (RO=0,LH) */ #define LX970A_ANE_LPNPA 0x0008 /* Link Partner Next Page Able (RO=0) */ #define LX970A_ANE_NPA 0x0004 /* Next Page Able (RO=0) */ #define LX970A_ANE_PR 0x0002 /* Page Received (RO=0,LH) */ #define LX970A_ANE_LPANA 0x0001 /* Link Partner Auto Neg Able (RO=0) */ #define LX970A_ANE_RO_MASK 0xFFFF #define LX970A_ANE_RW_MASK 0x0000 #define LX970A_ANE_DEFAULT 0x0000 /* Vendor Specific MII Registers */ #define LX970A_MR 0x10 /* Table 52. Mirror Register (Address 16, Hex 10) */ #define LX970A_MR_USERDEFINED 0xFFFF /* User Defined (RW=0) */ #define LX970A_MR_RO_MASK 0x0000 #define LX970A_MR_RW_MASK 0xFFFF #define LX970A_MR_DEFAULT 0x0000 #define LX970A_IER 0x11 /* Table 53. Interrupt Enable Register (Address 17, Hex 11) */ #define LX970A_IER_RESERVED 0xFFF0 /* Reserved (RO=0) */ #define LX970A_IER_MIIDRVLVL 0x0008 /* MIIDRVLVL (RW=0) */ #define LX970A_IER_LNK_CRITERIA 0x0004 /* LNK CRITERIA (RW=0) */ #define LX970A_IER_INTEN 0x0002 /* INTEN (RW=0) */ #define LX970A_IER_TINT 0x0001 /* TINT (RW=0,IGN) */ #define LX970A_IER_RO_MASK 0xFFF0 #define LX970A_IER_RW_MASK 0x000F #define LX970A_IER_DEFAULT 0x0000 #define LX970A_ISR 0x12 /* Table 54. Interrupt Status Register (Address 18, Hex 12) */ #define LX970A_ISR_MINT 0x8000 /* MINT (RO=0?) */ #define LX970A_ISR_XTALOK 0x4000 /* XTALOK (RO=0) */ #define LX970A_ISR_RESERVED 0x3FFF /* Reserved (RO=0) */ #define LX970A_ISR_RO_MASK 0xFFFF #define LX970A_ISR_RW_MASK 0x0000 #define LX970A_ISR_DEFAULT 0x0000 #define LX970A_CFGR 0x13 /* Table 55. Configuration Register (Address 19, Hex 13) */ #define LX970A_CFGR_RESERVED_1 0x8000 /* Reserved (RO=0) */ #define LX970A_CFGR_TXMITTEST 0x4000 /* Txmit Test (100BASE-TX) (RW=0,RTOR) */ #define LX970A_CFGR_REPEATERMODE 0x2000 /* Repeater Mode (RW=0[MF1]) */ #define LX970A_CFGR_MDIOINT 0x1000 /* MDIO_INT (RW=0,IGN) */ #define LX970A_CFGR_TPLOOPBACK 0x0800 /* TP Loopback (10BASE-T) (RW=0) */ #define LX970A_CFGR_SQE 0x0400 /* SQE (10BASE-T) (RW=0) */ #define LX970A_CFGR_JABBER 0x0200 /* Jabber (10BASE-T) (RW=0) */ #define LX970A_CFGR_LINKTEST 0x0100 /* Link Test (10BASE-T) (RW=0[CFG1,MF0]) */ #define LX970A_CFGR_LEDC 0x00C0 /* LEDC Programming bits (RW=0) */ #define LX970A_CFGR_ATXC 0x0020 /* Advance TX Clock (RW=0) */ #define LX970A_CFGR_5BS_4BN 0x0010 /* 5B Symbol/(100BASE-X only) 4B Nibble (RW=1[MF2]) */ #define LX970A_CFGR_SCRAMBLER 0x0008 /* Scrambler (100BASE-X only) (RW=1[MF3]) */ #define LX970A_CFGR_100FX 0x0004 /* 100BASE-FX (RW=1[MF4,MF0]) */ #define LX970A_CFGR_RESERVED_2 0x0002 /* Reserved (RO=0) */ #define LX970A_CFGR_TD 0x0001 /* Transmit Disconnect (RW=0) */ #define LX970A_CFGR_RO_MASK 0x8002 #define LX970A_CFGR_RW_MASK 0x7FFD #define LX970A_CFGR_DEFAULT 0x0014 /* auxiliary */ #define LX970A_CFGR_LEDC_COLLISION 0x0000 /* 0 0 LEDC indicates collision */ #define LX970A_CFGR_LEDC_OFF 0x0040 /* 0 1 LEDC is off */ #define LX970A_CFGR_LEDC_ACTIVITY 0x0080 /* 1 0 LEDC indicates activity */ #define LX970A_CFGR_LEDC_ALWAYSON 0x00C0 /* 1 1 LEDC is continuously on (for diagnostic use) */ #define LX970A_CSR 0x14 /* Table 56. Chip Status Register (Address 20, Hex 14) */ #define LX970A_CSR_RESERVED_1 0xC000 /* Reserved (RO=0?) */ #define LX970A_CSR_LINK 0x2000 /* Link (RO=0,RTOR) */ #define LX970A_CSR_DUPLEXMODE 0x1000 /* Duplex Mode (RO=1[FDE],RTOR) */ #define LX970A_CSR_SPEED 0x0800 /* Speed (RO=1[CFG0],RTOR) */ #define LX970A_CSR_RESERVED_2 0x0400 /* Reserved (RO=0?) */ #define LX970A_CSR_ANCOMPLETE 0x0200 /* Auto-Negotiation Complete (RO=0,LH,RTOR) */ #define LX970A_CSR_PAGERECEIVED 0x0100 /* Page Received (RO=0,LH,RTOR) */ #define LX970A_CSR_RESERVED_3 0x00C0 /* Reserved (RO=0) */ #define LX970A_CSR_RESERVED_4 0x0038 /* Reserved (RO=0?) */ #define LX970A_CSR_LOWVOLTAGE 0x0004 /* Low-Voltage (RO=0?) */ #define LX970A_CSR_RESERVED_5 0x0003 /* Reserved (RO=0?) */ #define LX970A_CSR_RO_MASK 0xFFFF #define LX970A_CSR_RW_MASK 0x0000 #define LX970A_CSR_DEFAULT 0x1800 #endif dynamips-0.2.14/common/dev_mpc860.c000066400000000000000000001732711241034141600167400ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * MPC860 internal devices. */ #include #include #include #include "utils.h" #include "net.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net_io.h" #include "dev_lxt970a.h" #include "dev_mpc860.h" /* Debugging flags */ #define DEBUG_ACCESS 0 #define DEBUG_UNKNOWN 1 #define DEBUG_IDMA 0 #define DEBUG_SPI 0 #define DEBUG_SCC 0 #define DEBUG_FEC 0 #define MPC860_TXRING_PASS_COUNT 16 /* Dual-Port RAM */ #define MPC860_DPRAM_OFFSET 0x2000 #define MPC860_DPRAM_SIZE 0x2000 #define MPC860_DPRAM_END (MPC860_DPRAM_OFFSET + MPC860_DPRAM_SIZE) /* MPC860 registers */ #define MPC860_REG_SWSR 0x000e /* Software Service Register */ #define MPC860_REG_SIPEND 0x0010 /* SIU Interrupt Pending Register */ #define MPC860_REG_SIMASK 0x0014 /* SIU Interrupt Mask Register */ #define MPC860_REG_PIPR 0x00f0 /* PCMCIA Interface Input Pins Reg. */ #define MPC860_REG_TBSCR 0x0200 /* Timebase Status and Control Reg. */ #define MPC860_REG_PISCR 0x0240 /* Periodic Int. Status and Ctrl Reg. */ #define MPC860_REG_IDSR1 0x0910 /* IDMA1 Status Register */ #define MPC860_REG_IDMR1 0x0914 /* IDMA1 Mask Register */ #define MPC860_REG_IDSR2 0x0918 /* IDMA2 Status Register */ #define MPC860_REG_IDMR2 0x091c /* IDMA2 Mask Register */ #define MPC860_REG_CICR 0x0940 /* CPM Int Config Register */ #define MPC860_REG_CIPR 0x0944 /* CPM Int Pending Register */ #define MPC860_REG_CIMR 0x0948 /* CPM Int Mask Register */ #define MPC860_REG_PCSO 0x0964 /* Port C Special Options Register */ #define MPC860_REG_PCDAT 0x0966 /* Port C Data Register */ #define MPC860_REG_CPCR 0x09c0 /* CP Command Register */ #define MPC860_REG_SCC_BASE 0x0a00 /* SCC register base */ #define MPC860_REG_SPMODE 0x0aa0 /* SPI Mode Register */ #define MPC860_REG_SPIE 0x0aa6 /* SPI Event Register */ #define MPC860_REG_SPIM 0x0aaa /* SPI Mask Register */ #define MPC860_REG_SPCOM 0x0aad /* SPI Command Register */ #define MPC860_REG_PBDAT 0x0ac4 /* Port B Data Register */ #define MPC860_REG_FEC_BASE 0x0e00 /* FEC register base */ #define MPC860_REG_FEC_END 0x0f84 /* FEC register end */ /* ======================================================================== */ /* CICR (CPM Interrupt Config Register) */ #define MPC860_CICR_IRL_MASK 0x0000E000 /* Interrupt Level */ #define MPC860_CICR_IRL_SHIFT 13 #define MPC860_CICR_IEN 0x00000080 /* Interrupt Enable */ /* CIPR (CPM Interrupt Pending Register) */ #define MPC860_CIPR_PC15 0x80000000 #define MPC860_CIPR_SCC1 0x40000000 #define MPC860_CIPR_SCC2 0x20000000 #define MPC860_CIPR_SCC3 0x10000000 #define MPC860_CIPR_SCC4 0x08000000 #define MPC860_CIPR_PC14 0x04000000 #define MPC860_CIPR_TIMER1 0x02000000 #define MPC860_CIPR_PC13 0x01000000 #define MPC860_CIPR_PC12 0x00800000 #define MPC860_CIPR_SDMA 0x00400000 #define MPC860_CIPR_IDMA1 0x00200000 #define MPC860_CIPR_IDMA2 0x00100000 #define MPC860_CIPR_TIMER2 0x00040000 #define MPC860_CIPR_RTT 0x00020000 #define MPC860_CIPR_I2C 0x00010000 #define MPC860_CIPR_PC11 0x00008000 #define MPC860_CIPR_PC10 0x00004000 #define MPC860_CIPR_TIMER3 0x00001000 #define MPC860_CIPR_PC9 0x00000800 #define MPC860_CIPR_PC8 0x00000400 #define MPC860_CIPR_PC7 0x00000200 #define MPC860_CIPR_TIMER4 0x00000080 #define MPC860_CIPR_PC6 0x00000040 #define MPC860_CIPR_SPI 0x00000020 #define MPC860_CIPR_SMC1 0x00000010 #define MPC860_CIPR_SMC2 0x00000008 #define MPC860_CIPR_PC5 0x00000004 #define MPC860_CIPR_PC4 0x00000002 /* CPCR (CP Command Register) */ #define MPC860_CPCR_RST 0x8000 /* CP reset command */ #define MPC860_CPCR_FLG 0x0001 /* Command Semaphore Flag */ #define MPC860_CPCR_CHNUM_MASK 0x00F0 /* Channel Number */ #define MPC860_CPCR_CHNUM_SHIFT 4 #define MPC860_CPCR_OPCODE_MASK 0x0F00 /* Opcode */ #define MPC860_CPCR_OPCODE_SHIFT 8 /* CP channels */ #define MPC860_CHAN_SCC1 0x00 #define MPC860_CHAN_I2C_IDMA1 0x01 #define MPC860_CHAN_SCC2 0x04 #define MPC860_CHAN_SPI_IDMA2_RT 0x05 #define MPC860_CHAN_SCC3 0x08 #define MPC860_CHAN_SMC1 0x09 #define MPC860_CHAN_SCC4 0x0c #define MPC860_CHAN_SMC2_PIP 0x0d /* ======================================================================== */ /* IDMA Status Register */ #define MPC860_IDSR_OB 0x0001 /* Out of Buffers */ #define MPC860_IDSR_DONE 0x0002 /* Buffer chain done */ #define MPC860_IDSR_AD 0x0004 /* Auxiliary done */ /* Offsets of IDMA channels (from DPRAM base) */ #define MPC860_IDMA1_BASE 0x1cc0 #define MPC860_IDMA2_BASE 0x1dc0 /* Size of an IDMA buffer descriptor */ #define MPC860_IDMA_BD_SIZE 16 /* IDMA Buffer Descriptor Control Word */ #define MPC860_IDMA_CTRL_V 0x8000 /* Valid Bit */ #define MPC860_IDMA_CTRL_W 0x2000 /* Wrap */ #define MPC860_IDMA_CTRL_I 0x1000 /* Interrupt for this BD */ #define MPC860_IDMA_CTRL_L 0x0800 /* Last buffer of chain */ #define MPC860_IDMA_CTRL_CM 0x0200 /* Continuous mode */ /* IDMA buffer descriptor */ struct mpc860_idma_bd { m_uint16_t offset; /* Offset in DPRAM memory */ m_uint16_t ctrl; /* Control Word */ m_uint8_t dfcr,sfcr; /* Src/Dst Function code registers */ m_uint32_t buf_len; /* Buffer Length */ m_uint32_t src_bp; /* Source buffer pointer */ m_uint32_t dst_bp; /* Destination buffer pointer */ }; /* ======================================================================== */ /* SPI Mode Register (SPMODE) */ #define MPC860_SPMODE_LOOP 0x4000 /* Loop mode */ #define MPC860_SPMODE_CI 0x2000 /* Clock Invert */ #define MPC860_SPMODE_CP 0x1000 /* Clock Phase */ #define MPC860_SPMODE_DIV16 0x0800 /* Divide by 16 (SPI clock generator) */ #define MPC860_SPMODE_REV 0x0400 /* Reverse Data */ #define MPC860_SPMODE_MS 0x0200 /* Master/Slave mode select */ #define MPC860_SPMODE_EN 0x0100 /* Enable SPI */ #define MPC860_SPMODE_LEN_MASK 0x00F0 /* Data length (4 - 11 bits) */ #define MPC860_SPMODE_LEN_SHIFT 4 #define MPC860_SPMODE_PM_MASK 0x000F /* Prescale Modulus Select */ /* SPI Event/Mask Registers (SPIE/SPIM) */ #define MPC860_SPIE_MME 0x20 /* MultiMaster Error */ #define MPC860_SPIE_TXE 0x10 /* TX Error */ #define MPC860_SPIE_BSY 0x04 /* Busy (no RX buffer available) */ #define MPC860_SPIE_TXB 0x02 /* TX Buffer */ #define MPC860_SPIE_RXB 0x01 /* RX Buffer */ /* SPI Command Register (SPCOM) */ #define MPC860_SPCOM_STR 0x80 /* Start Transmit */ /* Offsets of SPI parameters (from DPRAM base) */ #define MPC860_SPI_BASE 0x1d80 #define MPC860_SPI_BASE_ADDR 0x1dac /* Size of an SPI buffer descriptor */ #define MPC860_SPI_BD_SIZE 8 /* SPI RX Buffer Descriptor Control Word */ #define MPC860_SPI_RXBD_CTRL_E 0x8000 /* Empty */ #define MPC860_SPI_RXBD_CTRL_W 0x2000 /* Wrap */ #define MPC860_SPI_RXBD_CTRL_I 0x1000 /* Interrupt */ #define MPC860_SPI_RXBD_CTRL_L 0x0800 /* Last */ #define MPC860_SPI_RXBD_CTRL_CM 0x0200 /* Continuous Mode */ #define MPC860_SPI_RXBD_CTRL_OV 0x0002 /* Overrun */ #define MPC860_SPI_RXBD_CTRL_ME 0x0001 /* MultiMaster Error */ /* SPI TX Buffer Descriptor Control Word */ #define MPC860_SPI_TXBD_CTRL_R 0x8000 /* Ready Bit */ #define MPC860_SPI_TXBD_CTRL_W 0x2000 /* Wrap */ #define MPC860_SPI_TXBD_CTRL_I 0x1000 /* Interrupt */ #define MPC860_SPI_TXBD_CTRL_L 0x0800 /* Last */ #define MPC860_SPI_TXBD_CTRL_CM 0x0200 /* Continuous Mode */ #define MPC860_SPI_TXBD_CTRL_UN 0x0002 /* Underrun */ #define MPC860_SPI_TXBD_CTRL_ME 0x0001 /* MultiMaster Error */ /* SPI buffer descriptor */ struct mpc860_spi_bd { m_uint16_t offset; /* Offset in DPRAM memory */ m_uint16_t ctrl; /* Control Word */ m_uint16_t buf_len; /* Buffer Length */ m_uint32_t bp; /* Buffer Pointer */ }; /* ======================================================================== */ /* Number of SCC channels */ #define MPC860_SCC_NR_CHAN 4 /* Maximum buffer size for SCC */ #define MPC860_SCC_MAX_PKT_SIZE 32768 /* Offsets of SCC channels (from DPRAM base) */ #define MPC860_SCC1_BASE 0x1c00 #define MPC860_SCC2_BASE 0x1d00 #define MPC860_SCC3_BASE 0x1e00 #define MPC860_SCC4_BASE 0x1f00 /* GSMR Low register */ #define MPC860_GSMRL_MODE_MASK 0x0000000F /* SCC Modes */ #define MPC860_SCC_MODE_HDLC 0x00 #define MPC860_SCC_MODE_UART 0x04 #define MPC860_SCC_MODE_BISYNC 0x08 #define MPC860_SCC_MODE_ETH 0x0c /* SCC Event (SCCE) register */ #define MPC860_SCCE_TXB 0x0002 /* TX buffer sent */ #define MPC860_SCCE_RXB 0x0001 /* RX buffer ready */ /* Size of an SCC buffer descriptor */ #define MPC860_SCC_BD_SIZE 8 /* SCC RX Buffer Descriptor Control Word */ #define MPC860_SCC_RXBD_CTRL_E 0x8000 /* Empty */ #define MPC860_SCC_RXBD_CTRL_W 0x2000 /* Wrap */ #define MPC860_SCC_RXBD_CTRL_I 0x1000 /* Interrupt */ #define MPC860_SCC_RXBD_CTRL_L 0x0800 /* Last */ #define MPC860_SCC_RXBD_CTRL_F 0x0400 /* First */ #define MPC860_SCC_RXBD_CTRL_CM 0x0200 /* Continuous Mode */ #define MPC860_SCC_RXBD_CTRL_OV 0x0002 /* Overrun */ /* SCC TX Buffer Descriptor Control Word */ #define MPC860_SCC_TXBD_CTRL_R 0x8000 /* Ready Bit */ #define MPC860_SCC_TXBD_CTRL_W 0x2000 /* Wrap */ #define MPC860_SCC_TXBD_CTRL_I 0x1000 /* Interrupt */ #define MPC860_SCC_TXBD_CTRL_L 0x0800 /* Last */ #define MPC860_SCC_TXBD_CTRL_TC 0x0400 /* Send TX CRC */ #define MPC860_SCC_TXBD_CTRL_CM 0x0200 /* Continuous Mode */ #define MPC860_SCC_TXBD_CTRL_UN 0x0002 /* Underrun */ /* SCC buffer descriptor */ struct mpc860_scc_bd { m_uint16_t offset; /* Offset in DPRAM memory */ m_uint16_t ctrl; /* Control Word */ m_uint16_t buf_len; /* Buffer Length */ m_uint32_t bp; /* Buffer Pointer */ }; /* ======================================================================== */ /* FEC Ethernet Control Register */ #define MPC860_ECNTRL_FEC_PIN_MUX 0x00000004 /* FEC enable */ #define MPC860_ECNTRL_ETHER_EN 0x00000002 /* Ethernet Enable */ #define MPC860_ECNTRL_RESET 0x00000001 /* Reset Ethernet controller */ /* FEC Interrupt Vector Register */ #define MPC860_IVEC_ILEVEL_MASK 0xE0000000 /* Interrupt Level */ #define MPC860_IVEC_ILEVEL_SHIFT 29 /* FEC Interrupt Event Register */ #define MPC860_IEVENT_HBERR 0x80000000 /* Hearbeat Error */ #define MPC860_IEVENT_BABR 0x40000000 /* Babbling Receive Error */ #define MPC860_IEVENT_BABT 0x20000000 /* Babbling Transmit Error */ #define MPC860_IEVENT_GRA 0x10000000 /* Graceful Stop Complete */ #define MPC860_IEVENT_TFINT 0x08000000 /* Transmit Frame Interrupt */ #define MPC860_IEVENT_TXB 0x04000000 /* Transmit Buffer Interrupt */ #define MPC860_IEVENT_RFINT 0x02000000 /* Receive Frame Interrupt */ #define MPC860_IEVENT_RXB 0x01000000 /* Receive Buffer Interrupt */ #define MPC860_IEVENT_MII 0x00800000 /* MII Interrupt */ #define MPC860_IEVENT_EBERR 0x00400000 /* Ethernet Bus Error */ /* MII data register */ #define MPC860_MII_OP_MASK 0x30000000 /* Opcode (10b:read,11b:write) */ #define MPC860_MII_OP_SHIFT 28 #define MPC860_MII_PHY_MASK 0x0F800000 /* PHY device */ #define MPC860_MII_PHY_SHIFT 23 #define MPC860_MII_REG_MASK 0x007C0000 /* PHY register */ #define MPC860_MII_REG_SHIFT 18 /* Size of an FEC buffer descriptor */ #define MPC860_FEC_BD_SIZE 8 /* Maximum packet size for FEC */ #define MPC860_FEC_MAX_PKT_SIZE 2048 /* FEC RX Buffer Descriptor Control Word */ #define MPC860_FEC_RXBD_CTRL_E 0x8000 /* Empty */ #define MPC860_FEC_RXBD_CTRL_RO1 0x4000 /* For software use */ #define MPC860_FEC_RXBD_CTRL_W 0x2000 /* Wrap */ #define MPC860_FEC_RXBD_CTRL_RO2 0x1000 /* For software use */ #define MPC860_FEC_RXBD_CTRL_L 0x0800 /* Last */ #define MPC860_FEC_RXBD_CTRL_M 0x0100 /* Miss */ #define MPC860_FEC_RXBD_CTRL_BC 0x0080 /* Broadcast DA */ #define MPC860_FEC_RXBD_CTRL_MC 0x0040 /* Multicast DA */ #define MPC860_FEC_RXBD_CTRL_LG 0x0020 /* RX Frame length violation */ #define MPC860_FEC_RXBD_CTRL_NO 0x0010 /* RX non-octet aligned frame */ #define MPC860_FEC_RXBD_CTRL_SH 0x0008 /* Short Frame */ #define MPC860_FEC_RXBD_CTRL_CR 0x0004 /* RX CRC Error */ #define MPC860_FEC_RXBD_CTRL_OV 0x0002 /* Overrun */ #define MPC860_FEC_RXBD_CTRL_TR 0x0001 /* Truncated Frame */ /* FEC TX Buffer Descriptor Control Word */ #define MPC860_FEC_TXBD_CTRL_R 0x8000 /* Ready Bit */ #define MPC860_FEC_TXBD_CTRL_TO1 0x4000 /* For software use */ #define MPC860_FEC_TXBD_CTRL_W 0x2000 /* Wrap */ #define MPC860_FEC_TXBD_CTRL_TO2 0x1000 /* For software use */ #define MPC860_FEC_TXBD_CTRL_L 0x0800 /* Last */ #define MPC860_FEC_TXBD_CTRL_TC 0x0400 /* Send TX CRC */ #define MPC860_FEC_TXBD_CTRL_DEF 0x0200 /* Defer Indication */ #define MPC860_FEC_TXBD_CTRL_HB 0x0100 /* Heartbeat Error */ #define MPC860_FEC_TXBD_CTRL_LC 0x0080 /* Late Collision */ #define MPC860_FEC_TXBD_CTRL_RL 0x0040 /* Retranmission Limit */ #define MPC860_FEC_TXBD_CTRL_UN 0x0002 /* Underrun */ #define MPC860_FEC_TXBD_CTRL_CSL 0x0001 /* Carrier Sense Lost */ /* FEC buffer descriptor */ struct mpc860_fec_bd { m_uint32_t bd_addr; /* Address in external memory */ m_uint16_t ctrl; /* Control Word */ m_uint16_t buf_len; /* Buffer Length */ m_uint32_t bp; /* Buffer Pointer */ }; /* ======================================================================== */ struct mpc860_scc_chan { netio_desc_t *nio; /* General SCC mode register (high and low parts) */ m_uint32_t gsmr_hi,gsmr_lo; /* Protocol-Specific mode register */ m_uint32_t psmr; /* SCC Event and Mask registers */ m_uint16_t scce,sccm; /* TX packet */ u_char tx_pkt[MPC860_SCC_MAX_PKT_SIZE]; }; /* MPC860 private data */ struct mpc860_data { char *name; vm_obj_t vm_obj; struct vdevice dev; struct pci_device *pci_dev; vm_instance_t *vm; /* SIU Interrupt Pending Register and Interrupt Mask Register */ m_uint32_t sipend,simask; /* CPM Interrupt Configuration Register */ m_uint32_t cicr; /* CPM Interrupt Pending Register and Interrupt Mask Register */ m_uint32_t cipr,cimr; /* IDMA status and mask registers */ m_uint8_t idsr[2],idmr[2]; /* Port B Data Register */ m_uint32_t pbdat,pcdat; /* SPI callback for TX data */ mpc860_spi_tx_callback_t spi_tx_callback; void *spi_user_arg; /* SCC channels */ struct mpc860_scc_chan scc_chan[MPC860_SCC_NR_CHAN]; /* FEC (Fast Ethernet Controller) */ m_uint32_t fec_rdes_start,fec_xdes_start; m_uint32_t fec_rdes_current,fec_xdes_current; m_uint32_t fec_rbuf_size; /* FEC Interrupt Event/Mask registers */ m_uint32_t fec_ievent,fec_imask,fec_ivec; m_uint32_t fec_ecntrl; /* FEC NetIO */ netio_desc_t *fec_nio; /* FEC MII registers */ m_uint32_t fec_mii_data; m_uint16_t fec_mii_regs[32]; m_uint8_t fec_mii_last_read_reg; /* Dual-Port RAM */ m_uint8_t dpram[MPC860_DPRAM_SIZE]; }; /* Log a MPC message */ #define MPC_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* ======================================================================== */ /* DPRAM access routines */ static inline m_uint8_t dpram_r8(struct mpc860_data *d,m_uint16_t offset) { return(d->dpram[offset]); } _unused static inline void dpram_w8(struct mpc860_data *d,m_uint16_t offset, m_uint8_t val) { d->dpram[offset] = val; } static inline m_uint16_t dpram_r16(struct mpc860_data *d,m_uint16_t offset) { m_uint16_t val; val = (m_uint16_t)d->dpram[offset] << 8; val |= d->dpram[offset+1]; return(val); } static inline void dpram_w16(struct mpc860_data *d,m_uint16_t offset, m_uint16_t val) { d->dpram[offset] = val >> 8; d->dpram[offset+1] = val & 0xFF; } static inline m_uint32_t dpram_r32(struct mpc860_data *d,m_uint16_t offset) { m_uint32_t val; val = d->dpram[offset] << 24; val |= d->dpram[offset+1] << 16; val |= d->dpram[offset+2] << 8; val |= d->dpram[offset+3]; return(val); } _unused static inline void dpram_w32(struct mpc860_data *d,m_uint16_t offset, m_uint32_t val) { d->dpram[offset] = val >> 24; d->dpram[offset+1] = val >> 16; d->dpram[offset+2] = val >> 8; d->dpram[offset+3] = val; } /* ======================================================================== */ /* Update interrupt status */ static void mpc860_update_irq_status(struct mpc860_data *d) { cpu_ppc_t *cpu = CPU_PPC32(d->vm->boot_cpu); cpu->irq_pending = d->sipend & d->simask; cpu->irq_check = cpu->irq_pending; } /* Map level to SIU Interrupt Pending Register bit */ static inline u_int mpc860_get_siu_lvl(u_int level) { return(16 + ((7 - level) << 1)); } /* Update CPM interrupt status */ static void mpc860_update_cpm_int_status(struct mpc860_data *d) { u_int level,siu_bit; level = (d->cicr & MPC860_CICR_IRL_MASK) >> MPC860_CICR_IRL_SHIFT; siu_bit = mpc860_get_siu_lvl(level); if ((d->cipr & d->cimr) && (d->cicr & MPC860_CICR_IEN)) mpc860_set_pending_irq(d,siu_bit); else mpc860_clear_pending_irq(d,siu_bit); } /* ======================================================================== */ /* IDMA */ /* ======================================================================== */ /* Update an IDMA status register */ static int mpc860_idma_update_idsr(struct mpc860_data *d,u_int id) { u_int cpm_int; switch(id) { case 0: cpm_int = MPC860_CIPR_IDMA1; break; case 1: cpm_int = MPC860_CIPR_IDMA2; break; default: return(-1); } if (d->idsr[id] & d->idmr[id]) d->cipr |= cpm_int; else d->cipr &= ~cpm_int; mpc860_update_cpm_int_status(d); return(0); } /* Process to an IDMA transfer for the specified buffer descriptor */ static void mpc860_idma_transfer(struct mpc860_data *d, struct mpc860_idma_bd *bd) { physmem_dma_transfer(d->vm,bd->src_bp,bd->dst_bp,bd->buf_len); } /* Fetch an IDMA descriptor from Dual-Port RAM */ static int mpc860_idma_fetch_bd(struct mpc860_data *d,m_uint16_t bd_addr, struct mpc860_idma_bd *bd) { if ((bd_addr < MPC860_DPRAM_OFFSET) || (bd_addr > MPC860_DPRAM_END)) return(-1); bd->offset = bd_addr - MPC860_DPRAM_OFFSET; /* Fetch control word */ bd->ctrl = dpram_r16(d,bd->offset+0x00); /* Fetch function code registers */ bd->dfcr = dpram_r8(d,bd->offset+0x02); bd->sfcr = dpram_r8(d,bd->offset+0x03); /* Fetch buffer length, source and destination addresses */ bd->buf_len = dpram_r32(d,bd->offset+0x04); bd->src_bp = dpram_r32(d,bd->offset+0x08); bd->dst_bp = dpram_r32(d,bd->offset+0x0c); #if DEBUG_IDMA MPC_LOG(d,"fetched IDMA BD at 0x%4.4x, src_bp=0x%8.8x, dst_bp=0x%8.8x " "len=%d\n",bd->offset,bd->src_bp,bd->dst_bp,bd->buf_len); #endif return(0); } /* Start an IDMA channel */ static int mpc860_idma_start_channel(struct mpc860_data *d,u_int id) { struct mpc860_idma_bd bd; m_uint16_t dma_base,bd_offset; switch(id) { case 0: dma_base = MPC860_IDMA1_BASE; break; case 1: dma_base = MPC860_IDMA2_BASE; break; default: return(-1); } /* Get the IBASE register (offset 0) */ bd_offset = dpram_r16(d,dma_base+0x00); while(1) { /* Fetch a descriptor */ if (mpc860_idma_fetch_bd(d,bd_offset,&bd) == -1) return(-1); if (!(bd.ctrl & MPC860_IDMA_CTRL_V)) { d->idsr[id] |= MPC860_IDSR_OB; break; } /* Run the DMA transfer */ mpc860_idma_transfer(d,&bd); /* Clear the Valid bit */ bd.ctrl &= ~MPC860_IDMA_CTRL_V; dpram_w16(d,bd_offset-MPC860_DPRAM_OFFSET+0x00,bd.ctrl); /* Generate an interrupt for this buffer ? */ if (bd.ctrl & MPC860_IDMA_CTRL_I) d->idsr[id] |= MPC860_IDSR_AD; /* Stop if this is the last buffer of chain */ if (bd.ctrl & MPC860_IDMA_CTRL_L) { d->idsr[id] |= MPC860_IDSR_DONE; break; } bd_offset += sizeof(MPC860_IDMA_BD_SIZE); } mpc860_idma_update_idsr(d,id); return(0); } /* ======================================================================== */ /* SPI (Serial Peripheral Interface) */ /* ======================================================================== */ /* Initialize SPI RX parameters */ static void mpc860_spi_init_rx_params(struct mpc860_data *d) { m_uint16_t spi_base,rbase; spi_base = dpram_r16(d,MPC860_SPI_BASE_ADDR); /* Get the RBASE (offset 0) and store it in RBPTR */ rbase = dpram_r16(d,spi_base+0x00); dpram_w16(d,spi_base+0x10,rbase); } /* Initialize SPI TX parameters */ static void mpc860_spi_init_tx_params(struct mpc860_data *d) { m_uint16_t spi_base,tbase; spi_base = dpram_r16(d,MPC860_SPI_BASE_ADDR); /* Get the TBASE (offset 2) and store it in TBPTR */ tbase = dpram_r16(d,spi_base+0x02); dpram_w16(d,spi_base+0x20,tbase); } /* Initialize SPI RX/TX parameters */ static void mpc860_spi_init_rx_tx_params(struct mpc860_data *d) { mpc860_spi_init_rx_params(d); mpc860_spi_init_tx_params(d); } /* Fetch a SPI buffer descriptor */ static int mpc860_spi_fetch_bd(struct mpc860_data *d,m_uint16_t bd_addr, struct mpc860_spi_bd *bd) { if ((bd_addr < MPC860_DPRAM_OFFSET) || (bd_addr > MPC860_DPRAM_END)) return(-1); bd->offset = bd_addr - MPC860_DPRAM_OFFSET; /* Fetch control word */ bd->ctrl = dpram_r16(d,bd->offset+0x00); /* Fetch buffer length and buffer pointer */ bd->buf_len = dpram_r16(d,bd->offset+0x02); bd->bp = dpram_r32(d,bd->offset+0x04); #if DEBUG_SPI MPC_LOG(d,"fetched SPI BD at 0x%4.4x, bp=0x%8.8x, len=%d\n", bd->offset,bd->bp,bd->buf_len); #endif return(0); } /* Start SPI transmit */ static int mpc860_spi_start_tx(struct mpc860_data *d) { struct mpc860_spi_bd bd; m_uint16_t bd_offset; m_uint16_t spi_base; u_char buffer[512]; u_int buf_len; spi_base = dpram_r16(d,MPC860_SPI_BASE_ADDR); /* Get the TBPTR (offset 0x20) register */ bd_offset = dpram_r16(d,spi_base+0x20); while(1) { /* Fetch a TX descriptor */ if (mpc860_spi_fetch_bd(d,bd_offset,&bd) == -1) return(FALSE); /* If the descriptor is not ready, stop now */ if (!(bd.ctrl & MPC860_SPI_TXBD_CTRL_R)) return(FALSE); /* Extract the data */ buf_len = bd.buf_len; if (bd.buf_len > sizeof(buffer)) { MPC_LOG(d,"SPI: buffer too small for transmit.\n"); buf_len = sizeof(buffer); } physmem_copy_from_vm(d->vm,buffer,bd.bp,buf_len); /* Send the data to the user callback (if specified) */ if (d->spi_tx_callback != NULL) d->spi_tx_callback(d,buffer,buf_len,d->spi_user_arg); /* Clear the Ready bit of the TX descriptor */ bd.ctrl &= ~MPC860_SPI_TXBD_CTRL_R; dpram_w16(d,bd.offset+0x00,bd.ctrl); /* Set pointer on next TX descriptor (wrap ring if necessary) */ if (bd.ctrl & MPC860_SPI_TXBD_CTRL_W) { bd_offset = dpram_r16(d,spi_base+0x02); } else { bd_offset += MPC860_SPI_BD_SIZE; } dpram_w16(d,spi_base+0x20,bd_offset); /* Stop if this is the last buffer in chain */ if (bd.ctrl & MPC860_SPI_TXBD_CTRL_L) break; } return(TRUE); } /* Put a buffer into SPI receive buffers */ int mpc860_spi_receive(struct mpc860_data *d,u_char *buffer,u_int len) { struct mpc860_spi_bd bd; m_uint16_t bd_offset; m_uint16_t spi_base; u_int clen,mrblr; spi_base = dpram_r16(d,MPC860_SPI_BASE_ADDR); /* Get the RBPTR (offset 0x10) */ bd_offset = dpram_r16(d,spi_base+0x10); /* Get the maximum buffer size */ mrblr = dpram_r16(d,spi_base+0x06); while(len > 0) { /* Fetch a RX descriptor */ if (mpc860_spi_fetch_bd(d,bd_offset,&bd) == -1) return(FALSE); /* If the buffer is not empty, do not use it */ if (!(bd.ctrl & MPC860_SPI_RXBD_CTRL_E)) return(FALSE); /* Write data into the RX buffer */ clen = m_min(mrblr,len); physmem_copy_to_vm(d->vm,buffer,bd.bp,clen); buffer += clen; len -= clen; /* Update the length field */ dpram_w16(d,bd.offset+0x02,clen); /* If no more data, set the "Last" bit */ if (!len) bd.ctrl |= MPC860_SPI_RXBD_CTRL_L; /* Clear the Empty bit of the RX descriptor */ bd.ctrl &= ~MPC860_SPI_RXBD_CTRL_E; dpram_w16(d,bd.offset+0x00,bd.ctrl); /* Set pointer on next RX descriptor */ if (bd.ctrl & MPC860_SPI_RXBD_CTRL_W) { bd_offset = dpram_r16(d,spi_base+0x00); } else { bd_offset += MPC860_SPI_BD_SIZE; } dpram_w16(d,spi_base+0x10,bd_offset); } if (len > 0) MPC_LOG(d,"SPI: no buffers available for receive.\n"); return(0); } /* Set SPI TX callback */ void mpc860_spi_set_tx_callback(struct mpc860_data *d, mpc860_spi_tx_callback_t cbk, void *user_arg) { d->spi_tx_callback = cbk; d->spi_user_arg = user_arg; } /* ======================================================================== */ /* SCC (Serial Communication Controller) */ /* ======================================================================== */ typedef struct { u_int cipr_irq; m_uint32_t dpram_base; }scc_chan_info_t; static scc_chan_info_t scc_chan_info[MPC860_SCC_NR_CHAN] = { { MPC860_CIPR_SCC1, MPC860_SCC1_BASE }, { MPC860_CIPR_SCC2, MPC860_SCC2_BASE }, { MPC860_CIPR_SCC3, MPC860_SCC3_BASE }, { MPC860_CIPR_SCC4, MPC860_SCC4_BASE }, }; /* Initialize SCC RX parameters */ static void mpc860_scc_init_rx_params(struct mpc860_data *d,u_int scc_chan) { m_uint16_t scc_base,rbase; scc_base = scc_chan_info[scc_chan].dpram_base; /* Get the RBASE (offset 0) and store it in RBPTR */ rbase = dpram_r16(d,scc_base+0x00); dpram_w16(d,scc_base+0x10,rbase); } /* Initialize SCC TX parameters */ static void mpc860_scc_init_tx_params(struct mpc860_data *d,u_int scc_chan) { m_uint16_t scc_base,tbase; scc_base = scc_chan_info[scc_chan].dpram_base; /* Get the TBASE (offset 2) and store it in TBPTR */ tbase = dpram_r16(d,scc_base+0x02); dpram_w16(d,scc_base+0x20,tbase); } /* Initialize SCC RX/TX parameters */ static void mpc860_scc_init_rx_tx_params(struct mpc860_data *d,u_int scc_chan) { mpc860_scc_init_rx_params(d,scc_chan); mpc860_scc_init_tx_params(d,scc_chan); } /* Set an SCC interrupt */ static int mpc860_scc_update_irq(struct mpc860_data *d,u_int scc_chan) { struct mpc860_scc_chan *chan = &d->scc_chan[scc_chan]; if (chan->scce & chan->sccm) d->cipr |= scc_chan_info[scc_chan].cipr_irq; else d->cipr &= ~scc_chan_info[scc_chan].cipr_irq; mpc860_update_cpm_int_status(d); return(0); } /* Fetch a SCC buffer descriptor */ static int mpc860_scc_fetch_bd(struct mpc860_data *d,m_uint16_t bd_addr, struct mpc860_scc_bd *bd) { if ((bd_addr < MPC860_DPRAM_OFFSET) || (bd_addr > MPC860_DPRAM_END)) return(-1); bd->offset = bd_addr - MPC860_DPRAM_OFFSET; /* Fetch control word */ bd->ctrl = dpram_r16(d,bd->offset+0x00); /* Fetch buffer length and buffer pointer */ bd->buf_len = dpram_r16(d,bd->offset+0x02); bd->bp = dpram_r32(d,bd->offset+0x04); #if DEBUG_SCC MPC_LOG(d,"fetched SCC BD at 0x%4.4x, bp=0x%8.8x, len=%d\n", bd->offset,bd->bp,bd->buf_len); #endif return(0); } /* Handle the TX ring of an SCC channel (transmit a single packet) */ static int mpc860_scc_handle_tx_ring_single(struct mpc860_data *d, u_int scc_chan) { struct mpc860_scc_bd txd0,ctxd,*ptxd; struct mpc860_scc_chan *chan; scc_chan_info_t *scc_info; m_uint16_t bd_offset; m_uint32_t clen,tot_len; u_char *pkt_ptr; int done = FALSE; int irq = FALSE; scc_info = &scc_chan_info[scc_chan]; chan = &d->scc_chan[scc_chan]; /* Get the TBPTR (offset 0x20) register */ bd_offset = dpram_r16(d,scc_info->dpram_base+0x20); /* Try to acquire the first descriptor */ ptxd = &txd0; mpc860_scc_fetch_bd(d,bd_offset,ptxd); /* If we don't own the first descriptor, we cannot transmit */ if (!(txd0.ctrl & MPC860_SCC_TXBD_CTRL_R)) return(FALSE); /* Empty packet for now */ pkt_ptr = chan->tx_pkt; tot_len = 0; do { /* Copy data into the buffer */ clen = ptxd->buf_len; physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->bp,clen); pkt_ptr += clen; tot_len += clen; /* Signal IRQ ? */ if (ptxd->ctrl & MPC860_SCC_TXBD_CTRL_I) irq = TRUE; /* * Clear the ready bit (except for the first descriptor, * which is cleared when the full packet has been sent). */ if (ptxd != &txd0) { ptxd->ctrl &= ~MPC860_SCC_TXBD_CTRL_R; dpram_w16(d,ptxd->offset+0x00,ptxd->ctrl); } /* Set pointer on next TX descriptor (wrap ring if necessary) */ if (ptxd->ctrl & MPC860_SCC_TXBD_CTRL_W) { bd_offset = dpram_r16(d,scc_info->dpram_base+0x02); } else { bd_offset += MPC860_SCC_BD_SIZE; } dpram_w16(d,scc_info->dpram_base+0x20,bd_offset); /* If this is the last descriptor, we have finished */ if (!(ptxd->ctrl & MPC860_SCC_TXBD_CTRL_L)) { mpc860_scc_fetch_bd(d,bd_offset,&ctxd); ptxd = &ctxd; } else { done = TRUE; } }while(!done); if (tot_len != 0) { #if DEBUG_SCC MPC_LOG(d,"SCC%u: sending packet of %u bytes\n",scc_chan+1,tot_len); mem_dump(log_file,chan->tx_pkt,tot_len); #endif /* send packet on wire */ netio_send(chan->nio,chan->tx_pkt,tot_len); } /* Clear the Ready bit of the first TX descriptor */ txd0.ctrl &= ~MPC860_SCC_TXBD_CTRL_R; dpram_w16(d,txd0.offset+0x00,txd0.ctrl); /* Trigger SCC IRQ */ if (irq) { chan->scce |= MPC860_SCCE_TXB; mpc860_scc_update_irq(d,scc_chan); } return(TRUE); } /* Handle the TX ring of the specified SCC channel (multiple pkts possible) */ static int mpc860_scc_handle_tx_ring(struct mpc860_data *d,u_int scc_chan) { int i; for(i=0;iscc_chan[scc_chan]; /* Get the RBPTR (offset 0x10) register */ bd_offset = dpram_r16(d,scc_info->dpram_base+0x10); /* Get the maximum buffer size */ mrblr = dpram_r16(d,scc_info->dpram_base+0x06); /* Try to acquire the first descriptor */ prxd = &rxd0; mpc860_scc_fetch_bd(d,bd_offset,prxd); /* If we don't own the first descriptor, we cannot transmit */ if (!(rxd0.ctrl & MPC860_SCC_RXBD_CTRL_E)) return(FALSE); pkt_ptr = pkt; tot_len = pkt_len; while(tot_len > 0) { /* Write data into the RX buffer */ clen = m_min(mrblr,tot_len); physmem_copy_to_vm(d->vm,pkt_ptr,prxd->bp,clen); pkt_ptr += clen; tot_len -= clen; /* Signal IRQ ? */ if (prxd->ctrl & MPC860_SCC_RXBD_CTRL_I) irq = TRUE; /* Set the Last flag if we have finished */ if (!tot_len) { /* Set the full length */ switch(chan->gsmr_lo & MPC860_GSMRL_MODE_MASK) { case MPC860_SCC_MODE_ETH: pkt_len += 4; break; case MPC860_SCC_MODE_HDLC: pkt_len += 2; break; } dpram_w16(d,prxd->offset+0x02,pkt_len); prxd->ctrl |= MPC860_SCC_RXBD_CTRL_L; } else { /* Update the length field */ dpram_w16(d,prxd->offset+0x02,clen); } /* * Clear the empty bit (except for the first descriptor, * which is cleared when the full packet has been stored). */ if (prxd != &rxd0) { prxd->ctrl &= ~MPC860_SCC_RXBD_CTRL_E; dpram_w16(d,prxd->offset+0x00,prxd->ctrl); } /* Set pointer on next RX descriptor (wrap ring if necessary) */ if (prxd->ctrl & MPC860_SCC_RXBD_CTRL_W) { bd_offset = dpram_r16(d,scc_info->dpram_base+0x00); } else { bd_offset += MPC860_SCC_BD_SIZE; } dpram_w16(d,scc_info->dpram_base+0x10,bd_offset); /* If this is the last descriptor, we have finished */ if (!tot_len) { mpc860_scc_fetch_bd(d,bd_offset,&crxd); prxd = &crxd; } } /* Clear the Empty bit of the first RX descriptor and set First bit */ rxd0.ctrl &= ~MPC860_SCC_RXBD_CTRL_E; rxd0.ctrl |= MPC860_SCC_RXBD_CTRL_F; dpram_w16(d,rxd0.offset+0x00,rxd0.ctrl); /* Trigger SCC IRQ */ if (irq) { d->scc_chan[scc_chan].scce |= MPC860_SCCE_RXB; mpc860_scc_update_irq(d,scc_chan); } return(TRUE); } /* Set NIO for the specified SCC channel */ int mpc860_scc_set_nio(struct mpc860_data *d,u_int scc_chan,netio_desc_t *nio) { struct mpc860_scc_chan *chan; if (!d || (scc_chan >= MPC860_SCC_NR_CHAN)) return(-1); chan = &d->scc_chan[scc_chan]; /* check that a NIO is not already bound */ if (chan->nio != NULL) return(-1); chan->nio = nio; netio_rxl_add(nio,(netio_rx_handler_t)mpc860_scc_handle_rx_pkt, d,(void *)(u_long)scc_chan); return(0); } /* Unset NIO of the specified SCC channel */ int mpc860_scc_unset_nio(struct mpc860_data *d,u_int scc_chan) { struct mpc860_scc_chan *chan; if (!d || (scc_chan >= MPC860_SCC_NR_CHAN)) return(-1); chan = &d->scc_chan[scc_chan]; if (chan->nio != NULL) { netio_rxl_remove(chan->nio); chan->nio = NULL; } return(0); } /* * SCC register access. * * SCC1: 0x0a00 to 0x0a1f * SCC2: 0x0a20 to 0x0a3f * SCC3: 0x0a40 to 0x0a5f * SCC4: 0x0a60 to 0x0a7f */ static int dev_mpc860_scc_access(struct mpc860_data *d,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct mpc860_scc_chan *chan; u_int scc_chan,reg; /* Extract channel ID and register */ scc_chan = (offset >> 5) & 0x03; reg = offset & 0x1F; chan = &d->scc_chan[scc_chan]; switch(reg) { /* GSMRL - General SCC mode register (Low part) */ case 0x00: if (op_type == MTS_READ) *data = chan->gsmr_lo; else chan->gsmr_lo = *data; break; /* GSMRH - General SCC mode register (High part) */ case 0x04: if (op_type == MTS_READ) *data = chan->gsmr_hi; else chan->gsmr_hi = *data; break; /* PSMR - Protocol-Specific Mode Register */ case 0x08: if (op_type == MTS_READ) *data = chan->psmr; else chan->psmr = *data; break; /* TOD - Transmit On Demand */ case 0x0c: if ((op_type == MTS_WRITE) && (*data & 0x8000)) mpc860_scc_handle_tx_ring(d,scc_chan); break; /* SCCE - SCC Event Register */ case 0x10: if (op_type == MTS_READ) *data = chan->scce; else { chan->scce &= ~(*data); mpc860_scc_update_irq(d,scc_chan); } break; /* SCCM - SCC Mask Register */ case 0x14: if (op_type == MTS_READ) *data = chan->sccm; else { chan->sccm = *data; mpc860_scc_update_irq(d,scc_chan); } break; } return(0); } /* ======================================================================== */ /* FEC (Fast Ethernet Controller) */ /* ======================================================================== */ /* link status */ static int mpc860_fec_link_is_up(struct mpc860_data *d) { return(d->fec_nio && !(d->fec_mii_regs[LX970A_CR] & LX970A_CR_POWERDOWN)); } /* Trigger interrupt for FEC */ static void mpc860_fec_update_irq_status(struct mpc860_data *d) { u_int level,siu_bit; level = (d->fec_ivec & MPC860_IVEC_ILEVEL_MASK) >> MPC860_IVEC_ILEVEL_SHIFT; siu_bit = mpc860_get_siu_lvl(level); if (d->fec_ievent & d->fec_imask) mpc860_set_pending_irq(d,siu_bit); else mpc860_clear_pending_irq(d,siu_bit); } /* Fetch a FEC buffer descriptor, located in external memory */ static int mpc860_fec_fetch_bd(struct mpc860_data *d,m_uint32_t bd_addr, struct mpc860_fec_bd *bd) { m_uint32_t w0,w1; /* Set BD address */ bd->bd_addr = bd_addr; w0 = physmem_copy_u32_from_vm(d->vm,bd_addr); w1 = physmem_copy_u32_from_vm(d->vm,bd_addr+4); bd->ctrl = w0 >> 16; bd->buf_len = w0 & 0xFFFF; bd->bp = w1; #if DEBUG_FEC MPC_LOG(d,"fetched FEC BD at 0x%8.8x, bp=0x%8.8x, len=%d\n", bd->bd_addr,bd->bp,bd->buf_len); #endif return(0); } /* Handle the TX ring of the FEC (transmit a single packet) */ static int mpc860_fec_handle_tx_ring_single(struct mpc860_data *d) { u_char tx_pkt[MPC860_FEC_MAX_PKT_SIZE]; struct mpc860_fec_bd txd0,ctxd,*ptxd; m_uint32_t clen,tot_len; u_char *pkt_ptr; int done = FALSE; if (!d->fec_xdes_current) return(FALSE); /* Try to acquire the first descriptor */ ptxd = &txd0; mpc860_fec_fetch_bd(d,d->fec_xdes_current,ptxd); /* If we don't own the first descriptor, we cannot transmit */ if (!(txd0.ctrl & MPC860_FEC_TXBD_CTRL_R)) return(FALSE); /* Empty packet for now */ pkt_ptr = tx_pkt; tot_len = 0; do { /* Copy data into the buffer */ clen = ptxd->buf_len; physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->bp,clen); pkt_ptr += clen; tot_len += clen; /* * Clear the ready bit (except for the first descriptor, * which is cleared when the full packet has been sent). */ if (ptxd != &txd0) { ptxd->ctrl &= ~MPC860_FEC_TXBD_CTRL_R; physmem_copy_u16_to_vm(d->vm,ptxd->bd_addr+0x00,ptxd->ctrl); } /* Set pointer on next TX descriptor (wrap ring if necessary) */ if (ptxd->ctrl & MPC860_FEC_TXBD_CTRL_W) { d->fec_xdes_current = d->fec_xdes_start; } else { d->fec_xdes_current += MPC860_FEC_BD_SIZE; } /* If this is the last descriptor, we have finished */ if (!(ptxd->ctrl & MPC860_FEC_TXBD_CTRL_L)) { mpc860_fec_fetch_bd(d,d->fec_xdes_current,&ctxd); ptxd = &ctxd; } else { done = TRUE; } }while(!done); if (tot_len != 0) { #if DEBUG_FEC MPC_LOG(d,"FEC: sending packet of %u bytes\n",tot_len); mem_dump(d->vm->log_fd,tx_pkt,tot_len); #endif /* send packet on wire */ netio_send(d->fec_nio,tx_pkt,tot_len); } /* Clear the Ready bit of the first TX descriptor */ txd0.ctrl &= ~MPC860_FEC_TXBD_CTRL_R; physmem_copy_u16_to_vm(d->vm,txd0.bd_addr+0x00,txd0.ctrl); /* Trigger FEC IRQ */ d->fec_ievent |= MPC860_IEVENT_TFINT | MPC860_IEVENT_TXB; mpc860_fec_update_irq_status(d); /* link down, report no heartbeat and lost carrier */ if (!mpc860_fec_link_is_up(d)) { ptxd->ctrl |= MPC860_FEC_TXBD_CTRL_HB | MPC860_FEC_TXBD_CTRL_CSL; physmem_copy_u16_to_vm(d->vm,ptxd->bd_addr+0x00,ptxd->ctrl); d->fec_ievent |= MPC860_IEVENT_HBERR; mpc860_fec_update_irq_status(d); } return(TRUE); } /* Handle the TX ring of the FEC (multiple pkts possible) */ static int mpc860_fec_handle_tx_ring(struct mpc860_data *d) { int i; for(i=0;ifec_rdes_current) return(FALSE); /* Try to acquire the first descriptor */ prxd = &rxd0; mpc860_fec_fetch_bd(d,d->fec_rdes_current,prxd); /* If we don't own the first descriptor, we cannot transmit */ if (!(rxd0.ctrl & MPC860_FEC_RXBD_CTRL_E)) return(FALSE); pkt_ptr = pkt; tot_len = pkt_len; while(tot_len > 0) { /* Write data into the RX buffer */ clen = m_min(d->fec_rbuf_size,tot_len); physmem_copy_to_vm(d->vm,pkt_ptr,prxd->bp,clen); pkt_ptr += clen; tot_len -= clen; /* Set the Last flag if we have finished */ if (!tot_len) { /* Set the full length */ physmem_copy_u16_to_vm(d->vm,prxd->bd_addr+0x02,pkt_len+4); prxd->ctrl |= MPC860_FEC_RXBD_CTRL_L; if (eth_addr_is_bcast(&hdr->daddr)) prxd->ctrl |= MPC860_FEC_RXBD_CTRL_BC; else if (eth_addr_is_mcast(&hdr->daddr)) prxd->ctrl |= MPC860_FEC_RXBD_CTRL_MC; } else { /* Update the length field */ physmem_copy_u16_to_vm(d->vm,prxd->bd_addr+0x02,clen); } /* * Clear the empty bit (except for the first descriptor, * which is cleared when the full packet has been stored). */ if (prxd != &rxd0) { prxd->ctrl &= ~MPC860_FEC_RXBD_CTRL_E; physmem_copy_u16_to_vm(d->vm,prxd->bd_addr+0x00,prxd->ctrl); } /* Set pointer on next RX descriptor (wrap ring if necessary) */ if (prxd->ctrl & MPC860_FEC_RXBD_CTRL_W) { d->fec_rdes_current = d->fec_rdes_start; } else { d->fec_rdes_current += MPC860_FEC_BD_SIZE; } /* If this is the last descriptor, we have finished */ if (!tot_len) { mpc860_fec_fetch_bd(d,d->fec_rdes_current,&crxd); prxd = &crxd; } } /* Clear the Empty bit of the first RX descriptor */ rxd0.ctrl &= ~MPC860_FEC_RXBD_CTRL_E; physmem_copy_u16_to_vm(d->vm,rxd0.bd_addr+0x00,rxd0.ctrl); /* Trigger FEC IRQ */ d->fec_ievent |= MPC860_IEVENT_RFINT | MPC860_IEVENT_RXB; mpc860_fec_update_irq_status(d); return(TRUE); } /* MII update registers */ static void mpc860_fec_mii_update_regs(struct mpc860_data *d) { m_uint16_t *regs = d->fec_mii_regs; if (mpc860_fec_link_is_up(d)) { /* link up, LX970A_SR_LINKSTATUS is latch down */ regs[LX970A_CSR] |= LX970A_CSR_LINK; if (!(regs[LX970A_CR] & LX970A_CR_ANENABLE)) { /* manual selection */ if ((regs[LX970A_CR] & LX970A_CR_SPEEDSELECT)) { /* 100Mbps */ regs[LX970A_CSR] |= LX970A_CSR_SPEED; } else { /* 10 Mbps */ regs[LX970A_CSR] &= ~LX970A_CSR_SPEED; } if ((regs[LX970A_CR] & LX970A_CR_DUPLEXMODE)) { /* full duplex */ regs[LX970A_CSR] |= LX970A_CSR_DUPLEXMODE; } else { /* half duplex */ regs[LX970A_CSR] &= ~LX970A_CSR_DUPLEXMODE; } } else { /* auto-negotiation, assume partner is standard 10/100 eth */ regs[LX970A_ANLPAR] = (LX970A_ANLPAR_ACKNOWLEDGE | LX970A_ANLPAR_100TX_FD | LX970A_ANLPAR_100TX_HD | LX970A_ANLPAR_10T_FD | LX970A_ANLPAR_10T_HD); if ((regs[LX970A_ANAR] & LX970A_ANAR_100TX_FD)) { /* 100Mbps full duplex */ regs[LX970A_CSR] |= (LX970A_CSR_DUPLEXMODE | LX970A_CSR_SPEED); } else if ((regs[LX970A_ANAR] & LX970A_ANAR_100TX_HD)) { /* 100Mbps half duplex */ regs[LX970A_CSR] = ((regs[LX970A_CSR] & ~LX970A_CSR_DUPLEXMODE) | LX970A_CSR_SPEED); } else if ((regs[LX970A_ANAR] & LX970A_ANAR_10T_FD)) { /* 10Mbps full duplex */ regs[LX970A_CSR] = (LX970A_CSR_DUPLEXMODE | (regs[LX970A_CSR] & ~LX970A_CSR_SPEED)); } else { /* 10Mbps half duplex */ regs[LX970A_ANAR] |= LX970A_ANAR_10T_HD; regs[LX970A_CSR] &= ~(LX970A_CSR_DUPLEXMODE | LX970A_CSR_SPEED); } d->fec_mii_regs[LX970A_CR] &= ~(LX970A_CR_ANRESTART); d->fec_mii_regs[LX970A_SR] |= LX970A_SR_ANCOMPLETE; d->fec_mii_regs[LX970A_CSR] |= LX970A_CSR_ANCOMPLETE; } } else { /* link down or administratively down */ d->fec_mii_regs[LX970A_SR] &= ~(LX970A_SR_ANCOMPLETE|LX970A_SR_LINKSTATUS); d->fec_mii_regs[LX970A_CSR] &= ~(LX970A_CSR_LINK|LX970A_CSR_DUPLEXMODE|LX970A_CSR_SPEED|LX970A_CSR_ANCOMPLETE); } } /* MII register defaults */ static void mpc860_fec_mii_defaults(struct mpc860_data *d) { /* default is 100Mb/s full duplex and auto-negotiation */ memset(d->fec_mii_regs, 0, sizeof(d->fec_mii_regs)); d->fec_mii_regs[LX970A_CR] = LX970A_CR_DEFAULT; d->fec_mii_regs[LX970A_SR] = LX970A_SR_DEFAULT; d->fec_mii_regs[LX970A_PIR1] = LX970A_PIR1_DEFAULT; d->fec_mii_regs[LX970A_PIR2] = LX970A_PIR2_DEFAULT; d->fec_mii_regs[LX970A_ANAR] = LX970A_ANAR_DEFAULT; d->fec_mii_regs[LX970A_ANE] = LX970A_ANE_DEFAULT; d->fec_mii_regs[LX970A_MR] = LX970A_MR_DEFAULT; d->fec_mii_regs[LX970A_IER] = LX970A_IER_DEFAULT; d->fec_mii_regs[LX970A_ISR] = LX970A_ISR_DEFAULT; d->fec_mii_regs[LX970A_CFGR] = LX970A_CFGR_DEFAULT; d->fec_mii_regs[LX970A_CSR] = LX970A_CSR_DEFAULT; /* chip is powered up and stable */ d->fec_mii_regs[LX970A_ISR] = LX970A_ISR_XTALOK; mpc860_fec_mii_update_regs(d); } /* MII register read access */ static void mpc860_fec_mii_read_access(struct mpc860_data *d, u_int phy,u_int reg) { m_uint16_t res; res = d->fec_mii_regs[reg]; /* update bits */ switch (reg) { case LX970A_SR: /* Latch Low */ if (mpc860_fec_link_is_up(d)) { d->fec_mii_regs[reg] &= LX970A_SR_LINKSTATUS; } break; case LX970A_ISR_MINT: if (d->fec_mii_last_read_reg == LX970A_SR) { d->fec_mii_regs[reg] &= ~LX970A_ISR_MINT; } default: /* XXX Latch High: LX970A_SR_REMOTEFAULT, LX970A_SR_JABBERDETECT LX970A_ANE_PDETECTFAULT, LX970A_ANE_PR, LX970A_CSR_ANC, LX970A_CSR_PAGERECEIVED */ break; } #if DEBUG_FEC MPC_LOG(d,"FEC: Reading 0x%4.4x (0x%4.4x) from MII phy %d reg %d\n",res,d->fec_mii_regs[reg],phy,reg); #endif d->fec_mii_last_read_reg = reg; d->fec_mii_data &= 0xFFFF0000; d->fec_mii_data |= res; } /* MII register read access */ static void mpc860_fec_mii_write_access(struct mpc860_data *d, u_int phy,u_int reg) { m_uint16_t data, ro_mask, rw_mask; int update_regs = FALSE; data = d->fec_mii_data & 0xFFFF; #if DEBUG_FEC MPC_LOG(d,"FEC: Writing 0x%4.4x to MII phy %d reg %d at ia=0x%4.4x,lr=0x%4.4x\n",data,phy,reg,CPU_PPC32(d->vm->boot_cpu)->ia,CPU_PPC32(d->vm->boot_cpu)->lr); #endif switch (reg) { case LX970A_CR: /* reset, self clearing */ if ((data & LX970A_CR_RESET)) { mpc860_fec_mii_defaults(d); return; } ro_mask = LX970A_CR_RO_MASK; rw_mask = LX970A_CR_RW_MASK; update_regs = TRUE; break; case LX970A_ANAR: ro_mask = LX970A_ANAR_RO_MASK; rw_mask = LX970A_ANAR_RW_MASK; break; case LX970A_MR: ro_mask = LX970A_MR_RO_MASK; rw_mask = LX970A_MR_RW_MASK; break; case LX970A_IER: ro_mask = LX970A_IER_RO_MASK; rw_mask = LX970A_IER_RW_MASK; break; case LX970A_CFGR: ro_mask = LX970A_CFGR_RO_MASK; rw_mask = LX970A_CFGR_RW_MASK; break; default: /* read-only register */ ro_mask = 0xFFFF; rw_mask = 0x0000; break; } d->fec_mii_regs[reg] = (d->fec_mii_regs[reg] & ro_mask) | (data & rw_mask); if (update_regs) { mpc860_fec_mii_update_regs(d); } } /* MII register access */ static void mpc860_fec_mii_access(struct mpc860_data *d) { u_int op,phy,reg; op = (d->fec_mii_data & MPC860_MII_OP_MASK) >> MPC860_MII_OP_SHIFT; phy = (d->fec_mii_data & MPC860_MII_PHY_MASK) >> MPC860_MII_PHY_SHIFT; reg = (d->fec_mii_data & MPC860_MII_REG_MASK) >> MPC860_MII_REG_SHIFT; switch(op) { /* MII write */ case 0x01: mpc860_fec_mii_write_access(d,phy,reg); break; /* MII read */ case 0x02: mpc860_fec_mii_read_access(d,phy,reg); break; default: MPC_LOG(d,"FEC: unknown MII opcode %u\n",op); } /* MII access completed */ d->fec_ievent |= MPC860_IEVENT_MII; mpc860_fec_update_irq_status(d); } /* * FEC register access (0xE00 to 0xF84). */ static int dev_mpc860_fec_access(struct mpc860_data *d,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { switch(offset) { /* R_DES_START: Beginning of RxBD ring */ case 0xE10: if (op_type == MTS_READ) *data = d->fec_rdes_start; else d->fec_rdes_start = *data & 0xFFFFFFFC; break; /* X_DES_START: Beginning of TxBD ring */ case 0xE14: if (op_type == MTS_READ) *data = d->fec_xdes_start; else d->fec_xdes_start = *data & 0xFFFFFFFC; break; /* R_BUFF_SIZE: Receive Buffer Size */ case 0xE18: if (op_type == MTS_READ) *data = d->fec_rbuf_size; else d->fec_rbuf_size = *data & 0x7F0; break; /* ECNTRL */ case 0xE40: if (op_type == MTS_READ) { *data = d->fec_ecntrl; } else { if (*data & MPC860_ECNTRL_RESET) d->fec_ecntrl = 0; else { if (!(*data & MPC860_ECNTRL_ETHER_EN)) { d->fec_xdes_current = d->fec_xdes_start; d->fec_rdes_current = d->fec_rdes_start; } d->fec_ecntrl = *data; } } break; /* IEVENT: Interrupt Event Register */ case 0xE44: if (op_type == MTS_READ) { *data = d->fec_ievent; } else { d->fec_ievent &= ~(*data); mpc860_fec_update_irq_status(d); } break; /* IMASK: Interrupt Mask Register */ case 0xE48: if (op_type == MTS_READ) { *data = d->fec_imask; } else { d->fec_imask = *data; mpc860_fec_update_irq_status(d); } break; /* IVEC: Interrupt Vector Register */ case 0xE4C: if (op_type == MTS_READ) *data = d->fec_ivec; else d->fec_ivec = *data; break; /* X_DES_ACTIVE: TxBD Active Register */ case 0xE54: mpc860_fec_handle_tx_ring(d); //printf("x_des_active set\n"); break; /* MII_DATA */ case 0xE80: if (op_type == MTS_READ) { *data = d->fec_mii_data; } else { d->fec_mii_data = *data; mpc860_fec_mii_access(d); } break; } return(0); } /* Set NIO for the Fast Ethernet Controller */ int mpc860_fec_set_nio(struct mpc860_data *d,netio_desc_t *nio) { /* check that a NIO is not already bound */ if (!d || (d->fec_nio != NULL)) return(-1); d->fec_nio = nio; netio_rxl_add(nio,(netio_rx_handler_t)mpc860_fec_handle_rx_pkt,d,NULL); mpc860_fec_mii_update_regs(d); return(0); } /* Unset NIO of the Fast Ethernet Controller */ int mpc860_fec_unset_nio(struct mpc860_data *d) { if (!d) return(-1); if (d->fec_nio != NULL) { netio_rxl_remove(d->fec_nio); d->fec_nio = NULL; mpc860_fec_mii_update_regs(d); } return(0); } /* ======================================================================== */ #define MPC860_CP_FOP(chan,op) (((chan) << 4) + (op)) /* Execute a command sent through CP Command Register (CPCR) */ static void mpc860_exec_cpcr(struct mpc860_data *d,m_uint32_t cpcr) { u_int channel,opcode,fop; #if DEBUG_UNKNOWN if ((cpcr & 0x1)) { /* TODO CP reset command (RST) */ MPC_LOG(d,"CPCR: CP reset command not implemented: cpcr=0x%4.4x\n",cpcr); } #endif channel = (cpcr >> 4) & 0x0F; opcode = (cpcr >> 8) & 0x0F; fop = MPC860_CP_FOP(channel,opcode); switch(fop) { /* SPI - Init RX and TX params */ case MPC860_CP_FOP(MPC860_CHAN_SPI_IDMA2_RT,0): mpc860_spi_init_rx_tx_params(d); break; /* SPI - Init RX params */ case MPC860_CP_FOP(MPC860_CHAN_SPI_IDMA2_RT,1): mpc860_spi_init_rx_params(d); break; /* SPI - Init TX params */ case MPC860_CP_FOP(MPC860_CHAN_SPI_IDMA2_RT,2): mpc860_spi_init_tx_params(d); break; /* SCC1 - Init RX and TX params */ case MPC860_CP_FOP(MPC860_CHAN_SCC1,0): mpc860_scc_init_rx_tx_params(d,0); break; /* SCC1 - Init RX params */ case MPC860_CP_FOP(MPC860_CHAN_SCC1,1): mpc860_scc_init_rx_params(d,0); break; /* SCC1 - Init TX params */ case MPC860_CP_FOP(MPC860_CHAN_SCC1,2): mpc860_scc_init_tx_params(d,0); break; /* SCC2 - Init RX and TX params */ case MPC860_CP_FOP(MPC860_CHAN_SCC2,0): mpc860_scc_init_rx_tx_params(d,1); break; /* SCC2 - Init RX params */ case MPC860_CP_FOP(MPC860_CHAN_SCC2,1): mpc860_scc_init_rx_params(d,1); break; /* SCC2 - Init TX params */ case MPC860_CP_FOP(MPC860_CHAN_SCC2,2): mpc860_scc_init_tx_params(d,1); break; /* SCC3 - Init RX and TX params */ case MPC860_CP_FOP(MPC860_CHAN_SCC3,0): mpc860_scc_init_rx_tx_params(d,2); break; /* SCC3 - Init RX params */ case MPC860_CP_FOP(MPC860_CHAN_SCC3,1): mpc860_scc_init_rx_params(d,2); break; /* SCC3 - Init TX params */ case MPC860_CP_FOP(MPC860_CHAN_SCC3,2): mpc860_scc_init_tx_params(d,2); break; /* SCC4 - Init RX and TX params */ case MPC860_CP_FOP(MPC860_CHAN_SCC4,0): mpc860_scc_init_rx_tx_params(d,3); break; /* SCC4 - Init RX params */ case MPC860_CP_FOP(MPC860_CHAN_SCC4,1): mpc860_scc_init_rx_params(d,3); break; /* SCC4 - Init TX params */ case MPC860_CP_FOP(MPC860_CHAN_SCC4,2): mpc860_scc_init_tx_params(d,3); break; default: MPC_LOG(d,"CPCR: unknown cmd: channel=0x%4.4x, opcode=0x%4.4x\n", channel,opcode); } } /* * dev_mpc860_access() */ void *dev_mpc860_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct mpc860_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0x0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,d->name, "read from offset 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name, "write to offset 0x%x, value=0x%llx, pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif /* Handle dual-port RAM access */ if ((offset >= MPC860_DPRAM_OFFSET) && (offset < MPC860_DPRAM_END)) return(d->dpram + (offset - MPC860_DPRAM_OFFSET)); /* Handle SCC channels */ if ((offset >= MPC860_REG_SCC_BASE) && (offset < (MPC860_REG_SCC_BASE + (4 * 0x20)))) { dev_mpc860_scc_access(d,offset,op_size,op_type,data); return NULL; } /* Handle Fast Ethernet Controller (FEC) registers */ if ((offset >= MPC860_REG_FEC_BASE) && (offset <= MPC860_REG_FEC_END)) { dev_mpc860_fec_access(d,offset,op_size,op_type,data); return NULL; } switch(offset) { /* SWSR - Software Service Register (Watchdog) */ case MPC860_REG_SWSR: break; /* SIU Interrupt Pending Register */ case MPC860_REG_SIPEND: if (op_type == MTS_READ) *data = d->sipend; break; /* SIU Interrupt Mask Register */ case MPC860_REG_SIMASK: if (op_type == MTS_READ) { *data = d->simask; } else { d->simask = *data; mpc860_update_irq_status(d); } break; /* * Cisco 2600: * Bit 30: 0=NM in slot 1 */ case MPC860_REG_PIPR: if (op_type == MTS_READ) *data = 0x3F00F600; break; /* PISCR - Periodic Interrupt Status and Control Register */ case MPC860_REG_PISCR: if (op_type == MTS_WRITE) { if (*data & 0x80) { d->sipend &= ~0x40000000; mpc860_update_irq_status(d); } } break; case MPC860_REG_TBSCR: if (op_type == MTS_READ) *data = 0x45; break; /* IDMA1 Status and Mask Registers */ case MPC860_REG_IDSR1: if (op_type == MTS_READ) { *data = d->idsr[0]; } else { d->idsr[0] &= ~(*data); } break; case MPC860_REG_IDMR1: if (op_type == MTS_READ) *data = d->idmr[0]; else d->idmr[0] = *data; break; /* IDMA2 Status and Mask Registers */ case MPC860_REG_IDSR2: if (op_type == MTS_READ) *data = d->idsr[1]; else d->idsr[1] &= ~(*data); break; case MPC860_REG_IDMR2: if (op_type == MTS_READ) *data = d->idmr[1]; else d->idmr[1] = *data; break; /* CICR - CPM Interrupt Configuration Register */ case MPC860_REG_CICR: if (op_type == MTS_READ) *data = d->cicr; else d->cicr = *data; break; /* CIPR - CPM Interrupt Pending Register */ case MPC860_REG_CIPR: if (op_type == MTS_READ) *data = d->cipr; else { d->cipr &= ~(*data); mpc860_update_cpm_int_status(d); } break; /* CIMR - CPM Interrupt Mask Register */ case MPC860_REG_CIMR: if (op_type == MTS_READ) *data = d->cimr; else { d->cimr = *data; mpc860_update_cpm_int_status(d); } break; /* PCSO - Port C Special Options Register */ case MPC860_REG_PCSO: if (op_type == MTS_WRITE) { if (*data & 0x01) { #if DEBUG_IDMA MPC_LOG(d,"activating IDMA0\n"); #endif mpc860_idma_start_channel(d,0); } } break; /* PCDAT - Port C Data Register */ case MPC860_REG_PCDAT: if (op_type == MTS_WRITE) d->pcdat = *data; else *data = d->pcdat; break; /* PBDAT - Port B Data Register */ case MPC860_REG_PBDAT: if (op_type == MTS_WRITE) d->pbdat = *data; else *data = d->pbdat; break; /* CPCR - CP Command Register */ case MPC860_REG_CPCR: if (op_type == MTS_WRITE) mpc860_exec_cpcr(d,(m_uint32_t)(*data)); break; /* SPCOM - SPI Command Register */ case MPC860_REG_SPCOM: if ((op_type == MTS_WRITE) && (*data & MPC860_SPCOM_STR)) mpc860_spi_start_tx(d); break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,d->name, "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name, "write to addr 0x%x, value=0x%llx, pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* Set IRQ pending status */ void mpc860_set_pending_irq(struct mpc860_data *d,m_uint32_t val) { d->sipend |= 1 << val; mpc860_update_irq_status(d); } /* Clear a pending IRQ */ void mpc860_clear_pending_irq(struct mpc860_data *d,m_uint32_t val) { d->sipend &= ~(1 << val); mpc860_update_irq_status(d); } /* Shutdown the MPC860 device */ void dev_mpc860_shutdown(vm_instance_t *vm,struct mpc860_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* Create the MPC860 device */ int dev_mpc860_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len) { struct mpc860_data *d; if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"mpc860: unable to create device data.\n"); return(-1); } memset(d,0,sizeof(*d)); d->name = name; d->vm = vm; vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_mpc860_shutdown; dev_init(&d->dev); d->dev.name = name; d->dev.priv_data = d; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_mpc860_access; /* Set the default SPI base address */ dpram_w16(d,MPC860_SPI_BASE_ADDR,MPC860_SPI_BASE); /* Set MII register defaults */ mpc860_fec_mii_defaults(d); /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_mpc860.h000066400000000000000000000032111241034141600167270ustar00rootroot00000000000000/* * Cisco Router Simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __DEV_MPC860_H__ #define __DEV_MPC860_H__ #include #include "utils.h" #include "mips64.h" #include "cpu.h" #include "device.h" #include "net_io.h" #include "vm.h" /* Forward declaration for MPC860 private data */ struct mpc860_data; /* SPI callback for TX data */ typedef void (*mpc860_spi_tx_callback_t)(struct mpc860_data *d, u_char *buffer,u_int len, void *user_arg); /* Set IRQ pending status */ void mpc860_set_pending_irq(struct mpc860_data *d,m_uint32_t val); /* Clear a pending IRQ */ void mpc860_clear_pending_irq(struct mpc860_data *d,m_uint32_t val); /* Put a buffer into SPI receive buffers */ int mpc860_spi_receive(struct mpc860_data *d,u_char *buffer,u_int len); /* Set SPI TX callback */ void mpc860_spi_set_tx_callback(struct mpc860_data *d, mpc860_spi_tx_callback_t cbk, void *user_arg); /* Set NIO for the specified SCC channel */ int mpc860_scc_set_nio(struct mpc860_data *d,u_int scc_chan,netio_desc_t *nio); /* Unset NIO of the specified SCC channel */ int mpc860_scc_unset_nio(struct mpc860_data *d,u_int scc_chan); /* Set NIO for the Fast Ethernet Controller */ int mpc860_fec_set_nio(struct mpc860_data *d,netio_desc_t *nio); /* Unset NIO of the Fast Ethernet Controller */ int mpc860_fec_unset_nio(struct mpc860_data *d); /* Create the MPC860 device */ int dev_mpc860_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len); #endif dynamips-0.2.14/common/dev_mueslix.c000066400000000000000000000716361241034141600174130ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (C) 2005,2006 Christophe Fillot. All rights reserved. * * Serial Interfaces (Mueslix). * * Note: "debug serial mueslix" gives more technical info. * * Chip mode: Cisco models 36xx and 72xx don't seem to use the same microcode, * so there are code variants to make things work properly. * * Chip mode 0 => 3600 * Chip mode 1 => 7200 * * 2 points noticed until now: * - RX/TX ring wrapping checks are done differently, * - TX packet sizes are not specified in the same way. * * Test methodology: * - Connect two virtual routers together ; * - Do pings by sending 10 packets by 10 packets. If this stops working, * count the number of transmitted packets and check with RX/TX rings * sizes. This is problably a ring wrapping problem. * - Do multiple pings with various sizes (padding checks); * - Check if CDP is working, with various hostname sizes. Since CDP * contains a checksum, it is a good way to determine if packets are * sent/received correctly. * - Do a Telnet from both virtual router to the other one, and do a * "sh run". */ #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_mueslix.h" /* Debugging flags */ #define DEBUG_ACCESS 0 #define DEBUG_UNKNOWN 0 #define DEBUG_PCI_REGS 0 #define DEBUG_TRANSMIT 0 #define DEBUG_RECEIVE 0 /* Mueslix PCI vendor/product codes */ #define MUESLIX_PCI_VENDOR_ID 0x1137 #define MUESLIX_PCI_PRODUCT_ID 0x0001 /* Number of channels (4 interfaces) */ #define MUESLIX_NR_CHANNELS 4 #define MUESLIX_CHANNEL_LEN 0x100 /* RX/TX status for a channel */ #define MUESLIX_CHANNEL_STATUS_RX 0x01 #define MUESLIX_CHANNEL_STATUS_TX 0x02 /* RX/TX enable masks (XXX check if bit position is correct) */ #define MUESLIX_TX_ENABLE 0x01 #define MUESLIX_RX_ENABLE 0x02 /* RX/TX IRQ masks */ #define MUESLIX_TX_IRQ 0x01 #define MUESLIX_RX_IRQ 0x10 /* Addresses of ports */ #define MUESLIX_CHANNEL0_OFFSET 0x100 #define MUESLIX_CHANNEL1_OFFSET 0x200 #define MUESLIX_CHANNEL2_OFFSET 0x300 #define MUESLIX_CHANNEL3_OFFSET 0x400 /* TPU Registers */ #define MUESLIX_TPU_CMD_OFFSET 0x2c24 #define MUESLIX_TPU_CMD_RSP_OFFSET 0x2c2c /* General and channels registers */ #define MUESLIX_GEN_CHAN_LEN 0x500 /* TPU microcode */ #define MUESLIX_UCODE_OFFSET 0x2000 #define MUESLIX_UCODE_LEN 0x800 /* TPU Xmem and YMem */ #define MUESLIX_XMEM_OFFSET 0x2a00 #define MUESLIX_YMEM_OFFSET 0x2b00 #define MUESLIX_XYMEM_LEN 0x100 /* Maximum packet size */ #define MUESLIX_MAX_PKT_SIZE 18000 /* Send up to 16 packets in a TX ring scan pass */ #define MUESLIX_TXRING_PASS_COUNT 16 /* RX descriptors */ #define MUESLIX_RXDESC_OWN 0x80000000 /* Ownership */ #define MUESLIX_RXDESC_FS 0x40000000 /* First Segment */ #define MUESLIX_RXDESC_LS 0x20000000 /* Last Segment */ #define MUESLIX_RXDESC_OVERRUN 0x10000000 /* Overrun */ #define MUESLIX_RXDESC_IGNORED 0x08000000 /* Ignored */ #define MUESLIX_RXDESC_ABORT 0x04000000 /* Abort */ #define MUESLIX_RXDESC_CRC 0x02000000 /* CRC error */ #define MUESLIX_RXDESC_LEN_MASK 0xffff /* TX descriptors */ #define MUESLIX_TXDESC_OWN 0x80000000 /* Ownership */ #define MUESLIX_TXDESC_FS 0x40000000 /* First Segment */ #define MUESLIX_TXDESC_LS 0x20000000 /* Last Segment */ #define MUESLIX_TXDESC_SUB 0x00100000 /* Length substractor ? */ #define MUESLIX_TXDESC_SUB_LEN 0x03000000 /* Length substrator ? */ #define MUESLIX_TXDESC_SUB_SHIFT 24 #define MUESLIX_TXDESC_PAD 0x00c00000 /* Sort of padding info ? */ #define MUESLIX_TXDESC_PAD_SHIFT 22 #define MUESLIX_TXDESC_LEN_MASK 0xffff /* RX Descriptor */ struct rx_desc { m_uint32_t rdes[2]; }; /* TX Descriptor */ struct tx_desc { m_uint32_t tdes[2]; }; /* Forward declaration of Mueslix data */ typedef struct mueslix_data mueslix_data_t; /* Mueslix channel */ struct mueslix_channel { /* Channel ID */ u_int id; /* Channel status (0=disabled) */ u_int status; /* Clock parameters */ u_int clk_shift,clk_div; u_int clk_rate; /* CRC control register */ u_int crc_ctrl_reg; /* CRC size */ u_int crc_size; /* NetIO descriptor */ netio_desc_t *nio; /* TX ring scanners task id */ ptask_id_t tx_tid; /* physical addresses for start and end of RX/TX rings */ m_uint32_t rx_start,rx_end,tx_start,tx_end; /* physical addresses of current RX and TX descriptors */ m_uint32_t rx_current,tx_current; /* Parent mueslix structure */ mueslix_data_t *parent; }; /* Mueslix Data */ struct mueslix_data { char *name; /* Lock */ pthread_mutex_t lock; /* IRQ status and mask */ m_uint32_t irq_status,irq_mask; u_int irq_clearing_count; /* TPU options */ m_uint32_t tpu_options; /* Virtual machine */ vm_instance_t *vm; /* Virtual device */ struct vdevice *dev; /* PCI device information */ struct pci_device *pci_dev; /* Chip mode: * * 0=increment ring pointers before check + direct TX size, * 1=increment ring pointers after check + "complex" TX size. */ int chip_mode; /* Channels */ struct mueslix_channel channel[MUESLIX_NR_CHANNELS]; m_uint32_t channel_enable_mask; /* TPU microcode */ u_char ucode[MUESLIX_UCODE_LEN]; /* TPU Xmem and Ymem */ u_char xmem[MUESLIX_XYMEM_LEN]; u_char ymem[MUESLIX_XYMEM_LEN]; }; /* Offsets of the 4 channels */ static m_uint32_t channel_offset[MUESLIX_NR_CHANNELS] = { MUESLIX_CHANNEL0_OFFSET, MUESLIX_CHANNEL1_OFFSET, MUESLIX_CHANNEL2_OFFSET, MUESLIX_CHANNEL3_OFFSET, }; /* Lock/Unlock primitives */ #define MUESLIX_LOCK(d) pthread_mutex_lock(&(d)->lock) #define MUESLIX_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) /* Log a Mueslix message */ #define MUESLIX_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* Returns TRUE if RX/TX is enabled for a channel */ static inline int dev_mueslix_is_rx_tx_enabled(struct mueslix_data *d,u_int id) { /* 2 bits for RX/TX, 4 channels max */ return((d->channel_enable_mask >> (id << 1)) & 0x03); } /* Update IRQ status */ static inline void dev_mueslix_update_irq_status(struct mueslix_data *d) { if (d->irq_status & d->irq_mask) pci_dev_trigger_irq(d->vm,d->pci_dev); else { if (++d->irq_clearing_count == 3) { pci_dev_clear_irq(d->vm,d->pci_dev); d->irq_clearing_count = 0; } } } /* Compute clock rate */ static void dev_mueslix_update_clk_rate(struct mueslix_channel *channel) { u_int clk_shift = channel->clk_shift; if (clk_shift == 8) clk_shift = 0; channel->clk_rate = (8064000 >> clk_shift) / (channel->clk_div + 1); MUESLIX_LOG(channel->parent,"channel %u: clock rate set to %u\n", channel->id,channel->clk_rate); /* Apply the bandwidth constraint to the NIO */ if (channel->nio != NULL) netio_set_bandwidth(channel->nio,(channel->clk_rate+1000)/1000); } /* * Access to channel registers. */ void dev_mueslix_chan_access(cpu_gen_t *cpu,struct mueslix_channel *channel, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { switch(offset) { case 0x00: /* CRC control register ? */ if (op_type == MTS_READ) { *data = channel->crc_ctrl_reg; } else { channel->crc_ctrl_reg = *data; switch(channel->crc_ctrl_reg) { case 0x08: case 0x0a: channel->crc_size = channel->crc_ctrl_reg - 0x06; break; default: MUESLIX_LOG(channel->parent,"channel %u: unknown value " "for CRC ctrl reg 0x%4.4x\n", channel->id,channel->crc_ctrl_reg); channel->crc_size = 2; } MUESLIX_LOG(channel->parent, "channel %u: CRC size set to 0x%4.4x\n", channel->id,channel->crc_size); } break; case 0x40: if (op_type == MTS_READ) *data = channel->clk_shift; else channel->clk_shift = *data; /* Recompute clock rate */ dev_mueslix_update_clk_rate(channel); break; case 0x44: if (op_type == MTS_READ) *data = channel->clk_div; else channel->clk_div = *data; break; case 0x60: /* signals ? */ if ((op_type == MTS_READ) && (channel->nio != NULL)) *data = 0xFFFFFFFF; break; case 0x64: /* port status - cable type and probably other things */ if (op_type == MTS_READ) *data = 0x7B; break; case 0x90: /* has influence on clock rate */ if (op_type == MTS_READ) *data = 0x11111111; break; case 0x80: /* TX start */ if (op_type == MTS_WRITE) channel->tx_start = channel->tx_current = *data; else *data = channel->tx_start; break; case 0x84: /* TX end */ if (op_type == MTS_WRITE) channel->tx_end = *data; else *data = channel->tx_end; break; case 0x88: /* RX start */ if (op_type == MTS_WRITE) channel->rx_start = channel->rx_current = *data; else *data = channel->rx_start; break; case 0x8c: /* RX end */ if (op_type == MTS_WRITE) channel->rx_end = *data; else *data = channel->rx_end; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_WRITE) { MUESLIX_LOG(channel->parent,"channel %u: " "write to unknown addr 0x%4.4x, value=0x%llx\n", channel->id,offset,*data); } #endif } } /* Handle TPU commands for chip mode 0 (3600) */ static void tpu_cm0_handle_cmd(struct mueslix_data *d,u_int cmd) { struct mueslix_channel *channel; u_int opcode,channel_id; opcode = (cmd >> 12) & 0xFF; channel_id = cmd & 0x03; channel = &d->channel[channel_id]; switch(opcode) { case 0x10: MUESLIX_LOG(d,"channel %u disabled\n",channel_id); channel->status = 0; break; case 0x00: MUESLIX_LOG(d,"channel %u enabled\n",channel_id); channel->status = 1; break; default: MUESLIX_LOG(d,"unknown command 0x%5x\n",cmd); } } /* Handle TPU commands for chip mode 1 (7200) */ static void tpu_cm1_handle_cmd(struct mueslix_data *d,u_int cmd) { struct mueslix_channel *channel; u_int opcode,channel_id; opcode = (cmd >> 12) & 0xFF; channel_id = cmd & 0x03; channel = &d->channel[channel_id]; switch(opcode) { case 0x50: case 0x30: MUESLIX_LOG(d,"channel %u disabled\n",channel_id); channel->status = 0; break; case 0x00: MUESLIX_LOG(d,"channel %u enabled\n",channel_id); channel->status = 1; break; default: MUESLIX_LOG(d,"unknown command 0x%5x\n",cmd); } } /* * dev_mueslix_access() */ void *dev_mueslix_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct mueslix_data *d = dev->priv_data; int i; #if DEBUG_ACCESS >= 2 if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to offset=0x%x, pc=0x%llx, size=%u\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name,"write access to offset=0x%x, pc=0x%llx, " "val=0x%llx, size=%u\n",offset,cpu_get_pc(cpu),*data,op_size); } #endif /* Returns 0 if we don't know the offset */ if (op_type == MTS_READ) *data = 0x00000000; /* Handle microcode access */ if ((offset >= MUESLIX_UCODE_OFFSET) && (offset < (MUESLIX_UCODE_OFFSET + MUESLIX_UCODE_LEN))) return(d->ucode + offset - MUESLIX_UCODE_OFFSET); /* Handle TPU XMem access */ if ((offset >= MUESLIX_XMEM_OFFSET) && (offset < (MUESLIX_XMEM_OFFSET + MUESLIX_XYMEM_LEN))) return(d->xmem + offset - MUESLIX_XMEM_OFFSET); /* Handle TPU YMem access */ if ((offset >= MUESLIX_YMEM_OFFSET) && (offset < (MUESLIX_YMEM_OFFSET + MUESLIX_XYMEM_LEN))) return(d->ymem + offset - MUESLIX_YMEM_OFFSET); /* Handle channel access */ for(i=0;i= channel_offset[i]) && (offset < (channel_offset[i] + MUESLIX_CHANNEL_LEN))) { MUESLIX_LOCK(d); dev_mueslix_chan_access(cpu,&d->channel[i], offset - channel_offset[i], op_size,op_type,data); MUESLIX_UNLOCK(d); return NULL; } MUESLIX_LOCK(d); /* Generic case */ switch(offset) { /* this reg is accessed when an interrupt occurs */ case 0x0: if (op_type == MTS_READ) { *data = d->irq_status; } else { d->irq_status &= ~(*data); dev_mueslix_update_irq_status(d); } break; /* Maybe interrupt mask */ case 0x10: if (op_type == MTS_READ) { *data = d->irq_mask; } else { d->irq_mask = *data; dev_mueslix_update_irq_status(d); } break; case 0x14: if (op_type == MTS_READ) *data = d->channel_enable_mask; else { #if DEBUG_ACCESS cpu_log(cpu,d->name, "channel_enable_mask = 0x%5.5llx at pc=0x%llx\n", *data,cpu_get_pc(cpu)); #endif d->channel_enable_mask = *data; } break; case 0x18: if (op_type == MTS_READ) *data = 0x7F7F7F7F; break; case 0x48: if (op_type == MTS_READ) *data = 0x00000000; break; case 0x7c: if (op_type == MTS_READ) *data = 0x492; break; case 0x2c00: if (op_type == MTS_READ) *data = d->tpu_options; else d->tpu_options = *data; break; /* cmd reg */ case MUESLIX_TPU_CMD_OFFSET: #if DEBUG_ACCESS if (op_type == MTS_WRITE) { cpu_log(cpu,d->name,"cmd_reg = 0x%5.5llx at pc=0x%llx\n", *data,cpu_get_pc(cpu)); } #endif switch(d->chip_mode) { case 0: /* 3600 */ tpu_cm0_handle_cmd(d,*data); break; case 1: /* 7200 */ tpu_cm1_handle_cmd(d,*data); break; } break; /* * cmd_rsp reg, it seems that 0xFFFF means OK * (seen on a "sh contr se1/0" with "debug serial mueslix" enabled). */ case MUESLIX_TPU_CMD_RSP_OFFSET: if (op_type == MTS_READ) *data = 0xFFFF; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,d->name, "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name, "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } MUESLIX_UNLOCK(d); return NULL; } /* * Get the address of the next RX descriptor. */ static m_uint32_t rxdesc_get_next(struct mueslix_channel *channel, m_uint32_t rxd_addr) { m_uint32_t nrxd_addr; switch(channel->parent->chip_mode) { case 0: nrxd_addr = rxd_addr + sizeof(struct rx_desc); if (nrxd_addr == channel->rx_end) nrxd_addr = channel->rx_start; break; case 1: default: if (rxd_addr == channel->rx_end) nrxd_addr = channel->rx_start; else nrxd_addr = rxd_addr + sizeof(struct rx_desc); break; } return(nrxd_addr); } /* Read an RX descriptor */ static void rxdesc_read(struct mueslix_data *d,m_uint32_t rxd_addr, struct rx_desc *rxd) { #if DEBUG_RECEIVE MUESLIX_LOG(d,"reading RX descriptor at address 0x%x\n",rxd_addr); #endif /* get the next descriptor from VM physical RAM */ physmem_copy_from_vm(d->vm,rxd,rxd_addr,sizeof(struct rx_desc)); /* byte-swapping */ rxd->rdes[0] = vmtoh32(rxd->rdes[0]); rxd->rdes[1] = vmtoh32(rxd->rdes[1]); } /* * Try to acquire the specified RX descriptor. Returns TRUE if we have it. * It assumes that the byte-swapping is done. */ static inline int rxdesc_acquire(m_uint32_t rdes0) { return(rdes0 & MUESLIX_RXDESC_OWN); } /* Put a packet in buffer of a descriptor */ static ssize_t rxdesc_put_pkt(struct mueslix_data *d,struct rx_desc *rxd, u_char **pkt,ssize_t *pkt_len) { ssize_t len,cp_len; len = rxd->rdes[0] & MUESLIX_RXDESC_LEN_MASK; /* compute the data length to copy */ cp_len = m_min(len,*pkt_len); #if DEBUG_RECEIVE MUESLIX_LOG(d,"copying %d bytes at 0x%x\n",cp_len,rxd->rdes[1]); #endif /* copy packet data to the VM physical RAM */ physmem_copy_to_vm(d->vm,*pkt,rxd->rdes[1],cp_len); *pkt += cp_len; *pkt_len -= cp_len; return(cp_len); } /* * Put a packet in the RX ring of the Mueslix specified channel. */ static void dev_mueslix_receive_pkt(struct mueslix_channel *channel, u_char *pkt,ssize_t pkt_len) { struct mueslix_data *d = channel->parent; m_uint32_t rx_start,rxdn_addr,rxdn_rdes0; struct rx_desc rxd0,rxdn,*rxdc; ssize_t cp_len,tot_len = pkt_len; u_char *pkt_ptr = pkt; int i; if ((channel->rx_start == 0) || (channel->status == 0) || (channel->nio == NULL)) return; /* Don't make anything if RX is not enabled for this channel */ if (!(dev_mueslix_is_rx_tx_enabled(d,channel->id) & MUESLIX_RX_ENABLE)) return; /* Truncate the packet if it is too big */ pkt_len = m_min(pkt_len,MUESLIX_MAX_PKT_SIZE); /* Copy the current rxring descriptor */ rxdesc_read(d,channel->rx_current,&rxd0); /* We must have the first descriptor... */ if (!rxdesc_acquire(rxd0.rdes[0])) return; /* Remember the first RX descriptor address */ rx_start = channel->rx_current; for(i=0,rxdc=&rxd0;tot_len>0;i++) { /* Put data into the descriptor buffers */ cp_len = rxdesc_put_pkt(d,rxdc,&pkt_ptr,&tot_len); /* Get address of the next descriptor */ rxdn_addr = rxdesc_get_next(channel,channel->rx_current); /* We have finished if the complete packet has been stored */ if (tot_len == 0) { rxdc->rdes[0] = MUESLIX_RXDESC_LS; rxdc->rdes[0] |= cp_len + channel->crc_size + 1; if (i != 0) physmem_copy_u32_to_vm(d->vm,channel->rx_current,rxdc->rdes[0]); channel->rx_current = rxdn_addr; break; } #if DEBUG_RECEIVE MUESLIX_LOG(d,"trying to acquire new descriptor at 0x%x\n",rxdn_addr); #endif /* Get status of the next descriptor to see if we can acquire it */ rxdn_rdes0 = physmem_copy_u32_from_vm(d->vm,rxdn_addr); if (!rxdesc_acquire(rxdn_rdes0)) rxdc->rdes[0] = MUESLIX_RXDESC_LS | MUESLIX_RXDESC_OVERRUN; else rxdc->rdes[0] = 0x00000000; /* ok, no special flag */ rxdc->rdes[0] |= cp_len; /* Update the new status (only if we are not on the first desc) */ if (i != 0) physmem_copy_u32_to_vm(d->vm,channel->rx_current,rxdc->rdes[0]); /* Update the RX pointer */ channel->rx_current = rxdn_addr; if (rxdc->rdes[0] & MUESLIX_RXDESC_LS) break; /* Read the next descriptor from VM physical RAM */ rxdesc_read(d,rxdn_addr,&rxdn); rxdc = &rxdn; } /* Update the first RX descriptor */ rxd0.rdes[0] |= MUESLIX_RXDESC_FS; physmem_copy_u32_to_vm(d->vm,rx_start,rxd0.rdes[0]); /* Indicate that we have a frame ready (XXX something to do ?) */ /* Generate IRQ on CPU */ d->irq_status |= MUESLIX_RX_IRQ << channel->id; dev_mueslix_update_irq_status(d); } /* Handle the Mueslix RX ring of the specified channel */ static int dev_mueslix_handle_rxring(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, struct mueslix_channel *channel) { struct mueslix_data *d = channel->parent; #if DEBUG_RECEIVE MUESLIX_LOG(d,"channel %u: receiving a packet of %d bytes\n", channel->id,pkt_len); mem_dump(log_file,pkt,pkt_len); #endif MUESLIX_LOCK(d); if (dev_mueslix_is_rx_tx_enabled(d,channel->id) & MUESLIX_RX_ENABLE) dev_mueslix_receive_pkt(channel,pkt,pkt_len); MUESLIX_UNLOCK(d); return(TRUE); } /* Read a TX descriptor */ static void txdesc_read(struct mueslix_data *d,m_uint32_t txd_addr, struct tx_desc *txd) { /* get the next descriptor from VM physical RAM */ physmem_copy_from_vm(d->vm,txd,txd_addr,sizeof(struct tx_desc)); /* byte-swapping */ txd->tdes[0] = vmtoh32(txd->tdes[0]); txd->tdes[1] = vmtoh32(txd->tdes[1]); } /* Set the address of the next TX descriptor */ static void txdesc_set_next(struct mueslix_channel *channel) { switch(channel->parent->chip_mode) { case 0: channel->tx_current += sizeof(struct tx_desc); if (channel->tx_current == channel->tx_end) channel->tx_current = channel->tx_start; break; case 1: default: if (channel->tx_current == channel->tx_end) channel->tx_current = channel->tx_start; else channel->tx_current += sizeof(struct tx_desc); } } /* Handle the TX ring of a specific channel (single packet) */ static int dev_mueslix_handle_txring_single(struct mueslix_channel *channel) { struct mueslix_data *d = channel->parent; u_char pkt[MUESLIX_MAX_PKT_SIZE],*pkt_ptr; m_uint32_t tx_start,clen,sub_len,tot_len,pad; struct tx_desc txd0,ctxd,*ptxd; int done = FALSE; if ((channel->tx_start == 0) || (channel->status == 0)) return(FALSE); /* Check if the NIO can transmit */ if (!netio_can_transmit(channel->nio)) return(FALSE); /* Copy the current txring descriptor */ tx_start = channel->tx_current; ptxd = &txd0; txdesc_read(d,channel->tx_current,ptxd); /* If we don't own the descriptor, we cannot transmit */ if (!(txd0.tdes[0] & MUESLIX_TXDESC_OWN)) return(FALSE); #if DEBUG_TRANSMIT MUESLIX_LOG(d,"mueslix_handle_txring: 1st desc: " "tdes[0]=0x%x, tdes[1]=0x%x\n", ptxd->tdes[0],ptxd->tdes[1]); #endif pkt_ptr = pkt; tot_len = 0; do { #if DEBUG_TRANSMIT MUESLIX_LOG(d,"mueslix_handle_txring: loop: " "tdes[0]=0x%x, tdes[1]=0x%x\n", ptxd->tdes[0],ptxd->tdes[1]); #endif if (!(ptxd->tdes[0] & MUESLIX_TXDESC_OWN)) { MUESLIX_LOG(d,"mueslix_handle_txring: descriptor not owned!\n"); return(FALSE); } switch(channel->parent->chip_mode) { case 0: clen = ptxd->tdes[0] & MUESLIX_TXDESC_LEN_MASK; break; case 1: default: clen = (ptxd->tdes[0] & MUESLIX_TXDESC_LEN_MASK) << 2; if (ptxd->tdes[0] & MUESLIX_TXDESC_SUB) { sub_len = ptxd->tdes[0] & MUESLIX_TXDESC_SUB_LEN; sub_len >>= MUESLIX_TXDESC_SUB_SHIFT; clen -= sub_len; } } /* Be sure that we have length not null */ if (clen != 0) { //printf("pkt_ptr = %p, ptxd->tdes[1] = 0x%x, clen = %d\n", // pkt_ptr, ptxd->tdes[1], clen); physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->tdes[1],clen); } pkt_ptr += clen; tot_len += clen; /* Clear the OWN bit if this is not the first descriptor */ if (!(ptxd->tdes[0] & MUESLIX_TXDESC_FS)) physmem_copy_u32_to_vm(d->vm,channel->tx_current,0); /* Go to the next descriptor */ txdesc_set_next(channel); /* Copy the next txring descriptor */ if (!(ptxd->tdes[0] & MUESLIX_TXDESC_LS)) { txdesc_read(d,channel->tx_current,&ctxd); ptxd = &ctxd; } else done = TRUE; }while(!done); if (tot_len != 0) { #if DEBUG_TRANSMIT MUESLIX_LOG(d,"sending packet of %u bytes (flags=0x%4.4x)\n", tot_len,txd0.tdes[0]); mem_dump(log_file,pkt,tot_len); #endif pad = ptxd->tdes[0] & MUESLIX_TXDESC_PAD; pad >>= MUESLIX_TXDESC_PAD_SHIFT; tot_len -= (4 - pad) & 0x03; /* send it on wire */ netio_send(channel->nio,pkt,tot_len); } /* Clear the OWN flag of the first descriptor */ physmem_copy_u32_to_vm(d->vm,tx_start,0); /* Interrupt on completion ? */ d->irq_status |= MUESLIX_TX_IRQ << channel->id; dev_mueslix_update_irq_status(d); return(TRUE); } /* Handle the TX ring of a specific channel */ static int dev_mueslix_handle_txring(struct mueslix_channel *channel) { struct mueslix_data *d = channel->parent; int res,i; if (!dev_mueslix_is_rx_tx_enabled(d,channel->id) & MUESLIX_TX_ENABLE) return(FALSE); for(i=0;inio); return(TRUE); } /* pci_mueslix_read() */ static m_uint32_t pci_mueslix_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { struct mueslix_data *d = dev->priv_data; switch(reg) { case 0x08: /* Rev ID */ return(0x2800001); case PCI_REG_BAR0: return(d->dev->phys_addr); default: return(0); } } /* pci_mueslix_write() */ static void pci_mueslix_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct mueslix_data *d = dev->priv_data; switch(reg) { case PCI_REG_BAR0: vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); MUESLIX_LOG(d,"registers are mapped at 0x%x\n",value); break; } } /* Initialize a Mueslix chip */ struct mueslix_data * dev_mueslix_init(vm_instance_t *vm,char *name,int chip_mode, struct pci_bus *pci_bus,int pci_device,int irq) { struct pci_device *pci_dev; struct mueslix_data *d; struct vdevice *dev; int i; /* Allocate the private data structure for Mueslix chip */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"%s (Mueslix): out of memory\n",name); return NULL; } memset(d,0,sizeof(*d)); pthread_mutex_init(&d->lock,NULL); d->chip_mode = chip_mode; for(i=0;ichannel[i].id = i; d->channel[i].parent = d; } /* Add as PCI device */ pci_dev = pci_dev_add(pci_bus,name, MUESLIX_PCI_VENDOR_ID,MUESLIX_PCI_PRODUCT_ID, pci_device,0,irq, d,NULL,pci_mueslix_read,pci_mueslix_write); if (!pci_dev) { fprintf(stderr,"%s (Mueslix): unable to create PCI device.\n",name); return NULL; } /* Create the device itself */ if (!(dev = dev_create(name))) { fprintf(stderr,"%s (Mueslix): unable to create device.\n",name); return NULL; } d->name = name; d->pci_dev = pci_dev; d->vm = vm; dev->phys_addr = 0; dev->phys_len = 0x4000; dev->handler = dev_mueslix_access; dev->priv_data = d; /* Store device info */ dev->priv_data = d; d->dev = dev; return(d); } /* Remove a Mueslix device */ void dev_mueslix_remove(struct mueslix_data *d) { if (d != NULL) { pci_dev_remove(d->pci_dev); vm_unbind_device(d->vm,d->dev); cpu_group_rebuild_mts(d->vm->cpu_group); free(d->dev); free(d); } } /* Bind a NIO to a Mueslix channel */ int dev_mueslix_set_nio(struct mueslix_data *d,u_int channel_id, netio_desc_t *nio) { struct mueslix_channel *channel; if (channel_id >= MUESLIX_NR_CHANNELS) return(-1); channel = &d->channel[channel_id]; /* check that a NIO is not already bound */ if (channel->nio != NULL) return(-1); /* define the new NIO */ channel->nio = nio; channel->tx_tid = ptask_add((ptask_callback)dev_mueslix_handle_txring, channel,NULL); netio_rxl_add(nio,(netio_rx_handler_t)dev_mueslix_handle_rxring, channel,NULL); return(0); } /* Unbind a NIO from a Mueslix channel */ int dev_mueslix_unset_nio(struct mueslix_data *d,u_int channel_id) { struct mueslix_channel *channel; if (channel_id >= MUESLIX_NR_CHANNELS) return(-1); channel = &d->channel[channel_id]; if (channel->nio) { ptask_remove(channel->tx_tid); netio_rxl_remove(channel->nio); channel->nio = NULL; } return(0); } dynamips-0.2.14/common/dev_mueslix.h000066400000000000000000000015501241034141600174040ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __DEV_MUESLIX_H__ #define __DEV_MUESLIX_H__ #include #include "utils.h" #include "cpu.h" #include "vm.h" #include "device.h" #include "net_io.h" /* Number of channels (4 interfaces) */ #define MUESLIX_NR_CHANNELS 4 /* Initialize a Mueslix chip */ struct mueslix_data * dev_mueslix_init(vm_instance_t *vm,char *name,int chip_mode, struct pci_bus *pci_bus,int pci_device,int irq); /* Remove a Mueslix device */ void dev_mueslix_remove(struct mueslix_data *d); /* Bind a NIO to a Mueslix channel */ int dev_mueslix_set_nio(struct mueslix_data *d,u_int channel_id, netio_desc_t *nio); /* Unbind a NIO from a Mueslix channel */ int dev_mueslix_unset_nio(struct mueslix_data *d,u_int channel_id); #endif dynamips-0.2.14/common/dev_mv64460.c000066400000000000000000002417271241034141600167530ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Marvell MV64460 system controller. * * Based on GT9100 documentation and Linux kernel sources. */ #include #include #include #include "utils.h" #include "net.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net_io.h" #include "ptask.h" #include "dev_vtty.h" #include "dev_mv64460.h" /* Debugging flags */ #define DEBUG_ACCESS 0 #define DEBUG_UNKNOWN 0 #define DEBUG_DMA 0 #define DEBUG_MII 0 #define DEBUG_ETH_UNKNOWN 1 #define DEBUG_ETH_TX 1 #define DEBUG_ETH_RX 1 #define DEBUG_ETH_HASH 0 /* PCI identification */ #define PCI_VENDOR_MARVELL 0x11ab /* Marvell/Galileo */ #define PCI_PRODUCT_MARVELL_MV64460 0x6485 /* MV-64460 */ /* FIXME */ #define MV64460_MAX_PKT_SIZE 2048 /* Interrupt Low Main Cause Register */ #define MV64460_REG_ILMCR 0x0004 #define MV64460_ILMCR_IDMA0_COMP 0x00000010 /* IDMA 0 */ #define MV64460_ILMCR_IDMA1_COMP 0x00000020 /* IDMA 1 */ #define MV64460_ILMCR_IDMA2_COMP 0x00000040 /* IDMA 2 */ #define MV64460_ILMCR_IDMA3_COMP 0x00000080 /* IDMA 3 */ #define MV64460_ILMCR_TIMER0_EXP 0x00000100 /* Timer 0 */ #define MV64460_ILMCR_TIMER1_EXP 0x00000200 /* Timer 1 */ #define MV64460_ILMCR_TIMER2_EXP 0x00000400 /* Timer 2 */ #define MV64460_ILMCR_TIMER3_EXP 0x00000800 /* Timer 3 */ #define MV64460_ILMCR_P1_GPP_0_7_SUM 0x01000000 /* GPP 0-7 (CPU1) */ #define MV64460_ILMCR_P1_GPP_8_15_SUM 0x02000000 /* GPP 8-15 (CPU1) */ #define MV64460_ILMCR_P1_GPP_16_23_SUM 0x04000000 /* GPP 16-23 (CPU1) */ #define MV64460_ILMCR_P1_GPP_24_31_SUM 0x08000000 /* GPP 24-31 (CPU1) */ /* Interrupt High Main Cause Register */ #define MV64460_REG_IHMCR 0x000c #define MV64460_IHMCR_ETH0_SUM 0x00000001 /* Ethernet0 summary */ #define MV64460_IHMCR_ETH1_SUM 0x00000002 /* Ethernet1 summary */ #define MV64460_IHMCR_ETH2_SUM 0x00000004 /* Ethernet2 summary */ #define MV64460_IHMCR_SDMA0_SUM 0x00000010 /* Serial DMA0 */ #define MV64460_IHMCR_SDMA1_SUM 0x00000040 /* Serial DMA1 */ #define MV64460_IHMCR_MPSC0_SUM 0x00000100 /* MPSC0 */ #define MV64460_IHMCR_MPSC1_SUM 0x00000200 /* MPSC1 */ #define MV64460_IHMCR_ETH_RX_SUM(i) (0x00000400 << ((i) * 3)) #define MV64460_IHMCR_ETH_TX_SUM(i) (0x00000800 << ((i) * 3)) #define MV64460_IHMCR_ETH_MISC_SUM(i) (0x00001000 << ((i) * 3)) #define MV64460_IHMCR_P0_GPP_0_7_SUM 0x01000000 /* GPP 0-7 (CPU0) */ #define MV64460_IHMCR_P0_GPP_8_15_SUM 0x02000000 /* GPP 8-15 (CPU0) */ #define MV64460_IHMCR_P0_GPP_16_23_SUM 0x04000000 /* GPP 16-23 (CPU0) */ #define MV64460_IHMCR_P0_GPP_24_31_SUM 0x08000000 /* GPP 24-31 (CPU0) */ /* Interrupt registers: CPU_INTn[0], CPU_INTn[1], INT0n, INT1n */ #define MV64460_REG_CPU_INTN0_MASK_LO 0x0014 #define MV64460_REG_CPU_INTN0_MASK_HI 0x001c #define MV64460_REG_CPU_INTN0_SEL_CAUSE 0x0024 #define MV64460_REG_CPU_INTN1_MASK_LO 0x0034 #define MV64460_REG_CPU_INTN1_MASK_HI 0x003c #define MV64460_REG_CPU_INTN1_SEL_CAUSE 0x0044 #define MV64460_REG_INT0N_MASK_LO 0x0054 #define MV64460_REG_INT0N_MASK_HI 0x005c #define MV64460_REG_INT0N_SEL_CAUSE 0x0064 #define MV64460_REG_INT1N_MASK_LO 0x0074 #define MV64460_REG_INT1N_MASK_HI 0x007c #define MV64460_REG_INT1N_SEL_CAUSE 0x0084 #define MV64460_IC_CAUSE_MASK 0x3FFFFFFF /* Shadow of cause register */ #define MV64460_IC_CAUSE_SEL 0x40000000 /* Low or High register */ #define MV64460_IC_CAUSE_STAT 0x80000000 /* Interrupt status */ /* GPP Interrupt cause and mask registers */ #define MV64460_REG_GPP_INTR_CAUSE 0xf108 #define MV64460_REG_GPP_INTR_MASK0 0xf10c #define MV64460_REG_GPP_INTR_MASK1 0xf114 #define MV64460_REG_GPP_VALUE_SET 0xf118 #define MV64460_REG_GPP_VALUE_CLEAR 0xf11c /* TX Descriptor */ #define MV64460_TXDESC_OWN 0x80000000 /* Ownership */ #define MV64460_TXDESC_AM 0x40000000 /* Auto-mode */ #define MV64460_TXDESC_EI 0x00800000 /* Enable Interrupt */ #define MV64460_TXDESC_GC 0x00400000 /* Generate CRC */ #define MV64460_TXDESC_P 0x00040000 /* Padding */ #define MV64460_TXDESC_F 0x00020000 /* First buffer of packet */ #define MV64460_TXDESC_L 0x00010000 /* Last buffer of packet */ #define MV64460_TXDESC_ES 0x00008000 /* Error Summary */ #define MV64460_TXDESC_RC 0x00003c00 /* Retransmit Count */ #define MV64460_TXDESC_COL 0x00000200 /* Collision */ #define MV64460_TXDESC_RL 0x00000100 /* Retransmit Limit Error */ #define MV64460_TXDESC_UR 0x00000040 /* Underrun Error */ #define MV64460_TXDESC_LC 0x00000020 /* Late Collision Error */ #define MV64460_TXDESC_BC_MASK 0xFFFF0000 /* Number of bytes to transmit */ #define MV64460_TXDESC_BC_SHIFT 16 /* RX Descriptor */ #define MV64460_RXDESC_OWN 0x80000000 /* Ownership */ #define MV64460_RXDESC_AM 0x40000000 /* Auto-mode */ #define MV64460_RXDESC_EI 0x00800000 /* Enable Interrupt */ #define MV64460_RXDESC_F 0x00020000 /* First buffer of packet */ #define MV64460_RXDESC_L 0x00010000 /* Last buffer of packet */ #define MV64460_RXDESC_ES 0x00008000 /* Error Summary */ #define MV64460_RXDESC_IGMP 0x00004000 /* IGMP packet detected */ #define MV64460_RXDESC_HE 0x00002000 /* Hash Table Expired */ #define MV64460_RXDESC_M 0x00001000 /* Missed Frame */ #define MV64460_RXDESC_FT 0x00000800 /* Frame Type (802.3/Ethernet) */ #define MV64460_RXDESC_SF 0x00000100 /* Short Frame Error */ #define MV64460_RXDESC_MFL 0x00000080 /* Maximum Frame Length Error */ #define MV64460_RXDESC_OR 0x00000040 /* Overrun Error */ #define MV64460_RXDESC_COL 0x00000010 /* Collision */ #define MV64460_RXDESC_CE 0x00000001 /* CRC Error */ #define MV64460_RXDESC_BC_MASK 0x0000FFFF /* Byte count */ #define MV64460_RXDESC_BS_MASK 0xFFFF0000 /* Buffer size */ #define MV64460_RXDESC_BS_SHIFT 16 /* === IDMA =============================================================== */ #define MV64460_IDMA_CHANNELS 4 /* Address decoding registers */ #define MV64460_IDMA_BAR_REGS 8 /* 8 BAR registers */ #define MV64460_IDMA_HAR_REGS 4 /* High address remap for BAR0-3 */ #define MV64460_REG_IDMA_DEC_BASE 0x0A00 #define MV64460_REG_IDMA_BAR(x) (0x0A00 + ((x) << 3)) #define MV64460_REG_IDMA_SR(x) (0x0A04 + ((x) << 3)) #define MV64460_REG_IDMA_HAR(x) (0x0A60 + ((x) << 2)) #define MV64460_REG_IDMA_CAP(x) (0x0A70 + ((x) << 2)) #define MV64460_REG_IDMA_BARE 0x0A80 /* Channel registers */ #define MV64460_REG_IDMA_BASE 0x0800 #define MV64460_REG_IDMA_BC(x) (0x0800 + ((x) << 2)) #define MV64460_REG_IDMA_SRC(x) (0x0810 + ((x) << 2)) #define MV64460_REG_IDMA_DST(x) (0x0820 + ((x) << 2)) #define MV64460_REG_IDMA_NDP(x) (0x0830 + ((x) << 2)) #define MV64460_REG_IDMA_CDP(x) (0x0870 + ((x) << 2)) #define MV64460_REG_IDMA_CTRL_LO(x) (0x0840 + ((x) << 2)) #define MV64460_REG_IDMA_CTRL_HI(x) (0x0880 + ((x) << 2)) /* Interrupt registers */ #define MV64460_REG_IDMA_IC 0x08c0 #define MV64460_REG_IDMA_IM 0x08c4 /* Galileo IDMA channel */ struct idma_channel { m_uint32_t byte_count; m_uint32_t src_addr; m_uint32_t dst_addr; m_uint32_t cdptr; m_uint32_t nrptr; m_uint32_t ctrl_lo,ctrl_hi; m_uint32_t cap; }; /* === Serial DMA (SDMA) ================================================== */ /* SDMA - number of channels */ #define MV64460_SDMA_CHANNELS 2 /* SDMA channel */ struct sdma_channel { u_int id; m_uint32_t sdc; m_uint32_t sdcm; m_uint32_t rx_desc; m_uint32_t rx_buf_ptr; m_uint32_t scrdp; m_uint32_t tx_desc; m_uint32_t sctdp; m_uint32_t sftdp; }; /* SDMA registers base offsets */ #define MV64460_REG_SDMA0 0x4000 #define MV64460_REG_SDMA1 0x6000 /* SDMA cause and mask registers */ #define MV64460_REG_SDMA_CAUSE 0xb800 #define MV64460_REG_SDMA_MASK 0xb880 #define MV64460_SDMA_CAUSE_SDMA0 0x0000000F #define MV64460_SDMA_CAUSE_SDMA1 0x00000F00 #define MV64460_SDMA_CAUSE_RXBUF0 0x00000001 /* RX Buffer returned */ #define MV64460_SDMA_CAUSE_RXERR0 0x00000002 /* RX Error */ #define MV64460_SDMA_CAUSE_TXBUF0 0x00000004 /* TX Buffer returned */ #define MV64460_SDMA_CAUSE_TXEND0 0x00000008 /* TX End */ #define MV64460_SDMA_CAUSE_RXBUF1 0x00000100 /* RX Buffer returned */ #define MV64460_SDMA_CAUSE_RXERR1 0x00000200 /* RX Error */ #define MV64460_SDMA_CAUSE_TXBUF1 0x00000400 /* TX Buffer returned */ #define MV64460_SDMA_CAUSE_TXEND1 0x00000800 /* TX End */ /* SDMA register offsets (per channel) */ #define MV64460_SDMA_SDC 0x0000 /* Configuration Register */ #define MV64460_SDMA_SDCM 0x0008 /* Command Register */ #define MV64460_SDMA_RX_DESC 0x0800 /* RX descriptor */ #define MV64460_SDMA_SCRDP 0x0810 /* Current RX descriptor */ #define MV64460_SDMA_TX_DESC 0x0c00 /* TX descriptor */ #define MV64460_SDMA_SCTDP 0x0c10 /* Current TX desc. pointer */ #define MV64460_SDMA_SFTDP 0x0c14 /* First TX desc. pointer */ /* SDCR: SDMA Configuration Register */ #define MV64460_SDCR_RFT 0x00000001 /* Receive FIFO Threshold */ #define MV64460_SDCR_SFM 0x00000002 /* Single Frame Mode */ #define MV64460_SDCR_RC 0x0000003c /* Retransmit count */ #define MV64460_SDCR_BLMR 0x00000040 /* Big/Little Endian RX mode */ #define MV64460_SDCR_BLMT 0x00000080 /* Big/Litlle Endian TX mode */ #define MV64460_SDCR_POVR 0x00000100 /* PCI override */ #define MV64460_SDCR_RIFB 0x00000200 /* RX IRQ on frame boundary */ #define MV64460_SDCR_BSZ 0x00003000 /* Burst size */ /* SDCMR: SDMA Command Register */ #define MV64460_SDCMR_ERD 0x00000080 /* Enable RX DMA */ #define MV64460_SDCMR_AR 0x00008000 /* Abort Receive */ #define MV64460_SDCMR_STD 0x00010000 /* Stop TX */ #define MV64460_SDCMR_STDH MV64460_SDCMR_STD /* Stop TX High */ #define MV64460_SDCMR_STDL 0x00020000 /* Stop TX Low */ #define MV64460_SDCMR_TXD 0x00800000 /* TX Demand */ #define MV64460_SDCMR_TXDH MV64460_SDCMR_TXD /* Start TX High */ #define MV64460_SDCMR_TXDL 0x01000000 /* Start TX Low */ #define MV64460_SDCMR_AT 0x80000000 /* Abort Transmit */ /* SDMA RX/TX descriptor */ struct sdma_desc { m_uint32_t buf_size; m_uint32_t cmd_stat; m_uint32_t next_ptr; m_uint32_t buf_ptr; }; /* SDMA Descriptor Command/Status word */ #define MV64460_SDMA_CMD_O 0x80000000 /* Owner bit */ #define MV64460_SDMA_CMD_AM 0x40000000 /* Auto-mode */ #define MV64460_SDMA_CMD_EI 0x00800000 /* Enable Interrupt */ #define MV64460_SDMA_CMD_F 0x00020000 /* First buffer */ #define MV64460_SDMA_CMD_L 0x00010000 /* Last buffer */ /* === MultiProtocol Serial Controller (MPSC) ============================= */ /* 2 MPSC channels */ #define MV64460_MPSC_CHANNELS 2 /* MPSC channel */ struct mpsc_channel { m_uint32_t mmcrl; m_uint32_t mmcrh; m_uint32_t mpcr; m_uint32_t chr[10]; vtty_t *vtty; netio_desc_t *nio; }; /* MPSC registers base offsets */ #define MV64460_REG_MPSC0 0x8000 #define MV64460_REG_MPSC1 0x9000 #define MV64460_MPSC_MMCRL 0x0000 /* Main Config Register Low */ #define MV64460_MPSC_MMCRH 0x0004 /* Main Config Register High */ #define MV64460_MPSC_MPCR 0x0008 /* Protocol Config Register */ #define MV64460_MPSC_CHR1 0x000C #define MV64460_MPSC_CHR2 0x0010 #define MV64460_MPSC_CHR3 0x0014 #define MV64460_MPSC_CHR4 0x0018 #define MV64460_MPSC_CHR5 0x001C #define MV64460_MPSC_CHR6 0x0020 #define MV64460_MPSC_CHR7 0x0024 #define MV64460_MPSC_CHR8 0x0028 #define MV64460_MPSC_CHR9 0x002C #define MV64460_MPSC_CHR10 0x0030 #define MV64460_MMCRL_MODE_MASK 0x0000007 #define MV64460_MPSC_MODE_HDLC 0 #define MV64460_MPSC_MODE_UART 4 #define MV64460_MPSC_MODE_BISYNC 5 /* === Gigabit Ethernet Ports ============================================= */ #define MV64460_ETH_PORTS 3 #define MV64460_ETH_RX_QUEUES 8 #define MV64460_ETH_TX_QUEUES 8 #define MV64460_REG_ETH_START 0x2000 #define MV64460_REG_ETH_END 0x4000 /* Base Address and Size Registers */ #define MV64460_ETH_WINDOWS 6 #define MV64460_REG_ETH_BARE 0x2290 /* Base Address Enable */ #define MV64460_REG_ETH_BA(i) (0x2200 + ((i) << 3)) #define MV64460_REG_ETH_SR(i) (0x2204 + ((i) << 3)) /* Port access protect */ #define MV64460_REG_ETH_EPAP(i) (0x2294 + ((i) << 2)) /* Default PHY addresses */ #define MV64460_ETH_PHY0_ADDR 0x08 #define MV64460_ETH_PHY1_ADDR 0x09 #define MV64460_ETH_PHY2_ADDR 0x0A /* SMI register */ #define MV64460_ETH_SMI_DATA_MASK 0x0000FFFF #define MV64460_ETH_SMI_PHYAD_MASK 0x001F0000 /* PHY Device Address */ #define MV64460_ETH_SMI_PHYAD_SHIFT 16 #define MV64460_ETH_SMI_REGAD_MASK 0x03e00000 /* PHY Device Reg Addr */ #define MV64460_ETH_SMI_REGAD_SHIFT 21 #define MV64460_ETH_SMI_OPCODE_MASK 0x04000000 /* Op (0: write, 1: read) */ #define MV64460_ETH_SMI_OPCODE_READ 0x04000000 #define MV64460_ETH_SMI_RVALID_FLAG 0x08000000 /* Read Valid */ #define MV64460_ETH_SMI_BUSY_FLAG 0x10000000 /* Busy: 1=in progress */ #define MV64460_REG_ETH_PHY_ADDR 0x2000 #define MV64460_REG_ETH_SMI 0x2004 /* Port registers */ #define MV64460_REG_ETH_PCR 0x2400 /* Port Configuration */ #define MV64460_REG_ETH_PCXR 0x2404 /* Port Configuration Extend */ #define MV64460_REG_ETH_EVLANE 0x2410 /* VLAN Ethertype */ #define MV64460_REG_ETH_MACAL 0x2414 /* MAC Address Low */ #define MV64460_REG_ETH_MACAH 0x2418 /* MAC Address High */ #define MV64460_REG_ETH_SDCR 0x241c /* SDMA Configuration */ #define MV64460_REG_ETH_TQC 0x2448 /* Transmit Queue Command */ #define MV64460_REG_ETH_IC 0x2460 /* Port Interrupt Cause */ #define MV64460_REG_ETH_ICE 0x2464 /* Port Interrupt Cause Extend */ #define MV64460_REG_ETH_PIM 0x2468 /* Port Interrupt Mask */ #define MV64460_REG_ETH_PEIM 0x246c /* Port Extend Interrupt Mask */ #define MV64460_REG_ETH_RQC 0x2680 /* Receive Queue Command */ #define MV64460_REG_ETH_CRDP(i) (0x260c + ((i) << 4)) #define MV64460_REG_ETH_TCQDP(i) (0x26c0 + ((i) << 2)) /* Transmit Command Queue */ #define MV64460_ETH_TQC_ENQ(i) (0x0001 << (i)) #define MV64460_ETH_TQC_DISQ(i) (0x0100 << (i)) /* Receive Command Queue */ #define MV64460_ETH_RQC_ENQ(i) (0x0001 << (i)) #define MV64460_ETH_RQC_DISQ(i) (0x0100 << (i)) /* MIB registers */ #define MV64460_REG_ETH_MIB_GOOD_RX_BYTES 0x3000 #define MV64460_REG_ETH_MIB_GOOD_RX_FRAMES 0x3010 #define MV64460_REG_ETH_MIB_GOOD_TX_BYTES 0x3038 #define MV64460_REG_ETH_MIB_GOOD_TX_FRAMES 0x3040 /* Port Configuration Register (PCR) */ #define MV64460_ETH_PCR_UPM 0x00000001 /* Promiscuous */ #define MV64460_ETH_PCR_RXQ_MASK 0x0000000E /* Defaut RX queue */ #define MV64460_ETH_PCR_RXQ_SHIFT 1 #define MV64460_ETH_PCR_RXQ_ARP_MASK 0x00000070 /* Defaut RX queue */ #define MV64460_ETH_PCR_RXQ_ARP_SHIFT 4 #define MV64460_ETH_PCR_RB 0x00000080 /* Reject Broadcast */ #define MV64460_ETH_PCR_RBIP 0x00000100 /* Reject IP Broadcast */ #define MV64460_ETH_PCR_RBARP 0x00000200 /* Reject ARP Broadcast */ #define MV64460_ETH_PCR_TCP_CAPEN 0x00004000 /* TCP capture enable */ #define MV64460_ETH_PCR_UDP_CAPEN 0x00008000 /* TCP capture enable */ #define MV64460_ETH_PCR_TCPQ_MASK 0x00070000 /* TCP Queue */ #define MV64460_ETH_PCR_TCPQ_SHIFT 16 #define MV64460_ETH_PCR_UDPQ_MASK 0x00380000 /* UDP Queue */ #define MV64460_ETH_PCR_UDPQ_SHIFT 19 #define MV64460_ETH_PCR_BPDUQ_MASK 0x01C00000 /* UDP Queue */ #define MV64460_ETH_PCR_BPDUQ_SHIFT 22 #define MV64460_ETH_PCR_RXCS 0x02000000 /* RX TCP Checksum mode */ /* Summary masks */ #define MV64460_ETH_IC_SUM_MASK 0x7FFFFFFF #define MV64460_ETH_ICE_SUM_MASK 0x7FFFFFFF /* RX, TX and Misc masks */ #define MV64460_ETH_INT_MISC_ICE_MASK 0x00F10000 #define MV64460_ETH_INT_RX_IC_MASK 0x0007FFFC #define MV64460_ETH_INT_RX_ICE_MASK 0x00060000 #define MV64460_ETH_INT_TX_IC_MASK 0x7FF80000 #define MV64460_ETH_INT_TX_ICE_MASK 0x0008FFFF /* Port Interrupt Cause */ #define MV64460_ETH_IC_RXBUF 0x00000001 #define MV64460_ETH_IC_EXTEND 0x00000002 #define MV64460_ETH_IC_RXBUFQ(i) (0x00000004 << (i)) #define MV64460_ETH_IC_RXERR 0x00000400 #define MV64460_ETH_IC_RXERRQ(i) (0x00000800 << (i)) #define MV64460_ETH_IC_TXEND(i) (0x00080000 << (i)) #define MV64460_ETH_IC_SUM 0x80000000 /* Port Interrupt Cause Extend */ #define MV64460_ETH_ICE_TXBUF(i) (0x00000001 << (i)) #define MV64460_ETH_ICE_TXERR(i) (0x00000100 << (i)) #define MV64460_ETH_ICE_PHY_STC 0x00010000 #define MV64460_ETH_ICE_RXOVR 0x00040000 #define MV64460_ETH_ICE_TXUDR 0x00080000 #define MV64460_ETH_ICE_LNKC 0x00100000 #define MV64460_ETH_ICE_PART 0x00200000 #define MV64460_ETH_ICE_ANDONE 0x00400000 #define MV64460_ETH_ICE_IAERR 0x00800000 #define MV64460_ETH_ICE_SUM 0x80000000 /* Ethernet TX Descriptor */ #define MV64460_ETH_TXDESC_ES 0x00000001 /* Error summary */ #define MV64460_ETH_TXDESC_LLC 0x00000200 /* LLC/SNAP */ #define MV64460_ETH_TXDESC_L4CHK_MODE 0x00000400 /* L4 Cksum Mode */ #define MV64460_ETH_TXDESC_IPHLEN_MASK 0x00007800 /* IP Header Length */ #define MV64460_ETH_TXDESC_IPHLEN_SHIFT 11 #define MV64460_ETH_TXDESC_VLAN 0x00008000 /* VLAN tag */ #define MV64460_ETH_TXDESC_L4_TYPE 0x00010000 /* L4 Cksum Mode */ #define MV64460_ETH_TXDESC_GL4CHK 0x00020000 /* TCP/UDP checksum */ #define MV64460_ETH_TXDESC_GIPCHK 0x00040000 /* IP checksum */ #define MV64460_ETH_TXDESC_P 0x00080000 /* Padding */ #define MV64460_ETH_TXDESC_L 0x00100000 /* Last buffer */ #define MV64460_ETH_TXDESC_F 0x00200000 /* First buffer */ #define MV64460_ETH_TXDESC_GC 0x00400000 /* Ethernet CRC */ #define MV64460_ETH_TXDESC_EI 0x00800000 /* Enable Interrupt */ #define MV64460_ETH_TXDESC_AM 0x40000000 /* Auto mode */ #define MV64460_ETH_TXDESC_OWN 0x80000000 /* OWNer (1=DMA,0=CPU) */ #define MV64460_ETH_TXDESC_BC_MASK 0xFFFF0000 /* TX Byte Count */ #define MV64460_ETH_TXDESC_BC_SHIFT 16 /* Ethernet RX Descriptor */ #define MV64460_ETH_RXDESC_ES 0x00000001 /* Error summary */ #define MV64460_ETH_RXDESC_L4CHK_MASK 0x0007FFF8 /* L4 Checksum */ #define MV64460_ETH_RXDESC_L4CHK_SHIFT 3 #define MV64460_ETH_RXDESC_VLAN 0x00080000 /* VLAN tag */ #define MV64460_ETH_RXDESC_BPDU 0x00100000 /* BPDU */ #define MV64460_ETH_RXDESC_L4P_MASK 0x00600000 /* L4 Protocol */ #define MV64460_ETH_RXDESC_L4P_SHIFT 21 #define MV64460_ETH_RXDESC_L2V2 0x00800000 /* Layer 2 - EthV2 */ #define MV64460_ETH_RXDESC_L3IP 0x01000000 /* Layer 3 - IP */ #define MV64460_ETH_RXDESC_IPH_OK 0x02000000 /* IP Header OK */ #define MV64460_ETH_RXDESC_L 0x04000000 /* Last Buffer */ #define MV64460_ETH_RXDESC_F 0x08000000 /* First Buffer */ #define MV64460_ETH_RXDESC_U 0x10000000 /* Unknown Dst Addr */ #define MV64460_ETH_RXDESC_EI 0x20000000 /* Enable Interrupt */ #define MV64460_ETH_RXDESC_L4CHK_OK 0x40000000 /* L4 Cksum OK */ #define MV64460_ETH_RXDESC_OWN 0x80000000 /* OWNer (1=DMA,0=CPU) */ #define MV64460_ETH_RXDESC_L4P_TCP 0x00 #define MV64460_ETH_RXDESC_L4P_UDP 0x01 #define MV64460_ETH_RXDESC_L4P_OTHER 0x02 #define MV64460_ETH_RXDESC_BC_MASK 0xFFFF0000 /* Byte count */ #define MV64460_ETH_RXDESC_BC_SHIFT 16 #define MV64460_ETH_RXDESC_BS_MASK 0x0000FFF8 /* Buffer size */ #define MV64460_ETH_RXDESC_BS_SHIFT 0 #define MV64460_ETH_RXDESC_IPV4_FRG 0x00000004 /* IPv4 fragmented */ /* Ethernet port */ struct eth_port { u_int id; netio_desc_t *nio; m_uint32_t pcr,pcxr,sdcr; m_uint16_t vlan_ether_type; /* MAC Address */ n_eth_addr_t mac_addr; /* RX and TX Queues */ m_uint32_t rqc,tqc; m_uint32_t crdp[MV64460_ETH_RX_QUEUES]; m_uint32_t tcqdp[MV64460_ETH_TX_QUEUES]; /* Interrupt Registers */ m_uint32_t pic,pice,pim,peim; /* MIB counters */ m_uint64_t mib_good_rx_bytes; m_uint32_t mib_good_rx_frames; m_uint64_t mib_good_tx_bytes; m_uint32_t mib_good_tx_frames; }; /* === Integrated SRAM ==================================================== */ #define MV64460_SRAM_WIDTH 18 #define MV64460_SRAM_SIZE (1 << MV64460_SRAM_WIDTH) #define MV64460_REG_SRAM_BASE 0x268 /* SRAM base register */ #define MV64460_SRAM_BASE_MASK 0x000FFFFC #define MV64460_SRAM_BASE_SHIFT 2 /* ======================================================================== */ /* MV64460 system controller private data */ struct mv64460_data { char *name; vm_obj_t vm_obj; struct vdevice dev; struct pci_device *pci_dev; vm_instance_t *vm; pthread_mutex_t lock; /* Interrupt controller registers */ m_uint32_t intr_lo,intr_hi; m_uint32_t cpu_intn0_mask_lo,cpu_intn0_mask_hi; m_uint32_t cpu_intn1_mask_lo,cpu_intn1_mask_hi; m_uint32_t int0n_mask_lo,int0n_mask_hi; m_uint32_t int1n_mask_lo,int1n_mask_hi; /* GPP interrupts */ m_uint32_t gpp_intr,gpp_mask0,gpp_mask1; /* IDMA registers */ m_uint32_t idma_ic,idma_im,idma_bare; m_uint32_t idma_bar[MV64460_IDMA_BAR_REGS]; m_uint32_t idma_sr[MV64460_IDMA_BAR_REGS]; m_uint32_t idma_har[MV64460_IDMA_HAR_REGS]; struct idma_channel idma[MV64460_IDMA_CHANNELS]; /* SDMA channels */ m_uint32_t sdma_cause,sdma_mask; struct sdma_channel sdma[MV64460_SDMA_CHANNELS]; /* MPSC - MultiProtocol Serial Controller */ struct mpsc_channel mpsc[MV64460_MPSC_CHANNELS]; /* Ethernet ports */ ptask_id_t eth_tx_tid; m_uint32_t eth_bare; m_uint32_t eth_ba[MV64460_ETH_WINDOWS]; m_uint32_t eth_sr[MV64460_ETH_WINDOWS]; m_uint32_t eth_pap[MV64460_ETH_PORTS]; m_uint16_t eth_phy_addr; m_uint32_t smi_reg; m_uint16_t mii_regs[32][32]; struct eth_port eth_ports[MV64460_ETH_PORTS]; /* Integrated SRAM */ struct vdevice sram_dev; /* PCI busses */ struct pci_bus *bus[2]; }; #define MV64460_LOCK(d) pthread_mutex_lock(&(d)->lock) #define MV64460_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) /* Log a GT message */ #define MV64460_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* ======================================================================== */ /* Forward declarations */ /* ======================================================================== */ static u_int mv64460_mpsc_get_channel_mode(struct mv64460_data *d,u_int id); /* ======================================================================== */ /* General interrupt control */ /* ======================================================================== */ /* Returns a select cause register */ static m_uint32_t mv64460_ic_get_sel_cause(struct mv64460_data *d, m_uint32_t mask_lo, m_uint32_t mask_hi) { int lo_act,hi_act; m_uint32_t res; lo_act = d->intr_lo & mask_lo; hi_act = d->intr_hi & mask_hi; if (!lo_act && hi_act) { res = (d->intr_hi & MV64460_IC_CAUSE_MASK) | MV64460_IC_CAUSE_SEL; } else { res = d->intr_lo & MV64460_IC_CAUSE_MASK; if (lo_act && hi_act) res |= MV64460_IC_CAUSE_STAT; } return(res); } /* Update the interrupt status for CPU 0 */ static void mv64460_ic_update_cpu0_status(struct mv64460_data *d) { cpu_ppc_t *cpu0 = CPU_PPC32(d->vm->boot_cpu); m_uint32_t lo_act,hi_act; lo_act = d->intr_lo & d->cpu_intn0_mask_lo; hi_act = d->intr_hi & d->cpu_intn0_mask_hi; cpu0->irq_pending = lo_act || hi_act; cpu0->irq_check = cpu0->irq_pending; } /* Update GPIO interrupt status */ static void mv64460_gpio_update_int_status(struct mv64460_data *d) { /* GPIO 0-7 */ if (d->gpp_intr & d->gpp_mask0 & 0x000000FF) d->intr_hi |= MV64460_IHMCR_P0_GPP_0_7_SUM; else d->intr_hi &= ~MV64460_IHMCR_P0_GPP_0_7_SUM; /* GPIO 8-15 */ if (d->gpp_intr & d->gpp_mask0 & 0x0000FF00) d->intr_hi |= MV64460_IHMCR_P0_GPP_8_15_SUM; else d->intr_hi &= ~MV64460_IHMCR_P0_GPP_8_15_SUM; /* GPIO 16-23 */ if (d->gpp_intr & d->gpp_mask0 & 0x00FF0000) d->intr_hi |= MV64460_IHMCR_P0_GPP_16_23_SUM; else d->intr_hi &= ~MV64460_IHMCR_P0_GPP_16_23_SUM; /* GPIO 24-32 */ if (d->gpp_intr & d->gpp_mask0 & 0xFF000000) d->intr_hi |= MV64460_IHMCR_P0_GPP_24_31_SUM; else d->intr_hi &= ~MV64460_IHMCR_P0_GPP_24_31_SUM; mv64460_ic_update_cpu0_status(d); } /* ======================================================================== */ /* IDMA */ /* ======================================================================== */ /* * IDMA channel registers access. */ static int mv64460_idma_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct mv64460_data *mv_data = dev->priv_data; int cid; if ((offset & 0xFF00) != MV64460_REG_IDMA_BASE) return(FALSE); switch(offset) { case MV64460_REG_IDMA_IM: if (op_type == MTS_READ) *data = mv_data->idma_im; else mv_data->idma_im = *data; break; case MV64460_REG_IDMA_BC(0): case MV64460_REG_IDMA_BC(1): case MV64460_REG_IDMA_BC(2): case MV64460_REG_IDMA_BC(3): cid = (offset - MV64460_REG_IDMA_BC(0)) >> 2; if (op_type == MTS_READ) *data = mv_data->idma[cid].byte_count; else mv_data->idma[cid].byte_count = *data; break; case MV64460_REG_IDMA_SRC(0): case MV64460_REG_IDMA_SRC(1): case MV64460_REG_IDMA_SRC(2): case MV64460_REG_IDMA_SRC(3): cid = (offset - MV64460_REG_IDMA_SRC(0)) >> 2; if (op_type == MTS_READ) *data = mv_data->idma[cid].src_addr; else mv_data->idma[cid].src_addr = *data; break; case MV64460_REG_IDMA_DST(0): case MV64460_REG_IDMA_DST(1): case MV64460_REG_IDMA_DST(2): case MV64460_REG_IDMA_DST(3): cid = (offset - MV64460_REG_IDMA_DST(0)) >> 2; if (op_type == MTS_READ) *data = mv_data->idma[cid].dst_addr; else mv_data->idma[cid].dst_addr = *data; break; case MV64460_REG_IDMA_NDP(0): case MV64460_REG_IDMA_NDP(1): case MV64460_REG_IDMA_NDP(2): case MV64460_REG_IDMA_NDP(3): cid = (offset - MV64460_REG_IDMA_NDP(0)) >> 2; if (op_type == MTS_READ) *data = mv_data->idma[cid].nrptr; else mv_data->idma[cid].nrptr = *data; break; case MV64460_REG_IDMA_CDP(0): case MV64460_REG_IDMA_CDP(1): case MV64460_REG_IDMA_CDP(2): case MV64460_REG_IDMA_CDP(3): cid = (offset - MV64460_REG_IDMA_CDP(0)) >> 2; if (op_type == MTS_READ) *data = mv_data->idma[cid].cdptr; else mv_data->idma[cid].cdptr = *data; break; case MV64460_REG_IDMA_CTRL_LO(0): case MV64460_REG_IDMA_CTRL_LO(1): case MV64460_REG_IDMA_CTRL_LO(2): case MV64460_REG_IDMA_CTRL_LO(3): cid = (offset - MV64460_REG_IDMA_CTRL_LO(0)) >> 2; if (op_type == MTS_READ) *data = mv_data->idma[cid].ctrl_lo; else mv_data->idma[cid].ctrl_lo = *data; break; case MV64460_REG_IDMA_CTRL_HI(0): case MV64460_REG_IDMA_CTRL_HI(1): case MV64460_REG_IDMA_CTRL_HI(2): case MV64460_REG_IDMA_CTRL_HI(3): cid = (offset - MV64460_REG_IDMA_CTRL_HI(0)) >> 2; if (op_type == MTS_READ) *data = mv_data->idma[cid].ctrl_hi; else mv_data->idma[cid].ctrl_hi = *data; break; default: return(FALSE); } return(TRUE); } /* * IDMA decode registers access. */ static int mv64460_idma_dec_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size, u_int op_type, m_uint64_t *data) { struct mv64460_data *mv_data = dev->priv_data; int reg; if ((offset & 0xFF00) != MV64460_REG_IDMA_DEC_BASE) return(FALSE); switch(offset) { case MV64460_REG_IDMA_BARE: if (op_type == MTS_READ) *data = mv_data->idma_bare; else mv_data->idma_bare = *data; break; case MV64460_REG_IDMA_BAR(0): case MV64460_REG_IDMA_BAR(1): case MV64460_REG_IDMA_BAR(2): case MV64460_REG_IDMA_BAR(3): case MV64460_REG_IDMA_BAR(4): case MV64460_REG_IDMA_BAR(5): case MV64460_REG_IDMA_BAR(6): case MV64460_REG_IDMA_BAR(7): reg = (offset - MV64460_REG_IDMA_BAR(0)) >> 3; if (op_type == MTS_READ) *data = mv_data->idma_bar[reg]; else mv_data->idma_bar[reg] = *data; break; case MV64460_REG_IDMA_SR(0): case MV64460_REG_IDMA_SR(1): case MV64460_REG_IDMA_SR(2): case MV64460_REG_IDMA_SR(3): case MV64460_REG_IDMA_SR(4): case MV64460_REG_IDMA_SR(5): case MV64460_REG_IDMA_SR(6): case MV64460_REG_IDMA_SR(7): reg = (offset - MV64460_REG_IDMA_SR(0)) >> 3; if (op_type == MTS_READ) *data = mv_data->idma_sr[reg]; else mv_data->idma_sr[reg] = *data; break; case MV64460_REG_IDMA_HAR(0): case MV64460_REG_IDMA_HAR(1): case MV64460_REG_IDMA_HAR(2): case MV64460_REG_IDMA_HAR(3): reg = (offset - MV64460_REG_IDMA_HAR(0)) >> 2; if (op_type == MTS_READ) *data = mv_data->idma_har[reg]; else mv_data->idma_har[reg] = *data; break; case MV64460_REG_IDMA_CAP(0): case MV64460_REG_IDMA_CAP(1): case MV64460_REG_IDMA_CAP(2): case MV64460_REG_IDMA_CAP(3): reg = (offset - MV64460_REG_IDMA_CAP(0)) >> 2; if (op_type == MTS_READ) *data = mv_data->idma[reg].cap; else mv_data->idma[reg].cap = *data; break; default: return(FALSE); } return(TRUE); } /* ======================================================================== */ /* SDMA (Serial DMA) */ /* ======================================================================== */ /* Update SDMA interrupt status */ static void mv64460_sdma_update_int_status(struct mv64460_data *d) { if (d->sdma_cause & d->sdma_mask & MV64460_SDMA_CAUSE_SDMA0) { d->intr_hi |= MV64460_IHMCR_SDMA0_SUM; } else { d->intr_hi &= ~MV64460_IHMCR_SDMA0_SUM; } if (d->sdma_cause & d->sdma_mask & MV64460_SDMA_CAUSE_SDMA1) { d->intr_hi |= MV64460_IHMCR_SDMA1_SUM; } else { d->intr_hi &= ~MV64460_IHMCR_SDMA1_SUM; } mv64460_ic_update_cpu0_status(d); } /* Set SDMA cause register for a channel */ static inline void mv64460_sdma_set_cause(struct mv64460_data *d,u_int chan_id, u_int value) { d->sdma_cause |= value << (chan_id << 3); } /* Read a SDMA descriptor from memory */ static void mv64460_sdma_desc_read(struct mv64460_data *d,m_uint32_t addr, struct sdma_desc *desc) { physmem_copy_from_vm(d->vm,desc,addr,sizeof(struct sdma_desc)); /* byte-swapping */ desc->buf_size = vmtoh32(desc->buf_size); desc->cmd_stat = vmtoh32(desc->cmd_stat); desc->next_ptr = vmtoh32(desc->next_ptr); desc->buf_ptr = vmtoh32(desc->buf_ptr); } /* Write a SDMA descriptor to memory */ static void mv64460_sdma_desc_write(struct mv64460_data *d,m_uint32_t addr, struct sdma_desc *desc) { struct sdma_desc tmp; /* byte-swapping */ tmp.cmd_stat = vmtoh32(desc->cmd_stat); tmp.buf_size = vmtoh32(desc->buf_size); tmp.next_ptr = vmtoh32(desc->next_ptr); tmp.buf_ptr = vmtoh32(desc->buf_ptr); physmem_copy_to_vm(d->vm,&tmp,addr,sizeof(struct sdma_desc)); } /* Send contents of a SDMA buffer */ static void mv64460_sdma_send_buffer(struct mv64460_data *d,u_int chan_id, u_char *buffer,m_uint32_t len) { struct mpsc_channel *channel; u_int mode; channel = &d->mpsc[chan_id]; mode = mv64460_mpsc_get_channel_mode(d,chan_id); switch(mode) { case MV64460_MPSC_MODE_HDLC: if (channel->nio != NULL) netio_send(channel->nio,buffer,len); break; case MV64460_MPSC_MODE_UART: if (channel->vtty != NULL) vtty_put_buffer(channel->vtty,(char *)buffer,len); break; } } /* Start TX DMA process */ static int mv64460_sdma_tx_start(struct mv64460_data *d, struct sdma_channel *chan) { u_char pkt[MV64460_MAX_PKT_SIZE],*pkt_ptr; struct sdma_desc txd0,ctxd,*ptxd; m_uint32_t tx_start,tx_current; m_uint32_t len,tot_len; int abort = FALSE; tx_start = tx_current = chan->sctdp; if (!tx_start) return(FALSE); ptxd = &txd0; mv64460_sdma_desc_read(d,tx_start,ptxd); /* If we don't own the first descriptor, we cannot transmit */ if (!(txd0.cmd_stat & MV64460_TXDESC_OWN)) return(FALSE); /* Empty packet for now */ pkt_ptr = pkt; tot_len = 0; for(;;) { /* Copy packet data to the buffer */ len = ptxd->buf_size & MV64460_TXDESC_BC_MASK; len >>= MV64460_TXDESC_BC_SHIFT; physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->buf_ptr,len); pkt_ptr += len; tot_len += len; /* Clear the OWN bit if this is not the first descriptor */ if (!(ptxd->cmd_stat & MV64460_TXDESC_F)) { ptxd->cmd_stat &= ~MV64460_TXDESC_OWN; physmem_copy_u32_to_vm(d->vm,tx_current+4,ptxd->cmd_stat); } //ptxd->buf_size &= 0xFFFF0000; //physmem_copy_u32_to_vm(d->vm,tx_current,ptxd->buf_size); tx_current = ptxd->next_ptr; /* Last descriptor or no more desc available ? */ if (ptxd->cmd_stat & MV64460_TXDESC_L) break; if (!tx_current) { abort = TRUE; break; } /* Fetch the next descriptor */ mv64460_sdma_desc_read(d,tx_current,&ctxd); ptxd = &ctxd; } if ((tot_len != 0) && !abort) { #if DEBUG_SDMA MV64460_LOG(d,"SDMA%u: sending packet of %u bytes\n",tot_len); mem_dump(log_file,pkt,tot_len); #endif /* send it on wire */ mv64460_sdma_send_buffer(d,chan->id,pkt,tot_len); /* Signal that a TX buffer has been transmitted */ mv64460_sdma_set_cause(d,chan->id,MV64460_SDMA_CAUSE_TXBUF0); } /* Clear the OWN flag of the first descriptor */ txd0.cmd_stat &= ~MV64460_TXDESC_OWN; physmem_copy_u32_to_vm(d->vm,tx_start+4,txd0.cmd_stat); chan->sctdp = tx_current; if (abort || !tx_current) { mv64460_sdma_set_cause(d,chan->id,MV64460_SDMA_CAUSE_TXEND0); chan->sdcm &= ~MV64460_SDCMR_TXD; } /* Update interrupt status */ mv64460_sdma_update_int_status(d); return(TRUE); } /* Put a packet in buffer of a descriptor */ static void mv64460_sdma_rxdesc_put_pkt(struct mv64460_data *d, struct sdma_desc *rxd, u_char **pkt,ssize_t *pkt_len) { ssize_t len,cp_len; len = (rxd->buf_size & MV64460_RXDESC_BS_MASK) >> MV64460_RXDESC_BS_SHIFT; /* compute the data length to copy */ cp_len = m_min(len,*pkt_len); /* copy packet data to the VM physical RAM */ physmem_copy_to_vm(d->vm,*pkt,rxd->buf_ptr,cp_len); /* set the byte count in descriptor */ rxd->buf_size |= cp_len; *pkt += cp_len; *pkt_len -= cp_len; } /* Put a packet into SDMA buffers */ static int mv64460_sdma_handle_rxqueue(struct mv64460_data *d, struct sdma_channel *channel, u_char *pkt,ssize_t pkt_len) { m_uint32_t rx_start,rx_current; struct sdma_desc rxd0,rxdn,*rxdc; ssize_t tot_len = pkt_len; u_char *pkt_ptr = pkt; int i; /* Truncate the packet if it is too big */ pkt_len = m_min(pkt_len,MV64460_MAX_PKT_SIZE); /* Copy the first RX descriptor */ if (!(rx_start = rx_current = channel->scrdp)) goto dma_error; /* Load the first RX descriptor */ mv64460_sdma_desc_read(d,rx_start,&rxd0); #if DEBUG_SDMA MV64460_LOG(d,"SDMA channel %u: reading desc at 0x%8.8x " "[buf_size=0x%8.8x,cmd_stat=0x%8.8x," "next_ptr=0x%8.8x,buf_ptr=0x%8.8x]\n", channel->id,rx_start,rxd0.buf_size,rxd0.cmd_stat, rxd0.next_ptr,rxd0.buf_ptr); #endif for(i=0,rxdc=&rxd0;tot_len>0;i++) { /* We must own the descriptor */ if (!(rxdc->cmd_stat & MV64460_RXDESC_OWN)) goto dma_error; /* Put data into the descriptor buffer */ mv64460_sdma_rxdesc_put_pkt(d,rxdc,&pkt_ptr,&tot_len); /* Clear the OWN bit */ rxdc->cmd_stat &= ~MV64460_RXDESC_OWN; /* We have finished if the complete packet has been stored */ if (tot_len == 0) { rxdc->cmd_stat |= MV64460_RXDESC_L; /* Fake HDLC CRC */ if (mv64460_mpsc_get_channel_mode(d,channel->id) == MV64460_MPSC_MODE_HDLC) { rxdc->buf_size += 2; /* Add 2 bytes for CRC */ } } /* Update the descriptor in host memory (but not the 1st) */ if (i != 0) mv64460_sdma_desc_write(d,rx_current,rxdc); /* Get address of the next descriptor */ rx_current = rxdc->next_ptr; if (tot_len == 0) break; if (!rx_current) goto dma_error; /* Read the next descriptor from VM physical RAM */ mv64460_sdma_desc_read(d,rx_current,&rxdn); rxdc = &rxdn; } /* Update the RX pointers */ channel->scrdp = rx_current; /* Update the first RX descriptor */ rxd0.cmd_stat |= MV64460_RXDESC_F; mv64460_sdma_desc_write(d,rx_start,&rxd0); /* Indicate that we have a frame ready */ mv64460_sdma_set_cause(d,channel->id,MV64460_SDMA_CAUSE_RXBUF0); mv64460_sdma_update_int_status(d); return(TRUE); dma_error: mv64460_sdma_set_cause(d,channel->id,MV64460_SDMA_CAUSE_RXERR0); mv64460_sdma_update_int_status(d); return(FALSE); } /* Handle RX packet for a SDMA channel */ _unused static int mv64460_sdma_handle_rx_pkt(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, struct mv64460_data *d,void *arg) { u_int chan_id = (u_int)(u_long)arg; MV64460_LOCK(d); mv64460_sdma_handle_rxqueue(d,&d->sdma[chan_id],pkt,pkt_len); MV64460_UNLOCK(d); return(TRUE); } /* Input on VTTY */ static void mv64460_sdma_vtty_input(vtty_t *vtty) { struct mv64460_data *d = vtty->priv_data; struct sdma_channel *chan = &d->sdma[vtty->user_arg]; u_char c; c = vtty_get_char(vtty); mv64460_sdma_handle_rxqueue(d,chan,&c,1); } /* Bind a VTTY to a SDMA/MPSC channel */ int mv64460_sdma_bind_vtty(struct mv64460_data *d,u_int chan_id,vtty_t *vtty) { if (chan_id >= MV64460_MPSC_CHANNELS) return(-1); vtty->priv_data = d; vtty->user_arg = chan_id; vtty->read_notifier = mv64460_sdma_vtty_input; d->mpsc[chan_id].vtty = vtty; return(0); } /* * SDMA registers access. */ static int mv64460_sdma_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct mv64460_data *mv_data = dev->priv_data; struct sdma_channel *channel; int id = -1; /* Access to SDMA channel 0 registers ? */ if ((offset >= MV64460_REG_SDMA0) && (offset < (MV64460_REG_SDMA0 + 0x1000))) { offset -= MV64460_REG_SDMA0; id = 0; } /* Access to SDMA channel 1 registers ? */ if ((offset >= MV64460_REG_SDMA1) && (offset < (MV64460_REG_SDMA1 + 0x1000))) { offset -= MV64460_REG_SDMA1; id = 1; } if (id == -1) return(FALSE); channel = &mv_data->sdma[id]; switch(offset) { case MV64460_SDMA_SDCM: if (op_type == MTS_READ) ; //*data = chan->sdcm; else { channel->sdcm = *data; if (channel->sdcm & MV64460_SDCMR_TXD) { while(mv64460_sdma_tx_start(mv_data,channel)) ; } } break; case MV64460_SDMA_SCRDP: if (op_type == MTS_READ) *data = channel->scrdp; else channel->scrdp = *data; break; case MV64460_SDMA_SCTDP: if (op_type == MTS_READ) *data = channel->sctdp; else channel->sctdp = *data; break; case MV64460_SDMA_SFTDP: if (op_type == MTS_READ) *data = channel->sftdp; else channel->sftdp = *data; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"MV64460/SDMA", "read access to unknown register 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"MV64460/SDMA", "write access to unknown register 0x%x, value=0x%llx, " "pc=0x%llx\n",offset,*data,cpu_get_pc(cpu)); } #endif } return(TRUE); } /* ======================================================================== */ /* MPSC (MultiProtocol Serial Controller) */ /* ======================================================================== */ /* Get mode (HDLC,UART,...) of the specified channel */ static u_int mv64460_mpsc_get_channel_mode(struct mv64460_data *d,u_int id) { struct mpsc_channel *channel; channel = &d->mpsc[id]; return(channel->mmcrl & MV64460_MMCRL_MODE_MASK); } /* Handle a MPSC channel */ static int mv64460_mpsc_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct mv64460_data *mv_data = dev->priv_data; struct mpsc_channel *channel; u_int reg; int id = -1; /* Access to MPSC channel 0 registers ? */ if ((offset >= MV64460_REG_MPSC0) && (offset < (MV64460_REG_MPSC0 + 0x1000))) { offset -= MV64460_REG_MPSC0; id = 0; } /* Access to SDMA channel 1 registers ? */ if ((offset >= MV64460_REG_MPSC1) && (offset < (MV64460_REG_MPSC1 + 0x1000))) { offset -= MV64460_REG_MPSC1; id = 1; } if (id == -1) return(FALSE); channel = &mv_data->mpsc[id]; switch(offset) { /* Main Config Register Low */ case MV64460_MPSC_MMCRL: if (op_type == MTS_READ) { *data = channel->mmcrl; } else { #if DEBUG_MPSC MV64460_LOG(gt_data,"MPSC channel %u set in mode %llu\n", chan_id,*data & 0x07); #endif channel->mmcrl = *data; } break; /* Main Config Register High */ case MV64460_MPSC_MMCRH: if (op_type == MTS_READ) *data = channel->mmcrh; else channel->mmcrh = *data; break; /* Protocol Config Register */ case MV64460_MPSC_MPCR: if (op_type == MTS_READ) *data = channel->mpcr; else channel->mpcr = *data; break; /* Channel registers */ case MV64460_MPSC_CHR1: case MV64460_MPSC_CHR2: case MV64460_MPSC_CHR3: case MV64460_MPSC_CHR4: case MV64460_MPSC_CHR5: case MV64460_MPSC_CHR6: case MV64460_MPSC_CHR7: case MV64460_MPSC_CHR8: case MV64460_MPSC_CHR9: //case MV64460_MPSC_CHR10: reg = (offset - MV64460_MPSC_CHR1) >> 2; if (op_type == MTS_READ) *data = channel->chr[reg]; else channel->chr[reg] = *data; break; case MV64460_MPSC_CHR10: if (op_type == MTS_READ) *data = channel->chr[9] | 0x20; else channel->chr[9] = *data; break; default: /* unknown/unmanaged register */ return(FALSE); } return(TRUE); } /* ======================================================================== */ /* Gigabit Ethernet Controller */ /* ======================================================================== */ /* Update the interrupt status for port interrupt register */ static void mv64460_eth_update_pic(struct mv64460_data *d, struct eth_port *port) { if (port->pic & port->pim & MV64460_ETH_IC_SUM_MASK) { port->pic |= MV64460_ETH_IC_SUM; d->intr_hi |= MV64460_IHMCR_ETH0_SUM << port->id; } else { port->pic &= ~MV64460_ETH_IC_SUM; d->intr_hi &= ~(MV64460_IHMCR_ETH0_SUM << port->id); } /* Update the various summary bits in high cause register */ if ((port->pic & MV64460_ETH_INT_RX_IC_MASK) || (port->pice & MV64460_ETH_INT_RX_ICE_MASK)) { d->intr_hi |= MV64460_IHMCR_ETH_RX_SUM(port->id); } else { d->intr_hi &= ~(MV64460_IHMCR_ETH_RX_SUM(port->id)); } if ((port->pic & MV64460_ETH_INT_TX_IC_MASK) || (port->pice & MV64460_ETH_INT_TX_ICE_MASK)) { d->intr_hi |= MV64460_IHMCR_ETH_TX_SUM(port->id); } else { d->intr_hi &= ~(MV64460_IHMCR_ETH_TX_SUM(port->id)); } if (port->pice & MV64460_ETH_INT_MISC_ICE_MASK) { d->intr_hi |= MV64460_IHMCR_ETH_MISC_SUM(port->id); } else { d->intr_hi &= ~(MV64460_IHMCR_ETH_MISC_SUM(port->id)); } /* Update the interrupt status */ mv64460_ic_update_cpu0_status(d); } /* Update the interrupt status for port interrupt extend register */ static void mv64460_eth_update_pice(struct mv64460_data *d, struct eth_port *port) { if (port->pice & port->peim & MV64460_ETH_ICE_SUM_MASK) { port->pice |= MV64460_ETH_ICE_SUM; } else { port->pice &= ~MV64460_ETH_ICE_SUM; } if (port->pice) { port->pic |= MV64460_ETH_IC_EXTEND; } else { port->pic &= ~MV64460_ETH_IC_EXTEND; } mv64460_eth_update_pic(d,port); } /* Handle a TX queue (single packet) */ static int mv64460_eth_handle_port_txqueue(struct mv64460_data *d, struct eth_port *port, int queue) { u_char pkt[MV64460_MAX_PKT_SIZE],*pkt_ptr; struct sdma_desc txd0,ctxd,*ptxd; m_uint32_t tx_start,tx_current; m_uint32_t len,tot_len; int abort = FALSE; /* Check if this TX queue is enabled */ if (!(port->tqc & MV64460_ETH_TQC_ENQ(queue))) return(FALSE); /* Copy the current txring descriptor */ tx_start = tx_current = port->tcqdp[queue]; if (!tx_start) return(FALSE); ptxd = &txd0; mv64460_sdma_desc_read(d,tx_start,ptxd); /* If we don't own the first descriptor, we cannot transmit */ if (!(txd0.cmd_stat & MV64460_ETH_TXDESC_OWN)) return(FALSE); /* Empty packet for now */ pkt_ptr = pkt; tot_len = 0; for(;;) { #if DEBUG_ETH_TX MV64460_LOG(d,"mv64460_eth_handle_txqueue: loop: " "cmd_stat=0x%x, buf_size=0x%x, " "next_ptr=0x%x, buf_ptr=0x%x\n", ptxd->cmd_stat,ptxd->buf_size, ptxd->next_ptr,ptxd->buf_ptr); #endif if (!(ptxd->cmd_stat & MV64460_ETH_TXDESC_OWN)) { MV64460_LOG(d,"mv64460_eth_handle_txqueue: descriptor not owned!\n"); abort = TRUE; break; } /* Copy packet data to the buffer */ len = ptxd->buf_size & MV64460_ETH_TXDESC_BC_MASK; len >>= MV64460_ETH_TXDESC_BC_SHIFT; physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->buf_ptr,len); pkt_ptr += len; tot_len += len; /* Clear the OWN bit if this is not the first descriptor */ if (!(ptxd->cmd_stat & MV64460_ETH_TXDESC_F)) { ptxd->cmd_stat &= ~MV64460_ETH_TXDESC_OWN; physmem_copy_u32_to_vm(d->vm,tx_current,ptxd->cmd_stat); } tx_current = ptxd->next_ptr; /* Last descriptor or no more desc available ? */ if (ptxd->cmd_stat & MV64460_ETH_TXDESC_L) break; if (!tx_current) { abort = TRUE; break; } /* Fetch the next descriptor */ mv64460_sdma_desc_read(d,tx_current,&ctxd); ptxd = &ctxd; } if ((tot_len != 0) && !abort) { #if DEBUG_ETH_TX MV64460_LOG(d,"Eth%u: sending packet of %u bytes\n",port->id,tot_len); mem_dump(log_file,pkt,tot_len); #endif /* send it on wire */ netio_send(port->nio,pkt,tot_len); /* Update MIB counters */ port->mib_good_tx_bytes += tot_len; port->mib_good_tx_frames++; } /* Clear the OWN flag of the first descriptor */ txd0.cmd_stat &= ~MV64460_ETH_TXDESC_OWN; physmem_copy_u32_to_vm(d->vm,tx_start+4,txd0.cmd_stat); port->tcqdp[queue] = tx_current; /* Notify host about transmitted packet */ port->pice |= MV64460_ETH_ICE_TXBUF(queue); if (abort) { /* TX underrun */ port->pice |= MV64460_ETH_ICE_TXUDR; port->pice |= MV64460_ETH_ICE_TXERR(queue); } else { /* End of queue has been reached */ if (!tx_current) port->pic |= MV64460_ETH_IC_TXEND(queue); } /* Update the interrupt status */ mv64460_eth_update_pice(d,port); return(TRUE); } /* Handle all TX queues of the specified port */ static void mv64460_eth_handle_port_txqueues(struct mv64460_data *d,u_int port) { int i; for(i=0;ieth_ports[port],i); } /* Handle all TX queues of all Ethernet ports */ static int mv64460_eth_handle_txqueues(struct mv64460_data *d) { int i; MV64460_LOCK(d); for(i=0;ibuf_ptr; len = rxd->buf_size & MV64460_ETH_RXDESC_BS_MASK; len >>= MV64460_ETH_RXDESC_BS_SHIFT; /* copy packet data to the VM physical RAM */ if (pos == 0) { buf_ptr += 2; len -= 2; } /* compute the data length to copy */ cp_len = m_min(len,*pkt_len); /* copy packet data to the VM physical RAM */ physmem_copy_to_vm(d->vm,*pkt,buf_ptr,cp_len); *pkt += cp_len; *pkt_len -= cp_len; } /* Put a packet in the specified RX queue */ static int mv64460_eth_handle_rxqueue(struct mv64460_data *d,u_int port_id, u_int queue,n_pkt_ctx_t *ctx) { struct eth_port *port = &d->eth_ports[port_id]; m_uint32_t rx_start,rx_current; struct sdma_desc rxd0,rxdn,*rxdc; ssize_t tot_len = ctx->pkt_len; u_char *pkt_ptr = ctx->pkt; u_int ip_proto; int i; /* Copy the first RX descriptor */ if (!(rx_start = rx_current = port->crdp[queue])) goto dma_error; /* Load the first RX descriptor */ mv64460_sdma_desc_read(d,rx_start,&rxd0); #if DEBUG_ETH_RX MV64460_LOG(d,"port %u/queue %u: reading desc at 0x%8.8x " "[buf_size=0x%8.8x,cmd_stat=0x%8.8x," "next_ptr=0x%8.8x,buf_ptr=0x%8.8x]\n", port_id,queue,rx_start, rxd0.buf_size,rxd0.cmd_stat,rxd0.next_ptr,rxd0.buf_ptr); #endif for(i=0,rxdc=&rxd0;tot_len>0;i++) { /* We must own the descriptor */ if (!(rxdc->cmd_stat & MV64460_ETH_RXDESC_OWN)) goto dma_error; /* Put data into the descriptor buffer */ mv64460_eth_rxdesc_put_pkt(d,rxdc,&pkt_ptr,&tot_len,i); /* Clear the OWN bit */ rxdc->cmd_stat &= ~MV64460_ETH_RXDESC_OWN; /* We have finished if the complete packet has been stored */ if (tot_len == 0) rxdc->cmd_stat |= MV64460_ETH_RXDESC_L; /* Update the descriptor in host memory (but not the 1st) */ if (i != 0) mv64460_sdma_desc_write(d,rx_current,rxdc); /* Get address of the next descriptor */ rx_current = rxdc->next_ptr; if (tot_len == 0) break; if (!rx_current) goto dma_error; /* Read the next descriptor from VM physical RAM */ mv64460_sdma_desc_read(d,rx_current,&rxdn); rxdc = &rxdn; } /* Update the RX pointer */ port->crdp[queue] = rx_current; /* Update the first RX descriptor */ rxd0.cmd_stat |= MV64460_ETH_RXDESC_F; /* Analyze Layer 2 */ if (ctx->flags & N_PKT_CTX_FLAG_ETHV2) rxd0.cmd_stat |= MV64460_ETH_RXDESC_L2V2; if (ctx->flags & N_PKT_CTX_FLAG_VLAN) rxd0.cmd_stat |= MV64460_ETH_RXDESC_VLAN; /* Analyze Layer 3 */ if (ctx->flags & N_PKT_CTX_FLAG_L3_IP) { rxd0.cmd_stat |= MV64460_ETH_RXDESC_L3IP; if (ctx->flags & N_PKT_CTX_FLAG_IPH_OK) { u_int rxcs,frag; m_uint16_t cksum; rxd0.cmd_stat |= MV64460_ETH_RXDESC_IPH_OK; if (ctx->flags & N_PKT_CTX_FLAG_IP_FRAG) { rxd0.buf_size |= MV64460_ETH_RXDESC_IPV4_FRG; frag = TRUE; } else { frag = FALSE; } rxcs = port->pcr & MV64460_ETH_PCR_RXCS; /* Set Layer 4 protocol info */ switch(ctx->ip_l4_proto) { case N_IP_PROTO_TCP: ip_proto = MV64460_ETH_RXDESC_L4P_TCP; if (!frag && rxcs) { cksum = pkt_ctx_tcp_cksum(ctx,TRUE); if (cksum == ntohs(ctx->tcp->cksum)) rxd0.cmd_stat |= MV64460_ETH_RXDESC_L4CHK_OK; } else { cksum = pkt_ctx_tcp_cksum(ctx,FALSE); } /* set the computed checksum */ rxd0.cmd_stat |= cksum << MV64460_ETH_RXDESC_L4CHK_SHIFT; break; case N_IP_PROTO_UDP: ip_proto = MV64460_ETH_RXDESC_L4P_UDP; if (!frag && rxcs) { cksum = pkt_ctx_tcp_cksum(ctx,TRUE); if (!ctx->udp->cksum || (cksum == ntohs(ctx->udp->cksum))) rxd0.cmd_stat |= MV64460_ETH_RXDESC_L4CHK_OK; } else { cksum = pkt_ctx_tcp_cksum(ctx,FALSE); } /* set the computed checksum */ rxd0.cmd_stat |= cksum << MV64460_ETH_RXDESC_L4CHK_SHIFT; break; default: ip_proto = MV64460_ETH_RXDESC_L4P_OTHER; } rxd0.cmd_stat |= ip_proto << MV64460_ETH_RXDESC_L4P_SHIFT; } } /* Set the buffer size */ tot_len = ctx->pkt_len + N_ETH_CRC_LEN + 2; rxd0.buf_size &= ~MV64460_ETH_RXDESC_BC_MASK; rxd0.buf_size |= tot_len << MV64460_ETH_RXDESC_BC_SHIFT; mv64460_sdma_desc_write(d,rx_start,&rxd0); /* Update MIB counters */ port->mib_good_rx_bytes += ctx->pkt_len; port->mib_good_rx_frames++; /* Indicate that we have a frame ready */ port->pic |= MV64460_ETH_IC_RXBUFQ(queue) | MV64460_ETH_IC_RXBUF; mv64460_eth_update_pic(d,port); return(TRUE); dma_error: port->pic |= MV64460_ETH_IC_RXERRQ(queue) | MV64460_ETH_IC_RXERR; mv64460_eth_update_pic(d,port); return(FALSE); } /* Handle RX packet for an Ethernet port */ static int mv64460_eth_handle_rx_pkt(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, struct mv64460_data *d,void *arg) { u_int queue,port_id = (u_int)(u_long)arg; struct eth_port *port; n_pkt_ctx_t ctx; port = &d->eth_ports[port_id]; MV64460_LOCK(d); queue = 0; /* At this time, only put packet in queue 0 */ pkt_ctx_analyze(&ctx,pkt,pkt_len); /* Check if queue is active */ if (!(port->rqc & MV64460_ETH_RQC_ENQ(queue))) { MV64460_UNLOCK(d); return(FALSE); } mv64460_eth_handle_rxqueue(d,port_id,queue,&ctx); MV64460_UNLOCK(d); return(TRUE); } /* Read a MII register */ static m_uint32_t mv64460_eth_mii_read(struct mv64460_data *d) { m_uint32_t port,reg; m_uint32_t res = 0; port = (d->smi_reg & MV64460_ETH_SMI_PHYAD_MASK); port >>= MV64460_ETH_SMI_PHYAD_SHIFT; reg = (d->smi_reg & MV64460_ETH_SMI_REGAD_MASK); reg >>= MV64460_ETH_SMI_REGAD_SHIFT; #if DEBUG_MII MV64460_LOG(d,"MII: port 0x%4.4x, reg 0x%2.2x: reading.\n",port,reg); #endif if (reg < 32) { res = d->mii_regs[port][reg]; switch(reg) { case 0x00: res &= ~0x8200; /* clear reset bit and autoneg restart */ res |= 0x2100; break; case 0x01: #if 0 if (d->ports[port].nio && bcm5600_mii_port_status(d,port)) d->mii_output = 0x782C; else d->mii_output = 0; #endif res = 0x782c; break; case 0x02: res = 0x0141; break; case 0x03: res = 0x0cd4; break; case 0x04: res = 0x1E1; break; case 0x05: res = 0x41E1; break; case 0x06: res = 0x0001; break; case 0x11: res = 0x4700; break; case 0x19: res = 0x800F; break; default: res = 0x0000; } } /* Mark the data as ready */ res |= MV64460_ETH_SMI_RVALID_FLAG; return(res); } /* Write a MII register */ static void mv64460_eth_mii_write(struct mv64460_data *d) { m_uint32_t port,reg; m_uint16_t isolation; port = (d->smi_reg & MV64460_ETH_SMI_PHYAD_MASK); port >>= MV64460_ETH_SMI_PHYAD_SHIFT; reg = (d->smi_reg & MV64460_ETH_SMI_REGAD_MASK); reg >>= MV64460_ETH_SMI_REGAD_SHIFT; if (reg < 32) { #if DEBUG_MII MV64460_LOG(d,"MII: port 0x%4.4x, reg 0x%2.2x: writing 0x%4.4x\n", port,reg,d->smi_reg & MV64460_ETH_SMI_DATA_MASK); #endif /* Check if PHY isolation status is changing */ if (reg == 0) { isolation = (d->smi_reg ^ d->mii_regs[port][reg]) & 0x400; if (isolation) { #if DEBUG_MII MV64460_LOG(d,"MII: port 0x%4.4x: generating IRQ\n",port); #endif printf("FIRING !!!\n"); d->eth_ports[port].pice |= MV64460_ETH_ICE_PHY_STC; mv64460_eth_update_pice(d,&d->eth_ports[port]); } } d->mii_regs[port][reg] = d->smi_reg & MV64460_ETH_SMI_DATA_MASK; } } /* Handle Ethernet registers */ static int mv64460_eth_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct mv64460_data *mv_data = dev->priv_data; struct eth_port *port; u_int group,reg; int port_id; if ((offset < MV64460_REG_ETH_START) || (offset >= MV64460_REG_ETH_END)) return(FALSE); switch(offset) { case MV64460_REG_ETH_BARE: if (op_type == MTS_READ) *data = mv_data->eth_bare; else mv_data->eth_bare = *data; break; case MV64460_REG_ETH_BA(0): case MV64460_REG_ETH_BA(1): case MV64460_REG_ETH_BA(2): case MV64460_REG_ETH_BA(3): case MV64460_REG_ETH_BA(4): case MV64460_REG_ETH_BA(5): reg = (offset - MV64460_REG_ETH_BA(0)) >> 3; if (op_type == MTS_READ) *data = mv_data->eth_ba[reg]; else mv_data->eth_ba[reg] = *data; break; case MV64460_REG_ETH_SR(0): case MV64460_REG_ETH_SR(1): case MV64460_REG_ETH_SR(2): case MV64460_REG_ETH_SR(3): case MV64460_REG_ETH_SR(4): case MV64460_REG_ETH_SR(5): reg = (offset - MV64460_REG_ETH_SR(0)) >> 3; if (op_type == MTS_READ) *data = mv_data->eth_sr[reg]; else mv_data->eth_sr[reg] = *data; break; case MV64460_REG_ETH_EPAP(0): case MV64460_REG_ETH_EPAP(1): case MV64460_REG_ETH_EPAP(2): reg = (offset - MV64460_REG_ETH_EPAP(0)) >> 2; if (op_type == MTS_READ) *data = mv_data->eth_pap[reg]; else mv_data->eth_pap[reg] = *data; break; case MV64460_REG_ETH_PHY_ADDR: if (op_type == MTS_READ) *data = mv_data->eth_phy_addr; else mv_data->eth_phy_addr = *data; break; case MV64460_REG_ETH_SMI: if (op_type == MTS_WRITE) { mv_data->smi_reg = *data; if (!(mv_data->smi_reg & MV64460_ETH_SMI_OPCODE_READ)) mv64460_eth_mii_write(mv_data); } else { *data = 0; if (mv_data->smi_reg & MV64460_ETH_SMI_OPCODE_READ) *data = mv64460_eth_mii_read(mv_data); } break; } /* Handle port-specific registers */ group = offset >> 8; switch(group) { case 0x24: case 0x26: case 0x27: port_id = 0; break; case 0x28: case 0x2A: case 0x2B: port_id = 1; break; case 0x2C: case 0x2E: case 0x2F: port_id = 2; break; case 0x30: case 0x31: port_id = (offset >> 7) & 0x03; offset -= (port_id * 0x80); break; default: port_id = -1; } if ((port_id == -1) || (port_id >= MV64460_ETH_PORTS)) return(TRUE); if ((group & 0xF0) != 0x30) offset -= (port_id * 0x0400); port = &mv_data->eth_ports[port_id]; switch(offset) { case 0x2444: if (op_type == MTS_READ) *data = 0x27; break; case 0x243c: if (op_type == MTS_READ) *data = 0x1; break; case MV64460_REG_ETH_PCR: if (op_type == MTS_READ) *data = port->pcr; else port->pcr = *data; break; case MV64460_REG_ETH_PCXR: if (op_type == MTS_READ) *data = port->pcxr; else port->pcxr = *data; break; case MV64460_REG_ETH_EVLANE: if (op_type == MTS_READ) *data = port->vlan_ether_type; else port->vlan_ether_type = *data; break; case MV64460_REG_ETH_MACAL: if (op_type == MTS_READ) { *data = port->mac_addr.eth_addr_byte[4] << 8; *data |= port->mac_addr.eth_addr_byte[5]; } else { port->mac_addr.eth_addr_byte[4] = *data >> 8; port->mac_addr.eth_addr_byte[5] = *data & 0xFF; } break; case MV64460_REG_ETH_MACAH: if (op_type == MTS_READ) { *data = port->mac_addr.eth_addr_byte[0] << 24; *data |= port->mac_addr.eth_addr_byte[1] << 16; *data |= port->mac_addr.eth_addr_byte[2] << 8; *data |= port->mac_addr.eth_addr_byte[3]; } else { port->mac_addr.eth_addr_byte[0] = *data >> 24; port->mac_addr.eth_addr_byte[1] = *data >> 16; port->mac_addr.eth_addr_byte[2] = *data >> 8; port->mac_addr.eth_addr_byte[3] = *data & 0xFF; } break; case MV64460_REG_ETH_SDCR: if (op_type == MTS_READ) *data = port->sdcr; else port->sdcr = *data; break; case MV64460_REG_ETH_TQC: if (op_type == MTS_READ) { *data = port->tqc; } else { int i; for(i=0;itqc |= MV64460_ETH_TQC_ENQ(i); port->tqc &= ~MV64460_ETH_TQC_DISQ(i); } if (*data & MV64460_ETH_TQC_DISQ(i)) { port->tqc &= ~MV64460_ETH_TQC_ENQ(i); port->tqc |= MV64460_ETH_TQC_DISQ(i); } } } break; case MV64460_REG_ETH_RQC: if (op_type == MTS_READ) { *data = port->rqc; } else { int i; for(i=0;irqc |= MV64460_ETH_RQC_ENQ(i); port->rqc &= ~MV64460_ETH_RQC_DISQ(i); } if (*data & MV64460_ETH_RQC_DISQ(i)) { port->rqc &= ~MV64460_ETH_RQC_ENQ(i); port->rqc |= MV64460_ETH_RQC_DISQ(i); } } } break; case MV64460_REG_ETH_IC: if (op_type == MTS_READ) { *data = port->pic; } else { port->pic &= *data; mv64460_eth_update_pic(mv_data,port); } break; case MV64460_REG_ETH_ICE: if (op_type == MTS_READ) { *data = port->pice; } else { port->pice &= *data; mv64460_eth_update_pice(mv_data,port); } break; case MV64460_REG_ETH_PIM: if (op_type == MTS_READ) *data = port->pim; else port->pim = *data; break; case MV64460_REG_ETH_PEIM: if (op_type == MTS_READ) *data = port->peim; else port->peim = *data; break; case MV64460_REG_ETH_CRDP(0): case MV64460_REG_ETH_CRDP(1): case MV64460_REG_ETH_CRDP(2): case MV64460_REG_ETH_CRDP(3): case MV64460_REG_ETH_CRDP(4): case MV64460_REG_ETH_CRDP(5): case MV64460_REG_ETH_CRDP(6): case MV64460_REG_ETH_CRDP(7): reg = (offset - MV64460_REG_ETH_CRDP(0)) >> 4; if (op_type == MTS_READ) *data = port->crdp[reg]; else port->crdp[reg] = *data; break; case MV64460_REG_ETH_TCQDP(0): case MV64460_REG_ETH_TCQDP(1): case MV64460_REG_ETH_TCQDP(2): case MV64460_REG_ETH_TCQDP(3): case MV64460_REG_ETH_TCQDP(4): case MV64460_REG_ETH_TCQDP(5): case MV64460_REG_ETH_TCQDP(6): case MV64460_REG_ETH_TCQDP(7): reg = (offset - MV64460_REG_ETH_TCQDP(0)) >> 2; if (op_type == MTS_READ) *data = port->tcqdp[reg]; else port->tcqdp[reg] = *data; break; case MV64460_REG_ETH_MIB_GOOD_RX_BYTES: if (op_type == MTS_READ) { *data = port->mib_good_rx_bytes >> 32; } else { port->mib_good_rx_bytes &= 0xFFFFFFFFULL; port->mib_good_rx_bytes |= *data << 32; } break; case MV64460_REG_ETH_MIB_GOOD_RX_BYTES+4: if (op_type == MTS_READ) *data = port->mib_good_rx_bytes; else port->mib_good_rx_bytes = *data; break; case MV64460_REG_ETH_MIB_GOOD_RX_FRAMES: if (op_type == MTS_READ) *data = port->mib_good_rx_frames; else port->mib_good_rx_frames = *data; break; case MV64460_REG_ETH_MIB_GOOD_TX_BYTES: if (op_type == MTS_READ) { *data = port->mib_good_tx_bytes >> 32; } else { port->mib_good_tx_bytes &= 0xFFFFFFFFULL; port->mib_good_tx_bytes |= *data << 32; } break; case MV64460_REG_ETH_MIB_GOOD_TX_BYTES+4: if (op_type == MTS_READ) *data = port->mib_good_tx_bytes; else port->mib_good_tx_bytes = *data; break; case MV64460_REG_ETH_MIB_GOOD_TX_FRAMES: if (op_type == MTS_READ) *data = port->mib_good_tx_frames; else port->mib_good_tx_frames = *data; break; default: #if DEBUG_ETH_UNKNOWN if (op_type == MTS_READ) { MV64460_LOG(mv_data, "read access to unknown ETH register 0x%x, " "pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { MV64460_LOG(mv_data, "write access to unknown ETH register 0x%x, " "value=0x%llx, pc=0x%llx\n", offset,*data,cpu_get_pc(cpu)); } #endif return(FALSE); } return(TRUE); } /* Bind a NIO to an Ethernet port */ int dev_mv64460_eth_set_nio(struct mv64460_data *d,u_int port_id, netio_desc_t *nio) { struct eth_port *port; if (!d || (port_id >= MV64460_ETH_PORTS)) return(-1); port = &d->eth_ports[port_id]; /* check that a NIO is not already bound */ if (port->nio != NULL) return(-1); port->nio = nio; netio_rxl_add(nio,(netio_rx_handler_t)mv64460_eth_handle_rx_pkt, d,(void *)(u_long)port_id); return(0); } /* Unbind a NIO from an Ethernet port */ int dev_mv64460_eth_unset_nio(struct mv64460_data *d,u_int port_id) { struct eth_port *port; if (!d || (port_id >= MV64460_ETH_PORTS)) return(-1); port = &d->eth_ports[port_id]; if (port->nio != NULL) { netio_rxl_remove(port->nio); port->nio = NULL; } return(0); } /* ======================================================================== */ /* * dev_mv64460_access() */ void *dev_mv64460_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct mv64460_data *mv_data = dev->priv_data; MV64460_LOCK(mv_data); if (op_type == MTS_READ) { *data = 0; } else { if (op_size == 4) *data = swap32(*data); } #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"MV64460", "read access to register 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"MV64460", "write access to register 0x%x, value=0x%llx, pc=0x%llx\n", offset,*data,cpu_get_pc(cpu)); } #endif /* IDMA channel registers */ if (mv64460_idma_access(cpu,dev,offset,op_size,op_type,data) != 0) goto done; /* IDMA decode registers */ if (mv64460_idma_dec_access(cpu,dev,offset,op_size,op_type,data) != 0) goto done; /* Serial DMA channel registers */ if (mv64460_sdma_access(cpu,dev,offset,op_size,op_type,data) != 0) goto done; /* MPSC registers */ if (mv64460_mpsc_access(cpu,dev,offset,op_size,op_type,data) != 0) goto done; /* Gigabit Ethernet registers */ if (mv64460_eth_access(cpu,dev,offset,op_size,op_type,data) != 0) goto done; switch(offset) { /* Interrupt Main Cause Low */ case MV64460_REG_ILMCR: if (op_type == MTS_READ) *data = mv_data->intr_lo; break; /* Interrupt Main Cause High */ case MV64460_REG_IHMCR: if (op_type == MTS_READ) *data = mv_data->intr_hi; break; /* CPU_INTn[0] Mask Low */ case MV64460_REG_CPU_INTN0_MASK_LO: if (op_type == MTS_READ) { *data = mv_data->cpu_intn0_mask_lo; } else { mv_data->cpu_intn0_mask_lo = *data; mv64460_ic_update_cpu0_status(mv_data); } break; /* CPU_INTn[0] Mask High */ case MV64460_REG_CPU_INTN0_MASK_HI: if (op_type == MTS_READ) { *data = mv_data->cpu_intn0_mask_hi; } else { mv_data->cpu_intn0_mask_hi = *data; mv64460_ic_update_cpu0_status(mv_data); } break; /* CPU_INTn[0] Select Cause (read-only) */ case MV64460_REG_CPU_INTN0_SEL_CAUSE: if (op_type == MTS_READ) { *data = mv64460_ic_get_sel_cause(mv_data, mv_data->cpu_intn0_mask_lo, mv_data->cpu_intn0_mask_hi); } break; /* CPU_INTn[1] Mask Low */ case MV64460_REG_CPU_INTN1_MASK_LO: if (op_type == MTS_READ) *data = mv_data->cpu_intn1_mask_lo; else mv_data->cpu_intn1_mask_lo = *data; break; /* CPU_INTn[1] Mask High */ case MV64460_REG_CPU_INTN1_MASK_HI: if (op_type == MTS_READ) *data = mv_data->cpu_intn1_mask_hi; else mv_data->cpu_intn1_mask_hi = *data; break; /* CPU_INTn[1] Select Cause (read-only) */ case MV64460_REG_CPU_INTN1_SEL_CAUSE: if (op_type == MTS_READ) { *data = mv64460_ic_get_sel_cause(mv_data, mv_data->cpu_intn1_mask_lo, mv_data->cpu_intn1_mask_hi); } break; /* INT0n Mask Low */ case MV64460_REG_INT0N_MASK_LO: if (op_type == MTS_READ) *data = mv_data->int0n_mask_lo; else mv_data->int0n_mask_lo = *data; break; /* INT0n Mask High */ case MV64460_REG_INT0N_MASK_HI: if (op_type == MTS_READ) *data = mv_data->int0n_mask_hi; else mv_data->int0n_mask_hi = *data; break; /* INT0n Select Cause (read-only) */ case MV64460_REG_INT0N_SEL_CAUSE: if (op_type == MTS_READ) { *data = mv64460_ic_get_sel_cause(mv_data, mv_data->int0n_mask_lo, mv_data->int0n_mask_hi); } break; /* INT1n Mask Low */ case MV64460_REG_INT1N_MASK_LO: if (op_type == MTS_READ) *data = mv_data->int1n_mask_lo; else mv_data->int1n_mask_lo = *data; break; /* INT1n Mask High */ case MV64460_REG_INT1N_MASK_HI: if (op_type == MTS_READ) *data = mv_data->int1n_mask_hi; else mv_data->int1n_mask_hi = *data; break; /* INT1n Select Cause (read-only) */ case MV64460_REG_INT1N_SEL_CAUSE: if (op_type == MTS_READ) { *data = mv64460_ic_get_sel_cause(mv_data, mv_data->int1n_mask_lo, mv_data->int1n_mask_hi); } break; /* ===== PCI Bus 0 ===== */ case PCI_BUS_ADDR: /* pci configuration address (0xcf8) */ pci_dev_addr_handler(cpu,mv_data->bus[0],op_type,FALSE,data); break; case PCI_BUS_DATA: /* pci data address (0xcfc) */ pci_dev_data_handler(cpu,mv_data->bus[0],op_type,FALSE,data); break; /* ===== PCI Bus 0 ===== */ case 0xc78: /* pci configuration address (0xc78) */ pci_dev_addr_handler(cpu,mv_data->bus[1],op_type,FALSE,data); break; case 0xc7c: /* pci data address (0xc7c) */ pci_dev_data_handler(cpu,mv_data->bus[1],op_type,FALSE,data); break; /* GPP interrupt cause */ case MV64460_REG_GPP_INTR_CAUSE: if (op_type == MTS_READ) *data = mv_data->gpp_intr; break; /* GPP interrupt mask0 */ case MV64460_REG_GPP_INTR_MASK0: if (op_type == MTS_READ) { *data = mv_data->gpp_mask0; } else { mv_data->gpp_mask0 = *data; mv64460_gpio_update_int_status(mv_data); } break; /* GPP interrupt mask1 */ case MV64460_REG_GPP_INTR_MASK1: if (op_type == MTS_READ) { *data = mv_data->gpp_mask1; } else { mv_data->gpp_mask1 = *data; mv64460_gpio_update_int_status(mv_data); } break; /* GPP value set */ case MV64460_REG_GPP_VALUE_SET: if (op_type == MTS_WRITE) { mv_data->gpp_intr |= *data; mv64460_gpio_update_int_status(mv_data); } break; /* GPP value clear */ case MV64460_REG_GPP_VALUE_CLEAR: if (op_type == MTS_WRITE) { mv_data->gpp_intr &= ~(*data); mv64460_gpio_update_int_status(mv_data); } break; /* SDMA cause register */ case MV64460_REG_SDMA_CAUSE: if (op_type == MTS_READ) { *data = mv_data->sdma_cause; } else { mv_data->sdma_cause = *data; mv64460_sdma_update_int_status(mv_data); } break; /* SDMA mask register */ case MV64460_REG_SDMA_MASK: if (op_type == MTS_READ) { *data = mv_data->sdma_mask; } else { mv_data->sdma_mask = *data; mv64460_sdma_update_int_status(mv_data); } break; /* Integrated SRAM base address */ case MV64460_REG_SRAM_BASE: if (op_type == MTS_READ) { *data = mv_data->sram_dev.phys_addr << MV64460_SRAM_WIDTH; } else { m_uint64_t sram_addr; sram_addr = *data & MV64460_SRAM_BASE_MASK; sram_addr >>= MV64460_SRAM_BASE_SHIFT; sram_addr <<= MV64460_SRAM_WIDTH; vm_map_device(mv_data->vm,&mv_data->sram_dev,sram_addr); MV64460_LOG(mv_data,"SRAM mapped at 0x%10.10llx\n", mv_data->sram_dev.phys_addr); } break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"MV64460","read from addr 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"MV64460","write to addr 0x%x, value=0x%llx, " "pc=0x%llx\n",offset,*data,cpu_get_pc(cpu)); } #endif } done: MV64460_UNLOCK(mv_data); if ((op_type == MTS_READ) && (op_size == 4)) *data = swap32(*data); return NULL; } /* Set value of GPP register */ void dev_mv64460_set_gpp_reg(struct mv64460_data *d,m_uint32_t val) { d->gpp_intr = val; mv64460_gpio_update_int_status(d); } /* Set a GPP interrupt */ void dev_mv64460_set_gpp_intr(struct mv64460_data *d,u_int irq) { d->gpp_intr |= 1 << irq; mv64460_gpio_update_int_status(d); } /* Clear a GPP interrupt */ void dev_mv64460_clear_gpp_intr(struct mv64460_data *d,u_int irq) { d->gpp_intr &= ~(1 << irq); mv64460_gpio_update_int_status(d); } /* * pci_mv64460_read() * * Read a PCI register. */ static m_uint32_t pci_mv64460_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { switch (reg) { default: return(0); } } /* Shutdown a MV64460 system controller */ void dev_mv64460_shutdown(vm_instance_t *vm,struct mv64460_data *d) { if (d != NULL) { /* Stop the Ethernet TX ring scanner */ ptask_remove(d->eth_tx_tid); /* Remove the SRAM */ dev_remove(vm,&d->sram_dev); /* Remove the device */ dev_remove(vm,&d->dev); /* Remove the PCI device */ pci_dev_remove(d->pci_dev); /* Free the structure itself */ free(d); } } /* Create a new MV64460 controller */ int dev_mv64460_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len) { struct mv64460_data *d; int i; if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"mv64460: unable to create device data.\n"); return(-1); } memset(d,0,sizeof(*d)); pthread_mutex_init(&d->lock,NULL); d->name = name; d->vm = vm; d->bus[0] = vm->pci_bus[0]; d->bus[1] = vm->pci_bus[1]; for(i=0;isdma[i].id = i; vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_mv64460_shutdown; dev_init(&d->dev); d->dev.name = name; d->dev.priv_data = d; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_mv64460_access; /* Ethernet */ d->eth_phy_addr = MV64460_ETH_PHY0_ADDR; d->eth_phy_addr |= MV64460_ETH_PHY1_ADDR << 5; d->eth_phy_addr |= MV64460_ETH_PHY2_ADDR << 10; for(i=0;ieth_ports[i].id = i; d->eth_ports[i].vlan_ether_type = N_ETH_PROTO_DOT1Q; } /* Create the SRAM device */ dev_init(&d->sram_dev); d->sram_dev.name = "mv64460_sram"; d->sram_dev.phys_len = MV64460_SRAM_SIZE; d->sram_dev.flags = VDEVICE_FLAG_CACHING; d->sram_dev.host_addr = (m_iptr_t)m_memalign(4096,d->sram_dev.phys_len); if (!d->sram_dev.host_addr) { fprintf(stderr,"mv64460: unable to create SRAM data.\n"); return(-1); } /* Add the controller as a PCI device */ if (!pci_dev_lookup(d->bus[0],0,0,0)) { d->pci_dev = pci_dev_add(d->bus[0],name, PCI_VENDOR_MARVELL,PCI_PRODUCT_MARVELL_MV64460, 0,0,-1,d,NULL,pci_mv64460_read,NULL); if (!d->pci_dev) { fprintf(stderr,"mv64460: unable to create PCI device.\n"); return(-1); } } /* TEST */ pci_dev_add(d->bus[1],name, PCI_VENDOR_MARVELL,PCI_PRODUCT_MARVELL_MV64460, 0,0,-1,d,NULL,pci_mv64460_read,NULL); /* Start the Ethernet TX ring scanner */ d->eth_tx_tid = ptask_add((ptask_callback)mv64460_eth_handle_txqueues, d,NULL); /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_mv64460.h000066400000000000000000000021561241034141600167470ustar00rootroot00000000000000/* * Cisco Router Simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __DEV_MV64460_H__ #define __DEV_MV64460_H__ #include #include "utils.h" #include "mips64.h" #include "cpu.h" #include "device.h" #include "net_io.h" #include "vm.h" struct mv64460_data; /* Create a new MV64460 controller */ int dev_mv64460_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len); /* Bind a VTTY to a SDMA channel */ int mv64460_sdma_bind_vtty(struct mv64460_data *d,u_int chan_id,vtty_t *vtty); /* Bind a NIO to an Ethernet port */ int dev_mv64460_eth_set_nio(struct mv64460_data *d,u_int port_id, netio_desc_t *nio); /* Unbind a NIO from an Ethernet port */ int dev_mv64460_eth_unset_nio(struct mv64460_data *d,u_int port_id); /* Set value of GPP register */ void dev_mv64460_set_gpp_reg(struct mv64460_data *d,m_uint32_t val); /* Set a GPP interrupt */ void dev_mv64460_set_gpp_intr(struct mv64460_data *d,u_int irq); /* Clear a GPP interrupt */ void dev_mv64460_clear_gpp_intr(struct mv64460_data *d,u_int irq); #endif dynamips-0.2.14/common/dev_nm_16esw.c000066400000000000000000002143171241034141600173570ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * NM-16ESW ethernet switch module (experimental!) * * It's an attempt of proof of concept, so not optimized at all at this time. * Only L2 switching will be managed (no L3 at all). * * To do next: QoS features (CoS/DSCP handling). */ #include #include #include #include #include #include #include #include #include "utils.h" #include "timer.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_nm_16esw.h" /* Debugging flags */ #define DEBUG_ACCESS 0 #define DEBUG_UNKNOWN 0 #define DEBUG_MII 0 #define DEBUG_MEM 0 #define DEBUG_REG 0 #define DEBUG_TRANSMIT 0 #define DEBUG_RECEIVE 0 #define DEBUG_FORWARD 0 #define DEBUG_MIRROR 0 #define DEBUG_ARL 0 /* Invalid VLAN value */ #define VLAN_INVALID 0xFFF /* Maximum packet size */ #define BCM5600_MAX_PKT_SIZE 2048 /* PCI vendor/product codes */ #define BCM5605_PCI_VENDOR_ID 0x14e4 #define BCM5605_PCI_PRODUCT_ID 0x5605 /* "S-channel" commands */ #define BCM5600_SCHAN_CMD_LINKSCAN 0x13 #define BCM5600_SCHAN_CMD_EXEC 0x80 #define BCM5600_SCHAN_CMD_READ_MII 0x90 #define BCM5600_SCHAN_CMD_WRITE_MII 0x91 /* Opcodes */ #define BCM5600_OP_BP_WARN_STATUS 0x01 #define BCM5600_OP_BP_DISCARD_STATUS 0x02 #define BCM5600_OP_COS_QSTAT_NOTIFY 0x03 #define BCM5600_OP_HOL_STAT_NOTIFY 0x04 #define BCM5600_OP_GBP_FULL_NOTIFY 0x05 #define BCM5600_OP_GBP_AVAIL_NOTIFY 0x06 #define BCM5600_OP_READ_MEM_CMD 0x07 #define BCM5600_OP_READ_MEM_ACK 0x08 #define BCM5600_OP_WRITE_MEM_CMD 0x09 #define BCM5600_OP_WRITE_MEM_ACK 0x0A #define BCM5600_OP_READ_REG_CMD 0x0B #define BCM5600_OP_READ_REG_ACK 0x0C #define BCM5600_OP_WRITE_REG_CMD 0x0D #define BCM5600_OP_WRITE_REG_ACK 0x0E #define BCM5600_OP_ARL_INSERT_CMD 0x0F #define BCM5600_OP_ARL_INSERT_DONE 0x10 #define BCM5600_OP_ARL_DELETE_CMD 0x11 #define BCM5600_OP_ARL_DELETE_DONE 0x12 #define BCM5600_OP_LINKSTAT_NOTIFY 0x13 #define BCM5600_OP_MEM_FAIL_NOTIFY 0x14 #define BCM5600_OP_INIT_CFAP 0x15 #define BCM5600_OP_INIT_SFAP 0x16 #define BCM5600_OP_ENTER_DEBUG_MODE 0x17 #define BCM5600_OP_EXIT_DEBUG_MODE 0x18 #define BCM5600_OP_ARL_LOOKUP_CMD 0x19 /* Command details */ #define BCM5600_CMD_OP_MASK 0xFC000000 #define BCM5600_CMD_OP_SHIFT 26 #define BCM5600_CMD_DST_MASK 0x03F00000 #define BCM5600_CMD_DST_SHIFT 20 #define BCM5600_CMD_SRC_MASK 0x000FC000 #define BCM5600_CMD_SRC_SHIFT 14 #define BCM5600_CMD_LEN_MASK 0x00003F80 #define BCM5600_CMD_LEN_SHIFT 7 #define BCM5600_CMD_EBIT_MASK 0x00000040 #define BCM5600_CMD_EBIT_SHIFT 6 #define BCM5600_CMD_ECODE_MASK 0x00000030 #define BCM5600_CMD_ECODE_SHIFT 4 #define BCM5600_CMD_COS_MASK 0x0000000E #define BCM5600_CMD_COS_SHIFT 1 #define BCM5600_CMD_CPU_MASK 0x00000001 #define BCM5600_CMD_CPU_SHIFT 0 /* Memory zones */ #define BCM5600_ADDR_ARLCNT0 0x01000000 #define BCM5600_ADDR_ARLCNT1 0x01100000 #define BCM5600_ADDR_ARLCNT2 0x01200000 #define BCM5600_ADDR_ARL0 0x02000000 #define BCM5600_ADDR_ARL1 0x02100000 #define BCM5600_ADDR_ARL2 0x02200000 #define BCM5600_ADDR_PTABLE0 0x03000000 #define BCM5600_ADDR_PTABLE1 0x03100000 #define BCM5600_ADDR_PTABLE2 0x03200000 #define BCM5600_ADDR_VTABLE0 0x05000000 #define BCM5600_ADDR_VTABLE1 0x05100000 #define BCM5600_ADDR_VTABLE2 0x05200000 #define BCM5600_ADDR_TTR0 0x06000000 #define BCM5600_ADDR_TBMAP0 0x06010000 #define BCM5600_ADDR_TTR1 0x06100000 #define BCM5600_ADDR_TBMAP1 0x06110000 #define BCM5600_ADDR_TTR2 0x06200000 #define BCM5600_ADDR_TBMAP2 0x06210000 #define BCM5600_ADDR_IMASK0 0x07000000 #define BCM5600_ADDR_IRULE0 0x07020000 #define BCM5600_ADDR_IMASK1 0x07100000 #define BCM5600_ADDR_IRULE1 0x07120000 #define BCM5600_ADDR_IMASK2 0x07200000 #define BCM5600_ADDR_IRULE2 0x07220000 #define BCM5600_ADDR_GIMASK 0x07300000 #define BCM5600_ADDR_GIRULE 0x07320000 #define BCM5600_ADDR_MARL0 0x08000000 #define BCM5600_ADDR_MARL1 0x08100000 #define BCM5600_ADDR_MARL2 0x08200000 #define BCM5600_ADDR_L3 0x09000000 #define BCM5600_ADDR_DEFIP 0x09010000 #define BCM5600_ADDR_L3INTF 0x09020000 #define BCM5600_ADDR_IPMC 0x09030000 #define BCM5600_ADDR_CBPHDR 0x0A600000 #define BCM5600_ADDR_CAB0 0x0A610000 #define BCM5600_ADDR_CAB1 0x0A620000 #define BCM5600_ADDR_CAB2 0x0A630000 #define BCM5600_ADDR_CAB3 0x0A640000 #define BCM5600_ADDR_CCP 0x0A650000 #define BCM5600_ADDR_PPP 0x0A660000 #define BCM5600_ADDR_CFAP 0x0A670000 #define BCM5600_ADDR_SFAP 0x0A680000 #define BCM5600_ADDR_CBPDATA0 0x0A6A0000 #define BCM5600_ADDR_CBPDATA1 0x0A6B0000 #define BCM5600_ADDR_CBPDATA2 0x0A6C0000 #define BCM5600_ADDR_CBPDATA3 0x0A6D0000 #define BCM5600_ADDR_PID 0x0A900000 #define BCM5600_ADDR_XQ_BASE 0x0B600000 #define BCM5600_ADDR_GBP 0x12000000 /* Number of "Data Words" */ #define BCM5600_DW_MAX 32 /* === VTABLE definitions === */ /* Word 0 */ #define BCM5600_VTABLE_VLAN_TAG_MASK 0x00000FFF /* Word 1: Port bitmap */ #define BCM5600_VTABLE_PORT_BMAP_MASK 0x1FFFFFFF /* Word 2: Untagged port bitmap */ #define BCM5600_VTABLE_UT_PORT_BMAP_MASK 0x1FFFFFFF /* Word 3: Module bitmap */ #define BCM5600_VTABLE_MOD_BMAP_MASK 0xFFFFFFFF /* === PTABLE definitions === */ /* Word 0 */ #define BCM5600_PTABLE_VLAN_TAG_MASK 0x00000FFF #define BCM5600_PTABLE_SP_ST_MASK 0x00003000 #define BCM5600_PTABLE_SP_ST_SHIFT 12 #define BCM5600_PTABLE_PRT_DIS_MASK 0x000FC000 #define BCM5600_PTABLE_PRT_DIS_SHIFT 14 #define BCM5600_PTABLE_JUMBO_FLAG 0x00100000 #define BCM5600_PTABLE_RTAG_MASK 0x00E00000 #define BCM5600_PTABLE_RTAG_SHIFT 21 #define BCM5600_PTABLE_TGID_MASK 0x07000000 #define BCM5600_PTABLE_TGID_SHIFT 24 #define BCM5600_PTABLE_TRUNK_FLAG 0x08000000 #define BCM5600_PTABLE_CPU_FLAG 0x10000000 #define BCM5600_PTABLE_PTYPE_MASK 0x60000000 #define BCM5600_PTABLE_PTYPE_SHIFT 29 #define BCM5600_PTABLE_BPDU_FLAG 0x80000000 /* Word 1 */ #define BCM5600_PTABLE_PORT_BMAP_MASK 0x1FFFFFFF #define BCM5600_PTABLE_MI_FLAG 0x20000000 #define BCM5600_PTABLE_CML_MASK 0xC0000000 #define BCM5600_PTABLE_CML_SHIFT 30 /* Word 2 */ #define BCM5600_PTABLE_UT_PORT_BMAP_MASK 0x1FFFFFFF /* Word 4 */ #define BCM5600_PTABLE_DSCP_MASK 0x0000003F #define BCM5600_PTABLE_DSCP_SHIFT 0 #define BCM5600_PTABLE_DSE_MODE_MASK 0x000000C0 #define BCM5600_PTABLE_DSE_MODE_SHIFT 6 #define BCM5600_PTABLE_RPE_FLAG 0x00000100 #define BCM5600_PTABLE_PRI_MASK 0x00000E00 #define BCM5600_PTABLE_PRI_SHIFT 9 #define BCM5600_PTABLE_L3_DIS_FLAG 0x00001000 /* === ARL (Addess Resolution Logic) definitions === */ /* Word 0: MAC address LSB */ /* Word 1 */ #define BCM5600_ARL_MAC_MSB_MASK 0x0000FFFF #define BCM5600_ARL_VLAN_TAG_MASK 0x0FFF0000 #define BCM5600_ARL_VLAN_TAG_SHIFT 16 #define BCM5600_ARL_COS_DST_MASK 0x70000000 #define BCM5600_ARL_COS_DST_SHIFT 28 #define BCM5600_ARL_CPU_FLAG 0x80000000 /* Word 2 */ #define BCM5600_ARL_L3_FLAG 0x00000001 #define BCM5600_ARL_SD_DIS_MASK 0x00000006 #define BCM5600_ARL_SD_DIS_SHIFT 1 #define BCM5600_ARL_ST_FLAG 0x00000008 #define BCM5600_ARL_HIT_FLAG 0x00000010 #define BCM5600_ARL_COS_SRC_MASK 0x000000E0 #define BCM5600_ARL_COS_SRC_SHIFT 5 #define BCM5600_ARL_TRUNK_FLAG 0x00000100 #define BCM5600_ARL_TGID_MASK 0x00000E00 #define BCM5600_ARL_TGID_SHIFT 9 #define BCM5600_ARL_RTAG_MASK 0x00007000 #define BCM5600_ARL_RTAG_SHIFT 12 #define BCM5600_ARL_PORT_MASK 0x001F8000 #define BCM5600_ARL_PORT_SHIFT 15 #define BCM5600_ARL_SCP_FLAG 0x00200000 #define BCM5600_ARL_MOD_ID_MASK 0x07C00000 #define BCM5600_ARL_MOD_ID_SHIFT 22 /* === Multicast ARL definitions === */ /* Word 0: MAC address LSB */ /* Word 1 */ #define BCM5600_MARL_MAC_MSB_MASK 0x0000FFFF #define BCM5600_MARL_VLAN_TAG_MASK 0x0FFF0000 #define BCM5600_MARL_VLAN_TAG_SHIFT 16 #define BCM5600_MARL_COS_DST_MASK 0x70000000 #define BCM5600_MARL_COS_DST_SHIFT 28 /* Word 2 */ #define BCM5600_MARL_PORT_BMAP_MASK 0x1FFFFFFF /* Word 3 */ #define BCM5600_MARL_UT_PORT_BMAP_MASK 0x1FFFFFFF /* Word 4 */ #define BCM5600_MARL_MOD_BMAP_MASK 0xFFFFFFFF /* === Trunk bitmap === */ #define BCM5600_TBMAP_MASK 0x0FFFFFFF /* === Trunk table === */ /* Word 0 */ #define BCM5600_TTR_TP0_MASK 0x0000003F #define BCM5600_TTR_TP0_SHIFT 0 #define BCM5600_TTR_TP1_MASK 0x00000FC0 #define BCM5600_TTR_TP1_SHIFT 6 #define BCM5600_TTR_TP2_MASK 0x0003F000 #define BCM5600_TTR_TP2_SHIFT 12 #define BCM5600_TTR_TP3_MASK 0x00FC0000 #define BCM5600_TTR_TP3_SHIFT 18 #define BCM5600_TTR_TP4_MASK 0x3F000000 #define BCM5600_TTR_TP4_SHIFT 24 /* Word 1 */ #define BCM5600_TTR_TP5_MASK 0x0000003F #define BCM5600_TTR_TP5_SHIFT 0 #define BCM5600_TTR_TP6_MASK 0x00000FC0 #define BCM5600_TTR_TP6_SHIFT 6 #define BCM5600_TTR_TP7_MASK 0x0003F000 #define BCM5600_TTR_TP7_SHIFT 12 #define BCM5600_TTR_TG_SIZE_MASK 0x003C0000 #define BCM5600_TTR_TG_SIZE_SHIFT 18 /* Trunks (port aggregation) */ #define BCM5600_MAX_TRUNKS 6 #define BCM5600_MAX_PORTS_PER_TRUNK 8 /* ======================================================================= */ /* Transmit descriptor size */ #define BCM5600_TXD_SIZE 32 #define BCM5600_TXD_RING_CONT 0x80000000 /* ring is continuing */ #define BCM5600_TXD_UNKNOWN 0x04000000 /* valid packet (?) */ #define BCM5600_TXD_NEOP 0x00040000 /* end of packet if not set */ /* Receive descriptor size */ #define BCM5600_RXD_SIZE 32 #define BCM5600_RXD_RING_CONT 0x80000000 /* ring is continuing */ #define BCM5600_RXD_UNKNOWN 0x00040000 /* unknown */ /* Interrupt sources */ #define BCM5600_INTR_STAT_ITER_DONE 0x00100000 /* Unknown */ #define BCM5600_INTR_RX_UNDERRUN 0x00000400 /* RX ring underrun */ #define BCM5600_INTR_RX_AVAIL 0x00000200 /* packet available */ #define BCM5600_INTR_TX_UNDERRUN 0x00000100 /* TX ring underrun */ #define BCM5600_INTR_LINKSTAT_MOD 0x00000010 /* Link status modified */ /* ======================================================================= */ /* Port Mirroring */ #define BCM5600_MIRROR_ENABLE 0x40 #define BCM5600_MIRROR_PORT_MASK 0x3F /* ======================================================================= */ #define BCM5600_REG_HASH_SIZE 8192 /* BCM5600 register */ struct bcm5600_reg { m_uint32_t addr; m_uint32_t value; struct bcm5600_reg *next; }; /* BCM5600 table */ struct bcm5600_table { char *name; long offset; m_uint32_t addr; u_int min_index,max_index; u_int nr_words; }; /* BCM5600 in-transit packet */ struct bcm5600_pkt { /* Received packet data */ u_char *pkt; ssize_t pkt_len; /* Rewritten packet (802.1Q tag pushed or poped) */ u_char *rewr_pkt; int rewrite_done; /* Original VLAN (-1 for untagged packet) and Real VLAN */ int orig_vlan,real_vlan; /* VLAN entry */ m_uint32_t *vlan_entry; /* Ingress Port and Egress Port bitmap */ u_int ingress_port,egress_bitmap,egress_ut_bitmap; u_int egress_filter_bitmap; /* RX descriptor */ m_uint32_t rdes[4]; /* Packet sent to CPU */ u_int sent_to_cpu; }; /* BCM5600 physical port */ struct bcm5600_port { netio_desc_t *nio; u_int id; char name[32]; }; /* NM-16ESW private data */ struct nm_16esw_data { char *name; u_int nr_port; vm_instance_t *vm; struct vdevice *dev; struct pci_device *pci_dev; pthread_mutex_t lock; /* Ager task */ timer_id ager_tid; /* S-channel command and command result */ m_uint32_t schan_cmd,schan_cmd_res; /* Data Words */ m_uint32_t dw[BCM5600_DW_MAX]; /* Interrupt mask */ m_uint32_t intr_mask; /* MII registers */ m_uint16_t mii_regs[64][32]; m_uint32_t mii_input,mii_output; u_int mii_intr; /* RX/TX rings addresses */ m_uint32_t rx_ring_addr,tx_ring_addr; m_uint32_t tx_current,tx_end_scan; m_uint32_t rx_current,rx_end_scan; /* TX ring scanner task id */ ptask_id_t tx_tid; /* TX buffer */ u_char tx_buffer[BCM5600_MAX_PKT_SIZE]; u_int tx_bufsize; /* Port Mirroring */ u_int mirror_dst_port; u_int mirror_egress_ports; /* Registers hash table */ struct bcm5600_reg *reg_hash_table[BCM5600_REG_HASH_SIZE]; /* Most used tables... */ struct bcm5600_table *t_ptable,*t_vtable; struct bcm5600_table *t_arl,*t_marl; struct bcm5600_table *t_tbmap,*t_ttr; /* Ports (only 16 are "real" and usable) */ struct bcm5600_port ports[32]; /* CPU port */ u_int cpu_port; /* Current egress port of all trunks */ u_int trunk_last_egress_port[BCM5600_MAX_TRUNKS]; /* ARL count table */ m_uint32_t *arl_cnt; /* ARL (Address Resolution Logic) Table */ m_uint32_t *arl_table; /* Multicast ARL Table */ m_uint32_t *marl_table; /* VTABLE (VLAN Table) */ m_uint32_t *vtable; /* Trunks */ m_uint32_t *ttr,*tbmap; /* PTABLE (Port Table) */ m_uint32_t *ptable; }; /* NM-16ESW Port physical port mapping table (Cisco => BCM) */ static int nm16esw_port_mapping[] = { 2, 0, 6, 4, 10, 8, 14, 12, 3, 1, 7, 5, 11, 9, 15, 13, }; /* Log a BCM message */ #define BCM_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* Lock/Unlock primitives */ #define BCM_LOCK(d) pthread_mutex_lock(&(d)->lock) #define BCM_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) /* Trunk group info */ struct bcm5600_tg_info { u_int index,mask,shift; }; static struct bcm5600_tg_info tg_info[8] = { { 0, BCM5600_TTR_TP0_MASK, BCM5600_TTR_TP0_SHIFT }, { 0, BCM5600_TTR_TP1_MASK, BCM5600_TTR_TP1_SHIFT }, { 0, BCM5600_TTR_TP2_MASK, BCM5600_TTR_TP2_SHIFT }, { 0, BCM5600_TTR_TP3_MASK, BCM5600_TTR_TP3_SHIFT }, { 0, BCM5600_TTR_TP4_MASK, BCM5600_TTR_TP4_SHIFT }, { 1, BCM5600_TTR_TP5_MASK, BCM5600_TTR_TP5_SHIFT }, { 1, BCM5600_TTR_TP6_MASK, BCM5600_TTR_TP6_SHIFT }, { 1, BCM5600_TTR_TP7_MASK, BCM5600_TTR_TP7_SHIFT }, }; /* Return port status (up or down), based on the MII register */ static int bcm5600_mii_port_status(struct nm_16esw_data *d,u_int port) { u_int mii_ctrl; mii_ctrl = d->mii_regs[port][0x00]; /* Isolate bit */ return(!(mii_ctrl & 0x400)); } /* Build port status bitmap */ static m_uint32_t bcm5600_mii_port_status_bmp(struct nm_16esw_data *d) { m_uint32_t bmp; int i; for(i=0,bmp=0;inr_port;i++) if (bcm5600_mii_port_status(d,i)) bmp |= 1 << i; return(bmp); } /* Read a MII register */ static void bcm5600_mii_read(struct nm_16esw_data *d) { m_uint8_t port,reg; port = (d->mii_input >> 16) & 0xFF; reg = (d->mii_input >> 24) & 0xFF; if ((port < 32) && (reg < 32)) { d->mii_output = d->mii_regs[port][reg]; switch(reg) { case 0x00: d->mii_output &= ~0x8200; break; case 0x01: if (d->ports[port].nio && bcm5600_mii_port_status(d,port)) d->mii_output = 0x782C; else d->mii_output = 0; break; case 0x02: d->mii_output = 0x40; break; case 0x03: d->mii_output = 0x61d4; break; case 0x04: d->mii_output = 0x1E1; break; case 0x05: d->mii_output = 0x41E1; break; default: d->mii_output = 0; } } } /* Write a MII register */ static void bcm5600_mii_write(struct nm_16esw_data *d) { m_uint8_t port,reg; m_uint16_t isolation; port = (d->mii_input >> 16) & 0xFF; reg = (d->mii_input >> 24) & 0xFF; if ((port < 32) && (reg < 32)) { #if DEBUG_MII BCM_LOG(d,"MII: port 0x%4.4x, reg 0x%2.2x: writing 0x%4.4x\n", port,reg,d->mii_input & 0xFFFF); #endif /* Check if PHY isolation status is changing */ if (reg == 0) { isolation = (d->mii_input ^ d->mii_regs[port][reg]) & 0x400; if (isolation) { #if DEBUG_MII BCM_LOG(d,"MII: port 0x%4.4x: generating IRQ\n",port); #endif d->mii_intr = TRUE; pci_dev_trigger_irq(d->vm,d->pci_dev); } } d->mii_regs[port][reg] = d->mii_input & 0xFFFF; } } /* Hash function for register */ static u_int bcm5600_reg_get_hash(m_uint32_t addr) { return((addr ^ (addr >> 16)) & (BCM5600_REG_HASH_SIZE - 1)); } /* Find a register entry */ static struct bcm5600_reg *bcm5600_reg_find(struct nm_16esw_data *d, m_uint32_t addr) { struct bcm5600_reg *reg; u_int h_index; h_index = bcm5600_reg_get_hash(addr); for(reg=d->reg_hash_table[h_index];reg;reg=reg->next) if (reg->addr == addr) return reg; return NULL; } /* Read a register */ static m_uint32_t bcm5600_reg_read(struct nm_16esw_data *d,m_uint32_t addr) { struct bcm5600_reg *reg; if (!(reg = bcm5600_reg_find(d,addr))) return(0); return(reg->value); } /* Write a register */ static int bcm5600_reg_write(struct nm_16esw_data *d,m_uint32_t addr, m_uint32_t value) { struct bcm5600_reg *reg; u_int h_index; if ((reg = bcm5600_reg_find(d,addr))) { reg->value = value; return(0); } /* create a new register */ if (!(reg = malloc(sizeof(*reg)))) return(-1); reg->addr = addr; reg->value = value; /* insert new register in hash table */ h_index = bcm5600_reg_get_hash(addr); reg->next = d->reg_hash_table[h_index]; d->reg_hash_table[h_index] = reg; return(0); } /* Register special handling */ static void bcm5600_reg_write_special(struct nm_16esw_data *d, m_uint32_t addr,m_uint32_t value) { switch(addr) { case 0x80006: d->mirror_dst_port = value; break; case 0x8000d: d->mirror_egress_ports = value; break; case 0x80009: /* age timer */ break; } } /* Free memory used to store register info */ static void bcm5600_reg_free(struct nm_16esw_data *d) { struct bcm5600_reg *reg,*next; int i; for(i=0;ireg_hash_table[i];reg;reg=next) { next = reg->next; free(reg); } } /* Dump all known registers */ static void bcm5600_reg_dump(struct nm_16esw_data *d,int show_null) { struct bcm5600_reg *reg; int i; printf("%s: dumping registers:\n",d->name); for(i=0;ireg_hash_table[i];reg;reg=reg->next) { if (reg->value || show_null) printf(" 0x%8.8x: 0x%8.8x\n",reg->addr,reg->value); } } /* Fill a string buffer with all ports of the specified bitmap */ _maybe_used static char *bcm5600_port_bitmap_str(struct nm_16esw_data *d, char *buffer,m_uint32_t bitmap) { char *ptr = buffer; int i; *ptr = 0; for(i=0;inr_port;i++) if (bitmap & (1 << i)) { ptr += sprintf(ptr,"%s ",d->ports[i].name); } return buffer; } /* BCM5600 tables */ #define BCM_OFFSET(x) (OFFSET(struct nm_16esw_data,x)) static struct bcm5600_table bcm5600_tables[] = { /* ARL tables */ { "arlcnt0", BCM_OFFSET(arl_cnt), BCM5600_ADDR_ARLCNT0, 0, 0, 1 }, { "arlcnt1", BCM_OFFSET(arl_cnt), BCM5600_ADDR_ARLCNT1, 0, 0, 1 }, { "arlcnt2", BCM_OFFSET(arl_cnt), BCM5600_ADDR_ARLCNT2, 0, 0, 1 }, /* ARL tables */ { "arl0", BCM_OFFSET(arl_table), BCM5600_ADDR_ARL0, 0, 8191, 3 }, { "arl1", BCM_OFFSET(arl_table), BCM5600_ADDR_ARL1, 0, 8191, 3 }, { "arl2", BCM_OFFSET(arl_table), BCM5600_ADDR_ARL2, 0, 8191, 3 }, /* Multicast ARL tables */ { "marl0", BCM_OFFSET(marl_table), BCM5600_ADDR_MARL0, 1, 255, 5 }, { "marl1", BCM_OFFSET(marl_table), BCM5600_ADDR_MARL1, 1, 255, 5 }, { "marl2", BCM_OFFSET(marl_table), BCM5600_ADDR_MARL2, 1, 255, 5 }, /* PTABLE - Physical Ports */ { "ptable0", BCM_OFFSET(ptable), BCM5600_ADDR_PTABLE0, 0, 31, 6 }, { "ptable1", BCM_OFFSET(ptable), BCM5600_ADDR_PTABLE1, 0, 31, 6 }, { "ptable2", BCM_OFFSET(ptable), BCM5600_ADDR_PTABLE2, 0, 31, 6 }, /* VTABLE - VLANs */ { "vtable0", BCM_OFFSET(vtable), BCM5600_ADDR_VTABLE0, 1, 255, 4 }, { "vtable1", BCM_OFFSET(vtable), BCM5600_ADDR_VTABLE1, 1, 255, 4 }, { "vtable2", BCM_OFFSET(vtable), BCM5600_ADDR_VTABLE2, 1, 255, 4 }, /* TTR */ { "ttr0", BCM_OFFSET(ttr), BCM5600_ADDR_TTR0, 0, 5, 3 }, { "ttr1", BCM_OFFSET(ttr), BCM5600_ADDR_TTR1, 0, 5, 3 }, { "ttr2", BCM_OFFSET(ttr), BCM5600_ADDR_TTR2, 0, 5, 3 }, /* TBMAP */ { "tbmap0", BCM_OFFSET(tbmap), BCM5600_ADDR_TBMAP0, 0, 5, 1 }, { "tbmap1", BCM_OFFSET(tbmap), BCM5600_ADDR_TBMAP1, 0, 5, 1 }, { "tbmap2", BCM_OFFSET(tbmap), BCM5600_ADDR_TBMAP2, 0, 5, 1 }, { NULL, -1, 0, 0, 0 }, }; /* Get table size (in number of words) */ static inline u_int bcm5600_table_get_size(struct bcm5600_table *table) { return(table->nr_words * (table->max_index + 1)); } /* Create automatically tables */ static int bcm5600_table_create(struct nm_16esw_data *d) { struct bcm5600_table *table; m_uint32_t *array; size_t nr_words; int i; for(i=0;bcm5600_tables[i].name;i++) { table = &bcm5600_tables[i]; nr_words = bcm5600_table_get_size(table); if (!(array = calloc(nr_words,sizeof(m_uint32_t)))) { fprintf(stderr,"BCM5600: unable to create table '%s'\n",table->name); return(-1); } *(PTR_ADJUST(m_uint32_t **,d,table->offset)) = array; } return(0); } /* Free tables */ static void bcm5600_table_free(struct nm_16esw_data *d) { struct bcm5600_table *table; m_uint32_t **array; int i; for(i=0;bcm5600_tables[i].name;i++) { table = &bcm5600_tables[i]; array = (PTR_ADJUST(m_uint32_t **,d,table->offset)); free(*array); /* avoid freeing the same table multiple times */ *array = NULL; } } /* Find a table given its address */ static struct bcm5600_table *bcm5600_table_find(struct nm_16esw_data *d, m_uint32_t addr) { int i; for(i=0;bcm5600_tables[i].name;i++) if (bcm5600_tables[i].addr == addr) return(&bcm5600_tables[i]); #if DEBUG_UNKNOWN BCM_LOG(d,"unknown table at address 0x%8.8x\n",addr); #endif return NULL; } /* Get a table entry */ static inline m_uint32_t *bcm5600_table_get_entry(struct nm_16esw_data *d, struct bcm5600_table *table, m_uint32_t index) { m_uint32_t *array; if ((index < table->min_index) || (index > table->max_index)) return NULL; array = *(PTR_ADJUST(m_uint32_t **,d,table->offset)); return(&array[index*table->nr_words]); } /* Read a table entry */ static int bcm5600_table_read_entry(struct nm_16esw_data *d) { struct bcm5600_table *table; m_uint32_t addr,index,*entry; int i; addr = d->dw[1] & 0xFFFF0000; index = d->dw[1] & 0x0000FFFF; if (!(table = bcm5600_table_find(d,addr))) { #if DEBUG_UNKNOWN BCM_LOG(d,"unknown mem address at address 0x%8.8x\n",d->dw[1]); #endif return(-1); } if (!(entry = bcm5600_table_get_entry(d,table,index))) return(-1); #if DEBUG_MEM BCM_LOG(d,"READ_MEM: addr=0x%8.8x (table %s)\n",d->dw[1],table->name); #endif for(i=0;inr_words;i++) d->dw[i+1] = entry[i]; return(0); } /* Write a table entry */ static int bcm5600_table_write_entry(struct nm_16esw_data *d) { struct bcm5600_table *table; m_uint32_t addr,index,*entry; int i; addr = d->dw[1] & 0xFFFF0000; index = d->dw[1] & 0x0000FFFF; if (!(table = bcm5600_table_find(d,addr))) return(-1); if (!(entry = bcm5600_table_get_entry(d,table,index))) return(-1); for(i=0;inr_words;i++) entry[i] = d->dw[i+2]; #if DEBUG_MEM { char buffer[512],*ptr = buffer; for(i=0;inr_words;i++) ptr += sprintf(ptr,"data[%d]=0x%8.8x ",i,entry[i]); BCM_LOG(d,"WRITE_MEM: addr=0x%8.8x (table %s) %s\n", d->dw[1],table->name,buffer); } #endif return(0); } /* Dump a table (for debugging) */ _unused static int bcm5600_table_dump(struct nm_16esw_data *d,m_uint32_t addr) { struct bcm5600_table *table; m_uint32_t *entry; int i,j; if (!(table = bcm5600_table_find(d,addr))) return(-1); printf("%s: dumping table \"%s\":\n",d->name,table->name); for(i=table->min_index;i<=table->max_index;i++) { if (!(entry = bcm5600_table_get_entry(d,table,i))) break; printf(" %4d:",i); for(j=0;jnr_words;j++) printf("0x%8.8x ",entry[j]); printf("\n"); } printf("\n"); return(0); } /* Dump the VLAN table */ static int bcm5600_dump_vtable(struct nm_16esw_data *d) { struct bcm5600_table *table; struct bcm5600_port *port; m_uint32_t *entry,tbmp,ubmp; u_int vlan; int i,j; if (!(table = bcm5600_table_find(d,BCM5600_ADDR_VTABLE0))) return(-1); printf("%s: dumping VLAN table:\n",d->name); for(i=table->min_index;i<=table->max_index;i++) { if (!(entry = bcm5600_table_get_entry(d,table,i))) break; /* Extract the VLAN info */ vlan = entry[0] & BCM5600_VTABLE_VLAN_TAG_MASK; if (vlan == VLAN_INVALID) continue; printf(" VLAN %4u: ",vlan); for(j=0;jnr_port;j++) { tbmp = entry[1] & (1 << j); ubmp = entry[2] & (1 << j); if (tbmp || ubmp) { port = &d->ports[j]; printf("%s (",port->name); if (tbmp) printf("T%s",ubmp ? "/" : ") "); if (ubmp) printf("UT) "); } } printf("\n"); } printf("\n"); return(0); } /* Dump the "trunk" ports */ static int bcm5600_dump_trunks(struct nm_16esw_data *d) { struct bcm5600_table *table; struct bcm5600_port *port; m_uint32_t *entry; int i,j; if (!(table = bcm5600_table_find(d,BCM5600_ADDR_TBMAP0))) return(-1); printf("%s: trunk ports:\n",d->name); for(i=table->min_index;i<=table->max_index;i++) { if (!(entry = bcm5600_table_get_entry(d,table,i))) break; if (!entry[0]) continue; printf(" Trunk %d: ",i); for(j=0;jnr_port;j++) { if (entry[0] & (1 << j)) { port = &d->ports[j]; printf("%s ",port->name); } } printf("\n"); } printf("\n"); return(0); } /* Dump the physical port info */ static int bcm5600_dump_ports(struct nm_16esw_data *d) { struct bcm5600_table *table; struct bcm5600_port *port; m_uint32_t *entry; u_int vlan,tgid; int i; if (!(table = bcm5600_table_find(d,BCM5600_ADDR_PTABLE0))) return(-1); printf("%s: physical ports:\n",d->name); for(i=0;inr_port;i++) { if (!(entry = bcm5600_table_get_entry(d,table,i))) break; port = &d->ports[i]; vlan = entry[0] & BCM5600_PTABLE_VLAN_TAG_MASK; printf(" %-10s: VLAN %u",port->name,vlan); if (entry[0] & BCM5600_PTABLE_TRUNK_FLAG) { tgid = entry[0] & BCM5600_PTABLE_TGID_MASK; tgid >>= BCM5600_PTABLE_TGID_SHIFT; printf(", Trunk Group %u ",tgid); } printf("\n"); } printf("\n"); return(0); } /* Dump the physical port bitmaps */ static int bcm5600_dump_port_bitmaps(struct nm_16esw_data *d) { struct bcm5600_table *table; struct bcm5600_port *port; m_uint32_t *entry,tbmp,ubmp; int i,j; if (!(table = bcm5600_table_find(d,BCM5600_ADDR_PTABLE0))) return(-1); printf("%s: dumping bitmaps of the port table:\n",d->name); for(i=0;inr_port;i++) { if (!(entry = bcm5600_table_get_entry(d,table,i))) break; port = &d->ports[i]; printf(" %-10s: ",port->name); for(j=0;jnr_port;j++) { tbmp = entry[1] & (1 << j); ubmp = entry[2] & (1 << j); if (tbmp || ubmp) { printf("%s (",d->ports[j].name); if (tbmp) printf("T%s",ubmp ? "/" : ") "); if (ubmp) printf("UT) "); } } printf("\n"); } printf("\n"); return(0); } /* Dump main tables */ static void bcm5600_dump_main_tables(struct nm_16esw_data *d) { bcm5600_dump_ports(d); bcm5600_dump_port_bitmaps(d); bcm5600_dump_vtable(d); bcm5600_dump_trunks(d); } /* Find a free ARL entry */ static int bcm5600_find_free_arl_entry(struct nm_16esw_data *d) { struct bcm5600_table *table = d->t_arl; if (d->arl_cnt[0] == table->max_index) return(-1); return(d->arl_cnt[0] - 1); } /* ARL Lookup. TODO: this must be optimized in the future. */ static inline int bcm5600_gen_arl_lookup(struct nm_16esw_data *d, struct bcm5600_table *table, u_int index_start,u_int index_end, n_eth_addr_t *mac_addr, u_int vlan) { m_uint32_t *entry,tmp[2],mask; int i; tmp[0] = mac_addr->eth_addr_byte[2] << 24; tmp[0] |= mac_addr->eth_addr_byte[3] << 16; tmp[0] |= mac_addr->eth_addr_byte[4] << 8; tmp[0] |= mac_addr->eth_addr_byte[5]; tmp[1] = (mac_addr->eth_addr_byte[0] << 8) | mac_addr->eth_addr_byte[1]; tmp[1] |= vlan << BCM5600_ARL_VLAN_TAG_SHIFT; mask = BCM5600_ARL_VLAN_TAG_MASK | BCM5600_ARL_MAC_MSB_MASK; for(i=index_start;it_arl; return(bcm5600_gen_arl_lookup(d,table,1,d->arl_cnt[0]-1,mac_addr,vlan)); } /* MARL Lookup */ static inline int bcm5600_marl_lookup(struct nm_16esw_data *d, n_eth_addr_t *mac_addr, u_int vlan) { struct bcm5600_table *table = d->t_marl; return(bcm5600_gen_arl_lookup(d,table,table->min_index,table->max_index+1, mac_addr,vlan)); } /* Invalidate an ARL entry */ static void bcm5600_invalidate_arl_entry(m_uint32_t *entry) { entry[0] = entry[1] = entry[2] = 0; } /* Insert an entry into the ARL table */ static int bcm5600_insert_arl_entry(struct nm_16esw_data *d) { struct bcm5600_table *table = d->t_arl; m_uint32_t *entry,mask; int i,index; mask = BCM5600_ARL_VLAN_TAG_MASK | BCM5600_ARL_MAC_MSB_MASK; for(i=0;iarl_cnt[0]-1;i++) { entry = bcm5600_table_get_entry(d,table,i); /* If entry already exists, just modify it */ if ((entry[0] == d->dw[1]) && ((entry[1] & mask) == (d->dw[2] & mask))) { entry[0] = d->dw[1]; entry[1] = d->dw[2]; entry[2] = d->dw[3]; d->dw[1] = i; return(0); } } index = d->arl_cnt[0] - 1; entry = bcm5600_table_get_entry(d,table,index); entry[0] = d->dw[1]; entry[1] = d->dw[2]; entry[2] = d->dw[3]; d->dw[1] = index; d->arl_cnt[0]++; return(0); } /* Delete an entry from the ARL table */ static int bcm5600_delete_arl_entry(struct nm_16esw_data *d) { struct bcm5600_table *table; m_uint32_t *entry,*last_entry,mac_msb; u_int cvlan,vlan; int i; if (!(table = bcm5600_table_find(d,BCM5600_ADDR_ARL0))) return(-1); vlan = d->dw[2] & BCM5600_ARL_VLAN_TAG_MASK; vlan >>= BCM5600_ARL_VLAN_TAG_SHIFT; mac_msb = d->dw[2] & BCM5600_ARL_MAC_MSB_MASK; for(i=table->min_index;i<=table->max_index;i++) { entry = bcm5600_table_get_entry(d,table,i); /* compare VLANs and MAC addresses */ cvlan = (entry[1] & BCM5600_ARL_VLAN_TAG_MASK); cvlan >>= BCM5600_ARL_VLAN_TAG_SHIFT; if ((cvlan == vlan) && (entry[0] == d->dw[1]) && ((entry[1] & BCM5600_ARL_MAC_MSB_MASK) == mac_msb)) { d->dw[1] = i; last_entry = bcm5600_table_get_entry(d,d->t_arl,d->arl_cnt[0]-2); entry[0] = last_entry[0]; entry[1] = last_entry[1]; entry[2] = last_entry[2]; d->arl_cnt[0]--; return(i); } } return(0); } /* Reset the ARL tables */ static int bcm5600_reset_arl(struct nm_16esw_data *d) { struct bcm5600_table *table; m_uint32_t *entry; int i; if (!(table = bcm5600_table_find(d,BCM5600_ADDR_ARL0))) return(-1); for(i=table->min_index;i<=table->max_index;i++) { entry = bcm5600_table_get_entry(d,table,i); bcm5600_invalidate_arl_entry(entry); } return(0); } /* MAC Address Ager */ static int bcm5600_arl_ager(struct nm_16esw_data *d) { m_uint32_t *entry,*last_entry; int i; BCM_LOCK(d); for(i=1;iarl_cnt[0]-1;i++) { entry = bcm5600_table_get_entry(d,d->t_arl,i); assert(entry); if (entry[2] & BCM5600_ARL_ST_FLAG) continue; /* The entry has expired, purge it */ if (!(entry[2] & BCM5600_ARL_HIT_FLAG)) { last_entry = bcm5600_table_get_entry(d,d->t_arl,d->arl_cnt[0]-2); entry[0] = last_entry[0]; entry[1] = last_entry[1]; entry[2] = last_entry[2]; d->arl_cnt[0]--; i--; } else { entry[2] &= ~BCM5600_ARL_HIT_FLAG; } } BCM_UNLOCK(d); return(TRUE); } /* Get the VTABLE entry matching the specified VLAN */ static m_uint32_t *bcm5600_vtable_get_entry_by_vlan(struct nm_16esw_data *d, u_int vlan) { struct bcm5600_table *table = d->t_vtable; m_uint32_t *entry; int i; for(i=table->min_index;i<=table->max_index;i++) { if (!(entry = bcm5600_table_get_entry(d,table,i))) break; if ((entry[0] & BCM5600_VTABLE_VLAN_TAG_MASK) == vlan) return entry; } return NULL; } /* Read memory command */ static void bcm5600_handle_read_mem_cmd(struct nm_16esw_data *d) { int i; if (bcm5600_table_read_entry(d) != 0) { for(i=1;idw[i] = 0; } d->dw[0] = BCM5600_OP_READ_MEM_ACK << BCM5600_CMD_OP_SHIFT; } /* Write memory command */ static void bcm5600_handle_write_mem_cmd(struct nm_16esw_data *d) { bcm5600_table_write_entry(d); d->dw[0] = BCM5600_OP_WRITE_MEM_ACK << BCM5600_CMD_OP_SHIFT; } /* Handle a "general" command */ static void bcm5600_handle_gen_cmd(struct nm_16esw_data *d) { m_uint32_t op; _maybe_used m_uint32_t src,dst,len; /* Extract the opcode */ op = (d->dw[0] & BCM5600_CMD_OP_MASK) >> BCM5600_CMD_OP_SHIFT; src = (d->dw[0] & BCM5600_CMD_SRC_MASK) >> BCM5600_CMD_SRC_SHIFT; dst = (d->dw[0] & BCM5600_CMD_DST_MASK) >> BCM5600_CMD_DST_SHIFT; len = (d->dw[0] & BCM5600_CMD_LEN_MASK) >> BCM5600_CMD_LEN_SHIFT; #if DEBUG_ACCESS BCM_LOG(d,"gen_cmd: opcode 0x%2.2x [src=0x%2.2x,dst=0x%2.2x,len=0x%2.2x] " "(dw[0]=0x%8.8x, dw[1]=0x%8.8x, dw[2]=0x%8.8x, dw[3]=0x%8.8x)\n", op,src,dst,len,d->dw[0],d->dw[1],d->dw[2],d->dw[3]); #endif switch(op) { case BCM5600_OP_READ_MEM_CMD: bcm5600_handle_read_mem_cmd(d); break; case BCM5600_OP_WRITE_MEM_CMD: bcm5600_handle_write_mem_cmd(d); break; case BCM5600_OP_READ_REG_CMD: d->dw[0] = BCM5600_OP_READ_REG_ACK << BCM5600_CMD_OP_SHIFT; #if DEBUG_REG BCM_LOG(d,"READ_REG: reg_addr=0x%8.8x\n",d->dw[1]); #endif d->dw[1] = bcm5600_reg_read(d,d->dw[1]); break; case BCM5600_OP_WRITE_REG_CMD: d->dw[0] = BCM5600_OP_WRITE_REG_ACK << BCM5600_CMD_OP_SHIFT; #if DEBUG_REG BCM_LOG(d,"WRITE_REG: reg_addr=0x%8.8x val=0x%8.8x\n", d->dw[1],d->dw[2]); #endif bcm5600_reg_write(d,d->dw[1],d->dw[2]); bcm5600_reg_write_special(d,d->dw[1],d->dw[2]); break; case BCM5600_OP_ARL_INSERT_CMD: d->dw[0] = BCM5600_OP_ARL_INSERT_DONE << BCM5600_CMD_OP_SHIFT; #if DEBUG_ARL BCM_LOG(d,"ARL_INSERT_CMD " "(dw[1]=0x%8.8x,dw[2]=0x%8.8x,dw[3]=0x%8.8x)\n", d->dw[1],d->dw[2],d->dw[3]); #endif bcm5600_insert_arl_entry(d); break; case BCM5600_OP_ARL_DELETE_CMD: d->dw[0] = BCM5600_OP_ARL_DELETE_DONE << BCM5600_CMD_OP_SHIFT; #if DEBUG_ARL BCM_LOG(d,"ARL_DELETE_CMD (dw[1]=0x%8.8x,dw[2]=0x%8.8x)\n", d->dw[1],d->dw[2]); #endif bcm5600_delete_arl_entry(d); break; case BCM5600_OP_ARL_LOOKUP_CMD: d->dw[0] = BCM5600_OP_READ_MEM_ACK << BCM5600_CMD_OP_SHIFT; break; default: BCM_LOG(d,"unknown opcode 0x%8.8x (cmd=0x%8.8x)\n",op,d->dw[0]); } } /* Handle a s-channel command */ static void bcm5600_handle_schan_cmd(struct nm_16esw_data *d,m_uint32_t cmd) { d->schan_cmd = cmd; #if DEBUG_ACCESS BCM_LOG(d,"s-chan command 0x%8.8x\n",cmd); #endif switch(cmd) { case BCM5600_SCHAN_CMD_EXEC: bcm5600_handle_gen_cmd(d); d->schan_cmd_res = 0x00008002; break; case BCM5600_SCHAN_CMD_READ_MII: bcm5600_mii_read(d); d->schan_cmd_res = 0x00048000; break; case BCM5600_SCHAN_CMD_WRITE_MII: bcm5600_mii_write(d); d->schan_cmd_res = 0x00048000; break; case BCM5600_SCHAN_CMD_LINKSCAN: d->schan_cmd_res = 0x0; break; default: #if DEBUG_UNKNOWN BCM_LOG(d,"unknown s-chan command 0x%8.8x\n",cmd); #endif d->schan_cmd_res = 0xFFFFFFFF; } } /* * dev_bcm5605_access() */ void *dev_bcm5605_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct nm_16esw_data *d = dev->priv_data; u_int reg; if (op_type == MTS_READ) *data = 0; #if DEBUG_ACCESS if (op_type == MTS_READ) { BCM_LOG(d,"read access to offset=0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { BCM_LOG(d,"write access to offset=0x%x, pc=0x%llx, val=0x%llx\n", offset,cpu_get_pc(cpu),*data); } #endif BCM_LOCK(d); switch(offset) { case 0x50: if (op_type == MTS_WRITE) { bcm5600_handle_schan_cmd(d,*data); } else { *data = d->schan_cmd_res; } break; case 0x140: if (op_type == MTS_READ) *data = bcm5600_mii_port_status_bmp(d); break; /* MII input register */ case 0x158: if (op_type == MTS_WRITE) d->mii_input = *data; break; /* MII output register */ case 0x15c: if (op_type == MTS_READ) *data = d->mii_output; break; /* Unknown (related to RX/TX rings ?) */ case 0x104: break; /* TX ring address */ case 0x110: if (op_type == MTS_READ) *data = d->tx_ring_addr; else { d->tx_ring_addr = d->tx_current = *data; d->tx_end_scan = 0; #if DEBUG_TRANSMIT BCM_LOG(d,"tx_ring_addr = 0x%8.8x\n",d->tx_ring_addr); #endif } break; /* RX ring address */ case 0x114: if (op_type == MTS_READ) *data = d->rx_ring_addr; else { d->rx_ring_addr = d->rx_current = *data; d->rx_end_scan = 0; #if DEBUG_RECEIVE BCM_LOG(d,"rx_ring_addr = 0x%8.8x\n",d->rx_ring_addr); #endif } break; /* Interrupt status */ case 0x144: if (op_type == MTS_READ) { *data = 0; /* RX/TX underrun (end of rings reached) */ if (d->tx_end_scan) *data |= BCM5600_INTR_TX_UNDERRUN; if (d->rx_end_scan) *data |= BCM5600_INTR_RX_UNDERRUN; /* RX packet available */ *data |= BCM5600_INTR_RX_AVAIL; /* Link status changed */ if (d->mii_intr) { *data |= BCM5600_INTR_LINKSTAT_MOD; d->mii_intr = FALSE; } pci_dev_clear_irq(d->vm,d->pci_dev); } break; /* Interrupt mask */ case 0x148: if (op_type == MTS_READ) *data = d->intr_mask; else d->intr_mask = *data; break; /* Data Words */ case 0x800 ... 0x850: reg = (offset - 0x800) >> 2; if (op_type == MTS_READ) *data = d->dw[reg]; else d->dw[reg] = *data; break; #if DEBUG_UNKNOWN /* Unknown offset */ default: if (op_type == MTS_READ) { BCM_LOG(d,"read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { BCM_LOG(d,"write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } BCM_UNLOCK(d); return NULL; } /* Show mirroring status */ static int bcm5600_mirror_show_status(struct nm_16esw_data *d) { m_uint32_t *port,dst_port; int i; printf("Mirroring status: "); if (!(d->mirror_dst_port & BCM5600_MIRROR_ENABLE)) { printf("disabled.\n\n"); return(FALSE); } printf("enabled. Dest port: "); dst_port = d->mirror_dst_port & BCM5600_MIRROR_PORT_MASK; if (dst_port < 32) printf("%s\n",d->ports[dst_port].name); else printf("none set.\n"); /* Ingress info */ printf(" Ingress Ports: "); for(i=0;inr_port;i++) { port = bcm5600_table_get_entry(d,d->t_ptable,i); if (port[1] & BCM5600_PTABLE_MI_FLAG) printf("%s ",d->ports[i].name); } printf("\n"); /* Egress info */ printf(" Egress Ports: "); for(i=0;inr_port;i++) if (d->mirror_egress_ports & (1 << i)) printf("%s ",d->ports[i].name); printf("\n\n"); return(TRUE); } /* Mirror a packet */ static int bcm5600_mirror_pkt(struct nm_16esw_data *d,struct bcm5600_pkt *p, int reason) { u_int mport; if (!(d->mirror_dst_port & BCM5600_MIRROR_ENABLE)) return(FALSE); #if DEBUG_MIRROR if (reason == 0) { BCM_LOG(d,"mirroring packet on ingress port %s\n", d->ports[p->ingress_port]); } else { BCM_LOG(d,"mirroring packet on egress port (input port %s)\n", d->ports[p->ingress_port]); } mem_dump(d->vm->log_fd,pkt,pkt_len); #endif mport = d->mirror_dst_port & BCM5600_MIRROR_PORT_MASK; if (mport < 32) netio_send(d->ports[mport].nio,p->pkt,p->pkt_len); return(TRUE); } /* Put a packet into the RX ring (tag it if necessary) */ static int bcm5600_send_pkt_to_cpu(struct nm_16esw_data *d, struct bcm5600_pkt *p) { m_uint32_t pkt_addr,pkt_len,dot1q_data; /* If the packet was already sent to CPU, don't send it again */ if (p->sent_to_cpu) return(FALSE); pkt_addr = p->rdes[0]; pkt_len = p->pkt_len; if (p->orig_vlan != -1) { /* 802.1Q packet: copy it directly */ physmem_copy_to_vm(d->vm,p->pkt,pkt_addr,pkt_len); } else { /* untagged packet: copy the dst and src addresses first */ physmem_copy_to_vm(d->vm,p->pkt,pkt_addr,N_ETH_HLEN - 2); /* add the 802.1Q protocol field (0x8100) + VLAN info */ dot1q_data = (N_ETH_PROTO_DOT1Q << 16) | p->real_vlan; physmem_copy_u32_to_vm(d->vm,pkt_addr+N_ETH_HLEN-2,dot1q_data); /* copy the payload */ physmem_copy_to_vm(d->vm,p->pkt+N_ETH_HLEN-2, pkt_addr+sizeof(n_eth_dot1q_hdr_t), pkt_len - (N_ETH_HLEN - 2)); pkt_len += 4; } physmem_copy_u32_to_vm(d->vm,d->rx_current+0x14,0x40000000 + (pkt_len+4)); physmem_copy_u32_to_vm(d->vm,d->rx_current+0x18,0x100 + p->ingress_port); p->sent_to_cpu = TRUE; #if DEBUG_RECEIVE BCM_LOG(d,"sending packet to CPU (orig_vlan=%d).\n",p->orig_vlan); #endif return(TRUE); } /* Source MAC address learning */ static int bcm5600_src_mac_learning(struct nm_16esw_data *d, struct bcm5600_pkt *p) { n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt; n_eth_addr_t *src_mac = ð_hdr->saddr; m_uint32_t *arl_entry,*src_port,*trunk; u_int trunk_id,old_ingress_port; int src_mac_index; trunk = NULL; trunk_id = 0; /* Skip multicast sources */ if (eth_addr_is_mcast(src_mac)) return(FALSE); src_port = bcm5600_table_get_entry(d,d->t_ptable,p->ingress_port); assert(src_port != NULL); /* * The packet comes from a trunk port. Prevent sending the packet * to the other ports of the trunk. */ if (src_port[0] & BCM5600_PTABLE_TRUNK_FLAG) { trunk_id = src_port[0] & BCM5600_PTABLE_TGID_MASK; trunk_id >>= BCM5600_PTABLE_TGID_SHIFT; trunk = bcm5600_table_get_entry(d,d->t_tbmap,trunk_id); assert(trunk != NULL); p->egress_filter_bitmap |= trunk[0] & BCM5600_TBMAP_MASK; } /* Source MAC address learning */ src_mac_index = bcm5600_arl_lookup(d,src_mac,p->real_vlan); if (src_mac_index != -1) { arl_entry = bcm5600_table_get_entry(d,d->t_arl,src_mac_index); assert(arl_entry != NULL); old_ingress_port = arl_entry[2] & BCM5600_ARL_PORT_MASK; old_ingress_port >>= BCM5600_ARL_PORT_SHIFT; if (old_ingress_port != p->ingress_port) { /* * Determine if we have a station movement. * If we have a trunk, check if the old ingress port is member * of this trunk, in this case this is not a movement. */ if (trunk != NULL) { if (trunk[0] & (1 << old_ingress_port)) arl_entry[2] |= BCM5600_ARL_HIT_FLAG; else arl_entry[2] &= ~BCM5600_ARL_HIT_FLAG; } else { arl_entry[2] &= ~(BCM5600_ARL_TRUNK_FLAG|BCM5600_ARL_HIT_FLAG); arl_entry[2] &= ~BCM5600_ARL_TGID_MASK; } /* Change the ingress port */ arl_entry[2] &= ~BCM5600_ARL_PORT_MASK; arl_entry[2] |= p->ingress_port << BCM5600_ARL_PORT_SHIFT; return(TRUE); } arl_entry[2] |= BCM5600_ARL_HIT_FLAG; return(TRUE); } #if DEBUG_FORWARD BCM_LOG(d,"source MAC address unknown, learning it.\n"); #endif /* Add the new learned MAC address */ src_mac_index = bcm5600_find_free_arl_entry(d); if (src_mac_index == -1) { BCM_LOG(d,"no free entries in ARL table!\n"); return(FALSE); } arl_entry = bcm5600_table_get_entry(d,d->t_arl,src_mac_index); assert(arl_entry != NULL); /* Fill the new ARL entry */ arl_entry[0] = src_mac->eth_addr_byte[2] << 24; arl_entry[0] |= src_mac->eth_addr_byte[3] << 16; arl_entry[0] |= src_mac->eth_addr_byte[4] << 8; arl_entry[0] |= src_mac->eth_addr_byte[5]; arl_entry[1] = src_mac->eth_addr_byte[0] << 8; arl_entry[1] |= src_mac->eth_addr_byte[1]; arl_entry[1] |= p->real_vlan << BCM5600_ARL_VLAN_TAG_SHIFT; arl_entry[2] = BCM5600_ARL_HIT_FLAG; arl_entry[2] |= p->ingress_port << BCM5600_ARL_PORT_SHIFT; if (trunk != NULL) { arl_entry[2] |= BCM5600_ARL_TRUNK_FLAG; arl_entry[2] |= (trunk_id << BCM5600_ARL_TGID_SHIFT); } d->arl_cnt[0]++; return(TRUE); } /* Select an egress port the specified trunk */ static int bcm5600_trunk_egress_port(struct nm_16esw_data *d, struct bcm5600_pkt *p, u_int trunk_id) { _maybe_used n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt; _maybe_used u_int i, hash; struct bcm5600_tg_info *tgi; m_uint32_t *ttr_entry; u_int nr_links; u_int port_id; ttr_entry = bcm5600_table_get_entry(d,d->t_ttr,trunk_id); assert(ttr_entry != NULL); nr_links = ttr_entry[1] & BCM5600_TTR_TG_SIZE_MASK; nr_links >>= BCM5600_TTR_TG_SIZE_SHIFT; #if 0 /* Hash on source and destination MAC addresses */ for(i=0,hash=0;isaddr.eth_addr_byte[i]; hash ^= eth_hdr->daddr.eth_addr_byte[i]; } hash ^= (hash >> 4); port_id = hash % nr_links; /* Maximum of 8 ports per trunk */ assert(hash < BCM5600_MAX_PORTS_PER_TRUNK); #else port_id = d->trunk_last_egress_port[trunk_id] + 1; port_id %= nr_links; #endif /* Save the latest port used for this trunk */ d->trunk_last_egress_port[trunk_id] = port_id; /* Select the egress port */ tgi = &tg_info[port_id]; return((ttr_entry[tgi->index] & tgi->mask) >> tgi->shift); } /* Destination address lookup (take the forwarding decision) */ static int bcm5600_dst_mac_lookup(struct nm_16esw_data *d, struct bcm5600_pkt *p) { n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt; n_eth_addr_t *dst_mac = ð_hdr->daddr; struct bcm5600_table *arl_table; m_uint32_t *arl_entry; u_int egress_port; u_int trunk_id; int dst_mac_index; int is_mcast; /* Select the appropriate ARL table and do the lookup on dst MAC + VLAN */ if (eth_addr_is_mcast(dst_mac)) { is_mcast = TRUE; arl_table = d->t_marl; dst_mac_index = bcm5600_marl_lookup(d,dst_mac,p->real_vlan); } else { is_mcast = FALSE; arl_table = d->t_arl; dst_mac_index = bcm5600_arl_lookup(d,dst_mac,p->real_vlan); } /* * Destination Lookup Failure (DLF). * * Use the VLAN bitmap to compute the Egress port bitmap. * Remove the ingress port from it. */ if (dst_mac_index == -1) { #if DEBUG_FORWARD BCM_LOG(d,"Destination MAC address " "%2.2x%2.2x.%2.2x%2.2x.%2.2x%2.2x unknown, flooding.\n", dst_mac->eth_addr_byte[0],dst_mac->eth_addr_byte[1], dst_mac->eth_addr_byte[2],dst_mac->eth_addr_byte[3], dst_mac->eth_addr_byte[4],dst_mac->eth_addr_byte[5]); #endif p->egress_bitmap = p->vlan_entry[1] & BCM5600_VTABLE_PORT_BMAP_MASK; /* Add the CPU to the egress ports */ p->egress_bitmap |= 1 << d->cpu_port; p->egress_ut_bitmap = p->vlan_entry[2]; p->egress_ut_bitmap &= BCM5600_VTABLE_UT_PORT_BMAP_MASK; return(TRUE); } /* The MAC address was found in the ARL/MARL table */ arl_entry = bcm5600_table_get_entry(d,arl_table,dst_mac_index); assert(arl_entry != NULL); /* If the CPU bit is set, send a copy of the packet to the CPU */ if (arl_entry[1] & BCM5600_ARL_CPU_FLAG) bcm5600_send_pkt_to_cpu(d,p); if (!is_mcast) { /* Unicast: send the packet to the port or trunk found in ARL table */ if (arl_entry[2] & BCM5600_ARL_TRUNK_FLAG) { trunk_id = arl_entry[2] & BCM5600_ARL_TGID_MASK; trunk_id >>= BCM5600_ARL_TGID_SHIFT; /* Select an output port for this trunk */ egress_port = bcm5600_trunk_egress_port(d,p,trunk_id); #if DEBUG_FORWARD BCM_LOG(d,"Sending packet to trunk port %u, egress port %u\n", trunk_id,egress_port); #endif } else { egress_port = arl_entry[2] & BCM5600_ARL_PORT_MASK; egress_port >>= BCM5600_ARL_PORT_SHIFT; } p->egress_bitmap = 1 << egress_port; p->egress_ut_bitmap = p->vlan_entry[2] & BCM5600_VTABLE_UT_PORT_BMAP_MASK; } else { /* Multicast: send the packet to the egress ports found in MARL table */ p->egress_bitmap = arl_entry[2] & BCM5600_MARL_PORT_BMAP_MASK; p->egress_ut_bitmap = arl_entry[3] & BCM5600_MARL_UT_PORT_BMAP_MASK; } #if DEBUG_FORWARD { char buffer[1024]; BCM_LOG(d,"bitmap: 0x%8.8x, filter: 0x%8.8x\n", p->egress_bitmap,p->egress_filter_bitmap); bcm5600_port_bitmap_str(d,buffer,p->egress_bitmap); /* without egress port filtering */ if (*buffer) BCM_LOG(d,"forwarding to egress port list w/o filter: %s\n",buffer); else BCM_LOG(d,"w/o filter: empty egress port list.\n"); /* with egress port filtering */ bcm5600_port_bitmap_str(d,buffer, p->egress_bitmap & ~p->egress_filter_bitmap); if (*buffer) BCM_LOG(d,"forwarding to egress port list w/ filter: %s\n",buffer); } #endif return(p->egress_bitmap != 0); } /* Prototype for a packet sending function */ typedef void (*bcm5600_send_pkt_t)(struct nm_16esw_data *d, struct bcm5600_pkt *p, netio_desc_t *nio); /* Directly forward a packet (not rewritten) */ static void bcm5600_send_pkt_direct(struct nm_16esw_data *d, struct bcm5600_pkt *p, netio_desc_t *nio) { netio_send(nio,p->pkt,p->pkt_len); } /* Send a packet with a 802.1Q tag */ static void bcm5600_send_pkt_push_dot1q(struct nm_16esw_data *d, struct bcm5600_pkt *p, netio_desc_t *nio) { n_eth_dot1q_hdr_t *hdr; if (!p->rewrite_done) { memcpy(p->rewr_pkt,p->pkt,(N_ETH_HLEN - 2)); hdr = (n_eth_dot1q_hdr_t *)p->rewr_pkt; hdr->type = htons(N_ETH_PROTO_DOT1Q); hdr->vlan_id = htons(p->real_vlan); memcpy(p->rewr_pkt + sizeof(n_eth_dot1q_hdr_t), p->pkt + (N_ETH_HLEN - 2), p->pkt_len - (N_ETH_HLEN - 2)); p->rewrite_done = TRUE; } netio_send(nio,p->rewr_pkt,p->pkt_len+4); } /* Send a packet deleting its 802.1Q tag */ static void bcm5600_send_pkt_pop_dot1q(struct nm_16esw_data *d, struct bcm5600_pkt *p, netio_desc_t *nio) { if (!p->rewrite_done) { memcpy(p->rewr_pkt,p->pkt,(N_ETH_HLEN - 2)); memcpy(p->rewr_pkt + (N_ETH_HLEN - 2), p->pkt + sizeof(n_eth_dot1q_hdr_t), p->pkt_len - sizeof(n_eth_dot1q_hdr_t)); p->rewrite_done = TRUE; } netio_send(nio,p->rewr_pkt,p->pkt_len-4); } /* Forward a packet on physical ports (egress bitmap must be defined) */ static int bcm5600_forward_pkt(struct nm_16esw_data *d,struct bcm5600_pkt *p) { u_char rewr_pkt[BCM5600_MAX_PKT_SIZE]; bcm5600_send_pkt_t send_pkt; u_int egress_untagged,trunk_id; m_uint32_t *dst_port,*trunk; int i; p->egress_bitmap &= ~p->egress_filter_bitmap; if (!p->egress_bitmap) return(FALSE); /* Process egress mirroring (if enabled) */ if (p->egress_bitmap & d->mirror_egress_ports) bcm5600_mirror_pkt(d,p,1); /* No rewrite done at this time */ p->rewr_pkt = rewr_pkt; p->rewrite_done = FALSE; /* Forward to CPU port ? */ if (p->egress_bitmap & (1 << d->cpu_port)) bcm5600_send_pkt_to_cpu(d,p); for(i=0;inr_port;i++) { if (!(p->egress_bitmap & (1 << i))) continue; /* * If this port is a member of a trunk, remove all other ports to avoid * duplicate frames (typically, when a dest MAC address is unknown * or for a broadcast/multicast). */ dst_port = bcm5600_table_get_entry(d,d->t_ptable,i); assert(dst_port != NULL); if (dst_port[0] & BCM5600_PTABLE_TRUNK_FLAG) { trunk_id = dst_port[0] & BCM5600_PTABLE_TGID_MASK; trunk_id >>= BCM5600_PTABLE_TGID_SHIFT; trunk = bcm5600_table_get_entry(d,d->t_tbmap,trunk_id); assert(trunk != NULL); p->egress_bitmap &= ~trunk[0]; } /* select the appropriate output vector */ if (p->orig_vlan == 0) send_pkt = bcm5600_send_pkt_direct; else { egress_untagged = p->egress_ut_bitmap & (1 << i); if (p->orig_vlan == -1) { /* Untagged packet */ if (egress_untagged) send_pkt = bcm5600_send_pkt_direct; else send_pkt = bcm5600_send_pkt_push_dot1q; } else { /* Tagged packet */ if (egress_untagged) send_pkt = bcm5600_send_pkt_pop_dot1q; else send_pkt = bcm5600_send_pkt_direct; } } #if DEBUG_FORWARD > 1 BCM_LOG(d,"forwarding on port %s (vector=%p)\n", d->ports[i].name,send_pkt); #endif send_pkt(d,p,d->ports[i].nio); } return(TRUE); } /* Determine if the specified MAC address matches a BPDU */ static inline int bcm5600_is_bpdu(n_eth_addr_t *m) { /* PVST+ */ if (!memcmp(m,"\x01\x00\x0c\xcc\xcc\xcd",6)) return(TRUE); /* Classical 802.1D */ if (!memcmp(m,"\x01\x80\xc2\x00\x00",5) && !(m->eth_addr_byte[5] & 0xF0)) return(TRUE); /* * CDP: this is cleary a hack, but IOS seems to program this address * in BPDU registers. */ if (!memcmp(m,"\x01\x00\x0c\xcc\xcc\xcc",6)) return(TRUE); return(FALSE); } /* Handle a received packet */ static int bcm5600_handle_rx_pkt(struct nm_16esw_data *d,struct bcm5600_pkt *p) { m_uint32_t *port_entry; n_eth_dot1q_hdr_t *eth_hdr; u_int discard; /* No egress port at this time */ p->egress_bitmap = 0; /* Never send back frames to the source port */ p->egress_filter_bitmap = 1 << p->ingress_port; if (!(port_entry = bcm5600_table_get_entry(d,d->t_ptable,p->ingress_port))) return(FALSE); /* Analyze the Ethernet header */ eth_hdr = (n_eth_dot1q_hdr_t *)p->pkt; /* Determine VLAN */ if (ntohs(eth_hdr->type) != N_ETH_PROTO_DOT1Q) { p->orig_vlan = -1; p->real_vlan = port_entry[0] & BCM5600_PTABLE_VLAN_TAG_MASK; /* TODO: 802.1p/CoS remarking */ if (port_entry[4] & BCM5600_PTABLE_RPE_FLAG) { } } else { p->orig_vlan = p->real_vlan = ntohs(eth_hdr->vlan_id) & 0xFFF; } /* Check that this VLAN exists */ if (!(p->vlan_entry = bcm5600_vtable_get_entry_by_vlan(d,p->real_vlan))) return(FALSE); /* Check for the reserved addresses (BPDU for spanning-tree) */ if (bcm5600_is_bpdu(ð_hdr->daddr)) { #if DEBUG_RECEIVE BCM_LOG(d,"Received a BPDU packet:\n"); mem_dump(d->vm->log_fd,p->pkt,p->pkt_len); #endif p->egress_bitmap |= 1 << d->cpu_port; return(bcm5600_forward_pkt(d,p)); } /* Check that this port is a member of this VLAN */ if (!(p->vlan_entry[1] & (1 << p->ingress_port))) return(FALSE); /* Discard packet ? */ discard = port_entry[0] & BCM5600_PTABLE_PRT_DIS_MASK; discard >>= BCM5600_PTABLE_PRT_DIS_SHIFT; if ((p->orig_vlan == -1) && discard) { if (discard != 0x20) { printf("\n\n\n" "-----------------------------------------------------------" "---------------------------------\n" "Unspported feature: please post your current configuration " "on http://www.ipflow.utc.fr/blog/\n" "-----------------------------------------------------------" "---------------------------------\n"); } /* Drop the packet */ return(FALSE); } /* Mirroring on Ingress ? */ if (port_entry[1] & BCM5600_PTABLE_MI_FLAG) bcm5600_mirror_pkt(d,p,0); #if DEBUG_RECEIVE BCM_LOG(d,"%s: received a packet on VLAN %u\n", d->ports[p->ingress_port].name,p->real_vlan); #endif /* Source MAC address learning */ if (!bcm5600_src_mac_learning(d,p)) return(FALSE); /* Take forwarding decision based on destination MAC address */ if (!bcm5600_dst_mac_lookup(d,p)) return(FALSE); /* Send the packet to the egress ports */ return(bcm5600_forward_pkt(d,p)); } /* Handle a packet to transmit */ static int bcm5600_handle_tx_pkt(struct nm_16esw_data *d, struct bcm5600_pkt *p, u_int egress_bitmap) { n_eth_dot1q_hdr_t *eth_hdr; /* Never send back frames to the source port */ p->egress_filter_bitmap = 1 << p->ingress_port; /* We take the complete forwarding decision if bit 23 is set */ if (egress_bitmap & (1 << 23)) { /* No egress port at this time */ p->egress_bitmap = 0; /* The packet must be tagged so that we can determine the VLAN */ eth_hdr = (n_eth_dot1q_hdr_t *)p->pkt; if (ntohs(eth_hdr->type) != N_ETH_PROTO_DOT1Q) { BCM_LOG(d,"bcm5600_handle_tx_pkt: untagged packet ?\n"); return(FALSE); } /* Find the appropriate, check it exists (just in case) */ p->orig_vlan = p->real_vlan = ntohs(eth_hdr->vlan_id) & 0xFFF; if (!(p->vlan_entry = bcm5600_vtable_get_entry_by_vlan(d,p->real_vlan))) return(FALSE); #if DEBUG_TRANSMIT BCM_LOG(d,"Transmitting a packet from TX ring to VLAN %u\n", p->real_vlan); #endif /* Take forwarding decision based on destination MAC address */ if (!bcm5600_dst_mac_lookup(d,p)) return(FALSE); } else { #if DEBUG_TRANSMIT BCM_LOG(d,"Transmitting natively a packet from TX ring.\n"); #endif /* The egress ports are specified, send the packet natively */ p->orig_vlan = 0; p->egress_bitmap = egress_bitmap; } /* Send the packet to the egress ports */ return(bcm5600_forward_pkt(d,p)); } /* Handle the TX ring */ static int dev_bcm5600_handle_txring(struct nm_16esw_data *d) { struct bcm5600_pkt pkt_data; m_uint32_t tdes[4],txd_len; BCM_LOCK(d); if (!d->tx_current || d->tx_end_scan) { BCM_UNLOCK(d); return(FALSE); } /* Read the current TX descriptor */ physmem_copy_from_vm(d->vm,tdes,d->tx_current,4*sizeof(m_uint32_t)); tdes[0] = vmtoh32(tdes[0]); tdes[1] = vmtoh32(tdes[1]); tdes[2] = vmtoh32(tdes[2]); tdes[3] = vmtoh32(tdes[3]); #if DEBUG_TRANSMIT BCM_LOG(d,"=== TRANSMIT PATH ===\n"); BCM_LOG(d,"tx_current=0x%8.8x, " "tdes[0]=0x%8.8x, tdes[1]=0x%8.8x, tdes[2]=0x%8.8x\n", d->tx_current,tdes[0],tdes[1],tdes[2]); #endif /* Get the buffer size */ txd_len = tdes[1] & 0x7FF; /* Check buffer size */ if ((d->tx_bufsize + txd_len) >= sizeof(d->tx_buffer)) goto done; /* Copy the packet from memory */ physmem_copy_from_vm(d->vm,d->tx_buffer+d->tx_bufsize,tdes[0],txd_len); d->tx_bufsize += txd_len; /* Packet not complete: handle it later */ if (tdes[1] & BCM5600_TXD_NEOP) goto done; #if DEBUG_TRANSMIT mem_dump(d->vm->log_fd,d->tx_buffer,d->tx_bufsize); #endif /* Transmit the packet */ pkt_data.ingress_port = d->cpu_port; pkt_data.pkt = d->tx_buffer; pkt_data.pkt_len = d->tx_bufsize - 4; pkt_data.sent_to_cpu = TRUE; bcm5600_handle_tx_pkt(d,&pkt_data,tdes[2]); /* Reset the TX buffer (packet fully transmitted) */ d->tx_bufsize = 0; done: /* We have reached end of ring: trigger the TX underrun interrupt */ if (!(tdes[1] & BCM5600_TXD_RING_CONT)) { d->tx_end_scan = 1; pci_dev_trigger_irq(d->vm,d->pci_dev); BCM_UNLOCK(d); return(TRUE); } /* Go to the next descriptor */ d->tx_current += BCM5600_TXD_SIZE; BCM_UNLOCK(d); return(TRUE); } /* Handle the RX ring */ static int dev_bcm5600_handle_rxring(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, struct nm_16esw_data *d, struct bcm5600_port *port) { struct bcm5600_pkt pkt_data; m_uint32_t rxd_len; #if DEBUG_RECEIVE BCM_LOG(d,"=== RECEIVE PATH ===\n"); BCM_LOG(d,"%s: received a packet of %ld bytes.\n", port->name,(u_long)pkt_len); mem_dump(d->vm->log_fd,pkt,pkt_len); #endif BCM_LOCK(d); if (!d->rx_current || d->rx_end_scan) { BCM_UNLOCK(d); return(FALSE); } /* Read the current TX descriptor */ physmem_copy_from_vm(d->vm,pkt_data.rdes,d->rx_current, (4 * sizeof(m_uint32_t))); pkt_data.rdes[0] = vmtoh32(pkt_data.rdes[0]); pkt_data.rdes[1] = vmtoh32(pkt_data.rdes[1]); pkt_data.rdes[2] = vmtoh32(pkt_data.rdes[2]); pkt_data.rdes[3] = vmtoh32(pkt_data.rdes[3]); #if DEBUG_RECEIVE BCM_LOG(d,"rx_current=0x%8.8x, " "rdes[0]=0x%8.8x, rdes[1]=0x%8.8x, rdes[2]=0x%8.8x\n", d->rx_current,pkt_data.rdes[0],pkt_data.rdes[1],pkt_data.rdes[2]); #endif /* Get the buffer size */ rxd_len = pkt_data.rdes[1] & 0x7FF; if (pkt_len > rxd_len) { BCM_UNLOCK(d); return(FALSE); } /* Fill the packet info */ pkt_data.ingress_port = port->id; pkt_data.pkt = pkt; pkt_data.pkt_len = pkt_len; pkt_data.sent_to_cpu = FALSE; /* Handle the packet */ bcm5600_handle_rx_pkt(d,&pkt_data); /* Signal only an interrupt when a packet has been sent to the CPU */ if (pkt_data.sent_to_cpu) { /* We have reached end of ring: trigger the RX underrun interrupt */ if (!(pkt_data.rdes[1] & BCM5600_RXD_RING_CONT)) { d->rx_end_scan = 1; pci_dev_trigger_irq(d->vm,d->pci_dev); BCM_UNLOCK(d); return(TRUE); } /* A packet was received */ pci_dev_trigger_irq(d->vm,d->pci_dev); /* Go to the next descriptor */ d->rx_current += BCM5600_RXD_SIZE; } BCM_UNLOCK(d); return(TRUE); } /* pci_bcm5605_read() */ static m_uint32_t pci_bcm5605_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { struct nm_16esw_data *d = dev->priv_data; switch(reg) { case PCI_REG_BAR0: return(d->dev->phys_addr); default: return(0); } } /* pci_bcm5605_write() */ static void pci_bcm5605_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct nm_16esw_data *d = dev->priv_data; switch(reg) { case PCI_REG_BAR0: vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); BCM_LOG(d,"BCM5600 registers are mapped at 0x%x\n",value); break; } } /* Rewrite the base MAC address */ int dev_nm_16esw_burn_mac_addr(vm_instance_t *vm,u_int nm_bay, struct cisco_eeprom *eeprom) { m_uint8_t eeprom_ver; size_t offset; n_eth_addr_t addr; m_uint16_t pid; pid = (m_uint16_t)getpid(); /* Generate automatically the MAC address */ addr.eth_addr_byte[0] = vm_get_mac_addr_msb(vm); addr.eth_addr_byte[1] = vm->instance_id & 0xFF; addr.eth_addr_byte[2] = pid >> 8; addr.eth_addr_byte[3] = pid & 0xFF; addr.eth_addr_byte[4] = 0xF0 + nm_bay; addr.eth_addr_byte[5] = 0x00; /* Read EEPROM format version */ cisco_eeprom_get_byte(eeprom,0,&eeprom_ver); if (eeprom_ver != 4) return(-1); if (cisco_eeprom_v4_find_field(eeprom,0xCF,&offset) == -1) return(-1); cisco_eeprom_set_region(eeprom,offset,addr.eth_addr_byte,6); return(0); } /* Initialize a NM-16ESW module */ struct nm_16esw_data * dev_nm_16esw_init(vm_instance_t *vm,char *name,u_int nm_bay, struct pci_bus *pci_bus,int pci_device,int irq) { struct nm_16esw_data *data; struct bcm5600_port *port; struct vdevice *dev; int i,port_id; /* Allocate the private data structure */ if (!(data = malloc(sizeof(*data)))) { fprintf(stderr,"%s: out of memory\n",name); return NULL; } memset(data,0,sizeof(*data)); pthread_mutex_init(&data->lock,NULL); data->name = name; data->nr_port = 16; data->vm = vm; /* Create the BCM5600 tables */ if (bcm5600_table_create(data) == -1) return NULL; /* Clear the various tables */ bcm5600_reset_arl(data); data->arl_cnt[0] = 1; data->t_ptable = bcm5600_table_find(data,BCM5600_ADDR_PTABLE0); data->t_vtable = bcm5600_table_find(data,BCM5600_ADDR_VTABLE0); data->t_arl = bcm5600_table_find(data,BCM5600_ADDR_ARL0); data->t_marl = bcm5600_table_find(data,BCM5600_ADDR_MARL0); data->t_tbmap = bcm5600_table_find(data,BCM5600_ADDR_TBMAP0); data->t_ttr = bcm5600_table_find(data,BCM5600_ADDR_TTR0); /* Initialize ports */ data->cpu_port = 27; for(i=0;inr_port;i++) { port_id = nm16esw_port_mapping[i]; port = &data->ports[port_id]; port->id = port_id; snprintf(port->name,sizeof(port->name),"Fa%u/%d",nm_bay,i); } /* Create the BCM5605 PCI device */ data->pci_dev = pci_dev_add(pci_bus,name, BCM5605_PCI_VENDOR_ID,BCM5605_PCI_PRODUCT_ID, pci_device,0,irq,data, NULL,pci_bcm5605_read,pci_bcm5605_write); if (!data->pci_dev) { fprintf(stderr,"%s: unable to create PCI device.\n",name); return NULL; } /* Create the BCM5605 device itself */ if (!(dev = dev_create(name))) { fprintf(stderr,"%s: unable to create device.\n",name); return NULL; } dev->phys_addr = 0; dev->phys_len = 0x200000; dev->handler = dev_bcm5605_access; /* Store device info */ dev->priv_data = data; data->dev = dev; /* Create the TX ring scanner */ data->tx_tid = ptask_add((ptask_callback)dev_bcm5600_handle_txring, data,NULL); /* Start the MAC address ager */ data->ager_tid = timer_create_entry(15000,FALSE,10, (timer_proc)bcm5600_arl_ager,data); return data; } /* Remove a NM-16ESW from the specified slot */ int dev_nm_16esw_remove(struct nm_16esw_data *data) { /* Stop the Ager */ timer_remove(data->ager_tid); /* Stop the TX ring task */ ptask_remove(data->tx_tid); /* Remove device + PCI stuff */ pci_dev_remove(data->pci_dev); vm_unbind_device(data->vm,data->dev); cpu_group_rebuild_mts(data->vm->cpu_group); free(data->dev); /* Free all tables and registers */ bcm5600_table_free(data); bcm5600_reg_free(data); free(data); return(0); } /* Bind a Network IO descriptor */ int dev_nm_16esw_set_nio(struct nm_16esw_data *d,u_int port_id, netio_desc_t *nio) { struct bcm5600_port *port; if (!d || (port_id >= d->nr_port)) return(-1); /* define the new NIO */ port = &d->ports[nm16esw_port_mapping[port_id]]; port->nio = nio; netio_rxl_add(nio,(netio_rx_handler_t)dev_bcm5600_handle_rxring,d,port); return(0); } /* Unbind a Network IO descriptor */ int dev_nm_16esw_unset_nio(struct nm_16esw_data *d,u_int port_id) { struct bcm5600_port *port; if (!d || (port_id >= d->nr_port)) return(-1); port = &d->ports[nm16esw_port_mapping[port_id]]; if (port->nio) { netio_rxl_remove(port->nio); port->nio = NULL; } return(0); } /* Show debugging information */ int dev_nm_16esw_show_info(struct nm_16esw_data *d) { BCM_LOCK(d); printf("ARL count = %u\n\n",d->arl_cnt[0]); bcm5600_dump_main_tables(d); bcm5600_mirror_show_status(d); bcm5600_reg_dump(d,FALSE); BCM_UNLOCK(d); return(0); } dynamips-0.2.14/common/dev_nm_16esw.h000066400000000000000000000022111241034141600173500ustar00rootroot00000000000000/* * Cisco simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __DEV_NM_16ESW_H__ #define __DEV_NM_16ESW_H__ #include #include "utils.h" #include "cpu.h" #include "vm.h" #include "device.h" #include "net_io.h" #include "cisco_eeprom.h" /* Forward declaration for NM-16ESW private data */ struct nm_16esw_data; /* Rewrite the base MAC address */ int dev_nm_16esw_burn_mac_addr(vm_instance_t *vm,u_int nm_bay, struct cisco_eeprom *eeprom); /* Initialize a NM-16ESW module */ struct nm_16esw_data * dev_nm_16esw_init(vm_instance_t *vm,char *name,u_int nm_bay, struct pci_bus *pci_bus,int pci_device,int irq); /* Remove a NM-16ESW from the specified slot */ int dev_nm_16esw_remove(struct nm_16esw_data *data); /* Bind a Network IO descriptor */ int dev_nm_16esw_set_nio(struct nm_16esw_data *d,u_int port_id, netio_desc_t *nio); /* Unbind a Network IO descriptor */ int dev_nm_16esw_unset_nio(struct nm_16esw_data *d,u_int port_id); /* Show debugging information */ int dev_nm_16esw_show_info(struct nm_16esw_data *d); #endif dynamips-0.2.14/common/dev_ns16552.c000066400000000000000000000243601241034141600167400ustar00rootroot00000000000000/* * Cisco 3600 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * Patched by Jeremy Grossmann for the GNS3 project (www.gns3.net) * * NS16552 DUART. */ #include #include #include #include #include #include #include #include #include "ptask.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_vtty.h" /* Debugging flags */ #define DEBUG_UNKNOWN 1 #define DEBUG_ACCESS 0 /* Interrupt Enable Register (IER) */ #define IER_ERXRDY 0x1 #define IER_ETXRDY 0x2 /* Interrupt Identification Register */ #define IIR_NPENDING 0x01 /* 0: irq pending, 1: no irq pending */ #define IIR_TXRDY 0x02 #define IIR_RXRDY 0x04 /* Line Status Register (LSR) */ #define LSR_RXRDY 0x01 #define LSR_TXRDY 0x20 #define LSR_TXEMPTY 0x40 /* Line Control Register */ #define LCR_WRL0 0x01 #define LCR_WRL1 0x02 #define LCR_NUMSTOP 0x04 #define LCR_PARITYON 0x08 #define LCR_PARITYEV 0x10 #define LCR_STICKP 0x20 #define LCR_SETBREAK 0x40 #define LCR_DIVLATCH 0x80 /* Modem Control Register */ #define MCR_DTR 0x01 #define MCR_RTS 0x02 #define MCR_OUT1 0x04 #define MCR_OUT2 0x08 #define MCR_LOOP 0x10 /* UART channel */ struct ns16552_channel { u_int ier,output; vtty_t *vtty; }; /* NS16552 structure */ struct ns16552_data { vm_obj_t vm_obj; struct vdevice dev; vm_instance_t *vm; u_int irq; /* Register offset divisor */ u_int reg_div; /* Periodic task to trigger DUART IRQ */ ptask_id_t tid; struct ns16552_channel channel[2]; u_int duart_irq_seq; u_int line_control_reg; u_int div_latch; u_int baud_divisor; }; /* Console port input */ static void tty_con_input(vtty_t *vtty) { struct ns16552_data *d = vtty->priv_data; if (d->channel[0].ier & IER_ERXRDY) vm_set_irq(d->vm,d->irq); } /* AUX port input */ static void tty_aux_input(vtty_t *vtty) { struct ns16552_data *d = vtty->priv_data; if (d->channel[1].ier & IER_ERXRDY) vm_set_irq(d->vm,d->irq); } /* IRQ trickery for Console and AUX ports */ static int tty_trigger_dummy_irq(struct ns16552_data *d,void *arg) { d->duart_irq_seq++; if (d->duart_irq_seq == 2) { if (d->channel[0].ier & IER_ETXRDY) { d->channel[0].output = TRUE; vm_set_irq(d->vm,d->irq); } if (d->channel[1].ier & IER_ETXRDY) { d->channel[1].output = TRUE; vm_set_irq(d->vm,d->irq); } d->duart_irq_seq = 0; } return(0); } /* * dev_ns16552_access() */ void *dev_ns16552_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct ns16552_data *d = dev->priv_data; int channel = 0; u_char odata; if (op_type == MTS_READ) *data = 0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"NS16552","read from 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"NS16552","write to 0x%x, value=0x%llx, pc=0x%llx\n", offset,*data,cpu_get_pc(cpu)); } #endif offset >>= d->reg_div; if (offset >= 0x08) channel = 1; // From the NS16552V datasheet, the following is known about the registers // Bit 4 is channel // Value 0 Receive or transmit buffer // Value 1 Interrupt enable // Value 2 Interrupt identification (READ), FIFO Config (Write) // Value 3 Line Control (Appears in IOS) // 0x1 - Word Length Selector bit 0 // 0x2 - Word Length Selector bit 1 // 0x4 - Num stop bits // 0x8 - Parity Enable // 0x16 - Parity even // 0x32 - Stick Parity // 0x64 - Set Break // 0x128 - Division Latch // Value 4 Modem Control (Appears in IOS) // Value 5 Line status // Value 6 Modem Status // Value 7 Scratch switch(offset) { /* Receiver Buffer Reg. (RBR) / Transmitting Holding Reg. (THR) */ case 0x00: case 0x08: if (d->div_latch == 0) { if (op_type == MTS_WRITE) { vtty_put_char(d->channel[channel].vtty,(char)*data); if (d->channel[channel].ier & IER_ETXRDY) vm_set_irq(d->vm,d->irq); d->channel[channel].output = TRUE; } else *data = vtty_get_char(d->channel[channel].vtty); } else { if (op_type == MTS_WRITE) d->baud_divisor = ((*data) & 0x00ff) | (d->baud_divisor & 0xff00); } break; /* Interrupt Enable Register (IER) */ case 0x01: case 0x09: if (d->div_latch == 0) { if (op_type == MTS_READ) { *data = d->channel[channel].ier; } else { d->channel[channel].ier = *data & 0xFF; if ((*data & 0x02) == 0) { /* transmit holding register */ d->channel[channel].vtty->managed_flush = TRUE; vtty_flush(d->channel[channel].vtty); } } } else { if (op_type == MTS_WRITE) d->baud_divisor = (((*data) & 0xff)<<8)|(d->baud_divisor & 0xff); } break; /* Interrupt Ident Register (IIR) */ case 0x02: case 0x0A: if (d->div_latch == 0) { vm_clear_irq(d->vm,d->irq); if (op_type == MTS_READ) { odata = IIR_NPENDING; if (vtty_is_char_avail(d->channel[channel].vtty)) { odata = IIR_RXRDY; } else { if (d->channel[channel].output) { odata = IIR_TXRDY; d->channel[channel].output = 0; } } *data = odata; } } break; /* Line Control Register (LCR) */ case 0x03: case 0x0B: if (op_type == MTS_READ) { *data = d->line_control_reg; } else { d->line_control_reg = (uint)*data; uint bits = 5; _maybe_used char *stop = "1"; _maybe_used char *parity = "no "; _maybe_used char *parityeven = "odd"; if (*data & LCR_WRL0) bits+=1; if (*data & LCR_WRL1) bits+=2; if (*data & LCR_NUMSTOP) { if ( bits >= 6) { stop = "2"; } else { stop = "1.5"; } } if (*data & LCR_PARITYON) parity=""; //Parity on if (*data & LCR_PARITYEV) parityeven="even"; // DIV LATCH changes the behavior of 0x0,0x1,and 0x2 if (*data & LCR_DIVLATCH) { d->div_latch = 1; } else { _maybe_used uint baud; d->div_latch = 0; // 1200 divisor was 192 // 9600 divisor was 24 // 19200 divisor was 12 // Suggests a crystal of 3686400 hz if (d->baud_divisor > 0) { baud = 3686400 / (d->baud_divisor * 16); } else { baud = 0; } } } break; /* MODEM Control Register (MCR) */ case 0x04: case 0x0C: if (op_type != MTS_READ) { _maybe_used char *f1 = ""; _maybe_used char *f2 = ""; _maybe_used char *f3 = ""; _maybe_used char *f4 = ""; _maybe_used char *f5 = ""; if (*data & MCR_DTR) f1 = "DTR "; if (*data & MCR_RTS) f2 = "RTS "; if (*data & MCR_OUT1) f3 = "OUT1 "; if (*data & MCR_OUT2) f4 = "OUT2 "; if (*data & MCR_LOOP) f5 = "LOOP "; } break; /* Line Status Register (LSR) */ case 0x05: case 0x0D: if (op_type == MTS_READ) { odata = 0; if (vtty_is_char_avail(d->channel[channel].vtty)) odata |= LSR_RXRDY; odata |= LSR_TXRDY|LSR_TXEMPTY; *data = odata; } break; /* MODEM Status Register (MSR)? case 0x06: case 0x0E: */ /* Scratch Register (SCR)? case 0x07: case 0x0F: */ #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"NS16552","read from addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu, "NS16552","write to addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* Shutdown a NS16552 device */ void dev_ns16552_shutdown(vm_instance_t *vm,struct ns16552_data *d) { if (d != NULL) { d->channel[0].vtty->read_notifier = NULL; d->channel[1].vtty->read_notifier = NULL; /* Remove the periodic task */ ptask_remove(d->tid); /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* Create a NS16552 device */ int dev_ns16552_init(vm_instance_t *vm,m_uint64_t paddr,m_uint32_t len, u_int reg_div,u_int irq,vtty_t *vtty_A,vtty_t *vtty_B) { struct ns16552_data *d; /* Allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"NS16552: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); d->vm = vm; d->irq = irq; d->reg_div = reg_div; d->channel[0].vtty = vtty_A; d->channel[1].vtty = vtty_B; d->line_control_reg = 0; d->div_latch = 0; d->baud_divisor=0; vm_object_init(&d->vm_obj); d->vm_obj.name = "ns16552"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_ns16552_shutdown; /* Set device properties */ dev_init(&d->dev); d->dev.name = "ns16552"; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_ns16552_access; d->dev.priv_data = d; vtty_A->priv_data = d; vtty_B->priv_data = d; vtty_A->read_notifier = tty_con_input; vtty_B->read_notifier = tty_aux_input; /* Trigger periodically a dummy IRQ to flush buffers */ d->tid = ptask_add((ptask_callback)tty_trigger_dummy_irq,d,NULL); /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_nvram.c000066400000000000000000000226201241034141600170350ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot. All rights reserved. * * Dallas DS1216 chip emulation: * - NVRAM * - Calendar * * Manuals: * http://pdfserv.maxim-ic.com/en/ds/DS1216-DS1216H.pdf * * Calendar stuff written by Mtve. */ #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "fs_nvram.h" #define DEBUG_ACCESS 0 /* SmartWatch pattern (p.5 of documentation) */ #define PATTERN 0x5ca33ac55ca33ac5ULL /* NVRAM private data */ struct nvram_data { vm_obj_t vm_obj; struct vdevice dev; char *filename; u_int cal_state; m_uint64_t cal_read,cal_write; }; /* Convert an 8-bit number to a BCD form */ static m_uint8_t u8_to_bcd(m_uint8_t val) { return(((val / 10) << 4) + (val % 10)); } /* Get the current time (p.8) */ static m_uint64_t get_current_time(cpu_gen_t *cpu) { m_uint64_t res; struct tm *tmx; time_t ct; time(&ct); tmx = localtime(&ct); res = u8_to_bcd(tmx->tm_sec) << 8; res += u8_to_bcd(tmx->tm_min) << 16; res += u8_to_bcd(tmx->tm_hour) << 24; res += ((m_uint64_t)(u8_to_bcd(tmx->tm_wday))) << 32; res += ((m_uint64_t)(u8_to_bcd(tmx->tm_mday))) << 40; res += ((m_uint64_t)(u8_to_bcd(tmx->tm_mon+1))) << 48; res += ((m_uint64_t)(u8_to_bcd(tmx->tm_year))) << 56; return(res); } /* * dev_nvram_access() */ void *dev_nvram_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct nvram_data *d = dev->priv_data; #if DEBUG_ACCESS if (op_type == MTS_READ) cpu_log(cpu,dev->name,"read access to offset=0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); else cpu_log(cpu,dev->name, "write access to vaddr=0x%x, pc=0x%llx, val=0x%llx\n", offset,cpu_get_pc(cpu),*data); #endif switch(offset) { case 0x03: if (op_type == MTS_READ) { *data = d->cal_read & 1; d->cal_read >>= 1; } else { d->cal_write >>= 1; d->cal_write |= *data << 63; if (d->cal_write == PATTERN) { d->cal_state = 1; vm_log(cpu->vm,"Calendar","reset\n"); d->cal_read = get_current_time(cpu); } else if(d->cal_state > 0 && d->cal_state++ == 64) { /* untested */ vm_log(cpu->vm,"Calendar","set 0x%016llx\n",d->cal_write); d->cal_state = 0; } } return NULL; } return((void *)(dev->host_addr + offset)); } /* Set appropriately the config register if the NVRAM is empty */ static void set_config_register(struct vdevice *dev,u_int *conf_reg) { m_uint32_t *ptr; int i; ptr = (m_uint32_t *)dev->host_addr; for(i=0;i<(dev->phys_len/4);i++,ptr++) if (*ptr) return; /* * nvram is empty: tells IOS to ignore its contents. * http://www.cisco.com/en/US/products/hw/routers/ps274/products_installation_guide_chapter09186a008007de4c.html */ *conf_reg |= 0x0040; printf("NVRAM is empty, setting config register to 0x%x\n",*conf_reg); } /* Shutdown the NVRAM device */ void dev_nvram_shutdown(vm_instance_t *vm,struct nvram_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* We don't remove the file, since it used as permanent storage */ if (d->filename) free(d->filename); /* Free the structure itself */ free(d); } } /* Create the NVRAM device */ int dev_nvram_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len, u_int *conf_reg) { struct nvram_data *d; u_char *ptr; /* allocate the private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"NVRAM: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_nvram_shutdown; if (!(d->filename = vm_build_filename(vm,name))) { fprintf(stderr,"NVRAM: unable to create filename.\n"); return(-1); } dev_init(&d->dev); d->dev.name = name; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_nvram_access; d->dev.fd = memzone_create_file(d->filename,d->dev.phys_len,&ptr); d->dev.host_addr = (m_iptr_t)ptr; d->dev.flags = VDEVICE_FLAG_NO_MTS_MMAP|VDEVICE_FLAG_SYNC; d->dev.priv_data = d; if (d->dev.fd == -1) { fprintf(stderr,"NVRAM: unable to map file '%s'\n",d->filename); return(-1); } /* Modify the configuration register if NVRAM is empty */ set_config_register(&d->dev,conf_reg); /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } /* Compute NVRAM checksum */ m_uint16_t nvram_cksum_old(vm_instance_t *vm,m_uint64_t addr,size_t count) { m_uint32_t sum = 0; while(count > 1) { sum = sum + physmem_copy_u16_from_vm(vm,addr); addr += sizeof(m_uint16_t); count -= sizeof(m_uint16_t); } if (count > 0) sum = sum + ((physmem_copy_u16_from_vm(vm,addr) & 0xFF) << 8); while(sum>>16) sum = (sum & 0xffff) + (sum >> 16); return(~sum); } /** Generic function for implementations of vm->platform->nvram_extract_config. * If nvram_size is 0, it is set based on the file size. * @param dev_name Device name * @param nvram_offset Where the filesystem starts * @param nvram_size Size of the filesystem */ int generic_nvram_extract_config(vm_instance_t *vm, char *dev_name, size_t nvram_offset, size_t nvram_size, m_uint32_t addr, u_int format, u_char **startup_config, size_t *startup_len, u_char **private_config, size_t *private_len) { // XXX add const to dev_name u_char *base_ptr; struct vdevice *nvram_dev; off_t file_size; int fd; int ret = 0; fs_nvram_t *fs = NULL; if ((nvram_dev = dev_get_by_name(vm, dev_name))) dev_sync(nvram_dev); fd = vm_mmap_open_file(vm, dev_name, &base_ptr, &file_size); if (fd == -1) return(-1); if (nvram_size == 0 && file_size >= nvram_offset + FS_NVRAM_SECTOR_SIZE) nvram_size = (size_t)file_size - nvram_offset; if (file_size < nvram_offset + nvram_size) { vm_error(vm,"generic_nvram_extract_config: NVRAM filesystem doesn't fit inside the %s file!\n", dev_name); goto done; } // normal + backup fs = fs_nvram_open(base_ptr + nvram_offset, nvram_size, addr, (format & FS_NVRAM_FORMAT_MASK)); if (fs == NULL) goto err_errno; ret = fs_nvram_read_config(fs, startup_config, startup_len, private_config, private_len); if (ret) goto err_ret; done: if (fs) fs_nvram_close(fs); vm_mmap_close_file(fd, base_ptr, file_size); return(ret); err_errno: ret = errno; err_ret: vm_error(vm,"generic_nvram_extract_config: %s\n", strerror(ret)); ret = -1; goto done; } /** Generic function for implementations of vm->platform->nvram_push_config. * If nvram_size is 0, it is set based on the file size. * Preserves startup-config if startup_config is NULL. * Preserves private-config if private_config is NULL. * @param dev_name Device name * @param file_size File size * @param nvram_offset Where the filesystem starts * @param nvram_size Size of the filesystem */ int generic_nvram_push_config(vm_instance_t *vm, char *dev_name, size_t file_size, size_t nvram_offset, size_t nvram_size, m_uint32_t addr, u_int format, u_char *startup_config, size_t startup_len, u_char *private_config, size_t private_len) { // XXX add const to dev_name u_char *base_ptr; int fd; int ret = 0; fs_nvram_t *fs = NULL; u_char *prev_startup_config = NULL; u_char *prev_private_config = NULL; size_t prev_startup_len; size_t prev_private_len; if (nvram_size == 0 && file_size >= nvram_offset + FS_NVRAM_SECTOR_SIZE) nvram_size = file_size - nvram_offset; if (file_size < nvram_offset + nvram_size) { vm_error(vm, "generic_nvram_push_config: NVRAM filesystem doesn't fit inside the %s file!\n", dev_name); return(-1); } fd = vm_mmap_create_file(vm, dev_name, file_size, &base_ptr); if (fd == -1) return(-1); fs = fs_nvram_open(base_ptr + nvram_offset, nvram_size, addr, (format & FS_NVRAM_FORMAT_MASK) | FS_NVRAM_FLAG_OPEN_CREATE); if (fs == NULL) goto err_errno; ret = fs_nvram_read_config(fs, &prev_startup_config, &prev_startup_len, &prev_private_config, &prev_private_len); if (ret) goto err_ret; if (startup_config == NULL ) { startup_config = prev_startup_config; startup_len = prev_startup_len; } if (private_config == NULL) { private_config = prev_private_config; private_len = prev_private_len; } ret = fs_nvram_write_config(fs, startup_config, startup_len, private_config, private_len); if (ret) goto err_ret; done: if (fs) fs_nvram_close(fs); if (prev_startup_config) free(prev_startup_config); if (prev_private_config) free(prev_private_config); vm_mmap_close_file(fd, base_ptr, file_size); return(ret); err_errno: ret = errno; err_ret: vm_error(vm, "generic_nvram_push_config: %s\n", strerror(ret)); ret = -1; goto done; } dynamips-0.2.14/common/dev_pa_a1.c000066400000000000000000001532251241034141600167010ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (C) 2005,2006 Christophe Fillot. All rights reserved. * * PA-A1 ATM interface based on TI1570 and PLX 9060-ES. * * EEPROM types: * - 0x17: PA-A1-OC3MM * - 0x2C: PA-A1-OC3SM * - 0x2D: PA-A1-OC3UTP * * IOS command: "sh controller atm2/0" * * Manuals: * * Texas Instruments TNETA1570 ATM segmentation and reassembly device * with integrated 64-bit PCI-host interface * http://focus.ti.com/docs/prod/folders/print/tneta1570.html * * PLX 9060-ES * http://www.plxtech.com/products/io_accelerators/PCI9060/default.htm * * TODO: * - RX error handling and RX AAL5-related stuff * - HEC and AAL5 CRC fields. * * Cell trains for faster NETIO communications ? */ #include #include #include #include #include #include "crc.h" #include "atm.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "ptask.h" #include "dev_c7200.h" /* Debugging flags */ #define DEBUG_ACCESS 0 #define DEBUG_UNKNOWN 0 #define DEBUG_TRANSMIT 0 #define DEBUG_RECEIVE 0 #define DEBUG_TX_DMA 0 /* PCI vendor/product codes */ #define TI1570_PCI_VENDOR_ID 0x104c #define TI1570_PCI_PRODUCT_ID 0xa001 #define PLX_9060ES_PCI_VENDOR_ID 0x10b5 #define PLX_9060ES_PCI_PRODUCT_ID 0x906e /* Number of buffers transmitted at each TX DMA ring scan pass */ #define TI1570_TXDMA_PASS_COUNT 16 /* TI1570 Internal Registers (p.58 of doc) */ #define TI1570_REG_CONFIG 0x0000 /* Configuration registers */ #define TI1570_REG_STATUS 0x0001 /* Status register */ #define TI1570_REG_IMASK 0x0002 /* Interrupt-mask register */ #define TI1570_REG_RGT_RAT 0x0003 /* RGT + RAT cycle-counter */ #define TI1570_REG_RX_UNKNOWN 0x0004 /* RX Unknown Register */ #define TI1570_REG_TX_CRING_SIZE 0x0005 /* TX Completion ring sizes */ #define TI1570_REG_RX_CRING_SIZE 0x0006 /* RX Completion ring sizes */ #define TI1570_REG_TX_PSR_SIZE 0x0007 /* TX Pkt-seg ring size + FIFO */ #define TI1570_REG_HEC_AAL5_DISC 0x0008 /* HEC err + AAL5 CPCS discard */ #define TI1570_REG_UNK_PROTO_CNT 0x0009 /* Unknown-protocols counter */ #define TI1570_REG_RX_ATM_COUNT 0x000A /* ATM-cells-received counter */ #define TI1570_REG_TX_ATM_COUNT 0x000B /* ATM-cells-tranmitted counter */ #define TI1570_REG_TX_RX_FIFO 0x000C /* TX/RX FIFO occupancy, VCI mask */ #define TI1570_REG_SCHED_SIZE 0x000D /* Scheduler Table size */ #define TI1570_REG_SOFT_RESET 0x000E /* Software Reset */ #define TI1570_REG_TCR_WOI_ADDR 0x0080 /* TX Compl. Ring w/o IRQ addr. */ #define TI1570_REG_TCR_WI_ADDR 0x0081 /* TX Compl. Ring w/ IRQ addr. */ #define TI1570_REG_RCR_WOI_ADDR 0x0082 /* RX Compl. Ring w/o IRQ addr. */ #define TI1570_REG_RCR_WI_ADDR 0x0083 /* RX Compl. Ring w/ IRQ addr. */ /* TI1570 configuration register (p.59) */ #define TI1570_CFG_EN_RAT 0x00000001 /* Reassembly Aging */ #define TI1570_CFG_BP_SEL 0x00000002 /* IRQ on packet or buffer */ #define TI1570_CFG_EN_RX 0x00000010 /* RX enable */ #define TI1570_CFG_EN_TX 0x00000020 /* TX enable */ #define TI1570_CFG_SMALL_MAP 0x00000040 /* Small map */ /* TI1570 status register (p.61) */ #define TI1570_STAT_CP_TX 0x00000001 /* Transmit completion ring */ #define TI1570_STAT_RX_IRR 0x00000040 /* Receive unknown reg set */ #define TI1570_STAT_CP_RX 0x00000080 /* Receive completion ring */ #define TI1570_STAT_TX_FRZ 0x00000100 /* TX Freeze */ #define TI1570_STAT_RX_FRZ 0x00000200 /* RX Freeze */ /* Mask for RX/TX completion-ring sizes */ #define TI1570_TCR_SIZE_MASK 0x00001FFF /* TX compl. ring size mask */ #define TI1570_RCR_SIZE_MASK 0x000003FF /* RX compl. ring size mask */ /* TI1750 TX packet segmentation ring register */ #define TI1570_PSR_SIZE_MASK 0x000000FF /* pkt-seg ring size */ /* Total size of the TI1570 Control Memory */ #define TI1570_CTRL_MEM_SIZE 0x100000 /* Offsets of the TI1570 structures (p.66) */ #define TI1570_TX_SCHED_OFFSET 0x0000 /* TX scheduler table */ #define TI1570_INTERNAL_REGS_OFFSET 0x3200 /* Internal Registers */ #define TI1570_FREE_BUFFERS_OFFSET 0x3800 /* Free-Buffer Pointers */ #define TI1570_RX_DMA_PTR_TABLE_OFFSET 0x4000 /* RX VPI/VCI pointer table */ #define TI1570_TX_DMA_TABLE_OFFSET 0x8000 /* TX DMA state table */ #define TI1570_RX_DMA_TABLE_OFFSET 0x10000 /* RX DMA state table */ /* TX scheduler table */ #define TI1570_TX_SCHED_ENTRY_COUNT 6200 #define TI1570_TX_SCHED_ENTRY_MASK 0x3FF /* Entry mask */ #define TI1570_TX_SCHED_E0_SHIFT 0 /* Shift for entry 0 */ #define TI1570_TX_SCHED_E1_SHIFT 16 /* Shift for entry 0 */ /* TX DMA state table */ #define TI1570_TX_DMA_ACT 0x80000000 /* ACTive (word 0) */ #define TI1570_TX_DMA_SOP 0x40000000 /* Start of Packet (SOP) */ #define TI1570_TX_DMA_EOP 0x20000000 /* End of Packet (EOP) */ #define TI1570_TX_DMA_ABORT 0x10000000 /* Abort */ #define TI1570_TX_DMA_TCR_SELECT 0x02000000 /* TX comp. ring selection */ #define TI1570_TX_DMA_AAL_TYPE_MASK 0x0C000000 /* AAL-type mask */ #define TI1570_TX_DMA_AAL_TRWPTI 0x00000000 /* Transp. AAL w/ PTI set */ #define TI1570_TX_DMA_AAL_AAL5 0x04000000 /* AAL5 */ #define TI1570_TX_DMA_AAL_TRWOPTI 0x08000000 /* Transp. AAL w/o PTI set */ #define TI1570_TX_DMA_OFFSET_MASK 0x00FF0000 #define TI1570_TX_DMA_OFFSET_SHIFT 16 #define TI1570_TX_DMA_DCOUNT_MASK 0x0000FFFF #define TI1570_TX_DMA_ON 0x80000000 /* DMA state (word 3) */ #define TI1570_TX_DMA_RING_OFFSET_MASK 0x3FFFFF00 #define TI1570_TX_DMA_RING_OFFSET_SHIFT 8 #define TI1570_TX_DMA_RING_INDEX_MASK 0x000000FF #define TI1570_TX_DMA_RING_AAL5_LEN_MASK 0x0000FFFF typedef struct ti1570_tx_dma_entry ti1570_tx_dma_entry_t; struct ti1570_tx_dma_entry { m_uint32_t ctrl_buf; /* Ctrl, Buffer Offset, Buffer data-byte count */ m_uint32_t cb_addr; /* Current Buffer Address */ m_uint32_t atm_hdr; /* 4-byte ATM header */ m_uint32_t dma_state; /* DMA state + Packet segmentation ring address */ m_uint32_t nb_addr; /* Next Buffer address */ m_uint32_t sb_addr; /* Start of Buffer address */ m_uint32_t aal5_crc; /* Partial AAL5-transmit CRC */ m_uint32_t aal5_ctrl; /* AAL5-control field and length field */ }; /* TX Packet-Segmentation Rings */ #define TI1570_TX_RING_OWN 0x80000000 /* If set, packet is ready */ #define TI1570_TX_RING_PTR_MASK 0x3FFFFFFF /* Buffer pointer */ /* TX Data Buffers */ #define TI1570_TX_BUFFER_RDY 0x80000000 /* If set, buffer is ready */ #define TI1570_TX_BUFFER_SOP 0x40000000 /* First buffer of packet */ #define TI1570_TX_BUFFER_EOP 0x20000000 /* Last buffer of packet */ #define TI1570_TX_BUFFER_ABORT 0x10000000 /* Abort */ #define TI1570_TX_BUFFER_OFFSET_MASK 0x00FF0000 #define TI1570_TX_BUFFER_OFFSET_SHIFT 16 #define TI1570_TX_BUFFER_DCOUNT_MASK 0x0000FFFF typedef struct ti1570_tx_buffer ti1570_tx_buffer_t; struct ti1570_tx_buffer { m_uint32_t ctrl_buf; /* Ctrl, Buffer offset, Buffer data-byte count */ m_uint32_t nb_addr; /* Start-of-next buffer pointer */ m_uint32_t atm_hdr; /* 4-byte ATM header */ m_uint32_t aal5_ctrl; /* PCS-UU/CPI field (AAL5 control field) */ }; /* TX completion-ring */ #define TI1570_TCR_OWN 0x80000000 /* OWNner bit */ #define TI1570_TCR_ABORT 0x40000000 /* Abort */ /* RX VPI/VCI DMA pointer table */ #define TI1570_RX_VPI_ENABLE 0x80000000 /* VPI enabled ? */ #define TI1570_RX_BASE_PTR_MASK 0x7FFF0000 /* Base pointer mask */ #define TI1570_RX_BASE_PTR_SHIFT 16 /* Base pointer shift */ #define TI1570_RX_VCI_RANGE_MASK 0x0000FFFF /* Valid VCI range */ /* RX DMA state table (p.36) */ #define TI1570_RX_DMA_ACT 0x80000000 /* ACTive (word 0) */ #define TI1570_RX_DMA_RCR_SELECT 0x20000000 /* RX comp. ring selection */ #define TI1570_RX_DMA_WAIT_EOP 0x10000000 /* Wait for EOP */ #define TI1570_RX_DMA_AAL_TYPE_MASK 0x0C000000 /* AAL-type mask */ #define TI1570_RX_DMA_AAL_PTI 0x00000000 /* PTI based tr. AAL pkt */ #define TI1570_RX_DMA_AAL_AAL5 0x04000000 /* AAL5 */ #define TI1570_RX_DMA_AAL_CNT 0x08000000 /* Cnt based tr. AAL pkt */ #define TI1570_RX_DMA_FIFO 0x02000000 /* FIFO used for free bufs */ #define TI1570_RX_DMA_TR_CNT_MASK 0xFFFF0000 /* Cnt-based Tr-AAL */ #define TI1570_RX_DMA_TR_CNT_SHIFT 16 #define TI1570_RX_DMA_CB_LEN_MASK 0x0000FFFF /* Current buffer length */ #define TI1570_RX_DMA_ON 0x80000000 /* DMA state (word 6) */ #define TI1570_RX_DMA_FILTER 0x40000000 /* Filter */ #define TI1570_RX_DMA_FB_PTR_MASK 0x3FFFFFFF /* Free-buffer ptr mask */ #define TI1570_RX_DMA_FB_INDEX_MASK 0x000000FF /* Index with Free-buf ring */ typedef struct ti1570_rx_dma_entry ti1570_rx_dma_entry_t; struct ti1570_rx_dma_entry { m_uint32_t ctrl; /* Control field, EFCN cell cnt, pkt length */ m_uint32_t cb_addr; /* Current Buffer Address */ m_uint32_t sb_addr; /* Start of Buffer address */ m_uint32_t cb_len; /* Transp-AAL pkt counter, current buf length */ m_uint32_t sp_ptr; /* Start-of-packet pointer */ m_uint32_t aal5_crc; /* Partial AAL5-receive CRC */ m_uint32_t fbr_entry; /* Free-buffer ring-pointer table entry */ m_uint32_t timeout; /* Timeout value, current timeout count */ }; /* RX free-buffer ring pointer table entry (p.39) */ #define TI1570_RX_FBR_PTR_MASK 0xFFFFFFFC #define TI1570_RX_FBR_BS_MASK 0xFFFF0000 /* Buffer size mask */ #define TI1570_RX_FBR_BS_SHIFT 16 #define TI1570_RX_FBR_RS_MASK 0x0000FC00 /* Ring size mask */ #define TI1570_RX_FBR_RS_SHIFT 10 #define TI1570_RX_FBR_IDX_MASK 0x000003FF /* Current index mask */ typedef struct ti1570_rx_fbr_entry ti1570_rx_fbr_entry_t; struct ti1570_rx_fbr_entry { m_uint32_t fbr_ptr; /* RX free-buffer ring pointer */ m_uint32_t ring_size; /* Ring size and buffer size */ }; /* RX buffer pointer (p.41) */ #define TI1570_RX_BUFPTR_OWN 0x80000000 /* If set, buffer is ready */ #define TI1570_RX_BUFPTR_MASK 0x3FFFFFFF /* Buffer address mask */ /* RX data buffer (p.42) */ #define TI1570_RX_BUFFER_SOP 0x80000000 /* Start-of-Packet buffer */ #define TI1570_RX_BUFFER_EOP 0x40000000 /* End-of-Packet buffer */ typedef struct ti1570_rx_buffer ti1570_rx_buffer_t; struct ti1570_rx_buffer { m_uint32_t reserved; /* Reserved, not used by the TI1570 */ m_uint32_t ctrl; /* Control field, Start of next buffer pointer */ m_uint32_t atm_hdr; /* ATM header */ m_uint32_t user; /* User-defined value */ }; /* Internal structure to hold free buffer info */ typedef struct ti1570_rx_buf_holder ti1570_rx_buf_holder_t; struct ti1570_rx_buf_holder { m_uint32_t buf_addr; m_uint32_t buf_size; ti1570_rx_buffer_t rx_buf; }; /* RX completion ring entry */ #define TI1570_RCR_PKT_OVFLW 0x80000000 /* Packet overflow (word 0) */ #define TI1570_RCR_CRC_ERROR 0x40000000 /* CRC error */ #define TI1570_RCR_BUF_STARV 0x20000000 /* Buffer starvation */ #define TI1570_RCR_TIMEOUT 0x10000000 /* Reassembly timeout */ #define TI1570_RCR_ABORT 0x08000000 /* Abort condition */ #define TI1570_RCR_AAL5 0x04000000 /* AAL5 indicator */ #define TI1570_RCR_VALID 0x80000000 /* Start-ptr valid (word 2) */ #define TI1570_RCR_OWN 0x80000000 /* Buffer ready (word 4) */ #define TI1570_RCR_ERROR 0x40000000 /* Error entry */ typedef struct ti1570_rcr_entry ti1570_rcr_entry_t; struct ti1570_rcr_entry { m_uint32_t atm_hdr; /* ATM header */ m_uint32_t error; /* Error Indicator + Congestion cell count */ m_uint32_t sp_addr; /* Start of packet */ m_uint32_t aal5_trailer; /* AAL5 trailer */ m_uint32_t fbr_entry; /* Free-buffer ring-pointer table entry */ m_uint32_t res[3]; /* Reserved, not used by the TI1570 */ }; /* TI1570 Data */ struct pa_a1_data { char *name; /* IRQ clearing counter */ u_int irq_clear_count; /* Control Memory pointer */ m_uint32_t *ctrl_mem_ptr; /* TI1570 internal registers */ m_uint32_t *iregs; /* TX FIFO cell */ m_uint8_t txfifo_cell[ATM_CELL_SIZE]; m_uint32_t txfifo_avail,txfifo_pos; /* TX Scheduler table */ m_uint32_t *tx_sched_table; /* TX DMA state table */ ti1570_tx_dma_entry_t *tx_dma_table; /* TX/RX completion ring current position */ m_uint32_t tcr_wi_pos,tcr_woi_pos; m_uint32_t rcr_wi_pos,rcr_woi_pos; /* RX VPI/VCI DMA pointer table */ m_uint32_t *rx_vpi_vci_dma_table; /* RX DMA state table */ ti1570_rx_dma_entry_t *rx_dma_table; /* RX Free-buffer ring pointer table */ ti1570_rx_fbr_entry_t *rx_fbr_table; /* Virtual device */ struct vdevice *dev; /* PCI device information */ struct pci_device *pci_dev_ti,*pci_dev_plx; /* Virtual machine */ vm_instance_t *vm; /* NetIO descriptor */ netio_desc_t *nio; /* TX ring scanner task id */ ptask_id_t tx_tid; }; /* Log a TI1570 message */ #define TI1570_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* Reset the TI1570 (forward declaration) */ static void ti1570_reset(struct pa_a1_data *d,int clear_ctrl_mem); /* Update the interrupt status */ static inline void dev_pa_a1_update_irq_status(struct pa_a1_data *d) { if (d->iregs[TI1570_REG_STATUS] & d->iregs[TI1570_REG_IMASK]) { pci_dev_trigger_irq(d->vm,d->pci_dev_ti); } else { pci_dev_clear_irq(d->vm,d->pci_dev_ti); } } /* * dev_pa_a1_access() */ void *dev_pa_a1_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct pa_a1_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"TI1570","read access to offset = 0x%x, pc = 0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"TI1570","write access to vaddr = 0x%x, pc = 0x%llx, " "val = 0x%llx\n",offset,cpu_get_pc(cpu),*data); } #endif /* Specific cases */ switch(offset) { /* Status register */ case 0x3204: if (op_type == MTS_READ) { *data = d->iregs[TI1570_REG_STATUS]; if (++d->irq_clear_count == 5) { d->iregs[TI1570_REG_STATUS] &= ~0x3FF; d->irq_clear_count = 0; } dev_pa_a1_update_irq_status(d); } break; /* Software Reset register */ case 0x3238: TI1570_LOG(d,"reset issued.\n"); ti1570_reset(d,FALSE); break; case 0x18000c: if (op_type == MTS_READ) { *data = 0xa6; return NULL; } break; } /* Control Memory access */ if (offset < TI1570_CTRL_MEM_SIZE) { if (op_type == MTS_READ) *data = d->ctrl_mem_ptr[offset >> 2]; else d->ctrl_mem_ptr[offset >> 2] = *data; return NULL; } /* Unknown offset */ #if DEBUG_UNKNOWN if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name,"write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n",offset,*data,cpu_get_pc(cpu),op_size); } #endif return NULL; } /* Fetch a TX data buffer from host memory */ static void ti1570_read_tx_buffer(struct pa_a1_data *d,m_uint32_t addr, ti1570_tx_buffer_t *tx_buf) { physmem_copy_from_vm(d->vm,tx_buf,addr,sizeof(ti1570_tx_buffer_t)); /* byte-swapping */ tx_buf->ctrl_buf = vmtoh32(tx_buf->ctrl_buf); tx_buf->nb_addr = vmtoh32(tx_buf->nb_addr); tx_buf->atm_hdr = vmtoh32(tx_buf->atm_hdr); tx_buf->aal5_ctrl = vmtoh32(tx_buf->aal5_ctrl); } /* Acquire a TX buffer */ static int ti1570_acquire_tx_buffer(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde, m_uint32_t buf_addr) { ti1570_tx_buffer_t tx_buf; m_uint32_t buf_offset; #if DEBUG_TRANSMIT TI1570_LOG(d,"ti1570_acquire_tx_buffer: acquiring buffer at address 0x%x\n", buf_addr); #endif /* Read the TX buffer from host memory */ ti1570_read_tx_buffer(d,buf_addr,&tx_buf); /* The buffer must be ready to be acquired */ if (!(tx_buf.ctrl_buf & TI1570_TX_BUFFER_RDY)) return(FALSE); /* Put the TX buffer data into the TX DMA state entry */ tde->ctrl_buf = tx_buf.ctrl_buf; tde->nb_addr = tx_buf.nb_addr << 2; /* Read the ATM header only from the first buffer */ if (tx_buf.ctrl_buf & TI1570_TX_BUFFER_SOP) { tde->atm_hdr = tx_buf.atm_hdr; tde->aal5_ctrl = tx_buf.aal5_ctrl; tde->aal5_crc = 0; /* will be inverted at first CRC update */ } /* Compute the current-buffer-data address */ buf_offset = tx_buf.ctrl_buf & TI1570_TX_BUFFER_OFFSET_MASK; buf_offset >>= TI1570_TX_BUFFER_OFFSET_SHIFT; tde->cb_addr = buf_addr + sizeof(tx_buf) + buf_offset; /* Remember the start address of the buffer */ tde->sb_addr = buf_addr; return(TRUE); } /* Returns TRUE if the TX DMA entry is for an AAL5 packet */ static inline int ti1570_is_tde_aal5(ti1570_tx_dma_entry_t *tde) { m_uint32_t pkt_type; pkt_type = tde->ctrl_buf & TI1570_TX_DMA_AAL_TYPE_MASK; return(pkt_type == TI1570_TX_DMA_AAL_AAL5); } /* Update the AAL5 partial CRC */ static void ti1570_update_aal5_crc(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde) { tde->aal5_crc = crc32_compute(~tde->aal5_crc, &d->txfifo_cell[ATM_HDR_SIZE], ATM_PAYLOAD_SIZE); } /* * Update the TX DMA entry buffer offset and count when "data_len" bytes * have been transmitted. */ static void ti1570_update_tx_dma_bufinfo(ti1570_tx_dma_entry_t *tde, m_uint32_t buf_size, m_uint32_t data_len) { m_uint32_t tmp,tot_len; /* update the current buffer address */ tde->cb_addr += data_len; /* set the remaining byte count */ tmp = tde->ctrl_buf & ~TI1570_TX_BUFFER_DCOUNT_MASK; tde->ctrl_buf = tmp + (buf_size - data_len); /* update the AAL5 count */ if (ti1570_is_tde_aal5(tde)) { tot_len = tde->aal5_ctrl & TI1570_TX_DMA_RING_AAL5_LEN_MASK; tot_len += data_len; tmp = (tde->aal5_ctrl & ~TI1570_TX_DMA_RING_AAL5_LEN_MASK) + tot_len; tde->aal5_ctrl = tmp; } } /* Clear the TX fifo */ static void ti1570_clear_tx_fifo(struct pa_a1_data *d) { d->txfifo_avail = ATM_PAYLOAD_SIZE; d->txfifo_pos = ATM_HDR_SIZE; memset(d->txfifo_cell,0,ATM_CELL_SIZE); } /* * Transmit the TX FIFO cell through the NETIO infrastructure if * it is full. */ static void ti1570_send_tx_fifo(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde, int update_aal5_crc) { if (d->txfifo_avail == 0) { #if DEBUG_TRANSMIT TI1570_LOG(d,"ti1570_transmit_cell: transmitting to NETIO device\n"); mem_dump(log_file,d->txfifo_cell,ATM_CELL_SIZE); #endif if (update_aal5_crc) ti1570_update_aal5_crc(d,tde); netio_send(d->nio,d->txfifo_cell,ATM_CELL_SIZE); ti1570_clear_tx_fifo(d); } } /* Add padding to the FIFO */ static void ti1570_add_tx_padding(struct pa_a1_data *d,m_uint32_t len) { if (len > d->txfifo_avail) { TI1570_LOG(d,"ti1570_add_tx_padding: trying to add too large " "padding (avail: 0x%x, pad: 0x%x)\n",d->txfifo_avail,len); len = d->txfifo_avail; } memset(&d->txfifo_cell[d->txfifo_pos],0,len); d->txfifo_pos += len; d->txfifo_avail -= len; } /* Initialize an ATM cell for tranmitting */ static m_uint32_t ti1570_init_tx_atm_cell(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde, int set_pti) { m_uint32_t buf_size,len,atm_hdr; buf_size = tde->ctrl_buf & TI1570_TX_DMA_DCOUNT_MASK; len = m_min(buf_size,d->txfifo_avail); #if DEBUG_TRANSMIT TI1570_LOG(d,"ti1570_init_tx_atm_cell: data ptr=0x%x, " "buf_size=%u (0x%x), len=%u (0x%x), atm_hdr=0x%x\n", tde->cb_addr,buf_size,buf_size,len,len,tde->atm_hdr); #endif /* copy the ATM header */ atm_hdr = tde->atm_hdr; if (set_pti) { atm_hdr &= ~ATM_PTI_NETWORK; atm_hdr |= ATM_PTI_EOP; } atm_hdr = htonl(atm_hdr); memcpy(&d->txfifo_cell[0],&atm_hdr,sizeof(atm_hdr)); /* compute HEC field */ atm_insert_hec(d->txfifo_cell); /* copy the payload and try to transmit if the FIFO is full */ if (len > 0) { physmem_copy_from_vm(d->vm,&d->txfifo_cell[d->txfifo_pos], tde->cb_addr,len); d->txfifo_pos += len; d->txfifo_avail -= len; } ti1570_update_tx_dma_bufinfo(tde,buf_size,len); return(len); } /* * Transmit an Transparent-AAL ATM cell through the NETIO infrastructure. */ static int ti1570_transmit_transp_cell(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde, int atm_set_eop,int *buf_end) { m_uint32_t buf_size,len; int pkt_end,last_cell; pkt_end = tde->ctrl_buf & TI1570_TX_DMA_EOP; buf_size = tde->ctrl_buf & TI1570_TX_DMA_DCOUNT_MASK; last_cell = FALSE; if (!pkt_end) { len = ti1570_init_tx_atm_cell(d,tde,FALSE); ti1570_send_tx_fifo(d,tde,FALSE); if ((buf_size - len) == 0) *buf_end = TRUE; return(FALSE); } /* this is the end of packet and the last buffer */ if (buf_size <= d->txfifo_avail) last_cell = TRUE; len = ti1570_init_tx_atm_cell(d,tde,last_cell & atm_set_eop); if (last_cell) ti1570_add_tx_padding(d,d->txfifo_avail); ti1570_send_tx_fifo(d,tde,FALSE); return(last_cell); } /* Add the AAL5 trailer to the TX FIFO */ static void ti1570_add_aal5_trailer(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde) { m_uint8_t *trailer; trailer = &d->txfifo_cell[ATM_AAL5_TRAILER_POS]; /* Control field + Length */ *(m_uint32_t *)trailer = htonl(tde->aal5_ctrl); /* Final CRC-32 computation */ tde->aal5_crc = crc32_compute(~tde->aal5_crc, &d->txfifo_cell[ATM_HDR_SIZE], ATM_PAYLOAD_SIZE - 4); *(m_uint32_t *)(trailer+4) = htonl(~tde->aal5_crc); /* Consider the FIFO as full */ d->txfifo_avail = 0; } /* * Tranmit an AAL5 cell through the NETIO infrastructure. * * Returns TRUE if this is the real end of packet. */ static int ti1570_transmit_aal5_cell(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde, int *buf_end) { m_uint32_t buf_size,len; int pkt_end; pkt_end = tde->ctrl_buf & TI1570_TX_DMA_EOP; buf_size = tde->ctrl_buf & TI1570_TX_DMA_DCOUNT_MASK; #if DEBUG_TRANSMIT TI1570_LOG(d,"ti1570_transmit_aal5_cell: data ptr=0x%x, " "buf_size=0x%x (%u)\n",tde->cb_addr,buf_size,buf_size); #endif /* If this is not the end of packet, transmit the cell normally */ if (!pkt_end) { len = ti1570_init_tx_atm_cell(d,tde,FALSE); ti1570_send_tx_fifo(d,tde,TRUE); if ((buf_size - len) == 0) *buf_end = TRUE; return(FALSE); } /* * This is the end of packet, check if we need to emit a special cell * for the AAL5 trailer. */ if ((buf_size + ATM_AAL5_TRAILER_SIZE) <= d->txfifo_avail) { len = ti1570_init_tx_atm_cell(d,tde,TRUE); /* add the padding */ ti1570_add_tx_padding(d,d->txfifo_avail - ATM_AAL5_TRAILER_SIZE); /* add the AAL5 trailer at offset 40 */ ti1570_add_aal5_trailer(d,tde); /* we can transmit the cell */ ti1570_send_tx_fifo(d,tde,FALSE); *buf_end = TRUE; return(TRUE); } /* Transmit the cell normally */ len = ti1570_init_tx_atm_cell(d,tde,FALSE); ti1570_add_tx_padding(d,d->txfifo_avail); ti1570_send_tx_fifo(d,tde,TRUE); return(FALSE); } /* Update the TX completion ring */ static void ti1570_update_tx_cring(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde) { m_uint32_t tcr_addr,tcr_end,val; if (tde->ctrl_buf & TI1570_TX_DMA_TCR_SELECT) { /* TX completion ring with interrupt */ tcr_addr = d->iregs[TI1570_REG_TCR_WI_ADDR] + (d->tcr_wi_pos * 4); } else { /* TX completion ring without interrupt */ tcr_addr = d->iregs[TI1570_REG_TCR_WOI_ADDR] + (d->tcr_woi_pos * 4); } #if DEBUG_TRANSMIT TI1570_LOG(d,"ti1570_update_tx_cring: posting 0x%x at address 0x%x\n", tde->sb_addr,tcr_addr); physmem_dump_vm(d->vm,tde->sb_addr,sizeof(ti1570_tx_buffer_t) >> 2); #endif /* we have a TX freeze if the buffer belongs to the host */ val = physmem_copy_u32_from_vm(d->vm,tcr_addr); if (!(val & TI1570_TCR_OWN)) { d->iregs[TI1570_REG_STATUS] |= TI1570_STAT_TX_FRZ; return; } /* put the buffer address in the ring */ val = tde->sb_addr >> 2; if (tde->ctrl_buf & TI1570_TX_DMA_ABORT) val |= TI1570_TCR_ABORT; physmem_copy_u32_to_vm(d->vm,tcr_addr,val); /* update the internal position pointer */ if (tde->ctrl_buf & TI1570_TX_DMA_TCR_SELECT) { tcr_end = d->iregs[TI1570_REG_TX_CRING_SIZE] & TI1570_TCR_SIZE_MASK; if ((d->tcr_wi_pos++) == tcr_end) d->tcr_wi_pos = 0; } else { tcr_end = (d->iregs[TI1570_REG_TX_CRING_SIZE] >> 16); tcr_end &= TI1570_TCR_SIZE_MASK; if ((d->tcr_woi_pos++) == tcr_end) d->tcr_woi_pos = 0; } } /* Analyze a TX DMA state table entry */ static int ti1570_scan_tx_dma_entry_single(struct pa_a1_data *d, m_uint32_t index) { ti1570_tx_dma_entry_t *tde; m_uint32_t psr_base,psr_addr,psr_entry,psr_end; m_uint32_t buf_addr,buf_size,pkt_type,tmp; m_uint32_t psr_index; int atm_set_eop = 0; int pkt_end,buf_end = 0; tde = &d->tx_dma_table[index]; /* The DMA channel state flag must be ON */ if (!(tde->dma_state & TI1570_TX_DMA_ON)) return(FALSE); #if DEBUG_TX_DMA /* We have a running DMA channel */ TI1570_LOG(d,"ti1570_scan_tx_dma_entry: TX DMA entry %u is ON " "(ctrl_buf = 0x%x)\n",index,tde->ctrl_buf); #endif /* Is this the start of a new packet ? */ if (!(tde->ctrl_buf & TI1570_TX_DMA_ACT)) { #if DEBUG_TX_DMA TI1570_LOG(d,"ti1570_scan_tx_dma_entry: TX DMA entry %u is not ACT\n", index); #endif /* No packet yet, fetch it from the packet-segmentation ring */ psr_base = tde->dma_state & TI1570_TX_DMA_RING_OFFSET_MASK; psr_index = tde->dma_state & TI1570_TX_DMA_RING_INDEX_MASK; /* Compute address of the current packet segmentation ring entry */ psr_addr = (psr_base + psr_index) << 2; psr_entry = physmem_copy_u32_from_vm(d->vm,psr_addr); #if DEBUG_TX_DMA TI1570_LOG(d,"ti1570_scan_tx_dma_entry: psr_addr = 0x%x, " "psr_entry = 0x%x\n",psr_addr,psr_entry); #endif /* The packet-segmentation-ring entry is owned by host, quit now */ if (!(psr_entry & TI1570_TX_RING_OWN)) return(FALSE); /* Acquire the first buffer (it MUST be in the ready state) */ buf_addr = (psr_entry & TI1570_TX_RING_PTR_MASK) << 2; if (!ti1570_acquire_tx_buffer(d,tde,buf_addr)) { TI1570_LOG(d,"ti1570_scan_tx_dma_entry: PSR entry with OWN bit set " "but buffer without RDY bit set.\n"); return(FALSE); } /* Set ACT bit for the DMA channel */ tde->ctrl_buf |= TI1570_TX_DMA_ACT; } /* Compute the remaining size and determine the packet type */ buf_size = tde->ctrl_buf & TI1570_TX_DMA_DCOUNT_MASK; pkt_type = tde->ctrl_buf & TI1570_TX_DMA_AAL_TYPE_MASK; pkt_end = tde->ctrl_buf & TI1570_TX_DMA_EOP; #if DEBUG_TRANSMIT TI1570_LOG(d,"ti1570_scan_tx_dma_entry: ctrl_buf=0x%8.8x, " "cb_addr=0x%8.8x, atm_hdr=0x%8.8x, dma_state=0x%8.8x\n", tde->ctrl_buf, tde->cb_addr, tde->atm_hdr, tde->dma_state); TI1570_LOG(d,"ti1570_scan_tx_dma_entry: nb_addr=0x%8.8x, " "sb_addr=0x%8.8x, aal5_crc=0x%8.8x, aal5_ctrl=0x%8.8x\n", tde->nb_addr, tde->sb_addr, tde->aal5_crc, tde->aal5_ctrl); #endif /* * If the current buffer is now empty and if this is not the last * buffer in the current packet, try to fetch a new buffer. * If the next buffer is not yet ready, we have finished. */ if (!buf_size && !pkt_end && !ti1570_acquire_tx_buffer(d,tde,tde->nb_addr)) return(FALSE); switch(pkt_type) { case TI1570_TX_DMA_AAL_TRWPTI: atm_set_eop = 1; case TI1570_TX_DMA_AAL_TRWOPTI: /* Transmit the ATM cell transparently */ pkt_end = ti1570_transmit_transp_cell(d,tde,atm_set_eop,&buf_end); break; case TI1570_TX_DMA_AAL_AAL5: pkt_end = ti1570_transmit_aal5_cell(d,tde,&buf_end); break; default: TI1570_LOG(d,"ti1570_scan_tx_dma_entry: invalid AAL-type\n"); return(FALSE); } /* Re-read the remaining buffer size */ buf_size = tde->ctrl_buf & TI1570_TX_DMA_DCOUNT_MASK; /* Put the buffer address in the transmit completion ring */ if (buf_end) ti1570_update_tx_cring(d,tde); /* * If we have reached end of packet (EOP): clear the ACT bit, * give back the packet-segmentation ring entry to the host, * and increment the PSR index. */ if (pkt_end) { tde->ctrl_buf &= ~TI1570_TX_DMA_ACT; /* Clear the OWN bit of the packet-segmentation ring entry */ psr_base = tde->dma_state & TI1570_TX_DMA_RING_OFFSET_MASK; psr_index = (tde->dma_state & TI1570_TX_DMA_RING_INDEX_MASK); psr_addr = (psr_base + psr_index) << 2; psr_entry = physmem_copy_u32_from_vm(d->vm,psr_addr); psr_entry &= ~TI1570_TX_RING_OWN; physmem_copy_u32_to_vm(d->vm,psr_addr,psr_entry); /* Increment the packet-segmentation ring index */ psr_index++; psr_end = d->iregs[TI1570_REG_TX_PSR_SIZE] >> 16; psr_end &= TI1570_PSR_SIZE_MASK; if (psr_index > psr_end) { psr_index = 0; #if DEBUG_TX_DMA TI1570_LOG(d,"ti1570_scan_tx_dma_entry: PSR ring rotation " "(psr_end = %u)\n",psr_end); #endif } tmp = (tde->dma_state & ~TI1570_TX_DMA_RING_INDEX_MASK); tmp |= (psr_index & TI1570_TX_DMA_RING_INDEX_MASK); tde->dma_state = tmp; } /* Generate an interrupt if required */ if (tde->ctrl_buf & TI1570_TX_DMA_TCR_SELECT) { if (((d->iregs[TI1570_REG_CONFIG] & TI1570_CFG_BP_SEL) && buf_end) || pkt_end) { d->iregs[TI1570_REG_STATUS] |= TI1570_STAT_CP_TX; dev_pa_a1_update_irq_status(d); } } return(TRUE); } /* Analyze a TX DMA state table entry */ static void ti1570_scan_tx_dma_entry(struct pa_a1_data *d,m_uint32_t index) { int i; for(i=0;i>1;i++) { cw = d->tx_sched_table[i]; /* We have 2 index in TX DMA state table per word */ index0 = (cw >> TI1570_TX_SCHED_E0_SHIFT) & TI1570_TX_SCHED_ENTRY_MASK; index1 = (cw >> TI1570_TX_SCHED_E1_SHIFT) & TI1570_TX_SCHED_ENTRY_MASK; /* Scan the two entries (null entry => nothing to do) */ if (index0) ti1570_scan_tx_dma_entry(d,index0); if (index1) ti1570_scan_tx_dma_entry(d,index1); } } /* * Read a RX buffer from the host memory. */ static void ti1570_read_rx_buffer(struct pa_a1_data *d,m_uint32_t addr, ti1570_rx_buffer_t *rx_buf) { physmem_copy_from_vm(d->vm,rx_buf,addr,sizeof(ti1570_rx_buffer_t)); /* byte-swapping */ rx_buf->reserved = vmtoh32(rx_buf->reserved); rx_buf->ctrl = vmtoh32(rx_buf->ctrl); rx_buf->atm_hdr = vmtoh32(rx_buf->atm_hdr); rx_buf->user = vmtoh32(rx_buf->user); } /* Update the RX completion ring */ static void ti1570_update_rx_cring(struct pa_a1_data *d, ti1570_rx_dma_entry_t *rde, m_uint32_t atm_hdr, m_uint32_t aal5_trailer, m_uint32_t err_ind, m_uint32_t fbuf_valid) { m_uint32_t rcr_addr,rcr_end,aal_type,ptr,val; ti1570_rcr_entry_t rcre; if (rde->ctrl & TI1570_RX_DMA_RCR_SELECT) { /* RX completion ring with interrupt */ rcr_addr = d->iregs[TI1570_REG_RCR_WI_ADDR]; rcr_addr += (d->rcr_wi_pos * sizeof(rcre)); } else { /* RX completion ring without interrupt */ rcr_addr = d->iregs[TI1570_REG_RCR_WOI_ADDR]; rcr_addr += (d->rcr_woi_pos * sizeof(rcre)); } #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_update_rx_cring: posting 0x%x at address 0x%x\n", (rde->sp_ptr << 2),rcr_addr); physmem_dump_vm(d->vm,rde->sp_ptr<<2,sizeof(ti1570_rx_buffer_t) >> 2); #endif /* we have a RX freeze if the buffer belongs to the host */ ptr = rcr_addr + OFFSET(ti1570_rcr_entry_t,fbr_entry); val = physmem_copy_u32_from_vm(d->vm,ptr); if (!(val & TI1570_RCR_OWN)) { TI1570_LOG(d,"ti1570_update_rx_cring: RX freeze...\n"); d->iregs[TI1570_REG_STATUS] |= TI1570_STAT_RX_FRZ; return; } /* fill the RX completion ring entry and write it back to the host */ memset(&rcre,0,sizeof(rcre)); /* word 0: atm header from last cell received */ rcre.atm_hdr = atm_hdr; /* word 1: error indicator */ aal_type = rde->ctrl & TI1570_RX_DMA_AAL_TYPE_MASK; if (aal_type == TI1570_RX_DMA_AAL_AAL5) rcre.error |= TI1570_RCR_AAL5; rcre.error |= err_ind; /* word 2: Start of packet */ if (fbuf_valid) rcre.sp_addr = TI1570_RCR_VALID | rde->sp_ptr; /* word 3: AAL5 trailer */ rcre.aal5_trailer = aal5_trailer; /* word 4: OWN + error entry + free-buffer ring pointer */ rcre.fbr_entry = rde->fbr_entry & TI1570_RX_DMA_FB_PTR_MASK; if (err_ind) rcre.fbr_entry |= TI1570_RCR_ERROR; /* byte-swap and write this back to the host memory */ rcre.atm_hdr = htonl(rcre.atm_hdr); rcre.error = htonl(rcre.error); rcre.sp_addr = htonl(rcre.sp_addr); rcre.aal5_trailer = htonl(rcre.aal5_trailer); rcre.fbr_entry = htonl(rcre.fbr_entry); physmem_copy_to_vm(d->vm,&rcre,rcr_addr,sizeof(rcre)); /* clear the active bit of the RX DMA entry */ rde->ctrl &= ~TI1570_RX_DMA_ACT; /* update the internal position pointer */ if (rde->ctrl & TI1570_RX_DMA_RCR_SELECT) { rcr_end = d->iregs[TI1570_REG_RX_CRING_SIZE] & TI1570_RCR_SIZE_MASK; if ((d->rcr_wi_pos++) == rcr_end) d->rcr_wi_pos = 0; /* generate the appropriate IRQ */ d->iregs[TI1570_REG_STATUS] |= TI1570_STAT_CP_RX; dev_pa_a1_update_irq_status(d); } else { rcr_end = (d->iregs[TI1570_REG_RX_CRING_SIZE] >> 16); rcr_end &= TI1570_RCR_SIZE_MASK; if ((d->rcr_woi_pos++) == rcr_end) d->rcr_woi_pos = 0; } } /* * Acquire a free RX buffer. * * Returns FALSE if no buffer is available (buffer starvation). */ static int ti1570_acquire_rx_buffer(struct pa_a1_data *d, ti1570_rx_dma_entry_t *rde, ti1570_rx_buf_holder_t *rbh, m_uint32_t atm_hdr) { ti1570_rx_fbr_entry_t *fbr_entry = NULL; m_uint32_t bp_addr,buf_addr,buf_size,buf_idx; m_uint32_t ring_index,ring_size; m_uint32_t buf_ptr,val; int fifo = FALSE; /* To keep this fucking compiler quiet */ ring_size = 0; buf_idx = 0; if (rde->ctrl & TI1570_RX_DMA_FIFO) { bp_addr = (rde->fbr_entry & TI1570_RX_DMA_FB_PTR_MASK) << 2; buf_ptr = physmem_copy_u32_from_vm(d->vm,bp_addr); buf_size = d->iregs[TI1570_REG_TX_PSR_SIZE] & 0xFFFF; fifo = TRUE; #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_acquire_rx_buffer: acquiring FIFO buffer\n"); #endif } else { ring_index = rde->fbr_entry & TI1570_RX_DMA_FB_INDEX_MASK; fbr_entry = &d->rx_fbr_table[ring_index]; #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_acquire_rx_buffer: acquiring non-FIFO buffer, " "ring index=%u (0x%x)\n",ring_index,ring_index); #endif /* Compute the number of entries in ring */ ring_size = fbr_entry->ring_size & TI1570_RX_FBR_RS_MASK; ring_size >>= TI1570_RX_FBR_RS_SHIFT; ring_size = (ring_size << 4) + 15 + 1; /* Compute the buffer size */ buf_size = fbr_entry->ring_size & TI1570_RX_FBR_BS_MASK; buf_size >>= TI1570_RX_FBR_BS_SHIFT; /* Compute the buffer address */ buf_idx = fbr_entry->ring_size & TI1570_RX_FBR_IDX_MASK; bp_addr = fbr_entry->fbr_ptr + (buf_idx << 2); #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_acquire_rx_buffer: ring size=%u (0x%x), " "buf size=%u ATM cells\n",ring_size,ring_size,buf_size); TI1570_LOG(d,"ti1570_acquire_rx_buffer: buffer index=%u (0x%x), " "buffer ptr address = 0x%x\n",buf_idx,buf_idx,bp_addr); #endif buf_ptr = physmem_copy_u32_from_vm(d->vm,bp_addr); } #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_acquire_rx_buffer: buf_ptr = 0x%x\n",buf_ptr); #endif /* The TI1570 must own the buffer */ if (!(buf_ptr & TI1570_RX_BUFPTR_OWN)) { TI1570_LOG(d,"ti1570_acquire_rx_buffer: no free buffer available.\n"); return(FALSE); } /* * If we are using a ring, we have to clear the OWN bit and increment * the index field. */ if (!fifo) { buf_ptr &= ~TI1570_RX_BUFPTR_OWN; physmem_copy_u32_to_vm(d->vm,bp_addr,buf_ptr); if (++buf_idx == ring_size) { #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_acquire_rx_buffer: buf_idx=0x%x, " "ring_size=0x%x -> resetting buf_idx\n", buf_idx-1,ring_size); #endif buf_idx = 0; } val = fbr_entry->ring_size & ~TI1570_RX_FBR_IDX_MASK; val |= buf_idx; fbr_entry->ring_size = val; } /* Get the buffer address */ buf_addr = (buf_ptr & TI1570_RX_BUFPTR_MASK) << 2; #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_acquire_rx_buffer: buf_addr = 0x%x\n",buf_addr); #endif /* Read the buffer descriptor itself and store info for caller */ rbh->buf_addr = buf_addr; rbh->buf_size = buf_size; ti1570_read_rx_buffer(d,buf_addr,&rbh->rx_buf); /* Clear the control field */ physmem_copy_u32_to_vm(d->vm,buf_addr+OFFSET(ti1570_rx_buffer_t,ctrl),0); /* Store the ATM header in data buffer */ physmem_copy_u32_to_vm(d->vm,buf_addr+OFFSET(ti1570_rx_buffer_t,atm_hdr), atm_hdr); return(TRUE); } /* Insert a new free buffer in a RX DMA entry */ static void ti1570_insert_rx_free_buf(struct pa_a1_data *d, ti1570_rx_dma_entry_t *rde, ti1570_rx_buf_holder_t *rbh) { m_uint32_t val,aal_type; aal_type = rde->ctrl & TI1570_RX_DMA_AAL_TYPE_MASK; /* Set current and start of buffer addresses */ rde->cb_addr = rbh->buf_addr + sizeof(ti1570_rx_buffer_t); rde->sb_addr = rbh->buf_addr >> 2; /* Set the buffer length */ val = rbh->buf_size; if (aal_type == TI1570_RX_DMA_AAL_CNT) val |= (rde->aal5_crc & 0xFFFF) << 16; rde->cb_len = val; } /* Store a RX cell */ static int ti1570_store_rx_cell(struct pa_a1_data *d, ti1570_rx_dma_entry_t *rde, m_uint8_t *atm_cell) { m_uint32_t aal_type,atm_hdr,aal5_trailer,pti,real_eop,pti_eop; m_uint32_t prev_buf_addr,buf_len,val,ptr,cnt; ti1570_rx_buf_holder_t rbh; real_eop = pti_eop = FALSE; aal_type = rde->ctrl & TI1570_RX_DMA_AAL_TYPE_MASK; /* Extract PTI from the ATM header */ atm_hdr = ntohl(*(m_uint32_t *)&atm_cell[0]); pti = (atm_hdr & ATM_HDR_PTI_MASK) >> ATM_HDR_PTI_SHIFT; /* PTI == 0x1 => EOP */ if ((pti == 0x01) || (pti == 0x03)) pti_eop = TRUE; if (rde->ctrl & TI1570_RX_DMA_WAIT_EOP) { TI1570_LOG(d,"ti1570_store_rx_cell: EOP processing, not handled yet.\n"); return(FALSE); } /* AAL5 special processing */ if (aal_type == TI1570_RX_DMA_AAL_AAL5) { /* Check that we don't exceed 1366 cells for AAL5 */ /* XXX TODO */ } else { /* EOP processing for non counter-based transparent-AAL packets */ if ((rde->ctrl & TI1570_RX_DMA_WAIT_EOP) && pti_eop) { /* XXX TODO */ } } /* do we have enough room in buffer ? */ buf_len = rde->cb_len & TI1570_RX_DMA_CB_LEN_MASK; if (!buf_len) { prev_buf_addr = rde->sb_addr << 2; /* acquire a new free buffer */ if (!ti1570_acquire_rx_buffer(d,rde,&rbh,atm_hdr)) { rde->ctrl |= TI1570_RX_DMA_WAIT_EOP; return(FALSE); } /* insert the free buffer in the RX DMA structure */ ti1570_insert_rx_free_buf(d,rde,&rbh); /* chain the buffers (keep SOP/EOP bits intact) */ ptr = prev_buf_addr + OFFSET(ti1570_rx_buffer_t,ctrl); val = physmem_copy_u32_from_vm(d->vm,ptr); val |= rde->sb_addr; physmem_copy_u32_to_vm(d->vm,ptr,val); /* read the new buffer length */ buf_len = rde->cb_len & TI1570_RX_DMA_CB_LEN_MASK; } /* copy the ATM payload */ #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_store_rx_cell: storing cell payload at 0x%x " "(buf_addr=0x%x)\n",rde->cb_addr,rde->sb_addr << 2); #endif physmem_copy_to_vm(d->vm,&atm_cell[ATM_HDR_SIZE], rde->cb_addr,ATM_PAYLOAD_SIZE); rde->cb_addr += ATM_PAYLOAD_SIZE; /* update the current buffer length */ val = rde->cb_len & ~TI1570_RX_DMA_CB_LEN_MASK; rde->cb_len = val | (--buf_len); #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_store_rx_cell: new rde->cb_len = 0x%x, " "buf_len=0x%x\n",rde->cb_len,buf_len); #endif /* determine if this is the end of the packet (EOP) */ if (aal_type == TI1570_RX_DMA_AAL_CNT) { /* counter-based tranparent-AAL packets */ cnt = rde->cb_len & TI1570_RX_DMA_TR_CNT_MASK; cnt >>= TI1570_RX_DMA_TR_CNT_SHIFT; /* if the counter reaches 0, this is the EOP */ if (--cnt == 0) real_eop = TRUE; val = rde->cb_len & ~TI1570_RX_DMA_TR_CNT_MASK; val |= cnt << TI1570_RX_DMA_TR_CNT_SHIFT; } else { /* PTI-based transparent AAL packets or AAL5 */ if (pti_eop) real_eop = TRUE; } if (real_eop) { /* mark the buffer as EOP */ ptr = (rde->sb_addr << 2) + OFFSET(ti1570_rx_buffer_t,ctrl); val = physmem_copy_u32_from_vm(d->vm,ptr); val |= TI1570_RX_BUFFER_EOP; physmem_copy_u32_to_vm(d->vm,ptr,val); /* get the aal5 trailer */ aal5_trailer = ntohl(*(m_uint32_t *)&atm_cell[ATM_AAL5_TRAILER_POS]); /* post the entry into the appropriate RX completion ring */ ti1570_update_rx_cring(d,rde,atm_hdr,aal5_trailer,0,TRUE); } return(TRUE); } /* Handle a received ATM cell */ static int ti1570_handle_rx_cell(netio_desc_t *nio, u_char *atm_cell,ssize_t cell_len, struct pa_a1_data *d) { m_uint32_t atm_hdr,vpi,vci,vci_idx,vci_mask; m_uint32_t vci_max,rvd_entry,bptr,pti,ptr; ti1570_rx_dma_entry_t *rde = NULL; ti1570_rx_buf_holder_t rbh; if (cell_len != ATM_CELL_SIZE) { TI1570_LOG(d,"invalid RX cell size (%ld)\n",(long)cell_len); return(FALSE); } /* Extract the VPI/VCI used as index in the RX VPI/VCI DMA pointer table */ atm_hdr = ntohl(*(m_uint32_t *)&atm_cell[0]); vpi = (atm_hdr & ATM_HDR_VPI_MASK) >> ATM_HDR_VPI_SHIFT; vci = (atm_hdr & ATM_HDR_VCI_MASK) >> ATM_HDR_VCI_SHIFT; pti = (atm_hdr & ATM_HDR_PTI_MASK) >> ATM_HDR_PTI_SHIFT; #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_handle_rx_cell: received cell with VPI/VCI=%u/%u\n", vpi,vci); #endif /* Get the entry corresponding to this VPI in RX VPI/VCI dma ptr table */ rvd_entry = d->rx_vpi_vci_dma_table[vpi]; if (!(rvd_entry & TI1570_RX_VPI_ENABLE)) { TI1570_LOG(d,"ti1570_handle_rx_cell: received cell with " "unknown VPI %u (VCI=%u)\n",vpi,vci); return(FALSE); } /* * Special routing for OAM F4 cells: * - VCI 3 : OAM F4 segment cell * - VCI 4 : OAM F4 end-to-end cell */ if ((vci == 3) || (vci == 4)) rde = &d->rx_dma_table[2]; else { if ((atm_hdr & ATM_PTI_NETWORK) != 0) { switch(pti) { case 0x04: /* OAM F5-segment cell */ case 0x05: /* OAM F5 end-to-end cell */ rde = &d->rx_dma_table[0]; break; case 0x06: case 0x07: rde = &d->rx_dma_table[1]; break; } } else { /* * Standard VPI/VCI. * Apply the VCI mask if we don't have an OAM cell. */ if (!(atm_hdr & ATM_PTI_NETWORK)) { vci_mask = d->iregs[TI1570_REG_TX_RX_FIFO] >> 16; vci_idx = vci & (~vci_mask); vci_max = rvd_entry & TI1570_RX_VCI_RANGE_MASK; if (vci_idx > vci_max) { TI1570_LOG(d,"ti1570_handle_rx_cell: out-of-range VCI %u " "(VPI=%u,vci_mask=%u,vci_max=%u)\n", vci,vpi,vci_mask,vci_max); return(FALSE); } #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_handle_rx_cell: VPI/VCI=%u/%u, " "vci_mask=0x%x, vci_idx=%u (0x%x), vci_max=%u (0x%x)\n", vpi,vci,vci_mask,vci_idx,vci_idx,vci_max,vci_max); #endif bptr = (rvd_entry & TI1570_RX_BASE_PTR_MASK); bptr >>= TI1570_RX_BASE_PTR_SHIFT; bptr = (bptr + vci) * sizeof(ti1570_rx_dma_entry_t); if (bptr < TI1570_RX_DMA_TABLE_OFFSET) { TI1570_LOG(d,"ti1570_handle_rx_cell: inconsistency in " "RX VPI/VCI table, VPI/VCI=%u/u, bptr=0x%x\n", vpi,vci,bptr); return(FALSE); } bptr -= TI1570_RX_DMA_TABLE_OFFSET; rde = &d->rx_dma_table[bptr / sizeof(ti1570_rx_dma_entry_t)]; } } } if (!rde) { TI1570_LOG(d,"ti1570_handle_rx_cell: no RX DMA table entry found!\n"); return(FALSE); } /* The entry must be active */ if (!(rde->fbr_entry & TI1570_RX_DMA_ON)) return(FALSE); /* Is this the start of a new packet ? */ if (!(rde->ctrl & TI1570_RX_DMA_ACT)) { /* Try to acquire a free buffer */ if (!ti1570_acquire_rx_buffer(d,rde,&rbh,atm_hdr)) { rde->ctrl |= TI1570_RX_DMA_WAIT_EOP; return(FALSE); } /* Insert the free buffer in the RX DMA structure */ ti1570_insert_rx_free_buf(d,rde,&rbh); rde->sp_ptr = rde->sb_addr; /* Mark the RX buffer as the start of packet (SOP) */ ptr = (rde->sb_addr << 2) + OFFSET(ti1570_rx_buffer_t,ctrl); physmem_copy_u32_to_vm(d->vm,ptr,TI1570_RX_BUFFER_SOP); /* Set ACT bit for the DMA channel */ rde->ctrl |= TI1570_RX_DMA_ACT; } /* Store the received cell */ ti1570_store_rx_cell(d,rde,atm_cell); return(TRUE); } /* * pci_ti1570_read() */ static m_uint32_t pci_ti1570_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { struct pa_a1_data *d = dev->priv_data; #if DEBUG_ACCESS TI1570_LOG(d,"pci_ti1570_read: read reg 0x%x\n",reg); #endif switch(reg) { case PCI_REG_BAR0: return(d->dev->phys_addr); default: return(0); } } /* * pci_ti1570_write() */ static void pci_ti1570_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct pa_a1_data *d = dev->priv_data; #if DEBUG_ACCESS TI1570_LOG(d,"pci_ti1570_write: write reg 0x%x, value 0x%x\n",reg,value); #endif switch(reg) { case PCI_REG_BAR0: vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); TI1570_LOG(d,"registers are mapped at 0x%x\n",value); break; } } /* * pci_plx9060es_read() */ static m_uint32_t pci_plx9060es_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { //struct pa_a1_data *d = dev->priv_data; #if DEBUG_ACCESS TI1570_LOG(d,"PLX9060ES","read reg 0x%x\n",reg); #endif switch(reg) { default: return(0); } } /* * pci_plx9060es_write() */ static void pci_plx9060es_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { //struct pa_a1_data *d = dev->priv_data; #if DEBUG_ACCESS TI1570_LOG(d,"PLX9060ES","write reg 0x%x, value 0x%x\n",reg,value); #endif switch(reg) { } } /* Reset the TI1570 */ static void ti1570_reset(struct pa_a1_data *d,int clear_ctrl_mem) { ti1570_clear_tx_fifo(d); d->tcr_wi_pos = d->tcr_woi_pos = 0; d->rcr_wi_pos = d->rcr_woi_pos = 0; if (clear_ctrl_mem) memset(d->ctrl_mem_ptr,0,TI1570_CTRL_MEM_SIZE); } /* * dev_c7200_pa_a1_init() * * Add a PA-A1 port adapter into specified slot. */ int dev_c7200_pa_a1_init(vm_instance_t *vm,struct cisco_card *card) { u_int slot = card->slot_id; struct pci_device *pci_dev_ti,*pci_dev_plx; struct pa_a1_data *d; struct vdevice *dev; m_uint8_t *p; /* Allocate the private data structure for TI1570 chip */ if (!(d = malloc(sizeof(*d)))) { vm_error(vm,"%s: out of memory\n",card->dev_name); return(-1); } memset(d,0,sizeof(*d)); /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_pa("PA-A1")); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Add PCI device TI1570 */ pci_dev_ti = pci_dev_add(card->pci_bus,card->dev_name, TI1570_PCI_VENDOR_ID,TI1570_PCI_PRODUCT_ID, 0,0,c7200_net_irq_for_slot_port(slot,0),d, NULL,pci_ti1570_read,pci_ti1570_write); if (!pci_dev_ti) { vm_error(vm,"%s: unable to create PCI device TI1570.\n", card->dev_name); return(-1); } /* Add PCI device PLX9060ES */ pci_dev_plx = pci_dev_add(card->pci_bus,card->dev_name, PLX_9060ES_PCI_VENDOR_ID, PLX_9060ES_PCI_PRODUCT_ID, 1,0,-1,d, NULL,pci_plx9060es_read,pci_plx9060es_write); if (!pci_dev_plx) { vm_error(vm,"%s: unable to create PCI device PLX 9060ES.\n", card->dev_name); return(-1); } /* Create the TI1570 structure */ d->name = card->dev_name; d->vm = vm; d->pci_dev_ti = pci_dev_ti; d->pci_dev_plx = pci_dev_plx; /* Allocate the control memory */ if (!(d->ctrl_mem_ptr = malloc(TI1570_CTRL_MEM_SIZE))) { vm_error(vm,"%s: unable to create control memory.\n",card->dev_name); return(-1); } /* Standard tables for the TI1570 */ p = (m_uint8_t *)d->ctrl_mem_ptr; d->iregs = (m_uint32_t *)(p + TI1570_INTERNAL_REGS_OFFSET); d->tx_sched_table = (m_uint32_t *)(p + TI1570_TX_SCHED_OFFSET); d->tx_dma_table = (ti1570_tx_dma_entry_t *)(p + TI1570_TX_DMA_TABLE_OFFSET); d->rx_vpi_vci_dma_table = (m_uint32_t *)(p+TI1570_RX_DMA_PTR_TABLE_OFFSET); d->rx_dma_table = (ti1570_rx_dma_entry_t *)(p + TI1570_RX_DMA_TABLE_OFFSET); d->rx_fbr_table = (ti1570_rx_fbr_entry_t *)(p + TI1570_FREE_BUFFERS_OFFSET); ti1570_reset(d,TRUE); /* Create the device itself */ if (!(dev = dev_create(card->dev_name))) { vm_error(vm,"%s: unable to create device.\n",card->dev_name); return(-1); } dev->phys_addr = 0; dev->phys_len = 0x200000; dev->handler = dev_pa_a1_access; /* Store device info */ dev->priv_data = d; d->dev = dev; /* Store device info into the router structure */ card->drv_info = d; return(0); } /* Remove a PA-A1 from the specified slot */ int dev_c7200_pa_a1_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct pa_a1_data *d = card->drv_info; /* Remove the PA EEPROM */ cisco_card_unset_eeprom(card); c7200_set_slot_eeprom(VM_C7200(vm),card->slot_id,NULL); /* Remove the PCI devices */ pci_dev_remove(d->pci_dev_ti); pci_dev_remove(d->pci_dev_plx); /* Remove the device from the VM address space */ vm_unbind_device(vm,d->dev); cpu_group_rebuild_mts(vm->cpu_group); /* Free the control memory */ free(d->ctrl_mem_ptr); /* Free the device structure itself */ free(d->dev); free(d); return(0); } /* Bind a Network IO descriptor to a specific port */ int dev_c7200_pa_a1_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct pa_a1_data *d = card->drv_info; if (!d || (port_id > 0)) return(-1); if (d->nio != NULL) return(-1); d->nio = nio; d->tx_tid = ptask_add((ptask_callback)ti1570_scan_tx_sched_table,d,NULL); netio_rxl_add(nio,(netio_rx_handler_t)ti1570_handle_rx_cell,d,NULL); return(0); } /* Unbind a Network IO descriptor to a specific port */ int dev_c7200_pa_a1_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct pa_a1_data *d = card->drv_info; if (!d || (port_id > 0)) return(-1); if (d->nio) { ptask_remove(d->tx_tid); netio_rxl_remove(d->nio); d->nio = NULL; } return(0); } /* PA-A1 driver */ struct cisco_card_driver dev_c7200_pa_a1_driver = { "PA-A1", 1, 0, dev_c7200_pa_a1_init, dev_c7200_pa_a1_shutdown, NULL, dev_c7200_pa_a1_set_nio, dev_c7200_pa_a1_unset_nio, NULL, }; dynamips-0.2.14/common/dev_pa_mc8te1.c000066400000000000000000000212301241034141600174670ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (C) 2005-2006 Christophe Fillot. All rights reserved. * * PA-MC-8TE1 card. Doesn't work at this time. */ #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_c7200.h" #include "dev_plx.h" /* Debugging flags */ #define DEBUG_ACCESS 1 #define DEBUG_UNKNOWN 1 #define DEBUG_TRANSMIT 1 #define DEBUG_RECEIVE 1 /* SSRAM */ #define SSRAM_START 0x10000 #define SSRAM_END 0x30000 /* PA-MC-8TE1 Data */ struct pa_mc_data { char *name; u_int irq; /* Virtual machine */ vm_instance_t *vm; /* PCI device information */ struct vdevice dev; struct pci_device *pci_dev; /* SSRAM device */ struct vdevice ssram_dev; char *ssram_name; m_uint8_t ssram_data[0x20000]; /* PLX9054 */ char *plx_name; vm_obj_t *plx_obj; /* NetIO descriptor */ netio_desc_t *nio; /* TX ring scanner task id */ ptask_id_t tx_tid; }; /* Log a PA-MC-8TE1 message */ #define PA_MC_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* * dev_ssram_access */ static void *dev_ssram_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct pa_mc_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0; if ((offset >= SSRAM_START) && (offset < SSRAM_END)) return(&d->ssram_data[offset-SSRAM_START]); #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,d->name, "read access to offset = 0x%x, pc = 0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name,"write access to vaddr = 0x%x, pc = 0x%llx, " "val = 0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),*data,op_size); } #endif switch(offset) { case 0xfff0c: if (op_type == MTS_READ) *data = 0xdeadbeef; break; case 0xfff10: if (op_type == MTS_READ) *data = 0xbeeffeed; break; case 0x08: /* max_dsx1 */ case 0x10: /* no_buf */ case 0x18: /* ev */ if (op_type == MTS_READ) *data = 0x0ULL; break; case 0x00: /* tx packets */ if (op_type == MTS_READ) *data = 0x0; break; case 0x04: /* rx packets */ if (op_type == MTS_READ) *data = 0x0; break; case 0x0c: /* rx drops */ if (op_type == MTS_READ) *data = 0; break; } return NULL; } /* Callback when PLX9054 PCI-to-Local register is written */ static void plx9054_doorbell_callback(struct plx_data *plx_data, struct pa_mc_data *pa_data, m_uint32_t val) { printf("DOORBELL: 0x%x\n",val); /* Trigger interrupt */ //vm_set_irq(pa_data->vm,pa_data->irq); vm_set_irq(pa_data->vm,3); } /* * pa_mc8te1_access() */ _unused static void *pa_mc8te1_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct pa_mc_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to offset = 0x%x, pc = 0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,d->name,"write access to vaddr = 0x%x, pc = 0x%llx, " "val = 0x%llx\n",offset,cpu_get_pc(cpu),*data); } #endif switch(offset) { #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,d->name, "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name, "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* * pci_pos_read() */ static m_uint32_t pci_pos_read(cpu_gen_t *cpu,struct pci_device *dev,int reg) { struct pa_mc_data *d = dev->priv_data; #if DEBUG_ACCESS PA_MC_LOG(d,"read PCI register 0x%x\n",reg); #endif switch(reg) { case PCI_REG_BAR0: return(d->dev.phys_addr); default: return(0); } } /* * pci_pos_write() */ static void pci_pos_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct pa_mc_data *d = dev->priv_data; #if DEBUG_ACCESS PA_MC_LOG(d,"write 0x%x to PCI register 0x%x\n",value,reg); #endif switch(reg) { case PCI_REG_BAR0: //vm_map_device(cpu->vm,&d->dev,(m_uint64_t)value); PA_MC_LOG(d,"registers are mapped at 0x%x\n",value); break; } } /* * dev_c7200_pa_mc8te1_init() * * Add a PA-MC-8TE1 port adapter into specified slot. */ int dev_c7200_pa_mc8te1_init(vm_instance_t *vm,struct cisco_card *card) { struct pa_mc_data *d; u_int slot = card->slot_id; /* Allocate the private data structure for PA-MC-8TE1 chip */ if (!(d = malloc(sizeof(*d)))) { vm_error(vm,"%s: out of memory\n",card->dev_name); return(-1); } memset(d,0,sizeof(*d)); d->name = card->dev_name; d->vm = vm; d->irq = c7200_net_irq_for_slot_port(slot,0); /* Set the PCI bus */ card->pci_bus = vm->slots_pci_bus[slot]; /* Set the EEPROM */ cisco_card_set_eeprom(vm,card,cisco_eeprom_find_pa("PA-MC-8TE1")); c7200_set_slot_eeprom(VM_C7200(vm),slot,&card->eeprom); /* Create the PM7380 */ d->pci_dev = pci_dev_add(card->pci_bus,card->dev_name, 0x11f8, 0x7380, 0,0,d->irq,d, NULL,pci_pos_read,pci_pos_write); /* Initialize SSRAM device */ d->ssram_name = dyn_sprintf("%s_ssram",card->dev_name); dev_init(&d->ssram_dev); d->ssram_dev.name = d->ssram_name; d->ssram_dev.priv_data = d; d->ssram_dev.handler = dev_ssram_access; /* Create the PLX9054 */ d->plx_name = dyn_sprintf("%s_plx",card->dev_name); d->plx_obj = dev_plx9054_init(vm,d->plx_name, card->pci_bus,1, &d->ssram_dev,NULL); /* Set callback function for PLX9054 PCI-To-Local doorbell */ dev_plx_set_pci2loc_doorbell_cbk(d->plx_obj->data, (dev_plx_doorbell_cbk) plx9054_doorbell_callback, d); /* Store device info into the router structure */ card->drv_info = d; return(0); } /* Remove a PA-POS-OC3 from the specified slot */ int dev_c7200_pa_mc8te1_shutdown(vm_instance_t *vm,struct cisco_card *card) { struct pa_mc_data *d = card->drv_info; /* Remove the PA EEPROM */ cisco_card_unset_eeprom(card); c7200_set_slot_eeprom(VM_C7200(vm),card->slot_id,NULL); /* Remove the PCI device */ pci_dev_remove(d->pci_dev); /* Remove the PLX9054 chip */ vm_object_remove(vm,d->plx_obj); /* Remove the device from the CPU address space */ //vm_unbind_device(vm,&d->dev); vm_unbind_device(vm,&d->ssram_dev); cpu_group_rebuild_mts(vm->cpu_group); /* Free the device structure itself */ free(d); return(0); } /* Bind a Network IO descriptor to a specific port */ int dev_c7200_pa_mc8te1_set_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id,netio_desc_t *nio) { struct pa_mc_data *d = card->drv_info; if (!d || (port_id > 0)) return(-1); if (d->nio != NULL) return(-1); d->nio = nio; //d->tx_tid = ptask_add((ptask_callback)dev_pos_oc3_handle_txring,d,NULL); //netio_rxl_add(nio,(netio_rx_handler_t)dev_pos_oc3_handle_rxring,d,NULL); return(0); } /* Bind a Network IO descriptor to a specific port */ int dev_c7200_pa_mc8te1_unset_nio(vm_instance_t *vm,struct cisco_card *card, u_int port_id) { struct pa_mc_data *d = card->drv_info; if (!d || (port_id > 0)) return(-1); if (d->nio) { ptask_remove(d->tx_tid); netio_rxl_remove(d->nio); d->nio = NULL; } return(0); } /* PA-MC-8TE1 driver */ struct cisco_card_driver dev_c7200_pa_mc8te1_driver = { "PA-MC-8TE1", 0, 0, dev_c7200_pa_mc8te1_init, dev_c7200_pa_mc8te1_shutdown, NULL, dev_c7200_pa_mc8te1_set_nio, dev_c7200_pa_mc8te1_unset_nio, NULL, }; dynamips-0.2.14/common/dev_pcmcia_disk.c000066400000000000000000000554521241034141600201710ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot. All rights reserved. * * PCMCIA ATA Flash emulation. */ #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "fs_mbr.h" #include "fs_fat.h" #define DEBUG_ACCESS 0 #define DEBUG_ATA 0 #define DEBUG_READ 0 #define DEBUG_WRITE 0 /* Default disk parameters: 4 heads, 32 sectors per track */ #define DISK_NR_HEADS 4 #define DISK_SECTS_PER_TRACK 32 /* Size (in bytes) of a sector */ #define SECTOR_SIZE 512 /* ATA commands */ #define ATA_CMD_NOP 0x00 #define ATA_CMD_READ_SECTOR 0x20 #define ATA_CMD_WRITE_SECTOR 0x30 #define ATA_CMD_IDENT_DEVICE 0xEC /* ATA status */ #define ATA_STATUS_BUSY 0x80 /* Controller busy */ #define ATA_STATUS_RDY 0x40 /* Device ready */ #define ATA_STATUS_DWF 0x20 /* Write fault */ #define ATA_STATUS_DSC 0x10 /* Device ready */ #define ATA_STATUS_DRQ 0x08 /* Data Request */ #define ATA_STATUS_CORR 0x04 /* Correctable error */ #define ATA_STATUS_IDX 0x02 /* Always 0 */ #define ATA_STATUS_ERR 0x01 /* Error */ /* ATA Drive/Head register */ #define ATA_DH_LBA 0x40 /* LBA Mode */ /* Card Information Structure */ static m_uint8_t cis_table[] = { /* CISTPL_DEVICE */ 0x01, 0x03, 0xd9, /* DSPEED_250NS | WPS | DTYPE_FUNCSPEC */ 0x01, /* 2 KBytes(Units)/64 KBytes(Max Size) */ 0xff, /* 0xFF */ /* CISTPL_DEVICE_OC */ 0x1c, 0x04, 0x03, /* MWAIT | 3.3 volt VCC operation */ 0xd9, /* DSPEED_250NS | WPS | DTYPE_FUNCSPEC */ 0x01, /* 2 KBytes(Units)/64 KBytes(Max Size) */ 0xff, /* 0xFF */ /* CISTPL_JEDEC_C */ 0x18, 0x02, 0xdf, /* PCMCIA */ 0x01, /* 0x01 */ /* CISTPL_MANFID */ 0x20, 0x04, 0x34, 0x12, /* 0x1234 ??? */ 0x00, 0x02, /* 0x0200 */ /* CISTPL_VERS_1 */ 0x15, 0x2b, 0x04, 0x01, /* PCMCIA 2.0/2.1 / JEIDA 4.1/4.2 */ 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x70, 0x73, 0x20, 0x41, 0x54, 0x41, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, 0x43, 0x61, 0x72, 0x64, 0x20, 0x20, 0x00, /* "Dynamips ATA Flash Card " */ 0x44, 0x59, 0x4e, 0x41, 0x30, 0x20, 0x20, 0x00, /* "DYNA0 " */ 0x44, 0x59, 0x4e, 0x41, 0x30, 0x00, /* "DYNA0" */ 0xff, /* 0xFF */ /* CISTPL_FUNCID */ 0x21, 0x02, 0x04, /* Fixed Disk */ 0x01, /* Power-On Self Test */ /* CISTPL_FUNCE */ 0x22, 0x02, 0x01, /* Disk Device Interface tuple */ 0x01, /* PC Card-ATA Interface */ /* CISTPL_FUNCE: */ 0x22, 0x03, 0x02, /* Basic PC Card ATA Interface tuple */ 0x04, /* S(Silicon Device) */ 0x5f, /* P0(Sleep) | P1(Standy) | P2(Idle) | P3(Auto) | N(3F7/377 Register Inhibit Available) | I(IOIS16# on Twin Card) */ /* CISTPL_CONFIG */ 0x1a, 0x05, 0x01, /* TPCC_RASZ=2, TPCC_RMSZ=1, TPCC_RFSZ=0 */ 0x03, /* TPCC_LAST=3 */ 0x00, 0x02, /* TPCC_RADR(Configuration Register Base Address)=0x0200 */ 0x0f, /* TPCC_RMSK(Configuration Register Presence Mask Field)=0x0F */ /* CISTPL_CFTABLE_ENTRY */ 0x1b, 0x0b, 0xc0, /* Index=0 | Default | Interface */ 0x40, /* Memory | READY Active */ 0xa1, /* VCC power-description-structure only | Single 2-byte length specified | Misc */ 0x27, /* Nom V | Min V | Max V | Peak I */ 0x55, /* Nom V=5V */ 0x4d, /* Min V=4.5V */ 0x5d, /* Max V=5V */ 0x75, /* Peak I=80mA */ 0x08, 0x00, /* Card Address=0 | Host Address=0x0008 * 256 bytes */ 0x21, /* Max Twin Cards=1 | Power Down */ /* CISTPL_CFTABLE_ENTRY */ 0x1b, 0x06, 0x00, /* Index=0 */ 0x01, /* VCC power-description-structure only */ 0x21, /* Nom V | Peak I */ 0xb5, 0x1e, /* Nom V=3.30V */ 0x4d, /* Peak I=45mA */ /* CISTPL_CFTABLE_ENTRY */ 0x1b, 0x0d, 0xc1, /* Index=1 | Default | Interface */ 0x41, /* I/O and Memory | READY Active */ 0x99, /* VCC power-description-structure only | IO Space | IRQ | Misc */ 0x27, /* Nom V | Min V | Max V | Peak I */ 0x55, /* Nom V=5V */ 0x4d, /* Min V=4.5V */ 0x5d, /* Max V=5V */ 0x75, /* Peak I=80mA */ 0x64, /* IOAddrLines=4 | All registers are accessible by both 8-bit or 16-bit accesses */ 0xf0, 0xff, 0xff, /* Mask | Level | Pulse | Share | IRQ0..IRQ15 */ 0x21, /* Max Twin Cards=1 | Power Down */ /* CISTPL_CFTABLE_ENTRY */ 0x1b, 0x06, 0x01, /* Index=1 */ 0x01, /* VCC power-description-structure only */ 0x21, /* Nom V | Peak I */ 0xb5, 0x1e, /* Nom V=3.30V */ 0x4d, /* Peak I=45mA */ /* CISTPL_CFTABLE_ENTRY */ 0x1b, 0x12, 0xc2, /* Index=2 | Default | Interface */ 0x41, /* I/O and Memory | READY Active */ 0x99, /* VCC power-description-structure only | IO Space | IRQ | Misc */ 0x27, /* Nom V | Min V | Max V | Peak I */ 0x55, /* Nom V=5V */ 0x4d, /* Min V=4.5V */ 0x5d, /* Max V=5V */ 0x75, /* Peak I=80mA */ 0xea, /* IOAddrLines=10 | All registers are accessible by both 8-bit or 16-bit accesses | Range */ 0x61, /* Number of I/O Address Ranges=2 | Size of Address=2 | Size of Length=1 */ 0xf0, 0x01, 0x07, /* Address=0x1F0, Length=8 */ 0xf6, 0x03, 0x01, /* Address=0x3F6, Length=2 */ 0xee, /* IRQ14 | Level | Pulse | Share */ 0x21, /* Max Twin Cards=1 | Power Down */ /* CISTPL_CFTABLE_ENTRY */ 0x1b, 0x06, 0x02, /* Index=2 */ 0x01, /* VCC power-description-structure only */ 0x21, /* Nom V | Peak I */ 0xb5, 0x1e, /* Nom V=3.30V */ 0x4d, /* Peak I=45mA */ /* CISTPL_CFTABLE_ENTRY */ 0x1b, 0x12, 0xc3, /* Index=3 | Default | Interface */ 0x41, /* I/O and Memory | READY Active */ 0x99, /* VCC power-description-structure only | IO Space | IRQ | Misc */ 0x27, /* Nom V | Min V | Max V | Peak I */ 0x55, /* Nom V=5V */ 0x4d, /* Min V=4.5V */ 0x5d, /* Max V=5V */ 0x75, /* Peak I=80mA */ 0xea, /* IOAddrLines=10 | All registers are accessible by both 8-bit or 16-bit accesses | Range */ 0x61, /* Number of I/O Address Ranges=2 | Size of Address=2 | Size of Length=1 */ 0x70, 0x01, 0x07, /* Address=0x170, Length=8 */ 0x76, 0x03, 0x01, /* Address=0x376, Length=2 */ 0xee, /* IRQ14 | Level | Pulse | Share */ 0x21, /* Max Twin Cards=1 | Power Down */ /* CISTPL_CFTABLE_ENTRY */ 0x1b, 0x06, 0x03, /* Index=3 */ 0x01, /* VCC power-description-structure only */ 0x21, /* Nom V | Peak I */ 0xb5, 0x1e, /* Nom V=3.30V */ 0x4d, /* Peak I=45mA */ /* CISTPL_NO_LINK */ 0x14, 0x00, /* CISTPL_END */ 0xff, }; /* PCMCIA private data */ struct pcmcia_disk_data { vm_instance_t *vm; vm_obj_t vm_obj; struct vdevice dev; char *filename; int fd; /* Disk parameters (C/H/S) */ u_int nr_heads; u_int nr_cylinders; u_int sects_per_track; /* Current ATA command and CHS info */ m_uint8_t ata_cmd,ata_cmd_in_progress; m_uint8_t ata_status; m_uint8_t cyl_low,cyl_high; m_uint8_t head,sect_no; m_uint8_t sect_count; /* Current sector */ m_uint32_t sect_pos; /* Remaining sectors to read or write */ u_int sect_remaining; /* Callback function when data buffer is validated */ void (*ata_cmd_callback)(struct pcmcia_disk_data *); /* Data buffer */ m_uint32_t data_offset; u_int data_pos; m_uint8_t data_buffer[SECTOR_SIZE]; }; /* Convert a CHS reference to an LBA reference */ static inline m_uint32_t chs_to_lba(struct pcmcia_disk_data *d, u_int cyl,u_int head,u_int sect) { return((((cyl * d->nr_heads) + head) * d->sects_per_track) + sect - 1); } /* Convert a LBA reference to a CHS reference */ static inline void lba_to_chs(struct pcmcia_disk_data *d,m_uint32_t lba, u_int *cyl,u_int *head,u_int *sect) { *cyl = lba / (d->sects_per_track * d->nr_heads); *head = (lba / d->sects_per_track) % d->nr_heads; *sect = (lba % d->sects_per_track) + 1; } /* Format disk with a single FAT16 partition */ static int disk_format(struct pcmcia_disk_data *d) { struct mbr_data mbr; struct mbr_partition *part; u_int cyl=0, head=1, sect=1; /* Master Boot Record */ memset(&mbr,0,sizeof(mbr)); mbr.signature[0] = MBR_SIGNATURE_0; mbr.signature[1] = MBR_SIGNATURE_1; part = &mbr.partition[0]; part->bootable = 0; part->type = MBR_PARTITION_TYPE_FAT16; part->lba = chs_to_lba(d, 0, 1, 1); part->nr_sectors = d->nr_heads * d->nr_cylinders * d->sects_per_track - part->lba; lba_to_chs(d, part->lba + part->nr_sectors - 1, &cyl, &head, §); mbr_set_chs(part->first_chs, 0, 1, 1); mbr_set_chs(part->last_chs, cyl, head, sect); if (mbr_write_fd(d->fd, &mbr)<0) { return(-1); } /* FAT16 partition */ if (fs_fat_format16(d->fd, part->lba, part->nr_sectors, d->sects_per_track, d->nr_heads, d->vm_obj.name)) { return(-1); } return(0); } /* Create the virtual disk */ static int disk_create(struct pcmcia_disk_data *d) { off_t disk_len; if ((d->fd = open(d->filename,O_CREAT|O_EXCL|O_RDWR,0600)) < 0) { /* already exists? */ if ((d->fd = open(d->filename,O_CREAT|O_RDWR,0600)) < 0) { perror("disk_create: open"); return(-1); } } else { /* new disk */ if (disk_format(d)) { return(-1); } } disk_len = d->nr_heads * d->nr_cylinders * d->sects_per_track * SECTOR_SIZE; ftruncate(d->fd,disk_len); return(0); } /* Read a sector from disk file */ static int disk_read_sector(struct pcmcia_disk_data *d,m_uint32_t sect, m_uint8_t *buffer) { off_t disk_offset = (off_t)sect * SECTOR_SIZE; #if DEBUG_READ vm_log(d->vm,d->dev.name,"reading sector 0x%8.8x\n",sect); #endif if (lseek(d->fd,disk_offset,SEEK_SET) == -1) { perror("read_sector: lseek"); return(-1); } if (read(d->fd,buffer,SECTOR_SIZE) != SECTOR_SIZE) { perror("read_sector: read"); return(-1); } return(0); } /* Write a sector to disk file */ static int disk_write_sector(struct pcmcia_disk_data *d,m_uint32_t sect, m_uint8_t *buffer) { off_t disk_offset = (off_t)sect * SECTOR_SIZE; #if DEBUG_WRITE vm_log(d->vm,d->dev.name,"writing sector 0x%8.8x\n",sect); #endif if (lseek(d->fd,disk_offset,SEEK_SET) == -1) { perror("write_sector: lseek"); return(-1); } if (write(d->fd,buffer,SECTOR_SIZE) != SECTOR_SIZE) { perror("write_sector: write"); return(-1); } return(0); } /* Identify PCMCIA device (ATA command 0xEC) */ static void ata_identify_device(struct pcmcia_disk_data *d) { m_uint8_t *p = d->data_buffer; m_uint32_t sect_count; sect_count = d->nr_heads * d->nr_cylinders * d->sects_per_track; /* Clear all fields (for safety) */ memset(p,0x00,SECTOR_SIZE); /* Word 0: General Configuration */ p[0] = 0x8a; /* Not MFM encoded | Hard sectored | Removable cartridge drive */ p[1] = 0x84; /* Disk transfer rate !<= 10Mbs | Non-rotating disk drive */ /* Word 1: Default number of cylinders */ p[2] = d->nr_cylinders & 0xFF; p[3] = (d->nr_cylinders >> 8) & 0xFF; /* Word 3: Default number of heads */ p[6] = d->nr_heads; /* Word 6: Default number of sectors per track */ p[12] = d->sects_per_track; /* Word 7: Number of sectors per card (MSW) */ p[14] = (sect_count >> 16) & 0xFF; p[15] = (sect_count >> 24); /* Word 8: Number of sectors per card (LSW) */ p[16] = sect_count & 0xFF; p[17] = (sect_count >> 8) & 0xFF; /* Word 22: ECC count */ p[44] = 0x04; /* Word 53: Translation parameters valid */ p[106] = 0x3; /* Word 54: Current number of cylinders */ p[108] = d->nr_cylinders & 0xFF; p[109] = (d->nr_cylinders >> 8) & 0xFF; /* Word 55: Current number of heads */ p[110] = d->nr_heads; /* Word 56: Current number of sectors per track */ p[112] = d->sects_per_track; /* Word 57/58: Current of sectors per card (LSW/MSW) */ p[114] = sect_count & 0xFF; p[115] = (sect_count >> 8) & 0xFF; p[116] = (sect_count >> 16) & 0xFF; p[117] = (sect_count >> 24); #if 0 /* Word 60/61: Total sectors addressable in LBA mode (MSW/LSW) */ p[120] = (sect_count >> 16) & 0xFF; p[121] = (sect_count >> 24); p[122] = sect_count & 0xFF; p[123] = (sect_count >> 8) & 0xFF; #endif } /* Set sector position */ static void ata_set_sect_pos(struct pcmcia_disk_data *d) { u_int cyl; if (d->head & ATA_DH_LBA) { d->sect_pos = (u_int)(d->head & 0x0F) << 24; d->sect_pos |= (u_int)d->cyl_high << 16; d->sect_pos |= (u_int)d->cyl_low << 8; d->sect_pos |= (u_int)d->sect_no; #if DEBUG_ATA vm_log(d->vm,d->dev.name,"ata_set_sect_pos: LBA sect=0x%x\n", d->sect_pos); #endif } else { cyl = (((u_int)d->cyl_high) << 8) + d->cyl_low; d->sect_pos = chs_to_lba(d,cyl,d->head & 0x0F,d->sect_no); #if DEBUG_ATA vm_log(d->vm,d->dev.name, "ata_set_sect_pos: cyl=0x%x,head=0x%x,sect=0x%x => " "sect_pos=0x%x\n", cyl,d->head & 0x0F,d->sect_no,d->sect_pos); #endif } } /* ATA device identifier callback */ static void ata_cmd_ident_device_callback(struct pcmcia_disk_data *d) { d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC; } /* ATA read sector callback */ static void ata_cmd_read_callback(struct pcmcia_disk_data *d) { d->sect_remaining--; if (!d->sect_remaining) { d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC; return; } /* Read the next sector */ d->sect_pos++; disk_read_sector(d,d->sect_pos,d->data_buffer); d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC|ATA_STATUS_DRQ; } /* ATA write sector callback */ static void ata_cmd_write_callback(struct pcmcia_disk_data *d) { /* Write the sector */ disk_write_sector(d,d->sect_pos,d->data_buffer); d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC|ATA_STATUS_DRQ; d->sect_pos++; d->sect_remaining--; if (!d->sect_remaining) { d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC; } } /* Handle an ATA command */ static void ata_handle_cmd(struct pcmcia_disk_data *d) { #if DEBUG_ATA vm_log(d->vm,d->dev.name,"ATA command 0x%2.2x\n",(u_int)d->ata_cmd); #endif d->data_pos = 0; switch(d->ata_cmd) { case ATA_CMD_IDENT_DEVICE: ata_identify_device(d); d->ata_cmd_callback = ata_cmd_ident_device_callback; d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC|ATA_STATUS_DRQ; break; case ATA_CMD_READ_SECTOR: d->sect_remaining = d->sect_count; if (!d->sect_remaining) d->sect_remaining = 256; ata_set_sect_pos(d); disk_read_sector(d,d->sect_pos,d->data_buffer); d->ata_cmd_callback = ata_cmd_read_callback; d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC|ATA_STATUS_DRQ; break; case ATA_CMD_WRITE_SECTOR: d->sect_remaining = d->sect_count; if (!d->sect_remaining) d->sect_remaining = 256; ata_set_sect_pos(d); d->ata_cmd_callback = ata_cmd_write_callback; d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC|ATA_STATUS_DRQ; break; default: vm_log(d->vm,d->dev.name,"unhandled ATA command 0x%2.2x\n", (u_int)d->ata_cmd); } } /* * dev_pcmcia_disk_access_0() */ void *dev_pcmcia_disk_access_0(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct pcmcia_disk_data *d = dev->priv_data; /* Compute the good internal offset */ offset = (offset >> 1) ^ 1; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,d->dev.name, "reading offset 0x%5.5x at pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->dev.name, "writing offset 0x%5.5x, data=0x%llx at pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif /* Card Information Structure */ if (offset < sizeof(cis_table)) { if (op_type == MTS_READ) *data = cis_table[offset]; return NULL; } switch(offset) { case 0x102: /* Pin Replacement Register */ if (op_type == MTS_READ) *data = 0x22; break; case 0x80001: /* Sector Count + Sector no */ if (op_type == MTS_READ) { *data = (d->sect_no << 8) + d->sect_count; } else { d->sect_no = *data >> 8; d->sect_count = *data & 0xFF; } break; case 0x80002: /* Cylinder Low + Cylinder High */ if (op_type == MTS_READ) { *data = (d->cyl_high << 8) + d->cyl_low; } else { d->cyl_high = *data >> 8; d->cyl_low = *data & 0xFF; } break; case 0x80003: /* Select Card/Head + Status/Command register */ if (op_type == MTS_READ) *data = (d->ata_status << 8) + d->head; else { d->ata_cmd = *data >> 8; d->head = *data; ata_handle_cmd(d); } break; default: /* Data buffer access ? */ if ((offset >= d->data_offset) && (offset < d->data_offset + (SECTOR_SIZE/2))) { if (op_type == MTS_READ) { *data = d->data_buffer[(d->data_pos << 1)]; *data += d->data_buffer[(d->data_pos << 1)+1] << 8; } else { d->data_buffer[(d->data_pos << 1)] = *data & 0xFF; d->data_buffer[(d->data_pos << 1)+1] = *data >> 8; } d->data_pos++; /* Buffer complete: call the callback function */ if (d->data_pos == (SECTOR_SIZE/2)) { d->data_pos = 0; if (d->ata_cmd_callback) d->ata_cmd_callback(d); } } } return NULL; } /* * dev_pcmcia_disk_access_1() */ void *dev_pcmcia_disk_access_1(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct pcmcia_disk_data *d = dev->priv_data; /* Compute the good internal offset */ offset = (offset >> 1) ^ 1; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,d->dev.name, "reading offset 0x%5.5x at pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->dev.name, "writing offset 0x%5.5x, data=0x%llx at pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif switch(offset) { case 0x02: /* Sector Count + Sector no */ if (op_type == MTS_READ) { *data = (d->sect_no << 8) + d->sect_count; } else { d->sect_no = *data >> 8; d->sect_count = *data & 0xFF; } break; case 0x04: /* Cylinder Low + Cylinder High */ if (op_type == MTS_READ) { *data = (d->cyl_high << 8) + d->cyl_low; } else { d->cyl_high = *data >> 8; d->cyl_low = *data & 0xFF; } break; case 0x06: /* Select Card/Head + Status/Command register */ if (op_type == MTS_READ) *data = (d->ata_status << 8) + d->head; else { d->ata_cmd = *data >> 8; d->head = *data & 0xFF; ata_handle_cmd(d); } break; case 0x08: /* Data */ if (op_type == MTS_READ) { *data = d->data_buffer[(d->data_pos << 1)]; *data += d->data_buffer[(d->data_pos << 1)+1] << 8; } else { d->data_buffer[(d->data_pos << 1)] = *data & 0xFF; d->data_buffer[(d->data_pos << 1)+1] = *data >> 8; } d->data_pos++; /* Buffer complete: call the callback function */ if (d->data_pos == (SECTOR_SIZE/2)) { d->data_pos = 0; if (d->ata_cmd_callback) d->ata_cmd_callback(d); } break; case 0x0E: /* Status/Drive Control + Drive Address */ break; } return NULL; } /* Shutdown a PCMCIA disk device */ void dev_pcmcia_disk_shutdown(vm_instance_t *vm,struct pcmcia_disk_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Close disk file */ if (d->fd != -1) close(d->fd); /* Free filename */ free(d->filename); /* Free the structure itself */ free(d); } } /* Initialize a PCMCIA disk */ vm_obj_t *dev_pcmcia_disk_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len, u_int disk_size,int mode) { struct pcmcia_disk_data *d; m_uint32_t tot_sect; /* allocate the private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"PCMCIA: unable to create disk device '%s'.\n",name); return NULL; } memset(d,0,sizeof(*d)); vm_object_init(&d->vm_obj); d->vm = vm; d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_pcmcia_disk_shutdown; d->fd = -1; if (!(d->filename = vm_build_filename(vm,name))) { fprintf(stderr,"PCMCIA: unable to create filename.\n"); goto err_filename; } /* Data buffer offset in mapped memory */ d->data_offset = 0x80200; d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC; /* Compute the number of cylinders given a disk size in Mb */ tot_sect = ((m_uint64_t)disk_size * 1048576) / SECTOR_SIZE; d->nr_heads = DISK_NR_HEADS; d->sects_per_track = DISK_SECTS_PER_TRACK; d->nr_cylinders = tot_sect / (d->nr_heads * d->sects_per_track); vm_log(vm,name,"C/H/S settings = %u/%u/%u\n", d->nr_cylinders,d->nr_heads,d->sects_per_track); /* Create the disk file */ if (disk_create(d) == -1) goto err_disk_create; dev_init(&d->dev); d->dev.name = name; d->dev.priv_data = d; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.flags = VDEVICE_FLAG_CACHING; if (mode == 0) d->dev.handler = dev_pcmcia_disk_access_0; else d->dev.handler = dev_pcmcia_disk_access_1; /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(&d->vm_obj); err_disk_create: free(d->filename); err_filename: free(d); return NULL; } /* Get the device associated with a PCMCIA disk object */ struct vdevice *dev_pcmcia_disk_get_device(vm_obj_t *obj) { struct pcmcia_disk_data *d; if (!obj || !(d = obj->data)) return NULL; return(&d->dev); } dynamips-0.2.14/common/dev_pericom.c000066400000000000000000000014021241034141600173430ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Pericom PCI bridge. */ #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "pci_dev.h" #define PCI_VENDOR_PERICOM 0x12d8 #define PCI_PRODUCT_PERICOM 0x8150 /* * dev_pericom_init() */ int dev_pericom_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus) { struct pci_device *dev; dev = pci_bridge_create_dev(pci_bus,"pericom", PCI_VENDOR_PERICOM,PCI_PRODUCT_PERICOM, pci_device,0,sec_bus,NULL,NULL); return((dev != NULL) ? 0 : -1); } dynamips-0.2.14/common/dev_plx.c000066400000000000000000000241431241034141600165170ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (C) 2005,2006 Christophe Fillot. All rights reserved. * * PLX PCI9060/PCI9054 - PCI bus master interface chip. * * This is very basic, it has been designed to allow the C7200 PA-POS-OC3 * to work, and for the upcoming PA-MC-8TE1. */ #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "pci_dev.h" #include "dev_plx.h" #define DEBUG_ACCESS 1 /* PLX vendor/product codes */ #define PLX_PCI_VENDOR_ID 0x10b5 #define PLX9060_PCI_PRODUCT_ID 0x9060 #define PLX9054_PCI_PRODUCT_ID 0x9054 /* Number of Local Spaces (1 on 9060, 2 on 9054) */ #define PLX_LOCSPC_MAX 2 /* Local Space ranges */ #define PLX_LOCSPC_RANGE_DEFAULT 0xFFF00000 #define PLX_LOCSPC_RANGE_DECODE_MASK 0xFFFFFFF0 /* Local space definition */ struct plx_locspace { m_uint32_t lbaddr; m_uint32_t range; struct vdevice *dev; }; /* PLX data */ struct plx_data { /* Device name */ char *name; /* Variant (9060, 9054) */ u_int variant; /* Virtual machine and object info */ vm_instance_t *vm; vm_obj_t vm_obj; /* Virtual PLX device */ struct vdevice plx_dev; struct pci_device *pci_plx_dev; /* Local spaces */ struct plx_locspace lspc[PLX_LOCSPC_MAX]; /* Doorbell registers */ m_uint32_t pci2loc_doorbell_reg,loc2pci_doorbell_reg; dev_plx_doorbell_cbk pci2loc_doorbell_cbk; void *pci2loc_doorbell_cbk_arg; }; /* Log a PLX message */ #define PLX_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* Map a local space device */ static void plx_map_space(struct plx_data *d,u_int id) { struct plx_locspace *lspc; lspc = &d->lspc[id]; if (!lspc->dev) return; lspc->dev->phys_len = 1+(~(lspc->range & PLX_LOCSPC_RANGE_DECODE_MASK)); vm_map_device(d->vm,lspc->dev,lspc->lbaddr); PLX_LOG(d,"device %u mapped at 0x%llx, size=0x%x\n", id,lspc->dev->phys_addr,lspc->dev->phys_len); } /* PLX device common access routine */ void *dev_plx_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct plx_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0; switch(offset) { /* Local Address Space 0 Range Register */ case 0x00: if (op_type == MTS_WRITE) { d->lspc[0].range = *data; plx_map_space(d,0); } else { *data = d->lspc[0].range; } break; /* PCI-to-Local Doorbell Register */ case 0x60: if (op_type == MTS_WRITE) { d->pci2loc_doorbell_reg = *data; if (d->pci2loc_doorbell_cbk != NULL) { d->pci2loc_doorbell_cbk(d,d->pci2loc_doorbell_cbk_arg, d->pci2loc_doorbell_reg); } } break; /* Local-to-PCI Doorbell Register */ case 0x64: if (op_type == MTS_READ) *data = d->loc2pci_doorbell_reg; else d->loc2pci_doorbell_reg &= ~(*data); break; default: if (op_type == MTS_READ) { cpu_log(cpu,d->name, "read from unhandled addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name, "write to handled addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } } return NULL; } /* PLX9054 access routine */ void *dev_plx9054_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct plx_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0; switch(offset) { /* Local Address Space 1 Range Register */ case 0xF0: if (op_type == MTS_WRITE) { d->lspc[1].range = *data; plx_map_space(d,1); } else { *data = d->lspc[1].range; } break; default: return(dev_plx_access(cpu,dev,offset,op_size,op_type,data)); } return NULL; } /* * pci_plx_read() - Common PCI read. */ static m_uint32_t pci_plx_read(cpu_gen_t *cpu,struct pci_device *dev,int reg) { struct plx_data *d = dev->priv_data; #if DEBUG_ACCESS PLX_LOG(d,"read PLX PCI register 0x%x\n",reg); #endif switch(reg) { /* PLX registers */ case PCI_REG_BAR0: return(d->plx_dev.phys_addr); /* Local space 0 */ case PCI_REG_BAR2: return(d->lspc[0].lbaddr); default: return(0); } } /* * pci_plx_write() - Common PCI write. */ static void pci_plx_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct plx_data *d = dev->priv_data; #if DEBUG_ACCESS PLX_LOG(d,"write 0x%x to PLX PCI register 0x%x\n",value,reg); #endif switch(reg) { /* PLX registers */ case PCI_REG_BAR0: vm_map_device(cpu->vm,&d->plx_dev,(m_uint64_t)value); PLX_LOG(d,"PLX registers are mapped at 0x%x\n",value); break; /* Local space 0 */ case PCI_REG_BAR2: d->lspc[0].lbaddr = value; plx_map_space(d,0); break; } } /* * pci_plx9054_read() */ static m_uint32_t pci_plx9054_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { struct plx_data *d = dev->priv_data; #if DEBUG_ACCESS PLX_LOG(d,"read PLX PCI register 0x%x\n",reg); #endif switch(reg) { /* Local space 1 */ case PCI_REG_BAR3: return(d->lspc[1].lbaddr); default: return(pci_plx_read(cpu,dev,reg)); } } /* * pci_plx9054_write() */ static void pci_plx9054_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct plx_data *d = dev->priv_data; #if DEBUG_ACCESS PLX_LOG(d,"write 0x%x to PLX PCI register 0x%x\n",value,reg); #endif switch(reg) { /* Local space 1 */ case PCI_REG_BAR3: d->lspc[1].lbaddr = value; plx_map_space(d,1); break; default: pci_plx_write(cpu,dev,reg,value); } } /* Shutdown a PLX device */ void dev_plx_shutdown(vm_instance_t *vm,struct plx_data *d) { int i; if (d != NULL) { /* Unbind the managed devices */ for(i=0;ilspc[i].dev) vm_unbind_device(vm,d->lspc[i].dev); } /* Remove the PLX device */ dev_remove(vm,&d->plx_dev); /* Remove the PCI PLX device */ pci_dev_remove(d->pci_plx_dev); /* Free the structure itself */ free(d); } } /* Create a generic PLX device */ struct plx_data *dev_plx_init(vm_instance_t *vm,char *name) { struct plx_data *d; int i; /* Allocate the private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"PLX: unable to create device.\n"); return NULL; } memset(d,0,sizeof(*d)); vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_plx_shutdown; d->vm = vm; d->name = name; for(i=0;ilspc[i].range = PLX_LOCSPC_RANGE_DEFAULT; dev_init(&d->plx_dev); d->plx_dev.name = name; d->plx_dev.priv_data = d; d->plx_dev.phys_addr = 0; d->plx_dev.phys_len = 0x1000; d->plx_dev.handler = dev_plx_access; return(d); } /* Create a PLX9060 device */ vm_obj_t *dev_plx9060_init(vm_instance_t *vm,char *name, struct pci_bus *pci_bus,int pci_device, struct vdevice *dev0) { struct plx_data *d; /* Create the PLX data */ if (!(d = dev_plx_init(vm,name))) { fprintf(stderr,"PLX: unable to create device.\n"); return NULL; } /* Set the PLX variant */ d->variant = 9060; /* Set device for Local Space 0 */ d->lspc[0].dev = dev0; /* Add PLX as a PCI device */ d->pci_plx_dev = pci_dev_add(pci_bus,name, PLX_PCI_VENDOR_ID,PLX9060_PCI_PRODUCT_ID, pci_device,0,-1,d, NULL,pci_plx_read,pci_plx_write); if (!d->pci_plx_dev) { fprintf(stderr,"%s (PLX9060): unable to create PCI device.\n",name); goto err_pci_dev; } vm_object_add(vm,&d->vm_obj); return(&d->vm_obj); err_pci_dev: free(d); return NULL; } /* Create a PLX9054 device */ vm_obj_t *dev_plx9054_init(vm_instance_t *vm,char *name, struct pci_bus *pci_bus,int pci_device, struct vdevice *dev0,struct vdevice *dev1) { struct plx_data *d; /* Create the PLX data */ if (!(d = dev_plx_init(vm,name))) { fprintf(stderr,"PLX: unable to create device.\n"); return NULL; } /* Set the PLX variant */ d->variant = 9054; d->plx_dev.handler = dev_plx9054_access; /* Set device for Local Space 0 and 1 */ d->lspc[0].dev = dev0; d->lspc[1].dev = dev1; /* Add PLX as a PCI device */ d->pci_plx_dev = pci_dev_add(pci_bus,name, PLX_PCI_VENDOR_ID,PLX9054_PCI_PRODUCT_ID, pci_device,0,-1,d, NULL,pci_plx9054_read,pci_plx9054_write); if (!d->pci_plx_dev) { fprintf(stderr,"%s (PLX9054): unable to create PCI device.\n",name); goto err_pci_dev; } vm_object_add(vm,&d->vm_obj); return(&d->vm_obj); err_pci_dev: free(d); return NULL; } /* Set callback function for PCI-to-Local doorbell register */ void dev_plx_set_pci2loc_doorbell_cbk(struct plx_data *d, dev_plx_doorbell_cbk cbk, void *arg) { if (d != NULL) { d->pci2loc_doorbell_cbk = cbk; d->pci2loc_doorbell_cbk_arg = arg; } } /* Set the Local-to-PCI doorbell register (for Local device use) */ void dev_plx_set_loc2pci_doorbell_reg(struct plx_data *d,m_uint32_t value) { if (d != NULL) d->loc2pci_doorbell_reg = value; } dynamips-0.2.14/common/dev_plx.h000066400000000000000000000024741241034141600165270ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __DEV_PLX_H__ #define __DEV_PLX_H__ #include #include "utils.h" #include "mips64.h" #include "cpu.h" #include "device.h" #include "net_io.h" #include "vm.h" /* Forward declaration for PLX private data */ struct plx_data; /* PLX PCI-to-Local doorbell register callback */ typedef void (*dev_plx_doorbell_cbk)(struct plx_data *d,void *arg, m_uint32_t val); /* Create a PLX9060 device */ vm_obj_t *dev_plx9060_init(vm_instance_t *vm,char *name, struct pci_bus *pci_bus,int pci_device, struct vdevice *dev0); /* Create a PLX9054 device */ vm_obj_t *dev_plx9054_init(vm_instance_t *vm,char *name, struct pci_bus *pci_bus,int pci_device, struct vdevice *dev0,struct vdevice *dev1); /* Set callback function for PCI-to-Local doorbell register */ void dev_plx_set_pci2loc_doorbell_cbk(struct plx_data *d, dev_plx_doorbell_cbk wcbk, void *arg); /* Set the Local-to-PCI doorbell register (for Local device use) */ void dev_plx_set_loc2pci_doorbell_reg(struct plx_data *d,m_uint32_t value); #endif dynamips-0.2.14/common/dev_plx6520cb.c000066400000000000000000000014611241034141600173370ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * PLX6520CB PCI bridge. * This is just a fake device. */ #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "pci_dev.h" #define PCI_VENDOR_PLX 0x10b5 #define PCI_PRODUCT_PLX_6520CB 0x6520 /* * dev_plx6520cb_init() */ int dev_plx6520cb_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus) { struct pci_device *dev; dev = pci_bridge_create_dev(pci_bus,"plx6520cb", PCI_VENDOR_PLX,PCI_PRODUCT_PLX_6520CB, pci_device,0,sec_bus,NULL,NULL); return((dev != NULL) ? 0 : -1); } dynamips-0.2.14/common/dev_ram.c000066400000000000000000000060401241034141600164670ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot. All rights reserved. * * RAM emulation. */ #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" /* RAM private data */ struct ram_data { vm_obj_t vm_obj; struct vdevice *dev; char *filename; int delete_file; }; /* Shutdown a RAM device */ void dev_ram_shutdown(vm_instance_t *vm,struct ram_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,d->dev); free(d->dev); /* Remove filename used to virtualize RAM */ if (d->filename) { if (d->delete_file) unlink(d->filename); free(d->filename); } /* Free the structure itself */ free(d); } } /* Initialize a RAM zone */ int dev_ram_init(vm_instance_t *vm,char *name,int use_mmap,int delete_file, char *alternate_name,int sparse, m_uint64_t paddr,m_uint32_t len) { struct ram_data *d; /* allocate the private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"RAM: unable to create device.\n"); return(-1); } memset(d,0,sizeof(*d)); d->delete_file = delete_file; vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_ram_shutdown; if (use_mmap) { if (!alternate_name) d->filename = vm_build_filename(vm,name); else d->filename = strdup(alternate_name); if (!d->filename) { fprintf(stderr,"RAM: unable to create filename.\n"); goto err_filename; } } if (!(d->dev = dev_create_ram(vm,name,sparse,d->filename,paddr,len))) { fprintf(stderr,"RAM: unable to create device.\n"); goto err_dev_create; } vm_object_add(vm,&d->vm_obj); return(0); err_dev_create: free(d->filename); err_filename: free(d); return(-1); } /* Initialize a ghosted RAM zone */ int dev_ram_ghost_init(vm_instance_t *vm,char *name,int sparse,char *filename, m_uint64_t paddr,m_uint32_t len) { struct ram_data *d; /* allocate the private data structure */ if (!filename || !(d = malloc(sizeof(*d)))) { vm_error(vm,"RAM_ghost: unable to create device (filename=%s).\n",filename); return(-1); } memset(d,0,sizeof(*d)); d->delete_file = FALSE; if (!(d->filename = strdup(filename))) goto err_filename; vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_ram_shutdown; if (!(d->dev = dev_create_ghost_ram(vm,name,sparse,d->filename, paddr,len))) { vm_error(vm,"RAM_ghost: unable to create device (filename=%s)\n",d->filename); goto err_dev_create; } vm_object_add(vm,&d->vm_obj); return(0); err_dev_create: free(d->filename); err_filename: free(d); return(-1); } dynamips-0.2.14/common/dev_remote.c000066400000000000000000000176161241034141600172160ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (C) 2006 Christophe Fillot. All rights reserved. * * Remote control module. */ #include #include #include #include #include #include #include #include #include #include "utils.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net.h" #include "net_io.h" #include "registry.h" #include "ptask.h" #include "dev_c7200.h" #include "dev_c3600.h" #include "dev_c2691.h" #include "dev_c3725.h" #include "dev_c3745.h" #include "dev_c2600.h" #define DEBUG_ACCESS 0 #define ROMMON_SET_VAR 0x01 #define ROMMON_GET_VAR 0x02 #define ROMMON_CLEAR_VAR_STAT 0x03 /* Remote control private data */ struct remote_data { vm_obj_t vm_obj; struct vdevice dev; /* Console buffer */ char con_buffer[512]; u_int con_buf_pos; /* ROMMON variables buffer */ char var_buffer[512]; u_int var_buf_pos; u_int var_status; /* Position for cookie reading */ u_int cookie_pos; }; /* * dev_remote_control_access() */ void *dev_remote_control_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { vm_instance_t *vm = cpu->vm; struct remote_data *d = dev->priv_data; struct vdevice *storage_dev; size_t len; if (op_type == MTS_READ) *data = 0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"REMOTE","reading reg 0x%x at pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"REMOTE","writing reg 0x%x at pc=0x%llx, data=0x%llx\n", offset,cpu_get_pc(cpu),*data); } #endif switch(offset) { /* ROM Identification tag */ case 0x000: if (op_type == MTS_READ) *data = ROM_ID; break; /* CPU ID */ case 0x004: if (op_type == MTS_READ) *data = cpu->id; break; /* Display CPU registers */ case 0x008: if (op_type == MTS_WRITE) cpu->reg_dump(cpu); break; /* Display CPU memory info */ case 0x00c: if (op_type == MTS_WRITE) cpu->mmu_dump(cpu); break; /* Reserved/Unused */ case 0x010: break; /* RAM size */ case 0x014: if (op_type == MTS_READ) *data = vm->ram_size - vm->ram_res_size; break; /* ROM size */ case 0x018: if (op_type == MTS_READ) *data = vm->rom_size; break; /* NVRAM size */ case 0x01c: if (op_type == MTS_READ) *data = vm->nvram_size; break; /* IOMEM size */ case 0x020: if (op_type == MTS_READ) *data = vm->iomem_size; break; /* Config Register */ case 0x024: if (op_type == MTS_READ) *data = vm->conf_reg; break; /* ELF entry point */ case 0x028: if (op_type == MTS_READ) *data = vm->ios_entry_point; break; /* ELF machine id */ case 0x02c: if (op_type == MTS_READ) *data = vm->elf_machine_id; break; /* Restart IOS Image */ case 0x030: /* not implemented */ break; /* Stop the virtual machine */ case 0x034: vm->status = VM_STATUS_SHUTDOWN; break; /* Debugging/Log message: /!\ physical address */ case 0x038: if (op_type == MTS_WRITE) { len = physmem_strlen(vm,*data); if (len < sizeof(d->con_buffer)) { physmem_copy_from_vm(vm,d->con_buffer,*data,len+1); vm_log(vm,"ROM",d->con_buffer); } } break; /* Console Buffering */ case 0x03c: if (op_type == MTS_WRITE) { if (d->con_buf_pos < (sizeof(d->con_buffer)-1)) { d->con_buffer[d->con_buf_pos++] = *data & 0xFF; d->con_buffer[d->con_buf_pos] = 0; if (d->con_buffer[d->con_buf_pos-1] == '\n') { vm_log(vm,"ROM","%s",d->con_buffer); d->con_buf_pos = 0; } } else d->con_buf_pos = 0; } break; /* Console output */ case 0x040: if (op_type == MTS_WRITE) vtty_put_char(vm->vtty_con,(char)*data); break; /* NVRAM address */ case 0x044: if (op_type == MTS_READ) { if ((storage_dev = dev_get_by_name(vm,"nvram"))) *data = storage_dev->phys_addr; if ((storage_dev = dev_get_by_name(vm,"ssa"))) *data = storage_dev->phys_addr; if (cpu->type == CPU_TYPE_MIPS64) *data += MIPS_KSEG1_BASE; } break; /* IO memory size for Smart-Init (C3600, others ?) */ case 0x048: if (op_type == MTS_READ) *data = vm->nm_iomem_size; break; /* Cookie position selector */ case 0x04c: if (op_type == MTS_READ) *data = d->cookie_pos; else d->cookie_pos = *data; break; /* Cookie data */ case 0x050: if ((op_type == MTS_READ) && (d->cookie_pos < 64)) *data = vm->chassis_cookie[d->cookie_pos]; break; /* ROMMON variable */ case 0x054: if (op_type == MTS_WRITE) { if (d->var_buf_pos < (sizeof(d->var_buffer)-1)) { d->var_buffer[d->var_buf_pos++] = *data & 0xFF; d->var_buffer[d->var_buf_pos] = 0; } else d->var_buf_pos = 0; } else { if (d->var_buf_pos < (sizeof(d->var_buffer)-1)) { *data = d->var_buffer[d->var_buf_pos++]; } else { d->var_buf_pos = 0; *data = 0; } } break; /* ROMMON variable command */ case 0x058: if (op_type == MTS_WRITE) { switch(*data & 0xFF) { case ROMMON_SET_VAR: d->var_status = rommon_var_add_str(&vm->rommon_vars, d->var_buffer); d->var_buf_pos = 0; break; case ROMMON_GET_VAR: d->var_status = rommon_var_get(&vm->rommon_vars, d->var_buffer, d->var_buffer, sizeof(d->var_buffer)); d->var_buf_pos = 0; break; case ROMMON_CLEAR_VAR_STAT: d->var_buf_pos = 0; break; default: d->var_status = -1; } } else { *data = d->var_status; } break; } return NULL; } /* Shutdown a remote control device */ void dev_remote_control_shutdown(vm_instance_t *vm,struct remote_data *d) { if (d != NULL) { dev_remove(vm,&d->dev); free(d); } } /* remote control device */ int dev_remote_control_init(vm_instance_t *vm,m_uint64_t paddr,m_uint32_t len) { struct remote_data *d; if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"Remote Control: unable to create device.\n"); return(-1); } memset(d,0,sizeof(*d)); vm_object_init(&d->vm_obj); d->vm_obj.name = "remote_ctrl"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_remote_control_shutdown; dev_init(&d->dev); d->dev.name = "remote_ctrl"; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_remote_control_access; d->dev.priv_data = d; /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_rom.c000066400000000000000000000046051241034141600165120ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot. All rights reserved. * * ROM Emulation. */ #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" /* Embedded MIPS64 ROM */ m_uint8_t mips64_microcode[] = { #include "mips64_microcode_dump.inc" }; ssize_t mips64_microcode_len = sizeof(mips64_microcode); /* Embedded PPC32 ROM */ m_uint8_t ppc32_microcode[] = { #include "ppc32_microcode_dump.inc" }; ssize_t ppc32_microcode_len = sizeof(ppc32_microcode); /* ROM private data */ struct rom_data { vm_obj_t vm_obj; struct vdevice dev; m_uint8_t *rom_ptr; m_uint32_t rom_size; }; /* * dev_rom_access() */ void *dev_rom_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct rom_data *d = dev->priv_data; if (op_type == MTS_WRITE) { cpu_log(cpu,"ROM","write attempt at address 0x%llx (data=0x%llx)\n", dev->phys_addr+offset,*data); return NULL; } if (offset >= d->rom_size) { *data = 0; return NULL; } return((void *)(d->rom_ptr + offset)); } /* Shutdown a ROM device */ void dev_rom_shutdown(vm_instance_t *vm,struct rom_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* Initialize a ROM zone */ int dev_rom_init(vm_instance_t *vm,char *name,m_uint64_t paddr,m_uint32_t len, m_uint8_t *rom_data,ssize_t rom_data_size) { struct rom_data *d; /* allocate the private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"ROM: unable to create device.\n"); return(-1); } memset(d,0,sizeof(*d)); d->rom_ptr = rom_data; d->rom_size = rom_data_size; vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_rom_shutdown; dev_init(&d->dev); d->dev.name = name; d->dev.priv_data = d; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.flags = VDEVICE_FLAG_CACHING; d->dev.handler = dev_rom_access; /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_rom.h000066400000000000000000000011541241034141600165130ustar00rootroot00000000000000/* * Cisco Router Simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __DEV_ROM_H__ #define __DEV_ROM_H__ #include #include "utils.h" #include "cpu.h" #include "device.h" #include "net_io.h" #include "vm.h" /* MIPS64 ROM */ extern m_uint8_t mips64_microcode[]; extern ssize_t mips64_microcode_len; /* PPC32 ROM */ extern m_uint8_t ppc32_microcode[]; extern ssize_t ppc32_microcode_len; /* Initialize a ROM zone */ int dev_rom_init(vm_instance_t *vm,char *name,m_uint64_t paddr,m_uint32_t len, m_uint8_t *rom_data,ssize_t rom_data_size); #endif dynamips-0.2.14/common/dev_sb1.c000066400000000000000000000047021241034141600164000ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005 Christophe Fillot (cf@utc.fr) * * SB-1 system control devices. */ #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_c7200.h" #define DEBUG_UNKNOWN 1 /* SB-1 private data */ struct sb1_data { vm_obj_t vm_obj; struct vdevice dev; /* Virtual machine */ vm_instance_t *vm; }; /* * dev_sb1_access() */ void *dev_sb1_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { //struct sb1_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0; switch(offset) { case 0x20000: if (op_type == MTS_READ) *data = 0x125020FF; break; /* Seen on a real NPE-G1 :) */ case 0x20008: if (op_type == MTS_READ) *data = 0x00800000FCDB0700ULL; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"SB1","read from addr 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"SB1","write to addr 0x%x, value=0x%llx, pc=0x%llx\n", offset,*data,cpu_get_pc(cpu)); } #endif } return NULL; } /* Shutdown the SB-1 system control devices */ void dev_sb1_shutdown(vm_instance_t *vm,struct sb1_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* Create SB-1 system control devices */ int dev_sb1_init(vm_instance_t *vm) { struct sb1_data *d; /* allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"SB1: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); vm_object_init(&d->vm_obj); d->vm_obj.name = "sb1_sysctrl"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_sb1_shutdown; dev_init(&d->dev); d->dev.name = "sb1_sysctrl"; d->dev.priv_data = d; d->dev.phys_addr = 0x10000000ULL; d->dev.phys_len = 0x60000; d->dev.handler = dev_sb1_access; /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_sb1_io.c000066400000000000000000000206701241034141600170710ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005 Christophe Fillot (cf@utc.fr) * * SB-1 I/O devices. * * XXX: just for tests! */ #include #include #include #include #include #include #include #include #include "utils.h" #include "ptask.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_c7200.h" #define DEBUG_UNKNOWN 1 /* DUART Status Register */ #define DUART_SR_RX_RDY 0x01 /* Receiver ready */ #define DUART_SR_RX_FFUL 0x02 /* Receive FIFO full */ #define DUART_SR_TX_RDY 0x04 /* Transmitter ready */ #define DUART_SR_TX_EMT 0x08 /* Transmitter empty */ /* DUART Interrupt Status Register */ #define DUART_ISR_TXA 0x01 /* Channel A Transmitter Ready */ #define DUART_ISR_RXA 0x02 /* Channel A Receiver Ready */ #define DUART_ISR_TXB 0x10 /* Channel B Transmitter Ready */ #define DUART_ISR_RXB 0x20 /* Channel B Receiver Ready */ /* DUART Interrupt Mask Register */ #define DUART_IMR_TXA 0x01 /* Channel A Transmitter Ready */ #define DUART_IMR_RXA 0x02 /* Channel A Receiver Ready */ #define DUART_IMR_TXB 0x10 /* Channel B Transmitter Ready */ #define DUART_IMR_RXB 0x20 /* Channel B Receiver Ready */ /* SB-1 DUART channel */ struct sb1_duart_channel { m_uint8_t mode; m_uint8_t cmd; }; /* SB-1 I/O private data */ struct sb1_io_data { vm_obj_t vm_obj; struct vdevice dev; /* Virtual machine */ vm_instance_t *vm; /* DUART info */ u_int duart_irq,duart_irq_seq; m_uint8_t duart_isr,duart_imr; struct sb1_duart_channel duart_chan[2]; /* Periodic task to trigger dummy DUART IRQ */ ptask_id_t duart_irq_tid; }; /* Console port input */ static void tty_con_input(vtty_t *vtty) { struct sb1_io_data *d = vtty->priv_data; if (d->duart_imr & DUART_IMR_RXA) { d->duart_isr |= DUART_ISR_RXA; vm_set_irq(d->vm,d->duart_irq); } } /* AUX port input */ static void tty_aux_input(vtty_t *vtty) { struct sb1_io_data *d = vtty->priv_data; if (d->duart_imr & DUART_IMR_RXB) { d->duart_isr |= DUART_ISR_RXB; vm_set_irq(d->vm,d->duart_irq); } } /* IRQ trickery for Console and AUX ports */ static int tty_trigger_dummy_irq(struct sb1_io_data *d,void *arg) { u_int mask; d->duart_irq_seq++; if (d->duart_irq_seq == 2) { mask = DUART_IMR_TXA|DUART_IMR_TXB; if (d->duart_imr & mask) { d->duart_isr |= DUART_ISR_TXA|DUART_ISR_TXB; vm_set_irq(d->vm,d->duart_irq); } d->duart_irq_seq = 0; } return(0); } /* * dev_sb1_io_access() */ void *dev_sb1_io_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct sb1_io_data *d = dev->priv_data; u_char odata; if (op_type == MTS_READ) *data = 0; switch(offset) { case 0x390: /* DUART Interrupt Status Register */ if (op_type == MTS_READ) *data = d->duart_isr; break; case 0x320: /* DUART Channel A Only Interrupt Status Register */ if (op_type == MTS_READ) *data = d->duart_isr & 0x0F; break; case 0x340: /* DUART Channel B Only Interrupt Status Register */ if (op_type == MTS_READ) *data = (d->duart_isr >> 4) & 0x0F; break; case 0x3a0: /* DUART Interrupt Mask Register */ if (op_type == MTS_READ) *data = d->duart_imr; else d->duart_imr = *data; break; case 0x330: /* DUART Channel A Only Interrupt Mask Register */ if (op_type == MTS_READ) { *data = d->duart_imr & 0x0F; } else { d->duart_imr &= ~0x0F; d->duart_imr |= *data & 0x0F; } break; case 0x350: /* DUART Channel B Only Interrupt Mask Register */ if (op_type == MTS_READ) { *data = (d->duart_imr >> 4) & 0x0F; } else { d->duart_imr &= ~0xF0; d->duart_imr |= (*data & 0x0F) << 4; } break; case 0x100: /* DUART Mode (Channel A) */ if (op_type == MTS_READ) d->duart_chan[0].mode = *data; else *data = d->duart_chan[0].mode; break; case 0x200: /* DUART Mode (Channel B) */ if (op_type == MTS_READ) d->duart_chan[1].mode = *data; else *data = d->duart_chan[1].mode; break; case 0x150: /* DUART Command Register (Channel A) */ if (op_type == MTS_READ) d->duart_chan[0].cmd = *data; else *data = d->duart_chan[0].cmd; break; case 0x250: /* DUART Command Register (Channel B) */ if (op_type == MTS_READ) d->duart_chan[1].cmd = *data; else *data = d->duart_chan[1].cmd; break; case 0x120: /* DUART Status Register (Channel A) */ if (op_type == MTS_READ) { odata = 0; if (vtty_is_char_avail(d->vm->vtty_con)) odata |= DUART_SR_RX_RDY; odata |= DUART_SR_TX_RDY; vm_clear_irq(d->vm,d->duart_irq); *data = odata; } break; case 0x220: /* DUART Status Register (Channel B) */ if (op_type == MTS_READ) { odata = 0; if (vtty_is_char_avail(d->vm->vtty_aux)) odata |= DUART_SR_RX_RDY; odata |= DUART_SR_TX_RDY; //vm_clear_irq(d->vm,d->duart_irq); *data = odata; } break; case 0x160: /* DUART Received Data Register (Channel A) */ if (op_type == MTS_READ) { *data = vtty_get_char(d->vm->vtty_con); d->duart_isr &= ~DUART_ISR_RXA; } break; case 0x260: /* DUART Received Data Register (Channel B) */ if (op_type == MTS_READ) { *data = vtty_get_char(d->vm->vtty_aux); d->duart_isr &= ~DUART_ISR_RXB; } break; case 0x170: /* DUART Transmit Data Register (Channel A) */ if (op_type == MTS_WRITE) { vtty_put_char(d->vm->vtty_con,(char)*data); d->duart_isr &= ~DUART_ISR_TXA; } break; case 0x270: /* DUART Transmit Data Register (Channel B) */ if (op_type == MTS_WRITE) { vtty_put_char(d->vm->vtty_aux,(char)*data); d->duart_isr &= ~DUART_ISR_TXB; } break; case 0x1a76: /* pcmcia status */ if (op_type == MTS_READ) *data = 0xFF; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,"SB1_IO","read from addr 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"SB1_IO","write to addr 0x%x, value=0x%llx, " "pc=0x%llx\n",offset,*data,cpu_get_pc(cpu)); } #endif } return NULL; } /* Shutdown the SB-1 I/O devices */ void dev_sb1_io_shutdown(vm_instance_t *vm,struct sb1_io_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* Create SB-1 I/O devices */ int dev_sb1_io_init(vm_instance_t *vm,u_int duart_irq) { struct sb1_io_data *d; /* allocate private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"SB1_IO: out of memory\n"); return(-1); } memset(d,0,sizeof(*d)); d->vm = vm; d->duart_irq = duart_irq; vm_object_init(&d->vm_obj); d->vm_obj.name = "sb1_io"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_sb1_io_shutdown; /* Set device properties */ dev_init(&d->dev); d->dev.name = "sb1_io"; d->dev.priv_data = d; d->dev.phys_addr = 0x10060000ULL; d->dev.phys_len = 0x10000; d->dev.handler = dev_sb1_io_access; /* Set console and AUX port notifying functions */ vm->vtty_con->priv_data = d; vm->vtty_aux->priv_data = d; vm->vtty_con->read_notifier = tty_con_input; vm->vtty_aux->read_notifier = tty_aux_input; /* Trigger periodically a dummy IRQ to flush buffers */ d->duart_irq_tid = ptask_add((ptask_callback)tty_trigger_dummy_irq,d,NULL); /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_sb1_pci.c000066400000000000000000000074411241034141600172360ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot. All rights reserved. * * PCI configuration space for SB-1 processor. */ #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #define DEBUG_ACCESS 0 /* Sibyte PCI ID */ #define SB1_PCI_VENDOR_ID 0x166D /* SB-1 PCI private data */ struct sb1_pci_data { vm_obj_t vm_obj; struct vdevice dev; struct pci_bus *pci_bus; /* PCI configuration (Bus 0, Device 0) */ struct pci_device *pci_cfg_dev; /* HyperTransport configuration (Bus 0, Device 1) */ struct pci_device *ht_cfg_dev; }; /* * sb1_pci_cfg_read() * * PCI Configuration (Bus 0, Device 0). */ static m_uint32_t sb1_pci_cfg_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { switch(reg) { case 0x08: return(0x06000002); default: return(0); } } /* * sb1_ht_cfg_read() * * HyperTransport Configuration (Bus 0, Device 1). */ static m_uint32_t sb1_ht_cfg_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { switch(reg) { case 0x08: return(0x06000002); case 0x44: return(1<<5); /* HyperTransport OK */ default: return(0); } } /* * dev_sb1_pci_access() */ void *dev_sb1_pci_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct sb1_pci_data *d = dev->priv_data; #if DEBUG_ACCESS if (op_type == MTS_READ) cpu_log(cpu,dev->name,"read access to offset = 0x%x, pc = 0x%llx\n", offset,cpu_get_pc(cpu)); else cpu_log(cpu,dev->name,"write access to vaddr = 0x%x, pc = 0x%llx, " "val = 0x%llx\n",offset,cpu_get_pc(cpu),*data); #endif if (op_type == MTS_READ) *data = 0; d->pci_bus->pci_addr = offset; pci_dev_data_handler(cpu,d->pci_bus,op_type,FALSE,data); return NULL; } /* Shutdown the PCI bus configuration zone */ void dev_sb1_pci_shutdown(vm_instance_t *vm,struct sb1_pci_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* Create the SB-1 PCI bus configuration zone */ int dev_sb1_pci_init(vm_instance_t *vm,char *name,m_uint64_t paddr) { struct sb1_pci_data *d; /* allocate the private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"SB1_PCI: unable to create device.\n"); return(-1); } memset(d,0,sizeof(*d)); d->pci_bus = vm->pci_bus[0]; vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_sb1_pci_shutdown; dev_init(&d->dev); d->dev.name = name; d->dev.priv_data = d; d->dev.phys_addr = paddr; d->dev.phys_len = 1 << 24; d->dev.handler = dev_sb1_pci_access; /* PCI configuration header on Bus 0, Device 0 */ d->pci_cfg_dev = pci_dev_add(d->pci_bus,"sb1_pci_cfg", SB1_PCI_VENDOR_ID,0x0001,0,0,-1,NULL, NULL,sb1_pci_cfg_read,NULL); /* Create the HyperTransport bus #1 */ vm->pci_bus_pool[28] = pci_bus_create("HT bus #1",-1); /* HyperTransport configuration header on Bus 0, Device 1 */ d->ht_cfg_dev = pci_bridge_create_dev(d->pci_bus,"sb1_ht_cfg", SB1_PCI_VENDOR_ID,0x0002, 1,0,vm->pci_bus_pool[28], sb1_ht_cfg_read,NULL); /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/dev_ti2050b.c000066400000000000000000000014221241034141600167740ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Texas Instruments PCI205B PCI bridge. */ #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "pci_dev.h" #define PCI_VENDOR_TI 0x104C #define PCI_PRODUCT_PCI2050B 0xAC28 /* * dev_ti2050b_init() */ int dev_ti2050b_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus) { struct pci_device *dev; dev = pci_bridge_create_dev(pci_bus,"ti2050b", PCI_VENDOR_TI,PCI_PRODUCT_PCI2050B, pci_device,0,sec_bus,NULL,NULL); return((dev != NULL) ? 0 : -1); } dynamips-0.2.14/common/dev_vtty.c000066400000000000000000001006321241034141600167200ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Virtual console TTY. * * "Interactive" part idea by Mtve. * TCP console added by Mtve. * Serial console by Peter Ross (suxen_drol@hotmail.com) */ #include "dynamips_common.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "mips64_exec.h" #include "ppc32_exec.h" #include "device.h" #include "memory.h" #include "dev_vtty.h" #ifdef USE_UNSTABLE #include "tcb.h" #endif #ifndef SOL_TCP #define SOL_TCP 6 #endif /* VTTY list */ static pthread_mutex_t vtty_list_mutex = PTHREAD_MUTEX_INITIALIZER; static vtty_t *vtty_list = NULL; static pthread_t vtty_thread; #define VTTY_LIST_LOCK() pthread_mutex_lock(&vtty_list_mutex); #define VTTY_LIST_UNLOCK() pthread_mutex_unlock(&vtty_list_mutex); static struct termios tios,tios_orig; static int ctrl_code_ok = 1; static int telnet_message_ok = 1; /* Allow the user to disable the CTRL code for the monitor interface */ void vtty_set_ctrlhandler(int n) { ctrl_code_ok = n; } /* Allow the user to disable the telnet message for AUX and CONSOLE */ void vtty_set_telnetmsg(int n) { telnet_message_ok = n; } /* Send Telnet command: WILL TELOPT_ECHO */ static void vtty_telnet_will_echo(int fd) { u_char cmd[] = { IAC, WILL, TELOPT_ECHO }; write(fd,cmd,sizeof(cmd)); } /* Send Telnet command: Suppress Go-Ahead */ static void vtty_telnet_will_suppress_go_ahead(int fd) { u_char cmd[] = { IAC, WILL, TELOPT_SGA }; write(fd,cmd,sizeof(cmd)); } /* Send Telnet command: Don't use linemode */ static void vtty_telnet_dont_linemode(int fd) { u_char cmd[] = { IAC, DONT, TELOPT_LINEMODE }; write(fd,cmd,sizeof(cmd)); } /* Send Telnet command: does the client support terminal type message? */ static void vtty_telnet_do_ttype(int fd) { u_char cmd[] = { IAC, DO, TELOPT_TTYPE }; write(fd,cmd,sizeof(cmd)); } /* Restore TTY original settings */ static void vtty_term_reset(void) { tcsetattr(STDIN_FILENO,TCSANOW,&tios_orig); } /* Initialize real TTY */ static void vtty_term_init(void) { tcgetattr(STDIN_FILENO,&tios); memcpy(&tios_orig,&tios,sizeof(struct termios)); atexit(vtty_term_reset); tios.c_cc[VTIME] = 0; tios.c_cc[VMIN] = 1; /* Disable Ctrl-C, Ctrl-S, Ctrl-Q and Ctrl-Z */ tios.c_cc[VINTR] = 0; tios.c_cc[VSTART] = 0; tios.c_cc[VSTOP] = 0; tios.c_cc[VSUSP] = 0; tios.c_lflag &= ~(ICANON|ECHO); tios.c_iflag &= ~ICRNL; tcsetattr(STDIN_FILENO, TCSANOW, &tios); tcflush(STDIN_FILENO,TCIFLUSH); } #if HAS_RFC2553 /* Wait for a TCP connection */ static int vtty_tcp_conn_wait(vtty_t *vtty) { struct addrinfo hints,*res,*res0; char port_str[20],*addr,*proto; int i, nsock; int one = 1; for(i=0;ifd_array[i] = -1; memset(&hints,0,sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; snprintf(port_str,sizeof(port_str),"%d",vtty->tcp_port); addr = (binding_addr && strlen(binding_addr)) ? binding_addr : NULL; if (getaddrinfo(addr,port_str,&hints,&res0) != 0) { perror("vtty_tcp_waitcon: getaddrinfo"); return(-1); } nsock = 0; for (res=res0;(res && (nsock < VTTY_MAX_FD));res=res->ai_next) { if ((res->ai_family != PF_INET) && (res->ai_family != PF_INET6)) continue; vtty->fd_array[nsock] = socket(res->ai_family,res->ai_socktype, res->ai_protocol); if (vtty->fd_array[nsock] < 0) continue; if (setsockopt(vtty->fd_array[nsock],SOL_SOCKET,SO_REUSEADDR,&one,sizeof(one)) < 0) perror("vtty_tcp_waitcon: setsockopt(SO_REUSEADDR)"); if (setsockopt(vtty->fd_array[nsock],SOL_SOCKET,SO_KEEPALIVE,&one,sizeof(one)) < 0) perror("vtty_tcp_waitcon: setsockopt(SO_KEEPALIVE)"); // Send telnet packets asap. Dont wait to fill packets up if (setsockopt(vtty->fd_array[nsock],SOL_TCP,TCP_NODELAY, &one,sizeof(one)) < 0) perror("vtty_tcp_waitcon: setsockopt(TCP_NODELAY)"); if ((bind(vtty->fd_array[nsock],res->ai_addr,res->ai_addrlen) < 0) || (listen(vtty->fd_array[nsock],1) < 0)) { close(vtty->fd_array[nsock]); vtty->fd_array[nsock] = -1; continue; } proto = (res->ai_family == PF_INET6) ? "IPv6" : "IPv4"; vm_log(vtty->vm,"VTTY","%s: waiting connection on tcp port %d for protocol %s (FD %d)\n", vtty->name,vtty->tcp_port,proto,vtty->fd_array[nsock]); nsock++; } freeaddrinfo(res0); return(nsock); } #else /* Wait for a TCP connection */ static int vtty_tcp_conn_wait(vtty_t *vtty) { struct sockaddr_in serv; int i; int one = 1; for(i=0;ifd_array[i] = -1; if ((vtty->fd_array[0] = socket(PF_INET,SOCK_STREAM,0)) < 0) { perror("vtty_tcp_waitcon: socket"); return(-1); } if (setsockopt(vtty->fd_array[0],SOL_SOCKET,SO_REUSEADDR,&one,sizeof(one)) < 0) { perror("vtty_tcp_waitcon: setsockopt(SO_REUSEADDR)"); goto error; } if (setsockopt(vtty->fd_array[0],SOL_SOCKET,SO_KEEPALIVE,&one,sizeof(one)) < 0) { perror("vtty_tcp_waitcon: setsockopt(SO_KEEPALIVE)"); goto error; } // Send telnet packets asap. Dont wait to fill packets up if (setsockopt(vtty->fd_array[0],SOL_TCP,TCP_NODELAY,&one,sizeof(one)) < 0) { perror("vtty_tcp_waitcon: setsockopt(TCP_NODELAY)"); goto error; } memset(&serv,0,sizeof(serv)); serv.sin_family = AF_INET; serv.sin_addr.s_addr = htonl(INADDR_ANY); serv.sin_port = htons(vtty->tcp_port); if (bind(vtty->fd_array[0],(struct sockaddr *)&serv,sizeof(serv)) < 0) { perror("vtty_tcp_waitcon: bind"); goto error; } if (listen(vtty->fd_array[0],1) < 0) { perror("vtty_tcp_waitcon: listen"); goto error; } vm_log(vtty->vm,"VTTY","%s: waiting connection on tcp port %d (FD %d)\n", vtty->name,vtty->tcp_port,vtty->fd_array[0]); return(1); error: close(vtty->fd_array[0]); vtty->fd_array[0] = -1; return(-1); } #endif /* Accept a TCP connection */ static int vtty_tcp_conn_accept(vtty_t *vtty, int nsock) { int fd,*fd_slot; u_int i; if (fd_pool_get_free_slot(&vtty->fd_pool,&fd_slot) < 0) { vm_error(vtty->vm,"unable to create a new VTTY TCP connection\n"); return(-1); } if ((fd = accept(vtty->fd_array[nsock],NULL,NULL)) < 0) { vm_error(vtty->vm,"vtty_tcp_conn_accept: accept on port %d failed %s\n", vtty->tcp_port,strerror(errno)); return(-1); } /* Register the new FD */ *fd_slot = fd; vm_log(vtty->vm,"VTTY","%s is now connected (accept_fd=%d,conn_fd=%d)\n", vtty->name,vtty->fd_array[nsock],fd); /* Adapt Telnet settings */ if (vtty->terminal_support) { vtty_telnet_do_ttype(fd); vtty_telnet_will_echo(fd); vtty_telnet_will_suppress_go_ahead(fd); vtty_telnet_dont_linemode(fd); vtty->input_state = VTTY_INPUT_TEXT; } if (telnet_message_ok == 1) { fd_printf(fd,0, "Connected to Dynamips VM \"%s\" (ID %u, type %s) - %s\r\n" "Press ENTER to get the prompt.\r\n", vtty->vm->name, vtty->vm->instance_id, vm_get_type(vtty->vm), vtty->name); /* replay old text */ for (i = vtty->replay_ptr; i < VTTY_BUFFER_SIZE; i++) { if (vtty->replay_buffer[i] != 0) { send(fd,&vtty->replay_buffer[i],VTTY_BUFFER_SIZE-i,0); break; } } for (i = 0; i < vtty->replay_ptr; i++) { if (vtty->replay_buffer[i] != 0) { send(fd,&vtty->replay_buffer[i],vtty->replay_ptr-i,0); break; } } /* warn if not running */ if (vtty->vm->status != VM_STATUS_RUNNING) fd_printf(fd,0,"\r\n!!! WARNING - VM is not running, will be unresponsive (status=%d) !!!\r\n",vtty->vm->status); vtty_flush(vtty); } return(0); } /* * Parse serial interface descriptor string, return 0 if success * string takes the form "device:baudrate:databits:parity:stopbits:hwflow" * device is mandatory, other options are optional (default=9600,8,N,1,0). */ int vtty_parse_serial_option(vtty_serial_option_t *option, char *optarg) { char *array[6]; int count; if ((count = m_strtok(optarg, ':', array, 6)) < 1) { fprintf(stderr,"vtty_parse_serial_option: invalid string\n"); return(-1); } if (!(option->device = strdup(array[0]))) { fprintf(stderr,"vtty_parse_serial_option: unable to copy string\n"); return(-1); } option->baudrate = (count>1) ? atoi(array[1]) : 9600; option->databits = (count>2) ? atoi(array[2]) : 8; if (count > 3) { switch(*array[3]) { case 'o': case 'O': option->parity = 1; /* odd */ case 'e': case 'E': option->parity = 2; /* even */ default: option->parity = 0; /* none */ } } else { option->parity = 0; } option->stopbits = (count>4) ? atoi(array[4]) : 1; option->hwflow = (count>5) ? atoi(array[5]) : 0; return(0); } #if defined(__CYGWIN__) || defined(SUNOS) void cfmakeraw(struct termios *termios_p) { termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP| INLCR|IGNCR|ICRNL|IXON); termios_p->c_oflag &= ~OPOST; termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); termios_p->c_cflag &= ~(CSIZE|PARENB); termios_p->c_cflag |= CS8; } #endif /* * Setup serial port, return 0 if success. */ static int vtty_serial_setup(vtty_t *vtty, const vtty_serial_option_t *option) { struct termios tio; int tio_baudrate; if (tcgetattr(vtty->fd_array[0], &tio) != 0) { fprintf(stderr, "error: tcgetattr failed\n"); return(-1); } cfmakeraw(&tio); tio.c_cflag = 0 |CLOCAL // ignore modem control lines ; tio.c_cflag &= ~CREAD; tio.c_cflag |= CREAD; switch(option->baudrate) { case 50 : tio_baudrate = B50; break; case 75 : tio_baudrate = B75; break; case 110 : tio_baudrate = B110; break; case 134 : tio_baudrate = B134; break; case 150 : tio_baudrate = B150; break; case 200 : tio_baudrate = B200; break; case 300 : tio_baudrate = B300; break; case 600 : tio_baudrate = B600; break; case 1200 : tio_baudrate = B1200; break; case 1800 : tio_baudrate = B1800; break; case 2400 : tio_baudrate = B2400; break; case 4800 : tio_baudrate = B4800; break; case 9600 : tio_baudrate = B9600; break; case 19200 : tio_baudrate = B19200; break; case 38400 : tio_baudrate = B38400; break; case 57600 : tio_baudrate = B57600; break; #if defined(B76800) case 76800 : tio_baudrate = B76800; break; #endif case 115200 : tio_baudrate = B115200; break; #if defined(B230400) case 230400 : tio_baudrate = B230400; break; #endif default: fprintf(stderr, "error: unsupported baudrate\n"); return(-1); } cfsetospeed(&tio, tio_baudrate); cfsetispeed(&tio, tio_baudrate); tio.c_cflag &= ~CSIZE; /* clear size flag */ switch(option->databits) { case 5 : tio.c_cflag |= CS5; break; case 6 : tio.c_cflag |= CS6; break; case 7 : tio.c_cflag |= CS7; break; case 8 : tio.c_cflag |= CS8; break; default : fprintf(stderr, "error: unsupported databits\n"); return(-1); } tio.c_iflag &= ~INPCK; /* clear parity flag */ tio.c_cflag &= ~(PARENB|PARODD); switch(option->parity) { case 0 : break; case 2 : tio.c_iflag|=INPCK; tio.c_cflag|=PARENB; break; /* even */ case 1 : tio.c_iflag|=INPCK; tio.c_cflag|=PARENB|PARODD; break; /* odd */ default: fprintf(stderr, "error: unsupported parity\n"); return(-1); } tio.c_cflag &= ~CSTOPB; /* clear stop flag */ switch(option->stopbits) { case 1 : break; case 2 : tio.c_cflag |= CSTOPB; break; default : fprintf(stderr, "error: unsupported stopbits\n"); return(-1); } #if defined(CRTSCTS) tio.c_cflag &= ~CRTSCTS; #endif #if defined(CNEW_RTSCTS) tio.c_cflag &= ~CNEW_RTSCTS; #endif if (option->hwflow) { #if defined(CRTSCTS) tio.c_cflag |= CRTSCTS; #else tio.c_cflag |= CNEW_RTSCTS; #endif } tio.c_cc[VTIME] = 0; tio.c_cc[VMIN] = 1; /* block read() until one character is available */ #if 0 /* not neccessary unless O_NONBLOCK used */ if (fcntl(vtty->fd_array[0], F_SETFL, 0) != 0) { /* enable blocking mode */ fprintf(stderr, "error: fnctl F_SETFL failed\n"); return(-1); } #endif if (tcflush(vtty->fd_array[0], TCIOFLUSH) != 0) { fprintf(stderr, "error: tcflush failed\n"); return(-1); } if (tcsetattr(vtty->fd_array[0], TCSANOW, &tio) != 0 ) { fprintf(stderr, "error: tcsetattr failed\n"); return(-1); } return(0); } /* Create a virtual tty */ vtty_t *vtty_create(vm_instance_t *vm,char *name,int type,int tcp_port, const vtty_serial_option_t *option) { vtty_t *vtty; int i; if (!(vtty = malloc(sizeof(*vtty)))) { fprintf(stderr,"VTTY: unable to create new virtual tty.\n"); return NULL; } memset(vtty,0,sizeof(*vtty)); vtty->name = name; vtty->type = type; vtty->vm = vm; vtty->fd_count = 0; pthread_mutex_init(&vtty->lock,NULL); vtty->terminal_support = 1; vtty->input_state = VTTY_INPUT_TEXT; fd_pool_init(&vtty->fd_pool); for(i=0;ifd_array[i] = -1; switch (vtty->type) { case VTTY_TYPE_NONE: break; case VTTY_TYPE_TERM: vtty_term_init(); vtty->fd_array[0] = STDIN_FILENO; break; case VTTY_TYPE_TCP: vtty->tcp_port = tcp_port; vtty->fd_count = vtty_tcp_conn_wait(vtty); break; case VTTY_TYPE_SERIAL: vtty->fd_array[0] = open(option->device, O_RDWR); if (vtty->fd_array[0] < 0) { fprintf(stderr,"VTTY: open failed\n"); free(vtty); return NULL; } if (vtty_serial_setup(vtty,option)) { fprintf(stderr,"VTTY: setup failed\n"); close(vtty->fd_array[0]); free(vtty); return NULL; } vtty->terminal_support = 0; break; default: fprintf(stderr,"tty_create: bad vtty type %d\n",vtty->type); return NULL; } /* Add this new VTTY to the list */ VTTY_LIST_LOCK(); vtty->next = vtty_list; vtty->pprev = &vtty_list; if (vtty_list != NULL) vtty_list->pprev = &vtty->next; vtty_list = vtty; VTTY_LIST_UNLOCK(); return vtty; } /* Delete a virtual tty */ void vtty_delete(vtty_t *vtty) { int i; if (vtty != NULL) { if (vtty->pprev != NULL) { VTTY_LIST_LOCK(); if (vtty->next) vtty->next->pprev = vtty->pprev; *(vtty->pprev) = vtty->next; VTTY_LIST_UNLOCK(); } switch(vtty->type) { case VTTY_TYPE_TCP: for(i=0;ifd_count;i++) if (vtty->fd_array[i] != -1) { vm_log(vtty->vm,"VTTY","%s: closing FD %d\n",vtty->name,vtty->fd_array[i]); close(vtty->fd_array[i]); } fd_pool_free(&vtty->fd_pool); vtty->fd_count = 0; break; default: /* We don't close FD 0 since it is stdin */ if (vtty->fd_array[0] > 0) { vm_log(vtty->vm,"VTTY","%s: closing FD %d\n",vtty->name,vtty->fd_array[0]); close(vtty->fd_array[0]); } } free(vtty); } } /* Store a character in the FIFO buffer */ static int vtty_store(vtty_t *vtty,u_char c) { u_int nwptr; VTTY_LOCK(vtty); nwptr = vtty->write_ptr + 1; if (nwptr == VTTY_BUFFER_SIZE) nwptr = 0; if (nwptr == vtty->read_ptr) { VTTY_UNLOCK(vtty); return(-1); } vtty->buffer[vtty->write_ptr] = c; vtty->write_ptr = nwptr; VTTY_UNLOCK(vtty); return(0); } /* Store arbritary data in the FIFO buffer */ int vtty_store_data(vtty_t *vtty,char *data, int len) { int bytes; if (!vtty || !data || len < 0) return(-1); // invalid argument for (bytes = 0; bytes < len; bytes++) { if (vtty_store(vtty,data[bytes]) == -1) break; } vtty->input_pending = TRUE; return(bytes); } /* Store CTRL+C in buffer */ int vtty_store_ctrlc(vtty_t *vtty) { if (vtty) vtty_store(vtty,0x03); return(0); } /* * Read a character from the terminal. */ static int vtty_term_read(vtty_t *vtty) { u_char c; if (read(vtty->fd_array[0],&c,1) == 1) return(c); perror("read from vtty failed"); return(-1); } /* * Read a character from the TCP connection. */ static int vtty_tcp_read(vtty_t *vtty,int *fd_slot) { int fd = *fd_slot; u_char c; if (read(fd,&c,1) == 1) return(c); /* problem with the connection */ shutdown(fd,2); close(fd); *fd_slot = -1; /* Shouldn't happen... */ return(-1); } /* * Read a character from the virtual TTY. * * If the VTTY is a TCP connection, restart it in case of error. */ static int vtty_read(vtty_t *vtty,int *fd_slot) { switch(vtty->type) { case VTTY_TYPE_TERM: case VTTY_TYPE_SERIAL: return(vtty_term_read(vtty)); case VTTY_TYPE_TCP: return(vtty_tcp_read(vtty,fd_slot)); default: fprintf(stderr,"vtty_read: bad vtty type %d\n",vtty->type); return(-1); } /* NOTREACHED */ return(-1); } /* Remote control for MIPS64 processors */ static int remote_control_mips64(vtty_t *vtty,char c,cpu_mips_t *cpu) { switch(c) { /* Show information about JIT compiled pages */ case 'b': printf("\nCPU0: %u JIT compiled pages [Exec Area Pages: %lu/%lu]\n", cpu->compiled_pages, (u_long)cpu->exec_page_alloc, (u_long)cpu->exec_page_count); break; /* Non-JIT mode statistics */ case 'j': mips64_dump_stats(cpu); break; default: return(FALSE); } return(TRUE); } /* Remote control for PPC32 processors */ static int remote_control_ppc32(vtty_t *vtty,char c,cpu_ppc_t *cpu) { switch(c) { /* Show information about JIT compiled pages */ case 'b': printf("\nCPU0: %u JIT compiled pages [Exec Area Pages: %lu/%lu]\n", cpu->compiled_pages, (u_long)cpu->exec_page_alloc, (u_long)cpu->exec_page_count); break; /* Non-JIT mode statistics */ case 'j': ppc32_dump_stats(cpu); break; default: return(FALSE); } return(TRUE); } /* Process remote control char */ static void remote_control(vtty_t *vtty,u_char c) { vm_instance_t *vm = vtty->vm; cpu_gen_t *cpu0; cpu0 = vm->boot_cpu; /* Specific commands for the different CPU models */ if (cpu0) { switch(cpu0->type) { case CPU_TYPE_MIPS64: if (remote_control_mips64(vtty,c,CPU_MIPS64(cpu0))) return; break; case CPU_TYPE_PPC32: if (remote_control_ppc32(vtty,c,CPU_PPC32(cpu0))) return; break; } } switch(c) { /* Show the object list */ case 'o': vm_object_dump(vm); break; /* Stop the MIPS VM */ case 'q': vm->status = VM_STATUS_SHUTDOWN; break; /* Reboot the C7200 */ case 'k': #if 0 if (vm->type == VM_TYPE_C7200) c7200_boot_ios(VM_C7200(vm)); #endif break; /* Show the device list */ case 'd': dev_show_list(vm); pci_dev_show_list(vm->pci_bus[0]); pci_dev_show_list(vm->pci_bus[1]); break; /* Show info about Port Adapters or Network Modules */ case 'p': vm_slot_show_all_info(vm); break; /* Dump the MIPS registers */ case 'r': if (cpu0) cpu0->reg_dump(cpu0); break; /* Dump the latest memory accesses */ case 'm': if (cpu0) memlog_dump(cpu0); break; /* Suspend CPU emulation */ case 's': vm_suspend(vm); break; /* Resume CPU emulation */ case 'u': vm_resume(vm); break; /* Dump the MMU information */ case 't': if (cpu0) cpu0->mmu_dump(cpu0); break; /* Dump the MMU information (raw mode) */ case 'z': if (cpu0) cpu0->mmu_raw_dump(cpu0); break; /* Memory translation cache statistics */ case 'l': if (cpu0) cpu0->mts_show_stats(cpu0); break; /* Extract the configuration from the NVRAM */ case 'c': vm_ios_save_config(vm); break; /* Determine an idle pointer counter */ case 'i': if (cpu0) cpu0->get_idling_pc(cpu0); break; /* Experimentations / Tests */ case 'x': #if 0 if (cpu0) { /* IRQ triggering */ vm_set_irq(vm,6); //CPU_MIPS64(cpu0)->irq_disable = TRUE; } #endif #ifdef USE_UNSTABLE tsg_show_stats(); #endif break; case 'y': if (cpu0) { /* IRQ clearing */ vm_clear_irq(vm,6); } break; /* Twice Ctrl + ']' (0x1d, 29), or Alt-Gr + '*' (0xb3, 179) */ case 0x1d: case 0xb3: vtty_store(vtty,c); break; default: printf("\n\nInstance %s (ID %d)\n\n",vm->name,vm->instance_id); printf("o - Show the VM object list\n" "d - Show the device list\n" "r - Dump CPU registers\n" "t - Dump MMU information\n" "z - Dump MMU information (raw mode)\n" "m - Dump the latest memory accesses\n" "s - Suspend CPU emulation\n" "u - Resume CPU emulation\n" "q - Quit the emulator\n" "k - Reboot the virtual machine\n" "b - Show info about JIT compiled pages\n" "l - MTS cache statistics\n" "c - Write IOS configuration to disk\n" "j - Non-JIT mode statistics\n" "i - Determine an idling pointer counter\n" "x - Experimentations (can crash the box!)\n" "^] - Send ^]\n" "Other - This help\n"); } } /* Read a character (until one is available) and store it in buffer */ static void vtty_read_and_store(vtty_t *vtty,int *fd_slot) { int c; /* wait until we get a character input */ c = vtty_read(vtty,fd_slot); /* if read error, do nothing */ if (c < 0) return; /* If something was read, make sure the handler is informed */ vtty->input_pending = TRUE; if (!vtty->terminal_support) { vtty_store(vtty,c); return; } switch(vtty->input_state) { case VTTY_INPUT_TEXT : switch(c) { case 0x1b: vtty->input_state = VTTY_INPUT_VT1; return; /* Ctrl + ']' (0x1d, 29), or Alt-Gr + '*' (0xb3, 179) */ case 0x1d: case 0xb3: if (ctrl_code_ok == 1) { vtty->input_state = VTTY_INPUT_REMOTE; } else { vtty_store(vtty,c); } return; case IAC : vtty->input_state = VTTY_INPUT_TELNET; return; case 0: /* NULL - Must be ignored - generated by Linux telnet */ case 10: /* LF (Line Feed) - Must be ignored on Windows platform */ return; default: /* Store a standard character */ vtty_store(vtty,c); return; } case VTTY_INPUT_VT1 : switch(c) { case 0x5b: vtty->input_state = VTTY_INPUT_VT2; return; default: vtty_store(vtty,0x1b); vtty_store(vtty,c); } vtty->input_state = VTTY_INPUT_TEXT; return; case VTTY_INPUT_VT2 : switch(c) { case 0x41: /* Up Arrow */ vtty_store(vtty,16); break; case 0x42: /* Down Arrow */ vtty_store(vtty,14); break; case 0x43: /* Right Arrow */ vtty_store(vtty,6); break; case 0x44: /* Left Arrow */ vtty_store(vtty,2); break; default: vtty_store(vtty,0x5b); vtty_store(vtty,0x1b); vtty_store(vtty,c); break; } vtty->input_state = VTTY_INPUT_TEXT; return; case VTTY_INPUT_REMOTE : remote_control(vtty, c); vtty->input_state = VTTY_INPUT_TEXT; return; case VTTY_INPUT_TELNET : vtty->telnet_cmd = c; switch(c) { case WILL: case WONT: case DO: case DONT: vtty->input_state = VTTY_INPUT_TELNET_IYOU; return; case SB : vtty->telnet_cmd = c; vtty->input_state = VTTY_INPUT_TELNET_SB1; return; case SE: break; case IAC : vtty_store(vtty, IAC); break; } vtty->input_state = VTTY_INPUT_TEXT; return; case VTTY_INPUT_TELNET_IYOU : vtty->telnet_opt = c; /* if telnet client can support ttype, ask it to send ttype string */ if ((vtty->telnet_cmd == WILL) && (vtty->telnet_opt == TELOPT_TTYPE)) { vtty_put_char(vtty, IAC); vtty_put_char(vtty, SB); vtty_put_char(vtty, TELOPT_TTYPE); vtty_put_char(vtty, TELQUAL_SEND); vtty_put_char(vtty, IAC); vtty_put_char(vtty, SE); } vtty->input_state = VTTY_INPUT_TEXT; return; case VTTY_INPUT_TELNET_SB1 : vtty->telnet_opt = c; vtty->input_state = VTTY_INPUT_TELNET_SB2; return; case VTTY_INPUT_TELNET_SB2 : vtty->telnet_qual = c; if ((vtty->telnet_opt == TELOPT_TTYPE) && (vtty->telnet_qual == TELQUAL_IS)) vtty->input_state = VTTY_INPUT_TELNET_SB_TTYPE; else vtty->input_state = VTTY_INPUT_TELNET_NEXT; return; case VTTY_INPUT_TELNET_SB_TTYPE : /* parse ttype string: first char is sufficient */ /* if client is xterm or vt, set the title bar */ if ((c == 'x') || (c == 'X') || (c == 'v') || (c == 'V')) { fd_printf(*fd_slot,0,"\033]0;%s\07", vtty->vm->name); } vtty->input_state = VTTY_INPUT_TELNET_NEXT; return; case VTTY_INPUT_TELNET_NEXT : /* ignore all chars until next IAC */ if (c == IAC) vtty->input_state = VTTY_INPUT_TELNET; return; } } /* Read a character from the buffer (-1 if the buffer is empty) */ int vtty_get_char(vtty_t *vtty) { u_char c; VTTY_LOCK(vtty); if (vtty->read_ptr == vtty->write_ptr) { VTTY_UNLOCK(vtty); return(-1); } c = vtty->buffer[vtty->read_ptr++]; if (vtty->read_ptr == VTTY_BUFFER_SIZE) vtty->read_ptr = 0; VTTY_UNLOCK(vtty); return(c); } /* Returns TRUE if a character is available in buffer */ int vtty_is_char_avail(vtty_t *vtty) { int res; VTTY_LOCK(vtty); res = (vtty->read_ptr != vtty->write_ptr); VTTY_UNLOCK(vtty); return(res); } /* Put char to vtty */ void vtty_put_char(vtty_t *vtty, char ch) { switch(vtty->type) { case VTTY_TYPE_NONE: break; case VTTY_TYPE_TERM: case VTTY_TYPE_SERIAL: if (write(vtty->fd_array[0],&ch,1) != 1) { vm_log(vtty->vm,"VTTY","%s: put char 0x%x failed (%s)\n", vtty->name,(int)ch,strerror(errno)); } break; case VTTY_TYPE_TCP: fd_pool_send(&vtty->fd_pool,&ch,1,0); break; default: vm_error(vtty->vm,"vtty_put_char: bad vtty type %d\n",vtty->type); exit(1); } /* store char for replay */ vtty->replay_buffer[vtty->replay_ptr] = ch; ++vtty->replay_ptr; if (vtty->replay_ptr == VTTY_BUFFER_SIZE) vtty->replay_ptr = 0; } /* Put a buffer to vtty */ void vtty_put_buffer(vtty_t *vtty,char *buf,size_t len) { size_t i; for(i=0;itype) { case VTTY_TYPE_TERM: case VTTY_TYPE_SERIAL: if (vtty->fd_array[0] != -1) fsync(vtty->fd_array[0]); break; } } /* VTTY TCP input */ static void vtty_tcp_input(int *fd_slot,void *opt) { vtty_read_and_store((vtty_t *)opt,fd_slot); } /* VTTY thread */ static void *vtty_thread_main(void *arg) { vtty_t *vtty; struct timeval tv; int fd_max,fd_tcp,res; fd_set rfds; int i; for(;;) { VTTY_LIST_LOCK(); /* Build the FD set */ FD_ZERO(&rfds); fd_max = -1; for(vtty=vtty_list;vtty;vtty=vtty->next) { switch(vtty->type) { case VTTY_TYPE_TCP: for(i=0;ifd_count;i++) if (vtty->fd_array[i] != -1) { FD_SET(vtty->fd_array[i],&rfds); if (vtty->fd_array[i] > fd_max) fd_max = vtty->fd_array[i]; } fd_tcp = fd_pool_set_fds(&vtty->fd_pool,&rfds); fd_max = m_max(fd_tcp,fd_max); break; default: if (vtty->fd_array[0] != -1) { FD_SET(vtty->fd_array[0],&rfds); fd_max = m_max(vtty->fd_array[0],fd_max); } } } VTTY_LIST_UNLOCK(); /* Wait for incoming data */ tv.tv_sec = 0; tv.tv_usec = 50 * 1000; /* 50 ms */ res = select(fd_max+1,&rfds,NULL,NULL,&tv); if (res == -1) { if (errno != EINTR) { perror("vtty_thread: select"); } continue; } /* Examine active FDs and call user handlers */ VTTY_LIST_LOCK(); for(vtty=vtty_list;vtty;vtty=vtty->next) { switch(vtty->type) { case VTTY_TYPE_TCP: /* check incoming connection */ for(i=0;ifd_count;i++) { if (vtty->fd_array[i] == -1) continue; if (!FD_ISSET(vtty->fd_array[i],&rfds)) continue; vtty_tcp_conn_accept(vtty, i); } /* check established connection */ fd_pool_check_input(&vtty->fd_pool,&rfds,vtty_tcp_input,vtty); break; /* Term, Serial */ default: if (vtty->fd_array[0] != -1 && FD_ISSET(vtty->fd_array[0],&rfds)) { vtty_read_and_store(vtty,&vtty->fd_array[0]); vtty->input_pending = TRUE; } } if (vtty->input_pending) { if (vtty->read_notifier != NULL) vtty->read_notifier(vtty); vtty->input_pending = FALSE; } /* Flush any pending output */ if (!vtty->managed_flush) vtty_flush(vtty); } VTTY_LIST_UNLOCK(); } return NULL; } /* Initialize the VTTY thread */ int vtty_init(void) { if (pthread_create(&vtty_thread,NULL,vtty_thread_main,NULL)) { perror("vtty: pthread_create"); return(-1); } return(0); } dynamips-0.2.14/common/dev_vtty.h000066400000000000000000000057731241034141600167370ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Virtual console TTY. */ #ifndef __DEV_VTTY_H__ #define __DEV_VTTY_H__ #include #include #include "vm.h" #include /* 4 Kb should be enough for a keyboard buffer */ #define VTTY_BUFFER_SIZE 4096 /* Maximum listening socket number */ #define VTTY_MAX_FD 10 /* VTTY connection types */ enum { VTTY_TYPE_NONE = 0, VTTY_TYPE_TERM, VTTY_TYPE_TCP, VTTY_TYPE_SERIAL, }; /* VTTY connection states (for TCP) */ enum { VTTY_STATE_TCP_INVALID, /* connection is not working */ VTTY_STATE_TCP_WAITING, /* waiting for incoming connection */ VTTY_STATE_TCP_RUNNING, /* character reading/writing ok */ }; /* VTTY input states */ enum { VTTY_INPUT_TEXT, VTTY_INPUT_VT1, VTTY_INPUT_VT2, VTTY_INPUT_REMOTE, VTTY_INPUT_TELNET, VTTY_INPUT_TELNET_IYOU, VTTY_INPUT_TELNET_SB1, VTTY_INPUT_TELNET_SB2, VTTY_INPUT_TELNET_SB_TTYPE, VTTY_INPUT_TELNET_NEXT }; /* Commmand line support utility */ typedef struct vtty_serial_option vtty_serial_option_t; struct vtty_serial_option { char *device; int baudrate, databits, parity, stopbits, hwflow; }; int vtty_parse_serial_option(vtty_serial_option_t *params, char *optarg); /* Virtual TTY structure */ typedef struct virtual_tty vtty_t; struct virtual_tty { vm_instance_t *vm; char *name; int type; int fd_array[VTTY_MAX_FD]; int fd_count; int tcp_port; int terminal_support; int input_state; int input_pending; int telnet_cmd, telnet_opt, telnet_qual; int managed_flush; u_char buffer[VTTY_BUFFER_SIZE]; u_int read_ptr,write_ptr; pthread_mutex_t lock; vtty_t *next,**pprev; void *priv_data; u_long user_arg; /* FD Pool (for TCP connections) */ fd_pool_t fd_pool; /* Read notification */ void (*read_notifier)(vtty_t *); /* Old text for replay */ u_char replay_buffer[VTTY_BUFFER_SIZE]; u_int replay_ptr; }; #define VTTY_LOCK(tty) pthread_mutex_lock(&(tty)->lock); #define VTTY_UNLOCK(tty) pthread_mutex_unlock(&(tty)->lock); /* create a virtual tty */ vtty_t *vtty_create(vm_instance_t *vm,char *name,int type,int tcp_port, const vtty_serial_option_t *option); /* delete a virtual tty */ void vtty_delete(vtty_t *vtty); /* Store arbritary data in the FIFO buffer */ int vtty_store_data(vtty_t *vtty,char *data, int len); /* read a character from the buffer (-1 if the buffer is empty) */ int vtty_get_char(vtty_t *vtty); /* print a character to vtty */ void vtty_put_char(vtty_t *vtty, char ch); /* Put a buffer to vtty */ void vtty_put_buffer(vtty_t *vtty,char *buf,size_t len); /* Flush VTTY output */ void vtty_flush(vtty_t *vtty); /* returns TRUE if a character is available in buffer */ int vtty_is_char_avail(vtty_t *vtty); /* write CTRL+C to buffer */ int vtty_store_ctrlc(vtty_t *); /* Initialize the VTTY thread */ int vtty_init(void); void vtty_set_ctrlhandler(int n); void vtty_set_telnetmsg(int n); #endif dynamips-0.2.14/common/dev_wic_serial.c000066400000000000000000000103011241034141600200240ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * WIC-1T & WIC-2T devices. */ #include #include #include #include #include #include #include #include #include "crc.h" #include "utils.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_wic_serial.h" /* Debugging flags */ #define DEBUG_UNKNOWN 1 struct wic_serial_data { char *name; struct vdevice dev; vm_instance_t *vm; }; /* * dev_wic1t_access() */ static void *dev_wic1t_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct wic_serial_data *d = dev->priv_data; switch(offset) { case 0x04: if (op_type == MTS_READ) *data = 0xFF; break; case 0x08: if (op_type == MTS_READ) *data = 0xFF; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,d->name, "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name, "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* * dev_wic2t_access() */ static void *dev_wic2t_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct wic_serial_data *d = dev->priv_data; switch(offset) { /* Port 0 */ case 0x04: if (op_type == MTS_READ) *data = 0xFF; break; case 0x08: if (op_type == MTS_READ) *data = 0xFF; break; /* Port 1 */ case 0x14: if (op_type == MTS_READ) *data = 0xFF; break; case 0x18: if (op_type == MTS_READ) *data = 0xFF; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,d->name, "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name, "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } return NULL; } /* * dev_wic_serial_init() * * Generic WIC Serial initialization code. */ struct wic_serial_data * dev_wic_serial_init(vm_instance_t *vm,char *name,u_int model, m_uint64_t paddr,m_uint32_t len) { struct wic_serial_data *d; struct vdevice *dev; /* Allocate the private data structure for WIC */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"%s (WIC-SERIAL): out of memory\n",name); return NULL; } memset(d,0,sizeof(*d)); /* Create the device itself */ if (!(dev = dev_create(name))) { fprintf(stderr,"%s (WIC): unable to create device.\n",name); free(d); return NULL; } d->name = name; d->vm = vm; dev_init(&d->dev); d->dev.name = name; d->dev.priv_data = d; d->dev.phys_addr = paddr; d->dev.phys_len = len; switch(model) { case WIC_SERIAL_MODEL_1T: d->dev.handler = dev_wic1t_access; break; case WIC_SERIAL_MODEL_2T: d->dev.handler = dev_wic2t_access; break; default: fprintf(stderr,"%s (WIC-SERIAL): unknown model %u\n",name,model); free(d); return NULL; } /* Map this device to the VM */ vm_bind_device(vm,&d->dev); return(d); } /* Remove a WIC serial device */ void dev_wic_serial_remove(struct wic_serial_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(d->vm,&d->dev); /* Free the structure itself */ free(d); } } dynamips-0.2.14/common/dev_wic_serial.h000066400000000000000000000011521241034141600200350ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * WIC-1T & WIC-2T devices. */ #ifndef __DEV_WIC_SERIAL_H__ #define __DEV_WIC_SERIAL_H__ #include #include "utils.h" #include "vm.h" #include "cpu.h" #include "device.h" enum { WIC_SERIAL_MODEL_1T = 1, WIC_SERIAL_MODEL_2T, }; /* Create a WIC serial device */ struct wic_serial_data * dev_wic_serial_init(vm_instance_t *vm,char *name,u_int model, m_uint64_t paddr,m_uint32_t len); /* Remove a WIC serial device */ void dev_wic_serial_remove(struct wic_serial_data *d); #endif dynamips-0.2.14/common/dev_zero.c000066400000000000000000000030201241034141600166620ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (C) 2005,2006 Christophe Fillot. All rights reserved. * * Zeroed memory zone. */ #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" /* Zero zone private data */ struct zero_data { vm_obj_t vm_obj; struct vdevice dev; }; /* * dev_zero_access() */ void *dev_zero_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { if (op_type == MTS_READ) *data = 0; return NULL; } /* Shutdown a zeroed memory zone */ void dev_zero_shutdown(vm_instance_t *vm,struct zero_data *d) { if (d != NULL) { dev_remove(vm,&d->dev); free(d); } } /* Initialized a zeroed memory zone */ int dev_zero_init(vm_instance_t *vm,char *name,m_uint64_t paddr,m_uint32_t len) { struct zero_data *d; if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"ZERO: unable to create device.\n"); return(-1); } vm_object_init(&d->vm_obj); d->vm_obj.name = name; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_zero_shutdown; dev_init(&d->dev); d->dev.name = name; d->dev.phys_addr = paddr; d->dev.phys_len = len; d->dev.handler = dev_zero_access; /* Map this device to the VM */ vm_bind_device(vm,&d->dev); vm_object_add(vm,&d->vm_obj); return(0); } dynamips-0.2.14/common/device.c000066400000000000000000000271301241034141600163140ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #define DEBUG_DEV_ACCESS 0 /* Get device by ID */ struct vdevice *dev_get_by_id(vm_instance_t *vm,u_int dev_id) { if (!vm || (dev_id >= VM_DEVICE_MAX)) return NULL; return(vm->dev_array[dev_id]); } /* Get device by name */ struct vdevice *dev_get_by_name(vm_instance_t *vm,char *name) { struct vdevice *dev; if (!vm) return NULL; for(dev=vm->dev_list;dev;dev=dev->next) if (!strcmp(dev->name,name)) return dev; return NULL; } /* Device lookup by physical address */ struct vdevice *dev_lookup(vm_instance_t *vm,m_uint64_t phys_addr,int cached) { struct vdevice *dev; if (!vm) return NULL; for(dev=vm->dev_list;dev;dev=dev->next) { if (cached && !(dev->flags & VDEVICE_FLAG_CACHING)) continue; if ((phys_addr >= dev->phys_addr) && ((phys_addr - dev->phys_addr) < dev->phys_len)) return dev; } return NULL; } /* Find the next device after the specified address */ struct vdevice *dev_lookup_next(vm_instance_t *vm,m_uint64_t phys_addr, struct vdevice *dev_start,int cached) { struct vdevice *dev; if (!vm) return NULL; dev = (dev_start != NULL) ? dev_start : vm->dev_list; for(;dev;dev=dev->next) { if (cached && !(dev->flags & VDEVICE_FLAG_CACHING)) continue; if (dev->phys_addr > phys_addr) return dev; } return NULL; } /* Initialize a device */ void dev_init(struct vdevice *dev) { memset(dev,0,sizeof(*dev)); dev->fd = -1; } /* Allocate a device */ struct vdevice *dev_create(char *name) { struct vdevice *dev; if (!(dev = malloc(sizeof(*dev)))) { fprintf(stderr,"dev_create: insufficient memory to " "create device '%s'.\n",name); return NULL; } dev_init(dev); dev->name = name; return dev; } /* Remove a device */ void dev_remove(vm_instance_t *vm,struct vdevice *dev) { if (dev == NULL) return; vm_unbind_device(vm,dev); vm_log(vm,"DEVICE", "Removal of device %s, fd=%d, host_addr=0x%llx, flags=%d\n", dev->name,dev->fd,(m_uint64_t)dev->host_addr,dev->flags); if (dev->flags & VDEVICE_FLAG_REMAP) { dev_init(dev); return; } if (dev->flags & VDEVICE_FLAG_SPARSE) { dev_sparse_shutdown(dev); if (dev->flags & VDEVICE_FLAG_GHOST) { vm_ghost_image_release(dev->fd); dev_init(dev); return; } } if (dev->fd != -1) { /* Unmap memory mapped file */ if (dev->host_addr) { if (dev->flags & VDEVICE_FLAG_SYNC) { memzone_sync_all((void *)dev->host_addr,dev->phys_len); } vm_log(vm,"MMAP","unmapping of device '%s', " "fd=%d, host_addr=0x%llx, len=0x%x\n", dev->name,dev->fd,(m_uint64_t)dev->host_addr,dev->phys_len); memzone_unmap((void *)dev->host_addr,dev->phys_len); } if (dev->flags & VDEVICE_FLAG_SYNC) fsync(dev->fd); close(dev->fd); } else { /* Use of malloc'ed host memory: free it */ if (dev->host_addr) free((void *)dev->host_addr); } /* reinitialize the device to a clean state */ dev_init(dev); } /* Show properties of a device */ void dev_show(struct vdevice *dev) { if (!dev) return; printf(" %-18s: 0x%12.12llx (0x%8.8x)\n", dev->name,dev->phys_addr,dev->phys_len); } /* Show the device list */ void dev_show_list(vm_instance_t *vm) { struct vdevice *dev; printf("\nVM \"%s\" (%u) Device list:\n",vm->name,vm->instance_id); for(dev=vm->dev_list;dev;dev=dev->next) dev_show(dev); printf("\n"); } /* device access function */ void *dev_access(cpu_gen_t *cpu,u_int dev_id,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct vdevice *dev = cpu->vm->dev_array[dev_id]; #if DEBUG_DEV_ACCESS cpu_log(cpu,"DEV_ACCESS","%s: dev_id=%u, offset=0x%8.8x, op_size=%u, " "op_type=%u, data=%p\n",dev->name,dev_id,offset,op_size,op_type,data); #endif return(dev->handler(cpu,dev,offset,op_size,op_type,data)); } /* Synchronize memory for a memory-mapped (mmap) device */ int dev_sync(struct vdevice *dev) { if (!dev || !dev->host_addr) return(-1); return(memzone_sync((void *)dev->host_addr,dev->phys_len)); } /* Remap a device at specified physical address */ struct vdevice *dev_remap(char *name,struct vdevice *orig, m_uint64_t paddr,m_uint32_t len) { struct vdevice *dev; if (!(dev = dev_create(name))) return NULL; dev->phys_addr = paddr; dev->phys_len = len; dev->flags = orig->flags | VDEVICE_FLAG_REMAP; dev->fd = orig->fd; dev->host_addr = orig->host_addr; dev->handler = orig->handler; dev->sparse_map = orig->sparse_map; return dev; } /* Create a RAM device */ struct vdevice *dev_create_ram(vm_instance_t *vm,char *name, int sparse,char *filename, m_uint64_t paddr,m_uint32_t len) { struct vdevice *dev; u_char *ram_ptr; if (!(dev = dev_create(name))) return NULL; dev->phys_addr = paddr; dev->phys_len = len; dev->flags = VDEVICE_FLAG_CACHING; if (!sparse) { if (filename) { dev->fd = memzone_create_file(filename,dev->phys_len,&ram_ptr); if (dev->fd == -1) { perror("dev_create_ram: mmap"); free(dev); return NULL; } dev->host_addr = (m_iptr_t)ram_ptr; } else { dev->host_addr = (m_iptr_t)m_memalign(4096,dev->phys_len); } if (!dev->host_addr) { free(dev); return NULL; } } else { dev_sparse_init(dev); } vm_bind_device(vm,dev); return dev; } /* Create a ghosted RAM device */ struct vdevice * dev_create_ghost_ram(vm_instance_t *vm,char *name,int sparse,char *filename, m_uint64_t paddr,m_uint32_t len) { struct vdevice *dev; u_char *ram_ptr; if (!(dev = dev_create(name))) return NULL; dev->phys_addr = paddr; dev->phys_len = len; dev->flags = VDEVICE_FLAG_CACHING|VDEVICE_FLAG_GHOST; if (!sparse) { dev->fd = memzone_open_cow_file(filename,dev->phys_len,&ram_ptr); if (dev->fd == -1) { perror("dev_create_ghost_ram: mmap"); free(dev); return NULL; } if (!(dev->host_addr = (m_iptr_t)ram_ptr)) { free(dev); return NULL; } } else { if (vm_ghost_image_get(filename,&ram_ptr,&dev->fd) == -1) { free(dev); return NULL; } dev->host_addr = (m_iptr_t)ram_ptr; dev_sparse_init(dev); } vm_bind_device(vm,dev); return dev; } /* Create a memory alias */ struct vdevice *dev_create_ram_alias(vm_instance_t *vm,char *name,char *orig, m_uint64_t paddr,m_uint32_t len) { struct vdevice *dev,*orig_dev; /* try to locate the device */ if (!(orig_dev = dev_get_by_name(vm,orig))) { fprintf(stderr,"VM%u: dev_create_ram_alias: unknown device '%s'.\n", vm->instance_id,orig); return NULL; } if (!(dev = dev_remap(name,orig_dev,paddr,len))) { fprintf(stderr,"VM%u: dev_create_ram_alias: unable to create " "new device %s.\n",vm->instance_id,name); return NULL; } vm_bind_device(vm,dev); return dev; } /* Initialize a sparse device */ int dev_sparse_init(struct vdevice *dev) { u_int i,nr_pages; size_t len; /* create the sparse mapping */ nr_pages = normalize_size(dev->phys_len,VM_PAGE_SIZE,VM_PAGE_SHIFT); len = nr_pages * sizeof(m_iptr_t); if (!(dev->sparse_map = malloc(len))) return(-1); if (!dev->host_addr) { memset(dev->sparse_map,0,len); } else { for(i=0;isparse_map[i] = dev->host_addr + (i << VM_PAGE_SHIFT); } dev->flags |= VDEVICE_FLAG_SPARSE; return(0); } /* Shutdown sparse device structures */ int dev_sparse_shutdown(struct vdevice *dev) { if (!(dev->flags & VDEVICE_FLAG_SPARSE)) return(-1); free(dev->sparse_map); dev->sparse_map = NULL; return(0); } /* Show info about a sparse device */ int dev_sparse_show_info(struct vdevice *dev) { u_int i,nr_pages,dirty_pages; printf("Sparse information for device '%s':\n",dev->name); if (!(dev->flags & VDEVICE_FLAG_SPARSE)) { printf("This is not a sparse device.\n"); return(-1); } if (!dev->sparse_map) { printf("No sparse map.\n"); return(-1); } nr_pages = normalize_size(dev->phys_len,VM_PAGE_SIZE,VM_PAGE_SHIFT); dirty_pages = 0; for(i=0;isparse_map[i] & VDEVICE_PTE_DIRTY) dirty_pages++; printf("%u dirty pages on a total of %u pages.\n",dirty_pages,nr_pages); return(0); } /* Get an host address for a sparse device */ m_iptr_t dev_sparse_get_host_addr(vm_instance_t *vm,struct vdevice *dev, m_uint64_t paddr,u_int op_type,int *cow) { m_iptr_t ptr,ptr_new; u_int offset; offset = (paddr - dev->phys_addr) >> VM_PAGE_SHIFT; ptr = dev->sparse_map[offset]; *cow = 0; /* * If the device is not in COW mode, allocate a host page if the physical * page is requested for the first time. */ if (!dev->host_addr) { if (!(ptr & VDEVICE_PTE_DIRTY)) { ptr = (m_iptr_t)vm_alloc_host_page(vm); assert(ptr); dev->sparse_map[offset] = ptr | VDEVICE_PTE_DIRTY; return(ptr); } return(ptr & VM_PAGE_MASK); } /* * We have a "ghost" base. We apply the copy-on-write (COW) mechanism * ourselves. */ if (ptr & VDEVICE_PTE_DIRTY) return(ptr & VM_PAGE_MASK); if (op_type == MTS_READ) { *cow = 1; return(ptr & VM_PAGE_MASK); } /* Write attempt on a "ghost" page. Duplicate it */ ptr_new = (m_iptr_t)vm_alloc_host_page(vm); assert(ptr_new); memcpy((void *)ptr_new,(void *)(ptr & VM_PAGE_MASK),VM_PAGE_SIZE); dev->sparse_map[offset] = ptr_new | VDEVICE_PTE_DIRTY; return(ptr_new); } /* Get virtual address space used on host for the specified device */ size_t dev_get_vspace_size(struct vdevice *dev) { /* if the device is simply remapped, don't count it */ if (dev->flags & VDEVICE_FLAG_REMAP) return(0); if (dev->host_addr || (dev->flags & VDEVICE_FLAG_SPARSE)) return(dev->phys_len >> 10); return(0); } /* dummy console handler */ static void *dummy_console_handler(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size, u_int op_type,m_uint64_t *data) { switch(offset) { case 0x40c: if (op_type == MTS_READ) *data = 0x04; /* tx ready */ break; case 0x41c: if (op_type == MTS_WRITE) { printf("%c",(u_char)(*data & 0xff)); fflush(stdout); } break; } return NULL; } /* Create a dummy console */ int dev_create_dummy_console(vm_instance_t *vm) { struct vdevice *dev; if (!(dev = dev_create("dummy_console"))) return(-1); dev->phys_addr = 0x1e840000; /* 0x1f000000; */ dev->phys_len = 4096; dev->handler = dummy_console_handler; vm_bind_device(vm,dev); return(0); } dynamips-0.2.14/common/device.h000066400000000000000000000224051241034141600163210ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __DEVICE_H__ #define __DEVICE_H__ #include #include "utils.h" #include "cpu.h" #include "net_io.h" #include "vm.h" /* Device Flags */ #define VDEVICE_FLAG_NO_MTS_MMAP 0x01 /* Prevent MMAPed access by MTS */ #define VDEVICE_FLAG_CACHING 0x02 /* Device does support caching */ #define VDEVICE_FLAG_REMAP 0x04 /* Physical address remapping */ #define VDEVICE_FLAG_SYNC 0x08 /* Forced sync */ #define VDEVICE_FLAG_SPARSE 0x10 /* Sparse device */ #define VDEVICE_FLAG_GHOST 0x20 /* Ghost device */ #define VDEVICE_PTE_DIRTY 0x01 typedef void *(*dev_handler_t)(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data); /* Virtual Device */ struct vdevice { char *name; u_int id; m_uint64_t phys_addr; m_uint32_t phys_len; m_iptr_t host_addr; void *priv_data; int flags; int fd; dev_handler_t handler; m_iptr_t *sparse_map; struct vdevice *next,**pprev; }; /* PCI part */ #include "pci_dev.h" /* device access function */ #ifdef MAC64HACK static void *__dev_access_fast(cpu_gen_t *cpu,u_int dev_id,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct vdevice *dev = cpu->vm->dev_array[dev_id]; if (unlikely(!dev)) { cpu_log(cpu,"dev_access_fast","null handler (dev_id=%u,offset=0x%x)\n", dev_id,offset); return NULL; } #if DEBUG_DEV_PERF_CNT cpu->dev_access_counter++; #endif return(dev->handler(cpu,dev,offset,op_size,op_type,data)); } static forced_inline void *dev_access_fast(cpu_gen_t *cpu,u_int dev_id,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { asm("sub $8, %rsp"); void* ret = __dev_access_fast(cpu, dev_id, offset, op_size, op_type, data); asm("add $8, %rsp"); return ret; } #else static forced_inline void *dev_access_fast(cpu_gen_t *cpu,u_int dev_id,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct vdevice *dev = cpu->vm->dev_array[dev_id]; if (unlikely(!dev)) { cpu_log(cpu,"dev_access_fast","null handler (dev_id=%u,offset=0x%x)\n", dev_id,offset); return NULL; } #if DEBUG_DEV_PERF_CNT cpu->dev_access_counter++; #endif return(dev->handler(cpu,dev,offset,op_size,op_type,data)); } #endif /* Get device by ID */ struct vdevice *dev_get_by_id(vm_instance_t *vm,u_int dev_id); /* Get device by name */ struct vdevice *dev_get_by_name(vm_instance_t *vm,char *name); /* Device lookup by physical address */ struct vdevice *dev_lookup(vm_instance_t *vm,m_uint64_t phys_addr,int cached); /* Find the next device after the specified address */ struct vdevice *dev_lookup_next(vm_instance_t *vm,m_uint64_t phys_addr, struct vdevice *dev_start,int cached); /* Initialize a device */ void dev_init(struct vdevice *dev); /* Allocate a device */ struct vdevice *dev_create(char *name); /* Remove a device */ void dev_remove(vm_instance_t *vm,struct vdevice *dev); /* Show properties of a device */ void dev_show(struct vdevice *dev); /* Show the device list */ void dev_show_list(vm_instance_t *vm); /* device access function */ void *dev_access(cpu_gen_t *cpu,u_int dev_id,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data); /* Synchronize memory for a memory-mapped (mmap) device */ int dev_sync(struct vdevice *dev); /* Remap a device at specified physical address */ struct vdevice *dev_remap(char *name,struct vdevice *orig, m_uint64_t paddr,m_uint32_t len); /* Create a RAM device */ struct vdevice *dev_create_ram(vm_instance_t *vm,char *name, int sparse,char *filename, m_uint64_t paddr,m_uint32_t len); /* Create a ghosted RAM device */ struct vdevice * dev_create_ghost_ram(vm_instance_t *vm,char *name,int sparse,char *filename, m_uint64_t paddr,m_uint32_t len); /* Create a memory alias */ struct vdevice *dev_create_ram_alias(vm_instance_t *vm,char *name,char *orig, m_uint64_t paddr,m_uint32_t len); /* Initialize a sparse device */ int dev_sparse_init(struct vdevice *dev); /* Shutdown sparse device structures */ int dev_sparse_shutdown(struct vdevice *dev); /* Get an host address for a sparse device */ m_iptr_t dev_sparse_get_host_addr(vm_instance_t *vm,struct vdevice *dev, m_uint64_t paddr,u_int op_type,int *cow); /* Get virtual address space used on host for the specified device */ size_t dev_get_vspace_size(struct vdevice *dev); /* Create a dummy console */ int dev_create_dummy_console(vm_instance_t *vm); /* Initialized a zeroed memory zone */ int dev_zero_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len); /* Initialized a byte-swap device */ int dev_bswap_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len,m_uint64_t remap_addr); /* Initialize a RAM zone */ int dev_ram_init(vm_instance_t *vm,char *name,int use_mmap,int delete_file, char *alternate_name,int sparse, m_uint64_t paddr,m_uint32_t len); /* Initialize a ghosted RAM zone */ int dev_ram_ghost_init(vm_instance_t *vm,char *name,int sparse,char *filename, m_uint64_t paddr,m_uint32_t len); /* Create the NVRAM device */ int dev_nvram_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len, u_int *conf_reg); /* Create a 8 Mb bootflash */ int dev_bootflash_init(vm_instance_t *vm,char *name,char *model, m_uint64_t paddr); /* Create a Flash device */ vm_obj_t *dev_flash_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len); /* Copy data directly to a flash device */ int dev_flash_copy_data(vm_obj_t *obj,m_uint32_t offset, u_char *ptr,ssize_t len); /* dev_dec21050_init() */ int dev_dec21050_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus); /* dev_dec21052_init() */ int dev_dec21052_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus); /* dev_dec21150_init() */ int dev_dec21150_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus); /* dev_dec21152_init() */ int dev_dec21152_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus); /* dev_dec21154_init() */ int dev_dec21154_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus); /* dev_pericom_init() */ int dev_pericom_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus); /* dev_ti2050b_init() */ int dev_ti2050b_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus); /* Create an AP1011 Sturgeon HyperTransport-PCI Bridge */ int dev_ap1011_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus); /* dev_plx6520cb_init() */ int dev_plx6520cb_init(struct pci_bus *pci_bus,int pci_device, struct pci_bus *sec_bus); /* dev_clpd6729_init() */ int dev_clpd6729_init(vm_instance_t *vm, struct pci_bus *pci_bus,int pci_device, struct pci_io_data *pci_io_data, m_uint32_t io_start,m_uint32_t io_end); /* Create a NS16552 device */ int dev_ns16552_init(vm_instance_t *vm,m_uint64_t paddr,m_uint32_t len, u_int reg_div,u_int irq,vtty_t *vtty_A,vtty_t *vtty_B); /* Initialize an SRAM device */ int dev_c7200_sram_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len, struct pci_bus *pci_bus,int pci_device); /* Initialize a PCMCIA disk */ vm_obj_t *dev_pcmcia_disk_init(vm_instance_t *vm,char *name, m_uint64_t paddr,m_uint32_t len, u_int disk_size,int mode); /* Get the device associated with a PCMCIA disk object */ struct vdevice *dev_pcmcia_disk_get_device(vm_obj_t *obj); /* Create SB-1 system control devices */ int dev_sb1_init(vm_instance_t *vm); /* Create SB-1 I/O devices */ int dev_sb1_io_init(vm_instance_t *vm,u_int duart_irq); /* Create the SB-1 PCI bus configuration zone */ int dev_sb1_pci_init(vm_instance_t *vm,char *name,m_uint64_t paddr); /* dev_sb1_duart_init() */ int dev_sb1_duart_init(vm_instance_t *vm,m_uint64_t paddr,m_uint32_t len); /* remote control device */ int dev_remote_control_init(vm_instance_t *vm,m_uint64_t paddr,m_uint32_t len); int generic_nvram_extract_config(vm_instance_t *vm, char *dev_name, size_t nvram_offset, size_t nvram_size, m_uint32_t addr, u_int format, u_char **startup_config, size_t *startup_len, u_char **private_config, size_t *private_len); int generic_nvram_push_config(vm_instance_t *vm, char *dev_name, size_t file_size, size_t nvram_offset, size_t nvram_size, m_uint32_t addr, u_int format, u_char *startup_config, size_t startup_len, u_char *private_config, size_t private_len); #endif dynamips-0.2.14/common/dynamips.c000066400000000000000000000660041241034141600167040ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * Patched by Jeremy Grossmann for the GNS3 project (www.gns3.net) * * Many thanks to Nicolas Szalay for his patch * for the command line parsing and virtual machine * settings (RAM, ROM, NVRAM, ...) */ #include #include #include #include #include #include #include #include #include #include #include #include "dynamips.h" #include "gen_uuid.h" #include "cpu.h" #include "vm.h" #ifdef USE_UNSTABLE #include "tcb.h" #endif #include "mips64_exec.h" #include "mips64_jit.h" #include "ppc32_exec.h" #include "ppc32_jit.h" #include "dev_c7200.h" #include "dev_c3600.h" #include "dev_c2691.h" #include "dev_c3725.h" #include "dev_c3745.h" #include "dev_c2600.h" #include "dev_c1700.h" #include "dev_c6msfc1.h" #include "dev_c6sup1.h" #include "ppc32_vmtest.h" #include "dev_vtty.h" #include "ptask.h" #include "timer.h" #include "plugin.h" #include "registry.h" #include "hypervisor.h" #include "net_io.h" #include "net_io_bridge.h" #include "net_io_filter.h" #include "crc.h" #include "atm.h" #include "atm_bridge.h" #include "frame_relay.h" #include "eth_switch.h" #ifdef GEN_ETH #include "gen_eth.h" #endif #ifdef PROFILE #include "profiler.h" #endif /* Default name for logfile */ #define LOGFILE_DEFAULT_NAME "dynamips_log.txt" /* Operating system name */ const char *os_name = STRINGIFY(OSNAME); /* Software version */ const char *sw_version = DYNAMIPS_VERSION"-"JIT_ARCH; /* Software version tag */ const char *sw_version_tag = "2014092320"; /* Hypervisor */ int hypervisor_mode = 0; int hypervisor_tcp_port = 0; char *hypervisor_ip_address = NULL; /* Log file */ char *log_file_name = NULL; FILE *log_file = NULL; /* VM flags */ volatile int vm_save_state = 0; /* Default platform */ static char *default_platform = "7200"; /* Binding address (NULL means any or 0.0.0.0) */ char *binding_addr = NULL; /* Generic signal handler */ void signal_gen_handler(int sig) { switch(sig) { case SIGHUP: /* For future use */ break; case SIGQUIT: /* save VM context */ vm_save_state = TRUE; break; /* Handle SIGPIPE by ignoring it */ case SIGPIPE: fprintf(stderr,"Error: unwanted SIGPIPE.\n"); break; case SIGINT: /* CTRL+C has been pressed */ if (hypervisor_mode) hypervisor_stopsig(); else { /* In theory, this shouldn't happen thanks to VTTY settings */ vm_instance_t *vm; if ((vm = vm_acquire("default")) != NULL) { /* Only forward ctrl-c if user has requested local terminal */ if (vm->vtty_con_type == VTTY_TYPE_TERM) { vtty_store_ctrlc(vm->vtty_con); } else { vm_stop(vm); } vm_release(vm); } else { fprintf(stderr,"Error: Cannot acquire instance handle.\n"); } } break; default: fprintf(stderr,"Unhandled signal %d\n",sig); } } /* Setups signals */ static void setup_signals(void) { struct sigaction act; memset(&act,0,sizeof(act)); act.sa_handler = signal_gen_handler; act.sa_flags = SA_RESTART; sigaction(SIGHUP,&act,NULL); sigaction(SIGQUIT,&act,NULL); sigaction(SIGINT,&act,NULL); sigaction(SIGPIPE,&act,NULL); } /* Create general log file */ static void create_log_file(void) { /* Set the default value of the log file name */ if (!log_file_name) { if (!(log_file_name = strdup(LOGFILE_DEFAULT_NAME))) { fprintf(stderr,"Unable to set log file name.\n"); exit(EXIT_FAILURE); } } if (!(log_file = fopen(log_file_name,"w"))) { fprintf(stderr,"Unable to create log file (%s).\n",strerror(errno)); exit(EXIT_FAILURE); } } /* Close general log file */ static void close_log_file(void) { if (log_file) fclose(log_file); free(log_file_name); log_file = NULL; log_file_name = NULL; } /* Display the command line use */ static void show_usage(vm_instance_t *vm,int argc,char *argv[]) { printf("Usage: %s [options] \n\n",argv[0]); printf("Available options:\n" " -H [:] : Run in hypervisor mode\n\n" " -P : Platform to emulate (7200, 3600, " "2691, 3725, 3745, 2600 or 1700) " "(default: 7200)\n\n" " -l : Set logging file (default is %s)\n" " -j : Disable the JIT compiler, very slow\n" " --idle-pc : Set the idle PC (default: disabled)\n" " --timer-itv : Timer IRQ interval check (default: %u)\n" "\n" " -i : Set instance ID\n" " -r : Set the virtual RAM size (default: %u Mb)\n" " -o : Set the virtual ROM size (default: %u Mb)\n" " -n : Set the NVRAM size (default: %d Kb)\n" " -c : Set the configuration register " "(default: 0x%04x)\n" " -m : Set the MAC address of the chassis\n" " (default: automatically generated)\n" " -C, --startup-config : Import IOS configuration file into NVRAM\n" " --private-config : Import IOS configuration file into NVRAM\n" " -X : Do not use a file to simulate RAM (faster)\n" " -G : Use a ghost file to simulate RAM\n" " -g : Generate a ghost RAM file\n" " --sparse-mem : Use sparse memory\n" " -R : Load an alternate ROM (default: embedded)\n" " -k : Set the clock divisor (default: %d)\n" "\n" " -T : Console is on TCP \n" " -U : Console in on serial interface \n" " (default is on the terminal)\n" "\n" " -A : AUX is on TCP \n" " -B : AUX is on serial interface \n" " (default is no AUX port)\n" "\n" " --disk0 : Set PCMCIA ATA disk0: size " "(default: %u Mb)\n" " --disk1 : Set PCMCIA ATA disk1: size " "(default: %u Mb)\n" "\n" " --noctrl : Disable ctrl+] monitor console\n" " --notelnetmsg : Disable message when using tcp console/aux\n" " --filepid filename : Store dynamips pid in a file\n" "\n", LOGFILE_DEFAULT_NAME,VM_TIMER_IRQ_CHECK_ITV, vm->ram_size,vm->rom_size,vm->nvram_size,vm->conf_reg_setup, vm->clock_divisor,vm->pcmcia_disk_size[0],vm->pcmcia_disk_size[1]); if (vm->platform->cli_show_options != NULL) vm->platform->cli_show_options(vm); printf("\n" #if DEBUG_SYM_TREE " -S : Load a symbol file\n" #endif " -a : Virtual ATM switch configuration file\n" " -f : Virtual Frame-Relay switch configuration " "file\n" " -E : Virtual Ethernet switch configuration file\n" " -b : Virtual bridge configuration file\n" " -e : Show network device list of the " "host machine\n" "\n"); printf(" format:\n" " \"device{:baudrate{:databits{:parity{:stopbits{:hwflow}}}}}}\"\n" "\n"); switch(vm->slots_type) { case CISCO_CARD_TYPE_PA: printf(" format:\n" " \"slot:sub_slot:pa_driver\"\n" "\n"); printf(" format:\n" " \"slot:port:netio_type{:netio_parameters}\"\n" "\n"); break; case CISCO_CARD_TYPE_NM: printf(" format:\n" " \"slot:sub_slot:nm_driver\"\n" "\n"); printf(" format:\n" " \"slot:port:netio_type{:netio_parameters}\"\n" "\n"); break; case CISCO_CARD_TYPE_WIC: printf(" format:\n" " \"slot:wic_driver\"\n" "\n"); printf(" format:\n" " \"slot:port:netio_type{:netio_parameters}\"\n" "\n"); break; } if (vm->platform->show_spec_drivers != NULL) vm->platform->show_spec_drivers(); /* Show possible slot drivers */ vm_slot_show_drivers(vm); /* Show the possible NETIO types */ netio_show_types(); } /* Find an option in the command line */ static char *cli_find_option(int argc,char *argv[],char *opt) { int i; for(i=1;iname,instance_id))) goto exit_failure; opterr = 0; vtty_set_ctrlhandler(1); /* By default allow ctrl ] */ vtty_set_telnetmsg(1); /* By default allow telnet message */ while((option = getopt_long(argc,argv,options_list, cmd_line_lopts,NULL)) != -1) { switch(option) { /* Instance ID (already managed) */ case 'i': break; /* Platform (already managed) */ case 'P': break; /* RAM size */ case 'r': vm->ram_size = strtol(optarg, NULL, 10); printf("Virtual RAM size set to %d MB.\n",vm->ram_size); break; /* ROM size */ case 'o': vm->rom_size = strtol(optarg, NULL, 10); printf("Virtual ROM size set to %d MB.\n",vm->rom_size); break; /* NVRAM size */ case 'n': vm->nvram_size = strtol(optarg, NULL, 10); printf("NVRAM size set to %d KB.\n",vm->nvram_size); break; /* PCMCIA disk0 size */ case OPT_DISK0_SIZE: vm->pcmcia_disk_size[0] = atoi(optarg); printf("PCMCIA ATA disk0 size set to %u MB.\n", vm->pcmcia_disk_size[0]); break; /* PCMCIA disk1 size */ case OPT_DISK1_SIZE: vm->pcmcia_disk_size[1] = atoi(optarg); printf("PCMCIA ATA disk1 size set to %u MB.\n", vm->pcmcia_disk_size[1]); break; case OPT_NOCTRL: vtty_set_ctrlhandler(0); /* Ignore ctrl ] */ printf("Block ctrl+] access to monitor console.\n"); break; /* Config Register */ case 'c': vm->conf_reg_setup = strtol(optarg, NULL, 0); printf("Config. Register set to 0x%x.\n",vm->conf_reg_setup); break; /* IOS startup configuration file */ case 'C': case OPT_STARTUP_CONFIG_FILE: vm_ios_set_config(vm,optarg,vm->ios_private_config); break; /* IOS private configuration file */ case OPT_PRIVATE_CONFIG_FILE: vm_ios_set_config(vm,vm->ios_startup_config,optarg); break; /* Use physical memory to emulate RAM (no-mapped file) */ case 'X': vm->ram_mmap = 0; break; /* Use a ghost file to simulate RAM */ case 'G': free(vm->ghost_ram_filename); vm->ghost_ram_filename = strdup(optarg); vm->ghost_status = VM_GHOST_RAM_USE; break; /* Generate a ghost RAM image */ case 'g': free(vm->ghost_ram_filename); vm->ghost_ram_filename = strdup(optarg); vm->ghost_status = VM_GHOST_RAM_GENERATE; break; /* Use sparse memory */ case OPT_SPARSE_MEM: vm->sparse_mem = TRUE; break; /* Alternate ROM */ case 'R': free(vm->rom_filename); vm->rom_filename = strdup(optarg); break; case OPT_NOTELMSG: vtty_set_telnetmsg(0); /* disable telnet greeting */ printf("Prevent telnet message on AUX/CONSOLE connecte.\n"); break; case OPT_FILEPID: if ((pid_file = fopen(optarg,"w"))) { fprintf(pid_file,"%d",getpid()); fclose(pid_file); } else { printf("Unable to save to %s.\n",optarg); } break; /* Idle PC */ case OPT_IDLE_PC: vm->idle_pc = strtoull(optarg,NULL,0); printf("Idle PC set to 0x%llx.\n",vm->idle_pc); break; /* Timer IRQ check interval */ case OPT_TIMER_ITV: vm->timer_irq_check_itv = atoi(optarg); break; /* Clock divisor */ case 'k': vm->clock_divisor = atoi(optarg); if (!vm->clock_divisor) { fprintf(stderr,"Invalid Clock Divisor specified!\n"); goto exit_failure; } printf("Using a clock divisor of %d.\n",vm->clock_divisor); break; /* Disable JIT */ case 'j': vm->jit_use = FALSE; break; /* VM debug level */ case OPT_VM_DEBUG: vm->debug_level = atoi(optarg); break; /* Log file */ case 'l': if (!(log_file_name = realloc(log_file_name, strlen(optarg)+1))) { fprintf(stderr,"Unable to set log file name.\n"); goto exit_failure; } strcpy(log_file_name, optarg); printf("Log file: writing to %s\n",log_file_name); break; #if DEBUG_SYM_TREE /* Symbol file */ case 'S': vm->sym_filename = strdup(optarg); break; #endif /* TCP server for Console Port */ case 'T': vm->vtty_con_type = VTTY_TYPE_TCP; vm->vtty_con_tcp_port = atoi(optarg); break; /* Serial interface for Console port */ case 'U': vm->vtty_con_type = VTTY_TYPE_SERIAL; if (vtty_parse_serial_option(&vm->vtty_con_serial_option,optarg)) { fprintf(stderr, "Invalid Console serial interface descriptor!\n"); goto exit_failure; } break; /* TCP server for AUX Port */ case 'A': vm->vtty_aux_type = VTTY_TYPE_TCP; vm->vtty_aux_tcp_port = atoi(optarg); break; /* Serial interface for AUX port */ case 'B': vm->vtty_aux_type = VTTY_TYPE_SERIAL; if (vtty_parse_serial_option(&vm->vtty_aux_serial_option,optarg)) { fprintf(stderr,"Invalid AUX serial interface descriptor!\n"); goto exit_failure; } break; /* Port settings */ case 'p': vm_slot_cmd_create(vm,optarg); break; /* NIO settings */ case 's': vm_slot_cmd_add_nio(vm,optarg); break; /* Virtual ATM switch */ case 'a': if (atmsw_start(optarg) == -1) goto exit_failure; break; /* Virtual ATM bridge */ case 'M': if (atm_bridge_start(optarg) == -1) goto exit_failure; break; /* Virtual Frame-Relay switch */ case 'f': if (frsw_start(optarg) == -1) goto exit_failure; break; /* Virtual Ethernet switch */ case 'E': if (ethsw_start(optarg) == -1) goto exit_failure; break; /* Virtual bridge */ case 'b': if (netio_bridge_start(optarg) == -1) goto exit_failure; break; #ifdef GEN_ETH /* Ethernet device list */ case 'e': gen_eth_show_dev_list(); goto exit_success; #endif /* Load plugin (already handled) */ case 'L': break; /* Oops ! */ case '?': show_usage(vm,argc,argv); goto exit_failure; /* Parse options specific to the platform */ default: if (vm->platform->cli_parse_options != NULL) /* If you get an option wrong, say which option is was */ /* Wont be pretty for a long option, but it will at least help */ if (vm->platform->cli_parse_options(vm,option) == -1) { printf("Flag not recognised: -%c\n",(char)option); goto exit_failure; } } } /* Last argument, this is the IOS filename */ if (optind == (argc - 1)) { /* setting IOS image file */ vm_ios_set_image(vm,argv[optind]); printf("IOS image file: %s\n\n",vm->ios_image); } else { /* IOS missing */ fprintf(stderr,"Please specify an IOS image filename\n"); show_usage(vm,argc,argv); goto exit_failure; } vm_release(vm); return(0); exit_success: if (vm) { vm_release(vm); vm_delete_instance("default"); } exit(EXIT_SUCCESS); return(-1); exit_failure: if (vm) { vm_release(vm); vm_delete_instance("default"); } exit(EXIT_FAILURE); return(-1); } /* * Run in hypervisor mode with a config file if the "-H" option * is present in command line. */ static int run_hypervisor(int argc,char *argv[]) { char *options_list = "H:l:hN:L:"; int i,option; char *index; size_t len; FILE *pid_file = NULL; // For saving the pid if requested vtty_set_ctrlhandler(1); /* By default allow ctrl ] */ vtty_set_telnetmsg(1); /* By default allow telnet message */ for(i=1;i 0) || (DEBUG_BLOCK_PERF_CNT > 0) { m_uint32_t counter,prev = 0,delta; while(vm->status == VM_STATUS_RUNNING) { counter = cpu_get_perf_counter(vm->boot_cpu); delta = counter - prev; prev = counter; printf("delta = %u\n",delta); sleep(1); } } #else /* Start instance monitoring */ vm_monitor(vm); #endif /* Free resources used by instance */ vm_release(vm); } else { hypervisor_tcp_server(hypervisor_ip_address,hypervisor_tcp_port); } dynamips_reset(); close_log_file(); return(0); } dynamips-0.2.14/common/dynamips.h000066400000000000000000000046301241034141600167060ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * Patched by Jeremy Grossmann for the GNS3 project (www.gns3.net) */ #ifndef __DYNAMIPS_H__ #define __DYNAMIPS_H__ #include #include "utils.h" /* Debugging flags */ #define DEBUG_BLOCK_SCAN 0 #define DEBUG_BLOCK_COMPILE 0 #define DEBUG_BLOCK_PATCH 0 #define DEBUG_BLOCK_CHUNK 0 #define DEBUG_BLOCK_TIMESTAMP 0 /* block timestamping (little overhead) */ #define DEBUG_SYM_TREE 0 /* use symbol tree (slow) */ #define DEBUG_MTS_MAP_DEV 0 #define DEBUG_MTS_MAP_VIRT 1 #define DEBUG_MTS_ACC_U 1 /* undefined memory */ #define DEBUG_MTS_ACC_T 1 /* tlb exception */ #define DEBUG_MTS_ACC_AE 1 /* address error exception */ #define DEBUG_MTS_DEV 0 /* debugging for device access */ #define DEBUG_MTS_STATS 1 /* MTS cache performance */ #define DEBUG_INSN_PERF_CNT 0 /* Instruction performance counter */ #define DEBUG_BLOCK_PERF_CNT 0 /* Block performance counter */ #define DEBUG_DEV_PERF_CNT 1 /* Device performance counter */ #define DEBUG_TLB_ACTIVITY 0 #define DEBUG_SYSCALL 0 #define DEBUG_CACHE 0 #define DEBUG_JR0 0 /* Debug register jumps to 0 */ /* Feature flags */ #define MEMLOG_ENABLE 0 /* Memlogger (fast memop must be off) */ #define BREAKPOINT_ENABLE 1 /* Virtual Breakpoints */ #define NJM_STATS_ENABLE 1 /* Non-JIT mode stats (little overhead) */ /* Symbol */ struct symbol { m_uint64_t addr; char name[0]; }; /* ROM identification tag */ #define ROM_ID 0x1e94b3df /* Global log file */ extern FILE *log_file; /* Operating system name */ extern const char *os_name; /* Software version */ extern const char *sw_version; /* Software version specific tag */ extern const char *sw_version_tag; /* Global binding address */ extern char *binding_addr; /* Command Line long options */ #define OPT_DISK0_SIZE 0x100 #define OPT_DISK1_SIZE 0x101 #define OPT_EXEC_AREA 0x102 #define OPT_IDLE_PC 0x103 #define OPT_TIMER_ITV 0x104 #define OPT_VM_DEBUG 0x105 #define OPT_IOMEM_SIZE 0x106 #define OPT_SPARSE_MEM 0x107 #define OPT_NOCTRL 0x120 #define OPT_NOTELMSG 0x121 #define OPT_FILEPID 0x122 #define OPT_STARTUP_CONFIG_FILE 0x140 #define OPT_PRIVATE_CONFIG_FILE 0x141 /* Delete all objects */ void dynamips_reset(void); #endif dynamips-0.2.14/common/dynamips_common.h000066400000000000000000000072541241034141600202630ustar00rootroot00000000000000/** @file * @brief Common includes, types, defines and platform specific stuff. * * This header should be included before other headers. * This header should not contain code. */ /* * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * Copyright (c) 2014 Flávio J. Saraiva */ #pragma once #ifndef __DYNAMIPS_COMMON_H__ #define __DYNAMIPS_COMMON_H__ /* Config file - not used at the moment */ #if HAVE_DYNAMIPS_CONFIG_H #include "dynamips_config.h" #endif /* By default, Cygwin supports only 64 FDs with select()! */ #if defined(__CYGWIN__) && !defined(FD_SETSIZE) #define FD_SETSIZE 1024 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include /* True/False definitions */ #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif /* Endianness */ #define ARCH_BIG_ENDIAN 0x4321 #define ARCH_LITTLE_ENDIAN 0x1234 #if defined(PPC) || defined(__powerpc__) || defined(__ppc__) #define ARCH_BYTE_ORDER ARCH_BIG_ENDIAN #elif defined(__sparc) || defined(__sparc__) #define ARCH_BYTE_ORDER ARCH_BIG_ENDIAN #elif defined(__alpha) || defined(__alpha__) #define ARCH_BYTE_ORDER ARCH_LITTLE_ENDIAN #elif defined(__i386) || defined(__i386__) || defined(i386) #define ARCH_BYTE_ORDER ARCH_LITTLE_ENDIAN #define ARCH_REGPARM_SUPPORTED 1 #elif defined(__x86_64__) #define ARCH_BYTE_ORDER ARCH_LITTLE_ENDIAN #elif defined(__ia64__) #define ARCH_BYTE_ORDER ARCH_LITTLE_ENDIAN #elif defined(__arm__) || defined (__aarch64__) #define ARCH_BYTE_ORDER ARCH_LITTLE_ENDIAN #endif #ifndef ARCH_BYTE_ORDER #error Please define your architecture! #endif /* Useful attributes for functions */ #ifdef ARCH_REGPARM_SUPPORTED #define asmlinkage __attribute__((regparm(0))) #define fastcall __attribute__((regparm(3))) #else #define asmlinkage #define fastcall #endif #ifndef _unused /* Function that is never used */ #define _unused __attribute__((unused)) #endif #ifndef _maybe_used /* Function that is referenced from excluded code (commented out or depends on preprocessor) */ #define _maybe_used __attribute__((unused)) #endif #ifndef UNUSED /* Variable that is never used (name is changed to get an error on use) */ #define UNUSED(x) UNUSED_ ## x __attribute__((unused)) #endif #if __GNUC__ > 2 #define forced_inline inline __attribute__((always_inline)) #define no_inline __attribute__ ((noinline)) #else #define forced_inline inline #define no_inline #endif #if __GNUC__ > 2 /* http://kerneltrap.org/node/4705 */ #define likely(x) __builtin_expect(!!(x),1) #define unlikely(x) __builtin_expect((x),0) #else #define likely(x) (x) #define unlikely(x) (x) #endif #ifndef _not_aligned #define _not_aligned __attribute__ ((aligned (1))) #endif /* Common types */ typedef unsigned char m_uint8_t; typedef signed char m_int8_t; typedef unsigned short m_uint16_t; typedef signed short m_int16_t; typedef unsigned int m_uint32_t; typedef signed int m_int32_t; typedef unsigned long long m_uint64_t; typedef signed long long m_int64_t; typedef unsigned long m_iptr_t; typedef m_uint64_t m_tmcnt_t; /* Max and min macro */ #define m_max(a,b) (((a) > (b)) ? (a) : (b)) #define m_min(a,b) (((a) < (b)) ? (a) : (b)) /* A simple macro for adjusting pointers */ #define PTR_ADJUST(type,ptr,size) (type)((char *)(ptr) + (size)) /* Size of a field in a structure */ #define SIZEOF(st,field) (sizeof(((st *)NULL)->field)) /* Compute offset of a field in a structure */ #define OFFSET(st,f) ((long)&((st *)(NULL))->f) /* Stringify a constant */ #define XSTRINGIFY(val) #val #define STRINGIFY(val) XSTRINGIFY(val) #endif dynamips-0.2.14/common/eth_switch.c000066400000000000000000000545361241034141600172300ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Virtual Ethernet switch with VLAN/Trunk support. */ #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "registry.h" #include "net_io.h" #include "eth_switch.h" /* Debbuging message */ void ethsw_debug(ethsw_table_t *t,char *fmt,...) { char module[128]; va_list ap; if (t->debug) { va_start(ap,fmt); snprintf(module,sizeof(module),"ETHSW %s",t->name); m_flog(log_file,module,fmt,ap); va_end(ap); } } /* Compute hash index on the specified MAC address and VLAN */ static inline u_int ethsw_hash_index(n_eth_addr_t *addr,u_int vlan_id) { u_int h_index; h_index = (addr->eth_addr_byte[0] << 8) | addr->eth_addr_byte[1]; h_index ^= (addr->eth_addr_byte[2] << 8) | addr->eth_addr_byte[3]; h_index ^= (addr->eth_addr_byte[4] << 8) | addr->eth_addr_byte[5]; h_index ^= vlan_id; return(h_index & (ETHSW_HASH_SIZE - 1)); } /* Invalidate the whole MAC address table */ static void ethsw_invalidate(ethsw_table_t *t) { memset(t->mac_addr_table,0,sizeof(t->mac_addr_table)); } /* Invalidate entry of the MAC address table referring to the specified NIO */ static void ethsw_invalidate_port(ethsw_table_t *t,netio_desc_t *nio) { ethsw_mac_entry_t *entry; int i; for(i=0;imac_addr_table[i]; if (entry->nio == nio) { entry->nio = NULL; entry->vlan_id = 0; } } } /* Push a 802.1Q tag */ static void dot1q_push_tag(m_uint8_t *pkt,ethsw_packet_t *sp,u_int vlan) { n_eth_dot1q_hdr_t *hdr; memcpy(pkt,sp->pkt,(N_ETH_HLEN - 2)); hdr = (n_eth_dot1q_hdr_t *)pkt; hdr->type = htons(N_ETH_PROTO_DOT1Q); hdr->vlan_id = htons(sp->input_vlan); memcpy(pkt + sizeof(n_eth_dot1q_hdr_t), sp->pkt + (N_ETH_HLEN - 2), sp->pkt_len - (N_ETH_HLEN - 2)); } /* Pop a 802.1Q tag */ static void dot1q_pop_tag(m_uint8_t *pkt,ethsw_packet_t *sp) { memcpy(pkt,sp->pkt,(N_ETH_HLEN - 2)); memcpy(pkt + (N_ETH_HLEN - 2), sp->pkt + sizeof(n_eth_dot1q_hdr_t), sp->pkt_len - sizeof(n_eth_dot1q_hdr_t)); } /* Input vector for ACCESS ports */ static void ethsw_iv_access(ethsw_table_t *t,ethsw_packet_t *sp, netio_desc_t *op) { m_uint8_t pkt[ETHSW_MAX_PKT_SIZE+4]; switch(op->vlan_port_type) { /* Access -> Access: no special treatment */ case ETHSW_PORT_TYPE_ACCESS: netio_send(op,sp->pkt,sp->pkt_len); break; /* Access -> 802.1Q: push tag */ case ETHSW_PORT_TYPE_DOT1Q: /* * If the native VLAN of output port is the same as input, * forward the packet without adding the tag. */ if (op->vlan_id == sp->input_vlan) { netio_send(op,sp->pkt,sp->pkt_len); } else { dot1q_push_tag(pkt,sp,op->vlan_id); netio_send(op,pkt,sp->pkt_len+4); } break; default: fprintf(stderr,"ethsw_iv_access: unknown port type %u\n", op->vlan_port_type); } } /* Input vector for 802.1Q ports */ static void ethsw_iv_dot1q(ethsw_table_t *t,ethsw_packet_t *sp, netio_desc_t *op) { m_uint8_t pkt[ETHSW_MAX_PKT_SIZE+4]; /* If we don't have an input tag, we work temporarily as an access port */ if (!sp->input_tag) { ethsw_iv_access(t,sp,op); return; } switch(op->vlan_port_type) { /* 802.1Q -> Access: pop tag */ case ETHSW_PORT_TYPE_ACCESS: dot1q_pop_tag(pkt,sp); netio_send(op,pkt,sp->pkt_len-4); break; /* 802.1Q -> 802.1Q: pop tag if native VLAN in output otherwise no-op */ case ETHSW_PORT_TYPE_DOT1Q: if (op->vlan_id == sp->input_vlan) { dot1q_pop_tag(pkt,sp); netio_send(op,pkt,sp->pkt_len-4); } else { netio_send(op,sp->pkt,sp->pkt_len); } break; /* * 802.1Q -> QinQ: pop outer tag if native VLAN in the one specified * tunnel port. */ case ETHSW_PORT_TYPE_QINQ: if (op->vlan_id == sp->input_vlan) { dot1q_pop_tag(pkt,sp); netio_send(op,pkt,sp->pkt_len-4); } break; default: fprintf(stderr,"ethsw_iv_dot1q: unknown port type %u\n", op->vlan_port_type); } } /* Input vector for QinQ ports */ static void ethsw_iv_qinq(ethsw_table_t *t,ethsw_packet_t *sp, netio_desc_t *op) { m_uint8_t pkt[ETHSW_MAX_PKT_SIZE+4]; switch(op->vlan_port_type) { /* QinQ -> 802.1Q: push outer tag */ case ETHSW_PORT_TYPE_DOT1Q: dot1q_push_tag(pkt,sp,sp->input_port->vlan_id); netio_send(op,pkt,sp->pkt_len+4); break; /* * QinQ -> QinQ: valid situation if we have the same customer connected * on two ports (so with identical VLAN id on tunnel ports). */ case ETHSW_PORT_TYPE_QINQ: if (sp->input_port->vlan_id == op->vlan_id) { dot1q_pop_tag(pkt,sp); netio_send(op,pkt,sp->pkt_len-4); } break; default: fprintf(stderr,"ethsw_iv_dot1q: unknown port type %u\n", op->vlan_port_type); } } /* Flood a packet */ static void ethsw_flood(ethsw_table_t *t,ethsw_packet_t *sp) { ethsw_input_vector_t input_vector; netio_desc_t *op; int i; input_vector = sp->input_port->vlan_input_vector; assert(input_vector != NULL); for(i=0;inio[i]; if (!op || (op == sp->input_port)) continue; /* skip output port configured in access mode with a different vlan */ if ((op->vlan_port_type == ETHSW_PORT_TYPE_ACCESS) && (op->vlan_id != sp->input_vlan)) continue; /* send the packet to the output port */ input_vector(t,sp,op); } } /* Forward a packet */ static void ethsw_forward(ethsw_table_t *t,ethsw_packet_t *sp) { n_eth_hdr_t *hdr = (n_eth_hdr_t *)sp->pkt; ethsw_input_vector_t input_vector; ethsw_mac_entry_t *entry; u_int h_index; /* Learn the source MAC address */ h_index = ethsw_hash_index(&hdr->saddr,sp->input_vlan); entry = &t->mac_addr_table[h_index]; entry->nio = sp->input_port; entry->vlan_id = sp->input_vlan; entry->mac_addr = hdr->saddr; /* If we have a broadcast/multicast packet, flood it */ if (eth_addr_is_mcast(&hdr->daddr)) { ethsw_debug(t,"multicast dest, flooding packet.\n"); ethsw_flood(t,sp); return; } /* Lookup on the destination MAC address (unicast) */ h_index = ethsw_hash_index(&hdr->daddr,sp->input_vlan); entry = &t->mac_addr_table[h_index]; /* If the dest MAC is unknown, flood the packet */ if (memcmp(&entry->mac_addr,&hdr->daddr,N_ETH_ALEN) || (entry->vlan_id != sp->input_vlan)) { ethsw_debug(t,"unknown dest, flooding packet.\n"); ethsw_flood(t,sp); return; } /* Forward the packet to the output port only */ if (entry->nio != sp->input_port) { input_vector = sp->input_port->vlan_input_vector; assert(input_vector != NULL); input_vector(t,sp,entry->nio); } else { ethsw_debug(t,"source and dest ports identical, dropping.\n"); } } /* Receive a packet and prepare its forwarding */ static inline int ethsw_receive(ethsw_table_t *t,netio_desc_t *nio, u_char *pkt,ssize_t pkt_len) { n_eth_dot1q_hdr_t *dot1q_hdr; n_eth_isl_hdr_t *isl_hdr; n_eth_hdr_t *eth_hdr; n_eth_llc_hdr_t *llc_hdr; ethsw_packet_t sp; u_char *ptr; sp.input_port = nio; sp.input_vlan = 0; sp.pkt = pkt; sp.pkt_len = pkt_len; /* Skip runt packets */ if (sp.pkt_len < N_ETH_HLEN) return(-1); /* Determine the input VLAN */ switch(nio->vlan_port_type) { case ETHSW_PORT_TYPE_ACCESS: sp.input_vlan = nio->vlan_id; break; case ETHSW_PORT_TYPE_DOT1Q: dot1q_hdr = (n_eth_dot1q_hdr_t *)sp.pkt; /* use the native VLAN if no tag is found */ if (ntohs(dot1q_hdr->type) != N_ETH_PROTO_DOT1Q) { sp.input_vlan = nio->vlan_id; sp.input_tag = FALSE; } else { sp.input_vlan = ntohs(dot1q_hdr->vlan_id) & 0xFFF; sp.input_tag = TRUE; } break; case ETHSW_PORT_TYPE_QINQ: dot1q_hdr = (n_eth_dot1q_hdr_t *)sp.pkt; /* Drop untagged traffic */ if (ntohs(dot1q_hdr->type) != N_ETH_PROTO_DOT1Q) return(-1); /* The MAC address lookup is done on the outer VLAN */ sp.input_vlan = nio->vlan_id; break; case ETHSW_PORT_TYPE_ISL: /* Check that we have an ISL packet */ eth_hdr = (n_eth_hdr_t *)pkt; if (!eth_addr_is_cisco_isl(ð_hdr->daddr)) break; /* Verify LLC header */ llc_hdr = PTR_ADJUST(n_eth_llc_hdr_t *,eth_hdr,sizeof(n_eth_hdr_t)); if (!eth_llc_check_snap(llc_hdr)) break; /* Get the VLAN id */ isl_hdr = PTR_ADJUST(n_eth_isl_hdr_t *,llc_hdr, sizeof(n_eth_llc_hdr_t)); ptr = (u_char *)&isl_hdr->vlan; sp.input_vlan = (((u_int)ptr[0] << 8) | ptr[1]) >> 1; break; default: fprintf(stderr,"ethsw_receive: unknown port type %u\n", nio->vlan_port_type); return(-1); } if (sp.input_vlan != 0) ethsw_forward(t,&sp); return(0); } /* Receive a packet (handle the locking part) */ static int ethsw_recv_pkt(netio_desc_t *nio,u_char *pkt,ssize_t pkt_len, ethsw_table_t *t) { ETHSW_LOCK(t); ethsw_receive(t,nio,pkt,pkt_len); ETHSW_UNLOCK(t); return(0); } /* Set a port as an access port with the specified VLAN */ static void set_access_port(netio_desc_t *nio,u_int vlan_id) { nio->vlan_port_type = ETHSW_PORT_TYPE_ACCESS; nio->vlan_id = vlan_id; nio->vlan_input_vector = ethsw_iv_access; } /* Set a port as a 802.1Q trunk port */ static void set_dot1q_port(netio_desc_t *nio,u_int native_vlan) { nio->vlan_port_type = ETHSW_PORT_TYPE_DOT1Q; nio->vlan_id = native_vlan; nio->vlan_input_vector = ethsw_iv_dot1q; } /* Set a port as a Q-in-Q trunk port */ static void set_qinq_port(netio_desc_t *nio,u_int outer_vlan) { nio->vlan_port_type = ETHSW_PORT_TYPE_QINQ; nio->vlan_id = outer_vlan; nio->vlan_input_vector = ethsw_iv_qinq; } /* Acquire a reference to an Ethernet switch (increment reference count) */ ethsw_table_t *ethsw_acquire(char *name) { return(registry_find(name,OBJ_TYPE_ETHSW)); } /* Release an Ethernet switch (decrement reference count) */ int ethsw_release(char *name) { return(registry_unref(name,OBJ_TYPE_ETHSW)); } /* Create a virtual ethernet switch */ ethsw_table_t *ethsw_create(char *name) { ethsw_table_t *t; /* Allocate a new switch structure */ if (!(t = malloc(sizeof(*t)))) return NULL; memset(t,0,sizeof(*t)); pthread_mutex_init(&t->lock,NULL); if (!(t->name = strdup(name))) goto err_name; /* Record this object in registry */ if (registry_add(t->name,OBJ_TYPE_ETHSW,t) == -1) { fprintf(stderr,"ethsw_create: unable to register switch '%s'\n",name); goto err_reg; } return t; err_reg: free(t->name); err_name: free(t); return NULL; } /* Add a NetIO descriptor to a virtual ethernet switch */ int ethsw_add_netio(ethsw_table_t *t,char *nio_name) { netio_desc_t *nio; int i; ETHSW_LOCK(t); /* Try to find a free slot in the NIO array */ for(i=0;inio[i] == NULL) break; /* No free slot found ... */ if (i == ETHSW_MAX_NIO) goto error; /* Acquire the NIO descriptor and increment its reference count */ if (!(nio = netio_acquire(nio_name))) goto error; /* By default, the port is an access port in VLAN 1 */ set_access_port(nio,1); t->nio[i] = nio; netio_rxl_add(nio,(netio_rx_handler_t)ethsw_recv_pkt,t,NULL); ETHSW_UNLOCK(t); return(0); error: ETHSW_UNLOCK(t); return(-1); } /* Free resources used by a NIO */ static void ethsw_free_nio(netio_desc_t *nio) { netio_rxl_remove(nio); netio_release(nio->name); } /* Remove a NetIO descriptor from a virtual ethernet switch */ int ethsw_remove_netio(ethsw_table_t *t,char *nio_name) { netio_desc_t *nio; int i; ETHSW_LOCK(t); if (!(nio = registry_exists(nio_name,OBJ_TYPE_NIO))) goto error; /* Try to find the NIO in the NIO array */ for(i=0;inio[i] == nio) break; if (i == ETHSW_MAX_NIO) goto error; /* Invalidate this port in the MAC address table */ ethsw_invalidate_port(t,nio); t->nio[i] = NULL; ETHSW_UNLOCK(t); /* Remove the NIO from the RX multiplexer */ ethsw_free_nio(nio); return(0); error: ETHSW_UNLOCK(t); return(-1); } /* Clear the MAC address table */ int ethsw_clear_mac_addr_table(ethsw_table_t *t) { ETHSW_LOCK(t); ethsw_invalidate(t); ETHSW_UNLOCK(t); return(0); } /* Iterate over all entries of the MAC address table */ int ethsw_iterate_mac_addr_table(ethsw_table_t *t,ethsw_foreach_entry_t cb, void *opt_arg) { ethsw_mac_entry_t *entry; int i; ETHSW_LOCK(t); for(i=0;imac_addr_table[i]; if (!entry->nio) continue; cb(t,entry,opt_arg); } ETHSW_UNLOCK(t); return(0); } /* Set port as an access port */ int ethsw_set_access_port(ethsw_table_t *t,char *nio_name,u_int vlan_id) { int i,res = -1; ETHSW_LOCK(t); for(i=0;inio[i] && !strcmp(t->nio[i]->name,nio_name)) { set_access_port(t->nio[i],vlan_id); res = 0; break; } ETHSW_UNLOCK(t); return(res); } /* Set port as a 802.1q trunk port */ int ethsw_set_dot1q_port(ethsw_table_t *t,char *nio_name,u_int native_vlan) { int i,res = -1; ETHSW_LOCK(t); for(i=0;inio[i] && !strcmp(t->nio[i]->name,nio_name)) { set_dot1q_port(t->nio[i],native_vlan); res = 0; break; } ETHSW_UNLOCK(t); return(res); } /* Set port as a Q-in-Q port */ int ethsw_set_qinq_port(ethsw_table_t *t,char *nio_name,u_int outer_vlan) { int i,res = -1; ETHSW_LOCK(t); for(i=0;inio[i] && !strcmp(t->nio[i]->name,nio_name)) { set_qinq_port(t->nio[i],outer_vlan); res = 0; break; } ETHSW_UNLOCK(t); return(res); } /* Save the configuration of a switch */ void ethsw_save_config(ethsw_table_t *t,FILE *fd) { netio_desc_t *nio; int i; fprintf(fd,"ethsw create %s\n",t->name); ETHSW_LOCK(t); for(i=0;inio[i]; fprintf(fd,"ethsw add_nio %s %s\n",t->name,nio->name); switch(nio->vlan_port_type) { case ETHSW_PORT_TYPE_ACCESS: fprintf(fd,"ethsw set_access_port %s %s %u\n", t->name,nio->name,nio->vlan_id); break; case ETHSW_PORT_TYPE_DOT1Q: fprintf(fd,"ethsw set_dot1q_port %s %s %u\n", t->name,nio->name,nio->vlan_id); break; case ETHSW_PORT_TYPE_QINQ: fprintf(fd,"ethsw set_qinq_port %s %s %u\n", t->name,nio->name,nio->vlan_id); break; default: fprintf(stderr,"ethsw_save_config: unknown port type %u\n", nio->vlan_port_type); } } ETHSW_UNLOCK(t); fprintf(fd,"\n"); } /* Save configurations of all Ethernet switches */ static void ethsw_reg_save_config(registry_entry_t *entry,void *opt,int *err) { ethsw_save_config((ethsw_table_t *)entry->data,(FILE *)opt); } void ethsw_save_config_all(FILE *fd) { registry_foreach_type(OBJ_TYPE_ETHSW,ethsw_reg_save_config,fd,NULL); } /* Free resources used by a virtual ethernet switch */ static int ethsw_free(void *data,void *arg) { ethsw_table_t *t = data; int i; for(i=0;inio[i]) continue; ethsw_free_nio(t->nio[i]); } free(t->name); free(t); return(TRUE); } /* Delete a virtual ethernet switch */ int ethsw_delete(char *name) { return(registry_delete_if_unused(name,OBJ_TYPE_ETHSW,ethsw_free,NULL)); } /* Delete all virtual ethernet switches */ int ethsw_delete_all(void) { return(registry_delete_type(OBJ_TYPE_ETHSW,ethsw_free,NULL)); } /* Create a new interface */ static int ethsw_cfg_create_if(ethsw_table_t *t,char **tokens,int count) { netio_desc_t *nio = NULL; int nio_type; nio_type = netio_get_type(tokens[2]); switch(nio_type) { case NETIO_TYPE_UNIX: if (count != 5) { fprintf(stderr,"ETHSW: invalid number of arguments " "for UNIX NIO\n"); break; } nio = netio_desc_create_unix(tokens[1],tokens[3],tokens[4]); break; case NETIO_TYPE_TAP: if (count != 4) { fprintf(stderr,"ETHSW: invalid number of arguments " "for TAP NIO\n"); break; } nio = netio_desc_create_tap(tokens[1],tokens[3]); break; case NETIO_TYPE_UDP: if (count != 6) { fprintf(stderr,"ETHSW: invalid number of arguments " "for UDP NIO\n"); break; } nio = netio_desc_create_udp(tokens[1],atoi(tokens[3]), tokens[4],atoi(tokens[5])); break; case NETIO_TYPE_MCAST: if (count != 5) { fprintf(stderr,"ETHSW: invalid number of arguments " "for Multicast NIO\n"); break; } nio = netio_desc_create_mcast(tokens[1],tokens[3],atoi(tokens[4])); break; case NETIO_TYPE_TCP_CLI: if (count != 5) { fprintf(stderr,"ETHSW: invalid number of arguments " "for TCP CLI NIO\n"); break; } nio = netio_desc_create_tcp_cli(tokens[1],tokens[3],tokens[4]); break; case NETIO_TYPE_TCP_SER: if (count != 4) { fprintf(stderr,"ETHSW: invalid number of arguments " "for TCP SER NIO\n"); break; } nio = netio_desc_create_tcp_ser(tokens[1],tokens[3]); break; #ifdef GEN_ETH case NETIO_TYPE_GEN_ETH: if (count != 4) { fprintf(stderr,"ETHSW: invalid number of arguments " "for Generic Ethernet NIO\n"); break; } nio = netio_desc_create_geneth(tokens[1],tokens[3]); break; #endif #ifdef LINUX_ETH case NETIO_TYPE_LINUX_ETH: if (count != 4) { fprintf(stderr,"ETHSW: invalid number of arguments " "for Linux Ethernet NIO\n"); break; } nio = netio_desc_create_lnxeth(tokens[1],tokens[3]); break; #endif default: fprintf(stderr,"ETHSW: unknown/invalid NETIO type '%s'\n", tokens[2]); } if (!nio) { fprintf(stderr,"ETHSW: unable to create NETIO descriptor\n"); return(-1); } if (ethsw_add_netio(t,tokens[1]) == -1) { fprintf(stderr,"ETHSW: unable to add NETIO descriptor.\n"); netio_release(nio->name); return(-1); } netio_release(nio->name); return(0); } /* Set a port as an access port */ static int ethsw_cfg_set_access_port(ethsw_table_t *t,char **tokens,int count) { /* 3 parameters: "ACCESS", IF, VLAN */ if (count != 3) { fprintf(stderr,"ETHSW: invalid access port description.\n"); return(-1); } return(ethsw_set_access_port(t,tokens[1],atoi(tokens[2]))); } /* Set a port as a 802.1q trunk port */ static int ethsw_cfg_set_dot1q_port(ethsw_table_t *t,char **tokens,int count) { /* 3 parameters: "DOT1Q", IF, Native VLAN */ if (count != 3) { fprintf(stderr,"ETHSW: invalid trunk port description.\n"); return(-1); } return(ethsw_set_dot1q_port(t,tokens[1],atoi(tokens[2]))); } /* Set a port as a Q-in-Q port */ static int ethsw_cfg_set_qinq_port(ethsw_table_t *t,char **tokens,int count) { /* 3 parameters: "QINQ", IF, Outer VLAN */ if (count != 3) { fprintf(stderr,"ETHSW: invalid QinQ port description.\n"); return(-1); } return(ethsw_set_qinq_port(t,tokens[1],atoi(tokens[2]))); } #define ETHSW_MAX_TOKENS 16 /* Handle a ETHSW configuration line */ static int ethsw_handle_cfg_line(ethsw_table_t *t,char *str) { char *tokens[ETHSW_MAX_TOKENS]; int count; if ((count = m_strsplit(str,':',tokens,ETHSW_MAX_TOKENS)) <= 1) return(-1); if (!strcmp(tokens[0],"IF")) return(ethsw_cfg_create_if(t,tokens,count)); else if (!strcmp(tokens[0],"ACCESS")) return(ethsw_cfg_set_access_port(t,tokens,count)); else if (!strcmp(tokens[0],"DOT1Q")) return(ethsw_cfg_set_dot1q_port(t,tokens,count)); else if (!strcmp(tokens[0],"QINQ")) return(ethsw_cfg_set_qinq_port(t,tokens,count)); fprintf(stderr, "ETHSW: Unknown statement \"%s\" (allowed: IF,ACCESS,TRUNK)\n", tokens[0]); return(-1); } /* Read a ETHSW configuration file */ static int ethsw_read_cfg_file(ethsw_table_t *t,char *filename) { char buffer[1024],*ptr; FILE *fd; if (!(fd = fopen(filename,"r"))) { perror("fopen"); return(-1); } while(!feof(fd)) { if (!fgets(buffer,sizeof(buffer),fd)) break; /* skip comments and end of line */ if ((ptr = strpbrk(buffer,"#\r\n")) != NULL) *ptr = 0; /* analyze non-empty lines */ if (strchr(buffer,':')) ethsw_handle_cfg_line(t,buffer); } fclose(fd); return(0); } /* Start a virtual Ethernet switch */ int ethsw_start(char *filename) { ethsw_table_t *t; if (!(t = ethsw_create("default"))) { fprintf(stderr,"ETHSW: unable to create virtual fabric table.\n"); return(-1); } if (ethsw_read_cfg_file(t,filename) == -1) { fprintf(stderr,"ETHSW: unable to parse configuration file.\n"); return(-1); } ethsw_release("default"); return(0); } dynamips-0.2.14/common/eth_switch.h000066400000000000000000000063551241034141600172310ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Virtual Ethernet switch definitions. */ #ifndef __ETH_SWITCH_H__ #define __ETH_SWITCH_H__ #include #include "utils.h" #include "net.h" #include "net_io.h" /* Hash entries for the MAC address table */ #define ETHSW_HASH_SIZE 4096 /* Maximum port number */ #define ETHSW_MAX_NIO 64 /* Maximum packet size */ #define ETHSW_MAX_PKT_SIZE 2048 /* Port types: access, 802.1Q, 802.1Q tunnel (QinQ) */ enum { ETHSW_PORT_TYPE_ACCESS = 1, ETHSW_PORT_TYPE_DOT1Q, ETHSW_PORT_TYPE_QINQ, ETHSW_PORT_TYPE_ISL, }; /* Received packet */ typedef struct ethsw_packet ethsw_packet_t; struct ethsw_packet { u_char *pkt; ssize_t pkt_len; netio_desc_t *input_port; u_int input_vlan; int input_tag; }; /* MAC address table entry */ typedef struct ethsw_mac_entry ethsw_mac_entry_t; struct ethsw_mac_entry { netio_desc_t *nio; n_eth_addr_t mac_addr; m_uint16_t vlan_id; }; /* Virtual Ethernet switch */ typedef struct ethsw_table ethsw_table_t; struct ethsw_table { char *name; pthread_mutex_t lock; int debug; /* Virtual Ports */ netio_desc_t *nio[ETHSW_MAX_NIO]; /* MAC address table */ ethsw_mac_entry_t mac_addr_table[ETHSW_HASH_SIZE]; }; /* Packet input vector */ typedef void (*ethsw_input_vector_t)(ethsw_table_t *t,ethsw_packet_t *sp, netio_desc_t *output_port); /* "foreach" vector */ typedef void (*ethsw_foreach_entry_t)(ethsw_table_t *t, ethsw_mac_entry_t *entry, void *opt); #define ETHSW_LOCK(t) pthread_mutex_lock(&(t)->lock) #define ETHSW_UNLOCK(t) pthread_mutex_unlock(&(t)->lock) /* Acquire a reference to an Ethernet switch (increment reference count) */ ethsw_table_t *ethsw_acquire(char *name); /* Release an Ethernet switch (decrement reference count) */ int ethsw_release(char *name); /* Create a virtual ethernet switch */ ethsw_table_t *ethsw_create(char *name); /* Add a NetIO descriptor to a virtual ethernet switch */ int ethsw_add_netio(ethsw_table_t *t,char *nio_name); /* Remove a NetIO descriptor from a virtual ethernet switch */ int ethsw_remove_netio(ethsw_table_t *t,char *nio_name); /* Clear the MAC address table */ int ethsw_clear_mac_addr_table(ethsw_table_t *t); /* Iterate over all entries of the MAC address table */ int ethsw_iterate_mac_addr_table(ethsw_table_t *t,ethsw_foreach_entry_t cb, void *opt_arg); /* Set port as an access port */ int ethsw_set_access_port(ethsw_table_t *t,char *nio_name,u_int vlan_id); /* Set port as a 802.1q trunk port */ int ethsw_set_dot1q_port(ethsw_table_t *t,char *nio_name,u_int native_vlan); /* Set port as a Q-in-Q port */ int ethsw_set_qinq_port(ethsw_table_t *t,char *nio_name,u_int outer_vlan); /* Save the configuration of a switch */ void ethsw_save_config(ethsw_table_t *t,FILE *fd); /* Save configurations of all Ethernet switches */ void ethsw_save_config_all(FILE *fd); /* Delete a virtual ethernet switch */ int ethsw_delete(char *name); /* Delete all virtual ethernet switches */ int ethsw_delete_all(void); /* Start a virtual Ethernet switch */ int ethsw_start(char *filename); #endif dynamips-0.2.14/common/frame_relay.c000066400000000000000000000360771241034141600173550ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Frame-Relay switch. */ #include #include #include #include #include #include #include #include #include #include "utils.h" #include "mempool.h" #include "registry.h" #include "net_io.h" #include "frame_relay.h" #define DEBUG_FRSW 0 extern FILE *log_file; /* ANSI LMI packet header */ static const m_uint8_t lmi_ansi_hdr[] = { 0x00, 0x01, 0x03, 0x08, 0x00, 0x75, 0x95, }; /* DLCI hash function */ static inline u_int frsw_dlci_hash(u_int dlci) { return((dlci ^ (dlci >> 8)) & (FRSW_HASH_SIZE-1)); } /* DLCI lookup */ frsw_conn_t *frsw_dlci_lookup(frsw_table_t *t,netio_desc_t *input,u_int dlci) { frsw_conn_t *vc; for(vc=t->dlci_table[frsw_dlci_hash(dlci)];vc;vc=vc->hash_next) if ((vc->input == input) && (vc->dlci_in == dlci)) return vc; return NULL; } /* Handle a ANSI LMI packet */ ssize_t frsw_handle_lmi_ansi_pkt(frsw_table_t *t,netio_desc_t *input, m_uint8_t *pkt,ssize_t len) { m_uint8_t resp[FR_MAX_PKT_SIZE],*pres,*preq; m_uint8_t itype,isize; int msg_type,seq_ok; ssize_t rlen; frsw_conn_t *sc; u_int dlci; if ((len <= sizeof(lmi_ansi_hdr)) || memcmp(pkt,lmi_ansi_hdr,sizeof(lmi_ansi_hdr))) return(-1); #if DEBUG_FRSW m_log(input->name,"received an ANSI LMI packet:\n"); mem_dump(log_file,pkt,len); #endif /* Prepare response packet */ memcpy(resp,lmi_ansi_hdr,sizeof(lmi_ansi_hdr)); resp[FR_LMI_ANSI_STATUS_OFFSET] = FR_LMI_ANSI_STATUS; preq = &pkt[sizeof(lmi_ansi_hdr)]; pres = &resp[sizeof(lmi_ansi_hdr)]; msg_type = -1; seq_ok = FALSE; while((preq + 2) < (pkt + len)) { /* get item type and size */ itype = preq[0]; isize = preq[1]; /* check packet boundary */ if ((preq + isize + 2) > (pkt + len)) { m_log(input->name,"invalid LMI packet:\n"); mem_dump(log_file,pkt,len); return(-1); } switch(itype) { case 0x01: /* report information element */ if (isize != 1) { m_log(input->name,"invalid LMI item size.\n"); return(-1); } if ((msg_type = preq[2]) > 1) { m_log(input->name,"unknown LMI report type 0x%x.\n",msg_type); return(-1); } pres[0] = 0x01; pres[1] = 0x01; pres[2] = msg_type; pres += 3; break; case 0x03: /* sequences */ if (isize != 2) { m_log(input->name,"invalid LMI item size.\n"); return(-1); } pres[0] = 0x03; pres[1] = 0x02; if (input->fr_lmi_seq != preq[3]) { m_log(input->name,"resynchronization with LMI sequence...\n"); input->fr_lmi_seq = preq[3]; } input->fr_lmi_seq++; if (!input->fr_lmi_seq) input->fr_lmi_seq++; pres[2] = input->fr_lmi_seq; pres[3] = preq[2]; #if DEBUG_FRSW m_log(input->name,"iSSN=0x%x, iRSN=0x%x, oSSN=0x%x, oRSN=0x%x\n", preq[2],preq[3],pres[2],pres[3]); #endif pres += 4; seq_ok = TRUE; break; default: m_log(input->name,"unknown LMI item type %u\n",itype); goto done; } /* proceed next item */ preq += isize + 2; } done: if ((msg_type == -1) || !seq_ok) { m_log(input->name,"incomplete LMI packet.\n"); return(-1); } /* full status, send DLCI info */ if (msg_type == 0) { #if DEBUG_FRSW m_log(input->name,"LMI full status, advertising DLCIs\n"); #endif for(sc=input->fr_conn_list;sc;sc=sc->next) { dlci = sc->dlci_in; #if DEBUG_FRSW m_log(input->name,"sending LMI adv for DLCI %u\n",dlci); #endif pres[0] = 0x07; pres[1] = 0x03; pres[2] = dlci >> 4; pres[3] = 0x80 | ((dlci & 0x0f) << 3); pres[4] = 0x82; pres += 5; } } rlen = pres - resp; #if DEBUG_FRSW m_log(input->name,"sending ANSI LMI packet:\n"); mem_dump(log_file,resp,rlen); #endif netio_send(input,resp,rlen); return(0); } /* DLCI switching */ void frsw_dlci_switch(frsw_conn_t *vc,m_uint8_t *pkt) { pkt[0] = (pkt[0] & 0x03) | ((vc->dlci_out >> 4) << 2); pkt[1] = (pkt[1] & 0x0f) | ((vc->dlci_out & 0x0f) << 4); /* update the statistics counter */ vc->count++; } /* Handle a Frame-Relay packet */ ssize_t frsw_handle_pkt(frsw_table_t *t,netio_desc_t *input, m_uint8_t *pkt,ssize_t len) { netio_desc_t *output = NULL; frsw_conn_t *vc; m_uint32_t dlci; ssize_t slen; /* Extract DLCI information */ dlci = ((pkt[0] & 0xfc) >> 2) << 4; dlci |= (pkt[1] & 0xf0) >> 4; #if DEBUG_FRSW m_log(input->name,"Trying to switch packet with input DLCI %u.\n",dlci); mem_dump(log_file,pkt,len); #endif /* LMI ? */ if (dlci == FR_DLCI_LMI_ANSI) return(frsw_handle_lmi_ansi_pkt(t,input,pkt,len)); /* DLCI switching */ if ((vc = frsw_dlci_lookup(t,input,dlci)) != NULL) { frsw_dlci_switch(vc,pkt); output = vc->output; } #if DEBUG_FRSW if (output) { m_log(input->name,"Switching packet to interface %s.\n",output->name); } else { m_log(input->name,"Unable to switch packet.\n"); } #endif /* Send the packet on output interface */ slen = netio_send(output,pkt,len); if (len != slen) { t->drop++; return(-1); } return(0); } /* Receive a Frame-Relay packet */ static int frsw_recv_pkt(netio_desc_t *nio,u_char *pkt,ssize_t pkt_len, frsw_table_t *t) { int res; FRSW_LOCK(t); res = frsw_handle_pkt(t,nio,pkt,pkt_len); FRSW_UNLOCK(t); return(res); } /* Acquire a reference to a Frame-Relay switch (increment reference count) */ frsw_table_t *frsw_acquire(char *name) { return(registry_find(name,OBJ_TYPE_FRSW)); } /* Release a Frame-Relay switch (decrement reference count) */ int frsw_release(char *name) { return(registry_unref(name,OBJ_TYPE_FRSW)); } /* Create a virtual switch table */ frsw_table_t *frsw_create_table(char *name) { frsw_table_t *t; /* Allocate a new switch structure */ if (!(t = malloc(sizeof(*t)))) return NULL; memset(t,0,sizeof(*t)); pthread_mutex_init(&t->lock,NULL); mp_create_fixed_pool(&t->mp,"Frame-Relay Switch"); if (!(t->name = mp_strdup(&t->mp,name))) goto err_name; /* Record this object in registry */ if (registry_add(t->name,OBJ_TYPE_FRSW,t) == -1) { fprintf(stderr,"frsw_create_table: unable to create switch '%s'\n",name); goto err_reg; } return t; err_reg: err_name: mp_free_pool(&t->mp); free(t); return NULL; } /* Unlink a VC */ static void frsw_unlink_vc(frsw_conn_t *vc) { if (vc) { if (vc->next) vc->next->pprev = vc->pprev; if (vc->pprev) *(vc->pprev) = vc->next; } } /* Free resources used by a VC */ static void frsw_release_vc(frsw_conn_t *vc) { if (vc) { /* release input NIO */ if (vc->input) { netio_rxl_remove(vc->input); netio_release(vc->input->name); } /* release output NIO */ if (vc->output) netio_release(vc->output->name); } } /* Free resources used by a Frame-Relay switch */ static int frsw_free(void *data,void *arg) { frsw_table_t *t = data; frsw_conn_t *vc; int i; for(i=0;idlci_table[i];vc;vc=vc->hash_next) frsw_release_vc(vc); mp_free_pool(&t->mp); free(t); return(TRUE); } /* Delete a Frame-Relay switch */ int frsw_delete(char *name) { return(registry_delete_if_unused(name,OBJ_TYPE_FRSW,frsw_free,NULL)); } /* Delete all Frame-Relay switches */ int frsw_delete_all(void) { return(registry_delete_type(OBJ_TYPE_FRSW,frsw_free,NULL)); } /* Create a switch connection */ int frsw_create_vc(frsw_table_t *t,char *nio_input,u_int dlci_in, char *nio_output,u_int dlci_out) { frsw_conn_t *vc,**p; u_int hbucket; FRSW_LOCK(t); /* Allocate a new VC */ if (!(vc = mp_alloc(&t->mp,sizeof(*vc)))) { FRSW_UNLOCK(t); return(-1); } vc->input = netio_acquire(nio_input); vc->output = netio_acquire(nio_output); vc->dlci_in = dlci_in; vc->dlci_out = dlci_out; /* Check these NIOs are valid and the input VC does not exists */ if (!vc->input || !vc->output) goto error; if (frsw_dlci_lookup(t,vc->input,dlci_in)) { fprintf(stderr,"FRSW %s: switching for VC %u on IF %s " "already defined.\n",t->name,dlci_in,vc->input->name); goto error; } /* Add as a RX listener */ if (netio_rxl_add(vc->input,(netio_rx_handler_t)frsw_recv_pkt,t,NULL) == -1) goto error; hbucket = frsw_dlci_hash(dlci_in); vc->hash_next = t->dlci_table[hbucket]; t->dlci_table[hbucket] = vc; for(p=(frsw_conn_t **)&vc->input->fr_conn_list;*p;p=&(*p)->next) if ((*p)->dlci_in > dlci_in) break; vc->next = *p; if (*p) (*p)->pprev = &vc->next; vc->pprev = p; *p = vc; FRSW_UNLOCK(t); return(0); error: FRSW_UNLOCK(t); frsw_release_vc(vc); mp_free(vc); return(-1); } /* Remove a switch connection */ int frsw_delete_vc(frsw_table_t *t,char *nio_input,u_int dlci_in, char *nio_output,u_int dlci_out) { netio_desc_t *input,*output; frsw_conn_t **vc,*p; u_int hbucket; FRSW_LOCK(t); input = registry_exists(nio_input,OBJ_TYPE_NIO); output = registry_exists(nio_output,OBJ_TYPE_NIO); if (!input || !output) { FRSW_UNLOCK(t); return(-1); } hbucket = frsw_dlci_hash(dlci_in); for(vc=&t->dlci_table[hbucket];*vc;vc=&(*vc)->hash_next) { p = *vc; if ((p->input == input) && (p->output == output) && (p->dlci_in == dlci_in) && (p->dlci_out == dlci_out)) { /* Found a matching VC, remove it */ *vc = (*vc)->hash_next; frsw_unlink_vc(p); FRSW_UNLOCK(t); /* Release NIOs */ frsw_release_vc(p); mp_free(p); return(0); } } FRSW_UNLOCK(t); return(-1); } /* Save the configuration of a Frame-Relay switch */ void frsw_save_config(frsw_table_t *t,FILE *fd) { frsw_conn_t *vc; int i; fprintf(fd,"frsw create %s\n",t->name); FRSW_LOCK(t); for(i=0;idlci_table[i];vc;vc=vc->next) { fprintf(fd,"frsw create_vc %s %s %u %s %u\n", t->name,vc->input->name,vc->dlci_in, vc->output->name,vc->dlci_out); } } FRSW_UNLOCK(t); fprintf(fd,"\n"); } /* Save configurations of all Frame-Relay switches */ static void frsw_reg_save_config(registry_entry_t *entry,void *opt,int *err) { frsw_save_config((frsw_table_t *)entry->data,(FILE *)opt); } void frsw_save_config_all(FILE *fd) { registry_foreach_type(OBJ_TYPE_FRSW,frsw_reg_save_config,fd,NULL); } /* Create a new interface */ int frsw_cfg_create_if(frsw_table_t *t,char **tokens,int count) { netio_desc_t *nio = NULL; int nio_type; /* at least: IF, interface name, NetIO type */ if (count < 3) { fprintf(stderr,"frsw_cfg_create_if: invalid interface description\n"); return(-1); } nio_type = netio_get_type(tokens[2]); switch(nio_type) { case NETIO_TYPE_UNIX: if (count != 5) { fprintf(stderr,"FRSW: invalid number of arguments " "for UNIX NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_unix(tokens[1],tokens[3],tokens[4]); break; case NETIO_TYPE_UDP: if (count != 6) { fprintf(stderr,"FRSW: invalid number of arguments " "for UDP NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_udp(tokens[1],atoi(tokens[3]), tokens[4],atoi(tokens[5])); break; case NETIO_TYPE_MCAST: if (count != 5) { fprintf(stderr,"FRSW: invalid number of arguments " "for UDP NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_mcast(tokens[1],tokens[3],atoi(tokens[4])); break; case NETIO_TYPE_TCP_CLI: if (count != 5) { fprintf(stderr,"FRSW: invalid number of arguments " "for TCP CLI NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_tcp_cli(tokens[1],tokens[3],tokens[4]); break; case NETIO_TYPE_TCP_SER: if (count != 4) { fprintf(stderr,"FRSW: invalid number of arguments " "for TCP SER NIO '%s'\n",tokens[1]); break; } nio = netio_desc_create_tcp_ser(tokens[1],tokens[3]); break; default: fprintf(stderr,"FRSW: unknown/invalid NETIO type '%s'\n", tokens[2]); } if (!nio) { fprintf(stderr,"FRSW: unable to create NETIO descriptor of " "interface %s\n",tokens[1]); return(-1); } netio_release(nio->name); return(0); } /* Create a new virtual circuit */ int frsw_cfg_create_vc(frsw_table_t *t,char **tokens,int count) { /* 5 parameters: "VC", InputIF, InDLCI, OutputIF, OutDLCI */ if (count != 5) { fprintf(stderr,"FRSW: invalid VPC descriptor.\n"); return(-1); } return(frsw_create_vc(t,tokens[1],atoi(tokens[2]), tokens[3],atoi(tokens[4]))); } #define FRSW_MAX_TOKENS 16 /* Handle a FRSW configuration line */ int frsw_handle_cfg_line(frsw_table_t *t,char *str) { char *tokens[FRSW_MAX_TOKENS]; int count; if ((count = m_strsplit(str,':',tokens,FRSW_MAX_TOKENS)) <= 1) return(-1); if (!strcmp(tokens[0],"IF")) return(frsw_cfg_create_if(t,tokens,count)); else if (!strcmp(tokens[0],"VC")) return(frsw_cfg_create_vc(t,tokens,count)); fprintf(stderr,"FRSW: Unknown statement \"%s\" (allowed: IF,VC)\n", tokens[0]); return(-1); } /* Read a FRSW configuration file */ int frsw_read_cfg_file(frsw_table_t *t,char *filename) { char buffer[1024],*ptr; FILE *fd; if (!(fd = fopen(filename,"r"))) { perror("fopen"); return(-1); } while(!feof(fd)) { if (!fgets(buffer,sizeof(buffer),fd)) break; /* skip comments and end of line */ if ((ptr = strpbrk(buffer,"#\r\n")) != NULL) *ptr = 0; /* analyze non-empty lines */ if (strchr(buffer,':')) frsw_handle_cfg_line(t,buffer); } fclose(fd); return(0); } /* Start a virtual Frame-Relay switch */ int frsw_start(char *filename) { frsw_table_t *t; if (!(t = frsw_create_table("default"))) { fprintf(stderr,"FRSW: unable to create virtual fabric table.\n"); return(-1); } if (frsw_read_cfg_file(t,filename) == -1) { fprintf(stderr,"FRSW: unable to parse configuration file.\n"); return(-1); } frsw_release("default"); return(0); } dynamips-0.2.14/common/frame_relay.h000066400000000000000000000042261241034141600173510ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Frame-Relay definitions. */ #ifndef __FRAME_RELAY_H__ #define __FRAME_RELAY_H__ #include #include "utils.h" #include "mempool.h" #include "net_io.h" /* DLCIs used for LMI */ #define FR_DLCI_LMI_ANSI 0 /* ANSI LMI */ #define FR_DLCI_LMI_CISCO 1023 /* Cisco LMI */ #define FR_LMI_ANSI_STATUS_OFFSET 5 #define FR_LMI_ANSI_STATUS_ENQUIRY 0x75 /* sent by user */ #define FR_LMI_ANSI_STATUS 0x7d /* sent by network */ /* Maximum packet size */ #define FR_MAX_PKT_SIZE 2048 /* Frame-Relay switch table */ typedef struct frsw_conn frsw_conn_t; struct frsw_conn { frsw_conn_t *hash_next,*next,**pprev; netio_desc_t *input,*output; u_int dlci_in,dlci_out; m_uint64_t count; }; /* Virtual Frame-Relay switch table */ #define FRSW_HASH_SIZE 256 typedef struct frsw_table frsw_table_t; struct frsw_table { char *name; pthread_mutex_t lock; mempool_t mp; m_uint64_t drop; frsw_conn_t *dlci_table[FRSW_HASH_SIZE]; }; #define FRSW_LOCK(t) pthread_mutex_lock(&(t)->lock) #define FRSW_UNLOCK(t) pthread_mutex_unlock(&(t)->lock) /* Acquire a reference to a Frame-Relay switch (increment reference count) */ frsw_table_t *frsw_acquire(char *name); /* Release a Frame-Relay switch (decrement reference count) */ int frsw_release(char *name); /* Create a virtual switch table */ frsw_table_t *frsw_create_table(char *name); /* Delete a Frame-Relay switch */ int frsw_delete(char *name); /* Delete all Frame-Relay switches */ int frsw_delete_all(void); /* Create a switch connection */ int frsw_create_vc(frsw_table_t *t,char *nio_input,u_int dlci_in, char *nio_output,u_int dlci_out); /* Remove a switch connection */ int frsw_delete_vc(frsw_table_t *t,char *nio_input,u_int dlci_in, char *nio_output,u_int dlci_out); /* Save the configuration of a Frame-Relay switch */ void frsw_save_config(frsw_table_t *t,FILE *fd); /* Save configurations of all Frame-Relay switches */ void frsw_save_config_all(FILE *fd); /* Start a virtual Frame-Relay switch */ int frsw_start(char *filename); #endif dynamips-0.2.14/common/fs_fat.c000066400000000000000000000147031241034141600163210ustar00rootroot00000000000000/** @file * @brief FAT filesystem. * * Based on http://www.win.tue.nl/~aeb/linux/fs/fat/fat-1.html */ /* * Copyright (c) 2014 Flávio J. Saraiva */ #include "fs_fat.h" /* */ struct fat16_data { const char *volume_name; m_uint32_t volume_sectors; m_uint16_t reserved_sectors; m_uint16_t root_entry_count; m_uint16_t fat_sectors; m_uint16_t sects_per_track; m_uint16_t heads; m_uint8_t sects_per_cluster; m_uint8_t nr_fats; }; struct sec_per_clus_table { m_uint32_t sectors; m_uint8_t sectors_per_cluster; }; static struct { m_uint32_t sectors; m_uint8_t sects_per_cluster; } cluster_size_table16[] = { { 32680, 2}, /* 16MB - 1K */ { 262144, 4}, /* 128MB - 2K */ { 524288, 8}, /* 256MB - 4K */ { 1048576, 16}, /* 512MB - 8K */ { 2097152, 32}, /* 1GB - 16K */ { 4194304, 64}, /* 2GB - 32K */ { 8388608, 128},/* 2GB - 64K (not supported on some systems) */ { 0 , 0 } /* done */ }; static inline void set_u32(m_uint8_t *p, size_t i, m_uint32_t v) { p[i+0] = (m_uint8_t)((v>>0)&0xFF); p[i+1] = (m_uint8_t)((v>>8)&0xFF); p[i+1] = (m_uint8_t)((v>>16)&0xFF); p[i+1] = (m_uint8_t)((v>>24)&0xFF); } static inline void set_u16(m_uint8_t *p, size_t i, m_uint16_t v) { p[i+0] = (m_uint8_t)((v>>0)&0xFF); p[i+1] = (m_uint8_t)((v>>8)&0xFF); } static void boot16(m_uint8_t *sector, struct fat16_data *fat16) { int i; memset(sector, 0x00, FS_FAT_SECTOR_SIZE); /* start of boot program */ sector[0x0] = 0xEB;// jmp 0x3E sector[0x1] = 0x3C; sector[0x2] = 0x90;// nop /* OEM string */ sector[0x3] = 'D'; sector[0x4] = 'Y'; sector[0x5] = 'N'; sector[0x6] = 'A'; sector[0x7] = 'M'; sector[0x8] = 'I'; sector[0x9] = 'P'; sector[0xA] = 'S'; // Bytes per sector set_u16(sector,0xB,FS_FAT_SECTOR_SIZE); // Sectors per cluster sector[0xD] = fat16->sects_per_cluster; // Reserved Sectors set_u16(sector,0xE,fat16->reserved_sectors); // Number of FATS sector[0x10] = fat16->nr_fats; // Max entries in root dir (FAT16 only) set_u16(sector,0x11,fat16->root_entry_count); // [FAT16] Total sectors (use FAT32 count instead) set_u16(sector,0x13,0x0000); // Media type (Fixed Disk) sector[0x15] = 0xF8; // FAT16 Bootstrap Details // Count of sectors used by the FAT table (FAT16 only) set_u16(sector,0x16,fat16->fat_sectors); // Sectors per track set_u16(sector,0x18,fat16->sects_per_track); // Heads set_u16(sector,0x1A,fat16->heads); // Hidden sectors set_u16(sector,0x1C,0x0000); // Total sectors for this volume set_u32(sector,0x20,fat16->volume_sectors); // Drive number (1st Hard Disk) sector[0x24] = 0x80; // Reserved sector[0x25] = 0x00; // Boot signature sector[0x26] = 0x29; // Volume ID sector[0x27] = (rand()&0xFF); sector[0x28] = (rand()&0xFF); sector[0x29] = (rand()&0xFF); sector[0x2A] = (rand()&0xFF); // Volume name for (i = 0; i < 11 && fat16->volume_name[i]; i++) { sector[i+0x2B] = fat16->volume_name[i]; } for (; i < 11; i++) { sector[i+0x2B] = ' '; } // File sys type sector[0x36] = 'F'; sector[0x37] = 'A'; sector[0x38] = 'T'; sector[0x39] = '1'; sector[0x3A] = '6'; sector[0x3B] = ' '; sector[0x3C] = ' '; sector[0x3D] = ' '; /* boot program (empty) */ /* Signature */ sector[0x1FE] = 0x55; sector[0x1FF] = 0xAA; } static void fat16_first(m_uint8_t *sector, struct fat16_data *fat16) { memset(sector, 0x00, FS_FAT_SECTOR_SIZE); // Initialise default allocate / reserved clusters set_u16(sector,0x0,0xFFF8); set_u16(sector,0x2,0xFFFF); } static void fat16_empty(m_uint8_t *sector, struct fat16_data *fat16) { memset(sector, 0x00, FS_FAT_SECTOR_SIZE); } static int write_sector(int fd, m_uint32_t lba, m_uint8_t *sector) { off_t offset; errno = 0; offset = (off_t)lba * FS_FAT_SECTOR_SIZE; if (lseek(fd,offset,SEEK_SET) != offset) { perror("write_sector(fs_fat): lseek"); return(-1); } if (write(fd, sector, FS_FAT_SECTOR_SIZE) != FS_FAT_SECTOR_SIZE) { perror("write_sector(fs_fat): write"); return(-1); } return(0); } /** Format partition as FAT16. */ int fs_fat_format16(int fd, m_uint32_t begin_lba, m_uint32_t nr_sectors, m_uint16_t sects_per_track, m_uint16_t heads, const char *volume_name) { m_uint8_t sector[FS_FAT_SECTOR_SIZE]; struct fat16_data data, *fat16 = NULL; size_t i, ifat, isec; m_uint32_t total_clusters; m_uint32_t fat_lba; m_uint32_t rootdir_lba; m_uint32_t rootdir_sectors; char name[12]; if (!volume_name) { name[0] = 0; snprintf(name,sizeof(name), "DISK%dMB", (nr_sectors / (1048576 / FS_FAT_SECTOR_SIZE))); volume_name = name; } /* prepare FAT16 */ fat16 = &data; memset(fat16, 0x00, sizeof(*fat16)); fat16->volume_name = volume_name; fat16->volume_sectors = nr_sectors; fat16->sects_per_track = sects_per_track; fat16->heads = heads; for (i=0; ; i++) { if (!cluster_size_table16[i].sectors) return(-1); if (nr_sectors <= cluster_size_table16[i].sectors) { fat16->sects_per_cluster = cluster_size_table16[i].sects_per_cluster; break; } } total_clusters = (fat16->volume_sectors / fat16->sects_per_cluster) + 1; fat16->fat_sectors = (total_clusters/(FS_FAT_SECTOR_SIZE/2)) + 1; fat16->reserved_sectors = 1; fat16->nr_fats = 2; fat16->root_entry_count = 512; /* Boot sector */ boot16(sector, fat16); if (write_sector(fd, begin_lba, sector) < 0) return(-1); /* FAT sectors */ for (ifat = 0; ifat < fat16->nr_fats; ifat++) { fat_lba = begin_lba + fat16->reserved_sectors + ifat * fat16->fat_sectors; fat16_first(sector, fat16); if (write_sector(fd, fat_lba, sector) < 0) return(-1); fat16_empty(sector, fat16); for (isec = 1; isec < fat16->fat_sectors; isec++) { if (write_sector(fd, isec + fat_lba, sector) < 0) return(-1); } } /* Root directory */ rootdir_lba = begin_lba + fat16->reserved_sectors + (fat16->nr_fats * fat16->fat_sectors); rootdir_sectors = ((fat16->root_entry_count * 32) + (FS_FAT_SECTOR_SIZE - 1)) / FS_FAT_SECTOR_SIZE; fat16_empty(sector, fat16); for (isec = 0; isec < rootdir_sectors; isec++) { if (write_sector(fd, rootdir_lba + isec, sector) < 0) { return(-1); } } return(0); } dynamips-0.2.14/common/fs_fat.h000066400000000000000000000007351241034141600163260ustar00rootroot00000000000000/** @file * @brief FAT filesystem. * * Based on http://www.win.tue.nl/~aeb/linux/fs/fat/fat-1.html */ /* * Copyright (c) 2014 Flávio J. Saraiva */ #ifndef FS_FAT_H__ #define FS_FAT_H__ #include "dynamips_common.h" #define FS_FAT_SECTOR_SIZE 512 int fs_fat_format16(int fd, m_uint32_t begin_lba, m_uint32_t nr_sectors, m_uint16_t sects_per_track, m_uint16_t heads, const char *volume_name); #endif dynamips-0.2.14/common/fs_mbr.c000066400000000000000000000031501241034141600163210ustar00rootroot00000000000000/** @file * @brief Master Boot Record * * Based on http://thestarman.pcministry.com/asm/mbr/PartTables.htm */ /* * Copyright (c) 2014 Flávio J. Saraiva */ #include "fs_mbr.h" /** Decode a CHS reference */ void mbr_get_chs(m_uint8_t chs[3], m_uint16_t *cyl, m_uint8_t *head, m_uint8_t *sect) { if (head) *head = chs[0]; if (sect) *sect = (chs[1]&0x3F); if (cyl) *cyl = ((m_uint16_t)(chs[1]&0xC0)<<2) | (chs[2]); } /** Encode a CHS reference */ void mbr_set_chs(m_uint8_t chs[3], m_uint16_t cyl, m_uint8_t head, m_uint8_t sect) { if (cyl > MBR_CYLINDER_MAX) { /* c=1023, h=254, s=63 */ chs[0] = 0xFE; chs[1] = 0xFF; chs[2] = 0xFF; } else { chs[0] = head; chs[1] = ((m_uint8_t)(cyl>>2)&0xC0) | (sect&0x3F); chs[2] = (m_uint8_t)(cyl&0xFF); } } /** Write MBR data */ int mbr_write_fd(int fd, struct mbr_data *mbr) { if (!mbr) { fprintf(stderr,"mbr_write_fd: null"); return(-1); } if (lseek(fd, MBR_OFFSET, SEEK_SET) != MBR_OFFSET) { perror("mbr_write_fd: lseek"); return(-1); } if (write(fd, mbr, sizeof(*mbr)) != sizeof(*mbr)) { perror("mbr_write_fd: write"); return(-1); } return(0); } /** Read MBR data */ int mbr_read_fd(int fd, struct mbr_data *mbr) { if (!mbr) { fprintf(stderr,"mbr_read_fd: null"); return(-1); } if (lseek(fd, MBR_OFFSET, SEEK_SET) != MBR_OFFSET) { perror("mbr_read_fd: lseek"); return(-1); } if (read(fd, mbr, sizeof(*mbr)) != sizeof(*mbr)) { perror("mbr_read_fd: read"); return(-1); } return(0); } dynamips-0.2.14/common/fs_mbr.h000066400000000000000000000023431241034141600163310ustar00rootroot00000000000000/** @file * @brief Master Boot Record * * Based on http://thestarman.pcministry.com/asm/mbr/PartTables.htm */ /* * Copyright (c) 2014 Flávio J. Saraiva */ #ifndef FS_MBR__ #define FS_MBR__ #include "dynamips_common.h" #define MBR_CYLINDER_MIN 0 #define MBR_CYLINDER_MAX 1023 #define MBR_HEAD_MIN 0 #define MBR_HEAD_MAX 254 #define MBR_SECTOR_MIN 1 #define MBR_SECTOR_MAX 63 #define MBR_PARTITION_BOOTABLE 0x80 #define MBR_PARTITION_TYPE_FAT16 0x04 #define MBR_SIGNATURE_0 0x55 #define MBR_SIGNATURE_1 0xAA #define MBR_OFFSET 512 - sizeof(struct mbr_data) /* A partition of the MBR */ struct mbr_partition { m_uint8_t bootable; m_uint8_t first_chs[3]; m_uint8_t type; m_uint8_t last_chs[3]; m_uint32_t lba; m_uint32_t nr_sectors; } __attribute__((__packed__)); /* The MBR data */ struct mbr_data { struct mbr_partition partition[4]; m_uint8_t signature[2]; } __attribute__((__packed__)); void mbr_get_chs(m_uint8_t chs[3], m_uint16_t *cyl, m_uint8_t *head, m_uint8_t *sect); void mbr_set_chs(m_uint8_t chs[3], m_uint16_t cyl, m_uint8_t head, m_uint8_t sect); int mbr_write_fd(int fd, struct mbr_data *mbr); int mbr_read_fd(int fd, struct mbr_data *mbr); #endif dynamips-0.2.14/common/fs_nvram.c000066400000000000000000000576071241034141600167040ustar00rootroot00000000000000/** @file * @brief Cisco NVRAM filesystem. */ /* * Copyright (c) 2013 Flávio J. Saraiva */ #include #include #include // offsetof #include #include #include #include "fs_nvram.h" #define DEBUG_BACKUP 0 /** NVRAM filesystem. */ struct fs_nvram { u_char *base; size_t len; m_uint32_t addr; /// start address of the filesystem (for absolute addresses) u_int flags; /// filesystem flags u_int shift; /// scale byte offsets u_int padding; /// base padding value size_t backup; /// start offset of the backup filesystem m_uint8_t (*read_byte)(fs_nvram_t *fs, u_int offset); void (*write_byte)(fs_nvram_t *fs, u_int offset, m_uint8_t val); }; //========================================================= // Auxiliary /** Convert a 16 bit value from big endian to native. */ static inline void be_to_native16(m_uint16_t *val) { union { m_uint16_t val; m_uint8_t b[2]; } u; u.val = *val; *val = (((m_uint16_t)u.b[0] << 8) | u.b[1]); } /** Convert a 32 bit value from big endian to native. */ static inline void be_to_native32(m_uint32_t *val) { union { m_uint32_t val; m_uint8_t b[4]; } u; u.val = *val; *val = (((m_uint32_t)u.b[0] << 24) | ((m_uint32_t)u.b[1] << 16) | ((m_uint32_t)u.b[2] << 8) | u.b[3]); } /** Convert startup-config header values from big endian to native. */ static void be_to_native_header_startup(struct fs_nvram_header_startup_config *head) { be_to_native16(&head->magic); be_to_native16(&head->format); be_to_native16(&head->checksum); be_to_native16(&head->unk1); be_to_native32(&head->start); be_to_native32(&head->end); be_to_native32(&head->len); be_to_native32(&head->unk2); be_to_native32(&head->unk3); be_to_native16(&head->unk4); be_to_native16(&head->unk5); be_to_native32(&head->uncompressed_len); } /** Convert private-config header values from big endian to native. */ static void be_to_native_header_private(struct fs_nvram_header_private_config *head) { be_to_native16(&head->magic); be_to_native16(&head->format); be_to_native32(&head->start); be_to_native32(&head->end); be_to_native32(&head->len); } /** Convert a 16 bit value from native to big endian. */ static inline void native_to_be16(m_uint16_t *val) { union { m_uint16_t val; m_uint8_t b[2]; } u; u.b[0] = (m_uint8_t)(*val >> 8); u.b[1] = (m_uint8_t)(*val & 0xFF); *val = u.val; } /** Convert a 32 bit value from native to big endian. */ static inline void native_to_be32(m_uint32_t *val) { union { m_uint32_t val; m_uint8_t b[4]; } u; u.b[0] = (m_uint8_t)(*val >> 24); u.b[1] = (m_uint8_t)(*val >> 16); u.b[2] = (m_uint8_t)(*val >> 8); u.b[3] = (m_uint8_t)(*val & 0xFF); *val = u.val; } /** Convert startup-config header values from native to big endian. */ static void native_to_be_header_startup(struct fs_nvram_header_startup_config *head) { native_to_be16(&head->magic); native_to_be16(&head->format); native_to_be16(&head->checksum); native_to_be16(&head->unk1); native_to_be32(&head->start); native_to_be32(&head->end); native_to_be32(&head->len); native_to_be32(&head->unk2); native_to_be32(&head->unk3); native_to_be16(&head->unk4); native_to_be16(&head->unk5); native_to_be32(&head->uncompressed_len); } /** Convert private-config header values from native to big endian. */ static void native_to_be_header_private(struct fs_nvram_header_private_config *head) { native_to_be16(&head->magic); native_to_be16(&head->format); native_to_be32(&head->start); native_to_be32(&head->end); native_to_be32(&head->len); } /** Uncompress data in .Z file format. * Adapted from 7zip's ZDecoder.cpp, which is licensed under LGPL 2.1. */ static int uncompress_LZC(const u_char *in_data, u_int in_len, u_char *out_data, u_int out_len) { #define LZC_MAGIC_1 0x1F #define LZC_MAGIC_2 0x9D #define LZC_NUM_BITS_MASK 0x1F #define LZC_BLOCK_MODE_MASK 0x80 #define LZC_NUM_BITS_MIN 9 #define LZC_NUM_BITS_MAX 16 m_uint16_t *parents = NULL; m_uint8_t *suffixes = NULL; m_uint8_t *stack = NULL; int err; if (in_len < 3 || (in_data == NULL && in_len > 0) || (out_data == NULL && out_len > 0)) return EINVAL; // invalid argument if (in_data[0] != LZC_MAGIC_1 || in_data[1] != LZC_MAGIC_2) return ENOTSUP; // no magic int maxbits = in_data[2] & LZC_NUM_BITS_MASK; if (maxbits < LZC_NUM_BITS_MIN || maxbits > LZC_NUM_BITS_MAX) return ENOTSUP; // maxbits not supported m_uint32_t numItems = 1 << maxbits; m_uint8_t blockMode = ((in_data[2] & LZC_BLOCK_MODE_MASK) != 0); parents = (m_uint16_t *)malloc(numItems * sizeof(*parents)); if (parents == NULL) goto err_memory; suffixes = (m_uint8_t *)malloc(numItems * sizeof(*suffixes)); if (suffixes == NULL) goto err_memory; stack = (m_uint8_t *)malloc(numItems * sizeof(*stack)); if (stack == NULL) goto err_memory; u_int in_pos = 3; u_int out_pos = 0; int numBits = LZC_NUM_BITS_MIN; m_uint32_t head = blockMode ? 257 : 256; m_uint8_t needPrev = 0; u_int bitPos = 0; u_int numBufBits = 0; u_char buf[LZC_NUM_BITS_MAX + 4]; parents[256] = 0; suffixes[256] = 0; for (;;) { if (numBufBits == bitPos) { u_int len = m_min(in_len - in_pos, numBits); memcpy(buf, in_data + in_pos, len); numBufBits = len << 3; bitPos = 0; in_pos += len; } u_int bytePos = bitPos >> 3; m_uint32_t symbol = buf[bytePos] | ((m_uint32_t)buf[bytePos + 1] << 8) | ((m_uint32_t)buf[bytePos + 2] << 16); symbol >>= (bitPos & 7); symbol &= (1 << numBits) - 1; bitPos += numBits; if (bitPos > numBufBits) break; if (symbol >= head) goto err_data; if (blockMode && symbol == 256) { numBufBits = bitPos = 0; numBits = LZC_NUM_BITS_MIN; head = 257; needPrev = 0; continue; } m_uint32_t cur = symbol; int i = 0; while (cur >= 256) { stack[i++] = suffixes[cur]; cur = parents[cur]; } stack[i++] = (u_char)cur; if (needPrev) { suffixes[head - 1] = (u_char)cur; if (symbol == head - 1) stack[0] = (u_char)cur; } do { if (out_pos < out_len) out_data[out_pos++] = stack[--i]; else i = 0; } while (i > 0); if (head < numItems) { needPrev = 1; parents[head++] = (m_uint16_t)symbol; if (head > ((m_uint32_t)1 << numBits)) { if (numBits < maxbits) { numBufBits = bitPos = 0; numBits++; } } } else { needPrev = 0; } } free(parents); free(suffixes); free(stack); return 0; err_memory: err = ENOMEM; // out of memory goto err_end; err_data: err = -1;//EIO; // invalid data goto err_end; err_end: if (parents) free(parents); if (suffixes) free(suffixes); if (stack) free(stack); return err; } //========================================================= // Private /** Retuns address of the specified filesystem offset */ static inline m_uint32_t fs_nvram_address_of(fs_nvram_t *fs, m_uint32_t offset) { if ((fs->flags & FS_NVRAM_FLAG_ADDR_ABSOLUTE)) return(fs->addr + offset); else return(offset - 8); } /** Retuns filesystem offset of the specified address */ static inline m_uint32_t fs_nvram_offset_of(fs_nvram_t *fs, m_uint32_t address) { if ((fs->flags & FS_NVRAM_FLAG_ADDR_ABSOLUTE)) return(address - fs->addr); else return(address + 8); } /** Retuns padding at the specified offset */ static inline u_int fs_nvram_padding_at(fs_nvram_t *fs, u_int offset) { u_int padding = 0; if (offset % 4 != 0) padding = fs->padding - offset % 4; return padding; } /** Read a 16-bit value from NVRAM. */ static inline m_uint16_t fs_nvram_read16(fs_nvram_t *fs,u_int offset) { m_uint16_t val; val = fs->read_byte(fs,offset) << 8; val |= fs->read_byte(fs,offset+1); return(val); } /** Write a 16-bit value to NVRAM. */ static void fs_nvram_write16(fs_nvram_t *fs,u_int offset,m_uint16_t val) { fs->write_byte(fs,offset,val >> 8); fs->write_byte(fs,offset+1,val & 0xFF); } /** Read a 32-bit value from NVRAM. */ static m_uint32_t fs_nvram_read32(fs_nvram_t *fs,u_int offset) { m_uint32_t val; val = fs->read_byte(fs,offset) << 24; val |= fs->read_byte(fs,offset+1) << 16; val |= fs->read_byte(fs,offset+2) << 8; val |= fs->read_byte(fs,offset+3); return(val); } /** Write a 32-bit value to NVRAM. */ _unused static void fs_nvram_write32(fs_nvram_t *fs,u_int offset,m_uint32_t val) { fs->write_byte(fs,offset,val >> 24); fs->write_byte(fs,offset+1,val >> 16); fs->write_byte(fs,offset+2,val >> 8); fs->write_byte(fs,offset+3,val & 0xFF); } /** Read a buffer from NVRAM. */ static void fs_nvram_memcpy_from(fs_nvram_t *fs, u_int offset, u_char *data, u_int len) { u_int i; for (i = 0; i < len; i++) { *data = fs->read_byte(fs, offset + i); data++; } } /** Write a buffer to NVRAM. */ static void fs_nvram_memcpy_to(fs_nvram_t *fs, u_int offset, const u_char *data, u_int len) { u_int i; for (i = 0; i < len; i++) { fs->write_byte(fs, offset + i, *data); data++; } } /** Clear section of NVRAM. */ static void fs_nvram_clear(fs_nvram_t *fs, u_int offset, u_int len) { u_int i; for (i = 0; i < len; i++) { fs->write_byte(fs, offset + i, 0); } } /** Update the filesystem checksum. */ static void fs_nvram_update_checksum(fs_nvram_t *fs) { u_int offset, count; m_uint32_t sum = 0; fs_nvram_write16(fs, sizeof(struct fs_nvram_header) + offsetof(struct fs_nvram_header_startup_config, checksum), 0x0000); offset = sizeof(struct fs_nvram_header); count = fs->len - offset; while (count > 1) { sum = sum + fs_nvram_read16(fs, offset); offset += 2; count -= sizeof(m_uint16_t); } if (count > 0) sum = sum + ((fs->read_byte(fs, offset)) << 8); while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); sum = ~sum; fs_nvram_write16(fs, sizeof(struct fs_nvram_header) + offsetof(struct fs_nvram_header_startup_config, checksum), sum); } /** Read data from NVRAM.*/ static inline u_char *fs_nvram_read_data(fs_nvram_t *fs, u_int offset, u_int len) { u_char *data; data = (u_char *)malloc(len + 1); if (data == NULL) return NULL; // out of memory fs_nvram_memcpy_from(fs, offset, data, len); data[len] = 0; return data; } /** Create a NVRAM filesystem. */ static void fs_nvram_create(fs_nvram_t *fs) { fs_nvram_clear(fs, 0, fs->len); fs_nvram_write16(fs, offsetof(struct fs_nvram_header, magic), FS_NVRAM_MAGIC_FILESYSTEM); fs_nvram_write16(fs, sizeof(struct fs_nvram_header) + offsetof(struct fs_nvram_header_startup_config, checksum), 0xFFFF); } /** Read a byte from the NVRAM filesystem. */ static m_uint8_t fs_nvram_read_byte(fs_nvram_t *fs, u_int offset) { m_uint8_t *ptr; ptr = (m_uint8_t *)fs->base + (offset << fs->shift); return(*ptr); } /** Write a byte to the NVRAM filesystem. */ static void fs_nvram_write_byte(fs_nvram_t *fs, u_int offset, m_uint8_t val) { m_uint8_t *ptr; ptr = (m_uint8_t *)fs->base + (offset << fs->shift); *ptr = val; } /** Returns the normal offset of the NVRAM filesystem with backup. */ static inline u_int fs_nvram_offset1_with_backup(fs_nvram_t *fs, u_int offset) { if (offset < FS_NVRAM_NORMAL_FILESYSTEM_BLOCK1) return(offset << fs->shift); else return((FS_NVRAM_BACKUP_FILESYSTEM_BLOCK1 + offset) << fs->shift); } /** Returns the backup offset of the NVRAM filesystem with backup. */ static inline u_int fs_nvram_offset2_with_backup(fs_nvram_t *fs, u_int offset) { if (offset < FS_NVRAM_BACKUP_FILESYSTEM_BLOCK1) return((fs->backup + offset) << fs->shift); else return((fs->len + offset) << fs->shift); } /** Read a byte from the NVRAM filesystem with backup. */ static m_uint8_t fs_nvram_read_byte_with_backup(fs_nvram_t *fs, u_int offset) { m_uint8_t *ptr1; ptr1 = (m_uint8_t *)fs->base + fs_nvram_offset1_with_backup(fs, offset); #if DEBUG_BACKUP { m_uint8_t *ptr2 = (m_uint8_t *)fs->base + fs_nvram_offset2_with_backup(fs, offset); if (*ptr1 != *ptr2) fprintf(stderr, "fs_nvram_read_byte_with_backup: data in backup filesystem doesn't match (offset=%u, offset1=%u, offset2=%u, normal=0x%02X, backup=0x%02X)\n", offset, fs_nvram_offset1_with_backup(fs, offset), fs_nvram_offset2_with_backup(fs, offset), *ptr1, *ptr2); } #endif return(*ptr1); } /** Write a byte to the NVRAM filesystem with backup. */ static void fs_nvram_write_byte_with_backup(fs_nvram_t *fs, u_int offset, m_uint8_t val) { m_uint8_t *ptr1; m_uint8_t *ptr2; ptr1 = (m_uint8_t *)fs->base + fs_nvram_offset1_with_backup(fs, offset); ptr2 = (m_uint8_t *)fs->base + fs_nvram_offset2_with_backup(fs, offset); *ptr1 = val; *ptr2 = val; } //========================================================= // Public /** Open NVRAM filesystem. Sets errno. */ fs_nvram_t *fs_nvram_open(u_char *base, size_t len, m_uint32_t addr, u_int flags) { struct fs_nvram *fs; size_t len_div = 1; if ((flags & FS_NVRAM_FLAG_SCALE_4)) len_div *= 4; // a quarter of the size if ((flags & FS_NVRAM_FLAG_WITH_BACKUP)) len_div *= 2; // half the size is for the backup if (base == NULL || len < sizeof(struct fs_nvram_header) * len_div || len % (FS_NVRAM_SECTOR_SIZE * len_div) != 0) { errno = EINVAL; return NULL; // invalid argument } fs = (struct fs_nvram*)malloc(sizeof(*fs)); if (fs == NULL) { errno = ENOMEM; return NULL; // out of memory } fs->base = base; fs->len = len / len_div; fs->addr = addr; fs->flags = flags; fs->shift = (flags & FS_NVRAM_FLAG_SCALE_4) ? 2 : 0; fs->padding = (flags & FS_NVRAM_FLAG_ALIGN_4_PAD_4) ? 4 : 8; fs->backup = (flags & FS_NVRAM_FLAG_WITH_BACKUP) ? m_min(fs->len, FS_NVRAM_NORMAL_FILESYSTEM_BLOCK1) : 0; fs->read_byte = (flags & FS_NVRAM_FLAG_WITH_BACKUP) ? fs_nvram_read_byte_with_backup : fs_nvram_read_byte; fs->write_byte = (flags & FS_NVRAM_FLAG_WITH_BACKUP) ? fs_nvram_write_byte_with_backup : fs_nvram_write_byte; if (FS_NVRAM_MAGIC_FILESYSTEM != fs_nvram_read16(fs, offsetof(struct fs_nvram_header, magic))) { if (!(flags & FS_NVRAM_FLAG_OPEN_CREATE)) { fs_nvram_close(fs); errno = FS_NVRAM_ERR_NO_MAGIC; return NULL; // no magic } fs_nvram_create(fs); } errno = 0; return fs; } /** Close NVRAM filesystem. */ void fs_nvram_close(fs_nvram_t *fs) { if (fs) free(fs); } /** Read startup-config and/or private-config from NVRAM. * Returns 0 on success. */ int fs_nvram_read_config(fs_nvram_t *fs, u_char **startup_config, size_t *startup_len, u_char **private_config, size_t *private_len) { int err; size_t off; u_char *buf; struct fs_nvram_header_startup_config startup_head; struct fs_nvram_header_private_config private_head; if (fs == NULL) return(EINVAL); // invalid argument // initial values if (startup_config) *startup_config = NULL; if (startup_len) *startup_len = 0; if (private_config) *private_config = NULL; if (private_len) *private_len = 0; // read headers off = sizeof(struct fs_nvram_header); fs_nvram_memcpy_from(fs, off, (u_char *)&startup_head, sizeof(startup_head)); be_to_native_header_startup(&startup_head); if (FS_NVRAM_MAGIC_STARTUP_CONFIG != startup_head.magic) return(0); // done, no startup-config and no private-config off = fs_nvram_offset_of(fs, startup_head.end); off += fs_nvram_padding_at(fs, off); fs_nvram_memcpy_from(fs, off, (u_char *)&private_head, sizeof(private_head)); be_to_native_header_private(&private_head); // read startup-config if (FS_NVRAM_FORMAT_RAW == startup_head.format) { if (startup_config) { off = fs_nvram_offset_of(fs, startup_head.start); *startup_config = fs_nvram_read_data(fs, off, startup_head.len); if (*startup_config == NULL) goto err_memory; } if (startup_len) *startup_len = startup_head.len; } else if (FS_NVRAM_FORMAT_LZC == startup_head.format) { if (startup_config) { off = fs_nvram_offset_of(fs, startup_head.start); *startup_config = (u_char *)malloc(startup_head.uncompressed_len + 1); if (*startup_config == NULL) goto err_memory; buf = fs_nvram_read_data(fs, off, startup_head.len); if (buf == NULL) goto err_memory; err = uncompress_LZC(buf, startup_head.len, *startup_config, startup_head.uncompressed_len); if (err) goto err_uncompress; (*startup_config)[startup_head.uncompressed_len] = 0; free(buf); } if (startup_len) *startup_len = startup_head.uncompressed_len; } else { goto err_format; } // read private-config if (FS_NVRAM_MAGIC_PRIVATE_CONFIG != private_head.magic) return(0); // done, no private-config if (FS_NVRAM_FORMAT_RAW == private_head.format) { if (private_config) { off = fs_nvram_offset_of(fs, private_head.start); *private_config = fs_nvram_read_data(fs, off, private_head.len); if (*private_config == NULL) goto err_memory; } if (private_len) *private_len = private_head.len; } else { goto err_format; } return(0); // done err_memory: err = ENOMEM; // out of memory goto err_end; err_format: err = ENOTSUP; // unsupported format goto err_end; err_uncompress: free(buf); goto err_end; err_end: if (startup_config && *startup_config) { free(*startup_config); *startup_config = NULL; } if (startup_len) *startup_len = 0; if (private_config && *private_config) { free(*private_config); *private_config = NULL; } if (private_len) *private_len = 0; return(err); } /** Write startup-config and private-config to NVRAM. * Returns 0 on success. */ int fs_nvram_write_config(fs_nvram_t *fs, const u_char *startup_config, size_t startup_len, const u_char *private_config, size_t private_len) { size_t len; size_t off; size_t padding; struct fs_nvram_header_startup_config startup_head; struct fs_nvram_header_private_config private_head; if (fs == NULL || (startup_config == NULL && startup_len > 0) || (private_config == NULL && private_len > 0)) return(EINVAL); // invalid argument // check space and padding // XXX ignores normal files in NVRAM len = sizeof(struct fs_nvram_header) + sizeof(struct fs_nvram_header_startup_config) + startup_len; padding = fs_nvram_padding_at(fs, len); len += padding + sizeof(struct fs_nvram_header_private_config) + private_len; if (fs->len < len) return(ENOSPC); // not enough space // old length len = sizeof(struct fs_nvram_header); if (FS_NVRAM_MAGIC_STARTUP_CONFIG == fs_nvram_read16(fs, len + offsetof(typeof(startup_head), magic))) { len += fs_nvram_read32(fs, len + offsetof(typeof(startup_head), len)); if (len % 4 != 0) len += 8 - len % 4; if (FS_NVRAM_MAGIC_PRIVATE_CONFIG == fs_nvram_read16(fs, len + offsetof(typeof(private_head), magic))) len += fs_nvram_read32(fs, len + offsetof(typeof(private_head), len)); } if (len % FS_NVRAM_SECTOR_SIZE != 0) len += FS_NVRAM_SECTOR_SIZE - len % FS_NVRAM_SECTOR_SIZE; // whole sector if (len > fs->len) len = fs->len; // should never happen // prepare headers memset(&startup_head, 0, sizeof(startup_head)); startup_head.magic = FS_NVRAM_MAGIC_STARTUP_CONFIG; startup_head.format = FS_NVRAM_FORMAT_RAW; startup_head.unk1 = (fs->flags & FS_NVRAM_FLAGS_UNK1_0C01) ? 0x0C01 : (fs->flags & FS_NVRAM_FLAGS_UNK1_0C03) ? 0x0C03 : 0x0C04; startup_head.start = fs_nvram_address_of(fs, sizeof(struct fs_nvram_header) + sizeof(struct fs_nvram_header_startup_config)); startup_head.end = startup_head.start + startup_len; startup_head.len = startup_len; memset(&private_head, 0, sizeof(private_head)); private_head.magic = FS_NVRAM_MAGIC_PRIVATE_CONFIG; private_head.format = FS_NVRAM_FORMAT_RAW; private_head.start = startup_head.end + padding + sizeof(struct fs_nvram_header_private_config); private_head.end = private_head.start + private_len; private_head.len = private_len; native_to_be_header_startup(&startup_head); native_to_be_header_private(&private_head); // write data off = sizeof(struct fs_nvram_header); fs_nvram_memcpy_to(fs, off, (const u_char *)&startup_head, sizeof(struct fs_nvram_header_startup_config)); off += sizeof(struct fs_nvram_header_startup_config); fs_nvram_memcpy_to(fs, off, startup_config, startup_len); off += startup_len; fs_nvram_clear(fs, off, padding); off += padding; fs_nvram_memcpy_to(fs, off, (const u_char *)&private_head, sizeof(struct fs_nvram_header_private_config)); off += sizeof(struct fs_nvram_header_private_config); fs_nvram_memcpy_to(fs, off, private_config, private_len); off += private_len; if (off < len) fs_nvram_clear(fs, off, len - off); fs_nvram_update_checksum(fs); return(0); } /** Returns the number of sectors in the NVRAM filesystem. */ size_t fs_nvram_num_sectors(fs_nvram_t *fs) { if (fs == NULL) return(0); return( fs->len / FS_NVRAM_SECTOR_SIZE ); } /** Verify the contents of the filesystem. * Returns 0 on success. */ int fs_nvram_verify(fs_nvram_t *fs, u_int what) { size_t offset; if (fs == NULL) return(EINVAL); // invalid argument if ((what & FS_NVRAM_VERIFY_BACKUP)) { if ((fs->flags & FS_NVRAM_FLAG_WITH_BACKUP)) { for (offset = 0; offset < fs->len; offset++) { m_uint8_t b1 = fs->base[fs_nvram_offset1_with_backup(fs, offset)]; m_uint8_t b2 = fs->base[fs_nvram_offset2_with_backup(fs, offset)]; if (b1 != b2) return(FS_NVRAM_ERR_BACKUP_MISSMATCH); // data is corrupted? length is wrong? } } } if ((what & FS_NVRAM_VERIFY_CONFIG)) { struct fs_nvram_header_startup_config startup_head; struct fs_nvram_header_private_config private_head; offset = sizeof(struct fs_nvram_header); fs_nvram_memcpy_from(fs, offset, (u_char *)&startup_head, sizeof(startup_head)); be_to_native_header_startup(&startup_head); if (FS_NVRAM_MAGIC_STARTUP_CONFIG == startup_head.magic) { if (startup_head.end != startup_head.start + startup_head.len || startup_head.len > fs->len) return(FS_NVRAM_ERR_INVALID_ADDRESS); // data is corrupted? if (startup_head.start < fs->addr || startup_head.end > fs->addr + fs->len) return(FS_NVRAM_ERR_INVALID_ADDRESS); // fs->addr has the wrong value? offset = fs_nvram_offset_of(fs, startup_head.end); offset += fs_nvram_padding_at(fs, offset); if (fs->len < offset + sizeof(private_head)) return(FS_NVRAM_ERR_INVALID_ADDRESS); // data is corrupted? fs_nvram_memcpy_from(fs, offset, (u_char *)&private_head, sizeof(private_head)); be_to_native_header_private(&private_head); if (FS_NVRAM_MAGIC_PRIVATE_CONFIG == private_head.magic) { if (private_head.end != private_head.start + private_head.len || private_head.len > fs->len) return(FS_NVRAM_ERR_INVALID_ADDRESS); // data is corrupted? if (private_head.start < fs->addr || private_head.end > fs->addr + fs->len) return(FS_NVRAM_ERR_INVALID_ADDRESS); // fs->addr has the wrong value? if (private_head.end != private_head.start + private_head.len) return(FS_NVRAM_ERR_INVALID_ADDRESS); // data is corrupted? } } } return(0); } dynamips-0.2.14/common/fs_nvram.h000066400000000000000000000175761241034141600167120ustar00rootroot00000000000000/** @file * @brief Cisco NVRAM filesystem. * * Format was inferred by analysing the NVRAM data after changing/erasing stuff. * All data is big endian. * * Based on the platforms c1700/c2600/c2692/c3600/c3725/c3745/c7200/c6msfc1. */ /* * Copyright (c) 2013 Flávio J. Saraiva */ #ifndef FS_NVRAM_H__ #define FS_NVRAM_H__ #include "utils.h" /////////////////////////////////////////////////////////// // Filesystem /** Size of a sector. */ #define FS_NVRAM_SECTOR_SIZE 0x400 /** Sector contains the start of the file .*/ #define FS_NVRAM_FLAG_FILE_START 0x01 /** Sector contains the end of the file. */ #define FS_NVRAM_FLAG_FILE_END 0x02 /** File does not have read or write permission. */ #define FS_NVRAM_FLAG_FILE_NO_RW #define FS_NVRAM_MAGIC_FILESYSTEM 0xF0A5 #define FS_NVRAM_MAGIC_STARTUP_CONFIG 0xABCD #define FS_NVRAM_MAGIC_PRIVATE_CONFIG 0xFEDC #define FS_NVRAM_MAGIC_FILE_SECTOR 0xDCBA /** Data is not compressed. */ #define FS_NVRAM_FORMAT_RAW 1 /** Data is compressed in .Z file format. */ #define FS_NVRAM_FORMAT_LZC 2 /** Magic not found - custom errno code. */ #define FS_NVRAM_ERR_NO_MAGIC ( - FS_NVRAM_MAGIC_FILESYSTEM ) /** Backup data doesn't match. */ #define FS_NVRAM_ERR_BACKUP_MISSMATCH ( FS_NVRAM_ERR_NO_MAGIC - 1 ) /** Invalid address found in filesystem. */ #define FS_NVRAM_ERR_INVALID_ADDRESS ( FS_NVRAM_ERR_NO_MAGIC - 2 ) /* Size of blocks in a NVRAM filesystem with backup (total size is 0x4C000 in c3745) */ #define FS_NVRAM_NORMAL_FILESYSTEM_BLOCK1 0x20000 #define FS_NVRAM_BACKUP_FILESYSTEM_BLOCK1 0x1C000 /////////////////////////////////////////////////////////// // Optional flags for open /** Create NVRAM filesystem if no magic. */ #define FS_NVRAM_FLAG_OPEN_CREATE 0x0001 /** Don't scale byte offsets. (default, ignored) */ #define FS_NVRAM_FLAG_NO_SCALE 0x0010 /** Scale byte offsets by 4. */ #define FS_NVRAM_FLAG_SCALE_4 0x0020 /** Align the private-config header to 4 bytes with a padding of 7/6/5/0 bytes. (default, ignored) */ #define FS_NVRAM_FLAG_ALIGN_4_PAD_8 0x0040 /** Align the private-config header to 4 bytes with a padding of 3/2/1/0 bytes. */ #define FS_NVRAM_FLAG_ALIGN_4_PAD_4 0x0080 /** Has a backup filesystem. * Data is not continuous: * up to 0x20000 bytes of the normal filesystem; * up to 0x1C000 bytes of the backup filesystem; * rest of normal filesystem; * rest of backup filesystem. */ #define FS_NVRAM_FLAG_WITH_BACKUP 0x0100 /** Use addresses relative to the the end of the filesystem magic. (default, ignored) * Add 8 to get the raw offset. */ #define FS_NVRAM_FLAG_ADDR_RELATIVE 0x0200 /** Use absolute addresses. * The base address of the filesystem is the addr argument. */ #define FS_NVRAM_FLAG_ADDR_ABSOLUTE 0x0400 /** Value of unk1 is set to 0x0C04. (default, ignored) */ #define FS_NVRAM_FLAGS_UNK1_0C04 0x0800 /** Value of unk1 is set to 0x0C03. */ #define FS_NVRAM_FLAGS_UNK1_0C03 0x1000 /** Value of unk1 is set to 0x0C01. */ #define FS_NVRAM_FLAGS_UNK1_0C01 0x2000 #define FS_NVRAM_FORMAT_MASK 0x3FF0 /** Default filesystem format. (default, ignored) */ #define FS_NVRAM_FORMAT_DEFAULT ( FS_NVRAM_FLAG_NO_SCALE | FS_NVRAM_FLAG_ALIGN_4_PAD_8 | FS_NVRAM_FLAG_ADDR_RELATIVE | FS_NVRAM_FLAGS_UNK1_0C04 ) /** Filesystem format for the c2600 platform. */ #define FS_NVRAM_FORMAT_SCALE_4 ( FS_NVRAM_FLAG_SCALE_4 | FS_NVRAM_FLAG_ALIGN_4_PAD_8 | FS_NVRAM_FLAG_ADDR_RELATIVE | FS_NVRAM_FLAGS_UNK1_0C03 ) /** Filesystem format for the c3725/c3745 platforms. */ #define FS_NVRAM_FORMAT_WITH_BACKUP ( FS_NVRAM_FLAG_NO_SCALE | FS_NVRAM_FLAG_ALIGN_4_PAD_4 | FS_NVRAM_FLAG_ADDR_RELATIVE | FS_NVRAM_FLAGS_UNK1_0C04 | FS_NVRAM_FLAG_WITH_BACKUP ) /** Filesystem format for the c7000 platform. */ #define FS_NVRAM_FORMAT_ABSOLUTE ( FS_NVRAM_FLAG_NO_SCALE | FS_NVRAM_FLAG_ALIGN_4_PAD_4 | FS_NVRAM_FLAG_ADDR_ABSOLUTE | FS_NVRAM_FLAGS_UNK1_0C04 ) /** Filesystem format for the c6msfc1 platform. */ #define FS_NVRAM_FORMAT_ABSOLUTE_C6 ( FS_NVRAM_FLAG_NO_SCALE | FS_NVRAM_FLAG_ALIGN_4_PAD_4 | FS_NVRAM_FLAG_ADDR_ABSOLUTE | FS_NVRAM_FLAGS_UNK1_0C01 ) /////////////////////////////////////////////////////////// // Flags for verify /** Verify backup data. */ #define FS_NVRAM_VERIFY_BACKUP 0x01 /** Verify config data. */ #define FS_NVRAM_VERIFY_CONFIG 0x02 // TODO Verify file data. //#define FS_NVRAM_VERIFY_FILES 0x04 /** Verify everything. */ #define FS_NVRAM_VERIFY_ALL 0x07 /////////////////////////////////////////////////////////// /** Header of the NVRAM filesystem. * When empty, only this magic and the checksum are filled. * @see nvram_header_startup_config * @see nvram_header_private_config */ struct fs_nvram_header { /** Padding. */ u_char padding[6]; /** Magic value 0xF0A5. */ m_uint16_t magic; // Following data: // - nvram_header_startup_config // - startup-config data // - padding to align the next header to a multiple of 4 // - nvram_header_private_config // - private-config data // - padding till end of sector // - the next 2 sectors are reserved for expansion of config files // - the rest of sectors are for normal files } __attribute__((__packed__)); /** Header of special file startup-config. * @see nvram_header */ struct fs_nvram_header_startup_config { /** Magic value 0xABCD. */ m_uint16_t magic; /** Format of the data. * 0x0001 - raw data; * 0x0002 - .Z compressed (12 bits); */ m_uint16_t format; /** Checksum of filesystem data. (all data after the filesystem magic) */ m_uint16_t checksum; /** 0x0C04 - maybe maximum amount of free space that will be reserved? */ m_uint16_t unk1; /** Address of the data. */ m_uint32_t start; /** Address right after the data. */ m_uint32_t end; /** Length of block. */ m_uint32_t len; /** 0x00000000 */ m_uint32_t unk2; /** 0x00000000 if raw data, 0x00000001 if compressed */ m_uint32_t unk3; /** 0x0000 if raw data, 0x0001 if compressed */ m_uint16_t unk4; /** 0x0000 */ m_uint16_t unk5; /** Length of uncompressed data, 0 if raw data. */ m_uint32_t uncompressed_len; // startup-config data comes after this header } __attribute__((__packed__)); /** Header of special file private-config. * @see nvram_header */ struct fs_nvram_header_private_config { /** Magic value 0xFEDC. */ m_uint16_t magic; /** Format of the file. * 0x0001 - raw data; */ m_uint16_t format; /** Address of the data. */ m_uint32_t start; /** Address right after the data. */ m_uint32_t end; /** Length of block. */ m_uint32_t len; // private-config data comes after this header } __attribute__((__packed__)); /** Sector containing file data. */ struct fs_nvram_file_sector { /** Magic value 0xDCBA */ m_uint16_t magic; /** Next sector with data, 0 by default */ m_uint16_t next_sector; /** Flags. * @see FS_NVRAM_FLAG_FILE_START * @see FS_NVRAM_FLAG_FILE_END * @see FS_NVRAM_FLAG_FILE_NO_RW */ m_uint16_t flags; /** Amount of data in this sector. */ m_uint16_t length; /** File name, always NUL-terminated. */ char filename[24]; /** File data. */ u_char data[992]; } __attribute__((__packed__)); typedef struct fs_nvram fs_nvram_t; /* Functions */ fs_nvram_t *fs_nvram_open(u_char *base, size_t len, m_uint32_t addr, u_int flags); void fs_nvram_close(fs_nvram_t *fs); int fs_nvram_read_config(fs_nvram_t *fs, u_char **startup_config, size_t *startup_len, u_char **private_config, size_t *private_len); int fs_nvram_write_config(fs_nvram_t *fs, const u_char *startup_config, size_t startup_len, const u_char *private_config, size_t private_len); size_t fs_nvram_num_sectors(fs_nvram_t *fs); // TODO read/write file sectors int fs_nvram_verify(fs_nvram_t *fs, u_int what); #endif dynamips-0.2.14/common/gen_eth.c000066400000000000000000000054341241034141600164710ustar00rootroot00000000000000/* * Copyright (c) 2006 Christophe Fillot. * E-mail: cf@utc.fr * * gen_eth.c: module used to send/receive Ethernet packets. * * Use libpcap (0.9+) or WinPcap (0.4alpha1+) to receive and send packets. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CYGWIN /* Needed for pcap_open() flags */ #define HAVE_REMOTE #endif #include "pcap.h" #include "utils.h" #include "gen_eth.h" /* Initialize a generic ethernet driver */ pcap_t *gen_eth_init(char *device) { char pcap_errbuf[PCAP_ERRBUF_SIZE]; pcap_t *p; #ifndef CYGWIN if (!(p = pcap_open_live(device,65535,TRUE,10,pcap_errbuf))) goto pcap_error; pcap_setdirection(p,PCAP_D_INOUT); #ifdef BIOCFEEDBACK { int on = 1; ioctl(pcap_fileno(p), BIOCFEEDBACK, &on); } #endif #else p = pcap_open(device,65535, PCAP_OPENFLAG_PROMISCUOUS | PCAP_OPENFLAG_NOCAPTURE_LOCAL | PCAP_OPENFLAG_MAX_RESPONSIVENESS | PCAP_OPENFLAG_NOCAPTURE_RPCAP, 10,NULL,pcap_errbuf); if (!p) goto pcap_error; #endif return p; pcap_error: fprintf(stderr,"gen_eth_init: unable to open device '%s' " "with PCAP (%s)\n",device,pcap_errbuf); return NULL; } /* Free resources of a generic ethernet driver */ void gen_eth_close(pcap_t *p) { pcap_close(p); } /* Send an ethernet frame */ ssize_t gen_eth_send(pcap_t *p,char *buffer,size_t len) { return(pcap_sendpacket(p,(u_char *)buffer,len)); } /* Receive an ethernet frame */ ssize_t gen_eth_recv(pcap_t *p,char *buffer,size_t len) { struct pcap_pkthdr pkt_info; u_char *pkt_ptr; ssize_t rlen; if (!(pkt_ptr = (u_char *)pcap_next(p,&pkt_info))) return(-1); rlen = m_min(len,pkt_info.caplen); memcpy(buffer,pkt_ptr,rlen); return(rlen); } /* Display Ethernet interfaces of the system */ int gen_eth_show_dev_list(void) { char pcap_errbuf[PCAP_ERRBUF_SIZE]; pcap_if_t *dev_list,*dev; int res; printf("Network device list:\n\n"); #ifndef CYGWIN res = pcap_findalldevs(&dev_list,pcap_errbuf); #else res = pcap_findalldevs_ex(PCAP_SRC_IF_STRING,NULL,&dev_list,pcap_errbuf); #endif if (res < 0) { fprintf(stderr,"PCAP: unable to find device list (%s)\n",pcap_errbuf); return(-1); } for(dev=dev_list;dev;dev=dev->next) { printf(" %s : %s\n", dev->name, dev->description ? dev->description : "no info provided"); } printf("\n"); pcap_freealldevs(dev_list); return(0); } dynamips-0.2.14/common/gen_eth.h000066400000000000000000000012771241034141600164770ustar00rootroot00000000000000/* * Copyright (c) 2006 Christophe Fillot. * E-mail: cf@utc.fr * * gen_eth: module used to send/receive Ethernet packets. * * Use libpcap (0.9+) to receive and send packets. */ #ifndef __GEN_ETH_H__ #define __GEN_ETH_H__ 1 #include #include /* Initialize a generic ethernet driver */ pcap_t *gen_eth_init(char *device); /* Free resources of a generic ethernet driver */ void gen_eth_close(pcap_t *p); /* Send an ethernet frame */ ssize_t gen_eth_send(pcap_t *p,char *buffer,size_t len); /* Receive an ethernet frame */ ssize_t gen_eth_recv(pcap_t *p,char *buffer,size_t len); /* Display Ethernet interfaces of the system */ int gen_eth_show_dev_list(void); #endif dynamips-0.2.14/common/gen_uuid.c000066400000000000000000000011671241034141600166560ustar00rootroot00000000000000/* * Copyright (c) 2008 Christophe Fillot. * E-mail: cf@utc.fr * * UUID handling. */ #include #include #include #include #include #include "gen_uuid.h" /* Our local UUID */ static uuid_t local_uuid; /* Get local ID */ void gen_uuid_get_local(uuid_t dst) { uuid_copy(dst,local_uuid); } /* Compare UUID */ int gen_uuid_compare(uuid_t id) { return(uuid_compare(local_uuid,id)); } /* Initialize UUID */ void gen_uuid_init(void) { char buffer[40]; uuid_generate(local_uuid); uuid_unparse(local_uuid,buffer); printf("Local UUID: %s\n\n",buffer); } dynamips-0.2.14/common/gen_uuid.h000066400000000000000000000005151241034141600166570ustar00rootroot00000000000000/* * Copyright (c) 2008 Christophe Fillot. * E-mail: cf@utc.fr * * UUID handling. */ #ifndef __GEN_UUID_H__ #define __GEN_UUID_H__ 1 #include /* Get local ID */ void gen_uuid_get_local(uuid_t dst); /* Compare UUID */ int gen_uuid_compare(uuid_t id); /* Initialize UUID */ void gen_uuid_init(void); #endif dynamips-0.2.14/common/get_cpu_time.c000066400000000000000000000050671241034141600175260ustar00rootroot00000000000000/* * Author: David Robert Nadeau * Site: http://NadeauSoftware.com/ * License: Creative Commons Attribution 3.0 Unported License * http://creativecommons.org/licenses/by/3.0/deed.en_US */ #if defined(_WIN32) #include #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) #include #include #include #include #else #error "Unable to define get_cpu_time() for an unknown OS." #endif /** * Returns the amount of CPU time used by the current process, * in seconds, or -1.0 if an error occurred. */ double get_cpu_time() { #if defined(_WIN32) /* Windows -------------------------------------------------- */ FILETIME createTime; FILETIME exitTime; FILETIME kernelTime; FILETIME userTime; if ( GetProcessTimes( GetCurrentProcess( ), &createTime, &exitTime, &kernelTime, &userTime ) != -1 ) { SYSTEMTIME userSystemTime; if ( FileTimeToSystemTime( &userTime, &userSystemTime ) != -1 ) return (double)userSystemTime.wHour * 3600.0 + (double)userSystemTime.wMinute * 60.0 + (double)userSystemTime.wSecond + (double)userSystemTime.wMilliseconds / 1000.0; } #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) /* AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris --------- */ #if _POSIX_TIMERS > 0 /* Prefer high-res POSIX timers, when available. */ { clockid_t id; struct timespec ts; #if _POSIX_CPUTIME > 0 /* Clock ids vary by OS. Query the id, if possible. */ if ( clock_getcpuclockid( 0, &id ) == -1 ) #endif #if defined(CLOCK_PROCESS_CPUTIME_ID) /* Use known clock id for AIX, Linux, or Solaris. */ id = CLOCK_PROCESS_CPUTIME_ID; #elif defined(CLOCK_VIRTUAL) /* Use known clock id for BSD or HP-UX. */ id = CLOCK_VIRTUAL; #else id = (clockid_t)-1; #endif if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 ) return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0; } #endif #if defined(RUSAGE_SELF) { struct rusage rusage; if ( getrusage( RUSAGE_SELF, &rusage ) != -1 ) return (double)rusage.ru_utime.tv_sec + (double)rusage.ru_utime.tv_usec / 1000000.0; } #endif #if defined(_SC_CLK_TCK) { const double ticks = (double)sysconf( _SC_CLK_TCK ); struct tms tms; if ( times( &tms ) != (clock_t)-1 ) return (double)tms.tms_utime / ticks; } #endif #if defined(CLOCKS_PER_SEC) { clock_t cl = clock( ); if ( cl != (clock_t)-1 ) return (double)cl / (double)CLOCKS_PER_SEC; } #endif #endif return -1.0; /* Failed. */ } dynamips-0.2.14/common/get_cpu_time.h000066400000000000000000000003001241034141600175140ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * */ #ifndef __GET_CPU_TIME_H__ #define __GET_CPU_TIME_H__ double get_cpu_time(); #endif dynamips-0.2.14/common/hash.c000066400000000000000000000115351241034141600160020ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Generic Hash Tables. */ #include #include #include #include #include #include #include #include "utils.h" #include "hash.h" /* Compare two strings */ int str_equal(void *s1, void *s2) { return(strcmp((char *)s1, (char *)s2) == 0); } /* Hash function for a string */ u_int str_hash(void *str) { char *p,*s = (char *)str; u_int h,g; for(h=0,p=s;*p!='\0';p+=1) { h = (h << 4) + *p; if ((g = h & 0xf0000000)) { h = h ^ (g >> 24); h = h ^ g; } } return(h); } /* Compare two integers (yes, it's stupid) */ int int_equal(void *i1, void *i2) { return(((int)(long)i1) == (int)(long)i2); } /* Hash function for an integer (see above) */ u_int int_hash(void *i) { u_int val = (u_int)(long)i; return(val ^ (val >> 16)); } /* Compare two u64 (yes, it's stupid) */ int u64_equal(void *i1, void *i2) { return((*(m_uint64_t *)i1) == (*(m_uint64_t *)i2)); } /* Hash function for an u64 (see above) */ u_int u64_hash(void *i) { m_uint64_t val = *(m_uint64_t *)i; return((u_int)(val ^ (val >> 32))); } /* Compare 2 pointers */ int ptr_equal(void *i1,void *i2) { return((int)(i1 == i2)); } /* Hash function for a pointer (see above) */ u_int ptr_hash(void *i) { m_uint64_t val = (m_uint64_t)(m_iptr_t)i; return((u_int)((val & 0xFFFF) ^ ((val >> 24) & 0xFFFF) ^ ((val >> 48) & 0xFFFF))); } /* Free memory used by a node */ static inline void hash_node_free(hash_node_t *node) { free(node); } /* Allocate memory for a new node */ static hash_node_t *hash_node_alloc(hash_table_t *ht,void *key,void *value) { hash_node_t *node; node = malloc(sizeof(*node)); assert(node!=NULL); node->key = key; node->value = value; node->next = NULL; return node; } /* Create a new hash table */ hash_table_t *hash_table_create(hash_fcompute hash_func,hash_fcompare key_cmp, int hash_size) { hash_table_t *ht; if (!hash_func || (hash_size <= 0)) return NULL; ht = malloc(sizeof(*ht)); assert(ht!=NULL); memset(ht,0,sizeof(*ht)); ht->hash_func = hash_func; ht->key_cmp = key_cmp; ht->size = hash_size; ht->nodes = calloc(ht->size,sizeof(hash_node_t *)); assert(ht->nodes!=NULL); return ht; } /* Delete an existing Hash Table */ void hash_table_delete(hash_table_t *ht) { hash_node_t *node, *node_next; u_int hash_val; if (!ht) return; for (hash_val = 0; hash_val < ht->size; hash_val++) { for (node = ht->nodes[hash_val]; node; node = node_next) { node_next = node->next; hash_node_free(node); } ht->nodes[hash_val] = NULL; } free(ht->nodes); free(ht); } /* Insert a new (key,value). If key already exists in table, replace value */ int hash_table_insert(hash_table_t *ht,void *key,void *value) { hash_node_t *node; u_int hash_val; assert(ht!=NULL); hash_val = ht->hash_func(key) % ht->size; for(node=ht->nodes[hash_val];node;node=node->next) if (ht->key_cmp(node->key,key)) { node->value = value; return(0); } node = hash_node_alloc(ht,key,value); node->next = ht->nodes[hash_val]; ht->nodes[hash_val] = node; ht->nnodes++; return(0); } /* Remove a pair (key,value) from an hash table */ void *hash_table_remove(hash_table_t *ht,void *key) { hash_node_t **node,*tmp; u_int hash_val; void *value; assert(ht!=NULL); hash_val = ht->hash_func(key) % ht->size; for(node=&ht->nodes[hash_val];*node;node=&(*node)->next) if (ht->key_cmp((*node)->key,key)) { tmp = *node; value = tmp->value; *node = tmp->next; hash_node_free(tmp); return(value); } return NULL; } /* Hash Table Lookup */ void *hash_table_lookup(hash_table_t *ht,void *key) { hash_node_t *node; u_int hash_val; assert(ht!=NULL); hash_val = ht->hash_func(key) % ht->size; for(node=ht->nodes[hash_val];node;node=node->next) if (ht->key_cmp(node->key,key)) return node->value; return NULL; } /* Hash Table Lookup - key direct comparison */ void *hash_table_lookup_dcmp(hash_table_t *ht,void *key) { hash_node_t *node; u_int hash_val; assert(ht!=NULL); hash_val = ht->hash_func(key) % ht->size; for(node=ht->nodes[hash_val];node;node=node->next) if (node->key == key) return node->value; return NULL; } /* Call the specified function for each node found in hash table */ int hash_table_foreach(hash_table_t *ht,hash_fforeach user_fn,void *opt_arg) { hash_node_t *node; int i; assert(ht!=NULL); for(i=0;isize;i++) for(node=ht->nodes[i];node;node=node->next) user_fn(node->key,node->value,opt_arg); return(0); } dynamips-0.2.14/common/hash.h000066400000000000000000000046771241034141600160200ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Generic Hash Tables. */ #ifndef __HASH_H__ #define __HASH_H__ 1 #include #include "utils.h" /* Key computation function */ typedef u_int (*hash_fcompute)(void *key); /* Comparison function for 2 keys */ typedef int (*hash_fcompare)(void *key1,void *key2); /* User function to call when using hash_table_foreach */ typedef void (*hash_fforeach)(void *key,void *value,void *opt_arg); /* Hash element (pair key,value) */ typedef struct hash_node hash_node_t; struct hash_node { void *key, *value; hash_node_t *next; }; /* Hash Table definition */ typedef struct hash_table hash_table_t; struct hash_table { int size,nnodes; hash_node_t **nodes; hash_fcompute hash_func; hash_fcompare key_cmp; }; #define hash_string_create(hash_size) \ hash_table_create(str_hash,str_equal,hash_size) #define hash_int_create(hash_size) \ hash_table_create(int_hash,int_equal,hash_size) #define hash_u64_create(hash_size) \ hash_table_create(u64_hash,u64_equal,hash_size) #define hash_ptr_create(hash_size) \ hash_table_create(ptr_hash,ptr_equal,hash_size) #define HASH_TABLE_FOREACH(i,ht,hn) \ for(i=0;isize;i++) \ for(hn=ht->nodes[i];hn;hn=hn->next) /* Create a new hash table */ hash_table_t *hash_table_create(hash_fcompute hash_func,hash_fcompare key_cmp, int hash_size); /* Delete an existing Hash Table */ void hash_table_delete(hash_table_t *ht); /* Insert a new (key,value). If key already exist in table, replace value */ int hash_table_insert(hash_table_t *ht,void *key,void *value); /* Remove a pair (key,value) from an hash table */ void *hash_table_remove(hash_table_t *ht,void *key); /* Hash Table Lookup */ void *hash_table_lookup(hash_table_t *ht,void *key); /* Call the specified function for each node found in hash table */ int hash_table_foreach(hash_table_t *ht,hash_fforeach user_fn,void *opt_arg); /* Hash Table Lookup - key direct comparison */ void *hash_table_lookup_dcmp(hash_table_t *ht,void *key); /* Hash Functions for strings */ int str_equal(void *s1,void *s2); u_int str_hash(void *str); /* Hash Functions for integers */ int int_equal(void *i1,void *i2); u_int int_hash(void *i); /* Hash Functions for u64 */ int u64_equal(void *i1,void *i2); u_int u64_hash(void *i); /* Hash Function for pointers */ int ptr_equal(void *i1,void *i2); u_int ptr_hash(void *i); #endif dynamips-0.2.14/common/hv_atm_bridge.c000066400000000000000000000121021241034141600176400ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor ATM switch routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "atm_bridge.h" #include "crc.h" #include "net_io.h" #include "registry.h" #include "hypervisor.h" /* Create a new ATM bridge object */ static int cmd_create(hypervisor_conn_t *conn,int argc,char *argv[]) { atm_bridge_t *t; if (!(t = atm_bridge_create(argv[0]))) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to create ATM bridge '%s'", argv[0]); return(-1); } atm_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"ATM bridge '%s' created",argv[0]); return(0); } /* Rename an ATM bridge */ static int cmd_rename(hypervisor_conn_t *conn,int argc,char *argv[]) { atm_bridge_t *t; char *newname; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ATM_BRIDGE))) return(-1); if (registry_exists(argv[1],OBJ_TYPE_ATM_BRIDGE)) { atm_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename ATM bridge '%s', '%s' already exists", argv[0],argv[1]); return(-1); } if(!(newname = strdup(argv[1]))) { atm_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename ATM bridge '%s', out of memory", argv[0]); return(-1); } if (registry_rename(argv[0],newname,OBJ_TYPE_ATM_BRIDGE)) { free(newname); atm_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename ATM bridge '%s'", argv[0]); return(-1); } free(t->name); t->name = newname; atm_bridge_release(argv[1]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"ATM bridge '%s' renamed to '%s'",argv[0],argv[1]); return(0); } /* Delete an ATM bridge */ static int cmd_delete(hypervisor_conn_t *conn,int argc,char *argv[]) { int res; res = atm_bridge_delete(argv[0]); if (res == 1) { hypervisor_send_reply(conn,HSC_INFO_OK,1, "ATM bridge '%s' deleted",argv[0]); } else { hypervisor_send_reply(conn,HSC_ERR_DELETE,1, "unable to delete ATM bridge '%s'",argv[0]); } return(res); } /* * Configure an ATM bridge * * Parameters: */ static int cmd_configure(hypervisor_conn_t *conn,int argc,char *argv[]) { atm_bridge_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ATM_BRIDGE))) return(-1); /* create the connection */ if (atm_bridge_configure(t,argv[1],argv[2], atoi(argv[3]),atoi(argv[4])) == -1) { atm_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "unable to configure bridge"); return(-1); } atm_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"ATM bridge configured"); return(0); } /* * Unconfigure a bridge */ static int cmd_unconfigure(hypervisor_conn_t *conn,int argc,char *argv[]) { atm_bridge_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ATM_BRIDGE))) return(-1); if (atm_bridge_unconfigure(t) == -1) { atm_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "unable to unconfigure bridge"); return(-1); } atm_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"ATM bridge unconfigured"); return(0); } /* Show info about a ATM bridge object */ static void cmd_show_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",entry->name); } /* ATM bridge List */ static int cmd_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_ATM_BRIDGE,cmd_show_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* ATM Bridge commands */ static hypervisor_cmd_t atmbr_cmd_array[] = { { "create", 1, 1, cmd_create, NULL }, { "rename", 2, 2, cmd_rename, NULL }, { "delete", 1, 1, cmd_delete, NULL }, { "configure", 5, 5, cmd_configure, NULL }, { "unconfigure", 1, 1, cmd_unconfigure, NULL }, { "list", 0, 0, cmd_list, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor ATM bridge initialization */ int hypervisor_atm_bridge_init(void) { hypervisor_module_t *module; module = hypervisor_register_module("atm_bridge",NULL); assert(module != NULL); hypervisor_register_cmd_array(module,atmbr_cmd_array); return(0); } dynamips-0.2.14/common/hv_atmsw.c000066400000000000000000000150611241034141600167050ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor ATM switch routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "atm.h" #include "crc.h" #include "net_io.h" #include "registry.h" #include "hypervisor.h" /* Create a new ATMSW object */ static int cmd_create(hypervisor_conn_t *conn,int argc,char *argv[]) { atmsw_table_t *t; if (!(t = atmsw_create_table(argv[0]))) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to create ATM switch '%s'", argv[0]); return(-1); } atmsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"ATMSW '%s' created",argv[0]); return(0); } /* Rename an ATM switch */ static int cmd_rename(hypervisor_conn_t *conn,int argc,char *argv[]) { atmsw_table_t *t; char *newname; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ATMSW))) return(-1); if (registry_exists(argv[1],OBJ_TYPE_ATMSW)) { atmsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename ATMSW '%s', '%s' already exists", argv[0],argv[1]); return(-1); } if(!(newname = mp_strdup(&t->mp,argv[1]))) { atmsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename ATMSW '%s', out of memory", argv[0]); return(-1); } if (registry_rename(argv[0],newname,OBJ_TYPE_ATMSW)) { mp_free(newname); atmsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename ATMSW '%s'", argv[0]); return(-1); } mp_free(t->name); t->name = newname; atmsw_release(argv[1]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"ATMSW '%s' renamed to '%s'",argv[0],argv[1]); return(0); } /* Delete an ATM switch */ static int cmd_delete(hypervisor_conn_t *conn,int argc,char *argv[]) { int res; res = atmsw_delete(argv[0]); if (res == 1) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"ATMSW '%s' deleted",argv[0]); } else { hypervisor_send_reply(conn,HSC_ERR_DELETE,1, "unable to delete ATMSW '%s'",argv[0]); } return(res); } /* * Create a Virtual Path Connection * * Parameters: */ static int cmd_create_vpc(hypervisor_conn_t *conn,int argc,char *argv[]) { atmsw_table_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ATMSW))) return(-1); /* create the connection */ if (atmsw_create_vpc(t,argv[1],atoi(argv[2]),argv[3],atoi(argv[4])) == -1) { atmsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1,"unable to create VPC"); return(-1); } atmsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VPC created"); return(0); } /* * Delete a Virtual Path Connection * * Parameters: */ static int cmd_delete_vpc(hypervisor_conn_t *conn,int argc,char *argv[]) { atmsw_table_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ATMSW))) return(-1); /* delete the connection */ if (atmsw_delete_vpc(t,argv[1],atoi(argv[2]),argv[3],atoi(argv[4])) == -1) { atmsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1,"unable to delete VPC"); return(-1); } atmsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VPC deleted"); return(0); } /* * Create a Virtual Circuit Connection * * Parameters: * */ static int cmd_create_vcc(hypervisor_conn_t *conn,int argc,char *argv[]) { atmsw_table_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ATMSW))) return(-1); /* create the connection */ if (atmsw_create_vcc(t,argv[1],atoi(argv[2]),atoi(argv[3]), argv[4],atoi(argv[5]),atoi(argv[6])) == -1) { atmsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1,"unable to create VCC"); return(-1); } atmsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VCC created"); return(0); } /* * Delete a Virtual Circuit Connection * * Parameters: * */ static int cmd_delete_vcc(hypervisor_conn_t *conn,int argc,char *argv[]) { atmsw_table_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ATMSW))) return(-1); /* create the connection */ if (atmsw_delete_vcc(t,argv[1],atoi(argv[2]),atoi(argv[3]), argv[4],atoi(argv[5]),atoi(argv[6])) == -1) { atmsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1,"unable to delete VCC"); return(-1); } atmsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VCC deleted"); return(0); } /* Show info about a ATM switch object */ static void cmd_show_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",entry->name); } /* ATM switch List */ static int cmd_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_ATMSW,cmd_show_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* ATMSW commands */ static hypervisor_cmd_t atmsw_cmd_array[] = { { "create", 1, 1, cmd_create, NULL }, { "rename", 2, 2, cmd_rename, NULL }, { "delete", 1, 1, cmd_delete, NULL }, { "create_vpc", 5, 5, cmd_create_vpc, NULL }, { "delete_vpc", 5, 5, cmd_delete_vpc, NULL }, { "create_vcc", 7, 7, cmd_create_vcc, NULL }, { "delete_vcc", 7, 7, cmd_delete_vcc, NULL }, { "list", 0, 0, cmd_list, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor ATM switch initialization */ int hypervisor_atmsw_init(void) { hypervisor_module_t *module; module = hypervisor_register_module("atmsw",NULL); assert(module != NULL); hypervisor_register_cmd_array(module,atmsw_cmd_array); return(0); } dynamips-0.2.14/common/hv_c1700.c000066400000000000000000000121261241034141600163030ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor C1700 routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "device.h" #include "dev_c1700.h" #include "dev_vtty.h" #include "utils.h" #include "net.h" #include "atm.h" #include "frame_relay.h" #include "crc.h" #include "net_io.h" #include "net_io_bridge.h" #ifdef GEN_ETH #include "gen_eth.h" #endif #include "registry.h" #include "hypervisor.h" /* Set the chassis type */ static int cmd_set_chassis(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if ((c1700_mainboard_set_type(VM_C1700(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set Chassis type for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the I/O mem size */ static int cmd_set_iomem(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); vm->nm_iomem_size = 0x8000 | atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } static int cmd_set_system_id(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if (( c1700_set_system_id(VM_C1700(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set the system id for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Get the base MAC address for the chassis */ static int cmd_get_mac_addr(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c1700_t *router; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C1700(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1, "%2.2x%2.2x.%2.2x%2.2x.%2.2x%2.2x", router->mac_addr.eth_addr_byte[0], router->mac_addr.eth_addr_byte[1], router->mac_addr.eth_addr_byte[2], router->mac_addr.eth_addr_byte[3], router->mac_addr.eth_addr_byte[4], router->mac_addr.eth_addr_byte[5]); vm_release(vm); return(0); } /* Set the base MAC address for the chassis */ static int cmd_set_mac_addr(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if ((c1700_chassis_set_mac_addr(VM_C1700(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set MAC address for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show C1700 hardware */ static int cmd_show_hardware(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c1700_t *router; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C1700(vm); c1700_show_hardware(router); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show info about C1700 object */ static void cmd_show_c1700_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; vm_instance_t *vm = entry->data; if (vm->platform == conn->cur_module->opt) hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",entry->name); } /* C1700 List */ static int cmd_c1700_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_VM,cmd_show_c1700_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* C1700 commands */ static hypervisor_cmd_t c1700_cmd_array[] = { { "set_chassis", 2, 2, cmd_set_chassis, NULL }, { "set_iomem", 2, 2, cmd_set_iomem, NULL }, { "get_mac_addr", 1, 1, cmd_get_mac_addr, NULL }, { "set_mac_addr", 2, 2, cmd_set_mac_addr, NULL }, { "set_system_id", 2, 2, cmd_set_system_id, NULL }, { "show_hardware", 1, 1, cmd_show_hardware, NULL }, { "list", 0, 0, cmd_c1700_list, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor C1700 initialization */ int hypervisor_c1700_init(vm_platform_t *platform) { hypervisor_module_t *module; module = hypervisor_register_module(platform->name,platform); assert(module != NULL); hypervisor_register_cmd_array(module,c1700_cmd_array); return(0); } dynamips-0.2.14/common/hv_c2600.c000066400000000000000000000121561241034141600163060ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor C2600 routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "device.h" #include "dev_c2600.h" #include "dev_vtty.h" #include "utils.h" #include "net.h" #include "atm.h" #include "frame_relay.h" #include "crc.h" #include "net_io.h" #include "net_io_bridge.h" #ifdef GEN_ETH #include "gen_eth.h" #endif #include "registry.h" #include "hypervisor.h" /* Set the chassis type */ static int cmd_set_chassis(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if ((c2600_mainboard_set_type(VM_C2600(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set Chassis type for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the I/O mem size */ static int cmd_set_iomem(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); vm->nm_iomem_size = 0x8000 | atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the system id */ static int cmd_set_system_id(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if (( c2600_set_system_id(VM_C2600(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set the system id for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Get the base MAC address for the chassis */ static int cmd_get_mac_addr(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c2600_t *router; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C2600(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1, "%2.2x%2.2x.%2.2x%2.2x.%2.2x%2.2x", router->mac_addr.eth_addr_byte[0], router->mac_addr.eth_addr_byte[1], router->mac_addr.eth_addr_byte[2], router->mac_addr.eth_addr_byte[3], router->mac_addr.eth_addr_byte[4], router->mac_addr.eth_addr_byte[5]); vm_release(vm); return(0); } /* Set the base MAC address for the chassis */ static int cmd_set_mac_addr(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if ((c2600_chassis_set_mac_addr(VM_C2600(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set MAC address for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show C2600 hardware */ static int cmd_show_hardware(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c2600_t *router; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C2600(vm); c2600_show_hardware(router); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show info about C2600 object */ static void cmd_show_c2600_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; vm_instance_t *vm = entry->data; if (vm->platform == conn->cur_module->opt) hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",entry->name); } /* C2600 List */ static int cmd_c2600_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_VM,cmd_show_c2600_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* C2600 commands */ static hypervisor_cmd_t c2600_cmd_array[] = { { "set_chassis", 2, 2, cmd_set_chassis, NULL }, { "set_iomem", 2, 2, cmd_set_iomem, NULL }, { "get_mac_addr", 1, 1, cmd_get_mac_addr, NULL }, { "set_mac_addr", 2, 2, cmd_set_mac_addr, NULL }, { "set_system_id", 2, 2, cmd_set_system_id, NULL }, { "show_hardware", 1, 1, cmd_show_hardware, NULL }, { "list", 0, 0, cmd_c2600_list, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor C2600 initialization */ int hypervisor_c2600_init(vm_platform_t *platform) { hypervisor_module_t *module; module = hypervisor_register_module(platform->name,platform); assert(module != NULL); hypervisor_register_cmd_array(module,c2600_cmd_array); return(0); } dynamips-0.2.14/common/hv_c2691.c000066400000000000000000000110201241034141600163050ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor C2691 routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "device.h" #include "dev_c2691.h" #include "dev_vtty.h" #include "utils.h" #include "net.h" #include "atm.h" #include "frame_relay.h" #include "crc.h" #include "net_io.h" #include "net_io_bridge.h" #ifdef GEN_ETH #include "gen_eth.h" #endif #include "registry.h" #include "hypervisor.h" /* Set the I/O mem size */ static int cmd_set_iomem(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); vm->nm_iomem_size = 0x8000 | atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the system id */ static int cmd_set_system_id(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if (( c2691_set_system_id(VM_C2691(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set the system id for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Get the base MAC address for the chassis */ static int cmd_get_mac_addr(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c2691_t *router; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C2691(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1, "%2.2x%2.2x.%2.2x%2.2x.%2.2x%2.2x", router->mac_addr.eth_addr_byte[0], router->mac_addr.eth_addr_byte[1], router->mac_addr.eth_addr_byte[2], router->mac_addr.eth_addr_byte[3], router->mac_addr.eth_addr_byte[4], router->mac_addr.eth_addr_byte[5]); vm_release(vm); return(0); } /* Set the base MAC address for the chassis */ static int cmd_set_mac_addr(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if ((c2691_chassis_set_mac_addr(VM_C2691(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set MAC address for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show C2691 hardware */ static int cmd_show_hardware(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c2691_t *router; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C2691(vm); c2691_show_hardware(router); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show info about C2691 object */ static void cmd_show_c2691_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; vm_instance_t *vm = entry->data; if (vm->platform == conn->cur_module->opt) hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",entry->name); } /* C2691 List */ static int cmd_c2691_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_VM,cmd_show_c2691_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* C2691 commands */ static hypervisor_cmd_t c2691_cmd_array[] = { { "set_iomem", 2, 2, cmd_set_iomem, NULL }, { "get_mac_addr", 1, 1, cmd_get_mac_addr, NULL }, { "set_mac_addr", 2, 2, cmd_set_mac_addr, NULL }, { "set_system_id", 2, 2, cmd_set_system_id, NULL }, { "show_hardware", 1, 1, cmd_show_hardware, NULL }, { "list", 0, 0, cmd_c2691_list, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor C2691 initialization */ int hypervisor_c2691_init(vm_platform_t *platform) { hypervisor_module_t *module; module = hypervisor_register_module(platform->name,platform); assert(module != NULL); hypervisor_register_cmd_array(module,c2691_cmd_array); return(0); } dynamips-0.2.14/common/hv_c3600.c000066400000000000000000000121521241034141600163030ustar00rootroot00000000000000/* * Cisco 3600 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor C3600 routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "device.h" #include "dev_c3600.h" #include "dev_vtty.h" #include "utils.h" #include "net.h" #include "atm.h" #include "frame_relay.h" #include "crc.h" #include "net_io.h" #include "net_io_bridge.h" #ifdef GEN_ETH #include "gen_eth.h" #endif #include "registry.h" #include "hypervisor.h" /* Set the chassis type */ static int cmd_set_chassis(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if ((c3600_chassis_set_type(VM_C3600(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set Chassis type for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the I/O mem size */ static int cmd_set_iomem(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); vm->nm_iomem_size = 0x8000 | atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the system id */ static int cmd_set_system_id(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if (( c3600_set_system_id(VM_C3600(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set the system id for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Get the base MAC address for the chassis */ static int cmd_get_mac_addr(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c3600_t *router; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C3600(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1, "%2.2x%2.2x.%2.2x%2.2x.%2.2x%2.2x", router->mac_addr.eth_addr_byte[0], router->mac_addr.eth_addr_byte[1], router->mac_addr.eth_addr_byte[2], router->mac_addr.eth_addr_byte[3], router->mac_addr.eth_addr_byte[4], router->mac_addr.eth_addr_byte[5]); vm_release(vm); return(0); } /* Set the base MAC address for the chassis */ static int cmd_set_mac_addr(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if ((c3600_chassis_set_mac_addr(VM_C3600(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set MAC address for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show C3600 hardware */ static int cmd_show_hardware(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c3600_t *router; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C3600(vm); c3600_show_hardware(router); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show info about C3600 object */ static void cmd_show_c3600_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; vm_instance_t *vm = entry->data; if (vm->platform == conn->cur_module->opt) hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",entry->name); } /* C3600 List */ static int cmd_c3600_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_VM,cmd_show_c3600_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* C3600 commands */ static hypervisor_cmd_t c3600_cmd_array[] = { { "set_chassis", 2, 2, cmd_set_chassis, NULL }, { "set_iomem", 2, 2, cmd_set_iomem, NULL }, { "get_mac_addr", 1, 1, cmd_get_mac_addr, NULL }, { "set_mac_addr", 2, 2, cmd_set_mac_addr, NULL }, { "set_system_id", 2, 2, cmd_set_system_id, NULL }, { "show_hardware", 1, 1, cmd_show_hardware, NULL }, { "list", 0, 0, cmd_c3600_list, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor C3600 initialization */ int hypervisor_c3600_init(vm_platform_t *platform) { hypervisor_module_t *module; module = hypervisor_register_module(platform->name,platform); assert(module != NULL); hypervisor_register_cmd_array(module,c3600_cmd_array); return(0); } dynamips-0.2.14/common/hv_c3725.c000066400000000000000000000110161241034141600163110ustar00rootroot00000000000000/* * Cisco 3725 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor C3725 routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "device.h" #include "dev_c3725.h" #include "dev_vtty.h" #include "utils.h" #include "net.h" #include "atm.h" #include "frame_relay.h" #include "crc.h" #include "net_io.h" #include "net_io_bridge.h" #ifdef GEN_ETH #include "gen_eth.h" #endif #include "registry.h" #include "hypervisor.h" /* Set the I/O mem size */ static int cmd_set_iomem(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); vm->nm_iomem_size = 0x8000 | atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the system id */ static int cmd_set_system_id(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if (( c3725_set_system_id(VM_C3725(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set the system id for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Get the base MAC address for the chassis */ static int cmd_get_mac_addr(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c3725_t *router; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C3725(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1, "%2.2x%2.2x.%2.2x%2.2x.%2.2x%2.2x", router->mac_addr.eth_addr_byte[0], router->mac_addr.eth_addr_byte[1], router->mac_addr.eth_addr_byte[2], router->mac_addr.eth_addr_byte[3], router->mac_addr.eth_addr_byte[4], router->mac_addr.eth_addr_byte[5]); vm_release(vm); return(0); } /* Set the base MAC address for the chassis */ static int cmd_set_mac_addr(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if ((c3725_chassis_set_mac_addr(VM_C3725(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set MAC address for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show C3725 hardware */ static int cmd_show_hardware(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c3725_t *router; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C3725(vm); c3725_show_hardware(router); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show info about C3725 object */ static void cmd_show_c3725_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; vm_instance_t *vm = entry->data; if (vm->platform == conn->cur_module->opt) hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",entry->name); } /* C3725 List */ static int cmd_c3725_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_VM,cmd_show_c3725_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* C3725 commands */ static hypervisor_cmd_t c3725_cmd_array[] = { { "set_iomem", 2, 2, cmd_set_iomem, NULL }, { "get_mac_addr", 1, 1, cmd_get_mac_addr, NULL }, { "set_mac_addr", 2, 2, cmd_set_mac_addr, NULL }, { "set_system_id", 2, 2, cmd_set_system_id, NULL }, { "show_hardware", 1, 1, cmd_show_hardware, NULL }, { "list", 0, 0, cmd_c3725_list, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor C3725 initialization */ int hypervisor_c3725_init(vm_platform_t *platform) { hypervisor_module_t *module; module = hypervisor_register_module(platform->name,platform); assert(module != NULL); hypervisor_register_cmd_array(module,c3725_cmd_array); return(0); } dynamips-0.2.14/common/hv_c3745.c000066400000000000000000000110161241034141600163130ustar00rootroot00000000000000/* * Cisco 3745 simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor C3745 routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "device.h" #include "dev_c3745.h" #include "dev_vtty.h" #include "utils.h" #include "net.h" #include "atm.h" #include "frame_relay.h" #include "crc.h" #include "net_io.h" #include "net_io_bridge.h" #ifdef GEN_ETH #include "gen_eth.h" #endif #include "registry.h" #include "hypervisor.h" /* Set the I/O mem size */ static int cmd_set_iomem(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); vm->nm_iomem_size = 0x8000 | atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the system id */ static int cmd_set_system_id(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if (( c3745_set_system_id(VM_C3745(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set the system id for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Get the base MAC address for the chassis */ static int cmd_get_mac_addr(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c3745_t *router; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C3745(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1, "%2.2x%2.2x.%2.2x%2.2x.%2.2x%2.2x", router->mac_addr.eth_addr_byte[0], router->mac_addr.eth_addr_byte[1], router->mac_addr.eth_addr_byte[2], router->mac_addr.eth_addr_byte[3], router->mac_addr.eth_addr_byte[4], router->mac_addr.eth_addr_byte[5]); vm_release(vm); return(0); } /* Set the base MAC address for the chassis */ static int cmd_set_mac_addr(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if ((c3745_chassis_set_mac_addr(VM_C3745(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set MAC address for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show C3745 hardware */ static int cmd_show_hardware(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c3745_t *router; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C3745(vm); c3745_show_hardware(router); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show info about C3745 object */ static void cmd_show_c3745_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; vm_instance_t *vm = entry->data; if (vm->platform == conn->cur_module->opt) hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",entry->name); } /* C3745 List */ static int cmd_c3745_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_VM,cmd_show_c3745_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* C3745 commands */ static hypervisor_cmd_t c3745_cmd_array[] = { { "set_iomem", 2, 2, cmd_set_iomem, NULL }, { "get_mac_addr", 1, 1, cmd_get_mac_addr, NULL }, { "set_mac_addr", 2, 2, cmd_set_mac_addr, NULL }, { "set_system_id", 2, 2, cmd_set_system_id, NULL }, { "show_hardware", 1, 1, cmd_show_hardware, NULL }, { "list", 0, 0, cmd_c3745_list, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor C3745 initialization */ int hypervisor_c3745_init(vm_platform_t *platform) { hypervisor_module_t *module; module = hypervisor_register_module(platform->name,platform); assert(module != NULL); hypervisor_register_cmd_array(module,c3745_cmd_array); return(0); } dynamips-0.2.14/common/hv_c7200.c000066400000000000000000000152431241034141600163070ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor C7200 routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "device.h" #include "dev_c7200.h" #include "dev_vtty.h" #include "utils.h" #include "net.h" #include "atm.h" #include "frame_relay.h" #include "crc.h" #include "net_io.h" #include "net_io_bridge.h" #ifdef GEN_ETH #include "gen_eth.h" #endif #include "registry.h" #include "hypervisor.h" /* Set the NPE type */ static int cmd_set_npe(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if ((c7200_npe_set_type(VM_C7200(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set NPE type for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the Midplane type */ static int cmd_set_midplane(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if ((c7200_midplane_set_type(VM_C7200(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set Midplane type for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the system id */ static int cmd_set_system_id(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if (( c7200_set_system_id(VM_C7200(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set the system id for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Get the base MAC address for the chassis */ static int cmd_get_mac_addr(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c7200_t *router; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C7200(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1, "%2.2x%2.2x.%2.2x%2.2x.%2.2x%2.2x", router->mac_addr.eth_addr_byte[0], router->mac_addr.eth_addr_byte[1], router->mac_addr.eth_addr_byte[2], router->mac_addr.eth_addr_byte[3], router->mac_addr.eth_addr_byte[4], router->mac_addr.eth_addr_byte[5]); vm_release(vm); return(0); } /* Set the base MAC address for the chassis */ static int cmd_set_mac_addr(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); if ((c7200_midplane_set_mac_addr(VM_C7200(vm),argv[1])) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to set MAC address for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* * Set temperature for a DS1620 sensor. * This can be used to simulate environmental problems (overheat) */ static int cmd_set_temp_sensor(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c7200_t *router; u_int index; int temp; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C7200(vm); index = atoi(argv[1]); temp = atoi(argv[2]); if (index < C7200_TEMP_SENSORS) ds1620_set_temp(&router->ds1620_sensors[index],temp); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* * Set power supply status. * This can be used to simulate environmental problems (power loss) */ static int cmd_set_power_supply(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c7200_t *router; u_int index; int status; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C7200(vm); index = atoi(argv[1]); status = atoi(argv[2]); if (status) router->ps_status |= 1 << index; else router->ps_status &= ~(1 << index); /* only 2 power supplies */ router->ps_status &= 0x03; vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show C7200 hardware */ static int cmd_show_hardware(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; c7200_t *router; if (!(vm = hypervisor_find_vm(conn,argv[0]))) return(-1); router = VM_C7200(vm); c7200_show_hardware(router); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show info about C7200 object */ static void cmd_show_c7200_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; vm_instance_t *vm = entry->data; if (vm->platform == conn->cur_module->opt) hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",entry->name); } /* C7200 List */ static int cmd_c7200_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_VM,cmd_show_c7200_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* C7200 commands */ static hypervisor_cmd_t c7200_cmd_array[] = { { "set_npe", 2, 2, cmd_set_npe, NULL }, { "set_midplane", 2, 2, cmd_set_midplane, NULL }, { "get_mac_addr", 1, 1, cmd_get_mac_addr, NULL }, { "set_mac_addr", 2, 2, cmd_set_mac_addr, NULL }, { "set_system_id", 2, 2, cmd_set_system_id, NULL }, { "set_temp_sensor", 3, 3, cmd_set_temp_sensor, NULL }, { "set_power_supply", 3, 3, cmd_set_power_supply, NULL }, { "show_hardware", 1, 1, cmd_show_hardware, NULL }, { "list", 0, 0, cmd_c7200_list, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor C7200 initialization */ int hypervisor_c7200_init(vm_platform_t *platform) { hypervisor_module_t *module; module = hypervisor_register_module(platform->name,platform); assert(module != NULL); hypervisor_register_cmd_array(module,c7200_cmd_array); return(0); } dynamips-0.2.14/common/hv_ethsw.c000066400000000000000000000213161241034141600167040ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor Ethernet switch routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "eth_switch.h" #include "crc.h" #include "net_io.h" #include "registry.h" #include "hypervisor.h" /* Create a new Ethernet switch object */ static int cmd_create(hypervisor_conn_t *conn,int argc,char *argv[]) { ethsw_table_t *t; if (!(t = ethsw_create(argv[0]))) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to create Ethernet switch '%s'", argv[0]); return(-1); } ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"ETHSW '%s' created",argv[0]); return(0); } /* Rename an Ethernet switch */ static int cmd_rename(hypervisor_conn_t *conn,int argc,char *argv[]) { ethsw_table_t *t; char *newname; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ETHSW))) return(-1); if (registry_exists(argv[1],OBJ_TYPE_ETHSW)) { ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename ETHSW '%s', '%s' already exists", argv[0],argv[1]); return(-1); } if(!(newname = strdup(argv[1]))) { ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename ETHSW '%s', out of memory", argv[0]); return(-1); } if (registry_rename(argv[0],newname,OBJ_TYPE_ETHSW)) { free(newname); ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename ETHSW '%s'", argv[0]); return(-1); } free(t->name); t->name = newname; ethsw_release(argv[1]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"ETHSW '%s' renamed to '%s'",argv[0],argv[1]); return(0); } /* Delete an Ethernet switch */ static int cmd_delete(hypervisor_conn_t *conn,int argc,char *argv[]) { int res; res = ethsw_delete(argv[0]); if (res == 1) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"ETHSW '%s' deleted",argv[0]); } else { hypervisor_send_reply(conn,HSC_ERR_DELETE,1, "unable to delete ETHSW '%s'",argv[0]); } return(res); } /* * Add a NIO to an Ethernet switch. * * Parameters: */ static int cmd_add_nio(hypervisor_conn_t *conn,int argc,char *argv[]) { ethsw_table_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ETHSW))) return(-1); if (ethsw_add_netio(t,argv[1]) == -1) { ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "unable to bind NIO '%s' to switch '%s'", argv[1],argv[0]); return(-1); } ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' bound.",argv[1]); return(0); } /* * Remove a NIO from an Ethernet switch * * Parameters: */ static int cmd_remove_nio(hypervisor_conn_t *conn,int argc,char *argv[]) { ethsw_table_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ETHSW))) return(-1); if (ethsw_remove_netio(t,argv[1]) == -1) { ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "unable to bind NIO '%s' to switch '%s'", argv[1],argv[0]); return(-1); } ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' unbound.",argv[1]); return(0); } /* * Set a port as an access port. * * Parameters: */ static int cmd_set_access_port(hypervisor_conn_t *conn,int argc,char *argv[]) { ethsw_table_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ETHSW))) return(-1); if (ethsw_set_access_port(t,argv[1],atoi(argv[2])) == -1) { ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "unable to apply port settings"); return(-1); } ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"Port settings OK"); return(0); } /* * Set a port as a trunk (802.1Q) port. * * Parameters: */ static int cmd_set_dot1q_port(hypervisor_conn_t *conn,int argc,char *argv[]) { ethsw_table_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ETHSW))) return(-1); if (ethsw_set_dot1q_port(t,argv[1],atoi(argv[2])) == -1) { ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "unable to apply port settings"); return(-1); } ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"Port settings OK"); return(0); } /* * Set a port as a trunk (QinQ) port. * * Parameters: */ static int cmd_set_qinq_port(hypervisor_conn_t *conn,int argc,char *argv[]) { ethsw_table_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ETHSW))) return(-1); if (ethsw_set_qinq_port(t,argv[1],atoi(argv[2])) == -1) { ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "unable to apply port settings"); return(-1); } ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"Port settings OK"); return(0); } /* Clear the MAC address table */ static int cmd_clear_mac_addr_table(hypervisor_conn_t *conn, int argc,char *argv[]) { ethsw_table_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ETHSW))) return(-1); ethsw_clear_mac_addr_table(t); ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show the MAC address table */ static void cmd_show_mac_addr_entry(ethsw_table_t *t,ethsw_mac_entry_t *entry, hypervisor_conn_t *conn) { hypervisor_send_reply(conn,HSC_INFO_MSG,0, "%2.2x%2.2x.%2.2x%2.2x.%2.2x%2.2x %u %s", entry->mac_addr.eth_addr_byte[0], entry->mac_addr.eth_addr_byte[1], entry->mac_addr.eth_addr_byte[2], entry->mac_addr.eth_addr_byte[3], entry->mac_addr.eth_addr_byte[4], entry->mac_addr.eth_addr_byte[5], entry->vlan_id, entry->nio->name); } static int cmd_show_mac_addr_table(hypervisor_conn_t *conn, int argc,char *argv[]) { ethsw_table_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_ETHSW))) return(-1); ethsw_iterate_mac_addr_table(t, (ethsw_foreach_entry_t)cmd_show_mac_addr_entry, conn); ethsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show info about a ETHSW object */ static void cmd_show_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",entry->name); } /* Ethernet switch List */ static int cmd_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_ETHSW,cmd_show_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* ETHSW commands */ static hypervisor_cmd_t ethsw_cmd_array[] = { { "create", 1, 1, cmd_create, NULL }, { "rename", 2, 2, cmd_rename, NULL }, { "delete", 1, 1, cmd_delete, NULL }, { "add_nio", 2, 2, cmd_add_nio, NULL }, { "remove_nio", 2, 2, cmd_remove_nio, NULL }, { "set_access_port", 3, 3, cmd_set_access_port, NULL }, { "set_dot1q_port", 3, 3, cmd_set_dot1q_port, NULL }, { "set_qinq_port", 3, 3, cmd_set_qinq_port, NULL }, { "clear_mac_addr_table", 1, 1, cmd_clear_mac_addr_table, NULL }, { "show_mac_addr_table", 1, 1, cmd_show_mac_addr_table, NULL }, { "list", 0, 0, cmd_list, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor Ethernet switch initialization */ int hypervisor_ethsw_init(void) { hypervisor_module_t *module; module = hypervisor_register_module("ethsw",NULL); assert(module != NULL); hypervisor_register_cmd_array(module,ethsw_cmd_array); return(0); } dynamips-0.2.14/common/hv_frsw.c000066400000000000000000000117411241034141600165340ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor Frame-Relay switch routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "frame_relay.h" #include "crc.h" #include "net_io.h" #include "registry.h" #include "hypervisor.h" /* Create a new FRSW object */ static int cmd_create(hypervisor_conn_t *conn,int argc,char *argv[]) { frsw_table_t *t; if (!(t = frsw_create_table(argv[0]))) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to create frame-relay switch '%s'", argv[0]); return(-1); } frsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"FRSW '%s' created",argv[0]); return(0); } /* Rename a Frame-Relay switch */ static int cmd_rename(hypervisor_conn_t *conn,int argc,char *argv[]) { frsw_table_t *t; char *newname; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_FRSW))) return(-1); if (registry_exists(argv[1],OBJ_TYPE_FRSW)) { frsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename FRSW '%s', '%s' already exists", argv[0],argv[1]); return(-1); } if(!(newname = mp_strdup(&t->mp,argv[1]))) { frsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename FRSW '%s', out of memory", argv[0]); return(-1); } if (registry_rename(argv[0],newname,OBJ_TYPE_FRSW)) { mp_free(newname); frsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename FRSW '%s'", argv[0]); return(-1); } mp_free(t->name); t->name = newname; frsw_release(argv[1]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"FRSW '%s' renamed to '%s'",argv[0],argv[1]); return(0); } /* Delete a Frame-Relay switch */ static int cmd_delete(hypervisor_conn_t *conn,int argc,char *argv[]) { int res; res = frsw_delete(argv[0]); if (res == 1) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"FRSW '%s' deleted",argv[0]); } else { hypervisor_send_reply(conn,HSC_ERR_DELETE,1, "unable to delete FRSW '%s'",argv[0]); } return(res); } /* * Create a Virtual Circuit * * Parameters: */ static int cmd_create_vc(hypervisor_conn_t *conn,int argc,char *argv[]) { frsw_table_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_FRSW))) return(-1); /* create the connection */ if (frsw_create_vc(t,argv[1],atoi(argv[2]),argv[3],atoi(argv[4])) == -1) { frsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1,"unable to create VC"); return(-1); } frsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VC created"); return(0); } /* * Delete a Virtual Circuit * * Parameters: */ static int cmd_delete_vc(hypervisor_conn_t *conn,int argc,char *argv[]) { frsw_table_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_FRSW))) return(-1); /* delete the connection */ if (frsw_delete_vc(t,argv[1],atoi(argv[2]),argv[3],atoi(argv[4])) == -1) { frsw_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1,"unable to delete VC"); return(-1); } frsw_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VC deleted"); return(0); } /* Show info about a FRSW object */ static void cmd_show_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",entry->name); } /* Frame-Relay switch List */ static int cmd_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_FRSW,cmd_show_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* FRSW commands */ static hypervisor_cmd_t frsw_cmd_array[] = { { "create", 1, 1, cmd_create, NULL }, { "rename", 2, 2, cmd_rename, NULL }, { "delete", 1, 1, cmd_delete, NULL }, { "create_vc", 5, 5, cmd_create_vc, NULL }, { "delete_vc", 5, 5, cmd_delete_vc, NULL }, { "list", 0, 0, cmd_list, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor Frame-Relay switch initialization */ int hypervisor_frsw_init(void) { hypervisor_module_t *module; module = hypervisor_register_module("frsw",NULL); assert(module != NULL); hypervisor_register_cmd_array(module,frsw_cmd_array); return(0); } dynamips-0.2.14/common/hv_nio.c000066400000000000000000000336721241034141600163470ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor NIO routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "atm.h" #include "frame_relay.h" #include "crc.h" #include "net_io.h" #include "net_io_bridge.h" #include "net_io_filter.h" #ifdef GEN_ETH #include "gen_eth.h" #endif #include "registry.h" #include "hypervisor.h" /* * Create a UDP NIO * * Parameters: */ static int cmd_create_udp(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; nio = netio_desc_create_udp(argv[0],atoi(argv[1]),argv[2],atoi(argv[3])); if (!nio) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to create UDP NIO"); return(-1); } netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' created",argv[0]); return(0); } /* * Create a Auto UDP NIO * * Parameters: */ static int cmd_create_udp_auto(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; int local_port; nio = netio_desc_create_udp_auto(argv[0],argv[1],atoi(argv[2]),atoi(argv[3])); if (!nio) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to create UDP Auto NIO"); return(-1); } local_port = netio_udp_auto_get_local_port(nio); netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"%d",local_port); return(0); } /* * Connect an UDP Auto NIO to a remote host/port. * * Parameters: */ static int cmd_connect_udp_auto(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; int res; if (!(nio = hypervisor_find_object(conn,argv[0],OBJ_TYPE_NIO))) return(-1); res = netio_udp_auto_connect(nio,argv[1],atoi(argv[2])); netio_release(argv[0]); if (res == 0) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' connected",argv[0]); return(0); } else { hypervisor_send_reply(conn,HSC_ERR_CREATE,1,"unable to connect NIO"); return(-1); } } /* * Create a Multicast NIO * * Parameters: */ static int cmd_create_mcast(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; nio = netio_desc_create_mcast(argv[0],argv[1],atoi(argv[2])); if (!nio) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to create Multicast NIO"); return(-1); } netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' created",argv[0]); return(0); } /* Set TTL for a Multicast NIO */ static int cmd_set_mcast_ttl(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; if (!(nio = hypervisor_find_object(conn,argv[0],OBJ_TYPE_NIO))) return(-1); netio_mcast_set_ttl(nio,atoi(argv[1])); netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' TTL changed",argv[0]); return(0); } /* * Create a UNIX NIO * * Parameters: */ static int cmd_create_unix(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; nio = netio_desc_create_unix(argv[0],argv[1],argv[2]); if (!nio) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1,"unable to create UNIX NIO"); return(-1); } netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' created",argv[0]); return(0); } /* * Create a VDE NIO * * Parameters: */ static int cmd_create_vde(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; nio = netio_desc_create_vde(argv[0],argv[1],argv[2]); if (!nio) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1,"unable to create VDE NIO"); return(-1); } netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' created",argv[0]); return(0); } /* * Create a TAP NIO * * Parameters: */ static int cmd_create_tap(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; nio = netio_desc_create_tap(argv[0],argv[1]); if (!nio) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1,"unable to create TAP NIO"); return(-1); } netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' created",argv[0]); return(0); } /* * Create a generic ethernet PCAP NIO * * Parameters: */ #ifdef GEN_ETH static int cmd_create_gen_eth(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; nio = netio_desc_create_geneth(argv[0],argv[1]); if (!nio) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to create generic ethernet NIO"); return(-1); } netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' created",argv[0]); return(0); } #endif /* * Create a linux raw ethernet NIO * * Parameters: */ #ifdef LINUX_ETH static int cmd_create_linux_eth(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; nio = netio_desc_create_lnxeth(argv[0],argv[1]); if (!nio) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to create Linux raw ethernet NIO"); return(-1); } netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' created",argv[0]); return(0); } #endif /* * Create a Null NIO * * Parameters: */ static int cmd_create_null(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; nio = netio_desc_create_null(argv[0]); if (!nio) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to create Null NIO"); return(-1); } netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' created",argv[0]); return(0); } /* * Create a FIFO NIO * * Parameters: */ static int cmd_create_fifo(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; nio = netio_desc_create_fifo(argv[0]); if (!nio) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to create FIFO NIO"); return(-1); } netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' created",argv[0]); return(0); } /* * Establish a cross-connect between 2 FIFO NIO * * Parameters: */ static int cmd_crossconnect_fifo(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *a,*b; if (!(a = hypervisor_find_object(conn,argv[0],OBJ_TYPE_NIO))) return(-1); if (!(b = hypervisor_find_object(conn,argv[1],OBJ_TYPE_NIO))) { netio_release(argv[0]); return(-1); } netio_fifo_crossconnect(a,b); netio_release(argv[0]); netio_release(argv[1]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Rename a NIO */ static int cmd_rename(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; char *newname; if (!(nio = hypervisor_find_object(conn,argv[0],OBJ_TYPE_NIO))) return(-1); if (registry_exists(argv[1],OBJ_TYPE_NIO)) { netio_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename NIO '%s', '%s' already exists", argv[0],argv[1]); return(-1); } if(!(newname = strdup(argv[1]))) { netio_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename NIO '%s', out of memory", argv[0]); return(-1); } if (registry_rename(argv[0],newname,OBJ_TYPE_NIO)) { free(newname); netio_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename NIO '%s'", argv[0]); return(-1); } free(nio->name); nio->name = newname; netio_release(argv[1]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' renamed to '%s'",argv[0],argv[1]); return(0); } /* Delete a NIO */ static int cmd_delete(hypervisor_conn_t *conn,int argc,char *argv[]) { int res; res = netio_delete(argv[0]); if (res == 1) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' deleted",argv[0]); } else { hypervisor_send_reply(conn,HSC_ERR_DELETE,1, "unable to delete NIO '%s'",argv[0]); } return(res); } /* * Enable/Disable debugging for an NIO * * Parameters: */ static int cmd_set_debug(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; if (!(nio = hypervisor_find_object(conn,argv[0],OBJ_TYPE_NIO))) return(-1); nio->debug = atoi(argv[1]); netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Bind a packet filter */ static int cmd_bind_filter(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; int res; if (!(nio = hypervisor_find_object(conn,argv[0],OBJ_TYPE_NIO))) return(-1); res = netio_filter_bind(nio,atoi(argv[1]),argv[2]); netio_release(argv[0]); if (!res) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); } else { hypervisor_send_reply(conn,HSC_ERR_UNK_OBJ,1, "Unknown filter %s",argv[2]); } return(0); } /* Unbind a packet filter */ static int cmd_unbind_filter(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; int res; if (!(nio = hypervisor_find_object(conn,argv[0],OBJ_TYPE_NIO))) return(-1); res = netio_filter_unbind(nio,atoi(argv[1])); netio_release(argv[0]); if (!res) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); } else { hypervisor_send_reply(conn,HSC_ERR_UNK_OBJ,1, "No filter previously defined"); } return(0); } /* Setup a packet filter for a given NIO */ static int cmd_setup_filter(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; int res; if (!(nio = hypervisor_find_object(conn,argv[0],OBJ_TYPE_NIO))) return(-1); res = netio_filter_setup(nio,atoi(argv[1]),argc-2,&argv[2]); netio_release(argv[0]); if (!res) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); } else { hypervisor_send_reply(conn,HSC_ERR_UNSPECIFIED,1,"Failed to setup filter"); } return(0); } /* Get statistics of a NIO */ static int cmd_get_stats(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; m_uint64_t spi,spo,sbi,sbo; if (!(nio = hypervisor_find_object(conn,argv[0],OBJ_TYPE_NIO))) return(-1); spi = nio->stats_pkts_in; spo = nio->stats_pkts_out; sbi = nio->stats_bytes_in; sbo = nio->stats_bytes_out; netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"%llu %llu %llu %llu", spi,spo,sbi,sbo); return(0); } /* Reset statistics of a NIO */ static int cmd_reset_stats(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; if (!(nio = hypervisor_find_object(conn,argv[0],OBJ_TYPE_NIO))) return(-1); netio_reset_stats(nio); netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set bandwidth constraint */ static int cmd_set_bandwidth(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_desc_t *nio; if (!(nio = hypervisor_find_object(conn,argv[0],OBJ_TYPE_NIO))) return(-1); netio_set_bandwidth(nio,atoi(argv[1])); netio_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show info about a NIO object */ static void cmd_show_nio_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",entry->name); } /* NIO List */ static int cmd_nio_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_NIO,cmd_show_nio_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* NIO commands */ static hypervisor_cmd_t nio_cmd_array[] = { { "create_udp", 4, 4, cmd_create_udp, NULL }, { "create_udp_auto", 4, 4, cmd_create_udp_auto, NULL }, { "connect_udp_auto", 3, 3, cmd_connect_udp_auto, NULL }, { "create_mcast", 3, 3, cmd_create_mcast, NULL }, { "set_mcast_ttl", 2, 2, cmd_set_mcast_ttl, NULL }, { "create_unix", 3, 3, cmd_create_unix, NULL }, { "create_vde", 3, 3, cmd_create_vde, NULL }, { "create_tap", 2, 2, cmd_create_tap, NULL }, #ifdef GEN_ETH { "create_gen_eth", 2, 2, cmd_create_gen_eth, NULL }, #endif #ifdef LINUX_ETH { "create_linux_eth", 2, 2, cmd_create_linux_eth, NULL }, #endif { "create_null", 1, 1, cmd_create_null, NULL }, { "create_fifo", 1, 1, cmd_create_fifo, NULL }, { "crossconnect_fifo", 2, 2, cmd_crossconnect_fifo, NULL }, { "rename", 2, 2, cmd_rename, NULL }, { "delete", 1, 1, cmd_delete, NULL }, { "set_debug", 2, 2, cmd_set_debug, NULL }, { "bind_filter", 3, 3, cmd_bind_filter, NULL }, { "unbind_filter", 2, 2, cmd_unbind_filter, NULL }, { "setup_filter", 2, 10, cmd_setup_filter, NULL }, { "get_stats", 1, 1, cmd_get_stats }, { "reset_stats", 1, 1, cmd_reset_stats }, { "set_bandwidth", 2, 2, cmd_set_bandwidth }, { "list", 0, 0, cmd_nio_list, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor NIO initialization */ int hypervisor_nio_init(void) { hypervisor_module_t *module; module = hypervisor_register_module("nio",NULL); assert(module != NULL); hypervisor_register_cmd_array(module,nio_cmd_array); return(0); } dynamips-0.2.14/common/hv_nio_bridge.c000066400000000000000000000122361241034141600176540ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor NIO bridge routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #include "crc.h" #include "net_io.h" #include "net_io_bridge.h" #include "registry.h" #include "hypervisor.h" /* Create a new NIO bridge */ static int cmd_create(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_bridge_t *t; if (!(t = netio_bridge_create(argv[0]))) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to create NIO bridge '%s'", argv[0]); return(-1); } netio_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO bridge '%s' created",argv[0]); return(0); } /* Rename a NIO bridge */ static int cmd_rename(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_bridge_t *t; char *newname; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_NIO_BRIDGE))) return(-1); if (registry_exists(argv[1],OBJ_TYPE_NIO_BRIDGE)) { netio_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename NIO bridge '%s', '%s' already exists", argv[0],argv[1]); return(-1); } if(!(newname = strdup(argv[1]))) { netio_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename NIO bridge '%s', out of memory", argv[0]); return(-1); } if (registry_rename(argv[0],newname,OBJ_TYPE_NIO_BRIDGE)) { free(newname); netio_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename NIO bridge '%s'", argv[0]); return(-1); } free(t->name); t->name = newname; netio_bridge_release(argv[1]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO bridge '%s' renamed to '%s'",argv[0],argv[1]); return(0); } /* Delete an NIO bridge */ static int cmd_delete(hypervisor_conn_t *conn,int argc,char *argv[]) { int res; res = netio_bridge_delete(argv[0]); if (res == 1) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO bridge '%s' deleted", argv[0]); } else { hypervisor_send_reply(conn,HSC_ERR_DELETE,1, "unable to delete NIO bridge '%s'",argv[0]); } return(res); } /* * Add a NIO to a bridge * * Parameters: */ static int cmd_add_nio(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_bridge_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_NIO_BRIDGE))) return(-1); if (netio_bridge_add_netio(t,argv[1]) == -1) { netio_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "unable to bind NIO '%s' to bridge '%s'", argv[1],argv[0]); return(-1); } netio_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' bound.",argv[1]); return(0); } /* * Remove a NIO from a bridge * * Parameters: */ static int cmd_remove_nio(hypervisor_conn_t *conn,int argc,char *argv[]) { netio_bridge_t *t; if (!(t = hypervisor_find_object(conn,argv[0],OBJ_TYPE_NIO_BRIDGE))) return(-1); if (netio_bridge_remove_netio(t,argv[1]) == -1) { netio_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "unable to bind NIO '%s' to bridge '%s'", argv[1],argv[0]); return(-1); } netio_bridge_release(argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"NIO '%s' unbound.",argv[1]); return(0); } /* Show info about a NIO bridge object */ static void cmd_show_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",entry->name); } /* Bridge switch List */ static int cmd_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_NIO_BRIDGE,cmd_show_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* NIO bridge commands */ static hypervisor_cmd_t nio_bridge_cmd_array[] = { { "create", 1, 1, cmd_create, NULL }, { "rename", 2, 2, cmd_rename, NULL }, { "delete", 1, 1, cmd_delete, NULL }, { "add_nio", 2, 2, cmd_add_nio, NULL }, { "remove_nio", 2, 2, cmd_remove_nio, NULL }, { "list", 0, 0, cmd_list, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor NIO bridge initialization */ int hypervisor_nio_bridge_init(void) { hypervisor_module_t *module; module = hypervisor_register_module("nio_bridge",NULL); assert(module != NULL); hypervisor_register_cmd_array(module,nio_bridge_cmd_array); return(0); } dynamips-0.2.14/common/hv_store.c000066400000000000000000000145701241034141600167120ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * Hypervisor store. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "base64.h" #include "registry.h" #include "hypervisor.h" struct store_object { char *name; /* Object name */ void *data; /* Data pointer */ size_t len; /* Data length */ }; /* Create an object of the given name */ static struct store_object *so_create(char *name,void *data,size_t len) { struct store_object *so; if (!(so = malloc(sizeof(*so)))) return NULL; if (!(so->name = strdup(name))) goto err_name; so->data = data; so->len = len; if (registry_add(so->name,OBJ_TYPE_STORE,so) == -1) goto err_registry; return so; err_registry: free(so->name); err_name: free(so); return NULL; } /* Delete a store object */ static void so_delete(struct store_object *so) { if (so != NULL) { free(so->name); free(so->data); free(so); } } /* Free resources used by a store object (registry callback) */ static int so_reg_delete(void *data,void *arg) { so_delete((struct store_object *)data); return(TRUE); } /* Delete a store object from the registry */ static int so_delete_from_registry(char *name) { return(registry_delete_if_unused(name,OBJ_TYPE_STORE,so_reg_delete,NULL)); } /* Write an object (data provided in base64 encoding) */ static int cmd_write(hypervisor_conn_t *conn,int argc,char *argv[]) { struct store_object *so; u_char *buffer; ssize_t len; /* Convert base64 input to standard text */ if (!(buffer = malloc(3 * strlen(argv[1])))) goto err_alloc_base64; if ((len = base64_decode(buffer,(u_char *)argv[1],0)) < 0) goto err_decode_base64; if (!(so = so_create(argv[0],buffer,len))) { free(buffer); hypervisor_send_reply(conn,HSC_ERR_CREATE,1,"unable to store object"); return(-1); } registry_unref(so->name,OBJ_TYPE_STORE); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); err_decode_base64: free(buffer); err_alloc_base64: hypervisor_send_reply(conn,HSC_ERR_CREATE,1,"unable to decode base64"); return(-1); } /* Read an object and return data in base64 encoding */ static int cmd_read(hypervisor_conn_t *conn,int argc,char *argv[]) { struct store_object *so; u_char *buffer; if (!(so = hypervisor_find_object(conn,argv[0],OBJ_TYPE_STORE))) return(-1); /* * Convert data to base64. base64 is about 1/3 larger than input, * let's be on the safe side with twice longer. */ if (!(buffer = malloc(so->len * 2))) goto err_alloc_base64; base64_encode(buffer,so->data,so->len); registry_unref(so->name,OBJ_TYPE_STORE); hypervisor_send_reply(conn,HSC_INFO_OK,1,"%s",buffer); free(buffer); return(0); err_alloc_base64: registry_unref(so->name,OBJ_TYPE_STORE); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to encode data in base64", argv[0]); return(-1); } /* Rename an object */ static int cmd_rename(hypervisor_conn_t *conn,int argc,char *argv[]) { struct store_object *so; char *newname; if (!(so = hypervisor_find_object(conn,argv[0],OBJ_TYPE_STORE))) return(-1); if (registry_exists(argv[1],OBJ_TYPE_STORE)) { registry_unref(so->name,OBJ_TYPE_STORE); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename object '%s', '%s' already exists", argv[0],argv[1]); return(-1); } if(!(newname = strdup(argv[1]))) { registry_unref(so->name,OBJ_TYPE_STORE); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename object '%s', out of memory", argv[0]); return(-1); } if (registry_rename(argv[0],newname,OBJ_TYPE_STORE)) { free(newname); registry_unref(so->name,OBJ_TYPE_STORE); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename object '%s'", argv[0]); return(-1); } free(so->name); so->name = newname; registry_unref(so->name,OBJ_TYPE_STORE); hypervisor_send_reply(conn,HSC_INFO_OK,1,"object '%s' renamed to '%s'",argv[0],argv[1]); return(0); } /* Delete an object from the store */ static int cmd_delete(hypervisor_conn_t *conn,int argc,char *argv[]) { if (so_delete_from_registry(argv[0]) < 0) { hypervisor_send_reply(conn,HSC_ERR_DELETE,1,"unable to delete object %s", argv[0]); return(-1); } else { hypervisor_send_reply(conn,HSC_INFO_OK,1,"object %s deleted",argv[0]); return(0); } } /* Delete all objects from the store */ static int cmd_delete_all(hypervisor_conn_t *conn,int argc,char *argv[]) { registry_delete_type(OBJ_TYPE_STORE,so_reg_delete,NULL); hypervisor_send_reply(conn,HSC_INFO_OK,1,"object store deleted"); return(0); } /* Show info about a store object */ static void cmd_show_obj_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; struct store_object *so = entry->data; hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s (len=%ld)", entry->name,(long)so->len); } /* Object list */ static int cmd_obj_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_STORE,cmd_show_obj_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* "store" commands */ static hypervisor_cmd_t store_cmd_array[] = { { "write", 2, 2, cmd_write, NULL }, { "read", 1, 1, cmd_read, NULL }, { "rename", 2, 2, cmd_rename, NULL }, { "delete", 1, 1, cmd_delete, NULL }, { "delete_all", 0, 0, cmd_delete_all, NULL }, { "list", 0, 0, cmd_obj_list, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor store initialization */ int hypervisor_store_init(void) { hypervisor_module_t *module; module = hypervisor_register_module("object_store",NULL); assert(module != NULL); hypervisor_register_cmd_array(module,store_cmd_array); return(0); } dynamips-0.2.14/common/hv_vm_debug.c000066400000000000000000000164571241034141600173540ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor routines for VM debugging. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "device.h" #include "dev_c7200.h" #include "dev_vtty.h" #include "utils.h" #include "registry.h" #include "hypervisor.h" /* Show CPU registers */ static int cmd_show_cpu_regs(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if ((cpu = cpu_group_find_id(vm->cpu_group,atoi(argv[1]))) != NULL) cpu->reg_dump(cpu); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show CPU MMU info */ static int cmd_show_cpu_mmu(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if ((cpu = cpu_group_find_id(vm->cpu_group,atoi(argv[1]))) != NULL) cpu->mmu_dump(cpu); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set a CPU register */ static int cmd_set_cpu_reg(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; int reg_index; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); cpu = cpu_group_find_id(vm->cpu_group,atoi(argv[1])); reg_index = atoi(argv[2]); if (!cpu || (reg_index < 1)) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BAD_OBJ,1,"Bad CPU or register"); return(-1); } /* Set register value */ cpu->reg_set(cpu,reg_index,strtoull(argv[3],NULL,0)); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Add a breakpoint */ static int cmd_add_cpu_breakpoint(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; m_uint64_t addr; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!(cpu = cpu_group_find_id(vm->cpu_group,atoi(argv[1])))) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BAD_OBJ,1,"Bad CPU"); return(-1); } addr = strtoull(argv[2],NULL,0); cpu->add_breakpoint(cpu,addr); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Remove a breakpoint */ static int cmd_remove_cpu_breakpoint(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; m_uint64_t addr; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!(cpu = cpu_group_find_id(vm->cpu_group,atoi(argv[1])))) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BAD_OBJ,1,"Bad CPU"); return(-1); } addr = strtoull(argv[2],NULL,0); cpu->remove_breakpoint(cpu,addr); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Write a 32-bit memory word in physical memory */ static int cmd_pmem_w32(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; m_uint64_t addr; m_uint32_t value; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); /* Write word */ addr = strtoull(argv[2],NULL,0); value = strtoul(argv[3],NULL,0); physmem_copy_u32_to_vm(vm,addr,value); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Read a 32-bit memory word */ static int cmd_pmem_r32(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; m_uint64_t addr; m_uint32_t value; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); /* Write word */ addr = strtoull(argv[2],NULL,0); value = physmem_copy_u32_from_vm(vm,addr); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"0x%8.8x",value); return(0); } /* Write a 16-bit memory word */ static int cmd_pmem_w16(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; m_uint64_t addr; m_uint16_t value; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); /* Write word */ addr = strtoull(argv[2],NULL,0); value = strtoul(argv[3],NULL,0); physmem_copy_u16_to_vm(vm,addr,value); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Read a 16-bit memory word */ static int cmd_pmem_r16(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; m_uint64_t addr; m_uint16_t value; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); /* Write word */ addr = strtoull(argv[2],NULL,0); value = physmem_copy_u16_from_vm(vm,addr); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"0x%4.4x",value); return(0); } /* Finds a sequence of bytes in cacheable physical memory */ static int cmd_pmem_cfind(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; m_uint64_t start, end, addr; m_uint8_t *bytes; size_t len; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); len = strlen(argv[2]); if (len == 0 || len % 2 == 1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_INV_PARAM,1,"Invalid byte sequence (invalid length=%u)", (unsigned int)len); return(-1); } len = len / 2; bytes = malloc(sizeof(m_uint8_t)*len); if (len != hex_decode(bytes, (unsigned char*)argv[2], len)) { free(bytes); vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_INV_PARAM,1,"Invalid byte sequence (non-hexadecimal character)"); return(-1); } start = (argc > 3)? strtoull(argv[3],NULL,0): 0; end = (argc > 4)? strtoull(argv[4],NULL,0): 0xFFFFFFFFFFFFFFFFULL; if (physmem_cfind(vm, bytes, len, start, end, &addr)) { free(bytes); vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_NOT_FOUND,1,"Not found"); return(-1); } free(bytes); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"0x%4.4x",addr); return(0); } /* VM debug commands */ static hypervisor_cmd_t vm_cmd_array[] = { { "show_cpu_regs", 2, 2, cmd_show_cpu_regs, NULL }, { "show_cpu_mmu", 2, 2, cmd_show_cpu_mmu, NULL }, { "set_cpu_reg", 4, 4, cmd_set_cpu_reg, NULL }, { "add_cpu_breakpoint", 3, 3, cmd_add_cpu_breakpoint, NULL }, { "remove_cpu_breakpoint", 3, 3, cmd_remove_cpu_breakpoint, NULL }, { "pmem_w32", 4, 4, cmd_pmem_w32, NULL }, { "pmem_r32", 3, 3, cmd_pmem_r32, NULL }, { "pmem_w16", 4, 4, cmd_pmem_w16, NULL }, { "pmem_r16", 3, 3, cmd_pmem_r16, NULL }, { "pmem_cfind", 3, 5, cmd_pmem_cfind, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor VM debugging initialization */ int hypervisor_vm_debug_init(void) { hypervisor_module_t *module; module = hypervisor_register_module("vm_debug",NULL); assert(module != NULL); hypervisor_register_cmd_array(module,vm_cmd_array); return(0); } dynamips-0.2.14/common/hypervisor.h000066400000000000000000000104311241034141600172700ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor routines. */ #ifndef __HYPERVISOR_H__ #define __HYPERVISOR_H__ /* Default TCP port */ #define HYPERVISOR_TCP_PORT 7200 /* Maximum listening socket number */ #define HYPERVISOR_MAX_FD 10 /* Maximum tokens per line */ #define HYPERVISOR_MAX_TOKENS 16 /* Hypervisor status codes */ #define HSC_INFO_OK 100 /* ok */ #define HSC_INFO_MSG 101 /* informative message */ #define HSC_INFO_DEBUG 102 /* debugging message */ #define HSC_ERR_PARSING 200 /* parse error */ #define HSC_ERR_UNK_MODULE 201 /* unknown module */ #define HSC_ERR_UNK_CMD 202 /* unknown command */ #define HSC_ERR_BAD_PARAM 203 /* bad number of parameters */ #define HSC_ERR_INV_PARAM 204 /* invalid parameter */ #define HSC_ERR_BINDING 205 /* binding error */ #define HSC_ERR_CREATE 206 /* unable to create object */ #define HSC_ERR_DELETE 207 /* unable to delete object */ #define HSC_ERR_UNK_OBJ 208 /* unknown object */ #define HSC_ERR_START 209 /* unable to start object */ #define HSC_ERR_STOP 210 /* unable to stop object */ #define HSC_ERR_FILE 211 /* file error */ #define HSC_ERR_BAD_OBJ 212 /* Bad object */ #define HSC_ERR_RENAME 213 /* unable to rename object */ #define HSC_ERR_NOT_FOUND 214 /* not found (generic) */ #define HSC_ERR_UNSPECIFIED 215 /* unspecified error (generic) */ typedef struct hypervisor_conn hypervisor_conn_t; typedef struct hypervisor_cmd hypervisor_cmd_t; typedef struct hypervisor_module hypervisor_module_t; /* Hypervisor connection */ struct hypervisor_conn { pthread_t tid; /* Thread identifier */ volatile int active; /* Connection is active ? */ int client_fd; /* Client FD */ FILE *in,*out; /* I/O buffered streams */ hypervisor_module_t *cur_module; /* Module of current command */ hypervisor_conn_t *next,**pprev; }; /* Hypervisor command handler */ typedef int (*hypervisor_cmd_handler)(hypervisor_conn_t *conn,int argc, char *argv[]); /* Hypervisor command */ struct hypervisor_cmd { char *name; int min_param,max_param; hypervisor_cmd_handler handler; hypervisor_cmd_t *next; }; /* Hypervisor module */ struct hypervisor_module { char *name; void *opt; hypervisor_cmd_t *cmd_list; hypervisor_module_t *next; }; /* Hypervisor NIO initialization */ extern int hypervisor_nio_init(void); /* Hypervisor NIO bridge initialization */ extern int hypervisor_nio_bridge_init(void); /* Hypervisor Frame-Relay switch initialization */ extern int hypervisor_frsw_init(void); /* Hypervisor ATM switch initialization */ extern int hypervisor_atmsw_init(void); /* Hypervisor ATM bridge initialization */ extern int hypervisor_atm_bridge_init(void); /* Hypervisor Ethernet switch initialization */ extern int hypervisor_ethsw_init(void); /* Hypervisor VM initialization */ extern int hypervisor_vm_init(void); /* Hypervisor VM debugging initialization */ extern int hypervisor_vm_debug_init(void); /* Hypervisor store initialization */ extern int hypervisor_store_init(void); /* Send a reply */ int hypervisor_send_reply(hypervisor_conn_t *conn,int code,int done, char *format,...); /* Find a module */ hypervisor_module_t *hypervisor_find_module(char *name); /* Find a command in a module */ hypervisor_cmd_t *hypervisor_find_cmd(hypervisor_module_t *module,char *name); /* Find an object in the registry */ void *hypervisor_find_object(hypervisor_conn_t *conn,char *name,int obj_type); /* Find a VM in the registry */ void *hypervisor_find_vm(hypervisor_conn_t *conn,char *name); /* Register a module */ hypervisor_module_t *hypervisor_register_module(char *name,void *opt); /* Register a list of commands */ int hypervisor_register_cmd_list(hypervisor_module_t *module, hypervisor_cmd_t *cmd_list); /* Register an array of commands */ int hypervisor_register_cmd_array(hypervisor_module_t *module, hypervisor_cmd_t *cmd_array); /* Stop hypervisor from sighandler */ int hypervisor_stopsig(void); /* Hypervisor TCP server */ int hypervisor_tcp_server(char *ip_addr,int tcp_port); #endif dynamips-0.2.14/common/insn_lookup.c000066400000000000000000000320541241034141600174160ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Instruction Lookup Tables. */ #include #include #include #include #include #include #include #include "utils.h" #include "hash.h" #include "insn_lookup.h" #include "dynamips.h" /* Hash function for a CBM */ static inline u_int cbm_hash_f(void *ccbm) { cbm_array_t *cbm = (cbm_array_t *)ccbm; char *p,*s = (char *)(cbm->tab); u_int h,g,i; for(h=0,i=0,p=s;i<(cbm->nr_entries*sizeof(int));p+=1,i++) { h = (h << 4) + *p; if ((g = h & 0xf0000000)) { h = h ^ (g >> 24); h = h ^ g; } } return(h); } /* Comparison function for 2 CBM */ static inline int cbm_cmp_f(void *b1,void *b2) { cbm_array_t *cbm1 = (cbm_array_t *)b1; cbm_array_t *cbm2 = (cbm_array_t *)b2; int i; for(i=0;inr_entries;i++) if (cbm1->tab[i] != cbm2->tab[i]) return(FALSE); return(TRUE); } /* Set bit corresponding to a rule number in a CBM */ static inline void cbm_set_rule(cbm_array_t *cbm,int rule_id) { CBM_ARRAY(cbm,(rule_id >> CBM_SHIFT)) |= 1 << (rule_id & (CBM_SIZE-1)); } /* Clear bit corresponding to a rule number in a CBM */ static inline void cbm_unset_rule(cbm_array_t *cbm,int rule_id) { CBM_ARRAY(cbm,(rule_id >> CBM_SHIFT)) &= ~(1 << (rule_id & (CBM_SIZE-1))); } /* Returns TRUE if bit corresponding to a rule number in a CBM is set */ static inline int cbm_check_rule(cbm_array_t *cbm,int rule_id) { return(CBM_ARRAY(cbm,(rule_id >> CBM_SHIFT)) & (1 << (rule_id & (CBM_SIZE-1)))); } /* Compute bitwise ANDing of two CBM */ static inline void cbm_bitwise_and(cbm_array_t *result,cbm_array_t *a1,cbm_array_t *a2) { int i; /* Compute bitwise ANDing */ for(i=0;inr_entries;i++) CBM_ARRAY(result,i) = CBM_ARRAY(a1,i) & CBM_ARRAY(a2,i); } /* Get first matching rule number */ static inline int cbm_first_match(insn_lookup_t *ilt,cbm_array_t *cbm) { int i; for(i=0;inr_insn;i++) if (cbm_check_rule(cbm,i)) return(i); return(-1); } /* Create a class bitmap (CBM) */ static cbm_array_t *cbm_create(insn_lookup_t *ilt) { cbm_array_t *array; int size; size = CBM_CSIZE(ilt->cbm_size); /* CBM are simply bit arrays */ array = malloc(size); assert(array); memset(array,0,size); array->nr_entries = ilt->cbm_size; return array; } /* Duplicate a class bitmap */ static cbm_array_t *cbm_duplicate(cbm_array_t *cbm) { int size = CBM_CSIZE(cbm->nr_entries); cbm_array_t *array; array = malloc(size); assert(array); memcpy(array,cbm,size); return array; } /* * Get equivalent class corresponding to a class bitmap. Create eqclass * structure if needed (CBM not previously seen). */ static rfc_eqclass_t *cbm_get_eqclass(rfc_array_t *rfct,cbm_array_t *cbm) { rfc_eqclass_t *eqcl; cbm_array_t *bmp; /* Lookup for CBM into hash table */ if ((eqcl = hash_table_lookup(rfct->cbm_hash,cbm)) == NULL) { /* Duplicate CBM */ bmp = cbm_duplicate(cbm); assert(bmp); /* CBM is not already known */ eqcl = malloc(sizeof(rfc_eqclass_t)); assert(eqcl); assert(rfct->nr_eqid < rfct->nr_elements); /* Get a new equivalent ID */ eqcl->eqID = rfct->nr_eqid++; eqcl->cbm = bmp; rfct->id2cbm[eqcl->eqID] = bmp; /* Insert it in hash table */ if (hash_table_insert(rfct->cbm_hash,bmp,eqcl) == -1) return NULL; } return eqcl; } /* Allocate an array for Recursive Flow Classification */ static rfc_array_t *rfc_alloc_array(int nr_elements) { rfc_array_t *array; int total_size; /* Compute size of memory chunk needed to store the array */ total_size = (nr_elements * sizeof(int)) + sizeof(rfc_array_t); array = malloc(total_size); assert(array); memset(array,0,total_size); array->nr_elements = nr_elements; /* Initialize hash table for Class Bitmaps */ array->cbm_hash = hash_table_create(cbm_hash_f,cbm_cmp_f,CBM_HASH_SIZE); assert(array->cbm_hash); /* Initialize table for converting ID to CBM */ array->id2cbm = calloc(nr_elements,sizeof(cbm_array_t *)); assert(array->id2cbm); return(array); } /* Free value of cbm_hash */ static void rfc_free_array_cbm_hash_value(void *key,void *value,void *opt_arg) { free(value); // rfc_eqclass_t * } /* Free an array for Recursive Flow Classification */ static void rfc_free_array(rfc_array_t *array) { int i; assert(array); /* Free hash table for Class Bitmaps */ if (array->cbm_hash) { hash_table_foreach(array->cbm_hash, rfc_free_array_cbm_hash_value, array); hash_table_delete(array->cbm_hash); array->cbm_hash = NULL; } /* Free table for converting ID to CBM */ if (array->id2cbm) { for (i = 0; i < array->nr_elements; i++) { if (array->id2cbm[i]) free(array->id2cbm[i]); } free(array->id2cbm); array->id2cbm = NULL; } /* Free array */ free(array); } /* Check an instruction with specified parameter */ static void rfc_check_insn(insn_lookup_t *ilt,cbm_array_t *cbm, ilt_check_cbk_t pcheck,int value) { void *p; int i; for(i=0;inr_insn;i++) { p = ilt->get_insn(i); if (pcheck(p,value)) cbm_set_rule(cbm,i); else cbm_unset_rule(cbm,i); } } /* RFC Chunk preprocessing: phase 0 */ static rfc_array_t *rfc_phase_0(insn_lookup_t *ilt,ilt_check_cbk_t pcheck) { rfc_eqclass_t *eqcl; rfc_array_t *rfct; cbm_array_t *bmp; int i; /* allocate a temporary class bitmap */ bmp = cbm_create(ilt); assert(bmp); /* Allocate a new RFC array of 16-bits entries */ rfct = rfc_alloc_array(RFC_ARRAY_MAXSIZE); assert(rfct); for(i=0;ieqID[i] = eqcl->eqID; } free(bmp); return rfct; } /* RFC Chunk preprocessing: phase j (j > 0) */ static rfc_array_t *rfc_phase_j(insn_lookup_t *ilt,rfc_array_t *p0, rfc_array_t *p1) { rfc_eqclass_t *eqcl; rfc_array_t *rfct; cbm_array_t *bmp; int nr_elements; int index = 0; int i,j; /* allocate a temporary class bitmap */ bmp = cbm_create(ilt); assert(bmp); /* compute number of elements */ nr_elements = p0->nr_eqid * p1->nr_eqid; /* allocate a new RFC array */ rfct = rfc_alloc_array(nr_elements); assert(rfct); rfct->parent0 = p0; rfct->parent1 = p1; /* make a cross product between p0 and p1 */ for(i=0;inr_eqid;i++) for(j=0;jnr_eqid;j++) { /* compute bitwise AND */ cbm_bitwise_and(bmp,p0->id2cbm[i],p1->id2cbm[j]); /* get equivalent class for this bitmap */ eqcl = cbm_get_eqclass(rfct,bmp); assert(eqcl); /* fill RFC table */ rfct->eqID[index++] = eqcl->eqID; } free(bmp); return rfct; } /* Compute RFC phase 0 */ static void ilt_phase_0(insn_lookup_t *ilt,int idx,ilt_check_cbk_t pcheck) { rfc_array_t *rfct; rfct = rfc_phase_0(ilt,pcheck); assert(rfct); ilt->rfct[idx] = rfct; } /* Compute RFC phase j */ static void ilt_phase_j(insn_lookup_t *ilt,int p0,int p1,int res) { rfc_array_t *rfct; rfct = rfc_phase_j(ilt,ilt->rfct[p0],ilt->rfct[p1]); assert(rfct); ilt->rfct[res] = rfct; } /* Postprocessing */ static void ilt_postprocessing(insn_lookup_t *ilt) { rfc_array_t *rfct = ilt->rfct[2]; int i; for(i=0;inr_elements;i++) rfct->eqID[i] = cbm_first_match(ilt,rfct->id2cbm[rfct->eqID[i]]); } /* Instruction lookup table compilation */ static void ilt_compile(insn_lookup_t *ilt) { ilt_phase_0(ilt,0,ilt->chk_hi); ilt_phase_0(ilt,1,ilt->chk_lo); ilt_phase_j(ilt,0,1,2); ilt_postprocessing(ilt); } /* Dump an instruction lookup table */ _unused static int ilt_dump(char *table_name,insn_lookup_t *ilt) { rfc_array_t *rfct; char *filename; FILE *fd; int i,j; filename = dyn_sprintf("ilt_dump_%s_%s.txt",sw_version_tag,table_name); assert(filename != NULL); fd = fopen(filename,"w"); assert(fd != NULL); fprintf(fd,"ILT %p: nr_insn=%d, cbm_size=%d\n", ilt,ilt->nr_insn,ilt->cbm_size); for(i=0;irfct[i]; fprintf(fd,"RFCT %d: nr_elements=%d, nr_eqid=%d\n", i,rfct->nr_elements,rfct->nr_eqid); for(j=0;jnr_elements;j++) fprintf(fd," (0x%4.4x,0x%4.4x) = 0x%4.4x\n",i,j,rfct->eqID[j]); } fclose(fd); free(filename); return(0); } /* Write the specified RFC array to disk */ static void ilt_store_rfct(FILE *fd,int id,rfc_array_t *rfct) { /* Store RFC array ID + number of elements */ fwrite(&id,sizeof(id),1,fd); fwrite(&rfct->nr_elements,sizeof(rfct->nr_elements),1,fd); fwrite(&rfct->nr_eqid,sizeof(rfct->nr_eqid),1,fd); fwrite(rfct->eqID,sizeof(int),rfct->nr_elements,fd); } /* Write the full instruction lookup table */ static void ilt_store_table(FILE *fd,insn_lookup_t *ilt) { int i; for(i=0;irfct[i] != NULL) ilt_store_rfct(fd,i,ilt->rfct[i]); } /* Load an RFC array from disk */ static int ilt_load_rfct(FILE *fd,insn_lookup_t *ilt) { u_int id,nr_elements,nr_eqid; rfc_array_t *rfct; size_t len; /* Read ID and number of elements */ if ((fread(&id,sizeof(id),1,fd) != 1) || (fread(&nr_elements,sizeof(nr_elements),1,fd) != 1) || (fread(&nr_eqid,sizeof(nr_eqid),1,fd) != 1)) return(-1); if ((id >= RFC_ARRAY_NUMBER) || (nr_elements > RFC_ARRAY_MAXSIZE)) return(-1); /* Allocate the RFC array with the eqID table */ len = sizeof(*rfct) + (nr_elements * sizeof(int)); if (!(rfct = malloc(len))) return(-1); memset(rfct,0,sizeof(*rfct)); rfct->nr_elements = nr_elements; rfct->nr_eqid = nr_eqid; /* Read the equivalent ID array */ if (fread(rfct->eqID,sizeof(int),nr_elements,fd) != nr_elements) { free(rfct); return(-1); } ilt->rfct[id] = rfct; return(0); } /* Check an instruction table loaded from disk */ static int ilt_check_cached_table(insn_lookup_t *ilt) { int i; /* All arrays must have been loaded */ for(i=0;irfct[i]) return(-1); return(0); } /* Load a full instruction table from disk */ static insn_lookup_t *ilt_load_table(FILE *fd) { insn_lookup_t *ilt; int i; if (!(ilt = malloc(sizeof(*ilt)))) return NULL; memset(ilt,0,sizeof(*ilt)); fseek(fd,0,SEEK_SET); for(i=0;icbm_size = normalize_size(nr_insn,CBM_SIZE,CBM_SHIFT); ilt->nr_insn = nr_insn; ilt->get_insn = get_insn; ilt->chk_lo = chk_lo; ilt->chk_hi = chk_hi; /* Compile the instruction opcodes */ ilt_compile(ilt); /* Store the result on disk for future exec */ ilt_cache_store(table_name,ilt); return(ilt); } /* Destroy an instruction lookup table */ void ilt_destroy(insn_lookup_t *ilt) { int i; assert(ilt); /* Free instruction opcodes */ for (i = 0; i < RFC_ARRAY_NUMBER; i++) { if (ilt->rfct[i]) rfc_free_array(ilt->rfct[i]); } /* Free instruction lookup table */ free(ilt); } dynamips-0.2.14/common/insn_lookup.h000066400000000000000000000054371241034141600174300ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * MIPS Instruction Lookup Tables. */ #ifndef __INSN_LOOKUP_H__ #define __INSN_LOOKUP_H__ #include "utils.h" #include "hash.h" /* Forward declaration for instruction lookup table */ typedef struct insn_lookup insn_lookup_t; /* CBM (Class BitMap) array */ #define CBM_SHIFT 5 /* log2(32) */ #define CBM_SIZE (1 << CBM_SHIFT) /* Arrays of 32-bits Integers */ #define CBM_HASH_SIZE 256 /* Size for Hash Tables */ typedef struct cbm_array cbm_array_t; struct cbm_array { int nr_entries; /* Number of entries */ int tab[0]; /* Values... */ }; #define CBM_ARRAY(array,i) ((array)->tab[(i)]) #define CBM_CSIZE(count) (((count)*sizeof(int))+sizeof(cbm_array_t)) /* callback function prototype for instruction checking */ typedef int (*ilt_check_cbk_t)(void *,int value); typedef void *(*ilt_get_insn_cbk_t)(int index); /* RFC (Recursive Flow Classification) arrays */ #define RFC_ARRAY_MAXSIZE 65536 #define RFC_ARRAY_MAXBITS 16 #define RFC_ARRAY_NUMBER 3 typedef struct rfc_array rfc_array_t; struct rfc_array { rfc_array_t *parent0,*parent1; int nr_elements; /* Number of Equivalent ID */ int nr_eqid; /* Hash Table for Class Bitmaps */ hash_table_t *cbm_hash; /* Array to get Class Bitmaps from IDs */ cbm_array_t **id2cbm; /* Equivalent ID (eqID) array */ int eqID[0]; }; /* Equivalent Classes */ typedef struct rfc_eqclass rfc_eqclass_t; struct rfc_eqclass { cbm_array_t *cbm; /* Class Bitmap */ int eqID; /* Index associated to this class */ }; /* Instruction lookup table */ struct insn_lookup { int nr_insn; /* Number of instructions */ int cbm_size; /* Size of Class Bitmaps */ ilt_get_insn_cbk_t get_insn; ilt_check_cbk_t chk_lo,chk_hi; /* RFC tables */ rfc_array_t *rfct[RFC_ARRAY_NUMBER]; }; /* Instruction lookup */ static forced_inline int ilt_get_index(rfc_array_t *a1,rfc_array_t *a2, int i1,int i2) { return((a1->eqID[i1]*a2->nr_eqid) + a2->eqID[i2]); } static forced_inline int ilt_get_idx(insn_lookup_t *ilt,int a1,int a2, int i1,int i2) { return(ilt_get_index(ilt->rfct[a1],ilt->rfct[a2],i1,i2)); } static forced_inline int ilt_lookup(insn_lookup_t *ilt,mips_insn_t insn) { int id_i; id_i = ilt_get_idx(ilt,0,1,insn >> 16,insn & 0xFFFF); return(ilt->rfct[2]->eqID[id_i]); } /* Create an instruction lookup table */ insn_lookup_t *ilt_create(char *table_name, int nr_insn,ilt_get_insn_cbk_t get_insn, ilt_check_cbk_t chk_lo,ilt_check_cbk_t chk_hi); /* Destroy an instruction lookup table */ void ilt_destroy(insn_lookup_t *ilt); #endif dynamips-0.2.14/common/jit_op.c000066400000000000000000000041521241034141600163400ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * JIT operations. */ #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "jit_op.h" u_int jit_op_blk_sizes[JIT_OP_POOL_NR] = { 0, 32, 64, 128, 256, 384, 512, 1024, }; /* Get a JIT op (allocate one if necessary) */ jit_op_t *jit_op_get(cpu_gen_t *cpu,int size_index,u_int opcode) { jit_op_t *op; size_t len; assert(size_index < JIT_OP_POOL_NR); op = cpu->jit_op_pool[size_index]; if (op != NULL) { assert(op->ob_size_index == size_index); cpu->jit_op_pool[size_index] = op->next; } else { /* no block found, allocate one */ len = sizeof(*op) + jit_op_blk_sizes[size_index]; op = malloc(len); assert(op != NULL); op->ob_size_index = size_index; } op->opcode = opcode; op->param[0] = op->param[1] = op->param[2] = -1; op->next = NULL; op->ob_ptr = op->ob_data; op->arg_ptr = NULL; op->insn_name = NULL; return op; } /* Release a JIT op */ void jit_op_free(cpu_gen_t *cpu,jit_op_t *op) { assert(op->ob_size_index < JIT_OP_POOL_NR); op->next = cpu->jit_op_pool[op->ob_size_index]; cpu->jit_op_pool[op->ob_size_index] = op; } /* Free a list of JIT ops */ void jit_op_free_list(cpu_gen_t *cpu,jit_op_t *op_list) { jit_op_t *op,*opn; for(op=op_list;op;op=opn) { opn = op->next; jit_op_free(cpu,op); } } /* Initialize JIT op pools for the specified CPU */ int jit_op_init_cpu(cpu_gen_t *cpu) { cpu->jit_op_array = calloc(cpu->jit_op_array_size,sizeof(jit_op_t *)); if (!cpu->jit_op_array) return(-1); memset(cpu->jit_op_pool,0,sizeof(cpu->jit_op_pool)); return(0); } /* Free memory used by pools */ void jit_op_free_pools(cpu_gen_t *cpu) { jit_op_t *op,*opn; int i; for(i=0;ijit_op_pool[i];op;op=opn) { opn = op->next; free(op); } cpu->jit_op_pool[i] = NULL; } } dynamips-0.2.14/common/jit_op.h000066400000000000000000000032151241034141600163440ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) */ #ifndef __JIT_OP_H__ #define __JIT_OP_H__ #include "utils.h" /* Number of JIT pools */ #define JIT_OP_POOL_NR 8 /* Invalid register in op */ #define JIT_OP_INV_REG -1 /* All flags */ #define JIT_OP_PPC_ALL_FLAGS -1 /* All registers */ #define JIT_OP_ALL_REGS -1 /* JIT opcodes */ enum { JIT_OP_INVALID = 0, JIT_OP_INSN_OUTPUT, JIT_OP_BRANCH_TARGET, JIT_OP_BRANCH_JUMP, JIT_OP_EOB, JIT_OP_LOAD_GPR, JIT_OP_STORE_GPR, JIT_OP_UPDATE_FLAGS, JIT_OP_REQUIRE_FLAGS, JIT_OP_TRASH_FLAGS, JIT_OP_ALTER_HOST_REG, JIT_OP_MOVE_HOST_REG, JIT_OP_SET_HOST_REG_IMM32, }; /* JIT operation */ struct jit_op { u_int opcode; int param[3]; void *arg_ptr; char *insn_name; struct jit_op *next; /* JIT output buffer */ u_int ob_size_index; u_char *ob_final; u_char *ob_ptr; u_char ob_data[0]; }; extern u_int jit_op_blk_sizes[]; /* Find a specific opcode in a JIT op list */ static inline jit_op_t *jit_op_find_opcode(jit_op_t *op_list,u_int opcode) { jit_op_t *op; for(op=op_list;op;op=op->next) if (op->opcode == opcode) return op; return NULL; } /* Get a JIT op (allocate one if necessary) */ jit_op_t *jit_op_get(cpu_gen_t *cpu,int size_index,u_int opcode); /* Release a JIT op */ void jit_op_free(cpu_gen_t *cpu,jit_op_t *op); /* Free a list of JIT ops */ void jit_op_free_list(cpu_gen_t *cpu,jit_op_t *op_list); /* Initialize JIT op pools for the specified CPU */ int jit_op_init_cpu(cpu_gen_t *cpu); /* Free memory used by pools */ void jit_op_free_pools(cpu_gen_t *cpu); #endif dynamips-0.2.14/common/linux_eth.c000066400000000000000000000054511241034141600170560ustar00rootroot00000000000000/* * Copyright (c) 2006 Christophe Fillot. * E-mail: cf@utc.fr * * linux_eth.c: module used to send/receive Ethernet packets. * * Specific to the Linux operating system. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "linux_eth.h" /* Get interface index of specified device */ int lnx_eth_get_dev_index(char *name) { struct ifreq if_req; int fd; /* Create dummy file descriptor */ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { fprintf(stderr,"eth_get_dev_index: socket: %s\n",strerror(errno)); return(-1); } memset((void *)&if_req,0,sizeof(if_req)); strcpy(if_req.ifr_name,name); if (ioctl(fd,SIOCGIFINDEX,&if_req) < 0) { fprintf(stderr,"eth_get_dev_index: SIOCGIFINDEX: %s\n",strerror(errno)); close(fd); return(-1); } close(fd); return(if_req.ifr_ifindex); } /* Initialize a new ethernet raw socket */ int lnx_eth_init_socket(char *device) { struct sockaddr_ll sa; struct packet_mreq mreq; int sck; if ((sck = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL))) == -1) { fprintf(stderr,"eth_init_socket: socket: %s\n",strerror(errno)); return(-1); } memset(&sa,0,sizeof(struct sockaddr_ll)); sa.sll_family = AF_PACKET; sa.sll_protocol = htons(ETH_P_ALL); sa.sll_hatype = ARPHRD_ETHER; sa.sll_halen = ETH_ALEN; sa.sll_ifindex = lnx_eth_get_dev_index(device); memset(&mreq,0,sizeof(mreq)); mreq.mr_ifindex = sa.sll_ifindex; mreq.mr_type = PACKET_MR_PROMISC; if (bind(sck,(struct sockaddr *)&sa,sizeof(struct sockaddr_ll)) == -1) { fprintf(stderr,"eth_init_socket: bind: %s\n",strerror(errno)); return(-1); } if (setsockopt(sck,SOL_PACKET,PACKET_ADD_MEMBERSHIP, &mreq,sizeof(mreq)) == -1) { fprintf(stderr,"eth_init_socket: setsockopt: %s\n",strerror(errno)); return(-1); } return(sck); } /* Send an ethernet frame */ ssize_t lnx_eth_send(int sck,int dev_id,char *buffer,size_t len) { struct sockaddr_ll sa; memset(&sa,0,sizeof(struct sockaddr_ll)); sa.sll_family = AF_PACKET; sa.sll_protocol = htons(ETH_P_ALL); sa.sll_hatype = ARPHRD_ETHER; sa.sll_halen = ETH_ALEN; sa.sll_ifindex = dev_id; return(sendto(sck,buffer,len,0,(struct sockaddr *)&sa,sizeof(sa))); } /* Receive an ethernet frame */ ssize_t lnx_eth_recv(int sck,char *buffer,size_t len) { return(recv(sck,buffer,len,0)); } dynamips-0.2.14/common/linux_eth.h000066400000000000000000000011511241034141600170540ustar00rootroot00000000000000/* * Copyright (c) 2006 Christophe Fillot. * E-mail: cf@utc.fr * * linux_eth.c: module used to send/receive Ethernet packets. * * Specific to the Linux operating system. */ #ifndef __LINUX_ETH_H__ #define __LINUX_ETH_H__ 1 #include /* Get interface index of specified device */ int lnx_eth_get_dev_index(char *name); /* Initialize a new ethernet raw socket */ int lnx_eth_init_socket(char *device); /* Send an ethernet frame */ ssize_t lnx_eth_send(int sck,int dev_id,char *buffer,size_t len); /* Receive an ethernet frame */ ssize_t lnx_eth_recv(int sck,char *buffer,size_t len); #endif dynamips-0.2.14/common/memory.c000066400000000000000000000236411241034141600163700ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" /* Record a memory access */ void memlog_rec_access(cpu_gen_t *cpu,m_uint64_t vaddr,m_uint64_t data, m_uint32_t op_size,m_uint32_t op_type) { memlog_access_t *acc; acc = &cpu->memlog_array[cpu->memlog_pos]; acc->iaddr = cpu_get_pc(cpu); acc->vaddr = vaddr; acc->data = data; acc->op_size = op_size; acc->op_type = op_type; acc->data_valid = (op_type == MTS_WRITE); cpu->memlog_pos = (cpu->memlog_pos + 1) & (MEMLOG_COUNT - 1); } /* Show the latest memory accesses */ void memlog_dump(cpu_gen_t *cpu) { memlog_access_t *acc; char s_data[64]; u_int i,pos; for(i=0;imemlog_pos + i; pos &= (MEMLOG_COUNT-1); acc = &cpu->memlog_array[pos]; if (cpu_get_pc(cpu)) { if (acc->data_valid) snprintf(s_data,sizeof(s_data),"0x%llx",acc->data); else snprintf(s_data,sizeof(s_data),"XXXXXXXX"); printf("CPU%u: pc=0x%8.8llx, vaddr=0x%8.8llx, " "size=%u, type=%s, data=%s\n", cpu->id,acc->iaddr,acc->vaddr,acc->op_size, (acc->op_type == MTS_READ) ? "read " : "write", s_data); } } } /* Update the data obtained by a read access */ void memlog_update_read(cpu_gen_t *cpu,m_iptr_t raddr) { memlog_access_t *acc; acc = &cpu->memlog_array[(cpu->memlog_pos-1) & (MEMLOG_COUNT-1)]; if (acc->op_type == MTS_READ) { switch(acc->op_size) { case 1: acc->data = *(m_uint8_t *)raddr; break; case 2: acc->data = vmtoh16(*(m_uint16_t *)raddr); break; case 4: acc->data = vmtoh32(*(m_uint32_t *)raddr); break; case 8: acc->data = vmtoh64(*(m_uint64_t *)raddr); break; } acc->data_valid = TRUE; } } /* === Operations on physical memory ====================================== */ /* Get host pointer for the physical address */ static inline void *physmem_get_hptr(vm_instance_t *vm,m_uint64_t paddr, u_int op_size,u_int op_type, m_uint64_t *data) { struct vdevice *dev; m_uint32_t offset; void *ptr; int cow; if (!(dev = dev_lookup(vm,paddr,FALSE))) return NULL; if (dev->flags & VDEVICE_FLAG_SPARSE) { ptr = (void *)dev_sparse_get_host_addr(vm,dev,paddr,op_type,&cow); if (!ptr) return NULL; return(ptr + (paddr & VM_PAGE_IMASK)); } if ((dev->host_addr != 0) && !(dev->flags & VDEVICE_FLAG_NO_MTS_MMAP)) return((void *)dev->host_addr + (paddr - dev->phys_addr)); if (op_size == 0) return NULL; offset = paddr - dev->phys_addr; return(dev->handler(vm->boot_cpu,dev,offset,op_size,op_type,data)); } /* Copy a memory block from VM physical RAM to real host */ void physmem_copy_from_vm(vm_instance_t *vm,void *real_buffer, m_uint64_t paddr,size_t len) { m_uint64_t dummy; m_uint32_t r; u_char *ptr; while(len > 0) { r = m_min(VM_PAGE_SIZE - (paddr & VM_PAGE_IMASK), len); ptr = physmem_get_hptr(vm,paddr,0,MTS_READ,&dummy); if (likely(ptr != NULL)) { memcpy(real_buffer,ptr,r); } else { r = m_min(len,4); switch(r) { case 4: *(m_uint32_t *)real_buffer = htovm32(physmem_copy_u32_from_vm(vm,paddr)); break; case 2: *(m_uint16_t *)real_buffer = htovm16(physmem_copy_u16_from_vm(vm,paddr)); break; case 1: *(m_uint8_t *)real_buffer = physmem_copy_u8_from_vm(vm,paddr); break; } } real_buffer += r; paddr += r; len -= r; } } /* Copy a memory block to VM physical RAM from real host */ void physmem_copy_to_vm(vm_instance_t *vm,void *real_buffer, m_uint64_t paddr,size_t len) { m_uint64_t dummy; m_uint32_t r; u_char *ptr; while(len > 0) { r = m_min(VM_PAGE_SIZE - (paddr & VM_PAGE_IMASK), len); ptr = physmem_get_hptr(vm,paddr,0,MTS_WRITE,&dummy); if (likely(ptr != NULL)) { memcpy(ptr,real_buffer,r); } else { r = m_min(len,4); switch(r) { case 4: physmem_copy_u32_to_vm(vm,paddr, htovm32(*(m_uint32_t *)real_buffer)); break; case 2: physmem_copy_u16_to_vm(vm,paddr, htovm16(*(m_uint16_t *)real_buffer)); break; case 1: physmem_copy_u8_to_vm(vm,paddr,*(m_uint8_t *)real_buffer); break; } } real_buffer += r; paddr += r; len -= r; } } /* Copy a 32-bit word from the VM physical RAM to real host */ m_uint32_t physmem_copy_u32_from_vm(vm_instance_t *vm,m_uint64_t paddr) { m_uint64_t tmp = 0; m_uint32_t *ptr; if ((ptr = physmem_get_hptr(vm,paddr,4,MTS_READ,&tmp)) != NULL) return(vmtoh32(*ptr)); return(tmp); } /* Copy a 32-bit word to the VM physical RAM from real host */ void physmem_copy_u32_to_vm(vm_instance_t *vm,m_uint64_t paddr,m_uint32_t val) { m_uint64_t tmp = val; m_uint32_t *ptr; if ((ptr = physmem_get_hptr(vm,paddr,4,MTS_WRITE,&tmp)) != NULL) *ptr = htovm32(val); } /* Copy a 16-bit word from the VM physical RAM to real host */ m_uint16_t physmem_copy_u16_from_vm(vm_instance_t *vm,m_uint64_t paddr) { m_uint64_t tmp = 0; m_uint16_t *ptr; if ((ptr = physmem_get_hptr(vm,paddr,2,MTS_READ,&tmp)) != NULL) return(vmtoh16(*ptr)); return(tmp); } /* Copy a 16-bit word to the VM physical RAM from real host */ void physmem_copy_u16_to_vm(vm_instance_t *vm,m_uint64_t paddr,m_uint16_t val) { m_uint64_t tmp = val; m_uint16_t *ptr; if ((ptr = physmem_get_hptr(vm,paddr,2,MTS_WRITE,&tmp)) != NULL) *ptr = htovm16(val); } /* Copy a byte from the VM physical RAM to real host */ m_uint8_t physmem_copy_u8_from_vm(vm_instance_t *vm,m_uint64_t paddr) { m_uint64_t tmp = 0; m_uint8_t *ptr; if ((ptr = physmem_get_hptr(vm,paddr,1,MTS_READ,&tmp)) != NULL) return(*ptr); return(tmp); } /* Copy a 16-bit word to the VM physical RAM from real host */ void physmem_copy_u8_to_vm(vm_instance_t *vm,m_uint64_t paddr,m_uint8_t val) { m_uint64_t tmp = val; m_uint8_t *ptr; if ((ptr = physmem_get_hptr(vm,paddr,1,MTS_WRITE,&tmp)) != NULL) *ptr = val; } /* DMA transfer operation */ void physmem_dma_transfer(vm_instance_t *vm,m_uint64_t src,m_uint64_t dst, size_t len) { m_uint64_t dummy; u_char *sptr,*dptr; size_t clen,sl,dl; while(len > 0) { sptr = physmem_get_hptr(vm,src,0,MTS_READ,&dummy); dptr = physmem_get_hptr(vm,dst,0,MTS_WRITE,&dummy); if (!sptr || !dptr) { vm_log(vm,"DMA","unable to transfer from 0x%llx to 0x%llx\n",src,dst); return; } sl = VM_PAGE_SIZE - (src & VM_PAGE_IMASK); dl = VM_PAGE_SIZE - (dst & VM_PAGE_IMASK); clen = m_min(sl,dl); clen = m_min(clen,len); memcpy(dptr,sptr,clen); src += clen; dst += clen; len -= clen; } } /* strlen in VM physical memory */ size_t physmem_strlen(vm_instance_t *vm,m_uint64_t paddr) { struct vdevice *vm_ram; size_t len = 0; char *ptr; if ((vm_ram = dev_lookup(vm,paddr,TRUE)) != NULL) { ptr = (char *)vm_ram->host_addr + (paddr - vm_ram->phys_addr); len = strlen(ptr); } return(len); } /* find sequence of bytes in VM cacheable physical memory interval [first,last] */ int physmem_cfind(vm_instance_t *vm,m_uint8_t *bytes,size_t len, m_uint64_t first,m_uint64_t last, m_uint64_t *paddr) { struct vdevice *dev; m_uint8_t *buffer; size_t i, buflen; m_uint64_t last_dev_addr; if (len <= 0 || first > last || len + 1 > last - first) return(-1); // nothing to find buffer = malloc(len); i = 0; buflen = 0; for(dev = vm->dev_list; dev; dev = dev->next) { // each device if (dev == NULL || dev->phys_addr > last) break; // no more devices if (!(dev->flags & VDEVICE_FLAG_CACHING)) continue; // not cacheable // reset buffer if previous device is not continuous in memory if (first + buflen != dev->phys_addr) { i = 0; buflen = 0; } last_dev_addr = dev->phys_addr + dev->phys_len - 1; if (last_dev_addr > last) last_dev_addr = last; // fill buffer for (; buflen < len && first + buflen <= last_dev_addr; ++buflen) { buffer[buflen] = physmem_copy_u8_from_vm(vm, first + buflen); } if (buflen < len) continue; // not enough data // test each possible match while (first + len <= last_dev_addr) { if (i >= len) i = 0; if ((i == 0 || memcmp(buffer, bytes+len-i, i) == 0) && memcmp(buffer+i, bytes, len-i) == 0) { // match found if (paddr) *paddr = first; free(buffer); return(0); } buffer[i] = physmem_copy_u8_from_vm(vm, first + len); ++i; ++first; } } free(buffer); return(-1); // not found } /* Physical memory dump (32-bit words) */ void physmem_dump_vm(vm_instance_t *vm,m_uint64_t paddr,m_uint32_t u32_count) { m_uint32_t i; for(i=0;i #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "mempool.h" /* * Internal function used to allocate a memory block, and do basic operations * on it. It does not manipulate pools, so no mutex is needed. */ static inline memblock_t *memblock_alloc(size_t size,int zeroed) { memblock_t *block; size_t total_size; total_size = size + sizeof(memblock_t); if (!(block = malloc(total_size))) return NULL; if (zeroed) memset(block,0,total_size); block->tag = MEMBLOCK_TAG; block->block_size = size; block->prev = block->next = NULL; return block; } /* Insert block in linked list */ static inline void memblock_insert(mempool_t *pool,memblock_t *block) { MEMPOOL_LOCK(pool); pool->nr_blocks++; pool->total_size += block->block_size; block->prev = NULL; block->next = pool->block_list; if (block->next) block->next->prev = block; pool->block_list = block; MEMPOOL_UNLOCK(pool); } /* Remove block from linked list */ static inline void memblock_delete(mempool_t *pool,memblock_t *block) { MEMPOOL_LOCK(pool); pool->nr_blocks--; pool->total_size -= block->block_size; if (!block->prev) pool->block_list = block->next; else block->prev->next = block->next; if (block->next) block->next->prev = block->prev; block->next = block->prev = NULL; MEMPOOL_UNLOCK(pool); } /* Allocate a new block in specified pool (internal function) */ static inline void *mp_alloc_inline(mempool_t *pool,size_t size,int zeroed) { memblock_t *block; if (!(block = memblock_alloc(size,zeroed))) return NULL; block->pool = pool; memblock_insert(pool,block); return(block->data); } /* Allocate a new block in specified pool */ void *mp_alloc(mempool_t *pool,size_t size) { return(mp_alloc_inline(pool,size,TRUE)); } /* Allocate a new block which will not be zeroed */ void *mp_alloc_n0(mempool_t *pool,size_t size) { return(mp_alloc_inline(pool,size,FALSE)); } /* Reallocate a block */ void *mp_realloc(void *addr,size_t new_size) { memblock_t *ptr,*block = (memblock_t *)addr - 1; mempool_t *pool; size_t total_size; assert(block->tag == MEMBLOCK_TAG); pool = block->pool; /* remove this block from list */ memblock_delete(pool,block); /* reallocate block with specified size */ total_size = new_size + sizeof(memblock_t); if (!(ptr = realloc(block,total_size))) { memblock_insert(pool,block); return NULL; } ptr->block_size = new_size; memblock_insert(pool,ptr); return ptr->data; } /* Allocate a new memory block and copy data into it */ void *mp_dup(mempool_t *pool,void *data,size_t size) { void *p; if ((p = mp_alloc_n0(pool,size))) memcpy(p,data,size); return p; } /* Duplicate specified string and insert it in a memory pool */ char *mp_strdup(mempool_t *pool,char *str) { char *new_str; if ((new_str = mp_alloc(pool,strlen(str)+1)) == NULL) return NULL; strcpy(new_str,str); return new_str; } /* Free block at specified address */ int mp_free(void *addr) { memblock_t *block = (memblock_t *)addr - 1; mempool_t *pool; if (addr != NULL) { assert(block->tag == MEMBLOCK_TAG); pool = block->pool; memblock_delete(pool,block); memset(block,0,sizeof(memblock_t)); free(block); } return(0); } /* Free block at specified address and clean pointer */ int mp_free_ptr(void *addr) { void *p; assert(addr != NULL); p = *(void **)addr; *(void **)addr = NULL; mp_free(p); return(0); } /* Free all blocks of specified pool */ void mp_free_all_blocks(mempool_t *pool) { memblock_t *block,*next; MEMPOOL_LOCK(pool); for(block=pool->block_list;block;block=next) { next = block->next; free(block); } pool->block_list = NULL; pool->nr_blocks = 0; pool->total_size = 0; MEMPOOL_UNLOCK(pool); } /* Free specified memory pool */ void mp_free_pool(mempool_t *pool) { mp_free_all_blocks(pool); if (!(pool->flags & MEMPOOL_FIXED)) free(pool); } /* Create a new pool in a fixed memory area */ mempool_t *mp_create_fixed_pool(mempool_t *mp,char *name) { memset(mp,0,sizeof(*mp)); if (pthread_mutex_init(&mp->lock,NULL) != 0) return NULL; mp->name = name; mp->block_list = NULL; mp->flags = MEMPOOL_FIXED; return mp; } /* Create a new pool */ mempool_t *mp_create_pool(char *name) { mempool_t *mp = malloc(sizeof(*mp)); if (!mp || !mp_create_fixed_pool(mp,name)) { free(mp); return NULL; } mp->flags = 0; /* clear "FIXED" flag */ return mp; } dynamips-0.2.14/common/mempool.h000066400000000000000000000052361241034141600165350ustar00rootroot00000000000000/* * Copyright (c) 1999-2006 Christophe Fillot. * E-mail: cf@utc.fr * * mempool.h: Simple Memory Pools. */ #ifndef __MEMPOOL_H__ #define __MEMPOOL_H__ 1 #include #include #include #include "utils.h" /* Memory Pool "Fixed" Flag */ #define MEMPOOL_FIXED 1 /* Dummy value used to check if a memory block is invalid */ #define MEMBLOCK_TAG 0xdeadbeef typedef struct memblock memblock_t; typedef struct mempool mempool_t; /* Memory block */ struct memblock { int tag; /* MEMBLOCK_TAG if block is valid */ size_t block_size; /* Block size (without header) */ memblock_t *next,*prev; /* Double linked list pointers */ mempool_t *pool; /* Pool which contains this block */ m_uint64_t data[0]; /* Memory block itself */ }; /* Memory Pool */ struct mempool { memblock_t *block_list; /* Double-linked block list */ pthread_mutex_t lock; /* Mutex for managing pool */ char *name; /* Name of this pool */ int flags; /* Flags */ int nr_blocks; /* Number of blocks in this pool */ size_t total_size; /* Total bytes allocated */ size_t max_size; /* Maximum memory */ }; /* Lock and unlock access to a memory pool */ #define MEMPOOL_LOCK(mp) pthread_mutex_lock(&(mp)->lock) #define MEMPOOL_UNLOCK(mp) pthread_mutex_unlock(&(mp)->lock) /* Callback function for use with mp_foreach */ typedef void (*mp_foreach_cbk)(memblock_t *block,void *user_arg); /* Execute an action for each block in specified pool */ static inline void mp_foreach(mempool_t *pool,mp_foreach_cbk cbk,void *arg) { memblock_t *mb; for(mb=pool->block_list;mb;mb=mb->next) cbk(mb,arg); } /* Allocate a new block in specified pool */ void *mp_alloc(mempool_t *pool,size_t size); /* Allocate a new block which will not be zeroed */ void *mp_alloc_n0(mempool_t *pool,size_t size); /* Reallocate a block */ void *mp_realloc(void *addr,size_t new_size); /* Allocate a new memory block and copy data into it */ void *mp_dup(mempool_t *pool,void *data,size_t size); /* Duplicate specified string and insert it in a memory pool */ char *mp_strdup(mempool_t *pool,char *str); /* Free block at specified address */ int mp_free(void *addr); /* Free block at specified address and clean pointer */ int mp_free_ptr(void *addr); /* Free all blocks of specified pool */ void mp_free_all_blocks(mempool_t *pool); /* Free specified memory pool */ void mp_free_pool(mempool_t *pool); /* Create a new pool in a fixed memory area */ mempool_t *mp_create_fixed_pool(mempool_t *mp,char *name); /* Create a new pool */ mempool_t *mp_create_pool(char *name); #endif dynamips-0.2.14/common/mips64_mem.h000066400000000000000000000004771241034141600170470ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) */ #ifndef __MIPS64_MEM_H__ #define __MIPS64_MEM_H__ /* Shutdown the MTS subsystem */ void mips64_mem_shutdown(cpu_mips_t *cpu); /* Set the address mode */ int mips64_set_addr_mode(cpu_mips_t *cpu,u_int addr_mode); #endif dynamips-0.2.14/common/net.c000066400000000000000000000714061241034141600156500ustar00rootroot00000000000000/* * Copyright (c) 2005,2006 Christophe Fillot. * E-mail: cf@utc.fr * * Network Utility functions. */ #include "utils.h" #include "net.h" #include "crc.h" /* * IP mask table, which allows to find quickly a network mask * with a prefix length. */ n_ip_addr_t ip_masks[N_IP_ADDR_BITS+1] = { 0x0, 0x80000000, 0xC0000000, 0xE0000000, 0xF0000000, 0xF8000000, 0xFC000000, 0xFE000000, 0xFF000000, 0xFF800000, 0xFFC00000, 0xFFE00000, 0xFFF00000, 0xFFF80000, 0xFFFC0000, 0xFFFE0000, 0xFFFF0000, 0xFFFF8000, 0xFFFFC000, 0xFFFFE000, 0xFFFFF000, 0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00, 0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0, 0xFFFFFFE0, 0xFFFFFFF0, 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE, 0xFFFFFFFF }; /* * IPv6 mask table, which allows to find quickly a network mask * with a prefix length. Note this is a particularly ugly way * to do this, since we use statically 2 Kb. */ n_ipv6_addr_t ipv6_masks[N_IPV6_ADDR_BITS+1]; /* Initialize IPv6 masks */ void ipv6_init_masks(void) { int i,index; /* Set all bits to 1 */ memset(ipv6_masks,0xff,sizeof(ipv6_masks)); for(i=0;i> 3; /* Compute byte index (divide by 8) */ /* rotate byte */ ipv6_masks[i].ip6.u6_addr8[index++] <<= (8 - (i & 7)); /* clear following bytes */ while(index N_IP_ADDR_BITS) return(-1); if ((tmp = strdup(token)) == NULL) return(-1); sl = strchr(tmp,'/'); *sl = 0; /* Parse IP Address */ if (n_ip_aton(net_addr,tmp) == -1) { free(tmp); return(-1); } /* Set netmask */ *net_mask = ip_masks[mask]; free(tmp); return(0); } #if HAS_RFC2553 /* Parse an IPv6 CIDR prefix */ int ipv6_parse_cidr(char *token,n_ipv6_addr_t *net_addr,u_int *net_mask) { char *sl,*tmp,*err; u_long mask; /* Find separator */ if ((sl = strchr(token,'/')) == NULL) return(-1); /* Get mask */ mask = strtoul(sl+1,&err,0); if (*err != 0) return(-1); /* Ensure that mask has a correct value */ if (mask > N_IPV6_ADDR_BITS) return(-1); if ((tmp = strdup(token)) == NULL) return(-1); sl = strchr(tmp,'/'); *sl = 0; /* Parse IP Address */ if (n_ipv6_aton(net_addr,tmp) <= 0) { free(tmp); return(-1); } /* Set netmask */ *net_mask = (u_int)mask; free(tmp); return(0); } #endif /* Parse a processor board id and return the eeprom settings in a buffer */ int parse_board_id(m_uint8_t *buf,const char *id,int encode) { // Encode the serial board id // encode 4 maps this into 4 bytes // encode 9 maps into 9 bytes // encode 11 maps into 11 bytes // memset(buf,0,11); if (encode == 4) { int res; int v; res = sscanf(id,"%d",&v); if (res != 1) return (-1); buf[3] = (v & 0xFF); v>>=8; buf[2] = (v & 0xFF); v>>=8; buf[1] = (v & 0xFF); v>>=8; buf[0] = (v & 0xFF); v>>=8; //printf("%x %x %x %x \n",buf[0],buf[1],buf[2],buf[3]); return (0); } else if (encode == 9) { int res; res = sscanf(id,"%c%c%c%2hx%2hx%c%c%c%c", &buf[0],&buf[1], &buf[2],(unsigned short int *)&buf[3], (unsigned short int *)&buf[4],&buf[5], &buf[6],&buf[7],&buf[8] ); if (res != 9) return (-1); //printf("%x %x %x %x %x %x %x %x .. %x\n", // buf[0],buf[1],buf[2],buf[3], // buf[4],buf[5],buf[6],buf[7] ,buf[8]); return (0); } else if (encode == 11) { int res; res = sscanf(id,"%c%c%c%c%c%c%c%c%c%c%c", &buf[0],&buf[1], &buf[2],&buf[3], &buf[4],&buf[5], &buf[6],&buf[7], &buf[8],&buf[9], &buf[10] ); if (res != 11) return (-1); //printf("%x %x %x %x %x %x %x %x %x %x .. %x\n", // buf[0],buf[1],buf[2],buf[3],buf[4],buf[5], // buf[6],buf[7],buf[8],buf[9], buf[10]); return (0); } return (-1); } /* Parse a MAC address */ int parse_mac_addr(n_eth_addr_t *addr,char *str) { u_int v[N_ETH_ALEN]; int i,res; /* First try, standard format (00:01:02:03:04:05) */ res = sscanf(str,"%x:%x:%x:%x:%x:%x",&v[0],&v[1],&v[2],&v[3],&v[4],&v[5]); if (res == 6) { for(i=0;ieth_addr_byte[i] = v[i]; return(0); } /* Second try, Cisco format (0001.0002.0003) */ res = sscanf(str,"%x.%x.%x",&v[0],&v[1],&v[2]); if (res == 3) { addr->eth_addr_byte[0] = (v[0] >> 8) & 0xFF; addr->eth_addr_byte[1] = v[0] & 0xFF; addr->eth_addr_byte[2] = (v[1] >> 8) & 0xFF; addr->eth_addr_byte[3] = v[1] & 0xFF; addr->eth_addr_byte[4] = (v[2] >> 8) & 0xFF; addr->eth_addr_byte[5] = v[2] & 0xFF; return(0); } return(-1); } /* Convert an Ethernet address into a string */ char *n_eth_ntoa(char *buffer,n_eth_addr_t *addr,int format) { char *str_format; if (format == 0) { str_format = "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x"; } else { str_format = "%2.2x%2.2x.%2.2x%2.2x.%2.2x%2.2x"; } sprintf(buffer,str_format, addr->eth_addr_byte[0],addr->eth_addr_byte[1], addr->eth_addr_byte[2],addr->eth_addr_byte[3], addr->eth_addr_byte[4],addr->eth_addr_byte[5]); return(buffer); } #if HAS_RFC2553 /* Create a new socket to connect to specified host */ int udp_connect(int local_port,char *remote_host,int remote_port) { struct addrinfo hints,*res,*res0; struct sockaddr_storage st; int error, sck = -1; char port_str[20]; memset(&hints,0,sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; snprintf(port_str,sizeof(port_str),"%d",remote_port); if ((error = getaddrinfo(remote_host,port_str,&hints,&res0)) != 0) { fprintf(stderr,"%s\n",gai_strerror(error)); return(-1); } for(res=res0;res;res=res->ai_next) { /* We want only IPv4 or IPv6 */ if ((res->ai_family != PF_INET) && (res->ai_family != PF_INET6)) continue; /* create new socket */ if ((sck = socket(res->ai_family,SOCK_DGRAM,res->ai_protocol)) < 0) { perror("udp_connect: socket"); continue; } /* bind to the local port */ memset(&st,0,sizeof(st)); switch(res->ai_family) { case PF_INET: { struct sockaddr_in *sin = (struct sockaddr_in *)&st; sin->sin_family = PF_INET; sin->sin_port = htons(local_port); break; } case PF_INET6: { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&st; #ifdef SIN6_LEN sin6->sin6_len = res->ai_addrlen; #endif sin6->sin6_family = PF_INET6; sin6->sin6_port = htons(local_port); break; } default: /* shouldn't happen */ close(sck); sck = -1; continue; } /* try to connect to remote host */ if (!bind(sck,(struct sockaddr *)&st,res->ai_addrlen) && !connect(sck,res->ai_addr,res->ai_addrlen)) break; close(sck); sck = -1; } freeaddrinfo(res0); return(sck); } #else /* * Create a new socket to connect to specified host. * Version for old systems that do not support RFC 2553 (getaddrinfo()) * * See http://www.faqs.org/rfcs/rfc2553.html for more info. */ int udp_connect(int local_port,char *remote_host,int remote_port) { struct sockaddr_in sin; struct hostent *hp; int sck; if (!(hp = gethostbyname(remote_host))) { fprintf(stderr,"udp_connect: unable to resolve '%s'\n",remote_host); return(-1); } if ((sck = socket(AF_INET,SOCK_DGRAM,0)) < 0) { perror("udp_connect: socket"); return(-1); } /* bind local port */ memset(&sin,0,sizeof(sin)); sin.sin_family = PF_INET; sin.sin_port = htons(local_port); if (bind(sck,(struct sockaddr *)&sin,sizeof(sin)) < 0) { perror("udp_connect: bind"); close(sck); return(-1); } /* try to connect to remote host */ memset(&sin,0,sizeof(sin)); memcpy(&sin.sin_addr,hp->h_addr_list[0],sizeof(struct in_addr)); sin.sin_family = PF_INET; sin.sin_port = htons(remote_port); if (connect(sck,(struct sockaddr *)&sin,sizeof(sin)) < 0) { perror("udp_connect: connect"); close(sck); } return(sck); } #endif /* HAS_RFC2553 */ #if HAS_RFC2553 /* Listen on the specified port */ int ip_listen(char *ip_addr,int port,int sock_type,int max_fd,int fd_array[]) { struct addrinfo hints,*res,*res0; char port_str[20],*addr; int nsock,error,i; int reuse = 1; for(i=0;iai_next) { if ((res->ai_family != PF_INET) && (res->ai_family != PF_INET6)) continue; fd_array[nsock] = socket(res->ai_family,res->ai_socktype, res->ai_protocol); if (fd_array[nsock] < 0) continue; setsockopt(fd_array[nsock],SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)); if ((bind(fd_array[nsock],res->ai_addr,res->ai_addrlen) < 0) || ((sock_type == SOCK_STREAM) && (listen(fd_array[nsock],5) < 0))) { close(fd_array[nsock]); fd_array[nsock] = -1; continue; } nsock++; } freeaddrinfo(res0); return(nsock); } #else /* Listen on the specified port */ int ip_listen(char *ip_addr,int port,int sock_type,int max_fd,int fd_array[]) { struct sockaddr_in sin; int i,sck,reuse=1; for(i=0;isa_family) { case AF_INET: return(ntohs(((struct sockaddr_in *)addr)->sin_port)); case AF_INET6: return(ntohs(((struct sockaddr_in6 *)addr)->sin6_port)); default: fprintf(stderr,"ip_socket_get_port: unknown address family %d\n", addr->sa_family); return(-1); } } /* Set port in an address info structure */ static int ip_socket_set_port(struct sockaddr *addr,int port) { if (!addr) return(-1); switch(addr->sa_family) { case AF_INET: ((struct sockaddr_in *)addr)->sin_port = htons(port); return(0); case AF_INET6: ((struct sockaddr_in6 *)addr)->sin6_port = htons(port); return(0); default: fprintf(stderr,"ip_socket_set_port: unknown address family %d\n", addr->sa_family); return(-1); } } /* Try to create a socket and bind to the specified address info */ static int ip_socket_bind(struct addrinfo *addr) { int fd,off=0; if ((fd = socket(addr->ai_family,addr->ai_socktype,addr->ai_protocol)) < 0) return(-1); #ifdef IPV6_V6ONLY setsockopt(fd,IPPROTO_IPV6,IPV6_V6ONLY,&off,sizeof(off)); #endif if ( (bind(fd,addr->ai_addr,addr->ai_addrlen) < 0) || ((addr->ai_socktype == SOCK_STREAM) && (listen(fd,5) < 0)) ) { close(fd); return(-1); } return(fd); } /* Listen on a TCP/UDP port - port is choosen in the specified range */ int ip_listen_range(char *ip_addr,int port_start,int port_end,int *port, int sock_type) { struct addrinfo hints,*res,*res0; struct sockaddr_storage st; socklen_t st_len; char port_str[20],*addr; int error,i,fd = -1; memset(&hints,0,sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = sock_type; hints.ai_flags = AI_PASSIVE; snprintf(port_str,sizeof(port_str),"%d",port_start); addr = (ip_addr && strlen(ip_addr)) ? ip_addr : NULL; if ((error = getaddrinfo(addr,port_str,&hints,&res0)) != 0) { fprintf(stderr,"ip_listen_range: %s", gai_strerror(error)); return(-1); } for(i=port_start;i<=port_end;i++) { for(res=res0;res!=NULL;res=res->ai_next) { ip_socket_set_port(res->ai_addr,i); if ((fd = ip_socket_bind(res)) >= 0) { st_len = sizeof(st); getsockname(fd,(struct sockaddr *)&st,&st_len); *port = ip_socket_get_port((struct sockaddr *)&st); goto done; } } } done: freeaddrinfo(res0); return(fd); } /* Connect an existing socket to connect to specified host */ int ip_connect_fd(int fd,char *remote_host,int remote_port) { struct addrinfo hints,*res,*res0; char port_str[20]; int error; memset(&hints,0,sizeof(hints)); hints.ai_family = PF_UNSPEC; snprintf(port_str,sizeof(port_str),"%d",remote_port); if ((error = getaddrinfo(remote_host,port_str,&hints,&res0)) != 0) { fprintf(stderr,"%s\n",gai_strerror(error)); return(-1); } for(res=res0;res;res=res->ai_next) { if ((res->ai_family != PF_INET) && (res->ai_family != PF_INET6)) continue; if (!connect(fd,res->ai_addr,res->ai_addrlen)) break; } freeaddrinfo(res0); return(0); } #else /* Try to create a socket and bind to the specified address info */ static int ip_socket_bind(struct sockaddr_in *sin,int sock_type) { int fd; if ((fd = socket(sin->sin_family,sock_type,0)) < 0) return(-1); if ( (bind(fd,(struct sockaddr *)sin,sizeof(*sin)) < 0) || ((sock_type == SOCK_STREAM) && (listen(fd,5) < 0)) ) { close(fd); return(-1); } return(fd); } /* Listen on a TCP/UDP port - port is choosen in the specified range */ int ip_listen_range(char *ip_addr,int port_start,int port_end,int *port, int sock_type) { struct hostent *hp; struct sockaddr_in sin; socklen_t len; int i,fd; memset(&sin,0,sizeof(sin)); sin.sin_family = PF_INET; if (ip_addr && strlen(ip_addr)) { if (!(hp = gethostbyname(ip_addr))) { fprintf(stderr,"ip_listen_range: unable to resolve '%s'\n",ip_addr); return(-1); } memcpy(&sin.sin_addr,hp->h_addr_list[0],sizeof(struct in_addr)); } for(i=port_start;i<=port_end;i++) { sin.sin_port = htons(i); if ((fd = ip_socket_bind(&sin,sock_type)) >= 0) { len = sizeof(sin); getsockname(fd,(struct sockaddr *)&sin,&len); *port = ntohs(sin.sin_port); return(fd); } } return(-1); } /* Connect an existing socket to connect to specified host */ int ip_connect_fd(int fd,char *remote_host,int remote_port) { struct sockaddr_in sin; struct hostent *hp; if (!(hp = gethostbyname(remote_host))) { fprintf(stderr,"ip_connect_fd: unable to resolve '%s'\n",remote_host); return(-1); } /* try to connect to remote host */ memset(&sin,0,sizeof(sin)); memcpy(&sin.sin_addr,hp->h_addr_list[0],sizeof(struct in_addr)); sin.sin_family = PF_INET; sin.sin_port = htons(remote_port); return(connect(fd,(struct sockaddr *)&sin,sizeof(sin))); } #endif /* Create a socket UDP listening in a port of specified range */ int udp_listen_range(char *ip_addr,int port_start,int port_end,int *port) { return(ip_listen_range(ip_addr,port_start,port_end,port,SOCK_DGRAM)); } #if HAS_RFC2553 #if !defined(IPV6_JOIN_GROUP) && defined(IPV6_ADD_MEMBERSHIP) // cygwin doesn't have IPV6_JOIN_GROUP anymore (is copy of IPV6_ADD_MEMBERSHIP) #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP #endif /* Open a multicast socket */ int udp_mcast_socket(char *mcast_group,int mcast_port, struct sockaddr *sa,int *sa_len) { struct addrinfo hints,*res,*res0; struct sockaddr_storage st; int error, sck = -1, tmp = 1; char port_str[20]; memset(&hints,0,sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; snprintf(port_str,sizeof(port_str),"%d",mcast_port); if ((error = getaddrinfo(mcast_group,port_str,&hints,&res0)) != 0) { fprintf(stderr,"%s\n",gai_strerror(error)); return(-1); } for(res=res0;res;res=res->ai_next) { /* We want only IPv4 or IPv6 */ if ((res->ai_family != PF_INET) && (res->ai_family != PF_INET6)) continue; /* create new socket */ if ((sck = socket(res->ai_family,SOCK_DGRAM,res->ai_protocol)) < 0) { perror("udp_mcast_socket: socket"); continue; } #ifdef SO_REUSEPORT setsockopt(sck,SOL_SOCKET,SO_REUSEPORT,&tmp,sizeof(tmp)); #endif setsockopt(sck,SOL_SOCKET,SO_REUSEADDR,&tmp,sizeof(tmp)); /* bind to the mcast port */ memset(&st,0,sizeof(st)); switch(res->ai_family) { case PF_INET: { struct sockaddr_in *sin = (struct sockaddr_in *)&st; struct ip_mreq mreq; /* prepare the bind() call */ sin->sin_family = PF_INET; sin->sin_port = htons(mcast_port); sin->sin_addr = ((struct sockaddr_in *)res->ai_addr)->sin_addr; /* try to join the multicast group */ memset(&mreq,0,sizeof(mreq)); mreq.imr_multiaddr = sin->sin_addr; if (setsockopt(sck,IPPROTO_IP,IP_ADD_MEMBERSHIP, &mreq,sizeof(mreq)) == -1) goto next_try; break; } case PF_INET6: { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&st; struct ipv6_mreq mreq; /* prepare the bind() call */ #ifdef SIN6_LEN sin6->sin6_len = res->ai_addrlen; #endif sin6->sin6_family = PF_INET6; sin6->sin6_port = htons(mcast_port); sin6->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; /* try to join the multicast group */ memset(&mreq,0,sizeof(mreq)); mreq.ipv6mr_multiaddr = sin6->sin6_addr; if (setsockopt(sck,IPPROTO_IPV6,IPV6_JOIN_GROUP, &mreq,sizeof(mreq)) == -1) goto next_try; break; } default: /* shouldn't happen */ goto next_try; } if (!bind(sck,(struct sockaddr *)&st,res->ai_addrlen)) { /* Prepare info for sendto() */ memcpy(sa,res->ai_addr,res->ai_addrlen); *sa_len = res->ai_addrlen; break; } next_try: close(sck); sck = -1; } freeaddrinfo(res0); return(sck); } #else int udp_mcast_socket(char *mcast_group,int mcast_port, struct sockaddr *sa,int *sa_len) { struct sockaddr_in sin; struct ip_mreq mreq; struct hostent *hp; int sck,tmp = 1; if (!(hp = gethostbyname(mcast_group))) { fprintf(stderr,"udp_mcast_connect: unable to resolve '%s'\n", mcast_group); return(-1); } if ((sck = socket(AF_INET,SOCK_DGRAM,0)) < 0) { perror("udp_mcast_socket: socket"); return(-1); } /* Bind multicast port */ #ifdef SO_REUSEPORT setsockopt(sck,SOL_SOCKET,SO_REUSEPORT,&tmp,sizeof(tmp)); #endif setsockopt(sck,SOL_SOCKET,SO_REUSEADDR,&tmp,sizeof(tmp)); memset(&sin,0,sizeof(sin)); sin.sin_family = PF_INET; sin.sin_port = htons(mcast_port); memcpy(&sin.sin_addr,hp->h_addr_list[0],sizeof(struct in_addr)); if (bind(sck,(struct sockaddr *)&sin,sizeof(sin)) < 0) { perror("udp_mcast_socket: bind"); goto error; } /* Join the IP Multicast group */ memset(&mreq,0,sizeof(mreq)); mreq.imr_multiaddr = sin.sin_addr; if (setsockopt(sck,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) == -1) { perror("udp_mcast_socket: setsockopt"); goto error; } /* Prepare info for sendto() */ memset(&sin,0,sizeof(sin)); sin.sin_family = PF_INET; sin.sin_port = htons(mcast_port); sin.sin_addr = mreq.imr_multiaddr; memcpy(sa,&sin,sizeof(sin)); *sa_len = sizeof(sin); return(sck); error: close(sck); return(-1); } #endif /* Set TTL for a multicast socket */ int udp_mcast_set_ttl(int sck,int ttl) { setsockopt(sck,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl)); #if HAS_RFC2553 setsockopt(sck,IPPROTO_IPV6,IPV6_MULTICAST_HOPS,&ttl,sizeof(ttl)); #endif return(0); } /* * ISL rewrite. * * See: http://www.cisco.com/en/US/tech/tk389/tk390/technologies_tech_note09186a0080094665.shtml */ void cisco_isl_rewrite(m_uint8_t *pkt,m_uint32_t tot_len) { static m_uint8_t isl_xaddr[N_ETH_ALEN] = { 0x01,0x00,0x0c,0x00,0x10,0x00 }; u_int real_offset,real_len; n_eth_hdr_t *hdr; m_uint32_t ifcs; hdr = (n_eth_hdr_t *)pkt; if (!memcmp(&hdr->daddr,isl_xaddr,N_ETH_ALEN)) { real_offset = N_ETH_HLEN + N_ISL_HDR_SIZE; real_len = ntohs(hdr->type); real_len -= (N_ISL_HDR_SIZE + 4); if ((real_offset+real_len) > tot_len) return; /* Rewrite the destination MAC address */ hdr->daddr.eth_addr_byte[4] = 0x00; /* Compute the internal FCS on the encapsulated packet */ ifcs = crc32_compute(0xFFFFFFFF,pkt+real_offset,real_len); pkt[tot_len-4] = ifcs & 0xff; pkt[tot_len-3] = (ifcs >> 8) & 0xff; pkt[tot_len-2] = (ifcs >> 16) & 0xff; pkt[tot_len-1] = ifcs >> 24; } } /* Verify checksum of an IP header */ int ip_verify_cksum(n_ip_hdr_t *hdr) { m_uint8_t *p = (m_uint8_t *)hdr; m_uint32_t sum = 0; u_int len; len = (hdr->ihl & 0x0F) << 1; while(len-- > 0) { sum += ((m_uint16_t)p[0] << 8) | p[1]; p += sizeof(m_uint16_t); } while(sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); return(sum == 0xFFFF); } /* Compute an IP checksum */ void ip_compute_cksum(n_ip_hdr_t *hdr) { m_uint8_t *p = (m_uint8_t *)hdr; m_uint32_t sum = 0; u_int len; hdr->cksum = 0; len = (hdr->ihl & 0x0F) << 1; while(len-- > 0) { sum += ((m_uint16_t)p[0] << 8) | p[1]; p += sizeof(m_uint16_t); } while(sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); hdr->cksum = htons(~sum); } /* Partial checksum (for UDP/TCP) */ static inline m_uint32_t ip_cksum_partial(m_uint8_t *buf,int len) { m_uint32_t sum = 0; while(len > 1) { sum += ((m_uint16_t)buf[0] << 8) | buf[1]; buf += sizeof(m_uint16_t); len -= sizeof(m_uint16_t); } if (len == 1) sum += (m_uint16_t)(*buf) << 8; return(sum); } /* Partial checksum test */ int ip_cksum_partial_test(void) { #define N_BUF 4 m_uint8_t buffer[N_BUF][512]; m_uint16_t psum[N_BUF]; m_uint32_t tmp,sum,gsum; int i; for(i=0;i> 16) sum = (sum & 0xFFFF) + (sum >> 16); psum[i] = ~sum; } /* partial sums + accumulator */ for(i=0,tmp=0;i> 16) sum = (sum & 0xFFFF) + (sum >> 16); gsum = sum; /* accumulator */ while(tmp >> 16) tmp = (tmp & 0xFFFF) + (tmp >> 16); //printf("gsum = 0x%4.4x, tmp = 0x%4.4x : %s\n", // gsum,tmp,(gsum == tmp) ? "OK" : "FAILURE"); return(tmp == gsum); #undef N_BUF } /* Compute TCP/UDP checksum */ m_uint16_t pkt_ctx_tcp_cksum(n_pkt_ctx_t *ctx,int ph) { m_uint32_t sum; m_uint16_t old_cksum = 0; u_int len; /* replace the actual checksum value with 0 to recompute it */ if (!(ctx->flags & N_PKT_CTX_FLAG_IP_FRAG)) { switch(ctx->ip_l4_proto) { case N_IP_PROTO_TCP: old_cksum = ctx->tcp->cksum; ctx->tcp->cksum = 0; break; case N_IP_PROTO_UDP: old_cksum = ctx->udp->cksum; ctx->udp->cksum = 0; break; } } len = ntohs(ctx->ip->tot_len) - ((ctx->ip->ihl & 0x0F) << 2); sum = ip_cksum_partial(ctx->l4,len); /* include pseudo-header */ if (ph) { sum += ip_cksum_partial((m_uint8_t *)&ctx->ip->saddr,8); sum += ctx->ip_l4_proto + len; } while(sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); /* restore the old value */ if (!(ctx->flags & N_PKT_CTX_FLAG_IP_FRAG)) { switch(ctx->ip_l4_proto) { case N_IP_PROTO_TCP: ctx->tcp->cksum = old_cksum; break; case N_IP_PROTO_UDP: ctx->udp->cksum = old_cksum; break; } } return(~sum); } /* Analyze L4 for an IP packet */ int pkt_ctx_ip_analyze_l4(n_pkt_ctx_t *ctx) { switch(ctx->ip_l4_proto) { case N_IP_PROTO_TCP: ctx->flags |= N_PKT_CTX_FLAG_L4_TCP; break; case N_IP_PROTO_UDP: ctx->flags |= N_PKT_CTX_FLAG_L4_UDP; break; case N_IP_PROTO_ICMP: ctx->flags |= N_PKT_CTX_FLAG_L4_ICMP; break; } return(TRUE); } /* Analyze a packet */ int pkt_ctx_analyze(n_pkt_ctx_t *ctx,m_uint8_t *pkt,size_t pkt_len) { n_eth_dot1q_hdr_t *eth = (n_eth_dot1q_hdr_t *)pkt; m_uint16_t eth_type; m_uint8_t *p; ctx->pkt = pkt; ctx->pkt_len = pkt_len; ctx->flags = 0; ctx->vlan_id = 0; ctx->l3 = NULL; ctx->l4 = NULL; eth_type = ntohs(eth->type); p = PTR_ADJUST(m_uint8_t *,eth,N_ETH_HLEN); if (eth_type >= N_ETH_MTU) { if (eth_type == N_ETH_PROTO_DOT1Q) { ctx->flags |= N_PKT_CTX_FLAG_VLAN; ctx->vlan_id = htons(eth->vlan_id); /* override the ethernet type */ eth_type = ntohs(*(m_uint16_t *)(p+2)); /* skip 802.1Q header info */ p += sizeof(m_uint32_t); } } if (eth_type < N_ETH_MTU) { /* LLC/SNAP: TODO */ return(TRUE); } else { ctx->flags |= N_PKT_CTX_FLAG_ETHV2; } switch(eth_type) { case N_ETH_PROTO_IP: { n_ip_hdr_t *ip; u_int len,offset; ctx->flags |= N_PKT_CTX_FLAG_L3_IP; ctx->ip = ip = (n_ip_hdr_t *)p; /* Check header */ if (((ip->ihl & 0xF0) != 0x40) || ((len = ip->ihl & 0x0F) < N_IP_MIN_HLEN) || ((len << 2) > ntohs(ip->tot_len)) || !ip_verify_cksum(ctx->ip)) return(TRUE); ctx->flags |= N_PKT_CTX_FLAG_IPH_OK; ctx->ip_l4_proto = ip->proto; ctx->l4 = PTR_ADJUST(void *,ip,len << 2); /* Check if the packet is a fragment */ offset = ntohs(ip->frag_off); if (((offset & N_IP_OFFMASK) != 0) || (offset & N_IP_FLAG_MF)) ctx->flags |= N_PKT_CTX_FLAG_IP_FRAG; break; } case N_ETH_PROTO_ARP: ctx->flags |= N_PKT_CTX_FLAG_L3_ARP; ctx->arp = (n_arp_hdr_t *)p; return(TRUE); default: /* other: unknown, stop now */ return(TRUE); } return(TRUE); } /* Dump packet context */ void pkt_ctx_dump(n_pkt_ctx_t *ctx) { printf("pkt=%p (len=%lu), flags=0x%8.8x, vlan_id=0x%4.4x, l3=%p, l4=%p\n", ctx->pkt,(u_long)ctx->pkt_len,ctx->flags,ctx->vlan_id, ctx->l3,ctx->l4); } dynamips-0.2.14/common/net.h000066400000000000000000000255431241034141600156560ustar00rootroot00000000000000/* * Copyright (c) 2006 Christophe Fillot. * E-mail: cf@utc.fr * * net.h: Protocol Headers and Constants Definitions. */ #ifndef __NET_H__ #define __NET_H__ 1 #include "dynamips_common.h" #include #include /* TODO missing in MinGW */ #include #include #define N_IP_ADDR_LEN 4 #define N_IP_ADDR_BITS 32 #define N_IPV6_ADDR_LEN 16 #define N_IPV6_ADDR_BITS 128 /* IPv4 Address definition */ typedef m_uint32_t n_ip_addr_t; /* IP Network definition */ typedef struct { n_ip_addr_t net_addr; n_ip_addr_t net_mask; }n_ip_network_t; /* IPv6 Address definition */ typedef struct { union { m_uint32_t u6_addr32[4]; m_uint16_t u6_addr16[8]; m_uint8_t u6_addr8[16]; }ip6; }n_ipv6_addr_t; /* IPv6 Network definition */ typedef struct { n_ipv6_addr_t net_addr; u_int net_mask; }n_ipv6_network_t; /* IP header minimum length */ #define N_IP_MIN_HLEN 5 /* IP: Common Protocols */ #define N_IP_PROTO_ICMP 1 #define N_IP_PROTO_IGMP 2 #define N_IP_PROTO_TCP 6 #define N_IP_PROTO_UDP 17 #define N_IP_PROTO_IPV6 41 #define N_IP_PROTO_GRE 47 #define N_IP_PROTO_ESP 50 #define N_IP_PROTO_AH 51 #define N_IP_PROTO_ICMPV6 58 #define N_IP_PROTO_EIGRP 88 #define N_IP_PROTO_OSPF 89 #define N_IP_PROTO_PIM 103 #define N_IP_PROTO_SCTP 132 #define N_IP_PROTO_MAX 256 #define N_IP_FLAG_DF 0x4000 #define N_IP_FLAG_MF 0x2000 #define N_IP_OFFMASK 0x1fff /* Maximum number of ports */ #define N_IP_PORT_MAX 65536 /* TCP: Header Flags */ #define N_TCP_FIN 0x01 #define N_TCP_SYN 0x02 #define N_TCP_RST 0x04 #define N_TCP_PUSH 0x08 #define N_TCP_ACK 0x10 #define N_TCP_URG 0x20 #define N_TCP_FLAGMASK 0x3F /* IPv6 Header Codes */ #define N_IPV6_PROTO_ICMP 58 #define N_IPV6_OPT_HOP_BY_HOP 0 /* Hop-by-Hop header */ #define N_IPV6_OPT_DST 60 /* Destination Options Header */ #define N_IPV6_OPT_ROUTE 43 /* Routing header */ #define N_IPV6_OPT_FRAG 44 /* Fragment Header */ #define N_IPV6_OPT_AH 51 /* Authentication Header */ #define N_IPV6_OPT_ESP 50 /* Encryption Security Payload */ #define N_IPV6_OPT_COMP 108 /* Payload Compression Protocol */ #define N_IPV6_OPT_END 59 /* No more headers */ /* Standard Ethernet MTU */ #define N_ETH_MTU 1500 /* Ethernet Constants */ #define N_ETH_ALEN 6 #define N_ETH_HLEN sizeof(n_eth_hdr_t) /* CRC Length */ #define N_ETH_CRC_LEN 4 /* Minimum size for ethernet payload */ #define N_ETH_MIN_DATA_LEN 46 #define N_ETH_MIN_FRAME_LEN (N_ETH_MIN_DATA_LEN + N_ETH_HLEN) #define N_ETH_PROTO_IP 0x0800 #define N_ETH_PROTO_IPV6 0x86DD #define N_ETH_PROTO_ARP 0x0806 #define N_ETH_PROTO_DOT1Q 0x8100 #define N_ETH_PROTO_DOT1Q_2 0x9100 #define N_ETH_PROTO_DOT1Q_3 0x9200 #define N_ETH_PROTO_MPLS 0x8847 #define N_ETH_PROTO_MPLS_MC 0x8848 #define N_ETH_PROTO_LOOP 0x9000 /* size needed for a string buffer */ #define N_ETH_SLEN (N_ETH_ALEN*3) /* ARP opcodes */ #define N_ARP_REQUEST 0x1 #define N_ARP_REPLY 0x2 /* Ethernet Address */ typedef struct { m_uint8_t eth_addr_byte[N_ETH_ALEN]; } __attribute__ ((__packed__)) n_eth_addr_t; /* Ethernet Header */ typedef struct { n_eth_addr_t daddr; /* destination eth addr */ n_eth_addr_t saddr; /* source ether addr */ m_uint16_t type; /* packet type ID field */ } __attribute__ ((__packed__)) n_eth_hdr_t; /* 802.1Q Ethernet Header */ typedef struct { n_eth_addr_t daddr; /* destination eth addr */ n_eth_addr_t saddr; /* source ether addr */ m_uint16_t type; /* packet type ID field (0x8100) */ m_uint16_t vlan_id; /* VLAN id + CoS */ } __attribute__ ((__packed__)) n_eth_dot1q_hdr_t; /* LLC header */ typedef struct { m_uint8_t dsap; m_uint8_t ssap; m_uint8_t ctrl; } __attribute__ ((__packed__)) n_eth_llc_hdr_t; /* SNAP header */ typedef struct { m_uint8_t oui[3]; m_uint16_t type; } __attribute__ ((__packed__)) n_eth_snap_hdr_t; /* Cisco ISL header */ typedef struct { m_uint16_t hsa1; /* High bits of source MAC address */ m_uint8_t hsa2; /* (in theory: 0x00-00-0c) */ m_uint16_t vlan; /* VLAN + BPDU */ m_uint16_t index; /* Index port of source */ m_uint16_t res; /* Reserved for TokenRing and FDDI */ } __attribute__ ((__packed__)) n_eth_isl_hdr_t; #define N_ISL_HDR_SIZE (sizeof(n_eth_llc_hdr_t) + sizeof(n_eth_isl_hdr_t)) /* Cisco SCP/RBCP header */ typedef struct { m_uint8_t sa; /* Source Address */ m_uint8_t da; /* Destination Address */ m_uint16_t len; /* Data Length */ m_uint8_t dsap; /* Destination Service Access Point */ m_uint8_t ssap; /* Source Service Access Point */ m_uint16_t opcode; /* Opcode */ m_uint16_t seqno; /* Sequence Number */ m_uint8_t flags; /* Flags: command/response */ m_uint8_t unk1; /* Unknown */ m_uint16_t unk2; /* Unknown */ m_uint16_t unk3; /* Unknown */ } __attribute__ ((__packed__)) n_scp_hdr_t; /* ----- ARP Header for the IPv4 protocol over Ethernet ------------------ */ typedef struct { m_uint16_t hw_type; /* Hardware type */ m_uint16_t proto_type; /* L3 protocol */ m_uint8_t hw_len; /* Length of hardware address */ m_uint8_t proto_len; /* Length of L3 address */ m_uint16_t opcode; /* ARP Opcode */ n_eth_addr_t eth_saddr; /* Source hardware address */ m_uint32_t ip_saddr; /* Source IP address */ n_eth_addr_t eth_daddr; /* Dest. hardware address */ m_uint32_t ip_daddr; /* Dest. IP address */ } __attribute__ ((__packed__)) n_arp_hdr_t; /* ----- IP Header ------------------------------------------------------- */ typedef struct { m_uint8_t ihl; m_uint8_t tos; m_uint16_t tot_len; m_uint16_t id; m_uint16_t frag_off; m_uint8_t ttl; m_uint8_t proto; m_uint16_t cksum; m_uint32_t saddr; m_uint32_t daddr; }n_ip_hdr_t; /* ----- UDP Header ------------------------------------------------------ */ typedef struct { m_uint16_t sport; m_uint16_t dport; m_uint16_t len; m_uint16_t cksum; }n_udp_hdr_t; /* ----- TCP Header ------------------------------------------------------ */ typedef struct { m_uint16_t sport; m_uint16_t dport; m_uint32_t seq; m_uint32_t ack_seq; m_uint8_t offset; m_uint8_t flags; m_uint16_t window; m_uint16_t cksum; m_uint16_t urg_ptr; }n_tcp_hdr_t; /* ----- Packet Context -------------------------------------------------- */ #define N_PKT_CTX_FLAG_ETHV2 0x0001 #define N_PKT_CTX_FLAG_VLAN 0x0002 #define N_PKT_CTX_FLAG_L3_ARP 0x0008 #define N_PKT_CTX_FLAG_L3_IP 0x0010 #define N_PKT_CTX_FLAG_L4_UDP 0x0020 #define N_PKT_CTX_FLAG_L4_TCP 0x0040 #define N_PKT_CTX_FLAG_L4_ICMP 0x0080 #define N_PKT_CTX_FLAG_IPH_OK 0x0100 #define N_PKT_CTX_FLAG_IP_FRAG 0x0200 typedef struct { /* full packet */ m_uint8_t *pkt; size_t pkt_len; /* Packet flags */ m_uint32_t flags; /* VLAN information */ m_uint16_t vlan_id; /* L4 protocol for IP */ u_int ip_l4_proto; /* L3 header */ union { n_arp_hdr_t *arp; n_ip_hdr_t *ip; void *l3; }; /* L4 header */ union { n_udp_hdr_t *udp; n_tcp_hdr_t *tcp; void *l4; }; }n_pkt_ctx_t; /* ----------------------------------------------------------------------- */ /* Check for a broadcast ethernet address */ static inline int eth_addr_is_bcast(n_eth_addr_t *addr) { static const char *bcast_addr = "\xff\xff\xff\xff\xff\xff"; return(!memcmp(addr,bcast_addr,6)); } /* Check for a broadcast/multicast ethernet address */ static inline int eth_addr_is_mcast(n_eth_addr_t *addr) { return(addr->eth_addr_byte[0] & 1); } /* Check for Cisco ISL destination address */ static inline int eth_addr_is_cisco_isl(n_eth_addr_t *addr) { static const char *isl_addr = "\x01\x00\x0c\x00\x00"; return(!memcmp(addr,isl_addr,5)); /* only 40 bits to compare */ } /* Check for a SNAP header */ static inline int eth_llc_check_snap(n_eth_llc_hdr_t *llc_hdr) { return((llc_hdr->dsap == 0xAA) && (llc_hdr->ssap == 0xAA) && (llc_hdr->ctrl == 0x03)); } /* Number of bits in a contiguous netmask */ static inline int ip_bits_mask(n_ip_addr_t mask) { int prefix = 0; while(mask) { prefix++; mask = mask & (mask - 1); } return(prefix); } /* Initialize IPv6 masks */ void ipv6_init_masks(void); /* Convert an IPv4 address into a string */ char *n_ip_ntoa(char *buffer,n_ip_addr_t ip_addr); /* Convert in IPv6 address into a string */ char *n_ipv6_ntoa(char *buffer,n_ipv6_addr_t *ipv6_addr); /* Convert a string containing an IP address in binary */ int n_ip_aton(n_ip_addr_t *ip_addr,char *ip_str); /* Convert an IPv6 address from string into binary */ int n_ipv6_aton(n_ipv6_addr_t *ipv6_addr,char *ip_str); /* Parse an IPv4 CIDR prefix */ int ip_parse_cidr(char *token,n_ip_addr_t *net_addr,n_ip_addr_t *net_mask); /* Parse an IPv6 CIDR prefix */ int ipv6_parse_cidr(char *token,n_ipv6_addr_t *net_addr,u_int *net_mask); /* Parse a MAC address */ int parse_mac_addr(n_eth_addr_t *addr,char *str); /* Parse a board id */ int parse_board_id(m_uint8_t * buf,const char *id,int encode); /* Convert an Ethernet address into a string */ char *n_eth_ntoa(char *buffer,n_eth_addr_t *addr,int format); /* Create a new socket to connect to specified host */ int udp_connect(int local_port,char *remote_host,int remote_port); /* Listen on the specified port */ int ip_listen(char *ip_addr,int port,int sock_type,int max_fd,int fd_array[]); /* Listen on a TCP/UDP port - port is choosen in the specified rnaage */ int ip_listen_range(char *ip_addr,int port_start,int port_end,int *port, int sock_type); /* Create a socket UDP listening in a port of specified range */ int udp_listen_range(char *ip_addr,int port_start,int port_end,int *port); /* Connect an existing socket to connect to specified host */ int ip_connect_fd(int fd,char *remote_host,int remote_port); /* Open a multicast socket */ int udp_mcast_socket(char *mcast_group,int mcast_port, struct sockaddr *sa,int *sa_len); /* Set TTL for a multicast socket */ int udp_mcast_set_ttl(int sck,int ttl); /* ISL rewrite */ void cisco_isl_rewrite(m_uint8_t *pkt,m_uint32_t tot_len); /* Verify checksum of an IP header */ int ip_verify_cksum(n_ip_hdr_t *hdr); /* Compute an IP checksum */ void ip_compute_cksum(n_ip_hdr_t *hdr); /* Compute TCP/UDP checksum */ m_uint16_t pkt_ctx_tcp_cksum(n_pkt_ctx_t *ctx,int ph); /* Analyze L4 for an IP packet */ int pkt_ctx_ip_analyze_l4(n_pkt_ctx_t *ctx); /* Analyze a packet */ int pkt_ctx_analyze(n_pkt_ctx_t *ctx,m_uint8_t *pkt,size_t pkt_len); /* Dump packet context */ void pkt_ctx_dump(n_pkt_ctx_t *ctx); #endif dynamips-0.2.14/common/net_io.c000066400000000000000000001323401241034141600163320ustar00rootroot00000000000000/* * Cisco router) simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Network Input/Output Abstraction Layer. */ #include "dynamips_common.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #include #endif #include "registry.h" #include "gen_uuid.h" #include "net.h" #include "net_io.h" #include "net_io_filter.h" #include "ptask.h" /* Free a NetIO descriptor */ static int netio_free(void *data,void *arg); /* NIO RX listener */ static pthread_mutex_t netio_rxl_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t netio_rxq_mutex = PTHREAD_MUTEX_INITIALIZER; static struct netio_rx_listener *netio_rxl_list = NULL; static struct netio_rx_listener *netio_rxl_add_list = NULL; static netio_desc_t *netio_rxl_remove_list = NULL; static pthread_t netio_rxl_thread; static pthread_cond_t netio_rxl_cond; #define NETIO_RXL_LOCK() pthread_mutex_lock(&netio_rxl_mutex); #define NETIO_RXL_UNLOCK() pthread_mutex_unlock(&netio_rxl_mutex); #define NETIO_RXQ_LOCK() pthread_mutex_lock(&netio_rxq_mutex); #define NETIO_RXQ_UNLOCK() pthread_mutex_unlock(&netio_rxq_mutex); /* NetIO type */ typedef struct { char *name; char *desc; }netio_type_t; /* NETIO types (must follow the enum definition) */ static netio_type_t netio_types[NETIO_TYPE_MAX] = { { "unix" , "UNIX local sockets" }, { "vde" , "Virtual Distributed Ethernet / UML switch" }, { "tap" , "Linux/FreeBSD TAP device" }, { "udp" , "UDP sockets" }, { "udp_auto" , "Auto UDP sockets" }, { "tcp_cli" , "TCP client" }, { "tcp_ser" , "TCP server" }, { "mcast" , "Multicast bus" }, #ifdef LINUX_ETH { "linux_eth" , "Linux Ethernet device" }, #endif #ifdef GEN_ETH { "gen_eth" , "Generic Ethernet device (PCAP)" }, #endif { "fifo" , "FIFO (intra-hypervisor)" }, { "null" , "Null device" }, }; /* Get NETIO type given a description */ int netio_get_type(char *type) { int i; for(i=0;iname,OBJ_TYPE_NIO,nio)); } /* Create a new NetIO descriptor */ static netio_desc_t *netio_create(char *name) { netio_desc_t *nio; if (!(nio = malloc(sizeof(*nio)))) return NULL; /* setup as a NULL descriptor */ memset(nio,0,sizeof(*nio)); nio->type = NETIO_TYPE_NULL; /* save name for registry */ if (!(nio->name = strdup(name))) { free(nio); return NULL; } return nio; } /* Delete a NetIO descriptor */ int netio_delete(char *name) { return(registry_delete_if_unused(name,OBJ_TYPE_NIO,netio_free,NULL)); } /* Delete all NetIO descriptors */ int netio_delete_all(void) { return(registry_delete_type(OBJ_TYPE_NIO,netio_free,NULL)); } /* Save the configuration of a NetIO descriptor */ void netio_save_config(netio_desc_t *nio,FILE *fd) { if (nio->save_cfg) nio->save_cfg(nio,fd); } /* Save configurations of all NetIO descriptors */ static void netio_reg_save_config(registry_entry_t *entry,void *opt,int *err) { netio_save_config((netio_desc_t *)entry->data,(FILE *)opt); } void netio_save_config_all(FILE *fd) { registry_foreach_type(OBJ_TYPE_NIO,netio_reg_save_config,fd,NULL); fprintf(fd,"\n"); } /* Send a packet through a NetIO descriptor */ ssize_t netio_send(netio_desc_t *nio,void *pkt,size_t len) { int res; if (!nio) return(-1); if (nio->debug) { printf("NIO %s: sending a packet of %lu bytes:\n",nio->name,(u_long)len); mem_dump(stdout,pkt,len); } /* Apply the TX filter */ if (nio->tx_filter != NULL) { res = nio->tx_filter->pkt_handler(nio,pkt,len,nio->tx_filter_data); if (res <= 0) return(-1); } /* Apply the bidirectional filter */ if (nio->both_filter != NULL) { res = nio->both_filter->pkt_handler(nio,pkt,len,nio->both_filter_data); if (res == NETIO_FILTER_ACTION_DROP) return(-1); } /* Update output statistics */ nio->stats_pkts_out++; nio->stats_bytes_out += len; netio_update_bw_stat(nio,len); return(nio->send(nio->dptr,pkt,len)); } /* Receive a packet through a NetIO descriptor */ ssize_t netio_recv(netio_desc_t *nio,void *pkt,size_t max_len) { ssize_t len; int res; if (!nio) return(-1); /* Receive the packet */ if ((len = nio->recv(nio->dptr,pkt,max_len)) <= 0) return(-1); if (nio->debug) { printf("NIO %s: receiving a packet of %ld bytes:\n",nio->name,(long)len); mem_dump(stdout,pkt,len); } /* Apply the RX filter */ if (nio->rx_filter != NULL) { res = nio->rx_filter->pkt_handler(nio,pkt,len,nio->rx_filter_data); if (res == NETIO_FILTER_ACTION_DROP) return(-1); } /* Apply the bidirectional filter */ if (nio->both_filter != NULL) { res = nio->both_filter->pkt_handler(nio,pkt,len,nio->both_filter_data); if (res == NETIO_FILTER_ACTION_DROP) return(-1); } /* Update input statistics */ nio->stats_pkts_in++; nio->stats_bytes_in += len; return(len); } /* Get a NetIO FD */ int netio_get_fd(netio_desc_t *nio) { int fd = -1; switch(nio->type) { case NETIO_TYPE_UNIX: fd = nio->u.nud.fd; break; case NETIO_TYPE_VDE: fd = nio->u.nvd.data_fd; break; case NETIO_TYPE_TAP: fd = nio->u.ntd.fd; break; case NETIO_TYPE_TCP_CLI: case NETIO_TYPE_TCP_SER: case NETIO_TYPE_UDP: case NETIO_TYPE_UDP_AUTO: fd = nio->u.nid.fd; break; case NETIO_TYPE_MCAST: fd = nio->u.nmd.fd; break; #ifdef LINUX_ETH case NETIO_TYPE_LINUX_ETH: fd = nio->u.nled.fd; break; #endif } return(fd); } /* * ========================================================================= * UNIX sockets * ========================================================================= */ /* Create an UNIX socket */ static int netio_unix_create_socket(netio_unix_desc_t *nud) { struct sockaddr_un local_sock; if ((nud->fd = socket(AF_UNIX,SOCK_DGRAM,0)) == -1) { perror("netio_unix: socket"); return(-1); } memset(&local_sock,0,sizeof(local_sock)); local_sock.sun_family = AF_UNIX; strcpy(local_sock.sun_path,nud->local_filename); if (bind(nud->fd,(struct sockaddr *)&local_sock,sizeof(local_sock)) == -1) { perror("netio_unix: bind"); return(-1); } return(nud->fd); } /* Free a NetIO unix descriptor */ static void netio_unix_free(netio_unix_desc_t *nud) { if (nud->fd != -1) close(nud->fd); if (nud->local_filename) { unlink(nud->local_filename); free(nud->local_filename); } } /* Allocate a new NetIO UNIX descriptor */ static int netio_unix_create(netio_unix_desc_t *nud,char *local,char *remote) { memset(nud,0,sizeof(*nud)); nud->fd = -1; /* check lengths */ if ((strlen(local) >= sizeof(nud->remote_sock.sun_path)) || (strlen(remote) >= sizeof(nud->remote_sock.sun_path))) goto nomem_error; if (!(nud->local_filename = strdup(local))) goto nomem_error; if (netio_unix_create_socket(nud) == -1) return(-1); /* prepare the remote info */ nud->remote_sock.sun_family = AF_UNIX; strcpy(nud->remote_sock.sun_path,remote); return(0); nomem_error: fprintf(stderr,"netio_unix_create: " "invalid file size or insufficient memory\n"); return(-1); } /* Send a packet to an UNIX socket */ static ssize_t netio_unix_send(netio_unix_desc_t *nud,void *pkt,size_t pkt_len) { return(sendto(nud->fd,pkt,pkt_len,0, (struct sockaddr *)&nud->remote_sock, sizeof(nud->remote_sock))); } /* Receive a packet from an UNIX socket */ static ssize_t netio_unix_recv(netio_unix_desc_t *nud,void *pkt,size_t max_len) { return(recvfrom(nud->fd,pkt,max_len,0,NULL,NULL)); } /* Save the NIO configuration */ static void netio_unix_save_cfg(netio_desc_t *nio,FILE *fd) { netio_unix_desc_t *nud = nio->dptr; fprintf(fd,"nio create_unix %s %s %s\n", nio->name,nud->local_filename,nud->remote_sock.sun_path); } /* Create a new NetIO descriptor with UNIX method */ netio_desc_t *netio_desc_create_unix(char *nio_name,char *local,char *remote) { netio_desc_t *nio; if (!(nio = netio_create(nio_name))) return NULL; if (netio_unix_create(&nio->u.nud,local,remote) == -1) { netio_free(nio,NULL); return NULL; } nio->type = NETIO_TYPE_UNIX; nio->send = (void *)netio_unix_send; nio->recv = (void *)netio_unix_recv; nio->free = (void *)netio_unix_free; nio->save_cfg = netio_unix_save_cfg; nio->dptr = &nio->u.nud; if (netio_record(nio) == -1) { netio_free(nio,NULL); return NULL; } return nio; } /* * ========================================================================= * VDE (Virtual Distributed Ethernet) interface * ========================================================================= */ /* Free a NetIO VDE descriptor */ static void netio_vde_free(netio_vde_desc_t *nvd) { if (nvd->data_fd != -1) close(nvd->data_fd); if (nvd->ctrl_fd != -1) close(nvd->ctrl_fd); if (nvd->local_filename) { unlink(nvd->local_filename); free(nvd->local_filename); } } /* Create a new NetIO VDE descriptor */ static int netio_vde_create(netio_vde_desc_t *nvd,char *control,char *local) { struct sockaddr_un ctrl_sock,tst; struct vde_request_v3 req; ssize_t len; int res; memset(nvd,0,sizeof(*nvd)); nvd->ctrl_fd = nvd->data_fd = -1; if ((strlen(control) >= sizeof(ctrl_sock.sun_path)) || (strlen(local) >= sizeof(nvd->remote_sock.sun_path))) { fprintf(stderr,"netio_vde_create: bad filenames specified\n"); return(-1); } /* Copy the local filename */ if (!(nvd->local_filename = strdup(local))) { fprintf(stderr,"netio_vde_create: insufficient memory\n"); return(-1); } /* Connect to the VDE switch controller */ nvd->ctrl_fd = socket(AF_UNIX,SOCK_STREAM,0); if (nvd->ctrl_fd < 0) { perror("netio_vde_create: socket(control)"); return(-1); } memset(&ctrl_sock,0,sizeof(ctrl_sock)); ctrl_sock.sun_family = AF_UNIX; strcpy(ctrl_sock.sun_path,control); res = connect(nvd->ctrl_fd,(struct sockaddr *)&ctrl_sock, sizeof(ctrl_sock)); if (res < 0) { perror("netio_vde_create: connect(control)"); return(-1); } tst.sun_family = AF_UNIX; strcpy(tst.sun_path,local); /* Create the data connection */ nvd->data_fd = socket(AF_UNIX,SOCK_DGRAM,0); if (nvd->data_fd < 0) { perror("netio_vde_create: socket(data)"); return(-1); } if (bind(nvd->data_fd,(struct sockaddr *)&tst,sizeof(tst))<0) { perror("netio_vde_create: bind(data)"); return(-1); } /* Now, process to registration */ memset(&req,0,sizeof(req)); req.sock.sun_family = AF_UNIX; strcpy(req.sock.sun_path,local); req.magic = VDE_SWITCH_MAGIC; req.version = VDE_SWITCH_VERSION; req.type = VDE_REQ_NEW_CONTROL; len = write(nvd->ctrl_fd,&req,sizeof(req)); if (len != sizeof(req)) { perror("netio_vde_create: write(req)"); return(-1); } /* Read the remote socket descriptor */ len = read(nvd->ctrl_fd,&nvd->remote_sock,sizeof(nvd->remote_sock)); if (len != sizeof(nvd->remote_sock)) { perror("netio_vde_create: read(req)"); return(-1); } return(0); } /* Send a packet to a VDE data socket */ static ssize_t netio_vde_send(netio_vde_desc_t *nvd,void *pkt,size_t pkt_len) { return(sendto(nvd->data_fd,pkt,pkt_len,0, (struct sockaddr *)&nvd->remote_sock, sizeof(nvd->remote_sock))); } /* Receive a packet from a VDE socket */ static ssize_t netio_vde_recv(netio_vde_desc_t *nvd,void *pkt,size_t max_len) { return(recvfrom(nvd->data_fd,pkt,max_len,0,NULL,NULL)); } /* Save the NIO configuration */ static void netio_vde_save_cfg(netio_desc_t *nio,FILE *fd) { netio_vde_desc_t *nvd = nio->dptr; fprintf(fd,"nio create_vde %s %s %s\n", nio->name,nvd->remote_sock.sun_path,nvd->local_filename); } /* Create a new NetIO descriptor with VDE method */ netio_desc_t *netio_desc_create_vde(char *nio_name,char *control,char *local) { netio_vde_desc_t *nvd; netio_desc_t *nio; if (!(nio = netio_create(nio_name))) return NULL; nvd = &nio->u.nvd; if (netio_vde_create(nvd,control,local) == -1) { netio_free(nio,NULL); return NULL; } nio->type = NETIO_TYPE_VDE; nio->send = (void *)netio_vde_send; nio->recv = (void *)netio_vde_recv; nio->free = (void *)netio_vde_free; nio->save_cfg = netio_vde_save_cfg; nio->dptr = &nio->u.nvd; if (netio_record(nio) == -1) { netio_free(nio,NULL); return NULL; } return nio; } /* * ========================================================================= * TAP devices * ========================================================================= */ /* Free a NetIO TAP descriptor */ static void netio_tap_free(netio_tap_desc_t *ntd) { if (ntd->fd != -1) close(ntd->fd); } /* Open a TAP device */ static int netio_tap_open(char *tap_devname) { #ifdef __linux__ struct ifreq ifr; int fd,err; if ((fd = open("/dev/net/tun",O_RDWR)) < 0) return(-1); memset(&ifr,0,sizeof(ifr)); /* Flags: IFF_TUN - TUN device (no Ethernet headers) * IFF_TAP - TAP device * * IFF_NO_PI - Do not provide packet information */ ifr.ifr_flags = IFF_TAP|IFF_NO_PI; if (*tap_devname) strncpy(ifr.ifr_name,tap_devname,IFNAMSIZ); if ((err = ioctl(fd,TUNSETIFF,(void *)&ifr)) < 0) { close(fd); return err; } strcpy(tap_devname,ifr.ifr_name); return(fd); #else int i,fd = -1; if (*tap_devname) { fd = open(tap_devname,O_RDWR); } else { for(i=0;i<16;i++) { snprintf(tap_devname,NETIO_DEV_MAXLEN,"/dev/tap%d",i); if ((fd = open(tap_devname,O_RDWR)) >= 0) break; } } return(fd); #endif } /* Allocate a new NetIO TAP descriptor */ static int netio_tap_create(netio_tap_desc_t *ntd,char *tap_name) { if (strlen(tap_name) >= NETIO_DEV_MAXLEN) { fprintf(stderr,"netio_tap_create: bad TAP device string specified.\n"); return(-1); } memset(ntd,0,sizeof(*ntd)); strcpy(ntd->filename,tap_name); ntd->fd = netio_tap_open(ntd->filename); if (ntd->fd == -1) { fprintf(stderr,"netio_tap_create: unable to open TAP device %s (%s)\n", tap_name,strerror(errno)); return(-1); } return(0); } /* Send a packet to a TAP device */ static ssize_t netio_tap_send(netio_tap_desc_t *ntd,void *pkt,size_t pkt_len) { return(write(ntd->fd,pkt,pkt_len)); } /* Receive a packet through a TAP device */ static ssize_t netio_tap_recv(netio_tap_desc_t *ntd,void *pkt,size_t max_len) { return(read(ntd->fd,pkt,max_len)); } /* Save the NIO configuration */ static void netio_tap_save_cfg(netio_desc_t *nio,FILE *fd) { netio_tap_desc_t *ntd = nio->dptr; fprintf(fd,"nio create_tap %s %s\n",nio->name,ntd->filename); } /* Create a new NetIO descriptor with TAP method */ netio_desc_t *netio_desc_create_tap(char *nio_name,char *tap_name) { netio_tap_desc_t *ntd; netio_desc_t *nio; if (!(nio = netio_create(nio_name))) return NULL; ntd = &nio->u.ntd; if (netio_tap_create(ntd,tap_name) == -1) { netio_free(nio,NULL); return NULL; } nio->type = NETIO_TYPE_TAP; nio->send = (void *)netio_tap_send; nio->recv = (void *)netio_tap_recv; nio->free = (void *)netio_tap_free; nio->save_cfg = netio_tap_save_cfg; nio->dptr = &nio->u.ntd; if (netio_record(nio) == -1) { netio_free(nio,NULL); return NULL; } return nio; } /* * ========================================================================= * TCP sockets * ========================================================================= */ /* Free a NetIO TCP descriptor */ static void netio_tcp_free(netio_inet_desc_t *nid) { if (nid->fd != -1) close(nid->fd); } /* * very simple protocol to send packets over tcp * 32 bits in network format - size of packet, then packet itself and so on. */ static ssize_t netio_tcp_send(netio_inet_desc_t *nid,void *pkt,size_t pkt_len) { u_long l = htonl(pkt_len); if (write(nid->fd,&l,sizeof(l)) == -1) return(-1); return(write(nid->fd,pkt,pkt_len)); } static ssize_t netio_tcp_recv(netio_inet_desc_t *nid,void *pkt,size_t max_len) { u_long l; if (read(nid->fd,&l,sizeof(l)) != sizeof(l)) return(-1); if (ntohl(l) > max_len) return(-1); return(read(nid->fd,pkt,ntohl(l))); } static int netio_tcp_cli_create(netio_inet_desc_t *nid,char *host,char *port) { struct sockaddr_in serv; struct servent *sp; struct hostent *hp; if ((nid->fd = socket(PF_INET,SOCK_STREAM,0)) < 0) { perror("netio_tcp_cli_create: socket"); return(-1); } memset(&serv,0,sizeof(serv)); serv.sin_family = AF_INET; if (atoi(port) == 0) { if (!(sp = getservbyname(port,"tcp"))) { fprintf(stderr,"netio_tcp_cli_create: port %s is neither " "number not service %s\n",port,strerror(errno)); close(nid->fd); return(-1); } serv.sin_port = sp->s_port; } else serv.sin_port = htons(atoi(port)); if (inet_addr(host) == INADDR_NONE) { if (!(hp = gethostbyname(host))) { fprintf(stderr,"netio_tcp_cli_create: no host %s\n",host); close(nid->fd); return(-1); } serv.sin_addr.s_addr = *hp->h_addr; } else serv.sin_addr.s_addr = inet_addr(host); if (connect(nid->fd,(struct sockaddr *)&serv,sizeof(serv)) < 0) { fprintf(stderr,"netio_tcp_cli_create: connect to %s:%s failed %s\n", host,port,strerror(errno)); close(nid->fd); return(-1); } return(0); } /* Create a new NetIO descriptor with TCP_CLI method */ netio_desc_t *netio_desc_create_tcp_cli(char *nio_name,char *host,char *port) { netio_desc_t *nio; if (!(nio = netio_create(nio_name))) return NULL; if (netio_tcp_cli_create(&nio->u.nid,host,port) < 0) { netio_free(nio,NULL); return NULL; } nio->type = NETIO_TYPE_TCP_CLI; nio->send = (void *)netio_tcp_send; nio->recv = (void *)netio_tcp_recv; nio->free = (void *)netio_tcp_free; nio->dptr = &nio->u.nid; if (netio_record(nio) == -1) { netio_free(nio,NULL); return NULL; } return nio; } static int netio_tcp_ser_create(netio_inet_desc_t *nid,char *port) { struct sockaddr_in serv; struct servent *sp; int sock_fd; if ((sock_fd = socket(PF_INET,SOCK_STREAM,0)) < 0) { perror("netio_tcp_cli_create: socket\n"); return(-1); } memset(&serv,0,sizeof(serv)); serv.sin_family = AF_INET; serv.sin_addr.s_addr = htonl(INADDR_ANY); if (atoi(port) == 0) { if (!(sp = getservbyname(port,"tcp"))) { fprintf(stderr,"netio_tcp_ser_create: port %s is neither " "number not service %s\n",port,strerror(errno)); close(sock_fd); return(-1); } serv.sin_port = sp->s_port; } else serv.sin_port = htons(atoi(port)); if (bind(sock_fd,(struct sockaddr *)&serv,sizeof(serv)) < 0) { fprintf(stderr,"netio_tcp_ser_create: bind %s failed %s\n", port,strerror(errno)); close(sock_fd); return(-1); } if (listen(sock_fd,1) < 0) { fprintf(stderr,"netio_tcp_ser_create: listen %s failed %s\n", port,strerror(errno)); close(sock_fd); return(-1); } fprintf(stderr,"Waiting connection on port %s...\n",port); if ((nid->fd = accept(sock_fd,NULL,NULL)) < 0) { fprintf(stderr,"netio_tcp_ser_create: accept %s failed %s\n", port,strerror(errno)); close(sock_fd); return(-1); } fprintf(stderr,"Connected\n"); close(sock_fd); return(0); } /* Create a new NetIO descriptor with TCP_SER method */ netio_desc_t *netio_desc_create_tcp_ser(char *nio_name,char *port) { netio_desc_t *nio; if (!(nio = netio_create(nio_name))) return NULL; if (netio_tcp_ser_create(&nio->u.nid,port) == -1) { netio_free(nio,NULL); return NULL; } nio->type = NETIO_TYPE_TCP_SER; nio->send = (void *)netio_tcp_send; nio->recv = (void *)netio_tcp_recv; nio->free = (void *)netio_tcp_free; nio->dptr = &nio->u.nid; if (netio_record(nio) == -1) { netio_free(nio,NULL); return NULL; } return nio; } /* * ========================================================================= * UDP sockets * ========================================================================= */ /* Free a NetIO UDP descriptor */ static void netio_udp_free(netio_inet_desc_t *nid) { if (nid->remote_host) { free(nid->remote_host); nid->remote_host = NULL; } if (nid->fd != -1) close(nid->fd); } /* Send a packet to an UDP socket */ static ssize_t netio_udp_send(netio_inet_desc_t *nid,void *pkt,size_t pkt_len) { return(send(nid->fd,pkt,pkt_len,0)); } /* Receive a packet from an UDP socket */ static ssize_t netio_udp_recv(netio_inet_desc_t *nid,void *pkt,size_t max_len) { return(recvfrom(nid->fd,pkt,max_len,0,NULL,NULL)); } /* Save the NIO configuration */ static void netio_udp_save_cfg(netio_desc_t *nio,FILE *fd) { netio_inet_desc_t *nid = nio->dptr; fprintf(fd,"nio create_udp %s %d %s %d\n", nio->name,nid->local_port,nid->remote_host,nid->remote_port); } /* Create a new NetIO descriptor with UDP method */ netio_desc_t *netio_desc_create_udp(char *nio_name,int local_port, char *remote_host,int remote_port) { netio_inet_desc_t *nid; netio_desc_t *nio; if (!(nio = netio_create(nio_name))) return NULL; nid = &nio->u.nid; nid->local_port = local_port; nid->remote_port = remote_port; if (!(nid->remote_host = strdup(remote_host))) { fprintf(stderr,"netio_desc_create_udp: insufficient memory\n"); goto error; } if ((nid->fd = udp_connect(local_port,remote_host,remote_port)) < 0) { fprintf(stderr,"netio_desc_create_udp: unable to connect to %s:%d\n", remote_host,remote_port); goto error; } nio->type = NETIO_TYPE_UDP; nio->send = (void *)netio_udp_send; nio->recv = (void *)netio_udp_recv; nio->free = (void *)netio_udp_free; nio->save_cfg = netio_udp_save_cfg; nio->dptr = &nio->u.nid; if (netio_record(nio) == -1) goto error; return nio; error: netio_free(nio,NULL); return NULL; } /* * ========================================================================= * UDP sockets with auto allocation * ========================================================================= */ /* Get local port */ int netio_udp_auto_get_local_port(netio_desc_t *nio) { if (nio->type != NETIO_TYPE_UDP_AUTO) return(-1); return(nio->u.nid.local_port); } /* Connect to a remote host/port */ int netio_udp_auto_connect(netio_desc_t *nio,char *host,int port) { netio_inet_desc_t *nid = nio->dptr; /* NIO already connected */ if (nid->remote_host != NULL) return(-1); if (!(nid->remote_host = strdup(host))) { fprintf(stderr,"netio_desc_create_udp_auto: insufficient memory\n"); return(-1); } nid->remote_port = port; if (ip_connect_fd(nid->fd,nid->remote_host,nid->remote_port) < 0) { free(nid->remote_host); nid->remote_host = NULL; return(-1); } return(0); } /* Create a new NetIO descriptor with auto UDP method */ netio_desc_t *netio_desc_create_udp_auto(char *nio_name,char *local_addr, int port_start,int port_end) { netio_inet_desc_t *nid; netio_desc_t *nio; if (!(nio = netio_create(nio_name))) return NULL; nid = &nio->u.nid; nid->local_port = -1; nid->remote_host = NULL; nid->remote_port = -1; if ((nid->fd = udp_listen_range(local_addr,port_start,port_end, &nid->local_port)) < 0) { fprintf(stderr, "netio_desc_create_udp_auto: unable to create socket " "(addr=%s,port_start=%d,port_end=%d)\n", local_addr,port_start,port_end); goto error; } nio->type = NETIO_TYPE_UDP_AUTO; nio->send = (void *)netio_udp_send; nio->recv = (void *)netio_udp_recv; nio->free = (void *)netio_udp_free; nio->save_cfg = netio_udp_save_cfg; nio->dptr = &nio->u.nid; if (netio_record(nio) == -1) goto error; return nio; error: netio_free(nio,NULL); return NULL; } /* * ========================================================================= * Multicast sockets * ========================================================================= */ /* Free a NetIO Mcast descriptor */ static void netio_mcast_free(netio_mcast_desc_t *nmd) { if (nmd->mcast_group) { free(nmd->mcast_group); nmd->mcast_group = NULL; } if (nmd->fd != -1) close(nmd->fd); } /* Send a packet to a Multicast socket */ static ssize_t netio_mcast_send(netio_mcast_desc_t *nmd, void *pkt,size_t pkt_len) { struct msghdr mh; struct iovec vec[2]; memset(&mh,0,sizeof(mh)); mh.msg_name = &nmd->sa; mh.msg_namelen = nmd->sa_len; mh.msg_iov = vec; mh.msg_iovlen = 2; vec[0].iov_base = nmd->local_id; vec[0].iov_len = sizeof(uuid_t); vec[1].iov_base = pkt; vec[1].iov_len = pkt_len; return(sendmsg(nmd->fd,&mh,0)); } /* Receive a packet from a Multicast socket */ static ssize_t netio_mcast_recv(netio_mcast_desc_t *nmd, void *pkt,size_t max_len) { uuid_t remote_id; struct msghdr mh; struct iovec vec[2]; ssize_t len; memset(&mh,0,sizeof(mh)); mh.msg_iov = vec; mh.msg_iovlen = 2; vec[0].iov_base = remote_id; vec[0].iov_len = sizeof(uuid_t); vec[1].iov_base = pkt; vec[1].iov_len = max_len; len = recvmsg(nmd->fd,&mh,0); if ((len <= sizeof(uuid_t)) || (uuid_compare(remote_id,nmd->local_id) == 0)) return(-1); return(len - sizeof(uuid_t)); } /* Save the NIO configuration */ static void netio_mcast_save_cfg(netio_desc_t *nio,FILE *fd) { netio_mcast_desc_t *nmd = nio->dptr; fprintf(fd,"nio create_mcast %s %s %d\n", nio->name,nmd->mcast_group,nmd->mcast_port); } /* Create a new NetIO descriptor with Multicast method */ netio_desc_t * netio_desc_create_mcast(char *nio_name,char *mcast_group,int mcast_port) { netio_mcast_desc_t *nmd; netio_desc_t *nio; if (!(nio = netio_create(nio_name))) return NULL; nmd = &nio->u.nmd; nmd->mcast_port = mcast_port; uuid_generate(nmd->local_id); if (!(nmd->mcast_group = strdup(mcast_group))) { fprintf(stderr,"netio_desc_create_mcast: insufficient memory\n"); goto error; } nmd->fd = udp_mcast_socket(mcast_group,mcast_port, (struct sockaddr *)&nmd->sa,&nmd->sa_len); if (nmd->fd < 0) { fprintf(stderr,"netio_desc_create_mcast: unable to connect to %s:%d\n", mcast_group,mcast_port); goto error; } nio->type = NETIO_TYPE_MCAST; nio->send = (void *)netio_mcast_send; nio->recv = (void *)netio_mcast_recv; nio->free = (void *)netio_mcast_free; nio->save_cfg = netio_mcast_save_cfg; nio->dptr = &nio->u.nmd; if (netio_record(nio) == -1) goto error; return nio; error: netio_free(nio,NULL); return NULL; } /* Set TTL for a multicast socket */ int netio_mcast_set_ttl(netio_desc_t *nio,int ttl) { if (nio->type == NETIO_TYPE_MCAST) return(-1); return(udp_mcast_set_ttl(netio_get_fd(nio),ttl)); } /* * ========================================================================= * Linux RAW Ethernet driver * ========================================================================= */ #ifdef LINUX_ETH /* Free a NetIO raw ethernet descriptor */ static void netio_lnxeth_free(netio_lnxeth_desc_t *nled) { if (nled->fd != -1) close(nled->fd); } /* Send a packet to a raw Ethernet socket */ static ssize_t netio_lnxeth_send(netio_lnxeth_desc_t *nled, void *pkt,size_t pkt_len) { return(lnx_eth_send(nled->fd,nled->dev_id,pkt,pkt_len)); } /* Receive a packet from an raw Ethernet socket */ static ssize_t netio_lnxeth_recv(netio_lnxeth_desc_t *nled, void *pkt,size_t max_len) { return(lnx_eth_recv(nled->fd,pkt,max_len)); } /* Save the NIO configuration */ static void netio_lnxeth_save_cfg(netio_desc_t *nio,FILE *fd) { netio_lnxeth_desc_t *nled = nio->dptr; fprintf(fd,"nio create_linux_eth %s %s\n",nio->name,nled->dev_name); } /* Create a new NetIO descriptor with raw Ethernet method */ netio_desc_t *netio_desc_create_lnxeth(char *nio_name,char *dev_name) { netio_lnxeth_desc_t *nled; netio_desc_t *nio; if (!(nio = netio_create(nio_name))) return NULL; nled = &nio->u.nled; if (strlen(dev_name) >= NETIO_DEV_MAXLEN) { fprintf(stderr,"netio_desc_create_lnxeth: bad Ethernet device string " "specified.\n"); netio_free(nio,NULL); return NULL; } strcpy(nled->dev_name,dev_name); nled->fd = lnx_eth_init_socket(dev_name); nled->dev_id = lnx_eth_get_dev_index(dev_name); if (nled->fd < 0) { netio_free(nio,NULL); return NULL; } nio->type = NETIO_TYPE_LINUX_ETH; nio->send = (void *)netio_lnxeth_send; nio->recv = (void *)netio_lnxeth_recv; nio->free = (void *)netio_lnxeth_free; nio->save_cfg = netio_lnxeth_save_cfg; nio->dptr = &nio->u.nled; if (netio_record(nio) == -1) { netio_free(nio,NULL); return NULL; } return nio; } #endif /* LINUX_ETH */ /* * ========================================================================= * Generic RAW Ethernet driver * ========================================================================= */ #ifdef GEN_ETH /* Free a NetIO raw ethernet descriptor */ static void netio_geneth_free(netio_geneth_desc_t *nged) { gen_eth_close(nged->pcap_dev); } /* Send a packet to an Ethernet device */ static ssize_t netio_geneth_send(netio_geneth_desc_t *nged, void *pkt,size_t pkt_len) { return(gen_eth_send(nged->pcap_dev,pkt,pkt_len)); } /* Receive a packet from an Ethernet device */ static ssize_t netio_geneth_recv(netio_geneth_desc_t *nged, void *pkt,size_t max_len) { return(gen_eth_recv(nged->pcap_dev,pkt,max_len)); } /* Save the NIO configuration */ static void netio_geneth_save_cfg(netio_desc_t *nio,FILE *fd) { netio_geneth_desc_t *nged = nio->dptr; fprintf(fd,"nio create_gen_eth %s %s\n",nio->name,nged->dev_name); } /* Create a new NetIO descriptor with generic raw Ethernet method */ netio_desc_t *netio_desc_create_geneth(char *nio_name,char *dev_name) { netio_geneth_desc_t *nged; netio_desc_t *nio; if (!(nio = netio_create(nio_name))) return NULL; nged = &nio->u.nged; if (strlen(dev_name) >= NETIO_DEV_MAXLEN) { fprintf(stderr,"netio_desc_create_geneth: bad Ethernet device string " "specified.\n"); netio_free(nio,NULL); return NULL; } strcpy(nged->dev_name,dev_name); if (!(nged->pcap_dev = gen_eth_init(dev_name))) { netio_free(nio,NULL); return NULL; } nio->type = NETIO_TYPE_GEN_ETH; nio->send = (void *)netio_geneth_send; nio->recv = (void *)netio_geneth_recv; nio->free = (void *)netio_geneth_free; nio->save_cfg = netio_geneth_save_cfg; nio->dptr = &nio->u.nged; if (netio_record(nio) == -1) { netio_free(nio,NULL); return NULL; } return nio; } #endif /* GEN_ETH */ /* * ========================================================================= * FIFO Driver (intra-hypervisor communications) * ========================================================================= */ /* Extract the first packet of the FIFO */ static netio_fifo_pkt_t *netio_fifo_extract_pkt(netio_fifo_desc_t *nfd) { netio_fifo_pkt_t *p; if (!(p = nfd->head)) return NULL; nfd->pkt_count--; nfd->head = p->next; if (!nfd->head) nfd->last = NULL; return p; } /* Insert a packet into the FIFO (in tail) */ static void netio_fifo_insert_pkt(netio_fifo_desc_t *nfd,netio_fifo_pkt_t *p) { pthread_mutex_lock(&nfd->lock); nfd->pkt_count++; p->next = NULL; if (nfd->last) { nfd->last->next = p; } else { nfd->head = p; } nfd->last = p; pthread_mutex_unlock(&nfd->lock); } /* Free the packet list */ static void netio_fifo_free_pkt_list(netio_fifo_desc_t *nfd) { netio_fifo_pkt_t *p,*next; for(p=nfd->head;p;p=next) { next = p->next; free(p); } nfd->head = nfd->last = NULL; nfd->pkt_count = 0; } /* Establish a cross-connect between two FIFO NetIO */ int netio_fifo_crossconnect(netio_desc_t *a,netio_desc_t *b) { netio_fifo_desc_t *pa,*pb; if ((a->type != NETIO_TYPE_FIFO) || (b->type != NETIO_TYPE_FIFO)) return(-1); pa = &a->u.nfd; pb = &b->u.nfd; /* A => B */ pthread_mutex_lock(&pa->endpoint_lock); pthread_mutex_lock(&pa->lock); pa->endpoint = pb; netio_fifo_free_pkt_list(pa); pthread_mutex_unlock(&pa->lock); pthread_mutex_unlock(&pa->endpoint_lock); /* B => A */ pthread_mutex_lock(&pb->endpoint_lock); pthread_mutex_lock(&pb->lock); pb->endpoint = pa; netio_fifo_free_pkt_list(pb); pthread_mutex_unlock(&pb->lock); pthread_mutex_unlock(&pb->endpoint_lock); return(0); } /* Unbind an endpoint */ static void netio_fifo_unbind_endpoint(netio_fifo_desc_t *nfd) { pthread_mutex_lock(&nfd->endpoint_lock); nfd->endpoint = NULL; pthread_mutex_unlock(&nfd->endpoint_lock); } /* Free a NetIO FIFO descriptor */ static void netio_fifo_free(netio_fifo_desc_t *nfd) { if (nfd->endpoint) netio_fifo_unbind_endpoint(nfd->endpoint); netio_fifo_free_pkt_list(nfd); pthread_mutex_destroy(&nfd->lock); pthread_cond_destroy(&nfd->cond); } /* Send a packet (to the endpoint FIFO) */ static ssize_t netio_fifo_send(netio_fifo_desc_t *nfd,void *pkt,size_t pkt_len) { netio_fifo_pkt_t *p; size_t len; pthread_mutex_lock(&nfd->endpoint_lock); /* The cross-connect must have been established before */ if (!nfd->endpoint) goto error; /* Allocate a a new packet and insert it into the endpoint FIFO */ len = sizeof(netio_fifo_pkt_t) + pkt_len; if (!(p = malloc(len))) goto error; memcpy(p->pkt,pkt,pkt_len); p->pkt_len = pkt_len; netio_fifo_insert_pkt(nfd->endpoint,p); pthread_cond_signal(&nfd->endpoint->cond); pthread_mutex_unlock(&nfd->endpoint_lock); return(pkt_len); error: pthread_mutex_unlock(&nfd->endpoint_lock); return(-1); } /* Read a packet from the local FIFO queue */ static ssize_t netio_fifo_recv(netio_fifo_desc_t *nfd,void *pkt,size_t max_len) { struct timespec ts; m_tmcnt_t expire; netio_fifo_pkt_t *p; size_t len = -1; /* Wait for the endpoint to signal a new arriving packet */ expire = m_gettime_usec() + 50000; ts.tv_sec = expire / 1000000; ts.tv_nsec = (expire % 1000000) * 1000; pthread_mutex_lock(&nfd->lock); pthread_cond_timedwait(&nfd->cond,&nfd->lock,&ts); /* Extract a packet from the list */ p = netio_fifo_extract_pkt(nfd); pthread_mutex_unlock(&nfd->lock); if (p) { len = m_min(p->pkt_len,max_len); memcpy(pkt,p->pkt,len); free(p); } return(len); } /* Create a new NetIO descriptor with FIFO method */ netio_desc_t *netio_desc_create_fifo(char *nio_name) { netio_fifo_desc_t *nfd; netio_desc_t *nio; if (!(nio = netio_create(nio_name))) return NULL; nfd = &nio->u.nfd; pthread_mutex_init(&nfd->lock,NULL); pthread_mutex_init(&nfd->endpoint_lock,NULL); pthread_cond_init(&nfd->cond,NULL); nio->type = NETIO_TYPE_FIFO; nio->send = (void *)netio_fifo_send; nio->recv = (void *)netio_fifo_recv; nio->free = (void *)netio_fifo_free; nio->dptr = nfd; if (netio_record(nio) == -1) { netio_free(nio,NULL); return NULL; } return nio; } /* * ========================================================================= * NULL Driver (does nothing, used for debugging) * ========================================================================= */ static ssize_t netio_null_send(void *null_ptr,void *pkt,size_t pkt_len) { return(pkt_len); } static ssize_t netio_null_recv(void *null_ptr,void *pkt,size_t max_len) { usleep(200000); return(-1); } static void netio_null_save_cfg(netio_desc_t *nio,FILE *fd) { fprintf(fd,"nio create_null %s\n",nio->name); } /* Create a new NetIO descriptor with NULL method */ netio_desc_t *netio_desc_create_null(char *nio_name) { netio_desc_t *nio; if (!(nio = netio_create(nio_name))) return NULL; nio->type = NETIO_TYPE_NULL; nio->send = (void *)netio_null_send; nio->recv = (void *)netio_null_recv; nio->save_cfg = netio_null_save_cfg; nio->dptr = NULL; if (netio_record(nio) == -1) { netio_free(nio,NULL); return NULL; } return nio; } /* Free a NetIO descriptor */ static int netio_free(void *data,void *arg) { netio_desc_t *nio = data; if (nio) { netio_filter_unbind(nio,NETIO_FILTER_DIR_RX); netio_filter_unbind(nio,NETIO_FILTER_DIR_TX); netio_filter_unbind(nio,NETIO_FILTER_DIR_BOTH); if (nio->free != NULL) nio->free(nio->dptr); free(nio->name); free(nio); } return(TRUE); } /* Reset NIO statistics */ void netio_reset_stats(netio_desc_t *nio) { nio->stats_pkts_in = nio->stats_pkts_out = 0; nio->stats_bytes_in = nio->stats_bytes_out = 0; } /* Indicate if a NetIO can transmit a packet */ int netio_can_transmit(netio_desc_t *nio) { u_int bw_current; /* No bandwidth constraint applied, can always transmit */ if (!nio->bandwidth) return(TRUE); /* Check that we verify the bandwidth constraint */ bw_current = nio->bw_cnt_total * 8 * 1000; bw_current /= 1024 * NETIO_BW_SAMPLE_ITV * NETIO_BW_SAMPLES; return(bw_current < nio->bandwidth); } /* Update bandwidth counter */ void netio_update_bw_stat(netio_desc_t *nio,m_uint64_t bytes) { nio->bw_cnt[nio->bw_pos] += bytes; nio->bw_cnt_total += bytes; } /* Reset NIO bandwidth counter */ void netio_clear_bw_stat(netio_desc_t *nio) { if (++nio->bw_ptask_cnt == (NETIO_BW_SAMPLE_ITV / ptask_sleep_time)) { nio->bw_ptask_cnt = 0; if (++nio->bw_pos == NETIO_BW_SAMPLES) nio->bw_pos = 0; nio->bw_cnt_total -= nio->bw_cnt[nio->bw_pos]; nio->bw_cnt[nio->bw_pos] = 0; } } /* Set the bandwidth constraint */ void netio_set_bandwidth(netio_desc_t *nio,u_int bandwidth) { nio->bandwidth = bandwidth; } /* * ========================================================================= * RX Listeners * ========================================================================= */ /* Find a RX listener */ static inline struct netio_rx_listener *netio_rxl_find(netio_desc_t *nio) { struct netio_rx_listener *rxl; for(rxl=netio_rxl_list;rxl;rxl=rxl->next) if (rxl->nio == nio) return rxl; return NULL; } /* Remove a NIO from the listener list */ static int netio_rxl_remove_internal(netio_desc_t *nio) { struct netio_rx_listener *rxl; int res = -1; if ((rxl = netio_rxl_find(nio))) { /* we suppress this NIO only when the ref count hits 0 */ rxl->ref_count--; if (!rxl->ref_count) { /* remove this listener from the double linked list */ if (rxl->next) rxl->next->prev = rxl->prev; if (rxl->prev) rxl->prev->next = rxl->next; else netio_rxl_list = rxl->next; /* if this is non-FD NIO, wait for thread to terminate */ if (netio_get_fd(rxl->nio) == -1) { rxl->running = FALSE; pthread_join(rxl->spec_thread,NULL); } free(rxl); } res = 0; } return(res); } /* Add a RXL listener to the listener list */ static void netio_rxl_add_internal(struct netio_rx_listener *rxl) { struct netio_rx_listener *tmp; if ((tmp = netio_rxl_find(rxl->nio))) { tmp->ref_count++; free(rxl); } else { rxl->prev = NULL; rxl->next = netio_rxl_list; if (rxl->next) rxl->next->prev = rxl; netio_rxl_list = rxl; } } /* RX Listener dedicated thread (for non-FD NIO) */ static void *netio_rxl_spec_thread(void *arg) { struct netio_rx_listener *rxl = arg; netio_desc_t *nio = rxl->nio; ssize_t pkt_len; while(rxl->running) { pkt_len = netio_recv(nio,nio->rx_pkt,sizeof(nio->rx_pkt)); if (pkt_len > 0) rxl->rx_handler(nio,nio->rx_pkt,pkt_len,rxl->arg1,rxl->arg2); } return NULL; } /* RX Listener General Thread */ void *netio_rxl_gen_thread(void *arg) { struct netio_rx_listener *rxl; ssize_t pkt_len; netio_desc_t *nio; struct timeval tv; int fd,fd_max,res; fd_set rfds; for(;;) { NETIO_RXL_LOCK(); NETIO_RXQ_LOCK(); /* Add the new waiting NIO to the active list */ while(netio_rxl_add_list != NULL) { rxl = netio_rxl_add_list; netio_rxl_add_list = netio_rxl_add_list->next; netio_rxl_add_internal(rxl); } /* Delete the NIO present in the remove list */ while(netio_rxl_remove_list != NULL) { nio = netio_rxl_remove_list; netio_rxl_remove_list = netio_rxl_remove_list->rxl_next; netio_rxl_remove_internal(nio); } pthread_cond_broadcast(&netio_rxl_cond); NETIO_RXQ_UNLOCK(); /* Build the FD set */ FD_ZERO(&rfds); fd_max = -1; for(rxl=netio_rxl_list;rxl;rxl=rxl->next) { if ((fd = netio_get_fd(rxl->nio)) == -1) continue; if (fd > fd_max) fd_max = fd; FD_SET(fd,&rfds); } NETIO_RXL_UNLOCK(); /* Wait for incoming packets */ tv.tv_sec = 0; tv.tv_usec = 20 * 1000; /* 200 ms */ res = select(fd_max+1,&rfds,NULL,NULL,&tv); if (res == -1) { if (errno != EINTR) perror("netio_rxl_thread: select"); continue; } /* Examine active FDs and call user handlers */ NETIO_RXL_LOCK(); for(rxl=netio_rxl_list;rxl;rxl=rxl->next) { nio = rxl->nio; if ((fd = netio_get_fd(nio)) == -1) continue; if (FD_ISSET(fd,&rfds)) { pkt_len = netio_recv(nio,nio->rx_pkt,sizeof(nio->rx_pkt)); if (pkt_len > 0) rxl->rx_handler(nio,nio->rx_pkt,pkt_len,rxl->arg1,rxl->arg2); } } NETIO_RXL_UNLOCK(); } return NULL; } /* Add a RX listener in the listener list */ int netio_rxl_add(netio_desc_t *nio,netio_rx_handler_t rx_handler, void *arg1,void *arg2) { struct netio_rx_listener *rxl; NETIO_RXQ_LOCK(); if (!(rxl = malloc(sizeof(*rxl)))) { NETIO_RXQ_UNLOCK(); fprintf(stderr,"netio_rxl_add: unable to create structure.\n"); return(-1); } memset(rxl,0,sizeof(*rxl)); rxl->nio = nio; rxl->ref_count = 1; rxl->rx_handler = rx_handler; rxl->arg1 = arg1; rxl->arg2 = arg2; rxl->running = TRUE; if ((netio_get_fd(rxl->nio) == -1) && pthread_create(&rxl->spec_thread,NULL,netio_rxl_spec_thread,rxl)) { NETIO_RXQ_UNLOCK(); fprintf(stderr,"netio_rxl_add: unable to create specific thread.\n"); free(rxl); return(-1); } rxl->next = netio_rxl_add_list; netio_rxl_add_list = rxl; pthread_cond_wait(&netio_rxl_cond,&netio_rxq_mutex); NETIO_RXQ_UNLOCK(); return(0); } /* Remove a NIO from the listener list */ int netio_rxl_remove(netio_desc_t *nio) { NETIO_RXQ_LOCK(); nio->rxl_next = netio_rxl_remove_list; netio_rxl_remove_list = nio; pthread_cond_wait(&netio_rxl_cond,&netio_rxq_mutex); NETIO_RXQ_UNLOCK(); return(0); } /* Initialize the RXL thread */ int netio_rxl_init(void) { pthread_cond_init(&netio_rxl_cond,NULL); if (pthread_create(&netio_rxl_thread,NULL,netio_rxl_gen_thread,NULL)) { perror("netio_rxl_init: pthread_create"); return(-1); } return(0); } dynamips-0.2.14/common/net_io.h000066400000000000000000000221271241034141600163400ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Network I/O Layer. */ #ifndef __NET_IO_H__ #define __NET_IO_H__ #include #include #include #include #include "utils.h" #include "gen_uuid.h" #ifdef LINUX_ETH #include "linux_eth.h" #endif #ifdef GEN_ETH #include "gen_eth.h" #endif /* Maximum packet size */ #define NETIO_MAX_PKT_SIZE 32768 /* Maximum device length */ #define NETIO_DEV_MAXLEN 64 enum { NETIO_TYPE_UNIX = 0, NETIO_TYPE_VDE, NETIO_TYPE_TAP, NETIO_TYPE_UDP, NETIO_TYPE_UDP_AUTO, NETIO_TYPE_TCP_CLI, NETIO_TYPE_TCP_SER, NETIO_TYPE_MCAST, #ifdef LINUX_ETH NETIO_TYPE_LINUX_ETH, #endif #ifdef GEN_ETH NETIO_TYPE_GEN_ETH, #endif NETIO_TYPE_FIFO, NETIO_TYPE_NULL, NETIO_TYPE_MAX, }; enum { NETIO_FILTER_ACTION_DROP = 0, NETIO_FILTER_ACTION_PASS, NETIO_FILTER_ACTION_ALTER, NETIO_FILTER_ACTION_DUPLICATE, }; typedef struct netio_desc netio_desc_t; /* VDE switch definitions */ enum vde_request_type { VDE_REQ_NEW_CONTROL }; #define VDE_SWITCH_MAGIC 0xfeedface #define VDE_SWITCH_VERSION 3 struct vde_request_v3 { m_uint32_t magic; m_uint32_t version; enum vde_request_type type; struct sockaddr_un sock; }; /* netio unix descriptor */ typedef struct netio_unix_desc netio_unix_desc_t; struct netio_unix_desc { char *local_filename; struct sockaddr_un remote_sock; int fd; }; /* netio vde descriptor */ typedef struct netio_vde_desc netio_vde_desc_t; struct netio_vde_desc { char *local_filename; struct sockaddr_un remote_sock; int ctrl_fd,data_fd; }; /* netio tap descriptor */ typedef struct netio_tap_desc netio_tap_desc_t; struct netio_tap_desc { char filename[NETIO_DEV_MAXLEN]; int fd; }; /* netio udp/tcp descriptor */ typedef struct netio_inet_desc netio_inet_desc_t; struct netio_inet_desc { int local_port,remote_port; char *remote_host; int fd; }; /* netio mcast descriptor */ typedef struct netio_mcast_desc netio_mcast_desc_t; struct netio_mcast_desc { uuid_t local_id; char *mcast_group; int mcast_port; int fd; #if HAS_RFC2553 struct sockaddr_storage sa; #else struct sockaddr_in sa; #endif int sa_len; }; #ifdef LINUX_ETH /* netio linux raw ethernet descriptor */ typedef struct netio_lnxeth_desc netio_lnxeth_desc_t; struct netio_lnxeth_desc { char dev_name[NETIO_DEV_MAXLEN]; int dev_id,fd; }; #endif #ifdef GEN_ETH /* netio generic raw ethernet descriptor */ typedef struct netio_geneth_desc netio_geneth_desc_t; struct netio_geneth_desc { char dev_name[NETIO_DEV_MAXLEN]; pcap_t *pcap_dev; }; #endif /* FIFO packet */ typedef struct netio_fifo_pkt netio_fifo_pkt_t; struct netio_fifo_pkt { netio_fifo_pkt_t *next; size_t pkt_len; char pkt[0]; }; /* Netio FIFO */ typedef struct netio_fifo_desc netio_fifo_desc_t; struct netio_fifo_desc { pthread_cond_t cond; pthread_mutex_t lock,endpoint_lock; netio_fifo_desc_t *endpoint; netio_fifo_pkt_t *head,*last; u_int pkt_count; }; /* Packet filter */ typedef struct netio_pktfilter netio_pktfilter_t; struct netio_pktfilter { char *name; int (*setup)(netio_desc_t *nio,void **opt,int argc,char *argv[]); void (*free)(netio_desc_t *nio,void **opt); int (*pkt_handler)(netio_desc_t *nio,void *pkt,size_t len,void *opt); netio_pktfilter_t *next; }; /* Statistics */ typedef struct netio_stat netio_stat_t; struct netio_stat { m_uint64_t pkts,bytes; }; #define NETIO_BW_SAMPLES 10 #define NETIO_BW_SAMPLE_ITV 30 /* Generic netio descriptor */ struct netio_desc { u_int type; void *dptr; char *name; int debug; /* Frame Relay specific information */ m_uint8_t fr_lmi_seq; void *fr_conn_list; /* Ethernet specific information */ u_int vlan_port_type; m_uint16_t vlan_id; void *vlan_input_vector; union { netio_unix_desc_t nud; netio_vde_desc_t nvd; netio_tap_desc_t ntd; netio_inet_desc_t nid; netio_mcast_desc_t nmd; #ifdef LINUX_ETH netio_lnxeth_desc_t nled; #endif #ifdef GEN_ETH netio_geneth_desc_t nged; #endif netio_fifo_desc_t nfd; } u; /* Send and receive prototypes */ ssize_t (*send)(void *desc,void *pkt,size_t len); ssize_t (*recv)(void *desc,void *pkt,size_t len); /* Configuration saving */ void (*save_cfg)(netio_desc_t *nio,FILE *fd); /* Free ressources */ void (*free)(void *desc); /* Bandwidth constraint (in Kb/s) */ u_int bandwidth; m_uint64_t bw_cnt[NETIO_BW_SAMPLES]; m_uint64_t bw_cnt_total; u_int bw_pos; u_int bw_ptask_cnt; /* Packet filters */ netio_pktfilter_t *rx_filter,*tx_filter,*both_filter; void *rx_filter_data,*tx_filter_data,*both_filter_data; /* Statistics */ m_uint64_t stats_pkts_in,stats_pkts_out; m_uint64_t stats_bytes_in,stats_bytes_out; /* Next pointer (for RX listener) */ netio_desc_t *rxl_next; /* Packet data */ u_char rx_pkt[NETIO_MAX_PKT_SIZE]; }; /* RX listener */ typedef int (*netio_rx_handler_t)(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, void *arg1,void *arg2); struct netio_rx_listener { netio_desc_t *nio; u_int ref_count; volatile int running; netio_rx_handler_t rx_handler; void *arg1,*arg2; pthread_t spec_thread; struct netio_rx_listener *prev,*next; }; /* Get NETIO type given a description */ int netio_get_type(char *type); /* Show the NETIO types */ void netio_show_types(void); /* Create a new NetIO descriptor */ netio_desc_t *netio_desc_create_unix(char *nio_name,char *local,char *remote); /* Create a new NetIO descriptor with VDE method */ netio_desc_t *netio_desc_create_vde(char *nio_name,char *control,char *local); /* Create a new NetIO descriptor with TAP method */ netio_desc_t *netio_desc_create_tap(char *nio_name,char *tap_name); /* Create a new NetIO descriptor with TCP_CLI method */ netio_desc_t *netio_desc_create_tcp_cli(char *nio_name,char *addr,char *port); /* Create a new NetIO descriptor with TCP_SER method */ netio_desc_t *netio_desc_create_tcp_ser(char *nio_name,char *port); /* Create a new NetIO descriptor with UDP method */ netio_desc_t *netio_desc_create_udp(char *nio_name,int local_port, char *remote_host,int remote_port); /* Get local port */ int netio_udp_auto_get_local_port(netio_desc_t *nio); /* Connect to a remote host/port */ int netio_udp_auto_connect(netio_desc_t *nio,char *host,int port); /* Create a new NetIO descriptor with auto UDP method */ netio_desc_t *netio_desc_create_udp_auto(char *nio_name,char *local_addr, int port_start,int port_end); /* Create a new NetIO descriptor with Multicast method */ netio_desc_t * netio_desc_create_mcast(char *nio_name,char *mcast_group,int mcast_port); /* Set TTL for a multicast socket */ int netio_mcast_set_ttl(netio_desc_t *nio,int ttl); #ifdef LINUX_ETH /* Create a new NetIO descriptor with raw Ethernet method */ netio_desc_t *netio_desc_create_lnxeth(char *nio_name,char *dev_name); #endif #ifdef GEN_ETH /* Create a new NetIO descriptor with generic raw Ethernet method */ netio_desc_t *netio_desc_create_geneth(char *nio_name,char *dev_name); #endif /* Establish a cross-connect between two FIFO NetIO */ int netio_fifo_crossconnect(netio_desc_t *a,netio_desc_t *b); /* Create a new NetIO descriptor with FIFO method */ netio_desc_t *netio_desc_create_fifo(char *nio_name); /* Create a new NetIO descriptor with NULL method */ netio_desc_t *netio_desc_create_null(char *nio_name); /* Acquire a reference to NIO from registry (increment reference count) */ netio_desc_t *netio_acquire(char *name); /* Release an NIO (decrement reference count) */ int netio_release(char *name); /* Delete a NetIO descriptor */ int netio_delete(char *name); /* Delete all NetIO descriptors */ int netio_delete_all(void); /* Save the configuration of a NetIO descriptor */ void netio_save_config(netio_desc_t *nio,FILE *fd); /* Save configurations of all NetIO descriptors */ void netio_save_config_all(FILE *fd); /* Send a packet through a NetIO descriptor */ ssize_t netio_send(netio_desc_t *nio,void *pkt,size_t len); /* Receive a packet through a NetIO descriptor */ ssize_t netio_recv(netio_desc_t *nio,void *pkt,size_t max_len); /* Get a NetIO FD */ int netio_get_fd(netio_desc_t *nio); /* Reset NIO statistics */ void netio_reset_stats(netio_desc_t *nio); /* Indicate if a NetIO can transmit a packet */ int netio_can_transmit(netio_desc_t *nio); /* Update bandwidth counter */ void netio_update_bw_stat(netio_desc_t *nio,m_uint64_t bytes); /* Reset NIO bandwidth counter */ void netio_clear_bw_stat(netio_desc_t *nio); /* Set the bandwidth constraint */ void netio_set_bandwidth(netio_desc_t *nio,u_int bandwidth); /* Enable a RX listener */ int netio_rxl_enable(netio_desc_t *nio); /* Add an RX listener in the listener list */ int netio_rxl_add(netio_desc_t *nio,netio_rx_handler_t rx_handler, void *arg1,void *arg2); /* Remove a NIO from the listener list */ int netio_rxl_remove(netio_desc_t *nio); /* Initialize the RXL thread */ int netio_rxl_init(void); #endif dynamips-0.2.14/common/net_io_bridge.c000066400000000000000000000213561241034141600176520ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * NetIO bridges. */ #include #include #include #include #include #include #include #include #include #include "utils.h" #include "registry.h" #include "net_io.h" #include "net_io_bridge.h" #define PKT_MAX_SIZE 2048 /* Receive a packet */ static int netio_bridge_recv_pkt(netio_desc_t *nio,u_char *pkt,ssize_t pkt_len, netio_bridge_t *t) { int i; NETIO_BRIDGE_LOCK(t); for(i=0;inio[i] != NULL) && (t->nio[i] != nio)) netio_send(t->nio[i],pkt,pkt_len); NETIO_BRIDGE_UNLOCK(t); return(0); } /* Acquire a reference to NetIO bridge from the registry (inc ref count) */ netio_desc_t *netio_bridge_acquire(char *name) { return(registry_find(name,OBJ_TYPE_NIO_BRIDGE)); } /* Release a NetIO bridge (decrement reference count) */ int netio_bridge_release(char *name) { return(registry_unref(name,OBJ_TYPE_NIO_BRIDGE)); } /* Create a virtual bridge */ netio_bridge_t *netio_bridge_create(char *name) { netio_bridge_t *t; /* Allocate a new bridge structure */ if (!(t = malloc(sizeof(*t)))) return NULL; memset(t,0,sizeof(*t)); pthread_mutex_init(&t->lock,NULL); if (!(t->name = strdup(name))) goto err_name; /* Record this object in registry */ if (registry_add(t->name,OBJ_TYPE_NIO_BRIDGE,t) == -1) { fprintf(stderr,"netio_bridge_create: unable to register bridge '%s'\n", name); goto err_reg; } return t; err_reg: free(t->name); err_name: free(t); return NULL; } /* Add a NetIO descriptor to a virtual bridge */ int netio_bridge_add_netio(netio_bridge_t *t,char *nio_name) { netio_desc_t *nio; int i; NETIO_BRIDGE_LOCK(t); /* Try to find a free slot in the NIO array */ for(i=0;inio[i] == NULL) break; /* No free slot found ... */ if (i == NETIO_BRIDGE_MAX_NIO) goto error; /* Acquire the NIO descriptor and increment its reference count */ if (!(nio = netio_acquire(nio_name))) goto error; t->nio[i] = nio; netio_rxl_add(nio,(netio_rx_handler_t)netio_bridge_recv_pkt,t,NULL); NETIO_BRIDGE_UNLOCK(t); return(0); error: NETIO_BRIDGE_UNLOCK(t); return(-1); } /* Free resources used by a NIO in a bridge */ static void netio_bridge_free_nio(netio_desc_t *nio) { netio_rxl_remove(nio); netio_release(nio->name); } /* Remove a NetIO descriptor from a virtual bridge */ int netio_bridge_remove_netio(netio_bridge_t *t,char *nio_name) { netio_desc_t *nio; int i; NETIO_BRIDGE_LOCK(t); if (!(nio = registry_exists(nio_name,OBJ_TYPE_NIO))) goto error; /* Try to find the NIO in the NIO array */ for(i=0;inio[i] == nio) break; if (i == NETIO_BRIDGE_MAX_NIO) goto error; /* Remove the NIO from the RX multiplexer */ netio_bridge_free_nio(t->nio[i]); t->nio[i] = NULL; NETIO_BRIDGE_UNLOCK(t); return(0); error: NETIO_BRIDGE_UNLOCK(t); return(-1); } /* Save the configuration of a bridge */ void netio_bridge_save_config(netio_bridge_t *t,FILE *fd) { int i; fprintf(fd,"nio_bridge create %s\n",t->name); for(i=0;iname,t->nio[i]->name); fprintf(fd,"\n"); } /* Save configurations of all NIO bridges */ static void netio_bridge_reg_save_config(registry_entry_t *entry, void *opt,int *err) { netio_bridge_save_config((netio_bridge_t *)entry->data,(FILE *)opt); } void netio_bridge_save_config_all(FILE *fd) { registry_foreach_type(OBJ_TYPE_NIO_BRIDGE,netio_bridge_reg_save_config, fd,NULL); } /* Free resources used by a NIO bridge */ static int netio_bridge_free(void *data,void *arg) { netio_bridge_t *t = data; int i; NETIO_BRIDGE_LOCK(t); for(i=0;inio[i]) continue; netio_bridge_free_nio(t->nio[i]); } NETIO_BRIDGE_UNLOCK(t); free(t->name); free(t); return(TRUE); } /* Delete a virtual bridge */ int netio_bridge_delete(char *name) { return(registry_delete_if_unused(name,OBJ_TYPE_NIO_BRIDGE, netio_bridge_free,NULL)); } /* Delete all virtual bridges */ int netio_bridge_delete_all(void) { return(registry_delete_type(OBJ_TYPE_NIO_BRIDGE,netio_bridge_free,NULL)); } /* Create a new interface */ static int netio_bridge_cfg_create_if(netio_bridge_t *t, char **tokens,int count) { netio_desc_t *nio = NULL; int nio_type; nio_type = netio_get_type(tokens[1]); switch(nio_type) { case NETIO_TYPE_UNIX: if (count != 4) { fprintf(stderr,"NETIO_BRIDGE: invalid number of arguments " "for UNIX NIO\n"); break; } nio = netio_desc_create_unix(tokens[0],tokens[2],tokens[3]); break; case NETIO_TYPE_TAP: if (count != 3) { fprintf(stderr,"NETIO_BRIDGE: invalid number of arguments " "for TAP NIO\n"); break; } nio = netio_desc_create_tap(tokens[0],tokens[2]); break; case NETIO_TYPE_UDP: if (count != 5) { fprintf(stderr,"NETIO_BRIDGE: invalid number of arguments " "for UDP NIO\n"); break; } nio = netio_desc_create_udp(tokens[0],atoi(tokens[2]), tokens[3],atoi(tokens[4])); break; case NETIO_TYPE_TCP_CLI: if (count != 4) { fprintf(stderr,"NETIO_BRIDGE: invalid number of arguments " "for TCP CLI NIO\n"); break; } nio = netio_desc_create_tcp_cli(tokens[0],tokens[2],tokens[3]); break; case NETIO_TYPE_TCP_SER: if (count != 3) { fprintf(stderr,"NETIO_BRIDGE: invalid number of arguments " "for TCP SER NIO\n"); break; } nio = netio_desc_create_tcp_ser(tokens[0],tokens[2]); break; #ifdef GEN_ETH case NETIO_TYPE_GEN_ETH: if (count != 3) { fprintf(stderr,"NETIO_BRIDGE: invalid number of arguments " "for Generic Ethernet NIO\n"); break; } nio = netio_desc_create_geneth(tokens[0],tokens[2]); break; #endif #ifdef LINUX_ETH case NETIO_TYPE_LINUX_ETH: if (count != 3) { fprintf(stderr,"NETIO_BRIDGE: invalid number of arguments " "for Linux Ethernet NIO\n"); break; } nio = netio_desc_create_lnxeth(tokens[0],tokens[2]); break; #endif default: fprintf(stderr,"NETIO_BRIDGE: unknown/invalid NETIO type '%s'\n", tokens[1]); } if (!nio) { fprintf(stderr,"NETIO_BRIDGE: unable to create NETIO descriptor\n"); return(-1); } if (netio_bridge_add_netio(t,tokens[0]) == -1) { fprintf(stderr,"NETIO_BRIDGE: unable to add NETIO descriptor.\n"); netio_release(nio->name); return(-1); } netio_release(nio->name); return(0); } #define NETIO_BRIDGE_MAX_TOKENS 16 /* Handle a configuration line */ static int netio_bridge_handle_cfg_line(netio_bridge_t *t,char *str) { char *tokens[NETIO_BRIDGE_MAX_TOKENS]; int count; if ((count = m_strsplit(str,':',tokens,NETIO_BRIDGE_MAX_TOKENS)) <= 2) return(-1); return(netio_bridge_cfg_create_if(t,tokens,count)); } /* Read a configuration file */ static int netio_bridge_read_cfg_file(netio_bridge_t *t,char *filename) { char buffer[1024],*ptr; FILE *fd; if (!(fd = fopen(filename,"r"))) { perror("fopen"); return(-1); } while(!feof(fd)) { if (!fgets(buffer,sizeof(buffer),fd)) break; /* skip comments and end of line */ if ((ptr = strpbrk(buffer,"#\r\n")) != NULL) *ptr = 0; /* analyze non-empty lines */ if (strchr(buffer,':')) netio_bridge_handle_cfg_line(t,buffer); } fclose(fd); return(0); } /* Start a virtual bridge */ int netio_bridge_start(char *filename) { netio_bridge_t *t; if (!(t = netio_bridge_create("default"))) { fprintf(stderr,"NETIO_BRIDGE: unable to create virtual fabric table.\n"); return(-1); } if (netio_bridge_read_cfg_file(t,filename) == -1) { fprintf(stderr,"NETIO_BRIDGE: unable to parse configuration file.\n"); return(-1); } netio_bridge_release("default"); return(0); } dynamips-0.2.14/common/net_io_bridge.h000066400000000000000000000027211241034141600176520ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * NetIO bridge. */ #ifndef __NET_IO_BRIDGE_H__ #define __NET_IO_BRIDGE_H__ #include #include "utils.h" #include "net_io.h" #define NETIO_BRIDGE_MAX_NIO 32 typedef struct netio_bridge netio_bridge_t; struct netio_bridge { char *name; pthread_mutex_t lock; netio_desc_t *nio[NETIO_BRIDGE_MAX_NIO]; }; #define NETIO_BRIDGE_LOCK(t) pthread_mutex_lock(&(t)->lock) #define NETIO_BRIDGE_UNLOCK(t) pthread_mutex_unlock(&(t)->lock) /* Acquire a reference to NetIO bridge from the registry (inc ref count) */ netio_desc_t *netio_bridge_acquire(char *name); /* Release a NetIO bridge (decrement reference count) */ int netio_bridge_release(char *name); /* Create a virtual bridge */ netio_bridge_t *netio_bridge_create(char *name); /* Add a NetIO descriptor to a virtual bridge */ int netio_bridge_add_netio(netio_bridge_t *t,char *nio_name); /* Remove a NetIO descriptor from a virtual bridge */ int netio_bridge_remove_netio(netio_bridge_t *t,char *nio_name); /* Save the configuration of a brdige */ void netio_bridge_save_config(netio_bridge_t *t,FILE *fd); /* Save configurations of all NIO bridges */ void netio_bridge_save_config_all(FILE *fd); /* Delete a virtual bridge */ int netio_bridge_delete(char *name); /* Delete all virtual bridges */ int netio_bridge_delete_all(void); /* Start a virtual bridge */ int netio_bridge_start(char *filename); #endif dynamips-0.2.14/common/net_io_filter.c000066400000000000000000000174241241034141600177040ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * NetIO Filtering. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef GEN_ETH #include #endif #include "registry.h" #include "net.h" #include "net_io.h" #include "net_io_filter.h" /* Filter list */ static netio_pktfilter_t *pf_list = NULL; /* Find a filter */ netio_pktfilter_t *netio_filter_find(char *name) { netio_pktfilter_t *pf; for(pf=pf_list;pf;pf=pf->next) if (!strcmp(pf->name,name)) return pf; return NULL; } /* Add a new filter */ int netio_filter_add(netio_pktfilter_t *pf) { if (netio_filter_find(pf->name) != NULL) return(-1); pf->next = pf_list; pf_list = pf; return(0); } /* Bind a filter to a NIO */ int netio_filter_bind(netio_desc_t *nio,int direction,char *pf_name) { netio_pktfilter_t *pf; if (!(pf = netio_filter_find(pf_name))) return(-1); if (direction == NETIO_FILTER_DIR_RX) { nio->rx_filter_data = NULL; nio->rx_filter = pf; } else if (direction == NETIO_FILTER_DIR_TX) { nio->tx_filter_data = NULL; nio->tx_filter = pf; } else { nio->both_filter_data = NULL; nio->both_filter = pf; } return(0); } /* Unbind a filter from a NIO */ int netio_filter_unbind(netio_desc_t *nio,int direction) { netio_pktfilter_t *pf; void **opt; if (direction == NETIO_FILTER_DIR_RX) { opt = &nio->rx_filter_data; pf = nio->rx_filter; } else if (direction == NETIO_FILTER_DIR_TX) { opt = &nio->tx_filter_data; pf = nio->tx_filter; } else { opt = &nio->both_filter_data; pf = nio->both_filter; } if (!pf) return(-1); pf->free(nio,opt); return(0); } /* Setup a filter */ int netio_filter_setup(netio_desc_t *nio,int direction,int argc,char *argv[]) { netio_pktfilter_t *pf; void **opt; if (direction == NETIO_FILTER_DIR_RX) { opt = &nio->rx_filter_data; pf = nio->rx_filter; } else if (direction == NETIO_FILTER_DIR_TX) { opt = &nio->tx_filter_data; pf = nio->tx_filter; } else { opt = &nio->both_filter_data; pf = nio->both_filter; } if (!pf) return(-1); return(pf->setup(nio,opt,argc,argv)); } /* ======================================================================== */ /* Packet Capture ("capture") */ /* GFA */ /* ======================================================================== */ #ifdef GEN_ETH struct netio_filter_capture { pcap_t *desc; pcap_dumper_t *dumper; pthread_mutex_t lock; }; /* Free resources used by filter */ static void pf_capture_free(netio_desc_t *nio,void **opt) { struct netio_filter_capture *c = *opt; if (c != NULL) { printf("NIO %s: ending packet capture.\n",nio->name); /* Close dumper */ if (c->dumper) pcap_dump_close(c->dumper); /* Close PCAP descriptor */ if (c->desc) pcap_close(c->desc); pthread_mutex_destroy(&c->lock); free(c); *opt = NULL; } } /* Setup filter resources */ static int pf_capture_setup(netio_desc_t *nio,void **opt, int argc,char *argv[]) { struct netio_filter_capture *c; int link_type; /* We must have a link type and a filename */ if (argc != 2) return(-1); /* Free resources if something has already been done */ pf_capture_free(nio,opt); /* Allocate structure to hold PCAP info */ if (!(c = malloc(sizeof(*c)))) return(-1); if (pthread_mutex_init(&c->lock,NULL)) { fprintf(stderr,"NIO %s: pthread_mutex_init failure (file %s)\n", nio->name,argv[0]); goto pcap_lock_err; } if ((link_type = pcap_datalink_name_to_val(argv[0])) == -1) { fprintf(stderr,"NIO %s: unknown link type %s, assuming Ethernet.\n", nio->name,argv[0]); link_type = DLT_EN10MB; } /* Open a dead pcap descriptor */ if (!(c->desc = pcap_open_dead(link_type,65535))) { fprintf(stderr,"NIO %s: pcap_open_dead failure\n",nio->name); goto pcap_open_err; } /* Open the output file */ if (!(c->dumper = pcap_dump_open(c->desc,argv[1]))) { fprintf(stderr,"NIO %s: pcap_dump_open failure (file %s)\n", nio->name,argv[0]); goto pcap_dump_err; } printf("NIO %s: capturing to file '%s'\n",nio->name,argv[1]); *opt = c; return(0); pcap_dump_err: pcap_close(c->desc); pcap_open_err: pthread_mutex_destroy(&c->lock); pcap_lock_err: free(c); return(-1); } /* Packet handler: write packets to a file in CAP format */ static int pf_capture_pkt_handler(netio_desc_t *nio,void *pkt,size_t len, void *opt) { struct netio_filter_capture *c = opt; struct pcap_pkthdr pkt_hdr; if (c != NULL) { gettimeofday(&pkt_hdr.ts,0); pkt_hdr.caplen = m_min(len, (u_int)pcap_snapshot(c->desc)); pkt_hdr.len = len; /* thread safe dump */ pthread_mutex_lock(&c->lock); pcap_dump((u_char *)c->dumper,&pkt_hdr,pkt); pcap_dump_flush(c->dumper); pthread_mutex_unlock(&c->lock); } return(NETIO_FILTER_ACTION_PASS); } /* Packet capture */ static netio_pktfilter_t pf_capture_def = { "capture", pf_capture_setup, pf_capture_free, pf_capture_pkt_handler, NULL, }; #endif /* ======================================================================== */ /* Frequency Dropping ("freq_drop"). */ /* ======================================================================== */ struct pf_freqdrop_data { int frequency; int current; }; /* Setup filter ressources */ static int pf_freqdrop_setup(netio_desc_t *nio,void **opt, int argc,char *argv[]) { struct pf_freqdrop_data *data = *opt; if (argc != 1) return(-1); if (!data) { if (!(data = malloc(sizeof(*data)))) return(-1); *opt = data; } data->current = 0; data->frequency = atoi(argv[0]); return(0); } /* Free ressources used by filter */ static void pf_freqdrop_free(netio_desc_t *nio,void **opt) { if (*opt) free(*opt); *opt = NULL; } /* Packet handler: drop 1 out of n packets */ static int pf_freqdrop_pkt_handler(netio_desc_t *nio,void *pkt,size_t len, void *opt) { struct pf_freqdrop_data *data = opt; if (data != NULL) { switch(data->frequency) { case -1: return(NETIO_FILTER_ACTION_DROP); case 0: return(NETIO_FILTER_ACTION_PASS); default: data->current++; if (data->current == data->frequency) { data->current = 0; return(NETIO_FILTER_ACTION_DROP); } } } return(NETIO_FILTER_ACTION_PASS); } /* Packet dropping at 1/n frequency */ static netio_pktfilter_t pf_freqdrop_def = { "freq_drop", pf_freqdrop_setup, pf_freqdrop_free, pf_freqdrop_pkt_handler, NULL, }; /* ======================================================================== */ /* Initialization of packet filters. */ /* ======================================================================== */ void netio_filter_load_all(void) { netio_filter_add(&pf_freqdrop_def); #ifdef GEN_ETH netio_filter_add(&pf_capture_def); #endif } dynamips-0.2.14/common/net_io_filter.h000066400000000000000000000016611241034141600177050ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * NetIO Packet Filters. */ #ifndef __NET_IO_FILTER_H__ #define __NET_IO_FILTER_H__ #include #include #include #include #include "utils.h" #include "net_io.h" /* Directions for filters */ #define NETIO_FILTER_DIR_RX 0 #define NETIO_FILTER_DIR_TX 1 #define NETIO_FILTER_DIR_BOTH 2 /* Find a filter */ netio_pktfilter_t *netio_filter_find(char *name); /* Add a new filter */ int netio_filter_add(netio_pktfilter_t *pf); /* Bind a filter to a NIO */ int netio_filter_bind(netio_desc_t *nio,int direction,char *pf_name); /* Unbind a filter from a NIO */ int netio_filter_unbind(netio_desc_t *nio,int direction); /* Setup a filter */ int netio_filter_setup(netio_desc_t *nio,int direction,int argc,char *argv[]); /* Load all packet filters */ void netio_filter_load_all(void); #endif dynamips-0.2.14/common/nmc93cX6.c000066400000000000000000000145421241034141600163720ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * NMC93C46/NMC93C56 Serial EEPROM. */ #include #include #include #include #include "nmc93cX6.h" #define DEBUG_EEPROM 0 /* Internal states */ enum { EEPROM_STATE_INACTIVE = 0, EEPROM_STATE_WAIT_CMD, EEPROM_STATE_DATAOUT, }; /* Get command length for the specified group */ static u_int nmc94cX6_get_cmd_len(struct nmc93cX6_group *g) { switch(g->eeprom_type) { case EEPROM_TYPE_NMC93C46: return(NMC93C46_CMD_BITLEN); case EEPROM_TYPE_NMC93C56: return(NMC93C56_CMD_BITLEN); default: return(0); } } /* Extract EEPROM data address */ static u_int nmc94cX6_get_addr(struct nmc93cX6_group *g,u_int cmd) { switch(g->eeprom_type) { case EEPROM_TYPE_NMC93C46: return((cmd >> 3) & 0x3f); case EEPROM_TYPE_NMC93C56: return(m_reverse_u8((cmd >> 3) & 0xff)); default: return(0); } } /* Check chip select */ static void nmc93cX6_check_cs(struct nmc93cX6_group *g,u_int old,u_int new) { int i,res; for(i=0;inr_eeprom;i++) { if (g->dout_status == EEPROM_DOUT_HIGH) g->state[i].dataout_val = 1; if (g->debug) { printf("EEPROM %s(%d): check_cs: check_bit(old,new,select_bit) " "[%8.8x, %8.8x, %d (mask = %8.8x)] = %d\n", g->description, i, old, new, g->def[i]->select_bit, 1 << g->def[i]->select_bit, check_bit(old,new,g->def[i]->select_bit)); } if ((res = check_bit(old,new,g->def[i]->select_bit)) != 0) { g->state[i].cmd_len = 0; /* no bit for command sent now */ g->state[i].cmd_val = 0; //g->state[i].dataout_val = 1; if (res == 2) g->state[i].state = EEPROM_STATE_WAIT_CMD; else g->state[i].state = EEPROM_STATE_INACTIVE; } } } /* Check clock set for a specific group */ static void nmc93cX6_check_clk_group(struct nmc93cX6_group *g,int group_id, u_int old,u_int new) { struct cisco_eeprom *eeprom; u_int cmd,op,addr,pos; u_int clk_bit, din_bit; u_int cmd_len; clk_bit = g->def[group_id]->clock_bit; din_bit = g->def[group_id]->din_bit; if (g->debug) { printf("EEPROM %s(%d): check_clk: check_bit(old,new,select_bit) " "[%8.8x, %8.8x, %d (mask = %8.8x)] = %d\n", g->description, group_id, old, new, clk_bit, 1 << clk_bit, check_bit(old,new,clk_bit)); } /* CLK bit set ? */ if (check_bit(old,new,clk_bit) != 2) return; switch(g->state[group_id].state) { case EEPROM_STATE_WAIT_CMD: /* The first bit must be set to "1" */ if ((g->state[group_id].cmd_len == 0) && !(new & (1 << din_bit))) break; /* Read DATAIN bit */ if (new & (1 << din_bit)) g->state[group_id].cmd_val |= (1 << g->state[group_id].cmd_len); g->state[group_id].cmd_len++; cmd_len = nmc94cX6_get_cmd_len(g); /* Command is complete ? */ if (g->state[group_id].cmd_len == cmd_len) { #if DEBUG_EEPROM printf("nmc93cX6: %s(%d): command = %x\n", g->description,group_id,g->state[group_id].cmd_val); #endif g->state[group_id].cmd_len = 0; /* we have the command! extract the opcode */ cmd = g->state[group_id].cmd_val; op = cmd & 0x7; switch(op) { case NMC93CX6_CMD_READ: g->state[group_id].state = EEPROM_STATE_DATAOUT; g->state[group_id].dataout_pos = 0; break; #if DEBUG_EEPROM default: printf("nmc93cX6: unhandled opcode %d\n",op); #endif } } break; case EEPROM_STATE_DATAOUT: /* * user want to read data. we read 16-bits. * extract address (6/9 bits) from command. */ cmd = g->state[group_id].cmd_val; addr = nmc94cX6_get_addr(g,cmd); #if DEBUG_EEPROM if (g->state[group_id].dataout_pos == 0) { printf("nmc93cX6: %s(%d): " "read addr=%x (%d), val=%4.4x [eeprom=%p]\n", g->description,group_id,addr,addr, g->state[group_id].cmd_val, g->eeprom[group_id]); } #endif pos = g->state[group_id].dataout_pos++; if (g->reverse_data) pos = 15 - pos; eeprom = g->eeprom[group_id]; if (eeprom && eeprom->data && (addr < eeprom->len)) { g->state[group_id].dataout_val = eeprom->data[addr] & (1 << pos); } else { /* access out of bounds */ g->state[group_id].dataout_val = (1 << pos); } if (g->state[group_id].dataout_pos == NMC93CX6_CMD_DATALEN) { g->state[group_id].state = EEPROM_STATE_INACTIVE; g->state[group_id].dataout_pos = 0; } break; #if DEBUG_EEPROM default: printf("nmc93cX6: unhandled state %d\n",g->state[group_id].state); #endif } } /* Check clock set for all group */ void nmc93cX6_check_clk(struct nmc93cX6_group *g,u_int old,u_int new) { int i; for(i=0;inr_eeprom;i++) nmc93cX6_check_clk_group(g,i,old,new); } /* Handle write */ void nmc93cX6_write(struct nmc93cX6_group *g,u_int data) { u_int new = data, old = g->eeprom_reg; nmc93cX6_check_cs(g,old,new); nmc93cX6_check_clk(g,old,new); g->eeprom_reg = new; } /* Returns the TRUE if the EEPROM is active */ u_int nmc93cX6_is_active(struct nmc93cX6_group *g,u_int group_id) { return(g->eeprom_reg & (1 << g->def[group_id]->select_bit)); } /* Returns the DOUT bit value */ u_int nmc93cX6_get_dout(struct nmc93cX6_group *g,u_int group_id) { if (g->state[group_id].dataout_val) return(1 << g->def[group_id]->dout_bit); else return(0); } /* Handle read */ u_int nmc93cX6_read(struct nmc93cX6_group *g) { u_int res; int i; res = g->eeprom_reg; for(i=0;inr_eeprom;i++) { if (!(g->eeprom_reg & (1 << g->def[i]->select_bit))) continue; if (g->state[i].dataout_val) res |= 1 << g->def[i]->dout_bit; else res &= ~(1 << g->def[i]->dout_bit); } return(res); } dynamips-0.2.14/common/nmc93cX6.h000066400000000000000000000045271241034141600164010ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot. * * NMC93C46/NMC93C56 Serial EEPROM. */ #ifndef __NMC93CX6_H__ #define __NMC93CX6_H__ #include #include "utils.h" #include "cisco_eeprom.h" /* EEPROM types */ enum { EEPROM_TYPE_NMC93C46, EEPROM_TYPE_NMC93C56, }; /* EEPROM data bit order */ enum { EEPROM_DORD_NORMAL = 0, EEPROM_DORD_REVERSED, }; /* EEPROM debugging */ enum { EEPROM_DEBUG_DISABLED = 0, EEPROM_DEBUG_ENABLED, }; /* EEPROM DOUT default status */ enum { EEPROM_DOUT_HIGH = 0, EEPROM_DOUT_KEEP, }; /* 8 groups with 4 differents bits (clock,select,data_in,data_out) */ #define NMC93CX6_MAX_EEPROM_PER_GROUP 16 /* NMC93C46 EEPROM command bit length */ #define NMC93C46_CMD_BITLEN 9 /* NMC93C56 EEPROM command bit length */ #define NMC93C56_CMD_BITLEN 11 /* NMC93C46 EEPROM data bit length */ #define NMC93CX6_CMD_DATALEN 16 /* NMC93C46 EEPROM commands: SB (1) OP(2) Address(6/9) */ #define NMC93CX6_CMD_CONTROL (0x1 | 0x0) #define NMC93CX6_CMD_WRDS (0x1 | 0x0 | 0x00) #define NMC93CX6_CMD_ERASE_ALL (0x1 | 0x0 | 0x08) #define NMC93CX6_CMD_WRITE_ALL (0x1 | 0x0 | 0x10) #define NMC93CX6_CMD_WREN (0x1 | 0x0 | 0x18) #define NMC93CX6_CMD_READ (0x1 | 0x2) #define NMC93CX6_CMD_WRITE (0x1 | 0x4) #define NMC93CX6_CMD_ERASE (0x1 | 0x6) struct nmc93cX6_eeprom_def { u_int clock_bit; u_int select_bit; u_int din_bit; u_int dout_bit; }; struct nmc93cX6_eeprom_state { u_int cmd_len; u_int cmd_val; u_int state; u_int dataout_pos; u_int dataout_val; }; struct nmc93cX6_group { u_int eeprom_type; u_int nr_eeprom; u_int eeprom_reg; u_int reverse_data; u_int dout_status; int debug; char *description; const struct nmc93cX6_eeprom_def *def[NMC93CX6_MAX_EEPROM_PER_GROUP]; struct nmc93cX6_eeprom_state state[NMC93CX6_MAX_EEPROM_PER_GROUP]; struct cisco_eeprom *eeprom[NMC93CX6_MAX_EEPROM_PER_GROUP]; }; /* Handle write */ void nmc93cX6_write(struct nmc93cX6_group *g,u_int data); /* Returns the TRUE if the EEPROM is active */ u_int nmc93cX6_is_active(struct nmc93cX6_group *g,u_int group_id); /* Returns the DOUT bit value */ u_int nmc93cX6_get_dout(struct nmc93cX6_group *g,u_int group_id); /* Handle read */ u_int nmc93cX6_read(struct nmc93cX6_group *p); #endif /* __NMC93CX6_H__ */ dynamips-0.2.14/common/nvram_export.c000066400000000000000000000146461241034141600176110ustar00rootroot00000000000000/* * Cisco C7200 Simulation Platform. * Copyright (c) 2013 Flávio J. Saraiva * * Extract IOS configuration from a NVRAM file (standalone tool) */ #include #include #include #include #include "cpu.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_c7200.h" #include "dev_c3745.h" #include "dev_c3725.h" #include "dev_c3600.h" #include "dev_c2691.h" #include "dev_c2600.h" #include "dev_c1700.h" #include "dev_c6msfc1.h" #include "fs_nvram.h" struct nvram_format { const char *name; const char *rom_res_0x200; size_t offset; size_t size; m_uint32_t addr; u_int format; } nvram_formats[] = { {"c1700", "C1700", C1700_NVRAM_ROM_RES_SIZE, 0, 0, FS_NVRAM_FORMAT_DEFAULT}, {"c2600", "C2600", C2600_NVRAM_ROM_RES_SIZE*4, 0, 0, FS_NVRAM_FORMAT_SCALE_4}, {"c3600", "3600", C3600_NVRAM_ROM_RES_SIZE, 0, 0, FS_NVRAM_FORMAT_DEFAULT}, {"c7200", "7200", C7200_NVRAM_ROM_RES_SIZE, 0, C7200_NVRAM_ADDR + C7200_NVRAM_ROM_RES_SIZE, FS_NVRAM_FORMAT_ABSOLUTE}, {"c7200-npe-g2", "7200", C7200_NVRAM_ROM_RES_SIZE, 0, C7200_G2_NVRAM_ADDR + C7200_NVRAM_ROM_RES_SIZE, FS_NVRAM_FORMAT_ABSOLUTE}, {"c6msfc1", NULL, C6MSFC1_NVRAM_ROM_RES_SIZE, 0, C6MSFC1_NVRAM_ADDR + C6MSFC1_NVRAM_ROM_RES_SIZE, FS_NVRAM_FORMAT_ABSOLUTE_C6}, {"c2691", NULL, C2691_NVRAM_OFFSET, C2691_NVRAM_SIZE, 0, FS_NVRAM_FORMAT_WITH_BACKUP}, {"c3725", NULL, C3725_NVRAM_OFFSET, C3725_NVRAM_SIZE, 0, FS_NVRAM_FORMAT_WITH_BACKUP}, // XXX same as c2691 {"c3745", NULL, C3745_NVRAM_OFFSET, C3745_NVRAM_SIZE, 0, FS_NVRAM_FORMAT_WITH_BACKUP}, {"c7200-npe-g1", NULL, C7200_NVRAM_ROM_RES_SIZE, 0, C7200_G1_NVRAM_ADDR + C7200_NVRAM_ROM_RES_SIZE, FS_NVRAM_FORMAT_ABSOLUTE}, // XXX didn't find working image {NULL, NULL, 0, 0, 0, 0} }; /** Read file data. */ int read_file(const char *filename, u_char **data, size_t *data_len) { FILE *fd; long len; // open fd = fopen(filename,"rb"); if (fd == NULL) { return(-1); } // len fseek(fd, 0, SEEK_END); len = ftell(fd); fseek(fd, 0, SEEK_SET); if (len < 0 || ferror(fd)) { fclose(fd); return(-1); } if (data_len) { *data_len = (size_t)len; } // data if (data) { *data = (u_char *)malloc((size_t)len); if (fread(*data, (size_t)len, 1, fd) != 1) { free(*data); *data = NULL; fclose(fd); return(-1); } } // close fclose(fd); return(0); } /** Write file data. */ int write_file(const char *filename, u_char *data, size_t len) { FILE *fp; fp = fopen(filename,"wb+"); if (fp == NULL) { return(-1); } if (fwrite(data, len, 1, fp) != 1) { fclose(fp); return(-1); } fclose(fp); return(0); } /** Export configuration from NVRAM. */ int nvram_export_config(const char *nvram_filename, const char *startup_filename, const char *private_filename) { u_char *data = NULL; u_char *startup_config = NULL; u_char *private_config = NULL; size_t data_len = 0; size_t startup_len = 0; size_t private_len; fs_nvram_t *fs; size_t len; struct nvram_format *fmt; // read nvram printf("Reading %s...\n", nvram_filename); if (read_file(nvram_filename, &data, &data_len)) { perror(nvram_filename); return(-1); } // try each format for (fmt = &nvram_formats[0]; ; fmt++) { if (fmt->name == NULL) { fprintf(stderr,"NVRAM not found\n"); return(-1); } if (fmt->rom_res_0x200) { len = strlen(fmt->rom_res_0x200); if (data_len < 0x200 + len || memcmp(data + 0x200, fmt->rom_res_0x200, len)) { continue; // must match } } if (fmt->size > 0) { if (data_len < fmt->offset + fmt->size) { continue; // must fit } len = fmt->size; } else { if (data_len < fmt->offset) { continue; // must fit } len = data_len - fmt->offset; } fs = fs_nvram_open(data + fmt->offset, len, fmt->addr, fmt->format); if (fs == NULL) { continue; // filesystem not found } if (fs_nvram_verify(fs, FS_NVRAM_VERIFY_ALL) || fs_nvram_read_config(fs, &startup_config, &startup_len, &private_config, &private_len)) { fs_nvram_close(fs); fs = NULL; continue; // filesystem error } printf("Found NVRAM format %s\n", fmt->name); fs_nvram_close(fs); fs = NULL; break; } // write config if (startup_filename) { printf("Writing startup-config to %s...\n", startup_filename); if (write_file(startup_filename, startup_config, startup_len)) { perror(startup_filename); return(-1); } } if (private_filename) { printf("Writing private-config to %s...\n", private_filename); if (write_file(private_filename, private_config, private_len)) { perror(private_filename); return(-1); } } // cleanup if (startup_config) { free(startup_config); startup_config = NULL; } if (private_config) { free(private_config); private_config = NULL; } return(0); } int main(int argc,char *argv[]) { const char *nvram_filename; const char *startup_filename; const char *private_filename = NULL; struct nvram_format *fmt; printf("Cisco NVRAM configuration export.\n"); printf("Copyright (c) 2013 Flávio J. Saraiva.\n\n"); if (argc < 3 || argc > 4) { fprintf(stderr,"Usage: %s nvram_file config_file [private_file]\n",argv[0]); fprintf(stderr,"\n"); fprintf(stderr,"This tools extracts 'startup-config' and 'private-config' from NVRAM.\n"); fprintf(stderr," nvram_file - file that contains the NVRAM data\n"); fprintf(stderr," (on some platforms, NVRAM is simulated inside the ROM)\n"); fprintf(stderr," config_file - file for 'startup-config'\n"); fprintf(stderr," private_file - file for 'private-config' (optional)\n"); fprintf(stderr,"\n"); fprintf(stderr,"Supports:"); for (fmt = &nvram_formats[0]; fmt->name; fmt++) { fprintf(stderr," %s",fmt->name); } fprintf(stderr,"\n"); exit(EXIT_FAILURE); } nvram_filename = argv[1]; startup_filename = argv[2]; if (argc > 3) private_filename = argv[3]; if (nvram_export_config(nvram_filename,startup_filename,private_filename)) return(EXIT_FAILURE); printf("Done\n"); return(0); } dynamips-0.2.14/common/parser.c000066400000000000000000000211641241034141600163520ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Mini-parser. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "parser.h" #define TOKEN_MAX_SIZE 512 /* Character types */ enum { PARSER_CHAR_BLANK, PARSER_CHAR_NEWLINE, PARSER_CHAR_COMMENT, PARSER_CHAR_QUOTE, PARSER_CHAR_OTHER, }; /* Get a description given an error code */ char *parser_strerror(parser_context_t *ctx) { printf("error = %d\n",ctx->error); switch(ctx->error) { case 0: return "no error"; case PARSER_ERROR_NOMEM: return "insufficient memory"; case PARSER_ERROR_UNEXP_QUOTE: return "unexpected quote"; case PARSER_ERROR_UNEXP_EOL: return "unexpected end of line"; default: return "unknown error"; } } /* Dump a token list */ void parser_dump_tokens(parser_context_t *ctx) { parser_token_t *tok; for(tok=ctx->tok_head;tok;tok=tok->next) printf("\"%s\" ",tok->value); } /* Map a token list to an array */ char **parser_map_array(parser_context_t *ctx) { parser_token_t *tok; char **map; int i; if (ctx->tok_count <= 0) return NULL; if (!(map = calloc(ctx->tok_count,sizeof(char **)))) return NULL; for(i=0,tok=ctx->tok_head;(itok_count) && tok;i++,tok=tok->next) map[i] = tok->value; return map; } /* Add a character to temporary token (resize if necessary) */ static int tmp_token_add_char(parser_context_t *ctx,char c) { size_t new_size; char *new_str; if (!ctx->tmp_tok || (ctx->tmp_cur_len == (ctx->tmp_tot_len - 1))) { new_size = ctx->tmp_tot_len + TOKEN_MAX_SIZE; new_str = realloc(ctx->tmp_tok,new_size); if (!new_str) return(-1); ctx->tmp_tok = new_str; ctx->tmp_tot_len = new_size; } ctx->tmp_tok[ctx->tmp_cur_len++] = c; ctx->tmp_tok[ctx->tmp_cur_len] = 0; return(0); } /* Move current token to the active token list */ static int parser_move_tmp_token(parser_context_t *ctx) { parser_token_t *tok; /* no token ... */ if (!ctx->tmp_tok) return(0); if (!(tok = malloc(sizeof(*tok)))) return(-1); tok->value = ctx->tmp_tok; tok->next = NULL; /* add it to the token list */ if (ctx->tok_last != NULL) ctx->tok_last->next = tok; else ctx->tok_head = tok; ctx->tok_last = tok; ctx->tok_count++; /* start a new token */ ctx->tmp_tok = NULL; ctx->tmp_tot_len = ctx->tmp_cur_len = 0; return(0); } /* Initialize parser context */ void parser_context_init(parser_context_t *ctx) { ctx->tok_head = ctx->tok_last = NULL; ctx->tok_count = 0; ctx->tmp_tok = NULL; ctx->tmp_tot_len = ctx->tmp_cur_len = 0; ctx->state = PARSER_STATE_BLANK; ctx->error = 0; ctx->consumed_len = 0; } /* Free a token list */ void parser_free_tokens(parser_token_t *tok_list) { parser_token_t *t,*next; for(t=tok_list;t;t=next) { next = t->next; free(t->value); free(t); } } /* Free memory used by a parser context */ void parser_context_free(parser_context_t *ctx) { parser_free_tokens(ctx->tok_head); if (ctx->tmp_tok != NULL) free(ctx->tmp_tok); parser_context_init(ctx); } /* Determine the type of the input character */ static int parser_get_char_type(u_char c) { switch(c) { case '\n': case '\r': case 0: return(PARSER_CHAR_NEWLINE); case '\t': //case '\r': case ' ': return(PARSER_CHAR_BLANK); case '!': case '#': return(PARSER_CHAR_COMMENT); case '"': return(PARSER_CHAR_QUOTE); default: return(PARSER_CHAR_OTHER); } } /* Send a buffer to the tokenizer */ int parser_scan_buffer(parser_context_t *ctx,char *buf,size_t buf_size) { int i,type; u_char c; for(i=0;(istate != PARSER_STATE_DONE);i++) { ctx->consumed_len++; c = buf[i]; /* Determine character type */ type = parser_get_char_type(c); /* Basic finite state machine */ switch(ctx->state) { case PARSER_STATE_SKIP: if (type == PARSER_CHAR_NEWLINE) ctx->state = PARSER_STATE_DONE; /* Simply ignore character until we reach end of line */ break; case PARSER_STATE_BLANK: switch(type) { case PARSER_CHAR_BLANK: /* Eat space */ break; case PARSER_CHAR_COMMENT: ctx->state = PARSER_STATE_SKIP; break; case PARSER_CHAR_NEWLINE: ctx->state = PARSER_STATE_DONE; break; case PARSER_CHAR_QUOTE: ctx->state = PARSER_STATE_QUOTED_STRING; break; default: /* Begin a new string */ if (!tmp_token_add_char(ctx,c)) { ctx->state = PARSER_STATE_STRING; } else { ctx->state = PARSER_STATE_SKIP; ctx->error = PARSER_ERROR_NOMEM; } } break; case PARSER_STATE_STRING: switch(type) { case PARSER_CHAR_BLANK: if (!parser_move_tmp_token(ctx)) { ctx->state = PARSER_STATE_BLANK; } else { ctx->state = PARSER_STATE_SKIP; ctx->error = PARSER_ERROR_NOMEM; } break; case PARSER_CHAR_NEWLINE: if (parser_move_tmp_token(ctx) == -1) ctx->error = PARSER_ERROR_NOMEM; ctx->state = PARSER_STATE_DONE; break; case PARSER_CHAR_COMMENT: if (parser_move_tmp_token(ctx) == -1) ctx->error = PARSER_ERROR_NOMEM; ctx->state = PARSER_STATE_SKIP; break; case PARSER_CHAR_QUOTE: ctx->error = PARSER_ERROR_UNEXP_QUOTE; ctx->state = PARSER_STATE_SKIP; break; default: /* Add the character to the buffer */ if (tmp_token_add_char(ctx,c) == -1) { ctx->state = PARSER_STATE_SKIP; ctx->error = PARSER_ERROR_NOMEM; } } break; case PARSER_STATE_QUOTED_STRING: switch(type) { case PARSER_CHAR_NEWLINE: /* Unterminated string! */ ctx->error = PARSER_ERROR_UNEXP_EOL; ctx->state = PARSER_STATE_DONE; break; case PARSER_CHAR_QUOTE: if (!parser_move_tmp_token(ctx)) { ctx->state = PARSER_STATE_BLANK; } else { ctx->state = PARSER_STATE_SKIP; ctx->error = PARSER_ERROR_NOMEM; } break; default: /* Add the character to the buffer */ if (tmp_token_add_char(ctx,c) == -1) { ctx->state = PARSER_STATE_SKIP; ctx->error = PARSER_ERROR_NOMEM; } } break; } } return(ctx->state == PARSER_STATE_DONE); } /* Parser tests */ static char *parser_test_str[] = { "c7200 show_hardware R1", "c7200 show_hardware \"R1\"", " c7200 show_hardware \"R1\" ", "\"c7200\" \"show_hardware\" \"R1\"", "hypervisor set_working_dir \"C:\\Program Files\\Dynamips Test\"", "hypervisor # This is a comment set_working_dir \"C:\\Program Files\"", "\"c7200\" \"show_hardware\" \"R1", NULL, }; void parser_run_tests(void) { parser_context_t ctx; int i,res; for(i=0;parser_test_str[i];i++) { parser_context_init(&ctx); res = parser_scan_buffer(&ctx,parser_test_str[i], strlen(parser_test_str[i])+1); printf("\n%d: Test string: [%s] => res=%d, state=%d\n", i,parser_test_str[i],res,ctx.state); if ((res != 0) && (ctx.error == 0)) { if (ctx.tok_head) { printf("Tokens: "); parser_dump_tokens(&ctx); printf("\n"); } } parser_context_free(&ctx); } } dynamips-0.2.14/common/parser.h000066400000000000000000000031541241034141600163560ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __PARSER_H__ #define __PARSER_H__ #include /* Parser Errors */ enum { PARSER_ERROR_NOMEM = 1, PARSER_ERROR_UNEXP_QUOTE, /* Unexpected quote in a word */ PARSER_ERROR_UNEXP_EOL, /* Unexpected end of line */ }; /* Parser states */ enum { PARSER_STATE_DONE, PARSER_STATE_SKIP, PARSER_STATE_BLANK, PARSER_STATE_STRING, PARSER_STATE_QUOTED_STRING, }; /* Token */ typedef struct parser_token parser_token_t; struct parser_token { char *value; struct parser_token *next; }; /* Parser context */ typedef struct parser_context parser_context_t; struct parser_context { /* Token list */ parser_token_t *tok_head,*tok_last; int tok_count; /* Temporary token */ char *tmp_tok; size_t tmp_tot_len,tmp_cur_len; /* Parser state and error */ int state,error; /* Number of consumed chars */ size_t consumed_len; }; /* Get a description given an error code */ char *parser_strerror(parser_context_t *ctx); /* Dump a token list */ void parser_dump_tokens(parser_context_t *ctx); /* Map a token list to an array */ char **parser_map_array(parser_context_t *ctx); /* Initialize parser context */ void parser_context_init(parser_context_t *ctx); /* Free memory used by a parser context */ void parser_context_free(parser_context_t *ctx); /* Send a buffer to the tokenizer */ int parser_scan_buffer(parser_context_t *ctx,char *buf,size_t buf_size); /* Tokenize a string */ int parser_tokenize(char *str,struct parser_token **tokens,int *tok_count); #endif dynamips-0.2.14/common/pci_dev.c000066400000000000000000000353271241034141600164750ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * PCI devices. * * Very interesting docs: * http://www.science.unitn.it/~fiorella/guidelinux/tlk/node72.html * http://www.science.unitn.it/~fiorella/guidelinux/tlk/node76.html */ #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #define DEBUG_PCI 1 #define GET_PCI_ADDR(offset,mask) ((pci_bus->pci_addr >> offset) & mask) /* Trigger a PCI device IRQ */ void pci_dev_trigger_irq(vm_instance_t *vm,struct pci_device *dev) { if (dev->irq != -1) vm_set_irq(vm,dev->irq); } /* Clear a PCI device IRQ */ void pci_dev_clear_irq(vm_instance_t *vm,struct pci_device *dev) { if (dev->irq != -1) vm_clear_irq(vm,dev->irq); } /* Swapping function */ static inline m_uint32_t pci_swap(m_uint32_t val,int swap) { return((swap) ? swap32(val) : val); } /* PCI bus lookup */ struct pci_bus *pci_bus_lookup(struct pci_bus *pci_bus_root,int bus) { struct pci_bus *next_bus,*cur_bus = pci_bus_root; struct pci_bridge *bridge; while(cur_bus != NULL) { if (cur_bus->bus == bus) return cur_bus; /* Try busses behind PCI bridges */ next_bus = NULL; for(bridge=cur_bus->bridge_list;bridge;bridge=bridge->next) { /* * Specific case: final bridge with no checking of secondary * bus number. Dynamically programming. */ if (bridge->skip_bus_check) { pci_bridge_set_bus_info(bridge,cur_bus->bus,bus,bus); bridge->skip_bus_check = FALSE; return bridge->pci_bus; } if ((bus >= bridge->sec_bus) && (bus <= bridge->sub_bus)) { next_bus = bridge->pci_bus; break; } } cur_bus = next_bus; } return NULL; } /* PCI device local lookup */ struct pci_device *pci_dev_lookup_local(struct pci_bus *pci_bus, int device,int function) { struct pci_device *dev; for(dev=pci_bus->dev_list;dev;dev=dev->next) if ((dev->device == device) && (dev->function == function)) return dev; return NULL; } /* PCI Device lookup */ struct pci_device *pci_dev_lookup(struct pci_bus *pci_bus_root, int bus,int device,int function) { struct pci_bus *req_bus; /* Find, try to find the request bus */ if (!(req_bus = pci_bus_lookup(pci_bus_root,bus))) return NULL; /* Walk through devices present on this bus */ return pci_dev_lookup_local(req_bus,device,function); } /* Handle the address register access */ void pci_dev_addr_handler(cpu_gen_t *cpu,struct pci_bus *pci_bus, u_int op_type,int swap,m_uint64_t *data) { if (op_type == MTS_WRITE) pci_bus->pci_addr = pci_swap(*data,swap); else *data = pci_swap(pci_bus->pci_addr,swap); } /* * Handle the data register access. * * The address of requested register is first written at address 0xcf8 * (with pci_dev_addr_handler). * * The data is read/written at address 0xcfc. */ void pci_dev_data_handler(cpu_gen_t *cpu,struct pci_bus *pci_bus, u_int op_type,int swap,m_uint64_t *data) { struct pci_device *dev; int bus,device,function,reg; if (op_type == MTS_READ) *data = 0x0; /* * http://www.mega-tokyo.com/osfaq2/index.php/PciSectionOfPentiumVme * * 31 : Enable Bit * 30 - 24 : Reserved * 23 - 16 : Bus Number * 15 - 11 : Device Number * 10 - 8 : Function Number * 7 - 2 : Register Number * 1 - 0 : always 00 */ bus = GET_PCI_ADDR(16,0xff); device = GET_PCI_ADDR(11,0x1f); function = GET_PCI_ADDR(8,0x7); reg = GET_PCI_ADDR(0,0xff); /* Find the corresponding PCI device */ dev = pci_dev_lookup(pci_bus,bus,device,function); if (!dev) { if (op_type == MTS_READ) { cpu_log(cpu,"PCI","read request for unknown device at pc=0x%llx " "(bus=%d,device=%d,function=%d,reg=0x%2.2x).\n", cpu_get_pc(cpu), bus, device, function, reg); } else { cpu_log(cpu,"PCI","write request (data=0x%8.8x) for unknown device " "at pc=0x%llx (bus=%d,device=%d,function=%d,reg=0x%2.2x).\n", pci_swap(*data,swap), cpu_get_pc(cpu), bus, device, function, reg); } /* Returns an invalid device ID */ if ((op_type == MTS_READ) && (reg == PCI_REG_ID)) *data = 0xffffffff; } else { #if DEBUG_PCI if (op_type == MTS_READ) { cpu_log(cpu,"PCI","read request for device '%s' at pc=0x%llx: " "bus=%d,device=%d,function=%d,reg=0x%2.2x\n", dev->name, cpu_get_pc(cpu), bus, device, function, reg); } else { cpu_log(cpu,"PCI","write request (data=0x%8.8x) for device '%s' at pc=0x%llx: " "bus=%d,device=%d,function=%d,reg=0x%2.2x\n", pci_swap(*data,swap), dev->name, cpu_get_pc(cpu), bus, device, function, reg); } #endif if (op_type == MTS_WRITE) { if (dev->write_register != NULL) dev->write_register(cpu,dev,reg,pci_swap(*data,swap)); } else { if (reg == PCI_REG_ID) *data = pci_swap((dev->product_id << 16) | dev->vendor_id,swap); else { if (dev->read_register != NULL) *data = pci_swap(dev->read_register(cpu,dev,reg),swap); } } } } /* Add a PCI bridge */ struct pci_bridge *pci_bridge_add(struct pci_bus *pci_bus) { struct pci_bridge *bridge; if (!pci_bus) return NULL; if (!(bridge = malloc(sizeof(*bridge)))) { fprintf(stderr,"pci_bridge_add: unable to create new PCI bridge.\n"); return NULL; } memset(bridge,0,sizeof(*bridge)); bridge->pri_bus = pci_bus->bus; bridge->sec_bus = -1; bridge->sub_bus = -1; bridge->pci_bus = NULL; /* Insert the bridge in the double-linked list */ bridge->next = pci_bus->bridge_list; bridge->pprev = &pci_bus->bridge_list; if (pci_bus->bridge_list != NULL) pci_bus->bridge_list->pprev = &bridge->next; pci_bus->bridge_list = bridge; return bridge; } /* Remove a PCI bridge from the double-linked list */ static inline void pci_bridge_remove_from_list(struct pci_bridge *bridge) { if (bridge->next) bridge->next->pprev = bridge->pprev; if (bridge->pprev) *(bridge->pprev) = bridge->next; } /* Remove a PCI bridge */ void pci_bridge_remove(struct pci_bridge *bridge) { if (bridge != NULL) { pci_bridge_remove_from_list(bridge); free(bridge); } } /* Map secondary bus to a PCI bridge */ void pci_bridge_map_bus(struct pci_bridge *bridge,struct pci_bus *pci_bus) { if (bridge != NULL) { bridge->pci_bus = pci_bus; if (bridge->pci_bus != NULL) bridge->pci_bus->bus = bridge->sec_bus; } } /* Set PCI bridge bus info */ void pci_bridge_set_bus_info(struct pci_bridge *bridge, int pri_bus,int sec_bus,int sub_bus) { if (bridge != NULL) { bridge->pri_bus = pri_bus; bridge->sec_bus = sec_bus; bridge->sub_bus = sub_bus; if (bridge->pci_bus != NULL) bridge->pci_bus->bus = bridge->sec_bus; } } /* Add a PCI device */ struct pci_device * pci_dev_add(struct pci_bus *pci_bus,char *name, u_int vendor_id,u_int product_id, int device,int function,int irq, void *priv_data,pci_init_t init, pci_reg_read_t read_register, pci_reg_write_t write_register) { struct pci_device *dev; if (!pci_bus) return NULL; if ((dev = pci_dev_lookup_local(pci_bus,device,function)) != NULL) { fprintf(stderr,"pci_dev_add: bus %s, device %d, function %d already " "registered (device '%s').\n", pci_bus->name,device,function,dev->name); return NULL; } /* we can create safely the new device */ if (!(dev = malloc(sizeof(*dev)))) { fprintf(stderr,"pci_dev_add: unable to create new PCI device.\n"); return NULL; } memset(dev,0,sizeof(*dev)); dev->name = name; dev->vendor_id = vendor_id; dev->product_id = product_id; dev->pci_bus = pci_bus; dev->device = device; dev->function = function; dev->irq = irq; dev->priv_data = priv_data; dev->init = init; dev->read_register = read_register; dev->write_register = write_register; /* Insert the device in the double-linked list */ dev->next = pci_bus->dev_list; dev->pprev = &pci_bus->dev_list; if (pci_bus->dev_list != NULL) pci_bus->dev_list->pprev = &dev->next; pci_bus->dev_list = dev; if (init) init(dev); return dev; } /* Add a basic PCI device that just returns a Vendor/Product ID */ struct pci_device * pci_dev_add_basic(struct pci_bus *pci_bus, char *name,u_int vendor_id,u_int product_id, int device,int function) { return(pci_dev_add(pci_bus,name,vendor_id,product_id, device,function,-1,NULL, NULL,NULL,NULL)); } /* Remove a device from the double-linked list */ static inline void pci_dev_remove_from_list(struct pci_device *dev) { if (dev->next) dev->next->pprev = dev->pprev; if (dev->pprev) *(dev->pprev) = dev->next; } /* Remove a PCI device */ void pci_dev_remove(struct pci_device *dev) { if (dev != NULL) { pci_dev_remove_from_list(dev); free(dev); } } /* Remove a PCI device given its ID (bus,device,function) */ int pci_dev_remove_by_id(struct pci_bus *pci_bus, int bus,int device,int function) { struct pci_device *dev; if (!(dev = pci_dev_lookup(pci_bus,bus,device,function))) return(-1); pci_dev_remove(dev); return(0); } /* Remove a PCI device given its name */ int pci_dev_remove_by_name(struct pci_bus *pci_bus,char *name) { struct pci_device *dev,*next; int count = 0; for(dev=pci_bus->dev_list;dev;dev=next) { next = dev->next; if (!strcmp(dev->name,name)) { pci_dev_remove(dev); count++; } } return(count); } /* Create a PCI bus */ struct pci_bus *pci_bus_create(char *name,int bus) { struct pci_bus *d; if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"pci_bus_create: unable to create PCI info.\n"); return NULL; } memset(d,0,sizeof(*d)); d->name = strdup(name); d->bus = bus; return d; } /* Delete a PCI bus */ void pci_bus_remove(struct pci_bus *pci_bus) { struct pci_device *dev,*next; struct pci_bridge *bridge,*next_bridge; if (pci_bus) { /* Remove all devices */ for(dev=pci_bus->dev_list;dev;dev=next) { next = dev->next; free(dev); } /* Remove all bridges */ for(bridge=pci_bus->bridge_list;bridge;bridge=next_bridge) { next_bridge = bridge->next; free(bridge); } /* Free the structure itself */ free(pci_bus->name); free(pci_bus); } } /* Read a configuration register of a PCI bridge */ static m_uint32_t pci_bridge_read_reg(cpu_gen_t *cpu,struct pci_device *dev, int reg) { struct pci_bridge *bridge = dev->priv_data; m_uint32_t val = 0; switch(reg) { case 0x18: return(bridge->cfg_reg_bus); default: if (bridge->fallback_read != NULL) val = bridge->fallback_read(cpu,dev,reg); /* Returns appropriate PCI bridge class code if nothing defined */ if ((reg == 0x08) && !val) val = 0x06040000; return(val); } } /* Write a configuration register of a PCI bridge */ static void pci_bridge_write_reg(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct pci_bridge *bridge = dev->priv_data; u_int pri_bus,sec_bus,sub_bus; switch(reg) { case 0x18: bridge->cfg_reg_bus = value; sub_bus = (value >> 16) & 0xFF; sec_bus = (value >> 8) & 0xFF; pri_bus = value & 0xFF; /* Modify the PCI bridge settings */ vm_log(cpu->vm,"PCI", "PCI bridge %d,%d,%d -> pri: %2.2u, sec: %2.2u, sub: %2.2u\n", dev->pci_bus->bus,dev->device,dev->function, pri_bus,sec_bus,sub_bus); pci_bridge_set_bus_info(bridge,pri_bus,sec_bus,sub_bus); break; default: if (bridge->fallback_write != NULL) bridge->fallback_write(cpu,dev,reg,value); } } /* Create a PCI bridge device */ struct pci_device *pci_bridge_create_dev(struct pci_bus *pci_bus,char *name, u_int vendor_id,u_int product_id, int device,int function, struct pci_bus *sec_bus, pci_reg_read_t fallback_read, pci_reg_write_t fallback_write) { struct pci_bridge *bridge; struct pci_device *dev; /* Create the PCI bridge structure */ if (!(bridge = pci_bridge_add(pci_bus))) return NULL; /* Create the PCI device corresponding to the bridge */ dev = pci_dev_add(pci_bus,name,vendor_id,product_id,device,function,-1, bridge,NULL,pci_bridge_read_reg,pci_bridge_write_reg); if (!dev) goto err_pci_dev; /* Keep the associated PCI device for this bridge */ bridge->pci_dev = dev; /* Set the fallback functions */ bridge->fallback_read = fallback_read; bridge->fallback_write = fallback_write; /* Map the secondary bus (disabled at startup) */ pci_bridge_map_bus(bridge,sec_bus); return dev; err_pci_dev: pci_bridge_remove(bridge); return NULL; } /* Show PCI device list of the specified bus */ static void pci_bus_show_dev_list(struct pci_bus *pci_bus) { struct pci_device *dev; struct pci_bridge *bridge; char bus_id[32]; if (!pci_bus) return; if (pci_bus->bus != -1) { snprintf(bus_id,sizeof(bus_id),"%2d",pci_bus->bus); } else { strcpy(bus_id,"XX"); } for(dev=pci_bus->dev_list;dev;dev=dev->next) { printf(" %-18s: ID %4.4x:%4.4x, Bus %s, Dev. %2d, Func. %2d", dev->name,dev->vendor_id,dev->product_id, bus_id,dev->device,dev->function); if (dev->irq != -1) printf(", IRQ: %d\n",dev->irq); else printf("\n"); } for(bridge=pci_bus->bridge_list;bridge;bridge=bridge->next) pci_bus_show_dev_list(bridge->pci_bus); } /* Show PCI device list */ void pci_dev_show_list(struct pci_bus *pci_bus) { if (!pci_bus) return; printf("PCI Bus \"%s\" Device list:\n",pci_bus->name); pci_bus_show_dev_list(pci_bus); printf("\n"); } dynamips-0.2.14/common/pci_dev.h000066400000000000000000000117431241034141600164760ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __PCI_DEV_H__ #define __PCI_DEV_H__ #include "utils.h" #define PCI_BUS_ADDR 0xcf8 #define PCI_BUS_DATA 0xcfc /* PCI ID (Vendor + Device) register */ #define PCI_REG_ID 0x00 /* PCI Base Address Registers (BAR) */ #define PCI_REG_BAR0 0x10 #define PCI_REG_BAR1 0x14 #define PCI_REG_BAR2 0x18 #define PCI_REG_BAR3 0x1c #define PCI_REG_BAR4 0x20 #define PCI_REG_BAR5 0x24 /* Forward declaration for PCI device */ typedef struct pci_device pci_dev_t; /* PCI function prototypes */ typedef void (*pci_init_t)(pci_dev_t *dev); typedef m_uint32_t (*pci_reg_read_t)(cpu_gen_t *cpu,pci_dev_t *dev,int reg); typedef void (*pci_reg_write_t)(cpu_gen_t *cpu,pci_dev_t *dev,int reg, m_uint32_t value); /* PCI device */ struct pci_device { char *name; u_int vendor_id,product_id; int device,function,irq; void *priv_data; /* Parent bus */ struct pci_bus *pci_bus; pci_init_t init; pci_reg_read_t read_register; pci_reg_write_t write_register; struct pci_device *next,**pprev; }; /* PCI bus */ struct pci_bus { char *name; m_uint32_t pci_addr; /* Bus number */ int bus; /* PCI device list on this bus */ struct pci_device *dev_list; /* PCI bridges to access other busses */ struct pci_bridge *bridge_list; }; /* PCI bridge */ struct pci_bridge { int pri_bus; /* Primary Bus */ int sec_bus; /* Secondary Bus */ int sub_bus; /* Subordinate Bus */ int skip_bus_check; /* Bus configuration register */ m_uint32_t cfg_reg_bus; /* PCI bridge device */ struct pci_device *pci_dev; /* Secondary PCI bus */ struct pci_bus *pci_bus; /* Fallback handlers to read/write config registers */ pci_reg_read_t fallback_read; pci_reg_write_t fallback_write; struct pci_bridge *next,**pprev; }; /* PCI IO device */ struct pci_io_device { m_uint32_t start,end; struct vdevice *real_dev; dev_handler_t handler; struct pci_io_device *next,**pprev; }; /* Trigger a PCI device IRQ */ void pci_dev_trigger_irq(vm_instance_t *vm,struct pci_device *dev); /* Clear a PCI device IRQ */ void pci_dev_clear_irq(vm_instance_t *vm,struct pci_device *dev); /* PCI bus lookup */ struct pci_bus *pci_bus_lookup(struct pci_bus *pci_bus_root,int bus); /* PCI device local lookup */ struct pci_device *pci_dev_lookup_local(struct pci_bus *pci_bus, int device,int function); /* PCI device lookup */ struct pci_device *pci_dev_lookup(struct pci_bus *pci_bus_root, int bus,int device,int function); /* Handle the address register access */ void pci_dev_addr_handler(cpu_gen_t *cpu,struct pci_bus *pci_bus, u_int op_type,int swap,m_uint64_t *data); /* Handle the data register access */ void pci_dev_data_handler(cpu_gen_t *cpu,struct pci_bus *pci_bus, u_int op_type,int swap,m_uint64_t *data); /* Add a PCI bridge */ struct pci_bridge *pci_bridge_add(struct pci_bus *pci_bus); /* Remove a PCI bridge */ void pci_bridge_remove(struct pci_bridge *bridge); /* Map secondary bus to a PCI bridge */ void pci_bridge_map_bus(struct pci_bridge *bridge,struct pci_bus *pci_bus); /* Set PCI bridge bus info */ void pci_bridge_set_bus_info(struct pci_bridge *bridge, int pri_bus,int sec_bus,int sub_bus); /* Add a PCI device */ struct pci_device * pci_dev_add(struct pci_bus *pci_bus, char *name,u_int vendor_id,u_int product_id, int device,int function,int irq, void *priv_data,pci_init_t init, pci_reg_read_t read_register, pci_reg_write_t write_register); /* Add a basic PCI device that just returns a Vendor/Product ID */ struct pci_device * pci_dev_add_basic(struct pci_bus *pci_bus, char *name,u_int vendor_id,u_int product_id, int device,int function); /* Remove a PCI device */ void pci_dev_remove(struct pci_device *dev); /* Remove a PCI device given its ID (bus,device,function) */ int pci_dev_remove_by_id(struct pci_bus *pci_bus, int bus,int device,int function); /* Remove a PCI device given its name */ int pci_dev_remove_by_name(struct pci_bus *pci_bus,char *name); /* Create a PCI bus */ struct pci_bus *pci_bus_create(char *name,int bus); /* Delete a PCI bus */ void pci_bus_remove(struct pci_bus *pci_bus); /* Create a PCI bridge device */ struct pci_device *pci_bridge_create_dev(struct pci_bus *pci_bus,char *name, u_int vendor_id,u_int product_id, int device,int function, struct pci_bus *sec_bus, pci_reg_read_t fallback_read, pci_reg_write_t fallback_write); /* Show PCI device list */ void pci_dev_show_list(struct pci_bus *pci_bus); #endif dynamips-0.2.14/common/pci_io.c000066400000000000000000000056251241034141600163240ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * PCI I/O space. */ #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "pci_io.h" /* Debugging flags */ #define DEBUG_ACCESS 0 /* Add a new PCI I/O device */ struct pci_io_device *pci_io_add(struct pci_io_data *d, m_uint32_t start,m_uint32_t end, struct vdevice *dev,dev_handler_t handler) { struct pci_io_device *p; if (!(p = malloc(sizeof(*p)))) { fprintf(stderr,"pci_io_add: unable to create a new device.\n"); return NULL; } p->start = start; p->end = end; p->real_dev = dev; p->handler = handler; p->next = d->dev_list; p->pprev = &d->dev_list; if (d->dev_list != NULL) d->dev_list->pprev = &p->next; d->dev_list = p; return p; } /* Remove a PCI I/O device */ void pci_io_remove(struct pci_io_device *dev) { if (dev != NULL) { if (dev->next) dev->next->pprev = dev->pprev; *(dev->pprev) = dev->next; free(dev); } } /* * pci_io_access() */ static void *pci_io_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { struct pci_io_data *d = dev->priv_data; struct pci_io_device *p; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"PCI_IO","read request at pc=0x%llx, offset=0x%x\n", cpu_get_pc(cpu),offset); } else { cpu_log(cpu,"PCI_IO", "write request (data=0x%llx) at pc=0x%llx, offset=0x%x\n", *data,cpu_get_pc(cpu),offset); } #endif if (op_type == MTS_READ) *data = 0; for(p=d->dev_list;p;p=p->next) if ((offset >= p->start) && (offset <= p->end)) { return(p->handler(cpu,p->real_dev,(offset - p->start), op_size,op_type,data)); } return NULL; } /* Remove PCI I/O space */ void pci_io_data_remove(vm_instance_t *vm,struct pci_io_data *d) { if (d != NULL) { /* Remove the device */ dev_remove(vm,&d->dev); /* Free the structure itself */ free(d); } } /* Initialize PCI I/O space */ struct pci_io_data *pci_io_data_init(vm_instance_t *vm,m_uint64_t paddr) { struct pci_io_data *d; /* Allocate the PCI I/O data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"PCI_IO: out of memory\n"); return NULL; } memset(d,0,sizeof(*d)); dev_init(&d->dev); d->dev.name = "pci_io"; d->dev.priv_data = d; d->dev.phys_addr = paddr; d->dev.phys_len = 2 * 1048576; d->dev.handler = pci_io_access; /* Map this device to the VM */ vm_bind_device(vm,&d->dev); return(d); } dynamips-0.2.14/common/pci_io.h000066400000000000000000000014211241034141600163170ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __PCI_IO_H__ #define __PCI_IO_H__ #include "pci_dev.h" /* PCI I/O data */ struct pci_io_data { struct vdevice dev; struct pci_io_device *dev_list; }; /* Add a new PCI I/O device */ struct pci_io_device *pci_io_add(struct pci_io_data *d, m_uint32_t start,m_uint32_t end, struct vdevice *dev,dev_handler_t handler); /* Remove a PCI I/O device */ void pci_io_remove(struct pci_io_device *dev); /* Remove PCI I/O space */ void pci_io_data_remove(vm_instance_t *vm,struct pci_io_data *d); /* Initialize PCI I/O space */ struct pci_io_data *pci_io_data_init(vm_instance_t *vm,m_uint64_t paddr); #endif dynamips-0.2.14/common/plugin.c000066400000000000000000000026071241034141600163550ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * Plugin management. */ #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "plugin.h" /* Plugin list */ static struct plugin *plugin_list = NULL; /* Find a symbol address */ void *plugin_find_symbol(struct plugin *plugin,char *symbol) { return((plugin != NULL) ? dlsym(plugin->dl_handle,symbol) : NULL); } /* Initialize a plugin */ static int plugin_init(struct plugin *plugin) { plugin_init_t init; if (!(init = plugin_find_symbol(plugin,"init"))) return(-1); return(init()); } /* Load a plugin */ struct plugin *plugin_load(char *filename) { struct plugin *p; if (!(p = malloc(sizeof(*p)))) return NULL; memset(p,0,sizeof(*p)); if (!(p->filename = strdup(filename))) goto err_strdup; if (!(p->dl_handle = dlopen(filename,RTLD_LAZY))) { fprintf(stderr,"plugin_load(\"%s\"): %s\n",filename,dlerror()); goto err_dlopen; } if (plugin_init(p) == -1) goto err_init; p->next = plugin_list; plugin_list = p; return p; err_init: dlclose(p->dl_handle); err_dlopen: free(p->filename); err_strdup: free(p); return NULL; } dynamips-0.2.14/common/plugin.h000066400000000000000000000006661241034141600163650ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * Plugins. */ #ifndef __PLUGIN_H__ #define __PLUGIN_H__ struct plugin { char *filename; void *dl_handle; struct plugin *next; }; typedef int (*plugin_init_t)(void); /* Find a symbol address */ void *plugin_find_symbol(struct plugin *plugin,char *symbol); /* Load a plugin */ struct plugin *plugin_load(char *filename); #endif dynamips-0.2.14/common/ppc32_amd64_trans.h000066400000000000000000000044161241034141600202150ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __PPC32_AMD64_TRANS_H__ #define __PPC32_AMD64_TRANS_H__ #include "utils.h" #include "amd64-codegen.h" #include "cpu.h" #include "dynamips.h" #include "ppc32_exec.h" #define JIT_SUPPORT 1 /* Manipulate bitmasks atomically */ static forced_inline void atomic_or(m_uint32_t *v,m_uint32_t m) { __asm__ __volatile__("lock; orl %1,%0":"=m"(*v):"ir"(m),"m"(*v)); } static forced_inline void atomic_and(m_uint32_t *v,m_uint32_t m) { __asm__ __volatile__("lock; andl %1,%0":"=m"(*v):"ir"(m),"m"(*v)); } /* Wrappers to amd64-codegen functions */ #define ppc32_jit_tcb_set_patch amd64_patch #define ppc32_jit_tcb_set_jump amd64_jump_code /* PPC instruction array */ extern struct ppc32_insn_tag ppc32_insn_tags[]; /* Push epilog for an x86 instruction block */ static forced_inline void ppc32_jit_tcb_push_epilog(u_char **ptr) { amd64_ret(*ptr); } /* Execute JIT code */ static forced_inline void ppc32_jit_tcb_exec(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block) { insn_tblock_fptr jit_code; m_uint32_t offset; offset = (cpu->ia & PPC32_MIN_PAGE_IMASK) >> 2; jit_code = (insn_tblock_fptr)block->jit_insn_ptr[offset]; if (unlikely(!jit_code)) { ppc32_jit_tcb_set_target_bit(block,cpu->ia); if (++block->target_undef_cnt == 16) { ppc32_jit_tcb_recompile(cpu,block); jit_code = (insn_tblock_fptr)block->jit_insn_ptr[offset]; } else { ppc32_exec_page(cpu); return; } } asm volatile ("movq %0,%%r15"::"r"(cpu): "r13","r14","r15","rax","rbx","rcx","rdx","rdi","rsi"); jit_code(); } static inline void amd64_patch(u_char *code,u_char *target) { /* Skip REX */ if ((code[0] >= 0x40) && (code[0] <= 0x4f)) code += 1; if ((code [0] & 0xf8) == 0xb8) { /* amd64_set_reg_template */ *(m_uint64_t *)(code + 1) = (m_uint64_t)target; } else if (code [0] == 0x8b) { /* mov 0(%rip), %dreg */ *(m_uint32_t *)(code + 2) = (m_uint32_t)(m_uint64_t)target - 7; } else if ((code [0] == 0xff) && (code [1] == 0x15)) { /* call *(%rip) */ *(m_uint32_t *)(code + 2) = ((m_uint32_t)(m_uint64_t)target) - 7; } else x86_patch(code,target); } #endif dynamips-0.2.14/common/ppc32_exec.h000066400000000000000000000017051241034141600170150ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __PPC32_EXEC_H__ #define __PPC32_EXEC_H__ #include "utils.h" /* PowerPC instruction recognition */ struct ppc32_insn_exec_tag { char *name; fastcall int (*exec)(cpu_ppc_t *,ppc_insn_t); m_uint32_t mask,value; int instr_type; m_uint64_t count; }; /* Get a rotation mask */ static forced_inline m_uint32_t ppc32_rotate_mask(m_uint32_t mb,m_uint32_t me) { m_uint32_t mask; mask = (0xFFFFFFFFU >> mb) ^ ((0xFFFFFFFFU >> me) >> 1); if (me < mb) mask = ~mask; return(mask); } /* Initialize instruction lookup table */ void ppc32_exec_create_ilt(void); /* Dump statistics */ void ppc32_dump_stats(cpu_ppc_t *cpu); /* Execute a page */ fastcall int ppc32_exec_page(cpu_ppc_t *cpu); /* Execute a single instruction (external) */ fastcall int ppc32_exec_single_insn_ext(cpu_ppc_t *cpu,ppc_insn_t insn); #endif dynamips-0.2.14/common/ppc32_mem.h000066400000000000000000000026061241034141600166500ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) */ #ifndef __PPC32_MEM_H__ #define __PPC32_MEM_H__ /* Initialize the MTS subsystem for the specified CPU */ int ppc32_mem_init(cpu_ppc_t *cpu); /* Free memory used by MTS */ void ppc32_mem_shutdown(cpu_ppc_t *cpu); /* Invalidate the MTS caches (instruction and data) */ void ppc32_mem_invalidate_cache(cpu_ppc_t *cpu); /* Set a BAT register */ int ppc32_set_bat(cpu_ppc_t *cpu,struct ppc32_bat_prog *bp); /* Load BAT registers from a BAT array */ void ppc32_load_bat_array(cpu_ppc_t *cpu,struct ppc32_bat_prog *bp); /* Get the host address for SDR1 */ int ppc32_set_sdr1(cpu_ppc_t *cpu,m_uint32_t sdr1); /* Initialize the page table */ int ppc32_init_page_table(cpu_ppc_t *cpu); /* Map a page */ int ppc32_map_page(cpu_ppc_t *cpu,u_int vsid,m_uint32_t vaddr,m_uint64_t paddr, u_int wimg,u_int pp); /* Map a memory zone */ int ppc32_map_zone(cpu_ppc_t *cpu,u_int vsid,m_uint32_t vaddr,m_uint64_t paddr, m_uint32_t size,u_int wimg,u_int pp); /* Get a BAT SPR */ m_uint32_t ppc32_get_bat_spr(cpu_ppc_t *cpu,u_int spr); /* Set a BAT SPR */ void ppc32_set_bat_spr(cpu_ppc_t *cpu,u_int spr,m_uint32_t val); /* Initialize memory access vectors */ void ppc32_init_memop_vectors(cpu_ppc_t *cpu); /* Restart the memory subsystem */ int ppc32_mem_restart(cpu_ppc_t *cpu); #endif dynamips-0.2.14/common/ppc32_nojit_trans.c000066400000000000000000000026621241034141600204210ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Just an empty JIT template file for architectures not supported by the JIT * code. */ #include #include #include #include #include #include #include #include "cpu.h" #include "ppc32_jit.h" #include "ppc32_nojit_trans.h" #define EMPTY(func) func { \ fprintf(stderr,"This function should not be called: "#func"\n"); \ abort(); \ } EMPTY(void ppc32_emit_breakpoint(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block)); EMPTY(void ppc32_jit_tcb_push_epilog(u_char **ptr)); EMPTY(void ppc32_jit_tcb_exec(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block)); EMPTY(void ppc32_set_ia(u_char **ptr,m_uint32_t new_ia)); EMPTY(void ppc32_inc_perf_counter(cpu_ppc_t *cpu)); EMPTY(void ppc32_jit_init_hreg_mapping(cpu_ppc_t *cpu)); EMPTY(void ppc32_op_insn_output(ppc32_jit_tcb_t *b,jit_op_t *op)); EMPTY(void ppc32_op_load_gpr(ppc32_jit_tcb_t *b,jit_op_t *op)); EMPTY(void ppc32_op_store_gpr(ppc32_jit_tcb_t *b,jit_op_t *op)); EMPTY(void ppc32_op_update_flags(ppc32_jit_tcb_t *b,jit_op_t *op)); EMPTY(void ppc32_op_move_host_reg(ppc32_jit_tcb_t *b,jit_op_t *op)); EMPTY(void ppc32_op_set_host_reg_imm32(ppc32_jit_tcb_t *b,jit_op_t *op)); EMPTY(void ppc32_set_page_jump(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b)); /* PowerPC instruction array */ struct ppc32_insn_tag ppc32_insn_tags[] = { { NULL, 0, 0 }, }; dynamips-0.2.14/common/ppc32_nojit_trans.h000066400000000000000000000013211241034141600204150ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __PPC32_NOJIT_TRANS_H__ #define __PPC32_NOJIT_TRANS_H__ #include "utils.h" #include "x86-codegen.h" #include "cpu.h" #include "ppc32_exec.h" #include "dynamips.h" #define JIT_SUPPORT 0 /* Wrappers to x86-codegen functions */ #define ppc32_jit_tcb_set_patch(a,b) (void)(a); (void)(b) #define ppc32_jit_tcb_set_jump(a,b) (void)(a); (void)(b) /* PPC instruction array */ extern struct ppc32_insn_tag ppc32_insn_tags[]; /* Push epilog for an x86 instruction block */ void ppc32_jit_tcb_push_epilog(u_char **ptr); /* Execute JIT code */ void ppc32_jit_tcb_exec(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block); #endif dynamips-0.2.14/common/ppc32_x86_trans.h000066400000000000000000000032101241034141600177160ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __PPC32_X86_TRANS_H__ #define __PPC32_X86_TRANS_H__ #include "utils.h" #include "x86-codegen.h" #include "cpu.h" #include "ppc32_exec.h" #include "dynamips.h" #define JIT_SUPPORT 1 /* Manipulate bitmasks atomically */ static forced_inline void atomic_or(m_uint32_t *v,m_uint32_t m) { __asm__ __volatile__("lock; orl %1,%0":"=m"(*v):"ir"(m),"m"(*v)); } static forced_inline void atomic_and(m_uint32_t *v,m_uint32_t m) { __asm__ __volatile__("lock; andl %1,%0":"=m"(*v):"ir"(m),"m"(*v)); } /* Wrappers to x86-codegen functions */ #define ppc32_jit_tcb_set_patch x86_patch #define ppc32_jit_tcb_set_jump x86_jump_code /* PPC instruction array */ extern struct ppc32_insn_tag ppc32_insn_tags[]; /* Push epilog for an x86 instruction block */ static forced_inline void ppc32_jit_tcb_push_epilog(u_char **ptr) { x86_ret(*ptr); } /* Execute JIT code */ static forced_inline void ppc32_jit_tcb_exec(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block) { insn_tblock_fptr jit_code; m_uint32_t offset; offset = (cpu->ia & PPC32_MIN_PAGE_IMASK) >> 2; jit_code = (insn_tblock_fptr)block->jit_insn_ptr[offset]; if (unlikely(!jit_code)) { ppc32_jit_tcb_set_target_bit(block,cpu->ia); if (++block->target_undef_cnt == 16) { ppc32_jit_tcb_recompile(cpu,block); jit_code = (insn_tblock_fptr)block->jit_insn_ptr[offset]; } else { ppc32_exec_page(cpu); return; } } asm volatile ("movl %0,%%edi"::"r"(cpu): "esi","edi","eax","ebx","ecx","edx"); jit_code(); } #endif dynamips-0.2.14/common/profiler.c000066400000000000000000000224751241034141600167060ustar00rootroot00000000000000/* * Contribution of Mtve. */ #include #include #include #include #include #define THREADED 1 #if THREADED #include #endif /* * Call to profiling routine .mcount is automatically inserted by gcc -p. * * However, standard .mcount from (g)libc is not working well for me * with optimized (-O3 -fomit-frame-pointer) threaded code, * at least because it doesn't save all registers. * * So here is another square wheel. It works only on IA32 (i386). * * Theory: * .mcount is called like this * * 08048479 : * 8048679: 55 push %ebp # can be * 804847a: 89 e5 movl %esp,%ebp # absent * 804847c: 83 ec 1c subl $28,%esp * 804847f: 55 pushl %ebp * 8048480: 57 pushl %edi * 8048481: 56 pushl %esi * 8048482: 53 pushl %ebx * 8048483: e8 94 fe ff ff call .mcount * 08048488 * * So in the entrance of .mcount we have in stack * * %esp -> some_func_x (dword), where mcount should return * saved registers (4 dwords in example) * stack frame (28 bytes in example) * some_func_callee (dword) * * We will: * - check if the code of some_func matches this pattern * - find some_func address and depth of stack * - modify stack by replacing some_func_callee address to ours * - collect statistic */ /* better to be a prime number */ #define FUNCSMAX 32749 #define CSTACKSIZE 256 #if THREADED /* better to be a prime number */ #define THREADSMAX 37 #else #define THREADSMAX 1 #endif static struct { int addr; int enters; int exits; int aways; int rets; long long timetotal; long long timeoutside; } arr[FUNCSMAX + 1]; static struct { #if THREADED pthread_t tid; #endif int depth; int ret[CSTACKSIZE]; int func[CSTACKSIZE]; } cstack[THREADSMAX]; #define core() (*(char *)0 = 0) #if __GNUC__ > 2 #define NOPROF __attribute__ ((no_instrument_function)) /* forward declaration of all functions */ static inline long long curtime() NOPROF; static inline int findaddr() NOPROF; static inline int findthread() NOPROF; static void stat_enter() NOPROF; static void stat_exit() NOPROF; static void stat_away() NOPROF; static void stat_ret() NOPROF; void profiler__asm_enter_stub() NOPROF; void profiler__asm_exit_stub() NOPROF; void profiler__c_enter() NOPROF; void profiler__c_exit() NOPROF; void profiler_savestat() NOPROF; #else #warning be sure to compile profiler.c WITHOUT -p flag #endif static inline long long curtime(void) { long long t; asm volatile(".byte 15;.byte 49" : "=A"(t)); /* RDTSC */ return t; } static inline int findaddr(int addr) { int i,j; i = j = addr % FUNCSMAX; do { if (arr[i].addr == addr) { return i; } else if (arr[i].addr == 0) { arr[i].addr = addr; return i; } i = (i+1) % FUNCSMAX; } while (i != j); core(); /* increase FUNCSMAX */ return(FUNCSMAX); } static inline int findthread(void) { #if THREADED int i,j; pthread_t k = pthread_self(); i = j = (int)k % THREADSMAX; do { if (cstack[i].tid == k) { return i; } else if (cstack[i].tid == 0) { cstack[i].tid = k; return i; } i = (i+1) % THREADSMAX; } while (i != j); core(); /* increase THREADSMAX */ #endif return(0); } static void stat_enter(int slot) { arr[slot].enters++; arr[slot].timetotal -= curtime(); } static void stat_exit(int slot) { arr[slot].exits++; arr[slot].timetotal += curtime(); } static void stat_away(int slot) { arr[slot].aways++; arr[slot].timeoutside -= curtime(); } static void stat_ret(int slot) { arr[slot].rets++; arr[slot].timeoutside += curtime(); } void profiler__asm_enter(void); void profiler__asm_exit(void); #define A __asm__ /* * that't really weird but compatible with both gcc2 and gcc3 * * things i don't want to care of * - what size on stack pusha/pops use * - what current function framing is */ void profiler__asm_enter_stub(void) { A(" .globl .mcount "); A(" .globl profiler__asm_enter "); A("profiler__asm_enter: "); A(".mcount: "); A(" pushl %eax "); /* save %eax */ A(" movl %esp,%eax "); /* %eax = old %esp - 4 */ A(" pusha "); /* save all registers */ A(" push %eax "); /* push parameter to stack */ A(" call profiler__c_enter "); /* call c routine */ A(" pop %eax "); /* clear parameter from stack */ A(" popa "); /* restore all registers */ A(" pop %eax "); /* restore %eax */ A(" ret "); /* return */ } void profiler__asm_exit_stub(void) { A("profiler__asm_exit: "); A(" pushl $0xdeadbeaf "); /* placeholder to return address */ A(" pushl %eax "); /* save %eax */ A(" movl %esp,%eax "); /* %eax = addr of placeholder - 4 */ A(" pusha "); /* save all registers */ A(" pushl %eax "); /* push parameter to stack */ A(" call profiler__c_exit "); /* call C routine */ A(" popl %eax "); /* clear parameter from stack */ A(" popa "); /* restore all registers */ A(" popl %eax "); /* restore %eax */ A(" ret "); /* return */ } void profiler__c_enter(int *sp_1) { unsigned char *pc; int stdepth = 2, i, thr, slot, gcc2 = 1; if (sizeof(int) != 4) core(); /* sizeof int != 4 */ if (sizeof(long long) != 8) core(); /* sizeof long long != 8 */ if (sizeof(void *) != 4) core(); /* sizeof pointer != 4 */ pc = (char *)(sp_1[1]); pc -= 5; if (*pc != 0xe8) /* call */ core(); /* called not by 0xe8 */ if ((int)pc + 5 + *(int *)(pc+1) != (int)profiler__asm_enter) core(); /* call points not to .mcount */ if (pc[-1] == 0x53) /* push %ebx */ pc--, stdepth++; if (pc[-1] == 0x56) /* push %esi */ pc--, stdepth++; if (pc[-1] == 0x57) /* push %edi */ pc--, stdepth++; if (pc[-1] == 0x55) /* push %ebp */ pc--, stdepth++; if (pc[-6]==0x81 && pc[-5]==0xec && pc[-2]==0 && pc[-1]==0) { /* sub ,%esp */ stdepth += *(int *)(pc - 4)/4; pc -= 6; } else if (pc[-3]==0x83 && pc[-2]==0xec && pc[-1]%4==0) { /* sub ,%esp */ stdepth += pc[-1]/4; pc -= 3; } else gcc2 = 0; while (pc[-1] >= 0x50 && pc[-1] <= 0x57) /* push %e[reg] */ pc--, stdepth++; /* "pushl %ebp; movl %esp,%ebp;" */ if (pc[-3]==0x55 && pc[-2]==0x89 && pc[-1]==0xe5) { stdepth++; pc -= 3; } else if(!gcc2) core(); /* unknown prologue, examine x/10i pc-10 */ /* * Now we know that it's standard prologue, so we modify the stack */ thr = findthread(); slot = findaddr((int)pc); i = cstack[thr].depth++; if(i >= CSTACKSIZE) core(); /* call stack overflow */ cstack[thr].func[i] = slot; cstack[thr].ret[i] = sp_1[stdepth]; sp_1[stdepth] = (int)profiler__asm_exit; if (i > 0) stat_away(cstack[thr].func[i - 1]); stat_enter(slot); } void profiler__c_exit(int *sp) { int i, thr; thr = findthread(); i = --cstack[thr].depth; if (i < 0) core(); /* call stack underflow */ sp[1] = cstack[thr].ret[i]; stat_exit(cstack[thr].func[i]); if (i > 0) stat_ret(cstack[thr].func[i - 1]); #if THREADED else cstack[thr].tid = 0; /* free this stack */ #endif } #ifndef PROFILE_FILE #error define PROFILE_FILE where to save statistic #endif static void mywrite(int fd,char *str) { int len, i; for (len = strlen(str); len > 0; str += i, len -= i) if ((i = write(fd,str,len)) < 0) return; } void profiler_savestat(void) { int i, fd; char buf[1024]; fd = open(PROFILE_FILE,O_CREAT | O_TRUNC | O_WRONLY,0666); if (fd < 0) { mywrite(2,"open " PROFILE_FILE " failed - "); mywrite(2,strerror(errno)); mywrite(2,"\n"); return; } snprintf(buf,sizeof(buf),"\nProfiling statistic %s at time %lld:\n" "\n%8s %10s %10s %10s %10s %20s %20s\n",PROFILE_FILE,curtime(), "Function","Enters","Exits","Aways","Returns", "Cycles_Total","Cycles_Inside"); mywrite(fd,buf); for (i = 0; i < FUNCSMAX; i++) if (arr[i].addr) { snprintf(buf,sizeof(buf),"%08x %10d %10d %10d %10d %20lld %20lld\n", arr[i].addr,arr[i].enters,arr[i].exits, arr[i].aways,arr[i].rets, arr[i].timetotal + (arr[i].enters-arr[i].exits) * curtime(), arr[i].timetotal - arr[i].timeoutside + curtime() * (arr[i].enters-arr[i].exits-arr[i].aways+arr[i].rets)); mywrite(fd,buf); } close(fd); } dynamips-0.2.14/common/ptask.c000066400000000000000000000053321241034141600161770ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Periodic tasks centralization. Used for TX part of network devices. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ptask.h" static pthread_t ptask_thread; static pthread_mutex_t ptask_mutex = PTHREAD_MUTEX_INITIALIZER; static ptask_t *ptask_list = NULL; static ptask_id_t ptask_current_id = 0; u_int ptask_sleep_time = 10; #define PTASK_LOCK() pthread_mutex_lock(&ptask_mutex) #define PTASK_UNLOCK() pthread_mutex_unlock(&ptask_mutex) /* Periodic task thread */ static void *ptask_run(void *arg) { pthread_mutex_t umutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t ucond = PTHREAD_COND_INITIALIZER; ptask_t *task; for(;;) { PTASK_LOCK(); for(task=ptask_list;task;task=task->next) task->cbk(task->object,task->arg); PTASK_UNLOCK(); /* For testing! */ { struct timespec t_spc; m_tmcnt_t expire; expire = m_gettime_usec() + (ptask_sleep_time * 1000); pthread_mutex_lock(&umutex); t_spc.tv_sec = expire / 1000000; t_spc.tv_nsec = (expire % 1000000) * 1000; pthread_cond_timedwait(&ucond,&umutex,&t_spc); pthread_mutex_unlock(&umutex); } /* Old method... */ //usleep(ptask_sleep_time*1000); } return NULL; } /* Add a new task */ ptask_id_t ptask_add(ptask_callback cbk,void *object,void *arg) { ptask_t *task; ptask_id_t id; if (!(task = malloc(sizeof(*task)))) { fprintf(stderr,"ptask_add: unable to add new task.\n"); return(-1); } memset(task,0,sizeof(*task)); task->cbk = cbk; task->object = object; task->arg = arg; PTASK_LOCK(); id = ++ptask_current_id; assert(id != 0); task->id = id; task->next = ptask_list; ptask_list = task; PTASK_UNLOCK(); return(id); } /* Remove a task */ int ptask_remove(ptask_id_t id) { ptask_t **task,*p; int res = -1; PTASK_LOCK(); for(task=&ptask_list;*task;task=&(*task)->next) if ((*task)->id == id) { p = *task; *task = (*task)->next; free(p); res = 0; break; } PTASK_UNLOCK(); return(res); } /* Initialize ptask module */ int ptask_init(u_int sleep_time) { if (sleep_time) ptask_sleep_time = sleep_time; if (pthread_create(&ptask_thread,NULL,ptask_run,NULL) != 0) { fprintf(stderr,"ptask_init: unable to create thread.\n"); return(-1); } return(0); } dynamips-0.2.14/common/ptask.h000066400000000000000000000014661241034141600162100ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Periodic tasks centralization. */ #ifndef __PTASK_H__ #define __PTASK_H__ #include #include #include #include "utils.h" /* ptask identifier */ typedef m_int64_t ptask_id_t; /* periodic task callback prototype */ typedef int (*ptask_callback)(void *object,void *arg); /* periodic task definition */ typedef struct ptask ptask_t; struct ptask { ptask_id_t id; ptask_t *next; ptask_callback cbk; void *object,*arg; }; extern u_int ptask_sleep_time; /* Add a new task */ ptask_id_t ptask_add(ptask_callback cbk,void *object,void *arg); /* Remove a task */ int ptask_remove(ptask_id_t id); /* Initialize ptask module */ int ptask_init(u_int sleep_time); #endif dynamips-0.2.14/common/rbtree.c000066400000000000000000000266571241034141600163550ustar00rootroot00000000000000/* * Dynamips * Copyright (c) 2005 Christophe Fillot. * E-mail: cf@utc.fr * * rbtree.c: Red/Black Trees. */ static const char rcsid[] = "$Id$"; #include #include #include #include #include #include #include #include #include #include "utils.h" #include "rbtree.h" #define rbtree_nil(tree) (&(tree)->nil) #define NIL(tree,x) (((x) == rbtree_nil(tree)) || !x) /* Allocate memory for a new node */ static rbtree_node *rbtree_node_alloc(rbtree_tree *tree,void *key,void *value) { rbtree_node *node; if (!(node = mp_alloc_n0(&tree->mp,sizeof(*node)))) return NULL; node->key = key; node->value = value; node->left = rbtree_nil(tree); node->right = rbtree_nil(tree); node->parent = rbtree_nil(tree); node->color = -1; return node; } /* Free memory used by a node */ static inline void rbtree_node_free(rbtree_tree *tree,rbtree_node *node) { mp_free(node); } /* Returns the node which represents the minimum value */ static inline rbtree_node *rbtree_min(rbtree_tree *tree,rbtree_node *x) { while(!NIL(tree,x->left)) x = x->left; return(x); } /* Returns the node which represents the maximum value */ _unused static inline rbtree_node *rbtree_max(rbtree_tree *tree,rbtree_node *x) { while(!NIL(tree,x->right)) x = x->right; return(x); } /* Returns the successor of a node */ static inline rbtree_node *rbtree_successor(rbtree_tree *tree,rbtree_node *x) { rbtree_node *y; if (!NIL(tree,x->right)) return(rbtree_min(tree,x->right)); y = x->parent; while(!NIL(tree,y) && (x == y->right)) { x = y; y = y->parent; } return(y); } /* Left rotation */ static inline void rbtree_left_rotate(rbtree_tree *tree,rbtree_node *x) { rbtree_node *y; y = x->right; x->right = y->left; if (!NIL(tree,x->right)) x->right->parent = x; y->parent = x->parent; if (NIL(tree,x->parent)) tree->root = y; else { if (x == x->parent->left) x->parent->left = y; else x->parent->right = y; } y->left = x; x->parent = y; } /* Right rotation */ static inline void rbtree_right_rotate(rbtree_tree *tree,rbtree_node *y) { rbtree_node *x; x = y->left; y->left = x->right; if (!NIL(tree,y->left)) y->left->parent = y; x->parent = y->parent; if (NIL(tree,y->parent)) tree->root = x; else { if (y->parent->left == y) y->parent->left = x; else y->parent->right = x; } x->right = y; y->parent = x; } /* insert a new node */ static rbtree_node *rbtree_insert_new(rbtree_tree *tree,void *key,void *value, int *exists) { rbtree_node *parent,*node,*new_node,**nodeplace; int comp; nodeplace = &tree->root; parent = NULL; *exists = FALSE; for(;;) { node = *nodeplace; if (NIL(tree,node)) break; comp = tree->key_cmp(key,node->key,tree->opt_data); if (!comp) { *exists = TRUE; node->value = value; return node; } parent = node; nodeplace = (comp > 0) ? &node->right : &node->left; } /* create a new node */ if (!(new_node = rbtree_node_alloc(tree,key,value))) return NULL; *nodeplace = new_node; new_node->parent = parent; tree->node_count++; return new_node; } /* Insert a node in a Red/Black Tree */ int rbtree_insert(rbtree_tree *tree,void *key,void *value) { rbtree_node *x,*y; int exists; /* insert a new node (if necessary) */ x = rbtree_insert_new(tree,key,value,&exists); if (exists) return(0); if (!x) return(-1); tree->node_count++; /* maintains red-black properties */ x->color = RBTREE_RED; while((x != tree->root) && (x->parent->color == RBTREE_RED)) { if (x->parent == x->parent->parent->left) { y = x->parent->parent->right; if (y->color == RBTREE_RED) { x->parent->color = RBTREE_BLACK; y->color = RBTREE_BLACK; x->parent->parent->color = RBTREE_RED; x = x->parent->parent; } else { if (x == x->parent->right) { x = x->parent; rbtree_left_rotate(tree,x); } x->parent->color = RBTREE_BLACK; x->parent->parent->color = RBTREE_RED; rbtree_right_rotate(tree,x->parent->parent); } } else { y = x->parent->parent->left; if (y->color == RBTREE_RED) { x->parent->color = RBTREE_BLACK; y->color = RBTREE_BLACK; x->parent->parent->color = RBTREE_RED; x = x->parent->parent; } else { if (x == x->parent->left) { x = x->parent; rbtree_right_rotate(tree,x); } x->parent->color = RBTREE_BLACK; x->parent->parent->color = RBTREE_RED; rbtree_left_rotate(tree,x->parent->parent); } } } tree->root->color = RBTREE_BLACK; return(0); } /* Lookup for a node corresponding to "key" */ static inline rbtree_node *rbtree_lookup_node(rbtree_tree *tree,void *key) { rbtree_node *node; int comp; node = tree->root; for (;;) { if (NIL(tree,node)) /* key not found */ break; if (!(comp = tree->key_cmp(key,node->key,tree->opt_data))) break; /* exact match */ node = (comp > 0) ? node->right : node->left; } return(node); } /* * Lookup for a node corresponding to "key". If node does not exist, * function returns null pointer. */ void *rbtree_lookup(rbtree_tree *tree,void *key) { return(rbtree_lookup_node(tree,key)->value); } /* Restore Red/black tree properties after a removal */ static void rbtree_removal_fixup(rbtree_tree *tree,rbtree_node *x) { rbtree_node *w; while((x != tree->root) && (x->color == RBTREE_BLACK)) { if (x == x->parent->left) { w = x->parent->right; if (w->color == RBTREE_RED) { w->color = RBTREE_BLACK; x->parent->color = RBTREE_RED; rbtree_left_rotate(tree,x->parent); w = x->parent->right; } if ((w->left->color == RBTREE_BLACK) && (w->right->color == RBTREE_BLACK)) { w->color = RBTREE_RED; x = x->parent; } else { if (w->right->color == RBTREE_BLACK) { w->left->color = RBTREE_BLACK; w->color = RBTREE_RED; rbtree_right_rotate(tree,w); w = x->parent->right; } w->color = x->parent->color; x->parent->color = RBTREE_BLACK; w->right->color = RBTREE_BLACK; rbtree_left_rotate(tree,x->parent); x = tree->root; } } else { w = x->parent->left; if (w->color == RBTREE_RED) { w->color = RBTREE_BLACK; x->parent->color = RBTREE_RED; rbtree_right_rotate(tree,x->parent); w = x->parent->left; } if ((w->right->color == RBTREE_BLACK) && (w->left->color == RBTREE_BLACK)) { w->color = RBTREE_RED; x = x->parent; } else { if (w->left->color == RBTREE_BLACK) { w->right->color = RBTREE_BLACK; w->color = RBTREE_RED; rbtree_left_rotate(tree,w); w = x->parent->left; } w->color = x->parent->color; x->parent->color = RBTREE_BLACK; w->left->color = RBTREE_BLACK; rbtree_right_rotate(tree,x->parent); x = tree->root; } } } x->color = RBTREE_BLACK; } /* Removes a node out of a tree */ void *rbtree_remove(rbtree_tree *tree,void *key) { rbtree_node *z = rbtree_lookup_node(tree,key); rbtree_node *x,*y; void *value; if (NIL(tree,z)) return NULL; value = z->value; if (NIL(tree,z->left) || NIL(tree,z->right)) y = z; else y = rbtree_successor(tree,z); if (!NIL(tree,y->left)) x = y->left; else x = y->right; x->parent = y->parent; if (NIL(tree,y->parent)) tree->root = x; else { if (y == y->parent->left) y->parent->left = x; else y->parent->right = x; } if (y != z) { z->key = y->key; z->value = y->value; } if (y->color == RBTREE_BLACK) rbtree_removal_fixup(tree,x); rbtree_node_free(tree,y); tree->node_count++; return(value); } static void rbtree_foreach_node(rbtree_tree *tree,rbtree_node *node, tree_fforeach user_fn,void *opt) { if (!NIL(tree,node)) { rbtree_foreach_node(tree,node->left,user_fn,opt); user_fn(node->key,node->value,opt); rbtree_foreach_node(tree,node->right,user_fn,opt); } } /* Call the specified function for each node */ int rbtree_foreach(rbtree_tree *tree,tree_fforeach user_fn,void *opt) { if (!tree) return(-1); rbtree_foreach_node(tree,tree->root,user_fn,opt); return(0); } /* Returns the maximum height of the right and left sub-trees */ static int rbtree_height_node(rbtree_tree *tree,rbtree_node *node) { int lh,rh; lh = (!NIL(tree,node->left)) ? rbtree_height_node(tree,node->left) : 0; rh = (!NIL(tree,node->right)) ? rbtree_height_node(tree,node->right) : 0; return(1 + m_max(lh,rh)); } /* Compute the height of a Red/Black tree */ int rbtree_height(rbtree_tree *tree) { return(!NIL(tree,tree->root) ? rbtree_height_node(tree,tree->root) : 0); } /* Returns the number of nodes */ int rbtree_node_count(rbtree_tree *tree) { return(tree->node_count); } /* Purge all nodes */ void rbtree_purge(rbtree_tree *tree) { mp_free_all_blocks(&tree->mp); tree->node_count = 0; /* just in case */ memset(rbtree_nil(tree),0,sizeof(rbtree_node)); rbtree_nil(tree)->color = RBTREE_BLACK; /* reset root */ tree->root = rbtree_nil(tree); } /* Check a node */ static int rbtree_check_node(rbtree_tree *tree,rbtree_node *node) { if (!NIL(tree,node)) return(0); if (!NIL(tree,node->left)) { if (tree->key_cmp(node->key,node->left->key,tree->opt_data) <= 0) return(-1); if (rbtree_check_node(tree,node->left) == -1) return(-1); } if (!NIL(tree,node->right)) { if (tree->key_cmp(node->key,node->right->key,tree->opt_data) >= 0) return(-1); if (rbtree_check_node(tree,node->right) == -1) return(-1); } return(0); } /* Check tree consistency */ int rbtree_check(rbtree_tree *tree) { return(rbtree_check_node(tree,tree->root)); } /* Create a new Red/Black tree */ rbtree_tree *rbtree_create(tree_fcompare key_cmp,void *opt_data) { rbtree_tree *tree; if (!(tree = malloc(sizeof(*tree)))) return NULL; memset(tree,0,sizeof(*tree)); /* initialize the memory pool */ if (!mp_create_fixed_pool(&tree->mp,"Red-Black Tree")) { free(tree); return NULL; } /* initialize the "nil" pointer */ memset(rbtree_nil(tree),0,sizeof(rbtree_node)); rbtree_nil(tree)->color = RBTREE_BLACK; tree->key_cmp = key_cmp; tree->opt_data = opt_data; tree->root = rbtree_nil(tree); return tree; } /* Delete a Red/Black tree */ void rbtree_delete(rbtree_tree *tree) { if (tree) { mp_free_pool(&tree->mp); free(tree); } } dynamips-0.2.14/common/rbtree.h000066400000000000000000000046641241034141600163540ustar00rootroot00000000000000/* * IPFlow Collector * Copyright (c) 2004 Christophe Fillot. * E-mail: cf@utc.fr * * rbtree.c: Red/Black Trees. */ #ifndef __RBTREE_H__ #define __RBTREE_H__ 1 static const char rcsid_rbtree[] = "$Id$"; #include #include "mempool.h" /* Comparison function for 2 keys */ typedef int (*tree_fcompare)(void *key1,void *key2,void *opt); /* User function to call when using rbtree_foreach */ typedef void (*tree_fforeach)(void *key,void *value,void *opt); /* Node colors */ enum { RBTREE_RED = 0, RBTREE_BLACK, }; /* * Description of a node in a Red/Black tree. To be more efficient, keys are * stored with a void * pointer, allowing to use different type keys. */ typedef struct rbtree_node rbtree_node; struct rbtree_node { /* Key and Value */ void *key,*value; /* Left and right nodes */ rbtree_node *left,*right; /* Parent node */ rbtree_node *parent; /* Node color */ short color; }; /* * Description of a Red/Black tree. For commodity, a name can be given to the * tree. "rbtree_comp" is a pointer to a function, defined by user, which * compares keys during node operations. */ typedef struct rbtree_tree rbtree_tree; struct rbtree_tree { int node_count; /* Number of Nodes */ mempool_t mp; /* Memory pool */ rbtree_node nil; /* Sentinel */ rbtree_node *root; /* Root node */ tree_fcompare key_cmp; /* Key comparison function */ void *opt_data; /* Optional data for comparison */ }; /* Insert a node in an Red/Black tree */ int rbtree_insert(rbtree_tree *tree,void *key,void *value); /* Removes a node out of a tree */ void *rbtree_remove(rbtree_tree *tree,void *key); /* * Lookup for a node corresponding to "key". If node does not exist, * function returns null pointer. */ void *rbtree_lookup(rbtree_tree *tree,void *key); /* Call the specified function for each node */ int rbtree_foreach(rbtree_tree *tree,tree_fforeach user_fn,void *opt); /* Compute the height of a Red/Black tree */ int rbtree_height(rbtree_tree *tree); /* Returns the number of nodes */ int rbtree_node_count(rbtree_tree *tree); /* Purge all nodes */ void rbtree_purge(rbtree_tree *tree); /* Check tree consistency */ int rbtree_check(rbtree_tree *tree); /* Create a new Red/Black tree */ rbtree_tree *rbtree_create(tree_fcompare key_cmp,void *opt_data); /* Delete an Red/Black tree */ void rbtree_delete(rbtree_tree *tree); #endif dynamips-0.2.14/common/registry.c000066400000000000000000000246211241034141600167270ustar00rootroot00000000000000/* * IPFlow Collector * Copyright (c) 2003 Christophe Fillot. * E-mail: cf@utc.fr * * registry.c: Object Registry. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "hash.h" #include "mempool.h" #include "registry.h" #define DEBUG_REGISTRY 0 static registry_t *registry = NULL; #define REGISTRY_LOCK() pthread_mutex_lock(®istry->lock) #define REGISTRY_UNLOCK() pthread_mutex_unlock(®istry->lock) /* Terminate the registry */ static void registry_terminate(void) { mp_free(registry->ht_types); mp_free(registry->ht_names); mp_free_pool(®istry->mp); pthread_mutex_destroy(®istry->lock); free(registry); registry = NULL; } /* Initialize registry */ int registry_init(void) { registry_entry_t *p; pthread_mutexattr_t attr; size_t len; int i; registry = malloc(sizeof(*registry)); assert(registry != NULL); pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(®istry->lock,&attr); pthread_mutexattr_destroy(&attr); /* initialize registry memory pool */ mp_create_fixed_pool(®istry->mp,"registry"); registry->ht_name_entries = REGISTRY_HT_NAME_ENTRIES; registry->ht_type_entries = REGISTRY_MAX_TYPES; /* initialize hash table for names, with sentinels */ len = registry->ht_name_entries * sizeof(registry_entry_t); registry->ht_names = mp_alloc(®istry->mp,len); assert(registry->ht_names != NULL); for(i=0;iht_name_entries;i++) { p = ®istry->ht_names[i]; p->hname_next = p->hname_prev = p; } /* initialize hash table for types, with sentinels */ len = registry->ht_type_entries * sizeof(registry_entry_t); registry->ht_types = mp_alloc(®istry->mp,len); assert(registry->ht_types != NULL); for(i=0;iht_type_entries;i++) { p = ®istry->ht_types[i]; p->htype_next = p->htype_prev = p; } atexit(registry_terminate); return(0); } /* Insert a new entry */ static void registry_insert_entry(registry_entry_t *entry) { registry_entry_t *bucket; u_int h_index; /* insert new entry in hash table for names */ h_index = str_hash(entry->name) % registry->ht_name_entries; bucket = ®istry->ht_names[h_index]; entry->hname_next = bucket->hname_next; entry->hname_prev = bucket; bucket->hname_next->hname_prev = entry; bucket->hname_next = entry; /* insert new entry in hash table for object types */ bucket = ®istry->ht_types[entry->object_type]; entry->htype_next = bucket->htype_next; entry->htype_prev = bucket; bucket->htype_next->htype_prev = entry; bucket->htype_next = entry; } /* Detach a registry entry */ static void registry_detach_entry(registry_entry_t *entry) { entry->hname_prev->hname_next = entry->hname_next; entry->hname_next->hname_prev = entry->hname_prev; entry->htype_prev->htype_next = entry->htype_next; entry->htype_next->htype_prev = entry->htype_prev; } /* Remove a registry entry */ static void registry_remove_entry(registry_entry_t *entry) { registry_detach_entry(entry); mp_free(entry); } /* Locate an entry */ static inline registry_entry_t *registry_find_entry(char *name,int object_type) { registry_entry_t *entry,*bucket; u_int h_index; h_index = str_hash(name) % registry->ht_name_entries; bucket = ®istry->ht_names[h_index]; for(entry=bucket->hname_next;entry!=bucket;entry=entry->hname_next) if (!strcmp(entry->name,name) && (entry->object_type == object_type)) return entry; return NULL; } /* Add a new entry to the registry */ int registry_add(char *name,int object_type,void *data) { registry_entry_t *entry; if (!name) return(-1); REGISTRY_LOCK(); /* check if we have already a reference for this name */ if ((entry = registry_find_entry(name,object_type))) { REGISTRY_UNLOCK(); return(-1); } /* create a new entry */ if (!(entry = mp_alloc(®istry->mp,sizeof(*entry)))) { REGISTRY_UNLOCK(); return(-1); } entry->name = name; entry->data = data; entry->object_type = object_type; entry->ref_count = 1; /* consider object is referenced by the caller */ registry_insert_entry(entry); #if DEBUG_REGISTRY printf("Registry: object %s: ref_count = %d after add.\n", entry->name, entry->ref_count); #endif REGISTRY_UNLOCK(); return(0); } /* Delete an entry from the registry */ int registry_delete(char *name,int object_type) { registry_entry_t *entry; if (!name) return(-1); REGISTRY_LOCK(); if (!(entry = registry_find_entry(name,object_type))) { REGISTRY_UNLOCK(); return(-1); } /* if the entry is referenced, just decrement ref counter */ if (--entry->ref_count > 0) { #if DEBUG_REGISTRY printf("Registry: object %s: ref_count = %d after delete.\n", entry->name, entry->ref_count); #endif REGISTRY_UNLOCK(); return(0); } registry_remove_entry(entry); REGISTRY_UNLOCK(); return(0); } /* Rename an entry in the registry */ int registry_rename(char *name,char *newname,int object_type) { registry_entry_t *entry; if (!name || !newname) return(-1); REGISTRY_LOCK(); if (!(entry = registry_find_entry(name,object_type))) { REGISTRY_UNLOCK(); return(-1); } if (registry_find_entry(newname,object_type)) { REGISTRY_UNLOCK(); return(-1); } registry_detach_entry(entry); entry->name = newname; registry_insert_entry(entry); REGISTRY_UNLOCK(); return(0); } /* Find an entry (increment the reference count) */ void *registry_find(char *name,int object_type) { registry_entry_t *entry; void *data; if (!name) return NULL; REGISTRY_LOCK(); if ((entry = registry_find_entry(name,object_type))) { entry->ref_count++; #if DEBUG_REGISTRY printf("Registry: object %s: ref_count = %d after find.\n", entry->name, entry->ref_count); #endif data = entry->data; } else data = NULL; REGISTRY_UNLOCK(); return data; } /* Check if entry exists (does not change reference count) */ void *registry_exists(char *name,int object_type) { registry_entry_t *entry; void *data = NULL; if (!name) return NULL; REGISTRY_LOCK(); entry = registry_find_entry(name,object_type); if (entry) data = entry->data; REGISTRY_UNLOCK(); return data; } /* Release a reference of an entry (decrement the reference count) */ int registry_unref(char *name,int object_type) { registry_entry_t *entry; int res = -1; if (!name) return(-1); REGISTRY_LOCK(); if ((entry = registry_find_entry(name,object_type))) { entry->ref_count--; #if DEBUG_REGISTRY printf("Registry: object %s: ref_count = %d after unref.\n", name, entry->ref_count); #endif if (entry->ref_count < 0) { fprintf(stderr,"Registry: object %s (type %d): negative ref_count.\n", name, object_type); } else res = 0; } REGISTRY_UNLOCK(); return(res); } /* * Execute action on an object if its reference count is less or equal to * the specified count. */ int registry_exec_refcount(char *name,int object_type,int max_ref,int reg_del, registry_exec obj_action,void *opt_arg) { registry_entry_t *entry; int res = -1; int status; if (!name) return(-1); REGISTRY_LOCK(); entry = registry_find_entry(name,object_type); if (entry) { if (entry->ref_count <= max_ref) { status = TRUE; if (obj_action != NULL) status = obj_action(entry->data,opt_arg); if (reg_del && status) registry_remove_entry(entry); res = 1; } else res = 0; } REGISTRY_UNLOCK(); return(res); } /* Delete object if unused */ int registry_delete_if_unused(char *name,int object_type, registry_exec obj_destructor,void *opt_arg) { return(registry_exec_refcount(name,object_type,0,TRUE, obj_destructor,opt_arg)); } /* Execute a callback function for all objects of specified type */ int registry_foreach_type(int object_type,registry_foreach cb, void *opt,int *err) { registry_entry_t *p,*bucket,*next; int count = 0; REGISTRY_LOCK(); bucket = ®istry->ht_types[object_type]; for(p=bucket->htype_next;p!=bucket;p=next) { next = p->htype_next; if (cb) cb(p,opt,err); count++; } REGISTRY_UNLOCK(); return(count); } /* Delete all objects of the specified type */ int registry_delete_type(int object_type,registry_exec cb,void *opt) { registry_entry_t *p,*bucket,*next; int count = 0; int status; REGISTRY_LOCK(); bucket = ®istry->ht_types[object_type]; for(p=bucket->htype_next;p!=bucket;p=next) { next = p->htype_next; if (p->ref_count == 0) { status = TRUE; if (cb != NULL) status = cb(p->data,opt); if (status) { registry_remove_entry(p); count++; } } else { fprintf(stderr,"registry_delete_type: object \"%s\" (type %d) still " "referenced (count=%d)\n",p->name,object_type,p->ref_count); } } REGISTRY_UNLOCK(); return(count); } /* Dump the registry */ void registry_dump(void) { registry_entry_t *p,*bucket; int i; REGISTRY_LOCK(); printf("Registry dump:\n"); printf(" Objects (from name hash table):\n"); /* dump hash table of names */ for(i=0;iht_name_entries;i++) { bucket = ®istry->ht_names[i]; for(p=bucket->hname_next;p!=bucket;p=p->hname_next) printf(" %s (type %d, ref_count=%d)\n", p->name,p->object_type,p->ref_count); } printf("\n Objects classed by types:\n"); /* dump hash table of types */ for(i=0;iht_type_entries;i++) { printf(" Type %d: ",i); bucket = ®istry->ht_types[i]; for(p=bucket->htype_next;p!=bucket;p=p->htype_next) printf("%s(%d) ",p->name,p->ref_count); printf("\n"); } REGISTRY_UNLOCK(); } dynamips-0.2.14/common/registry.h000066400000000000000000000057771241034141600167470ustar00rootroot00000000000000/* * IPFlow Collector * Copyright (c) 2003 Christophe Fillot. * E-mail: cf@utc.fr * * registry.h: Object Registry. */ #ifndef __REGISTRY_H__ #define __REGISTRY_H__ 1 static const char rcsid_registry[] = "$Id$"; #include #include #include #include "mempool.h" #define REGISTRY_HT_NAME_ENTRIES 1024 #define REGISTRY_MAX_TYPES 256 /* Object types for Registry */ enum { OBJ_TYPE_VM, /* Virtual machine */ OBJ_TYPE_NIO, /* Network IO descriptor */ OBJ_TYPE_NIO_BRIDGE, /* Network IO bridge */ OBJ_TYPE_FRSW, /* Frame-Relay switch */ OBJ_TYPE_ATMSW, /* ATM switch */ OBJ_TYPE_ATM_BRIDGE, /* ATM bridge */ OBJ_TYPE_ETHSW, /* Ethernet switch */ OBJ_TYPE_STORE, /* Hypervisor store */ }; /* Registry entry */ typedef struct registry_entry registry_entry_t; struct registry_entry { char *name; void *data; int object_type; int ref_count; registry_entry_t *hname_next,*hname_prev; registry_entry_t *htype_next,*htype_prev; }; /* Registry info */ typedef struct registry registry_t; struct registry { pthread_mutex_t lock; mempool_t mp; int ht_name_entries,ht_type_entries; registry_entry_t *ht_names; /* Hash table for names */ registry_entry_t *ht_types; /* Hash table for types */ }; /* Registry "foreach" callback */ typedef void (*registry_foreach)(registry_entry_t *entry,void *opt_arg, int *err); /* Registry "exec" callback */ typedef int (*registry_exec)(void *data,void *opt_arg); /* Initialize registry */ int registry_init(void); /* Add a new entry to the registry */ int registry_add(char *name,int object_type,void *data); /* Delete an entry from the registry */ int registry_delete(char *name,int object_type); /* Rename an entry in the registry */ int registry_rename(char *name,char *newname,int object_type); /* Find an entry (increment reference count) */ void *registry_find(char *name,int object_type); /* Check if entry exists (does not change reference count) */ void *registry_exists(char *name,int object_type); /* Release a reference of an entry (decrement the reference count) */ int registry_unref(char *name,int object_type); /* * Execute action on an object if its reference count is less or equal to * the specified count. */ int registry_exec_refcount(char *name,int object_type,int max_ref,int reg_del, registry_exec obj_action,void *opt_arg); /* Delete object if unused */ int registry_delete_if_unused(char *name,int object_type, registry_exec obj_destructor, void *opt_arg); /* Execute a callback function for all objects of specified type */ int registry_foreach_type(int object_type,registry_foreach cb, void *opt,int *err); /* Delete all objects of the specified type */ int registry_delete_type(int object_type,registry_exec cb,void *opt); /* Dump the registry */ void registry_dump(void); #endif dynamips-0.2.14/common/rom2c.c000066400000000000000000000042611241034141600160770ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) */ #include #include #include #include #include #include #include #include #include #include #include "utils.h" /* Extract ROM code+data from an ELF file and convert it into a C array */ int main(int argc,char *argv[]) { unsigned char buffer[8]; m_uint32_t vaddr,start; Elf32_Ehdr *ehdr; Elf32_Phdr *phdr; Elf *img_elf; size_t len,clen; int i,j,fd; FILE *bfd,*fd_out; if (argc != 4) { fprintf(stderr,"Usage: %s \n",argv[0]); exit(EXIT_FAILURE); } start = strtoul(argv[3],NULL,0); if ((fd = open(argv[1],O_RDONLY)) == -1) return(-1); if (elf_version(EV_CURRENT) == EV_NONE) { fprintf(stderr,"load_elf_image: library out of date\n"); return(-1); } if (!(img_elf = elf_begin(fd,ELF_C_READ,NULL))) { fprintf(stderr,"load_elf_image: elf_begin: %s\n", elf_errmsg(elf_errno())); return(-1); } if (!(phdr = elf32_getphdr(img_elf))) { fprintf(stderr,"load_elf_image: elf32_getphdr: %s\n", elf_errmsg(elf_errno())); return(-1); } if (!(fd_out = fopen(argv[2],"w"))) { fprintf(stderr,"Unable to create file \"%s\"\n",argv[2]); exit(EXIT_FAILURE); } ehdr = elf32_getehdr(img_elf); phdr = elf32_getphdr(img_elf); printf("Extracting ROM from ELF file '%s'...\n",argv[1]); bfd = fdopen(fd,"r"); if (!bfd) { perror("load_elf_image: fdopen"); return(-1); } for(i=0;ie_phnum;i++,phdr++) { fseek(bfd,phdr->p_offset,SEEK_SET); vaddr = (m_uint64_t)phdr->p_vaddr; len = phdr->p_filesz; if (vaddr != start) continue; while(len > 0) { clen = fread(buffer,1,sizeof(buffer),bfd); if (clen == 0) break; fprintf(fd_out," "); for(j=0;jfilename) return(-1); if (!(fd = fopen(rvl->filename,"r"))) { #if DEBUG_OPEN fprintf(stderr,"%s: unable to open file %s (%s)\n", __func__,rvl->filename,strerror(errno)); #endif return(-1); } while(!feof(fd)) { if (m_fgets(buffer,sizeof(buffer),fd)) rommon_var_add_str(rvl,buffer); } fclose(fd); return(0); } /* Write a file with all ROMMON variables */ int rommon_var_update_file(struct rommon_var_list *rvl) { struct rommon_var *var; FILE *fd; if (!rvl->filename) return(-1); if (!(fd = fopen(rvl->filename,"w"))) { fprintf(stderr,"%s: unable to create file %s (%s)\n", __func__,rvl->filename,strerror(errno)); return(-1); } for(var=rvl->var_list;var;var=var->next) fprintf(fd,"%s=%s\n",var->name,var->value ? var->value : ""); fclose(fd); return(0); } /* Find the specified variable */ struct rommon_var *rommon_var_find(struct rommon_var_list *rvl,char *name) { struct rommon_var *var; for(var=rvl->var_list;var;var=var->next) if (!strcmp(var->name,name)) return var; return NULL; } /* Create a new variable */ static struct rommon_var *rommon_var_create(char *name) { struct rommon_var *var; if (!(var = malloc(sizeof(*var)))) return NULL; var->next = NULL; var->value = NULL; var->name = strdup(name); if (!var->name) { free(var); return NULL; } return var; } /* Set value for a variable */ static int rommon_var_set(struct rommon_var *var,char *value) { char *new_value; if (!(new_value = strdup(value))) return(-1); /* free old value */ if (var->value) free(var->value); var->value = new_value; return(0); } /* Add a new variable */ int rommon_var_add(struct rommon_var_list *rvl,char *name,char *value) { struct rommon_var *var; /* if the variable already exists, overwrite it */ if (!(var = rommon_var_find(rvl,name))) { var = rommon_var_create(name); if (!var) return(-1); if (rommon_var_set(var,value) == -1) return(-1); var->next = rvl->var_list; rvl->var_list = var; } else { rommon_var_set(var,value); } /* synchronize disk file */ return(rommon_var_update_file(rvl)); } /* * Add a new variable, specified at the format: var=value. * The string is modified. */ int rommon_var_add_str(struct rommon_var_list *rvl,char *str) { char *eq_sym; if (!(eq_sym = strchr(str,'='))) return(-1); /* The variable cannot be null */ if (str == eq_sym) return(-1); *eq_sym = 0; return(rommon_var_add(rvl,str,eq_sym+1)); } /* Get the specified variable */ int rommon_var_get(struct rommon_var_list *rvl,char *name, char *buffer,size_t len) { struct rommon_var *var; if (!(var = rommon_var_find(rvl,name)) || !var->value) return(-1); strncpy(buffer,var->value,len-1); buffer[len-1] = '\0'; return(0); } /* Clear all the variables */ void rommon_var_clear(struct rommon_var_list *rvl) { struct rommon_var *var, *next_var; if (!rvl) return; for (var = rvl->var_list; var; var = next_var) { next_var = var->next; free(var->value); free(var->name); free(var); } rvl->var_list = NULL; } dynamips-0.2.14/common/rommon_var.h000066400000000000000000000017631241034141600172450ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * ROMMON Environment Variables. */ #ifndef __ROMMON_VAR_H__ #define __ROMMON_VAR_H__ #include "dynamips_common.h" /* ROMMON variable */ struct rommon_var { struct rommon_var *next; char *name; char *value; }; /* List of ROMMON variables */ struct rommon_var_list { char *filename; struct rommon_var *var_list; }; /* Load file containing ROMMON variables */ int rommon_load_file(struct rommon_var_list *rvl); /* Add a new variable */ int rommon_var_add(struct rommon_var_list *rvl,char *name,char *value); /* * Add a new variable, specified at the format: var=value. * The string is modified. */ int rommon_var_add_str(struct rommon_var_list *rvl,char *str); /* Get the specified variable */ int rommon_var_get(struct rommon_var_list *rvl,char *name, char *buffer,size_t len); /* Clear all the variables */ void rommon_var_clear(struct rommon_var_list *rvl); #endif dynamips-0.2.14/common/sbox.c000066400000000000000000000066271241034141600160400ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * S-Box: http://bretm.home.comcast.net/hash/10.html */ #include "dynamips_common.h" m_uint32_t sbox_array[] = { 0xF53E1837, 0x5F14C86B, 0x9EE3964C, 0xFA796D53, 0x32223FC3, 0x4D82BC98, 0xA0C7FA62, 0x63E2C982, 0x24994A5B, 0x1ECE7BEE, 0x292B38EF, 0xD5CD4E56, 0x514F4303, 0x7BE12B83, 0x7192F195, 0x82DC7300, 0x084380B4, 0x480B55D3, 0x5F430471, 0x13F75991, 0x3F9CF22C, 0x2FE0907A, 0xFD8E1E69, 0x7B1D5DE8, 0xD575A85C, 0xAD01C50A, 0x7EE00737, 0x3CE981E8, 0x0E447EFA, 0x23089DD6, 0xB59F149F, 0x13600EC7, 0xE802C8E6, 0x670921E4, 0x7207EFF0, 0xE74761B0, 0x69035234, 0xBFA40F19, 0xF63651A0, 0x29E64C26, 0x1F98CCA7, 0xD957007E, 0xE71DDC75, 0x3E729595, 0x7580B7CC, 0xD7FAF60B, 0x92484323, 0xA44113EB, 0xE4CBDE08, 0x346827C9, 0x3CF32AFA, 0x0B29BCF1, 0x6E29F7DF, 0xB01E71CB, 0x3BFBC0D1, 0x62EDC5B8, 0xB7DE789A, 0xA4748EC9, 0xE17A4C4F, 0x67E5BD03, 0xF3B33D1A, 0x97D8D3E9, 0x09121BC0, 0x347B2D2C, 0x79A1913C, 0x504172DE, 0x7F1F8483, 0x13AC3CF6, 0x7A2094DB, 0xC778FA12, 0xADF7469F, 0x21786B7B, 0x71A445D0, 0xA8896C1B, 0x656F62FB, 0x83A059B3, 0x972DFE6E, 0x4122000C, 0x97D9DA19, 0x17D5947B, 0xB1AFFD0C, 0x6EF83B97, 0xAF7F780B, 0x4613138A, 0x7C3E73A6, 0xCF15E03D, 0x41576322, 0x672DF292, 0xB658588D, 0x33EBEFA9, 0x938CBF06, 0x06B67381, 0x07F192C6, 0x2BDA5855, 0x348EE0E8, 0x19DBB6E3, 0x3222184B, 0xB69D5DBA, 0x7E760B88, 0xAF4D8154, 0x007A51AD, 0x35112500, 0xC9CD2D7D, 0x4F4FB761, 0x694772E3, 0x694C8351, 0x4A7E3AF5, 0x67D65CE1, 0x9287DE92, 0x2518DB3C, 0x8CB4EC06, 0xD154D38F, 0xE19A26BB, 0x295EE439, 0xC50A1104, 0x2153C6A7, 0x82366656, 0x0713BC2F, 0x6462215A, 0x21D9BFCE, 0xBA8EACE6, 0xAE2DF4C1, 0x2A8D5E80, 0x3F7E52D1, 0x29359399, 0xFEA1D19C, 0x18879313, 0x455AFA81, 0xFADFE838, 0x62609838, 0xD1028839, 0x0736E92F, 0x3BCA22A3, 0x1485B08A, 0x2DA7900B, 0x852C156D, 0xE8F24803, 0x00078472, 0x13F0D332, 0x2ACFD0CF, 0x5F747F5C, 0x87BB1E2F, 0xA7EFCB63, 0x23F432F0, 0xE6CE7C5C, 0x1F954EF6, 0xB609C91B, 0x3B4571BF, 0xEED17DC0, 0xE556CDA0, 0xA7846A8D, 0xFF105F94, 0x52B7CCDE, 0x0E33E801, 0x664455EA, 0xF2C70414, 0x73E7B486, 0x8F830661, 0x8B59E826, 0xBB8AEDCA, 0xF3D70AB9, 0xD739F2B9, 0x4A04C34A, 0x88D0F089, 0xE02191A2, 0xD89D9C78, 0x192C2749, 0xFC43A78F, 0x0AAC88CB, 0x9438D42D, 0x9E280F7A, 0x36063802, 0x38E8D018, 0x1C42A9CB, 0x92AAFF6C, 0xA24820C5, 0x007F077F, 0xCE5BC543, 0x69668D58, 0x10D6FF74, 0xBE00F621, 0x21300BBE, 0x2E9E8F46, 0x5ACEA629, 0xFA1F86C7, 0x52F206B8, 0x3EDF1A75, 0x6DA8D843, 0xCF719928, 0x73E3891F, 0xB4B95DD6, 0xB2A42D27, 0xEDA20BBF, 0x1A58DBDF, 0xA449AD03, 0x6DDEF22B, 0x900531E6, 0x3D3BFF35, 0x5B24ABA2, 0x472B3E4C, 0x387F2D75, 0x4D8DBA36, 0x71CB5641, 0xE3473F3F, 0xF6CD4B7F, 0xBF7D1428, 0x344B64D0, 0xC5CDFCB6, 0xFE2E0182, 0x2C37A673, 0xDE4EB7A3, 0x63FDC933, 0x01DC4063, 0x611F3571, 0xD167BFAF, 0x4496596F, 0x3DEE0689, 0xD8704910, 0x7052A114, 0x068C9EC5, 0x75D0E766, 0x4D54CC20, 0xB44ECDE2, 0x4ABC653E, 0x2C550A21, 0x1A52C0DB, 0xCFED03D0, 0x119BAFE2, 0x876A6133, 0xBC232088, 0x435BA1B2, 0xAE99BBFA, 0xBB4F08E4, 0xA62B5F49, 0x1DA4B695, 0x336B84DE, 0xDC813D31, 0x00C134FB, 0x397A98E6, 0x151F0E64, 0xD9EB3E69, 0xD3C7DF60, 0xD2F2C336, 0x2DDD067B, 0xBD122835, 0xB0B3BD3A, 0xB0D54E46, 0x8641F1E4, 0xA0B38F96, 0x51D39199, 0x37A6AD75, 0xDF84EE41, 0x3C034CBA, 0xACDA62FC, 0x11923B8B, 0x45EF170A, }; dynamips-0.2.14/common/sbox.h000066400000000000000000000014131241034141600160310ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2007 Christophe Fillot (cf@utc.fr) * * S-box functions. */ #ifndef __SBOX_H__ #define __SBOX_H__ #include "dynamips_common.h" extern m_uint32_t sbox_array[]; static inline m_uint32_t sbox_compute(m_uint8_t *data,int len) { m_uint32_t hash = 0; while(len > 0) { hash ^= sbox_array[*data]; hash *= 3; data++; } return(hash); } static forced_inline m_uint32_t sbox_u32(m_uint32_t val) { m_uint32_t hash = 0; hash ^= sbox_array[(m_uint8_t)val]; hash *= 3; val >>= 8; hash ^= sbox_array[(m_uint8_t)val]; hash *= 3; val >>= 8; hash ^= sbox_array[(m_uint8_t)val]; hash *= 3; val >>= 8; hash ^= sbox_array[(m_uint8_t)val]; return(hash); } #endif dynamips-0.2.14/common/timer.c000066400000000000000000000321561241034141600162010ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * timer.c: Management of timers. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "mempool.h" #include "hash.h" #include "timer.h" /* Lock and unlock access to global structures */ #define TIMER_LOCK() pthread_mutex_lock(&timer_mutex) #define TIMER_UNLOCK() pthread_mutex_unlock(&timer_mutex) /* Pool of Timer Queues */ static timer_queue_t *timer_queue_pool = NULL; /* Hash table to map Timer ID to timer entries */ static hash_table_t *timer_id_hash = NULL; /* Last ID used. */ static timer_id timer_next_id = 1; /* Mutex to access to global structures (Hash Tables, Pool of queues, ...) */ static pthread_mutex_t timer_mutex = PTHREAD_MUTEX_INITIALIZER; /* Find a timer by its ID */ static inline timer_entry_t *timer_find_by_id(timer_id id) { return(hash_table_lookup(timer_id_hash,&id)); } /* Allocate a new ID. Disgusting method but it should work. */ static inline timer_id timer_alloc_id(void) { while(hash_table_lookup(timer_id_hash,&timer_next_id)) timer_next_id++; return(timer_next_id); } /* Free an ID */ static inline void timer_free_id(timer_id id) { hash_table_remove(timer_id_hash,&id); } /* * Select the queue of the pool that has the lowest criticity level. This * is a stupid method. */ timer_queue_t *timer_select_queue_from_pool(void) { timer_queue_t *s_queue,*queue; int level; /* to begin, select the first queue of the pool */ s_queue = timer_queue_pool; level = s_queue->level; /* walk through timer queues */ for(queue=timer_queue_pool->next;queue;queue=queue->next) { if (queue->level < level) { level = queue->level; s_queue = queue; } } /* returns selected queue */ return s_queue; } /* Add a timer in a queue */ static inline void timer_add_to_queue(timer_queue_t *queue, timer_entry_t *timer) { timer_entry_t *t,*prev = NULL; /* Insert after the last timer with the same or earlier time */ for(t=queue->list;t;t=t->next) { if (t->expire > timer->expire) break; prev = t; } /* Add it in linked list */ timer->next = t; timer->prev = prev; timer->queue = queue; if (timer->next) timer->next->prev = timer; if (timer->prev) timer->prev->next = timer; else queue->list = timer; /* Increment number of timers in queue */ queue->timer_count++; /* Increment criticity level */ queue->level += timer->level; } /* Add a timer in a queue atomically */ _unused static inline void timer_add_to_queue_atomic(timer_queue_t *queue, timer_entry_t *timer) { TIMERQ_LOCK(queue); timer_add_to_queue(queue,timer); TIMERQ_UNLOCK(queue); } /* Remove a timer from queue */ static inline void timer_remove_from_queue(timer_queue_t *queue, timer_entry_t *timer) { if (timer->prev) timer->prev->next = timer->next; else queue->list = timer->next; if (timer->next) timer->next->prev = timer->prev; timer->next = timer->prev = NULL; /* Decrement number of timers in queue */ queue->timer_count--; /* Decrement criticity level */ queue->level -= timer->level; } /* Remove a timer from a queue atomically */ static inline void timer_remove_from_queue_atomic(timer_queue_t *queue,timer_entry_t *timer) { TIMERQ_LOCK(queue); timer_remove_from_queue(queue,timer); TIMERQ_UNLOCK(queue); } /* Free ressources used by a timer */ static inline void timer_free(timer_entry_t *timer,int take_lock) { if (take_lock) TIMER_LOCK(); /* Remove ID from hash table */ hash_table_remove(timer_id_hash,&timer->id); if (take_lock) TIMER_UNLOCK(); /* Free memory used by timer */ free(timer); } /* Run timer action */ static inline int timer_exec(timer_entry_t *timer) { return(timer->callback(timer->user_arg,timer)); } /* Schedule a timer in a queue */ static inline void timer_schedule_in_queue(timer_queue_t *queue, timer_entry_t *timer) { m_tmcnt_t current,current_adj; /* Set new expiration date and clear "run" flag */ if (timer->flags & TIMER_BOUNDARY) { current_adj = m_gettime_adj(); current = m_gettime(); timer->expire = current + timer->offset + (timer->interval - (current_adj % timer->interval)); } else timer->expire += timer->interval; timer->flags &= ~TIMER_RUNNING; timer_add_to_queue(queue,timer); } /* Schedule a timer */ static int timer_schedule(timer_entry_t *timer) { timer_queue_t *queue; /* Select the least used queue of the pool */ if (!(queue = timer_select_queue_from_pool())) { fprintf(stderr, "timer_schedule: no pool available for timer with ID %llu", timer->id); return(-1); } /* Reschedule it in queue */ TIMERQ_LOCK(queue); timer_schedule_in_queue(queue,timer); TIMERQ_UNLOCK(queue); return(0); } /* Timer loop */ static void *timer_loop(timer_queue_t *queue) { struct timespec t_spc; timer_entry_t *timer; m_tmcnt_t c_time; /* Set signal properties */ m_signal_block(SIGINT); m_signal_block(SIGQUIT); m_signal_block(SIGTERM); for(;;) { /* Prevent asynchronous access problems */ TIMERQ_LOCK(queue); /* We need to check "running" flags to know if we must stop */ if (!queue->running) { TIMERQ_UNLOCK(queue); break; } /* Get first event */ timer = queue->list; /* * If we have timers in queue, we setup a timer to wait for first one. * In all cases, thread is woken up when a reschedule occurs. */ if (timer) { t_spc.tv_sec = timer->expire / 1000; t_spc.tv_nsec = (timer->expire % 1000) * 1000000; pthread_cond_timedwait(&queue->schedule,&queue->lock,&t_spc); } else { /* We just wait for reschedule since we don't have any timer */ pthread_cond_wait(&queue->schedule,&queue->lock); } /* We need to check "running" flags to know if we must stop */ if (!queue->running) { TIMERQ_UNLOCK(queue); break; } /* * Now, we need to find why we were woken up. So, we compare current * time with first timer to see if we must execute action associated * with it. */ c_time = m_gettime(); /* Get first event */ timer = queue->list; /* If there is nothing to do for now, wait again */ if ((timer == NULL) || (timer->expire > c_time)) { TIMERQ_UNLOCK(queue); continue; } /* * We have a timer to manage. Remove it from queue and mark it as * running. */ timer_remove_from_queue(queue,timer); timer->flags |= TIMER_RUNNING; /* Execute user function and reschedule timer if required */ if (timer_exec(timer)) timer_schedule_in_queue(queue,timer); TIMERQ_UNLOCK(queue); } return NULL; } /* Remove a timer */ int timer_remove(timer_id id) { timer_queue_t *queue = NULL; timer_entry_t *timer; TIMER_LOCK(); /* Find timer */ if (!(timer = timer_find_by_id(id))) { TIMER_UNLOCK(); return(-1); } /* If we have a queue, remove timer from it atomically */ if (timer->queue) { queue = timer->queue; timer_remove_from_queue_atomic(queue,timer); } /* Release timer ID */ timer_free_id(id); /* Free memory used by timer */ free(timer); TIMER_UNLOCK(); /* Signal to this queue that it has been modified */ if (queue) pthread_cond_signal(&queue->schedule); return(0); } /* Enable a timer */ static timer_id timer_enable(timer_entry_t *timer) { /* Allocate a new ID */ TIMER_LOCK(); timer->id = timer_alloc_id(); /* Insert ID in hash table */ if (hash_table_insert(timer_id_hash,&timer->id,timer) == -1) { TIMER_UNLOCK(); free(timer); return(0); } /* Schedule event */ if (timer_schedule(timer) == -1) { timer_free(timer,FALSE); timer = NULL; TIMER_UNLOCK(); return(0); } /* Returns timer ID */ TIMER_UNLOCK(); pthread_cond_signal(&timer->queue->schedule); return(timer->id); } /* Create a new timer */ timer_id timer_create_entry(m_tmcnt_t interval,int boundary,int level, timer_proc callback,void *user_arg) { timer_entry_t *timer; /* Allocate memory for new timer entry */ if (!(timer = malloc(sizeof(*timer)))) return(0); timer->interval = interval; timer->offset = 0; timer->callback = callback; timer->user_arg = user_arg; timer->flags = 0; timer->level = level; /* Set expiration delay */ if (boundary) { timer->flags |= TIMER_BOUNDARY; } else timer->expire = m_gettime(); return(timer_enable(timer)); } /* Create a timer on boundary, with an offset */ timer_id timer_create_with_offset(m_tmcnt_t interval,m_tmcnt_t offset, int level,timer_proc callback,void *user_arg) { timer_entry_t *timer; /* Allocate memory for new timer entry */ if (!(timer = malloc(sizeof(*timer)))) return(0); timer->interval = interval; timer->offset = 0; timer->callback = callback; timer->user_arg = user_arg; timer->flags = 0; timer->level = level; timer->flags |= TIMER_BOUNDARY; return(timer_enable(timer)); } /* Set a new interval for a timer */ int timer_set_interval(timer_id id,long interval) { timer_queue_t *queue; timer_entry_t *timer; TIMER_LOCK(); /* Locate timer */ if (!(timer = timer_find_by_id(id))) { TIMER_UNLOCK(); return(-1); } queue = timer->queue; TIMERQ_LOCK(queue); /* Compute new expiration date */ timer->interval = interval; timer->expire = m_gettime() + (m_tmcnt_t)interval; timer_remove_from_queue(queue,timer); timer_schedule_in_queue(queue,timer); TIMERQ_UNLOCK(queue); TIMER_UNLOCK(); /* Reschedule */ pthread_cond_signal(&queue->schedule); return(0); } /* Create a new timer queue */ timer_queue_t *timer_create_queue(void) { timer_queue_t *queue; /* Create new queue structure */ if (!(queue = malloc(sizeof(*queue)))) return NULL; queue->running = TRUE; queue->list = NULL; queue->level = 0; /* Create mutex */ if (pthread_mutex_init(&queue->lock,NULL)) goto err_mutex; /* Create condition */ if (pthread_cond_init(&queue->schedule,NULL)) goto err_cond; /* Create thread */ if (pthread_create(&queue->thread,NULL,(void *(*)(void *))timer_loop,queue)) goto err_create; return queue; err_create: pthread_cond_destroy(&queue->schedule); err_cond: pthread_mutex_destroy(&queue->lock); err_mutex: free(queue); return NULL; } /* Flush queues */ void timer_flush_queues(void) { timer_entry_t *timer,*next_timer; timer_queue_t *queue,*next_queue; pthread_t thread; TIMER_LOCK(); for(queue=timer_queue_pool;queue;queue=next_queue) { TIMERQ_LOCK(queue); next_queue = queue->next; thread = queue->thread; /* mark queue as not running */ queue->running = FALSE; /* suppress all timers */ for(timer=queue->list;timer;timer=next_timer) { next_timer = timer->next; timer_free_id(timer->id); free(timer); } /* signal changes to the queue thread */ pthread_cond_signal(&queue->schedule); TIMERQ_UNLOCK(queue); /* wait for thread to terminate */ pthread_join(thread,NULL); pthread_cond_destroy(&queue->schedule); pthread_mutex_destroy(&queue->lock); free(queue); } timer_queue_pool = NULL; TIMER_UNLOCK(); } /* Add a specified number of queues to the pool */ int timer_pool_add_queues(int nr_queues) { timer_queue_t *queue; int i; for(i=0;inext = timer_queue_pool; timer_queue_pool = queue; TIMER_UNLOCK(); } return(0); } /* Terminate timer sub-sytem */ static void timer_terminate(void) { timer_flush_queues(); assert(timer_id_hash); hash_table_delete(timer_id_hash); timer_id_hash = NULL; } /* Initialize timer sub-system */ int timer_init(void) { /* Initialize hash table which maps ID to timer entries */ assert(!timer_id_hash); if (!(timer_id_hash = hash_u64_create(TIMER_HASH_SIZE))) { fprintf(stderr,"timer_init: unable to create hash table."); return(-1); } /* Initialize default queues. If this fails, try to continue. */ if (timer_pool_add_queues(TIMERQ_NUMBER) == -1) { fprintf(stderr, "timer_init: unable to initialize at least one timer queue."); } atexit(timer_terminate); return(0); } dynamips-0.2.14/common/timer.h000066400000000000000000000054471241034141600162110ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * timer.h: Management of timers. */ #ifndef __TIMER_H__ #define __TIMER_H__ 1 #include #include #include "utils.h" /* Default number of Timer Queues */ #define TIMERQ_NUMBER 10 /* Timer definitions */ typedef m_uint64_t timer_id; typedef struct timer_entry timer_entry_t; typedef struct timer_queue timer_queue_t; /* Defines callback function format */ typedef int (*timer_proc)(void *,timer_entry_t *); /* Timer flags */ #define TIMER_DELETED 1 #define TIMER_RUNNING 2 #define TIMER_BOUNDARY 4 /* Number of entries in hash table */ #define TIMER_HASH_SIZE 512 /* Timer properties */ struct timer_entry { long interval; /* Interval in msecs */ m_tmcnt_t expire,offset; /* Next execution date */ timer_proc callback; /* User callback function */ void *user_arg; /* Optional user data */ int flags; /* Flags */ timer_id id; /* Unique identifier */ int level; /* Criticity level */ timer_queue_t *queue; /* Associated Timer Queue */ timer_entry_t *prev,*next; /* Double linked-list */ }; /* Timer Queue */ struct timer_queue { timer_entry_t * volatile list; /* List of timers */ pthread_mutex_t lock; /* Mutex for concurrent accesses */ pthread_cond_t schedule; /* Scheduling condition */ pthread_t thread; /* Thread running timer loop */ int volatile running; /* Running flag */ int timer_count; /* Number of timers */ int level; /* Sum of criticity levels */ timer_queue_t *next; /* Next Timer Queue (for pools) */ }; /* Lock and unlock access to a timer queue */ #define TIMERQ_LOCK(queue) pthread_mutex_lock(&(queue)->lock) #define TIMERQ_UNLOCK(queue) pthread_mutex_unlock(&(queue)->lock) /* Remove a timer */ int timer_remove(timer_id id); /* Create a new timer */ timer_id timer_create_entry(m_tmcnt_t interval,int boundary,int level, timer_proc callback,void *user_arg); /* Create a timer on boundary, with an offset */ timer_id timer_create_with_offset(m_tmcnt_t interval,m_tmcnt_t offset, int level,timer_proc callback, void *user_arg); /* Set a new interval for a timer */ int timer_set_interval(timer_id id,long interval); /* Create a new timer queue */ timer_queue_t *timer_create_queue(void); /* Flush queues */ void timer_flush_queues(void); /* Add a specified number of queues to the pool */ int timer_pool_add_queues(int nr_queues); /* Initialize timer sub-system */ int timer_init(void); #endif dynamips-0.2.14/common/udp_recv.c000066400000000000000000000014561241034141600166670ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #define MAX_PKT_SIZE 2048 int main(int argc,char *argv[]) { char pkt[MAX_PKT_SIZE]; ssize_t pkt_size; int sck = -1; FILE *fd; /* Wait connection */ if (ip_listen(NULL,atoi(argv[2]),SOCK_DGRAM,1,&sck) < 1) { perror("ip_listen"); exit(EXIT_FAILURE); } /* Receive packet and store it */ if ((pkt_size = recvfrom(sck,pkt,sizeof(pkt),0,NULL,NULL)) < 0) { perror("recvfrom"); exit(EXIT_FAILURE); } if (!(fd = fopen(argv[1],"w"))) { perror("fopen"); exit(EXIT_FAILURE); } fwrite(pkt,1,pkt_size,fd); fclose(fd); close(sck); return(0); } dynamips-0.2.14/common/udp_send.c000066400000000000000000000013421241034141600166530ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "utils.h" #include "net.h" #define MAX_PKT_SIZE 2048 int main(int argc,char *argv[]) { char pkt[MAX_PKT_SIZE]; size_t pkt_size; int sck; FILE *fd; if (!(fd = fopen(argv[1],"r"))) { perror("fopen"); exit(EXIT_FAILURE); } /* Read packet from file */ pkt_size = fread(pkt,1,MAX_PKT_SIZE,fd); /* Connect to remote port */ if ((sck = udp_connect(atoi(argv[2]),argv[3],atoi(argv[4]))) < 0) exit(EXIT_FAILURE); /* Send it */ if (send(sck,pkt,pkt_size,0) < 0) exit(EXIT_FAILURE); close(sck); return(0); } dynamips-0.2.14/common/utils.c000066400000000000000000000344531241034141600162230ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot. All rights reserved. * * Utility functions. */ #include "dynamips_common.h" #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __CYGWIN__ #include #endif #include "utils.h" extern FILE *log_file; /* Add an element to a list */ m_list_t *m_list_add(m_list_t **head,void *data) { m_list_t *item; if ((item = malloc(sizeof(*item))) != NULL) { item->data = data; item->next = *head; *head = item; } return item; } /* Dynamic sprintf */ char *dyn_sprintf(const char *fmt,...) { int n,size = 512; va_list ap; char *p,*p2; if ((p = malloc(size)) == NULL) { perror("dyn_sprintf: malloc"); return NULL; } for(;;) { /* Try to print in the allocated space */ va_start(ap,fmt); n = vsnprintf(p,size,fmt,ap); va_end(ap); /* If that worked, return the string */ if ((n > -1) && (n < size)) return p; /* Else try again with more space. */ if (n > -1) size = n + 1; else size *= 2; if ((p2 = realloc(p,size)) == NULL) { perror("dyn_sprintf: realloc"); free(p); return NULL; } p = p2; } } /* Split a string */ int m_strsplit(char *str,char delim,char **array,int max_count) { int i,pos = 0; size_t len; char *ptr; for(i=0;i sizeof(hexval) || hexval[*in] == BAD) break; if (empty) { *out = (unsigned char)hexval[*in] << 4; } else { *out |= (unsigned char)hexval[*in]; ++out; ++len; } ++in; empty = !empty; } return(len); } /* Ugly function that dumps a structure in hexa and ascii. */ void mem_dump(FILE *f_output,u_char *pkt,u_int len) { u_int x,i = 0, tmp; while (i < len) { if ((len - i) > 16) x = 16; else x = len - i; fprintf(f_output,"%4.4x: ",i); for (tmp=0;tmp= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9'))) fprintf(f_output,"%c",c); else fputs(".",f_output); } i += x; fprintf(f_output,"\n"); } fprintf(f_output,"\n"); fflush(f_output); } /* Logging function */ void m_flog(FILE *fd,char *module,char *fmt,va_list ap) { struct timeval now; struct tm tmn; time_t ct; char buf[256]; if (fd != NULL) { gettimeofday(&now,0); ct = now.tv_sec; localtime_r(&ct,&tmn); strftime(buf,sizeof(buf),"%b %d %H:%M:%S",&tmn); fprintf(fd,"%s.%03ld %s: ",buf,(long)now.tv_usec/1000,module); vfprintf(fd,fmt,ap); fflush(fd); } } /* Logging function */ void m_log(char *module,char *fmt,...) { va_list ap; va_start(ap,fmt); m_flog(log_file,module,fmt,ap); va_end(ap); } /* Write an array of string to a logfile */ void m_flog_str_array(FILE *fd,int count,char *str[]) { int i; for(i=0;i 1) { sum = sum + ntohs(*ptr); ptr++; count -= sizeof(m_uint16_t); } if (count > 0) sum = sum + ((ntohs(*ptr) & 0xFF) << 8); while(sum>>16) sum = (sum & 0xffff) + (sum >> 16); return(~sum); } /* Byte-swap a memory block */ void mem_bswap32(void *ptr,size_t len) { m_uint32_t *p _not_aligned = ptr; size_t count = len >> 2; int i; for(i=0;inext; for(i=0;ifd[i] != -1) { shutdown(p->fd[i],2); close(p->fd[i]); } } if (pool != p) free(p); } } /* Initialize an empty pool */ void fd_pool_init(fd_pool_t *pool) { int i; for(i=0;ifd[i] = -1; pool->next = NULL; } /* Get a free slot for a FD in a pool */ int fd_pool_get_free_slot(fd_pool_t *pool,int **slot) { fd_pool_t *p; int i; for(p=pool;p;p=p->next) { for(i=0;ifd[i] == -1) { *slot = &p->fd[i]; return(0); } } } /* No free slot, allocate a new pool */ if (!(p = malloc(sizeof(*p)))) return(-1); fd_pool_init(p); *slot = &p->fd[0]; p->next = pool->next; pool->next = p; return(0); } /* Fill a FD set and get the maximum FD in order to use with select */ int fd_pool_set_fds(fd_pool_t *pool,fd_set *fds) { fd_pool_t *p; int i,max_fd = -1; for(p=pool;p;p=p->next) for(i=0;ifd[i] != -1) { FD_SET(p->fd[i],fds); if (p->fd[i] > max_fd) max_fd = p->fd[i]; } } return(max_fd); } /* Send a buffer to all FDs of a pool */ int fd_pool_send(fd_pool_t *pool,void *buffer,size_t len,int flags) { fd_pool_t *p; ssize_t res; int i,err; for(p=pool,err=0;p;p=p->next) for(i=0;ifd[i] == -1) continue; res = send(p->fd[i],buffer,len,flags); if (res != len) { shutdown(p->fd[i],2); close(p->fd[i]); p->fd[i] = -1; err++; } } return(err); } /* Call a function for each FD having incoming data */ int fd_pool_check_input(fd_pool_t *pool,fd_set *fds, void (*cbk)(int *fd_slot,void *opt),void *opt) { fd_pool_t *p; int i,count; for(p=pool,count=0;p;p=p->next) for(i=0;ifd[i] != -1) && FD_ISSET(p->fd[i],fds)) { cbk(&p->fd[i],opt); count++; } } return(count); } /* Equivalent to fprintf, but for a posix fd */ ssize_t fd_printf(int fd,int flags,char *fmt,...) { char buffer[2048]; va_list ap; va_start(ap,fmt); vsnprintf(buffer,sizeof(buffer),fmt,ap); va_end(ap); return(send(fd,buffer,strlen(buffer),flags)); } dynamips-0.2.14/man/000077500000000000000000000000001241034141600141715ustar00rootroot00000000000000dynamips-0.2.14/man/CMakeLists.txt000066400000000000000000000001331241034141600167260ustar00rootroot00000000000000# man install_man_pages ( "dynamips.1" "nvram_export.1" "hypervisor_mode.7" ) dynamips-0.2.14/man/dynamips.1000066400000000000000000000363641241034141600161130ustar00rootroot00000000000000.\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH DYNAMIPS 1 "Sep 28, 2013" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME dynamips \- Cisco router simulator .SH SYNOPSIS .B dynamips .RI [ options ] .I ios_image .br .SH DESCRIPTION Emulates Cisco routers on a traditional PC. You can use \fBdynamips\fP to create labs. It uses real Cisco IOS Images, which are not included in this package. Of course, this emulator cannot replace a real router. It is simply a complementary tool to real labs for administrators of Cisco networks or people wanting to pass their CCNA/CCNP/CCIE exams. .br The emulator currently supports Cisco 7200, Cisco 3745, Cisco 3725, Cisco 3600, Cisco 2691, Cisco 2600, and Cisco 1700 series. .br By default, a Cisco 7206VXR with NPE\-200 (256 Mb of DRAM) is emulated. .br To emulate another platform, like the Cisco 3600 series, use the "\-P" command line option. You can change the chassis type with "\-t". Don't forget to set it depending on your IOS image, a c3660 image will not run on c3640 hardware and vice\-versa. .PP .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invode bold face and italics, .\" respectively. .SH OPTIONS A summary of options is included below. .TP .B \-H Enable hypervisor mode. .br The hypervisor mode of dynamips allows you to run simultaneously many virtual router instances, and to simulate ATM, Ethernet or Frame\(hyRelay networks. .br You can connect directly to the TCP control port with telnet, or use \fBdynagen\fP(1), \fBdynagui\fP(1) that will pass commands transparently. The second method is highly recommended. .TP .B \-l Set logging file (default is dynamips_log.txt) .TP .B \-j Disable the JIT compiler, very slow .TP .B \-\-exec\-area Set the exec area size (default: 64 Mb) .br The exec area is a pool of host memory used to store pages translated by the JIT (they contain the native code corresponding to MIPS code pages). .TP .B \-\-idle\-pc Set the idle PC (default: disabled) .br The "idle PC" feature allows you to run a router instance without having a 100% CPU load. This implies that you can run a larger number of instances per real machine. .br To determine the "idle PC", start normally the emulator with your Cisco IOS image, and a totally IOS empty configuration (although not mandatory, this will give better results). When the image is fully booted, wait for the "Press RETURN to get started!" message prompt, but do not press Enter key. Wait about 5 seconds, then press "Ctrl\(hy] + i". Some statistics will be gathered during 10 seconds. At the end, the emulator will display a list of possible values to pass to the "\-\-idle\-pc" option. You may have to try some values before finding the good one. To check if the idle PC value is good, just boot the Cisco IOS image, and check your CPU load when the console prompt is available. If it is low, you have found a good value, keep it preciously. .br Important remarks: .br * An "idle PC" value is *specific* to a Cisco IOS image. You cannot boot a different IOS image without proceeding as described above. .br * Do not run the process while having the "autoconfiguration" prompt. .TP .B \-\-timer\-itv Timer IRQ interval check (default: 1000) .TP .B \-i Set instance ID .TP .B \-r Set the virtual RAM size (default: 256 Mb) .TP .B \-o Set the virtual ROM size (default: 4 Mb) .TP .B \-n Set the NVRAM size (default: 128 Kb) .TP .B \-c Set the configuration register (default: 0x2102) .TP .B \-m Set the MAC address of the chassis (default: automatically generated) .TP .B \-C, \-\-startup\-config Import IOS configuration file into NVRAM .TP .B \-\-private\-config Import IOS configuration file into NVRAM .TP .B \-X Do not use a file to simulate RAM (faster) .TP .B \-R Load an alternate ROM (default: embedded) .TP .B \-k Set the clock divisor (default: 4) .br Specify the clock divider (integer) based on the host clock. Alter the value to match the CISCO clock with the real time. The command "show clock" at the IOS' CLI will help you set this value. .TP .B \-T Console is on TCP .TP .B \-U Console in on serial interface (default is on the terminal) .TP .B \-A AUX is on TCP .TP .B \-B AUX is on serial interface (default is no AUX port) .TP .B \-\-disk0 Set PCMCIA ATA disk0: size (default: 64 Mb) .TP .B \-\-disk1 Set PCMCIA ATA disk1: size (default: 0 Mb) .TP .B \-a Virtual ATM switch configuration file. .TP .B \-f Virtual Frame\(hyRelay switch configuration file. .TP .B \-E Virtual Ethernet switch configuration file. .TP .B \-e Show network device list of the host machine. .SH OPTIONS specific to the Cisco 7200 series .TP .B \-t Select NPE type (default: "npe\(hy200") .TP .B \-M Select Midplane ("std" or "vxr") .TP .B \-p Define a Port Adapter .TP .B \-s Bind a Network IO interface to a Port Adapter .SH OPTIONS specific to the Cisco 3600 series .TP .B \-t Select Chassis type (default: "3640") .TP .B \-p Define a Network Module .TP .B \-s Bind a Network IO interface to a Network Module .SH Cisco 7200 Port Adapter Description "" .TP .B Format slot:pa_driver .TP .B slot the number of the physical slot (starts from 0) .TP .B pa_driver the name of a Port Adapter driver in: .RS .IP C7200\(hyIO\(hyFE (FastEthernet, slot 0 only) .IP PA\(hyFE\(hyTX (FastEthernet, slots 1 to 6) .IP PA\(hy4E (Ethernet, 4 ports) .IP PA\(hy8E (Ethernet, 8 ports) .IP PA\(hy4T+ (Serial, 4 ports) .IP PA\(hy8T (Serial, 8 ports) .IP PA\(hyA1 (ATM) .SH Cisco 3600 Network Module Description "" .TP .B Format slot:nm_driver .TP .B slot the number of the physical slot (starts from 0) .TP .B nm_driver the name of a Network Module driver in: .RS .IP NM\(hy1E (Ethernet, 1 port) .IP NM\(hy4E (Ethernet, 4 ports) .IP NM\(hy1FE\(hyTX (FastEthernet, 1 port) .IP NM\(hy4T (Serial, 4 ports) .IP Leopard\(hy2FE (Cisco 3660 FastEthernet in slot 0, automatically used) .SH NIO binding to Port Adapter "" and Network Modules "": .TP .B Format slot:port:netio_type[:netio_parameters] .TP .B slot the number of the physical slot (starts from 0) .TP .B port the port in the specified slot (starts from 0) .TP .B netio_type host interface for communication .RS .IP unix:: Use unix sockets for local communication. is created and represents the local NIC. is the file used by the other interface. (ex. "/tmp/local:/tmp/remote") .IP vde:: For use with UML (User\(hyMode\(hyLinux) or VDE switches. VDE stands for "Virtual Distributed Ethernet". Please refer to : http://sourceforge.net/projects/vde/ .IP tap: Use a virtual ethernet device for communication. is the name of the tap device (ex. "tap0") .IP gen_eth: Use a real ethernet device for communication, using libpcap 0.9 or WinPcap. Works on Windows and Unix systems. .br is the name of the Ethernet device (ex. "eth0") .br The device list can be found using the "\-e" option. .IP linux_eth: Use a real ethernet device for communication (Linux specific). is the name of the Ethernet device (ex. "eth0") .IP udp::: Use an UDP socket for connection between remote instances. is the port we listen to. is the host listening the port you want to connect to. is the port you want to connect to. (ex. "1000:somehost:2000" and "2000:otherhost:1000" on the other side) .IP tcp_cli:: Client side of a tcp connection. is the ip address of the server. is the port to connect to. .IP tcp_ser: Server side of a tcp connection. is the port to listen to. .IP null Dummy netio (used for testing/debugging), no parameters needed. .SH VTTY binding to real serial port device "" .TP .B Format {:baudrate{:databits{:parity{:stopbits{:hwflow}}}}}} .RS .IP device character device name, e.g. /dev/ttyS0 .IP baudrate baudrate .IP databits number of databits .IP parity data parity: N=none, O=odd, E=even .IP stopbits number of stop bits .IP hwflow hardware flow control (0=disable, 1=enable) .br Note that the device field is mandatory, however other fields are optional. (dynamips will default to 9600, 8, N, 1, no hardware flow control) .br Note that access to the escape commands (described below) through a serial port are deliberately prevented, as the escape commands interfere with serial encapsulation protocols. .SH Escape commands You can press ^] (Ctrl + ]) at any time, followed by one of these characters: .TP .B o Show the VM object list .TP .B d Show the device list .TP .B r Dump MIPS CPU registers .TP .B t Dump MIPS TLB entries .TP .B m Dump the latest memory accesses .TP .B s Suspend CPU emulation .TP .B u Resume CPU emulation .TP .B q Quit the emulator .TP .B b Dump the instruction block tree .TP .B h JIT hash table statistics .TP .B l MTS64 cache statistics .TP .B c Write IOS configuration to disk (ios_cfg.txt) .TP .B j Non\(hyJIT mode statistics .TP .B x Experimentations (can crash the box!) .TP .B ^] Send ^] .br If you press an unrecognized key, help will be shown. Note: on Windows, it may be the "Ctrl + $" sequence. .SH Virtual Bridge The virtual bridge is used to emulate a shared network between emulator instances. Any emulator instance can act as a virtual bridge. .br The configuration file (specified by the "\-b" option) contains a list of NetIO descriptors, with the following syntax: .TP .B interface_name:netio_type[:netio_parameters] .TP Example: .nf # Connection to instance "I0" I0:udp:10000:127.0.0.1:10001 # Connection to instance "I1" I1:udp:10002:127.0.0.1:10003 # Connection to instance "I2" I2:udp:10004:127.0.0.1:10005 .fi .PP The "I0" instance would be launched with the following parameters: .TP dynamips ios.bin \-p 1:PA\-FE\-TX \-s 1:0:udp:10001:127.0.0.1:10000 .SH Virtual Ethernet switch The virtual ethernet switch is used to emulate an Ethernet network between emulator instances. This switch supports access and trunk ports (802.1Q). ISL will be available in a future release. .br Any emulator instance can act as a virtual ethernet switch. .br The configuration file (specified by the "\-E" option) contains a list of NetIO descriptors (representing interfaces) and a list of interface properties (access/trunk port, VLAN info...) .br The interface definition is similar to Port Adapters: .TP .B IF:interface_name:netio_type[:netio_parameters] .TP .B Access Port ACCESS:interface_name:vlan_id .TP .B 802.1Q Trunk Port DOT1Q:interface_name:native_vlan .PP The native VLAN is not tagged. On Cisco devices, by default the native VLAN is VLAN 1. .TP Example of configuration file: .nf IF:E0:udp:10000:127.0.0.1:10001 IF:E1:udp:10002:127.0.0.1:10003 IF:E2:gen_eth:eth0 DOT1Q:E0:1 ACCESS:E1:4 DOT1Q:E2:1 .fi .SH Virtual ATM switch The virtual ATM switch fabric is used to emulate an ATM backbone between emulator instances. The use of this virtual switch is not mandatory, you can directly connect emulator instances for point\(hyto\(hypoint ATM connections. Please note that only basic VP/VC switching is supported, there is no support for ILMI/QSAAL/\|.\|.\|. or other specific ATM protocols. .br Any emulator instance can act as a virtual ATM switch. .TP Example of configuration file (specified by the "\-a" option): .nf # Virtual Interface List IF:A0:udp:10001:127.0.0.1:10000 IF:A1:udp:10002:127.0.0.1:10003 IF:A2:udp:10004:127.0.0.1:10005 # VP connection between I0 and I1 VP:A0:10:A1:20 VP:A1:20:A0:10 # VP connection between I0 and I2 VP:A0:11:A2:30 VP:A2:30:A0:11 # VC connection between I1 and I2 VC:A1:5:2:A2:7:3 VC:A2:7:3:A1:5:2 .fi .PP In this example, we have 3 virtual interfaces, A0, A1 and A2. The syntax for interface definition is similar to Port Adapters: .TP .B IF:interface_name:netio_type[:netio_parameters] You can do VP switching or VC switching: .TP .B VP switching VP:input_if:input_vpi:output_if:output_vpi .TP .B VC switching VC:input_if:input_vpi:input_vci:output_if:output_vpi:output_vci .SH Testing the Virtual ATM switch with one dynamips instance .TP Virtual ATM switch configuration file ("atm.cfg"): .nf IF:A0:udp:10003:127.0.0.1:10001 IF:A1:udp:10004:127.0.0.1:10002 # a0/vpi=1/vci=100 connects to a1/vpi=2/vci=200 VC:A0:1:100:A1:2:200 VC:A1:2:200:A0:1:100 .fi .TP Invoking dynamips: .B \|./dynamips \-p 1:PA\-A1 \-s 1:0:udp:10001:127.0.0.1:10003 \-p 2:PA\-A1 \-s 2:0:udp:10002:127.0.0.1:10004 \-a atm.cfg IOS.BIN .br (note input ports of IOS interfaces are output ports of ATM switch interfaces, and vice versa). .br .TP IOS Configuration: .nf ip cef ip vrf test rd 1:1 route\-target both 1:1 int a1/0 no shut int a1/0.2 p ip addr 1.1.1.1 255.255.255.0 pvc 1/100 interface a2/0 no shut interface a2/0.2 p ip vrf forwarding test ip addr 1.1.1.2 255.255.255.0 pvc 2/200 ! .fi .SH Virtual Frame\(hyRelay switch The virtual Frame\(hyRelay switch fabric is used to emulate a Frame\(hyRelay backbone between emulator instances. The use of this virtual switch is not mandatory, you can directly connect emulator instances with appropriate IOS configuration. .br Any emulator instance can act as a virtual Frame\(hyRelay switch. There is only a basic implementation of the LMI protocol (ANSI Annex D), which is probably not conforming but works with Cisco IOS. Fortunately, Cisco IOS is able to detect automatically the LMI protocol. .TP Example of configuration file (specified by the "\-f" option): .nf # Virtual Interface List IF:S0:udp:10001:127.0.0.1:10000 IF:S1:udp:10002:127.0.0.1:10003 # DLCI switching between S0 and S1 VC:S0:200:S1:100 VC:S1:100:S0:200 .fi .PP In this example, we have 2 virtual interfaces, S0 and S1. The syntax for interface definition is similar to Port Adapters: .TP .B IF:interface_name:netio_type[:netio_parameters] .TP .B DLCI switching syntax: .RS .IP VC:input_if:input_dlci:output_if:output_dlci .RE .br In the example above, the switch is configured to switch packets received on interface S0 with DLCI 200 to interface S1 with DLCI 100, and vice\(hyversa. .SH BUGS .TP See RELEASE\-NOTES. .SH REPORTING BUGS .br Please send bug reports to .UR https://github.com/GNS3/dynamips/issues .UE .SH SEE ALSO .br \fBnvram_export\fP(1), \fBhypervisor_mode\fP(7), \fBdynagen\fP(1), \fBdynagui\fP(1) .br .UR http://www.gns3.net/dynamips/ .UE .br .UR http://forum.gns3.net/ .UE .br .UR https://github.com/GNS3/dynamips .UE .br .SH OLD WEBSITES .UR http://www.ipflow.utc.fr/index.php/ .UE .br .UR http://www.ipflow.utc.fr/blog/ .UE .br .UR http://hacki.at/7200emu/index.php .UE .SH AUTHOR \fBdynamips\fP is being maintained by Flávio J. Saraiva . This manual page was initially written by Erik Wenzel for the Debian GNU/Linux system. dynamips-0.2.14/man/hypervisor_mode.7000066400000000000000000000650621241034141600175100ustar00rootroot00000000000000.TH HYPERVISOR_MODE 7 "Mar 27, 2014" .\" Please adjust this date whenever revising the manpage. .SH NAME hypervisor_mode \- allows you to run simultaneously many virtual router instances, and to simulate ATM, Ethernet or Frame\(hyRelay networks. .SH SYNOPSIS .B dynamips \-H .I [ip_address:]tcp_port .SH DESCRIPTION You can connect directly to the TCP control port with telnet, or use dynagen/dynagui that will pass commands transparently. The second method is highly recommended. .br The command syntax is simple: [arguments...] For example: "vm start R1" starts virtual instance named "R1". .TP Available since version 0.2.5. .TP .B The modules that are currently defined are given below: .br .RS .B hypervisor General hypervisor management .TP .B vm General virtual machine (VM) management .TP .B vm_debug General virtual machine (VM) debugging .TP .B c7200 Virtual instances of Cisco 7200 .TP .B c3745 Virtual instances of Cisco 3745 .TP .B c3725 Virtual instances of Cisco 3725 .TP .B c3600 Virtual instances of Cisco 3600 .TP .B c2691 Virtual instances of Cisco 2691 .TP .B c2600 Virtual instances of Cisco 2600 .TP .B c1700 Virtual instances of Cisco 1700 .TP .B nio Network Input/Output (NIO) descriptors .TP .B nio_bridge NIO bridges (shared media) .TP .B atmsw ATM switches .TP .B atm_bridge ATM bridges .TP .B frsw Frame\(hyRelay switches .TP .B ethsw Ethernet switches .TP .B object_store Object store .RE .TP .B Hypervisor management module ("hypervisor") .RS .TP .B hypervisor version Display the version of dynamips. .TP .B hypervisor module_list Display the module list. .TP .B hypervisor cmd_list Display commands recognized by the specified module. .TP .B hypervisor close Close the current session. .TP .B hypervisor stop Destroy all objects and stop hypervisor. .TP .B hypervisor reset Destroy all objects. (used to get an empty configuration) .TP .B hypervisor working_dir Set the directory to use to store files. .TP .B hypervisor save_config Save the configuration of all objects into the specified file. .TP .B hypervisor parser_test [ [... ]] Display up to 10 arguments. (since version 0.2.6\-RC1) .TP .B hypervisor uuid Display the local uuid. (since version 0.2.8\-RC3) .TP .B hypervisor tsg_stats Dump statistics about JIT code sharing to the console. (since version 0.2.8\-RC3, unstable) .RE .TP .B Virtual Machine module ("vm") .RS .TP .B vm list List all VM instances. (c7200, c3745, c3725, c3600, c2691, c2600, c1700) .TP .B vm list_con_ports List all VM console TCP port. (since version 0.2.6\-RC5) .TP .B vm create Create a new router instance. The ID must be unique and is used to name files on disk. (since version 0.2.8\-RC1) .TP .B vm rename Rename a router instance. (since version 0.2.11) .TP .B vm delete Delete the specified instance. (since version 0.2.8\-RC1) .TP .B vm start Start the instance. At least the IOS image must be set. (since version 0.2.8\-RC1) .TP .B vm stop Stop the instance. The settings are kept. (since version 0.2.8\-RC1) .TP .B vm get_status Get the status of a VM instance. Return values: 0=inactive, 1=shutting down, 2=running, 3=suspended. (since version 0.2.11) .TP .B vm set_tsg Set translation sharing group. (since version 0.2.8\-RC3\-community, unstable) .TP .B vm set_debug_level Set the debug level (which is a number) for a VM. By default, no specific debug is enabled (level = 0). .TP .B vm set_ios Set the IOS image file to use. There is no default. .TP .B vm set_config [] Set the config files that are pushed to startup\-config and private\-config in NVRAM when the instance is started. To keep existing data, use an empty string ('') for the filename. The optional is an empty string by default. (supports since version 0.2.10) .TP .B vm extract_config Get the contents of the config files startup\-config and private\-config from NVRAM. The data of each file is encoded in a Base64 string, surrounded by single quotes. (returns private\-config since version 0.2.10) .TP .B vm push_config [] Push configuration to the config files startup\-config and private\-config in NVRAM. The data is a Base64 encoded string, or '(keep)' to keep existing data. The optional is '(keep)' by default. (supports since version 0.2.11) .TP .B vm set_ram Set the RAM size, specified in Mbytes. .TP .B vm set_nvram Set the NVRAM size, specified in Kbytes. .TP .B vm set_ram_mmap <0|1> Enable/Disable use of a mapped file to simulate router memory. By default, a mapped file is used. This is a bit slower, but requires less memory. .TP .B vm set_sparse_mem <0|1> Enable/disable use of sparse memory. (since version 0.2.7\-RC1) .TP .B vm suspend Suspend execution of the instance. .TP .B vm resume Resume execution of the instance. .TP .B vm set_clock_divisor Set the clock divisor value. The higher is the value, the faster is the clock in the virtual machine. The default is 4, but it is often required to adjust it. .TP .B vm set_blk_direct_jump <0|1> Enable/disable use of block direct jump. (compatibility option, since version 0.2.7\-RC2) .TP .B vm set_idle_pc Set the idle Pointer Counter (PC). You must determine it through the method explained in the main README file. .TP .B vm set_idle_pc_online Set the idle PC value when the CPU is online. (since version 0.2.6\-RC2) .TP .B vm get_idle_pc_prop Get the idle PC proposals. Takes 1000 measurements and records up to 10 idle PC proposals. There is a 10ms wait between each measurement. (since version 0.2.6\-RC2) .TP .B vm show_idle_pc_prop Dump the idle PC proposals. (since version 0.2.6\-RC2) .TP .B vm set_idle_max Set CPU idle max value. (since version 0.2.6\-RC2) .TP .B vm set_idle_sleep_time Set CPU idle sleep time value. (since version 0.2.6\-RC2) .TP .B vm show_timer_drift Show info about potential timer drift. (since version 0.2.6\-RC3) .TP .B vm set_ghost_file Set ghost RAM file. (since version 0.2.6\-RC3, needs an extra bogus argument before version 0.2.6\-RC4) .TP .B vm set_ghost_status " Set ghost RAM status. (since version 0.2.6\-RC3, needs an extra bogus argument before version 0.2.6\-RC4) .TP .B vm set_exec_area Set the exec area size. The exec area is a pool of host memory used to store pages translated by the JIT (they contain the native code corresponding to MIPS code pages). .TP .B vm set_disk0 Set size of PCMCIA ATA disk0. .TP .B vm set_disk1 Set size of PCMCIA ATA disk1. .TP .B vm set_conf_reg Set the config register value. The default is 0x2102. .TP .B vm set_con_tcp_port Set the TCP port to use for console. By default, no TCP port is chosen, meaning that you cannot get access to the console. .TP .B vm set_aux_tcp_port Set the TCP port to use for AUX port. By default, no TCP port is chosen, meaning that you cannot get access to the AUX port. .TP .B vm cpu_info Show info about the CPU identified by "cpu_id". The boot CPU (which is typically the only CPU) has ID 0. .TP .B vm cpu_usage Show cpu usage of dynamips in seconds. (experimental) .br The instance must exist, "cpu_id" is ignored. (since version 0.2.8\-RC5\-community) .TP .B vm send_con_msg [] (since version 0.2.6\-RC3) Send a message on the console. It only writes the bytes that fit in the console buffer. .RS .PP (since version 0.2.14) The optional argument indicates the string format. On success it will report "X byte(s) written". .PP String formats: * plain - plain string (default, old behavior) * base64 - base64 encoded string .RE .TP .B vm send_aux_msg [] (since version 0.2.6\-RC3) Send a message on the AUX port. It only writes the bytes that fit in the aux buffer. .RS .PP (since version 0.2.14) The optional argument indicates the string format. On success it will report "X byte(s) written". .PP String formats: * plain - plain string (default, old behavior) * base64 - base64 encoded string .RE .TP .B vm slot_bindings Show slot bindings. (since version 0.2.8\-RC1) .TP .B vm slot_nio_bindings Show NIO bindings for the specified slot. (since version 0.2.8\-RC1) .TP .B vm slot_add_binding Add a slot binding. (since version 0.2.8\-RC1) .TP .B vm slot_remove_binding Remove a slot binding . (since version 0.2.8\-RC1) .TP .B vm slot_add_nio_binding Add a NIO binding for a slot/port. (since version 0.2.8\-RC1) .TP .B vm slot_remove_nio_binding Remove a NIO binding for a slot/port. (since version 0.2.8\-RC1) .TP .B vm slot_enable_nio Enable NIO of the specified slot/port. (since version 0.2.8\-RC1) .TP .B vm slot_disable_nio Disable NIO of the specified slot/port. (since version 0.2.8\-RC1) .TP .B vm slot_oir_start OIR to start a slot/subslot. (since version 0.2.8\-RC3\-community) .TP .B vm slot_oir_stop OIR to stop a slot/subslot. (since version 0.2.8\-RC3\-community) .RE .TP .B Virtual Machine debugging module ("vm_debug") .RS .TP Available since version 0.2.6\-RC1. .TP .B vm_debug show_cpu_regs Dump CPU registers to the console. .TP .B vm_debug show_cpu_mmu Dump CPU MMU info to the console. (since version 0.2.7\-RC1) .TP .B vm_debug set_cpu_reg Set the value of a CPU register. .TP .B vm_debug add_cpu_breakpoint
Add a breakpoint. .TP .B vm_debug remove_cpu_breakpoint
Remove a breakpoint. .TP .B vm_debug pmem_w32
Write a 32\-bit memory word to physical memory. .TP .B vm_debug pmem_r32
Read a 32\-bit memory word from physical memory. .TP .B vm_debug pmem_w16
Write a 16\-bit memory word to physical memory. .TP .B vm_debug pmem_r16
Read a 16\-bit memory word from physical memory. .TP .B vm_debug pmem_cfind [ []] Find a sequence of bytes in physical memory interval [first,last]. The byte sequence is composed of hexadecimal characters and must have a length multiple of 2. The interval defaults to the complete memory space. Only the memory of cacheable devices (ram, rom, disks, ...) is searched. (since version 0.2.12) .RE .TP .B Virtual Cisco 7200 instances module ("c7200") .RS .TP .B c7200 list List all existing Cisco 7200 instances. .TP .B c7200 set_npe Set the NPE model. For example: npe\(hy100, npe\(hy400, ... The default is "npe\(hy400". .TP .B c7200 set_midplane Set the midplane model, it can be either "std" or "vxr". The default is "vxr". .TP .B c7200 get_mac_addr Get the base MAC address of the router. By default, the address is automatically generated with this pattern : ca..0000 (Cisco format). (since version 0.2.11) .TP .B c7200 set_mac_addr Set the base MAC address of the router. The MAC address patern can be the Cisco format (e.g. ca01.1234.0000) or the standard format (e.g. ca:01:12:34:00:00). .TP .B c7200 set_system_id Set the system id. (since version 0.2.8\-RC3\-community) .TP .B c7200 set_temp_sensor Set temperature for a DS1620 sensor. This can be used to simulate environmental problems like overheat. (since version 0.2.8\-RC3\-community) .TP .B c7200 set_power_supply <0|1> Set power supply status. This can be used to simulate environmental problems like power loss. (since version 0.2.8\-RC3\-community) .TP .B c7200 show_hardware Display virtual hardware info about the instance. .RE .TP .B Virtual Cisco 3745 instances module ("c3745") .RS .TP .B c3745 list List all existing Cisco 3745 instances. .TP .B c3745 set_iomem Set the I/O mem size. .TP .B c3745 get_mac_addr Get the base MAC address of the router. By default, the address is automatically generated with this pattern : c4..0000 (Cisco format). (since version 0.2.11) .TP .B c3745 set_mac_addr Set the base MAC address of the router. The MAC address patern can be the Cisco format (e.g. c401.1234.0000) or the standard format (e.g. c4:01:12:34:00:00). .TP .B c3745 set_system_id Set the system id. (since version 0.2.8\-RC3\-community) .TP .B c3745 show_hardware Display virtual hardware info about the instance. .RE .TP .B Virtual Cisco 3725 instances module ("c3725") .RS .TP .B c3725 list List all existing Cisco 3725 instances. .TP .B c3725 set_iomem Set the I/O mem size. .TP .B c3725 get_mac_addr Get the base MAC address of the router. By default, the address is automatically generated with this pattern : c2..0000 (Cisco format). (since version 0.2.11) .TP .B c3725 set_mac_addr Set the base MAC address of the router. The MAC address patern can be the Cisco format (e.g. c201.1234.0000) or the standard format (e.g. c2:01:12:34:00:00). .TP .B c3725 set_system_id Set the system id. (since version 0.2.8\-RC3\-community) .TP .B c3725 show_hardware Display virtual hardware info about the instance. .RE .TP .B Virtual Cisco 3600 instances module ("c3600") .RS .TP .B c3600 list List all existing Cisco 3600 instances. .TP .B c3600 set_chassis Set the chassis model. Possible values: 3620, 3640, 3660. The default is "3640". .TP .B c3600 set_iomem Set the I/O mem size. .TP .B c3600 get_mac_addr Get the base MAC address of the router. By default, the address is automatically generated with this pattern : cc..0000 (Cisco format). (since version 0.2.11) .TP .B c3600 set_mac_addr Set the base MAC address of the router. The MAC address patern can be the Cisco format (e.g. cc01.1234.0000) or the standard format (e.g. cc:01:12:34:00:00). .TP .B c3600 set_system_id Set the system id. (since version 0.2.8\-RC3\-community) .TP .B c3600 show_hardware Display virtual hardware info about the instance. .RE .TP .B Virtual Cisco 2691 instances module ("c2691") .RS .TP .B c2691 list List all existing Cisco 2691 instances. .TP .B c2691 set_iomem Set the I/O mem size. .TP .B c2691 get_mac_addr Get the base MAC address of the router. By default, the address is automatically generated with this pattern : c0..0000 (Cisco format). (since version 0.2.11) .TP .B c2691 set_mac_addr Set the base MAC address of the router. The MAC address patern can be the Cisco format (e.g. c001.1234.0000) or the standard format (e.g. c0:01:12:34:00:00). .TP .B c2691 set_system_id Set the system id. (since version 0.2.11) .TP .B c2691 show_hardware Display virtual hardware info about the instance. .RE .TP .B Virtual Cisco 2600 instances module ("c2600") .RS .TP Available since version 0.2.7\-RC1. .TP .B c2600 list List all existing Cisco 2600 instances. .TP .B c2600 set_chassis Set the chassis model. Possible values: 2610, 2611, 2620, 2621, 2610XM, 2611XM, 2620XM, 2621XM, 2650XM, 2651XM. The default is "2610". .TP .B c2600 set_iomem Set the I/O mem size. .TP .B c2600 get_mac_addr Get the base MAC address of the router. By default, the address is automatically generated with this pattern : c8..0000 (Cisco format). (since version 0.2.11) .TP .B c2600 set_mac_addr Set the base MAC address of the router. The MAC address patern can be the Cisco format (e.g. c801.1234.0000) or the standard format (e.g. c8:01:12:34:00:00). .TP .B c2600 set_system_id Set the system id. (since version 0.2.8\-RC3\-community) .TP .B c2600 show_hardware Display virtual hardware info about the instance. .RE .TP .B Virtual Cisco 1700 instances module ("c1700") .RS .TP Available since version 0.2.8\-RC1. .TP .B c1700 list List all existing Cisco 1700 instances. .TP .B c1700 set_chassis Set the chassis model. Possible values: 1710, 1720, 1721, 1750, 1751, 1760. The default is "1720". .TP .B c1700 set_iomem Set the I/O mem size. .TP .B c1700 get_mac_addr Get the base MAC address of the router. By default, the address is automatically generated with this pattern : d0..0000 (Cisco format). (since version 0.2.11) .TP .B c1700 set_mac_addr Set the base MAC address of the router. The MAC address patern can be the Cisco format (e.g. d001.1234.0000) or the standard format (e.g. d0:01:12:34:00:00). .TP .B c1700 set_system_id Set the system id. (since version 0.2.8\-RC3\-community) .TP .B c1700 show_hardware Display virtual hardware info about the instance. .RE .TP .B Network Input/Output (NIO) module ("nio") .RS .TP .B nio list List all exiting NIOs. .TP .B nio create_udp Create an UDP NIO with the specified parameters. .TP .B nio create_udp_auto Create an auto UDP NIO. (since version 0.2.8\-RC3\-community) .TP .B nio connect_udp_auto Connect an UDP Auto NIO to a remote host/port. (since version 0.2.8\-RC3\-community) .TP .B nio create_mcast Create a Multicast NIO. (since version 0.2.8\-RC3\-community) .TP .B nio set_mcast_ttl Set TTL for a Multicast NIO. (since version 0.2.8\-RC3\-community) .TP .B nio create_unix Create an UNIX NIO with the specified parameters. .TP .B nio create_vde Create a VDE NIO with the specified parameters. VDE stands for "Virtual Distributed Ethernet" and is compatible with UML (User\(hyMode\(hyLinux) switch. .TP .B nio create_tap Create a TAP NIO. TAP devices are supported only on Linux and FreeBSD and require root access. .TP .B nio create_gen_eth Create a generic ethernet NIO, using PCAP (0.9.4 and greater). It requires root access. Available if compiled with GEN_ETH. .TP .B nio create_linux_eth Create a Linux ethernet NIO. It requires root access and is supported only on Linux platforms. Available if compiled with LINUX_ETH. .TP .B nio create_null Create a Null NIO. .TP .B nio create_fifo Create a Null NIO. .TP .B nio crossconnect_fifo Establish a cross-connect between 2 FIFO NIO. .TP .B nio rename Rename a NIO. (since version 0.2.11) .TP .B nio delete Delete the specified NIO. The NIO can be deleted only when it is not anymore in use by another object. .TP .B nio set_debug Enable/Disable debugging for the specified NIO. When debugging is enabled, received and emitted packets are displayed at screen. It is mainly used to debug interface drivers. .TP .B nio bind_filter Bind a packet filter. Direction is 0 for receiving, 1 for sending, 2 for both. Filter .B "freq_drop" drops packets. Filter .B "capture" captures packets and is only available if compiled with GEN_ETH. .TP .B nio unbind_filter Unbind a packet filter. .TP .B nio setup_filter [ [...]] Setup a packet filter for a given NIO. The arguments are passed on to the setup function of the filter. Filter .B "freq_drop" has 1 argument .B "" \[char46] It will drop everything with a \-1 frequency, drop every Nth packet with a positive frequency, or drop nothing. Filter .B "capture" has 2 arguments .B " " \[char46] It will capture packets to the target output file. The link type name is a case\(hyinsensitive DLT_ name from the pcap library constants with the DLT_ part removed. .TP .B nio get_stats Get statistics of a NIO. (since version 0.2.8\-RC3\-community) .TP .B nio reset_stats Reset statistics of a NIO. (since version 0.2.8\-RC3\-community) .TP .B nio set_bandwidth Set bandwidth constraint. (since version 0.2.8\-RC3\-community) .RE .TP .B NIO bridge module ("nio_bridge") .RS .TP .B nio_bridge list List all NIO bridges. .TP .B nio_bridge create Create a NIO bridge. A NIO bridge acts as a shared media (a kind of hub). .TP .B nio_bridge rename Rename a NIO bridge. (since version 0.2.11) .TP .B nio_bridge delete Delete a NIO bridge. .TP .B nio_bridge add_nio Add a NIO as new port in a NIO bridge. The NIO must be created through the "nio" module. .TP .B nio_bridge remove_nio Remove the specified NIO as member of the NIO bridge. .RE .TP .B Virtual Ethernet switch module ("ethsw") .RS .TP .B ethsw list List all Ethernet switches. .TP .B ethsw create Create a new Ethernet switch. .TP .B ethsw rename Rename an Ethernet switch. (since version 0.2.11) .TP .B ethsw delete Delete the specified Ethernet switch. .TP .B ethsw add_nio Add a NIO as new port in an Ethernet switch. The NIO must be created through the "nio" module. .TP .B ethsw remove_nio Remove the specified NIO as member of the Ethernet switch. .TP .B ethsw set_access_port Set the specified port as an ACCESS port in VLAN . .TP .B ethsw set_dot1q_port Set the specified port as a 802.1Q trunk port, with native VLAN . .TP .B ethsw set_qinq_port Set the specified port as a trunk (QinQ) port. (since version 0.2.3\-RC3\-community) .TP .B ethsw clear_mac_addr_table Clear the MAC address table. .TP .B ethsw show_mac_addr_table Show the MAC address table (output format: Ethernet address, VLAN, NIO) .RE .TP .B Virtual ATM switch module ("atmsw") .RS .TP .B atmsw list List all ATM switches. .TP .B atmsw create Create a new ATM switch. .TP .B atmsw rename Rename an ATM switch. (since version 0.2.11) .TP .B atmsw delete Delete the specified ATM switch. .TP .B atmsw create_vpc Create a new Virtual Path connection (unidirectional). .TP .B atmsw delete_vpc Delete a Virtual Path connection (unidirectional). .TP .B atmsw create_vcc Create a new Virtual Channel connection (unidirectional). .TP .B atmsw delete_vcc Delete a Virtual Channel connection (unidirectional). .RE .TP .B Virtual ATM bridge module ("atm_bridge") .RS .TP Available since version 0.2.8\-RC2. .TP .B atm_bridge list List all ATM bridges. .TP .B atm_bridge create Create a new ATM bridge. .TP .B atm_bridge rename Rename an ATM bridge. (since version 0.2.11) .TP .B atm_bridge delete Delete an ATM bridge. .TP .B atm_bridge configure Configure an ATM bridge. .TP .B atm_bridge unconfigure Unconfigure an ATM bridge. .RE .TP .B Virtual Frame\(hyRelay switch module ("frsw") .RS .TP .B frsw list List all Frame\(hyRelay switches. .TP .B frsw create Create a new Frame\(hyRelay switch. .TP .B frsw rename Rename a Frame-Relay switch. (since version 0.2.11) .TP .B frsw delete Delete the specified Frame\(hyRelay switch. frsw create_vc Create a new Virtual Circuit connection (unidirectional). .TP .B frsw delete_vc Delete a Virtual Circuit connection (unidirectional). .RE .TP .B Object store module ("object_store") .RS .TP Available since version 0.2.8\-RC2. .TP .B object_store write Write an object, data provided in base64 encoding. .TP .B object_store read Read an object and return data in base64 encoding. .TP .B object_store rename Rename an object. (since version 0.2.11) .TP .B object_store delete Delete an object from the store. .TP .B object_store delete_all Delete all objects from the store .TP .B object_store list Object list. .RE .SH REPORTING BUGS .br Please send bug reports to .UR https://github.com/GNS3/dynamips/issues .UE .SH SEE ALSO .br \fBdynamips\fP(1), \fBnvram_export\fP(1), \fBdynagen\fP(1), \fBdynagui\fP(1) .br .UR http://www.gns3.net/dynamips/ .UE .br .UR http://forum.gns3.net/ .UE .br .UR https://github.com/GNS3/dynamips .UE .br .SH OLD WEBSITES .UR http://www.ipflow.utc.fr/index.php/ .UE .br .UR http://www.ipflow.utc.fr/blog/ .UE .br .UR http://hacki.at/7200emu/index.php .UE .SH AUTHOR \fBdynamips\fP is being maintained by Flávio J. Saraiva . This manual page was initially written by Erik Wenzel for the Debian GNU/Linux system. dynamips-0.2.14/man/nvram_export.1000066400000000000000000000023161241034141600170010ustar00rootroot00000000000000.TH NVRAM_EXPORT 1 "Sep 28, 2013" .\" Please adjust this date whenever revising the manpage. .SH NAME nvram_export \- Cisco NVRAM configuration export .SH SYNOPSIS .B nvram_export .I nvram_file .I config_file [ .I private_file ] .br .SH DESCRIPTION Exports configuration files \fBstartup\-config\fP and \fBprivate\-config\fP from a dynamips NVRAM file to the target files. .PP .SH OPTIONS .TP .B nvram_file File that contains the NVRAM data. On some platforms, NVRAM is simulated inside the ROM. .TP .B config_file File for 'startup\-config'. .TP .B private_file File for 'private\-config'. .SH REPORTING BUGS .br Please send bug reports to .UR https://github.com/GNS3/dynamips/issues .UE .SH SEE ALSO .br \fBdynamips\fP(1), \fBhypervisor_mode\fP(7) .br .UR http://www.gns3.net/dynamips/ .UE .br .UR http://forum.gns3.net/ .UE .br .UR https://github.com/GNS3/dynamips .UE .br .SH OLD WEBSITES .UR http://www.ipflow.utc.fr/index.php/ .UE .br .UR http://www.ipflow.utc.fr/blog/ .UE .br .UR http://hacki.at/7200emu/index.php .UE .SH AUTHOR \fBdynamips\fP is being maintained by Flávio J. Saraiva . This manual page was initially written by Erik Wenzel for the Debian GNU/Linux system. dynamips-0.2.14/profiler_resolve.pl000077500000000000000000000011031241034141600173320ustar00rootroot00000000000000#! /usr/bin/perl -w use strict; my @files = <*.profile>; die "No *.profile files found\n" if !@files; for my $file (@files) { print STDERR "processing $file.\n"; open my ($f), '<', $file or die "open $file: $!"; open my ($fnew), '>', "$file.names" or die "create $file.names: $!"; my ($prog) = $file =~ /^(.*)\.profile\z/ or die; open my ($nm), "nm -n $prog |" or die; my %nm; while (<$nm>) { next if /^\s/; /^([0-9a-f]{8}) . (.*)/ or die "bad nm"; $nm{$1} = sprintf "%-30s", $2; } while (<$f>) { s/^([0-9a-f]{8})/$nm{$1} || $1/e; print $fnew $_; } } dynamips-0.2.14/stable/000077500000000000000000000000001241034141600146705ustar00rootroot00000000000000dynamips-0.2.14/stable/CMakeLists.txt000066400000000000000000000175151241034141600174410ustar00rootroot00000000000000# dynamips - stable code + tools set ( COMMON "${CMAKE_SOURCE_DIR}/common" ) set ( LOCAL "${CMAKE_CURRENT_SOURCE_DIR}" ) include_directories ( "${CMAKE_CURRENT_SOURCE_DIR}" "${COMMON}" "${CMAKE_CURRENT_BINARY_DIR}" ) # udp_send if ( BUILD_UDP_SEND ) add_executable ( udp_send "${COMMON}/crc.c" "${COMMON}/net.c" "${COMMON}/udp_send.c" "${COMMON}/utils.c" ) target_link_libraries ( udp_send ${DYNAMIPS_LIBRARIES} ) install_executable ( udp_send ) endif ( BUILD_UDP_SEND ) # udp_recv if ( BUILD_UDP_RECV ) add_executable ( udp_recv "${COMMON}/crc.c" "${COMMON}/net.c" "${COMMON}/udp_recv.c" "${COMMON}/utils.c" ) target_link_libraries ( udp_recv ${DYNAMIPS_LIBRARIES} ) install_executable ( udp_recv ) endif ( BUILD_UDP_RECV ) # rom2c # XXX must be built for the host, not target, to support cross-compiling add_executable ( rom2c EXCLUDE_FROM_ALL "${COMMON}/rom2c.c" ) target_link_libraries ( rom2c ${DYNAMIPS_LIBRARIES} ) set ( ROM2C_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/rom2c${CMAKE_EXECUTABLE_SUFFIX}" CACHE INTERNAL "rom2c executable" ) # mips64_microcode_dump.inc set ( _input "${LOCAL}/mips64_microcode" ) set ( _output "${CMAKE_CURRENT_BINARY_DIR}/mips64_microcode_dump.inc" ) add_custom_command ( OUTPUT ${_output} COMMAND ${ROM2C_EXECUTABLE} ARGS ${_input} ${_output} 0xbfc00000 DEPENDS rom2c ${_input} ) add_custom_target ( mips64_microcode_dump_stable DEPENDS ${_output} ) # ppc32_microcode_dump.inc set ( _input "${LOCAL}/ppc32_microcode" ) set ( _output "${CMAKE_CURRENT_BINARY_DIR}/ppc32_microcode_dump.inc" ) add_custom_command ( OUTPUT ${_output} COMMAND ${ROM2C_EXECUTABLE} ARGS ${_input} ${_output} 0xfff00000 DEPENDS rom2c ${_input} ) add_custom_target ( ppc32_microcode_dump_stable DEPENDS ${_output} ) # nvram_export if ( BUILD_NVRAM_EXPORT ) add_executable ( nvram_export "${COMMON}/fs_nvram.c" "${COMMON}/nvram_export.c" ) target_link_libraries ( nvram_export ${DYNAMIPS_LIBRARIES} ) install_executable ( nvram_export ) endif ( BUILD_NVRAM_EXPORT ) #-----------------------# # dynamips: stable code # #-----------------------# if ( NOT BUILD_DYNAMIPS_STABLE ) return () endif ( NOT BUILD_DYNAMIPS_STABLE ) # dynamips_*_stable set ( _files "${COMMON}/mempool.c" "${COMMON}/registry.c" "${COMMON}/rbtree.c" "${COMMON}/hash.c" "${COMMON}/sbox.c" "${COMMON}/utils.c" "${COMMON}/parser.c" "${COMMON}/gen_uuid.c" "${COMMON}/plugin.c" "${COMMON}/ptask.c" "${COMMON}/timer.c" "${COMMON}/crc.c" "${COMMON}/base64.c" "${COMMON}/net.c" "${COMMON}/net_io.c" "${COMMON}/net_io_bridge.c" "${COMMON}/net_io_filter.c" "${COMMON}/atm.c" "${COMMON}/atm_vsar.c" "${COMMON}/atm_bridge.c" "${COMMON}/frame_relay.c" "${COMMON}/eth_switch.c" "${COMMON}/dynamips.c" "${COMMON}/insn_lookup.c" "${LOCAL}/vm.c" "${LOCAL}/cpu.c" "${COMMON}/jit_op.c" "${LOCAL}/mips64.c" "${LOCAL}/mips64_mem.c" "${LOCAL}/mips64_cp0.c" "${LOCAL}/mips64_jit.c" "${LOCAL}/mips64_exec.c" "${LOCAL}/ppc32.c" "${LOCAL}/ppc32_mem.c" "${LOCAL}/ppc32_jit.c" "${LOCAL}/ppc32_exec.c" "${LOCAL}/ppc32_vmtest.c" "${COMMON}/memory.c" "${COMMON}/device.c" "${COMMON}/nmc93cX6.c" "${COMMON}/cisco_eeprom.c" "${COMMON}/cisco_card.c" "${COMMON}/pci_dev.c" "${COMMON}/pci_io.c" "${COMMON}/dev_zero.c" "${COMMON}/dev_bswap.c" "${COMMON}/dev_vtty.c" "${COMMON}/dev_ram.c" "${COMMON}/dev_rom.c" "${COMMON}/dev_nvram.c" "${COMMON}/dev_bootflash.c" "${COMMON}/dev_flash.c" "${COMMON}/dev_mpc860.c" "${COMMON}/dev_ds1620.c" "${COMMON}/dev_remote.c" "${COMMON}/dev_clpd6729.c" "${COMMON}/dev_pcmcia_disk.c" "${COMMON}/dev_gt.c" "${COMMON}/dev_mv64460.c" "${COMMON}/dev_plx.c" "${COMMON}/dev_dec21x50.c" "${COMMON}/dev_pericom.c" "${COMMON}/dev_ti2050b.c" "${COMMON}/dev_ap1011.c" "${COMMON}/dev_plx6520cb.c" "${COMMON}/dev_ns16552.c" "${COMMON}/dev_dec21140.c" "${COMMON}/dev_am79c971.c" "${COMMON}/dev_i8254x.c" "${COMMON}/dev_i8255x.c" "${COMMON}/dev_mueslix.c" "${COMMON}/dev_wic_serial.c" "${COMMON}/dev_c3600.c" "${COMMON}/dev_c3600_bay.c" "${COMMON}/dev_c3600_iofpga.c" "${COMMON}/dev_c3600_eth.c" "${COMMON}/dev_c3600_serial.c" "${COMMON}/dev_c7200.c" "${COMMON}/dev_c7200_iofpga.c" "${COMMON}/dev_c7200_mpfpga.c" "${COMMON}/dev_c7200_sram.c" "${COMMON}/dev_c7200_eth.c" "${COMMON}/dev_c7200_serial.c" "${COMMON}/dev_c7200_pos.c" "${COMMON}/dev_c7200_bri.c" "${COMMON}/dev_c7200_jcpa.c" "${COMMON}/dev_c2691.c" "${COMMON}/dev_c2691_iofpga.c" "${COMMON}/dev_c2691_eth.c" "${COMMON}/dev_c2691_serial.c" "${COMMON}/dev_c2691_wic.c" "${COMMON}/dev_c2691_pcmod.c" "${COMMON}/dev_c3725.c" "${COMMON}/dev_c3725_iofpga.c" "${COMMON}/dev_c3725_eth.c" "${COMMON}/dev_c3725_serial.c" "${COMMON}/dev_c3725_wic.c" "${COMMON}/dev_c3725_pcmod.c" "${COMMON}/dev_c3745.c" "${COMMON}/dev_c3745_iofpga.c" "${COMMON}/dev_c3745_eth.c" "${COMMON}/dev_c3745_serial.c" "${COMMON}/dev_c3745_wic.c" "${COMMON}/dev_c3745_pcmod.c" "${COMMON}/dev_c2600.c" "${COMMON}/dev_c2600_pci.c" "${COMMON}/dev_c2600_iofpga.c" "${COMMON}/dev_c2600_eth.c" "${COMMON}/dev_c2600_pcmod.c" "${COMMON}/dev_c2600_wic.c" "${COMMON}/dev_c1700.c" "${COMMON}/dev_c1700_iofpga.c" "${COMMON}/dev_c1700_eth.c" "${COMMON}/dev_c1700_wic.c" "${COMMON}/dev_c6msfc1.c" "${COMMON}/dev_c6msfc1_iofpga.c" "${COMMON}/dev_c6msfc1_mpfpga.c" "${COMMON}/dev_c6sup1.c" "${COMMON}/dev_c6sup1_iofpga.c" "${COMMON}/dev_c6sup1_mpfpga.c" "${COMMON}/dev_nm_16esw.c" "${COMMON}/dev_pa_a1.c" "${COMMON}/dev_pa_mc8te1.c" "${COMMON}/dev_sb1.c" "${COMMON}/dev_sb1_io.c" "${COMMON}/dev_sb1_pci.c" "${LOCAL}/hypervisor.c" "${COMMON}/hv_nio.c" "${COMMON}/hv_nio_bridge.c" "${COMMON}/hv_frsw.c" "${COMMON}/hv_atmsw.c" "${COMMON}/hv_atm_bridge.c" "${COMMON}/hv_ethsw.c" "${LOCAL}/hv_vm.c" "${COMMON}/hv_vm_debug.c" "${COMMON}/hv_store.c" "${COMMON}/hv_c7200.c" "${COMMON}/hv_c3600.c" "${COMMON}/hv_c2691.c" "${COMMON}/hv_c3725.c" "${COMMON}/hv_c3745.c" "${COMMON}/hv_c2600.c" "${COMMON}/hv_c1700.c" "${COMMON}/rommon_var.c" "${COMMON}/get_cpu_time.c" "${COMMON}/fs_fat.c" "${COMMON}/fs_mbr.c" "${COMMON}/fs_nvram.c" "${COMMON}/dev_lxt970a.c" ) if ( ENABLE_LINUX_ETH ) set ( _files ${_files} "${COMMON}/linux_eth.c" ) endif ( ENABLE_LINUX_ETH ) if ( ENABLE_GEN_ETH ) set ( _files ${_files} "${COMMON}/gen_eth.c" ) endif ( ENABLE_GEN_ETH ) set ( _dependencies ppc32_microcode_dump_stable mips64_microcode_dump_stable ) # dynamips_amd64_stable if ( "amd64" STREQUAL "${DYNAMIPS_ARCH}" ) add_executable ( dynamips_amd64_stable ${_files} "${LOCAL}/mips64_amd64_trans.c" "${LOCAL}/ppc32_amd64_trans.c" ) add_dependencies ( dynamips_amd64_stable ${_dependencies} ) target_link_libraries ( dynamips_amd64_stable ${DYNAMIPS_LIBRARIES} ) maybe_rename_to_dynamips ( dynamips_amd64_stable ) install_executable ( dynamips_amd64_stable ) endif () # dynamips_x86_stable if ( "x86" STREQUAL "${DYNAMIPS_ARCH}" ) add_executable ( dynamips_x86_stable ${_files} "${LOCAL}/mips64_x86_trans.c" "${LOCAL}/ppc32_x86_trans.c" ) add_dependencies ( dynamips_x86_stable ${_dependencies} ) target_link_libraries ( dynamips_x86_stable ${DYNAMIPS_LIBRARIES} ) maybe_rename_to_dynamips ( dynamips_x86_stable ) install_executable ( dynamips_x86_stable ) endif () # dynamips_nojit_stable if ( "nojit" STREQUAL "${DYNAMIPS_ARCH}" ) add_executable ( dynamips_nojit_stable ${_files} "${LOCAL}/mips64_nojit_trans.c" "${COMMON}/ppc32_nojit_trans.c" ) add_dependencies ( dynamips_nojit_stable ${_dependencies} ) target_link_libraries ( dynamips_nojit_stable ${DYNAMIPS_LIBRARIES} ) maybe_rename_to_dynamips ( dynamips_nojit_stable ) install_executable ( dynamips_nojit_stable ) endif () dynamips-0.2.14/stable/amd64-codegen.h000066400000000000000000001671241241034141600173710ustar00rootroot00000000000000/* * amd64-codegen.h: Macros for generating amd64 code * * Authors: * Paolo Molaro (lupus@ximian.com) * Intel Corporation (ORP Project) * Sergey Chaban (serge@wildwestsoftware.com) * Dietmar Maurer (dietmar@ximian.com) * Patrik Torstensson * Zalman Stern * * Copyright (C) 2000 Intel Corporation. All rights reserved. * Copyright (C) 2001, 2002 Ximian, Inc. */ #ifndef AMD64_H #define AMD64_H typedef enum { AMD64_RAX = 0, AMD64_RCX = 1, AMD64_RDX = 2, AMD64_RBX = 3, AMD64_RSP = 4, AMD64_RBP = 5, AMD64_RSI = 6, AMD64_RDI = 7, AMD64_R8 = 8, AMD64_R9 = 9, AMD64_R10 = 10, AMD64_R11 = 11, AMD64_R12 = 12, AMD64_R13 = 13, AMD64_R14 = 14, AMD64_R15 = 15, AMD64_RIP = 16, AMD64_NREG } AMD64_Reg_No; typedef enum { AMD64_XMM0 = 0, AMD64_XMM1 = 1, AMD64_XMM2 = 2, AMD64_XMM3 = 3, AMD64_XMM4 = 4, AMD64_XMM5 = 5, AMD64_XMM6 = 6, AMD64_XMM7 = 7, AMD64_XMM8 = 8, AMD64_XMM9 = 9, AMD64_XMM10 = 10, AMD64_XMM11 = 11, AMD64_XMM12 = 12, AMD64_XMM13 = 13, AMD64_XMM14 = 14, AMD64_XMM15 = 15, AMD64_XMM_NREG = 16, } AMD64_XMM_Reg_No; typedef enum { AMD64_REX_B = 1, /* The register in r/m field, base register in SIB byte, or reg in opcode is 8-15 rather than 0-7 */ AMD64_REX_X = 2, /* The index register in SIB byte is 8-15 rather than 0-7 */ AMD64_REX_R = 4, /* The reg field of ModRM byte is 8-15 rather than 0-7 */ AMD64_REX_W = 8 /* Opeartion is 64-bits instead of 32 (default) or 16 (with 0x66 prefix) */ } AMD64_REX_Bits; #define AMD64_CALLEE_REGS ((1< 4) ? AMD64_REX_W : 0) | \ (((reg_modrm) > 7) ? AMD64_REX_R : 0) | \ (((reg_index) > 7) ? AMD64_REX_X : 0) | \ (((reg_rm_base_opcode) > 7) ? AMD64_REX_B : 0); \ if ((_amd64_rex_bits != 0) || (((width) == 1))) *(inst)++ = AMD64_REX(_amd64_rex_bits); \ } while (0) typedef union { long val; unsigned char b [8]; } amd64_imm_buf; #include "x86-codegen.h" #define amd64_bswap32(inst,reg) \ do { \ *(inst)++ = 0x0f; \ *(inst)++ = (unsigned char)0xc8 + (reg); \ } while (0) /* In 64 bit mode, all registers have a low byte subregister */ #undef X86_IS_BYTE_REG #define X86_IS_BYTE_REG(reg) 1 #define amd64_modrm_mod(modrm) ((modrm) >> 6) #define amd64_modrm_reg(modrm) (((modrm) >> 3) & 0x7) #define amd64_modrm_rm(modrm) ((modrm) & 0x7) #define amd64_rex_r(rex) ((((rex) >> 2) & 0x1) << 3) #define amd64_rex_x(rex) ((((rex) >> 1) & 0x1) << 3) #define amd64_rex_b(rex) ((((rex) >> 0) & 0x1) << 3) #define amd64_is_imm32(val) ((glong)val >= -((glong)1<<31) && (glong)val <= (((glong)1<<31)-1)) #define x86_imm_emit64(inst,imm) \ do { \ amd64_imm_buf imb; imb.val = (long) (imm); \ *(inst)++ = imb.b [0]; \ *(inst)++ = imb.b [1]; \ *(inst)++ = imb.b [2]; \ *(inst)++ = imb.b [3]; \ *(inst)++ = imb.b [4]; \ *(inst)++ = imb.b [5]; \ *(inst)++ = imb.b [6]; \ *(inst)++ = imb.b [7]; \ } while (0) #define amd64_membase_emit(inst,reg,basereg,disp) do { \ if ((basereg) == AMD64_RIP) { \ x86_address_byte ((inst), 0, (reg)&0x7, 5); \ x86_imm_emit32 ((inst), (disp)); \ } \ else \ x86_membase_emit ((inst),(reg)&0x7, (basereg)&0x7, (disp)); \ } while (0) #define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) \ do { \ if ((reg) == X86_EAX) { \ amd64_emit_rex(inst, size, 0, 0, 0); \ *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ x86_imm_emit32 ((inst), (imm)); \ break; \ } \ if (x86_is_imm8((imm))) { \ amd64_emit_rex(inst, size, 0, 0, (reg)); \ *(inst)++ = (unsigned char)0x83; \ x86_reg_emit ((inst), (opc), (reg)); \ x86_imm_emit8 ((inst), (imm)); \ } else { \ amd64_emit_rex(inst, size, 0, 0, (reg)); \ *(inst)++ = (unsigned char)0x81; \ x86_reg_emit ((inst), (opc), (reg)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define amd64_alu_reg_imm(inst,opc,reg,imm) amd64_alu_reg_imm_size((inst),(opc),(reg),(imm),8) #define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) \ do { \ amd64_emit_rex(inst, size, (dreg), 0, (reg)); \ *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) #define amd64_alu_reg_reg(inst,opc,dreg,reg) amd64_alu_reg_reg_size ((inst),(opc),(dreg),(reg),8) #define amd64_mov_regp_reg(inst,regp,reg,size) \ do { \ if ((size) == 2) \ *(inst)++ = (unsigned char)0x66; \ amd64_emit_rex(inst, (size), (reg), 0, (regp)); \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x88; break; \ case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ default: assert (0); \ } \ x86_regp_emit ((inst), (reg), (regp)); \ } while (0) #define amd64_mov_membase_reg(inst,basereg,disp,reg,size) \ do { \ if ((size) == 2) \ *(inst)++ = (unsigned char)0x66; \ amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x88; break; \ case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ default: assert (0); \ } \ x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ } while (0) #define amd64_mov_reg_reg(inst,dreg,reg,size) \ do { \ if ((size) == 2) \ *(inst)++ = (unsigned char)0x66; \ amd64_emit_rex(inst, (size), (dreg), 0, (reg)); \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x8a; break; \ case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) #define amd64_mov_reg_mem(inst,reg,mem,size) \ do { \ if ((size) == 2) \ *(inst)++ = (unsigned char)0x66; \ amd64_emit_rex(inst, (size), (reg), 0, 0); \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x8a; break; \ case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ x86_address_byte ((inst), 0, (reg), 4); \ x86_address_byte ((inst), 0, 4, 5); \ x86_imm_emit32 ((inst), (mem)); \ } while (0) #define amd64_mov_reg_membase(inst,reg,basereg,disp,size) \ do { \ if ((size) == 2) \ *(inst)++ = (unsigned char)0x66; \ amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x8a; break; \ case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define amd64_movzx_reg_membase(inst,reg,basereg,disp,size) \ do { \ amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb6; break; \ case 2: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb7; break; \ case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ } while (0) #define amd64_movsxd_reg_membase(inst,reg,basereg,disp) \ do { \ amd64_emit_rex(inst,8,(reg),0,(basereg)); \ *(inst)++ = (unsigned char)0x63; \ x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ } while (0) #define amd64_movsxd_reg_reg(inst,dreg,reg) \ do { \ amd64_emit_rex(inst,8,(dreg),0,(reg)); \ *(inst)++ = (unsigned char)0x63; \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) /* Pretty much the only instruction that supports a 64-bit immediate. Optimize for common case of * 32-bit immediate. Pepper with casts to avoid warnings. */ #define amd64_mov_reg_imm_size(inst,reg,imm,size) \ do { \ amd64_emit_rex(inst, (size), 0, 0, (reg)); \ *(inst)++ = (unsigned char)0xb8 + ((reg) & 0x7); \ if ((size) == 8) \ x86_imm_emit64 ((inst), (long)(imm)); \ else \ x86_imm_emit32 ((inst), (int)(long)(imm)); \ } while (0) #define amd64_mov_reg_imm(inst,reg,imm) \ do { \ int _amd64_width_temp = ((long)(imm) == (long)(int)(long)(imm)); \ amd64_mov_reg_imm_size ((inst), (reg), (imm), (_amd64_width_temp ? 4 : 8)); \ } while (0) #define amd64_set_reg_template(inst,reg) amd64_mov_reg_imm_size ((inst),(reg), 0, 8) #define amd64_set_template(inst,reg) amd64_set_reg_template((inst),(reg)) #define amd64_mov_membase_imm(inst,basereg,disp,imm,size) \ do { \ if ((size) == 2) \ *(inst)++ = (unsigned char)0x66; \ amd64_emit_rex(inst, (size) == 1 ? 0 : (size), 0, 0, (basereg)); \ if ((size) == 1) { \ *(inst)++ = (unsigned char)0xc6; \ x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ x86_imm_emit8 ((inst), (imm)); \ } else if ((size) == 2) { \ *(inst)++ = (unsigned char)0xc7; \ x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ x86_imm_emit16 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0xc7; \ x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define amd64_lea_membase(inst,reg,basereg,disp) \ do { \ amd64_emit_rex(inst, 8, (reg), 0, (basereg)); \ *(inst)++ = (unsigned char)0x8d; \ amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) /* Instruction are implicitly 64-bits so don't generate REX for just the size. */ #define amd64_push_reg(inst,reg) \ do { \ amd64_emit_rex(inst, 0, 0, 0, (reg)); \ *(inst)++ = (unsigned char)0x50 + ((reg) & 0x7); \ } while (0) /* Instruction is implicitly 64-bits so don't generate REX for just the size. */ #define amd64_push_membase(inst,basereg,disp) \ do { \ amd64_emit_rex(inst, 0, 0, 0, (basereg)); \ *(inst)++ = (unsigned char)0xff; \ x86_membase_emit ((inst), 6, (basereg) & 0x7, (disp)); \ } while (0) #define amd64_pop_reg(inst,reg) \ do { \ amd64_emit_rex(inst, 0, 0, 0, (reg)); \ *(inst)++ = (unsigned char)0x58 + ((reg) & 0x7); \ } while (0) #define amd64_call_reg(inst,reg) \ do { \ amd64_emit_rex(inst, 8, 0, 0, (reg)); \ *(inst)++ = (unsigned char)0xff; \ x86_reg_emit ((inst), 2, ((reg) & 0x7)); \ } while (0) #define amd64_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) #define amd64_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) #define amd64_movsd_reg_regp(inst,reg,regp) \ do { \ *(inst)++ = (unsigned char)0xf2; \ amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x10; \ x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ } while (0) #define amd64_movsd_regp_reg(inst,regp,reg) \ do { \ *(inst)++ = (unsigned char)0xf2; \ amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x11; \ x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ } while (0) #define amd64_movss_reg_regp(inst,reg,regp) \ do { \ *(inst)++ = (unsigned char)0xf3; \ amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x10; \ x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ } while (0) #define amd64_movss_regp_reg(inst,regp,reg) \ do { \ *(inst)++ = (unsigned char)0xf3; \ amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x11; \ x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ } while (0) #define amd64_movsd_reg_membase(inst,reg,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xf2; \ amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x10; \ x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ } while (0) #define amd64_movss_reg_membase(inst,reg,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xf3; \ amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x10; \ x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ } while (0) #define amd64_movsd_membase_reg(inst,basereg,disp,reg) \ do { \ *(inst)++ = (unsigned char)0xf2; \ amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x11; \ x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ } while (0) #define amd64_movss_membase_reg(inst,basereg,disp,reg) \ do { \ *(inst)++ = (unsigned char)0xf3; \ amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x11; \ x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ } while (0) /* The original inc_reg opcode is used as the REX prefix */ #define amd64_inc_reg_size(inst,reg,size) \ do { \ amd64_emit_rex ((inst),(size),0,0,(reg)); \ *(inst)++ = (unsigned char)0xff; \ x86_reg_emit ((inst),0,(reg) & 0x7); \ } while (0) #define amd64_dec_reg_size(inst,reg,size) \ do { \ amd64_emit_rex ((inst),(size),0,0,(reg)); \ *(inst)++ = (unsigned char)0xff; \ x86_reg_emit ((inst),1,(reg) & 0x7); \ } while (0) #define amd64_padding_size(inst,size) \ do { if (size == 1) x86_padding ((inst),(size)); else { amd64_emit_rex ((inst),8,0,0,0); x86_padding((inst),(size) - 1); } } while (0) #define amd64_fld_membase_size(inst,basereg,disp,is_double,size) do { \ amd64_emit_rex ((inst),0,0,0,(basereg)); \ *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ amd64_membase_emit ((inst), 0, (basereg), (disp)); \ } while (0) #define amd64_call_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); *(inst)++ = (unsigned char)0xff; amd64_membase_emit ((inst),2, (basereg),(disp)); } while (0) /* * SSE */ #define emit_opcode3(inst,op1,op2,op3) do { \ *(inst)++ = (unsigned char)(op1); \ *(inst)++ = (unsigned char)(op2); \ *(inst)++ = (unsigned char)(op3); \ } while (0) #define emit_sse_reg_reg_size(inst,dreg,reg,op1,op2,op3,size) do { \ *(inst)++ = (unsigned char)(op1); \ amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ *(inst)++ = (unsigned char)(op2); \ *(inst)++ = (unsigned char)(op3); \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) #define emit_sse_reg_reg(inst,dreg,reg,op1,op2,op3) emit_sse_reg_reg_size ((inst), (dreg), (reg), (op1), (op2), (op3), 0) #define emit_sse_membase_reg(inst,basereg,disp,reg,op1,op2,op3) do { \ *(inst)++ = (unsigned char)(op1); \ amd64_emit_rex ((inst), 0, (reg), 0, (basereg)); \ *(inst)++ = (unsigned char)(op2); \ *(inst)++ = (unsigned char)(op3); \ amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define emit_sse_reg_membase(inst,dreg,basereg,disp,op1,op2,op3) do { \ *(inst)++ = (unsigned char)(op1); \ amd64_emit_rex ((inst), 0, (dreg), 0, (basereg) == AMD64_RIP ? 0 : (basereg)); \ *(inst)++ = (unsigned char)(op2); \ *(inst)++ = (unsigned char)(op3); \ amd64_membase_emit ((inst), (dreg), (basereg), (disp)); \ } while (0) #define amd64_sse_xorpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg), 0x66, 0x0f, 0x57) #define amd64_sse_xorpd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst),(dreg),(basereg), (disp), 0x66, 0x0f, 0x57) #define amd64_sse_movsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x10) #define amd64_sse_movsd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0xf2, 0x0f, 0x10) #define amd64_sse_movsd_membase_reg(inst,basereg,disp,reg) emit_sse_membase_reg ((inst), (basereg), (disp), (reg), 0xf2, 0x0f, 0x11) #define amd64_sse_movss_membase_reg(inst,basereg,disp,reg) emit_sse_membase_reg ((inst), (basereg), (disp), (reg), 0xf3, 0x0f, 0x11) #define amd64_sse_movss_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0xf3, 0x0f, 0x10) #define amd64_sse_comisd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg),0x66,0x0f,0x2f) #define amd64_sse_comisd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0x66, 0x0f, 0x2f) #define amd64_sse_cvtsd2si_reg_reg(inst,dreg,reg) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2d, 8) #define amd64_sse_cvttsd2si_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2c, (size)) #define amd64_sse_cvttsd2si_reg_reg(inst,dreg,reg) amd64_sse_cvttsd2si_reg_reg_size ((inst), (dreg), (reg), 8) #define amd64_sse_cvtsi2sd_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2a, (size)) #define amd64_sse_cvtsi2sd_reg_reg(inst,dreg,reg) amd64_sse_cvtsi2sd_reg_reg_size ((inst), (dreg), (reg), 8) #define amd64_sse_cvtsd2ss_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5a) #define amd64_sse_cvtss2sd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf3, 0x0f, 0x5a) #define amd64_sse_addsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x58) #define amd64_sse_subsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5c) #define amd64_sse_mulsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x59) #define amd64_sse_divsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5e) /* Generated from x86-codegen.h */ #define amd64_breakpoint_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_breakpoint(inst); } while (0) #define amd64_cld_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_cld(inst); } while (0) #define amd64_stosb_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_stosb(inst); } while (0) #define amd64_stosl_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_stosl(inst); } while (0) #define amd64_stosd_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_stosd(inst); } while (0) #define amd64_movsb_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_movsb(inst); } while (0) #define amd64_movsl_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_movsl(inst); } while (0) #define amd64_movsd_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_movsd(inst); } while (0) #define amd64_prefix_size(inst,p,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_prefix((inst), p); } while (0) #define amd64_rdtsc_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_rdtsc(inst); } while (0) #define amd64_cmpxchg_reg_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_cmpxchg_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); } while (0) #define amd64_cmpxchg_mem_reg_size(inst,mem,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_cmpxchg_mem_reg((inst),(mem),((reg)&0x7)); } while (0) #define amd64_cmpxchg_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_cmpxchg_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7)); } while (0) #define amd64_xchg_reg_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_xchg_reg_reg((inst),((dreg)&0x7),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) #define amd64_xchg_mem_reg_size(inst,mem,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_xchg_mem_reg((inst),(mem),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) #define amd64_xchg_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg))); x86_xchg_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) #define amd64_inc_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_inc_mem((inst),(mem)); } while (0) #define amd64_inc_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_inc_membase((inst),((basereg)&0x7),(disp)); } while (0) //#define amd64_inc_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_inc_reg((inst),((reg)&0x7)); } while (0) #define amd64_dec_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_dec_mem((inst),(mem)); } while (0) #define amd64_dec_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_dec_membase((inst),((basereg)&0x7),(disp)); } while (0) //#define amd64_dec_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_dec_reg((inst),((reg)&0x7)); } while (0) #define amd64_not_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_not_mem((inst),(mem)); } while (0) #define amd64_not_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_not_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_not_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_not_reg((inst),((reg)&0x7)); } while (0) #define amd64_neg_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_neg_mem((inst),(mem)); } while (0) #define amd64_neg_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_neg_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_neg_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_neg_reg((inst),((reg)&0x7)); } while (0) #define amd64_nop_size(inst,size) do { x86_nop(inst); } while (0) //#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_reg_imm((inst),(opc),((reg)&0x7),(imm)); } while (0) #define amd64_alu_mem_imm_size(inst,opc,mem,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_alu_mem_imm((inst),(opc),(mem),(imm)); } while (0) #define amd64_alu_membase_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_alu_membase_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); } while (0) #define amd64_alu_mem_reg_size(inst,opc,mem,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_mem_reg((inst),(opc),(mem),((reg)&0x7)); } while (0) #define amd64_alu_membase_reg_size(inst,opc,basereg,disp,reg,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_alu_membase_reg((inst),(opc),((basereg)&0x7),(disp),((reg)&0x7)); } while (0) //#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_alu_reg_reg((inst),(opc),((dreg)&0x7),((reg)&0x7)); } while (0) #define amd64_alu_reg8_reg8_size(inst,opc,dreg,reg,is_dreg_h,is_reg_h,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_alu_reg8_reg8((inst),(opc),((dreg)&0x7),((reg)&0x7),(is_dreg_h),(is_reg_h)); } while (0) #define amd64_alu_reg_mem_size(inst,opc,reg,mem,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_reg_mem((inst),(opc),((reg)&0x7),(mem)); } while (0) #define amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_alu_reg_membase((inst),(opc),((reg)&0x7),((basereg)&0x7),(disp)); } while (0) #define amd64_test_reg_imm_size(inst,reg,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_test_reg_imm((inst),((reg)&0x7),(imm)); } while (0) #define amd64_test_mem_imm_size(inst,mem,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_test_mem_imm((inst),(mem),(imm)); } while (0) #define amd64_test_membase_imm_size(inst,basereg,disp,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_test_membase_imm((inst),((basereg)&0x7),(disp),(imm)); } while (0) #define amd64_test_reg_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_test_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); } while (0) #define amd64_test_mem_reg_size(inst,mem,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_test_mem_reg((inst),(mem),((reg)&0x7)); } while (0) #define amd64_test_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_test_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7)); } while (0) #define amd64_shift_reg_imm_size(inst,opc,reg,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_reg_imm((inst),(opc),((reg)&0x7),(imm)); } while (0) #define amd64_shift_mem_imm_size(inst,opc,mem,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_mem_imm((inst),(opc),(mem),(imm)); } while (0) #define amd64_shift_membase_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_shift_membase_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); } while (0) #define amd64_shift_reg_size(inst,opc,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_reg((inst),(opc),((reg)&0x7)); } while (0) #define amd64_shift_mem_size(inst,opc,mem,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_mem((inst),(opc),(mem)); } while (0) #define amd64_shift_membase_size(inst,opc,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_shift_membase((inst),(opc),((basereg)&0x7),(disp)); } while (0) #define amd64_shrd_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shrd_reg((inst),((dreg)&0x7),((reg)&0x7)); } while (0) #define amd64_shrd_reg_imm_size(inst,dreg,reg,shamt,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shrd_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(shamt)); } while (0) #define amd64_shld_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shld_reg((inst),((dreg)&0x7),((reg)&0x7)); } while (0) #define amd64_shld_reg_imm_size(inst,dreg,reg,shamt,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shld_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(shamt)); } while (0) #define amd64_mul_reg_size(inst,reg,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mul_reg((inst),((reg)&0x7),(is_signed)); } while (0) #define amd64_mul_mem_size(inst,mem,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_mul_mem((inst),(mem),(is_signed)); } while (0) #define amd64_mul_membase_size(inst,basereg,disp,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_mul_membase((inst),((basereg)&0x7),(disp),(is_signed)); } while (0) #define amd64_imul_reg_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_imul_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); } while (0) #define amd64_imul_reg_mem_size(inst,reg,mem,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_imul_reg_mem((inst),((reg)&0x7),(mem)); } while (0) #define amd64_imul_reg_membase_size(inst,reg,basereg,disp,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_imul_reg_membase((inst),((reg)&0x7),((basereg)&0x7),(disp)); } while (0) #define amd64_imul_reg_reg_imm_size(inst,dreg,reg,imm,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_imul_reg_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(imm)); } while (0) #define amd64_imul_reg_mem_imm_size(inst,reg,mem,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_imul_reg_mem_imm((inst),((reg)&0x7),(mem),(imm)); } while (0) #define amd64_imul_reg_membase_imm_size(inst,reg,basereg,disp,imm,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_imul_reg_membase_imm((inst),((reg)&0x7),((basereg)&0x7),(disp),(imm)); } while (0) #define amd64_div_reg_size(inst,reg,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_div_reg((inst),((reg)&0x7),(is_signed)); } while (0) #define amd64_div_mem_size(inst,mem,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_div_mem((inst),(mem),(is_signed)); } while (0) #define amd64_div_membase_size(inst,basereg,disp,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_div_membase((inst),((basereg)&0x7),(disp),(is_signed)); } while (0) #define amd64_mov_mem_reg_size(inst,mem,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_mem_reg((inst),(mem),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) //#define amd64_mov_regp_reg_size(inst,regp,reg,size) do { amd64_emit_rex ((inst),(size),(regp),0,(reg)); x86_mov_regp_reg((inst),(regp),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) //#define amd64_mov_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_mov_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) #define amd64_mov_memindex_reg_size(inst,basereg,disp,indexreg,shift,reg,size) do { amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_mov_memindex_reg((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) #define amd64_mov_reg_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_mov_reg_reg((inst),((dreg)&0x7),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) //#define amd64_mov_reg_mem_size(inst,reg,mem,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_reg_mem((inst),((reg)&0x7),(mem),(size) == 8 ? 4 : (size)); } while (0) //#define amd64_mov_reg_membase_size(inst,reg,basereg,disp,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_mov_reg_membase((inst),((reg)&0x7),((basereg)&0x7),(disp),(size) == 8 ? 4 : (size)); } while (0) #define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) do { amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_mov_reg_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(size) == 8 ? 4 : (size)); } while (0) #define amd64_clear_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_clear_reg((inst),((reg)&0x7)); } while (0) //#define amd64_mov_reg_imm_size(inst,reg,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_reg_imm((inst),((reg)&0x7),(imm)); } while (0) #define amd64_mov_mem_imm_size(inst,mem,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_mov_mem_imm((inst),(mem),(imm),(size) == 8 ? 4 : (size)); } while (0) //#define amd64_mov_membase_imm_size(inst,basereg,disp,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_mov_membase_imm((inst),((basereg)&0x7),(disp),(imm),(size) == 8 ? 4 : (size)); } while (0) #define amd64_mov_memindex_imm_size(inst,basereg,disp,indexreg,shift,imm,size) do { amd64_emit_rex ((inst),(size),0,(indexreg),(basereg)); x86_mov_memindex_imm((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(imm),(size) == 8 ? 4 : (size)); } while (0) #define amd64_lea_mem_size(inst,reg,mem,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_lea_mem((inst),((reg)&0x7),(mem)); } while (0) //#define amd64_lea_membase_size(inst,reg,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_lea_membase((inst),((reg)&0x7),((basereg)&0x7),(disp)); } while (0) #define amd64_lea_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) do { amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_lea_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift)); } while (0) #define amd64_widen_reg_size(inst,dreg,reg,is_signed,is_half,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_widen_reg((inst),((dreg)&0x7),((reg)&0x7),(is_signed),(is_half)); } while (0) #define amd64_widen_mem_size(inst,dreg,mem,is_signed,is_half,size) do { amd64_emit_rex ((inst),(size),(dreg),0,0); x86_widen_mem((inst),((dreg)&0x7),(mem),(is_signed),(is_half)); } while (0) #define amd64_widen_membase_size(inst,dreg,basereg,disp,is_signed,is_half,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(basereg)); x86_widen_membase((inst),((dreg)&0x7),((basereg)&0x7),(disp),(is_signed),(is_half)); } while (0) #define amd64_widen_memindex_size(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half,size) do { amd64_emit_rex ((inst),(size),(dreg),(indexreg),(basereg)); x86_widen_memindex((inst),((dreg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(is_signed),(is_half)); } while (0) #define amd64_cdq_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_cdq(inst); } while (0) #define amd64_wait_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_wait(inst); } while (0) #define amd64_fp_op_mem_size(inst,opc,mem,is_double,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fp_op_mem((inst),(opc),(mem),(is_double)); } while (0) #define amd64_fp_op_membase_size(inst,opc,basereg,disp,is_double,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fp_op_membase((inst),(opc),((basereg)&0x7),(disp),(is_double)); } while (0) #define amd64_fp_op_size(inst,opc,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fp_op((inst),(opc),(index)); } while (0) #define amd64_fp_op_reg_size(inst,opc,index,pop_stack,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fp_op_reg((inst),(opc),(index),(pop_stack)); } while (0) #define amd64_fp_int_op_membase_size(inst,opc,basereg,disp,is_int,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fp_int_op_membase((inst),(opc),((basereg)&0x7),(disp),(is_int)); } while (0) #define amd64_fstp_size(inst,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fstp((inst),(index)); } while (0) #define amd64_fcompp_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fcompp(inst); } while (0) #define amd64_fucompp_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fucompp(inst); } while (0) #define amd64_fnstsw_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fnstsw(inst); } while (0) #define amd64_fnstcw_size(inst,mem,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fnstcw((inst),(mem)); } while (0) #define amd64_fnstcw_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_fnstcw_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_fldcw_size(inst,mem,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fldcw((inst),(mem)); } while (0) #define amd64_fldcw_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fldcw_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_fchs_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fchs(inst); } while (0) #define amd64_frem_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_frem(inst); } while (0) #define amd64_fxch_size(inst,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fxch((inst),(index)); } while (0) #define amd64_fcomi_size(inst,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fcomi((inst),(index)); } while (0) #define amd64_fcomip_size(inst,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fcomip((inst),(index)); } while (0) #define amd64_fucomi_size(inst,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fucomi((inst),(index)); } while (0) #define amd64_fucomip_size(inst,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fucomip((inst),(index)); } while (0) #define amd64_fld_size(inst,mem,is_double,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fld((inst),(mem),(is_double)); } while (0) //#define amd64_fld_membase_size(inst,basereg,disp,is_double,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fld_membase((inst),((basereg)&0x7),(disp),(is_double)); } while (0) #define amd64_fld80_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fld80_mem((inst),(mem)); } while (0) #define amd64_fld80_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_fld80_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_fild_size(inst,mem,is_long,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fild((inst),(mem),(is_long)); } while (0) #define amd64_fild_membase_size(inst,basereg,disp,is_long,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fild_membase((inst),((basereg)&0x7),(disp),(is_long)); } while (0) #define amd64_fld_reg_size(inst,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fld_reg((inst),(index)); } while (0) #define amd64_fldz_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fldz(inst); } while (0) #define amd64_fld1_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fld1(inst); } while (0) #define amd64_fldpi_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fldpi(inst); } while (0) #define amd64_fst_size(inst,mem,is_double,pop_stack,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fst((inst),(mem),(is_double),(pop_stack)); } while (0) #define amd64_fst_membase_size(inst,basereg,disp,is_double,pop_stack,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fst_membase((inst),((basereg)&0x7),(disp),(is_double),(pop_stack)); } while (0) #define amd64_fst80_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fst80_mem((inst),(mem)); } while (0) #define amd64_fst80_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fst80_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_fist_pop_size(inst,mem,is_long,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fist_pop((inst),(mem),(is_long)); } while (0) #define amd64_fist_pop_membase_size(inst,basereg,disp,is_long,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fist_pop_membase((inst),((basereg)&0x7),(disp),(is_long)); } while (0) #define amd64_fstsw_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fstsw(inst); } while (0) #define amd64_fist_membase_size(inst,basereg,disp,is_int,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fist_membase((inst),((basereg)&0x7),(disp),(is_int)); } while (0) //#define amd64_push_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_push_reg((inst),((reg)&0x7)); } while (0) #define amd64_push_regp_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_push_regp((inst),((reg)&0x7)); } while (0) #define amd64_push_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_push_mem((inst),(mem)); } while (0) //#define amd64_push_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_push_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_push_memindex_size(inst,basereg,disp,indexreg,shift,size) do { amd64_emit_rex ((inst),(size),0,(indexreg),(basereg)); x86_push_memindex((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift)); } while (0) #define amd64_push_imm_size(inst,imm,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_push_imm((inst),(imm)); } while (0) //#define amd64_pop_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_pop_reg((inst),((reg)&0x7)); } while (0) #define amd64_pop_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_pop_mem((inst),(mem)); } while (0) #define amd64_pop_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_pop_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_pushad_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_pushad(inst); } while (0) #define amd64_pushfd_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_pushfd(inst); } while (0) #define amd64_popad_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_popad(inst); } while (0) #define amd64_popfd_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_popfd(inst); } while (0) #define amd64_loop_size(inst,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_loop((inst),(imm)); } while (0) #define amd64_loope_size(inst,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_loope((inst),(imm)); } while (0) #define amd64_loopne_size(inst,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_loopne((inst),(imm)); } while (0) #define amd64_jump32_size(inst,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_jump32((inst),(imm)); } while (0) #define amd64_jump8_size(inst,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_jump8((inst),(imm)); } while (0) #define amd64_jump_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_jump_reg((inst),((reg)&0x7)); } while (0) #define amd64_jump_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_jump_mem((inst),(mem)); } while (0) #define amd64_jump_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_jump_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_jump_code_size(inst,target,size) do { x86_jump_code((inst),(target)); } while (0) #define amd64_jump_disp_size(inst,disp,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_jump_disp((inst),(disp)); } while (0) #define amd64_branch8_size(inst,cond,imm,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_branch8((inst),(cond),(imm),(is_signed)); } while (0) #define amd64_branch32_size(inst,cond,imm,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_branch32((inst),(cond),(imm),(is_signed)); } while (0) #define amd64_branch_size(inst,cond,target,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_branch((inst),(cond),(target),(is_signed)); } while (0) #define amd64_branch_disp_size(inst,cond,disp,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_branch_disp((inst),(cond),(disp),(is_signed)); } while (0) #define amd64_set_reg_size(inst,cond,reg,is_signed,size) do { amd64_emit_rex((inst),1,0,0,(reg)); x86_set_reg((inst),(cond),((reg)&0x7),(is_signed)); } while (0) #define amd64_set_mem_size(inst,cond,mem,is_signed,size) do { x86_set_mem((inst),(cond),(mem),(is_signed)); } while (0) #define amd64_set_membase_size(inst,cond,basereg,disp,is_signed,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_set_membase((inst),(cond),((basereg)&0x7),(disp),(is_signed)); } while (0) #define amd64_call_imm_size(inst,disp,size) do { x86_call_imm((inst),(disp)); } while (0) //#define amd64_call_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_call_reg((inst),((reg)&0x7)); } while (0) #define amd64_call_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_call_mem((inst),(mem)); } while (0) #define amd64_call_code_size(inst,target,size) do { x86_call_code((inst),(target)); } while (0) //#define amd64_ret_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_ret(inst); } while (0) #define amd64_ret_imm_size(inst,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_ret_imm((inst),(imm)); } while (0) #define amd64_cmov_reg_size(inst,cond,is_signed,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_cmov_reg((inst),(cond),(is_signed),((dreg)&0x7),((reg)&0x7)); } while (0) #define amd64_cmov_mem_size(inst,cond,is_signed,reg,mem,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_cmov_mem((inst),(cond),(is_signed),((reg)&0x7),(mem)); } while (0) #define amd64_cmov_membase_size(inst,cond,is_signed,reg,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_cmov_membase((inst),(cond),(is_signed),((reg)&0x7),((basereg)&0x7),(disp)); } while (0) #define amd64_enter_size(inst,framesize) do { amd64_emit_rex ((inst),(size),0,0,0); x86_enter((inst),(framesize)); } while (0) //#define amd64_leave_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_leave(inst); } while (0) #define amd64_sahf_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_sahf(inst); } while (0) #define amd64_fsin_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fsin(inst); } while (0) #define amd64_fcos_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fcos(inst); } while (0) #define amd64_fabs_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fabs(inst); } while (0) #define amd64_ftst_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_ftst(inst); } while (0) #define amd64_fxam_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fxam(inst); } while (0) #define amd64_fpatan_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fpatan(inst); } while (0) #define amd64_fprem_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fprem(inst); } while (0) #define amd64_fprem1_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fprem1(inst); } while (0) #define amd64_frndint_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_frndint(inst); } while (0) #define amd64_fsqrt_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fsqrt(inst); } while (0) #define amd64_fptan_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fptan(inst); } while (0) //#define amd64_padding_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_padding((inst),(size)); } while (0) #define amd64_prolog_size(inst,frame_size,reg_mask,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_prolog((inst),(frame_size),(reg_mask)); } while (0) #define amd64_epilog_size(inst,reg_mask,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_epilog((inst),(reg_mask)); } while (0) #define amd64_xadd_reg_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_xadd_reg_reg ((inst), (dreg), (reg), (size)); } while (0) #define amd64_xadd_mem_reg_size(inst,mem,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_xadd_mem_reg((inst),(mem),((reg)&0x7), (size)); } while (0) #define amd64_xadd_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_xadd_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size)); } while (0) #define amd64_breakpoint(inst) amd64_breakpoint_size(inst,8) #define amd64_cld(inst) amd64_cld_size(inst,8) #define amd64_stosb(inst) amd64_stosb_size(inst,8) #define amd64_stosl(inst) amd64_stosl_size(inst,8) #define amd64_stosd(inst) amd64_stosd_size(inst,8) #define amd64_movsb(inst) amd64_movsb_size(inst,8) #define amd64_movsl(inst) amd64_movsl_size(inst,8) #define amd64_movsd(inst) amd64_movsd_size(inst,8) #define amd64_prefix(inst,p) amd64_prefix_size(inst,p,8) #define amd64_rdtsc(inst) amd64_rdtsc_size(inst,8) #define amd64_cmpxchg_reg_reg(inst,dreg,reg) amd64_cmpxchg_reg_reg_size(inst,dreg,reg,8) #define amd64_cmpxchg_mem_reg(inst,mem,reg) amd64_cmpxchg_mem_reg_size(inst,mem,reg,8) #define amd64_cmpxchg_membase_reg(inst,basereg,disp,reg) amd64_cmpxchg_membase_reg_size(inst,basereg,disp,reg,8) #define amd64_xchg_reg_reg(inst,dreg,reg,size) amd64_xchg_reg_reg_size(inst,dreg,reg,size) #define amd64_xchg_mem_reg(inst,mem,reg,size) amd64_xchg_mem_reg_size(inst,mem,reg,size) #define amd64_xchg_membase_reg(inst,basereg,disp,reg,size) amd64_xchg_membase_reg_size(inst,basereg,disp,reg,size) #define amd64_xadd_reg_reg(inst,dreg,reg,size) amd64_xadd_reg_reg_size(inst,dreg,reg,size) #define amd64_xadd_mem_reg(inst,mem,reg,size) amd64_xadd_mem_reg_size(inst,mem,reg,size) #define amd64_xadd_membase_reg(inst,basereg,disp,reg,size) amd64_xadd_membase_reg_size(inst,basereg,disp,reg,size) #define amd64_inc_mem(inst,mem) amd64_inc_mem_size(inst,mem,8) #define amd64_inc_membase(inst,basereg,disp) amd64_inc_membase_size(inst,basereg,disp,8) #define amd64_inc_reg(inst,reg) amd64_inc_reg_size(inst,reg,8) #define amd64_dec_mem(inst,mem) amd64_dec_mem_size(inst,mem,8) #define amd64_dec_membase(inst,basereg,disp) amd64_dec_membase_size(inst,basereg,disp,8) #define amd64_dec_reg(inst,reg) amd64_dec_reg_size(inst,reg,8) #define amd64_not_mem(inst,mem) amd64_not_mem_size(inst,mem,8) #define amd64_not_membase(inst,basereg,disp) amd64_not_membase_size(inst,basereg,disp,8) #define amd64_not_reg(inst,reg) amd64_not_reg_size(inst,reg,8) #define amd64_neg_mem(inst,mem) amd64_neg_mem_size(inst,mem,8) #define amd64_neg_membase(inst,basereg,disp) amd64_neg_membase_size(inst,basereg,disp,8) #define amd64_neg_reg(inst,reg) amd64_neg_reg_size(inst,reg,8) #define amd64_nop(inst) amd64_nop_size(inst,8) //#define amd64_alu_reg_imm(inst,opc,reg,imm) amd64_alu_reg_imm_size(inst,opc,reg,imm,8) #define amd64_alu_mem_imm(inst,opc,mem,imm) amd64_alu_mem_imm_size(inst,opc,mem,imm,8) #define amd64_alu_membase_imm(inst,opc,basereg,disp,imm) amd64_alu_membase_imm_size(inst,opc,basereg,disp,imm,8) #define amd64_alu_mem_reg(inst,opc,mem,reg) amd64_alu_mem_reg_size(inst,opc,mem,reg,8) #define amd64_alu_membase_reg(inst,opc,basereg,disp,reg) amd64_alu_membase_reg_size(inst,opc,basereg,disp,reg,8) //#define amd64_alu_reg_reg(inst,opc,dreg,reg) amd64_alu_reg_reg_size(inst,opc,dreg,reg,8) #define amd64_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) amd64_alu_reg8_reg8_size(inst,opc,dreg,reg,is_dreg_h,is_reg_h,8) #define amd64_alu_reg_mem(inst,opc,reg,mem) amd64_alu_reg_mem_size(inst,opc,reg,mem,8) #define amd64_alu_reg_membase(inst,opc,reg,basereg,disp) amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,8) #define amd64_test_reg_imm(inst,reg,imm) amd64_test_reg_imm_size(inst,reg,imm,8) #define amd64_test_mem_imm(inst,mem,imm) amd64_test_mem_imm_size(inst,mem,imm,8) #define amd64_test_membase_imm(inst,basereg,disp,imm) amd64_test_membase_imm_size(inst,basereg,disp,imm,8) #define amd64_test_reg_reg(inst,dreg,reg) amd64_test_reg_reg_size(inst,dreg,reg,8) #define amd64_test_mem_reg(inst,mem,reg) amd64_test_mem_reg_size(inst,mem,reg,8) #define amd64_test_membase_reg(inst,basereg,disp,reg) amd64_test_membase_reg_size(inst,basereg,disp,reg,8) #define amd64_shift_reg_imm(inst,opc,reg,imm) amd64_shift_reg_imm_size(inst,opc,reg,imm,8) #define amd64_shift_mem_imm(inst,opc,mem,imm) amd64_shift_mem_imm_size(inst,opc,mem,imm,8) #define amd64_shift_membase_imm(inst,opc,basereg,disp,imm) amd64_shift_membase_imm_size(inst,opc,basereg,disp,imm,8) #define amd64_shift_reg(inst,opc,reg) amd64_shift_reg_size(inst,opc,reg,8) #define amd64_shift_mem(inst,opc,mem) amd64_shift_mem_size(inst,opc,mem,8) #define amd64_shift_membase(inst,opc,basereg,disp) amd64_shift_membase_size(inst,opc,basereg,disp,8) #define amd64_shrd_reg(inst,dreg,reg) amd64_shrd_reg_size(inst,dreg,reg,8) #define amd64_shrd_reg_imm(inst,dreg,reg,shamt) amd64_shrd_reg_imm_size(inst,dreg,reg,shamt,8) #define amd64_shld_reg(inst,dreg,reg) amd64_shld_reg_size(inst,dreg,reg,8) #define amd64_shld_reg_imm(inst,dreg,reg,shamt) amd64_shld_reg_imm_size(inst,dreg,reg,shamt,8) #define amd64_mul_reg(inst,reg,is_signed) amd64_mul_reg_size(inst,reg,is_signed,8) #define amd64_mul_mem(inst,mem,is_signed) amd64_mul_mem_size(inst,mem,is_signed,8) #define amd64_mul_membase(inst,basereg,disp,is_signed) amd64_mul_membase_size(inst,basereg,disp,is_signed,8) #define amd64_imul_reg_reg(inst,dreg,reg) amd64_imul_reg_reg_size(inst,dreg,reg,8) #define amd64_imul_reg_mem(inst,reg,mem) amd64_imul_reg_mem_size(inst,reg,mem,8) #define amd64_imul_reg_membase(inst,reg,basereg,disp) amd64_imul_reg_membase_size(inst,reg,basereg,disp,8) #define amd64_imul_reg_reg_imm(inst,dreg,reg,imm) amd64_imul_reg_reg_imm_size(inst,dreg,reg,imm,8) #define amd64_imul_reg_mem_imm(inst,reg,mem,imm) amd64_imul_reg_mem_imm_size(inst,reg,mem,imm,8) #define amd64_imul_reg_membase_imm(inst,reg,basereg,disp,imm) amd64_imul_reg_membase_imm_size(inst,reg,basereg,disp,imm,8) #define amd64_div_reg(inst,reg,is_signed) amd64_div_reg_size(inst,reg,is_signed,8) #define amd64_div_mem(inst,mem,is_signed) amd64_div_mem_size(inst,mem,is_signed,8) #define amd64_div_membase(inst,basereg,disp,is_signed) amd64_div_membase_size(inst,basereg,disp,is_signed,8) #define amd64_mov_mem_reg(inst,mem,reg,size) amd64_mov_mem_reg_size(inst,mem,reg,size) //#define amd64_mov_regp_reg(inst,regp,reg,size) amd64_mov_regp_reg_size(inst,regp,reg,size) //#define amd64_mov_membase_reg(inst,basereg,disp,reg,size) amd64_mov_membase_reg_size(inst,basereg,disp,reg,size) #define amd64_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) amd64_mov_memindex_reg_size(inst,basereg,disp,indexreg,shift,reg,size) //#define amd64_mov_reg_reg(inst,dreg,reg,size) amd64_mov_reg_reg_size(inst,dreg,reg,size) //#define amd64_mov_reg_mem(inst,reg,mem,size) amd64_mov_reg_mem_size(inst,reg,mem,size) //#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) amd64_mov_reg_membase_size(inst,reg,basereg,disp,size) #define amd64_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) #define amd64_clear_reg(inst,reg) amd64_clear_reg_size(inst,reg,8) //#define amd64_mov_reg_imm(inst,reg,imm) amd64_mov_reg_imm_size(inst,reg,imm,8) #define amd64_mov_mem_imm(inst,mem,imm,size) amd64_mov_mem_imm_size(inst,mem,imm,size) //#define amd64_mov_membase_imm(inst,basereg,disp,imm,size) amd64_mov_membase_imm_size(inst,basereg,disp,imm,size) #define amd64_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) amd64_mov_memindex_imm_size(inst,basereg,disp,indexreg,shift,imm,size) #define amd64_lea_mem(inst,reg,mem) amd64_lea_mem_size(inst,reg,mem,8) //#define amd64_lea_membase(inst,reg,basereg,disp) amd64_lea_membase_size(inst,reg,basereg,disp,8) #define amd64_lea_memindex(inst,reg,basereg,disp,indexreg,shift) amd64_lea_memindex_size(inst,reg,basereg,disp,indexreg,shift,8) #define amd64_widen_reg(inst,dreg,reg,is_signed,is_half) amd64_widen_reg_size(inst,dreg,reg,is_signed,is_half,8) #define amd64_widen_mem(inst,dreg,mem,is_signed,is_half) amd64_widen_mem_size(inst,dreg,mem,is_signed,is_half,8) #define amd64_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) amd64_widen_membase_size(inst,dreg,basereg,disp,is_signed,is_half,8) #define amd64_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) amd64_widen_memindex_size(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half,8) #define amd64_cdq(inst) amd64_cdq_size(inst,8) #define amd64_wait(inst) amd64_wait_size(inst,8) #define amd64_fp_op_mem(inst,opc,mem,is_double) amd64_fp_op_mem_size(inst,opc,mem,is_double,8) #define amd64_fp_op_membase(inst,opc,basereg,disp,is_double) amd64_fp_op_membase_size(inst,opc,basereg,disp,is_double,8) #define amd64_fp_op(inst,opc,index) amd64_fp_op_size(inst,opc,index,8) #define amd64_fp_op_reg(inst,opc,index,pop_stack) amd64_fp_op_reg_size(inst,opc,index,pop_stack,8) #define amd64_fp_int_op_membase(inst,opc,basereg,disp,is_int) amd64_fp_int_op_membase_size(inst,opc,basereg,disp,is_int,8) #define amd64_fstp(inst,index) amd64_fstp_size(inst,index,8) #define amd64_fcompp(inst) amd64_fcompp_size(inst,8) #define amd64_fucompp(inst) amd64_fucompp_size(inst,8) #define amd64_fnstsw(inst) amd64_fnstsw_size(inst,8) #define amd64_fnstcw(inst,mem) amd64_fnstcw_size(inst,mem,8) #define amd64_fnstcw_membase(inst,basereg,disp) amd64_fnstcw_membase_size(inst,basereg,disp,8) #define amd64_fldcw(inst,mem) amd64_fldcw_size(inst,mem,8) #define amd64_fldcw_membase(inst,basereg,disp) amd64_fldcw_membase_size(inst,basereg,disp,8) #define amd64_fchs(inst) amd64_fchs_size(inst,8) #define amd64_frem(inst) amd64_frem_size(inst,8) #define amd64_fxch(inst,index) amd64_fxch_size(inst,index,8) #define amd64_fcomi(inst,index) amd64_fcomi_size(inst,index,8) #define amd64_fcomip(inst,index) amd64_fcomip_size(inst,index,8) #define amd64_fucomi(inst,index) amd64_fucomi_size(inst,index,8) #define amd64_fucomip(inst,index) amd64_fucomip_size(inst,index,8) #define amd64_fld(inst,mem,is_double) amd64_fld_size(inst,mem,is_double,8) #define amd64_fld_membase(inst,basereg,disp,is_double) amd64_fld_membase_size(inst,basereg,disp,is_double,8) #define amd64_fld80_mem(inst,mem) amd64_fld80_mem_size(inst,mem,8) #define amd64_fld80_membase(inst,basereg,disp) amd64_fld80_membase_size(inst,basereg,disp,8) #define amd64_fild(inst,mem,is_long) amd64_fild_size(inst,mem,is_long,8) #define amd64_fild_membase(inst,basereg,disp,is_long) amd64_fild_membase_size(inst,basereg,disp,is_long,8) #define amd64_fld_reg(inst,index) amd64_fld_reg_size(inst,index,8) #define amd64_fldz(inst) amd64_fldz_size(inst,8) #define amd64_fld1(inst) amd64_fld1_size(inst,8) #define amd64_fldpi(inst) amd64_fldpi_size(inst,8) #define amd64_fst(inst,mem,is_double,pop_stack) amd64_fst_size(inst,mem,is_double,pop_stack,8) #define amd64_fst_membase(inst,basereg,disp,is_double,pop_stack) amd64_fst_membase_size(inst,basereg,disp,is_double,pop_stack,8) #define amd64_fst80_mem(inst,mem) amd64_fst80_mem_size(inst,mem,8) #define amd64_fst80_membase(inst,basereg,disp) amd64_fst80_membase_size(inst,basereg,disp,8) #define amd64_fist_pop(inst,mem,is_long) amd64_fist_pop_size(inst,mem,is_long,8) #define amd64_fist_pop_membase(inst,basereg,disp,is_long) amd64_fist_pop_membase_size(inst,basereg,disp,is_long,8) #define amd64_fstsw(inst) amd64_fstsw_size(inst,8) #define amd64_fist_membase(inst,basereg,disp,is_int) amd64_fist_membase_size(inst,basereg,disp,is_int,8) //#define amd64_push_reg(inst,reg) amd64_push_reg_size(inst,reg,8) #define amd64_push_regp(inst,reg) amd64_push_regp_size(inst,reg,8) #define amd64_push_mem(inst,mem) amd64_push_mem_size(inst,mem,8) //#define amd64_push_membase(inst,basereg,disp) amd64_push_membase_size(inst,basereg,disp,8) #define amd64_push_memindex(inst,basereg,disp,indexreg,shift) amd64_push_memindex_size(inst,basereg,disp,indexreg,shift,8) #define amd64_push_imm(inst,imm) amd64_push_imm_size(inst,imm,8) //#define amd64_pop_reg(inst,reg) amd64_pop_reg_size(inst,reg,8) #define amd64_pop_mem(inst,mem) amd64_pop_mem_size(inst,mem,8) #define amd64_pop_membase(inst,basereg,disp) amd64_pop_membase_size(inst,basereg,disp,8) #define amd64_pushad(inst) amd64_pushad_size(inst,8) #define amd64_pushfd(inst) amd64_pushfd_size(inst,8) #define amd64_popad(inst) amd64_popad_size(inst,8) #define amd64_popfd(inst) amd64_popfd_size(inst,8) #define amd64_loop(inst,imm) amd64_loop_size(inst,imm,8) #define amd64_loope(inst,imm) amd64_loope_size(inst,imm,8) #define amd64_loopne(inst,imm) amd64_loopne_size(inst,imm,8) #define amd64_jump32(inst,imm) amd64_jump32_size(inst,imm,8) #define amd64_jump8(inst,imm) amd64_jump8_size(inst,imm,8) #define amd64_jump_reg(inst,reg) amd64_jump_reg_size(inst,reg,8) #define amd64_jump_mem(inst,mem) amd64_jump_mem_size(inst,mem,8) #define amd64_jump_membase(inst,basereg,disp) amd64_jump_membase_size(inst,basereg,disp,8) #define amd64_jump_code(inst,target) amd64_jump_code_size(inst,target,8) #define amd64_jump_disp(inst,disp) amd64_jump_disp_size(inst,disp,8) #define amd64_branch8(inst,cond,imm,is_signed) amd64_branch8_size(inst,cond,imm,is_signed,8) #define amd64_branch32(inst,cond,imm,is_signed) amd64_branch32_size(inst,cond,imm,is_signed,8) #define amd64_branch(inst,cond,target,is_signed) amd64_branch_size(inst,cond,target,is_signed,8) #define amd64_branch_disp(inst,cond,disp,is_signed) amd64_branch_disp_size(inst,cond,disp,is_signed,8) #define amd64_set_reg(inst,cond,reg,is_signed) amd64_set_reg_size(inst,cond,reg,is_signed,8) #define amd64_set_mem(inst,cond,mem,is_signed) amd64_set_mem_size(inst,cond,mem,is_signed,8) #define amd64_set_membase(inst,cond,basereg,disp,is_signed) amd64_set_membase_size(inst,cond,basereg,disp,is_signed,8) #define amd64_call_imm(inst,disp) amd64_call_imm_size(inst,disp,8) //#define amd64_call_reg(inst,reg) amd64_call_reg_size(inst,reg,8) #define amd64_call_mem(inst,mem) amd64_call_mem_size(inst,mem,8) #define amd64_call_membase(inst,basereg,disp) amd64_call_membase_size(inst,basereg,disp,8) #define amd64_call_code(inst,target) amd64_call_code_size(inst,target,8) //#define amd64_ret(inst) amd64_ret_size(inst,8) #define amd64_ret_imm(inst,imm) amd64_ret_imm_size(inst,imm,8) #define amd64_cmov_reg(inst,cond,is_signed,dreg,reg) amd64_cmov_reg_size(inst,cond,is_signed,dreg,reg,8) #define amd64_cmov_mem(inst,cond,is_signed,reg,mem) amd64_cmov_mem_size(inst,cond,is_signed,reg,mem,8) #define amd64_cmov_membase(inst,cond,is_signed,reg,basereg,disp) amd64_cmov_membase_size(inst,cond,is_signed,reg,basereg,disp,8) #define amd64_enter(inst,framesize) amd64_enter_size(inst,framesize) //#define amd64_leave(inst) amd64_leave_size(inst,8) #define amd64_sahf(inst) amd64_sahf_size(inst,8) #define amd64_fsin(inst) amd64_fsin_size(inst,8) #define amd64_fcos(inst) amd64_fcos_size(inst,8) #define amd64_fabs(inst) amd64_fabs_size(inst,8) #define amd64_ftst(inst) amd64_ftst_size(inst,8) #define amd64_fxam(inst) amd64_fxam_size(inst,8) #define amd64_fpatan(inst) amd64_fpatan_size(inst,8) #define amd64_fprem(inst) amd64_fprem_size(inst,8) #define amd64_fprem1(inst) amd64_fprem1_size(inst,8) #define amd64_frndint(inst) amd64_frndint_size(inst,8) #define amd64_fsqrt(inst) amd64_fsqrt_size(inst,8) #define amd64_fptan(inst) amd64_fptan_size(inst,8) #define amd64_padding(inst,size) amd64_padding_size(inst,size) #define amd64_prolog(inst,frame,reg_mask) amd64_prolog_size(inst,frame,reg_mask,8) #define amd64_epilog(inst,reg_mask) amd64_epilog_size(inst,reg_mask,8) #endif // AMD64_H dynamips-0.2.14/stable/cpu.c000066400000000000000000000162431241034141600156310ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Management of CPU groups (for MP systems). */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "memory.h" #include "device.h" #include "mips64.h" #include "mips64_cp0.h" #include "mips64_exec.h" #include "mips64_jit.h" #include "ppc32.h" #include "ppc32_exec.h" #include "ppc32_jit.h" #include "dynamips.h" #include "vm.h" /* Find a CPU in a group given its ID */ cpu_gen_t *cpu_group_find_id(cpu_group_t *group,u_int id) { cpu_gen_t *cpu; if (!group) return NULL; for(cpu=group->cpu_list;cpu;cpu=cpu->next) if (cpu->id == id) return cpu; return NULL; } /* Find the highest CPU ID in a CPU group */ int cpu_group_find_highest_id(cpu_group_t *group,u_int *highest_id) { cpu_gen_t *cpu; u_int max_id = 0; if (!group || group->cpu_list) return(-1); for(cpu=group->cpu_list;cpu;cpu=cpu->next) if (cpu->id >= max_id) max_id = cpu->id; *highest_id = max_id; return(0); } /* Add a CPU in a CPU group */ int cpu_group_add(cpu_group_t *group,cpu_gen_t *cpu) { if (!group) return(-1); /* check that we don't already have a CPU with this id */ if (cpu_group_find_id(group,cpu->id) != NULL) { fprintf(stderr,"cpu_group_add: CPU%u already present in group.\n", cpu->id); return(-1); } cpu->next = group->cpu_list; group->cpu_list = cpu; return(0); } /* Create a new CPU group */ cpu_group_t *cpu_group_create(char *name) { cpu_group_t *group; if (!(group = malloc(sizeof(*group)))) return NULL; group->name = name; group->cpu_list = NULL; return group; } /* Delete a CPU group */ void cpu_group_delete(cpu_group_t *group) { cpu_gen_t *cpu,*next; if (group != NULL) { for(cpu=group->cpu_list;cpu;cpu=next) { next = cpu->next; cpu_delete(cpu); } free(group); } } /* Rebuild the MTS subsystem for a CPU group */ int cpu_group_rebuild_mts(cpu_group_t *group) { cpu_gen_t *cpu; for(cpu=group->cpu_list;cpu;cpu=cpu->next) cpu->mts_rebuild(cpu); return(0); } /* Log a message for a CPU */ void cpu_log(cpu_gen_t *cpu,char *module,char *format,...) { char buffer[256]; va_list ap; va_start(ap,format); snprintf(buffer,sizeof(buffer),"CPU%u: %s",cpu->id,module); vm_flog(cpu->vm,buffer,format,ap); va_end(ap); } /* Create a new CPU */ cpu_gen_t *cpu_create(vm_instance_t *vm,u_int type,u_int id) { void *(*cpu_run_fn)(void *); cpu_gen_t *cpu; if (!(cpu = malloc(sizeof(*cpu)))) return NULL; memset(cpu,0,sizeof(*cpu)); cpu->vm = vm; cpu->id = id; cpu->type = type; cpu->state = CPU_STATE_SUSPENDED; switch(cpu->type) { case CPU_TYPE_MIPS64: cpu->jit_op_array_size = MIPS_INSN_PER_PAGE; CPU_MIPS64(cpu)->vm = vm; CPU_MIPS64(cpu)->gen = cpu; mips64_init(CPU_MIPS64(cpu)); cpu_run_fn = (void *)mips64_jit_run_cpu; if (!cpu->vm->jit_use) cpu_run_fn = (void *)mips64_exec_run_cpu; else mips64_jit_init(CPU_MIPS64(cpu)); break; case CPU_TYPE_PPC32: cpu->jit_op_array_size = PPC32_INSN_PER_PAGE; CPU_PPC32(cpu)->vm = vm; CPU_PPC32(cpu)->gen = cpu; ppc32_init(CPU_PPC32(cpu)); cpu_run_fn = (void *)ppc32_jit_run_cpu; if (!cpu->vm->jit_use) cpu_run_fn = (void *)ppc32_exec_run_cpu; else ppc32_jit_init(CPU_PPC32(cpu)); break; default: fprintf(stderr,"CPU type %u is not supported yet\n",cpu->type); abort(); break; } /* create the CPU thread execution */ if (pthread_create(&cpu->cpu_thread,NULL,cpu_run_fn,cpu) != 0) { fprintf(stderr,"cpu_create: unable to create thread for CPU%u\n",id); free(cpu); return NULL; } return cpu; } /* Delete a CPU */ void cpu_delete(cpu_gen_t *cpu) { if (cpu) { /* Stop activity of this CPU */ cpu_stop(cpu); pthread_join(cpu->cpu_thread,NULL); /* Free resources */ switch(cpu->type) { case CPU_TYPE_MIPS64: mips64_delete(CPU_MIPS64(cpu)); break; case CPU_TYPE_PPC32: ppc32_delete(CPU_PPC32(cpu)); break; } free(cpu->jit_op_array); free(cpu); } } /* Start a CPU */ void cpu_start(cpu_gen_t *cpu) { if (cpu) { cpu_log(cpu,"CPU_STATE","Starting CPU (old state=%u)...\n",cpu->state); cpu->state = CPU_STATE_RUNNING; } } /* Stop a CPU */ void cpu_stop(cpu_gen_t *cpu) { if (cpu) { cpu_log(cpu,"CPU_STATE","Halting CPU (old state=%u)...\n",cpu->state); cpu->state = CPU_STATE_HALTED; } } /* Start all CPUs of a CPU group */ void cpu_group_start_all_cpu(cpu_group_t *group) { cpu_gen_t *cpu; for(cpu=group->cpu_list;cpu;cpu=cpu->next) cpu_start(cpu); } /* Stop all CPUs of a CPU group */ void cpu_group_stop_all_cpu(cpu_group_t *group) { cpu_gen_t *cpu; for(cpu=group->cpu_list;cpu;cpu=cpu->next) cpu_stop(cpu); } /* Set a state of all CPUs of a CPU group */ void cpu_group_set_state(cpu_group_t *group,u_int state) { cpu_gen_t *cpu; for(cpu=group->cpu_list;cpu;cpu=cpu->next) cpu->state = state; } /* Returns TRUE if all CPUs in a CPU group are inactive */ static int cpu_group_check_activity(cpu_group_t *group) { cpu_gen_t *cpu; for(cpu=group->cpu_list;cpu;cpu=cpu->next) { if (!cpu->cpu_thread_running) continue; if ((cpu->state == CPU_STATE_RUNNING) || !cpu->seq_state) return(FALSE); } return(TRUE); } /* Synchronize on CPUs (all CPUs must be inactive) */ int cpu_group_sync_state(cpu_group_t *group) { cpu_gen_t *cpu; m_tmcnt_t t1,t2; /* Check that CPU activity is really suspended */ t1 = m_gettime(); for(cpu=group->cpu_list;cpu;cpu=cpu->next) cpu->seq_state = 0; while(!cpu_group_check_activity(group)) { t2 = m_gettime(); if (t2 > (t1 + 10000)) return(-1); usleep(50000); } return(0); } /* Save state of all CPUs */ int cpu_group_save_state(cpu_group_t *group) { cpu_gen_t *cpu; for(cpu=group->cpu_list;cpu;cpu=cpu->next) cpu->prev_state = cpu->state; return(TRUE); } /* Restore state of all CPUs */ int cpu_group_restore_state(cpu_group_t *group) { cpu_gen_t *cpu; for(cpu=group->cpu_list;cpu;cpu=cpu->next) cpu->state = cpu->prev_state; return(TRUE); } /* Virtual idle loop */ void cpu_idle_loop(cpu_gen_t *cpu) { struct timespec t_spc; m_tmcnt_t expire; expire = m_gettime_usec() + cpu->idle_sleep_time; pthread_mutex_lock(&cpu->idle_mutex); t_spc.tv_sec = expire / 1000000; t_spc.tv_nsec = (expire % 1000000) * 1000; pthread_cond_timedwait(&cpu->idle_cond,&cpu->idle_mutex,&t_spc); pthread_mutex_unlock(&cpu->idle_mutex); } /* Break idle wait state */ void cpu_idle_break_wait(cpu_gen_t *cpu) { pthread_cond_signal(&cpu->idle_cond); cpu->idle_count = 0; } dynamips-0.2.14/stable/cpu.h000066400000000000000000000126431241034141600156360ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __CPU_H__ #define __CPU_H__ #include #include #include "utils.h" #include "jit_op.h" #include "mips64.h" #include "mips64_cp0.h" #include "ppc32.h" /* Possible CPU types */ enum { CPU_TYPE_MIPS64 = 1, CPU_TYPE_PPC32, }; /* Virtual CPU states */ enum { CPU_STATE_RUNNING = 0, CPU_STATE_HALTED, CPU_STATE_SUSPENDED, }; /* Maximum results for idle pc */ #define CPU_IDLE_PC_MAX_RES 10 /* Idle PC proposed value */ struct cpu_idle_pc { m_uint64_t pc; u_int count; }; /* Number of recorded memory accesses (power of two) */ #define MEMLOG_COUNT 16 typedef struct memlog_access memlog_access_t; struct memlog_access { m_uint64_t iaddr; m_uint64_t vaddr; m_uint64_t data; m_uint32_t data_valid; m_uint32_t op_size; m_uint32_t op_type; }; /* Undefined memory access handler */ typedef int (*cpu_undefined_mem_handler_t)(cpu_gen_t *cpu,m_uint64_t vaddr, u_int op_size,u_int op_type, m_uint64_t *data); /* Generic CPU definition */ struct cpu_gen { /* CPU type and identifier for MP systems */ u_int type,id; /* CPU states */ volatile u_int state,prev_state; volatile m_uint64_t seq_state; /* Thread running this CPU */ pthread_t cpu_thread; volatile int cpu_thread_running; /* Exception restore point */ jmp_buf exec_loop_env; /* "Idle" loop management */ u_int idle_count,idle_max,idle_sleep_time; pthread_mutex_t idle_mutex; pthread_cond_t idle_cond; /* VM instance */ vm_instance_t *vm; /* Next CPU in group */ cpu_gen_t *next; /* Idle PC proposal */ struct cpu_idle_pc idle_pc_prop[CPU_IDLE_PC_MAX_RES]; u_int idle_pc_prop_count; /* Specific CPU part */ union { cpu_mips_t mips64_cpu; cpu_ppc_t ppc32_cpu; }sp; /* Methods */ void (*reg_set)(cpu_gen_t *cpu,u_int reg_index,m_uint64_t val); void (*reg_dump)(cpu_gen_t *cpu); void (*mmu_dump)(cpu_gen_t *cpu); void (*mmu_raw_dump)(cpu_gen_t *cpu); void (*add_breakpoint)(cpu_gen_t *cpu,m_uint64_t addr); void (*remove_breakpoint)(cpu_gen_t *cpu,m_uint64_t addr); void (*set_idle_pc)(cpu_gen_t *cpu,m_uint64_t addr); void (*get_idling_pc)(cpu_gen_t *cpu); void (*mts_rebuild)(cpu_gen_t *cpu); void (*mts_show_stats)(cpu_gen_t *cpu); cpu_undefined_mem_handler_t undef_mem_handler; /* Memory access log for fault debugging */ u_int memlog_pos; memlog_access_t memlog_array[MEMLOG_COUNT]; /* Statistics */ m_uint64_t dev_access_counter; /* JIT op array for current compiled page */ u_int jit_op_array_size; jit_op_t **jit_op_array; jit_op_t **jit_op_current; /* JIT op pool */ jit_op_t *jit_op_pool[JIT_OP_POOL_NR]; }; /* CPU group definition */ typedef struct cpu_group cpu_group_t; struct cpu_group { char *name; cpu_gen_t *cpu_list; void *priv_data; }; #define CPU_MIPS64(cpu) (&(cpu)->sp.mips64_cpu) #define CPU_PPC32(cpu) (&(cpu)->sp.ppc32_cpu) /* Get CPU instruction pointer */ static forced_inline m_uint64_t cpu_get_pc(cpu_gen_t *cpu) { switch(cpu->type) { case CPU_TYPE_MIPS64: return(CPU_MIPS64(cpu)->pc); case CPU_TYPE_PPC32: return((m_uint64_t)CPU_PPC32(cpu)->ia); default: return(0); } } /* Get CPU performance counter */ static forced_inline m_uint32_t cpu_get_perf_counter(cpu_gen_t *cpu) { switch(cpu->type) { case CPU_TYPE_MIPS64: return(CPU_MIPS64(cpu)->perf_counter); case CPU_TYPE_PPC32: return(CPU_PPC32(cpu)->perf_counter); default: return(0); } } /* Find a CPU in a group given its ID */ cpu_gen_t *cpu_group_find_id(cpu_group_t *group,u_int id); /* Find the highest CPU ID in a CPU group */ int cpu_group_find_highest_id(cpu_group_t *group,u_int *highest_id); /* Add a CPU in a CPU group */ int cpu_group_add(cpu_group_t *group,cpu_gen_t *cpu); /* Create a new CPU group */ cpu_group_t *cpu_group_create(char *name); /* Delete a CPU group */ void cpu_group_delete(cpu_group_t *group); /* Rebuild the MTS subsystem for a CPU group */ int cpu_group_rebuild_mts(cpu_group_t *group); /* Log a message for a CPU */ void cpu_log(cpu_gen_t *cpu,char *module,char *format,...); /* Create a new CPU */ cpu_gen_t *cpu_create(vm_instance_t *vm,u_int type,u_int id); /* Delete a CPU */ void cpu_delete(cpu_gen_t *cpu); /* Start a CPU */ void cpu_start(cpu_gen_t *cpu); /* Stop a CPU */ void cpu_stop(cpu_gen_t *cpu); /* Start all CPUs of a CPU group */ void cpu_group_start_all_cpu(cpu_group_t *group); /* Stop all CPUs of a CPU group */ void cpu_group_stop_all_cpu(cpu_group_t *group); /* Set a state of all CPUs of a CPU group */ void cpu_group_set_state(cpu_group_t *group,u_int state); /* Synchronize on CPUs (all CPUs must be inactive) */ int cpu_group_sync_state(cpu_group_t *group); /* Save state of all CPUs */ int cpu_group_save_state(cpu_group_t *group); /* Restore state of all CPUs */ int cpu_group_restore_state(cpu_group_t *group); /* Virtual idle loop */ void cpu_idle_loop(cpu_gen_t *cpu); /* Break idle wait state */ void cpu_idle_break_wait(cpu_gen_t *cpu); /* Returns to the CPU exec loop */ static inline void cpu_exec_loop_enter(cpu_gen_t *cpu) { longjmp(cpu->exec_loop_env,1); } /* Set the exec loop entry point */ #define cpu_exec_loop_set(cpu) setjmp((cpu)->exec_loop_env) #endif dynamips-0.2.14/stable/hv_vm.c000066400000000000000000001056271241034141600161660ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor generic VM routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "device.h" #include "dev_c7200.h" #include "dev_vtty.h" #include "utils.h" #include "base64.h" #include "net.h" #include "atm.h" #include "frame_relay.h" #include "crc.h" #include "net_io.h" #include "net_io_bridge.h" #ifdef GEN_ETH #include "gen_eth.h" #endif #include "registry.h" #include "hypervisor.h" #include "get_cpu_time.h" /* Find the specified CPU */ static cpu_gen_t *find_cpu(hypervisor_conn_t *conn,vm_instance_t *vm, u_int cpu_id) { cpu_gen_t *cpu; cpu = cpu_group_find_id(vm->cpu_group,cpu_id); if (!cpu) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BAD_OBJ,1,"Bad CPU specified"); return NULL; } return cpu; } /* Create a VM instance */ static int cmd_create(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = vm_create_instance(argv[0],atoi(argv[1]),argv[2]))) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to create VM instance '%s'", argv[0]); return(-1); } vm->vtty_con_type = VTTY_TYPE_NONE; vm->vtty_aux_type = VTTY_TYPE_NONE; vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' created",argv[0]); return(0); } /* Rename a VM instance */ static int cmd_rename(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (registry_exists(argv[1],OBJ_TYPE_VM)) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename VM instance '%s', '%s' already exists", argv[0],argv[1]); return(-1); } if (vm_rename_instance(vm,argv[1])) { hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename VM instance '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' renamed to '%s'",argv[0],argv[1]); return(0); } /* Delete a VM instance */ static int cmd_delete(hypervisor_conn_t *conn,int argc,char *argv[]) { int res; res = vm_delete_instance(argv[0]); if (res == 1) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' deleted",argv[0]); } else { hypervisor_send_reply(conn,HSC_ERR_DELETE,1, "unable to delete VM '%s'",argv[0]); } return(res); } /* Delete a VM instance and related files */ static int cmd_clean_delete(hypervisor_conn_t *conn,int argc,char *argv[]) { int res, i; glob_t globbuf; char *pattern; vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); pattern = vm_build_filename(vm, "*"); vm_release(vm); res = vm_delete_instance(argv[0]); if (res == 1) { /* delete related files (best effort) */ if (pattern != NULL && glob(pattern, GLOB_NOSORT, NULL, &globbuf) == 0) { for (i = 0; i < globbuf.gl_pathc; i++) { remove(globbuf.gl_pathv[i]); } globfree(&globbuf); } hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' and related files deleted",argv[0]); } else { hypervisor_send_reply(conn,HSC_ERR_DELETE,1, "unable to delete VM '%s'",argv[0]); } free(pattern); return(res); } /* Start a VM instance */ static int cmd_start(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (vm->vtty_con_type == VTTY_TYPE_NONE) { hypervisor_send_reply(conn,HSC_INFO_MSG,0, "Warning: no console port defined for " "VM '%s'",argv[0]); } if (vm_init_instance(vm) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_START,1, "unable to start VM instance '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' started",argv[0]); return(0); } /* Stop a VM instance */ static int cmd_stop(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (vm_stop_instance(vm) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_STOP,1, "unable to stop VM instance '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' stopped",argv[0]); return(0); } /* Get the status of a VM instance */ static int cmd_get_status(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; int status; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); status = vm->status; vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"%d",status); return(0); } /* Set debugging level */ static int cmd_set_debug_level(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->debug_level = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set IOS image filename */ static int cmd_set_ios(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (vm_ios_set_image(vm,argv[1]) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to store IOS image name for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"IOS image set for '%s'",argv[0]); return(0); } /* Set IOS configuration filename to load at startup */ static int cmd_set_config(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; const char *startup_filename = argv[1]; const char *private_filename = ((argc > 2) ? argv[2] : ""); if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (0 == strlen(startup_filename)) startup_filename = NULL; // keep existing data if (0 == strlen(private_filename)) private_filename = NULL; // keep existing data if (vm_ios_set_config(vm,startup_filename,private_filename) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to store IOS config for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"IOS config file set for '%s'", argv[0]); return(0); } /* Set RAM size */ static int cmd_set_ram(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->ram_size = atoi(argv[1]); /* XXX npe-400 can split excess ram into iomem, adjusting ram_size */ if (vm->elf_machine_id == C7200_ELF_MACHINE_ID) VM_C7200(vm)->npe400_ram_size = vm->ram_size; vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set NVRAM size */ static int cmd_set_nvram(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->nvram_size = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Enable/disable use of a memory-mapped file to simulate RAM */ static int cmd_set_ram_mmap(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->ram_mmap = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Enable/disable use of sparse memory */ static int cmd_set_sparse_mem(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->sparse_mem = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the clock divisor */ static int cmd_set_clock_divisor(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_int clock_div; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if ((clock_div = atoi(argv[1])) != 0) vm->clock_divisor = clock_div; vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Enable/disable use of block direct jump (compatibility option) */ static int cmd_set_blk_direct_jump(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->exec_blk_direct_jump = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the idle PC */ static int cmd_set_idle_pc(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->idle_pc = strtoull(argv[1],NULL,0); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the idle PC value when the CPU is online */ static int cmd_set_idle_pc_online(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!(cpu = find_cpu(conn,vm,atoi(argv[1])))) return(-1); cpu->set_idle_pc(cpu,strtoull(argv[2],NULL,0)); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Get the idle PC proposals */ static int cmd_get_idle_pc_prop(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; int i; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!(cpu = find_cpu(conn,vm,atoi(argv[1])))) return(-1); cpu->get_idling_pc(cpu); for(i=0;iidle_pc_prop_count;i++) { hypervisor_send_reply(conn,HSC_INFO_MSG,0,"0x%llx [%d]", cpu->idle_pc_prop[i].pc, cpu->idle_pc_prop[i].count); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Dump the idle PC proposals */ static int cmd_show_idle_pc_prop(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; int i; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!(cpu = find_cpu(conn,vm,atoi(argv[1])))) return(-1); for(i=0;iidle_pc_prop_count;i++) { hypervisor_send_reply(conn,HSC_INFO_MSG,0,"0x%llx [%d]", cpu->idle_pc_prop[i].pc, cpu->idle_pc_prop[i].count); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set CPU idle max value */ static int cmd_set_idle_max(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!(cpu = find_cpu(conn,vm,atoi(argv[1])))) return(-1); cpu->idle_max = atoi(argv[2]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set CPU idle sleep time value */ static int cmd_set_idle_sleep_time(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!(cpu = find_cpu(conn,vm,atoi(argv[1])))) return(-1); cpu->idle_sleep_time = atoi(argv[2]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show info about potential timer drift */ static int cmd_show_timer_drift(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!(cpu = find_cpu(conn,vm,atoi(argv[1])))) return(-1); switch(cpu->type) { case CPU_TYPE_MIPS64: hypervisor_send_reply(conn,HSC_INFO_MSG,0,"Timer Drift: %u", CPU_MIPS64(cpu)->timer_drift); hypervisor_send_reply(conn,HSC_INFO_MSG,0,"Pending Timer IRQ: %u", CPU_MIPS64(cpu)->timer_irq_pending); break; case CPU_TYPE_PPC32: hypervisor_send_reply(conn,HSC_INFO_MSG,0,"Timer Drift: %u", CPU_PPC32(cpu)->timer_drift); hypervisor_send_reply(conn,HSC_INFO_MSG,0,"Pending Timer IRQ: %u", CPU_PPC32(cpu)->timer_irq_pending); break; } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the exec area size */ static int cmd_set_exec_area(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->exec_area_size = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set ghost RAM file */ static int cmd_set_ghost_file(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); free(vm->ghost_ram_filename); vm->ghost_ram_filename = strdup(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set ghost RAM status */ static int cmd_set_ghost_status(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->ghost_status = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set PCMCIA ATA disk0 size */ static int cmd_set_disk0(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->pcmcia_disk_size[0] = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set PCMCIA ATA disk1 size */ static int cmd_set_disk1(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->pcmcia_disk_size[1] = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the config register used at startup */ static int cmd_set_conf_reg(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->conf_reg_setup = strtol(argv[1],NULL,0); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set TCP port for console */ static int cmd_set_con_tcp_port(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->vtty_con_type = VTTY_TYPE_TCP; vm->vtty_con_tcp_port = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set TCP port for AUX port */ static int cmd_set_aux_tcp_port(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->vtty_aux_type = VTTY_TYPE_TCP; vm->vtty_aux_tcp_port = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Read IOS configuration files from a given router */ static int cmd_extract_config(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_char *startup_config = NULL; u_char *private_config = NULL; size_t startup_len; size_t private_len; u_char *startup_base64 = NULL; u_char *private_base64 = NULL; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!vm->platform->nvram_extract_config) goto err_no_extract_method; /* Extract the IOS configuration */ if ((vm->platform->nvram_extract_config(vm,&startup_config,&startup_len,&private_config,&private_len))) goto err_nvram_extract; /* * Convert config to base64. base64 generates 4 bytes for each group of 3 bytes. */ if (!(startup_base64 = malloc(1 + (startup_len + 2) / 3 * 4)) || !(private_base64 = malloc(1 + (private_len + 2) / 3 * 4))) goto err_alloc_base64; base64_encode(startup_base64,startup_config,startup_len); base64_encode(private_base64,private_config,private_len); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"conf '%s' '%s' '%s'",argv[0],startup_base64,private_base64); free(private_base64); free(startup_base64); free(private_config); free(startup_config); return(0); err_alloc_base64: free(private_base64); free(startup_base64); free(private_config); free(startup_config); err_nvram_extract: err_no_extract_method: vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to extract config of VM '%s'",argv[0]); return(-1); } /* Push IOS configuration to a given router */ static int cmd_push_config(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_char *startup_config = NULL; u_char *private_config = NULL; int startup_len = 0; int private_len = 0; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!vm->platform->nvram_push_config) goto err_no_push_method; /* * Convert base64 input to standard text. base64 uses 4 bytes for each group of 3 bytes. */ if (strcmp(argv[1],"(keep)") != 0) { startup_len = (strlen(argv[1]) + 3) / 4 * 3; if (!(startup_config = malloc(1 + startup_len))) goto err_alloc_base64; if ((startup_len = base64_decode(startup_config,(u_char *)argv[1],startup_len)) < 0) goto err_decode_base64; startup_config[startup_len] = '\0'; } if (argc > 2 && strcmp(argv[2],"(keep)") != 0) { private_len = (strlen(argv[2]) + 3) / 4 * 3; if (!(private_config = malloc(1 + private_len))) goto err_alloc_base64; if ((private_len = base64_decode(private_config,(u_char *)argv[2],private_len)) < 0) goto err_decode_base64; private_config[private_len] = '\0'; } /* Push configuration */ if (vm->platform->nvram_push_config(vm,startup_config,(size_t)startup_len,private_config,(size_t)private_len) < 0) goto err_nvram_push; free(private_config); free(startup_config); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1, "IOS config file pushed tm VM '%s'", argv[0]); return(0); err_nvram_push: err_decode_base64: err_alloc_base64: free(private_config); free(startup_config); err_no_push_method: vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to push IOS config for VM '%s'", argv[0]); return(-1); } /* Show info about the specified CPU */ static int cmd_show_cpu_info(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); cpu = cpu_group_find_id(vm->cpu_group,atoi(argv[1])); if (cpu) { cpu->reg_dump(cpu); cpu->mmu_dump(cpu); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show CPU usage - experimental */ static int cmd_show_cpu_usage(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; double usage; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); usage = get_cpu_time(); if (usage == -1) return(-1); hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%u", (unsigned long)usage); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Suspend a VM instance */ static int cmd_suspend(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm_suspend(vm); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' suspended",argv[0]); return(0); } /* Resume a VM instance */ static int cmd_resume(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm_resume(vm); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' resumed",argv[0]); return(0); } /* Send a message on the console */ static int cmd_send_con_msg(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm = NULL; const char *format = NULL; char *data = NULL; int len,written; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); format = (argc > 2)? argv[2]: "plain"; len = (int)strlen(argv[1]); written = -1; if (strcmp(format,"plain") == 0 && len >= 0) { written = vtty_store_data(vm->vtty_con,argv[1],len); } else if (strcmp(format,"base64") == 0 && len >= 0) { data = malloc(len+1); len = base64_decode((unsigned char*)data,(const unsigned char *)argv[1],len); written = vtty_store_data(vm->vtty_con,data,len); free(data); data = NULL; } vm_release(vm); if (written < 0) { hypervisor_send_reply(conn,HSC_ERR_INV_PARAM,1,"Invalid parameter"); return(-1); } else { hypervisor_send_reply(conn,HSC_INFO_OK,1,"%d byte(s) written",written); return(0); } } /* Send a message on the AUX port */ static int cmd_send_aux_msg(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm = NULL; const char *format = NULL; char *data = NULL; int len,written; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); format = (argc > 2)? argv[2]: "plain"; len = (int)strlen(argv[1]); written = -1; if (strcmp(format,"plain") == 0 && len >= 0) { written = vtty_store_data(vm->vtty_aux,argv[1],len); } else if (strcmp(format,"base64") == 0 && len >= 0) { data = malloc(len+1); len = base64_decode((unsigned char*)data,(const unsigned char *)argv[1],len); written = vtty_store_data(vm->vtty_aux,data,len); free(data); data = NULL; } vm_release(vm); if (written < 0) { hypervisor_send_reply(conn,HSC_ERR_INV_PARAM,1,"Invalid parameter"); return(-1); } else { hypervisor_send_reply(conn,HSC_INFO_OK,1,"%d byte(s) written",written); return(0); } } /* Show slot bindings */ static int cmd_slot_bindings(hypervisor_conn_t *conn,int argc,char *argv[]) { struct cisco_card *card,*sc; vm_instance_t *vm; int i,j; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); for(i=0;inr_slots;i++) { if (!(card = vm_slot_get_card_ptr(vm,i))) continue; /* main module */ hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%u/%u: %s", card->slot_id,card->subslot_id,card->dev_type); /* sub-slots */ for(j=0;jsub_slots[j])) continue; hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%u/%u: %s", card->slot_id,card->subslot_id,card->dev_type); } } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show NIO bindings for the specified slot */ static int cmd_slot_nio_bindings(hypervisor_conn_t *conn,int argc,char *argv[]) { struct cisco_nio_binding *nb; struct cisco_card *card,*sc; vm_instance_t *vm; u_int i,slot; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); if ((card = vm_slot_get_card_ptr(vm,slot))) { /* main module */ for(nb=card->nio_list;nb;nb=nb->next) { hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%u: %s", nb->port_id,nb->nio->name); } /* sub-slots */ for(i=0;isub_slots[i])) continue; for(nb=sc->nio_list;nb;nb=nb->next) { hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%u: %s", nb->port_id,nb->nio->name); } } } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Add a slot binding */ static int cmd_slot_add_binding(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_int slot,port; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); port = atoi(argv[2]); if (vm_slot_add_binding(vm,argv[3],slot,port) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "VM %s: unable to add binding for slot %u/%u", argv[0],slot,port); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Remove a slot binding */ static int cmd_slot_remove_binding(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; u_int slot,port; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); port = atoi(argv[2]); if (vm_slot_remove_binding(vm,slot,port) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "VM %s: unable to remove binding for slot %u/%u", argv[0],slot,port); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Add a NIO binding for a slot/port */ static int cmd_slot_add_nio_binding(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; u_int slot,port; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); port = atoi(argv[2]); if (vm_slot_add_nio_binding(vm,slot,port,argv[3]) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "VM %s: unable to add binding " "for slot %u/%u",argv[0],slot,port); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Remove a NIO binding for a slot/port */ static int cmd_slot_remove_nio_binding(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; u_int slot,port; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); port = atoi(argv[2]); if (vm_slot_remove_nio_binding(vm,slot,port) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "VM %s: unable to remove NIO binding " "for slot %u/%u",argv[0],slot,port); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Enable NIO of the specified slot/port */ static int cmd_slot_enable_nio(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_int slot,port; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); port = atoi(argv[2]); if (vm_slot_enable_nio(vm,slot,port) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "VM %s: unable to enable NIO for slot %u/%u", argv[0],slot,port); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Disable NIO of the specified slot/port */ static int cmd_slot_disable_nio(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_int slot,port; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); port = atoi(argv[2]); if (vm_slot_disable_nio(vm,slot,port) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "VM %s: unable to disable NIO for slot %u/%u", argv[0],slot,port); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* OIR to start a slot/subslot */ static int cmd_slot_oir_start(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_int slot,subslot; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); subslot = atoi(argv[2]); if (vm_oir_start(vm,slot,subslot) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_START,1, "VM %s: unable to engage OIR for slot %u/%u", argv[0],slot,subslot); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* OIR to stop a slot/subslot */ static int cmd_slot_oir_stop(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_int slot,subslot; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); subslot = atoi(argv[2]); if (vm_oir_stop(vm,slot,subslot) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_STOP,1, "VM %s: unable to engage OIR for slot %u/%u", argv[0],slot,subslot); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show info about VM object */ static void cmd_show_vm_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; vm_instance_t *vm = entry->data; hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s (%s)", entry->name,vm_get_type(vm)); } /* VM List */ static int cmd_vm_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_VM,cmd_show_vm_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show console TCP port info about VM object */ static void cmd_show_vm_list_con_ports(registry_entry_t *entry,void *opt, int *err) { hypervisor_conn_t *conn = opt; vm_instance_t *vm = entry->data; if (vm->vtty_con_type == VTTY_TYPE_TCP) hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s (%d)", vm->name,vm->vtty_con_tcp_port); } /* VM console TCP port list */ static int cmd_vm_list_con_ports(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_VM,cmd_show_vm_list_con_ports,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* VM commands */ static hypervisor_cmd_t vm_cmd_array[] = { { "create", 3, 3, cmd_create, NULL }, { "rename", 2, 2, cmd_rename, NULL }, { "delete", 1, 1, cmd_delete, NULL }, { "clean_delete", 1, 1, cmd_clean_delete, NULL, }, { "start", 1, 1, cmd_start, NULL }, { "stop", 1, 1, cmd_stop, NULL }, { "get_status", 1, 1, cmd_get_status, NULL }, { "set_debug_level", 2, 2, cmd_set_debug_level, NULL }, { "set_ios", 2, 2, cmd_set_ios, NULL }, { "set_config", 2, 3, cmd_set_config, NULL }, { "set_ram", 2, 2, cmd_set_ram, NULL }, { "set_nvram", 2, 2, cmd_set_nvram, NULL }, { "set_ram_mmap", 2, 2, cmd_set_ram_mmap, NULL }, { "set_sparse_mem", 2, 2, cmd_set_sparse_mem, NULL }, { "set_clock_divisor", 2, 2, cmd_set_clock_divisor, NULL }, { "set_blk_direct_jump", 2, 2, cmd_set_blk_direct_jump, NULL }, { "set_exec_area", 2, 2, cmd_set_exec_area, NULL }, { "set_disk0", 2, 2, cmd_set_disk0, NULL }, { "set_disk1", 2, 2, cmd_set_disk1, NULL }, { "set_conf_reg", 2, 2, cmd_set_conf_reg, NULL }, { "set_idle_pc", 2, 2, cmd_set_idle_pc, NULL }, { "set_idle_pc_online", 3, 3, cmd_set_idle_pc_online, NULL }, { "get_idle_pc_prop", 2, 2, cmd_get_idle_pc_prop, NULL }, { "show_idle_pc_prop", 2, 2, cmd_show_idle_pc_prop, NULL }, { "set_idle_max", 3, 3, cmd_set_idle_max, NULL }, { "set_idle_sleep_time", 3, 3, cmd_set_idle_sleep_time, NULL }, { "show_timer_drift", 2, 2, cmd_show_timer_drift, NULL }, { "set_ghost_file", 2, 2, cmd_set_ghost_file, NULL }, { "set_ghost_status", 2, 2, cmd_set_ghost_status, NULL }, { "set_con_tcp_port", 2, 2, cmd_set_con_tcp_port, NULL }, { "set_aux_tcp_port", 2, 2, cmd_set_aux_tcp_port, NULL }, { "extract_config", 1, 1, cmd_extract_config, NULL }, { "push_config", 2, 3, cmd_push_config, NULL }, { "cpu_info", 2, 2, cmd_show_cpu_info, NULL }, { "cpu_usage", 2, 2, cmd_show_cpu_usage, NULL }, { "suspend", 1, 1, cmd_suspend, NULL }, { "resume", 1, 1, cmd_resume, NULL }, { "send_con_msg", 2, 3, cmd_send_con_msg, NULL }, { "send_aux_msg", 2, 3, cmd_send_aux_msg, NULL }, { "slot_bindings", 1, 1, cmd_slot_bindings, NULL }, { "slot_nio_bindings", 2, 2, cmd_slot_nio_bindings, NULL }, { "slot_add_binding", 4, 4, cmd_slot_add_binding, NULL }, { "slot_remove_binding", 3, 3, cmd_slot_remove_binding, NULL }, { "slot_add_nio_binding", 4, 4, cmd_slot_add_nio_binding, NULL }, { "slot_remove_nio_binding", 3, 3, cmd_slot_remove_nio_binding, NULL }, { "slot_enable_nio", 3, 3, cmd_slot_enable_nio, NULL }, { "slot_disable_nio", 3, 3, cmd_slot_disable_nio, NULL }, { "slot_oir_start", 3, 3, cmd_slot_oir_start, NULL }, { "slot_oir_stop", 3, 3, cmd_slot_oir_stop, NULL }, { "list", 0, 0, cmd_vm_list, NULL }, { "list_con_ports", 0, 0, cmd_vm_list_con_ports, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor VM initialization */ int hypervisor_vm_init(void) { hypervisor_module_t *module; module = hypervisor_register_module("vm",NULL); assert(module != NULL); hypervisor_register_cmd_array(module,vm_cmd_array); return(0); } dynamips-0.2.14/stable/hypervisor.c000066400000000000000000000413371241034141600172560ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor routines. */ #include "dynamips_common.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "gen_uuid.h" #include "parser.h" #include "net.h" #include "registry.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "dev_c7200.h" #include "dev_c3600.h" #include "dev_c2691.h" #include "dev_c3725.h" #include "dev_c3745.h" #include "dev_c2600.h" #include "dev_c1700.h" #include "hypervisor.h" #include "net_io.h" #include "net_io_bridge.h" #include "frame_relay.h" #include "atm.h" #define DEBUG_TOKEN 0 /* Hypervisor modules */ static hypervisor_module_t *module_list = NULL; static volatile int hypervisor_running = 0; /* Hypervisor connection list */ static hypervisor_conn_t *hypervisor_conn_list = NULL; /* Show hypervisor version */ static int cmd_version(hypervisor_conn_t *conn,int argc,char *argv[]) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"%s",sw_version); return(0); } /* Show UUID */ static int cmd_uuid(hypervisor_conn_t *conn,int argc,char *argv[]) { char buffer[40]; uuid_t local_uuid; gen_uuid_get_local(local_uuid); uuid_unparse(local_uuid,buffer); hypervisor_send_reply(conn,HSC_INFO_OK,1,"%s",buffer); return(0); } /* Parser test */ static int cmd_parser_test(hypervisor_conn_t *conn,int argc,char *argv[]) { int i; for(i=0;inext) hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",m->name); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show module command list */ static int cmd_modcmd_list(hypervisor_conn_t *conn,int argc,char *argv[]) { hypervisor_module_t *m; hypervisor_cmd_t *cmd; if (!(m = hypervisor_find_module(argv[0]))) { hypervisor_send_reply(conn,HSC_ERR_UNK_MODULE,1,"unknown module '%s'", argv[0]); return(-1); } for(cmd=m->cmd_list;cmd;cmd=cmd->next) hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s (min/max args: %d/%d)", cmd->name,cmd->min_param,cmd->max_param); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set working directory */ static int cmd_set_working_dir(hypervisor_conn_t *conn,int argc,char *argv[]) { if (chdir(argv[0]) == -1) { hypervisor_send_reply(conn,HSC_ERR_INV_PARAM,1, "chdir: %s",strerror(errno)); } else { m_log("GENERAL","working_dir=%s\n",argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); } return(0); } /* Save the hypervisor configuration in the specified file */ static int cmd_save_config(hypervisor_conn_t *conn,int argc,char *argv[]) { FILE *fd; if (!(fd = fopen(argv[0],"w"))) { hypervisor_send_reply(conn,HSC_ERR_FILE,1,"fopen: %s",strerror(errno)); return(-1); } /* Save configuration for all objects */ netio_save_config_all(fd); frsw_save_config_all(fd); atmsw_save_config_all(fd); //atm_bridge_save_config_all(fd); netio_bridge_save_config_all(fd); vm_save_config_all(fd); fclose(fd); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Reset hypervisor (delete all objects) */ static int cmd_reset(hypervisor_conn_t *conn,int argc,char *argv[]) { dynamips_reset(); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Close connection */ static int cmd_close(hypervisor_conn_t *conn,int argc,char *argv[]) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); conn->active = FALSE; return(0); } /* Stop hypervisor */ static int cmd_stop(hypervisor_conn_t *conn,int argc,char *argv[]) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); hypervisor_running = FALSE; return(0); } /* Hypervisor commands */ static hypervisor_cmd_t hypervisor_cmd_array[] = { { "version", 0, 0, cmd_version, NULL }, { "uuid", 0, 0, cmd_uuid, NULL }, { "parser_test", 0, 10, cmd_parser_test, NULL }, { "module_list", 0, 0, cmd_mod_list, NULL }, { "cmd_list", 1, 1, cmd_modcmd_list, NULL }, { "working_dir", 1, 1, cmd_set_working_dir, NULL }, { "save_config", 1, 1, cmd_save_config, NULL }, { "reset", 0, 0, cmd_reset, NULL }, { "close", 0, 0, cmd_close, NULL }, { "stop", 0, 0, cmd_stop, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Send a reply */ int hypervisor_send_reply(hypervisor_conn_t *conn,int code,int done, char *format,...) { va_list ap; size_t n = 0; if (conn != NULL) { va_start(ap,format); n += fprintf(conn->out,"%3d%s",code,(done)?"-":" "); n += vfprintf(conn->out,format,ap); n += fprintf(conn->out,"\r\n"); fflush(conn->out); va_end(ap); } return(n); } /* Find a module */ hypervisor_module_t *hypervisor_find_module(char *name) { hypervisor_module_t *m; for(m=module_list;m;m=m->next) if (!strcmp(m->name,name)) return m; return NULL; } /* Find a command in a module */ hypervisor_cmd_t *hypervisor_find_cmd(hypervisor_module_t *module,char *name) { hypervisor_cmd_t *cmd; for(cmd=module->cmd_list;cmd;cmd=cmd->next) if (!strcmp(cmd->name,name)) return cmd; return NULL; } /* Find an object in the registry */ void *hypervisor_find_object(hypervisor_conn_t *conn,char *name,int obj_type) { void *p; if (!(p = registry_find(name,obj_type))) { hypervisor_send_reply(conn,HSC_ERR_UNK_OBJ,1, "unable to find object '%s'",name); return NULL; } return p; } /* Find a VM in the registry */ void *hypervisor_find_vm(hypervisor_conn_t *conn,char *name) { vm_platform_t *platform = conn->cur_module->opt; vm_instance_t *vm; if (!(vm = vm_acquire(name))) { hypervisor_send_reply(conn,HSC_ERR_UNK_OBJ,1, "unable to find VM '%s'",name); return NULL; } if (vm->platform != platform) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BAD_OBJ,1, "VM '%s' is not a VM type %s", name,platform->name); return NULL; } return vm; } /* Destroy module_list */ static void destroy_module_list(void) { hypervisor_module_t *m, *next; for (m = module_list; m; m = next) { next = m->next; free(m); } module_list = NULL; } /* Register a module */ hypervisor_module_t *hypervisor_register_module(char *name,void *opt) { hypervisor_module_t *m; if (hypervisor_find_module(name) != NULL) { fprintf(stderr,"Hypervisor: module '%s' already exists.\n",name); return NULL; } if (!(m = malloc(sizeof(*m)))) { fprintf(stderr,"Hypervisor: unable to register new module.\n"); return NULL; } if (!module_list) atexit(destroy_module_list); m->name = name; m->opt = opt; m->cmd_list = NULL; m->next = module_list; module_list = m; return m; } /* Register a list of commands */ int hypervisor_register_cmd_list(hypervisor_module_t *module, hypervisor_cmd_t *cmd_list) { hypervisor_cmd_t *cmd = cmd_list; while(cmd->next != NULL) cmd = cmd->next; cmd->next = module->cmd_list; module->cmd_list = cmd_list; return(0); } /* Register an array of commands */ int hypervisor_register_cmd_array(hypervisor_module_t *module, hypervisor_cmd_t *cmd_array) { hypervisor_cmd_t *cmd; for(cmd=cmd_array;cmd->name!=NULL;cmd++) { cmd->next = module->cmd_list; module->cmd_list = cmd; } return(0); } /* Locate the module and execute command */ static int hypervisor_exec_cmd(hypervisor_conn_t *conn, char *mod_name,char *cmd_name, int argc,char *argv[]) { hypervisor_module_t *module; hypervisor_cmd_t *cmd; if (!(module = hypervisor_find_module(mod_name))) { hypervisor_send_reply(conn,HSC_ERR_UNK_MODULE,1,"Unknown module '%s'", mod_name); return(-1); } if (!(cmd = hypervisor_find_cmd(module,cmd_name))) { hypervisor_send_reply(conn,HSC_ERR_UNK_CMD,1,"Unknown command '%s'", cmd_name); return(-1); } if ((argc < cmd->min_param) || (argc > cmd->max_param)) { hypervisor_send_reply(conn,HSC_ERR_BAD_PARAM,1, "Bad number of parameters (%d with min/max=%d/%d)", argc,cmd->min_param,cmd->max_param); return(-1); } conn->cur_module = module; return(cmd->handler(conn,argc,argv)); } /* Thread for servicing connections */ static void *hypervisor_thread(void *arg) { hypervisor_conn_t *conn = arg; char buffer[512],**tokens; parser_context_t ctx; int res; tokens = NULL; parser_context_init(&ctx); while(conn->active) { if (!fgets(buffer,sizeof(buffer),conn->in)) break; if (!*buffer) continue; /* Tokenize command line */ res = parser_scan_buffer(&ctx,buffer,strlen(buffer)); if (res != 0) { tokens = NULL; if (ctx.error != 0) { hypervisor_send_reply(conn,HSC_ERR_PARSING,1,"Parse error: %s", parser_strerror(&ctx)); goto free_tokens; } if (ctx.tok_count < 2) { hypervisor_send_reply(conn,HSC_ERR_PARSING,1, "At least a module and a command " "must be specified"); goto free_tokens; } /* Map token list to an array */ tokens = parser_map_array(&ctx); if (!tokens) { hypervisor_send_reply(conn,HSC_ERR_PARSING,1,"No memory"); goto free_tokens; } /* Execute command */ m_log("HYPERVISOR","exec_cmd: "); m_flog_str_array(log_file,ctx.tok_count,tokens); hypervisor_exec_cmd(conn,tokens[0],tokens[1],ctx. tok_count-2,&tokens[2]); free_tokens: free(tokens); tokens = NULL; parser_context_free(&ctx); } } free(tokens); parser_context_free(&ctx); return NULL; } static void sigpipe_handler(int sig) { printf("SIGPIPE received.\n"); } /* Initialize hypervisor */ int hypervisor_init(void) { hypervisor_module_t *module; module = hypervisor_register_module("hypervisor",NULL); assert(module != NULL); hypervisor_register_cmd_array(module,hypervisor_cmd_array); return(0); } /* Remove a connection from the list */ static void hypervisor_remove_conn(hypervisor_conn_t *conn) { if (conn->pprev != NULL) { if (conn->next) conn->next->pprev = conn->pprev; *(conn->pprev) = conn->next; } } /* Close a connection */ static void hypervisor_close_conn(hypervisor_conn_t *conn) { if (conn != NULL) { conn->active = FALSE; shutdown(conn->client_fd,2); pthread_join(conn->tid,NULL); fclose(conn->in); fclose(conn->out); shutdown(conn->client_fd,2); close(conn->client_fd); hypervisor_remove_conn(conn); free(conn); } } /* Close connections (dead or all) */ static void hypervisor_close_conn_list(int dead_status) { hypervisor_conn_t *conn,*next; for(conn=hypervisor_conn_list;conn;conn=next) { next = conn->next; if (dead_status && conn->active) continue; hypervisor_close_conn(conn); } } /* Add a new connection to the list */ static void hypervisor_add_conn(hypervisor_conn_t *conn) { conn->next = hypervisor_conn_list; conn->pprev = &hypervisor_conn_list; if (hypervisor_conn_list != NULL) hypervisor_conn_list->pprev = &conn->next; hypervisor_conn_list = conn; } /* Create a new connection */ static hypervisor_conn_t *hypervisor_create_conn(int client_fd) { hypervisor_conn_t *conn; if (!(conn = malloc(sizeof(*conn)))) goto err_malloc; memset(conn,0,sizeof(*conn)); conn->active = TRUE; conn->client_fd = client_fd; /* Open input buffered stream */ if (!(conn->in = fdopen(client_fd,"r"))) { perror("hypervisor_create_conn: fdopen/in"); goto err_fd_in; } /* Open output buffered stream */ if (!(conn->out = fdopen(client_fd,"w"))) { perror("hypervisor_create_conn: fdopen/out"); goto err_fd_out; } /* Set line buffering */ setlinebuf(conn->in); setlinebuf(conn->out); /* Create the managing thread */ if (pthread_create(&conn->tid,NULL,hypervisor_thread,conn) != 0) goto err_thread; /* Add it to the connection list */ hypervisor_add_conn(conn); return conn; err_thread: fclose(conn->out); err_fd_out: fclose(conn->in); err_fd_in: free(conn); err_malloc: return NULL; } /* Stop hypervisor from sighandler */ int hypervisor_stopsig(void) { hypervisor_running = FALSE; return(0); } /* Hypervisor TCP server */ int hypervisor_tcp_server(char *ip_addr,int tcp_port) { int fd_array[HYPERVISOR_MAX_FD]; struct sockaddr_storage remote_addr; socklen_t remote_len; int i,res,clnt,fd_count,fd_max; struct timeval tv; fd_set fds; /* Initialize all hypervisor modules */ hypervisor_init(); hypervisor_nio_init(); hypervisor_nio_bridge_init(); hypervisor_frsw_init(); hypervisor_atmsw_init(); hypervisor_atm_bridge_init(); hypervisor_ethsw_init(); hypervisor_vm_init(); hypervisor_vm_debug_init(); hypervisor_store_init(); signal(SIGPIPE,sigpipe_handler); if (!tcp_port) tcp_port = HYPERVISOR_TCP_PORT; fd_count = ip_listen(ip_addr,tcp_port,SOCK_STREAM, HYPERVISOR_MAX_FD,fd_array); if (fd_count <= 0) { fprintf(stderr,"Hypervisor: unable to create TCP sockets.\n"); return(-1); } /* Start accepting connections */ m_log("HYPERVISOR","Release %s/%s (tag %s)\n", sw_version,os_name,sw_version_tag); if (ip_addr != NULL) { binding_addr = ip_addr; m_log("HYPERVISOR","Started on IP = %s, TCP port = %d.\n", ip_addr, tcp_port); printf("Hypervisor TCP control server started (IP %s port %d).\n", ip_addr, tcp_port); } else { m_log("HYPERVISOR","Started on TCP port = %d.\n",tcp_port); printf("Hypervisor TCP control server started (port %d).\n",tcp_port); } hypervisor_running = TRUE; while(hypervisor_running) { FD_ZERO(&fds); fd_max = -1; for(i=0;i fd_max) fd_max = fd_array[i]; } /* Wait for incoming connections */ tv.tv_sec = 0; tv.tv_usec = 500 * 1000; /* 500 ms */ res = select(fd_max+1,&fds,NULL,NULL,&tv); if (res == -1) { if (errno == EINTR) continue; else perror("hypervisor_tcp_server: select"); } /* Accept connections on signaled sockets */ for(i=0;i #include "utils.h" /* MTS operation */ #define MTS_READ 0 #define MTS_WRITE 1 /* 0.5GB value */ #define MTS_SIZE_512M 0x20000000 /* MTS flag bits: D (device), ACC (memory access), C (chain) */ #define MTS_FLAG_BITS 4 #define MTS_FLAG_MASK 0x0000000fUL /* Masks for MTS entries */ #define MTS_CHAIN_MASK 0x00000001 #define MTS_ACC_MASK 0x00000006 #define MTS_DEV_MASK 0x00000008 #define MTS_ADDR_MASK (~MTS_FLAG_MASK) /* Device ID mask and shift, device offset mask */ #define MTS_DEVID_MASK 0xfc000000 #define MTS_DEVID_SHIFT 26 #define MTS_DEVOFF_MASK 0x03ffffff /* Memory access flags */ #define MTS_ACC_AE 0x00000002 /* Address Error */ #define MTS_ACC_T 0x00000004 /* TLB Exception */ #define MTS_ACC_U 0x00000006 /* Unexistent */ /* Hash table size for MTS64 (default: [shift:16,bits:12]) */ #define MTS64_HASH_SHIFT 12 #define MTS64_HASH_BITS 14 #define MTS64_HASH_SIZE (1 << MTS64_HASH_BITS) #define MTS64_HASH_MASK (MTS64_HASH_SIZE - 1) /* MTS64 hash on virtual addresses */ #define MTS64_HASH(vaddr) (((vaddr) >> MTS64_HASH_SHIFT) & MTS64_HASH_MASK) /* Hash table size for MTS32 (default: [shift:15,bits:15]) */ #define MTS32_HASH_SHIFT 12 #define MTS32_HASH_BITS 14 #define MTS32_HASH_SIZE (1 << MTS32_HASH_BITS) #define MTS32_HASH_MASK (MTS32_HASH_SIZE - 1) /* MTS32 hash on virtual addresses */ #define MTS32_HASH(vaddr) (((vaddr) >> MTS32_HASH_SHIFT) & MTS32_HASH_MASK) /* Number of entries per chunk */ #define MTS64_CHUNK_SIZE 256 #define MTS32_CHUNK_SIZE 256 /* MTS64: chunk definition */ struct mts64_chunk { mts64_entry_t entry[MTS64_CHUNK_SIZE]; struct mts64_chunk *next; u_int count; }; /* MTS32: chunk definition */ struct mts32_chunk { mts32_entry_t entry[MTS32_CHUNK_SIZE]; struct mts32_chunk *next; u_int count; }; /* Record a memory access */ void memlog_rec_access(cpu_gen_t *cpu,m_uint64_t vaddr,m_uint64_t data, m_uint32_t op_size,m_uint32_t op_type); /* Show the last memory accesses */ void memlog_dump(cpu_gen_t *cpu); /* Update the data obtained by a read access */ void memlog_update_read(cpu_gen_t *cpu,m_iptr_t raddr); /* Copy a memory block from VM physical RAM to real host */ void physmem_copy_from_vm(vm_instance_t *vm,void *real_buffer, m_uint64_t paddr,size_t len); /* Copy a memory block to VM physical RAM from real host */ void physmem_copy_to_vm(vm_instance_t *vm,void *real_buffer, m_uint64_t paddr,size_t len); /* Copy a 32-bit word from the VM physical RAM to real host */ m_uint32_t physmem_copy_u32_from_vm(vm_instance_t *vm,m_uint64_t paddr); /* Copy a 32-bit word to the VM physical RAM from real host */ void physmem_copy_u32_to_vm(vm_instance_t *vm,m_uint64_t paddr,m_uint32_t val); /* Copy a 16-bit word from the VM physical RAM to real host */ m_uint16_t physmem_copy_u16_from_vm(vm_instance_t *vm,m_uint64_t paddr); /* Copy a 16-bit word to the VM physical RAM from real host */ void physmem_copy_u16_to_vm(vm_instance_t *vm,m_uint64_t paddr,m_uint16_t val); /* Copy a byte from the VM physical RAM to real host */ m_uint8_t physmem_copy_u8_from_vm(vm_instance_t *vm,m_uint64_t paddr); /* Copy a 16-bit word to the VM physical RAM from real host */ void physmem_copy_u8_to_vm(vm_instance_t *vm,m_uint64_t paddr,m_uint8_t val); /* DMA transfer operation */ void physmem_dma_transfer(vm_instance_t *vm,m_uint64_t src,m_uint64_t dst, size_t len); /* strlen in VM physical memory */ size_t physmem_strlen(vm_instance_t *vm,m_uint64_t paddr); /* find sequence of bytes in VM cacheable physical memory interval [first,last] */ int physmem_cfind(vm_instance_t *vm,m_uint8_t *bytes,size_t len, m_uint64_t first,m_uint64_t last, m_uint64_t *paddr); /* Physical memory dump (32-bit words) */ void physmem_dump_vm(vm_instance_t *vm,m_uint64_t paddr,m_uint32_t u32_count); #endif dynamips-0.2.14/stable/mips64.c000066400000000000000000000650151241034141600161650ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * XXX TODO: proper context save/restore for CPUs. */ #include #include #include #include #include #include #include #include #include "rbtree.h" #include "cpu.h" #include "mips64_mem.h" #include "mips64_exec.h" #include "mips64_jit.h" #include "dynamips.h" #include "memory.h" #include "device.h" /* MIPS general purpose registers names */ char *mips64_gpr_reg_names[MIPS64_GPR_NR] = { "zr", "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", "fp", "ra", }; /* Cacheability and Coherency Attribute */ static int cca_cache_status[8] = { 1, 1, 0, 1, 0, 1, 0, 0, }; /* Get register index given its name */ int mips64_get_reg_index(char *name) { int i; for(i=0;ipc = MIPS_ROM_PC; cpu->gpr[MIPS_GPR_SP] = MIPS_ROM_SP; cpu->cp0.reg[MIPS_CP0_STATUS] = MIPS_CP0_STATUS_BEV; cpu->cp0.reg[MIPS_CP0_CAUSE] = 0; cpu->cp0.reg[MIPS_CP0_CONFIG] = 0x00c08ff0ULL; /* Clear the complete TLB */ memset(&cpu->cp0.tlb,0,MIPS64_TLB_MAX_ENTRIES*sizeof(tlb_entry_t)); /* Restart the MTS subsystem */ mips64_set_addr_mode(cpu,32/*64*/); /* zzz */ cpu->gen->mts_rebuild(cpu->gen); /* Flush JIT structures */ mips64_jit_flush(cpu,0); return(0); } /* Initialize a MIPS64 processor */ int mips64_init(cpu_mips_t *cpu) { cpu->addr_bus_mask = 0xFFFFFFFFFFFFFFFFULL; cpu->cp0.reg[MIPS_CP0_PRID] = MIPS_PRID_R4600; cpu->cp0.tlb_entries = MIPS64_TLB_STD_ENTRIES; /* Initialize idle timer */ cpu->gen->idle_max = 500; cpu->gen->idle_sleep_time = 30000; /* Timer IRQ parameters (default frequency: 250 Hz <=> 4ms period) */ cpu->timer_irq_check_itv = 1000; cpu->timer_irq_freq = 250; /* Enable fast memory operations */ cpu->fast_memop = TRUE; /* Enable/Disable direct block jump */ cpu->exec_blk_direct_jump = cpu->vm->exec_blk_direct_jump; /* Create the IRQ lock (for non-jit architectures) */ pthread_mutex_init(&cpu->irq_lock,NULL); /* Idle loop mutex and condition */ pthread_mutex_init(&cpu->gen->idle_mutex,NULL); pthread_cond_init(&cpu->gen->idle_cond,NULL); /* Set the CPU methods */ cpu->gen->reg_set = (void *)mips64_reg_set; cpu->gen->reg_dump = (void *)mips64_dump_regs; cpu->gen->mmu_dump = (void *)mips64_tlb_dump; cpu->gen->mmu_raw_dump = (void *)mips64_tlb_raw_dump; cpu->gen->add_breakpoint = (void *)mips64_add_breakpoint; cpu->gen->remove_breakpoint = (void *)mips64_remove_breakpoint; cpu->gen->set_idle_pc = (void *)mips64_set_idle_pc; cpu->gen->get_idling_pc = (void *)mips64_get_idling_pc; /* Set the startup parameters */ mips64_reset(cpu); return(0); } /* Delete a MIPS64 processor */ void mips64_delete(cpu_mips_t *cpu) { if (cpu) { mips64_mem_shutdown(cpu); mips64_jit_shutdown(cpu); } } /* Set the CPU PRID register */ void mips64_set_prid(cpu_mips_t *cpu,m_uint32_t prid) { cpu->cp0.reg[MIPS_CP0_PRID] = prid; if ((prid == MIPS_PRID_R7000) || (prid == MIPS_PRID_BCM1250)) cpu->cp0.tlb_entries = MIPS64_TLB_MAX_ENTRIES; } /* Set idle PC value */ void mips64_set_idle_pc(cpu_gen_t *cpu,m_uint64_t addr) { CPU_MIPS64(cpu)->idle_pc = addr; } /* Timer IRQ */ void *mips64_timer_irq_run(cpu_mips_t *cpu) { pthread_mutex_t umutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t ucond = PTHREAD_COND_INITIALIZER; struct timespec t_spc; m_tmcnt_t expire; u_int interval; u_int threshold; interval = 1000000 / cpu->timer_irq_freq; threshold = cpu->timer_irq_freq * 10; expire = m_gettime_usec() + interval; while(cpu->gen->state != CPU_STATE_HALTED) { pthread_mutex_lock(&umutex); t_spc.tv_sec = expire / 1000000; t_spc.tv_nsec = (expire % 1000000) * 1000; pthread_cond_timedwait(&ucond,&umutex,&t_spc); pthread_mutex_unlock(&umutex); if (likely(!cpu->irq_disable) && likely(cpu->gen->state == CPU_STATE_RUNNING)) { cpu->timer_irq_pending++; if (unlikely(cpu->timer_irq_pending > threshold)) { cpu->timer_irq_pending = 0; cpu->timer_drift++; #if 0 printf("Timer IRQ not accurate (%u pending IRQ): " "reduce the \"--timer-irq-check-itv\" parameter " "(current value: %u)\n", cpu->timer_irq_pending,cpu->timer_irq_check_itv); #endif } } expire += interval; } return NULL; } #define IDLE_HASH_SIZE 8192 /* Idle PC hash item */ struct mips64_idle_pc_hash { m_uint64_t pc; u_int count; struct mips64_idle_pc_hash *next; }; /* Determine an "idling" PC */ int mips64_get_idling_pc(cpu_gen_t *cpu) { cpu_mips_t *mcpu = CPU_MIPS64(cpu); struct mips64_idle_pc_hash **pc_hash,*p; struct cpu_idle_pc *res; u_int h_index; m_uint64_t cur_pc; int i; cpu->idle_pc_prop_count = 0; if (mcpu->idle_pc != 0) { printf("\nYou already use an idle PC, using the calibration would give " "incorrect results.\n"); return(-1); } printf("\nPlease wait while gathering statistics...\n"); pc_hash = calloc(IDLE_HASH_SIZE,sizeof(struct mips64_idle_pc_hash *)); /* Disable IRQ */ mcpu->irq_disable = TRUE; /* Take 1000 measures, each mesure every 10ms */ for(i=0;i<1000;i++) { cur_pc = mcpu->pc; h_index = (cur_pc >> 2) & (IDLE_HASH_SIZE-1); for(p=pc_hash[h_index];p;p=p->next) if (p->pc == cur_pc) { p->count++; break; } if (!p) { if ((p = malloc(sizeof(*p)))) { p->pc = cur_pc; p->count = 1; p->next = pc_hash[h_index]; pc_hash[h_index] = p; } } usleep(10000); } /* Select PCs */ for(i=0;inext) if ((p->count >= 20) && (p->count <= 80)) { res = &cpu->idle_pc_prop[cpu->idle_pc_prop_count++]; res->pc = p->pc; res->count = p->count; if (cpu->idle_pc_prop_count >= CPU_IDLE_PC_MAX_RES) goto done; } } done: /* Set idle PC */ if (cpu->idle_pc_prop_count) { printf("Done. Suggested idling PC:\n"); for(i=0;iidle_pc_prop_count;i++) { printf(" 0x%llx (count=%u)\n", cpu->idle_pc_prop[i].pc, cpu->idle_pc_prop[i].count); } printf("Restart the emulator with \"--idle-pc=0x%llx\" (for example)\n", cpu->idle_pc_prop[0].pc); } else { printf("Done. No suggestion for idling PC\n"); for(i=0;inext) { printf(" 0x%16.16llx (%3u)\n",p->pc,p->count); if (cpu->idle_pc_prop_count < CPU_IDLE_PC_MAX_RES) { res = &cpu->idle_pc_prop[cpu->idle_pc_prop_count++]; res->pc = p->pc; res->count = p->count; } } printf("\n"); } /* Re-enable IRQ */ mcpu->irq_disable = FALSE; return(0); } /* Set an IRQ (VM IRQ standard routing) */ void mips64_vm_set_irq(vm_instance_t *vm,u_int irq) { cpu_mips_t *boot_cpu; boot_cpu = CPU_MIPS64(vm->boot_cpu); if (boot_cpu->irq_disable) { boot_cpu->irq_pending = 0; return; } mips64_set_irq(boot_cpu,irq); if (boot_cpu->irq_idle_preempt[irq]) cpu_idle_break_wait(vm->boot_cpu); } /* Clear an IRQ (VM IRQ standard routing) */ void mips64_vm_clear_irq(vm_instance_t *vm,u_int irq) { cpu_mips_t *boot_cpu; boot_cpu = CPU_MIPS64(vm->boot_cpu); mips64_clear_irq(boot_cpu,irq); } /* Update the IRQ flag (inline) */ static forced_inline int mips64_update_irq_flag_fast(cpu_mips_t *cpu) { mips_cp0_t *cp0 = &cpu->cp0; m_uint32_t imask,sreg_mask; m_uint32_t cause; cpu->irq_pending = FALSE; cause = cp0->reg[MIPS_CP0_CAUSE] & ~MIPS_CP0_CAUSE_IMASK; cp0->reg[MIPS_CP0_CAUSE] = cause | cpu->irq_cause; sreg_mask = MIPS_CP0_STATUS_IE|MIPS_CP0_STATUS_EXL|MIPS_CP0_STATUS_ERL; if ((cp0->reg[MIPS_CP0_STATUS] & sreg_mask) == MIPS_CP0_STATUS_IE) { imask = cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_IMASK; if (unlikely(cp0->reg[MIPS_CP0_CAUSE] & imask)) { cpu->irq_pending = TRUE; return(TRUE); } } return(FALSE); } /* Update the IRQ flag */ void mips64_update_irq_flag(cpu_mips_t *cpu) { mips64_update_irq_flag_fast(cpu); } /* Generate an exception */ void mips64_trigger_exception(cpu_mips_t *cpu,u_int exc_code,int bd_slot) { mips_cp0_t *cp0 = &cpu->cp0; m_uint64_t cause,vector; /* we don't set EPC if EXL is set */ if (!(cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_EXL)) { cp0->reg[MIPS_CP0_EPC] = cpu->pc; /* keep IM, set exception code and bd slot */ cause = cp0->reg[MIPS_CP0_CAUSE] & MIPS_CP0_CAUSE_IMASK; if (bd_slot) cause |= MIPS_CP0_CAUSE_BD_SLOT; else cause &= ~MIPS_CP0_CAUSE_BD_SLOT; cause |= (exc_code << MIPS_CP0_CAUSE_SHIFT); cp0->reg[MIPS_CP0_CAUSE] = cause; /* XXX properly set vector */ vector = 0x180ULL; } else { /* keep IM and set exception code */ cause = cp0->reg[MIPS_CP0_CAUSE] & MIPS_CP0_CAUSE_IMASK; cause |= (exc_code << MIPS_CP0_CAUSE_SHIFT); cp0->reg[MIPS_CP0_CAUSE] = cause; /* set vector */ vector = 0x180ULL; } /* Set EXL bit in status register */ cp0->reg[MIPS_CP0_STATUS] |= MIPS_CP0_STATUS_EXL; /* Use bootstrap vectors ? */ if (cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_BEV) cpu->pc = 0xffffffffbfc00200ULL + vector; else cpu->pc = 0xffffffff80000000ULL + vector; /* Clear the pending IRQ flag */ cpu->irq_pending = 0; } /* * Increment count register and trigger the timer IRQ if value in compare * register is the same. */ fastcall void mips64_exec_inc_cp0_cnt(cpu_mips_t *cpu) { cpu->cp0_virt_cnt_reg++; #if 0 /* TIMER_IRQ */ mips_cp0_t *cp0 = &cpu->cp0; if (unlikely((cpu->cp0_virt_cnt_reg == cpu->cp0_virt_cmp_reg))) { cp0->reg[MIPS_CP0_COUNT] = (m_uint32_t)cp0->reg[MIPS_CP0_COMPARE]; mips64_set_irq(cpu,7); mips64_update_irq_flag_fast(cpu); } #endif } /* Trigger the Timer IRQ */ fastcall void mips64_trigger_timer_irq(cpu_mips_t *cpu) { mips_cp0_t *cp0 = &cpu->cp0; cpu->timer_irq_count++; cp0->reg[MIPS_CP0_COUNT] = (m_uint32_t)cp0->reg[MIPS_CP0_COMPARE]; mips64_set_irq(cpu,7); mips64_update_irq_flag_fast(cpu); } /* Execute ERET instruction */ fastcall void mips64_exec_eret(cpu_mips_t *cpu) { mips_cp0_t *cp0 = &cpu->cp0; if (cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_ERL) { cp0->reg[MIPS_CP0_STATUS] &= ~MIPS_CP0_STATUS_ERL; cpu->pc = cp0->reg[MIPS_CP0_ERR_EPC]; } else { cp0->reg[MIPS_CP0_STATUS] &= ~MIPS_CP0_STATUS_EXL; cpu->pc = cp0->reg[MIPS_CP0_EPC]; } /* We have to clear the LLbit */ cpu->ll_bit = 0; /* Update the pending IRQ flag */ mips64_update_irq_flag_fast(cpu); } /* Execute SYSCALL instruction */ fastcall void mips64_exec_syscall(cpu_mips_t *cpu) { #if DEBUG_SYSCALL printf("MIPS64: SYSCALL at PC=0x%llx (RA=0x%llx)\n" " a0=0x%llx, a1=0x%llx, a2=0x%llx, a3=0x%llx\n", cpu->pc, cpu->gpr[MIPS_GPR_RA], cpu->gpr[MIPS_GPR_A0], cpu->gpr[MIPS_GPR_A1], cpu->gpr[MIPS_GPR_A2], cpu->gpr[MIPS_GPR_A3]); #endif /* XXX TODO: Branch Delay slot */ mips64_trigger_exception(cpu,MIPS_CP0_CAUSE_SYSCALL,0); } /* Execute BREAK instruction */ fastcall void mips64_exec_break(cpu_mips_t *cpu,u_int code) { printf("MIPS64: BREAK instruction (code=%u)\n",code); mips64_dump_regs(cpu->gen); /* XXX TODO: Branch Delay slot */ mips64_trigger_exception(cpu,MIPS_CP0_CAUSE_BP,0); } /* Trigger a Trap Exception */ fastcall void mips64_trigger_trap_exception(cpu_mips_t *cpu) { /* XXX TODO: Branch Delay slot */ printf("MIPS64: TRAP exception, CPU=%p\n",cpu); mips64_trigger_exception(cpu,MIPS_CP0_CAUSE_TRAP,0); } /* Trigger IRQs */ fastcall void mips64_trigger_irq(cpu_mips_t *cpu) { if (unlikely(cpu->irq_disable)) { cpu->irq_pending = 0; return; } cpu->irq_count++; if (mips64_update_irq_flag_fast(cpu)) mips64_trigger_exception(cpu,MIPS_CP0_CAUSE_INTERRUPT,0); else cpu->irq_fp_count++; } /* DMFC1 */ fastcall void mips64_exec_dmfc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg) { cpu->gpr[gp_reg] = cpu->fpu.reg[cp1_reg]; } /* DMTC1 */ fastcall void mips64_exec_dmtc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg) { cpu->fpu.reg[cp1_reg] = cpu->gpr[gp_reg]; } /* MFC1 */ fastcall void mips64_exec_mfc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg) { m_int64_t val; val = cpu->fpu.reg[cp1_reg] & 0xffffffff; cpu->gpr[gp_reg] = sign_extend(val,32); } /* MTC1 */ fastcall void mips64_exec_mtc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg) { cpu->fpu.reg[cp1_reg] = cpu->gpr[gp_reg] & 0xffffffff; } /* Virtual breakpoint */ fastcall void mips64_run_breakpoint(cpu_mips_t *cpu) { cpu_log(cpu->gen,"BREAKPOINT", "Virtual breakpoint reached at PC=0x%llx\n",cpu->pc); printf("[[[ Virtual Breakpoint reached at PC=0x%llx RA=0x%llx]]]\n", cpu->pc,cpu->gpr[MIPS_GPR_RA]); mips64_dump_regs(cpu->gen); memlog_dump(cpu->gen); } /* Add a virtual breakpoint */ int mips64_add_breakpoint(cpu_gen_t *cpu,m_uint64_t pc) { cpu_mips_t *mcpu = CPU_MIPS64(cpu); int i; for(i=0;ibreakpoints[i]) break; if (i == MIPS64_MAX_BREAKPOINTS) return(-1); mcpu->breakpoints[i] = pc; mcpu->breakpoints_enabled = TRUE; return(0); } /* Remove a virtual breakpoint */ void mips64_remove_breakpoint(cpu_gen_t *cpu,m_uint64_t pc) { cpu_mips_t *mcpu = CPU_MIPS64(cpu); int i,j; for(i=0;ibreakpoints[i] == pc) { for(j=i;jbreakpoints[j] = mcpu->breakpoints[j+1]; mcpu->breakpoints[MIPS64_MAX_BREAKPOINTS-1] = 0; } for(i=0;ibreakpoints[i] != 0) return; mcpu->breakpoints_enabled = FALSE; } /* Debugging for register-jump to address 0 */ fastcall void mips64_debug_jr0(cpu_mips_t *cpu) { printf("MIPS64: cpu %p jumping to address 0...\n",cpu); mips64_dump_regs(cpu->gen); } /* Set a register */ void mips64_reg_set(cpu_gen_t *cpu,u_int reg,m_uint64_t val) { if (reg < MIPS64_GPR_NR) CPU_MIPS64(cpu)->gpr[reg] = val; } /* Dump registers of a MIPS64 processor */ void mips64_dump_regs(cpu_gen_t *cpu) { cpu_mips_t *mcpu = CPU_MIPS64(cpu); mips_insn_t *ptr,insn; char buffer[80]; int i; printf("MIPS64 Registers:\n"); for(i=0;igpr[i*2], mips64_gpr_reg_names[(i*2)+1], (i*2)+1, mcpu->gpr[(i*2)+1]); } printf(" lo = 0x%16.16llx, hi = 0x%16.16llx\n", mcpu->lo, mcpu->hi); printf(" pc = 0x%16.16llx, ll_bit = %u\n", mcpu->pc, mcpu->ll_bit); /* Fetch the current instruction */ ptr = mcpu->mem_op_lookup(mcpu,mcpu->pc); if (ptr) { insn = vmtoh32(*ptr); if (mips64_dump_insn(buffer,sizeof(buffer),1,mcpu->pc,insn) != -1) printf(" Instruction: %s\n",buffer); } printf("\nCP0 Registers:\n"); for(i=0;iirq_count,mcpu->irq_fp_count,mcpu->irq_pending); printf(" Timer IRQ count: %llu, pending: %u, timer drift: %u\n\n", mcpu->timer_irq_count,mcpu->timer_irq_pending,mcpu->timer_drift); printf(" Device access count: %llu\n",cpu->dev_access_counter); printf("\n"); } /* Dump a memory block */ void mips64_dump_memory(cpu_mips_t *cpu,m_uint64_t vaddr,u_int count) { void *haddr; u_int i; for(i=0;imem_op_lookup(cpu,vaddr); if (haddr) printf("0x%8.8x ",htovm32(*(m_uint32_t *)haddr)); else printf("XXXXXXXXXX "); } printf("\n\n"); } /* Dump the stack */ void mips64_dump_stack(cpu_mips_t *cpu,u_int count) { printf("MIPS Stack Dump at 0x%16.16llx:",cpu->gpr[MIPS_GPR_SP]); mips64_dump_memory(cpu,cpu->gpr[MIPS_GPR_SP],count); } /* Save the CPU state into a file */ int mips64_save_state(cpu_mips_t *cpu,char *filename) { FILE *fd; int i; if (!(fd = fopen(filename,"w"))) { perror("mips64_save_state: fopen"); return(-1); } /* pc, lo and hi */ fprintf(fd,"pc: %16.16llx\n",cpu->pc); fprintf(fd,"lo: %16.16llx\n",cpu->lo); fprintf(fd,"hi: %16.16llx\n",cpu->hi); /* general purpose registers */ for(i=0;igpr[i]); printf("\n"); /* cp0 registers */ for(i=0;icp0.reg[i]); printf("\n"); /* cp1 registers */ for(i=0;ifpu.reg[i]); printf("\n"); /* tlb entries */ for(i=0;icp0.tlb_entries;i++) { fprintf(fd,"tlb%d_mask: %16.16llx\n",i,cpu->cp0.tlb[i].mask); fprintf(fd,"tlb%d_hi: %16.16llx\n",i,cpu->cp0.tlb[i].hi); fprintf(fd,"tlb%d_lo0: %16.16llx\n",i,cpu->cp0.tlb[i].lo0); fprintf(fd,"tlb%d_lo1: %16.16llx\n",i,cpu->cp0.tlb[i].lo1); } fclose(fd); return(0); } /* Read a 64-bit unsigned integer */ static m_uint64_t mips64_hex_u64(char *str,int *err) { m_uint64_t res = 0; u_char c; /* remove leading spaces */ while((*str == ' ') || (*str == '\t')) str++; while(*str) { c = *str; if ((c >= '0') && (c <= '9')) res = (res << 4) + (c - '0'); if ((c >= 'a') && (c <= 'f')) res = (res << 4) + ((c - 'a') + 10); if ((c >= 'A') && (c <= 'F')) res = (res << 4) + ((c - 'A') + 10); str++; } return(res); } /* Restore the CPU state from a file */ int mips64_restore_state(cpu_mips_t *cpu,char *filename) { char buffer[4096],*sep,*value,*ep,*field; size_t len; FILE *fd; int index; if (!(fd = fopen(filename,"r"))) { perror("mips64_restore_state: fopen"); return(-1); } while(!feof(fd)) { *buffer = 0; fgets(buffer,sizeof(buffer),fd); len = strlen(buffer); if (buffer[len-1] == '\n') buffer[len-1] = 0; sep = strchr(buffer,':'); if (!sep) continue; value = sep + 1; *sep = 0; /* gpr ? */ if ((index = mips64_get_reg_index(buffer)) != -1) { cpu->gpr[index] = mips64_hex_u64(value,NULL); continue; } /* cp0 register ? */ if ((index = mips64_cp0_get_reg_index(buffer)) != -1) { cpu->cp0.reg[index] = mips64_hex_u64(value,NULL); continue; } /* cp1 register ? */ if ((len > 3) && (!strncmp(buffer,"fpu",3))) { index = atoi(buffer+3); cpu->fpu.reg[index] = mips64_hex_u64(value,NULL); } /* tlb entry ? */ if ((len > 3) && (!strncmp(buffer,"tlb",3))) { ep = strchr(buffer,'_'); if (ep) { index = atoi(buffer+3); field = ep + 1; if (!strcmp(field,"mask")) { cpu->cp0.tlb[index].mask = mips64_hex_u64(value,NULL); continue; } if (!strcmp(field,"hi")) { cpu->cp0.tlb[index].hi = mips64_hex_u64(value,NULL); continue; } if (!strcmp(field,"lo0")) { cpu->cp0.tlb[index].lo0 = mips64_hex_u64(value,NULL); continue; } if (!strcmp(field,"lo1")) { cpu->cp0.tlb[index].lo1 = mips64_hex_u64(value,NULL); continue; } } } /* pc, lo, hi ? */ if (!strcmp(buffer,"pc")) { cpu->pc = mips64_hex_u64(value,NULL); continue; } if (!strcmp(buffer,"lo")) { cpu->lo = mips64_hex_u64(value,NULL); continue; } if (!strcmp(buffer,"hi")) { cpu->hi = mips64_hex_u64(value,NULL); continue; } } mips64_cp0_map_all_tlb_to_mts(cpu); mips64_dump_regs(cpu->gen); mips64_tlb_dump(cpu->gen); fclose(fd); return(0); } /* Load a raw image into the simulated memory */ int mips64_load_raw_image(cpu_mips_t *cpu,char *filename,m_uint64_t vaddr) { struct stat file_info; size_t len,clen; m_uint32_t remain; void *haddr; FILE *bfd; if (!(bfd = fopen(filename,"r"))) { perror("fopen"); return(-1); } if (fstat(fileno(bfd),&file_info) == -1) { perror("stat"); return(-1); } len = file_info.st_size; printf("Loading RAW file '%s' at virtual address 0x%llx (size=%lu)\n", filename,vaddr,(u_long)len); while(len > 0) { haddr = cpu->mem_op_lookup(cpu,vaddr); if (!haddr) { fprintf(stderr,"load_raw_image: invalid load address 0x%llx\n", vaddr); return(-1); } if (len > MIPS_MIN_PAGE_SIZE) clen = MIPS_MIN_PAGE_SIZE; else clen = len; remain = MIPS_MIN_PAGE_SIZE; remain -= (vaddr - (vaddr & MIPS_MIN_PAGE_MASK)); clen = m_min(clen,remain); if (fread((u_char *)haddr,clen,1,bfd) != 1) break; vaddr += clen; len -= clen; } fclose(bfd); return(0); } /* Load an ELF image into the simulated memory */ int mips64_load_elf_image(cpu_mips_t *cpu,char *filename,int skip_load, m_uint32_t *entry_point) { m_uint64_t vaddr; m_uint32_t remain; void *haddr; Elf32_Ehdr *ehdr; Elf32_Shdr *shdr; Elf_Scn *scn; Elf *img_elf; size_t len,clen; _maybe_used char *name; int i,fd; FILE *bfd; if (!filename) return(-1); #ifdef __CYGWIN__ fd = open(filename,O_RDONLY|O_BINARY); #else fd = open(filename,O_RDONLY); #endif if (fd == -1) { perror("load_elf_image: open"); return(-1); } if (elf_version(EV_CURRENT) == EV_NONE) { fprintf(stderr,"load_elf_image: library out of date\n"); return(-1); } if (!(img_elf = elf_begin(fd,ELF_C_READ,NULL))) { fprintf(stderr,"load_elf_image: elf_begin: %s\n", elf_errmsg(elf_errno())); return(-1); } if (!(ehdr = elf32_getehdr(img_elf))) { fprintf(stderr,"load_elf_image: invalid ELF file\n"); return(-1); } printf("Loading ELF file '%s'...\n",filename); bfd = fdopen(fd,"rb"); if (!bfd) { perror("load_elf_image: fdopen"); return(-1); } if (!skip_load) { for(i=0;ie_shnum;i++) { scn = elf_getscn(img_elf,i); shdr = elf32_getshdr(scn); name = elf_strptr(img_elf, ehdr->e_shstrndx, (size_t)shdr->sh_name); len = shdr->sh_size; if (!(shdr->sh_flags & SHF_ALLOC) || !len) continue; fseek(bfd,shdr->sh_offset,SEEK_SET); vaddr = sign_extend(shdr->sh_addr,32); if (cpu->vm->debug_level > 0) { printf(" * Adding section at virtual address 0x%8.8llx " "(len=0x%8.8lx)\n",vaddr & 0xFFFFFFFF,(u_long)len); } while(len > 0) { haddr = cpu->mem_op_lookup(cpu,vaddr); if (!haddr) { fprintf(stderr,"load_elf_image: invalid load address 0x%llx\n", vaddr); return(-1); } if (len > MIPS_MIN_PAGE_SIZE) clen = MIPS_MIN_PAGE_SIZE; else clen = len; remain = PPC32_MIN_PAGE_SIZE; remain -= (vaddr - (vaddr & PPC32_MIN_PAGE_MASK)); clen = m_min(clen,remain); if (fread((u_char *)haddr,clen,1,bfd) < 1) break; vaddr += clen; len -= clen; } } } else { printf("ELF loading skipped, using a ghost RAM file.\n"); } printf("ELF entry point: 0x%x\n",ehdr->e_entry); if (entry_point) *entry_point = ehdr->e_entry; elf_end(img_elf); fclose(bfd); return(0); } /* Symbol lookup */ struct symbol *mips64_sym_lookup(cpu_mips_t *cpu,m_uint64_t addr) { return(rbtree_lookup(cpu->sym_tree,&addr)); } /* Insert a new symbol */ struct symbol *mips64_sym_insert(cpu_mips_t *cpu,char *name,m_uint64_t addr) { struct symbol *sym; size_t len; if (!cpu->sym_tree) return NULL; len = strlen(name); if (!(sym = malloc(len+1+sizeof(*sym)))) return NULL; memcpy(sym->name,name,len+1); sym->addr = addr; if (rbtree_insert(cpu->sym_tree,sym,sym) == -1) { free(sym); return NULL; } return sym; } /* Symbol comparison function */ static int mips64_sym_compare(m_uint64_t *a1,struct symbol *sym) { if (*a1 > sym->addr) return(1); if (*a1 < sym->addr) return(-1); return(0); } /* Create the symbol tree */ int mips64_sym_create_tree(cpu_mips_t *cpu) { cpu->sym_tree = rbtree_create((tree_fcompare)mips64_sym_compare,NULL); return(cpu->sym_tree ? 0 : -1); } /* Load a symbol file */ int mips64_sym_load_file(cpu_mips_t *cpu,char *filename) { char buffer[4096],func_name[128]; m_uint64_t addr; char sym_type; FILE *fd; if (!cpu->sym_tree && (mips64_sym_create_tree(cpu) == -1)) { fprintf(stderr,"CPU%u: Unable to create symbol tree.\n",cpu->gen->id); return(-1); } if (!(fd = fopen(filename,"r"))) { perror("load_sym_file: fopen"); return(-1); } while(!feof(fd)) { fgets(buffer,sizeof(buffer),fd); if (sscanf(buffer,"%llx %c %s",&addr,&sym_type,func_name) == 3) { mips64_sym_insert(cpu,func_name,addr); } } fclose(fd); return(0); } dynamips-0.2.14/stable/mips64.h000066400000000000000000000446611241034141600161760ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __MIPS_64_H__ #define __MIPS_64_H__ #include #include "utils.h" #include "rbtree.h" /* * MIPS General Purpose Registers */ #define MIPS_GPR_ZERO 0 /* zero */ #define MIPS_GPR_AT 1 /* at */ #define MIPS_GPR_V0 2 /* v0 */ #define MIPS_GPR_V1 3 /* v1 */ #define MIPS_GPR_A0 4 /* a0 */ #define MIPS_GPR_A1 5 /* a1 */ #define MIPS_GPR_A2 6 /* a2 */ #define MIPS_GPR_A3 7 /* a3 */ #define MIPS_GPR_T0 8 /* t0 */ #define MIPS_GPR_T1 9 /* t1 */ #define MIPS_GPR_T2 10 /* t2 */ #define MIPS_GPR_T3 11 /* t3 */ #define MIPS_GPR_T4 12 /* t4 */ #define MIPS_GPR_T5 13 /* t5 */ #define MIPS_GPR_T6 14 /* t6 */ #define MIPS_GPR_T7 15 /* t7 */ #define MIPS_GPR_S0 16 /* s0 */ #define MIPS_GPR_S1 17 /* s1 */ #define MIPS_GPR_S2 18 /* s2 */ #define MIPS_GPR_S3 19 /* s3 */ #define MIPS_GPR_S4 20 /* s4 */ #define MIPS_GPR_S5 21 /* s5 */ #define MIPS_GPR_S6 22 /* s6 */ #define MIPS_GPR_S7 23 /* s7 */ #define MIPS_GPR_T8 24 /* t8 */ #define MIPS_GPR_T9 25 /* t9 */ #define MIPS_GPR_K0 26 /* k0 */ #define MIPS_GPR_K1 27 /* k1 */ #define MIPS_GPR_GP 28 /* gp */ #define MIPS_GPR_SP 29 /* sp */ #define MIPS_GPR_FP 30 /* fp */ #define MIPS_GPR_RA 31 /* ra */ /* * Coprocessor 0 (System Coprocessor) Register definitions */ #define MIPS_CP0_INDEX 0 /* TLB Index */ #define MIPS_CP0_RANDOM 1 /* TLB Random */ #define MIPS_CP0_TLB_LO_0 2 /* TLB Entry Lo0 */ #define MIPS_CP0_TLB_LO_1 3 /* TLB Entry Lo1 */ #define MIPS_CP0_CONTEXT 4 /* Kernel PTE pointer */ #define MIPS_CP0_PAGEMASK 5 /* TLB Page Mask */ #define MIPS_CP0_WIRED 6 /* TLB Wired */ #define MIPS_CP0_INFO 7 /* Info (RM7000) */ #define MIPS_CP0_BADVADDR 8 /* Bad Virtual Address */ #define MIPS_CP0_COUNT 9 /* Count */ #define MIPS_CP0_TLB_HI 10 /* TLB Entry Hi */ #define MIPS_CP0_COMPARE 11 /* Timer Compare */ #define MIPS_CP0_STATUS 12 /* Status */ #define MIPS_CP0_CAUSE 13 /* Cause */ #define MIPS_CP0_EPC 14 /* Exception PC */ #define MIPS_CP0_PRID 15 /* Proc Rev ID */ #define MIPS_CP0_CONFIG 16 /* Configuration */ #define MIPS_CP0_LLADDR 17 /* Load/Link address */ #define MIPS_CP0_WATCHLO 18 /* Low Watch address */ #define MIPS_CP0_WATCHHI 19 /* High Watch address */ #define MIPS_CP0_XCONTEXT 20 /* Extended context */ #define MIPS_CP0_ECC 26 /* ECC and parity */ #define MIPS_CP0_CACHERR 27 /* Cache Err/Status */ #define MIPS_CP0_TAGLO 28 /* Cache Tag Lo */ #define MIPS_CP0_TAGHI 29 /* Cache Tag Hi */ #define MIPS_CP0_ERR_EPC 30 /* Error exception PC */ /* * CP0 Set 1 Registers (R7000) */ #define MIPS_CP0_S1_CONFIG 16 /* Configuration Register */ #define MIPS_CP0_S1_IPLLO 18 /* Priority level for IRQ [7:0] */ #define MIPS_CP0_S1_IPLHI 19 /* Priority level for IRQ [15:8] */ #define MIPS_CP0_S1_INTCTL 20 /* Interrupt Control */ #define MIPS_CP0_S1_DERRADDR0 26 /* Imprecise Error Address */ #define MIPS_CP0_S1_DERRADDR1 27 /* Imprecise Error Address */ /* * CP0 Status Register */ #define MIPS_CP0_STATUS_CU0 0x10000000 #define MIPS_CP0_STATUS_CU1 0x20000000 #define MIPS_CP0_STATUS_BEV 0x00400000 #define MIPS_CP0_STATUS_TS 0x00200000 #define MIPS_CP0_STATUS_SR 0x00100000 #define MIPS_CP0_STATUS_CH 0x00040000 #define MIPS_CP0_STATUS_CE 0x00020000 #define MIPS_CP0_STATUS_DE 0x00010000 #define MIPS_CP0_STATUS_RP 0x08000000 #define MIPS_CP0_STATUS_FR 0x04000000 #define MIPS_CP0_STATUS_RE 0x02000000 #define MIPS_CP0_STATUS_KX 0x00000080 #define MIPS_CP0_STATUS_SX 0x00000040 #define MIPS_CP0_STATUS_UX 0x00000020 #define MIPS_CP0_STATUS_KSU 0x00000018 #define MIPS_CP0_STATUS_ERL 0x00000004 #define MIPS_CP0_STATUS_EXL 0x00000002 #define MIPS_CP0_STATUS_IE 0x00000001 #define MIPS_CP0_STATUS_IMASK7 0x00008000 #define MIPS_CP0_STATUS_IMASK6 0x00004000 #define MIPS_CP0_STATUS_IMASK5 0x00002000 #define MIPS_CP0_STATUS_IMASK4 0x00001000 #define MIPS_CP0_STATUS_IMASK3 0x00000800 #define MIPS_CP0_STATUS_IMASK2 0x00000400 #define MIPS_CP0_STATUS_IMASK1 0x00000200 #define MIPS_CP0_STATUS_IMASK0 0x00000100 #define MIPS_CP0_STATUS_DS_MASK 0x00770000 #define MIPS_CP0_STATUS_CU_MASK 0xF0000000 #define MIPS_CP0_STATUS_IMASK 0x0000FF00 /* Addressing mode: Kernel, Supervisor and User */ #define MIPS_CP0_STATUS_KSU_SHIFT 0x03 #define MIPS_CP0_STATUS_KSU_MASK 0x03 #define MIPS_CP0_STATUS_KM 0x00 #define MIPS_CP0_STATUS_SM 0x01 #define MIPS_CP0_STATUS_UM 0x10 /* * CP0 Cause register */ #define MIPS_CP0_CAUSE_BD_SLOT 0x80000000 #define MIPS_CP0_CAUSE_MASK 0x0000007C #define MIPS_CP0_CAUSE_CEMASK 0x30000000 #define MIPS_CP0_CAUSE_IMASK 0x0000FF00 #define MIPS_CP0_CAUSE_SHIFT 2 #define MIPS_CP0_CAUSE_CESHIFT 28 #define MIPS_CP0_CAUSE_ISHIFT 8 #define MIPS_CP0_CAUSE_INTERRUPT 0 #define MIPS_CP0_CAUSE_TLB_MOD 1 #define MIPS_CP0_CAUSE_TLB_LOAD 2 #define MIPS_CP0_CAUSE_TLB_SAVE 3 #define MIPS_CP0_CAUSE_ADDR_LOAD 4 /* ADEL */ #define MIPS_CP0_CAUSE_ADDR_SAVE 5 /* ADES */ #define MIPS_CP0_CAUSE_BUS_INSTR 6 #define MIPS_CP0_CAUSE_BUS_DATA 7 #define MIPS_CP0_CAUSE_SYSCALL 8 #define MIPS_CP0_CAUSE_BP 9 #define MIPS_CP0_CAUSE_ILLOP 10 #define MIPS_CP0_CAUSE_CP_UNUSABLE 11 #define MIPS_CP0_CAUSE_OVFLW 12 #define MIPS_CP0_CAUSE_TRAP 13 #define MIPS_CP0_CAUSE_VC_INSTR 14 /* Virtual Coherency */ #define MIPS_CP0_CAUSE_FPE 15 #define MIPS_CP0_CAUSE_WATCH 23 #define MIPS_CP0_CAUSE_VC_DATA 31 /* Virtual Coherency */ #define MIPS_CP0_CAUSE_IBIT7 0x00008000 #define MIPS_CP0_CAUSE_IBIT6 0x00004000 #define MIPS_CP0_CAUSE_IBIT5 0x00002000 #define MIPS_CP0_CAUSE_IBIT4 0x00001000 #define MIPS_CP0_CAUSE_IBIT3 0x00000800 #define MIPS_CP0_CAUSE_IBIT2 0x00000400 #define MIPS_CP0_CAUSE_IBIT1 0x00000200 #define MIPS_CP0_CAUSE_IBIT0 0x00000100 /* TLB masks and shifts */ #define MIPS_TLB_PAGE_MASK 0x01ffe000 #define MIPS_TLB_PAGE_SHIFT 13 #define MIPS_TLB_VPN2_MASK_32 0xffffe000ULL #define MIPS_TLB_VPN2_MASK_64 0xc00000ffffffe000ULL #define MIPS_TLB_PFN_MASK 0x3fffffc0 #define MIPS_TLB_ASID_MASK 0x000000ff /* "asid" in EntryHi */ #define MIPS_TLB_G_MASK 0x00001000 /* "Global" in EntryHi */ #define MIPS_TLB_V_MASK 0x2 /* "Valid" in EntryLo */ #define MIPS_TLB_D_MASK 0x4 /* "Dirty" in EntryLo */ #define MIPS_TLB_C_MASK 0x38 /* Page Coherency Attribute */ #define MIPS_TLB_C_SHIFT 3 #define MIPS_CP0_LO_G_MASK 0x00000001 /* "Global" in Lo0/1 reg */ #define MIPS_CP0_HI_SAFE_MASK 0xffffe0ff /* Safety mask for Hi reg */ #define MIPS_CP0_LO_SAFE_MASK 0x7fffffff /* Safety mask for Lo reg */ /* MIPS "jr ra" instruction */ #define MIPS_INSN_JR_RA 0x03e00008 /* Minimum page size: 4 Kb */ #define MIPS_MIN_PAGE_SHIFT 12 #define MIPS_MIN_PAGE_SIZE (1 << MIPS_MIN_PAGE_SHIFT) #define MIPS_MIN_PAGE_IMASK (MIPS_MIN_PAGE_SIZE - 1) #define MIPS_MIN_PAGE_MASK 0xfffffffffffff000ULL /* Addressing mode: Kernel, Supervisor and User */ #define MIPS_MODE_KERNEL 00 /* Segments in 32-bit User mode */ #define MIPS_USEG_BASE 0x00000000 #define MIPS_USEG_SIZE 0x80000000 /* Segments in 32-bit Supervisor mode */ #define MIPS_SUSEG_BASE 0x00000000 #define MIPS_SUSEG_SIZE 0x80000000 #define MIPS_SSEG_BASE 0xc0000000 #define MIPS_SSEG_SIZE 0x20000000 /* Segments in 32-bit Kernel mode */ #define MIPS_KUSEG_BASE 0x00000000 #define MIPS_KUSEG_SIZE 0x80000000 #define MIPS_KSEG0_BASE 0x80000000 #define MIPS_KSEG0_SIZE 0x20000000 #define MIPS_KSEG1_BASE 0xa0000000 #define MIPS_KSEG1_SIZE 0x20000000 #define MIPS_KSSEG_BASE 0xc0000000 #define MIPS_KSSEG_SIZE 0x20000000 #define MIPS_KSEG3_BASE 0xe0000000 #define MIPS_KSEG3_SIZE 0x20000000 /* xkphys mask (36-bit physical address) */ #define MIPS64_XKPHYS_ZONE_MASK 0xF800000000000000ULL #define MIPS64_XKPHYS_PHYS_SIZE (1ULL << 36) #define MIPS64_XKPHYS_PHYS_MASK (MIPS64_XKPHYS_PHYS_SIZE - 1) #define MIPS64_XKPHYS_CCA_SHIFT 59 /* Initial Program Counter and Stack pointer for ROM */ #define MIPS_ROM_PC 0xffffffffbfc00000ULL #define MIPS_ROM_SP 0xffffffff80004000ULL /* Number of GPR (general purpose registers) */ #define MIPS64_GPR_NR 32 /* Number of registers in CP0 */ #define MIPS64_CP0_REG_NR 32 /* Number of registers in CP1 */ #define MIPS64_CP1_REG_NR 32 /* Number of TLB entries */ #define MIPS64_TLB_STD_ENTRIES 48 #define MIPS64_TLB_MAX_ENTRIES 64 #define MIPS64_TLB_IDX_MASK 0x3f /* 6 bits */ /* Enable the 64 TLB entries for R7000 CPU */ #define MIPS64_R7000_TLB64_ENABLE 0x20000000 /* Number of instructions per page */ #define MIPS_INSN_PER_PAGE (MIPS_MIN_PAGE_SIZE/sizeof(mips_insn_t)) /* MIPS CPU Identifiers */ #define MIPS_PRID_R4600 0x00002012 #define MIPS_PRID_R4700 0x00002112 #define MIPS_PRID_R5000 0x00002312 #define MIPS_PRID_R7000 0x00002721 #define MIPS_PRID_R527x 0x00002812 #define MIPS_PRID_BCM1250 0x00040102 /* Memory operations */ enum { MIPS_MEMOP_LOOKUP = 0, MIPS_MEMOP_LB, MIPS_MEMOP_LBU, MIPS_MEMOP_LH, MIPS_MEMOP_LHU, MIPS_MEMOP_LW, MIPS_MEMOP_LWU, MIPS_MEMOP_LD, MIPS_MEMOP_SB, MIPS_MEMOP_SH, MIPS_MEMOP_SW, MIPS_MEMOP_SD, MIPS_MEMOP_LWL, MIPS_MEMOP_LWR, MIPS_MEMOP_LDL, MIPS_MEMOP_LDR, MIPS_MEMOP_SWL, MIPS_MEMOP_SWR, MIPS_MEMOP_SDL, MIPS_MEMOP_SDR, MIPS_MEMOP_LL, MIPS_MEMOP_SC, MIPS_MEMOP_LDC1, MIPS_MEMOP_SDC1, MIPS_MEMOP_CACHE, MIPS_MEMOP_MAX, }; /* Maximum number of breakpoints */ #define MIPS64_MAX_BREAKPOINTS 8 /* MIPS CPU type */ typedef struct cpu_mips cpu_mips_t; /* Memory operation function prototype */ typedef fastcall void (*mips_memop_fn)(cpu_mips_t *cpu,m_uint64_t vaddr, u_int reg); /* TLB entry definition */ typedef struct { m_uint64_t mask; m_uint64_t hi; m_uint64_t lo0; m_uint64_t lo1; }tlb_entry_t; /* System Coprocessor (CP0) definition */ typedef struct { m_uint64_t reg[MIPS64_CP0_REG_NR]; tlb_entry_t tlb[MIPS64_TLB_MAX_ENTRIES]; /* Number of TLB entries */ u_int tlb_entries; /* Extensions for R7000 CP0 Set1 */ m_uint32_t ipl_lo,ipl_hi,int_ctl; m_uint32_t derraddr0,derraddr1; }mips_cp0_t; /* FPU Coprocessor (CP1) definition */ typedef struct { m_uint64_t reg[MIPS64_CP1_REG_NR]; }mips_cp1_t; /* MIPS CPU definition */ struct cpu_mips { /* MTS32/MTS64 caches */ union { mts32_entry_t *mts32_cache; mts64_entry_t *mts64_cache; }mts_u; /* Virtual version of CP0 Compare Register */ m_uint32_t cp0_virt_cnt_reg,cp0_virt_cmp_reg; /* General Purpose Registers, Pointer Counter, LO/HI, IRQ */ m_uint32_t irq_pending,irq_cause,ll_bit; m_uint64_t pc,gpr[MIPS64_GPR_NR]; m_uint64_t lo,hi,ret_pc; /* Code page translation cache */ mips64_jit_tcb_t **exec_blk_map; /* Virtual address to physical page translation */ fastcall int (*translate)(cpu_mips_t *cpu,m_uint64_t vaddr, m_uint32_t *phys_page); /* Memory access functions */ mips_memop_fn mem_op_fn[MIPS_MEMOP_MAX]; /* Memory lookup function (to load ELF image,...) */ void *(*mem_op_lookup)(cpu_mips_t *cpu,m_uint64_t vaddr); /* System coprocessor (CP0) */ mips_cp0_t cp0; /* FPU (CP1) */ mips_cp1_t fpu; /* Address bus mask for physical addresses */ m_uint64_t addr_bus_mask; /* IRQ counters and cause */ m_uint64_t irq_count,timer_irq_count,irq_fp_count; pthread_mutex_t irq_lock; /* Current and free lists of translated code blocks */ mips64_jit_tcb_t *tcb_list,*tcb_last,*tcb_free_list; /* Executable page area */ void *exec_page_area; size_t exec_page_area_size; size_t exec_page_count,exec_page_alloc; insn_exec_page_t *exec_page_free_list; insn_exec_page_t *exec_page_array; /* Idle PC value */ volatile m_uint64_t idle_pc; /* Timer IRQs */ volatile u_int timer_irq_pending; u_int timer_irq_freq; u_int timer_irq_check_itv; u_int timer_drift; /* IRQ disable flag */ volatile u_int irq_disable; /* IRQ idling preemption */ u_int irq_idle_preempt[8]; /* Generic CPU instance pointer */ cpu_gen_t *gen; /* VM instance */ vm_instance_t *vm; /* non-JIT mode instruction counter */ m_uint64_t insn_exec_count; /* MTS map/unmap/rebuild operations */ void (*mts_map)(cpu_mips_t *cpu,m_uint64_t vaddr, m_uint64_t paddr,m_uint32_t len, int cache_access,int tlb_index); void (*mts_unmap)(cpu_mips_t *cpu,m_uint64_t vaddr,m_uint32_t len, m_uint32_t val,int tlb_index); void (*mts_shutdown)(cpu_mips_t *cpu); /* MTS cache statistics */ m_uint64_t mts_misses,mts_lookups; /* JIT flush method */ u_int jit_flush_method; /* Number of compiled pages */ u_int compiled_pages; /* Fast memory operations use */ u_int fast_memop; /* Direct block jump */ u_int exec_blk_direct_jump; /* Address mode (32 or 64 bits) */ u_int addr_mode; /* Current exec page (non-JIT) info */ m_uint64_t njm_exec_page; mips_insn_t *njm_exec_ptr; /* Performance counter (number of instructions executed by CPU) */ m_uint32_t perf_counter; /* Breakpoints */ m_uint64_t breakpoints[MIPS64_MAX_BREAKPOINTS]; u_int breakpoints_enabled; /* Symtrace */ int sym_trace; rbtree_tree *sym_tree; }; #define MIPS64_IRQ_LOCK(cpu) pthread_mutex_lock(&(cpu)->irq_lock) #define MIPS64_IRQ_UNLOCK(cpu) pthread_mutex_unlock(&(cpu)->irq_lock) /* Register names */ extern char *mips64_gpr_reg_names[]; /* Get cacheability info */ int mips64_cca_cached(m_uint8_t val); /* Reset a MIPS64 CPU */ int mips64_reset(cpu_mips_t *cpu); /* Initialize a MIPS64 processor */ int mips64_init(cpu_mips_t *cpu); /* Delete a MIPS64 processor */ void mips64_delete(cpu_mips_t *cpu); /* Set the CPU PRID register */ void mips64_set_prid(cpu_mips_t *cpu,m_uint32_t prid); /* Set idle PC value */ void mips64_set_idle_pc(cpu_gen_t *cpu,m_uint64_t addr); /* Timer IRQ */ void *mips64_timer_irq_run(cpu_mips_t *cpu); /* Determine an "idling" PC */ int mips64_get_idling_pc(cpu_gen_t *cpu); /* Set an IRQ (VM IRQ standard routing) */ void mips64_vm_set_irq(vm_instance_t *vm,u_int irq); /* Clear an IRQ (VM IRQ standard routing) */ void mips64_vm_clear_irq(vm_instance_t *vm,u_int irq); /* Update the IRQ flag */ void mips64_update_irq_flag(cpu_mips_t *cpu); /* Generate an exception */ void mips64_trigger_exception(cpu_mips_t *cpu,u_int exc_code,int bd_slot); /* * Increment count register and trigger the timer IRQ if value in compare * register is the same. */ fastcall void mips64_exec_inc_cp0_cnt(cpu_mips_t *cpu); /* Trigger the Timer IRQ */ fastcall void mips64_trigger_timer_irq(cpu_mips_t *cpu); /* Execute ERET instruction */ fastcall void mips64_exec_eret(cpu_mips_t *cpu); /* Execute SYSCALL instruction */ fastcall void mips64_exec_syscall(cpu_mips_t *cpu); /* Execute BREAK instruction */ fastcall void mips64_exec_break(cpu_mips_t *cpu,u_int code); /* Trigger a Trap Exception */ fastcall void mips64_trigger_trap_exception(cpu_mips_t *cpu); /* Trigger IRQs */ fastcall void mips64_trigger_irq(cpu_mips_t *cpu); /* Set an IRQ */ void mips64_set_irq(cpu_mips_t *cpu,m_uint8_t irq); /* Clear an IRQ */ void mips64_clear_irq(cpu_mips_t *cpu,m_uint8_t irq); /* DMFC1 */ fastcall void mips64_exec_dmfc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg); /* DMTC1 */ fastcall void mips64_exec_dmtc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg); /* MFC1 */ fastcall void mips64_exec_mfc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg); /* MTC1 */ fastcall void mips64_exec_mtc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg); /* Virtual breakpoint */ fastcall void mips64_run_breakpoint(cpu_mips_t *cpu); /* Add a virtual breakpoint */ int mips64_add_breakpoint(cpu_gen_t *cpu,m_uint64_t pc); /* Remove a virtual breakpoint */ void mips64_remove_breakpoint(cpu_gen_t *cpu,m_uint64_t pc); /* Debugging for register-jump to address 0 */ fastcall void mips64_debug_jr0(cpu_mips_t *cpu); /* Set a register */ void mips64_reg_set(cpu_gen_t *cpu,u_int reg,m_uint64_t val); /* Dump registers of a MIPS64 processor */ void mips64_dump_regs(cpu_gen_t *cpu); /* Dump a memory block */ void mips64_dump_memory(cpu_mips_t *cpu,m_uint64_t vaddr,u_int count); /* Dump the stack */ void mips64_dump_stack(cpu_mips_t *cpu,u_int count); /* Save the CPU state into a file */ int mips64_save_state(cpu_mips_t *cpu,char *filename); /* Load a raw image into the simulated memory */ int mips64_load_raw_image(cpu_mips_t *cpu,char *filename,m_uint64_t vaddr); /* Load an ELF image into the simulated memory */ int mips64_load_elf_image(cpu_mips_t *cpu,char *filename,int skip_load, m_uint32_t *entry_point); /* Symbol lookup */ struct symbol *mips64_sym_lookup(cpu_mips_t *cpu,m_uint64_t addr); /* Insert a new symbol */ struct symbol *mips64_sym_insert(cpu_mips_t *cpu,char *name,m_uint64_t addr); /* Create the symbol tree */ int mips64_sym_create_tree(cpu_mips_t *cpu); /* Load a symbol file */ int mips64_sym_load_file(cpu_mips_t *cpu,char *filename); #endif dynamips-0.2.14/stable/mips64_amd64_trans.c000066400000000000000000002267011241034141600203700ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #include #include #include #include #include #include #include #include "cpu.h" #include "mips64_jit.h" #include "mips64_amd64_trans.h" #include "mips64_cp0.h" #include "memory.h" /* Macros for CPU structure access */ #define REG_OFFSET(reg) (OFFSET(cpu_mips_t,gpr[(reg)])) #define CP0_REG_OFFSET(c0reg) (OFFSET(cpu_mips_t,cp0.reg[(c0reg)])) #define MEMOP_OFFSET(op) (OFFSET(cpu_mips_t,mem_op_fn[(op)])) #define DECLARE_INSN(name) \ static int mips64_emit_##name(cpu_mips_t *cpu,mips64_jit_tcb_t *b, \ mips_insn_t insn) /* Set an IRQ */ void mips64_set_irq(cpu_mips_t *cpu,m_uint8_t irq) { m_uint32_t m; m = (1 << (irq + MIPS_CP0_CAUSE_ISHIFT)) & MIPS_CP0_CAUSE_IMASK; atomic_or(&cpu->irq_cause,m); } /* Clear an IRQ */ void mips64_clear_irq(cpu_mips_t *cpu,m_uint8_t irq) { m_uint32_t m; m = (1 << (irq + MIPS_CP0_CAUSE_ISHIFT)) & MIPS_CP0_CAUSE_IMASK; atomic_and(&cpu->irq_cause,~m); if (!cpu->irq_cause) cpu->irq_pending = 0; } /* Load a 64 bit immediate value */ static inline void mips64_load_imm(mips64_jit_tcb_t *b,u_int reg, m_uint64_t value) { if (value > 0xffffffffULL) amd64_mov_reg_imm_size(b->jit_ptr,reg,value,8); else amd64_mov_reg_imm(b->jit_ptr,reg,value); } /* Set the Pointer Counter (PC) register */ void mips64_set_pc(mips64_jit_tcb_t *b,m_uint64_t new_pc) { mips64_load_imm(b,AMD64_RAX,new_pc); amd64_mov_membase_reg(b->jit_ptr, AMD64_R15,OFFSET(cpu_mips_t,pc), AMD64_RAX,8); } /* Set the Return Address (RA) register */ void mips64_set_ra(mips64_jit_tcb_t *b,m_uint64_t ret_pc) { mips64_load_imm(b,AMD64_RAX,ret_pc); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15, REG_OFFSET(MIPS_GPR_RA), AMD64_RAX,8); } /* * Try to branch directly to the specified JIT block without returning to * main loop. */ static void mips64_try_direct_far_jump(cpu_mips_t *cpu,mips64_jit_tcb_t *b, m_uint64_t new_pc) { m_uint64_t new_page; m_uint32_t pc_hash,pc_offset; u_char *test1,*test2,*test3; new_page = new_pc & MIPS_MIN_PAGE_MASK; pc_offset = (new_pc & MIPS_MIN_PAGE_IMASK) >> 2; pc_hash = mips64_jit_get_pc_hash(new_pc); /* Get JIT block info in %rdx */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RBX, AMD64_R15,OFFSET(cpu_mips_t,exec_blk_map),8); amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX, AMD64_RBX,pc_hash*sizeof(void *),8); /* no JIT block found ? */ amd64_test_reg_reg(b->jit_ptr,AMD64_RDX,AMD64_RDX); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_Z, 0, 1); /* Check block IA */ mips64_load_imm(b,AMD64_RAX,new_page); amd64_alu_reg_membase_size(b->jit_ptr,X86_CMP,X86_EAX,AMD64_RDX, OFFSET(mips64_jit_tcb_t,start_pc),4); test2 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Jump to the code */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RSI, AMD64_RDX,OFFSET(mips64_jit_tcb_t,jit_insn_ptr),8); amd64_mov_reg_membase(b->jit_ptr,AMD64_RBX, AMD64_RSI,pc_offset * sizeof(void *),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RBX,AMD64_RBX); test3 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_Z, 0, 1); amd64_jump_reg(b->jit_ptr,AMD64_RBX); /* Returns to caller... */ amd64_patch(test1,b->jit_ptr); amd64_patch(test2,b->jit_ptr); amd64_patch(test3,b->jit_ptr); mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); } /* Set Jump */ static void mips64_set_jump(cpu_mips_t *cpu,mips64_jit_tcb_t *b, m_uint64_t new_pc,int local_jump) { int return_to_caller = FALSE; u_char *jump_ptr; if (cpu->sym_trace && !local_jump) return_to_caller = TRUE; if (!return_to_caller && mips64_jit_tcb_local_addr(b,new_pc,&jump_ptr)) { if (jump_ptr) { amd64_jump_code(b->jit_ptr,jump_ptr); } else { /* Never jump directly to code in a delay slot */ if (mips64_jit_is_delay_slot(b,new_pc)) { mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); return; } mips64_jit_tcb_record_patch(b,b->jit_ptr,new_pc); amd64_jump32(b->jit_ptr,0); } } else { if (cpu->exec_blk_direct_jump) { /* Block lookup optimization */ mips64_try_direct_far_jump(cpu,b,new_pc); } else { mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); } } } /* Basic C call */ static forced_inline void mips64_emit_basic_c_call(mips64_jit_tcb_t *b,void *f) { amd64_mov_reg_imm(b->jit_ptr,AMD64_RCX,f); amd64_call_reg(b->jit_ptr,AMD64_RCX); } /* Emit a simple call to a C function without any parameter */ static void mips64_emit_c_call(mips64_jit_tcb_t *b,void *f) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); amd64_mov_reg_imm(b->jit_ptr,AMD64_RCX,f); amd64_call_reg(b->jit_ptr,AMD64_RCX); } /* Single-step operation */ void mips64_emit_single_step(mips64_jit_tcb_t *b,mips_insn_t insn) { amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_mov_reg_imm(b->jit_ptr,AMD64_RSI,insn); mips64_emit_basic_c_call(b,mips64_exec_single_step); } /* Fast memory operation prototype */ typedef void (*memop_fast_access)(mips64_jit_tcb_t *b,int target); /* Fast LW */ static void mips64_memop_fast_lw(mips64_jit_tcb_t *b,int target) { amd64_mov_reg_memindex(b->jit_ptr,AMD64_RAX,AMD64_RBX,0,AMD64_RSI,0,4); amd64_bswap32(b->jit_ptr,X86_EAX); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RDX,X86_EAX); /* Save value in register */ amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(target),AMD64_RDX,8); } /* Fast SW */ static void mips64_memop_fast_sw(mips64_jit_tcb_t *b,int target) { /* Load value from register */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(target),4); amd64_bswap32(b->jit_ptr,X86_EAX); amd64_mov_memindex_reg(b->jit_ptr,AMD64_RBX,0,AMD64_RSI,0,AMD64_RAX,4); } /* Fast memory operation (64-bit) */ static void mips64_emit_memop_fast64(mips64_jit_tcb_t *b,int write_op, int opcode,int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { m_uint32_t val = sign_extend(offset,16); u_char *test1,*test2,*p_exit; test2 = NULL; /* RSI = GPR[base] + sign-extended offset */ mips64_load_imm(b,AMD64_RSI,val); amd64_alu_reg_membase(b->jit_ptr,X86_ADD, AMD64_RSI,AMD64_R15,REG_OFFSET(base)); /* RBX = mts64_entry index */ amd64_mov_reg_reg_size(b->jit_ptr,X86_EBX,X86_ESI,4); amd64_shift_reg_imm_size(b->jit_ptr,X86_SHR,X86_EBX,MTS64_HASH_SHIFT,4); amd64_alu_reg_imm_size(b->jit_ptr,X86_AND,X86_EBX,MTS64_HASH_MASK,4); /* RCX = mts32 entry */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX, AMD64_R15, OFFSET(cpu_mips_t,mts_u.mts64_cache),8); amd64_shift_reg_imm(b->jit_ptr,X86_SHL,AMD64_RBX,5); /* TO FIX */ amd64_alu_reg_reg(b->jit_ptr,X86_ADD,AMD64_RCX,AMD64_RBX); /* Compare virtual page address (EAX = vpage) */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RSI,8); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RAX,MIPS_MIN_PAGE_MASK); amd64_alu_reg_membase_size(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RCX, OFFSET(mts64_entry_t,gvpa),8); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); /* Test if we are writing to a COW page */ if (write_op) { amd64_test_membase_imm_size(b->jit_ptr, AMD64_RCX,OFFSET(mts64_entry_t,flags), MTS_FLAG_COW,4); test2 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); } /* ESI = offset in page, RBX = Host Page Address */ amd64_alu_reg_imm(b->jit_ptr,X86_AND,X86_ESI,MIPS_MIN_PAGE_IMASK); amd64_mov_reg_membase(b->jit_ptr,AMD64_RBX, AMD64_RCX,OFFSET(mts64_entry_t,hpa),8); /* Memory access */ op_handler(b,target); p_exit = b->jit_ptr; amd64_jump8(b->jit_ptr,0); if (test2) amd64_patch(test2,b->jit_ptr); /* === Slow lookup === */ amd64_patch(test1,b->jit_ptr); /* Save PC for exception handling */ mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); /* Sign-extend virtual address */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RSI,X86_ESI); /* RDX = target register */ amd64_mov_reg_imm(b->jit_ptr,AMD64_RDX,target); /* RDI = CPU instance */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); /* Call memory access function */ amd64_call_membase(b->jit_ptr,AMD64_R15,MEMOP_OFFSET(opcode)); amd64_patch(p_exit,b->jit_ptr); } /* Fast memory operation (32-bit) */ static void mips64_emit_memop_fast32(mips64_jit_tcb_t *b,int write_op, int opcode,int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { m_uint32_t val = sign_extend(offset,16); u_char *test1,*test2,*p_exit; test2 = NULL; /* ESI = GPR[base] + sign-extended offset */ amd64_mov_reg_imm(b->jit_ptr,X86_ESI,val); amd64_alu_reg_membase_size(b->jit_ptr,X86_ADD, X86_ESI,AMD64_R15,REG_OFFSET(base),4); /* RBX = mts32_entry index */ amd64_mov_reg_reg_size(b->jit_ptr,X86_EBX,X86_ESI,4); amd64_shift_reg_imm_size(b->jit_ptr,X86_SHR,X86_EBX,MTS32_HASH_SHIFT,4); amd64_alu_reg_imm_size(b->jit_ptr,X86_AND,X86_EBX,MTS32_HASH_MASK,4); /* RCX = mts32 entry */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX, AMD64_R15, OFFSET(cpu_mips_t,mts_u.mts32_cache),8); amd64_shift_reg_imm(b->jit_ptr,X86_SHL,AMD64_RBX,5); /* TO FIX */ amd64_alu_reg_reg(b->jit_ptr,X86_ADD,AMD64_RCX,AMD64_RBX); /* Compare virtual page address (EAX = vpage) */ amd64_mov_reg_reg(b->jit_ptr,X86_EAX,X86_ESI,4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,X86_EAX,MIPS_MIN_PAGE_MASK); amd64_alu_reg_membase_size(b->jit_ptr,X86_CMP,X86_EAX,AMD64_RCX, OFFSET(mts32_entry_t,gvpa),4); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); /* Test if we are writing to a COW page */ if (write_op) { amd64_test_membase_imm_size(b->jit_ptr, AMD64_RCX,OFFSET(mts32_entry_t,flags), MTS_FLAG_COW,4); test2 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); } /* ESI = offset in page, RBX = Host Page Address */ amd64_alu_reg_imm(b->jit_ptr,X86_AND,X86_ESI,MIPS_MIN_PAGE_IMASK); amd64_mov_reg_membase(b->jit_ptr,AMD64_RBX, AMD64_RCX,OFFSET(mts32_entry_t,hpa),8); /* Memory access */ op_handler(b,target); p_exit = b->jit_ptr; amd64_jump8(b->jit_ptr,0); /* === Slow lookup === */ amd64_patch(test1,b->jit_ptr); if (test2) amd64_patch(test2,b->jit_ptr); /* Save PC for exception handling */ mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); /* Sign-extend virtual address */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RSI,X86_ESI); /* RDX = target register */ amd64_mov_reg_imm(b->jit_ptr,AMD64_RDX,target); /* RDI = CPU instance */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); /* Call memory access function */ amd64_call_membase(b->jit_ptr,AMD64_R15,MEMOP_OFFSET(opcode)); amd64_patch(p_exit,b->jit_ptr); } /* Fast memory operation */ static void mips64_emit_memop_fast(cpu_mips_t *cpu,mips64_jit_tcb_t *b, int write_op,int opcode, int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { switch(cpu->addr_mode) { case 32: mips64_emit_memop_fast32(b,write_op,opcode,base,offset,target, keep_ll_bit,op_handler); break; case 64: mips64_emit_memop_fast64(b,write_op,opcode,base,offset,target, keep_ll_bit,op_handler); break; } } /* Memory operation */ static void mips64_emit_memop(mips64_jit_tcb_t *b,int op,int base,int offset, int target,int keep_ll_bit) { m_uint64_t val = sign_extend(offset,16); /* Save PC for exception handling */ mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); /* RDI = CPU instance */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); if (!keep_ll_bit) { amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_mov_membase_reg(b->jit_ptr,AMD64_RDI,OFFSET(cpu_mips_t,ll_bit), X86_ECX,4); } /* RSI = GPR[base] + sign-extended offset */ mips64_load_imm(b,AMD64_RSI,val); amd64_alu_reg_membase(b->jit_ptr,X86_ADD, AMD64_RSI,AMD64_RDI,REG_OFFSET(base)); /* RDX = target register */ amd64_mov_reg_imm(b->jit_ptr,AMD64_RDX,target); /* Call memory access function */ amd64_call_membase(b->jit_ptr,AMD64_RDI,MEMOP_OFFSET(op)); } /* Coprocessor Register transfert operation */ static void mips64_emit_cp_xfr_op(mips64_jit_tcb_t *b,int rt,int rd,void *f) { /* update pc */ mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); /* cp0 register */ amd64_mov_reg_imm(b->jit_ptr,AMD64_RDX,rd); /* gpr */ amd64_mov_reg_imm(b->jit_ptr,AMD64_RSI,rt); /* cpu instance */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_basic_c_call(b,f); } /* Virtual Breakpoint */ void mips64_emit_breakpoint(mips64_jit_tcb_t *b) { amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_c_call(b,mips64_run_breakpoint); } /* Unknown opcode handler */ static fastcall void mips64_unknown_opcode(cpu_mips_t *cpu,m_uint32_t opcode) { printf("CPU = %p\n",cpu); printf("MIPS64: unhandled opcode 0x%8.8x at 0x%llx (ra=0x%llx)\n", opcode,cpu->pc,cpu->gpr[MIPS_GPR_RA]); mips64_dump_regs(cpu->gen); } /* Emit unhandled instruction code */ static int mips64_emit_unknown(cpu_mips_t *cpu,mips64_jit_tcb_t *b, mips_insn_t opcode) { amd64_mov_reg_imm(b->jit_ptr,AMD64_RSI,opcode); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_c_call(b,mips64_unknown_opcode); return(0); } /* Invalid delay slot handler */ static fastcall void mips64_invalid_delay_slot(cpu_mips_t *cpu) { printf("MIPS64: invalid instruction in delay slot at 0x%llx (ra=0x%llx)\n", cpu->pc,cpu->gpr[MIPS_GPR_RA]); mips64_dump_regs(cpu->gen); /* Halt the virtual CPU */ cpu->pc = 0; } /* Emit unhandled instruction code */ int mips64_emit_invalid_delay_slot(mips64_jit_tcb_t *b) { amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_c_call(b,mips64_invalid_delay_slot); return(0); } /* * Increment count register and trigger the timer IRQ if value in compare * register is the same. */ void mips64_inc_cp0_count_reg(mips64_jit_tcb_t *b) { amd64_inc_membase(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,cp0_virt_cnt_reg)); #if 0 /* TIMER_IRQ */ u_char *test1; /* increment the virtual count register */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX, AMD64_R15,OFFSET(cpu_mips_t,cp0_virt_cnt_reg),4); amd64_inc_reg_size(b->jit_ptr,AMD64_RAX,4); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15, OFFSET(cpu_mips_t,cp0_virt_cnt_reg), AMD64_RAX,4); /* check with the virtual compare register */ amd64_alu_reg_membase_size(b->jit_ptr,X86_CMP,AMD64_RAX, AMD64_R15,OFFSET(cpu_mips_t,cp0_virt_cmp_reg),4); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* we have to trigger the timer irq */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_basic_c_call(b,mips64_trigger_timer_irq); amd64_patch(test1,b->jit_ptr); #endif } /* Check if there are pending IRQ */ void mips64_check_pending_irq(mips64_jit_tcb_t *b) { u_char *test1; /* Check the pending IRQ flag */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX, AMD64_R15,OFFSET(cpu_mips_t,irq_pending),4); amd64_test_reg_reg_size(b->jit_ptr,AMD64_RAX,AMD64_RAX,4); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_Z, 0, 1); /* Update PC */ mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); /* Trigger the IRQ */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_basic_c_call(b,mips64_trigger_irq); mips64_jit_tcb_push_epilog(b); amd64_patch(test1,b->jit_ptr); } /* Increment the number of executed instructions (performance debugging) */ void mips64_inc_perf_counter(mips64_jit_tcb_t *b) { amd64_inc_membase_size(b->jit_ptr, AMD64_R15,OFFSET(cpu_mips_t,perf_counter),4); } /* ADD */ DECLARE_INSN(ADD) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_ADD,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* ADDI */ DECLARE_INSN(ADDI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); /* TODO: Exception handling */ mips64_load_imm(b,AMD64_RAX,val); amd64_alu_reg_membase(b->jit_ptr,X86_ADD,AMD64_RAX, AMD64_R15,REG_OFFSET(rs)); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RAX,8); return(0); } /* ADDIU */ DECLARE_INSN(ADDIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); mips64_load_imm(b,AMD64_RAX,val); if (rs != 0) { amd64_alu_reg_membase(b->jit_ptr,X86_ADD,AMD64_RAX, AMD64_R15,REG_OFFSET(rs)); } amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RDX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RDX,8); return(0); } /* ADDU */ DECLARE_INSN(ADDU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_ADD,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* AND */ DECLARE_INSN(AND) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_AND,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* ANDI */ DECLARE_INSN(ANDI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); mips64_load_imm(b,AMD64_RAX,imm); amd64_alu_reg_membase(b->jit_ptr,X86_AND,AMD64_RAX, AMD64_R15,REG_OFFSET(rs)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RAX,8); return(0); } /* B (Branch, virtual instruction) */ DECLARE_INSN(B) { int offset = bits(insn,0,15); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); return(0); } /* BAL (Branch and Link, virtual instruction) */ DECLARE_INSN(BAL) { int offset = bits(insn,0,15); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,0); return(0); } /* BEQ (Branch On Equal) */ DECLARE_INSN(BEQ) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_CMP,AMD64_RAX, AMD64_R15,REG_OFFSET(rt)); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NE, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BEQL (Branch On Equal Likely) */ DECLARE_INSN(BEQL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_CMP,AMD64_RAX, AMD64_R15,REG_OFFSET(rt)); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NE, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BEQZ (Branch On Equal Zero) */ DECLARE_INSN(BEQZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] with 0. */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NZ, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BNEZ (Branch On Not Equal Zero) */ DECLARE_INSN(BNEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] with 0. */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_Z, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZ (Branch On Greater or Equal Than Zero) */ DECLARE_INSN(BGEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* If sign bit is set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZAL (Branch On Greater or Equal Than Zero And Link) */ DECLARE_INSN(BGEZAL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* If sign bit is set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZALL (Branch On Greater or Equal Than Zero And Link Likely) */ DECLARE_INSN(BGEZALL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* If sign bit is set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BGEZL (Branch On Greater or Equal Than Zero Likely) */ DECLARE_INSN(BGEZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* If sign bit is set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BGTZ (Branch On Greater Than Zero) */ DECLARE_INSN(BGTZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* compare reg to zero */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RCX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_LE, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGTZL (Branch On Greater Than Zero Likely) */ DECLARE_INSN(BGTZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* compare reg to zero */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RCX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_LE, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BLEZ (Branch On Less or Equal Than Zero) */ DECLARE_INSN(BLEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* compare reg to zero */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RCX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_GT, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLEZL (Branch On Less or Equal Than Zero Likely) */ DECLARE_INSN(BLEZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* compare reg to zero */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RCX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_GT, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BLTZ (Branch On Less Than Zero) */ DECLARE_INSN(BLTZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* If sign bit isn't set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLTZAL (Branch On Less Than Zero And Link) */ DECLARE_INSN(BLTZAL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* If sign bit isn't set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLTZALL (Branch On Less Than Zero And Link Likely) */ DECLARE_INSN(BLTZALL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* If sign bit isn't set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BLTZL (Branch On Less Than Zero Likely) */ DECLARE_INSN(BLTZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* If sign bit isn't set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BNE (Branch On Not Equal) */ DECLARE_INSN(BNE) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_CMP,AMD64_RAX, AMD64_R15,REG_OFFSET(rt)); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_E, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BNEL (Branch On Not Equal Likely) */ DECLARE_INSN(BNEL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_CMP,AMD64_RAX, AMD64_R15,REG_OFFSET(rt)); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_E, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BREAK */ DECLARE_INSN(BREAK) { u_int code = bits(insn,6,25); amd64_mov_reg_imm(b->jit_ptr,AMD64_RSI,code); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_basic_c_call(b,mips64_exec_break); mips64_jit_tcb_push_epilog(b); return(0); } /* CACHE */ DECLARE_INSN(CACHE) { int base = bits(insn,21,25); int op = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_CACHE,base,offset,op,0); return(0); } /* CFC0 */ DECLARE_INSN(CFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_cfc0); return(0); } /* CTC0 */ DECLARE_INSN(CTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_ctc0); return(0); } /* DADDIU */ DECLARE_INSN(DADDIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); mips64_load_imm(b,AMD64_RCX,val); amd64_alu_reg_membase(b->jit_ptr,X86_ADD,AMD64_RCX, AMD64_R15,REG_OFFSET(rs)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RCX,8); return(0); } /* DADDU: rd = rs + rt */ DECLARE_INSN(DADDU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_ADD,AMD64_RCX, AMD64_R15,REG_OFFSET(rt)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RCX,8); return(0); } /* DIV */ DECLARE_INSN(DIV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); /* eax = gpr[rs] */ amd64_clear_reg(b->jit_ptr,AMD64_RDX); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),4); /* ecx = gpr[rt] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rt),4); /* eax = quotient (LO), edx = remainder (HI) */ amd64_div_reg_size(b->jit_ptr,AMD64_RCX,1,4); /* store LO */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,lo), AMD64_RAX,8); /* store HI */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RDX,X86_EDX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,hi), AMD64_RDX,8); return(0); } /* DIVU */ DECLARE_INSN(DIVU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); /* eax = gpr[rs] */ amd64_clear_reg(b->jit_ptr,AMD64_RDX); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),4); /* ecx = gpr[rt] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rt),4); /* eax = quotient (LO), edx = remainder (HI) */ amd64_div_reg_size(b->jit_ptr,AMD64_RCX,0,4); /* store LO */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,lo), AMD64_RAX,8); /* store HI */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RDX,X86_EDX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,hi), AMD64_RDX,8); return(0); } /* DMFC0 */ DECLARE_INSN(DMFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_dmfc0); return(0); } /* DMFC1 */ DECLARE_INSN(DMFC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_dmfc1); return(0); } /* DMTC0 */ DECLARE_INSN(DMTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_dmtc0); return(0); } /* DMTC1 */ DECLARE_INSN(DMTC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_dmtc1); return(0); } /* DSLL */ DECLARE_INSN(DSLL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg_imm(b->jit_ptr,X86_SHL,AMD64_RAX,sa); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSLL32 */ DECLARE_INSN(DSLL32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg_imm(b->jit_ptr,X86_SHL,AMD64_RAX,sa+32); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSLLV */ DECLARE_INSN(DSLLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rs),4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RCX,0x3f); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg(b->jit_ptr,X86_SHL,AMD64_RAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSRA */ DECLARE_INSN(DSRA) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg_imm(b->jit_ptr,X86_SAR,AMD64_RAX,sa); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSRA32 */ DECLARE_INSN(DSRA32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg_imm(b->jit_ptr,X86_SAR,AMD64_RAX,sa+32); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSRAV */ DECLARE_INSN(DSRAV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rs),4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RCX,0x3f); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg(b->jit_ptr,X86_SAR,AMD64_RAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSRL */ DECLARE_INSN(DSRL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg_imm(b->jit_ptr,X86_SHR,AMD64_RAX,sa); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSRL32 */ DECLARE_INSN(DSRL32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg_imm(b->jit_ptr,X86_SHR,AMD64_RAX,sa+32); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSRLV */ DECLARE_INSN(DSRLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rs),4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RCX,0x3f); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg(b->jit_ptr,X86_SHR,AMD64_RAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSUBU: rd = rs - rt */ DECLARE_INSN(DSUBU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_SUB,AMD64_RAX, AMD64_R15,REG_OFFSET(rt)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* ERET */ DECLARE_INSN(ERET) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_basic_c_call(b,mips64_exec_eret); mips64_jit_tcb_push_epilog(b); return(0); } /* J (Jump) */ DECLARE_INSN(J) { u_int instr_index = bits(insn,0,25); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc &= ~((1 << 28) - 1); new_pc |= instr_index << 2; /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); return(0); } /* JAL (Jump And Link) */ DECLARE_INSN(JAL) { u_int instr_index = bits(insn,0,25); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc &= ~((1 << 28) - 1); new_pc |= instr_index << 2; /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,0); return(0); } /* JALR (Jump and Link Register) */ DECLARE_INSN(JALR) { int rs = bits(insn,21,25); int rd = bits(insn,11,15); m_uint64_t ret_pc; /* set the return pc (instruction after the delay slot) in GPR[rd] */ ret_pc = b->start_pc + ((b->mips_trans_pos + 1) << 2); mips64_load_imm(b,AMD64_RAX,ret_pc); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); /* get the new pc */ amd64_mov_reg_membase(b->jit_ptr,AMD64_R14,AMD64_R15,REG_OFFSET(rs),8); #if DEBUG_JR0 { u_char *test1; amd64_test_reg_reg(b->jit_ptr,AMD64_R14,AMD64_R14); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_c_call(b,mips64_debug_jr0); amd64_patch(test1,b->jit_ptr); } #endif /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc */ amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,pc), AMD64_R14,8); /* returns to the caller which will determine the next path */ mips64_jit_tcb_push_epilog(b); return(0); } /* JR (Jump Register) */ DECLARE_INSN(JR) { int rs = bits(insn,21,25); /* get the new pc */ amd64_mov_reg_membase(b->jit_ptr,AMD64_R14,AMD64_R15,REG_OFFSET(rs),8); #if DEBUG_JR0 { u_char *test1; amd64_test_reg_reg(b->jit_ptr,AMD64_RCX,AMD64_RCX); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_c_call(b,mips64_debug_jr0); amd64_patch(test1,b->jit_ptr); } #endif /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc */ amd64_mov_membase_reg(b->jit_ptr, AMD64_R15,OFFSET(cpu_mips_t,pc), AMD64_R14,8); /* returns to the caller which will determine the next path */ mips64_jit_tcb_push_epilog(b); return(0); } /* LB (Load Byte) */ DECLARE_INSN(LB) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LB,base,offset,rt,TRUE); return(0); } /* LBU (Load Byte Unsigned) */ DECLARE_INSN(LBU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LBU,base,offset,rt,TRUE); return(0); } /* LD (Load Double-Word) */ DECLARE_INSN(LD) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LD,base,offset,rt,TRUE); return(0); } /* LDC1 (Load Double-Word to Coprocessor 1) */ DECLARE_INSN(LDC1) { int base = bits(insn,21,25); int ft = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDC1,base,offset,ft,TRUE); return(0); } /* LDL (Load Double-Word Left) */ DECLARE_INSN(LDL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDL,base,offset,rt,TRUE); return(0); } /* LDR (Load Double-Word Right) */ DECLARE_INSN(LDR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDR,base,offset,rt,TRUE); return(0); } /* LH (Load Half-Word) */ DECLARE_INSN(LH) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LH,base,offset,rt,TRUE); return(0); } /* LHU (Load Half-Word Unsigned) */ DECLARE_INSN(LHU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LHU,base,offset,rt,TRUE); return(0); } /* LI (virtual) */ DECLARE_INSN(LI) { int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); mips64_load_imm(b,AMD64_RCX,val); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RCX,8); return(0); } /* LL (Load Linked) */ DECLARE_INSN(LL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LL,base,offset,rt,TRUE); return(0); } /* LUI */ DECLARE_INSN(LUI) { int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16) << 16; #if 1 mips64_load_imm(b,AMD64_RCX,val); #else amd64_mov_reg_imm(b->jit_ptr,AMD64_RCX,imm); amd64_shift_reg_imm(b->jit_ptr,X86_SHL,AMD64_RCX,48); amd64_shift_reg_imm(b->jit_ptr,X86_SAR,AMD64_RCX,32); #endif amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RCX,8); return(0); } /* LW (Load Word) */ DECLARE_INSN(LW) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,0,MIPS_MEMOP_LW,base,offset,rt,TRUE, mips64_memop_fast_lw); } else { mips64_emit_memop(b,MIPS_MEMOP_LW,base,offset,rt,TRUE); } return(0); } /* LWL (Load Word Left) */ DECLARE_INSN(LWL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LWL,base,offset,rt,TRUE); return(0); } /* LWR (Load Word Right) */ DECLARE_INSN(LWR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LWR,base,offset,rt,TRUE); return(0); } /* LWU (Load Word Unsigned) */ DECLARE_INSN(LWU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LWU,base,offset,rt,TRUE); return(0); } /* MFC0 */ DECLARE_INSN(MFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_mfc0); return(0); } /* MFC1 */ DECLARE_INSN(MFC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_mfc1); return(0); } /* MFHI */ DECLARE_INSN(MFHI) { int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX, AMD64_R15,OFFSET(cpu_mips_t,hi),8); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RDX,8); return(0); } /* MFLO */ DECLARE_INSN(MFLO) { int rd = bits(insn,11,15); if (!rd) return(0); amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX, AMD64_R15,OFFSET(cpu_mips_t,lo),8); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RDX,8); return(0); } /* MOVE (virtual instruction, real: ADDU) */ DECLARE_INSN(MOVE) { int rs = bits(insn,21,25); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX,AMD64_R15,REG_OFFSET(rs),4); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RDX,X86_EDX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RDX,8); return(0); } /* MTC0 */ DECLARE_INSN(MTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_mtc0); return(0); } /* MTC1 */ DECLARE_INSN(MTC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_mtc1); return(0); } /* MTHI */ DECLARE_INSN(MTHI) { int rs = bits(insn,21,25); amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX,AMD64_R15,REG_OFFSET(rs),8); amd64_mov_membase_reg(b->jit_ptr, AMD64_R15,OFFSET(cpu_mips_t,hi),AMD64_RDX,8); return(0); } /* MTLO */ DECLARE_INSN(MTLO) { int rs = bits(insn,21,25); amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX,AMD64_R15,REG_OFFSET(rs),8); amd64_mov_membase_reg(b->jit_ptr, AMD64_R15,OFFSET(cpu_mips_t,lo),AMD64_RDX,8); return(0); } /* MUL */ DECLARE_INSN(MUL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); /* eax = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),4); /* ecx = gpr[rt] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rt),4); amd64_mul_reg_size(b->jit_ptr,AMD64_RCX,1,4); /* store result in gpr[rd] */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* MULT */ DECLARE_INSN(MULT) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); /* eax = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),4); /* ecx = gpr[rt] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rt),4); amd64_mul_reg_size(b->jit_ptr,AMD64_RCX,1,4); /* store LO */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,lo), AMD64_RAX,8); /* store HI */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RDX,X86_EDX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,hi), AMD64_RDX,8); return(0); } /* MULTU */ DECLARE_INSN(MULTU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); /* eax = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),4); /* ecx = gpr[rt] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rt),4); amd64_mul_reg_size(b->jit_ptr,AMD64_RCX,0,4); /* store LO */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,lo), AMD64_RAX,8); /* store HI */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RDX,X86_EDX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,hi), AMD64_RDX,8); return(0); } /* NOP */ DECLARE_INSN(NOP) { return(0); } /* NOR */ DECLARE_INSN(NOR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_OR,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_not_reg(b->jit_ptr,AMD64_RAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* OR */ DECLARE_INSN(OR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_OR,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* ORI */ DECLARE_INSN(ORI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); mips64_load_imm(b,AMD64_RAX,imm); amd64_alu_reg_membase(b->jit_ptr,X86_OR,AMD64_RAX, AMD64_R15,REG_OFFSET(rs)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RAX,8); return(0); } /* PREF */ DECLARE_INSN(PREF) { amd64_nop(b->jit_ptr); return(0); } /* PREFI */ DECLARE_INSN(PREFI) { amd64_nop(b->jit_ptr); return(0); } /* SB (Store Byte) */ DECLARE_INSN(SB) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SB,base,offset,rt,FALSE); return(0); } /* SC (Store Conditional) */ DECLARE_INSN(SC) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SC,base,offset,rt,TRUE); return(0); } /* SD (Store Double-Word) */ DECLARE_INSN(SD) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SD,base,offset,rt,FALSE); return(0); } /* SDL (Store Double-Word Left) */ DECLARE_INSN(SDL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDL,base,offset,rt,FALSE); return(0); } /* SDR (Store Double-Word Right) */ DECLARE_INSN(SDR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDR,base,offset,rt,FALSE); return(0); } /* SDC1 (Store Double-Word from Coprocessor 1) */ DECLARE_INSN(SDC1) { int base = bits(insn,21,25); int ft = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDC1,base,offset,ft,FALSE); return(0); } /* SH (Store Half-Word) */ DECLARE_INSN(SH) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SH,base,offset,rt,FALSE); return(0); } /* SLL */ DECLARE_INSN(SLL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),4); amd64_shift_reg_imm(b->jit_ptr,X86_SHL,AMD64_RAX,sa); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SLLV */ DECLARE_INSN(SLLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rs),4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RCX,0x1f); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),4); amd64_shift_reg(b->jit_ptr,X86_SHL,AMD64_RAX); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SLT */ DECLARE_INSN(SLT) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); u_char *test1; /* RDX = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX,AMD64_R15,REG_OFFSET(rs),8); /* RAX = gpr[rt] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); /* we set rd to 1 when gpr[rs] < gpr[rt] */ amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RCX,8); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RDX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_GE, 0, 1); amd64_inc_membase(b->jit_ptr,AMD64_R15,REG_OFFSET(rd)); /* end */ amd64_patch(test1,b->jit_ptr); return(0); } /* SLTI */ DECLARE_INSN(SLTI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); u_char *test1; /* RDX = val */ mips64_load_imm(b,AMD64_RDX,val); /* RAX = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); /* we set rt to 1 when gpr[rs] < val */ amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RCX,8); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RDX); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_GE, 0, 1); amd64_inc_membase(b->jit_ptr,AMD64_R15,REG_OFFSET(rt)); /* end */ amd64_patch(test1,b->jit_ptr); return(0); } /* SLTU */ DECLARE_INSN(SLTU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); u_char *test1; /* RDX = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX,AMD64_R15,REG_OFFSET(rs),8); /* RAX = gpr[rt] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); /* we set rd to 1 when gpr[rs] < gpr[rt] */ amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RCX,8); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RDX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_AE, 0, 0); amd64_inc_membase(b->jit_ptr,AMD64_R15,REG_OFFSET(rd)); /* end */ amd64_patch(test1,b->jit_ptr); return(0); } /* SLTIU */ DECLARE_INSN(SLTIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); u_char *test1; /* RDX = val */ mips64_load_imm(b,AMD64_RDX,val); /* RAX = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); /* we set rt to 1 when gpr[rs] < val */ amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RCX,8); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RDX); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_AE, 0, 0); amd64_inc_membase(b->jit_ptr,AMD64_R15,REG_OFFSET(rt)); /* end */ amd64_patch(test1,b->jit_ptr); return(0); } /* SRA */ DECLARE_INSN(SRA) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),4); amd64_shift_reg_imm_size(b->jit_ptr,X86_SAR,AMD64_RAX,sa,4); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SRAV */ DECLARE_INSN(SRAV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rs),4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RCX,0x1f); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),4); amd64_shift_reg_size(b->jit_ptr,X86_SAR,AMD64_RAX,4); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SRL */ DECLARE_INSN(SRL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),4); amd64_shift_reg_imm(b->jit_ptr,X86_SHR,AMD64_RAX,sa); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SRLV */ DECLARE_INSN(SRLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rs),4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RCX,0x1f); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),4); amd64_shift_reg(b->jit_ptr,X86_SHR,AMD64_RAX); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SUB */ DECLARE_INSN(SUB) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); /* TODO: Exception handling */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_SUB,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SUBU */ DECLARE_INSN(SUBU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_SUB,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SW (Store Word) */ DECLARE_INSN(SW) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,1,MIPS_MEMOP_SW,base,offset,rt,FALSE, mips64_memop_fast_sw); } else { mips64_emit_memop(b,MIPS_MEMOP_SW,base,offset,rt,FALSE); } return(0); } /* SWL (Store Word Left) */ DECLARE_INSN(SWL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SWL,base,offset,rt,FALSE); return(0); } /* SWR (Store Word Right) */ DECLARE_INSN(SWR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SWR,base,offset,rt,FALSE); return(0); } /* SYNC */ DECLARE_INSN(SYNC) { return(0); } /* SYSCALL */ DECLARE_INSN(SYSCALL) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_basic_c_call(b,mips64_exec_syscall); mips64_jit_tcb_push_epilog(b); return(0); } /* TEQ (Trap If Equal) */ DECLARE_INSN(TEQ) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); u_char *test1; /* * compare gpr[rs] and gpr[rt]. */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_CMP,AMD64_RAX, AMD64_R15,REG_OFFSET(rt)); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Generate trap exception */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_c_call(b,mips64_trigger_trap_exception); mips64_jit_tcb_push_epilog(b); /* end */ amd64_patch(test1,b->jit_ptr); return(0); } /* TEQI (Trap If Equal Immediate) */ DECLARE_INSN(TEQI) { int rs = bits(insn,21,25); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); u_char *test1; /* RDX = val */ mips64_load_imm(b,AMD64_RDX,val); /* RAX = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RDX); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Generate trap exception */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_c_call(b,mips64_trigger_trap_exception); mips64_jit_tcb_push_epilog(b); /* end */ amd64_patch(test1,b->jit_ptr); return(0); } /* TLBP */ DECLARE_INSN(TLBP) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbp); return(0); } /* TLBR */ DECLARE_INSN(TLBR) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbr); return(0); } /* TLBWI */ DECLARE_INSN(TLBWI) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbwi); return(0); } /* TLBWR */ DECLARE_INSN(TLBWR) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbwr); return(0); } /* XOR */ DECLARE_INSN(XOR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_XOR,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* XORI */ DECLARE_INSN(XORI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); mips64_load_imm(b,AMD64_RAX,imm); amd64_alu_reg_membase(b->jit_ptr,X86_XOR,AMD64_RAX, AMD64_R15,REG_OFFSET(rs)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RAX,8); return(0); } /* MIPS instruction array */ struct mips64_insn_tag mips64_insn_tags[] = { { mips64_emit_LI , 0xffe00000 , 0x24000000, 1 }, /* virtual */ { mips64_emit_MOVE , 0xfc1f07ff , 0x00000021, 1 }, /* virtual */ { mips64_emit_B , 0xffff0000 , 0x10000000, 0 }, /* virtual */ { mips64_emit_BAL , 0xffff0000 , 0x04110000, 0 }, /* virtual */ { mips64_emit_BEQZ , 0xfc1f0000 , 0x10000000, 0 }, /* virtual */ { mips64_emit_BNEZ , 0xfc1f0000 , 0x14000000, 0 }, /* virtual */ { mips64_emit_ADD , 0xfc0007ff , 0x00000020, 1 }, { mips64_emit_ADDI , 0xfc000000 , 0x20000000, 1 }, { mips64_emit_ADDIU , 0xfc000000 , 0x24000000, 1 }, { mips64_emit_ADDU , 0xfc0007ff , 0x00000021, 1 }, { mips64_emit_AND , 0xfc0007ff , 0x00000024, 1 }, { mips64_emit_ANDI , 0xfc000000 , 0x30000000, 1 }, { mips64_emit_BEQ , 0xfc000000 , 0x10000000, 0 }, { mips64_emit_BEQL , 0xfc000000 , 0x50000000, 0 }, { mips64_emit_BGEZ , 0xfc1f0000 , 0x04010000, 0 }, { mips64_emit_BGEZAL , 0xfc1f0000 , 0x04110000, 0 }, { mips64_emit_BGEZALL , 0xfc1f0000 , 0x04130000, 0 }, { mips64_emit_BGEZL , 0xfc1f0000 , 0x04030000, 0 }, { mips64_emit_BGTZ , 0xfc1f0000 , 0x1c000000, 0 }, { mips64_emit_BGTZL , 0xfc1f0000 , 0x5c000000, 0 }, { mips64_emit_BLEZ , 0xfc1f0000 , 0x18000000, 0 }, { mips64_emit_BLEZL , 0xfc1f0000 , 0x58000000, 0 }, { mips64_emit_BLTZ , 0xfc1f0000 , 0x04000000, 0 }, { mips64_emit_BLTZAL , 0xfc1f0000 , 0x04100000, 0 }, { mips64_emit_BLTZALL , 0xfc1f0000 , 0x04120000, 0 }, { mips64_emit_BLTZL , 0xfc1f0000 , 0x04020000, 0 }, { mips64_emit_BNE , 0xfc000000 , 0x14000000, 0 }, { mips64_emit_BNEL , 0xfc000000 , 0x54000000, 0 }, { mips64_emit_BREAK , 0xfc00003f , 0x0000000d, 1 }, { mips64_emit_CACHE , 0xfc000000 , 0xbc000000, 1 }, { mips64_emit_CFC0 , 0xffe007ff , 0x40400000, 1 }, { mips64_emit_CTC0 , 0xffe007ff , 0x40600000, 1 }, { mips64_emit_DADDIU , 0xfc000000 , 0x64000000, 1 }, { mips64_emit_DADDU , 0xfc0007ff , 0x0000002d, 1 }, { mips64_emit_DIV , 0xfc00ffff , 0x0000001a, 1 }, { mips64_emit_DIVU , 0xfc00ffff , 0x0000001b, 1 }, { mips64_emit_DMFC0 , 0xffe007f8 , 0x40200000, 1 }, { mips64_emit_DMFC1 , 0xffe007ff , 0x44200000, 1 }, { mips64_emit_DMTC0 , 0xffe007f8 , 0x40a00000, 1 }, { mips64_emit_DMTC1 , 0xffe007ff , 0x44a00000, 1 }, { mips64_emit_DSLL , 0xffe0003f , 0x00000038, 1 }, { mips64_emit_DSLL32 , 0xffe0003f , 0x0000003c, 1 }, { mips64_emit_DSLLV , 0xfc0007ff , 0x00000014, 1 }, { mips64_emit_DSRA , 0xffe0003f , 0x0000003b, 1 }, { mips64_emit_DSRA32 , 0xffe0003f , 0x0000003f, 1 }, { mips64_emit_DSRAV , 0xfc0007ff , 0x00000017, 1 }, { mips64_emit_DSRL , 0xffe0003f , 0x0000003a, 1 }, { mips64_emit_DSRL32 , 0xffe0003f , 0x0000003e, 1 }, { mips64_emit_DSRLV , 0xfc0007ff , 0x00000016, 1 }, { mips64_emit_DSUBU , 0xfc0007ff , 0x0000002f, 1 }, { mips64_emit_ERET , 0xffffffff , 0x42000018, 0 }, { mips64_emit_J , 0xfc000000 , 0x08000000, 0 }, { mips64_emit_JAL , 0xfc000000 , 0x0c000000, 0 }, { mips64_emit_JALR , 0xfc1f003f , 0x00000009, 0 }, { mips64_emit_JR , 0xfc1ff83f , 0x00000008, 0 }, { mips64_emit_LB , 0xfc000000 , 0x80000000, 1 }, { mips64_emit_LBU , 0xfc000000 , 0x90000000, 1 }, { mips64_emit_LD , 0xfc000000 , 0xdc000000, 1 }, { mips64_emit_LDC1 , 0xfc000000 , 0xd4000000, 1 }, { mips64_emit_LDL , 0xfc000000 , 0x68000000, 1 }, { mips64_emit_LDR , 0xfc000000 , 0x6c000000, 1 }, { mips64_emit_LH , 0xfc000000 , 0x84000000, 1 }, { mips64_emit_LHU , 0xfc000000 , 0x94000000, 1 }, { mips64_emit_LL , 0xfc000000 , 0xc0000000, 1 }, { mips64_emit_LUI , 0xffe00000 , 0x3c000000, 1 }, { mips64_emit_LW , 0xfc000000 , 0x8c000000, 1 }, { mips64_emit_LWL , 0xfc000000 , 0x88000000, 1 }, { mips64_emit_LWR , 0xfc000000 , 0x98000000, 1 }, { mips64_emit_LWU , 0xfc000000 , 0x9c000000, 1 }, { mips64_emit_MFC0 , 0xffe007ff , 0x40000000, 1 }, { mips64_emit_CFC0 , 0xffe007ff , 0x40000001, 1 }, /* MFC0 / Set 1 */ { mips64_emit_MFC1 , 0xffe007ff , 0x44000000, 1 }, { mips64_emit_MFHI , 0xffff07ff , 0x00000010, 1 }, { mips64_emit_MFLO , 0xffff07ff , 0x00000012, 1 }, { mips64_emit_MTC0 , 0xffe007ff , 0x40800000, 1 }, { mips64_emit_MTC1 , 0xffe007ff , 0x44800000, 1 }, { mips64_emit_MTHI , 0xfc1fffff , 0x00000011, 1 }, { mips64_emit_MTLO , 0xfc1fffff , 0x00000013, 1 }, { mips64_emit_MUL , 0xfc0007ff , 0x70000002, 1 }, { mips64_emit_MULT , 0xfc00ffff , 0x00000018, 1 }, { mips64_emit_MULTU , 0xfc00ffff , 0x00000019, 1 }, { mips64_emit_NOP , 0xffffffff , 0x00000000, 1 }, { mips64_emit_NOR , 0xfc0007ff , 0x00000027, 1 }, { mips64_emit_OR , 0xfc0007ff , 0x00000025, 1 }, { mips64_emit_ORI , 0xfc000000 , 0x34000000, 1 }, { mips64_emit_PREF , 0xfc000000 , 0xcc000000, 1 }, { mips64_emit_PREFI , 0xfc0007ff , 0x4c00000f, 1 }, { mips64_emit_SB , 0xfc000000 , 0xa0000000, 1 }, { mips64_emit_SC , 0xfc000000 , 0xe0000000, 1 }, { mips64_emit_SD , 0xfc000000 , 0xfc000000, 1 }, { mips64_emit_SDC1 , 0xfc000000 , 0xf4000000, 1 }, { mips64_emit_SDL , 0xfc000000 , 0xb0000000, 1 }, { mips64_emit_SDR , 0xfc000000 , 0xb4000000, 1 }, { mips64_emit_SH , 0xfc000000 , 0xa4000000, 1 }, { mips64_emit_SLL , 0xffe0003f , 0x00000000, 1 }, { mips64_emit_SLLV , 0xfc0007ff , 0x00000004, 1 }, { mips64_emit_SLT , 0xfc0007ff , 0x0000002a, 1 }, { mips64_emit_SLTI , 0xfc000000 , 0x28000000, 1 }, { mips64_emit_SLTIU , 0xfc000000 , 0x2c000000, 1 }, { mips64_emit_SLTU , 0xfc0007ff , 0x0000002b, 1 }, { mips64_emit_SRA , 0xffe0003f , 0x00000003, 1 }, { mips64_emit_SRAV , 0xfc0007ff , 0x00000007, 1 }, { mips64_emit_SRL , 0xffe0003f , 0x00000002, 1 }, { mips64_emit_SRLV , 0xfc0007ff , 0x00000006, 1 }, { mips64_emit_SUB , 0xfc0007ff , 0x00000022, 1 }, { mips64_emit_SUBU , 0xfc0007ff , 0x00000023, 1 }, { mips64_emit_SW , 0xfc000000 , 0xac000000, 1 }, { mips64_emit_SWL , 0xfc000000 , 0xa8000000, 1 }, { mips64_emit_SWR , 0xfc000000 , 0xb8000000, 1 }, { mips64_emit_SYNC , 0xfffff83f , 0x0000000f, 1 }, { mips64_emit_SYSCALL , 0xfc00003f , 0x0000000c, 1 }, { mips64_emit_TEQ , 0xfc00003f , 0x00000034, 1 }, { mips64_emit_TEQI , 0xfc1f0000 , 0x040c0000, 1 }, { mips64_emit_TLBP , 0xffffffff , 0x42000008, 1 }, { mips64_emit_TLBR , 0xffffffff , 0x42000001, 1 }, { mips64_emit_TLBWI , 0xffffffff , 0x42000002, 1 }, { mips64_emit_TLBWR , 0xffffffff , 0x42000006, 1 }, { mips64_emit_XOR , 0xfc0007ff , 0x00000026, 1 }, { mips64_emit_XORI , 0xfc000000 , 0x38000000, 1 }, { mips64_emit_unknown , 0x00000000 , 0x00000000, 1 }, { NULL , 0x00000000 , 0x00000000, 0 }, }; dynamips-0.2.14/stable/mips64_amd64_trans.h000066400000000000000000000041461241034141600203720ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __MIPS64_AMD64_TRANS_H__ #define __MIPS64_AMD64_TRANS_H__ #include "utils.h" #include "amd64-codegen.h" #include "cpu.h" #include "dynamips.h" #include "mips64_exec.h" #define JIT_SUPPORT 1 /* Manipulate bitmasks atomically */ static forced_inline void atomic_or(m_uint32_t *v,m_uint32_t m) { __asm__ __volatile__("lock; orl %1,%0":"=m"(*v):"ir"(m),"m"(*v)); } static forced_inline void atomic_and(m_uint32_t *v,m_uint32_t m) { __asm__ __volatile__("lock; andl %1,%0":"=m"(*v):"ir"(m),"m"(*v)); } /* Wrappers to amd64-codegen functions */ #define mips64_jit_tcb_set_patch amd64_patch #define mips64_jit_tcb_set_jump amd64_jump_code /* MIPS instruction array */ extern struct mips64_insn_tag mips64_insn_tags[]; /* Push epilog for an amd64 instruction block */ static forced_inline void mips64_jit_tcb_push_epilog(mips64_jit_tcb_t *block) { amd64_ret(block->jit_ptr); } /* Execute JIT code */ static forced_inline void mips64_jit_tcb_exec(cpu_mips_t *cpu,mips64_jit_tcb_t *block) { insn_tblock_fptr jit_code; m_uint32_t offset; offset = (cpu->pc & MIPS_MIN_PAGE_IMASK) >> 2; jit_code = (insn_tblock_fptr)block->jit_insn_ptr[offset]; if (unlikely(!jit_code)) { mips64_exec_single_step(cpu,vmtoh32(block->mips_code[offset])); return; } asm volatile ("movq %0,%%r15"::"r"(cpu): "r14","r15","rax","rbx","rcx","rdx","rdi","rsi"); jit_code(); } static inline void amd64_patch(u_char *code,u_char *target) { /* Skip REX */ if ((code[0] >= 0x40) && (code[0] <= 0x4f)) code += 1; if ((code [0] & 0xf8) == 0xb8) { /* amd64_set_reg_template */ *(m_uint64_t *)(code + 1) = (m_uint64_t)target; } else if (code [0] == 0x8b) { /* mov 0(%rip), %dreg */ *(m_uint32_t *)(code + 2) = (m_uint32_t)(m_uint64_t)target - 7; } else if ((code [0] == 0xff) && (code [1] == 0x15)) { /* call *(%rip) */ *(m_uint32_t *)(code + 2) = ((m_uint32_t)(m_uint64_t)target) - 7; } else x86_patch(code,target); } #endif dynamips-0.2.14/stable/mips64_cp0.c000066400000000000000000000422271241034141600167270ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * MIPS Coprocessor 0 (System Coprocessor) implementation. * We don't use the JIT here, since there is no high performance needed. */ #include #include #include #include #include #include #include #include "device.h" #include "mips64.h" #include "mips64_cp0.h" #include "dynamips.h" #include "memory.h" /* MIPS cp0 registers names */ char *mips64_cp0_reg_names[MIPS64_CP0_REG_NR] = { "index" , "random", "entry_lo0", "entry_lo1", "context", "pagemask", "wired", "info", "badvaddr", "count", "entry_hi", "compare", "status", "cause", "epc", "prid", "config", "ll_addr", "watch_lo", "watch_hi", "xcontext", "cp0_r21", "cp0_r22", "cp0_r23", "cp0_r24", "cp0_r25", "ecc", "cache_err", "tag_lo", "tag_hi", "err_epc", "cp0_r31", }; /* Get cp0 register index given its name */ int mips64_cp0_get_reg_index(char *name) { int i; for(i=0;icp0; u_int cpu_mode; cpu_mode = cp0->reg[MIPS_CP0_STATUS] >> MIPS_CP0_STATUS_KSU_SHIFT; cpu_mode &= MIPS_CP0_STATUS_KSU_MASK; return(cpu_mode); } /* Get the CPU operating mode (User,Supervisor or Kernel) */ u_int mips64_cp0_get_mode(cpu_mips_t *cpu) { return(mips64_cp0_get_mode_inline(cpu)); } /* Check that we are running in kernel mode */ int mips64_cp0_check_kernel_mode(cpu_mips_t *cpu) { u_int cpu_mode; cpu_mode = mips64_cp0_get_mode(cpu); if (cpu_mode != MIPS_CP0_STATUS_KM) { /* XXX Branch delay slot */ mips64_trigger_exception(cpu,MIPS_CP0_CAUSE_ILLOP,0); return(1); } return(0); } /* Get value of random register */ static inline u_int mips64_cp0_get_random_reg(cpu_mips_t *cpu) { u_int wired; /* We use the virtual count register as a basic "random" value */ wired = cpu->cp0.reg[MIPS_CP0_WIRED]; return(wired + (cpu->cp0_virt_cnt_reg % (cpu->cp0.tlb_entries - wired))); } /* Get a cp0 register (fast version) */ static inline m_uint64_t mips64_cp0_get_reg_fast(cpu_mips_t *cpu,u_int cp0_reg) { mips_cp0_t *cp0 = &cpu->cp0; m_uint32_t delta,res; switch(cp0_reg) { case MIPS_CP0_COUNT: delta = cpu->cp0_virt_cmp_reg - cpu->cp0_virt_cnt_reg; res = (m_uint32_t)cp0->reg[MIPS_CP0_COMPARE]; res -= cpu->vm->clock_divisor * delta; return(sign_extend(res,32)); #if 1 case MIPS_CP0_COMPARE: return(sign_extend(cp0->reg[MIPS_CP0_COMPARE],32)); #else /* really useful and logical ? */ case MIPS_CP0_COMPARE: delta = cpu->cp0_virt_cmp_reg - cpu->cp0_virt_cnt_reg; res = (m_uint32_t)cp0->reg[MIPS_CP0_COUNT]; res += (cpu->vm->clock_divisor * delta); return(res); #endif case MIPS_CP0_INFO: return(MIPS64_R7000_TLB64_ENABLE); case MIPS_CP0_RANDOM: return(mips64_cp0_get_random_reg(cpu)); default: return(cp0->reg[cp0_reg]); } } /* Get a cp0 register */ m_uint64_t mips64_cp0_get_reg(cpu_mips_t *cpu,u_int cp0_reg) { return(mips64_cp0_get_reg_fast(cpu,cp0_reg)); } /* Set a cp0 register */ static inline void mips64_cp0_set_reg(cpu_mips_t *cpu,u_int cp0_reg, m_uint64_t val) { mips_cp0_t *cp0 = &cpu->cp0; m_uint32_t delta; switch(cp0_reg) { case MIPS_CP0_STATUS: case MIPS_CP0_CAUSE: cp0->reg[cp0_reg] = val; mips64_update_irq_flag(cpu); break; case MIPS_CP0_PAGEMASK: cp0->reg[cp0_reg] = val & MIPS_TLB_PAGE_MASK; break; case MIPS_CP0_COMPARE: mips64_clear_irq(cpu,7); mips64_update_irq_flag(cpu); cp0->reg[cp0_reg] = val; delta = val - cp0->reg[MIPS_CP0_COUNT]; cpu->cp0_virt_cnt_reg = 0; cpu->cp0_virt_cmp_reg = delta / cpu->vm->clock_divisor; break; case MIPS_CP0_COUNT: cp0->reg[cp0_reg] = val; delta = cp0->reg[MIPS_CP0_COMPARE] - val; cpu->cp0_virt_cnt_reg = 0; cpu->cp0_virt_cmp_reg = delta / cpu->vm->clock_divisor; break; case MIPS_CP0_TLB_HI: cp0->reg[cp0_reg] = val & MIPS_CP0_HI_SAFE_MASK; break; case MIPS_CP0_TLB_LO_0: case MIPS_CP0_TLB_LO_1: cp0->reg[cp0_reg] = val & MIPS_CP0_LO_SAFE_MASK; break; case MIPS_CP0_RANDOM: case MIPS_CP0_PRID: case MIPS_CP0_CONFIG: /* read only registers */ break; case MIPS_CP0_WIRED: cp0->reg[cp0_reg] = val & MIPS64_TLB_IDX_MASK; break; default: cp0->reg[cp0_reg] = val; } } /* Get a cp0 "set 1" register (R7000) */ m_uint64_t mips64_cp0_s1_get_reg(cpu_mips_t *cpu,u_int cp0_s1_reg) { switch(cp0_s1_reg) { case MIPS_CP0_S1_CONFIG: return(0x7F << 25); case MIPS_CP0_S1_IPLLO: return(cpu->cp0.ipl_lo); case MIPS_CP0_S1_IPLHI: return(cpu->cp0.ipl_hi); case MIPS_CP0_S1_INTCTL: return(cpu->cp0.int_ctl); case MIPS_CP0_S1_DERRADDR0: return(cpu->cp0.derraddr0); case MIPS_CP0_S1_DERRADDR1: return(cpu->cp0.derraddr1); default: /* undefined register */ cpu_log(cpu->gen,"CP0_S1","trying to read unknown register %u\n", cp0_s1_reg); return(0); } } /* Set a cp0 "set 1" register (R7000) */ static inline void mips64_cp0_s1_set_reg(cpu_mips_t *cpu,u_int cp0_s1_reg, m_uint64_t val) { mips_cp0_t *cp0 = &cpu->cp0; switch(cp0_s1_reg) { case MIPS_CP0_S1_IPLLO: cp0->ipl_lo = val; break; case MIPS_CP0_S1_IPLHI: cp0->ipl_hi = val; break; case MIPS_CP0_S1_INTCTL: cp0->int_ctl = val; break; case MIPS_CP0_S1_DERRADDR0: cp0->derraddr0 = val; break; case MIPS_CP0_S1_DERRADDR1: cp0->derraddr1 = val; break; default: cpu_log(cpu->gen, "CP0_S1","trying to set unknown register %u (val=0x%x)\n", cp0_s1_reg,val); } } /* DMFC0 */ fastcall void mips64_cp0_exec_dmfc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg) { cpu->gpr[gp_reg] = mips64_cp0_get_reg_fast(cpu,cp0_reg); } /* DMTC0 */ fastcall void mips64_cp0_exec_dmtc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg) { mips64_cp0_set_reg(cpu,cp0_reg,cpu->gpr[gp_reg]); } /* MFC0 */ fastcall void mips64_cp0_exec_mfc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg) { cpu->gpr[gp_reg] = sign_extend(mips64_cp0_get_reg_fast(cpu,cp0_reg),32); } /* MTC0 */ fastcall void mips64_cp0_exec_mtc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg) { mips64_cp0_set_reg(cpu,cp0_reg,cpu->gpr[gp_reg] & 0xffffffff); } /* CFC0 */ fastcall void mips64_cp0_exec_cfc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg) { cpu->gpr[gp_reg] = sign_extend(mips64_cp0_s1_get_reg(cpu,cp0_reg),32); } /* CTC0 */ fastcall void mips64_cp0_exec_ctc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg) { mips64_cp0_s1_set_reg(cpu,cp0_reg,cpu->gpr[gp_reg] & 0xffffffff); } /* Get the page size corresponding to a page mask */ static inline m_uint32_t get_page_size(m_uint32_t page_mask) { return((page_mask + 0x2000) >> 1); } /* Write page size in buffer */ static char *get_page_size_str(char *buffer,size_t len,m_uint32_t page_mask) { m_uint32_t page_size; page_size = get_page_size(page_mask); /* Mb ? */ if (page_size >= (1024*1024)) snprintf(buffer,len,"%uMB",page_size >> 20); else snprintf(buffer,len,"%uKB",page_size >> 10); return buffer; } /* Get the VPN2 mask */ static forced_inline m_uint64_t mips64_cp0_get_vpn2_mask(cpu_mips_t *cpu) { if (cpu->addr_mode == 64) return(MIPS_TLB_VPN2_MASK_64); else return(MIPS_TLB_VPN2_MASK_32); } /* TLB lookup */ int mips64_cp0_tlb_lookup(cpu_mips_t *cpu,m_uint64_t vaddr,mts_map_t *res) { mips_cp0_t *cp0 = &cpu->cp0; m_uint64_t vpn_addr,vpn2_mask; m_uint64_t page_mask,hi_addr; m_uint32_t page_size,pca; tlb_entry_t *entry; u_int asid; int i; vpn2_mask = mips64_cp0_get_vpn2_mask(cpu); vpn_addr = vaddr & vpn2_mask; asid = cp0->reg[MIPS_CP0_TLB_HI] & MIPS_TLB_ASID_MASK; for(i=0;itlb_entries;i++) { entry = &cp0->tlb[i]; page_mask = ~(entry->mask + 0x1FFF); hi_addr = entry->hi & vpn2_mask; if (((vpn_addr & page_mask) == hi_addr) && ((entry->hi & MIPS_TLB_G_MASK) || ((entry->hi & MIPS_TLB_ASID_MASK) == asid))) { page_size = get_page_size(entry->mask); if ((vaddr & page_size) == 0) { /* Even Page */ if (entry->lo0 & MIPS_TLB_V_MASK) { res->vaddr = vaddr & MIPS_MIN_PAGE_MASK; res->paddr = (entry->lo0 & MIPS_TLB_PFN_MASK) << 6; res->paddr += (res->vaddr & (page_size-1)); res->paddr &= cpu->addr_bus_mask; res->offset = vaddr & MIPS_MIN_PAGE_IMASK; pca = (entry->lo0 & MIPS_TLB_C_MASK); pca >>= MIPS_TLB_C_SHIFT; res->cached = mips64_cca_cached(pca); res->tlb_index = i; return(TRUE); } } else { /* Odd Page */ if (entry->lo1 & MIPS_TLB_V_MASK) { res->vaddr = vaddr & MIPS_MIN_PAGE_MASK; res->paddr = (entry->lo1 & MIPS_TLB_PFN_MASK) << 6; res->paddr += (res->vaddr & (page_size-1)); res->paddr &= cpu->addr_bus_mask; res->offset = vaddr & MIPS_MIN_PAGE_IMASK; pca = (entry->lo1 & MIPS_TLB_C_MASK); pca >>= MIPS_TLB_C_SHIFT; res->cached = mips64_cca_cached(pca); res->tlb_index = i; return(TRUE); } } /* Invalid entry */ return(FALSE); } } /* No matching entry */ return(FALSE); } /* * Map a TLB entry into the MTS. * * We apply the physical address bus masking here. * * TODO: - Manage ASID * - Manage CPU Mode (user,supervisor or kernel) */ void mips64_cp0_map_tlb_to_mts(cpu_mips_t *cpu,int index) { m_uint64_t v0_addr,v1_addr,p0_addr,p1_addr; m_uint32_t page_size,pca; tlb_entry_t *entry; int cacheable; entry = &cpu->cp0.tlb[index]; page_size = get_page_size(entry->mask); v0_addr = entry->hi & mips64_cp0_get_vpn2_mask(cpu); v1_addr = v0_addr + page_size; if (entry->lo0 & MIPS_TLB_V_MASK) { pca = (entry->lo0 & MIPS_TLB_C_MASK); pca >>= MIPS_TLB_C_SHIFT; cacheable = mips64_cca_cached(pca); p0_addr = (entry->lo0 & MIPS_TLB_PFN_MASK) << 6; cpu->mts_map(cpu,v0_addr,p0_addr & cpu->addr_bus_mask,page_size, cacheable,index); } if (entry->lo1 & MIPS_TLB_V_MASK) { pca = (entry->lo1 & MIPS_TLB_C_MASK); pca >>= MIPS_TLB_C_SHIFT; cacheable = mips64_cca_cached(pca); p1_addr = (entry->lo1 & MIPS_TLB_PFN_MASK) << 6; cpu->mts_map(cpu,v1_addr,p1_addr & cpu->addr_bus_mask,page_size, cacheable,index); } } /* * Unmap a TLB entry in the MTS. */ void mips64_cp0_unmap_tlb_to_mts(cpu_mips_t *cpu,int index) { m_uint64_t v0_addr,v1_addr; m_uint32_t page_size; tlb_entry_t *entry; entry = &cpu->cp0.tlb[index]; page_size = get_page_size(entry->mask); v0_addr = entry->hi & mips64_cp0_get_vpn2_mask(cpu); v1_addr = v0_addr + page_size; if (entry->lo0 & MIPS_TLB_V_MASK) cpu->mts_unmap(cpu,v0_addr,page_size,MTS_ACC_T,index); if (entry->lo1 & MIPS_TLB_V_MASK) cpu->mts_unmap(cpu,v1_addr,page_size,MTS_ACC_T,index); } /* Map all TLB entries into the MTS */ void mips64_cp0_map_all_tlb_to_mts(cpu_mips_t *cpu) { int i; for(i=0;icp0.tlb_entries;i++) mips64_cp0_map_tlb_to_mts(cpu,i); } /* TLBP: Probe a TLB entry */ fastcall void mips64_cp0_exec_tlbp(cpu_mips_t *cpu) { mips_cp0_t *cp0 = &cpu->cp0; m_uint64_t hi_reg,asid; m_uint64_t vpn2,vpn2_mask; tlb_entry_t *entry; int i; vpn2_mask = mips64_cp0_get_vpn2_mask(cpu); hi_reg = cp0->reg[MIPS_CP0_TLB_HI]; asid = hi_reg & MIPS_TLB_ASID_MASK; vpn2 = hi_reg & vpn2_mask; cp0->reg[MIPS_CP0_INDEX] = 0xffffffff80000000ULL; for(i=0;itlb_entries;i++) { entry = &cp0->tlb[i]; if (((entry->hi & vpn2_mask) == vpn2) && ((entry->hi & MIPS_TLB_G_MASK) || ((entry->hi & MIPS_TLB_ASID_MASK) == asid))) { cp0->reg[MIPS_CP0_INDEX] = i; #if DEBUG_TLB_ACTIVITY printf("CPU: CP0_TLBP returned %u\n",i); tlb_dump(cpu); #endif } } } /* TLBR: Read Indexed TLB entry */ fastcall void mips64_cp0_exec_tlbr(cpu_mips_t *cpu) { mips_cp0_t *cp0 = &cpu->cp0; tlb_entry_t *entry; u_int index; index = cp0->reg[MIPS_CP0_INDEX]; #if DEBUG_TLB_ACTIVITY cpu_log(cpu,"TLB","CP0_TLBR: reading entry %u.\n",index); #endif if (index < cp0->tlb_entries) { entry = &cp0->tlb[index]; cp0->reg[MIPS_CP0_PAGEMASK] = entry->mask; cp0->reg[MIPS_CP0_TLB_HI] = entry->hi; cp0->reg[MIPS_CP0_TLB_LO_0] = entry->lo0; cp0->reg[MIPS_CP0_TLB_LO_1] = entry->lo1; /* * The G bit must be reported in both Lo0 and Lo1 registers, * and cleared in Hi register. */ if (entry->hi & MIPS_TLB_G_MASK) { cp0->reg[MIPS_CP0_TLB_LO_0] |= MIPS_CP0_LO_G_MASK; cp0->reg[MIPS_CP0_TLB_LO_1] |= MIPS_CP0_LO_G_MASK; cp0->reg[MIPS_CP0_TLB_HI] &= ~MIPS_TLB_G_MASK; } } } /* TLBW: Write a TLB entry */ static inline void mips64_cp0_exec_tlbw(cpu_mips_t *cpu,u_int index) { mips_cp0_t *cp0 = &cpu->cp0; tlb_entry_t *entry; #if DEBUG_TLB_ACTIVITY cpu_log(cpu,"TLB","CP0_TLBWI: writing entry %u " "[mask=0x%8.8llx,hi=0x%8.8llx,lo0=0x%8.8llx,lo1=0x%8.8llx]\n", index,cp0->reg[MIPS_CP0_PAGEMASK],cp0->reg[MIPS_CP0_TLB_HI], cp0->reg[MIPS_CP0_TLB_LO_0],cp0->reg[MIPS_CP0_TLB_LO_1]); #endif if (index < cp0->tlb_entries) { entry = &cp0->tlb[index]; /* Unmap the old entry if it was valid */ mips64_cp0_unmap_tlb_to_mts(cpu,index); entry->mask = cp0->reg[MIPS_CP0_PAGEMASK] & MIPS_TLB_PAGE_MASK; entry->hi = cp0->reg[MIPS_CP0_TLB_HI] & ~entry->mask; entry->hi &= MIPS_CP0_HI_SAFE_MASK; /* clear G bit */ entry->lo0 = cp0->reg[MIPS_CP0_TLB_LO_0]; entry->lo1 = cp0->reg[MIPS_CP0_TLB_LO_1]; /* if G bit is set in lo0 and lo1, set it in hi */ if ((entry->lo0 & entry->lo1) & MIPS_CP0_LO_G_MASK) entry->hi |= MIPS_TLB_G_MASK; /* Clear G bit in TLB lo0 and lo1 */ entry->lo0 &= ~MIPS_CP0_LO_G_MASK; entry->lo1 &= ~MIPS_CP0_LO_G_MASK; /* Inform the MTS subsystem */ mips64_cp0_map_tlb_to_mts(cpu,index); #if DEBUG_TLB_ACTIVITY mips64_tlb_dump_entry(cpu,index); #endif } } /* TLBWI: Write Indexed TLB entry */ fastcall void mips64_cp0_exec_tlbwi(cpu_mips_t *cpu) { mips64_cp0_exec_tlbw(cpu,cpu->cp0.reg[MIPS_CP0_INDEX]); } /* TLBWR: Write Random TLB entry */ fastcall void mips64_cp0_exec_tlbwr(cpu_mips_t *cpu) { mips64_cp0_exec_tlbw(cpu,mips64_cp0_get_random_reg(cpu)); } /* Raw dump of the TLB */ void mips64_tlb_raw_dump(cpu_gen_t *cpu) { cpu_mips_t *mcpu = CPU_MIPS64(cpu); tlb_entry_t *entry; u_int i; printf("TLB dump:\n"); for(i=0;icp0.tlb_entries;i++) { entry = &mcpu->cp0.tlb[i]; printf(" %2d: mask=0x%16.16llx hi=0x%16.16llx " "lo0=0x%16.16llx lo1=0x%16.16llx\n", i, entry->mask, entry->hi, entry->lo0, entry->lo1); } printf("\n"); } /* Dump the specified TLB entry */ void mips64_tlb_dump_entry(cpu_mips_t *cpu,u_int index) { tlb_entry_t *entry; char buffer[256]; entry = &cpu->cp0.tlb[index]; /* virtual Address */ printf(" %2d: vaddr=0x%8.8llx ", index, entry->hi & mips64_cp0_get_vpn2_mask(cpu)); /* global or ASID */ if (entry->hi & MIPS_TLB_G_MASK) printf("(global) "); else printf("(asid 0x%2.2llx) ",entry->hi & MIPS_TLB_ASID_MASK); /* 1st page: Lo0 */ printf("p0="); if (entry->lo0 & MIPS_TLB_V_MASK) printf("0x%9.9llx",(entry->lo0 & MIPS_TLB_PFN_MASK) << 6); else printf("(invalid) "); printf(" %c ",(entry->lo0 & MIPS_TLB_D_MASK) ? 'D' : ' '); /* 2nd page: Lo1 */ printf("p1="); if (entry->lo1 & MIPS_TLB_V_MASK) printf("0x%9.9llx",(entry->lo1 & MIPS_TLB_PFN_MASK) << 6); else printf("(invalid) "); printf(" %c ",(entry->lo1 & MIPS_TLB_D_MASK) ? 'D' : ' '); /* page size */ printf(" (%s)\n",get_page_size_str(buffer,sizeof(buffer),entry->mask)); } /* Human-Readable dump of the TLB */ void mips64_tlb_dump(cpu_gen_t *cpu) { cpu_mips_t *mcpu = CPU_MIPS64(cpu); u_int i; printf("TLB dump:\n"); for(i=0;icp0.tlb_entries;i++) mips64_tlb_dump_entry(mcpu,i); printf("\n"); } dynamips-0.2.14/stable/mips64_cp0.h000066400000000000000000000036021241034141600167260ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __CP0_H__ #define __CP0_H__ #include "utils.h" /* CP0 register names */ extern char *mips64_cp0_reg_names[]; /* Get cp0 register index given its name */ int mips64_cp0_get_reg_index(char *name); /* Get the CPU operating mode (User,Supervisor or Kernel) */ u_int mips64_cp0_get_mode(cpu_mips_t *cpu); /* Get a cp0 register */ m_uint64_t mips64_cp0_get_reg(cpu_mips_t *cpu,u_int cp0_reg); /* DMFC0 */ fastcall void mips64_cp0_exec_dmfc0(cpu_mips_t *cpu,u_int gp_reg, u_int cp0_reg); /* DMTC0 */ fastcall void mips64_cp0_exec_dmtc0(cpu_mips_t *cpu,u_int gp_reg, u_int cp0_reg); /* MFC0 */ fastcall void mips64_cp0_exec_mfc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg); /* MTC0 */ fastcall void mips64_cp0_exec_mtc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg); /* CFC0 */ fastcall void mips64_cp0_exec_cfc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg); /* CTC0 */ fastcall void mips64_cp0_exec_ctc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg); /* TLB lookup */ int mips64_cp0_tlb_lookup(cpu_mips_t *cpu,m_uint64_t vaddr,mts_map_t *res); /* Map all TLB entries into the MTS */ void mips64_cp0_map_all_tlb_to_mts(cpu_mips_t *cpu); /* TLBP: Probe a TLB entry */ fastcall void mips64_cp0_exec_tlbp(cpu_mips_t *cpu); /* TLBR: Read Indexed TLB entry */ fastcall void mips64_cp0_exec_tlbr(cpu_mips_t *cpu); /* TLBWI: Write Indexed TLB entry */ fastcall void mips64_cp0_exec_tlbwi(cpu_mips_t *cpu); /* TLBWR: Write Random TLB entry */ fastcall void mips64_cp0_exec_tlbwr(cpu_mips_t *cpu); /* Raw dump of the TLB */ void mips64_tlb_raw_dump(cpu_gen_t *cpu); /* Dump the specified TLB entry */ void mips64_tlb_dump_entry(cpu_mips_t *cpu,u_int index); /* Human-Readable dump of the TLB */ void mips64_tlb_dump(cpu_gen_t *cpu); #endif dynamips-0.2.14/stable/mips64_exec.c000066400000000000000000001655441241034141600172010ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * MIPS64 Step-by-step execution. */ #if __GNUC__ > 2 #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "mips64_exec.h" #include "memory.h" #include "insn_lookup.h" #include "dynamips.h" /* Forward declaration of instruction array */ static struct mips64_insn_exec_tag mips64_exec_tags[]; static insn_lookup_t *ilt = NULL; /* ILT */ static forced_inline void *mips64_exec_get_insn(int index) { return(&mips64_exec_tags[index]); } static int mips64_exec_chk_lo(struct mips64_insn_exec_tag *tag,int value) { return((value & tag->mask) == (tag->value & 0xFFFF)); } static int mips64_exec_chk_hi(struct mips64_insn_exec_tag *tag,int value) { return((value & (tag->mask >> 16)) == (tag->value >> 16)); } /* Destroy instruction lookup table */ static void destroy_ilt(void) { assert(ilt); ilt_destroy(ilt); ilt = NULL; } /* Initialize instruction lookup table */ void mips64_exec_create_ilt(void) { int i,count; for(i=0,count=0;mips64_exec_tags[i].exec;i++) count++; ilt = ilt_create("mips64e",count, (ilt_get_insn_cbk_t)mips64_exec_get_insn, (ilt_check_cbk_t)mips64_exec_chk_lo, (ilt_check_cbk_t)mips64_exec_chk_hi); atexit(destroy_ilt); } /* Dump statistics */ void mips64_dump_stats(cpu_mips_t *cpu) { int i; #if NJM_STATS_ENABLE printf("\n"); for(i=0;mips64_exec_tags[i].exec;i++) printf(" * %-10s : %10llu\n", mips64_exec_tags[i].name,mips64_exec_tags[i].count); printf("%llu instructions executed since startup.\n",cpu->insn_exec_count); #else printf("Statistics support is not compiled in.\n"); #endif } /* Dump an instruction */ int mips64_dump_insn(char *buffer,size_t buf_size,size_t insn_name_size, m_uint64_t pc,mips_insn_t instruction) { char insn_name[64],insn_format[32],*name; int base,rs,rd,rt,sa,offset,imm; struct mips64_insn_exec_tag *tag; m_uint64_t new_pc; int index; /* Lookup for instruction */ index = ilt_lookup(ilt,instruction); tag = mips64_exec_get_insn(index); if (!tag) { snprintf(buffer,buf_size,"%8.8x (unknown)",instruction); return(-1); } if (!(name = tag->name)) name = "[unknown]"; if (!insn_name_size) insn_name_size = 10; snprintf(insn_format,sizeof(insn_format),"%%-%lus",(u_long)insn_name_size); snprintf(insn_name,sizeof(insn_name),insn_format,name); switch(tag->instr_type) { case 1: /* instructions without operands */ snprintf(buffer,buf_size,"%8.8x %s",instruction,insn_name); break; case 2: /* load/store instructions */ base = bits(instruction,21,25); rt = bits(instruction,16,20); offset = (m_int16_t)bits(instruction,0,15); snprintf(buffer,buf_size,"%8.8x %s %s,%d(%s)", instruction,insn_name,mips64_gpr_reg_names[rt], offset,mips64_gpr_reg_names[base]); break; case 3: /* GPR[rd] = GPR[rs] op GPR[rt] */ rs = bits(instruction,21,25); rt = bits(instruction,16,20); rd = bits(instruction,11,15); snprintf(buffer,buf_size,"%8.8x %s %s,%s,%s", instruction,insn_name,mips64_gpr_reg_names[rd], mips64_gpr_reg_names[rs],mips64_gpr_reg_names[rt]); break; case 4: /* GPR[rd] = GPR[rt] op GPR[rs] */ rs = bits(instruction,21,25); rt = bits(instruction,16,20); rd = bits(instruction,11,15); snprintf(buffer,buf_size,"%8.8x %s %s,%s,%s", instruction,insn_name,mips64_gpr_reg_names[rd], mips64_gpr_reg_names[rt],mips64_gpr_reg_names[rs]); break; case 5: /* GPR[rt] = GPR[rs] op immediate (hex) */ rs = bits(instruction,21,25); rt = bits(instruction,16,20); imm = bits(instruction,0,15); snprintf(buffer,buf_size,"%8.8x %s %s,%s,0x%x", instruction,insn_name,mips64_gpr_reg_names[rt], mips64_gpr_reg_names[rs],imm); break; case 6: /* GPR[rt] = GPR[rs] op immediate (dec) */ rs = bits(instruction,21,25); rt = bits(instruction,16,20); imm = bits(instruction,0,15); snprintf(buffer,buf_size,"%8.8x %s %s,%s,%d", instruction,insn_name,mips64_gpr_reg_names[rt], mips64_gpr_reg_names[rs],(m_int16_t)imm); break; case 7: /* GPR[rd] = GPR[rt] op sa */ rt = bits(instruction,16,20); rd = bits(instruction,11,15); sa = bits(instruction,6,10); snprintf(buffer,buf_size,"%8.8x %s %s,%s,%d", instruction,insn_name,mips64_gpr_reg_names[rd], mips64_gpr_reg_names[rt],sa); break; case 8: /* Branch with: GPR[rs] / GPR[rt] / offset */ rs = bits(instruction,21,25); rt = bits(instruction,16,20); offset = bits(instruction,0,15); new_pc = (pc + 4) + sign_extend(offset << 2,18); snprintf(buffer,buf_size,"%8.8x %s %s,%s,0x%llx", instruction,insn_name,mips64_gpr_reg_names[rs], mips64_gpr_reg_names[rt],new_pc); break; case 9: /* Branch with: GPR[rs] / offset */ rs = bits(instruction,21,25); offset = bits(instruction,0,15); new_pc = (pc + 4) + sign_extend(offset << 2,18); snprintf(buffer,buf_size,"%8.8x %s %s,0x%llx", instruction,insn_name,mips64_gpr_reg_names[rs],new_pc); break; case 10: /* Branch with: offset */ offset = bits(instruction,0,15); new_pc = (pc + 4) + sign_extend(offset << 2,18); snprintf(buffer,buf_size,"%8.8x %s 0x%llx", instruction,insn_name,new_pc); break; case 11: /* Jump */ offset = bits(instruction,0,25); new_pc = (pc & ~((1 << 28) - 1)) | (offset << 2); snprintf(buffer,buf_size,"%8.8x %s 0x%llx", instruction,insn_name,new_pc); break; case 13: /* op GPR[rs] */ rs = bits(instruction,21,25); snprintf(buffer,buf_size,"%8.8x %s %s", instruction,insn_name,mips64_gpr_reg_names[rs]); break; case 14: /* op GPR[rd] */ rd = bits(instruction,11,15); snprintf(buffer,buf_size,"%8.8x %s %s", instruction,insn_name,mips64_gpr_reg_names[rd]); break; case 15: /* op GPR[rd], GPR[rs] */ rs = bits(instruction,21,25); rd = bits(instruction,11,15); snprintf(buffer,buf_size,"%8.8x %s %s,%s", instruction,insn_name,mips64_gpr_reg_names[rd], mips64_gpr_reg_names[rs]); break; case 16: /* op GPR[rt], imm */ rt = bits(instruction,16,20); imm = bits(instruction,0,15); snprintf(buffer,buf_size,"%8.8x %s %s,0x%x", instruction,insn_name,mips64_gpr_reg_names[rt],imm); break; case 17: /* op GPR[rs], GPR[rt] */ rs = bits(instruction,21,25); rt = bits(instruction,16,20); snprintf(buffer,buf_size,"%8.8x %s %s,%s", instruction,insn_name,mips64_gpr_reg_names[rs], mips64_gpr_reg_names[rt]); break; case 18: /* op GPR[rt], CP0[rd] */ rt = bits(instruction,16,20); rd = bits(instruction,11,15); snprintf(buffer,buf_size,"%8.8x %s %s,%s", instruction,insn_name,mips64_gpr_reg_names[rt], mips64_cp0_reg_names[rd]); break; case 19: /* op GPR[rt], $rd */ rt = bits(instruction,16,20); rd = bits(instruction,11,15); snprintf(buffer,buf_size,"%8.8x %s %s,$%d", instruction,insn_name,mips64_gpr_reg_names[rt],rd); break; case 20: /* op GPR[rs], imm */ rs = bits(instruction,21,25); imm = bits(instruction,0,15); snprintf(buffer,buf_size,"%8.8x %s %s,0x%x", instruction,insn_name,mips64_gpr_reg_names[rs],imm); break; default: snprintf(buffer,buf_size,"%8.8x %s (TO DEFINE - %d)", instruction,insn_name,tag->instr_type); return(-1); } return(0); } /* Dump an instruction block */ void mips64_dump_insn_block(cpu_mips_t *cpu,m_uint64_t pc,u_int count, size_t insn_name_size) { mips_insn_t *ptr,insn; char buffer[80]; int i; for(i=0;imem_op_lookup(cpu,pc); insn = vmtoh32(*ptr); mips64_dump_insn(buffer,sizeof(buffer),insn_name_size,pc,insn); printf("0x%llx: %s\n",pc,buffer); pc += sizeof(mips_insn_t); } } /* Execute a memory operation */ _unused static forced_inline void mips64_exec_memop(cpu_mips_t *cpu,int memop, m_uint64_t vaddr,u_int dst_reg, int keep_ll_bit) { fastcall mips_memop_fn fn; if (!keep_ll_bit) cpu->ll_bit = 0; fn = cpu->mem_op_fn[memop]; fn(cpu,vaddr,dst_reg); } /* Execute a memory operation (2) */ static forced_inline void mips64_exec_memop2(cpu_mips_t *cpu,int memop, m_uint64_t base,int offset, u_int dst_reg,int keep_ll_bit) { m_uint64_t vaddr = cpu->gpr[base] + sign_extend(offset,16); fastcall mips_memop_fn fn; if (!keep_ll_bit) cpu->ll_bit = 0; fn = cpu->mem_op_fn[memop]; fn(cpu,vaddr,dst_reg); } /* Fetch an instruction */ static forced_inline int mips64_exec_fetch(cpu_mips_t *cpu,m_uint64_t pc, mips_insn_t *insn) { m_uint64_t exec_page; m_uint32_t offset; exec_page = pc & ~(m_uint64_t)MIPS_MIN_PAGE_IMASK; if (unlikely(exec_page != cpu->njm_exec_page)) { cpu->njm_exec_page = exec_page; cpu->njm_exec_ptr = cpu->mem_op_lookup(cpu,exec_page); } offset = (pc & MIPS_MIN_PAGE_IMASK) >> 2; *insn = vmtoh32(cpu->njm_exec_ptr[offset]); return(0); } /* Unknown opcode */ static fastcall int mips64_exec_unknown(cpu_mips_t *cpu,mips_insn_t insn) { printf("MIPS64: unknown opcode 0x%8.8x at pc = 0x%llx\n",insn,cpu->pc); mips64_dump_regs(cpu->gen); return(0); } /* Execute a single instruction */ static forced_inline int mips64_exec_single_instruction(cpu_mips_t *cpu,mips_insn_t instruction) { register fastcall int (*exec)(cpu_mips_t *,mips_insn_t) = NULL; struct mips64_insn_exec_tag *tag; int index; #if DEBUG_INSN_PERF_CNT cpu->perf_counter++; #endif /* Increment CP0 count register */ mips64_exec_inc_cp0_cnt(cpu); /* Lookup for instruction */ index = ilt_lookup(ilt,instruction); tag = mips64_exec_get_insn(index); exec = tag->exec; #if NJM_STATS_ENABLE cpu->insn_exec_count++; mips64_exec_tags[index].count++; #endif #if 0 { char buffer[80]; if (mips64_dump_insn(buffer,sizeof(buffer),0,cpu->pc,instruction)!=-1) cpu_log(cpu->gen,"EXEC","0x%llx: %s\n",cpu->pc,buffer); } #endif return(exec(cpu,instruction)); } /* Single-step execution */ fastcall void mips64_exec_single_step(cpu_mips_t *cpu,mips_insn_t instruction) { int res; res = mips64_exec_single_instruction(cpu,instruction); /* Normal flow ? */ if (likely(!res)) cpu->pc += 4; } /* Run MIPS code in step-by-step mode */ void *mips64_exec_run_cpu(cpu_gen_t *gen) { cpu_mips_t *cpu = CPU_MIPS64(gen); pthread_t timer_irq_thread; int timer_irq_check = 0; mips_insn_t insn; int res; if (pthread_create(&timer_irq_thread,NULL, (void *)mips64_timer_irq_run,cpu)) { fprintf(stderr,"VM '%s': unable to create Timer IRQ thread for CPU%u.\n", cpu->vm->name,gen->id); cpu_stop(gen); return NULL; } gen->cpu_thread_running = TRUE; cpu_exec_loop_set(gen); start_cpu: gen->idle_count = 0; for(;;) { if (unlikely(gen->state != CPU_STATE_RUNNING)) break; /* Handle virtual idle loop */ if (unlikely(cpu->pc == cpu->idle_pc)) { if (++gen->idle_count == gen->idle_max) { cpu_idle_loop(gen); gen->idle_count = 0; } } /* Handle the virtual CPU clock */ if (++timer_irq_check == cpu->timer_irq_check_itv) { timer_irq_check = 0; if (cpu->timer_irq_pending && !cpu->irq_disable) { mips64_trigger_timer_irq(cpu); mips64_trigger_irq(cpu); cpu->timer_irq_pending--; } } /* Reset "zero register" (for safety) */ cpu->gpr[0] = 0; /* Check IRQ */ if (unlikely(cpu->irq_pending)) { mips64_trigger_irq(cpu); continue; } /* Fetch and execute the instruction */ mips64_exec_fetch(cpu,cpu->pc,&insn); res = mips64_exec_single_instruction(cpu,insn); /* Normal flow ? */ if (likely(!res)) cpu->pc += sizeof(mips_insn_t); } if (!cpu->pc) { cpu_stop(gen); cpu_log(gen,"SLOW_EXEC","PC=0, halting CPU.\n"); } /* Check regularly if the CPU has been restarted */ while(gen->cpu_thread_running) { gen->seq_state++; switch(gen->state) { case CPU_STATE_RUNNING: gen->state = CPU_STATE_RUNNING; goto start_cpu; case CPU_STATE_HALTED: gen->cpu_thread_running = FALSE; pthread_join(timer_irq_thread,NULL); break; } /* CPU is paused */ usleep(200000); } return NULL; } /* Execute the instruction in delay slot */ static forced_inline void mips64_exec_bdslot(cpu_mips_t *cpu) { mips_insn_t insn; /* Fetch the instruction in delay slot */ mips64_exec_fetch(cpu,cpu->pc+4,&insn); /* Execute the instruction */ mips64_exec_single_instruction(cpu,insn); } /* ADD */ static fastcall int mips64_exec_ADD(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_uint64_t res; /* TODO: Exception handling */ res = (m_uint32_t)cpu->gpr[rs] + (m_uint32_t)cpu->gpr[rt]; cpu->gpr[rd] = sign_extend(res,32); return(0); } /* ADDI */ static fastcall int mips64_exec_ADDI(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t res,val = sign_extend(imm,16); /* TODO: Exception handling */ res = (m_uint32_t)cpu->gpr[rs] + val; cpu->gpr[rt] = sign_extend(res,32); return(0); } /* ADDIU */ static fastcall int mips64_exec_ADDIU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t res,val = sign_extend(imm,16); res = (m_uint32_t)cpu->gpr[rs] + val; cpu->gpr[rt] = sign_extend(res,32); return(0); } /* ADDU */ static fastcall int mips64_exec_ADDU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_uint32_t res; res = (m_uint32_t)cpu->gpr[rs] + (m_uint32_t)cpu->gpr[rt]; cpu->gpr[rd] = sign_extend(res,32); return(0); } /* AND */ static fastcall int mips64_exec_AND(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rs] & cpu->gpr[rt]; return(0); } /* ANDI */ static fastcall int mips64_exec_ANDI(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); cpu->gpr[rt] = cpu->gpr[rs] & imm; return(0); } /* B (Branch, virtual instruction) */ static fastcall int mips64_exec_B(cpu_mips_t *cpu,mips_insn_t insn) { int offset = bits(insn,0,15); m_uint64_t new_pc; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* set the new pc in cpu structure */ cpu->pc = new_pc; return(1); } /* BAL (Branch And Link, virtual instruction) */ static fastcall int mips64_exec_BAL(cpu_mips_t *cpu,mips_insn_t insn) { int offset = bits(insn,0,15); m_uint64_t new_pc; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ cpu->gpr[MIPS_GPR_RA] = cpu->pc + 8; /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* set the new pc in cpu structure */ cpu->pc = new_pc; return(1); } /* BEQ (Branch On Equal) */ static fastcall int mips64_exec_BEQ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] == gpr[rt] */ res = (cpu->gpr[rs] == cpu->gpr[rt]); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BEQL (Branch On Equal Likely) */ static fastcall int mips64_exec_BEQL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] == gpr[rt] */ res = (cpu->gpr[rs] == cpu->gpr[rt]); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BEQZ (Branch On Equal Zero) - Virtual Instruction */ static fastcall int mips64_exec_BEQZ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] == 0 */ res = (cpu->gpr[rs] == 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BNEZ (Branch On Not Equal Zero) - Virtual Instruction */ static fastcall int mips64_exec_BNEZ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] != 0 */ res = (cpu->gpr[rs] != 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BGEZ (Branch On Greater or Equal Than Zero) */ static fastcall int mips64_exec_BGEZ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] >= 0 */ res = ((m_int64_t)cpu->gpr[rs] >= 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BGEZAL (Branch On Greater or Equal Than Zero And Link) */ static fastcall int mips64_exec_BGEZAL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ cpu->gpr[MIPS_GPR_RA] = cpu->pc + 8; /* take the branch if gpr[rs] >= 0 */ res = ((m_int64_t)cpu->gpr[rs] >= 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BGEZALL (Branch On Greater or Equal Than Zero And Link Likely) */ static fastcall int mips64_exec_BGEZALL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ cpu->gpr[MIPS_GPR_RA] = cpu->pc + 8; /* take the branch if gpr[rs] >= 0 */ res = ((m_int64_t)cpu->gpr[rs] >= 0); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BGEZL (Branch On Greater or Equal Than Zero Likely) */ static fastcall int mips64_exec_BGEZL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] >= 0 */ res = ((m_int64_t)cpu->gpr[rs] >= 0); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BGTZ (Branch On Greater Than Zero) */ static fastcall int mips64_exec_BGTZ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] > 0 */ res = ((m_int64_t)cpu->gpr[rs] > 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BGTZL (Branch On Greater Than Zero Likely) */ static fastcall int mips64_exec_BGTZL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] > 0 */ res = ((m_int64_t)cpu->gpr[rs] > 0); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BLEZ (Branch On Less or Equal Than Zero) */ static fastcall int mips64_exec_BLEZ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] <= 0 */ res = ((m_int64_t)cpu->gpr[rs] <= 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BLEZL (Branch On Less or Equal Than Zero Likely) */ static fastcall int mips64_exec_BLEZL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] <= 0 */ res = ((m_int64_t)cpu->gpr[rs] <= 0); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BLTZ (Branch On Less Than Zero) */ static fastcall int mips64_exec_BLTZ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] < 0 */ res = ((m_int64_t)cpu->gpr[rs] < 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BLTZAL (Branch On Less Than Zero And Link) */ static fastcall int mips64_exec_BLTZAL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ cpu->gpr[MIPS_GPR_RA] = cpu->pc + 8; /* take the branch if gpr[rs] < 0 */ res = ((m_int64_t)cpu->gpr[rs] < 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BLTZALL (Branch On Less Than Zero And Link Likely) */ static fastcall int mips64_exec_BLTZALL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ cpu->gpr[MIPS_GPR_RA] = cpu->pc + 8; /* take the branch if gpr[rs] < 0 */ res = ((m_int64_t)cpu->gpr[rs] < 0); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BLTZL (Branch On Less Than Zero Likely) */ static fastcall int mips64_exec_BLTZL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] < 0 */ res = ((m_int64_t)cpu->gpr[rs] < 0); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BNE (Branch On Not Equal) */ static fastcall int mips64_exec_BNE(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] != gpr[rt] */ res = (cpu->gpr[rs] != cpu->gpr[rt]); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BNEL (Branch On Not Equal Likely) */ static fastcall int mips64_exec_BNEL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] != gpr[rt] */ res = (cpu->gpr[rs] != cpu->gpr[rt]); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BREAK */ static fastcall int mips64_exec_BREAK(cpu_mips_t *cpu,mips_insn_t insn) { u_int code = bits(insn,6,25); mips64_exec_break(cpu,code); return(1); } /* CACHE */ static fastcall int mips64_exec_CACHE(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int op = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_CACHE,base,offset,op,FALSE); return(0); } /* CFC0 */ static fastcall int mips64_exec_CFC0(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_cp0_exec_cfc0(cpu,rt,rd); return(0); } /* CTC0 */ static fastcall int mips64_exec_CTC0(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_cp0_exec_ctc0(cpu,rt,rd); return(0); } /* DADDIU */ static fastcall int mips64_exec_DADDIU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); cpu->gpr[rt] = cpu->gpr[rs] + val; return(0); } /* DADDU: rd = rs + rt */ static fastcall int mips64_exec_DADDU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rs] + cpu->gpr[rt]; return(0); } /* DIV */ static fastcall int mips64_exec_DIV(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); cpu->lo = (m_int32_t)cpu->gpr[rs] / (m_int32_t)cpu->gpr[rt]; cpu->hi = (m_int32_t)cpu->gpr[rs] % (m_int32_t)cpu->gpr[rt]; cpu->lo = sign_extend(cpu->lo,32); cpu->hi = sign_extend(cpu->hi,32); return(0); } /* DIVU */ static fastcall int mips64_exec_DIVU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); if (cpu->gpr[rt] == 0) return(0); cpu->lo = (m_uint32_t)cpu->gpr[rs] / (m_uint32_t)cpu->gpr[rt]; cpu->hi = (m_uint32_t)cpu->gpr[rs] % (m_uint32_t)cpu->gpr[rt]; cpu->lo = sign_extend(cpu->lo,32); cpu->hi = sign_extend(cpu->hi,32); return(0); } /* DMFC0 */ static fastcall int mips64_exec_DMFC0(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_cp0_exec_dmfc0(cpu,rt,rd); return(0); } /* DMFC1 */ static fastcall int mips64_exec_DMFC1(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_exec_dmfc1(cpu,rt,rd); return(0); } /* DMTC0 */ static fastcall int mips64_exec_DMTC0(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_cp0_exec_dmtc0(cpu,rt,rd); return(0); } /* DMTC1 */ static fastcall int mips64_exec_DMTC1(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_exec_dmtc1(cpu,rt,rd); return(0); } /* DSLL */ static fastcall int mips64_exec_DSLL(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); cpu->gpr[rd] = cpu->gpr[rt] << sa; return(0); } /* DSLL32 */ static fastcall int mips64_exec_DSLL32(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); cpu->gpr[rd] = cpu->gpr[rt] << (32 + sa); return(0); } /* DSLLV */ static fastcall int mips64_exec_DSLLV(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rt] << (cpu->gpr[rs] & 0x3f); return(0); } /* DSRA */ static fastcall int mips64_exec_DSRA(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); cpu->gpr[rd] = (m_int64_t)cpu->gpr[rt] >> sa; return(0); } /* DSRA32 */ static fastcall int mips64_exec_DSRA32(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); cpu->gpr[rd] = (m_int64_t)cpu->gpr[rt] >> (32 + sa); return(0); } /* DSRAV */ static fastcall int mips64_exec_DSRAV(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = (m_int64_t)cpu->gpr[rt] >> (cpu->gpr[rs] & 0x3f); return(0); } /* DSRL */ static fastcall int mips64_exec_DSRL(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); cpu->gpr[rd] = cpu->gpr[rt] >> sa; return(0); } /* DSRL32 */ static fastcall int mips64_exec_DSRL32(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); cpu->gpr[rd] = cpu->gpr[rt] >> (32 + sa); return(0); } /* DSRLV */ static fastcall int mips64_exec_DSRLV(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rt] >> (cpu->gpr[rs] & 0x3f); return(0); } /* DSUBU */ static fastcall int mips64_exec_DSUBU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rs] - cpu->gpr[rt]; return(0); } /* ERET */ static fastcall int mips64_exec_ERET(cpu_mips_t *cpu,mips_insn_t insn) { mips64_exec_eret(cpu); return(1); } /* J */ static fastcall int mips64_exec_J(cpu_mips_t *cpu,mips_insn_t insn) { u_int instr_index = bits(insn,0,25); m_uint64_t new_pc; /* compute the new pc */ new_pc = cpu->pc & ~((1 << 28) - 1); new_pc |= instr_index << 2; /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* set the new pc */ cpu->pc = new_pc; return(1); } /* JAL */ static fastcall int mips64_exec_JAL(cpu_mips_t *cpu,mips_insn_t insn) { u_int instr_index = bits(insn,0,25); m_uint64_t new_pc; /* compute the new pc */ new_pc = cpu->pc & ~((1 << 28) - 1); new_pc |= instr_index << 2; /* set the return address (instruction after the delay slot) */ cpu->gpr[MIPS_GPR_RA] = cpu->pc + 8; /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* set the new pc */ cpu->pc = new_pc; return(1); } /* JALR */ static fastcall int mips64_exec_JALR(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rd = bits(insn,11,15); m_uint64_t new_pc; /* set the return pc (instruction after the delay slot) in GPR[rd] */ cpu->gpr[rd] = cpu->pc + 8; /* get the new pc */ new_pc = cpu->gpr[rs]; /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* set the new pc */ cpu->pc = new_pc; return(1); } /* JR */ static fastcall int mips64_exec_JR(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); m_uint64_t new_pc; /* get the new pc */ new_pc = cpu->gpr[rs]; /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* set the new pc */ cpu->pc = new_pc; return(1); } /* LB (Load Byte) */ static fastcall int mips64_exec_LB(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LB,base,offset,rt,TRUE); return(0); } /* LBU (Load Byte Unsigned) */ static fastcall int mips64_exec_LBU(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LBU,base,offset,rt,TRUE); return(0); } /* LD (Load Double-Word) */ static fastcall int mips64_exec_LD(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LD,base,offset,rt,TRUE); return(0); } /* LDC1 (Load Double-Word to Coprocessor 1) */ static fastcall int mips64_exec_LDC1(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int ft = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LDC1,base,offset,ft,TRUE); return(0); } /* LDL (Load Double-Word Left) */ static fastcall int mips64_exec_LDL(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LDL,base,offset,rt,TRUE); return(0); } /* LDR (Load Double-Word Right) */ static fastcall int mips64_exec_LDR(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LDR,base,offset,rt,TRUE); return(0); } /* LH (Load Half-Word) */ static fastcall int mips64_exec_LH(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LH,base,offset,rt,TRUE); return(0); } /* LHU (Load Half-Word Unsigned) */ static fastcall int mips64_exec_LHU(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LHU,base,offset,rt,TRUE); return(0); } /* LI (virtual) */ static fastcall int mips64_exec_LI(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int imm = bits(insn,0,15); cpu->gpr[rt] = sign_extend(imm,16); return(0); } /* LL (Load Linked) */ static fastcall int mips64_exec_LL(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LL,base,offset,rt,TRUE); return(0); } /* LUI */ static fastcall int mips64_exec_LUI(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int imm = bits(insn,0,15); cpu->gpr[rt] = sign_extend(imm,16) << 16; return(0); } /* LW (Load Word) */ static fastcall int mips64_exec_LW(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LW,base,offset,rt,TRUE); return(0); } /* LWL (Load Word Left) */ static fastcall int mips64_exec_LWL(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LWL,base,offset,rt,TRUE); return(0); } /* LWR (Load Word Right) */ static fastcall int mips64_exec_LWR(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LWR,base,offset,rt,TRUE); return(0); } /* LWU (Load Word Unsigned) */ static fastcall int mips64_exec_LWU(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LWU,base,offset,rt,TRUE); return(0); } /* MFC0 */ static fastcall int mips64_exec_MFC0(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_cp0_exec_mfc0(cpu,rt,rd); return(0); } /* MFC1 */ static fastcall int mips64_exec_MFC1(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_exec_mfc1(cpu,rt,rd); return(0); } /* MFHI */ static fastcall int mips64_exec_MFHI(cpu_mips_t *cpu,mips_insn_t insn) { int rd = bits(insn,11,15); if (rd) cpu->gpr[rd] = cpu->hi; return(0); } /* MFLO */ static fastcall int mips64_exec_MFLO(cpu_mips_t *cpu,mips_insn_t insn) { int rd = bits(insn,11,15); if (rd) cpu->gpr[rd] = cpu->lo; return(0); } /* MOVE (virtual instruction, real: ADDU) */ static fastcall int mips64_exec_MOVE(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rd = bits(insn,11,15); cpu->gpr[rd] = sign_extend(cpu->gpr[rs],32); return(0); } /* MTC0 */ static fastcall int mips64_exec_MTC0(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_cp0_exec_mtc0(cpu,rt,rd); return(0); } /* MTC1 */ static fastcall int mips64_exec_MTC1(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_exec_mtc1(cpu,rt,rd); return(0); } /* MTHI */ static fastcall int mips64_exec_MTHI(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); cpu->hi = cpu->gpr[rs]; return(0); } /* MTLO */ static fastcall int mips64_exec_MTLO(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); cpu->lo = cpu->gpr[rs]; return(0); } /* MUL */ static fastcall int mips64_exec_MUL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_int32_t val; /* note: after this instruction, HI/LO regs are undefined */ val = (m_int32_t)cpu->gpr[rs] * (m_int32_t)cpu->gpr[rt]; cpu->gpr[rd] = sign_extend(val,32); return(0); } /* MULT */ static fastcall int mips64_exec_MULT(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); m_int64_t val; val = (m_int64_t)(m_int32_t)cpu->gpr[rs]; val *= (m_int64_t)(m_int32_t)cpu->gpr[rt]; cpu->lo = sign_extend(val,32); cpu->hi = sign_extend(val >> 32,32); return(0); } /* MULTU */ static fastcall int mips64_exec_MULTU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); m_uint64_t val; val = (m_uint64_t)(m_uint32_t)cpu->gpr[rs]; val *= (m_uint64_t)(m_uint32_t)cpu->gpr[rt]; cpu->lo = sign_extend(val,32); cpu->hi = sign_extend(val >> 32,32); return(0); } /* NOP */ static fastcall int mips64_exec_NOP(cpu_mips_t *cpu,mips_insn_t insn) { return(0); } /* NOR */ static fastcall int mips64_exec_NOR(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = ~(cpu->gpr[rs] | cpu->gpr[rt]); return(0); } /* OR */ static fastcall int mips64_exec_OR(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rs] | cpu->gpr[rt]; return(0); } /* ORI */ static fastcall int mips64_exec_ORI(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); cpu->gpr[rt] = cpu->gpr[rs] | imm; return(0); } /* PREF */ static fastcall int mips64_exec_PREF(cpu_mips_t *cpu,mips_insn_t insn) { return(0); } /* PREFI */ static fastcall int mips64_exec_PREFI(cpu_mips_t *cpu,mips_insn_t insn) { return(0); } /* SB (Store Byte) */ static fastcall int mips64_exec_SB(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SB,base,offset,rt,FALSE); return(0); } /* SC (Store Conditional) */ static fastcall int mips64_exec_SC(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SC,base,offset,rt,TRUE); return(0); } /* SD (Store Double-Word) */ static fastcall int mips64_exec_SD(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SD,base,offset,rt,FALSE); return(0); } /* SDL (Store Double-Word Left) */ static fastcall int mips64_exec_SDL(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SDL,base,offset,rt,FALSE); return(0); } /* SDR (Store Double-Word Right) */ static fastcall int mips64_exec_SDR(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SDR,base,offset,rt,FALSE); return(0); } /* SDC1 (Store Double-Word from Coprocessor 1) */ static fastcall int mips64_exec_SDC1(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int ft = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SDC1,base,offset,ft,FALSE); return(0); } /* SH (Store Half-Word) */ static fastcall int mips64_exec_SH(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SH,base,offset,rt,FALSE); return(0); } /* SLL */ static fastcall int mips64_exec_SLL(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); m_uint32_t res; res = (m_uint32_t)cpu->gpr[rt] << sa; cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SLLV */ static fastcall int mips64_exec_SLLV(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_uint32_t res; res = (m_uint32_t)cpu->gpr[rt] << (cpu->gpr[rs] & 0x1f); cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SLT */ static fastcall int mips64_exec_SLT(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); if ((m_int64_t)cpu->gpr[rs] < (m_int64_t)cpu->gpr[rt]) cpu->gpr[rd] = 1; else cpu->gpr[rd] = 0; return(0); } /* SLTI */ static fastcall int mips64_exec_SLTI(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_int64_t val = sign_extend(imm,16); if ((m_int64_t)cpu->gpr[rs] < val) cpu->gpr[rt] = 1; else cpu->gpr[rt] = 0; return(0); } /* SLTIU */ static fastcall int mips64_exec_SLTIU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); if (cpu->gpr[rs] < val) cpu->gpr[rt] = 1; else cpu->gpr[rt] = 0; return(0); } /* SLTU */ static fastcall int mips64_exec_SLTU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); if (cpu->gpr[rs] < cpu->gpr[rt]) cpu->gpr[rd] = 1; else cpu->gpr[rd] = 0; return(0); } /* SRA */ static fastcall int mips64_exec_SRA(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); m_int32_t res; res = (m_int32_t)cpu->gpr[rt] >> sa; cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SRAV */ static fastcall int mips64_exec_SRAV(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_int32_t res; res = (m_int32_t)cpu->gpr[rt] >> (cpu->gpr[rs] & 0x1f); cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SRL */ static fastcall int mips64_exec_SRL(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); m_uint32_t res; res = (m_uint32_t)cpu->gpr[rt] >> sa; cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SRLV */ static fastcall int mips64_exec_SRLV(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_uint32_t res; res = (m_uint32_t)cpu->gpr[rt] >> (cpu->gpr[rs] & 0x1f); cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SUB */ static fastcall int mips64_exec_SUB(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_uint32_t res; /* TODO: Exception handling */ res = (m_uint32_t)cpu->gpr[rs] - (m_uint32_t)cpu->gpr[rt]; cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SUBU */ static fastcall int mips64_exec_SUBU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_uint32_t res; res = (m_uint32_t)cpu->gpr[rs] - (m_uint32_t)cpu->gpr[rt]; cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SW (Store Word) */ static fastcall int mips64_exec_SW(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SW,base,offset,rt,FALSE); return(0); } /* SWL (Store Word Left) */ static fastcall int mips64_exec_SWL(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SWL,base,offset,rt,FALSE); return(0); } /* SWR (Store Word Right) */ static fastcall int mips64_exec_SWR(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SWR,base,offset,rt,FALSE); return(0); } /* SYNC */ static fastcall int mips64_exec_SYNC(cpu_mips_t *cpu,mips_insn_t insn) { return(0); } /* SYSCALL */ static fastcall int mips64_exec_SYSCALL(cpu_mips_t *cpu,mips_insn_t insn) { mips64_exec_syscall(cpu); return(1); } /* TEQ (Trap if Equal) */ static fastcall int mips64_exec_TEQ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); if (unlikely(cpu->gpr[rs] == cpu->gpr[rt])) { mips64_trigger_trap_exception(cpu); return(1); } return(0); } /* TEQI (Trap if Equal Immediate) */ static fastcall int mips64_exec_TEQI(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); if (unlikely(cpu->gpr[rs] == val)) { mips64_trigger_trap_exception(cpu); return(1); } return(0); } /* TLBP */ static fastcall int mips64_exec_TLBP(cpu_mips_t *cpu,mips_insn_t insn) { mips64_cp0_exec_tlbp(cpu); return(0); } /* TLBR */ static fastcall int mips64_exec_TLBR(cpu_mips_t *cpu,mips_insn_t insn) { mips64_cp0_exec_tlbr(cpu); return(0); } /* TLBWI */ static fastcall int mips64_exec_TLBWI(cpu_mips_t *cpu,mips_insn_t insn) { mips64_cp0_exec_tlbwi(cpu); return(0); } /* TLBWR */ static fastcall int mips64_exec_TLBWR(cpu_mips_t *cpu,mips_insn_t insn) { mips64_cp0_exec_tlbwr(cpu); return(0); } /* XOR */ static fastcall int mips64_exec_XOR(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rs] ^ cpu->gpr[rt]; return(0); } /* XORI */ static fastcall int mips64_exec_XORI(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); cpu->gpr[rt] = cpu->gpr[rs] ^ imm; return(0); } /* MIPS instruction array */ static struct mips64_insn_exec_tag mips64_exec_tags[] = { { "li" , mips64_exec_LI , 0xffe00000 , 0x24000000, 1, 16 }, { "move" , mips64_exec_MOVE , 0xfc1f07ff , 0x00000021, 1, 15 }, { "b" , mips64_exec_B , 0xffff0000 , 0x10000000, 0, 10 }, { "bal" , mips64_exec_BAL , 0xffff0000 , 0x04110000, 0, 10 }, { "beqz" , mips64_exec_BEQZ , 0xfc1f0000 , 0x10000000, 0, 9 }, { "bnez" , mips64_exec_BNEZ , 0xfc1f0000 , 0x14000000, 0, 9 }, { "add" , mips64_exec_ADD , 0xfc0007ff , 0x00000020, 1, 3 }, { "addi" , mips64_exec_ADDI , 0xfc000000 , 0x20000000, 1, 6 }, { "addiu" , mips64_exec_ADDIU , 0xfc000000 , 0x24000000, 1, 6 }, { "addu" , mips64_exec_ADDU , 0xfc0007ff , 0x00000021, 1, 3 }, { "and" , mips64_exec_AND , 0xfc0007ff , 0x00000024, 1, 3 }, { "andi" , mips64_exec_ANDI , 0xfc000000 , 0x30000000, 1, 5 }, { "beq" , mips64_exec_BEQ , 0xfc000000 , 0x10000000, 0, 8 }, { "beql" , mips64_exec_BEQL , 0xfc000000 , 0x50000000, 0, 8 }, { "bgez" , mips64_exec_BGEZ , 0xfc1f0000 , 0x04010000, 0, 9 }, { "bgezal" , mips64_exec_BGEZAL , 0xfc1f0000 , 0x04110000, 0, 9 }, { "bgezall", mips64_exec_BGEZALL , 0xfc1f0000 , 0x04130000, 0, 9 }, { "bgezl" , mips64_exec_BGEZL , 0xfc1f0000 , 0x04030000, 0, 9 }, { "bgtz" , mips64_exec_BGTZ , 0xfc1f0000 , 0x1c000000, 0, 9 }, { "bgtzl" , mips64_exec_BGTZL , 0xfc1f0000 , 0x5c000000, 0, 9 }, { "blez" , mips64_exec_BLEZ , 0xfc1f0000 , 0x18000000, 0, 9 }, { "blezl" , mips64_exec_BLEZL , 0xfc1f0000 , 0x58000000, 0, 9 }, { "bltz" , mips64_exec_BLTZ , 0xfc1f0000 , 0x04000000, 0, 9 }, { "bltzal" , mips64_exec_BLTZAL , 0xfc1f0000 , 0x04100000, 0, 9 }, { "bltzall", mips64_exec_BLTZALL , 0xfc1f0000 , 0x04120000, 0, 9 }, { "bltzl" , mips64_exec_BLTZL , 0xfc1f0000 , 0x04020000, 0, 9 }, { "bne" , mips64_exec_BNE , 0xfc000000 , 0x14000000, 0, 8 }, { "bnel" , mips64_exec_BNEL , 0xfc000000 , 0x54000000, 0, 8 }, { "break" , mips64_exec_BREAK , 0xfc00003f , 0x0000000d, 1, 0 }, { "cache" , mips64_exec_CACHE , 0xfc000000 , 0xbc000000, 1, 2 }, { "cfc0" , mips64_exec_CFC0 , 0xffe007ff , 0x40400000, 1, 18 }, { "ctc0" , mips64_exec_CTC0 , 0xffe007ff , 0x40600000, 1, 18 }, { "daddiu" , mips64_exec_DADDIU , 0xfc000000 , 0x64000000, 1, 5 }, { "daddu" , mips64_exec_DADDU , 0xfc0007ff , 0x0000002d, 1, 3 }, { "div" , mips64_exec_DIV , 0xfc00ffff , 0x0000001a, 1, 17 }, { "divu" , mips64_exec_DIVU , 0xfc00ffff , 0x0000001b, 1, 17 }, { "dmfc0" , mips64_exec_DMFC0 , 0xffe007f8 , 0x40200000, 1, 18 }, { "dmfc1" , mips64_exec_DMFC1 , 0xffe007ff , 0x44200000, 1, 19 }, { "dmtc0" , mips64_exec_DMTC0 , 0xffe007f8 , 0x40a00000, 1, 18 }, { "dmtc1" , mips64_exec_DMTC1 , 0xffe007ff , 0x44a00000, 1, 19 }, { "dsll" , mips64_exec_DSLL , 0xffe0003f , 0x00000038, 1, 7 }, { "dsll32" , mips64_exec_DSLL32 , 0xffe0003f , 0x0000003c, 1, 7 }, { "dsllv" , mips64_exec_DSLLV , 0xfc0007ff , 0x00000014, 1, 4 }, { "dsra" , mips64_exec_DSRA , 0xffe0003f , 0x0000003b, 1, 7 }, { "dsra32" , mips64_exec_DSRA32 , 0xffe0003f , 0x0000003f, 1, 7 }, { "dsrav" , mips64_exec_DSRAV , 0xfc0007ff , 0x00000017, 1, 4 }, { "dsrl" , mips64_exec_DSRL , 0xffe0003f , 0x0000003a, 1, 7 }, { "dsrl32" , mips64_exec_DSRL32 , 0xffe0003f , 0x0000003e, 1, 7 }, { "dsrlv" , mips64_exec_DSRLV , 0xfc0007ff , 0x00000016, 1, 4 }, { "dsubu" , mips64_exec_DSUBU , 0xfc0007ff , 0x0000002f, 1, 3 }, { "eret" , mips64_exec_ERET , 0xffffffff , 0x42000018, 0, 1 }, { "j" , mips64_exec_J , 0xfc000000 , 0x08000000, 0, 11 }, { "jal" , mips64_exec_JAL , 0xfc000000 , 0x0c000000, 0, 11 }, { "jalr" , mips64_exec_JALR , 0xfc1f003f , 0x00000009, 0, 15 }, { "jr" , mips64_exec_JR , 0xfc1ff83f , 0x00000008, 0, 13 }, { "lb" , mips64_exec_LB , 0xfc000000 , 0x80000000, 1, 2 }, { "lbu" , mips64_exec_LBU , 0xfc000000 , 0x90000000, 1, 2 }, { "ld" , mips64_exec_LD , 0xfc000000 , 0xdc000000, 1, 2 }, { "ldc1" , mips64_exec_LDC1 , 0xfc000000 , 0xd4000000, 1, 3 }, { "ldl" , mips64_exec_LDL , 0xfc000000 , 0x68000000, 1, 2 }, { "ldr" , mips64_exec_LDR , 0xfc000000 , 0x6c000000, 1, 2 }, { "lh" , mips64_exec_LH , 0xfc000000 , 0x84000000, 1, 2 }, { "lhu" , mips64_exec_LHU , 0xfc000000 , 0x94000000, 1, 2 }, { "ll" , mips64_exec_LL , 0xfc000000 , 0xc0000000, 1, 2 }, { "lui" , mips64_exec_LUI , 0xffe00000 , 0x3c000000, 1, 16 }, { "lw" , mips64_exec_LW , 0xfc000000 , 0x8c000000, 1, 2 }, { "lwl" , mips64_exec_LWL , 0xfc000000 , 0x88000000, 1, 2 }, { "lwr" , mips64_exec_LWR , 0xfc000000 , 0x98000000, 1, 2 }, { "lwu" , mips64_exec_LWU , 0xfc000000 , 0x9c000000, 1, 2 }, { "mfc0" , mips64_exec_MFC0 , 0xffe007ff , 0x40000000, 1, 18 }, { "mfc0_1" , mips64_exec_CFC0 , 0xffe007ff , 0x40000001, 1, 19 }, { "mfc1" , mips64_exec_MFC1 , 0xffe007ff , 0x44000000, 1, 19 }, { "mfhi" , mips64_exec_MFHI , 0xffff07ff , 0x00000010, 1, 14 }, { "mflo" , mips64_exec_MFLO , 0xffff07ff , 0x00000012, 1, 14 }, { "mtc0" , mips64_exec_MTC0 , 0xffe007ff , 0x40800000, 1, 18 }, { "mtc1" , mips64_exec_MTC1 , 0xffe007ff , 0x44800000, 1, 19 }, { "mthi" , mips64_exec_MTHI , 0xfc1fffff , 0x00000011, 1, 13 }, { "mtlo" , mips64_exec_MTLO , 0xfc1fffff , 0x00000013, 1, 13 }, { "mul" , mips64_exec_MUL , 0xfc0007ff , 0x70000002, 1, 4 }, { "mult" , mips64_exec_MULT , 0xfc00ffff , 0x00000018, 1, 17 }, { "multu" , mips64_exec_MULTU , 0xfc00ffff , 0x00000019, 1, 17 }, { "nop" , mips64_exec_NOP , 0xffffffff , 0x00000000, 1, 1 }, { "nor" , mips64_exec_NOR , 0xfc0007ff , 0x00000027, 1, 3 }, { "or" , mips64_exec_OR , 0xfc0007ff , 0x00000025, 1, 3 }, { "ori" , mips64_exec_ORI , 0xfc000000 , 0x34000000, 1, 5 }, { "pref" , mips64_exec_PREF , 0xfc000000 , 0xcc000000, 1, 0 }, { "prefi" , mips64_exec_PREFI , 0xfc0007ff , 0x4c00000f, 1, 0 }, { "sb" , mips64_exec_SB , 0xfc000000 , 0xa0000000, 1, 2 }, { "sc" , mips64_exec_SC , 0xfc000000 , 0xe0000000, 1, 2 }, { "sd" , mips64_exec_SD , 0xfc000000 , 0xfc000000, 1, 2 }, { "sdc1" , mips64_exec_SDC1 , 0xfc000000 , 0xf4000000, 1, 3 }, { "sdl" , mips64_exec_SDL , 0xfc000000 , 0xb0000000, 1, 2 }, { "sdr" , mips64_exec_SDR , 0xfc000000 , 0xb4000000, 1, 2 }, { "sh" , mips64_exec_SH , 0xfc000000 , 0xa4000000, 1, 2 }, { "sll" , mips64_exec_SLL , 0xffe0003f , 0x00000000, 1, 7 }, { "sllv" , mips64_exec_SLLV , 0xfc0007ff , 0x00000004, 1, 4 }, { "slt" , mips64_exec_SLT , 0xfc0007ff , 0x0000002a, 1, 3 }, { "slti" , mips64_exec_SLTI , 0xfc000000 , 0x28000000, 1, 5 }, { "sltiu" , mips64_exec_SLTIU , 0xfc000000 , 0x2c000000, 1, 5 }, { "sltu" , mips64_exec_SLTU , 0xfc0007ff , 0x0000002b, 1, 3 }, { "sra" , mips64_exec_SRA , 0xffe0003f , 0x00000003, 1, 7 }, { "srav" , mips64_exec_SRAV , 0xfc0007ff , 0x00000007, 1, 4 }, { "srl" , mips64_exec_SRL , 0xffe0003f , 0x00000002, 1, 7 }, { "srlv" , mips64_exec_SRLV , 0xfc0007ff , 0x00000006, 1, 4 }, { "sub" , mips64_exec_SUB , 0xfc0007ff , 0x00000022, 1, 3 }, { "subu" , mips64_exec_SUBU , 0xfc0007ff , 0x00000023, 1, 3 }, { "sw" , mips64_exec_SW , 0xfc000000 , 0xac000000, 1, 2 }, { "swl" , mips64_exec_SWL , 0xfc000000 , 0xa8000000, 1, 2 }, { "swr" , mips64_exec_SWR , 0xfc000000 , 0xb8000000, 1, 2 }, { "sync" , mips64_exec_SYNC , 0xfffff83f , 0x0000000f, 1, 1 }, { "syscall", mips64_exec_SYSCALL , 0xfc00003f , 0x0000000c, 1, 1 }, { "teq" , mips64_exec_TEQ , 0xfc00003f , 0x00000034, 1, 17 }, { "teqi" , mips64_exec_TEQI , 0xfc1f0000 , 0x040c0000, 1, 20 }, { "tlbp" , mips64_exec_TLBP , 0xffffffff , 0x42000008, 1, 1 }, { "tlbr" , mips64_exec_TLBR , 0xffffffff , 0x42000001, 1, 1 }, { "tlbwi" , mips64_exec_TLBWI , 0xffffffff , 0x42000002, 1, 1 }, { "tlbwr" , mips64_exec_TLBWR , 0xffffffff , 0x42000006, 1, 1 }, { "xor" , mips64_exec_XOR , 0xfc0007ff , 0x00000026, 1, 3 }, { "xori" , mips64_exec_XORI , 0xfc000000 , 0x38000000, 1, 5 }, { "unknown", mips64_exec_unknown , 0x00000000 , 0x00000000, 1, 0 }, { NULL , NULL , 0x00000000 , 0x00000000, 1, 0 }, }; #endif dynamips-0.2.14/stable/mips64_exec.h000066400000000000000000000020371241034141600171710ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __MIPS64_EXEC_H__ #define __MIPS64_EXEC_H__ #include "utils.h" /* MIPS instruction recognition */ struct mips64_insn_exec_tag { char *name; fastcall int (*exec)(cpu_mips_t *,mips_insn_t); m_uint32_t mask,value; int delay_slot; int instr_type; m_uint64_t count; }; /* Initialize instruction lookup table */ void mips64_exec_create_ilt(void); /* Dump statistics */ void mips64_dump_stats(cpu_mips_t *cpu); /* Dump an instruction */ int mips64_dump_insn(char *buffer,size_t buf_size,size_t insn_name_size, m_uint64_t pc,mips_insn_t instruction); /* Dump an instruction block */ void mips64_dump_insn_block(cpu_mips_t *cpu,m_uint64_t pc,u_int count, size_t insn_name_size); /* Single-step execution */ fastcall void mips64_exec_single_step(cpu_mips_t *cpu,mips_insn_t instruction); /* Run MIPS code in step-by-step mode */ void *mips64_exec_run_cpu(cpu_gen_t *cpu); #endif dynamips-0.2.14/stable/mips64_jit.c000066400000000000000000000514611241034141600170330ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * MIPS64 JIT compiler. */ #include #include #include #include #include #include #include #include #include #include "sbox.h" #include "cpu.h" #include "device.h" #include "mips64.h" #include "mips64_cp0.h" #include "mips64_exec.h" #include "mips64_jit.h" #include "insn_lookup.h" #include "memory.h" #include "ptask.h" #include MIPS64_ARCH_INC_FILE #if DEBUG_BLOCK_TIMESTAMP static volatile m_uint64_t jit_jiffies = 0; #endif /* MIPS jump instructions for block scan */ struct mips64_insn_jump mips64_insn_jumps[] = { { "b" , 0xffff0000, 0x10000000, 16, 1 }, { "bal" , 0xffff0000, 0x04110000, 16, 1 }, { "beq" , 0xfc000000, 0x10000000, 16, 1 }, { "beql" , 0xfc000000, 0x50000000, 16, 1 }, { "bgez" , 0xfc1f0000, 0x04010000, 16, 1 }, { "bgezl" , 0xfc1f0000, 0x04030000, 16, 1 }, { "bgezal" , 0xfc1f0000, 0x04110000, 16, 1 }, { "bgezall" , 0xfc1f0000, 0x04130000, 16, 1 }, { "bgtz" , 0xfc1f0000, 0x1c000000, 16, 1 }, { "bgtzl" , 0xfc1f0000, 0x5c000000, 16, 1 }, { "blez" , 0xfc1f0000, 0x18000000, 16, 1 }, { "blezl" , 0xfc1f0000, 0x58000000, 16, 1 }, { "bltz" , 0xfc1f0000, 0x04000000, 16, 1 }, { "bltzl" , 0xfc1f0000, 0x04020000, 16, 1 }, { "bltzal" , 0xfc1f0000, 0x04100000, 16, 1 }, { "bltzall" , 0xfc1f0000, 0x04120000, 16, 1 }, { "bne" , 0xfc000000, 0x14000000, 16, 1 }, { "bnel" , 0xfc000000, 0x54000000, 16, 1 }, { "j" , 0xfc000000, 0x08000000, 26, 0 }, { NULL , 0x00000000, 0x00000000, 0, 0 }, }; /* Instruction Lookup Table */ static insn_lookup_t *ilt = NULL; static void *mips64_jit_get_insn(int index) { return(&mips64_insn_tags[index]); } static int mips64_jit_chk_lo(struct mips64_insn_tag *tag,int value) { return((value & tag->mask) == (tag->value & 0xFFFF)); } static int mips64_jit_chk_hi(struct mips64_insn_tag *tag,int value) { return((value & (tag->mask >> 16)) == (tag->value >> 16)); } /* Destroy instruction lookup table */ static void destroy_ilt(void) { assert(ilt); ilt_destroy(ilt); ilt = NULL; } /* Initialize instruction lookup table */ void mips64_jit_create_ilt(void) { int i,count; for(i=0,count=0;mips64_insn_tags[i].emit;i++) count++; ilt = ilt_create("mips64j",count, (ilt_get_insn_cbk_t)mips64_jit_get_insn, (ilt_check_cbk_t)mips64_jit_chk_lo, (ilt_check_cbk_t)mips64_jit_chk_hi); atexit(destroy_ilt); } /* Initialize the JIT structure */ int mips64_jit_init(cpu_mips_t *cpu) { insn_exec_page_t *cp; u_char *cp_addr; u_int area_size; size_t len; int i; /* Physical mapping for executable pages */ len = MIPS_JIT_PC_HASH_SIZE * sizeof(void *); cpu->exec_blk_map = m_memalign(4096,len); memset(cpu->exec_blk_map,0,len); /* Get area size */ if (!(area_size = cpu->vm->exec_area_size)) area_size = MIPS_EXEC_AREA_SIZE; /* Create executable page area */ cpu->exec_page_area_size = area_size * 1048576; cpu->exec_page_area = memzone_map_exec_area(cpu->exec_page_area_size); if (!cpu->exec_page_area) { fprintf(stderr, "mips64_jit_init: unable to create exec area (size %lu)\n", (u_long)cpu->exec_page_area_size); return(-1); } /* Carve the executable page area */ cpu->exec_page_count = cpu->exec_page_area_size / MIPS_JIT_BUFSIZE; cpu->exec_page_array = calloc(cpu->exec_page_count, sizeof(insn_exec_page_t)); if (!cpu->exec_page_array) { fprintf(stderr,"mips64_jit_init: unable to create exec page array\n"); return(-1); } for(i=0,cp_addr=cpu->exec_page_area;iexec_page_count;i++) { cp = &cpu->exec_page_array[i]; cp->ptr = cp_addr; cp_addr += MIPS_JIT_BUFSIZE; cp->next = cpu->exec_page_free_list; cpu->exec_page_free_list = cp; } printf("CPU%u: carved JIT exec zone of %lu Mb into %lu pages of %u Kb.\n", cpu->gen->id, (u_long)(cpu->exec_page_area_size / 1048576), (u_long)cpu->exec_page_count,MIPS_JIT_BUFSIZE / 1024); return(0); } /* Flush the JIT */ u_int mips64_jit_flush(cpu_mips_t *cpu,u_int threshold) { mips64_jit_tcb_t *p,*next; m_uint32_t pc_hash; u_int count = 0; if (!threshold) threshold = (u_int)(-1); /* UINT_MAX not defined everywhere */ for(p=cpu->tcb_list;p;p=next) { next = p->next; if (p->acc_count <= threshold) { pc_hash = mips64_jit_get_pc_hash(p->start_pc); cpu->exec_blk_map[pc_hash] = NULL; mips64_jit_tcb_free(cpu,p,TRUE); count++; } } cpu->compiled_pages -= count; return(count); } /* Shutdown the JIT */ void mips64_jit_shutdown(cpu_mips_t *cpu) { mips64_jit_tcb_t *p,*next; /* Flush the JIT */ mips64_jit_flush(cpu,0); /* Free the instruction blocks */ for(p=cpu->tcb_free_list;p;p=next) { next = p->next; free(p); } /* Unmap the executable page area */ if (cpu->exec_page_area) memzone_unmap(cpu->exec_page_area,cpu->exec_page_area_size); /* Free the exec page array */ free(cpu->exec_page_array); /* Free physical mapping for executable pages */ free(cpu->exec_blk_map); } /* Allocate an exec page */ static inline insn_exec_page_t *exec_page_alloc(cpu_mips_t *cpu) { insn_exec_page_t *p; u_int count; /* If the free list is empty, flush JIT */ if (unlikely(!cpu->exec_page_free_list)) { if (cpu->jit_flush_method) { cpu_log(cpu->gen, "JIT","flushing data structures (compiled pages=%u)\n", cpu->compiled_pages); mips64_jit_flush(cpu,0); } else { count = mips64_jit_flush(cpu,100); cpu_log(cpu->gen,"JIT","partial JIT flush (count=%u)\n",count); if (!cpu->exec_page_free_list) mips64_jit_flush(cpu,0); } /* Use both methods alternatively */ cpu->jit_flush_method = 1 - cpu->jit_flush_method; } if (unlikely(!(p = cpu->exec_page_free_list))) return NULL; cpu->exec_page_free_list = p->next; cpu->exec_page_alloc++; return p; } /* Free an exec page and returns it to the pool */ static inline void exec_page_free(cpu_mips_t *cpu,insn_exec_page_t *p) { if (p) { p->next = cpu->exec_page_free_list; cpu->exec_page_free_list = p; cpu->exec_page_alloc--; } } /* Find the JIT code emitter for the specified MIPS instruction */ static struct mips64_insn_tag *insn_tag_find(mips_insn_t ins) { struct mips64_insn_tag *tag = NULL; int index; index = ilt_lookup(ilt,ins); tag = mips64_jit_get_insn(index); return tag; } /* Check if the specified MIPS instruction is a jump */ _unused static struct mips64_insn_jump *insn_jump_find(mips_insn_t ins) { struct mips64_insn_jump *jump = NULL; int i; for(i=0;mips64_insn_jumps[i].name;i++) if ((ins & mips64_insn_jumps[i].mask) == mips64_insn_jumps[i].value) { jump = &mips64_insn_jumps[i]; break; } return(jump); } /* Fetch a MIPS instruction */ static forced_inline mips_insn_t insn_fetch(mips64_jit_tcb_t *b) { return(vmtoh32(b->mips_code[b->mips_trans_pos])); } /* Emit a breakpoint if necessary */ #if BREAKPOINT_ENABLE static void insn_emit_breakpoint(cpu_mips_t *cpu,mips64_jit_tcb_t *b) { m_uint64_t pc; int i; pc = b->start_pc+((b->mips_trans_pos-1)<<2); for(i=0;ibreakpoints[i]) { mips64_emit_breakpoint(b); break; } } #endif /* BREAKPOINT_ENABLE */ /* Check if an instruction is in a delay slot or not */ int mips64_jit_is_delay_slot(mips64_jit_tcb_t *b,m_uint64_t pc) { struct mips64_insn_tag *tag; m_uint32_t offset,insn; offset = (pc - b->start_pc) >> 2; if (!offset) return(FALSE); /* Fetch the previous instruction to determine if it is a jump */ insn = vmtoh32(b->mips_code[offset-1]); tag = insn_tag_find(insn); assert(tag != NULL); return(!tag->delay_slot); } /* Fetch a MIPS instruction and emit corresponding translated code */ struct mips64_insn_tag *mips64_jit_fetch_and_emit(cpu_mips_t *cpu, mips64_jit_tcb_t *block, int delay_slot) { struct mips64_insn_tag *tag; mips_insn_t code; code = insn_fetch(block); tag = insn_tag_find(code); assert(tag); /* Branch-delay slot is in another page: slow exec */ if ((block->mips_trans_pos == (MIPS_INSN_PER_PAGE-1)) && !tag->delay_slot) { block->jit_insn_ptr[block->mips_trans_pos] = block->jit_ptr; mips64_set_pc(block,block->start_pc + (block->mips_trans_pos << 2)); mips64_emit_single_step(block,code); mips64_jit_tcb_push_epilog(block); block->mips_trans_pos++; return tag; } if (delay_slot && !tag->delay_slot) { mips64_emit_invalid_delay_slot(block); return NULL; } if (!delay_slot) block->jit_insn_ptr[block->mips_trans_pos] = block->jit_ptr; if (delay_slot != 2) block->mips_trans_pos++; #if DEBUG_INSN_PERF_CNT mips64_inc_perf_counter(block); #endif if (!delay_slot) { /* Check for IRQs + Increment count register before jumps */ if (!tag->delay_slot) { mips64_inc_cp0_count_reg(block); mips64_check_pending_irq(block); } } #if BREAKPOINT_ENABLE if (cpu->breakpoints_enabled) insn_emit_breakpoint(cpu,block); #endif tag->emit(cpu,block,code); return tag; } /* Add end of JIT block */ static void mips64_jit_tcb_add_end(mips64_jit_tcb_t *b) { mips64_set_pc(b,b->start_pc+(b->mips_trans_pos<<2)); mips64_jit_tcb_push_epilog(b); } /* Record a patch to apply in a compiled block */ int mips64_jit_tcb_record_patch(mips64_jit_tcb_t *block,u_char *jit_ptr, m_uint64_t vaddr) { struct mips64_jit_patch_table *ipt = block->patch_table; struct mips64_insn_patch *patch; /* pc must be 32-bit aligned */ if (vaddr & 0x03) { fprintf(stderr,"Block 0x%8.8llx: trying to record an invalid PC " "(0x%8.8llx) - mips_trans_pos=%d.\n", block->start_pc,vaddr,block->mips_trans_pos); return(-1); } if (!ipt || (ipt->cur_patch >= MIPS64_INSN_PATCH_TABLE_SIZE)) { /* full table or no table, create a new one */ ipt = malloc(sizeof(*ipt)); if (!ipt) { fprintf(stderr,"Block 0x%8.8llx: unable to create patch table.\n", block->start_pc); return(-1); } memset(ipt,0,sizeof(*ipt)); ipt->next = block->patch_table; block->patch_table = ipt; } #if DEBUG_BLOCK_PATCH printf("Block 0x%8.8llx: recording patch [JIT:%p->mips:0x%8.8llx], " "MTP=%d\n",block->start_pc,jit_ptr,vaddr,block->mips_trans_pos); #endif patch = &ipt->patches[ipt->cur_patch]; patch->jit_insn = jit_ptr; patch->mips_pc = vaddr; ipt->cur_patch++; return(0); } /* Apply all patches */ static int mips64_jit_tcb_apply_patches(cpu_mips_t *cpu, mips64_jit_tcb_t *block) { struct mips64_jit_patch_table *ipt; struct mips64_insn_patch *patch; u_char *jit_dst; int i; for(ipt=block->patch_table;ipt;ipt=ipt->next) for(i=0;icur_patch;i++) { patch = &ipt->patches[i]; jit_dst = mips64_jit_tcb_get_host_ptr(block,patch->mips_pc); if (jit_dst) { #if DEBUG_BLOCK_PATCH printf("Block 0x%8.8llx: applying patch " "[JIT:%p->mips:0x%8.8llx=JIT:%p]\n", block->start_pc,patch->jit_insn,patch->mips_pc,jit_dst); #endif mips64_jit_tcb_set_patch(patch->jit_insn,jit_dst); } } return(0); } /* Free the patch table */ static void mips64_jit_tcb_free_patches(mips64_jit_tcb_t *block) { struct mips64_jit_patch_table *p,*next; for(p=block->patch_table;p;p=next) { next = p->next; free(p); } block->patch_table = NULL; } /* Adjust the JIT buffer if its size is not sufficient */ static int mips64_jit_tcb_adjust_buffer(cpu_mips_t *cpu, mips64_jit_tcb_t *block) { insn_exec_page_t *new_buffer; if ((block->jit_ptr - block->jit_buffer->ptr) <= (MIPS_JIT_BUFSIZE - 512)) return(0); #if DEBUG_BLOCK_CHUNK printf("Block 0x%llx: adjusting JIT buffer...\n",block->start_pc); #endif if (block->jit_chunk_pos >= MIPS_JIT_MAX_CHUNKS) { fprintf(stderr,"Block 0x%llx: too many JIT chunks.\n",block->start_pc); return(-1); } if (!(new_buffer = exec_page_alloc(cpu))) return(-1); /* record the new exec page */ block->jit_chunks[block->jit_chunk_pos++] = block->jit_buffer; block->jit_buffer = new_buffer; /* jump to the new exec page (link) */ mips64_jit_tcb_set_jump(block->jit_ptr,new_buffer->ptr); block->jit_ptr = new_buffer->ptr; return(0); } /* Allocate an instruction block */ static inline mips64_jit_tcb_t *mips64_jit_tcb_alloc(cpu_mips_t *cpu) { mips64_jit_tcb_t *p; if (cpu->tcb_free_list) { p = cpu->tcb_free_list; cpu->tcb_free_list = p->next; } else { if (!(p = malloc(sizeof(*p)))) return NULL; } memset(p,0,sizeof(*p)); return p; } /* Free an instruction block */ void mips64_jit_tcb_free(cpu_mips_t *cpu,mips64_jit_tcb_t *block, int list_removal) { int i; if (block) { if (list_removal) { /* Remove the block from the linked list */ if (block->next) block->next->prev = block->prev; else cpu->tcb_last = block->prev; if (block->prev) block->prev->next = block->next; else cpu->tcb_list = block->next; } /* Free the patch tables */ mips64_jit_tcb_free_patches(block); /* Free code pages */ for(i=0;ijit_chunks[i]); /* Free the current JIT buffer */ exec_page_free(cpu,block->jit_buffer); /* Free the MIPS-to-native code mapping */ free(block->jit_insn_ptr); /* Make the block return to the free list */ block->next = cpu->tcb_free_list; cpu->tcb_free_list = block; } } /* Create an instruction block */ static mips64_jit_tcb_t *mips64_jit_tcb_create(cpu_mips_t *cpu, m_uint64_t vaddr) { mips64_jit_tcb_t *block = NULL; if (!(block = mips64_jit_tcb_alloc(cpu))) goto err_block_alloc; block->start_pc = vaddr; /* Allocate the first JIT buffer */ if (!(block->jit_buffer = exec_page_alloc(cpu))) goto err_jit_alloc; block->jit_ptr = block->jit_buffer->ptr; block->mips_code = cpu->mem_op_lookup(cpu,block->start_pc); if (!block->mips_code) { fprintf(stderr,"%% No memory map for code execution at 0x%llx\n", block->start_pc); goto err_lookup; } #if DEBUG_BLOCK_TIMESTAMP block->tm_first_use = block->tm_last_use = jit_jiffies; #endif return block; err_lookup: err_jit_alloc: mips64_jit_tcb_free(cpu,block,FALSE); err_block_alloc: fprintf(stderr,"%% Unable to create instruction block for vaddr=0x%llx\n", vaddr); return NULL; } /* Compile a MIPS instruction page */ static inline mips64_jit_tcb_t *mips64_jit_tcb_compile(cpu_mips_t *cpu,m_uint64_t vaddr) { mips64_jit_tcb_t *block; struct mips64_insn_tag *tag; m_uint64_t page_addr; size_t len; page_addr = vaddr & ~(m_uint64_t)MIPS_MIN_PAGE_IMASK; if (unlikely(!(block = mips64_jit_tcb_create(cpu,page_addr)))) { fprintf(stderr,"insn_page_compile: unable to create JIT block.\n"); return NULL; } /* Allocate the array used to convert MIPS code ptr to native code ptr */ len = MIPS_MIN_PAGE_SIZE / sizeof(mips_insn_t); if (!(block->jit_insn_ptr = calloc(len,sizeof(u_char *)))) { fprintf(stderr,"insn_page_compile: unable to create JIT mappings.\n"); goto error; } /* Emit native code for each instruction */ block->mips_trans_pos = 0; while(block->mips_trans_pos < MIPS_INSN_PER_PAGE) { if (unlikely(!(tag = mips64_jit_fetch_and_emit(cpu,block,0)))) { fprintf(stderr,"insn_page_compile: unable to fetch instruction.\n"); goto error; } #if DEBUG_BLOCK_COMPILE printf("Page 0x%8.8llx: emitted tag 0x%8.8x/0x%8.8x\n", block->start_pc,tag->mask,tag->value); #endif mips64_jit_tcb_adjust_buffer(cpu,block); } mips64_jit_tcb_add_end(block); mips64_jit_tcb_apply_patches(cpu,block); mips64_jit_tcb_free_patches(block); /* Add the block to the linked list */ block->next = cpu->tcb_list; block->prev = NULL; if (cpu->tcb_list) cpu->tcb_list->prev = block; else cpu->tcb_last = block; cpu->tcb_list = block; cpu->compiled_pages++; return block; error: mips64_jit_tcb_free(cpu,block,FALSE); return NULL; } /* Run a compiled MIPS instruction block */ static forced_inline void mips64_jit_tcb_run(cpu_mips_t *cpu,mips64_jit_tcb_t *block) { #if DEBUG_SYM_TREE struct symbol *sym = NULL; int mark = FALSE; #endif if (unlikely(cpu->pc & 0x03)) { fprintf(stderr,"mips64_jit_tcb_run: Invalid PC 0x%llx.\n",cpu->pc); mips64_dump_regs(cpu->gen); mips64_tlb_dump(cpu->gen); cpu_stop(cpu->gen); return; } #if DEBUG_SYM_TREE if (cpu->sym_trace && cpu->sym_tree) { if ((sym = mips64_sym_lookup(cpu,cpu->pc)) != NULL) { cpu_log(cpu,"mips64_jit_tcb_run(start)", "%s (PC=0x%llx) RA = 0x%llx\na0=0x%llx, " "a1=0x%llx, a2=0x%llx, a3=0x%llx\n", sym->name, cpu->pc, cpu->gpr[MIPS_GPR_RA], cpu->gpr[MIPS_GPR_A0], cpu->gpr[MIPS_GPR_A1], cpu->gpr[MIPS_GPR_A2], cpu->gpr[MIPS_GPR_A3]); mark = TRUE; } } #endif /* Execute JIT compiled code */ mips64_jit_tcb_exec(cpu,block); #if DEBUG_SYM_TREE if (mark) { cpu_log(cpu,"mips64_jit_tcb_run(end)","%s, v0 = 0x%llx\n", sym->name,cpu->gpr[MIPS_GPR_V0]); } #endif } /* Execute compiled MIPS code */ void *mips64_jit_run_cpu(cpu_gen_t *gen) { cpu_mips_t *cpu = CPU_MIPS64(gen); pthread_t timer_irq_thread; mips64_jit_tcb_t *block; int timer_irq_check = 0; m_uint32_t pc_hash; if (pthread_create(&timer_irq_thread,NULL, (void *)mips64_timer_irq_run,cpu)) { fprintf(stderr, "VM '%s': unable to create Timer IRQ thread for CPU%u.\n", cpu->vm->name,gen->id); cpu_stop(cpu->gen); return NULL; } gen->cpu_thread_running = TRUE; cpu_exec_loop_set(gen); start_cpu: gen->idle_count = 0; for(;;) { if (unlikely(gen->state != CPU_STATE_RUNNING)) break; #if DEBUG_BLOCK_PERF_CNT cpu->perf_counter++; #endif /* Handle virtual idle loop */ if (unlikely(cpu->pc == cpu->idle_pc)) { if (++gen->idle_count == gen->idle_max) { cpu_idle_loop(gen); gen->idle_count = 0; } } /* Handle the virtual CPU clock */ if (++timer_irq_check == cpu->timer_irq_check_itv) { timer_irq_check = 0; if (cpu->timer_irq_pending && !cpu->irq_disable) { mips64_trigger_timer_irq(cpu); mips64_trigger_irq(cpu); cpu->timer_irq_pending--; } } pc_hash = mips64_jit_get_pc_hash(cpu->pc); block = cpu->exec_blk_map[pc_hash]; /* No block found, compile the page */ if (unlikely(!block) || unlikely(!mips64_jit_tcb_match(cpu,block))) { if (block != NULL) { mips64_jit_tcb_free(cpu,block,TRUE); cpu->exec_blk_map[pc_hash] = NULL; } block = mips64_jit_tcb_compile(cpu,cpu->pc); if (unlikely(!block)) { fprintf(stderr, "VM '%s': unable to compile block for CPU%u PC=0x%llx\n", cpu->vm->name,gen->id,cpu->pc); cpu_stop(gen); break; } cpu->exec_blk_map[pc_hash] = block; } #if DEBUG_BLOCK_TIMESTAMP block->tm_last_use = jit_jiffies++; #endif block->acc_count++; mips64_jit_tcb_run(cpu,block); } if (!cpu->pc) { cpu_stop(gen); cpu_log(gen,"JIT","PC=0, halting CPU.\n"); } /* Check regularly if the CPU has been restarted */ while(gen->cpu_thread_running) { gen->seq_state++; switch(gen->state) { case CPU_STATE_RUNNING: gen->state = CPU_STATE_RUNNING; goto start_cpu; case CPU_STATE_HALTED: gen->cpu_thread_running = FALSE; pthread_join(timer_irq_thread,NULL); return NULL; } /* CPU is paused */ usleep(200000); } return NULL; } dynamips-0.2.14/stable/mips64_jit.h000066400000000000000000000116221241034141600170330ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * MIPS64 JIT compiler. */ #ifndef __MIPS64_JIT_H__ #define __MIPS64_JIT_H__ #include "utils.h" #include "sbox.h" /* Size of executable page area (in Mb) */ #ifndef __CYGWIN__ #define MIPS_EXEC_AREA_SIZE 64 #else #define MIPS_EXEC_AREA_SIZE 16 #endif /* Buffer size for JIT code generation */ #define MIPS_JIT_BUFSIZE 32768 /* Maximum number of X86 chunks */ #define MIPS_JIT_MAX_CHUNKS 32 /* Size of hash for PC lookup */ #define MIPS_JIT_PC_HASH_BITS 16 #define MIPS_JIT_PC_HASH_MASK ((1 << MIPS_JIT_PC_HASH_BITS) - 1) #define MIPS_JIT_PC_HASH_SIZE (1 << MIPS_JIT_PC_HASH_BITS) /* Instruction jump patch */ struct mips64_insn_patch { u_char *jit_insn; m_uint64_t mips_pc; }; /* Instruction patch table */ #define MIPS64_INSN_PATCH_TABLE_SIZE 32 struct mips64_jit_patch_table { struct mips64_insn_patch patches[MIPS64_INSN_PATCH_TABLE_SIZE]; u_int cur_patch; struct mips64_jit_patch_table *next; }; /* MIPS64 translated code block */ struct mips64_jit_tcb { m_uint64_t start_pc; u_char **jit_insn_ptr; m_uint64_t acc_count; mips_insn_t *mips_code; u_int mips_trans_pos; u_int jit_chunk_pos; u_char *jit_ptr; insn_exec_page_t *jit_buffer; insn_exec_page_t *jit_chunks[MIPS_JIT_MAX_CHUNKS]; struct mips64_jit_patch_table *patch_table; mips64_jit_tcb_t *prev,*next; #if DEBUG_BLOCK_TIMESTAMP m_uint64_t tm_first_use,tm_last_use; #endif }; /* MIPS instruction recognition */ struct mips64_insn_tag { int (*emit)(cpu_mips_t *cpu,mips64_jit_tcb_t *,mips_insn_t); m_uint32_t mask,value; int delay_slot; }; /* MIPS jump instruction (for block scan) */ struct mips64_insn_jump { char *name; m_uint32_t mask,value; int offset_bits; int relative; }; /* Get the JIT instruction pointer in a translated block */ static forced_inline u_char *mips64_jit_tcb_get_host_ptr(mips64_jit_tcb_t *b,m_uint64_t vaddr) { m_uint32_t offset; offset = ((m_uint32_t)vaddr & MIPS_MIN_PAGE_IMASK) >> 2; return(b->jit_insn_ptr[offset]); } /* Check if the specified address belongs to the specified block */ static forced_inline int mips64_jit_tcb_local_addr(mips64_jit_tcb_t *block,m_uint64_t vaddr, u_char **jit_addr) { if ((vaddr & MIPS_MIN_PAGE_MASK) == block->start_pc) { *jit_addr = mips64_jit_tcb_get_host_ptr(block,vaddr); return(1); } return(0); } /* Check if PC register matches the compiled block virtual address */ static forced_inline int mips64_jit_tcb_match(cpu_mips_t *cpu,mips64_jit_tcb_t *block) { m_uint64_t vpage; vpage = cpu->pc & ~(m_uint64_t)MIPS_MIN_PAGE_IMASK; return(block->start_pc == vpage); } /* Compute the hash index for the specified PC value */ static forced_inline m_uint32_t mips64_jit_get_pc_hash(m_uint64_t pc) { m_uint32_t page_hash; page_hash = sbox_u32(pc >> MIPS_MIN_PAGE_SHIFT); return((page_hash ^ (page_hash >> 12)) & MIPS_JIT_PC_HASH_MASK); } /* Check if there are pending IRQ */ extern void mips64_check_pending_irq(mips64_jit_tcb_t *b); /* Initialize instruction lookup table */ void mips64_jit_create_ilt(void); /* Initialize the JIT structure */ int mips64_jit_init(cpu_mips_t *cpu); /* Flush the JIT */ u_int mips64_jit_flush(cpu_mips_t *cpu,u_int threshold); /* Shutdown the JIT */ void mips64_jit_shutdown(cpu_mips_t *cpu); /* Check if an instruction is in a delay slot or not */ int mips64_jit_is_delay_slot(mips64_jit_tcb_t *b,m_uint64_t pc); /* Fetch a MIPS instruction and emit corresponding x86 translated code */ struct mips64_insn_tag *mips64_jit_fetch_and_emit(cpu_mips_t *cpu, mips64_jit_tcb_t *block, int delay_slot); /* Record a patch to apply in a compiled block */ int mips64_jit_tcb_record_patch(mips64_jit_tcb_t *block,u_char *x86_ptr, m_uint64_t vaddr); /* Free an instruction block */ void mips64_jit_tcb_free(cpu_mips_t *cpu,mips64_jit_tcb_t *block, int list_removal); /* Execute compiled MIPS code */ void *mips64_jit_run_cpu(cpu_gen_t *cpu); /* Set the Pointer Counter (PC) register */ void mips64_set_pc(mips64_jit_tcb_t *b,m_uint64_t new_pc); /* Set the Return Address (RA) register */ void mips64_set_ra(mips64_jit_tcb_t *b,m_uint64_t ret_pc); /* Single-step operation */ void mips64_emit_single_step(mips64_jit_tcb_t *b,mips_insn_t insn); /* Virtual Breakpoint */ void mips64_emit_breakpoint(mips64_jit_tcb_t *b); /* Emit unhandled instruction code */ int mips64_emit_invalid_delay_slot(mips64_jit_tcb_t *b); /* * Increment count register and trigger the timer IRQ if value in compare * register is the same. */ void mips64_inc_cp0_count_reg(mips64_jit_tcb_t *b); /* Increment the number of executed instructions (performance debugging) */ void mips64_inc_perf_counter(mips64_jit_tcb_t *b); #endif dynamips-0.2.14/stable/mips64_mem.c000066400000000000000000000356531241034141600170300ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "cpu.h" #include "mips64_jit.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" /* MTS access with special access mask */ void mips64_access_special(cpu_mips_t *cpu,m_uint64_t vaddr,m_uint32_t mask, u_int op_code,u_int op_type,u_int op_size, m_uint64_t *data) { switch(mask) { case MTS_ACC_U: if (op_type == MTS_READ) *data = 0; if (cpu->gen->undef_mem_handler != NULL) { if (cpu->gen->undef_mem_handler(cpu->gen,vaddr,op_size,op_type, data)) return; } #if DEBUG_MTS_ACC_U if (op_type == MTS_READ) cpu_log(cpu->gen, "MTS","read access to undefined address 0x%llx at " "pc=0x%llx (size=%u)\n",vaddr,cpu->pc,op_size); else cpu_log(cpu->gen, "MTS","write access to undefined address 0x%llx at " "pc=0x%llx, value=0x%8.8llx (size=%u)\n", vaddr,cpu->pc,*data,op_size); #endif break; case MTS_ACC_T: if (op_code != MIPS_MEMOP_LOOKUP) { /* GR edit */ /* If the IOS tries to access memory at addr 0x0, it is probably */ /* a reload. Shut the vm down, otherwise 100% cpu and livespin */ if (vaddr == 0) { cpu_log(cpu->gen, "MTS","TLB exception suggests RELOAD for address 0x%llx at pc=0x%llx " "(%s access, size=%u)\n", vaddr,cpu->pc,(op_type == MTS_READ) ? "read":"write",op_size); vm_stop(cpu->vm); } /* GR edit end */ #if DEBUG_MTS_ACC_T cpu_log(cpu->gen, "MTS","TLB exception for address 0x%llx at pc=0x%llx " "(%s access, size=%u)\n", vaddr,cpu->pc,(op_type == MTS_READ) ? "read":"write",op_size); mips64_dump_regs(cpu->gen); #if MEMLOG_ENABLE memlog_dump(cpu->gen); #endif #endif cpu->cp0.reg[MIPS_CP0_BADVADDR] = vaddr; if (op_type == MTS_READ) mips64_trigger_exception(cpu,MIPS_CP0_CAUSE_TLB_LOAD,0); else mips64_trigger_exception(cpu,MIPS_CP0_CAUSE_TLB_SAVE,0); cpu_exec_loop_enter(cpu->gen); } break; case MTS_ACC_AE: if (op_code != MIPS_MEMOP_LOOKUP) { #if DEBUG_MTS_ACC_AE cpu_log(cpu->gen, "MTS","AE exception for address 0x%llx at pc=0x%llx " "(%s access)\n", vaddr,cpu->pc,(op_type == MTS_READ) ? "read":"write"); #endif cpu->cp0.reg[MIPS_CP0_BADVADDR] = vaddr; if (op_type == MTS_READ) mips64_trigger_exception(cpu,MIPS_CP0_CAUSE_ADDR_LOAD,0); else mips64_trigger_exception(cpu,MIPS_CP0_CAUSE_ADDR_SAVE,0); cpu_exec_loop_enter(cpu->gen); } break; } } /* === MTS for 64-bit address space ======================================= */ #define MTS_ADDR_SIZE 64 #define MTS_NAME(name) mts64_##name #define MTS_NAME_UP(name) MTS64_##name #define MTS_PROTO(name) mips64_mts64_##name #define MTS_PROTO_UP(name) MIPS64_MTS64_##name #include "mips_mts.c" /* === MTS for 32-bit address space ======================================= */ #define MTS_ADDR_SIZE 32 #define MTS_NAME(name) mts32_##name #define MTS_NAME_UP(name) MTS32_##name #define MTS_PROTO(name) mips64_mts32_##name #define MTS_PROTO_UP(name) MIPS64_MTS32_##name #include "mips_mts.c" /* === Specific operations for MTS64 ====================================== */ /* MTS64 slow lookup */ static mts64_entry_t * mips64_mts64_slow_lookup(cpu_mips_t *cpu,m_uint64_t vaddr, u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data, mts64_entry_t *alt_entry) { m_uint32_t hash_bucket,zone,sub_zone,cca; mts64_entry_t *entry; mts_map_t map; map.tlb_index = -1; hash_bucket = MTS64_HASH(vaddr); entry = &cpu->mts_u.mts64_cache[hash_bucket]; zone = vaddr >> 40; #if DEBUG_MTS_STATS cpu->mts_misses++; #endif switch(zone) { case 0x000000: /* xkuseg */ case 0x400000: /* xksseg */ case 0xc00000: /* xkseg */ /* trigger TLB exception if no matching entry found */ if (!mips64_cp0_tlb_lookup(cpu,vaddr,&map)) goto err_tlb; if (!(entry = mips64_mts64_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return(entry); case 0xffffff: sub_zone = (vaddr >> 29) & 0x7FF; switch(sub_zone) { case 0x7fc: /* ckseg0 */ map.vaddr = vaddr & MIPS_MIN_PAGE_MASK; map.paddr = map.vaddr - 0xFFFFFFFF80000000ULL; map.offset = vaddr & MIPS_MIN_PAGE_IMASK; map.cached = TRUE; if (!(entry = mips64_mts64_map(cpu,op_type,&map, entry,alt_entry))) goto err_undef; return(entry); case 0x7fd: /* ckseg1 */ map.vaddr = vaddr & MIPS_MIN_PAGE_MASK; map.paddr = map.vaddr - 0xFFFFFFFFA0000000ULL; map.offset = vaddr & MIPS_MIN_PAGE_IMASK; map.cached = FALSE; if (!(entry = mips64_mts64_map(cpu,op_type,&map, entry,alt_entry))) goto err_undef; return(entry); case 0x7fe: /* cksseg */ case 0x7ff: /* ckseg3 */ /* trigger TLB exception if no matching entry found */ if (!mips64_cp0_tlb_lookup(cpu,vaddr,&map)) goto err_tlb; if (!(entry = mips64_mts64_map(cpu,op_type, &map,entry,alt_entry))) goto err_undef; return(entry); default: /* Invalid zone: generate Address Error (AE) exception */ goto err_address; } break; /* xkphys */ case 0x800000: case 0x880000: case 0x900000: case 0x980000: case 0xa00000: case 0xa80000: case 0xb00000: case 0xb80000: cca = (vaddr >> MIPS64_XKPHYS_CCA_SHIFT) & 0x03; map.cached = mips64_cca_cached(cca); map.vaddr = vaddr & MIPS_MIN_PAGE_MASK; map.paddr = (vaddr & MIPS64_XKPHYS_PHYS_MASK); map.paddr &= MIPS_MIN_PAGE_MASK; map.offset = vaddr & MIPS_MIN_PAGE_IMASK; if (!(entry = mips64_mts64_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return(entry); default: /* Invalid zone: generate Address Error (AE) exception */ goto err_address; } err_undef: mips64_access_special(cpu,vaddr,MTS_ACC_U,op_code,op_type,op_size,data); return NULL; err_address: mips64_access_special(cpu,vaddr,MTS_ACC_AE,op_code,op_type,op_size,data); return NULL; err_tlb: mips64_access_special(cpu,vaddr,MTS_ACC_T,op_code,op_type,op_size,data); return NULL; } /* MTS64 access */ static forced_inline void *mips64_mts64_access(cpu_mips_t *cpu,m_uint64_t vaddr, u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data) { mts64_entry_t *entry,alt_entry; m_uint32_t hash_bucket; m_iptr_t haddr; u_int dev_id; int cow; #if MEMLOG_ENABLE /* Record the memory access */ memlog_rec_access(cpu->gen,vaddr,*data,op_size,op_type); #endif hash_bucket = MTS64_HASH(vaddr); entry = &cpu->mts_u.mts64_cache[hash_bucket]; #if DEBUG_MTS_STATS cpu->mts_lookups++; #endif /* Copy-On-Write for sparse device ? */ cow = (op_type == MTS_WRITE) && (entry->flags & MTS_FLAG_COW); /* Slow lookup if nothing found in cache */ if (unlikely(((vaddr & MIPS_MIN_PAGE_MASK) != entry->gvpa) || cow)) { entry = mips64_mts64_slow_lookup(cpu,vaddr,op_code,op_size,op_type, data,&alt_entry); if (!entry) return NULL; if (entry->flags & MTS_FLAG_DEV) { dev_id = (entry->hpa & MTS_DEVID_MASK) >> MTS_DEVID_SHIFT; haddr = entry->hpa & MTS_DEVOFF_MASK; return(dev_access_fast(cpu->gen,dev_id,haddr,op_size,op_type,data)); } } /* Raw memory access */ haddr = entry->hpa + (vaddr & MIPS_MIN_PAGE_IMASK); #if MEMLOG_ENABLE memlog_update_read(cpu->gen,haddr); #endif return((void *)haddr); } /* MTS64 virtual address to physical page translation */ static fastcall int mips64_mts64_translate(cpu_mips_t *cpu,m_uint64_t vaddr, m_uint32_t *phys_page) { mts64_entry_t *entry,alt_entry; m_uint32_t hash_bucket; m_uint64_t data = 0; hash_bucket = MTS64_HASH(vaddr); entry = &cpu->mts_u.mts64_cache[hash_bucket]; /* Slow lookup if nothing found in cache */ if (unlikely((vaddr & MIPS_MIN_PAGE_MASK) != entry->gvpa)) { entry = mips64_mts64_slow_lookup(cpu,vaddr,MIPS_MEMOP_LOOKUP,4,MTS_READ, &data,&alt_entry); if (!entry) return(-1); } *phys_page = entry->gppa >> MIPS_MIN_PAGE_SHIFT; return(0); } /* === Specific operations for MTS32 ====================================== */ /* MTS32 slow lookup */ static mts32_entry_t * mips64_mts32_slow_lookup(cpu_mips_t *cpu,m_uint64_t vaddr, u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data, mts32_entry_t *alt_entry) { m_uint32_t hash_bucket,zone; mts32_entry_t *entry; mts_map_t map; map.tlb_index = -1; hash_bucket = MTS32_HASH(vaddr); entry = &cpu->mts_u.mts32_cache[hash_bucket]; zone = (vaddr >> 29) & 0x7; #if DEBUG_MTS_STATS cpu->mts_misses++; #endif switch(zone) { case 0x00 ... 0x03: /* kuseg */ /* trigger TLB exception if no matching entry found */ if (!mips64_cp0_tlb_lookup(cpu,vaddr,&map)) goto err_tlb; if (!(entry = mips64_mts32_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return(entry); case 0x04: /* kseg0 */ map.vaddr = vaddr & MIPS_MIN_PAGE_MASK; map.paddr = map.vaddr - 0xFFFFFFFF80000000ULL; map.offset = vaddr & MIPS_MIN_PAGE_IMASK; map.cached = TRUE; if (!(entry = mips64_mts32_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return(entry); case 0x05: /* kseg1 */ map.vaddr = vaddr & MIPS_MIN_PAGE_MASK; map.paddr = map.vaddr - 0xFFFFFFFFA0000000ULL; map.offset = vaddr & MIPS_MIN_PAGE_IMASK; map.cached = FALSE; if (!(entry = mips64_mts32_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return(entry); case 0x06: /* ksseg */ case 0x07: /* kseg3 */ /* trigger TLB exception if no matching entry found */ if (!mips64_cp0_tlb_lookup(cpu,vaddr,&map)) goto err_tlb; if (!(entry = mips64_mts32_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return(entry); } err_undef: mips64_access_special(cpu,vaddr,MTS_ACC_U,op_code,op_type,op_size,data); return NULL; #if 0 err_address: mips64_access_special(cpu,vaddr,MTS_ACC_AE,op_code,op_type,op_size,data); return NULL; #endif err_tlb: mips64_access_special(cpu,vaddr,MTS_ACC_T,op_code,op_type,op_size,data); return NULL; } /* MTS32 access */ static forced_inline void *mips64_mts32_access(cpu_mips_t *cpu,m_uint64_t vaddr, u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data) { mts32_entry_t *entry,alt_entry; m_uint32_t hash_bucket; m_iptr_t haddr; u_int dev_id; int cow; #if MEMLOG_ENABLE /* Record the memory access */ memlog_rec_access(cpu->gen,vaddr,*data,op_size,op_type); #endif hash_bucket = MTS32_HASH(vaddr); entry = &cpu->mts_u.mts32_cache[hash_bucket]; #if DEBUG_MTS_STATS cpu->mts_lookups++; #endif /* Copy-On-Write for sparse device ? */ cow = (op_type == MTS_WRITE) && (entry->flags & MTS_FLAG_COW); /* Slow lookup if nothing found in cache */ if (unlikely((((m_uint32_t)vaddr & MIPS_MIN_PAGE_MASK) != entry->gvpa) || cow)) { entry = mips64_mts32_slow_lookup(cpu,vaddr,op_code,op_size,op_type, data,&alt_entry); if (!entry) return NULL; if (entry->flags & MTS_FLAG_DEV) { dev_id = (entry->hpa & MTS_DEVID_MASK) >> MTS_DEVID_SHIFT; haddr = entry->hpa & MTS_DEVOFF_MASK; return(dev_access_fast(cpu->gen,dev_id,haddr,op_size,op_type,data)); } } /* Raw memory access */ haddr = entry->hpa + (vaddr & MIPS_MIN_PAGE_IMASK); #if MEMLOG_ENABLE memlog_update_read(cpu->gen,haddr); #endif return((void *)haddr); } /* MTS32 virtual address to physical page translation */ static fastcall int mips64_mts32_translate(cpu_mips_t *cpu,m_uint64_t vaddr, m_uint32_t *phys_page) { mts32_entry_t *entry,alt_entry; m_uint32_t hash_bucket; m_uint64_t data = 0; hash_bucket = MTS32_HASH(vaddr); entry = &cpu->mts_u.mts32_cache[hash_bucket]; /* Slow lookup if nothing found in cache */ if (unlikely(((m_uint32_t)vaddr & MIPS_MIN_PAGE_MASK) != entry->gvpa)) { entry = mips64_mts32_slow_lookup(cpu,vaddr,MIPS_MEMOP_LOOKUP,4,MTS_READ, &data,&alt_entry); if (!entry) return(-1); } *phys_page = entry->gppa >> MIPS_MIN_PAGE_SHIFT; return(0); } /* ======================================================================== */ /* Shutdown MTS subsystem */ void mips64_mem_shutdown(cpu_mips_t *cpu) { if (cpu->mts_shutdown != NULL) cpu->mts_shutdown(cpu); } /* Set the address mode */ int mips64_set_addr_mode(cpu_mips_t *cpu,u_int addr_mode) { if (cpu->addr_mode != addr_mode) { mips64_mem_shutdown(cpu); switch(addr_mode) { case 32: mips64_mts32_init(cpu); mips64_mts32_init_memop_vectors(cpu); break; case 64: mips64_mts64_init(cpu); mips64_mts64_init_memop_vectors(cpu); break; default: fprintf(stderr, "mts_set_addr_mode: internal error (addr_mode=%u)\n", addr_mode); exit(EXIT_FAILURE); } } return(0); } dynamips-0.2.14/stable/mips64_microcode000077500000000000000000000372141241034141600177730ustar00rootroot00000000000000ELF¿À4<ü 4 (     ¿À¿À(à(àð¨'½þÿºÐÿ»Ø'ºÿºèÿ¿ø<¿À'Zà<¿À'{@'½þÿºÐÿ»Ø'ºÿºèÿ¿ø<¿À'Zà<¿À'{@'½þÿºÐÿ»Ø'ºÿºèÿ¿ø<¿À'Zà<¿À'{Ð@'½þÿºÐÿ»Ø'ºÿºèÿ¿ø<¿À'Zà<¿À'{@'½þÿºÐÿ»Ø'ºÿºèÿ¿ø<¿À'Zà<¿À'{@ÿ¡ÿ¢ÿ£ÿ¤ ÿ¥(ÿ¦0ÿ§8ÿ¨@ÿ©HÿªPÿ«Xÿ¬`ÿ­hÿ®pÿ¯xÿ°€ÿ±ˆÿ²ÿ³˜ÿ´ ÿµ¨ÿ¶°ÿ·¸ÿ¸Àÿ¹Èÿ¼àÿ¾ðÿ¿ø (ÿ¤ÿ¥@`¯¤@h¯¤ @%@ÿ¥@&pÿ¦(@'ðÿ§0  -`ø ¤@„`ߤ(@¤pߥ0@¥ðߤߥ€ ß»Øß¡ߢߣߤ ߥ(ߦ0ß§8ߨ@ß©HߪPß«X߬`ß­hß®p߯xß°€ß±ˆß²ß³˜ß´ ßµ¨ß¶°ß·¸ß¸Àß¹Èß¼àß¾ðß¿øß½èB¿À°¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿À°¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà@`à@„`à@hà@„hà@Hà@„Hà@„Xà< % ý< % ­<¿À'Zà€è-g½ÿÀ$ÿù¨è$€@-$(-0-ø < % < % Ýà<€7½@<¿À--(-0-8-@-H-P-X-`-h-p-€-ˆ--˜- -¨-°-¸-À-È-Ð-Ø-à-ð-ÿÇ'½ÿØÿ¿ ð‰ -<ð4„B@ðu4Dß¿ ðy'½(Œ‚ '½ÿÈÿ°(ÿ¿0@€€-Ü‚(dBü‚(Ž<ÿÿ4Bý‚ $ðy4„€Ž$Ž,Ž4Ž<Þ(𨯢$<>ú@þ<€b%þß¿0ß°(à'½8<¿À$¥" ðô$Þ(dc ðãþ(Œƒ <¿À0c|@$B'Øb!Œy'½ÿÐÿ° ÿ¿( €€-Œeß¿(ß°  '½0Œf<¿À$¥"Hðô -Ž0cP`Þ(<¿ÀÞ0$¥"Pðô -ŽŽ <¿Àß¿(ß° $¥"` - ðô'½0<¿À$¥"Pðô - ð%Ž'½ÿÐ<¿Àÿ° $¥"ˆ€€-ÿ¿(ð -Ž0BP@Þ(Þ0<¿À$¥"Pðô -ŽŽ <¿Àß¿(ß° $¥"` - ðô'½0<¿À$¥"Pðô -ŽŽ <¿Àß¿(ß° $¥"` - ðô'½0'½ÿÀÿ° €€-Ž Œ„ÿ²0ÿ±(0’ÿ0Qÿ2$0b@0ÿ¿80b@0b€@30b@@C0‚T@<¿ÀÞ(<¿À$¥"Pðô - ðŽð}$ýÿðD $<¿À$¥"Àðô -Ž0cP`ÿíÞ(<¿ÀÞ0$¥"Pðô -ŽŽ <¿Àß¿8ß²0ß±(ß° $¥"` - ðô'½@ð}$þÿðD $<¿À ð$¥" ð}<ÿÿ4„ÿðD $ð…<4„B@ß¿8ß²0ß±(ß° D ! ð'½@<¿À$¥"àðô -ŽŽ <¿À$¥"øðô -<¿À2:ß¿8ß²0ß±(ß° $¥#0 - ðô'½@€¦À€-€- f$¥€¦Àÿü$càÀ€-¢ ‚€£` 8-à€-¢ b€£`$¥$çæÿù‡!€-à€‚@(-$¥…!€CT`ÿý$¥à -'½ÿ°ÿ²8ÿ°(ÿ¿Hÿ³@ÿ±0 €-€¥'¢`ÿ¦`ÿ§h¯¢  "€-<¿À ð $S&0ð†@ -&‚ ß¿H$%T¢ÿø0¥ÿ&‚$sba(bt@$xeX$cbÿð&¢ @ -E$Bð†¯¢ ‚ ÿë$%ß¿Hß³@ß²8ß±0ß°(-à'½PTbÿÜ&¢ ŒQ$B',ƒ ¯¢ `$…0$…Wð†@ -0B,C `$E0$EWð†@ -0B,C `$E0$EWð†@ -0B,C `$E0$EWð†@ -0B,C `$E0$EWð†@ -0B,C `$E0$EWð†@ -0B,C `$E0$EWð†@ -2#,b @ÿš$eW ð$e0@ -ð†$% ð &¢ ŒEP `(-$B@ -悁ð & ðƒ€ƒ¬CT$„€ƒ`ÿü<¶$¬CXŒDX€$ÿÿ$Æÿÿ<¶ŒâT$„ ¢$¥†*@  `ÿö$¬âX-à 𡀃¬CT$„€ƒ`ÿü<¶$¬CXà$'½ÿ¨,‚…ÿ²Hÿ±@ÿ¿Pÿ°8€@- H-Àˆ-@ à-<¿À§|$¥&°0-$¯©$¯±,ðô¯²4 ðÈ$ÿÿ<¿À€$c C!ŒD€<¶ŒC-ß¿Pß²Hß±@ß°8 -à'½X<¶¬@LŒCP$¤¤£$<¶¬ELŒCP$¥$@¤ƒ¢ÿù$„(-ß¿Pß²Hß±@ß°8 -à'½X<¿À$¥&8 -ðô<¶Ž0`•$<¿À$¥&Xðô -ð¬Ž( ðÈ(-< ðÈŒD  ðÈ$<  ðÈŒE <¶ŒdH0‚€@<ÿÿŒbDŒEà ÿÂ<ÿÿ4Bÿ ðÈ‚($<¶ŒCD ðÈ$e<¶ŒCD$ ðȬià ðÈ$€ ðÈ$ €-<¿À$¥&p$ðô 0-‚`<¶¬CT&‚`ÿü<¶$¬CXŒDX€ÿŸ$ÿÿ 0-&Gÿÿ-<¶Œ‚T Â$Æ@ À$cg*@ÿö$(-¬‚X ðÉß¿P$<¶(- ðȬC4<¶ŒC$ ðÈ(' ðÈ$ 0¥ÿð† - ðÈ(-<¶ ðÈŒE$<¶ŒC ðÈ-<¶ŒC ðÈ*€<  ðÈŒE <¿À ðÈŒE(Ø<  ðÈŒE  €-<¿À$¥&$ðô 0-‚P`$<¶¬CT&‚`ÿü<¶$<¶$¬CX ðÉß¿P<¶ ðÈŒE,<¿À ðÈ$EH< ðÈŒD  ðÈ$`ø ®4 ðÈ(-€0¥ÿ<¶à¬E@<¶à¬E<£`€<¶¬C<$¥£T`ÿý¬C<à¬C@$¥£`ÿü<¶ ðšÈ- <¿À'½ÿÈ$¥' -ÿ¿0ÿ° ðôÿ±(<¿À$¥'0$ðô<¶<”Ž4B³ßPb ŽD<¿À$¥'Hðô --`ø ŽD<¿À&€` -$¥'< ðȬC &<¿À` -$¥'˜< ¬C ðÈ&€< ¬P ðÌ<¿À<¶Ž( -ðô&%' ð‘Ž(Ž0T@ÿø<¶<¿À$¥'Àðô -$®4ß¿0ß±(ß° à'½8¿ÀÌ¿À$¿À¿ÀÌ¿À¿ÀÌ¿À ¿ÀÜ¿À˜¿ÀÌ¿Àp¿ÀÌ¿ÀÌ¿Àd¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀT¿ÀÌ¿ÀÌ¿ÀD¿À8¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿Àì¿ÀØ¿À|¿Àô¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿Àè¿Àø¿À<¿Àˆ¿À\¿ÀT¿Àx¿Àx¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿À(¿ÀL¿ÀÌ¿ÀÌ¿Àð¿À8¿Àø¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿Àð¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀÌ¿ÀWarning: System Call in BD Slot. %s PC = 0x%x, Cause = 0x%x, Status Reg = 0x%x Cache Exception: *** Software Interrupt 0 *** *** Software Interrupt 1 *** *** Unknown IRQ *** Stacked Cause Reg = 0x%x, Stacked Status Reg = 0x%x Current Int Cause = 0x%x, Current Int Status = 0x%x *** CPU Interrupt ****** TLB Modification Exception ****** TLB (Load/Fetch) Exception ****** TLB (Store) Exception ****** Address Error (Load/Fetch) Exception ****** Address Error (Store) Exception ****** Bus Error (Fetch) Exception ****** Bus Error (Load) Exception ****** System Call Exception ****** Break Point Exception ****** Illegal Opcode Exception ****** Coprocessor Unusable Exception ****** Arithmetic Overflow Exception ****** Trap Exception ****** Virtual Coherency (Opcode) Exception ****** Floating Point Exception ****** Reserved General Exception ****** Watch Exception ****** Virtual Coherency (Data) Exception ***(null) ROM: reload requested... ROM: Restarting IOS... trying to read bootvar '%s' trying to set bootvar '%s' unhandled syscall 0x%x at pc=0x%x (a1=0x%x,a2=0x%x,a3=0x%x) ROMMON Emulation Microcode ROMMON emulation microcode. Microcode has started. The microcode need to be upgraded to match your emulator version. IOSBOOTLDRLaunching IOS image at 0x%x... Image returned to ROM. ¿À#h¿À p¿À#€¿À#¨¿À#пÀ#ð¿À$ ¿À$H¿À$p¿À$˜¿Àh¿À$¸¿À$Ø¿À%¿À%(¿À%P¿À%h¿À%˜¿À%À¿À%À¿À%À¿À%À¿À%À¿À%À¿À%À¿À%è¿À%À¿À%À¿À%À¿À%À¿À%À¿À%À¿À%À¿À&¿À&ðÿÿÿþ¿À°¿Àà¿ÀÔ¿Àä¿Àô¿À¿À¿À$¿À4¿ÀD¿À°¿À0€ÿÿÿø(¿Àh€ÿÿÿø8¿À€ÿÿÿø0¿ÀЀÿÿÿø0¿À p€ÿÿÿø@¿À ¿À P¿À ¨¿À ЀÿÿÿøP¿À ø¿Àp¿À €ÿÿÿøX¿À¿À@¿À¿À €ÿÿÿø8GCC: (GNU) 4.2.0GCC: (GNU) 4.2.0GCC: (GNU) 4.2.0GCC: (GNU) 4.2.0GCC: (GNU) 4.2.0.shstrtab.text.rodata.rodata.str1.8.data.bss.reginfo.pdr.comment ¿À ¿À 0 2¿À" 2 ¸(¿À'Ø7Ø.  t 3p8à<8ø`A #include #include #include #include #include #include #include "cpu.h" #include "mips64_jit.h" #include "mips64_nojit_trans.h" /* Set an IRQ */ void mips64_set_irq(cpu_mips_t *cpu,m_uint8_t irq) { m_uint32_t m; m = (1 << (irq + MIPS_CP0_CAUSE_ISHIFT)) & MIPS_CP0_CAUSE_IMASK; MIPS64_IRQ_LOCK(cpu); cpu->irq_cause |= m; MIPS64_IRQ_UNLOCK(cpu); } /* Clear an IRQ */ void mips64_clear_irq(cpu_mips_t *cpu,m_uint8_t irq) { m_uint32_t m; m = (1 << (irq + MIPS_CP0_CAUSE_ISHIFT)) & MIPS_CP0_CAUSE_IMASK; MIPS64_IRQ_LOCK(cpu); cpu->irq_cause &= ~m; MIPS64_IRQ_UNLOCK(cpu); if (!cpu->irq_cause) cpu->irq_pending = 0; } #define EMPTY(func) func { \ fprintf(stderr,"This function should not be called: "#func"\n"); \ abort(); \ } EMPTY(void mips64_jit_tcb_push_epilog(mips64_jit_tcb_t *block)); EMPTY(void mips64_jit_tcb_exec(cpu_mips_t *cpu,mips64_jit_tcb_t *block)); EMPTY(void mips64_set_pc(mips64_jit_tcb_t *b,m_uint64_t new_pc)); EMPTY(void mips64_set_ra(mips64_jit_tcb_t *b,m_uint64_t ret_pc)); EMPTY(void mips64_emit_breakpoint(mips64_jit_tcb_t *b)); EMPTY(void mips64_emit_single_step(mips64_jit_tcb_t *b,mips_insn_t insn)); EMPTY(int mips64_emit_invalid_delay_slot(mips64_jit_tcb_t *b)); EMPTY(void mips64_inc_cp0_count_reg(mips64_jit_tcb_t *b)); EMPTY(void mips64_check_pending_irq(mips64_jit_tcb_t *b)); EMPTY(void mips64_inc_perf_counter(mips64_jit_tcb_t *b)); /* MIPS instruction array */ struct mips64_insn_tag mips64_insn_tags[] = { { NULL, 0, 0, 0 }, }; dynamips-0.2.14/stable/mips64_nojit_trans.h000066400000000000000000000012701241034141600205750ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Just an empty JIT template file for architectures not supported by the JIT * code. */ #ifndef __MIPS64_NOJIT_TRANS_H__ #define __MIPS64_NOJIT_TRANS_H__ #include "utils.h" #include "cpu.h" #include "dynamips.h" #define JIT_SUPPORT 0 #define mips64_jit_tcb_set_patch(a,b) #define mips64_jit_tcb_set_jump(a,b) /* MIPS instruction array */ extern struct mips64_insn_tag mips64_insn_tags[]; /* Push epilog for an x86 instruction block */ void mips64_jit_tcb_push_epilog(mips64_jit_tcb_t *block); /* Execute JIT code */ void mips64_jit_tcb_exec(cpu_mips_t *cpu,mips64_jit_tcb_t *block); #endif dynamips-0.2.14/stable/mips64_x86_trans.c000066400000000000000000002550501241034141600201010ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #include #include #include #include #include #include #include #include "cpu.h" #include "mips64_jit.h" #include "mips64_x86_trans.h" #include "mips64_cp0.h" #include "memory.h" /* Macros for CPU structure access */ #define REG_OFFSET(reg) (OFFSET(cpu_mips_t,gpr[(reg)])) #define CP0_REG_OFFSET(c0reg) (OFFSET(cpu_mips_t,cp0.reg[(c0reg)])) #define MEMOP_OFFSET(op) (OFFSET(cpu_mips_t,mem_op_fn[(op)])) #define DECLARE_INSN(name) \ static int mips64_emit_##name(cpu_mips_t *cpu,mips64_jit_tcb_t *b, \ mips_insn_t insn) /* Set an IRQ */ void mips64_set_irq(cpu_mips_t *cpu,m_uint8_t irq) { m_uint32_t m; m = (1 << (irq + MIPS_CP0_CAUSE_ISHIFT)) & MIPS_CP0_CAUSE_IMASK; atomic_or(&cpu->irq_cause,m); } /* Clear an IRQ */ void mips64_clear_irq(cpu_mips_t *cpu,m_uint8_t irq) { m_uint32_t m; m = (1 << (irq + MIPS_CP0_CAUSE_ISHIFT)) & MIPS_CP0_CAUSE_IMASK; atomic_and(&cpu->irq_cause,~m); if (!cpu->irq_cause) cpu->irq_pending = 0; } /* Load a 64 bit immediate value */ static inline void mips64_load_imm(mips64_jit_tcb_t *b, u_int hi_reg,u_int lo_reg, m_uint64_t value) { m_uint32_t hi_val = value >> 32; m_uint32_t lo_val = value & 0xffffffff; if (lo_val) x86_mov_reg_imm(b->jit_ptr,lo_reg,lo_val); else x86_alu_reg_reg(b->jit_ptr,X86_XOR,lo_reg,lo_reg); if (hi_val) x86_mov_reg_imm(b->jit_ptr,hi_reg,hi_val); else x86_alu_reg_reg(b->jit_ptr,X86_XOR,hi_reg,hi_reg); } /* Set the Pointer Counter (PC) register */ void mips64_set_pc(mips64_jit_tcb_t *b,m_uint64_t new_pc) { x86_mov_membase_imm(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,pc), new_pc & 0xFFFFFFFF,4); x86_mov_membase_imm(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,pc)+4, new_pc >> 32,4); } /* Set the Return Address (RA) register */ void mips64_set_ra(mips64_jit_tcb_t *b,m_uint64_t ret_pc) { mips64_load_imm(b,X86_EDX,X86_EAX,ret_pc); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(MIPS_GPR_RA),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(MIPS_GPR_RA)+4,X86_EDX,4); } /* * Try to branch directly to the specified JIT block without returning to * main loop. */ static void mips64_try_direct_far_jump(cpu_mips_t *cpu,mips64_jit_tcb_t *b, m_uint64_t new_pc) { m_uint64_t new_page; m_uint32_t pc_hash,pc_offset; u_char *test1,*test2,*test3,*test4; new_page = new_pc & MIPS_MIN_PAGE_MASK; pc_offset = (new_pc & MIPS_MIN_PAGE_IMASK) >> 2; pc_hash = mips64_jit_get_pc_hash(new_pc); /* Get JIT block info in %edx */ x86_mov_reg_membase(b->jit_ptr,X86_EBX, X86_EDI,OFFSET(cpu_mips_t,exec_blk_map),4); x86_mov_reg_membase(b->jit_ptr,X86_EDX,X86_EBX,pc_hash*sizeof(void *),4); /* no JIT block found ? */ x86_test_reg_reg(b->jit_ptr,X86_EDX,X86_EDX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_Z, 0, 1); /* Check block PC (lower 32-bits first) */ x86_mov_reg_imm(b->jit_ptr,X86_EAX,(m_uint32_t)new_page); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EAX,X86_EDX, OFFSET(mips64_jit_tcb_t,start_pc)); test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Check higher bits... */ x86_mov_reg_imm(b->jit_ptr,X86_ECX,new_page >> 32); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_ECX,X86_EDX, OFFSET(mips64_jit_tcb_t,start_pc)+4); test3 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Jump to the code */ x86_mov_reg_membase(b->jit_ptr,X86_ESI, X86_EDX,OFFSET(mips64_jit_tcb_t,jit_insn_ptr),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX, X86_ESI,pc_offset * sizeof(void *),4); x86_test_reg_reg(b->jit_ptr,X86_EBX,X86_EBX); test4 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_Z, 0, 1); x86_jump_reg(b->jit_ptr,X86_EBX); /* Returns to caller... */ x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); x86_patch(test3,b->jit_ptr); x86_patch(test4,b->jit_ptr); mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); } /* Set Jump */ static void mips64_set_jump(cpu_mips_t *cpu,mips64_jit_tcb_t *b, m_uint64_t new_pc,int local_jump) { int return_to_caller = FALSE; u_char *jump_ptr; if (cpu->sym_trace && !local_jump) return_to_caller = TRUE; if (!return_to_caller && mips64_jit_tcb_local_addr(b,new_pc,&jump_ptr)) { if (jump_ptr) { x86_jump_code(b->jit_ptr,jump_ptr); } else { /* Never jump directly to code in a delay slot */ if (mips64_jit_is_delay_slot(b,new_pc)) { mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); return; } mips64_jit_tcb_record_patch(b,b->jit_ptr,new_pc); x86_jump32(b->jit_ptr,0); } } else { if (cpu->exec_blk_direct_jump) { /* Block lookup optimization */ mips64_try_direct_far_jump(cpu,b,new_pc); } else { mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); } } } /* Basic C call */ static forced_inline void mips64_emit_basic_c_call(mips64_jit_tcb_t *b,void *f) { x86_mov_reg_imm(b->jit_ptr,X86_EBX,f); x86_call_reg(b->jit_ptr,X86_EBX); } /* Emit a simple call to a C function without any parameter */ static void mips64_emit_c_call(mips64_jit_tcb_t *b,void *f) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); mips64_emit_basic_c_call(b,f); } /* Single-step operation */ void mips64_emit_single_step(mips64_jit_tcb_t *b,mips_insn_t insn) { x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); x86_mov_reg_imm(b->jit_ptr,X86_EDX,insn); mips64_emit_basic_c_call(b,mips64_exec_single_step); } /* Fast memory operation prototype */ typedef void (*memop_fast_access)(mips64_jit_tcb_t *b,int target); /* Fast LW */ static void mips64_memop_fast_lw(mips64_jit_tcb_t *b,int target) { x86_mov_reg_memindex(b->jit_ptr,X86_EAX,X86_EAX,0,X86_EBX,0,4); x86_bswap(b->jit_ptr,X86_EAX); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(target),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(target)+4,X86_EDX,4); } /* Fast SW */ static void mips64_memop_fast_sw(mips64_jit_tcb_t *b,int target) { x86_mov_reg_membase(b->jit_ptr,X86_EDX,X86_EDI,REG_OFFSET(target),4); x86_bswap(b->jit_ptr,X86_EDX); x86_mov_memindex_reg(b->jit_ptr,X86_EAX,0,X86_EBX,0,X86_EDX,4); } /* Fast memory operation (64-bit) */ static void mips64_emit_memop_fast64(mips64_jit_tcb_t *b,int write_op, int opcode,int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { m_uint64_t val = sign_extend(offset,16); u_char *test1,*test2,*test3,*p_exit; test3 = NULL; /* ECX:EBX = sign-extended offset */ mips64_load_imm(b,X86_ECX,X86_EBX,val); /* ECX:EBX = GPR[base] + sign-extended offset */ x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EBX,X86_EDI,REG_OFFSET(base)); x86_alu_reg_membase(b->jit_ptr,X86_ADC,X86_ECX,X86_EDI,REG_OFFSET(base)+4); /* EAX = mts32_entry index */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EBX,4); x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_EAX,MTS32_HASH_SHIFT); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_EAX,MTS32_HASH_MASK); /* EDX = mts32_entry */ x86_mov_reg_membase(b->jit_ptr,X86_EDX, X86_EDI,OFFSET(cpu_mips_t,mts_u.mts64_cache), 4); x86_shift_reg_imm(b->jit_ptr,X86_SHL,X86_EAX,5); x86_alu_reg_reg(b->jit_ptr,X86_ADD,X86_EDX,X86_EAX); /* Compare virtual page address (ESI = vpage) */ x86_mov_reg_reg(b->jit_ptr,X86_ESI,X86_EBX,4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ESI,MIPS_MIN_PAGE_MASK); /* Compare the high part of the vaddr */ x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_ECX,X86_EDX, OFFSET(mts64_entry_t,gvpa)+4); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); /* Compare the low part of the vaddr */ x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_ESI,X86_EDX, OFFSET(mts64_entry_t,gvpa)); test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); /* Test if we are writing to a COW page */ if (write_op) { x86_test_membase_imm(b->jit_ptr,X86_EDX,OFFSET(mts64_entry_t,flags), MTS_FLAG_COW); test3 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); } /* EBX = offset in page, EAX = Host Page Address */ x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_EBX,MIPS_MIN_PAGE_IMASK); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDX,OFFSET(mts64_entry_t,hpa),4); /* Memory access */ op_handler(b,target); p_exit = b->jit_ptr; x86_jump8(b->jit_ptr,0); /* === Slow lookup === */ x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); if (test3) x86_patch(test3,b->jit_ptr); /* Update PC (ECX:EBX = vaddr) */ x86_mov_reg_reg(b->jit_ptr,X86_ESI,X86_EBX,4); mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); x86_mov_reg_reg(b->jit_ptr,X86_EDX,X86_ESI,4); /* EBX = target register */ x86_mov_reg_imm(b->jit_ptr,X86_EBX,target); /* EAX = CPU instance pointer */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); /* * Push parameters on stack and call memory function. * Keep the stack aligned on a 16-byte boundary for Darwin/x86. */ x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,8); x86_push_reg(b->jit_ptr,X86_EBX); x86_call_membase(b->jit_ptr,X86_EDI,MEMOP_OFFSET(opcode)); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); x86_patch(p_exit,b->jit_ptr); } /* Fast memory operation (32-bit) */ static void mips64_emit_memop_fast32(mips64_jit_tcb_t *b,int write_op, int opcode,int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { m_uint32_t val = sign_extend(offset,16); u_char *test1,*test2,*p_exit; test2 = NULL; /* EBX = sign-extended offset */ x86_mov_reg_imm(b->jit_ptr,X86_EBX,val); /* EBX = GPR[base] + sign-extended offset */ x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EBX,X86_EDI,REG_OFFSET(base)); /* EAX = mts32_entry index */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EBX,4); x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_EAX,MTS32_HASH_SHIFT); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_EAX,MTS32_HASH_MASK); /* EDX = mts32_entry */ x86_mov_reg_membase(b->jit_ptr,X86_EDX, X86_EDI,OFFSET(cpu_mips_t,mts_u.mts32_cache), 4); x86_shift_reg_imm(b->jit_ptr,X86_SHL,X86_EAX,4); x86_alu_reg_reg(b->jit_ptr,X86_ADD,X86_EDX,X86_EAX); /* Compare virtual page address (ESI = vpage) */ x86_mov_reg_reg(b->jit_ptr,X86_ESI,X86_EBX,4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ESI,PPC32_MIN_PAGE_MASK); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_ESI,X86_EDX, OFFSET(mts32_entry_t,gvpa)); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); /* Test if we are writing to a COW page */ if (write_op) { x86_test_membase_imm(b->jit_ptr,X86_EDX,OFFSET(mts32_entry_t,flags), MTS_FLAG_COW); test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); } /* EBX = offset in page, EAX = Host Page Address */ x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_EBX,PPC32_MIN_PAGE_IMASK); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDX,OFFSET(mts32_entry_t,hpa),4); /* Memory access */ op_handler(b,target); p_exit = b->jit_ptr; x86_jump8(b->jit_ptr,0); /* === Slow lookup === */ x86_patch(test1,b->jit_ptr); if (test2) x86_patch(test2,b->jit_ptr); /* Update PC (EBX = vaddr) */ mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); /* Sign-extend virtual address and put vaddr in ECX:EDX */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EBX,4); x86_cdq(b->jit_ptr); x86_mov_reg_reg(b->jit_ptr,X86_ECX,X86_EDX,4); x86_mov_reg_reg(b->jit_ptr,X86_EDX,X86_EAX,4); /* EBX = target register */ x86_mov_reg_imm(b->jit_ptr,X86_EBX,target); /* EAX = CPU instance pointer */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); /* * Push parameters on stack and call memory function. * Keep the stack aligned on a 16-byte boundary for Darwin/x86. */ x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,8); x86_push_reg(b->jit_ptr,X86_EBX); x86_call_membase(b->jit_ptr,X86_EDI,MEMOP_OFFSET(opcode)); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); x86_patch(p_exit,b->jit_ptr); } /* Fast memory operation */ static void mips64_emit_memop_fast(cpu_mips_t *cpu,mips64_jit_tcb_t *b, int write_op,int opcode, int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { switch(cpu->addr_mode) { case 32: mips64_emit_memop_fast32(b,write_op,opcode,base,offset,target, keep_ll_bit,op_handler); break; case 64: mips64_emit_memop_fast64(b,write_op,opcode,base,offset,target, keep_ll_bit,op_handler); break; } } /* Memory operation */ static void mips64_emit_memop(mips64_jit_tcb_t *b,int op,int base,int offset, int target,int keep_ll_bit) { m_uint64_t val = sign_extend(offset,16); /* Save PC for exception handling */ mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); if (!keep_ll_bit) { x86_clear_reg(b->jit_ptr,X86_EAX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,ll_bit), X86_EAX,4); } /* ECX:EDX = sign-extended offset */ mips64_load_imm(b,X86_ECX,X86_EDX,val); /* ECX:EDX = GPR[base] + sign-extended offset */ x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EDX,X86_EDI,REG_OFFSET(base)); x86_alu_reg_membase(b->jit_ptr,X86_ADC,X86_ECX,X86_EDI,REG_OFFSET(base)+4); /* EBX = target register */ x86_mov_reg_imm(b->jit_ptr,X86_EBX,target); /* EAX = CPU instance pointer */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); /* * Push parameters on stack and call memory function. * Keep the stack aligned on a 16-byte boundary for Darwin/x86. */ x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,8); x86_push_reg(b->jit_ptr,X86_EBX); x86_call_membase(b->jit_ptr,X86_EDI,MEMOP_OFFSET(op)); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); } /* Coprocessor Register transfert operation */ static void mips64_emit_cp_xfr_op(mips64_jit_tcb_t *b,int rt,int rd,void *f) { /* update pc */ mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); /* cp0 register */ x86_mov_reg_imm(b->jit_ptr,X86_ECX,rd); /* gpr */ x86_mov_reg_imm(b->jit_ptr,X86_EDX,rt); /* cpu instance */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,f); } /* Virtual Breakpoint */ void mips64_emit_breakpoint(mips64_jit_tcb_t *b) { x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,12); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_c_call(b,mips64_run_breakpoint); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); } /* Unknown opcode handler */ static asmlinkage void mips64_unknown_opcode(cpu_mips_t *cpu,m_uint32_t opcode) { printf("MIPS64: unhandled opcode 0x%8.8x at 0x%llx (ra=0x%llx)\n", opcode,cpu->pc,cpu->gpr[MIPS_GPR_RA]); mips64_dump_regs(cpu->gen); } /* Emit unhandled instruction code */ static int mips64_emit_unknown(cpu_mips_t *cpu,mips64_jit_tcb_t *b, mips_insn_t opcode) { x86_mov_reg_imm(b->jit_ptr,X86_EAX,opcode); x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,4); x86_push_reg(b->jit_ptr,X86_EAX); x86_push_reg(b->jit_ptr,X86_EDI); mips64_emit_c_call(b,mips64_unknown_opcode); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); return(0); } /* Invalid delay slot handler */ static fastcall void mips64_invalid_delay_slot(cpu_mips_t *cpu) { printf("MIPS64: invalid instruction in delay slot at 0x%llx (ra=0x%llx)\n", cpu->pc,cpu->gpr[MIPS_GPR_RA]); mips64_dump_regs(cpu->gen); /* Halt the virtual CPU */ cpu->pc = 0; } /* Emit unhandled instruction code */ int mips64_emit_invalid_delay_slot(mips64_jit_tcb_t *b) { x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,12); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_c_call(b,mips64_invalid_delay_slot); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); mips64_jit_tcb_push_epilog(b); return(0); } /* * Increment count register and trigger the timer IRQ if value in compare * register is the same. */ void mips64_inc_cp0_count_reg(mips64_jit_tcb_t *b) { x86_inc_membase(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,cp0_virt_cnt_reg)); } /* Check if there are pending IRQ */ void mips64_check_pending_irq(mips64_jit_tcb_t *b) { u_char *test1; /* Check the pending IRQ flag */ x86_mov_reg_membase(b->jit_ptr,X86_EAX, X86_EDI,OFFSET(cpu_mips_t,irq_pending),4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_Z, 0, 1); /* Save PC */ mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); /* Trigger the IRQ */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_trigger_irq); mips64_jit_tcb_push_epilog(b); x86_patch(test1,b->jit_ptr); } /* Increment the number of executed instructions (performance debugging) */ void mips64_inc_perf_counter(mips64_jit_tcb_t *b) { x86_inc_membase(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,perf_counter)); } /* ADD */ DECLARE_INSN(ADD) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); /* TODO: Exception handling */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* ADDI */ DECLARE_INSN(ADDI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); /* TODO: Exception handling */ x86_mov_reg_imm(b->jit_ptr,X86_EAX,val & 0xffffffff); x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EAX,X86_EDI,REG_OFFSET(rs)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_EDX,4); return(0); } /* ADDIU */ DECLARE_INSN(ADDIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); x86_mov_reg_imm(b->jit_ptr,X86_EAX,val & 0xffffffff); x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EAX,X86_EDI,REG_OFFSET(rs)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_EDX,4); return(0); } /* ADDU */ DECLARE_INSN(ADDU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* AND */ DECLARE_INSN(AND) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_AND,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_alu_reg_membase(b->jit_ptr,X86_AND,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* ANDI */ DECLARE_INSN(ANDI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); x86_mov_reg_imm(b->jit_ptr,X86_EAX,imm); x86_alu_reg_reg(b->jit_ptr,X86_XOR,X86_EBX,X86_EBX); x86_alu_reg_membase(b->jit_ptr,X86_AND,X86_EAX,X86_EDI,REG_OFFSET(rs)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_EBX,4); return(0); } /* B (Branch, virtual instruction) */ DECLARE_INSN(B) { int offset = bits(insn,0,15); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); return(0); } /* BAL (Branch and Link, virtual instruction) */ DECLARE_INSN(BAL) { int offset = bits(insn,0,15); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,0); return(0); } /* BEQ (Branch On Equal) */ DECLARE_INSN(BEQ) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. * compare the low 32 bits first (higher probability). */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EAX,X86_EDI,REG_OFFSET(rt)); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NE, 0, 1); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NE, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BEQL (Branch On Equal Likely) */ DECLARE_INSN(BEQL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. * compare the low 32 bits first (higher probability). */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EAX,X86_EDI,REG_OFFSET(rt)); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NE, 0, 1); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NE, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); return(0); } /* BEQZ (Branch On Equal Zero - optimization) */ DECLARE_INSN(BEQZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] with 0. * compare the low 32 bits first (higher probability). */ x86_alu_membase_imm(b->jit_ptr,X86_CMP,X86_EDI,REG_OFFSET(rs),0); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NE, 0, 1); x86_alu_membase_imm(b->jit_ptr,X86_CMP,X86_EDI,REG_OFFSET(rs)+4,0); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NE, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BNEZ (Branch On Not Equal Zero - optimization) */ DECLARE_INSN(BNEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] with 0. * compare the low 32 bits first (higher probability). */ x86_alu_membase_imm(b->jit_ptr,X86_CMP,X86_EDI,REG_OFFSET(rs),0); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); x86_alu_membase_imm(b->jit_ptr,X86_CMP,X86_EDI,REG_OFFSET(rs)+4,0); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_E, 0, 1); x86_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test2,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZ (Branch On Greater or Equal Than Zero) */ DECLARE_INSN(BGEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* If sign bit is set, don't take the branch */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZAL (Branch On Greater or Equal Than Zero And Link) */ DECLARE_INSN(BGEZAL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* If sign bit is set, don't take the branch */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZALL (Branch On Greater or Equal Than Zero and Link Likely) */ DECLARE_INSN(BGEZALL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* if sign bit is set, don't take the branch */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); return(0); } /* BGEZL (Branch On Greater or Equal Than Zero Likely) */ DECLARE_INSN(BGEZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* if sign bit is set, don't take the branch */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); return(0); } /* BGTZ (Branch On Greater Than Zero) */ DECLARE_INSN(BGTZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2,*test3; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the hi word of gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_S, 0, 1); test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); /* test the lo word of gpr[rs] (here hi word = 0) */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs),4); x86_test_reg_reg(b->jit_ptr,X86_EBX,X86_EBX); test3 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_Z, 0, 1); /* here, we take the branch */ x86_patch(test2,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); x86_patch(test3,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGTZL (Branch On Greater Than Zero Likely) */ DECLARE_INSN(BGTZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2,*test3; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the hi word of gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_S, 0, 1); test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); /* test the lo word of gpr[rs] (here hi word = 0) */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs),4); x86_test_reg_reg(b->jit_ptr,X86_EBX,X86_EBX); test3 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_Z, 0, 1); /* here, we take the branch */ x86_patch(test2,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); x86_patch(test3,b->jit_ptr); return(0); } /* BLEZ (Branch On Less or Equal Than Zero) */ DECLARE_INSN(BLEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2,*test3; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the hi word of gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_S, 0, 1); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NZ, 0, 1); /* test the lo word of gpr[rs] (here hi word = 0) */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs),4); x86_test_reg_reg(b->jit_ptr,X86_EBX,X86_EBX); test3 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NZ, 0, 1); /* here, we take the branch */ x86_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test2,b->jit_ptr); x86_patch(test3,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLEZL (Branch On Less or Equal Than Zero Likely) */ DECLARE_INSN(BLEZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2,*test3; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the hi word of gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_S, 0, 1); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NZ, 0, 1); /* test the lo word of gpr[rs] (here hi word = 0) */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs),4); x86_test_reg_reg(b->jit_ptr,X86_EBX,X86_EBX); test3 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NZ, 0, 1); /* here, we take the branch */ x86_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test2,b->jit_ptr); x86_patch(test3,b->jit_ptr); return(0); } /* BLTZ (Branch On Less Than Zero) */ DECLARE_INSN(BLTZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the sign bit of gpr[rs], if set, take the branch. */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLTZAL (Branch On Less Than Zero And Link) */ DECLARE_INSN(BLTZAL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* * test the sign bit of gpr[rs], if set, take the branch. */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLTZALL (Branch On Less Than Zero And Link Likely) */ DECLARE_INSN(BLTZALL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* * test the sign bit of gpr[rs], if set, take the branch. */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); return(0); } /* BLTZL (Branch On Less Than Zero Likely) */ DECLARE_INSN(BLTZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the sign bit of gpr[rs], if set, take the branch. */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); return(0); } /* BNE (Branch On Not Equal) */ DECLARE_INSN(BNE) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. * compare the low 32 bits first (higher probability). */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EAX,X86_EDI,REG_OFFSET(rt)); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_E, 0, 1); x86_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test2,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BNEL (Branch On Not Equal Likely) */ DECLARE_INSN(BNEL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. * compare the low 32 bits first (higher probability). */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EAX,X86_EDI,REG_OFFSET(rt)); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_E, 0, 1); x86_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test2,b->jit_ptr); return(0); } /* BREAK */ DECLARE_INSN(BREAK) { u_int code = bits(insn,6,25); x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,12); x86_mov_reg_imm(b->jit_ptr,X86_EDX,code); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_exec_break); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); mips64_jit_tcb_push_epilog(b); return(0); } /* CACHE */ DECLARE_INSN(CACHE) { int base = bits(insn,21,25); int op = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_CACHE,base,offset,op,FALSE); return(0); } /* CFC0 */ DECLARE_INSN(CFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_cfc0); return(0); } /* CTC0 */ DECLARE_INSN(CTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_ctc0); return(0); } /* DADDIU */ DECLARE_INSN(DADDIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); mips64_load_imm(b,X86_EBX,X86_EAX,val); x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EAX,X86_EDI,REG_OFFSET(rs)); x86_alu_reg_membase(b->jit_ptr,X86_ADC,X86_EBX,X86_EDI,REG_OFFSET(rs)+4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_EBX,4); return(0); } /* DADDU: rd = rs + rt */ DECLARE_INSN(DADDU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_alu_reg_membase(b->jit_ptr,X86_ADC,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* DIV */ DECLARE_INSN(DIV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); /* eax = gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_cdq(b->jit_ptr); /* ebx = gpr[rt] */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt),4); /* eax = quotient (LO), edx = remainder (HI) */ x86_div_reg(b->jit_ptr,X86_EBX,1); /* store LO */ x86_mov_reg_reg(b->jit_ptr,X86_ECX,X86_EDX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo)+4,X86_EDX,4); /* store HI */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi)+4,X86_EDX,4); return(0); } /* DIVU */ DECLARE_INSN(DIVU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); /* eax = gpr[rs] */ x86_clear_reg(b->jit_ptr,X86_EDX); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); /* ebx = gpr[rt] */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt),4); /* eax = quotient (LO), edx = remainder (HI) */ x86_div_reg(b->jit_ptr,X86_EBX,0); /* store LO */ x86_mov_reg_reg(b->jit_ptr,X86_ECX,X86_EDX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo)+4,X86_EDX,4); /* store HI */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi)+4,X86_EDX,4); return(0); } /* DMFC0 */ DECLARE_INSN(DMFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_dmfc0); return(0); } /* DMFC1 */ DECLARE_INSN(DMFC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_dmfc1); return(0); } /* DMTC0 */ DECLARE_INSN(DMTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_dmtc0); return(0); } /* DMTC1 */ DECLARE_INSN(DMTC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_dmtc1); return(0); } /* DSLL */ DECLARE_INSN(DSLL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt)+4,4); x86_shld_reg_imm(b->jit_ptr,X86_EBX,X86_EAX,sa); x86_shift_reg_imm(b->jit_ptr,X86_SHL,X86_EAX,sa); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* DSLL32 */ DECLARE_INSN(DSLL32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_shift_reg_imm(b->jit_ptr,X86_SHL,X86_EAX,sa); x86_clear_reg(b->jit_ptr,X86_EDX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EDX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EAX,4); return(0); } /* DSLLV */ DECLARE_INSN(DSLLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt)+4,4); x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ECX,0x3f); x86_shld_reg(b->jit_ptr,X86_EBX,X86_EAX); x86_shift_reg(b->jit_ptr,X86_SHL,X86_EAX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* DSRA */ DECLARE_INSN(DSRA) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt)+4,4); x86_shrd_reg_imm(b->jit_ptr,X86_EAX,X86_EBX,sa); x86_shift_reg_imm(b->jit_ptr,X86_SAR,X86_EBX,sa); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* DSRA32 */ DECLARE_INSN(DSRA32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt)+4,4); x86_shift_reg_imm(b->jit_ptr,X86_SAR,X86_EAX,sa); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* DSRAV */ DECLARE_INSN(DSRAV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt)+4,4); x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ECX,0x3f); x86_shrd_reg(b->jit_ptr,X86_EAX,X86_EBX); x86_shift_reg(b->jit_ptr,X86_SAR,X86_EBX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* DSRL */ DECLARE_INSN(DSRL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt)+4,4); x86_shrd_reg_imm(b->jit_ptr,X86_EAX,X86_EBX,sa); x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_EBX,sa); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* DSRL32 */ DECLARE_INSN(DSRL32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt)+4,4); x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_EAX,sa); x86_clear_reg(b->jit_ptr,X86_EDX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* DSRLV */ DECLARE_INSN(DSRLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt)+4,4); x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ECX,0x3f); x86_shrd_reg(b->jit_ptr,X86_EAX,X86_EBX); x86_shift_reg(b->jit_ptr,X86_SHR,X86_EBX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* DSUBU: rd = rs - rt */ DECLARE_INSN(DSUBU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_SUB,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_alu_reg_membase(b->jit_ptr,X86_SBB,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* ERET */ DECLARE_INSN(ERET) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_exec_eret); mips64_jit_tcb_push_epilog(b); return(0); } /* J (Jump) */ DECLARE_INSN(J) { u_int instr_index = bits(insn,0,25); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc &= ~((1 << 28) - 1); new_pc |= instr_index << 2; /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); return(0); } /* JAL (Jump And Link) */ DECLARE_INSN(JAL) { u_int instr_index = bits(insn,0,25); m_uint64_t new_pc,ret_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc &= ~((1 << 28) - 1); new_pc |= instr_index << 2; /* set the return address (instruction after the delay slot) */ ret_pc = b->start_pc + ((b->mips_trans_pos + 1) << 2); mips64_set_ra(b,ret_pc); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,0); return(0); } /* JALR (Jump and Link Register) */ DECLARE_INSN(JALR) { int rs = bits(insn,21,25); int rd = bits(insn,11,15); m_uint64_t ret_pc; /* set the return pc (instruction after the delay slot) in GPR[rd] */ ret_pc = b->start_pc + ((b->mips_trans_pos + 1) << 2); mips64_load_imm(b,X86_EBX,X86_EAX,ret_pc); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); /* get the new pc */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EDX,X86_EDI,REG_OFFSET(rs)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,ret_pc),X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,ret_pc)+4, X86_EDX,4); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc */ x86_mov_reg_membase(b->jit_ptr,X86_ECX, X86_EDI,OFFSET(cpu_mips_t,ret_pc),4); x86_mov_reg_membase(b->jit_ptr,X86_EDX, X86_EDI,OFFSET(cpu_mips_t,ret_pc)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,pc),X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,pc)+4,X86_EDX,4); /* returns to the caller which will determine the next path */ mips64_jit_tcb_push_epilog(b); return(0); } /* JR (Jump Register) */ DECLARE_INSN(JR) { int rs = bits(insn,21,25); /* get the new pc */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EDX,X86_EDI,REG_OFFSET(rs)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,ret_pc),X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,ret_pc)+4, X86_EDX,4); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc */ x86_mov_reg_membase(b->jit_ptr,X86_ECX, X86_EDI,OFFSET(cpu_mips_t,ret_pc),4); x86_mov_reg_membase(b->jit_ptr,X86_EDX, X86_EDI,OFFSET(cpu_mips_t,ret_pc)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,pc),X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,pc)+4,X86_EDX,4); /* returns to the caller which will determine the next path */ mips64_jit_tcb_push_epilog(b); return(0); } /* LB (Load Byte) */ DECLARE_INSN(LB) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LB,base,offset,rt,TRUE); return(0); } /* LBU (Load Byte Unsigned) */ DECLARE_INSN(LBU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LBU,base,offset,rt,TRUE); return(0); } /* LD (Load Double-Word) */ DECLARE_INSN(LD) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LD,base,offset,rt,TRUE); return(0); } /* LDC1 (Load Double-Word to Coprocessor 1) */ DECLARE_INSN(LDC1) { int base = bits(insn,21,25); int ft = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDC1,base,offset,ft,TRUE); return(0); } /* LDL (Load Double-Word Left) */ DECLARE_INSN(LDL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDL,base,offset,rt,TRUE); return(0); } /* LDR (Load Double-Word Right) */ DECLARE_INSN(LDR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDR,base,offset,rt,TRUE); return(0); } /* LH (Load Half-Word) */ DECLARE_INSN(LH) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LH,base,offset,rt,TRUE); return(0); } /* LHU (Load Half-Word Unsigned) */ DECLARE_INSN(LHU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LHU,base,offset,rt,TRUE); return(0); } /* LI (virtual) */ DECLARE_INSN(LI) { int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); x86_mov_membase_imm(b->jit_ptr,X86_EDI,REG_OFFSET(rt),val & 0xffffffff,4); x86_mov_membase_imm(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,val >> 32,4); return(0); } /* LL (Load Linked) */ DECLARE_INSN(LL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LL,base,offset,rt,TRUE); return(0); } /* LUI */ DECLARE_INSN(LUI) { int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16) << 16; mips64_load_imm(b,X86_EBX,X86_EAX,val); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_EBX,4); return(0); } /* LW (Load Word) */ DECLARE_INSN(LW) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,0,MIPS_MEMOP_LW,base,offset,rt,TRUE, mips64_memop_fast_lw); } else { mips64_emit_memop(b,MIPS_MEMOP_LW,base,offset,rt,TRUE); } return(0); } /* LWL (Load Word Left) */ DECLARE_INSN(LWL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LWL,base,offset,rt,TRUE); return(0); } /* LWR (Load Word Right) */ DECLARE_INSN(LWR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LWR,base,offset,rt,TRUE); return(0); } /* LWU (Load Word Unsigned) */ DECLARE_INSN(LWU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LWU,base,offset,rt,TRUE); return(0); } /* MFC0 */ DECLARE_INSN(MFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_mfc0); return(0); } /* MFC1 */ DECLARE_INSN(MFC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_mfc1); return(0); } /* MFHI */ DECLARE_INSN(MFHI) { int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,OFFSET(cpu_mips_t,hi),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,OFFSET(cpu_mips_t,hi)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* MFLO */ DECLARE_INSN(MFLO) { int rd = bits(insn,11,15); if (!rd) return(0); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,OFFSET(cpu_mips_t,lo),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,OFFSET(cpu_mips_t,lo)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* MOVE (virtual instruction, real: ADDU) */ DECLARE_INSN(MOVE) { int rs = bits(insn,21,25); int rd = bits(insn,11,15); if (rs != 0) { x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); } else { x86_alu_reg_reg(b->jit_ptr,X86_XOR,X86_EBX,X86_EBX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EBX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); } return(0); } /* MTC0 */ DECLARE_INSN(MTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_mtc0); return(0); } /* MTC1 */ DECLARE_INSN(MTC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_mtc1); return(0); } /* MTHI */ DECLARE_INSN(MTHI) { int rs = bits(insn,21,25); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi)+4,X86_EBX,4); return(0); } /* MTLO */ DECLARE_INSN(MTLO) { int rs = bits(insn,21,25); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo)+4,X86_EBX,4); return(0); } /* MUL */ DECLARE_INSN(MUL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt),4); x86_mul_reg(b->jit_ptr,X86_EBX,1); /* store result in gpr[rd] */ x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* MULT */ DECLARE_INSN(MULT) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt),4); x86_mul_reg(b->jit_ptr,X86_EBX,1); /* store LO */ x86_mov_reg_reg(b->jit_ptr,X86_ECX,X86_EDX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo)+4,X86_EDX,4); /* store HI */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi)+4,X86_EDX,4); return(0); } /* MULTU */ DECLARE_INSN(MULTU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt),4); x86_mul_reg(b->jit_ptr,X86_EBX,0); /* store LO */ x86_mov_reg_reg(b->jit_ptr,X86_ECX,X86_EDX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo)+4,X86_EDX,4); /* store HI */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi)+4,X86_EDX,4); return(0); } /* NOP */ DECLARE_INSN(NOP) { //x86_nop(b->jit_ptr); return(0); } /* NOR */ DECLARE_INSN(NOR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_OR,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_alu_reg_membase(b->jit_ptr,X86_OR,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); x86_not_reg(b->jit_ptr,X86_EAX); x86_not_reg(b->jit_ptr,X86_EBX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* OR */ DECLARE_INSN(OR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_OR,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_alu_reg_membase(b->jit_ptr,X86_OR,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* ORI */ DECLARE_INSN(ORI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = imm; x86_mov_reg_imm(b->jit_ptr,X86_EAX,val & 0xffff); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_OR,X86_EAX,X86_EDI,REG_OFFSET(rs)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_EBX,4); return(0); } /* PREF */ DECLARE_INSN(PREF) { x86_nop(b->jit_ptr); return(0); } /* PREFI */ DECLARE_INSN(PREFI) { x86_nop(b->jit_ptr); return(0); } /* SB (Store Byte) */ DECLARE_INSN(SB) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SB,base,offset,rt,FALSE); return(0); } /* SC (Store Conditional) */ DECLARE_INSN(SC) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SC,base,offset,rt,TRUE); return(0); } /* SD (Store Double-Word) */ DECLARE_INSN(SD) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SD,base,offset,rt,FALSE); return(0); } /* SDL (Store Double-Word Left) */ DECLARE_INSN(SDL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDL,base,offset,rt,FALSE); return(0); } /* SDR (Store Double-Word Right) */ DECLARE_INSN(SDR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDR,base,offset,rt,FALSE); return(0); } /* SDC1 (Store Double-Word from Coprocessor 1) */ DECLARE_INSN(SDC1) { int base = bits(insn,21,25); int ft = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDC1,base,offset,ft,FALSE); return(0); } /* SH (Store Half-Word) */ DECLARE_INSN(SH) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SH,base,offset,rt,FALSE); return(0); } /* SLL */ DECLARE_INSN(SLL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_shift_reg_imm(b->jit_ptr,X86_SHL,X86_EAX,sa); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SLLV */ DECLARE_INSN(SLLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ECX,0x1f); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_shift_reg(b->jit_ptr,X86_SHL,X86_EAX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SLT */ DECLARE_INSN(SLT) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); u_char *test1,*test2,*test3; /* edx:eax = gpr[rt] */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EDX,X86_EDI,REG_OFFSET(rt)+4,4); /* ebx:ecx = gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); /* we set rd to 1 when gpr[rs] < gpr[rt] */ x86_clear_reg(b->jit_ptr,X86_ESI); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_ESI,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_ESI,4); /* rs(high) > rt(high) => end */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_EBX,X86_EDX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_GT, 0, 1); /* rs(high) < rt(high) => set rd to 1 */ test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_LT, 0, 1); /* rs(high) == rt(high), rs(low) >= rt(low) => end */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_ECX,X86_EAX); test3 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_AE, 0, 1); /* set rd to 1 */ x86_patch(test2,b->jit_ptr); x86_inc_membase(b->jit_ptr,X86_EDI,REG_OFFSET(rd)); /* end */ x86_patch(test1,b->jit_ptr); x86_patch(test3,b->jit_ptr); return(0); } /* SLTI */ DECLARE_INSN(SLTI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); u_char *test1,*test2,*test3; /* we set rt to 1 when gpr[rs] < val, rt to 0 when gpr[rs] >= val */ /* edx:eax = val */ mips64_load_imm(b,X86_EDX,X86_EAX,val); /* ebx:ecx = gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); /* we set rt to 1 when gpr[rs] < val */ x86_clear_reg(b->jit_ptr,X86_ESI); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_ESI,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_ESI,4); /* rs(high) > val(high) => end */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_EBX,X86_EDX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_GT, 0, 1); /* rs(high) < val(high) => set rt to 1 */ test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_LT, 0, 1); /* rs(high) == val(high), rs(low) >= val(low) => set rt to 0 */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_ECX,X86_EAX); test3 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_AE, 0, 1); /* set rt to 1 */ x86_patch(test2,b->jit_ptr); x86_inc_membase(b->jit_ptr,X86_EDI,REG_OFFSET(rt)); /* end */ x86_patch(test1,b->jit_ptr); x86_patch(test3,b->jit_ptr); return(0); } /* SLTIU */ DECLARE_INSN(SLTIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); u_char *test1,*test2,*test3; /* edx:eax = val */ mips64_load_imm(b,X86_EDX,X86_EAX,val); /* ebx:ecx = gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); /* we set rt to 1 when gpr[rs] < val */ x86_clear_reg(b->jit_ptr,X86_ESI); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_ESI,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_ESI,4); /* rs(high) > val(high) => end */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_EBX,X86_EDX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_A, 0, 0); /* rs(high) < val(high) => set rt to 1 */ test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_B, 0, 0); /* rs(high) == val(high), rs(low) >= val(low) => end */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_ECX,X86_EAX); test3 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_AE, 0, 1); /* set rt to 1 */ x86_patch(test2,b->jit_ptr); x86_inc_membase(b->jit_ptr,X86_EDI,REG_OFFSET(rt)); /* end */ x86_patch(test1,b->jit_ptr); x86_patch(test3,b->jit_ptr); return(0); } /* SLTU */ DECLARE_INSN(SLTU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); u_char *test1,*test2,*test3; /* edx:eax = gpr[rt] */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EDX,X86_EDI,REG_OFFSET(rt)+4,4); /* ebx:ecx = gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); /* we set rd to 1 when gpr[rs] < gpr[rt] */ x86_clear_reg(b->jit_ptr,X86_ESI); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_ESI,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_ESI,4); /* rs(high) > rt(high) => end */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_EBX,X86_EDX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_A, 0, 0); /* rs(high) < rt(high) => set rd to 1 */ test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_B, 0, 0); /* rs(high) == rt(high), rs(low) >= rt(low) => end */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_ECX,X86_EAX); test3 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_AE, 0, 1); /* set rd to 1 */ x86_patch(test2,b->jit_ptr); x86_inc_membase(b->jit_ptr,X86_EDI,REG_OFFSET(rd)); /* end */ x86_patch(test1,b->jit_ptr); x86_patch(test3,b->jit_ptr); return(0); } /* SRA */ DECLARE_INSN(SRA) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_shift_reg_imm(b->jit_ptr,X86_SAR,X86_EAX,sa); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SRAV */ DECLARE_INSN(SRAV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ECX,0x1f); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_shift_reg(b->jit_ptr,X86_SAR,X86_EAX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SRL */ DECLARE_INSN(SRL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_EAX,sa); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_clear_reg(b->jit_ptr,X86_EDX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SRLV */ DECLARE_INSN(SRLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ECX,0x1f); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_shift_reg(b->jit_ptr,X86_SHR,X86_EAX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_clear_reg(b->jit_ptr,X86_EDX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SUB */ DECLARE_INSN(SUB) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); /* TODO: Exception handling */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_SUB,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SUBU */ DECLARE_INSN(SUBU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_SUB,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SW (Store Word) */ DECLARE_INSN(SW) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,1,MIPS_MEMOP_SW,base,offset,rt,FALSE, mips64_memop_fast_sw); } else { mips64_emit_memop(b,MIPS_MEMOP_SW,base,offset,rt,FALSE); } return(0); } /* SWL (Store Word Left) */ DECLARE_INSN(SWL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SWL,base,offset,rt,FALSE); return(0); } /* SWR (Store Word Right) */ DECLARE_INSN(SWR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SWR,base,offset,rt,FALSE); return(0); } /* SYNC */ DECLARE_INSN(SYNC) { return(0); } /* SYSCALL */ DECLARE_INSN(SYSCALL) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_exec_syscall); mips64_jit_tcb_push_epilog(b); return(0); } /* TEQ (Trap If Equal) */ DECLARE_INSN(TEQ) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); u_char *test1,*test2; /* Compare low part */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_ECX,X86_EDI,REG_OFFSET(rt)); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Compare high part */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Generate trap exception */ x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,12); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_c_call(b,mips64_trigger_trap_exception); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); mips64_jit_tcb_push_epilog(b); /* end */ x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); return(0); } /* TEQI (Trap If Equal Immediate) */ DECLARE_INSN(TEQI) { int rs = bits(insn,21,25); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); u_char *test1,*test2; /* edx:eax = val */ mips64_load_imm(b,X86_EDX,X86_EAX,val); /* Compare low part */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_ECX,X86_EAX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Compare high part */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_EBX,X86_EDX); test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Generate trap exception */ x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,12); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_c_call(b,mips64_trigger_trap_exception); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); mips64_jit_tcb_push_epilog(b); /* end */ x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); return(0); } /* TLBP */ DECLARE_INSN(TLBP) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbp); return(0); } /* TLBR */ DECLARE_INSN(TLBR) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbr); return(0); } /* TLBWI */ DECLARE_INSN(TLBWI) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbwi); return(0); } /* TLBWR */ DECLARE_INSN(TLBWR) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbwr); return(0); } /* XOR */ DECLARE_INSN(XOR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_XOR,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_alu_reg_membase(b->jit_ptr,X86_XOR,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* XORI */ DECLARE_INSN(XORI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = imm; mips64_load_imm(b,X86_EBX,X86_EAX,val); x86_alu_reg_membase(b->jit_ptr,X86_XOR,X86_EAX,X86_EDI,REG_OFFSET(rs)); x86_alu_reg_membase(b->jit_ptr,X86_XOR,X86_EBX,X86_EDI,REG_OFFSET(rs)+4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_EBX,4); return(0); } /* MIPS instruction array */ struct mips64_insn_tag mips64_insn_tags[] = { { mips64_emit_LI , 0xffe00000 , 0x24000000, 1 }, /* virtual */ { mips64_emit_MOVE , 0xfc1f07ff , 0x00000021, 1 }, /* virtual */ { mips64_emit_B , 0xffff0000 , 0x10000000, 0 }, /* virtual */ { mips64_emit_BAL , 0xffff0000 , 0x04110000, 0 }, /* virtual */ { mips64_emit_BEQZ , 0xfc1f0000 , 0x10000000, 0 }, /* virtual */ { mips64_emit_BNEZ , 0xfc1f0000 , 0x14000000, 0 }, /* virtual */ { mips64_emit_ADD , 0xfc0007ff , 0x00000020, 1 }, { mips64_emit_ADDI , 0xfc000000 , 0x20000000, 1 }, { mips64_emit_ADDIU , 0xfc000000 , 0x24000000, 1 }, { mips64_emit_ADDU , 0xfc0007ff , 0x00000021, 1 }, { mips64_emit_AND , 0xfc0007ff , 0x00000024, 1 }, { mips64_emit_ANDI , 0xfc000000 , 0x30000000, 1 }, { mips64_emit_BEQ , 0xfc000000 , 0x10000000, 0 }, { mips64_emit_BEQL , 0xfc000000 , 0x50000000, 0 }, { mips64_emit_BGEZ , 0xfc1f0000 , 0x04010000, 0 }, { mips64_emit_BGEZAL , 0xfc1f0000 , 0x04110000, 0 }, { mips64_emit_BGEZALL , 0xfc1f0000 , 0x04130000, 0 }, { mips64_emit_BGEZL , 0xfc1f0000 , 0x04030000, 0 }, { mips64_emit_BGTZ , 0xfc1f0000 , 0x1c000000, 0 }, { mips64_emit_BGTZL , 0xfc1f0000 , 0x5c000000, 0 }, { mips64_emit_BLEZ , 0xfc1f0000 , 0x18000000, 0 }, { mips64_emit_BLEZL , 0xfc1f0000 , 0x58000000, 0 }, { mips64_emit_BLTZ , 0xfc1f0000 , 0x04000000, 0 }, { mips64_emit_BLTZAL , 0xfc1f0000 , 0x04100000, 0 }, { mips64_emit_BLTZALL , 0xfc1f0000 , 0x04120000, 0 }, { mips64_emit_BLTZL , 0xfc1f0000 , 0x04020000, 0 }, { mips64_emit_BNE , 0xfc000000 , 0x14000000, 0 }, { mips64_emit_BNEL , 0xfc000000 , 0x54000000, 0 }, { mips64_emit_BREAK , 0xfc00003f , 0x0000000d, 1 }, { mips64_emit_CACHE , 0xfc000000 , 0xbc000000, 1 }, { mips64_emit_CFC0 , 0xffe007ff , 0x40400000, 1 }, { mips64_emit_CTC0 , 0xffe007ff , 0x40600000, 1 }, { mips64_emit_DADDIU , 0xfc000000 , 0x64000000, 1 }, { mips64_emit_DADDU , 0xfc0007ff , 0x0000002d, 1 }, { mips64_emit_DIV , 0xfc00ffff , 0x0000001a, 1 }, { mips64_emit_DIVU , 0xfc00ffff , 0x0000001b, 1 }, { mips64_emit_DMFC0 , 0xffe007f8 , 0x40200000, 1 }, { mips64_emit_DMFC1 , 0xffe007ff , 0x44200000, 1 }, { mips64_emit_DMTC0 , 0xffe007f8 , 0x40a00000, 1 }, { mips64_emit_DMTC1 , 0xffe007ff , 0x44a00000, 1 }, { mips64_emit_DSLL , 0xffe0003f , 0x00000038, 1 }, { mips64_emit_DSLL32 , 0xffe0003f , 0x0000003c, 1 }, { mips64_emit_DSLLV , 0xfc0007ff , 0x00000014, 1 }, { mips64_emit_DSRA , 0xffe0003f , 0x0000003b, 1 }, { mips64_emit_DSRA32 , 0xffe0003f , 0x0000003f, 1 }, { mips64_emit_DSRAV , 0xfc0007ff , 0x00000017, 1 }, { mips64_emit_DSRL , 0xffe0003f , 0x0000003a, 1 }, { mips64_emit_DSRL32 , 0xffe0003f , 0x0000003e, 1 }, { mips64_emit_DSRLV , 0xfc0007ff , 0x00000016, 1 }, { mips64_emit_DSUBU , 0xfc0007ff , 0x0000002f, 1 }, { mips64_emit_ERET , 0xffffffff , 0x42000018, 0 }, { mips64_emit_J , 0xfc000000 , 0x08000000, 0 }, { mips64_emit_JAL , 0xfc000000 , 0x0c000000, 0 }, { mips64_emit_JALR , 0xfc1f003f , 0x00000009, 0 }, { mips64_emit_JR , 0xfc1ff83f , 0x00000008, 0 }, { mips64_emit_LB , 0xfc000000 , 0x80000000, 1 }, { mips64_emit_LBU , 0xfc000000 , 0x90000000, 1 }, { mips64_emit_LD , 0xfc000000 , 0xdc000000, 1 }, { mips64_emit_LDC1 , 0xfc000000 , 0xd4000000, 1 }, { mips64_emit_LDL , 0xfc000000 , 0x68000000, 1 }, { mips64_emit_LDR , 0xfc000000 , 0x6c000000, 1 }, { mips64_emit_LH , 0xfc000000 , 0x84000000, 1 }, { mips64_emit_LHU , 0xfc000000 , 0x94000000, 1 }, { mips64_emit_LL , 0xfc000000 , 0xc0000000, 1 }, { mips64_emit_LUI , 0xffe00000 , 0x3c000000, 1 }, { mips64_emit_LW , 0xfc000000 , 0x8c000000, 1 }, { mips64_emit_LWL , 0xfc000000 , 0x88000000, 1 }, { mips64_emit_LWR , 0xfc000000 , 0x98000000, 1 }, { mips64_emit_LWU , 0xfc000000 , 0x9c000000, 1 }, { mips64_emit_MFC0 , 0xffe007ff , 0x40000000, 1 }, { mips64_emit_CFC0 , 0xffe007ff , 0x40000001, 1 }, /* MFC0 / Set 1 */ { mips64_emit_MFC1 , 0xffe007ff , 0x44000000, 1 }, { mips64_emit_MFHI , 0xffff07ff , 0x00000010, 1 }, { mips64_emit_MFLO , 0xffff07ff , 0x00000012, 1 }, { mips64_emit_MTC0 , 0xffe007ff , 0x40800000, 1 }, { mips64_emit_MTC1 , 0xffe007ff , 0x44800000, 1 }, { mips64_emit_MTHI , 0xfc1fffff , 0x00000011, 1 }, { mips64_emit_MTLO , 0xfc1fffff , 0x00000013, 1 }, { mips64_emit_MUL , 0xfc0007ff , 0x70000002, 1 }, { mips64_emit_MULT , 0xfc00ffff , 0x00000018, 1 }, { mips64_emit_MULTU , 0xfc00ffff , 0x00000019, 1 }, { mips64_emit_NOP , 0xffffffff , 0x00000000, 1 }, { mips64_emit_NOR , 0xfc0007ff , 0x00000027, 1 }, { mips64_emit_OR , 0xfc0007ff , 0x00000025, 1 }, { mips64_emit_ORI , 0xfc000000 , 0x34000000, 1 }, { mips64_emit_PREF , 0xfc000000 , 0xcc000000, 1 }, { mips64_emit_PREFI , 0xfc0007ff , 0x4c00000f, 1 }, { mips64_emit_SB , 0xfc000000 , 0xa0000000, 1 }, { mips64_emit_SC , 0xfc000000 , 0xe0000000, 1 }, { mips64_emit_SD , 0xfc000000 , 0xfc000000, 1 }, { mips64_emit_SDC1 , 0xfc000000 , 0xf4000000, 1 }, { mips64_emit_SDL , 0xfc000000 , 0xb0000000, 1 }, { mips64_emit_SDR , 0xfc000000 , 0xb4000000, 1 }, { mips64_emit_SH , 0xfc000000 , 0xa4000000, 1 }, { mips64_emit_SLL , 0xffe0003f , 0x00000000, 1 }, { mips64_emit_SLLV , 0xfc0007ff , 0x00000004, 1 }, { mips64_emit_SLT , 0xfc0007ff , 0x0000002a, 1 }, { mips64_emit_SLTI , 0xfc000000 , 0x28000000, 1 }, { mips64_emit_SLTIU , 0xfc000000 , 0x2c000000, 1 }, { mips64_emit_SLTU , 0xfc0007ff , 0x0000002b, 1 }, { mips64_emit_SRA , 0xffe0003f , 0x00000003, 1 }, { mips64_emit_SRAV , 0xfc0007ff , 0x00000007, 1 }, { mips64_emit_SRL , 0xffe0003f , 0x00000002, 1 }, { mips64_emit_SRLV , 0xfc0007ff , 0x00000006, 1 }, { mips64_emit_SUB , 0xfc0007ff , 0x00000022, 1 }, { mips64_emit_SUBU , 0xfc0007ff , 0x00000023, 1 }, { mips64_emit_SW , 0xfc000000 , 0xac000000, 1 }, { mips64_emit_SWL , 0xfc000000 , 0xa8000000, 1 }, { mips64_emit_SWR , 0xfc000000 , 0xb8000000, 1 }, { mips64_emit_SYNC , 0xfffff83f , 0x0000000f, 1 }, { mips64_emit_SYSCALL , 0xfc00003f , 0x0000000c, 1 }, { mips64_emit_TEQ , 0xfc00003f , 0x00000034, 1 }, { mips64_emit_TEQI , 0xfc1f0000 , 0x040c0000, 1 }, { mips64_emit_TLBP , 0xffffffff , 0x42000008, 1 }, { mips64_emit_TLBR , 0xffffffff , 0x42000001, 1 }, { mips64_emit_TLBWI , 0xffffffff , 0x42000002, 1 }, { mips64_emit_TLBWR , 0xffffffff , 0x42000006, 1 }, { mips64_emit_XOR , 0xfc0007ff , 0x00000026, 1 }, { mips64_emit_XORI , 0xfc000000 , 0x38000000, 1 }, { mips64_emit_unknown , 0x00000000 , 0x00000000, 1 }, { NULL , 0x00000000 , 0x00000000, 0 }, }; dynamips-0.2.14/stable/mips64_x86_trans.h000066400000000000000000000027361241034141600201070ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __MIPS64_X86_TRANS_H__ #define __MIPS64_X86_TRANS_H__ #include "utils.h" #include "x86-codegen.h" #include "cpu.h" #include "mips64_exec.h" #include "dynamips.h" #define JIT_SUPPORT 1 /* Manipulate bitmasks atomically */ static forced_inline void atomic_or(m_uint32_t *v,m_uint32_t m) { __asm__ __volatile__("lock; orl %1,%0":"=m"(*v):"ir"(m),"m"(*v)); } static forced_inline void atomic_and(m_uint32_t *v,m_uint32_t m) { __asm__ __volatile__("lock; andl %1,%0":"=m"(*v):"ir"(m),"m"(*v)); } /* Wrappers to x86-codegen functions */ #define mips64_jit_tcb_set_patch x86_patch #define mips64_jit_tcb_set_jump x86_jump_code /* MIPS instruction array */ extern struct mips64_insn_tag mips64_insn_tags[]; /* Push epilog for an x86 instruction block */ static forced_inline void mips64_jit_tcb_push_epilog(mips64_jit_tcb_t *block) { x86_ret(block->jit_ptr); } /* Execute JIT code */ static forced_inline void mips64_jit_tcb_exec(cpu_mips_t *cpu,mips64_jit_tcb_t *block) { insn_tblock_fptr jit_code; m_uint32_t offset; offset = (cpu->pc & MIPS_MIN_PAGE_IMASK) >> 2; jit_code = (insn_tblock_fptr)block->jit_insn_ptr[offset]; if (unlikely(!jit_code)) { mips64_exec_single_step(cpu,vmtoh32(block->mips_code[offset])); return; } asm volatile ("movl %0,%%edi"::"r"(cpu): "esi","edi","eax","ebx","ecx","edx"); jit_code(); } #endif dynamips-0.2.14/stable/mips_mts.c000066400000000000000000000430021241034141600166660ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Template code for MTS. */ #define MTS_ENTRY MTS_NAME(entry_t) #define MTS_CACHE(cpu) ( cpu->mts_u. MTS_NAME(cache) ) /* Forward declarations */ static forced_inline void *MTS_PROTO(access)(cpu_mips_t *cpu,m_uint64_t vaddr, u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data); static fastcall int MTS_PROTO(translate)(cpu_mips_t *cpu,m_uint64_t vaddr, m_uint32_t *phys_page); /* Initialize the MTS subsystem for the specified CPU */ int MTS_PROTO(init)(cpu_mips_t *cpu) { size_t len; /* Initialize the cache entries to 0 (empty) */ len = MTS_NAME_UP(HASH_SIZE) * sizeof(MTS_ENTRY); if (!(MTS_CACHE(cpu) = malloc(len))) return(-1); memset(MTS_CACHE(cpu),0xFF,len); cpu->mts_lookups = 0; cpu->mts_misses = 0; return(0); } /* Free memory used by MTS */ void MTS_PROTO(shutdown)(cpu_mips_t *cpu) { /* Free the cache itself */ free(MTS_CACHE(cpu)); MTS_CACHE(cpu) = NULL; } /* Show MTS detailed information (debugging only!) */ void MTS_PROTO(show_stats)(cpu_gen_t *gen_cpu) { cpu_mips_t *cpu = CPU_MIPS64(gen_cpu); #if DEBUG_MTS_MAP_VIRT MTS_ENTRY *entry; u_int i,count; #endif printf("\nCPU%u: MTS%d statistics:\n",cpu->gen->id,MTS_ADDR_SIZE); #if DEBUG_MTS_MAP_VIRT /* Valid hash entries */ for(count=0,i=0;igvpa & MTS_INV_ENTRY_MASK)) { printf(" %4u: vaddr=0x%8.8llx, paddr=0x%8.8llx, hpa=%p\n", i,(m_uint64_t)entry->gvpa,(m_uint64_t)entry->gppa, (void *)entry->hpa); count++; } } printf(" %u/%u valid hash entries.\n",count,MTS_NAME_UP(HASH_SIZE)); #endif printf(" Total lookups: %llu, misses: %llu, efficiency: %g%%\n", cpu->mts_lookups, cpu->mts_misses, 100 - ((double)(cpu->mts_misses*100)/ (double)cpu->mts_lookups)); } /* Invalidate the complete MTS cache */ void MTS_PROTO(invalidate_cache)(cpu_mips_t *cpu) { size_t len; len = MTS_NAME_UP(HASH_SIZE) * sizeof(MTS_ENTRY); memset(MTS_CACHE(cpu),0xFF,len); } /* Invalidate partially the MTS cache, given a TLB entry index */ void MTS_PROTO(invalidate_tlb_entry)(cpu_mips_t *cpu,u_int tlb_index) { MTS_PROTO(invalidate_cache)(cpu); } /* * MTS mapping. * * It is NOT inlined since it triggers a GCC bug on my config (x86, GCC 3.3.5) */ static no_inline MTS_ENTRY * MTS_PROTO(map)(cpu_mips_t *cpu,u_int op_type,mts_map_t *map, MTS_ENTRY *entry,MTS_ENTRY *alt_entry) { struct vdevice *dev; m_uint32_t offset; m_iptr_t host_ptr; int cow; if (!(dev = dev_lookup(cpu->vm,map->paddr,map->cached))) return NULL; if (dev->flags & VDEVICE_FLAG_SPARSE) { host_ptr = dev_sparse_get_host_addr(cpu->vm,dev,map->paddr,op_type,&cow); entry->gvpa = map->vaddr; entry->gppa = map->paddr; entry->hpa = host_ptr; entry->flags = (cow) ? MTS_FLAG_COW : 0; return entry; } if (!dev->host_addr || (dev->flags & VDEVICE_FLAG_NO_MTS_MMAP)) { offset = (map->paddr + map->offset) - dev->phys_addr; /* device entries are never stored in virtual TLB */ alt_entry->hpa = (dev->id << MTS_DEVID_SHIFT) + offset; alt_entry->flags = MTS_FLAG_DEV; return alt_entry; } entry->gvpa = map->vaddr; entry->gppa = map->paddr; entry->hpa = dev->host_addr + (map->paddr - dev->phys_addr); entry->flags = 0; return entry; } /* MTS lookup */ static void *MTS_PROTO(lookup)(cpu_mips_t *cpu,m_uint64_t vaddr) { m_uint64_t data; return(MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LOOKUP,4,MTS_READ,&data)); } /* === MIPS Memory Operations ============================================= */ /* LB: Load Byte */ fastcall void MTS_PROTO(lb)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LB,1,MTS_READ,&data); if (likely(haddr != NULL)) data = *(m_uint8_t *)haddr; cpu->gpr[reg] = sign_extend(data,8); } /* LBU: Load Byte Unsigned */ fastcall void MTS_PROTO(lbu)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LBU,1,MTS_READ,&data); if (likely(haddr != NULL)) data = *(m_uint8_t *)haddr; cpu->gpr[reg] = data & 0xff; } /* LH: Load Half-Word */ fastcall void MTS_PROTO(lh)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LH,2,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh16(*(m_uint16_t *)haddr); cpu->gpr[reg] = sign_extend(data,16); } /* LHU: Load Half-Word Unsigned */ fastcall void MTS_PROTO(lhu)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LHU,2,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh16(*(m_uint16_t *)haddr); cpu->gpr[reg] = data & 0xffff; } /* LW: Load Word */ fastcall void MTS_PROTO(lw)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LW,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); cpu->gpr[reg] = sign_extend(data,32); } /* LWU: Load Word Unsigned */ fastcall void MTS_PROTO(lwu)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LWU,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); cpu->gpr[reg] = data & 0xffffffff; } /* LD: Load Double-Word */ fastcall void MTS_PROTO(ld)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LD,8,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); cpu->gpr[reg] = data; } /* SB: Store Byte */ fastcall void MTS_PROTO(sb)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->gpr[reg] & 0xff; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_SB,1,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint8_t *)haddr = data; } /* SH: Store Half-Word */ fastcall void MTS_PROTO(sh)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->gpr[reg] & 0xffff; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_SH,2,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint16_t *)haddr = htovm16(data); } /* SW: Store Word */ fastcall void MTS_PROTO(sw)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->gpr[reg] & 0xffffffff; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_SW,4,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint32_t *)haddr = htovm32(data); } /* SD: Store Double-Word */ fastcall void MTS_PROTO(sd)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->gpr[reg]; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_SD,8,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint64_t *)haddr = htovm64(data); } /* LDC1: Load Double-Word To Coprocessor 1 */ fastcall void MTS_PROTO(ldc1)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LDC1,8,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); cpu->fpu.reg[reg] = data; } /* LWL: Load Word Left */ fastcall void MTS_PROTO(lwl)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t r_mask,naddr; m_uint64_t data; u_int m_shift; void *haddr; naddr = vaddr & ~(0x03); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_LWL,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); m_shift = (vaddr & 0x03) << 3; r_mask = (1ULL << m_shift) - 1; data <<= m_shift; cpu->gpr[reg] &= r_mask; cpu->gpr[reg] |= data; cpu->gpr[reg] = sign_extend(cpu->gpr[reg],32); } /* LWR: Load Word Right */ fastcall void MTS_PROTO(lwr)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t r_mask,naddr; m_uint64_t data; u_int m_shift; void *haddr; naddr = vaddr & ~(0x03); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_LWR,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); m_shift = ((vaddr & 0x03) + 1) << 3; r_mask = (1ULL << m_shift) - 1; data = sign_extend(data >> (32 - m_shift),32); r_mask = sign_extend(r_mask,32); cpu->gpr[reg] &= ~r_mask; cpu->gpr[reg] |= data; } /* LDL: Load Double-Word Left */ fastcall void MTS_PROTO(ldl)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t r_mask,naddr; m_uint64_t data; u_int m_shift; void *haddr; naddr = vaddr & ~(0x07); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_LDL,8,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); m_shift = (vaddr & 0x07) << 3; r_mask = (1ULL << m_shift) - 1; data <<= m_shift; cpu->gpr[reg] &= r_mask; cpu->gpr[reg] |= data; } /* LDR: Load Double-Word Right */ fastcall void MTS_PROTO(ldr)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t r_mask,naddr; m_uint64_t data; u_int m_shift; void *haddr; naddr = vaddr & ~(0x07); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_LDR,8,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); m_shift = ((vaddr & 0x07) + 1) << 3; r_mask = (1ULL << m_shift) - 1; data >>= (64 - m_shift); cpu->gpr[reg] &= ~r_mask; cpu->gpr[reg] |= data; } /* SWL: Store Word Left */ fastcall void MTS_PROTO(swl)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t d_mask,naddr; m_uint64_t data; u_int r_shift; void *haddr; naddr = vaddr & ~(0x03ULL); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SWL,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); r_shift = (vaddr & 0x03) << 3; d_mask = 0xffffffff >> r_shift; data &= ~d_mask; data |= (cpu->gpr[reg] & 0xffffffff) >> r_shift; haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SWL,4,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint32_t *)haddr = htovm32(data); } /* SWR: Store Word Right */ fastcall void MTS_PROTO(swr)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t d_mask,naddr; m_uint64_t data; u_int r_shift; void *haddr; naddr = vaddr & ~(0x03); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SWR,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); r_shift = ((vaddr & 0x03) + 1) << 3; d_mask = 0xffffffff >> r_shift; data &= d_mask; data |= (cpu->gpr[reg] << (32 - r_shift)) & 0xffffffff; haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SWR,4,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint32_t *)haddr = htovm32(data); } /* SDL: Store Double-Word Left */ fastcall void MTS_PROTO(sdl)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t d_mask,naddr; m_uint64_t data; u_int r_shift; void *haddr; naddr = vaddr & ~(0x07); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SDL,8,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); r_shift = (vaddr & 0x07) << 3; d_mask = 0xffffffffffffffffULL >> r_shift; data &= ~d_mask; data |= cpu->gpr[reg] >> r_shift; haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SDL,8,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint64_t *)haddr = htovm64(data); } /* SDR: Store Double-Word Right */ fastcall void MTS_PROTO(sdr)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t d_mask,naddr; m_uint64_t data; u_int r_shift; void *haddr; naddr = vaddr & ~(0x07); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SDR,8,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); r_shift = ((vaddr & 0x07) + 1) << 3; d_mask = 0xffffffffffffffffULL >> r_shift; data &= d_mask; data |= cpu->gpr[reg] << (64 - r_shift); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SDR,8,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint64_t *)haddr = htovm64(data); } /* LL: Load Linked */ fastcall void MTS_PROTO(ll)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LL,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); cpu->gpr[reg] = sign_extend(data,32); cpu->ll_bit = 1; } /* SC: Store Conditional */ fastcall void MTS_PROTO(sc)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; if (cpu->ll_bit) { data = cpu->gpr[reg] & 0xffffffff; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_SC,4,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint32_t *)haddr = htovm32(data); } cpu->gpr[reg] = cpu->ll_bit; } /* SDC1: Store Double-Word from Coprocessor 1 */ fastcall void MTS_PROTO(sdc1)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->fpu.reg[reg]; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_SDC1,8,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint64_t *)haddr = htovm64(data); } /* CACHE: Cache operation */ fastcall void MTS_PROTO(cache)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int op) { mips64_jit_tcb_t *block; m_uint32_t pc_hash; #if DEBUG_CACHE cpu_log(cpu->gen, "MTS","CACHE: PC=0x%llx, vaddr=0x%llx, cache=%u, code=%u\n", cpu->pc, vaddr, op & 0x3, op >> 2); #endif if (cpu->exec_blk_map) { pc_hash = mips64_jit_get_pc_hash(vaddr); block = cpu->exec_blk_map[pc_hash]; if (block && (block->start_pc == (vaddr & MIPS_MIN_PAGE_MASK))) { #if DEBUG_CACHE cpu_log(cpu->gen,"MTS", "CACHE: removing compiled page at 0x%llx, pc=0x%llx\n", block->start_pc,cpu->pc); #endif cpu->exec_blk_map[pc_hash] = NULL; mips64_jit_tcb_free(cpu,block,TRUE); } else { #if DEBUG_CACHE cpu_log(cpu->gen,"MTS", "CACHE: trying to remove page 0x%llx with pc=0x%llx\n", vaddr, cpu->pc); #endif } } } /* === MTS Cache Management ============================================= */ /* MTS map/unmap/rebuild "API" functions */ void MTS_PROTO(api_map)(cpu_mips_t *cpu,m_uint64_t vaddr,m_uint64_t paddr, m_uint32_t len,int cache_access,int tlb_index) { /* nothing to do, the cache will be filled on-the-fly */ } void MTS_PROTO(api_unmap)(cpu_mips_t *cpu,m_uint64_t vaddr,m_uint32_t len, m_uint32_t val,int tlb_index) { /* Invalidate the TLB entry or the full cache if no index is specified */ if (tlb_index != -1) MTS_PROTO(invalidate_tlb_entry)(cpu,tlb_index); else MTS_PROTO(invalidate_cache)(cpu); } void MTS_PROTO(api_rebuild)(cpu_gen_t *cpu) { MTS_PROTO(invalidate_cache)(CPU_MIPS64(cpu)); } /* ======================================================================== */ /* Initialize memory access vectors */ void MTS_PROTO(init_memop_vectors)(cpu_mips_t *cpu) { /* XXX TODO: * - LD/SD forbidden in Supervisor/User modes with 32-bit addresses. */ cpu->addr_mode = MTS_ADDR_SIZE; /* API vectors */ cpu->mts_map = MTS_PROTO(api_map); cpu->mts_unmap = MTS_PROTO(api_unmap); /* Memory lookup operation */ cpu->mem_op_lookup = MTS_PROTO(lookup); /* Translation operation */ cpu->translate = MTS_PROTO(translate); /* Shutdown operation */ cpu->mts_shutdown = MTS_PROTO(shutdown); /* Rebuild MTS data structures */ cpu->gen->mts_rebuild = MTS_PROTO(api_rebuild); /* Show statistics */ cpu->gen->mts_show_stats = MTS_PROTO(show_stats); /* Load Operations */ cpu->mem_op_fn[MIPS_MEMOP_LB] = MTS_PROTO(lb); cpu->mem_op_fn[MIPS_MEMOP_LBU] = MTS_PROTO(lbu); cpu->mem_op_fn[MIPS_MEMOP_LH] = MTS_PROTO(lh); cpu->mem_op_fn[MIPS_MEMOP_LHU] = MTS_PROTO(lhu); cpu->mem_op_fn[MIPS_MEMOP_LW] = MTS_PROTO(lw); cpu->mem_op_fn[MIPS_MEMOP_LWU] = MTS_PROTO(lwu); cpu->mem_op_fn[MIPS_MEMOP_LD] = MTS_PROTO(ld); cpu->mem_op_fn[MIPS_MEMOP_LDL] = MTS_PROTO(ldl); cpu->mem_op_fn[MIPS_MEMOP_LDR] = MTS_PROTO(ldr); /* Store Operations */ cpu->mem_op_fn[MIPS_MEMOP_SB] = MTS_PROTO(sb); cpu->mem_op_fn[MIPS_MEMOP_SH] = MTS_PROTO(sh); cpu->mem_op_fn[MIPS_MEMOP_SW] = MTS_PROTO(sw); cpu->mem_op_fn[MIPS_MEMOP_SD] = MTS_PROTO(sd); /* Load Left/Right operations */ cpu->mem_op_fn[MIPS_MEMOP_LWL] = MTS_PROTO(lwl); cpu->mem_op_fn[MIPS_MEMOP_LWR] = MTS_PROTO(lwr); cpu->mem_op_fn[MIPS_MEMOP_LDL] = MTS_PROTO(ldl); cpu->mem_op_fn[MIPS_MEMOP_LDR] = MTS_PROTO(ldr); /* Store Left/Right operations */ cpu->mem_op_fn[MIPS_MEMOP_SWL] = MTS_PROTO(swl); cpu->mem_op_fn[MIPS_MEMOP_SWR] = MTS_PROTO(swr); cpu->mem_op_fn[MIPS_MEMOP_SDL] = MTS_PROTO(sdl); cpu->mem_op_fn[MIPS_MEMOP_SDR] = MTS_PROTO(sdr); /* LL/SC - Load Linked / Store Conditional */ cpu->mem_op_fn[MIPS_MEMOP_LL] = MTS_PROTO(ll); cpu->mem_op_fn[MIPS_MEMOP_SC] = MTS_PROTO(sc); /* Coprocessor 1 memory access functions */ cpu->mem_op_fn[MIPS_MEMOP_LDC1] = MTS_PROTO(ldc1); cpu->mem_op_fn[MIPS_MEMOP_SDC1] = MTS_PROTO(sdc1); /* Cache Operation */ cpu->mem_op_fn[MIPS_MEMOP_CACHE] = MTS_PROTO(cache); } #undef MTS_ADDR_SIZE #undef MTS_NAME #undef MTS_NAME_UP #undef MTS_PROTO #undef MTS_PROTO_UP #undef MTS_ENTRY #undef MTS_CHUNK dynamips-0.2.14/stable/ppc32.c000066400000000000000000000367001241034141600157710ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * PowerPC (32-bit) generic routines. */ #include #include #include #include #include #include #include #include #include "rbtree.h" #include "cpu.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "ppc32_mem.h" #include "ppc32_exec.h" #include "ppc32_jit.h" /* Reset a PowerPC CPU */ int ppc32_reset(cpu_ppc_t *cpu) { cpu->ia = PPC32_ROM_START; cpu->gpr[1] = PPC32_ROM_SP; cpu->msr = PPC32_MSR_IP; /* Restart the MTS subsystem */ ppc32_mem_restart(cpu); /* Flush JIT structures */ ppc32_jit_flush(cpu,0); return(0); } /* Initialize a PowerPC processor */ int ppc32_init(cpu_ppc_t *cpu) { /* Initialize JIT operations */ jit_op_init_cpu(cpu->gen); /* Initialize idle timer */ cpu->gen->idle_max = 500; cpu->gen->idle_sleep_time = 30000; /* Timer IRQ parameters (default frequency: 250 Hz <=> 4ms period) */ cpu->timer_irq_check_itv = 1000; cpu->timer_irq_freq = 250; /* Enable/disable direct block jump */ cpu->exec_blk_direct_jump = cpu->vm->exec_blk_direct_jump; /* Idle loop mutex and condition */ pthread_mutex_init(&cpu->gen->idle_mutex,NULL); pthread_cond_init(&cpu->gen->idle_cond,NULL); /* Set the CPU methods */ cpu->gen->reg_set = (void *)ppc32_reg_set; cpu->gen->reg_dump = (void *)ppc32_dump_regs; cpu->gen->mmu_dump = (void *)ppc32_dump_mmu; cpu->gen->mmu_raw_dump = (void *)ppc32_dump_mmu; cpu->gen->add_breakpoint = (void *)ppc32_add_breakpoint; cpu->gen->remove_breakpoint = (void *)ppc32_remove_breakpoint; cpu->gen->set_idle_pc = (void *)ppc32_set_idle_pc; cpu->gen->get_idling_pc = (void *)ppc32_get_idling_pc; /* zzz */ memset(cpu->vtlb,0xFF,sizeof(cpu->vtlb)); /* Set the startup parameters */ ppc32_reset(cpu); return(0); } /* Delete a PowerPC processor */ void ppc32_delete(cpu_ppc_t *cpu) { if (cpu) { ppc32_mem_shutdown(cpu); ppc32_jit_shutdown(cpu); } } /* Set the processor version register (PVR) */ void ppc32_set_pvr(cpu_ppc_t *cpu,m_uint32_t pvr) { cpu->pvr = pvr; ppc32_mem_restart(cpu); } /* Set idle PC value */ void ppc32_set_idle_pc(cpu_gen_t *cpu,m_uint64_t addr) { CPU_PPC32(cpu)->idle_pc = (m_uint32_t)addr; } /* Timer IRQ */ void *ppc32_timer_irq_run(cpu_ppc_t *cpu) { pthread_mutex_t umutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t ucond = PTHREAD_COND_INITIALIZER; struct timespec t_spc; m_tmcnt_t expire; u_int interval; u_int threshold; #if 0 while(!cpu->timer_irq_armed) sleep(1); #endif interval = 1000000 / cpu->timer_irq_freq; threshold = cpu->timer_irq_freq * 10; expire = m_gettime_usec() + interval; while(cpu->gen->state != CPU_STATE_HALTED) { pthread_mutex_lock(&umutex); t_spc.tv_sec = expire / 1000000; t_spc.tv_nsec = (expire % 1000000) * 1000; pthread_cond_timedwait(&ucond,&umutex,&t_spc); pthread_mutex_unlock(&umutex); if (likely(!cpu->irq_disable) && likely(cpu->gen->state == CPU_STATE_RUNNING) && likely(cpu->msr & PPC32_MSR_EE)) { cpu->timer_irq_pending++; if (unlikely(cpu->timer_irq_pending > threshold)) { cpu->timer_irq_pending = 0; cpu->timer_drift++; #if 0 printf("Timer IRQ not accurate (%u pending IRQ): " "reduce the \"--timer-irq-check-itv\" parameter " "(current value: %u)\n", cpu->timer_irq_pending,cpu->timer_irq_check_itv); #endif } } expire += interval; } return NULL; } #define IDLE_HASH_SIZE 8192 /* Idle PC hash item */ struct ppc32_idle_pc_hash { m_uint32_t ia; u_int count; struct ppc32_idle_pc_hash *next; }; /* Determine an "idling" PC */ int ppc32_get_idling_pc(cpu_gen_t *cpu) { cpu_ppc_t *pcpu = CPU_PPC32(cpu); struct ppc32_idle_pc_hash **pc_hash,*p; struct cpu_idle_pc *res; u_int h_index; m_uint32_t cur_ia; int i; cpu->idle_pc_prop_count = 0; if (pcpu->idle_pc != 0) { printf("\nYou already use an idle PC, using the calibration would give " "incorrect results.\n"); return(-1); } printf("\nPlease wait while gathering statistics...\n"); pc_hash = calloc(IDLE_HASH_SIZE,sizeof(struct ppc32_idle_pc_hash *)); /* Disable IRQ */ pcpu->irq_disable = TRUE; /* Take 1000 measures, each mesure every 10ms */ for(i=0;i<1000;i++) { cur_ia = pcpu->ia; h_index = (cur_ia >> 2) & (IDLE_HASH_SIZE-1); for(p=pc_hash[h_index];p;p=p->next) if (p->ia == cur_ia) { p->count++; break; } if (!p) { if ((p = malloc(sizeof(*p)))) { p->ia = cur_ia; p->count = 1; p->next = pc_hash[h_index]; pc_hash[h_index] = p; } } usleep(10000); } /* Select PCs */ for(i=0;inext) if ((p->count >= 20) && (p->count <= 80)) { res = &cpu->idle_pc_prop[cpu->idle_pc_prop_count++]; res->pc = p->ia; res->count = p->count; if (cpu->idle_pc_prop_count >= CPU_IDLE_PC_MAX_RES) goto done; } } done: /* Set idle PC */ if (cpu->idle_pc_prop_count) { printf("Done. Suggested idling PC:\n"); for(i=0;iidle_pc_prop_count;i++) { printf(" 0x%llx (count=%u)\n", cpu->idle_pc_prop[i].pc, cpu->idle_pc_prop[i].count); } printf("Restart the emulator with \"--idle-pc=0x%llx\" (for example)\n", cpu->idle_pc_prop[0].pc); } else { printf("Done. No suggestion for idling PC, dumping the full table:\n"); for(i=0;inext) { printf(" 0x%8.8x (%3u)\n",p->ia,p->count); if (cpu->idle_pc_prop_count < CPU_IDLE_PC_MAX_RES) { res = &cpu->idle_pc_prop[cpu->idle_pc_prop_count++]; res->pc = p->ia; res->count = p->count; } } printf("\n"); } /* Re-enable IRQ */ pcpu->irq_disable = FALSE; return(0); } #if 0 /* Set an IRQ (VM IRQ standard routing) */ void ppc32_vm_set_irq(vm_instance_t *vm,u_int irq) { cpu_ppc_t *boot_cpu; boot_cpu = CPU_PPC32(vm->boot_cpu); if (boot_cpu->irq_disable) { boot_cpu->irq_pending = 0; return; } ppc32_set_irq(boot_cpu,irq); if (boot_cpu->irq_idle_preempt[irq]) cpu_idle_break_wait(vm->boot_cpu); } /* Clear an IRQ (VM IRQ standard routing) */ void ppc32_vm_clear_irq(vm_instance_t *vm,u_int irq) { cpu_ppc_t *boot_cpu; boot_cpu = CPU_PPC32(vm->boot_cpu); ppc32_clear_irq(boot_cpu,irq); } #endif /* Generate an exception */ void ppc32_trigger_exception(cpu_ppc_t *cpu,u_int exc_vector) { //printf("TRIGGER_EXCEPTION: saving cpu->ia=0x%8.8x, msr=0x%8.8x\n", // cpu->ia,cpu->msr); /* Save the return instruction address */ cpu->srr0 = cpu->ia; if (exc_vector == PPC32_EXC_SYSCALL) cpu->srr0 += sizeof(ppc_insn_t); //printf("SRR0 = 0x%8.8x\n",cpu->srr0); /* Save Machine State Register (MSR) */ cpu->srr1 = cpu->msr & PPC32_EXC_SRR1_MASK; //printf("SRR1 = 0x%8.8x\n",cpu->srr1); /* Set the new SRR value */ cpu->msr &= ~PPC32_EXC_MSR_MASK; cpu->irq_check = FALSE; //printf("MSR = 0x%8.8x\n",cpu->msr); /* Use bootstrap vectors ? */ if (cpu->msr & PPC32_MSR_IP) cpu->ia = 0xFFF00000 + exc_vector; else cpu->ia = exc_vector; } /* Trigger IRQs */ fastcall void ppc32_trigger_irq(cpu_ppc_t *cpu) { if (unlikely(cpu->irq_disable)) { cpu->irq_pending = FALSE; cpu->irq_check = FALSE; return; } /* Clear the IRQ check flag */ cpu->irq_check = FALSE; if (cpu->irq_pending && (cpu->msr & PPC32_MSR_EE)) { cpu->irq_count++; cpu->irq_pending = FALSE; ppc32_trigger_exception(cpu,PPC32_EXC_EXT); } } /* Trigger the decrementer exception */ void ppc32_trigger_timer_irq(cpu_ppc_t *cpu) { cpu->timer_irq_count++; if (cpu->msr & PPC32_MSR_EE) ppc32_trigger_exception(cpu,PPC32_EXC_DEC); } /* Virtual breakpoint */ fastcall void ppc32_run_breakpoint(cpu_ppc_t *cpu) { cpu_log(cpu->gen,"BREAKPOINT", "Virtual breakpoint reached at IA=0x%8.8x\n",cpu->ia); printf("[[[ Virtual Breakpoint reached at IA=0x%8.8x LR=0x%8.8x]]]\n", cpu->ia,cpu->lr); ppc32_dump_regs(cpu->gen); } /* Add a virtual breakpoint */ int ppc32_add_breakpoint(cpu_gen_t *cpu,m_uint64_t ia) { cpu_ppc_t *pcpu = CPU_PPC32(cpu); int i; for(i=0;ibreakpoints[i]) break; if (i == PPC32_MAX_BREAKPOINTS) return(-1); pcpu->breakpoints[i] = ia; pcpu->breakpoints_enabled = TRUE; return(0); } /* Remove a virtual breakpoint */ void ppc32_remove_breakpoint(cpu_gen_t *cpu,m_uint64_t ia) { cpu_ppc_t *pcpu = CPU_PPC32(cpu); int i,j; for(i=0;ibreakpoints[i] == ia) { for(j=i;jbreakpoints[j] = pcpu->breakpoints[j+1]; pcpu->breakpoints[PPC32_MAX_BREAKPOINTS-1] = 0; } for(i=0;ibreakpoints[i] != 0) return; pcpu->breakpoints_enabled = FALSE; } /* Set a register */ void ppc32_reg_set(cpu_gen_t *cpu,u_int reg,m_uint64_t val) { if (reg < PPC32_GPR_NR) CPU_PPC32(cpu)->gpr[reg] = (m_uint32_t)val; } /* Dump registers of a PowerPC processor */ void ppc32_dump_regs(cpu_gen_t *cpu) { cpu_ppc_t *pcpu = CPU_PPC32(cpu); int i; printf("PowerPC Registers:\n"); for(i=0;igpr[i*4], (i*4)+1, pcpu->gpr[(i*4)+1], (i*4)+2, pcpu->gpr[(i*4)+2], (i*4)+3, pcpu->gpr[(i*4)+3]); } printf("\n"); printf(" ia = 0x%8.8x, lr = 0x%8.8x\n", pcpu->ia, pcpu->lr); printf(" cr = 0x%8.8x, msr = 0x%8.8x, xer = 0x%8.8x, dec = 0x%8.8x\n", ppc32_get_cr(pcpu), pcpu->msr, pcpu->xer | (pcpu->xer_ca << PPC32_XER_CA_BIT), pcpu->dec); printf(" sprg[0] = 0x%8.8x, sprg[1] = 0x%8.8x\n", pcpu->sprg[0],pcpu->sprg[1]); printf(" sprg[2] = 0x%8.8x, sprg[3] = 0x%8.8x\n", pcpu->sprg[2],pcpu->sprg[3]); printf("\n IRQ count: %llu, IRQ false positives: %llu, " "IRQ Pending: %u, IRQ Check: %s\n", pcpu->irq_count,pcpu->irq_fp_count,pcpu->irq_pending, pcpu->irq_check ? "yes" : "no"); printf(" Timer IRQ count: %llu, pending: %u, timer drift: %u\n\n", pcpu->timer_irq_count,pcpu->timer_irq_pending,pcpu->timer_drift); printf(" Device access count: %llu\n",cpu->dev_access_counter); printf("\n"); } /* Dump BAT registers */ static void ppc32_dump_bat(cpu_ppc_t *cpu,int index) { int i; for(i=0;ibat[index][i].reg[0],cpu->bat[index][i].reg[1]); } /* Dump MMU registers */ void ppc32_dump_mmu(cpu_gen_t *cpu) { cpu_ppc_t *pcpu = CPU_PPC32(cpu); int i; printf("PowerPC MMU Registers:\n"); printf(" - IBAT Registers:\n"); ppc32_dump_bat(pcpu,PPC32_IBAT_IDX); printf(" - DBAT Registers:\n"); ppc32_dump_bat(pcpu,PPC32_DBAT_IDX); printf(" - Segment Registers:\n"); for(i=0;isr[i]); printf(" - SDR1: 0x%8.8x\n",pcpu->sdr1); } /* Load a raw image into the simulated memory */ int ppc32_load_raw_image(cpu_ppc_t *cpu,char *filename,m_uint32_t vaddr) { struct stat file_info; size_t len,clen; m_uint32_t remain; void *haddr; FILE *bfd; if (!(bfd = fopen(filename,"r"))) { perror("fopen"); return(-1); } if (fstat(fileno(bfd),&file_info) == -1) { perror("stat"); return(-1); } len = file_info.st_size; printf("Loading RAW file '%s' at virtual address 0x%8.8x (size=%lu)\n", filename,vaddr,(u_long)len); while(len > 0) { haddr = cpu->mem_op_lookup(cpu,vaddr,PPC32_MTS_DCACHE); if (!haddr) { fprintf(stderr,"load_raw_image: invalid load address 0x%8.8x\n", vaddr); return(-1); } if (len > PPC32_MIN_PAGE_SIZE) clen = PPC32_MIN_PAGE_SIZE; else clen = len; remain = MIPS_MIN_PAGE_SIZE; remain -= (vaddr - (vaddr & MIPS_MIN_PAGE_MASK)); clen = m_min(clen,remain); if (fread((u_char *)haddr,clen,1,bfd) != 1) break; vaddr += clen; len -= clen; } fclose(bfd); return(0); } /* Load an ELF image into the simulated memory */ int ppc32_load_elf_image(cpu_ppc_t *cpu,char *filename,int skip_load, m_uint32_t *entry_point) { m_uint32_t vaddr,remain; void *haddr; Elf32_Ehdr *ehdr; Elf32_Shdr *shdr; Elf_Scn *scn; Elf *img_elf; size_t len,clen; _maybe_used char *name; int i,fd; FILE *bfd; if (!filename) return(-1); #ifdef __CYGWIN__ fd = open(filename,O_RDONLY|O_BINARY); #else fd = open(filename,O_RDONLY); #endif if (fd == -1) { perror("load_elf_image: open"); return(-1); } if (elf_version(EV_CURRENT) == EV_NONE) { fprintf(stderr,"load_elf_image: library out of date\n"); return(-1); } if (!(img_elf = elf_begin(fd,ELF_C_READ,NULL))) { fprintf(stderr,"load_elf_image: elf_begin: %s\n", elf_errmsg(elf_errno())); return(-1); } if (!(ehdr = elf32_getehdr(img_elf))) { fprintf(stderr,"load_elf_image: invalid ELF file\n"); return(-1); } printf("Loading ELF file '%s'...\n",filename); bfd = fdopen(fd,"rb"); if (!bfd) { perror("load_elf_image: fdopen"); return(-1); } if (!skip_load) { for(i=0;ie_shnum;i++) { scn = elf_getscn(img_elf,i); shdr = elf32_getshdr(scn); name = elf_strptr(img_elf, ehdr->e_shstrndx, (size_t)shdr->sh_name); len = shdr->sh_size; if (!(shdr->sh_flags & SHF_ALLOC) || !len) continue; fseek(bfd,shdr->sh_offset,SEEK_SET); vaddr = shdr->sh_addr; if (cpu->vm->debug_level > 0) { printf(" * Adding section at virtual address 0x%8.8x " "(len=0x%8.8lx)\n",vaddr,(u_long)len); } while(len > 0) { haddr = cpu->mem_op_lookup(cpu,vaddr,PPC32_MTS_DCACHE); if (!haddr) { fprintf(stderr,"load_elf_image: invalid load address 0x%x\n", vaddr); return(-1); } if (len > PPC32_MIN_PAGE_SIZE) clen = PPC32_MIN_PAGE_SIZE; else clen = len; remain = PPC32_MIN_PAGE_SIZE; remain -= (vaddr - (vaddr & PPC32_MIN_PAGE_MASK)); clen = m_min(clen,remain); if (fread((u_char *)haddr,clen,1,bfd) < 1) break; vaddr += clen; len -= clen; } } } else { printf("ELF loading skipped, using a ghost RAM file.\n"); } printf("ELF entry point: 0x%x\n",ehdr->e_entry); if (entry_point) *entry_point = ehdr->e_entry; elf_end(img_elf); fclose(bfd); return(0); } dynamips-0.2.14/stable/ppc32.h000066400000000000000000000412571241034141600160010ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) */ #ifndef __PPC_32_H__ #define __PPC_32_H__ #include #include "utils.h" #include "rbtree.h" /* CPU identifiers */ #define PPC32_PVR_405 0x40110000 /* Number of GPR (general purpose registers) */ #define PPC32_GPR_NR 32 /* Number of registers in FPU */ #define PPC32_FPU_REG_NR 32 /* Minimum page size: 4 Kb */ #define PPC32_MIN_PAGE_SHIFT 12 #define PPC32_MIN_PAGE_SIZE (1 << PPC32_MIN_PAGE_SHIFT) #define PPC32_MIN_PAGE_IMASK (PPC32_MIN_PAGE_SIZE - 1) #define PPC32_MIN_PAGE_MASK 0xFFFFF000 /* Number of instructions per page */ #define PPC32_INSN_PER_PAGE (PPC32_MIN_PAGE_SIZE/sizeof(ppc_insn_t)) /* Starting point for ROM */ #define PPC32_ROM_START 0xfff00100 #define PPC32_ROM_SP 0x00006000 /* Special Purpose Registers (SPR) */ #define PPC32_SPR_XER 1 #define PPC32_SPR_LR 8 /* Link Register */ #define PPC32_SPR_CTR 9 /* Count Register */ #define PPC32_SPR_DSISR 18 #define PPC32_SPR_DAR 19 #define PPC32_SPR_DEC 22 /* Decrementer */ #define PPC32_SPR_SDR1 25 /* Page Table Address */ #define PPC32_SPR_SRR0 26 #define PPC32_SPR_SRR1 27 #define PPC32_SPR_TBL_READ 268 /* Time Base Low (read) */ #define PPC32_SPR_TBU_READ 269 /* Time Base Up (read) */ #define PPC32_SPR_SPRG0 272 #define PPC32_SPR_SPRG1 273 #define PPC32_SPR_SPRG2 274 #define PPC32_SPR_SPRG3 275 #define PPC32_SPR_TBL_WRITE 284 /* Time Base Low (write) */ #define PPC32_SPR_TBU_WRITE 285 /* Time Base Up (write) */ #define PPC32_SPR_PVR 287 /* Processor Version Register */ #define PPC32_SPR_HID0 1008 #define PPC32_SPR_HID1 1009 #define PPC405_SPR_PID 945 /* Process Identifier */ /* Exception vectors */ #define PPC32_EXC_SYS_RST 0x00000100 /* System Reset */ #define PPC32_EXC_MC_CHK 0x00000200 /* Machine Check */ #define PPC32_EXC_DSI 0x00000300 /* Data memory access failure */ #define PPC32_EXC_ISI 0x00000400 /* Instruction fetch failure */ #define PPC32_EXC_EXT 0x00000500 /* External Interrupt */ #define PPC32_EXC_ALIGN 0x00000600 /* Alignment */ #define PPC32_EXC_PROG 0x00000700 /* FPU, Illegal instruction, ... */ #define PPC32_EXC_NO_FPU 0x00000800 /* FPU unavailable */ #define PPC32_EXC_DEC 0x00000900 /* Decrementer */ #define PPC32_EXC_SYSCALL 0x00000C00 /* System Call */ #define PPC32_EXC_TRACE 0x00000D00 /* Trace */ #define PPC32_EXC_FPU_HLP 0x00000E00 /* Floating-Point Assist */ /* Condition Register (CR) is accessed through 8 fields of 4 bits */ #define ppc32_get_cr_field(n) ((n) >> 2) #define ppc32_get_cr_bit(n) (~(n) & 0x03) /* Positions of LT, GT, EQ and SO bits in CR fields */ #define PPC32_CR_LT_BIT 3 #define PPC32_CR_GT_BIT 2 #define PPC32_CR_EQ_BIT 1 #define PPC32_CR_SO_BIT 0 /* CR0 (Condition Register Field 0) bits */ #define PPC32_CR0_LT_BIT 31 #define PPC32_CR0_LT (1 << PPC32_CR0_LT_BIT) /* Negative */ #define PPC32_CR0_GT_BIT 30 #define PPC32_CR0_GT (1 << PPC32_CR0_GT_BIT) /* Positive */ #define PPC32_CR0_EQ_BIT 29 #define PPC32_CR0_EQ (1 << PPC32_CR0_EQ_BIT) /* Zero */ #define PPC32_CR0_SO_BIT 28 #define PPC32_CR0_SO (1 << PPC32_CR0_SO_BIT) /* Summary overflow */ /* XER register */ #define PPC32_XER_SO_BIT 31 #define PPC32_XER_SO (1 << PPC32_XER_SO_BIT) /* Summary Overflow */ #define PPC32_XER_OV 0x40000000 /* Overflow */ #define PPC32_XER_CA_BIT 29 #define PPC32_XER_CA (1 << PPC32_XER_CA_BIT) /* Carry */ #define PPC32_XER_BC_MASK 0x0000007F /* Byte cnt (lswx/stswx) */ /* MSR (Machine State Register) */ #define PPC32_MSR_POW_MASK 0x00060000 /* Power Management */ #define PPC32_MSR_ILE 0x00010000 /* Exception Little-Endian Mode */ #define PPC32_MSR_EE 0x00008000 /* External Interrupt Enable */ #define PPC32_MSR_PR 0x00004000 /* Privilege Level (0=supervisor) */ #define PPC32_MSR_PR_SHIFT 14 #define PPC32_MSR_FP 0x00002000 /* Floating-Point Available */ #define PPC32_MSR_ME 0x00001000 /* Machine Check Enable */ #define PPC32_MSR_FE0 0x00000800 /* Floating-Point Exception Mode 0 */ #define PPC32_MSR_SE 0x00000400 /* Single-step trace enable */ #define PPC32_MSR_BE 0x00000200 /* Branch Trace Enable */ #define PPC32_MSR_FE1 0x00000100 /* Floating-Point Exception Mode 1 */ #define PPC32_MSR_IP 0x00000040 /* Exception Prefix */ #define PPC32_MSR_IR 0x00000020 /* Instruction address translation */ #define PPC32_MSR_DR 0x00000010 /* Data address translation */ #define PPC32_MSR_RI 0x00000002 /* Recoverable Exception */ #define PPC32_MSR_LE 0x00000001 /* Little-Endian mode enable */ #define PPC32_RFI_MSR_MASK 0x87c0ff73 #define PPC32_EXC_SRR1_MASK 0x0000ff73 #define PPC32_EXC_MSR_MASK 0x0006ef32 /* Number of BAT registers (8 for PowerPC 7448) */ #define PPC32_BAT_NR 8 /* Number of segment registers */ #define PPC32_SR_NR 16 /* Upper BAT register */ #define PPC32_UBAT_BEPI_MASK 0xFFFE0000 /* Block Effective Page Index */ #define PPC32_UBAT_BEPI_SHIFT 17 #define PPC32_UBAT_BL_MASK 0x00001FFC /* Block Length */ #define PPC32_UBAT_BL_SHIFT 2 #define PPC32_UBAT_XBL_MASK 0x0001FFFC /* Block Length */ #define PPC32_UBAT_XBL_SHIFT 2 #define PPC32_UBAT_VS 0x00000002 /* Supervisor mode valid bit */ #define PPC32_UBAT_VP 0x00000001 /* User mode valid bit */ #define PPC32_UBAT_PROT_MASK (PPC32_UBAT_VS|PPC32_UBAT_VP) /* Lower BAT register */ #define PPC32_LBAT_BRPN_MASK 0xFFFE0000 /* Physical address */ #define PPC32_LBAT_BRPN_SHIFT 17 #define PPC32_LBAT_WIMG_MASK 0x00000078 /* Memory/cache access mode bits */ #define PPC32_LBAT_PP_MASK 0x00000003 /* Protection bits */ #define PPC32_BAT_ADDR_SHIFT 17 /* Segment Descriptor */ #define PPC32_SD_T 0x80000000 #define PPC32_SD_KS 0x40000000 /* Supervisor-state protection key */ #define PPC32_SD_KP 0x20000000 /* User-state protection key */ #define PPC32_SD_N 0x10000000 /* No-execute protection bit */ #define PPC32_SD_VSID_MASK 0x00FFFFFF /* Virtual Segment ID */ /* SDR1 Register */ #define PPC32_SDR1_HTABORG_MASK 0xFFFF0000 /* Physical base address */ #define PPC32_SDR1_HTABEXT_MASK 0x0000E000 /* Extended base address */ #define PPC32_SDR1_HTABMASK 0x000001FF /* Mask for page table address */ #define PPC32_SDR1_HTMEXT_MASK 0x00001FFF /* Extended mask */ /* Page Table Entry (PTE) size: 64-bits */ #define PPC32_PTE_SIZE 8 /* PTE entry (Up and Lo) */ #define PPC32_PTEU_V 0x80000000 /* Valid entry */ #define PPC32_PTEU_VSID_MASK 0x7FFFFF80 /* Virtual Segment ID */ #define PPC32_PTEU_VSID_SHIFT 7 #define PPC32_PTEU_H 0x00000040 /* Hash function */ #define PPC32_PTEU_API_MASK 0x0000003F /* Abbreviated Page index */ #define PPC32_PTEL_RPN_MASK 0xFFFFF000 /* Physical Page Number */ #define PPC32_PTEL_XPN_MASK 0x00000C00 /* Extended Page Number (0-2) */ #define PPC32_PTEL_XPN_SHIFT 9 #define PPC32_PTEL_R 0x00000100 /* Referenced bit */ #define PPC32_PTEL_C 0x00000080 /* Changed bit */ #define PPC32_PTEL_WIMG_MASK 0x00000078 /* Mem/cache access mode bits */ #define PPC32_PTEL_WIMG_SHIFT 3 #define PPC32_PTEL_X_MASK 0x00000004 /* Extended Page Number (3) */ #define PPC32_PTEL_X_SHIFT 2 #define PPC32_PTEL_PP_MASK 0x00000003 /* Page Protection bits */ /* DSISR register */ #define PPC32_DSISR_NOTRANS 0x40000000 /* No valid translation */ #define PPC32_DSISR_STORE 0x02000000 /* Store operation */ /* PowerPC 405 TLB definitions */ #define PPC405_TLBHI_EPN_MASK 0xFFFFFC00 /* Effective Page Number */ #define PPC405_TLBHI_SIZE_MASK 0x00000380 /* Page Size */ #define PPC405_TLBHI_SIZE_SHIFT 7 #define PPC405_TLBHI_V 0x00000040 /* Valid TLB entry */ #define PPC405_TLBHI_E 0x00000020 /* Endianness */ #define PPC405_TLBHI_U0 0x00000010 /* User-Defined Attribute */ #define PPC405_TLBLO_RPN_MASK 0xFFFFFC00 /* Real Page Number */ #define PPC405_TLBLO_EX 0x00000200 /* Execute Enable */ #define PPC405_TLBLO_WR 0x00000100 /* Write Enable */ #define PPC405_TLBLO_ZSEL_MASK 0x000000F0 /* Zone Select */ #define PPC405_TLBLO_ZSEL_SHIFT 4 #define PPC405_TLBLO_W 0x00000008 /* Write-Through */ #define PPC405_TLBLO_I 0x00000004 /* Caching Inhibited */ #define PPC405_TLBLO_M 0x00000002 /* Memory Coherent */ #define PPC405_TLBLO_G 0x00000001 /* Guarded */ /* Number of TLB entries for PPC405 */ #define PPC405_TLB_ENTRIES 64 struct ppc405_tlb_entry { m_uint32_t tlb_hi,tlb_lo,tid; }; /* Memory operations */ enum { PPC_MEMOP_LOOKUP = 0, /* Instruction fetch operation */ PPC_MEMOP_IFETCH, /* Load operations */ PPC_MEMOP_LBZ, PPC_MEMOP_LHZ, PPC_MEMOP_LWZ, /* Load operation with sign-extend */ PPC_MEMOP_LHA, /* Store operations */ PPC_MEMOP_STB, PPC_MEMOP_STH, PPC_MEMOP_STW, /* Byte-Reversed operations */ PPC_MEMOP_LWBR, PPC_MEMOP_STWBR, /* String operations */ PPC_MEMOP_LSW, PPC_MEMOP_STSW, /* FPU operations */ PPC_MEMOP_LFD, PPC_MEMOP_STFD, /* ICBI - Instruction Cache Block Invalidate */ PPC_MEMOP_ICBI, PPC_MEMOP_MAX, }; /* PowerPC CPU type */ typedef struct cpu_ppc cpu_ppc_t; /* Memory operation function prototype */ typedef fastcall void (*ppc_memop_fn)(cpu_ppc_t *cpu,m_uint32_t vaddr, u_int reg); /* BAT type indexes */ enum { PPC32_IBAT_IDX = 0, PPC32_DBAT_IDX, }; /* BAT register */ struct ppc32_bat_reg { m_uint32_t reg[2]; }; /* BAT register programming */ struct ppc32_bat_prog { int type,index; m_uint32_t hi,lo; }; /* MTS Instruction Cache and Data Cache */ #define PPC32_MTS_ICACHE PPC32_IBAT_IDX #define PPC32_MTS_DCACHE PPC32_DBAT_IDX /* FPU Coprocessor definition */ typedef struct { m_uint64_t reg[PPC32_FPU_REG_NR]; }ppc_fpu_t; /* Maximum number of breakpoints */ #define PPC32_MAX_BREAKPOINTS 8 /* zzz */ struct ppc32_vtlb_entry { m_uint32_t vaddr; m_uint32_t haddr; }; /* PowerPC CPU definition */ struct cpu_ppc { /* Instruction address */ m_uint32_t ia; /* General Purpose registers */ m_uint32_t gpr[PPC32_GPR_NR]; struct ppc32_vtlb_entry vtlb[PPC32_GPR_NR]; /* Pending IRQ */ volatile m_uint32_t irq_pending,irq_check; /* XER, Condition Register, Link Register, Count Register */ m_uint32_t xer,lr,ctr,reserve; m_uint32_t xer_ca; /* Condition Register (CR) fields */ u_int cr_fields[8]; /* MTS caches (Instruction+Data) */ mts32_entry_t *mts_cache[2]; /* Code page translation cache and physical page mapping */ ppc32_jit_tcb_t **exec_blk_map,**exec_phys_map; /* Virtual address to physical page translation */ fastcall int (*translate)(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int cid, m_uint32_t *phys_page); /* Memory access functions */ ppc_memop_fn mem_op_fn[PPC_MEMOP_MAX]; /* Memory lookup function (to load ELF image,...) */ void *(*mem_op_lookup)(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int cid); /* MTS slow lookup function */ mts32_entry_t *(*mts_slow_lookup)(cpu_ppc_t *cpu,m_uint32_t vaddr, u_int cid,u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data, mts32_entry_t *alt_entry); /* IRQ counters */ m_uint64_t irq_count,timer_irq_count,irq_fp_count; pthread_mutex_t irq_lock; /* Current and free lists of translated code blocks */ ppc32_jit_tcb_t *tcb_list,*tcb_last,*tcb_free_list; /* Executable page area */ void *exec_page_area; size_t exec_page_area_size; size_t exec_page_count,exec_page_alloc; insn_exec_page_t *exec_page_free_list; insn_exec_page_t *exec_page_array; /* Idle PC value */ volatile m_uint32_t idle_pc; /* Timer IRQs */ volatile u_int timer_irq_pending,timer_irq_armed; u_int timer_irq_freq; u_int timer_irq_check_itv; u_int timer_drift; /* IRQ disable flag */ volatile u_int irq_disable; /* IBAT (Instruction) and DBAT (Data) registers */ struct ppc32_bat_reg bat[2][PPC32_BAT_NR]; /* Segment registers */ m_uint32_t sr[PPC32_SR_NR]; /* Page Table Address */ m_uint32_t sdr1; void *sdr1_hptr; /* MSR (Machine state register) */ m_uint32_t msr; /* Interrupt Registers (SRR0/SRR1) */ m_uint32_t srr0,srr1,dsisr,dar; /* SPRG registers */ m_uint32_t sprg[4]; /* PVR (Processor Version Register) */ m_uint32_t pvr; /* Time-Base register */ m_uint64_t tb; /* Decrementer */ m_uint32_t dec; /* Hardware Implementation Dependent Registers */ m_uint32_t hid0,hid1; /* String instruction position (lswi/stswi) */ u_int sw_pos; /* PowerPC 405 TLB */ struct ppc405_tlb_entry ppc405_tlb[PPC405_TLB_ENTRIES]; m_uint32_t ppc405_pid; /* MPC860 IMMR register */ m_uint32_t mpc860_immr; /* FPU */ ppc_fpu_t fpu; /* Generic CPU instance pointer */ cpu_gen_t *gen; /* VM instance */ vm_instance_t *vm; /* MTS cache statistics */ m_uint64_t mts_misses,mts_lookups; /* JIT flush method */ u_int jit_flush_method; /* Number of compiled pages */ u_int compiled_pages; /* Fast memory operations use */ u_int fast_memop; /* Direct block jump */ u_int exec_blk_direct_jump; /* Current exec page (non-JIT) info */ m_uint64_t njm_exec_page; mips_insn_t *njm_exec_ptr; /* Performance counter (non-JIT) */ m_uint32_t perf_counter; /* non-JIT mode instruction counter */ m_uint64_t insn_exec_count; /* Breakpoints */ m_uint32_t breakpoints[PPC32_MAX_BREAKPOINTS]; u_int breakpoints_enabled; /* JIT host register allocation */ char *jit_hreg_seq_name; int ppc_reg_map[PPC32_GPR_NR]; struct hreg_map *hreg_map_list,*hreg_lru; struct hreg_map hreg_map[JIT_HOST_NREG]; }; #define PPC32_CR_FIELD_OFFSET(f) \ (OFFSET(cpu_ppc_t,cr_fields)+((f) * sizeof(u_int))) /* Get the full CR register */ static forced_inline m_uint32_t ppc32_get_cr(cpu_ppc_t *cpu) { m_uint32_t cr = 0; int i; for(i=0;i<8;i++) cr |= cpu->cr_fields[i] << (28 - (i << 2)); return(cr); } /* Set the CR fields given a CR value */ static forced_inline void ppc32_set_cr(cpu_ppc_t *cpu,m_uint32_t cr) { int i; for(i=0;i<8;i++) cpu->cr_fields[i] = (cr >> (28 - (i << 2))) & 0x0F; } /* Get a CR bit */ static forced_inline m_uint32_t ppc32_read_cr_bit(cpu_ppc_t *cpu,u_int bit) { m_uint32_t res; res = cpu->cr_fields[ppc32_get_cr_field(bit)] >> ppc32_get_cr_bit(bit); return(res & 0x01); } /* Set a CR bit */ static forced_inline void ppc32_set_cr_bit(cpu_ppc_t *cpu,u_int bit) { cpu->cr_fields[ppc32_get_cr_field(bit)] |= 1 << ppc32_get_cr_bit(bit); } /* Clear a CR bit */ static forced_inline void ppc32_clear_cr_bit(cpu_ppc_t *cpu,u_int bit) { cpu->cr_fields[ppc32_get_cr_field(bit)] &= ~(1 << ppc32_get_cr_bit(bit)); } /* Reset a PowerPC CPU */ int ppc32_reset(cpu_ppc_t *cpu); /* Initialize a PowerPC processor */ int ppc32_init(cpu_ppc_t *cpu); /* Delete a PowerPC processor */ void ppc32_delete(cpu_ppc_t *cpu); /* Set the processor version register (PVR) */ void ppc32_set_pvr(cpu_ppc_t *cpu,m_uint32_t pvr); /* Set idle PC value */ void ppc32_set_idle_pc(cpu_gen_t *cpu,m_uint64_t addr); /* Timer IRQ */ void *ppc32_timer_irq_run(cpu_ppc_t *cpu); /* Determine an "idling" PC */ int ppc32_get_idling_pc(cpu_gen_t *cpu); /* Generate an exception */ void ppc32_trigger_exception(cpu_ppc_t *cpu,u_int exc_vector); /* Trigger the decrementer exception */ void ppc32_trigger_timer_irq(cpu_ppc_t *cpu); /* Trigger IRQs */ fastcall void ppc32_trigger_irq(cpu_ppc_t *cpu); /* Virtual breakpoint */ fastcall void ppc32_run_breakpoint(cpu_ppc_t *cpu); /* Add a virtual breakpoint */ int ppc32_add_breakpoint(cpu_gen_t *cpu,m_uint64_t ia); /* Remove a virtual breakpoint */ void ppc32_remove_breakpoint(cpu_gen_t *cpu,m_uint64_t ia); /* Set a register */ void ppc32_reg_set(cpu_gen_t *cpu,u_int reg,m_uint64_t val); /* Dump registers of a PowerPC processor */ void ppc32_dump_regs(cpu_gen_t *cpu); /* Dump MMU registers */ void ppc32_dump_mmu(cpu_gen_t *cpu); /* Load a raw image into the simulated memory */ int ppc32_load_raw_image(cpu_ppc_t *cpu,char *filename,m_uint32_t vaddr); /* Load an ELF image into the simulated memory */ int ppc32_load_elf_image(cpu_ppc_t *cpu,char *filename,int skip_load, m_uint32_t *entry_point); /* Run PowerPC code in step-by-step mode */ void *ppc32_exec_run_cpu(cpu_gen_t *gen); #endif dynamips-0.2.14/stable/ppc32_amd64_trans.c000066400000000000000000003277031241034141600202010ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #include #include #include #include #include #include #include #include "cpu.h" #include "jit_op.h" #include "ppc32_jit.h" #include "ppc32_amd64_trans.h" #include "memory.h" /* Macros for CPU structure access */ #define REG_OFFSET(reg) (OFFSET(cpu_ppc_t,gpr[(reg)])) #define MEMOP_OFFSET(op) (OFFSET(cpu_ppc_t,mem_op_fn[(op)])) #define DECLARE_INSN(name) \ static int ppc32_emit_##name(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, \ ppc_insn_t insn) /* EFLAGS to Condition Register (CR) field - signed */ static m_uint32_t eflags_to_cr_signed[64] = { 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, }; /* EFLAGS to Condition Register (CR) field - unsigned */ static m_uint32_t eflags_to_cr_unsigned[256] = { 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, }; /* Load a 32 bit immediate value */ static inline void ppc32_load_imm(u_char **ptr,u_int reg,m_uint32_t val) { if (val) amd64_mov_reg_imm_size(*ptr,reg,val,4); else amd64_alu_reg_reg_size(*ptr,X86_XOR,reg,reg,4); } /* Set the Instruction Address (IA) register */ void ppc32_set_ia(u_char **ptr,m_uint32_t new_ia) { amd64_mov_membase_imm(*ptr,AMD64_R15,OFFSET(cpu_ppc_t,ia),new_ia,4); } /* Set the Link Register (LR) */ static void ppc32_set_lr(jit_op_t *iop,m_uint32_t new_lr) { amd64_mov_membase_imm(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,lr),new_lr,4); } /* * Try to branch directly to the specified JIT block without returning to * main loop. */ static void ppc32_try_direct_far_jump(cpu_ppc_t *cpu,jit_op_t *iop, m_uint32_t new_ia) { m_uint32_t new_page,ia_hash,ia_offset; u_char *test1,*test2,*test3; /* Indicate that we throw %rbx, %rdx */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RBX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RSI); new_page = new_ia & PPC32_MIN_PAGE_MASK; ia_offset = (new_ia & PPC32_MIN_PAGE_IMASK) >> 2; ia_hash = ppc32_jit_get_ia_hash(new_ia); /* Get JIT block info in %rdx */ amd64_mov_reg_membase(iop->ob_ptr,AMD64_RBX, AMD64_R15,OFFSET(cpu_ppc_t,exec_blk_map),8); amd64_mov_reg_membase(iop->ob_ptr,AMD64_RDX, AMD64_RBX,ia_hash*sizeof(void *),8); /* no JIT block found ? */ amd64_test_reg_reg(iop->ob_ptr,AMD64_RDX,AMD64_RDX); test1 = iop->ob_ptr; amd64_branch8(iop->ob_ptr, X86_CC_Z, 0, 1); /* Check block IA */ ppc32_load_imm(&iop->ob_ptr,AMD64_RSI,new_page); amd64_alu_reg_membase_size(iop->ob_ptr,X86_CMP,AMD64_RAX,AMD64_RDX, OFFSET(ppc32_jit_tcb_t,start_ia),4); test2 = iop->ob_ptr; amd64_branch8(iop->ob_ptr, X86_CC_NE, 0, 1); /* Jump to the code */ amd64_mov_reg_membase(iop->ob_ptr,AMD64_RSI, AMD64_RDX,OFFSET(ppc32_jit_tcb_t,jit_insn_ptr),8); amd64_mov_reg_membase(iop->ob_ptr,AMD64_RBX, AMD64_RSI,ia_offset * sizeof(void *),8); amd64_test_reg_reg(iop->ob_ptr,AMD64_RBX,AMD64_RBX); test3 = iop->ob_ptr; amd64_branch8(iop->ob_ptr, X86_CC_Z, 0, 1); amd64_jump_reg(iop->ob_ptr,AMD64_RBX); /* Returns to caller... */ amd64_patch(test1,iop->ob_ptr); amd64_patch(test2,iop->ob_ptr); amd64_patch(test3,iop->ob_ptr); ppc32_set_ia(&iop->ob_ptr,new_ia); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); } /* Set Jump */ static void ppc32_set_jump(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b,jit_op_t *iop, m_uint32_t new_ia,int local_jump) { int return_to_caller = FALSE; u_char *jump_ptr; #if 0 if (cpu->sym_trace && !local_jump) return_to_caller = TRUE; #endif if (!return_to_caller && ppc32_jit_tcb_local_addr(b,new_ia,&jump_ptr)) { ppc32_jit_tcb_record_patch(b,iop,iop->ob_ptr,new_ia); amd64_jump32(iop->ob_ptr,0); } else { if (cpu->exec_blk_direct_jump) { /* Block lookup optimization */ ppc32_try_direct_far_jump(cpu,iop,new_ia); } else { ppc32_set_ia(&iop->ob_ptr,new_ia); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); } } } /* Jump to the next page */ void ppc32_set_page_jump(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b) { jit_op_t *iop,*op_list = NULL; cpu->gen->jit_op_current = &op_list; iop = ppc32_op_emit_insn_output(cpu,4,"set_page_jump"); ppc32_set_jump(cpu,b,iop,b->start_ia + PPC32_MIN_PAGE_SIZE,FALSE); ppc32_op_insn_output(b,iop); jit_op_free_list(cpu->gen,op_list); cpu->gen->jit_op_current = NULL; } /* Load a GPR into the specified host register */ static forced_inline void ppc32_load_gpr(u_char **ptr,u_int host_reg, u_int ppc_reg) { amd64_mov_reg_membase(*ptr,host_reg,AMD64_R15,REG_OFFSET(ppc_reg),4); } /* Store contents for a host register into a GPR register */ static forced_inline void ppc32_store_gpr(u_char **ptr,u_int ppc_reg, u_int host_reg) { amd64_mov_membase_reg(*ptr,AMD64_R15,REG_OFFSET(ppc_reg),host_reg,4); } /* Apply an ALU operation on a GPR register and a host register */ static forced_inline void ppc32_alu_gpr(u_char **ptr,u_int op, u_int host_reg,u_int ppc_reg) { amd64_alu_reg_membase_size(*ptr,op,host_reg, AMD64_R15,REG_OFFSET(ppc_reg),4); } /* * Update CR from %eflags * %rax, %rdx, %rsi are modified. */ static void ppc32_update_cr(ppc32_jit_tcb_t *b,int field,int is_signed) { /* Get status bits from EFLAGS */ amd64_pushfd_size(b->jit_ptr,8); amd64_pop_reg(b->jit_ptr,AMD64_RAX); if (!is_signed) { amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RAX,0xFF); amd64_mov_reg_imm_size(b->jit_ptr,AMD64_RDX,eflags_to_cr_unsigned,8); } else { amd64_shift_reg_imm(b->jit_ptr,X86_SHR,AMD64_RAX,6); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RAX,0x3F); amd64_mov_reg_imm_size(b->jit_ptr,AMD64_RDX,eflags_to_cr_signed,8); } amd64_mov_reg_memindex(b->jit_ptr,AMD64_RAX,AMD64_RDX,0,AMD64_RAX,2,4); #if 0 /* Check XER Summary of Overflow and report it */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX, AMD64_R15,OFFSET(cpu_ppc_t,xer),4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RCX,PPC32_XER_SO); amd64_shift_reg_imm(b->jit_ptr,X86_SHR,AMD64_RCX,(field << 2) + 3); amd64_alu_reg_reg(b->jit_ptr,X86_OR,AMD64_RDX,AMD64_RCX); #endif /* Store modified CR field */ amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,PPC32_CR_FIELD_OFFSET(field), AMD64_RAX,4); } /* * Update CR0 from %eflags * %eax, %ecx, %edx, %esi are modified. */ static void ppc32_update_cr0(ppc32_jit_tcb_t *b) { ppc32_update_cr(b,0,TRUE); } /* Indicate registers modified by ppc32_update_cr() functions */ void ppc32_update_cr_set_altered_hreg(cpu_ppc_t *cpu) { ppc32_op_emit_alter_host_reg(cpu,AMD64_RAX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); } /* Basic C call */ static forced_inline void ppc32_emit_basic_c_call(u_char **ptr,void *f) { amd64_mov_reg_imm(*ptr,AMD64_RBX,f); amd64_call_reg(*ptr,AMD64_RBX); } /* Emit a simple call to a C function without any parameter */ static void ppc32_emit_c_call(ppc32_jit_tcb_t *b,jit_op_t *iop,void *f) { ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); ppc32_emit_basic_c_call(&iop->ob_ptr,f); } /* ======================================================================== */ /* Initialize register mapping */ void ppc32_jit_init_hreg_mapping(cpu_ppc_t *cpu) { int avail_hregs[] = { AMD64_RSI, AMD64_RAX, AMD64_RCX, AMD64_RDX, AMD64_R13, AMD64_R14, AMD64_RDI, -1 }; struct hreg_map *map; int i,hreg; cpu->hreg_map_list = cpu->hreg_lru = NULL; /* Add the available registers to the map list */ for(i=0;avail_hregs[i]!=-1;i++) { hreg = avail_hregs[i]; map = &cpu->hreg_map[hreg]; /* Initialize mapping. At the beginning, no PPC reg is mapped */ map->flags = 0; map->hreg = hreg; map->vreg = -1; ppc32_jit_insert_hreg_mru(cpu,map); } /* Clear PPC registers mapping */ for(i=0;ippc_reg_map[i] = -1; } /* Allocate a specific temp register */ static int ppc32_jit_get_tmp_hreg(cpu_ppc_t *cpu) { return(AMD64_RBX); } /* ======================================================================== */ /* JIT operations (specific to target CPU). */ /* ======================================================================== */ /* INSN_OUTPUT */ void ppc32_op_insn_output(ppc32_jit_tcb_t *b,jit_op_t *op) { op->ob_final = b->jit_ptr; memcpy(b->jit_ptr,op->ob_data,op->ob_ptr - op->ob_data); b->jit_ptr += op->ob_ptr - op->ob_data; } /* LOAD_GPR: p[0] = %host_reg, p[1] = %ppc_reg */ void ppc32_op_load_gpr(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_load_gpr(&b->jit_ptr,op->param[0],op->param[1]); } /* STORE_GPR: p[0] = %host_reg, p[1] = %ppc_reg */ void ppc32_op_store_gpr(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_store_gpr(&b->jit_ptr,op->param[1],op->param[0]); } /* UPDATE_FLAGS: p[0] = cr_field, p[1] = is_signed */ void ppc32_op_update_flags(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_update_cr(b,op->param[0],op->param[1]); } /* MOVE_HOST_REG: p[0] = %host_dst_reg, p[1] = %host_src_reg */ void ppc32_op_move_host_reg(ppc32_jit_tcb_t *b,jit_op_t *op) { if ((op->param[0] != JIT_OP_INV_REG) && (op->param[1] != JIT_OP_INV_REG)) amd64_mov_reg_reg(b->jit_ptr,op->param[0],op->param[1],4); } /* SET_HOST_REG_IMM32: p[0] = %host_reg, p[1] = imm32 */ void ppc32_op_set_host_reg_imm32(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_load_imm(&b->jit_ptr,op->param[0],op->param[1]); } /* ======================================================================== */ /* Memory operation */ static void ppc32_emit_memop(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, int op,int base,int offset,int target,int update) { m_uint32_t val = sign_extend(offset,16); jit_op_t *iop; /* * Since an exception can be triggered, clear JIT state. This allows * to use branch target tag (we can directly branch on this instruction). */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_TARGET); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); iop = ppc32_op_emit_insn_output(cpu,5,"memop"); /* Save PC for exception handling */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* RSI = sign-extended offset */ ppc32_load_imm(&iop->ob_ptr,AMD64_RSI,val); /* RSI = GPR[base] + sign-extended offset */ if (update || (base != 0)) ppc32_alu_gpr(&iop->ob_ptr,X86_ADD,AMD64_RSI,base); if (update) amd64_mov_reg_reg(iop->ob_ptr,AMD64_R14,AMD64_RSI,4); /* RDX = target register */ amd64_mov_reg_imm(iop->ob_ptr,AMD64_RDX,target); /* RDI = CPU instance pointer */ amd64_mov_reg_reg(iop->ob_ptr,AMD64_RDI,AMD64_R15,8); /* Call memory function */ amd64_call_membase(iop->ob_ptr,AMD64_R15,MEMOP_OFFSET(op)); if (update) ppc32_store_gpr(&iop->ob_ptr,base,AMD64_R14); } /* Memory operation (indexed) */ static void ppc32_emit_memop_idx(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, int op,int ra,int rb,int target,int update) { jit_op_t *iop; /* * Since an exception can be triggered, clear JIT state. This allows * to use branch target tag (we can directly branch on this instruction). */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_TARGET); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); iop = ppc32_op_emit_insn_output(cpu,5,"memop_idx"); /* Save PC for exception handling */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* RSI = $rb */ ppc32_load_gpr(&iop->ob_ptr,AMD64_RSI,rb); /* RSI = GPR[base] + sign-extended offset */ if (update || (ra != 0)) ppc32_alu_gpr(&iop->ob_ptr,X86_ADD,AMD64_RSI,ra); if (update) amd64_mov_reg_reg(iop->ob_ptr,AMD64_R14,AMD64_RSI,4); /* RDX = target register */ amd64_mov_reg_imm(iop->ob_ptr,AMD64_RDX,target); /* RDI = CPU instance pointer */ amd64_mov_reg_reg(iop->ob_ptr,AMD64_RDI,AMD64_R15,8); /* Call memory function */ amd64_call_membase(iop->ob_ptr,AMD64_R15,MEMOP_OFFSET(op)); if (update) ppc32_store_gpr(&iop->ob_ptr,ra,AMD64_R14); } typedef void (*memop_fast_access)(jit_op_t *iop,int target); /* Fast LBZ */ static void ppc32_memop_fast_lbz(jit_op_t *iop,int target) { amd64_clear_reg(iop->ob_ptr,AMD64_RCX); amd64_mov_reg_memindex(iop->ob_ptr,AMD64_RCX,AMD64_RBX,0,AMD64_RSI,0,1); ppc32_store_gpr(&iop->ob_ptr,target,AMD64_RCX); } /* Fast STB */ static void ppc32_memop_fast_stb(jit_op_t *iop,int target) { ppc32_load_gpr(&iop->ob_ptr,AMD64_RDX,target); amd64_mov_memindex_reg(iop->ob_ptr,AMD64_RBX,0,AMD64_RSI,0,AMD64_RDX,1); } /* Fast LWZ */ static void ppc32_memop_fast_lwz(jit_op_t *iop,int target) { amd64_mov_reg_memindex(iop->ob_ptr,AMD64_RAX,AMD64_RBX,0,AMD64_RSI,0,4); amd64_bswap32(iop->ob_ptr,AMD64_RAX); ppc32_store_gpr(&iop->ob_ptr,target,AMD64_RAX); } /* Fast STW */ static void ppc32_memop_fast_stw(jit_op_t *iop,int target) { ppc32_load_gpr(&iop->ob_ptr,AMD64_RDX,target); amd64_bswap32(iop->ob_ptr,AMD64_RDX); amd64_mov_memindex_reg(iop->ob_ptr,AMD64_RBX,0,AMD64_RSI,0,AMD64_RDX,4); } /* Fast memory operation */ static void ppc32_emit_memop_fast(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, int write_op,int opcode, int base,int offset,int target, memop_fast_access op_handler) { m_uint32_t val = sign_extend(offset,16); u_char *test1,*test2,*p_exit; jit_op_t *iop; /* * Since an exception can be triggered, clear JIT state. This allows * to use branch target tag (we can directly branch on this instruction). */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_TARGET); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); iop = ppc32_op_emit_insn_output(cpu,5,"memop_fast"); test2 = NULL; /* RSI = GPR[base] + sign-extended offset */ ppc32_load_imm(&iop->ob_ptr,AMD64_RSI,val); if (base != 0) ppc32_alu_gpr(&iop->ob_ptr,X86_ADD,AMD64_RSI,base); /* RBX = mts32_entry index */ amd64_mov_reg_reg_size(iop->ob_ptr,X86_EBX,X86_ESI,4); amd64_shift_reg_imm_size(iop->ob_ptr,X86_SHR,X86_EBX,MTS32_HASH_SHIFT,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_AND,X86_EBX,MTS32_HASH_MASK,4); /* RCX = mts32 entry */ amd64_mov_reg_membase(iop->ob_ptr,AMD64_RCX, AMD64_R15, OFFSET(cpu_ppc_t,mts_cache[PPC32_MTS_DCACHE]),8); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,AMD64_RBX,5); /* TO FIX */ amd64_alu_reg_reg(iop->ob_ptr,X86_ADD,AMD64_RCX,AMD64_RBX); /* Compare virtual page address (EAX = vpage) */ amd64_mov_reg_reg(iop->ob_ptr,X86_EAX,X86_ESI,4); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,X86_EAX,PPC32_MIN_PAGE_MASK); amd64_alu_reg_membase_size(iop->ob_ptr,X86_CMP,X86_EAX,AMD64_RCX, OFFSET(mts32_entry_t,gvpa),4); test1 = iop->ob_ptr; amd64_branch8(iop->ob_ptr, X86_CC_NZ, 0, 1); /* Test if we are writing to a COW page */ if (write_op) { amd64_test_membase_imm_size(iop->ob_ptr, AMD64_RCX,OFFSET(mts32_entry_t,flags), MTS_FLAG_COW|MTS_FLAG_EXEC,4); test2 = iop->ob_ptr; amd64_branch8(iop->ob_ptr, X86_CC_NZ, 0, 1); } /* ESI = offset in page, RBX = Host Page Address */ amd64_alu_reg_imm(iop->ob_ptr,X86_AND,X86_ESI,PPC32_MIN_PAGE_IMASK); amd64_mov_reg_membase(iop->ob_ptr,AMD64_RBX, AMD64_RCX,OFFSET(mts32_entry_t,hpa),8); /* Memory access */ op_handler(iop,target); p_exit = iop->ob_ptr; amd64_jump8(iop->ob_ptr,0); /* === Slow lookup === */ amd64_patch(test1,iop->ob_ptr); if (test2) amd64_patch(test2,iop->ob_ptr); /* Save IA for exception handling */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* RDX = target register */ amd64_mov_reg_imm(iop->ob_ptr,AMD64_RDX,target); /* RDI = CPU instance */ amd64_mov_reg_reg(iop->ob_ptr,AMD64_RDI,AMD64_R15,8); /* Call memory access function */ amd64_call_membase(iop->ob_ptr,AMD64_R15,MEMOP_OFFSET(opcode)); amd64_patch(p_exit,iop->ob_ptr); } /* Emit unhandled instruction code */ static int ppc32_emit_unknown(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, ppc_insn_t opcode) { u_char *test1; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,3,"unknown"); /* Update IA */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* Fallback to non-JIT mode */ amd64_mov_reg_reg(iop->ob_ptr,AMD64_RDI,AMD64_R15,8); amd64_mov_reg_imm(iop->ob_ptr,AMD64_RSI,opcode); ppc32_emit_c_call(b,iop,ppc32_exec_single_insn_ext); amd64_test_reg_reg_size(iop->ob_ptr,AMD64_RAX,AMD64_RAX,4); test1 = iop->ob_ptr; amd64_branch8(iop->ob_ptr, X86_CC_Z, 0, 1); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); amd64_patch(test1,iop->ob_ptr); /* Signal this as an EOB to reset JIT state */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); return(0); } /* Virtual Breakpoint */ void ppc32_emit_breakpoint(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b) { jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,2,"breakpoint"); amd64_mov_reg_reg(iop->ob_ptr,AMD64_RDI,AMD64_R15,8); ppc32_emit_c_call(b,iop,ppc32_run_breakpoint); /* Signal this as an EOB to to reset JIT state */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); } /* Increment the number of executed instructions (performance debugging) */ void ppc32_inc_perf_counter(cpu_ppc_t *cpu) { jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,1,"perf_cnt"); amd64_inc_membase_size(iop->ob_ptr, AMD64_R15,OFFSET(cpu_ppc_t,perf_counter),4); } /* ======================================================================== */ /* BLR - Branch to Link Register */ DECLARE_INSN(BLR) { jit_op_t *iop; int hreg; ppc32_jit_start_hreg_seq(cpu,"blr"); hreg = ppc32_jit_alloc_hreg(cpu,-1); ppc32_op_emit_alter_host_reg(cpu,hreg); iop = ppc32_op_emit_insn_output(cpu,2,"blr"); amd64_mov_reg_membase(iop->ob_ptr,hreg,AMD64_R15,OFFSET(cpu_ppc_t,lr),4); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,ia),hreg,4); /* set the return address */ if (insn & 1) ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); ppc32_jit_close_hreg_seq(cpu); return(0); } /* BCTR - Branch to Count Register */ DECLARE_INSN(BCTR) { jit_op_t *iop; int hreg; ppc32_jit_start_hreg_seq(cpu,"bctr"); hreg = ppc32_jit_alloc_hreg(cpu,-1); ppc32_op_emit_alter_host_reg(cpu,hreg); iop = ppc32_op_emit_insn_output(cpu,2,"bctr"); amd64_mov_reg_membase(iop->ob_ptr,hreg,AMD64_R15,OFFSET(cpu_ppc_t,ctr),4); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,ia),hreg,4); /* set the return address */ if (insn & 1) ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFLR - Move From Link Register */ DECLARE_INSN(MFLR) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mflr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mflr"); amd64_mov_reg_membase(iop->ob_ptr,hreg_rd,AMD64_R15,OFFSET(cpu_ppc_t,lr),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MTLR - Move To Link Register */ DECLARE_INSN(MTLR) { int rs = bits(insn,21,25); int hreg_rs; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mtlr"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"mtlr"); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,lr),hreg_rs,4); return(0); } /* MFCTR - Move From Counter Register */ DECLARE_INSN(MFCTR) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mfctr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mfctr"); amd64_mov_reg_membase(iop->ob_ptr,hreg_rd, AMD64_R15,OFFSET(cpu_ppc_t,ctr),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MTCTR - Move To Counter Register */ DECLARE_INSN(MTCTR) { int rs = bits(insn,21,25); int hreg_rs; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mtctr"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"mtctr"); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,ctr), hreg_rs,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFTBU - Move from Time Base (Up) */ DECLARE_INSN(MFTBU) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mftbu"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mftbu"); amd64_mov_reg_membase(iop->ob_ptr,hreg_rd, AMD64_R15,OFFSET(cpu_ppc_t,tb)+4,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } #define PPC32_TB_INCREMENT 50 /* MFTBL - Move from Time Base (Lo) */ DECLARE_INSN(MFTBL) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mftbl"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,3,"mftbl"); amd64_mov_reg_membase(iop->ob_ptr,hreg_rd, AMD64_R15,OFFSET(cpu_ppc_t,tb),8); amd64_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_rd,PPC32_TB_INCREMENT); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,tb), hreg_rd,8); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADD */ DECLARE_INSN(ADD) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rd,hreg_ra,hreg_rb; jit_op_t *iop; /* $rd = $ra + $rb */ ppc32_jit_start_hreg_seq(cpu,"add"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"add"); if (rd == ra) amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb,4); else if (rd == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_rd,hreg_ra,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb,4); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDC */ DECLARE_INSN(ADDC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rd,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $rd = $ra + $rb */ ppc32_jit_start_hreg_seq(cpu,"addc"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); /* store the carry flag */ hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"addc"); if (rd == ra) amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb,4); else if (rd == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_rd,hreg_ra,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb,4); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); /* store the carry flag */ amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t0,FALSE); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x1); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t0,4); if (insn & 1) { amd64_test_reg_reg_size(iop->ob_ptr,hreg_rd,hreg_rd,4); ppc32_op_emit_update_flags(cpu,0,TRUE); } ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDE - Add Extended */ DECLARE_INSN(ADDE) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"adde"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,3,"adde"); /* $t0 = $ra + carry */ amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); amd64_alu_reg_membase_size(iop->ob_ptr,X86_ADD,hreg_t0, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca),4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); /* $t0 += $rb */ amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_t0,hreg_rb,4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); /* update cr0 */ if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_t0,hreg_t0,4); amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDI - ADD Immediate */ DECLARE_INSN(ADDI) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + imm */ ppc32_jit_start_hreg_seq(cpu,"addi"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); if (ra != 0) { hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,2,"addi"); if (rd != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_ADD,hreg_rd,tmp,4); } else { iop = ppc32_op_emit_insn_output(cpu,1,"addi"); ppc32_load_imm(&iop->ob_ptr,hreg_rd,tmp); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDIC - ADD Immediate with Carry */ DECLARE_INSN(ADDIC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + imm */ ppc32_jit_start_hreg_seq(cpu,"addic"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"addic"); if (rd != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_ADD,hreg_rd,tmp,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); amd64_set_membase(iop->ob_ptr,X86_CC_C, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca),FALSE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDIC. */ DECLARE_INSN(ADDIC_dot) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + imm */ ppc32_jit_start_hreg_seq(cpu,"addic."); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"addic."); if (rd != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_ADD,hreg_rd,tmp,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); amd64_set_membase(iop->ob_ptr,X86_CC_C, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca),FALSE); ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDIS - ADD Immediate Shifted */ DECLARE_INSN(ADDIS) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + (imm << 16) */ ppc32_jit_start_hreg_seq(cpu,"addis"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); if (ra != 0) { hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"addis"); if (rd != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_ADD,hreg_rd,tmp,4); } else { iop = ppc32_op_emit_insn_output(cpu,1,"addis"); amd64_mov_reg_imm(iop->ob_ptr,hreg_rd,tmp); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDZE */ DECLARE_INSN(ADDZE) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rd,hreg_ra,hreg_t0; jit_op_t *iop; /* $rd = $ra + xer_ca + set_carry */ ppc32_jit_start_hreg_seq(cpu,"addze"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,2,"addze"); amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,hreg_t0); if (rd != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_alu_reg_membase_size(iop->ob_ptr,X86_ADD,hreg_rd, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca),4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t0,FALSE); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t0,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* AND */ DECLARE_INSN(AND) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = $rs & $rb */ ppc32_jit_start_hreg_seq(cpu,"and"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"and"); if (ra == rs) amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb,4); else if (ra == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_ra,hreg_rs,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb,4); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ANDC */ DECLARE_INSN(ANDC) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $ra = $rs & ~$rb */ ppc32_jit_start_hreg_seq(cpu,"andc"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"andc"); /* $t0 = ~$rb */ hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rb,4); amd64_not_reg(iop->ob_ptr,hreg_t0); /* $ra = $rs & $t0 */ if (ra == rs) amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_ra,hreg_t0,4); else { amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_t0,hreg_rs,4); amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* AND Immediate */ DECLARE_INSN(ANDI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs & imm */ ppc32_jit_start_hreg_seq(cpu,"andi"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"andi"); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_AND,hreg_ra,tmp,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* AND Immediate Shifted */ DECLARE_INSN(ANDIS) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs & imm */ ppc32_jit_start_hreg_seq(cpu,"andis"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"andis"); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_AND,hreg_ra,tmp,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* B - Branch */ DECLARE_INSN(B) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"b"); /* compute the new ia */ new_ia = b->start_ia + (b->ppc_trans_pos << 2); new_ia += sign_extend(offset << 2,26); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BA - Branch Absolute */ DECLARE_INSN(BA) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"ba"); /* compute the new ia */ new_ia = sign_extend(offset << 2,26); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BL - Branch and Link */ DECLARE_INSN(BL) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"bl"); /* compute the new ia */ new_ia = b->start_ia + (b->ppc_trans_pos << 2); new_ia += sign_extend(offset << 2,26); /* set the return address */ ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BLA - Branch and Link Absolute */ DECLARE_INSN(BLA) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"bla"); /* compute the new ia */ new_ia = sign_extend(offset << 2,26); /* set the return address */ ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BC - Branch Conditional (Condition Check only) */ DECLARE_INSN(BCC) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); jit_op_t *iop; u_int cr_field,cr_bit; m_uint32_t new_ia; u_char *jump_ptr; int local_jump; int cond; ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_JUMP); iop = ppc32_op_emit_insn_output(cpu,5,"bcc"); /* Get the wanted value for the condition bit */ cond = (bo >> 3) & 0x1; /* Set the return address */ if (insn & 1) { ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1)<<2)); } /* Compute the new ia */ new_ia = sign_extend_32(bd << 2,16); if (!(insn & 0x02)) new_ia += b->start_ia + (b->ppc_trans_pos << 2); /* Test the condition bit */ cr_field = ppc32_get_cr_field(bi); cr_bit = ppc32_get_cr_bit(bi); ppc32_op_emit_require_flags(cpu,cr_field); amd64_test_membase_imm_size(iop->ob_ptr, AMD64_R15,PPC32_CR_FIELD_OFFSET(cr_field), (1 << cr_bit),4); local_jump = ppc32_jit_tcb_local_addr(b,new_ia,&jump_ptr); /* * Optimize the jump, depending if the destination is in the same * page or not. */ if (local_jump) { ppc32_jit_tcb_record_patch(b,iop,iop->ob_ptr,new_ia); amd64_branch32(iop->ob_ptr,(cond) ? X86_CC_NZ : X86_CC_Z,0,FALSE); } else { jump_ptr = iop->ob_ptr; amd64_branch32(iop->ob_ptr,(cond) ? X86_CC_Z : X86_CC_NZ,0,FALSE); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); amd64_patch(jump_ptr,iop->ob_ptr); } ppc32_op_emit_branch_target(cpu,b,new_ia); return(0); } /* BC - Branch Conditional */ DECLARE_INSN(BC) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); int hreg_t0,hreg_t1; jit_op_t *iop; u_int cr_field,cr_bit; m_uint32_t new_ia; u_char *jump_ptr; int local_jump; int cond,ctr; ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_JUMP); iop = ppc32_op_emit_insn_output(cpu,5,"bc"); ppc32_jit_start_hreg_seq(cpu,"bc"); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); /* Get the wanted value for the condition bit and CTR value */ cond = (bo >> 3) & 0x1; ctr = (bo >> 1) & 0x1; /* Set the return address */ if (insn & 1) { ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1)<<2)); } /* Compute the new ia */ new_ia = sign_extend_32(bd << 2,16); if (!(insn & 0x02)) new_ia += b->start_ia + (b->ppc_trans_pos << 2); amd64_mov_reg_imm(iop->ob_ptr,hreg_t0,1); /* Decrement the count register */ if (!(bo & 0x04)) { amd64_dec_membase_size(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,ctr),4); amd64_set_reg(iop->ob_ptr,(ctr) ? X86_CC_Z : X86_CC_NZ,hreg_t1,FALSE); amd64_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } /* Test the condition bit */ if (!((bo >> 4) & 0x01)) { cr_field = ppc32_get_cr_field(bi); cr_bit = ppc32_get_cr_bit(bi); ppc32_op_emit_require_flags(cpu,cr_field); amd64_test_membase_imm_size(iop->ob_ptr, AMD64_R15,PPC32_CR_FIELD_OFFSET(cr_field), (1 << cr_bit),4); amd64_set_reg(iop->ob_ptr,(cond) ? X86_CC_NZ : X86_CC_Z,hreg_t1,FALSE); amd64_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); local_jump = ppc32_jit_tcb_local_addr(b,new_ia,&jump_ptr); /* * Optimize the jump, depending if the destination is in the same * page or not. */ if (local_jump) { ppc32_jit_tcb_record_patch(b,iop,iop->ob_ptr,new_ia); amd64_branch32(iop->ob_ptr,X86_CC_NZ,0,FALSE); } else { jump_ptr = iop->ob_ptr; amd64_branch32(iop->ob_ptr,X86_CC_Z,0,FALSE); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); amd64_patch(jump_ptr,iop->ob_ptr); } ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_jit_close_hreg_seq(cpu); return(0); } /* BCLR - Branch Conditional to Link register */ DECLARE_INSN(BCLR) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); int hreg_t0,hreg_t1; jit_op_t *iop; u_int cr_field,cr_bit; m_uint32_t new_ia; u_char *jump_ptr; int cond,ctr; ppc32_jit_start_hreg_seq(cpu,"bclr"); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); iop = ppc32_op_emit_insn_output(cpu,5,"bclr"); /* Get the wanted value for the condition bit and CTR value */ cond = (bo >> 3) & 0x1; ctr = (bo >> 1) & 0x1; /* Compute the new ia */ new_ia = sign_extend_32(bd << 2,16); if (!(insn & 0x02)) new_ia += b->start_ia + (b->ppc_trans_pos << 2); amd64_mov_reg_imm(iop->ob_ptr,hreg_t0,1); /* Decrement the count register */ if (!(bo & 0x04)) { amd64_dec_membase_size(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,ctr),4); amd64_set_reg(iop->ob_ptr,(ctr) ? X86_CC_Z : X86_CC_NZ,hreg_t1,FALSE); amd64_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } /* Test the condition bit */ if (!((bo >> 4) & 0x01)) { cr_field = ppc32_get_cr_field(bi); cr_bit = ppc32_get_cr_bit(bi); ppc32_op_emit_require_flags(cpu,cr_field); amd64_test_membase_imm_size(iop->ob_ptr, AMD64_R15,PPC32_CR_FIELD_OFFSET(cr_field), (1 << cr_bit),4); amd64_set_reg(iop->ob_ptr,(cond) ? X86_CC_NZ : X86_CC_Z,hreg_t1,FALSE); amd64_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } /* Set the return address */ amd64_mov_reg_membase(iop->ob_ptr,hreg_t1,AMD64_R15,OFFSET(cpu_ppc_t,lr),4); if (insn & 1) { ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1)<<2)); } /* Branching */ amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); jump_ptr = iop->ob_ptr; amd64_branch32(iop->ob_ptr,X86_CC_Z,0,FALSE); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t1,0xFFFFFFFC); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,ia),hreg_t1,4); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); amd64_patch(jump_ptr,iop->ob_ptr); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMP - Compare */ DECLARE_INSN(CMP) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmp"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"cmp"); amd64_alu_reg_reg_size(iop->ob_ptr,X86_CMP,hreg_ra,hreg_rb,4); ppc32_op_emit_update_flags(cpu,rd,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMPI - Compare Immediate */ DECLARE_INSN(CMPI) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_ra; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmpi"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"cmpi"); amd64_alu_reg_imm_size(iop->ob_ptr,X86_CMP,hreg_ra,tmp,4); ppc32_op_emit_update_flags(cpu,rd,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMPL - Compare Logical */ DECLARE_INSN(CMPL) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmpl"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"cmpl"); amd64_alu_reg_reg_size(iop->ob_ptr,X86_CMP,hreg_ra,hreg_rb,4); ppc32_op_emit_update_flags(cpu,rd,FALSE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMPLI - Compare Immediate */ DECLARE_INSN(CMPLI) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); int hreg_ra; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmpli"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"cmpli"); amd64_alu_reg_imm_size(iop->ob_ptr,X86_CMP,hreg_ra,imm,4); ppc32_op_emit_update_flags(cpu,rd,FALSE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRAND - Condition Register AND */ DECLARE_INSN(CRAND) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"crand"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crand"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of AND between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,AMD64_RDX); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRANDC - Condition Register AND with Complement */ DECLARE_INSN(CRANDC) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"crandc"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crandc"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_Z,hreg_t0,FALSE); /* result of AND between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,AMD64_RDX); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CREQV - Condition Register EQV */ DECLARE_INSN(CREQV) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"creqv"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"creqv"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of XOR between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,AMD64_RDX); amd64_not_reg(iop->ob_ptr,hreg_t0); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRNAND - Condition Register NAND */ DECLARE_INSN(CRNAND) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"crnand"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crnand"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of NAND between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,AMD64_RDX); amd64_not_reg(iop->ob_ptr,hreg_t0); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRNOR - Condition Register NOR */ DECLARE_INSN(CRNOR) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"crnor"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crnor"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of NOR between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_t0,AMD64_RDX); amd64_not_reg(iop->ob_ptr,hreg_t0); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CROR - Condition Register OR */ DECLARE_INSN(CROR) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"cror"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"cror"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of NOR between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_t0,AMD64_RDX); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRORC - Condition Register OR with Complement */ DECLARE_INSN(CRORC) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"crorc"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crorc"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_Z,hreg_t0,FALSE); /* result of ORC between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_t0,AMD64_RDX); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRXOR - Condition Register XOR */ DECLARE_INSN(CRXOR) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"crxor"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crxor"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of XOR between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,AMD64_RDX); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* DIVWU - Divide Word Unsigned */ DECLARE_INSN(DIVWU) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"divwu"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RAX); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); /* $rd = $ra / $rb */ ppc32_op_emit_load_gpr(cpu,AMD64_RAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"divwu"); ppc32_load_imm(&iop->ob_ptr,AMD64_RDX,0); amd64_div_reg_size(iop->ob_ptr,hreg_rb,0,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,AMD64_RAX,AMD64_RAX,4); ppc32_op_emit_store_gpr(cpu,rd,AMD64_RAX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RAX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* EQV */ DECLARE_INSN(EQV) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = ~($rs ^ $rb) */ ppc32_jit_start_hreg_seq(cpu,"eqv"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"eqv"); if (ra == rs) amd64_alu_reg_reg_size(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb,4); else if (ra == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rs,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb,4); } amd64_not_reg(iop->ob_ptr,hreg_ra); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* EXTSB - Extend Sign Byte */ DECLARE_INSN(EXTSB) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = extsb($rs) */ ppc32_jit_start_hreg_seq(cpu,"extsb"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"extsb"); if (rs != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_shift_reg_imm_size(iop->ob_ptr,X86_SHL,hreg_ra,24,4); amd64_shift_reg_imm_size(iop->ob_ptr,X86_SAR,hreg_ra,24,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* EXTSH - Extend Sign Word */ DECLARE_INSN(EXTSH) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = extsh($rs) */ ppc32_jit_start_hreg_seq(cpu,"extsh"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"extsh"); if (rs != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_shift_reg_imm_size(iop->ob_ptr,X86_SHL,hreg_ra,16,4); amd64_shift_reg_imm_size(iop->ob_ptr,X86_SAR,hreg_ra,16,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* LBZ - Load Byte and Zero */ DECLARE_INSN(LBZ) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(b,PPC_MEMOP_LBZ,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,0,PPC_MEMOP_LBZ,ra,offset,rs, ppc32_memop_fast_lbz); return(0); } /* LBZU - Load Byte and Zero with Update */ DECLARE_INSN(LBZU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LBZ,ra,offset,rs,1); return(0); } /* LBZUX - Load Byte and Zero with Update Indexed */ DECLARE_INSN(LBZUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LBZ,ra,rb,rs,1); return(0); } /* LBZX - Load Byte and Zero Indexed */ DECLARE_INSN(LBZX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LBZ,ra,rb,rs,0); return(0); } /* LHA - Load Half-Word Algebraic */ DECLARE_INSN(LHA) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHA,ra,offset,rs,0); return(0); } /* LHAU - Load Half-Word Algebraic with Update */ DECLARE_INSN(LHAU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHA,ra,offset,rs,1); return(0); } /* LHAUX - Load Half-Word Algebraic with Update Indexed */ DECLARE_INSN(LHAUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHA,ra,rb,rs,1); return(0); } /* LHAX - Load Half-Word Algebraic Indexed */ DECLARE_INSN(LHAX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHA,ra,rb,rs,0); return(0); } /* LHZ - Load Half-Word and Zero */ DECLARE_INSN(LHZ) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHZ,ra,offset,rs,0); return(0); } /* LHZU - Load Half-Word and Zero with Update */ DECLARE_INSN(LHZU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHZ,ra,offset,rs,1); return(0); } /* LHZUX - Load Half-Word and Zero with Update Indexed */ DECLARE_INSN(LHZUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHZ,ra,rb,rs,1); return(0); } /* LHZX - Load Half-Word and Zero Indexed */ DECLARE_INSN(LHZX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHZ,ra,rb,rs,0); return(0); } /* LWZ - Load Word and Zero */ DECLARE_INSN(LWZ) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(b,PPC_MEMOP_LWZ,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,0,PPC_MEMOP_LWZ,ra,offset,rs, ppc32_memop_fast_lwz); return(0); } /* LWZU - Load Word and Zero with Update */ DECLARE_INSN(LWZU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LWZ,ra,offset,rs,1); return(0); } /* LWZUX - Load Word and Zero with Update Indexed */ DECLARE_INSN(LWZUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LWZ,ra,rb,rs,1); return(0); } /* LWZX - Load Word and Zero Indexed */ DECLARE_INSN(LWZX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LWZ,ra,rb,rs,0); return(0); } /* MCRF - Move Condition Register Field */ DECLARE_INSN(MCRF) { int rd = bits(insn,23,25); int rs = bits(insn,18,20); int hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mcrf"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_require_flags(cpu,rs); iop = ppc32_op_emit_insn_output(cpu,1,"mcrf"); /* Load "rs" field in %edx */ amd64_mov_reg_membase(iop->ob_ptr,hreg_t0, AMD64_R15,PPC32_CR_FIELD_OFFSET(rs),4); /* Store it in "rd" field */ amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,PPC32_CR_FIELD_OFFSET(rd), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFCR - Move from Condition Register */ DECLARE_INSN(MFCR) { int rd = bits(insn,21,25); int hreg_rd,hreg_t0; jit_op_t *iop; int i; ppc32_jit_start_hreg_seq(cpu,"mfcr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_require_flags(cpu,JIT_OP_PPC_ALL_FLAGS); iop = ppc32_op_emit_insn_output(cpu,3,"mfcr"); amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_rd,hreg_rd); for(i=0;i<8;i++) { /* load field in %edx */ amd64_mov_reg_membase(iop->ob_ptr,hreg_t0, AMD64_R15,PPC32_CR_FIELD_OFFSET(i),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_rd,4); amd64_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_rd,hreg_t0); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFMSR - Move from Machine State Register */ DECLARE_INSN(MFMSR) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mfmsr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mfmsr"); amd64_mov_reg_membase(iop->ob_ptr,hreg_rd, AMD64_R15,OFFSET(cpu_ppc_t,msr),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFSR - Move From Segment Register */ DECLARE_INSN(MFSR) { int rd = bits(insn,21,25); int sr = bits(insn,16,19); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mfsr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mfsr"); amd64_mov_reg_membase(iop->ob_ptr,hreg_rd, AMD64_R15,(OFFSET(cpu_ppc_t,sr) + (sr << 2)),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MTCRF - Move to Condition Register Fields */ DECLARE_INSN(MTCRF) { int rs = bits(insn,21,25); int crm = bits(insn,12,19); int hreg_rs,hreg_t0; jit_op_t *iop; int i; ppc32_jit_start_hreg_seq(cpu,"mtcrf"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,4,"mtcrf"); for(i=0;i<8;i++) if (crm & (1 << (7 - i))) { amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); if (i != 7) amd64_shift_reg_imm(iop->ob_ptr,X86_SHR,hreg_t0,28 - (i << 2)); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x0F); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,PPC32_CR_FIELD_OFFSET(i), hreg_t0,4); } ppc32_op_emit_basic_opcode(cpu,JIT_OP_TRASH_FLAGS); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULHW - Multiply High Word */ DECLARE_INSN(MULHW) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mulhw"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RAX); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,AMD64_RAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); /* rd = hi(ra * rb) */ iop = ppc32_op_emit_insn_output(cpu,2,"mulhw"); amd64_mul_reg_size(iop->ob_ptr,hreg_rb,1,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,AMD64_RDX,AMD64_RDX,4); ppc32_op_emit_store_gpr(cpu,rd,AMD64_RDX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RAX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULHWU - Multiply High Word Unsigned */ DECLARE_INSN(MULHWU) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mulhwu"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RAX); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,AMD64_RAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); /* rd = hi(ra * rb) */ iop = ppc32_op_emit_insn_output(cpu,2,"mulhwu"); amd64_mul_reg_size(iop->ob_ptr,hreg_rb,0,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,AMD64_RDX,AMD64_RDX,4); ppc32_op_emit_store_gpr(cpu,rd,AMD64_RDX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RAX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULLI - Multiply Low Immediate */ DECLARE_INSN(MULLI) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); int hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mulli"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RAX); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,AMD64_RAX,ra); /* rd = lo(ra * imm) */ iop = ppc32_op_emit_insn_output(cpu,2,"mulli"); ppc32_load_imm(&iop->ob_ptr,hreg_t0,sign_extend_32(imm,16)); amd64_mul_reg_size(iop->ob_ptr,hreg_t0,1,4); ppc32_op_emit_store_gpr(cpu,rd,AMD64_RAX); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RAX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULLW - Multiply Low Word */ DECLARE_INSN(MULLW) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mullw"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RAX); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,AMD64_RAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); /* rd = lo(ra * rb) */ iop = ppc32_op_emit_insn_output(cpu,2,"mullw"); amd64_mul_reg_size(iop->ob_ptr,hreg_rb,1,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,AMD64_RAX,AMD64_RAX,4); ppc32_op_emit_store_gpr(cpu,rd,AMD64_RAX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RAX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* NAND */ DECLARE_INSN(NAND) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = ~($rs & $rb) */ ppc32_jit_start_hreg_seq(cpu,"nand"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"nand"); if (ra == rs) amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb,4); else if (ra == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_ra,hreg_rs,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb,4); } amd64_not_reg(iop->ob_ptr,hreg_ra); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* NEG */ DECLARE_INSN(NEG) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = neg($ra) */ ppc32_jit_start_hreg_seq(cpu,"neg"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"neg"); if (rd != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_neg_reg(iop->ob_ptr,hreg_rd); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_rd,hreg_rd,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* NOR */ DECLARE_INSN(NOR) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = ~($rs | $rb) */ ppc32_jit_start_hreg_seq(cpu,"nor"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"nor"); if (ra == rs) amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb,4); else if (ra == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_rs,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb,4); } amd64_not_reg(iop->ob_ptr,hreg_ra); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR */ DECLARE_INSN(OR) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = $rs | $rb */ ppc32_jit_start_hreg_seq(cpu,"or"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); /* special optimization for move/nop operation */ if (rs == rb) { ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"or"); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"or"); if (ra == rs) { amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb,4); } else if (ra == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_rs,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb,4); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR with Complement */ DECLARE_INSN(ORC) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $ra = $rs & ~$rb */ ppc32_jit_start_hreg_seq(cpu,"orc"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"orc"); /* $t0 = ~$rb */ hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rb,4); amd64_not_reg(iop->ob_ptr,hreg_t0); /* $ra = $rs | $t0 */ if (ra == rs) amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_t0,4); else { amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_t0,hreg_rs,4); amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR Immediate */ DECLARE_INSN(ORI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs | imm */ ppc32_jit_start_hreg_seq(cpu,"ori"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"ori"); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_OR,hreg_ra,tmp,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR Immediate Shifted */ DECLARE_INSN(ORIS) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs | imm */ ppc32_jit_start_hreg_seq(cpu,"oris"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"oris"); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_OR,hreg_ra,tmp,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* RLWIMI - Rotate Left Word Immediate then Mask Insert */ DECLARE_INSN(RLWIMI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t mask; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"rlwimi"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); mask = ppc32_rotate_mask(mb,me); iop = ppc32_op_emit_insn_output(cpu,2,"rlwimi"); /* Apply inverse mask to $ra */ if (mask != 0) amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_ra,~mask); /* Rotate $rs of "sh" bits and apply the mask */ amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); if (sh != 0) amd64_shift_reg_imm_size(iop->ob_ptr,X86_ROL,hreg_t0,sh,4); if (mask != 0xFFFFFFFF) amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,mask); /* Store the result */ amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_t0,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* RLWINM - Rotate Left Word Immediate AND with Mask */ DECLARE_INSN(RLWINM) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t mask; int hreg_rs,hreg_ra; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"rlwinm"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"rlwinm"); /* Rotate $rs of "sh" bits and apply the mask */ mask = ppc32_rotate_mask(mb,me); if (rs != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); if (sh != 0) amd64_shift_reg_imm_size(iop->ob_ptr,X86_ROL,hreg_ra,sh,4); if (mask != 0xFFFFFFFF) amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_ra,mask); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* RLWNM - Rotate Left Word then Mask Insert */ DECLARE_INSN(RLWNM) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t mask; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; /* ecx is directly modified: throw it */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RCX); ppc32_jit_start_hreg_seq(cpu,"rlwnm"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RCX); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,AMD64_RCX,rb); iop = ppc32_op_emit_insn_output(cpu,2,"rlwnm"); /* Load the shift register ("sh") */ mask = ppc32_rotate_mask(mb,me); /* Rotate $rs and apply the mask */ amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); amd64_shift_reg_size(iop->ob_ptr,X86_ROL,hreg_t0,4); if (mask != 0xFFFFFFFF) amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,mask); amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* Shift Left Word */ DECLARE_INSN(SLW) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra; jit_op_t *iop; /* ecx is directly modified: throw it */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RCX); ppc32_jit_start_hreg_seq(cpu,"slw"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RCX); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); /* $ra = $rs << $rb. If count >= 32, then null result */ ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,AMD64_RCX,rb); iop = ppc32_op_emit_insn_output(cpu,3,"slw"); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,AMD64_RCX,0x3f); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_shift_reg(iop->ob_ptr,X86_SHL,hreg_ra); /* store the result */ if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SRAWI - Shift Right Algebraic Word Immediate */ DECLARE_INSN(SRAWI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); register m_uint32_t mask; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"srawi"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); /* $ra = (int32)$rs >> sh */ ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,3,"srawi"); amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_shift_reg_imm_size(iop->ob_ptr,X86_SAR,hreg_ra,sh,4); /* set XER_CA depending on the result */ mask = ~(0xFFFFFFFFU << sh) | 0x80000000; amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,mask); amd64_alu_reg_imm_size(iop->ob_ptr,X86_CMP,hreg_t0,0x80000000,4); amd64_set_reg(iop->ob_ptr,X86_CC_A,hreg_t0,FALSE); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x1); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t0,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* Shift Right Word */ DECLARE_INSN(SRW) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra; jit_op_t *iop; /* ecx is directly modified: throw it */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RCX); ppc32_jit_start_hreg_seq(cpu,"srw"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RCX); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); /* $ra = $rs >> $rb. If count >= 32, then null result */ ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,AMD64_RCX,rb); iop = ppc32_op_emit_insn_output(cpu,3,"srw"); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,AMD64_RCX,0x3f); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_shift_reg(iop->ob_ptr,X86_SHR,hreg_ra); /* store the result */ if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* STB - Store Byte */ DECLARE_INSN(STB) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(b,PPC_MEMOP_STB,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,1,PPC_MEMOP_STB,ra,offset,rs, ppc32_memop_fast_stb); return(0); } /* STBU - Store Byte with Update */ DECLARE_INSN(STBU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STB,ra,offset,rs,1); return(0); } /* STBUX - Store Byte with Update Indexed */ DECLARE_INSN(STBUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STB,ra,rb,rs,1); return(0); } /* STBUX - Store Byte Indexed */ DECLARE_INSN(STBX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STB,ra,rb,rs,0); return(0); } /* STH - Store Half-Word */ DECLARE_INSN(STH) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STH,ra,offset,rs,0); return(0); } /* STHU - Store Half-Word with Update */ DECLARE_INSN(STHU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STH,ra,offset,rs,1); return(0); } /* STHUX - Store Half-Word with Update Indexed */ DECLARE_INSN(STHUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STH,ra,rb,rs,1); return(0); } /* STHUX - Store Half-Word Indexed */ DECLARE_INSN(STHX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STH,ra,rb,rs,0); return(0); } /* STW - Store Word */ DECLARE_INSN(STW) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(b,PPC_MEMOP_STW,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,1,PPC_MEMOP_STW,ra,offset,rs, ppc32_memop_fast_stw); return(0); } /* STWU - Store Word with Update */ DECLARE_INSN(STWU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STW,ra,offset,rs,1); return(0); } /* STWUX - Store Word with Update Indexed */ DECLARE_INSN(STWUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STW,ra,rb,rs,1); return(0); } /* STWUX - Store Word Indexed */ DECLARE_INSN(STWX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STW,ra,rb,rs,0); return(0); } /* SUBF - Subtract From */ DECLARE_INSN(SUBF) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rd,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $rd = $rb - $ra */ ppc32_jit_start_hreg_seq(cpu,"subf"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"subf"); if (rd == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_SUB,hreg_rd,hreg_ra,4); else if (rd == ra) { amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rb,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_SUB,hreg_t0,hreg_ra,4); amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); } else { amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_rb,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_SUB,hreg_rd,hreg_ra,4); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SUBFC - Subtract From Carrying */ DECLARE_INSN(SUBFC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; /* $rd = ~$ra + 1 + $rb */ ppc32_jit_start_hreg_seq(cpu,"subfc"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,3,"subfc"); amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); /* $t0 = ~$ra + 1 */ amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); amd64_not_reg(iop->ob_ptr,hreg_t0); amd64_alu_reg_imm_size(iop->ob_ptr,X86_ADD,hreg_t0,1,4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); /* $t0 += $rb */ amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_t0,hreg_rb,4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_rd,hreg_rd,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); /* update cr0 */ if (insn & 1) ppc32_update_cr0(b); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SUBFE - Subtract From Extended */ DECLARE_INSN(SUBFE) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; /* $rd = ~$ra + $carry (xer_ca) + $rb */ ppc32_jit_start_hreg_seq(cpu,"subfe"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,3,"subfe"); amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); /* $t0 = ~$ra + $carry */ amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); amd64_not_reg(iop->ob_ptr,hreg_t0); amd64_alu_reg_membase_size(iop->ob_ptr,X86_ADD,hreg_t0, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca),4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); /* $t0 += $rb */ amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_t0,hreg_rb,4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_rd,hreg_rd,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); /* update cr0 */ if (insn & 1) ppc32_update_cr0(b); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SUBFIC - Subtract From Immediate Carrying */ DECLARE_INSN(SUBFIC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_ra,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; /* $rd = ~$ra + 1 + sign_extend(imm,16) */ ppc32_jit_start_hreg_seq(cpu,"subfic"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,3,"subfic"); amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); /* $t0 = ~$ra + 1 */ amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); amd64_not_reg(iop->ob_ptr,hreg_t0); amd64_alu_reg_imm_size(iop->ob_ptr,X86_ADD,hreg_t0,1,4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); /* $t0 += sign_extend(imm,16) */ amd64_alu_reg_imm_size(iop->ob_ptr,X86_ADD,hreg_t0,tmp,4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SYNC - Synchronize */ DECLARE_INSN(SYNC) { return(0); } /* XOR */ DECLARE_INSN(XOR) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = $rs ^ $rb */ ppc32_jit_start_hreg_seq(cpu,"xor"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"xor"); if (ra == rs) amd64_alu_reg_reg_size(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb,4); else if (ra == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rs,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb,4); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* XORI - XOR Immediate */ DECLARE_INSN(XORI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs ^ imm */ ppc32_jit_start_hreg_seq(cpu,"xori"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"xori"); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_imm(iop->ob_ptr,X86_XOR,hreg_ra,imm); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* XORIS - XOR Immediate Shifted */ DECLARE_INSN(XORIS) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs ^ (imm << 16) */ ppc32_jit_start_hreg_seq(cpu,"xoris"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"xoris"); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_imm(iop->ob_ptr,X86_XOR,hreg_ra,tmp); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* PPC instruction array */ struct ppc32_insn_tag ppc32_insn_tags[] = { { ppc32_emit_BLR , 0xfffffffe , 0x4e800020 }, { ppc32_emit_BCTR , 0xfffffffe , 0x4e800420 }, { ppc32_emit_MFLR , 0xfc1fffff , 0x7c0802a6 }, { ppc32_emit_MTLR , 0xfc1fffff , 0x7c0803a6 }, { ppc32_emit_MFCTR , 0xfc1fffff , 0x7c0902a6 }, { ppc32_emit_MTCTR , 0xfc1fffff , 0x7c0903a6 }, { ppc32_emit_MFTBL , 0xfc1ff7ff , 0x7c0c42e6 }, { ppc32_emit_MFTBU , 0xfc1ff7ff , 0x7c0d42e6 }, { ppc32_emit_ADD , 0xfc0007fe , 0x7c000214 }, { ppc32_emit_ADDC , 0xfc0007fe , 0x7c000014 }, { ppc32_emit_ADDE , 0xfc0007fe , 0x7c000114 }, { ppc32_emit_ADDI , 0xfc000000 , 0x38000000 }, { ppc32_emit_ADDIC , 0xfc000000 , 0x30000000 }, { ppc32_emit_ADDIC_dot , 0xfc000000 , 0x34000000 }, { ppc32_emit_ADDIS , 0xfc000000 , 0x3c000000 }, { ppc32_emit_ADDZE , 0xfc00fffe , 0x7c000194 }, { ppc32_emit_AND , 0xfc0007fe , 0x7c000038 }, { ppc32_emit_ANDC , 0xfc0007fe , 0x7c000078 }, { ppc32_emit_ANDI , 0xfc000000 , 0x70000000 }, { ppc32_emit_ANDIS , 0xfc000000 , 0x74000000 }, { ppc32_emit_B , 0xfc000003 , 0x48000000 }, { ppc32_emit_BA , 0xfc000003 , 0x48000002 }, { ppc32_emit_BL , 0xfc000003 , 0x48000001 }, { ppc32_emit_BLA , 0xfc000003 , 0x48000003 }, { ppc32_emit_BCC , 0xfe800000 , 0x40800000 }, { ppc32_emit_BC , 0xfc000000 , 0x40000000 }, { ppc32_emit_BCLR , 0xfc00fffe , 0x4c000020 }, { ppc32_emit_CMP , 0xfc6007ff , 0x7c000000 }, { ppc32_emit_CMPI , 0xfc600000 , 0x2c000000 }, { ppc32_emit_CMPL , 0xfc6007ff , 0x7c000040 }, { ppc32_emit_CMPLI , 0xfc600000 , 0x28000000 }, { ppc32_emit_CRAND , 0xfc0007ff , 0x4c000202 }, { ppc32_emit_CRANDC , 0xfc0007ff , 0x4c000102 }, { ppc32_emit_CREQV , 0xfc0007ff , 0x4c000242 }, { ppc32_emit_CRNAND , 0xfc0007ff , 0x4c0001c2 }, { ppc32_emit_CRNOR , 0xfc0007ff , 0x4c000042 }, { ppc32_emit_CROR , 0xfc0007ff , 0x4c000382 }, { ppc32_emit_CRORC , 0xfc0007ff , 0x4c000342 }, { ppc32_emit_CRXOR , 0xfc0007ff , 0x4c000182 }, { ppc32_emit_DIVWU , 0xfc0007fe , 0x7c000396 }, { ppc32_emit_EQV , 0xfc0007fe , 0x7c000238 }, { ppc32_emit_EXTSB , 0xfc00fffe , 0x7c000774 }, { ppc32_emit_EXTSH , 0xfc00fffe , 0x7c000734 }, { ppc32_emit_LBZ , 0xfc000000 , 0x88000000 }, { ppc32_emit_LBZU , 0xfc000000 , 0x8c000000 }, { ppc32_emit_LBZUX , 0xfc0007ff , 0x7c0000ee }, { ppc32_emit_LBZX , 0xfc0007ff , 0x7c0000ae }, { ppc32_emit_LHA , 0xfc000000 , 0xa8000000 }, { ppc32_emit_LHAU , 0xfc000000 , 0xac000000 }, { ppc32_emit_LHAUX , 0xfc0007ff , 0x7c0002ee }, { ppc32_emit_LHAX , 0xfc0007ff , 0x7c0002ae }, { ppc32_emit_LHZ , 0xfc000000 , 0xa0000000 }, { ppc32_emit_LHZU , 0xfc000000 , 0xa4000000 }, { ppc32_emit_LHZUX , 0xfc0007ff , 0x7c00026e }, { ppc32_emit_LHZX , 0xfc0007ff , 0x7c00022e }, { ppc32_emit_LWZ , 0xfc000000 , 0x80000000 }, { ppc32_emit_LWZU , 0xfc000000 , 0x84000000 }, { ppc32_emit_LWZUX , 0xfc0007ff , 0x7c00006e }, { ppc32_emit_LWZX , 0xfc0007ff , 0x7c00002e }, { ppc32_emit_MCRF , 0xfc63ffff , 0x4c000000 }, { ppc32_emit_MFCR , 0xfc1fffff , 0x7c000026 }, { ppc32_emit_MFMSR , 0xfc1fffff , 0x7c0000a6 }, { ppc32_emit_MFSR , 0xfc10ffff , 0x7c0004a6 }, { ppc32_emit_MTCRF , 0xfc100fff , 0x7c000120 }, { ppc32_emit_MULHW , 0xfc0007fe , 0x7c000096 }, { ppc32_emit_MULHWU , 0xfc0007fe , 0x7c000016 }, { ppc32_emit_MULLI , 0xfc000000 , 0x1c000000 }, { ppc32_emit_MULLW , 0xfc0007fe , 0x7c0001d6 }, { ppc32_emit_NAND , 0xfc0007fe , 0x7c0003b8 }, { ppc32_emit_NEG , 0xfc00fffe , 0x7c0000d0 }, { ppc32_emit_NOR , 0xfc0007fe , 0x7c0000f8 }, { ppc32_emit_OR , 0xfc0007fe , 0x7c000378 }, { ppc32_emit_ORC , 0xfc0007fe , 0x7c000338 }, { ppc32_emit_ORI , 0xfc000000 , 0x60000000 }, { ppc32_emit_ORIS , 0xfc000000 , 0x64000000 }, { ppc32_emit_RLWIMI , 0xfc000000 , 0x50000000 }, { ppc32_emit_RLWINM , 0xfc000000 , 0x54000000 }, { ppc32_emit_RLWNM , 0xfc000000 , 0x5c000000 }, { ppc32_emit_SLW , 0xfc0007fe , 0x7c000030 }, { ppc32_emit_SRAWI , 0xfc0007fe , 0x7c000670 }, { ppc32_emit_SRW , 0xfc0007fe , 0x7c000430 }, { ppc32_emit_STB , 0xfc000000 , 0x98000000 }, { ppc32_emit_STBU , 0xfc000000 , 0x9c000000 }, { ppc32_emit_STBUX , 0xfc0007ff , 0x7c0001ee }, { ppc32_emit_STBX , 0xfc0007ff , 0x7c0001ae }, { ppc32_emit_STH , 0xfc000000 , 0xb0000000 }, { ppc32_emit_STHU , 0xfc000000 , 0xb4000000 }, { ppc32_emit_STHUX , 0xfc0007ff , 0x7c00036e }, { ppc32_emit_STHX , 0xfc0007ff , 0x7c00032e }, { ppc32_emit_STW , 0xfc000000 , 0x90000000 }, { ppc32_emit_STWU , 0xfc000000 , 0x94000000 }, { ppc32_emit_STWUX , 0xfc0007ff , 0x7c00016e }, { ppc32_emit_STWX , 0xfc0007ff , 0x7c00012e }, { ppc32_emit_SUBF , 0xfc0007fe , 0x7c000050 }, { ppc32_emit_SUBFC , 0xfc0007fe , 0x7c000010 }, { ppc32_emit_SUBFE , 0xfc0007fe , 0x7c000110 }, { ppc32_emit_SUBFIC , 0xfc000000 , 0x20000000 }, { ppc32_emit_SYNC , 0xffffffff , 0x7c0004ac }, { ppc32_emit_XOR , 0xfc0007fe , 0x7c000278 }, { ppc32_emit_XORI , 0xfc000000 , 0x68000000 }, { ppc32_emit_XORIS , 0xfc000000 , 0x6c000000 }, { ppc32_emit_unknown , 0x00000000 , 0x00000000 }, { NULL , 0x00000000 , 0x00000000 }, }; dynamips-0.2.14/stable/ppc32_exec.c000066400000000000000000002734021241034141600167770ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * PowerPC (32-bit) step-by-step execution. */ #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "ppc32_exec.h" #include "ppc32_mem.h" #include "memory.h" #include "insn_lookup.h" #include "dynamips.h" /* Forward declaration of instruction array */ static struct ppc32_insn_exec_tag ppc32_exec_tags[]; static insn_lookup_t *ilt = NULL; /* ILT */ static forced_inline void *ppc32_exec_get_insn(int index) { return(&ppc32_exec_tags[index]); } static int ppc32_exec_chk_lo(struct ppc32_insn_exec_tag *tag,int value) { return((value & tag->mask) == (tag->value & 0xFFFF)); } static int ppc32_exec_chk_hi(struct ppc32_insn_exec_tag *tag,int value) { return((value & (tag->mask >> 16)) == (tag->value >> 16)); } /* Destroy instruction lookup table */ static void destroy_ilt(void) { assert(ilt); ilt_destroy(ilt); ilt = NULL; } /* Initialize instruction lookup table */ void ppc32_exec_create_ilt(void) { int i,count; for(i=0,count=0;ppc32_exec_tags[i].exec;i++) count++; ilt = ilt_create("ppc32e",count, (ilt_get_insn_cbk_t)ppc32_exec_get_insn, (ilt_check_cbk_t)ppc32_exec_chk_lo, (ilt_check_cbk_t)ppc32_exec_chk_hi); atexit(destroy_ilt); } /* Dump statistics */ void ppc32_dump_stats(cpu_ppc_t *cpu) { int i; #if NJM_STATS_ENABLE printf("\n"); for(i=0;ppc32_exec_tags[i].exec;i++) printf(" * %-10s : %10llu\n", ppc32_exec_tags[i].name,ppc32_exec_tags[i].count); printf("%llu instructions executed since startup.\n",cpu->insn_exec_count); #else printf("Statistics support is not compiled in.\n"); #endif } /* Execute a memory operation */ static forced_inline void ppc32_exec_memop(cpu_ppc_t *cpu,int memop, m_uint32_t vaddr,u_int dst_reg) { fastcall ppc_memop_fn fn; fn = cpu->mem_op_fn[memop]; fn(cpu,vaddr,dst_reg); } /* Fetch an instruction */ static forced_inline int ppc32_exec_fetch(cpu_ppc_t *cpu,m_uint32_t ia, ppc_insn_t *insn) { m_uint32_t exec_page,offset; exec_page = ia & ~PPC32_MIN_PAGE_IMASK; if (unlikely(exec_page != cpu->njm_exec_page)) { cpu->njm_exec_page = exec_page; cpu->njm_exec_ptr = cpu->mem_op_lookup(cpu,exec_page,PPC32_MTS_ICACHE); } offset = (ia & PPC32_MIN_PAGE_IMASK) >> 2; *insn = vmtoh32(cpu->njm_exec_ptr[offset]); return(0); } /* Unknown opcode */ static fastcall int ppc32_exec_unknown(cpu_ppc_t *cpu,ppc_insn_t insn) { printf("PPC32: unknown opcode 0x%8.8x at ia = 0x%x\n",insn,cpu->ia); ppc32_dump_regs(cpu->gen); return(0); } /* Execute a single instruction */ static forced_inline int ppc32_exec_single_instruction(cpu_ppc_t *cpu,ppc_insn_t instruction) { register fastcall int (*exec)(cpu_ppc_t *,ppc_insn_t) = NULL; struct ppc32_insn_exec_tag *tag; int index; #if DEBUG_INSN_PERF_CNT cpu->perf_counter++; #endif /* Lookup for instruction */ index = ilt_lookup(ilt,instruction); tag = ppc32_exec_get_insn(index); exec = tag->exec; #if NJM_STATS_ENABLE cpu->insn_exec_count++; ppc32_exec_tags[index].count++; #endif return(exec(cpu,instruction)); } /* Execute a single instruction (external) */ fastcall int ppc32_exec_single_insn_ext(cpu_ppc_t *cpu,ppc_insn_t insn) { int res; res = ppc32_exec_single_instruction(cpu,insn); if (likely(!res)) cpu->ia += sizeof(ppc_insn_t); return(res); } /* Execute a page */ fastcall int ppc32_exec_page(cpu_ppc_t *cpu) { m_uint32_t exec_page,offset; ppc_insn_t insn; int res; exec_page = cpu->ia & ~PPC32_MIN_PAGE_IMASK; cpu->njm_exec_page = exec_page; cpu->njm_exec_ptr = cpu->mem_op_lookup(cpu,exec_page,PPC32_MTS_ICACHE); do { offset = (cpu->ia & PPC32_MIN_PAGE_IMASK) >> 2; insn = vmtoh32(cpu->njm_exec_ptr[offset]); res = ppc32_exec_single_instruction(cpu,insn); if (likely(!res)) cpu->ia += sizeof(ppc_insn_t); }while((cpu->ia & ~PPC32_MIN_PAGE_IMASK) == exec_page); return(0); } /* Run PowerPC code in step-by-step mode */ void *ppc32_exec_run_cpu(cpu_gen_t *gen) { cpu_ppc_t *cpu = CPU_PPC32(gen); pthread_t timer_irq_thread; int timer_irq_check = 0; ppc_insn_t insn; int res; if (pthread_create(&timer_irq_thread,NULL, (void *)ppc32_timer_irq_run,cpu)) { fprintf(stderr,"VM '%s': unable to create Timer IRQ thread for CPU%u.\n", cpu->vm->name,gen->id); cpu_stop(gen); return NULL; } gen->cpu_thread_running = TRUE; cpu_exec_loop_set(gen); start_cpu: for(;;) { if (unlikely(gen->state != CPU_STATE_RUNNING)) break; /* Check IRQ */ if (unlikely(cpu->irq_check)) ppc32_trigger_irq(cpu); /* Handle virtual idle loop */ if (unlikely(cpu->ia == cpu->idle_pc)) { if (++gen->idle_count == gen->idle_max) { cpu_idle_loop(gen); gen->idle_count = 0; } } /* Handle the virtual CPU clock */ if (++timer_irq_check == cpu->timer_irq_check_itv) { timer_irq_check = 0; if (cpu->timer_irq_pending && !cpu->irq_disable && (cpu->msr & PPC32_MSR_EE)) { cpu->timer_irq_armed = 0; cpu->timer_irq_pending--; vm_set_irq(cpu->vm,0); //ppc32_trigger_timer_irq(cpu); } } /* Increment the time base */ cpu->tb += 100; /* Fetch and execute the instruction */ ppc32_exec_fetch(cpu,cpu->ia,&insn); res = ppc32_exec_single_instruction(cpu,insn); /* Normal flow ? */ if (likely(!res)) cpu->ia += sizeof(ppc_insn_t); } /* Check regularly if the CPU has been restarted */ while(gen->cpu_thread_running) { gen->seq_state++; switch(gen->state) { case CPU_STATE_RUNNING: gen->state = CPU_STATE_RUNNING; goto start_cpu; case CPU_STATE_HALTED: gen->cpu_thread_running = FALSE; pthread_join(timer_irq_thread,NULL); break; } /* CPU is paused */ usleep(200000); } return NULL; } /* ========================================================================= */ /* Update CR0 */ static forced_inline void ppc32_exec_update_cr0(cpu_ppc_t *cpu,m_uint32_t val) { m_uint32_t res; if (val & 0x80000000) res = 1 << PPC32_CR_LT_BIT; else { if (val > 0) res = 1 << PPC32_CR_GT_BIT; else res = 1 << PPC32_CR_EQ_BIT; } if (cpu->xer & PPC32_XER_SO) res |= 1 << PPC32_CR_SO_BIT; cpu->cr_fields[0] = res; } /* * Update Overflow bit from a sum result (r = a + b) * * (a > 0) && (b > 0) => r > 0, otherwise overflow * (a < 0) && (a < 0) => r < 0, otherwise overflow. */ static forced_inline void ppc32_exec_ov_sum(cpu_ppc_t *cpu,m_uint32_t r, m_uint32_t a,m_uint32_t b) { register m_uint32_t sc; sc = (~(a ^ b) & (a ^ r) & 0x80000000); if (unlikely(sc)) cpu->xer |= PPC32_XER_SO | PPC32_XER_OV; else cpu->xer &= ~PPC32_XER_OV; } /* * Update Overflow bit from a substraction result (r = a - b) * * (a > 0) && (b < 0) => r > 0, otherwise overflow * (a < 0) && (a > 0) => r < 0, otherwise overflow. */ static forced_inline void ppc32_exec_ov_sub(cpu_ppc_t *cpu,m_uint32_t r, m_uint32_t a,m_uint32_t b) { register m_uint32_t sc; sc = ((a ^ b) & (a ^ r) & 0x80000000); if (unlikely(sc)) cpu->xer |= PPC32_XER_SO | PPC32_XER_OV; else cpu->xer &= ~PPC32_XER_OV; } /* * Update CA bit from a sum result (r = a + b) */ static forced_inline void ppc32_exec_ca_sum(cpu_ppc_t *cpu,m_uint32_t r, m_uint32_t a,m_uint32_t b) { cpu->xer_ca = (r < a) ? 1 : 0; } /* * Update CA bit from a substraction result (r = a - b) */ static forced_inline void ppc32_exec_ca_sub(cpu_ppc_t *cpu,m_uint32_t r, m_uint32_t a,m_uint32_t b) { cpu->xer_ca = (b > a) ? 1 : 0; } /* Check condition code */ static forced_inline int ppc32_check_cond(cpu_ppc_t *cpu,m_uint32_t bo, m_uint32_t bi) { u_int ctr_ok = TRUE; u_int cond_ok; u_int cr_bit; if (!(bo & 0x04)) { cpu->ctr--; ctr_ok = (cpu->ctr != 0) ^ ((bo >> 1) & 0x1); } cr_bit = ppc32_read_cr_bit(cpu,bi); cond_ok = (bo >> 4) | ((cr_bit ^ (~bo >> 3)) & 0x1); return(ctr_ok & cond_ok); } /* MFLR - Move From Link Register */ static fastcall int ppc32_exec_MFLR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); cpu->gpr[rd] = cpu->lr; return(0); } /* MTLR - Move To Link Register */ static fastcall int ppc32_exec_MTLR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); cpu->lr = cpu->gpr[rs]; return(0); } /* MFCTR - Move From Counter Register */ static fastcall int ppc32_exec_MFCTR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); cpu->gpr[rd] = cpu->ctr; return(0); } /* MTCTR - Move To Counter Register */ static fastcall int ppc32_exec_MTCTR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); cpu->ctr = cpu->gpr[rs]; return(0); } /* ADD */ static fastcall int ppc32_exec_ADD(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[ra] + cpu->gpr[rb]; return(0); } /* ADD. */ static fastcall int ppc32_exec_ADD_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t tmp; tmp = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[rd] = tmp; return(0); } /* ADDO - Add with Overflow */ static fastcall int ppc32_exec_ADDO(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b; ppc32_exec_ov_sum(cpu,d,a,b); cpu->gpr[rd] = d; return(0); } /* ADDO. */ static fastcall int ppc32_exec_ADDO_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b; ppc32_exec_ov_sum(cpu,d,a,b); ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* ADDC - Add Carrying */ static fastcall int ppc32_exec_ADDC(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b; ppc32_exec_ca_sum(cpu,d,a,b); cpu->gpr[rd] = d; return(0); } /* ADDC. */ static fastcall int ppc32_exec_ADDC_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b; ppc32_exec_ca_sum(cpu,d,a,b); ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* ADDCO - Add Carrying with Overflow */ static fastcall int ppc32_exec_ADDCO(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b; ppc32_exec_ca_sum(cpu,d,a,b); ppc32_exec_ov_sum(cpu,d,a,b); cpu->gpr[rd] = d; return(0); } /* ADDCO. */ static fastcall int ppc32_exec_ADDCO_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b; ppc32_exec_ca_sum(cpu,d,a,b); ppc32_exec_ov_sum(cpu,d,a,b); ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* ADDE - Add Extended */ static fastcall int ppc32_exec_ADDE(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b + carry; if (((b + carry) < b) || (d < a)) cpu->xer_ca = 1; cpu->gpr[rd] = d; return(0); } /* ADDE. */ static fastcall int ppc32_exec_ADDE_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b + carry; if (((b + carry) < b) || (d < a)) cpu->xer_ca = 1; ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* ADDEO - Add Extended with Overflow */ static fastcall int ppc32_exec_ADDEO(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b + carry; if (((b + carry) < b) || (d < a)) cpu->xer_ca = 1; ppc32_exec_ov_sum(cpu,d,a,b); cpu->gpr[rd] = d; return(0); } /* ADDEO. */ static fastcall int ppc32_exec_ADDEO_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b + carry; if (((b + carry) < b) || (d < a)) cpu->xer_ca = 1; ppc32_exec_ov_sum(cpu,d,a,b); ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* ADDI - ADD Immediate */ static fastcall int ppc32_exec_ADDI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); register m_uint32_t tmp; tmp = sign_extend_32(imm,16); if (ra != 0) tmp += cpu->gpr[ra]; cpu->gpr[rd] = tmp; return(0); } /* ADDIC - ADD Immediate with Carry */ static fastcall int ppc32_exec_ADDIC(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); register m_uint32_t a,d; a = cpu->gpr[ra]; d = a + sign_extend_32(imm,16); ppc32_exec_ca_sum(cpu,d,a,0); cpu->gpr[rd] = d; return(0); } /* ADDIC. */ static fastcall int ppc32_exec_ADDIC_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); register m_uint32_t a,d; a = cpu->gpr[ra]; d = a + sign_extend_32(imm,16); ppc32_exec_ca_sum(cpu,d,a,0); ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* ADDIS - ADD Immediate Shifted */ static fastcall int ppc32_exec_ADDIS(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); register m_uint32_t tmp; tmp = imm << 16; if (ra != 0) tmp += cpu->gpr[ra]; cpu->gpr[rd] = tmp; return(0); } /* ADDME - Add to Minus One Extended */ static fastcall int ppc32_exec_ADDME(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t a,b,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; b = 0xFFFFFFFF; d = a + b + carry; if (((b + carry) < b) || (d < a)) cpu->xer_ca = 1; cpu->gpr[rd] = d; return(0); } /* ADDME. */ static fastcall int ppc32_exec_ADDME_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t a,b,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; b = 0xFFFFFFFF; d = a + b + carry; if (((b + carry) < b) || (d < a)) cpu->xer_ca = 1; ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* ADDZE - Add to Zero Extended */ static fastcall int ppc32_exec_ADDZE(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t a,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; d = a + carry; if (d < a) cpu->xer_ca = 1; cpu->gpr[rd] = d; return(0); } /* ADDZE. */ static fastcall int ppc32_exec_ADDZE_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t a,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; d = a + carry; if (d < a) cpu->xer_ca = 1; ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* AND */ static fastcall int ppc32_exec_AND(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = cpu->gpr[rs] & cpu->gpr[rb]; return(0); } /* AND. */ static fastcall int ppc32_exec_AND_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t tmp; tmp = cpu->gpr[rs] & cpu->gpr[rb]; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* ANDC - AND with Complement */ static fastcall int ppc32_exec_ANDC(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = cpu->gpr[rs] & (~cpu->gpr[rb]); return(0); } /* ANDC. - AND with Complement */ static fastcall int ppc32_exec_ANDC_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t tmp; tmp = cpu->gpr[rs] & (~cpu->gpr[rb]); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* ANDI. - AND Immediate */ static fastcall int ppc32_exec_ANDI_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); register m_uint32_t tmp; tmp = cpu->gpr[rs] & imm; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* ANDIS. - AND Immediate Shifted */ static fastcall int ppc32_exec_ANDIS_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); register m_uint32_t tmp; tmp = cpu->gpr[rs] & (imm << 16); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* B - Branch */ static fastcall int ppc32_exec_B(cpu_ppc_t *cpu,ppc_insn_t insn) { m_uint32_t offset = bits(insn,2,25); cpu->ia += sign_extend_32(offset << 2,26); return(1); } /* BA - Branch Absolute */ static fastcall int ppc32_exec_BA(cpu_ppc_t *cpu,ppc_insn_t insn) { m_uint32_t offset = bits(insn,2,25); cpu->ia = sign_extend_32(offset << 2,26); return(1); } /* BL - Branch and Link */ static fastcall int ppc32_exec_BL(cpu_ppc_t *cpu,ppc_insn_t insn) { m_uint32_t offset = bits(insn,2,25); cpu->lr = cpu->ia + 4; cpu->ia += sign_extend_32(offset << 2,26); return(1); } /* BLA - Branch and Link Absolute */ static fastcall int ppc32_exec_BLA(cpu_ppc_t *cpu,ppc_insn_t insn) { m_uint32_t offset = bits(insn,2,25); cpu->lr = cpu->ia + 4; cpu->ia = sign_extend_32(offset << 2,26); return(1); } /* BC - Branch Conditional */ static fastcall int ppc32_exec_BC(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia += sign_extend_32(bd << 2,16); return(1); } return(0); } /* BCA - Branch Conditional (absolute) */ static fastcall int ppc32_exec_BCA(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia = sign_extend_32(bd << 2,16); return(1); } return(0); } /* BCL - Branch Conditional and Link */ static fastcall int ppc32_exec_BCL(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); cpu->lr = cpu->ia + 4; if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia += sign_extend_32(bd << 2,16); return(1); } return(0); } /* BCLA - Branch Conditional and Link (absolute) */ static fastcall int ppc32_exec_BCLA(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); cpu->lr = cpu->ia + 4; if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia = sign_extend_32(bd << 2,16); return(1); } return(0); } /* BCLR - Branch Conditional to Link register */ static fastcall int ppc32_exec_BCLR(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia = cpu->lr & ~0x3; return(1); } return(0); } /* BCLRL - Branch Conditional to Link register */ static fastcall int ppc32_exec_BCLRL(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); m_uint32_t new_ia; new_ia = cpu->lr & ~0x03; cpu->lr = cpu->ia + 4; if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia = new_ia; return(1); } return(0); } /* BCCTR - Branch Conditional to Count register */ static fastcall int ppc32_exec_BCCTR(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia = cpu->ctr & ~0x3; return(1); } return(0); } /* BCCTRL - Branch Conditional to Count register and Link */ static fastcall int ppc32_exec_BCCTRL(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); cpu->lr = cpu->ia + 4; if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia = cpu->ctr & ~0x3; return(1); } return(0); } /* CMP - Compare */ static fastcall int ppc32_exec_CMP(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t res; m_int32_t a,b; a = (m_int32_t)cpu->gpr[ra]; b = (m_int32_t)cpu->gpr[rb]; if (a < b) res = 0x08; else { if (a > b) res = 0x04; else res = 0x02; } if (cpu->xer & PPC32_XER_SO) res |= 0x01; cpu->cr_fields[rd] = res; return(0); } /* CMPI - Compare Immediate */ static fastcall int ppc32_exec_CMPI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t res; m_int32_t a,b; a = (m_int32_t)cpu->gpr[ra]; b = sign_extend_32(imm,16); if (a < b) res = 0x08; else { if (a > b) res = 0x04; else res = 0x02; } if (cpu->xer & PPC32_XER_SO) res |= 0x01; cpu->cr_fields[rd] = res; return(0); } /* CMPL - Compare Logical */ static fastcall int ppc32_exec_CMPL(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t res,a,b; a = cpu->gpr[ra]; b = cpu->gpr[rb]; if (a < b) res = 0x08; else { if (a > b) res = 0x04; else res = 0x02; } if (cpu->xer & PPC32_XER_SO) res |= 0x01; cpu->cr_fields[rd] = res; return(0); } /* CMPLI - Compare Logical Immediate */ static fastcall int ppc32_exec_CMPLI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); m_uint32_t res,a; a = cpu->gpr[ra]; if (a < imm) res = 0x08; else { if (a > imm) res = 0x04; else res = 0x02; } if (cpu->xer & PPC32_XER_SO) res |= 0x01; cpu->cr_fields[rd] = res; return(0); } /* CNTLZW - Count Leading Zeros Word */ static fastcall int ppc32_exec_CNTLZW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t val,mask; int i; val = cpu->gpr[rs]; mask = 0x80000000; for(i=0;i<32;i++) { if (val & mask) break; mask >>= 1; } cpu->gpr[ra] = i; return(0); } /* CRAND - Condition Register AND */ static fastcall int ppc32_exec_CRAND(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp &= ppc32_read_cr_bit(cpu,bb); if (tmp & 0x1) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* CREQV - Condition Register Equivalent */ static fastcall int ppc32_exec_CREQV(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp ^= ppc32_read_cr_bit(cpu,bb); if (!(tmp & 0x1)) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* CRANDC - Condition Register AND with Complement */ static fastcall int ppc32_exec_CRANDC(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp &= ~ppc32_read_cr_bit(cpu,bb); if (tmp & 0x1) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* CRNAND - Condition Register NAND */ static fastcall int ppc32_exec_CRNAND(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp &= ppc32_read_cr_bit(cpu,bb); if (!(tmp & 0x1)) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* CRNOR - Condition Register NOR */ static fastcall int ppc32_exec_CRNOR(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp |= ppc32_read_cr_bit(cpu,bb); if (!(tmp & 0x1)) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* CROR - Condition Register OR */ static fastcall int ppc32_exec_CROR(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp |= ppc32_read_cr_bit(cpu,bb); if (tmp & 0x1) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* CRORC - Condition Register OR with complement */ static fastcall int ppc32_exec_CRORC(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp |= ~ppc32_read_cr_bit(cpu,bb); if (tmp & 0x1) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* CRXOR - Condition Register XOR */ static fastcall int ppc32_exec_CRXOR(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp ^= ppc32_read_cr_bit(cpu,bb); if (tmp & 0x1) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* DCBF - Data Cache Block Flush */ static fastcall int ppc32_exec_DCBF(cpu_ppc_t *cpu,ppc_insn_t insn) { int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; //printf("PPC32: DBCF: vaddr=0x%8.8x\n",vaddr); return(0); } /* DCBI - Data Cache Block Invalidate */ static fastcall int ppc32_exec_DCBI(cpu_ppc_t *cpu,ppc_insn_t insn) { int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; //printf("PPC32: DBCI: vaddr=0x%8.8x\n",vaddr); return(0); } /* DCBT - Data Cache Block Touch */ static fastcall int ppc32_exec_DCBT(cpu_ppc_t *cpu,ppc_insn_t insn) { int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; //printf("PPC32: DBCT: vaddr=0x%8.8x\n",vaddr); return(0); } /* DCBST - Data Cache Block Store */ static fastcall int ppc32_exec_DCBST(cpu_ppc_t *cpu,ppc_insn_t insn) { int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; //printf("PPC32: DBCST: vaddr=0x%8.8x\n",vaddr); return(0); } /* DIVW - Divide Word */ static fastcall int ppc32_exec_DIVW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b; a = (m_int32_t)cpu->gpr[ra]; b = (m_int32_t)cpu->gpr[rb]; if (!((b == 0) || ((cpu->gpr[ra] == 0x80000000) && (b == -1)))) cpu->gpr[rd] = a / b; return(0); } /* DIVW. - Divide Word */ static fastcall int ppc32_exec_DIVW_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_int32_t a,b,d; a = (m_int32_t)cpu->gpr[ra]; b = (m_int32_t)cpu->gpr[rb]; d = 0; if (!((b == 0) || ((cpu->gpr[ra] == 0x80000000) && (b == -1)))) d = a / b; ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* DIVWU - Divide Word Unsigned */ static fastcall int ppc32_exec_DIVWU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b; a = cpu->gpr[ra]; b = cpu->gpr[rb]; if (b != 0) cpu->gpr[rd] = a / b; return(0); } /* DIVWU. - Divide Word Unsigned */ static fastcall int ppc32_exec_DIVWU_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = 0; if (b != 0) d = a / b; ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* EIEIO - Enforce In-order Execution of I/O */ static fastcall int ppc32_exec_EIEIO(cpu_ppc_t *cpu,ppc_insn_t insn) { return(0); } /* EQV */ static fastcall int ppc32_exec_EQV(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = ~(cpu->gpr[rs] ^ cpu->gpr[rb]); return(0); } /* EXTSB - Extend Sign Byte */ static fastcall int ppc32_exec_EXTSB(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); cpu->gpr[ra] = sign_extend_32(cpu->gpr[rs],8); return(0); } /* EXTSB. */ static fastcall int ppc32_exec_EXTSB_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t tmp; tmp = sign_extend_32(cpu->gpr[rs],8); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* EXTSH - Extend Sign Word */ static fastcall int ppc32_exec_EXTSH(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); cpu->gpr[ra] = sign_extend_32(cpu->gpr[rs],16); return(0); } /* EXTSH. */ static fastcall int ppc32_exec_EXTSH_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t tmp; tmp = sign_extend_32(cpu->gpr[rs],16); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* ICBI - Instruction Cache Block Invalidate */ static fastcall int ppc32_exec_ICBI(cpu_ppc_t *cpu,ppc_insn_t insn) { int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_ICBI,vaddr,0); return(0); } /* ISYNC - Instruction Synchronize */ static fastcall int ppc32_exec_ISYNC(cpu_ppc_t *cpu,ppc_insn_t insn) { return(0); } /* LBZ - Load Byte and Zero */ static fastcall int ppc32_exec_LBZ(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LBZ,vaddr,rd); return(0); } /* LBZU - Load Byte and Zero with Update */ static fastcall int ppc32_exec_LBZU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_LBZ,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LBZUX - Load Byte and Zero with Update Indexed */ static fastcall int ppc32_exec_LBZUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_LBZ,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LBZX - Load Byte and Zero Indexed */ static fastcall int ppc32_exec_LBZX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LBZ,vaddr,rd); return(0); } /* LHA - Load Half-Word Algebraic */ static fastcall int ppc32_exec_LHA(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LHA,vaddr,rd); return(0); } /* LHAU - Load Half-Word Algebraic with Update */ static fastcall int ppc32_exec_LHAU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_LHA,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LHAUX - Load Half-Word Algebraic with Update Indexed */ static fastcall int ppc32_exec_LHAUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_LHA,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LHAX - Load Half-Word Algebraic ndexed */ static fastcall int ppc32_exec_LHAX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LHA,vaddr,rd); return(0); } /* LHZ - Load Half-Word and Zero */ static fastcall int ppc32_exec_LHZ(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LHZ,vaddr,rd); return(0); } /* LHZU - Load Half-Word and Zero with Update */ static fastcall int ppc32_exec_LHZU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_LHZ,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LHZUX - Load Half-Word and Zero with Update Indexed */ static fastcall int ppc32_exec_LHZUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_LHZ,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LHZX - Load Half-Word and Zero Indexed */ static fastcall int ppc32_exec_LHZX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LHZ,vaddr,rd); return(0); } /* LMW - Load Multiple Word */ static fastcall int ppc32_exec_LMW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; int r; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; for(r=rd;r<=31;r++) { ppc32_exec_memop(cpu,PPC_MEMOP_LWZ,vaddr,r); vaddr += sizeof(m_uint32_t); } return(0); } /* LWBRX - Load Word Byte-Reverse Indexed */ static fastcall int ppc32_exec_LWBRX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LWBR,vaddr,rd); return(0); } /* LWZ - Load Word and Zero */ static fastcall int ppc32_exec_LWZ(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LWZ,vaddr,rd); return(0); } /* LWZU - Load Word and Zero with Update */ static fastcall int ppc32_exec_LWZU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_LWZ,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LWZUX - Load Word and Zero with Update Indexed */ static fastcall int ppc32_exec_LWZUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_LWZ,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LWZX - Load Word and Zero Indexed */ static fastcall int ppc32_exec_LWZX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LWZ,vaddr,rd); return(0); } /* LWARX - Load Word and Reserve Indexed */ static fastcall int ppc32_exec_LWARX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; cpu->reserve = 1; ppc32_exec_memop(cpu,PPC_MEMOP_LWZ,vaddr,rd); return(0); } /* LFD - Load Floating-Point Double */ static fastcall int ppc32_exec_LFD(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LFD,vaddr,rd); return(0); } /* LFDU - Load Floating-Point Double with Update */ static fastcall int ppc32_exec_LFDU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_LFD,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LFDUX - Load Floating-Point Double with Update Indexed */ static fastcall int ppc32_exec_LFDUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_LFD,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LFDX - Load Floating-Point Double Indexed */ static fastcall int ppc32_exec_LFDX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LFD,vaddr,rd); return(0); } /* LSWI - Load String Word Immediate */ static fastcall int ppc32_exec_LSWI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int nb = bits(insn,11,15); m_uint32_t vaddr = 0; int r; if (ra != 0) vaddr += cpu->gpr[ra]; if (nb == 0) nb = 32; r = rd - 1; cpu->sw_pos = 0; while(nb > 0) { if (cpu->sw_pos == 0) { r = (r + 1) & 0x1F; cpu->gpr[r] = 0; } ppc32_exec_memop(cpu,PPC_MEMOP_LSW,vaddr,r); cpu->sw_pos += 8; if (cpu->sw_pos == 32) cpu->sw_pos = 0; vaddr++; nb--; } return(0); } /* LSWX - Load String Word Indexed */ static fastcall int ppc32_exec_LSWX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; int r,nb; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; nb = cpu->xer & PPC32_XER_BC_MASK; r = rd - 1; cpu->sw_pos = 0; while(nb > 0) { if (cpu->sw_pos == 0) { r = (r + 1) & 0x1F; cpu->gpr[r] = 0; } ppc32_exec_memop(cpu,PPC_MEMOP_LSW,vaddr,r); cpu->sw_pos += 8; if (cpu->sw_pos == 32) cpu->sw_pos = 0; vaddr++; nb--; } return(0); } /* MCRF - Move Condition Register Field */ static fastcall int ppc32_exec_MCRF(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,23,25); int rs = bits(insn,18,20); cpu->cr_fields[rd] = cpu->cr_fields[rs]; return(0); } /* MFCR - Move from Condition Register */ static fastcall int ppc32_exec_MFCR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); cpu->gpr[rd] = ppc32_get_cr(cpu); return(0); } /* MFMSR - Move from Machine State Register */ static fastcall int ppc32_exec_MFMSR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); cpu->gpr[rd] = cpu->msr; return(0); } /* MFTBU - Move from Time Base (Up) */ static fastcall int ppc32_exec_MFTBU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); cpu->gpr[rd] = cpu->tb >> 32; return(0); } /* MFTBL - Move from Time Base (Lo) */ static fastcall int ppc32_exec_MFTBL(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); cpu->tb += 50; cpu->gpr[rd] = cpu->tb & 0xFFFFFFFF; return(0); } /* MFSPR - Move from Special-Purpose Register */ static fastcall int ppc32_exec_MFSPR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int spr0 = bits(insn,16,20); int spr1 = bits(insn,11,15); u_int spr; spr = (spr1 << 5) | spr0; cpu->gpr[rd] = 0; //cpu_log(cpu->gen,"SPR","reading SPR=%d at cpu->ia=0x%8.8x\n",spr,cpu->ia); if ((spr1 == 0x10) || (spr1 == 0x11)) { cpu->gpr[rd] = ppc32_get_bat_spr(cpu,spr); return(0); } switch(spr) { case PPC32_SPR_XER: cpu->gpr[rd] = cpu->xer | (cpu->xer_ca << PPC32_XER_CA_BIT); break; case PPC32_SPR_DSISR: cpu->gpr[rd] = cpu->dsisr; break; case PPC32_SPR_DAR: cpu->gpr[rd] = cpu->dar; break; case PPC32_SPR_DEC: cpu->gpr[rd] = cpu->dec; break; case PPC32_SPR_SDR1: cpu->gpr[rd] = cpu->sdr1; break; case PPC32_SPR_SRR0: cpu->gpr[rd] = cpu->srr0; break; case PPC32_SPR_SRR1: cpu->gpr[rd] = cpu->srr1; break; case PPC32_SPR_TBL_READ: cpu->gpr[rd] = cpu->tb & 0xFFFFFFFF; break; case PPC32_SPR_TBU_READ: cpu->gpr[rd] = cpu->tb >> 32; break; case PPC32_SPR_SPRG0: cpu->gpr[rd] = cpu->sprg[0]; break; case PPC32_SPR_SPRG1: cpu->gpr[rd] = cpu->sprg[1]; break; case PPC32_SPR_SPRG2: cpu->gpr[rd] = cpu->sprg[2]; break; case PPC32_SPR_SPRG3: cpu->gpr[rd] = cpu->sprg[3]; break; case PPC32_SPR_PVR: cpu->gpr[rd] = cpu->pvr; break; case PPC32_SPR_HID0: cpu->gpr[rd] = cpu->hid0; break; case PPC32_SPR_HID1: cpu->gpr[rd] = cpu->hid1; break; case PPC405_SPR_PID: cpu->gpr[rd] = cpu->ppc405_pid; break; /* MPC860 IMMR */ case 638: cpu->gpr[rd] = cpu->mpc860_immr; break; default: cpu->gpr[rd] = 0x0; //printf("READING SPR = %d\n",spr); } return(0); } /* MFSR - Move From Segment Register */ static fastcall int ppc32_exec_MFSR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int sr = bits(insn,16,19); cpu->gpr[rd] = cpu->sr[sr]; return(0); } /* MFSRIN - Move From Segment Register Indirect */ static fastcall int ppc32_exec_MFSRIN(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int rb = bits(insn,11,15); cpu->gpr[rd] = cpu->sr[cpu->gpr[rb] >> 28]; return(0); } /* MTCRF - Move to Condition Register Fields */ static fastcall int ppc32_exec_MTCRF(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int crm = bits(insn,12,19); int i; for(i=0;i<8;i++) if (crm & (1 << (7 - i))) cpu->cr_fields[i] = (cpu->gpr[rs] >> (28 - (i << 2))) & 0x0F; return(0); } /* MTMSR - Move to Machine State Register */ static fastcall int ppc32_exec_MTMSR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); cpu->msr = cpu->gpr[rs]; cpu->irq_check = (cpu->msr & PPC32_MSR_EE) && cpu->irq_pending; //printf("New MSR = 0x%8.8x at cpu->ia=0x%8.8x\n",cpu->msr,cpu->ia); return(0); } /* MTSPR - Move to Special-Purpose Register */ static fastcall int ppc32_exec_MTSPR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int spr0 = bits(insn,16,20); int spr1 = bits(insn,11,15); u_int spr; spr = (spr1 << 5) | spr0; //cpu_log(cpu->gen,"SPR","writing SPR=%d, val=0x%8.8x at cpu->ia=0x%8.8x\n", // spr,cpu->ia,cpu->gpr[rd]); if ((spr1 == 0x10) || (spr1 == 0x11)) { ppc32_set_bat_spr(cpu,spr,cpu->gpr[rd]); return(0); } switch(spr) { case PPC32_SPR_XER: cpu->xer = cpu->gpr[rd] & ~PPC32_XER_CA; cpu->xer_ca = (cpu->gpr[rd] >> PPC32_XER_CA_BIT) & 0x1; break; case PPC32_SPR_DEC: //printf("WRITING DECR 0x%8.8x AT IA=0x%8.8x\n",cpu->gpr[rd],cpu->ia); cpu->dec = cpu->gpr[rd]; cpu->timer_irq_armed = TRUE; break; case PPC32_SPR_SDR1: cpu->sdr1 = cpu->gpr[rd]; ppc32_mem_invalidate_cache(cpu); break; case PPC32_SPR_SRR0: cpu->srr0 = cpu->gpr[rd]; break; case PPC32_SPR_SRR1: cpu->srr1 = cpu->gpr[rd]; break; case PPC32_SPR_SPRG0: cpu->sprg[0] = cpu->gpr[rd]; break; case PPC32_SPR_SPRG1: cpu->sprg[1] = cpu->gpr[rd]; break; case PPC32_SPR_SPRG2: cpu->sprg[2] = cpu->gpr[rd]; break; case PPC32_SPR_SPRG3: cpu->sprg[3] = cpu->gpr[rd]; break; case PPC32_SPR_HID0: cpu->hid0 = cpu->gpr[rd]; break; case PPC32_SPR_HID1: cpu->hid1 = cpu->gpr[rd]; break; case PPC405_SPR_PID: cpu->ppc405_pid = cpu->gpr[rd]; break; #if 0 default: printf("WRITING SPR=%d, data=0x%8.8x\n",spr,cpu->gpr[rd]); #endif } return(0); } /* MTSR - Move To Segment Register */ static fastcall int ppc32_exec_MTSR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int sr = bits(insn,16,19); cpu->sr[sr] = cpu->gpr[rs]; ppc32_mem_invalidate_cache(cpu); return(0); } /* MULHW - Multiply High Word */ static fastcall int ppc32_exec_MULHW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_int64_t tmp; m_uint32_t res; tmp = (m_int64_t)(m_int32_t)cpu->gpr[ra]; tmp *= (m_int64_t)(m_int32_t)cpu->gpr[rb]; res = tmp >> 32; cpu->gpr[rd] = res; return(0); } /* MULHW. */ static fastcall int ppc32_exec_MULHW_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_int64_t tmp; m_uint32_t res; tmp = (m_int64_t)(m_int32_t)cpu->gpr[ra]; tmp *= (m_int64_t)(m_int32_t)cpu->gpr[rb]; res = tmp >> 32; ppc32_exec_update_cr0(cpu,res); cpu->gpr[rd] = res; return(0); } /* MULHWU - Multiply High Word Unsigned */ static fastcall int ppc32_exec_MULHWU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint64_t tmp; m_uint32_t res; tmp = (m_uint64_t)cpu->gpr[ra]; tmp *= (m_uint64_t)cpu->gpr[rb]; res = tmp >> 32; cpu->gpr[rd] = res; return(0); } /* MULHWU. */ static fastcall int ppc32_exec_MULHWU_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint64_t tmp; m_uint32_t res; tmp = (m_uint64_t)cpu->gpr[ra]; tmp *= (m_uint64_t)cpu->gpr[rb]; res = tmp >> 32; ppc32_exec_update_cr0(cpu,res); cpu->gpr[rd] = res; return(0); } /* MULLI - Multiply Low Immediate */ static fastcall int ppc32_exec_MULLI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); cpu->gpr[rd] = (m_int32_t)cpu->gpr[ra] * sign_extend_32(imm,16); return(0); } /* MULLW - Multiply Low Word */ static fastcall int ppc32_exec_MULLW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_int64_t tmp; tmp = (m_int64_t)(m_int32_t)cpu->gpr[ra]; tmp *= (m_int64_t)(m_int32_t)cpu->gpr[rb]; cpu->gpr[rd] = (m_uint32_t)tmp; return(0); } /* MULLW. */ static fastcall int ppc32_exec_MULLW_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t res; m_int64_t tmp; tmp = (m_int64_t)(m_int32_t)cpu->gpr[ra]; tmp *= (m_int64_t)(m_int32_t)cpu->gpr[rb]; res = (m_uint32_t)tmp; ppc32_exec_update_cr0(cpu,res); cpu->gpr[rd] = res; return(0); } /* MULLWO - Multiply Low Word with Overflow */ static fastcall int ppc32_exec_MULLWO(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_int64_t tmp; tmp = (m_int64_t)(m_int32_t)cpu->gpr[ra]; tmp *= (m_int64_t)(m_int32_t)cpu->gpr[rb]; cpu->xer &= ~PPC32_XER_OV; if (unlikely(tmp != (m_int64_t)(m_int32_t)tmp)) cpu->xer |= PPC32_XER_OV|PPC32_XER_SO; cpu->gpr[rd] = (m_uint32_t)tmp; return(0); } /* MULLWO. */ static fastcall int ppc32_exec_MULLWO_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t res; m_int64_t tmp; tmp = (m_int64_t)(m_int32_t)cpu->gpr[ra]; tmp *= (m_int64_t)(m_int32_t)cpu->gpr[rb]; cpu->xer &= ~PPC32_XER_OV; if (unlikely(tmp != (m_int64_t)(m_int32_t)tmp)) cpu->xer |= PPC32_XER_OV|PPC32_XER_SO; res = (m_uint32_t)tmp; ppc32_exec_update_cr0(cpu,res); cpu->gpr[rd] = res; return(0); } /* NAND */ static fastcall int ppc32_exec_NAND(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = ~(cpu->gpr[rs] & cpu->gpr[rb]); return(0); } /* NAND. */ static fastcall int ppc32_exec_NAND_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t tmp; tmp = ~(cpu->gpr[rs] & cpu->gpr[rb]); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* NEG - Negate */ static fastcall int ppc32_exec_NEG(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); cpu->gpr[rd] = ~cpu->gpr[ra] + 1; return(0); } /* NEG. */ static fastcall int ppc32_exec_NEG_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t tmp; tmp = ~cpu->gpr[ra] + 1; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[rd] = tmp; return(0); } /* NEGO */ static fastcall int ppc32_exec_NEGO(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t tmp; tmp = ~cpu->gpr[ra] + 1; cpu->gpr[rd] = tmp; cpu->xer &= ~PPC32_XER_OV; if (unlikely(tmp == 0x80000000)) cpu->xer |= PPC32_XER_OV|PPC32_XER_SO; ppc32_exec_update_cr0(cpu,tmp); return(0); } /* NEGO. */ static fastcall int ppc32_exec_NEGO_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t tmp; tmp = ~cpu->gpr[ra] + 1; cpu->gpr[rd] = tmp; cpu->xer &= ~PPC32_XER_OV; if (unlikely(tmp == 0x80000000)) cpu->xer |= PPC32_XER_OV|PPC32_XER_SO; ppc32_exec_update_cr0(cpu,tmp); return(0); } /* NOR */ static fastcall int ppc32_exec_NOR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = ~(cpu->gpr[rs] | cpu->gpr[rb]); return(0); } /* NOR. */ static fastcall int ppc32_exec_NOR_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t tmp; tmp = ~(cpu->gpr[rs] | cpu->gpr[rb]); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* OR */ static fastcall int ppc32_exec_OR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = cpu->gpr[rs] | cpu->gpr[rb]; return(0); } /* OR. */ static fastcall int ppc32_exec_OR_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t tmp; tmp = cpu->gpr[rs] | cpu->gpr[rb]; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* ORC - OR with Complement */ static fastcall int ppc32_exec_ORC(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = cpu->gpr[rs] | ~cpu->gpr[rb]; return(0); } /* ORC. */ static fastcall int ppc32_exec_ORC_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t tmp; tmp = cpu->gpr[rs] | ~cpu->gpr[rb]; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* ORI - OR Immediate */ static fastcall int ppc32_exec_ORI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); cpu->gpr[ra] = cpu->gpr[rs] | imm; return(0); } /* ORIS - OR Immediate Shifted */ static fastcall int ppc32_exec_ORIS(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); cpu->gpr[ra] = cpu->gpr[rs] | (imm << 16); return(0); } /* RFI - Return From Interrupt */ static fastcall int ppc32_exec_RFI(cpu_ppc_t *cpu,ppc_insn_t insn) { //printf("RFI: srr0=0x%8.8x, srr1=0x%8.8x\n",cpu->srr0,cpu->srr1); cpu->msr &= ~PPC32_RFI_MSR_MASK; cpu->msr |= cpu->srr1 & PPC32_RFI_MSR_MASK; cpu->msr &= ~(1 << 13); cpu->ia = cpu->srr0 & ~0x03; cpu->irq_check = (cpu->msr & PPC32_MSR_EE) && cpu->irq_pending; //printf("NEW IA=0x%8.8x, NEW MSR=0x%8.8x\n",cpu->ia,cpu->msr); return(1); } /* RLWIMI - Rotate Left Word Immediate then Mask Insert */ static fastcall int ppc32_exec_RLWIMI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t r,mask; r = (cpu->gpr[rs] << sh) | (cpu->gpr[rs] >> (32 - sh)); mask = ppc32_rotate_mask(mb,me); cpu->gpr[ra] = (r & mask) | (cpu->gpr[ra] & ~mask); return(0); } /* RLWIMI. - Rotate Left Word Immediate then Mask Insert */ static fastcall int ppc32_exec_RLWIMI_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t r,mask,tmp; r = (cpu->gpr[rs] << sh) | (cpu->gpr[rs] >> (32 - sh)); mask = ppc32_rotate_mask(mb,me); tmp = (r & mask) | (cpu->gpr[ra] & ~mask); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* RLWINM - Rotate Left Word Immediate AND with Mask */ static fastcall int ppc32_exec_RLWINM(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t r,mask; r = (cpu->gpr[rs] << sh) | (cpu->gpr[rs] >> (32 - sh)); mask = ppc32_rotate_mask(mb,me); cpu->gpr[ra] = r & mask; return(0); } /* RLWINM. - Rotate Left Word Immediate AND with Mask */ static fastcall int ppc32_exec_RLWINM_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t r,mask,tmp; r = (cpu->gpr[rs] << sh) | (cpu->gpr[rs] >> (32 - sh)); mask = ppc32_rotate_mask(mb,me); tmp = r & mask; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* RLWNM - Rotate Left Word then Mask Insert */ static fastcall int ppc32_exec_RLWNM(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t r,sh,mask; sh = cpu->gpr[rb] & 0x1f; r = (cpu->gpr[rs] << sh) | (cpu->gpr[rs] >> (32 - sh)); mask = ppc32_rotate_mask(mb,me); cpu->gpr[ra] = r & mask; return(0); } /* RLWNM. - Rotate Left Word then Mask Insert */ static fastcall int ppc32_exec_RLWNM_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t r,sh,mask,tmp; sh = cpu->gpr[rb] & 0x1f; r = (cpu->gpr[rs] << sh) | (cpu->gpr[rs] >> (32 - sh)); mask = ppc32_rotate_mask(mb,me); tmp = r & mask; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* SC - System Call */ static fastcall int ppc32_exec_SC(cpu_ppc_t *cpu,ppc_insn_t insn) { ppc32_trigger_exception(cpu,PPC32_EXC_SYSCALL); return(1); } /* SLW - Shift Left Word */ static fastcall int ppc32_exec_SLW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t s; s = cpu->gpr[rb] & 0x3f; if (likely(!(s & 0x20))) cpu->gpr[ra] = cpu->gpr[rs] << s; else cpu->gpr[ra] = 0; return(0); } /* SLW. */ static fastcall int ppc32_exec_SLW_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t s,tmp; s = cpu->gpr[rb] & 0x3f; if (likely(!(s & 0x20))) tmp = cpu->gpr[rs] << s; else tmp = 0; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* SRAW - Shift Right Algebraic Word */ static fastcall int ppc32_exec_SRAW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t s,mask; int sh; cpu->xer_ca = 0; s = cpu->gpr[rs]; sh = cpu->gpr[rb]; if (unlikely(sh & 0x20)) { cpu->gpr[ra] = (m_int32_t)s >> 31; cpu->xer_ca = cpu->gpr[ra] & 0x1; return(0); } cpu->gpr[ra] = (m_int32_t)s >> sh; mask = ~(0xFFFFFFFFU << sh); if ((s & 0x80000000) && ((s & mask) != 0)) cpu->xer_ca = 1; return(0); } /* SRAWI - Shift Right Algebraic Word Immediate */ static fastcall int ppc32_exec_SRAWI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); register m_uint32_t s,mask; cpu->xer_ca = 0; s = cpu->gpr[rs]; cpu->gpr[ra] = (m_int32_t)s >> sh; mask = ~(0xFFFFFFFFU << sh); if ((s & 0x80000000) && ((s & mask) != 0)) cpu->xer_ca = 1; return(0); } /* SRAWI. */ static fastcall int ppc32_exec_SRAWI_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); register m_uint32_t s,r,mask; cpu->xer_ca = 0; s = cpu->gpr[rs]; r = (m_int32_t)s >> sh; mask = ~(0xFFFFFFFFU << sh); if ((s & 0x80000000) && ((s & mask) != 0)) cpu->xer_ca = 1; ppc32_exec_update_cr0(cpu,r); cpu->gpr[ra] = r; return(0); } /* SRW - Shift Right Word */ static fastcall int ppc32_exec_SRW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t s; s = cpu->gpr[rb] & 0x3f; if (likely(!(s & 0x20))) cpu->gpr[ra] = cpu->gpr[rs] >> s; else cpu->gpr[ra] = 0; return(0); } /* SRW. */ static fastcall int ppc32_exec_SRW_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t s,tmp; s = cpu->gpr[rb] & 0x3f; if (likely(!(s & 0x20))) tmp = cpu->gpr[rs] >> s; else tmp = 0; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* STB - Store Byte */ static fastcall int ppc32_exec_STB(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STB,vaddr,rs); return(0); } /* STBU - Store Byte with Update */ static fastcall int ppc32_exec_STBU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_STB,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STBUX - Store Byte with Update Indexed */ static fastcall int ppc32_exec_STBUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_STB,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STBX - Store Byte Indexed */ static fastcall int ppc32_exec_STBX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STB,vaddr,rs); return(0); } /* STH - Store Half-Word */ static fastcall int ppc32_exec_STH(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STH,vaddr,rs); return(0); } /* STHU - Store Half-Word with Update */ static fastcall int ppc32_exec_STHU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_STH,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STHUX - Store Half-Word with Update Indexed */ static fastcall int ppc32_exec_STHUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_STH,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STHX - Store Half-Word Indexed */ static fastcall int ppc32_exec_STHX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STH,vaddr,rs); return(0); } /* STMW - Store Multiple Word */ static fastcall int ppc32_exec_STMW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; int r; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; for(r=rs;r<=31;r++) { ppc32_exec_memop(cpu,PPC_MEMOP_STW,vaddr,r); vaddr += sizeof(m_uint32_t); } return(0); } /* STW - Store Word */ static fastcall int ppc32_exec_STW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STW,vaddr,rs); return(0); } /* STWU - Store Word with Update */ static fastcall int ppc32_exec_STWU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_STW,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STWUX - Store Word with Update Indexed */ static fastcall int ppc32_exec_STWUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_STW,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STWX - Store Word Indexed */ static fastcall int ppc32_exec_STWX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STW,vaddr,rs); return(0); } /* STWBRX - Store Word Byte-Reverse Indexed */ static fastcall int ppc32_exec_STWBRX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STWBR,vaddr,rs); return(0); } /* STWCX. - Store Word Conditional Indexed */ static fastcall int ppc32_exec_STWCX_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; if (cpu->reserve) { ppc32_exec_memop(cpu,PPC_MEMOP_STW,vaddr,rs); cpu->cr_fields[0] = 1 << PPC32_CR_EQ_BIT; if (cpu->xer & PPC32_XER_SO) cpu->cr_fields[0] |= 1 << PPC32_CR_SO_BIT; cpu->reserve = 0; } else { cpu->cr_fields[0] = 0; if (cpu->xer & PPC32_XER_SO) cpu->cr_fields[0] |= 1 << PPC32_CR_SO_BIT; } return(0); } /* STFD - Store Floating-Point Double */ static fastcall int ppc32_exec_STFD(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STFD,vaddr,rs); return(0); } /* STFDU - Store Floating-Point Double with Update */ static fastcall int ppc32_exec_STFDU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_STFD,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STFDUX - Store Floating-Point Double with Update Indexed */ static fastcall int ppc32_exec_STFDUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_STFD,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STFDX - Store Floating-Point Double Indexed */ static fastcall int ppc32_exec_STFDX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STFD,vaddr,rs); return(0); } /* STSWI - Store String Word Immediate */ static fastcall int ppc32_exec_STSWI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int nb = bits(insn,11,15); m_uint32_t vaddr = 0; int r; if (ra != 0) vaddr += cpu->gpr[ra]; if (nb == 0) nb = 32; r = rs - 1; cpu->sw_pos = 0; while(nb > 0) { if (cpu->sw_pos == 0) r = (r + 1) & 0x1F; ppc32_exec_memop(cpu,PPC_MEMOP_STSW,vaddr,r); cpu->sw_pos += 8; if (cpu->sw_pos == 32) cpu->sw_pos = 0; vaddr++; nb--; } return(0); } /* STSWX - Store String Word Indexed */ static fastcall int ppc32_exec_STSWX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; int r,nb; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; nb = cpu->xer & PPC32_XER_BC_MASK; r = rs - 1; cpu->sw_pos = 0; while(nb > 0) { if (cpu->sw_pos == 0) r = (r + 1) & 0x1F; ppc32_exec_memop(cpu,PPC_MEMOP_STSW,vaddr,r); cpu->sw_pos += 8; if (cpu->sw_pos == 32) cpu->sw_pos = 0; vaddr++; nb--; } return(0); } /* SUBF - Subtract From */ static fastcall int ppc32_exec_SUBF(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rb] - cpu->gpr[ra]; return(0); } /* SUBF. */ static fastcall int ppc32_exec_SUBF_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t tmp; tmp = cpu->gpr[rb] - cpu->gpr[ra]; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[rd] = tmp; return(0); } /* SUBFO - Subtract From with Overflow */ static fastcall int ppc32_exec_SUBFO(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,tmp; a = cpu->gpr[ra]; b = cpu->gpr[rb]; tmp = b - a; ppc32_exec_ov_sub(cpu,tmp,b,a); cpu->gpr[rd] = tmp; return(0); } /* SUBFO. */ static fastcall int ppc32_exec_SUBFO_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,tmp; a = cpu->gpr[ra]; b = cpu->gpr[rb]; tmp = b - a; ppc32_exec_ov_sub(cpu,tmp,b,a); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[rd] = tmp; return(0); } /* SUBFC - Subtract From Carrying */ static fastcall int ppc32_exec_SUBFC(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,r,tmp; a = ~cpu->gpr[ra]; b = cpu->gpr[rb]; tmp = a + 1; r = b + tmp; ppc32_exec_ca_sum(cpu,tmp,a,1); if (r < tmp) cpu->xer_ca = 1; cpu->gpr[rd] = r; return(0); } /* SUBFC. */ static fastcall int ppc32_exec_SUBFC_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,r,tmp; a = ~cpu->gpr[ra]; b = cpu->gpr[rb]; tmp = a + 1; r = b + tmp; ppc32_exec_ca_sum(cpu,tmp,a,1); if (r < tmp) cpu->xer_ca = 1; ppc32_exec_update_cr0(cpu,r); cpu->gpr[rd] = r; return(0); } /* SUBFCO - Subtract From with Overflow */ _unused static fastcall int ppc32_exec_SUBFCO(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,tmp; a = cpu->gpr[ra]; b = cpu->gpr[rb]; tmp = b - a; ppc32_exec_ca_sub(cpu,tmp,b,a); ppc32_exec_ov_sub(cpu,tmp,b,a); cpu->gpr[rd] = tmp; return(0); } /* SUBFCO. */ _unused static fastcall int ppc32_exec_SUBFCO_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,tmp; a = cpu->gpr[ra]; b = cpu->gpr[rb]; tmp = b - a; ppc32_exec_ca_sub(cpu,tmp,b,a); ppc32_exec_ov_sub(cpu,tmp,b,a); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[rd] = tmp; return(0); } /* SUBFE - Subtract From Carrying */ static fastcall int ppc32_exec_SUBFE(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,r,tmp; m_uint32_t carry; carry = cpu->xer_ca; a = ~cpu->gpr[ra]; b = cpu->gpr[rb]; tmp = a + carry; r = b + tmp; ppc32_exec_ca_sum(cpu,tmp,a,carry); if (r < tmp) cpu->xer_ca = 1; cpu->gpr[rd] = r; return(0); } /* SUBFIC - Subtract From Immediate Carrying */ static fastcall int ppc32_exec_SUBFIC(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); register m_uint32_t a,b,r,tmp; a = ~cpu->gpr[ra]; b = sign_extend_32(imm,16); tmp = a + 1; r = b + tmp; ppc32_exec_ca_sum(cpu,tmp,a,1); if (r < tmp) cpu->xer_ca = 1; cpu->gpr[rd] = r; return(0); } /* SUBFZE - Subtract From Zero extended */ static fastcall int ppc32_exec_SUBFZE(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t a,r; m_uint32_t carry; carry = cpu->xer_ca; a = ~cpu->gpr[ra]; r = a + carry; if (r < a) cpu->xer_ca = 1; cpu->gpr[rd] = r; return(0); } /* SUBFZE. */ static fastcall int ppc32_exec_SUBFZE_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t a,r; m_uint32_t carry; carry = cpu->xer_ca; a = ~cpu->gpr[ra]; r = a + carry; if (r < a) cpu->xer_ca = 1; ppc32_exec_update_cr0(cpu,r); cpu->gpr[rd] = r; return(0); } /* SYNC - Synchronize */ static fastcall int ppc32_exec_SYNC(cpu_ppc_t *cpu,ppc_insn_t insn) { return(0); } /* TLBIA - TLB Invalidate All */ static fastcall int ppc32_exec_TLBIA(cpu_ppc_t *cpu,ppc_insn_t insn) { ppc32_mem_invalidate_cache(cpu); return(0); } /* TLBIE - TLB Invalidate Entry */ static fastcall int ppc32_exec_TLBIE(cpu_ppc_t *cpu,ppc_insn_t insn) { ppc32_mem_invalidate_cache(cpu); return(0); } /* TLBSYNC - TLB Synchronize */ static fastcall int ppc32_exec_TLBSYNC(cpu_ppc_t *cpu,ppc_insn_t insn) { return(0); } /* TW - Trap Word */ static fastcall int ppc32_exec_TW(cpu_ppc_t *cpu,ppc_insn_t insn) { int to = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_int32_t a,b; a = cpu->gpr[ra]; b = cpu->gpr[rb]; if (((a < b) && (to & 0x10)) || ((a > b) && (to & 0x08)) || ((a == b) && (to & 0x04)) || (((m_uint32_t)a < (m_uint32_t)b) && (to & 0x02)) || (((m_uint32_t)a > (m_uint32_t)b) && (to & 0x01))) { ppc32_trigger_exception(cpu,PPC32_EXC_PROG); cpu->srr1 |= 1 << 17; return(1); } return(0); } /* TWI - Trap Word Immediate */ static fastcall int ppc32_exec_TWI(cpu_ppc_t *cpu,ppc_insn_t insn) { int to = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_int32_t a,b; a = cpu->gpr[ra]; b = sign_extend(imm,16); if (((a < b) && (to & 0x10)) || ((a > b) && (to & 0x08)) || ((a == b) && (to & 0x04)) || (((m_uint32_t)a < (m_uint32_t)b) && (to & 0x02)) || (((m_uint32_t)a > (m_uint32_t)b) && (to & 0x01))) { ppc32_trigger_exception(cpu,PPC32_EXC_PROG); cpu->srr1 |= 1 << 17; return(1); } return(0); } /* XOR */ static fastcall int ppc32_exec_XOR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = cpu->gpr[rs] ^ cpu->gpr[rb]; return(0); } /* XOR. */ static fastcall int ppc32_exec_XOR_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t tmp; tmp = cpu->gpr[rs] ^ cpu->gpr[rb]; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* XORI - XOR Immediate */ static fastcall int ppc32_exec_XORI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); cpu->gpr[ra] = cpu->gpr[rs] ^ imm; return(0); } /* XORIS - XOR Immediate Shifted */ static fastcall int ppc32_exec_XORIS(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); cpu->gpr[ra] = cpu->gpr[rs] ^ (imm << 16); return(0); } /* DCCCI - Data Cache Congruence Class Invalidate (PowerPC 405) */ static fastcall int ppc32_exec_DCCCI(cpu_ppc_t *cpu,ppc_insn_t insn) { int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; return(0); } /* ICCCI - Instruction Cache Congruence Class Invalidate (PowerPC 405) */ static fastcall int ppc32_exec_ICCCI(cpu_ppc_t *cpu,ppc_insn_t insn) { int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; return(0); } /* MFDCR - Move From Device Control Register (PowerPC 405) */ static fastcall int ppc32_exec_MFDCR(cpu_ppc_t *cpu,ppc_insn_t insn) { int UNUSED(rt) = bits(insn,21,25); return(0); } /* MTDCR - Move To Device Control Register (PowerPC 405) */ static fastcall int ppc32_exec_MTDCR(cpu_ppc_t *cpu,ppc_insn_t insn) { int UNUSED(rt) = bits(insn,21,25); return(0); } /* TLBRE - TLB Read Entry (PowerPC 405) */ static fastcall int ppc32_exec_TLBRE(cpu_ppc_t *cpu,ppc_insn_t insn) { int rt = bits(insn,21,25); int ra = bits(insn,16,20); int ws = bits(insn,11,15); m_uint32_t index; index = cpu->gpr[ra] & 0x3F; if (ws == 1) { cpu->gpr[rt] = cpu->ppc405_tlb[index].tlb_lo; } else { cpu->gpr[rt] = cpu->ppc405_tlb[index].tlb_hi; cpu->ppc405_pid = cpu->ppc405_tlb[index].tid; } return(0); } /* TLBWE - TLB Write Entry (PowerPC 405) */ static fastcall int ppc32_exec_TLBWE(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int ws = bits(insn,11,15); m_uint32_t index; index = cpu->gpr[ra] & 0x3F; if (ws == 1) { cpu->ppc405_tlb[index].tlb_lo = cpu->gpr[rs]; } else { cpu->ppc405_tlb[index].tlb_hi = cpu->gpr[rs]; cpu->ppc405_tlb[index].tid = cpu->ppc405_pid; } return(0); } /* PowerPC instruction array */ static struct ppc32_insn_exec_tag ppc32_exec_tags[] = { { "mflr" , ppc32_exec_MFLR , 0xfc1fffff , 0x7c0802a6, 0 }, { "mtlr" , ppc32_exec_MTLR , 0xfc1fffff , 0x7c0803a6, 0 }, { "mfctr" , ppc32_exec_MFCTR , 0xfc1fffff , 0x7c0902a6, 0 }, { "mtctr" , ppc32_exec_MTCTR , 0xfc1fffff , 0x7c0903a6, 0 }, { "add" , ppc32_exec_ADD , 0xfc0007ff , 0x7c000214, 0 }, { "add." , ppc32_exec_ADD_dot , 0xfc0007ff , 0x7c000215, 0 }, { "addo" , ppc32_exec_ADDO , 0xfc0007ff , 0x7c000614, 0 }, { "addo." , ppc32_exec_ADDO_dot , 0xfc0007ff , 0x7c000615, 0 }, { "addc" , ppc32_exec_ADDC , 0xfc0007ff , 0x7c000014, 0 }, { "addc." , ppc32_exec_ADDC_dot , 0xfc0007ff , 0x7c000015, 0 }, { "addco" , ppc32_exec_ADDCO , 0xfc0007ff , 0x7c000414, 0 }, { "addco." , ppc32_exec_ADDCO_dot , 0xfc0007ff , 0x7c000415, 0 }, { "adde" , ppc32_exec_ADDE , 0xfc0007ff , 0x7c000114, 0 }, { "adde." , ppc32_exec_ADDE_dot , 0xfc0007ff , 0x7c000115, 0 }, { "addeo" , ppc32_exec_ADDEO , 0xfc0007ff , 0x7c000514, 0 }, { "addeo." , ppc32_exec_ADDEO_dot , 0xfc0007ff , 0x7c000515, 0 }, { "addi" , ppc32_exec_ADDI , 0xfc000000 , 0x38000000, 0 }, { "addic" , ppc32_exec_ADDIC , 0xfc000000 , 0x30000000, 0 }, { "addic." , ppc32_exec_ADDIC_dot , 0xfc000000 , 0x34000000, 0 }, { "addis" , ppc32_exec_ADDIS , 0xfc000000 , 0x3c000000, 0 }, { "addme" , ppc32_exec_ADDME , 0xfc00ffff , 0x7c0001d4, 0 }, { "addme." , ppc32_exec_ADDME_dot , 0xfc00ffff , 0x7c0001d5, 0 }, { "addze" , ppc32_exec_ADDZE , 0xfc00ffff , 0x7c000194, 0 }, { "addze." , ppc32_exec_ADDZE_dot , 0xfc00ffff , 0x7c000195, 0 }, { "and" , ppc32_exec_AND , 0xfc0007ff , 0x7c000038, 0 }, { "and." , ppc32_exec_AND_dot , 0xfc0007ff , 0x7c000039, 0 }, { "andc" , ppc32_exec_ANDC , 0xfc0007ff , 0x7c000078, 0 }, { "andc." , ppc32_exec_ANDC_dot , 0xfc0007ff , 0x7c000079, 0 }, { "andi." , ppc32_exec_ANDI_dot , 0xfc000000 , 0x70000000, 0 }, { "andis." , ppc32_exec_ANDIS_dot , 0xfc000000 , 0x74000000, 0 }, { "b" , ppc32_exec_B , 0xfc000003 , 0x48000000, 0 }, { "ba" , ppc32_exec_BA , 0xfc000003 , 0x48000002, 0 }, { "bl" , ppc32_exec_BL , 0xfc000003 , 0x48000001, 0 }, { "bla" , ppc32_exec_BLA , 0xfc000003 , 0x48000003, 0 }, { "bc" , ppc32_exec_BC , 0xfc000003 , 0x40000000, 0 }, { "bca" , ppc32_exec_BCA , 0xfc000003 , 0x40000002, 0 }, { "bcl" , ppc32_exec_BCL , 0xfc000003 , 0x40000001, 0 }, { "bcla" , ppc32_exec_BCLA , 0xfc000003 , 0x40000003, 0 }, { "bclr" , ppc32_exec_BCLR , 0xfc00ffff , 0x4c000020, 0 }, { "bclrl" , ppc32_exec_BCLRL , 0xfc00ffff , 0x4c000021, 0 }, { "bcctr" , ppc32_exec_BCCTR , 0xfc00ffff , 0x4c000420, 0 }, { "bcctrl" , ppc32_exec_BCCTRL , 0xfc00ffff , 0x4c000421, 0 }, { "cmp" , ppc32_exec_CMP , 0xfc6007ff , 0x7c000000, 0 }, { "cmpi" , ppc32_exec_CMPI , 0xfc600000 , 0x2c000000, 0 }, { "cmpl" , ppc32_exec_CMPL , 0xfc6007ff , 0x7c000040, 0 }, { "cmpli" , ppc32_exec_CMPLI , 0xfc600000 , 0x28000000, 0 }, { "cntlzw" , ppc32_exec_CNTLZW , 0xfc00ffff , 0x7c000034, 0 }, { "crand" , ppc32_exec_CRAND , 0xfc0007ff , 0x4c000202, 0 }, { "crandc" , ppc32_exec_CRANDC , 0xfc0007ff , 0x4c000102, 0 }, { "creqv" , ppc32_exec_CREQV , 0xfc0007ff , 0x4c000242, 0 }, { "crnand" , ppc32_exec_CRNAND , 0xfc0007ff , 0x4c0001c2, 0 }, { "crnor" , ppc32_exec_CRNOR , 0xfc0007ff , 0x4c000042, 0 }, { "cror" , ppc32_exec_CROR , 0xfc0007ff , 0x4c000382, 0 }, { "crorc" , ppc32_exec_CRORC , 0xfc0007ff , 0x4c000342, 0 }, { "crxor" , ppc32_exec_CRXOR , 0xfc0007ff , 0x4c000182, 0 }, { "dcbf" , ppc32_exec_DCBF , 0xffe007ff , 0x7c0000ac, 0 }, { "dcbi" , ppc32_exec_DCBI , 0xffe007ff , 0x7c0003ac, 0 }, { "dcbt" , ppc32_exec_DCBT , 0xffe007ff , 0x7c00022c, 0 }, { "dcbst" , ppc32_exec_DCBST , 0xffe007ff , 0x7c00006c, 0 }, { "divw" , ppc32_exec_DIVW , 0xfc0007ff , 0x7c0003d6, 0 }, { "divw." , ppc32_exec_DIVW_dot , 0xfc0007ff , 0x7c0003d7, 0 }, { "divwu" , ppc32_exec_DIVWU , 0xfc0007ff , 0x7c000396, 0 }, { "divwu." , ppc32_exec_DIVWU_dot , 0xfc0007ff , 0x7c000397, 0 }, { "eieio" , ppc32_exec_EIEIO , 0xffffffff , 0x7c0006ac, 0 }, { "eqv" , ppc32_exec_EQV , 0xfc0007ff , 0x7c000238, 0 }, { "extsb" , ppc32_exec_EXTSB , 0xfc00ffff , 0x7c000774, 0 }, { "extsb." , ppc32_exec_EXTSB_dot , 0xfc00ffff , 0x7c000775, 0 }, { "extsh" , ppc32_exec_EXTSH , 0xfc00ffff , 0x7c000734, 0 }, { "extsh." , ppc32_exec_EXTSH_dot , 0xfc00ffff , 0x7c000735, 0 }, { "icbi" , ppc32_exec_ICBI , 0xffe007ff , 0x7c0007ac, 0 }, { "isync" , ppc32_exec_ISYNC , 0xffffffff , 0x4c00012c, 0 }, { "lbz" , ppc32_exec_LBZ , 0xfc000000 , 0x88000000, 0 }, { "lbzu" , ppc32_exec_LBZU , 0xfc000000 , 0x8c000000, 0 }, { "lbzux" , ppc32_exec_LBZUX , 0xfc0007ff , 0x7c0000ee, 0 }, { "lbzx" , ppc32_exec_LBZX , 0xfc0007ff , 0x7c0000ae, 0 }, { "lha" , ppc32_exec_LHA , 0xfc000000 , 0xa8000000, 0 }, { "lhau" , ppc32_exec_LHAU , 0xfc000000 , 0xac000000, 0 }, { "lhaux" , ppc32_exec_LHAUX , 0xfc0007ff , 0x7c0002ee, 0 }, { "lhax" , ppc32_exec_LHAX , 0xfc0007ff , 0x7c0002ae, 0 }, { "lhz" , ppc32_exec_LHZ , 0xfc000000 , 0xa0000000, 0 }, { "lhzu" , ppc32_exec_LHZU , 0xfc000000 , 0xa4000000, 0 }, { "lhzux" , ppc32_exec_LHZUX , 0xfc0007ff , 0x7c00026e, 0 }, { "lhzx" , ppc32_exec_LHZX , 0xfc0007ff , 0x7c00022e, 0 }, { "lmw" , ppc32_exec_LMW , 0xfc000000 , 0xb8000000, 0 }, { "lwbrx" , ppc32_exec_LWBRX , 0xfc0007ff , 0x7c00042c, 0 }, { "lwz" , ppc32_exec_LWZ , 0xfc000000 , 0x80000000, 0 }, { "lwzu" , ppc32_exec_LWZU , 0xfc000000 , 0x84000000, 0 }, { "lwzux" , ppc32_exec_LWZUX , 0xfc0007ff , 0x7c00006e, 0 }, { "lwzx" , ppc32_exec_LWZX , 0xfc0007ff , 0x7c00002e, 0 }, { "lwarx" , ppc32_exec_LWARX , 0xfc0007ff , 0x7c000028, 0 }, { "lfd" , ppc32_exec_LFD , 0xfc000000 , 0xc8000000, 0 }, { "lfdu" , ppc32_exec_LFDU , 0xfc000000 , 0xcc000000, 0 }, { "lfdux" , ppc32_exec_LFDUX , 0xfc0007ff , 0x7c0004ee, 0 }, { "lfdx" , ppc32_exec_LFDX , 0xfc0007ff , 0x7c0004ae, 0 }, { "lswi" , ppc32_exec_LSWI , 0xfc0007ff , 0x7c0004aa, 0 }, { "lswx" , ppc32_exec_LSWX , 0xfc0007ff , 0x7c00042a, 0 }, { "mcrf" , ppc32_exec_MCRF , 0xfc63ffff , 0x4c000000, 0 }, { "mfcr" , ppc32_exec_MFCR , 0xfc1fffff , 0x7c000026, 0 }, { "mfmsr" , ppc32_exec_MFMSR , 0xfc1fffff , 0x7c0000a6, 0 }, { "mfspr" , ppc32_exec_MFSPR , 0xfc0007ff , 0x7c0002a6, 0 }, { "mfsr" , ppc32_exec_MFSR , 0xfc10ffff , 0x7c0004a6, 0 }, { "mfsrin" , ppc32_exec_MFSRIN , 0xfc1f07ff , 0x7c000526, 0 }, { "mftbl" , ppc32_exec_MFTBL , 0xfc1ff7ff , 0x7c0c42e6, 0 }, { "mftbu" , ppc32_exec_MFTBU , 0xfc1ff7ff , 0x7c0d42e6, 0 }, { "mtcrf" , ppc32_exec_MTCRF , 0xfc100fff , 0x7c000120, 0 }, { "mtmsr" , ppc32_exec_MTMSR , 0xfc1fffff , 0x7c000124, 0 }, { "mtspr" , ppc32_exec_MTSPR , 0xfc0007ff , 0x7c0003a6, 0 }, { "mtsr" , ppc32_exec_MTSR , 0xfc10ffff , 0x7c0001a4, 0 }, { "mulhw" , ppc32_exec_MULHW , 0xfc0007ff , 0x7c000096, 0 }, { "mulhw." , ppc32_exec_MULHW_dot , 0xfc0007ff , 0x7c000097, 0 }, { "mulhwu" , ppc32_exec_MULHWU , 0xfc0007ff , 0x7c000016, 0 }, { "mulhwu." , ppc32_exec_MULHWU_dot , 0xfc0007ff , 0x7c000017, 0 }, { "mulli" , ppc32_exec_MULLI , 0xfc000000 , 0x1c000000, 0 }, { "mullw" , ppc32_exec_MULLW , 0xfc0007ff , 0x7c0001d6, 0 }, { "mullw." , ppc32_exec_MULLW_dot , 0xfc0007ff , 0x7c0001d7, 0 }, { "mullwo" , ppc32_exec_MULLWO , 0xfc0007ff , 0x7c0005d6, 0 }, { "mullwo." , ppc32_exec_MULLWO_dot , 0xfc0007ff , 0x7c0005d7, 0 }, { "nand" , ppc32_exec_NAND , 0xfc0007ff , 0x7c0003b8, 0 }, { "nand." , ppc32_exec_NAND_dot , 0xfc0007ff , 0x7c0003b9, 0 }, { "neg" , ppc32_exec_NEG , 0xfc00ffff , 0x7c0000d0, 0 }, { "neg." , ppc32_exec_NEG_dot , 0xfc00ffff , 0x7c0000d1, 0 }, { "nego" , ppc32_exec_NEGO , 0xfc00ffff , 0x7c0004d0, 0 }, { "nego." , ppc32_exec_NEGO_dot , 0xfc00ffff , 0x7c0004d1, 0 }, { "nor" , ppc32_exec_NOR , 0xfc0007ff , 0x7c0000f8, 0 }, { "nor." , ppc32_exec_NOR_dot , 0xfc0007ff , 0x7c0000f9, 0 }, { "or" , ppc32_exec_OR , 0xfc0007ff , 0x7c000378, 0 }, { "or." , ppc32_exec_OR_dot , 0xfc0007ff , 0x7c000379, 0 }, { "orc" , ppc32_exec_ORC , 0xfc0007ff , 0x7c000338, 0 }, { "orc." , ppc32_exec_ORC_dot , 0xfc0007ff , 0x7c000339, 0 }, { "ori" , ppc32_exec_ORI , 0xfc000000 , 0x60000000, 0 }, { "oris" , ppc32_exec_ORIS , 0xfc000000 , 0x64000000, 0 }, { "rfi" , ppc32_exec_RFI , 0xffffffff , 0x4c000064, 0 }, { "rlwimi" , ppc32_exec_RLWIMI , 0xfc000001 , 0x50000000, 0 }, { "rlwimi." , ppc32_exec_RLWIMI_dot , 0xfc000001 , 0x50000001, 0 }, { "rlwinm" , ppc32_exec_RLWINM , 0xfc000001 , 0x54000000, 0 }, { "rlwinm." , ppc32_exec_RLWINM_dot , 0xfc000001 , 0x54000001, 0 }, { "rlwnm" , ppc32_exec_RLWNM , 0xfc000001 , 0x5c000000, 0 }, { "rlwnm." , ppc32_exec_RLWNM_dot , 0xfc000001 , 0x5c000001, 0 }, { "sc" , ppc32_exec_SC , 0xffffffff , 0x44000002, 0 }, { "slw" , ppc32_exec_SLW , 0xfc0007ff , 0x7c000030, 0 }, { "slw." , ppc32_exec_SLW_dot , 0xfc0007ff , 0x7c000031, 0 }, { "sraw" , ppc32_exec_SRAW , 0xfc0007ff , 0x7c000630, 0 }, { "srawi" , ppc32_exec_SRAWI , 0xfc0007ff , 0x7c000670, 0 }, { "srawi." , ppc32_exec_SRAWI_dot , 0xfc0007ff , 0x7c000671, 0 }, { "srw" , ppc32_exec_SRW , 0xfc0007ff , 0x7c000430, 0 }, { "srw." , ppc32_exec_SRW_dot , 0xfc0007ff , 0x7c000431, 0 }, { "stb" , ppc32_exec_STB , 0xfc000000 , 0x98000000, 0 }, { "stbu" , ppc32_exec_STBU , 0xfc000000 , 0x9c000000, 0 }, { "stbux" , ppc32_exec_STBUX , 0xfc0007ff , 0x7c0001ee, 0 }, { "stbx" , ppc32_exec_STBX , 0xfc0007ff , 0x7c0001ae, 0 }, { "sth" , ppc32_exec_STH , 0xfc000000 , 0xb0000000, 0 }, { "sthu" , ppc32_exec_STHU , 0xfc000000 , 0xb4000000, 0 }, { "sthux" , ppc32_exec_STHUX , 0xfc0007ff , 0x7c00036e, 0 }, { "sthx" , ppc32_exec_STHX , 0xfc0007ff , 0x7c00032e, 0 }, { "stmw" , ppc32_exec_STMW , 0xfc000000 , 0xbc000000, 0 }, { "stw" , ppc32_exec_STW , 0xfc000000 , 0x90000000, 0 }, { "stwu" , ppc32_exec_STWU , 0xfc000000 , 0x94000000, 0 }, { "stwux" , ppc32_exec_STWUX , 0xfc0007ff , 0x7c00016e, 0 }, { "stwx" , ppc32_exec_STWX , 0xfc0007ff , 0x7c00012e, 0 }, { "stwbrx" , ppc32_exec_STWBRX , 0xfc0007ff , 0x7c00052c, 0 }, { "stwcx." , ppc32_exec_STWCX_dot , 0xfc0007ff , 0x7c00012d, 0 }, { "stfd" , ppc32_exec_STFD , 0xfc000000 , 0xd8000000, 0 }, { "stfdu" , ppc32_exec_STFDU , 0xfc000000 , 0xdc000000, 0 }, { "stfdux" , ppc32_exec_STFDUX , 0xfc0007ff , 0x7c0005ee, 0 }, { "stfdx" , ppc32_exec_STFDX , 0xfc0007ff , 0x7c0005ae, 0 }, { "stswi" , ppc32_exec_STSWI , 0xfc0007ff , 0x7c0005aa, 0 }, { "stswx" , ppc32_exec_STSWX , 0xfc0007ff , 0x7c00052a, 0 }, { "subf" , ppc32_exec_SUBF , 0xfc0007ff , 0x7c000050, 0 }, { "subf." , ppc32_exec_SUBF_dot , 0xfc0007ff , 0x7c000051, 0 }, { "subfo" , ppc32_exec_SUBFO , 0xfc0007ff , 0x7c000450, 0 }, { "subfo." , ppc32_exec_SUBFO_dot , 0xfc0007ff , 0x7c000451, 0 }, { "subfc" , ppc32_exec_SUBFC , 0xfc0007ff , 0x7c000010, 0 }, { "subfc." , ppc32_exec_SUBFC_dot , 0xfc0007ff , 0x7c000011, 0 }, //{ "subfco" , ppc32_exec_SUBFCO , 0xfc0007ff , 0x7c000410, 0 }, //{ "subfco." , ppc32_exec_SUBFCO_dot , 0xfc0007ff , 0x7c000411, 0 }, { "subfe" , ppc32_exec_SUBFE , 0xfc0007ff , 0x7c000110, 0 }, { "subfic" , ppc32_exec_SUBFIC , 0xfc000000 , 0x20000000, 0 }, { "subfze" , ppc32_exec_SUBFZE , 0xfc00ffff , 0x7c000190, 0 }, { "subfze." , ppc32_exec_SUBFZE_dot , 0xfc00ffff , 0x7c000191, 0 }, { "sync" , ppc32_exec_SYNC , 0xffffffff , 0x7c0004ac, 0 }, { "tlbia" , ppc32_exec_TLBIA , 0xffffffff , 0x7c0002e4, 0 }, { "tlbie" , ppc32_exec_TLBIE , 0xffff07ff , 0x7c000264, 0 }, { "tlbsync" , ppc32_exec_TLBSYNC , 0xffffffff , 0x7c00046c, 0 }, { "tw" , ppc32_exec_TW , 0xfc0007ff , 0x7c000008, 0 }, { "twi" , ppc32_exec_TWI , 0xfc000000 , 0x0c000000, 0 }, { "xor" , ppc32_exec_XOR , 0xfc0007ff , 0x7c000278, 0 }, { "xor." , ppc32_exec_XOR_dot , 0xfc0007ff , 0x7c000279, 0 }, { "xori" , ppc32_exec_XORI , 0xfc000000 , 0x68000000, 0 }, { "xoris" , ppc32_exec_XORIS , 0xfc000000 , 0x6c000000, 0 }, /* PowerPC 405 specific instructions */ { "dccci" , ppc32_exec_DCCCI , 0xfc0007ff , 0x7c00038c, 0 }, { "iccci" , ppc32_exec_ICCCI , 0xfc0007ff , 0x7c00078c, 0 }, { "mfdcr" , ppc32_exec_MFDCR , 0xfc0007ff , 0x7c000286, 0 }, { "mtdcr" , ppc32_exec_MTDCR , 0xfc0007ff , 0x7c000386, 0 }, { "tlbre" , ppc32_exec_TLBRE , 0xfc0007ff , 0x7c000764, 0 }, { "tlbwe" , ppc32_exec_TLBWE , 0xfc0007ff , 0x7c0007a4, 0 }, /* Unknown opcode fallback */ { "unknown" , ppc32_exec_unknown , 0x00000000 , 0x00000000, 0 }, { NULL , NULL, 0, 0, 0 }, }; dynamips-0.2.14/stable/ppc32_jit.c000066400000000000000000001053771241034141600166460ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * PPC32 JIT compiler. */ #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "device.h" #include "ppc32.h" #include "ppc32_exec.h" #include "ppc32_jit.h" #include "insn_lookup.h" #include "memory.h" #include "ptask.h" #include PPC32_ARCH_INC_FILE /* Instruction Lookup Table */ static insn_lookup_t *ilt = NULL; static void *ppc32_jit_get_insn(int index) { return(&ppc32_insn_tags[index]); } static int ppc32_jit_chk_lo(struct ppc32_insn_tag *tag,int value) { return((value & tag->mask) == (tag->value & 0xFFFF)); } static int ppc32_jit_chk_hi(struct ppc32_insn_tag *tag,int value) { return((value & (tag->mask >> 16)) == (tag->value >> 16)); } /* Destroy instruction lookup table */ static void destroy_ilt(void) { assert(ilt); ilt_destroy(ilt); ilt = NULL; } /* Initialize instruction lookup table */ void ppc32_jit_create_ilt(void) { int i,count; for(i=0,count=0;ppc32_insn_tags[i].emit;i++) count++; ilt = ilt_create("ppc32j",count, (ilt_get_insn_cbk_t)ppc32_jit_get_insn, (ilt_check_cbk_t)ppc32_jit_chk_lo, (ilt_check_cbk_t)ppc32_jit_chk_hi); atexit(destroy_ilt); } /* Initialize the JIT structure */ int ppc32_jit_init(cpu_ppc_t *cpu) { insn_exec_page_t *cp; u_char *cp_addr; u_int area_size; size_t len; int i; /* JIT mapping for executable pages */ len = PPC_JIT_IA_HASH_SIZE * sizeof(void *); cpu->exec_blk_map = m_memalign(4096,len); memset(cpu->exec_blk_map,0,len); /* Physical mapping for executable pages */ len = PPC_JIT_PHYS_HASH_SIZE * sizeof(void *); cpu->exec_phys_map = m_memalign(4096,len); memset(cpu->exec_phys_map,0,len); /* Get area size */ if (!(area_size = cpu->vm->exec_area_size)) area_size = PPC_EXEC_AREA_SIZE; /* Create executable page area */ cpu->exec_page_area_size = area_size * 1048576; cpu->exec_page_area = memzone_map_exec_area(cpu->exec_page_area_size); if (!cpu->exec_page_area) { fprintf(stderr, "ppc32_jit_init: unable to create exec area (size %lu)\n", (u_long)cpu->exec_page_area_size); return(-1); } /* Carve the executable page area */ cpu->exec_page_count = cpu->exec_page_area_size / PPC_JIT_BUFSIZE; cpu->exec_page_array = calloc(cpu->exec_page_count, sizeof(insn_exec_page_t)); if (!cpu->exec_page_array) { fprintf(stderr,"ppc32_jit_init: unable to create exec page array\n"); return(-1); } for(i=0,cp_addr=cpu->exec_page_area;iexec_page_count;i++) { cp = &cpu->exec_page_array[i]; cp->ptr = cp_addr; cp_addr += PPC_JIT_BUFSIZE; cp->next = cpu->exec_page_free_list; cpu->exec_page_free_list = cp; } printf("CPU%u: carved JIT exec zone of %lu Mb into %lu pages of %u Kb.\n", cpu->gen->id, (u_long)(cpu->exec_page_area_size / 1048576), (u_long)cpu->exec_page_count,PPC_JIT_BUFSIZE / 1024); return(0); } /* Flush the JIT */ u_int ppc32_jit_flush(cpu_ppc_t *cpu,u_int threshold) { ppc32_jit_tcb_t *p,*next; m_uint32_t ia_hash; u_int count = 0; if (!threshold) threshold = (u_int)(-1); /* UINT_MAX not defined everywhere */ for(p=cpu->tcb_list;p;p=next) { next = p->next; /* flushing not allowed */ if (p->flags & PPC32_JIT_TCB_FLAG_NO_FLUSH) continue; if (p->acc_count <= threshold) { ia_hash = ppc32_jit_get_ia_hash(p->start_ia); ppc32_jit_tcb_free(cpu,p,TRUE); if (cpu->exec_blk_map[ia_hash] == p) cpu->exec_blk_map[ia_hash] = NULL; count++; } } cpu->compiled_pages -= count; return(count); } /* Shutdown the JIT */ void ppc32_jit_shutdown(cpu_ppc_t *cpu) { ppc32_jit_tcb_t *p,*next; /* Flush the JIT */ ppc32_jit_flush(cpu,0); /* Free the instruction blocks */ for(p=cpu->tcb_free_list;p;p=next) { next = p->next; free(p); } /* Unmap the executable page area */ if (cpu->exec_page_area) memzone_unmap(cpu->exec_page_area,cpu->exec_page_area_size); /* Free the exec page array */ free(cpu->exec_page_array); /* Free JIT block mapping */ free(cpu->exec_blk_map); /* Free physical mapping for executable pages */ free(cpu->exec_phys_map); } /* Allocate an exec page */ static inline insn_exec_page_t *exec_page_alloc(cpu_ppc_t *cpu) { insn_exec_page_t *p; u_int count; /* If the free list is empty, flush JIT */ if (unlikely(!cpu->exec_page_free_list)) { if (cpu->jit_flush_method) { cpu_log(cpu->gen, "JIT","flushing data structures (compiled pages=%u)\n", cpu->compiled_pages); ppc32_jit_flush(cpu,0); } else { count = ppc32_jit_flush(cpu,100); cpu_log(cpu->gen,"JIT","partial JIT flush (count=%u)\n",count); if (!cpu->exec_page_free_list) ppc32_jit_flush(cpu,0); } /* Use both methods alternatively */ cpu->jit_flush_method = 1 - cpu->jit_flush_method; } if (unlikely(!(p = cpu->exec_page_free_list))) return NULL; cpu->exec_page_free_list = p->next; cpu->exec_page_alloc++; return p; } /* Free an exec page and returns it to the pool */ static inline void exec_page_free(cpu_ppc_t *cpu,insn_exec_page_t *p) { if (p) { p->next = cpu->exec_page_free_list; cpu->exec_page_free_list = p; cpu->exec_page_alloc--; } } /* Find the JIT code emitter for the specified PowerPC instruction */ static struct ppc32_insn_tag *insn_tag_find(ppc_insn_t ins) { struct ppc32_insn_tag *tag = NULL; int index; index = ilt_lookup(ilt,ins); tag = ppc32_jit_get_insn(index); return tag; } /* Fetch a PowerPC instruction */ static forced_inline ppc_insn_t insn_fetch(ppc32_jit_tcb_t *b) { return(vmtoh32(b->ppc_code[b->ppc_trans_pos])); } #define DEBUG_HREG 0 /* Show register allocation status */ _unused static void ppc32_jit_show_hreg_status(cpu_ppc_t *cpu) { struct hreg_map *map; printf("PPC32-JIT: reg status for insn '%s'\n",cpu->jit_hreg_seq_name); for(map=cpu->hreg_map_list;map;map=map->next) { switch(map->flags) { case 0: printf(" hreg %d is free, mapped to vreg %d\n", map->hreg,map->vreg); break; case HREG_FLAG_ALLOC_LOCKED: printf(" hreg %d is locked, mapped to vreg %d\n", map->hreg,map->vreg); break; case HREG_FLAG_ALLOC_FORCED: printf(" hreg %d is in forced alloc\n",map->hreg); break; } } } /* Extract an host reg mapping from the register list */ static void ppc32_jit_extract_hreg(cpu_ppc_t *cpu,struct hreg_map *map) { if (map->prev != NULL) map->prev->next = map->next; else cpu->hreg_map_list = map->next; if (map->next != NULL) map->next->prev = map->prev; else cpu->hreg_lru = map->prev; } /* Insert a reg map as head of list (as MRU element) */ void ppc32_jit_insert_hreg_mru(cpu_ppc_t *cpu,struct hreg_map *map) { map->next = cpu->hreg_map_list; map->prev = NULL; if (map->next == NULL) { cpu->hreg_lru = map; } else { map->next->prev = map; } cpu->hreg_map_list = map; } /* Start register allocation sequence */ void ppc32_jit_start_hreg_seq(cpu_ppc_t *cpu,char *insn) { struct hreg_map *map; #if DEBUG_HREG printf("Starting hreg_seq insn='%s'\n",insn); #endif /* Reset the allocation state of all host registers */ for(map=cpu->hreg_map_list;map;map=map->next) map->flags = 0; /* Save the instruction name for debugging/error analysis */ cpu->jit_hreg_seq_name = insn; } /* Close register allocation sequence */ void ppc32_jit_close_hreg_seq(cpu_ppc_t *cpu) { #if DEBUG_HREG ppc32_show_hreg_status(cpu); #endif } /* Find a free host register to use */ static struct hreg_map *ppc32_jit_get_free_hreg(cpu_ppc_t *cpu) { struct hreg_map *map,*oldest_free = NULL; for(map=cpu->hreg_lru;map;map=map->prev) { if ((map->vreg == -1) && (map->flags == 0)) return map; if ((map->flags == 0) && !oldest_free) oldest_free = map; } if (!oldest_free) { fprintf(stderr, "ppc32_get_free_hreg: unable to find free reg for insn %s\n", cpu->jit_hreg_seq_name); } return oldest_free; } /* Allocate an host register */ int ppc32_jit_alloc_hreg(cpu_ppc_t *cpu,int ppc_reg) { struct hreg_map *map; int hreg; /* * If PPC reg is invalid, the caller requested for a temporary register. */ if (ppc_reg == -1) { if ((map = ppc32_jit_get_free_hreg(cpu)) == NULL) return(-1); /* Allocate the register and invalidate its PPC mapping if present */ map->flags = HREG_FLAG_ALLOC_LOCKED; if (map->vreg != -1) { cpu->ppc_reg_map[map->vreg] = -1; map->vreg = -1; } return(map->hreg); } hreg = cpu->ppc_reg_map[ppc_reg]; /* * If the PPC register is already mapped to an host register, re-use this * mapping and put this as MRU mapping. */ if (hreg != -1) { map = &cpu->hreg_map[hreg]; } else { /* * This PPC register has no mapping to host register. Find a free * register. */ if ((map = ppc32_jit_get_free_hreg(cpu)) == NULL) return(-1); /* Remove the old PPC mapping if present */ if (map->vreg != -1) cpu->ppc_reg_map[map->vreg] = -1; /* Establish the new mapping */ cpu->ppc_reg_map[ppc_reg] = map->hreg; map->vreg = ppc_reg; } /* Prevent this register from further allocation in this instruction */ map->flags = HREG_FLAG_ALLOC_LOCKED; ppc32_jit_extract_hreg(cpu,map); ppc32_jit_insert_hreg_mru(cpu,map); return(map->hreg); } /* Force allocation of an host register */ int ppc32_jit_alloc_hreg_forced(cpu_ppc_t *cpu,int hreg) { int ppc_reg; ppc_reg = cpu->hreg_map[hreg].vreg; /* Check that this register is not already allocated */ if (cpu->hreg_map[hreg].flags != 0) { fprintf(stderr,"ppc32_alloc_hreg_forced: trying to force allocation " "of hreg %d (insn %s)\n", hreg,cpu->jit_hreg_seq_name); return(-1); } cpu->hreg_map[hreg].flags = HREG_FLAG_ALLOC_FORCED; cpu->hreg_map[hreg].vreg = -1; if (ppc_reg != -1) cpu->ppc_reg_map[ppc_reg] = -1; return(0); } /* Emit a breakpoint if necessary */ #if BREAKPOINT_ENABLE static void insn_emit_breakpoint(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b) { m_uint32_t ia; int i; ia = b->start_ia+((b->ppc_trans_pos-1)<<2); for(i=0;ibreakpoints[i]) { ppc32_emit_breakpoint(cpu,b); break; } } #endif /* BREAKPOINT_ENABLE */ /* Fetch a PowerPC instruction and emit corresponding translated code */ struct ppc32_insn_tag *ppc32_jit_fetch_and_emit(cpu_ppc_t *cpu, ppc32_jit_tcb_t *block) { struct ppc32_insn_tag *tag; ppc_insn_t code; code = insn_fetch(block); tag = insn_tag_find(code); assert(tag); tag->emit(cpu,block,code); return tag; } /* Add end of JIT block */ _unused static void ppc32_jit_tcb_add_end(ppc32_jit_tcb_t *b) { ppc32_set_ia(&b->jit_ptr,b->start_ia+(b->ppc_trans_pos<<2)); ppc32_jit_tcb_push_epilog(&b->jit_ptr); } /* Record a patch to apply in a compiled block */ int ppc32_jit_tcb_record_patch(ppc32_jit_tcb_t *block,jit_op_t *iop, u_char *jit_ptr,m_uint32_t vaddr) { struct ppc32_jit_patch_table *ipt = block->patch_table; struct ppc32_insn_patch *patch; /* pc must be 32-bit aligned */ if (vaddr & 0x03) { fprintf(stderr, "Block 0x%8.8x: trying to record an invalid IA (0x%8.8x)\n", block->start_ia,vaddr); return(-1); } if (!ipt || (ipt->cur_patch >= PPC32_INSN_PATCH_TABLE_SIZE)) { /* full table or no table, create a new one */ ipt = malloc(sizeof(*ipt)); if (!ipt) { fprintf(stderr,"Block 0x%8.8x: unable to create patch table.\n", block->start_ia); return(-1); } memset(ipt,0,sizeof(*ipt)); ipt->next = block->patch_table; block->patch_table = ipt; } #if DEBUG_BLOCK_PATCH printf("Block 0x%8.8x: recording patch [JIT:%p->ppc:0x%8.8x], " "MTP=%d\n",block->start_ia,jit_ptr,vaddr,block->ppc_trans_pos); #endif patch = &ipt->patches[ipt->cur_patch]; patch->jit_insn = jit_ptr; patch->ppc_ia = vaddr; ipt->cur_patch++; patch->next = iop->arg_ptr; iop->arg_ptr = patch; return(0); } /* Apply patches for a JIT instruction block */ static int ppc32_jit_tcb_apply_patches(cpu_ppc_t *cpu, ppc32_jit_tcb_t *block, jit_op_t *iop) { struct ppc32_insn_patch *patch; u_char *jit_ptr,*jit_dst; u_int pos; for(patch=iop->arg_ptr;patch;patch=patch->next) { jit_ptr = (patch->jit_insn - iop->ob_data) + iop->ob_final; pos = (patch->ppc_ia & PPC32_MIN_PAGE_IMASK) >> 2; jit_dst = block->jit_insn_ptr[pos]; if (jit_dst) { #if DEBUG_BLOCK_PATCH printf("Block 0x%8.8x: applying patch " "[JIT:%p->ppc:0x%8.8x=JIT:%p, ]\n", block->start_ia,patch->jit_insn,patch->ppc_ia,jit_dst); #endif ppc32_jit_tcb_set_patch(jit_ptr,jit_dst); } else { printf("Block 0x%8.8x: null dst for patch!\n",block->start_ia); } } return(0); } /* Free the patch table */ static void ppc32_jit_tcb_free_patches(ppc32_jit_tcb_t *block) { struct ppc32_jit_patch_table *p,*next; for(p=block->patch_table;p;p=next) { next = p->next; free(p); } block->patch_table = NULL; } /* Adjust the JIT buffer if its size is not sufficient */ static int ppc32_jit_tcb_adjust_buffer(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block) { insn_exec_page_t *new_buffer; if ((block->jit_ptr - block->jit_buffer->ptr) <= (PPC_JIT_BUFSIZE - 512)) return(0); #if DEBUG_BLOCK_CHUNK printf("Block 0x%8.8x: adjusting JIT buffer...\n",block->start_ia); #endif if (block->jit_chunk_pos >= PPC_JIT_MAX_CHUNKS) { fprintf(stderr,"Block 0x%8.8x: too many JIT chunks.\n",block->start_ia); return(-1); } if (!(new_buffer = exec_page_alloc(cpu))) return(-1); /* record the new exec page */ block->jit_chunks[block->jit_chunk_pos++] = block->jit_buffer; block->jit_buffer = new_buffer; /* jump to the new exec page (link) */ ppc32_jit_tcb_set_jump(block->jit_ptr,new_buffer->ptr); block->jit_ptr = new_buffer->ptr; return(0); } /* Allocate an instruction block */ static inline ppc32_jit_tcb_t *ppc32_jit_tcb_alloc(cpu_ppc_t *cpu) { ppc32_jit_tcb_t *p; if (cpu->tcb_free_list) { p = cpu->tcb_free_list; cpu->tcb_free_list = p->next; } else { if (!(p = malloc(sizeof(*p)))) return NULL; } memset(p,0,sizeof(*p)); return p; } /* Free the code chunks */ static void ppc32_jit_tcb_free_code_chunks(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block) { int i; /* Free code pages */ for(i=0;ijit_chunks[i]); block->jit_chunks[i] = NULL; } /* Free the current JIT buffer */ exec_page_free(cpu,block->jit_buffer); block->jit_buffer = NULL; } /* Free an instruction block */ void ppc32_jit_tcb_free(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block, int list_removal) { if (block) { if (list_removal) { /* Remove the block from the linked list */ if (block->next) block->next->prev = block->prev; else cpu->tcb_last = block->prev; if (block->prev) block->prev->next = block->next; else cpu->tcb_list = block->next; /* Remove the block from the physical mapping hash table */ if (block->phys_pprev) { if (block->phys_next) block->phys_next->phys_pprev = block->phys_pprev; *(block->phys_pprev) = block->phys_next; block->phys_pprev = NULL; block->phys_next = NULL; } } /* Free the patch tables */ ppc32_jit_tcb_free_patches(block); /* Free code pages */ ppc32_jit_tcb_free_code_chunks(cpu,block); /* Free the PowerPC-to-native code mapping */ free(block->jit_insn_ptr); block->next = cpu->tcb_free_list; cpu->tcb_free_list = block; } } /* Create an instruction block */ static ppc32_jit_tcb_t *ppc32_jit_tcb_create(cpu_ppc_t *cpu,m_uint32_t vaddr) { ppc32_jit_tcb_t *block = NULL; m_uint32_t phys_page; if (unlikely(cpu->translate(cpu,cpu->ia,PPC32_MTS_ICACHE,&phys_page))) return NULL; if (!(block = ppc32_jit_tcb_alloc(cpu))) goto err_block_alloc; block->start_ia = vaddr; block->phys_page = phys_page; block->phys_hash = ppc32_jit_get_phys_hash(phys_page); /* Allocate the first JIT buffer */ if (!(block->jit_buffer = exec_page_alloc(cpu))) goto err_jit_alloc; block->jit_ptr = block->jit_buffer->ptr; block->ppc_code = cpu->mem_op_lookup(cpu,block->start_ia,PPC32_MTS_ICACHE); if (!block->ppc_code) { fprintf(stderr,"%% No memory map for code execution at 0x%8.8x\n", block->start_ia); goto err_lookup; } #if DEBUG_BLOCK_TIMESTAMP block->tm_first_use = block->tm_last_use = jit_jiffies; #endif return block; err_lookup: err_jit_alloc: ppc32_jit_tcb_free(cpu,block,FALSE); err_block_alloc: fprintf(stderr,"%% Unable to create instruction block for vaddr=0x%8.8x\n", vaddr); return NULL; } /* ======================================================================== */ /* Dump a JIT opcode */ static void ppc32_op_dump_opcode(jit_op_t *op) { switch(op->opcode) { case JIT_OP_BRANCH_TARGET: printf("branch_target"); break; case JIT_OP_BRANCH_JUMP: printf("branch_jump"); break; case JIT_OP_EOB: printf("eob"); break; case JIT_OP_LOAD_GPR: printf("load_gpr(%d,$%d,r:%d)", op->param[0],op->param[1],op->param[2]); break; case JIT_OP_STORE_GPR: printf("store_gpr(%d,$%d,r:%d)", op->param[0],op->param[1],op->param[2]); break; case JIT_OP_ALTER_HOST_REG: printf("alter_host_reg(%d)",op->param[0]); break; case JIT_OP_UPDATE_FLAGS: printf("update_flags(%d,%s)", op->param[0],(op->param[1] ? "signed" : "unsigned")); break; case JIT_OP_REQUIRE_FLAGS: printf("require_flags(%d)",op->param[0]); break; case JIT_OP_TRASH_FLAGS: printf("trash_flags(%d)",op->param[0]); break; case JIT_OP_INSN_OUTPUT: printf("insn_out(\"%s\")",op->insn_name); break; case JIT_OP_SET_HOST_REG_IMM32: printf("set_host_reg_imm32(%d,0x%8.8x)",op->param[0],op->param[1]); break; default: printf("op(%u)",op->opcode); } } /* Dump JIT operations (debugging) */ _unused static void ppc32_op_dump(cpu_gen_t *cpu,ppc32_jit_tcb_t *b) { m_uint32_t ia = b->start_ia; jit_op_t *op; int i; printf("PPC32-JIT: dump of page 0x%8.8x\n",ia); for(i=0;ijit_op_array[i];op;op=op->next) { ppc32_op_dump_opcode(op); printf(" "); } printf("\n"); } printf("\n"); } /* PPC register mapping */ typedef struct { int host_reg; jit_op_t *last_store; m_uint32_t last_store_ia; }ppc_reg_map_t; /* Clear register mapping (with PPC register) */ static void ppc32_clear_ppc_reg_map(ppc_reg_map_t *ppc_map,int *host_map, int reg) { int i,hreg; if (reg == JIT_OP_ALL_REGS) { for(i=0;ihost_reg != JIT_OP_INV_REG) && (host_map[map->host_reg] != i)) goto error; } for(i=0;ijit_op_array[i];op;op=op->next) { //ppc32_check_reg_map(ppc_map,host_map); cur_ia = b->start_ia + (i << 2); switch(op->opcode) { /* Clear mapping if end of block or branch target */ case JIT_OP_BRANCH_TARGET: case JIT_OP_EOB: ppc32_clear_ppc_reg_map(ppc_map,host_map,JIT_OP_ALL_REGS); for(j=0;j<8;j++) last_cr_update[j] = NULL; break; /* Branch jump: clear "store" operation status */ case JIT_OP_BRANCH_JUMP: for(j=0;jparam[0]; if (reg != JIT_OP_ALL_REGS) { if (host_map[reg] != JIT_OP_INV_REG) ppc32_clear_ppc_reg_map(ppc_map,host_map,host_map[reg]); } else { ppc32_clear_ppc_reg_map(ppc_map,host_map,JIT_OP_ALL_REGS); } break; /* Save reg mapping and last operation */ case JIT_OP_STORE_GPR: reg = op->param[0]; map = &ppc_map[op->param[1]]; /* clear old mapping */ if (reg != map->host_reg) { ppc32_clear_host_reg_map(ppc_map,host_map,reg); ppc32_clear_ppc_reg_map(ppc_map,host_map,op->param[1]); } /* cancel previous store op for this PPC register */ if (map->last_store) { map->last_store->param[0] = JIT_OP_INV_REG; map->last_store = NULL; } map->host_reg = reg; map->last_store = op; map->last_store_ia = cur_ia; host_map[reg] = op->param[1]; break; /* Load reg: check if can avoid it */ case JIT_OP_LOAD_GPR: reg = op->param[0]; map = &ppc_map[op->param[1]]; if (reg == map->host_reg) { /* Cancel this load */ op->param[0] = JIT_OP_INV_REG; } else { /* clear old mapping */ ppc32_clear_host_reg_map(ppc_map,host_map,reg); ppc32_clear_ppc_reg_map(ppc_map,host_map,op->param[1]); /* Save this reg mapping */ map->host_reg = op->param[0]; map->last_store = NULL; host_map[op->param[0]] = op->param[1]; } break; /* Trash flags */ case JIT_OP_TRASH_FLAGS: for(j=0;j<8;j++) last_cr_update[j] = NULL; break; /* Flags required */ case JIT_OP_REQUIRE_FLAGS: if (op->param[0] != JIT_OP_PPC_ALL_FLAGS) { last_cr_update[op->param[0]] = NULL; } else { for(j=0;j<8;j++) last_cr_update[j] = NULL; } break; /* Update flags */ case JIT_OP_UPDATE_FLAGS: opx = last_cr_update[op->param[0]]; if (opx != NULL) opx->param[0] = JIT_OP_INV_REG; last_cr_update[op->param[0]] = op; break; } } } } /* Generate the JIT code for the specified JIT op list */ static void ppc32_op_gen_list(ppc32_jit_tcb_t *b,int ipos,jit_op_t *op_list, u_char *jit_start) { jit_op_t *op; for(op=op_list;op;op=op->next) { switch(op->opcode) { case JIT_OP_INSN_OUTPUT: ppc32_op_insn_output(b,op); break; case JIT_OP_LOAD_GPR: ppc32_op_load_gpr(b,op); break; case JIT_OP_STORE_GPR: ppc32_op_store_gpr(b,op); break; case JIT_OP_UPDATE_FLAGS: ppc32_op_update_flags(b,op); break; case JIT_OP_BRANCH_TARGET: b->jit_insn_ptr[ipos] = jit_start; break; case JIT_OP_MOVE_HOST_REG: ppc32_op_move_host_reg(b,op); break; case JIT_OP_SET_HOST_REG_IMM32: ppc32_op_set_host_reg_imm32(b,op); break; } } } /* Opcode emit start */ static inline void ppc32_op_emit_start(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b) { cpu_gen_t *c = cpu->gen; jit_op_t *op; if (c->jit_op_array[b->ppc_trans_pos] == NULL) c->jit_op_current = &c->jit_op_array[b->ppc_trans_pos]; else { for(op=c->jit_op_array[b->ppc_trans_pos];op;op=op->next) c->jit_op_current = &op->next; } } /* Generate the JIT code for the current page, given an op list */ static int ppc32_op_gen_page(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b) { struct ppc32_insn_tag *tag; cpu_gen_t *gcpu = cpu->gen; jit_op_t *iop; m_uint32_t cur_ia; u_char *jit_ptr; int i; /* Generate JIT opcodes */ for(b->ppc_trans_pos=0; b->ppc_trans_posppc_trans_pos++) { ppc32_op_emit_start(cpu,b); cur_ia = b->start_ia + (b->ppc_trans_pos << 2); if (ppc32_jit_tcb_get_target_bit(b,cur_ia)) ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_TARGET); #if DEBUG_INSN_PERF_CNT ppc32_inc_perf_counter(cpu); #endif #if BREAKPOINT_ENABLE if (cpu->breakpoints_enabled) insn_emit_breakpoint(cpu,b); #endif if (unlikely(!(tag = ppc32_jit_fetch_and_emit(cpu,b)))) { fprintf(stderr,"ppc32_op_gen_page: unable to fetch instruction.\n"); return(-1); } } /* * Mark the first instruction as a potential target, as well as the * current IA value. */ ppc32_op_emit_branch_target(cpu,b,b->start_ia); ppc32_op_emit_branch_target(cpu,b,cpu->ia); /* Optimize condition register and general registers */ ppc32_op_optimize(gcpu,b); /* Generate JIT code for each instruction in page */ for(i=0;ijit_ptr; /* Generate output code */ ppc32_op_gen_list(b,i,gcpu->jit_op_array[i],jit_ptr); /* Adjust the JIT buffer if its size is not sufficient */ ppc32_jit_tcb_adjust_buffer(cpu,b); } /* Apply patches and free opcodes */ for(i=0;ijit_op_array[i];iop;iop=iop->next) if (iop->opcode == JIT_OP_INSN_OUTPUT) ppc32_jit_tcb_apply_patches(cpu,b,iop); jit_op_free_list(gcpu,gcpu->jit_op_array[i]); gcpu->jit_op_array[i] = NULL; } /* Add end of page (returns to caller) */ ppc32_set_page_jump(cpu,b); /* Free patch tables */ ppc32_jit_tcb_free_patches(b); return(0); } /* ======================================================================== */ /* Compile a PowerPC instruction page */ static inline ppc32_jit_tcb_t *ppc32_jit_tcb_compile(cpu_ppc_t *cpu,m_uint32_t vaddr) { ppc32_jit_tcb_t *block; m_uint32_t page_addr; page_addr = vaddr & ~PPC32_MIN_PAGE_IMASK; if (unlikely(!(block = ppc32_jit_tcb_create(cpu,page_addr)))) { fprintf(stderr,"insn_page_compile: unable to create JIT block.\n"); return NULL; } /* Allocate the array used to convert PPC code ptr to native code ptr */ if (!(block->jit_insn_ptr = calloc(PPC32_INSN_PER_PAGE,sizeof(u_char *)))) { fprintf(stderr,"insn_page_compile: unable to create JIT mappings.\n"); goto error; } /* Compile the page */ if (ppc32_op_gen_page(cpu,block) == -1) { fprintf(stderr,"insn_page_compile: unable to compile page.\n"); goto error; } /* Add the block to the linked list */ block->next = cpu->tcb_list; block->prev = NULL; if (cpu->tcb_list) cpu->tcb_list->prev = block; else cpu->tcb_last = block; cpu->tcb_list = block; /* Add the block to the physical mapping hash table */ block->phys_next = cpu->exec_phys_map[block->phys_hash]; block->phys_pprev = &cpu->exec_phys_map[block->phys_hash]; if (cpu->exec_phys_map[block->phys_hash] != NULL) cpu->exec_phys_map[block->phys_hash]->phys_pprev = &block->phys_next; cpu->exec_phys_map[block->phys_hash] = block; cpu->compiled_pages++; return block; error: ppc32_jit_tcb_free(cpu,block,FALSE); return NULL; } /* Recompile a page */ int ppc32_jit_tcb_recompile(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block) { #if 0 printf("PPC32-JIT: recompiling page 0x%8.8x\n",block->start_ia); #endif /* Free old code chunks */ ppc32_jit_tcb_free_code_chunks(cpu,block); /* Reset code ptr array */ memset(block->jit_insn_ptr,0,PPC32_INSN_PER_PAGE * sizeof(u_char *)); /* Allocate the first JIT buffer */ if (!(block->jit_buffer = exec_page_alloc(cpu))) return(-1); /* Disable flushing to avoid dangling pointers */ block->flags |= PPC32_JIT_TCB_FLAG_NO_FLUSH; /* Recompile the page */ if (ppc32_op_gen_page(cpu,block) == -1) { fprintf(stderr,"insn_page_compile: unable to recompile page.\n"); return(-1); } block->flags &= ~PPC32_JIT_TCB_FLAG_NO_FLUSH; block->target_undef_cnt = 0; return(0); } /* Run a compiled PowerPC instruction block */ static forced_inline void ppc32_jit_tcb_run(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block) { if (unlikely(cpu->ia & 0x03)) { fprintf(stderr,"ppc32_jit_tcb_run: Invalid IA 0x%8.8x.\n",cpu->ia); ppc32_dump_regs(cpu->gen); ppc32_dump_mmu(cpu->gen); cpu_stop(cpu->gen); return; } /* Execute JIT compiled code */ ppc32_jit_tcb_exec(cpu,block); } /* Execute compiled PowerPC code */ void *ppc32_jit_run_cpu(cpu_gen_t *gen) { cpu_ppc_t *cpu = CPU_PPC32(gen); pthread_t timer_irq_thread; ppc32_jit_tcb_t *block; m_uint32_t ia_hash; int timer_irq_check = 0; ppc32_jit_init_hreg_mapping(cpu); if (pthread_create(&timer_irq_thread,NULL,(void *)ppc32_timer_irq_run,cpu)) { fprintf(stderr, "VM '%s': unable to create Timer IRQ thread for CPU%u.\n", cpu->vm->name,gen->id); cpu_stop(cpu->gen); return NULL; } gen->cpu_thread_running = TRUE; cpu_exec_loop_set(gen); start_cpu: gen->idle_count = 0; for(;;) { if (unlikely(gen->state != CPU_STATE_RUNNING)) break; #if DEBUG_BLOCK_PERF_CNT cpu->perf_counter++; #endif /* Handle virtual idle loop */ if (unlikely(cpu->ia == cpu->idle_pc)) { if (++gen->idle_count == gen->idle_max) { cpu_idle_loop(gen); gen->idle_count = 0; } } /* Handle the virtual CPU clock */ if (++timer_irq_check == cpu->timer_irq_check_itv) { timer_irq_check = 0; if (cpu->timer_irq_pending && !cpu->irq_disable && (cpu->msr & PPC32_MSR_EE)) { cpu->timer_irq_armed = 0; cpu->timer_irq_pending--; vm_set_irq(cpu->vm,0); } } /* Check IRQs */ if (unlikely(cpu->irq_check)) ppc32_trigger_irq(cpu); /* Get the JIT block corresponding to IA register */ ia_hash = ppc32_jit_get_ia_hash(cpu->ia); block = cpu->exec_blk_map[ia_hash]; /* No block found, compile the page */ if (unlikely(!block) || unlikely(!ppc32_jit_tcb_match(cpu,block))) { if (block != NULL) { ppc32_jit_tcb_free(cpu,block,TRUE); cpu->exec_blk_map[ia_hash] = NULL; } block = ppc32_jit_tcb_compile(cpu,cpu->ia); if (unlikely(!block)) { fprintf(stderr, "VM '%s': unable to compile block for CPU%u IA=0x%8.8x\n", cpu->vm->name,gen->id,cpu->ia); cpu_stop(gen); break; } cpu->exec_blk_map[ia_hash] = block; } #if DEBUG_BLOCK_TIMESTAMP block->tm_last_use = jit_jiffies++; #endif block->acc_count++; ppc32_jit_tcb_run(cpu,block); } if (!cpu->ia) { cpu_stop(gen); cpu_log(gen,"JIT","IA=0, halting CPU.\n"); } /* Check regularly if the CPU has been restarted */ while(gen->cpu_thread_running) { gen->seq_state++; switch(gen->state) { case CPU_STATE_RUNNING: gen->state = CPU_STATE_RUNNING; goto start_cpu; case CPU_STATE_HALTED: gen->cpu_thread_running = FALSE; pthread_join(timer_irq_thread,NULL); break; } /* CPU is paused */ usleep(200000); } return NULL; } dynamips-0.2.14/stable/ppc32_jit.h000066400000000000000000000246041241034141600166440ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * PPC32 JIT compiler. */ #ifndef __PPC32_JIT_H__ #define __PPC32_JIT_H__ #include "utils.h" #include "sbox.h" /* Size of executable page area (in Mb) */ #ifndef __CYGWIN__ #define PPC_EXEC_AREA_SIZE 64 #else #define PPC_EXEC_AREA_SIZE 16 #endif /* Buffer size for JIT code generation */ #define PPC_JIT_BUFSIZE 32768 /* Maximum number of X86 chunks */ #define PPC_JIT_MAX_CHUNKS 64 /* Size of hash for IA lookup */ #define PPC_JIT_IA_HASH_BITS 17 #define PPC_JIT_IA_HASH_MASK ((1 << PPC_JIT_IA_HASH_BITS) - 1) #define PPC_JIT_IA_HASH_SIZE (1 << PPC_JIT_IA_HASH_BITS) /* Size of hash for physical lookup */ #define PPC_JIT_PHYS_HASH_BITS 16 #define PPC_JIT_PHYS_HASH_MASK ((1 << PPC_JIT_PHYS_HASH_BITS) - 1) #define PPC_JIT_PHYS_HASH_SIZE (1 << PPC_JIT_PHYS_HASH_BITS) #define PPC_JIT_TARGET_BITMAP_INDEX(x) (((x) >> 7) & 0x1F) #define PPC_JIT_TARGET_BITMAP_POS(x) (((x) >> 2) & 0x1F) /* Instruction jump patch */ struct ppc32_insn_patch { struct ppc32_insn_patch *next; u_char *jit_insn; m_uint32_t ppc_ia; }; /* Instruction patch table */ #define PPC32_INSN_PATCH_TABLE_SIZE 32 struct ppc32_jit_patch_table { struct ppc32_jit_patch_table *next; struct ppc32_insn_patch patches[PPC32_INSN_PATCH_TABLE_SIZE]; u_int cur_patch; }; #define PPC32_JIT_TCB_FLAG_NO_FLUSH 0x2 /* No flushing */ /* PPC32 translated code block */ struct ppc32_jit_tcb { u_int flags; m_uint32_t start_ia; u_char **jit_insn_ptr; m_uint64_t acc_count; ppc_insn_t *ppc_code; u_int ppc_trans_pos; u_int jit_chunk_pos; u_char *jit_ptr; insn_exec_page_t *jit_buffer; insn_exec_page_t *jit_chunks[PPC_JIT_MAX_CHUNKS]; struct ppc32_jit_patch_table *patch_table; ppc32_jit_tcb_t *prev,*next; m_uint32_t phys_page; m_uint32_t phys_hash; ppc32_jit_tcb_t **phys_pprev,*phys_next; /* 1024 instructions per page, one bit per instruction */ m_uint32_t target_bitmap[32]; m_uint32_t target_undef_cnt; #if DEBUG_BLOCK_TIMESTAMP m_uint64_t tm_first_use,tm_last_use; #endif }; /* PPC instruction recognition */ struct ppc32_insn_tag { int (*emit)(cpu_ppc_t *cpu,ppc32_jit_tcb_t *,ppc_insn_t); m_uint32_t mask,value; }; /* Mark the specified IA as a target for further recompiling */ static inline void ppc32_jit_tcb_set_target_bit(ppc32_jit_tcb_t *b,m_uint32_t ia) { int index,pos; index = PPC_JIT_TARGET_BITMAP_INDEX(ia); pos = PPC_JIT_TARGET_BITMAP_POS(ia); b->target_bitmap[index] |= 1 << pos; } /* Returns TRUE if the specified IA is in the target bitmap */ static inline int ppc32_jit_tcb_get_target_bit(ppc32_jit_tcb_t *b,m_uint32_t ia) { int index,pos; index = PPC_JIT_TARGET_BITMAP_INDEX(ia); pos = PPC_JIT_TARGET_BITMAP_POS(ia); return(b->target_bitmap[index] & (1 << pos)); } /* Get the JIT instruction pointer in a translated block */ static forced_inline u_char *ppc32_jit_tcb_get_host_ptr(ppc32_jit_tcb_t *b,m_uint32_t vaddr) { m_uint32_t offset; offset = (vaddr & PPC32_MIN_PAGE_IMASK) >> 2; return(b->jit_insn_ptr[offset]); } /* Check if the specified address belongs to the specified block */ static forced_inline int ppc32_jit_tcb_local_addr(ppc32_jit_tcb_t *block,m_uint32_t vaddr, u_char **jit_addr) { if ((vaddr & PPC32_MIN_PAGE_MASK) == block->start_ia) { *jit_addr = ppc32_jit_tcb_get_host_ptr(block,vaddr); return(1); } return(0); } /* Check if PC register matches the compiled block virtual address */ static forced_inline int ppc32_jit_tcb_match(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block) { m_uint32_t vpage; vpage = cpu->ia & ~PPC32_MIN_PAGE_IMASK; return(block->start_ia == vpage); } /* Compute the hash index for the specified IA value */ static forced_inline m_uint32_t ppc32_jit_get_ia_hash(m_uint32_t ia) { m_uint32_t page_hash; page_hash = sbox_u32(ia >> PPC32_MIN_PAGE_SHIFT); return((page_hash ^ (page_hash >> 14)) & PPC_JIT_IA_HASH_MASK); } /* Compute the hash index for the specified physical page */ static forced_inline m_uint32_t ppc32_jit_get_phys_hash(m_uint32_t phys_page) { m_uint32_t page_hash; page_hash = sbox_u32(phys_page); return((page_hash ^ (page_hash >> 12)) & PPC_JIT_PHYS_HASH_MASK); } /* Find the JIT block matching a physical page */ static inline ppc32_jit_tcb_t * ppc32_jit_find_by_phys_page(cpu_ppc_t *cpu,m_uint32_t phys_page) { m_uint32_t page_hash = ppc32_jit_get_phys_hash(phys_page); ppc32_jit_tcb_t *block; for(block=cpu->exec_phys_map[page_hash];block;block=block->phys_next) if (block->phys_page == phys_page) return block; return NULL; } /* ======================================================================== */ /* JIT emit operations (generic). */ /* ======================================================================== */ /* Indicate registers modified by ppc32_update_cr() functions */ extern void ppc32_update_cr_set_altered_hreg(cpu_ppc_t *cpu); /* Set opcode */ static inline void ppc32_op_set(cpu_ppc_t *cpu,jit_op_t *op) { cpu_gen_t *c = cpu->gen; *c->jit_op_current = op; c->jit_op_current = &op->next; } /* EMIT_BASIC_OPCODE */ static inline void ppc32_op_emit_basic_opcode(cpu_ppc_t *cpu,u_int opcode) { jit_op_t *op = jit_op_get(cpu->gen,0,opcode); ppc32_op_set(cpu,op); } /* Trash the specified host register */ static inline void ppc32_op_emit_alter_host_reg(cpu_ppc_t *cpu,int host_reg) { jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_ALTER_HOST_REG); op->param[0] = host_reg; ppc32_op_set(cpu,op); } /* EMIT_INSN_OUTPUT */ static inline jit_op_t * ppc32_op_emit_insn_output(cpu_ppc_t *cpu,u_int size_index,char *insn_name) { jit_op_t *op = jit_op_get(cpu->gen,size_index,JIT_OP_INSN_OUTPUT); op->arg_ptr = NULL; op->insn_name = insn_name; ppc32_op_set(cpu,op); return op; } /* EMIT_LOAD_GPR */ static inline void ppc32_op_emit_load_gpr(cpu_ppc_t *cpu,int host_reg,int ppc_reg) { jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_LOAD_GPR); op->param[0] = host_reg; op->param[1] = ppc_reg; op->param[2] = host_reg; ppc32_op_set(cpu,op); } /* EMIT_STORE_GPR */ static inline void ppc32_op_emit_store_gpr(cpu_ppc_t *cpu,int ppc_reg,int host_reg) { jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_STORE_GPR); op->param[0] = host_reg; op->param[1] = ppc_reg; op->param[2] = host_reg; ppc32_op_set(cpu,op); } /* EMIT_UPDATE_FLAGS */ static inline void ppc32_op_emit_update_flags(cpu_ppc_t *cpu,int field,int is_signed) { jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_UPDATE_FLAGS); op->param[0] = field; op->param[1] = is_signed; ppc32_op_set(cpu,op); ppc32_update_cr_set_altered_hreg(cpu); } /* EMIT_REQUIRE_FLAGS */ static inline void ppc32_op_emit_require_flags(cpu_ppc_t *cpu,int field) { jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_REQUIRE_FLAGS); op->param[0] = field; ppc32_op_set(cpu,op); } /* EMIT_BRANCH_TARGET */ static inline void ppc32_op_emit_branch_target(cpu_ppc_t *cpu, ppc32_jit_tcb_t *b, m_uint32_t ia) { if ((ia & PPC32_MIN_PAGE_MASK) == b->start_ia) { cpu_gen_t *c = cpu->gen; jit_op_t *op = jit_op_get(c,0,JIT_OP_BRANCH_TARGET); u_int pos = (ia & PPC32_MIN_PAGE_IMASK) >> 2; /* Insert in head */ op->next = c->jit_op_array[pos]; c->jit_op_array[pos] = op; } } /* EMIT_SET_HOST_REG_IMM32 */ static inline void ppc32_op_emit_set_host_reg_imm32(cpu_ppc_t *cpu,int reg,m_uint32_t val) { jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_SET_HOST_REG_IMM32); op->param[0] = reg; op->param[1] = val; ppc32_op_set(cpu,op); } /* ======================================================================== */ /* JIT operations with implementations specific to target CPU */ void ppc32_op_insn_output(ppc32_jit_tcb_t *b,jit_op_t *op); void ppc32_op_load_gpr(ppc32_jit_tcb_t *b,jit_op_t *op); void ppc32_op_store_gpr(ppc32_jit_tcb_t *b,jit_op_t *op); void ppc32_op_update_flags(ppc32_jit_tcb_t *b,jit_op_t *op); void ppc32_op_move_host_reg(ppc32_jit_tcb_t *b,jit_op_t *op); void ppc32_op_set_host_reg_imm32(ppc32_jit_tcb_t *b,jit_op_t *op); /* Set the Instruction Address (IA) register */ void ppc32_set_ia(u_char **ptr,m_uint32_t new_ia); /* Jump to the next page */ void ppc32_set_page_jump(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b); /* Increment the number of executed instructions (performance debugging) */ void ppc32_inc_perf_counter(cpu_ppc_t *cpu); /* ======================================================================== */ /* Virtual Breakpoint */ void ppc32_emit_breakpoint(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b); /* Initialize instruction lookup table */ void ppc32_jit_create_ilt(void); /* Initialize the JIT structure */ int ppc32_jit_init(cpu_ppc_t *cpu); /* Flush the JIT */ u_int ppc32_jit_flush(cpu_ppc_t *cpu,u_int threshold); /* Shutdown the JIT */ void ppc32_jit_shutdown(cpu_ppc_t *cpu); /* Fetch a PowerPC instruction and emit corresponding translated code */ struct ppc32_insn_tag *ppc32_jit_fetch_and_emit(cpu_ppc_t *cpu, ppc32_jit_tcb_t *block); /* Record a patch to apply in a compiled block */ int ppc32_jit_tcb_record_patch(ppc32_jit_tcb_t *block,jit_op_t *iop, u_char *jit_ptr,m_uint32_t vaddr); /* Free an instruction block */ void ppc32_jit_tcb_free(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block, int list_removal); /* Check if the specified address belongs to the specified block */ int ppc32_jit_tcb_local_addr(ppc32_jit_tcb_t *block,m_uint32_t vaddr, u_char **jit_addr); /* Recompile a page */ int ppc32_jit_tcb_recompile(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block); /* Execute compiled PowerPC code */ void *ppc32_jit_run_cpu(cpu_gen_t *gen); /* Start register allocation sequence */ void ppc32_jit_start_hreg_seq(cpu_ppc_t *cpu,char *insn); /* Close register allocation sequence */ void ppc32_jit_close_hreg_seq(cpu_ppc_t *cpu); /* Insert a reg map as head of list (as MRU element) */ void ppc32_jit_insert_hreg_mru(cpu_ppc_t *cpu,struct hreg_map *map); /* Allocate an host register */ int ppc32_jit_alloc_hreg(cpu_ppc_t *cpu,int ppc_reg); /* Force allocation of an host register */ int ppc32_jit_alloc_hreg_forced(cpu_ppc_t *cpu,int hreg); /* Initialize register mapping */ void ppc32_jit_init_hreg_mapping(cpu_ppc_t *cpu); #endif dynamips-0.2.14/stable/ppc32_mem.c000066400000000000000000000674651241034141600166430ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * PowerPC MMU. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "ppc32_jit.h" #define DEBUG_ICBI 0 /* Memory access with special access mask */ void ppc32_access_special(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int cid, m_uint32_t mask,u_int op_code,u_int op_type, u_int op_size,m_uint64_t *data) { switch(mask) { case MTS_ACC_T: if (op_code != PPC_MEMOP_LOOKUP) { #if DEBUG_MTS_ACC_T cpu_log(cpu->gen, "MTS","MMU exception for address 0x%8.8x at ia=0x%8.8x " "(%s access, size=%u)\n", vaddr,cpu->ia,(op_type == MTS_READ) ? "read":"write",op_size); //ppc32_dump_regs(cpu->gen); #if MEMLOG_ENABLE memlog_dump(cpu->gen); #endif #endif if (cid == PPC32_MTS_DCACHE) { cpu->dsisr = PPC32_DSISR_NOTRANS; if (op_type == MTS_WRITE) cpu->dsisr |= PPC32_DSISR_STORE; cpu->dar = vaddr; ppc32_trigger_exception(cpu,PPC32_EXC_DSI); cpu_exec_loop_enter(cpu->gen); } } break; case MTS_ACC_U: if (op_type == MTS_READ) *data = 0; if (cpu->gen->undef_mem_handler != NULL) { if (cpu->gen->undef_mem_handler(cpu->gen,(m_uint64_t)vaddr, op_size,op_type,data)) return; } #if DEBUG_MTS_ACC_U if (op_type == MTS_READ) cpu_log(cpu->gen, "MTS","read access to undefined address 0x%8.8x at " "ia=0x%8.8x (size=%u)\n",vaddr,cpu->ia,op_size); else cpu_log(cpu->gen, "MTS","write access to undefined address 0x%8.8x at " "ia=0x%8.8x, value=0x%8.8llx (size=%u)\n", vaddr,cpu->ia,*data,op_size); #endif break; } } /* Initialize the MTS subsystem for the specified CPU */ int ppc32_mem_init(cpu_ppc_t *cpu) { size_t len; /* Initialize the cache entries to 0 (empty) */ len = MTS32_HASH_SIZE * sizeof(mts32_entry_t); if (!(cpu->mts_cache[PPC32_MTS_ICACHE] = malloc(len))) return(-1); if (!(cpu->mts_cache[PPC32_MTS_DCACHE] = malloc(len))) return(-1); memset(cpu->mts_cache[PPC32_MTS_ICACHE],0xFF,len); memset(cpu->mts_cache[PPC32_MTS_DCACHE],0xFF,len); cpu->mts_lookups = 0; cpu->mts_misses = 0; return(0); } /* Free memory used by MTS */ void ppc32_mem_shutdown(cpu_ppc_t *cpu) { if (cpu != NULL) { /* Free the caches themselves */ free(cpu->mts_cache[PPC32_MTS_ICACHE]); free(cpu->mts_cache[PPC32_MTS_DCACHE]); cpu->mts_cache[PPC32_MTS_ICACHE] = NULL; cpu->mts_cache[PPC32_MTS_DCACHE] = NULL; } } /* Show MTS detailed information (debugging only!) */ void ppc32_mem_show_stats(cpu_gen_t *gen_cpu) { cpu_ppc_t *cpu = CPU_PPC32(gen_cpu); #if DEBUG_MTS_MAP_VIRT mts32_entry_t *entry; u_int i,count; #endif printf("\nCPU%u: MTS statistics:\n",cpu->gen->id); #if DEBUG_MTS_MAP_VIRT printf("Instruction cache:\n"); /* Valid hash entries for Instruction Cache */ for(count=0,i=0;imts_cache[PPC32_MTS_ICACHE][i]; if (!(entry->gvpa & MTS_INV_ENTRY_MASK)) { printf(" %4u: vaddr=0x%8.8x, paddr=0x%8.8x, hpa=%p\n", i,entry->gvpa,entry->gppa,(void *)entry->hpa); count++; } } printf(" %u/%u valid hash entries for icache.\n",count,MTS32_HASH_SIZE); printf("Data cache:\n"); /* Valid hash entries for Instruction Cache */ for(count=0,i=0;imts_cache[PPC32_MTS_DCACHE][i]; if (!(entry->gvpa & MTS_INV_ENTRY_MASK)) { printf(" %4u: vaddr=0x%8.8x, paddr=0x%8.8x, hpa=%p\n", i,entry->gvpa,entry->gppa,(void *)entry->hpa); count++; } } printf(" %u/%u valid hash entries for dcache.\n",count,MTS32_HASH_SIZE); #endif printf("\n Total lookups: %llu, misses: %llu, efficiency: %g%%\n", cpu->mts_lookups, cpu->mts_misses, 100 - ((double)(cpu->mts_misses*100)/ (double)cpu->mts_lookups)); } /* Invalidate the MTS caches (instruction and data) */ void ppc32_mem_invalidate_cache(cpu_ppc_t *cpu) { size_t len; len = MTS32_HASH_SIZE * sizeof(mts32_entry_t); memset(cpu->mts_cache[PPC32_MTS_ICACHE],0xFF,len); memset(cpu->mts_cache[PPC32_MTS_DCACHE],0xFF,len); } /* * MTS mapping. * * It is NOT inlined since it triggers a GCC bug on my config (x86, GCC 3.3.5) */ static no_inline struct mts32_entry * ppc32_mem_map(cpu_ppc_t *cpu,u_int op_type,mts_map_t *map, mts32_entry_t *entry,mts32_entry_t *alt_entry) { ppc32_jit_tcb_t *block; struct vdevice *dev; m_uint32_t offset; m_iptr_t host_ptr; m_uint32_t exec_flag = 0; int cow; if (!(dev = dev_lookup(cpu->vm,map->paddr+map->offset,map->cached))) return NULL; if (cpu->exec_phys_map) { block = ppc32_jit_find_by_phys_page(cpu,map->paddr >> VM_PAGE_SHIFT); if (block) exec_flag = MTS_FLAG_EXEC; } if (dev->flags & VDEVICE_FLAG_SPARSE) { host_ptr = dev_sparse_get_host_addr(cpu->vm,dev,map->paddr,op_type,&cow); entry->gvpa = map->vaddr; entry->gppa = map->paddr; entry->hpa = host_ptr; entry->flags = (cow) ? MTS_FLAG_COW : 0; entry->flags |= exec_flag; return entry; } if (!dev->host_addr || (dev->flags & VDEVICE_FLAG_NO_MTS_MMAP)) { offset = (map->paddr + map->offset) - dev->phys_addr; /* device entries are never stored in virtual TLB */ alt_entry->gppa = dev->id; alt_entry->hpa = offset; alt_entry->flags = MTS_FLAG_DEV; return alt_entry; } entry->gvpa = map->vaddr; entry->gppa = map->paddr; entry->hpa = dev->host_addr + (map->paddr - dev->phys_addr); entry->flags = exec_flag; return entry; } /* BAT lookup */ static forced_inline int ppc32_bat_lookup(cpu_ppc_t *cpu,m_uint32_t vaddr, u_int cid,mts_map_t *map) { m_uint32_t bepi,mask,bl,pr,ubat; int i; pr = (cpu->msr & PPC32_MSR_PR) >> PPC32_MSR_PR_SHIFT; pr = ((~pr << 1) | pr) & 0x03; for(i=0;ibat[cid][i].reg[0]; if (!(ubat & pr)) continue; //bl = (ubat & PPC32_UBAT_BL_MASK) >> PPC32_UBAT_BL_SHIFT; bl = (ubat & PPC32_UBAT_XBL_MASK) >> PPC32_UBAT_XBL_SHIFT; mask = ~bl << PPC32_BAT_ADDR_SHIFT; bepi = ubat & PPC32_UBAT_BEPI_MASK; if (bepi == (vaddr & mask)) { map->vaddr = vaddr & PPC32_MIN_PAGE_MASK; map->paddr = cpu->bat[cid][i].reg[1] & PPC32_LBAT_BRPN_MASK; map->paddr += map->vaddr - bepi; map->offset = vaddr & PPC32_MIN_PAGE_IMASK; map->cached = FALSE; return(TRUE); } } return(FALSE); } /* Memory slow lookup */ static mts32_entry_t *ppc32_slow_lookup(cpu_ppc_t *cpu,m_uint32_t vaddr, u_int cid,u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data, mts32_entry_t *alt_entry) { m_uint32_t hash_bucket,segment,vsid; m_uint32_t hash,tmp,pteg_offset,pte_key,key,pte2; mts32_entry_t *entry; m_uint8_t *pte_haddr; m_uint64_t paddr; mts_map_t map; int i; #if DEBUG_MTS_STATS cpu->mts_misses++; #endif hash_bucket = MTS32_HASH(vaddr); entry = &cpu->mts_cache[cid][hash_bucket]; /* No translation - cover the 4GB space */ if (((cid == PPC32_MTS_ICACHE) && !(cpu->msr & PPC32_MSR_IR)) || ((cid == PPC32_MTS_DCACHE) && !(cpu->msr & PPC32_MSR_DR))) { map.vaddr = vaddr & PPC32_MIN_PAGE_MASK; map.paddr = vaddr & PPC32_MIN_PAGE_MASK; map.offset = vaddr & PPC32_MIN_PAGE_IMASK; map.cached = FALSE; if (!(entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return entry; } /* Walk through the BAT registers */ if (ppc32_bat_lookup(cpu,vaddr,cid,&map)) { if (!(entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return entry; } if (unlikely(!cpu->sdr1)) goto no_pte; /* Get the virtual segment identifier */ segment = vaddr >> 28; vsid = cpu->sr[segment] & PPC32_SD_VSID_MASK; /* Compute the first hash value */ hash = (vaddr >> PPC32_MIN_PAGE_SHIFT) & 0xFFFF; hash ^= vsid; hash &= 0x7FFFFF; tmp = (hash >> 10) & (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK); pteg_offset = (hash & 0x3FF) << 6; pteg_offset |= tmp << 16; pte_haddr = cpu->sdr1_hptr + pteg_offset; pte_key = 0x80000000 | (vsid << 7); pte_key |= (vaddr >> 22) & 0x3F; for(i=0;i<8;i++,pte_haddr+=PPC32_PTE_SIZE) { key = vmtoh32(*(m_uint32_t *)pte_haddr); if (key == pte_key) goto pte_lookup_done; } /* Secondary hash value */ hash = (~hash) & 0x7FFFFF; tmp = (hash >> 10) & (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK); pteg_offset = (hash & 0x3FF) << 6; pteg_offset |= tmp << 16; pte_haddr = cpu->sdr1_hptr + pteg_offset; pte_key = 0x80000040 | (vsid << 7); pte_key |= (vaddr >> 22) & 0x3F; for(i=0;i<8;i++,pte_haddr+=PPC32_PTE_SIZE) { key = vmtoh32(*(m_uint32_t *)pte_haddr); if (key == pte_key) goto pte_lookup_done; } no_pte: /* No matching PTE for this virtual address */ ppc32_access_special(cpu,vaddr,cid,MTS_ACC_T,op_code,op_type,op_size,data); return NULL; pte_lookup_done: pte2 = vmtoh32(*(m_uint32_t *)(pte_haddr + sizeof(m_uint32_t))); paddr = pte2 & PPC32_PTEL_RPN_MASK; paddr |= (pte2 & PPC32_PTEL_XPN_MASK) << (33 - PPC32_PTEL_XPN_SHIFT); paddr |= (pte2 & PPC32_PTEL_X_MASK) << (32 - PPC32_PTEL_X_SHIFT); map.vaddr = vaddr & ~PPC32_MIN_PAGE_IMASK; map.paddr = paddr; map.offset = vaddr & PPC32_MIN_PAGE_IMASK; map.cached = FALSE; if ((entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) return entry; err_undef: ppc32_access_special(cpu,vaddr,cid,MTS_ACC_U,op_code,op_type,op_size,data); return NULL; } /* Memory access */ static inline void *ppc32_mem_access(cpu_ppc_t *cpu,m_uint32_t vaddr, u_int cid,u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data) { mts32_entry_t *entry,alt_entry; ppc32_jit_tcb_t *block; m_uint32_t hash_bucket; m_uint32_t phys_page; m_uint32_t ia_hash; m_iptr_t haddr; u_int dev_id; int cow; #if MEMLOG_ENABLE /* Record the memory access */ memlog_rec_access(cpu->gen,vaddr,*data,op_size,op_type); #endif hash_bucket = MTS32_HASH(vaddr); entry = &cpu->mts_cache[cid][hash_bucket]; #if DEBUG_MTS_STATS cpu->mts_lookups++; #endif /* Copy-On-Write for sparse device ? */ cow = (op_type == MTS_WRITE) && (entry->flags & MTS_FLAG_COW); /* Slow lookup if nothing found in cache */ if (unlikely(((vaddr & PPC32_MIN_PAGE_MASK) != entry->gvpa) || cow)) { entry = cpu->mts_slow_lookup(cpu,vaddr,cid,op_code,op_size,op_type, data,&alt_entry); if (!entry) return NULL; if (entry->flags & MTS_FLAG_DEV) { dev_id = entry->gppa; haddr = entry->hpa; return(dev_access_fast(cpu->gen,dev_id,haddr,op_size,op_type,data)); } } /* Invalidate JIT code for written pages */ if ((op_type == MTS_WRITE) && (entry->flags & MTS_FLAG_EXEC)) { if (cpu->exec_phys_map) { phys_page = entry->gppa >> VM_PAGE_SHIFT; if (vaddr >= PPC32_EXC_SYS_RST) { block = ppc32_jit_find_by_phys_page(cpu,phys_page); if (block != NULL) { //printf("Invalidation of block 0x%8.8x\n",block->start_ia); ia_hash = ppc32_jit_get_ia_hash(block->start_ia); ppc32_jit_tcb_free(cpu,block,TRUE); if (cpu->exec_blk_map[ia_hash] == block) cpu->exec_blk_map[ia_hash] = NULL; entry->flags &= ~MTS_FLAG_EXEC; } } } } /* Raw memory access */ haddr = entry->hpa + (vaddr & PPC32_MIN_PAGE_IMASK); #if MEMLOG_ENABLE memlog_update_read(cpu->gen,haddr); #endif return((void *)haddr); } /* Memory data access */ #define PPC32_MEM_DACCESS(cpu,vaddr,op_code,op_size,op_type,data) \ ppc32_mem_access((cpu),(vaddr),PPC32_MTS_DCACHE,(op_code),(op_size),\ (op_type),(data)) /* Virtual address to physical page translation */ static fastcall int ppc32_translate(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int cid, m_uint32_t *phys_page) { mts32_entry_t *entry,alt_entry; m_uint32_t hash_bucket; m_uint64_t data = 0; hash_bucket = MTS32_HASH(vaddr); entry = &cpu->mts_cache[cid][hash_bucket]; /* Slow lookup if nothing found in cache */ if (unlikely(((m_uint32_t)vaddr & PPC32_MIN_PAGE_MASK) != entry->gvpa)) { entry = cpu->mts_slow_lookup(cpu,vaddr,cid,PPC_MEMOP_LOOKUP,4,MTS_READ, &data,&alt_entry); if (!entry) return(-1); } *phys_page = entry->gppa >> PPC32_MIN_PAGE_SHIFT; return(0); } /* Virtual address lookup */ static void *ppc32_mem_lookup(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int cid) { m_uint64_t data; return(ppc32_mem_access(cpu,vaddr,cid,PPC_MEMOP_LOOKUP,4,MTS_READ,&data)); } /* Set a BAT register */ int ppc32_set_bat(cpu_ppc_t *cpu,struct ppc32_bat_prog *bp) { struct ppc32_bat_reg *bat; if ((bp->type != PPC32_IBAT_IDX) && (bp->type != PPC32_DBAT_IDX)) return(-1); if (bp->index >= PPC32_BAT_NR) return(-1); bat = &cpu->bat[bp->type][bp->index]; bat->reg[0] = bp->hi; bat->reg[1] = bp->lo; return(0); } /* Load BAT registers from a BAT array */ void ppc32_load_bat_array(cpu_ppc_t *cpu,struct ppc32_bat_prog *bp) { while(bp->index != -1) { ppc32_set_bat(cpu,bp); bp++; } } /* Get the host address for SDR1 */ int ppc32_set_sdr1(cpu_ppc_t *cpu,m_uint32_t sdr1) { struct vdevice *dev; m_uint64_t pt_addr; cpu->sdr1 = sdr1; pt_addr = sdr1 & PPC32_SDR1_HTABORG_MASK; pt_addr |= ((m_uint64_t)(sdr1 & PPC32_SDR1_HTABEXT_MASK) << 20); if (!(dev = dev_lookup(cpu->vm,pt_addr,TRUE))) { fprintf(stderr,"ppc32_set_sdr1: unable to find haddr for SDR1=0x%8.8x\n", sdr1); return(-1); } cpu->sdr1_hptr = (char *)dev->host_addr + (pt_addr - dev->phys_addr); return(0); } /* Initialize the page table */ int ppc32_init_page_table(cpu_ppc_t *cpu) { m_uint32_t pt_size; if (!cpu->sdr1_hptr) return(-1); pt_size = (1 + (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK)) << 16; memset(cpu->sdr1_hptr,0,pt_size); return(0); } /* Map a page */ int ppc32_map_page(cpu_ppc_t *cpu,u_int vsid,m_uint32_t vaddr,m_uint64_t paddr, u_int wimg,u_int pp) { m_uint32_t hash,tmp,pteg_offset,key; m_uint8_t *pte_haddr; int i; /* Compute the first hash value */ hash = (vaddr >> PPC32_MIN_PAGE_SHIFT) & 0xFFFF; hash ^= vsid; hash &= 0x7FFFFF; tmp = (hash >> 10) & (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK); pteg_offset = (hash & 0x3FF) << 6; pteg_offset |= tmp << 16; pte_haddr = cpu->sdr1_hptr + pteg_offset; for(i=0;i<8;i++,pte_haddr+=PPC32_PTE_SIZE) { key = vmtoh32(*(m_uint32_t *)pte_haddr); if (!(key & PPC32_PTEU_V)) { hash = 0; goto free_pte_found; } } /* Secondary hash value */ hash = (~hash) & 0x7FFFFF; tmp = (hash >> 10) & (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK); pteg_offset = (hash & 0x3FF) << 6; pteg_offset |= tmp << 16; pte_haddr = cpu->sdr1_hptr + pteg_offset; for(i=0;i<8;i++,pte_haddr+=PPC32_PTE_SIZE) { key = vmtoh32(*(m_uint32_t *)pte_haddr); if (!(key & PPC32_PTEU_V)) { hash = PPC32_PTEU_H; goto free_pte_found; } } /* No free PTE found */ return(-1); free_pte_found: tmp = PPC32_PTEU_V | (vsid << PPC32_PTEU_VSID_SHIFT) | hash; tmp |= (vaddr >> 22) & 0x3F; *(m_uint32_t *)pte_haddr = htovm32(tmp); tmp = vaddr & PPC32_PTEL_RPN_MASK; tmp |= (vaddr >> (32 - PPC32_PTEL_X_SHIFT)) & PPC32_PTEL_X_MASK; tmp |= (vaddr >> (33 - PPC32_PTEL_XPN_SHIFT)) & PPC32_PTEL_XPN_MASK; tmp |= (wimg << PPC32_PTEL_WIMG_SHIFT) + pp; *(m_uint32_t *)(pte_haddr+sizeof(m_uint32_t)) = htovm32(tmp); return(0); } /* Map a memory zone */ int ppc32_map_zone(cpu_ppc_t *cpu,u_int vsid,m_uint32_t vaddr,m_uint64_t paddr, m_uint32_t size,u_int wimg,u_int pp) { while(size > 0) { if (ppc32_map_page(cpu,vsid,vaddr,paddr,wimg,pp) == -1) return(-1); size -= PPC32_MIN_PAGE_SIZE; vaddr += PPC32_MIN_PAGE_SIZE; paddr += PPC32_MIN_PAGE_SIZE; } return(0); } /* PowerPC 405 TLB masks */ static m_uint32_t ppc405_tlb_masks[8] = { 0xFFFFFC00, 0xFFFFF000, 0xFFFFC000, 0xFFFF0000, 0xFFFC0000, 0xFFF00000, 0xFFC00000, 0xFF000000, }; /* PowerPC 405 slow lookup */ static mts32_entry_t *ppc405_slow_lookup(cpu_ppc_t *cpu,m_uint32_t vaddr, u_int cid,u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data, mts32_entry_t *alt_entry) { struct ppc405_tlb_entry *tlb_entry; m_uint32_t hash_bucket,mask; m_uint32_t page_size; mts32_entry_t *entry; mts_map_t map; int i; #if DEBUG_MTS_STATS cpu->mts_misses++; #endif hash_bucket = MTS32_HASH(vaddr); entry = &cpu->mts_cache[cid][hash_bucket]; /* No translation - cover the 4GB space */ if (((cid == PPC32_MTS_ICACHE) && !(cpu->msr & PPC32_MSR_IR)) || ((cid == PPC32_MTS_DCACHE) && !(cpu->msr & PPC32_MSR_DR))) { map.vaddr = vaddr & PPC32_MIN_PAGE_MASK; map.paddr = vaddr & PPC32_MIN_PAGE_MASK; map.cached = FALSE; if (!(entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return entry; } /* Walk through the unified TLB */ for(i=0;ippc405_tlb[i]; /* We want a valid entry with TID = PID */ if (!(tlb_entry->tlb_hi & PPC405_TLBHI_V) || (tlb_entry->tid != cpu->ppc405_pid)) continue; /* Get the address mask corresponding to this entry */ page_size = tlb_entry->tlb_hi & PPC405_TLBHI_SIZE_MASK; page_size >>= PPC405_TLBHI_SIZE_SHIFT; mask = ppc405_tlb_masks[page_size]; /* Matching entry ? */ if ((vaddr & mask) == (tlb_entry->tlb_hi & mask)) { map.vaddr = vaddr & mask; map.paddr = tlb_entry->tlb_lo & mask; map.cached = FALSE; if (!(entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return entry; } } /* No matching TLB entry for this virtual address */ ppc32_access_special(cpu,vaddr,cid,MTS_ACC_T,op_code,op_type,op_size,data); return NULL; err_undef: ppc32_access_special(cpu,vaddr,cid,MTS_ACC_U,op_code,op_type,op_size,data); return NULL; } /* Dump a PowerPC 405 TLB entry */ static void ppc405_dump_tlb_entry(cpu_ppc_t *cpu,u_int index) { struct ppc405_tlb_entry *entry; entry = &cpu->ppc405_tlb[index]; printf(" %2d: hi=0x%8.8x lo=0x%8.8x tid=0x%2.2x\n", index,entry->tlb_hi,entry->tlb_lo,entry->tid); } /* Dump the PowerPC 405 TLB */ static void ppc405_dump_tlb(cpu_gen_t *cpu) { cpu_ppc_t *pcpu = CPU_PPC32(cpu); u_int i; for(i=0;igpr[reg] = data & 0xFF; } /* LHZ: Load Half-Word Zero */ fastcall void ppc32_lhz(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LHZ,2,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh16(*(m_uint16_t *)haddr); cpu->gpr[reg] = data & 0xFFFF; } /* LWZ: Load Word Zero */ fastcall void ppc32_lwz(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LWZ,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); cpu->gpr[reg] = data; } /* LWBR: Load Word Byte Reverse */ fastcall void ppc32_lwbr(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LWBR,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); cpu->gpr[reg] = swap32(data); } /* LHA: Load Half-Word Algebraic */ fastcall void ppc32_lha(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LHZ,2,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh16(*(m_uint16_t *)haddr); cpu->gpr[reg] = sign_extend_32(data,16); } /* STB: Store Byte */ fastcall void ppc32_stb(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->gpr[reg] & 0xff; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STB,1,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint8_t *)haddr = data; } /* STH: Store Half-Word */ fastcall void ppc32_sth(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->gpr[reg] & 0xffff; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STH,2,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint16_t *)haddr = htovm16(data); } /* STW: Store Word */ fastcall void ppc32_stw(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->gpr[reg]; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STW,4,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint32_t *)haddr = htovm32(data); } /* STWBR: Store Word Byte Reversed */ fastcall void ppc32_stwbr(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = swap32(cpu->gpr[reg]); haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STWBR,4,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint32_t *)haddr = htovm32(data); } /* LSW: Load String Word */ fastcall void ppc32_lsw(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LSW,1,MTS_READ,&data); if (likely(haddr != NULL)) data = *(m_uint8_t *)haddr; cpu->gpr[reg] |= (data & 0xFF) << (24 - cpu->sw_pos); } /* STW: Store String Word */ fastcall void ppc32_stsw(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = (cpu->gpr[reg] >> (24 - cpu->sw_pos)) & 0xFF; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STSW,1,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint8_t *)haddr = data; } /* LFD: Load Floating-Point Double */ fastcall void ppc32_lfd(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LWZ,8,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); cpu->fpu.reg[reg] = data; } /* STFD: Store Floating-Point Double */ fastcall void ppc32_stfd(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->fpu.reg[reg]; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STW,8,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint64_t *)haddr = htovm64(data); } /* ICBI: Instruction Cache Block Invalidate */ fastcall void ppc32_icbi(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int op) { ppc32_jit_tcb_t *block; m_uint32_t phys_page; #if DEBUG_ICBI cpu_log(cpu->gen,"MTS","ICBI: ia=0x%8.8x, vaddr=0x%8.8x\n",cpu->ia,vaddr); #endif if (!cpu->translate(cpu,vaddr,PPC32_MTS_ICACHE,&phys_page)) { if (cpu->exec_phys_map) { block = ppc32_jit_find_by_phys_page(cpu,phys_page); if (block && (block->start_ia == (vaddr & PPC32_MIN_PAGE_MASK))) { #if DEBUG_ICBI cpu_log(cpu->gen,"MTS", "ICBI: removing compiled page at 0x%8.8x, pc=0x%8.8x\n", block->start_ia,cpu->ia); #endif ppc32_jit_tcb_free(cpu,block,TRUE); cpu->exec_blk_map[ppc32_jit_get_ia_hash(vaddr)] = NULL; } else { #if DEBUG_ICBI cpu_log(cpu->gen,"MTS", "ICBI: trying to remove page 0x%llx with pc=0x%llx\n", block->start_ia,cpu->ia); #endif } } } } /* ======================================================================== */ /* Get a BAT register pointer given a SPR index */ static inline m_uint32_t *ppc32_get_bat_spr_ptr(cpu_ppc_t *cpu,u_int spr) { m_uint32_t spr_cat,cid,index; spr_cat = spr >> 5; if ((spr_cat != 0x10) && (spr_cat != 0x11)) return NULL; cid = (spr >> 3) & 0x1; index = (spr >> 1) & 0x3; if (spr & 0x20) index += 4; //printf("GET_BAT_SPR: SPR=%u => cid=%u, index=%u\n",spr,cid,index); return(&cpu->bat[cid][index].reg[spr & 0x1]); } /* Get a BAT SPR */ m_uint32_t ppc32_get_bat_spr(cpu_ppc_t *cpu,u_int spr) { m_uint32_t *p; if (!(p = ppc32_get_bat_spr_ptr(cpu,spr))) return(0); return(*p); } /* Set a BAT SPR */ void ppc32_set_bat_spr(cpu_ppc_t *cpu,u_int spr,m_uint32_t val) { m_uint32_t *p; if ((p = ppc32_get_bat_spr_ptr(cpu,spr))) { *p = val; ppc32_mem_invalidate_cache(cpu); } } /* ======================================================================== */ /* Rebuild MTS data structures */ static void ppc32_mem_rebuild_mts(cpu_gen_t *gen_cpu) { ppc32_mem_invalidate_cache(CPU_PPC32(gen_cpu)); } /* Initialize memory access vectors */ void ppc32_init_memop_vectors(cpu_ppc_t *cpu) { /* MTS slow lookup */ cpu->mts_slow_lookup = ppc32_slow_lookup; /* MTS rebuild */ cpu->gen->mts_rebuild = ppc32_mem_rebuild_mts; /* MTS statistics */ cpu->gen->mts_show_stats = ppc32_mem_show_stats; /* Memory lookup operation */ cpu->mem_op_lookup = ppc32_mem_lookup; /* Translation operation */ cpu->translate = ppc32_translate; /* Load Operations */ cpu->mem_op_fn[PPC_MEMOP_LBZ] = ppc32_lbz; cpu->mem_op_fn[PPC_MEMOP_LHZ] = ppc32_lhz; cpu->mem_op_fn[PPC_MEMOP_LWZ] = ppc32_lwz; /* Load Operation with sign-extension */ cpu->mem_op_fn[PPC_MEMOP_LHA] = ppc32_lha; /* Store Operations */ cpu->mem_op_fn[PPC_MEMOP_STB] = ppc32_stb; cpu->mem_op_fn[PPC_MEMOP_STH] = ppc32_sth; cpu->mem_op_fn[PPC_MEMOP_STW] = ppc32_stw; /* Byte-Reversed operations */ cpu->mem_op_fn[PPC_MEMOP_LWBR] = ppc32_lwbr; cpu->mem_op_fn[PPC_MEMOP_STWBR] = ppc32_stwbr; /* String operations */ cpu->mem_op_fn[PPC_MEMOP_LSW] = ppc32_lsw; cpu->mem_op_fn[PPC_MEMOP_STSW] = ppc32_stsw; /* FPU operations */ cpu->mem_op_fn[PPC_MEMOP_LFD] = ppc32_lfd; cpu->mem_op_fn[PPC_MEMOP_STFD] = ppc32_stfd; /* ICBI - Instruction Cache Block Invalidate */ cpu->mem_op_fn[PPC_MEMOP_ICBI] = ppc32_icbi; } /* Restart the memory subsystem */ int ppc32_mem_restart(cpu_ppc_t *cpu) { m_uint32_t family; ppc32_mem_shutdown(cpu); ppc32_mem_init(cpu); ppc32_init_memop_vectors(cpu); /* Override the MTS lookup vector depending on the cpu type */ family = cpu->pvr & 0xFFFF0000; if (family == PPC32_PVR_405) { cpu->mts_slow_lookup = ppc405_slow_lookup; cpu->gen->mmu_dump = ppc405_dump_tlb; cpu->gen->mmu_raw_dump = ppc405_dump_tlb; } return(0); } dynamips-0.2.14/stable/ppc32_microcode000077500000000000000000002426041241034141600176010ustar00rootroot00000000000000ELFÿð4DD4 (@@@ÿðÿðCÀCÀ8` <€ÿð8„ 8 tH.ñ8`<€ÿð8„8 H.Ý8` <€ÿð8„ 8 H.ÉH7-`LdLd}QC¦=@9J@‘ ‘*}B¦‘ }(¦‘* }¦‘ } &‘*=ÿð94<}¦N€!=@9J@ }ñ *}!¦ }¦ *JLd‹Ä›Ã8c8„8¥ÿÿ,@‚ÿèN€ |i¦8`8€8 |ñ |¦N€!N€ ˆ|uM‚ |ix˜ 9)Œ|u@‚ÿðN€ | +y| ¦L ˆ9`˜ˆ/€@¾N€ | ®| ®}+ ®/‰Mž 9kBÿèN€ ˆ|ix8`/€Mž 8c|H®/€@žÿôN€ |¦”!ÿÀ“¡4|}x“Á8|ž#x“0“á<Dˆ‘!(9!H|u8˜8¡Áá ‘$‘A,‘! A‚Œ= ÿð;‰B HT„>£ëxH}Œ|uA‚h/„%@žÿäŒ|t/€sAž|Ap/€%Až¨/€c@žÿ̉a+‹@œ´U`:!9k™a} Jˆ‰£ëxHŒ|u@‚ÿ €D8`ƒ0ƒ¡4|¦ƒÁ8ƒá<8!@N€ /€x@žÿh‰a+‹@œ@U`:!9k™a} JƒéWé'>+‰ 8‰0@8‰W£ëxH¡WéG>+‰ 8‰0@8‰W£ëxH…Wég>+‰ 8‰0@8‰W£ëxHiWé‡>+‰ 8‰0@8‰W£ëxHMWé§>+‰ 8‰0@8‰W£ëxH1WéÇ>+‰ 8‰0@8‰W£ëxHWéç>+‰ 8‰0@8‰W£ëxHùWé>+‰ 8‰WA½þh8‰0Kÿþ`‰a+‹@œ`U`:!9k™a|€J€„/„AžX£ëxHÍKÿþ0£ëx8€%HKÿþ ! 8  KÿþÌ! 8  KÿþX€ 8 €„/„@žÿ°„ãxKÿÿ¨ˆ|uA‚= ö TŒ|u@‚ÿð= ö8 X8`ÿÿ€ X/€Lž 4ÿÿ9`| ¦@@=@ö}$Z€ T|t/€| !®89k˜ AžBÿØ88` XN€ 8| ¦Kÿÿ¼ˆ|uA‚= ö TŒ|u@‚ÿð8= ö8` XN€ +ƒ„|¦”!ÿà|ˆ#x“¡|½+x“Á|Þ3x“á|æ;x$@0<€ÿð|ex}Cx8„B˜¨ëxÉóx8`LÆ1‚KÿüM8`ÿÿH,= ÿðT`:9)@}i.}kJ}i¦N€ = ö€ T €$ƒ¡ƒÁ|¦ƒá8! N€ = ö8 L9@€ P9 ?})¦°= öUK<‘IL9J€ P|[.Bÿè€$8`ƒ¡ƒÁ|¦ƒá8! N€ = ö€ $|øKÿÿ|8`T„>Hy8`Kÿÿh|Ÿ#x<€ÿð8„B\8`}CxLÆ1‚Kÿûeˆ|uA‚= ö TŒ|u@‚ÿð= ö8 X8`ÿÿ€ X/€@¾ÿ4ÿÿ9@| ¦@ü=ö}jê€T9 |t/€|Q®™+Až 9JBÿØ88`XKÿþÄ|Ÿ#x<€ÿð8„B|8`}CxLÆ1‚KÿúÁˆ|uA‚= ö TŒ|u@‚ÿð8= ö8` XKÿþt8= ö8` 4Kÿþ`= €i@$KÿùÕ8`KÿþL= €i@ Kÿþ@= €i@ Kÿùµ8`Kÿþ,= €i@$Kÿþ = ö€i$Kÿþ= ö€ T Kÿþ= ö€ TP*Kÿýô= €i@(Kÿýè= ÿð€iC¸KÿýÜ<€ÿð8`8„B(?àöLÆ1‚KÿùÝ?0/‰Ažœ<€ÿð8`8„BDLÆ1‚Kÿù½€(Kÿù8`Kÿý”= ö€i,Kÿýˆ8`€Kÿý€8`Kÿýx= ö€ Hp €T^@¢ýd)D€ià/ƒ@¾ýTT^KÿýL8` KÿýD= öiD8kKÿý4= ö8`iD‹àKÿý 8})¦4N€!8`Kÿý9 })¦Kÿþ/ƒ@ž= ö‰@N€ = ö‰ #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_rom.h" #include "pci_io.h" #include "dev_vtty.h" #include "registry.h" #include "net.h" #include "ppc32_mem.h" #include "ppc32_vmtest.h" static struct ppc32_bat_prog bat_array[] = { { PPC32_IBAT_IDX, 0, 0xfff0001e, 0xfff00001 }, { PPC32_IBAT_IDX, 1, 0x00001ffe, 0x00000001 }, { PPC32_IBAT_IDX, 2, 0x00000000, 0xee3e0072 }, { PPC32_IBAT_IDX, 3, 0x80001ffe, 0x80000001 }, { PPC32_DBAT_IDX, 0, 0x80001ffe, 0x80000042 }, { PPC32_DBAT_IDX, 1, 0x00001ffe, 0x0000002a }, { PPC32_DBAT_IDX, 2, 0x60001ffe, 0x6000002a }, { PPC32_DBAT_IDX, 3, 0xfc0007fe, 0xfc00002a }, { -1, -1, 0, 0 }, }; /* Create a new router instance */ static int ppc32_vmtest_create_instance(vm_instance_t *vm) { vm->ram_size = PPC32_VMTEST_DEFAULT_RAM_SIZE; return(0); } /* Free resources used by a test instance */ static int ppc32_vmtest_delete_instance(vm_instance_t *vm) { /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(FALSE); } } /* Free all resources used by VM */ vm_free(vm); return(TRUE); } /* Set IRQ line */ static void ppc32_vmtest_set_irq(vm_instance_t *vm,u_int irq) { cpu_ppc_t *cpu = CPU_PPC32(vm->boot_cpu); cpu->irq_check = cpu->irq_pending = TRUE; } /* Clear IRQ line */ static void ppc32_vmtest_clear_irq(vm_instance_t *vm,u_int irq) { cpu_ppc_t *cpu = CPU_PPC32(vm->boot_cpu); cpu->irq_check = cpu->irq_pending = FALSE; } /* Initialize the PPC32 VM test Platform */ static int ppc32_vmtest_init_platform(vm_instance_t *vm) { _maybe_used cpu_ppc_t *cpu0; cpu_gen_t *gen0; /* Create Console and AUX ports */ vm_init_vtty(vm); /* Create a CPU group */ vm->cpu_group = cpu_group_create("System CPU"); /* Initialize the virtual PowerPC processor */ if (!(gen0 = cpu_create(vm,CPU_TYPE_PPC32,0))) { vm_error(vm,"unable to create CPU0!\n"); return(-1); } cpu0 = CPU_PPC32(gen0); /* Enable as PowerPC 405 */ //ppc32_set_pvr(cpu0,PPC32_PVR_405 | 0x0102); /* Add this CPU to the system CPU group */ cpu_group_add(vm->cpu_group,gen0); vm->boot_cpu = gen0; /* Set IRQ vectors */ vm->set_irq = ppc32_vmtest_set_irq; vm->clear_irq = ppc32_vmtest_clear_irq; #if 0 { vm_obj_t *obj; /* Initialize ROM (as a Flash) */ if (!(obj = dev_flash_init(vm,"rom",0xFF000000,16*1048576))) return(-1); dev_flash_copy_data(obj,0x0F00000,ppc32_microcode,ppc32_microcode_len); } #endif //dev_bootflash_init(vm,"bootflash",0xFF000000,8*1048576); #if 1 /* Initialize ROM */ if (!vm->rom_filename) { /* use embedded ROM */ dev_rom_init(vm,"rom",0xFFF00000,512*1024, ppc32_microcode,ppc32_microcode_len); } else { /* use alternate ROM */ dev_ram_init(vm,"rom",TRUE,TRUE,NULL,FALSE, 0xFFF00000,512*1024); } #endif dev_ram_init(vm,"nvram",TRUE,FALSE,NULL,FALSE, 0x67c00000,vm->nvram_size*4096); dev_ns16552_init(vm,0xffe00000,0x1000,0,0,vm->vtty_con,vm->vtty_aux); /* Remote emulator control */ dev_remote_control_init(vm,0xf6000000,0x1000); /* Initialize RAM */ vm_ram_init(vm,0x00000000); /* RAM aliasing */ dev_create_ram_alias(vm,"ram_alias","ram",0x80000000,vm->ram_size*1048576); /* Display the device list */ dev_show_list(vm); return(0); } /* Boot the RAW image */ _unused static int ppc32_vmtest_boot_raw(vm_instance_t *vm) { cpu_ppc_t *cpu; if (!vm->boot_cpu) return(-1); /* Suspend CPU activity since we will restart directly from ROM */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Reset the boot CPU */ cpu = CPU_PPC32(vm->boot_cpu); ppc32_reset(cpu); /* Load RAW image */ if (ppc32_load_raw_image(cpu,vm->ios_image,0xFFF00000) < 0) { vm_error(vm,"failed to load RAW image '%s'.\n",vm->ios_image); return(-1); } cpu->ia = 0xFFF00100; cpu->gpr[1] = 0x2000; /* Launch the simulation */ printf("\nPPC32_VMTEST '%s': starting simulation (CPU0 IA=0x%8.8x), " "JIT %sabled.\n", vm->name,cpu->ia,vm->jit_use ? "en":"dis"); vm_log(vm,"PPC32_VMTEST_BOOT", "starting instance (CPU0 IA=0x%8.8x,JIT %s)\n", cpu->ia,vm->jit_use ? "on":"off"); /* Start main CPU */ if (vm->ghost_status != VM_GHOST_RAM_GENERATE) { vm->status = VM_STATUS_RUNNING; cpu_start(vm->boot_cpu); } else { vm->status = VM_STATUS_SHUTDOWN; } return(0); } /* Boot the ELF image */ static int ppc32_vmtest_boot_elf(vm_instance_t *vm) { m_uint32_t rom_entry_point; cpu_ppc_t *cpu; if (!vm->boot_cpu) return(-1); /* Suspend CPU activity since we will restart directly from ROM */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Reset the boot CPU */ cpu = CPU_PPC32(vm->boot_cpu); ppc32_reset(cpu); /* Load ROM (ELF image or embedded) */ cpu = CPU_PPC32(vm->boot_cpu); rom_entry_point = (m_uint32_t)PPC32_ROM_START; if ((vm->rom_filename != NULL) && (ppc32_load_elf_image(cpu,vm->rom_filename,0,&rom_entry_point) < 0)) { vm_error(vm,"unable to load alternate ROM '%s', " "fallback to embedded ROM.\n\n",vm->rom_filename); vm->rom_filename = NULL; } /* Load ELF image */ if (ppc32_load_elf_image(cpu,vm->ios_image, (vm->ghost_status == VM_GHOST_RAM_USE), &vm->ios_entry_point) < 0) { vm_error(vm,"failed to load ELF image '%s'.\n",vm->ios_image); return(-1); } /* Launch the simulation */ printf("\nPPC32_VMTEST '%s': starting simulation (CPU0 IA=0x%8.8x), " "JIT %sabled.\n", vm->name,cpu->ia,vm->jit_use ? "en":"dis"); vm_log(vm,"PPC32_VMTEST_BOOT", "starting instance (CPU0 IA=0x%8.8x,JIT %s)\n", cpu->ia,vm->jit_use ? "on":"off"); /* Start main CPU */ if (vm->ghost_status != VM_GHOST_RAM_GENERATE) { vm->status = VM_STATUS_RUNNING; cpu_start(vm->boot_cpu); } else { vm->status = VM_STATUS_SHUTDOWN; } return(0); } /* Initialize a test instance */ static int ppc32_vmtest_init_instance(vm_instance_t *vm) { /* Initialize the test platform */ if (ppc32_vmtest_init_platform(vm) == -1) { vm_error(vm,"unable to initialize the platform hardware.\n"); return(-1); } /* Load BAT registers */ ppc32_load_bat_array(CPU_PPC32(vm->boot_cpu),bat_array); return(ppc32_vmtest_boot_elf(vm)); } /* Stop a test instance */ static int ppc32_vmtest_stop_instance(vm_instance_t *vm) { printf("\nPPC32_VMTEST '%s': stopping simulation.\n",vm->name); vm_log(vm,"PPC32_VMTEST_STOP","stopping simulation.\n"); /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } } /* Free resources that were used during execution to emulate hardware */ vm_hardware_shutdown(vm); return(0); } /* Platform definition */ static vm_platform_t ppc32_vmtest_platform = { "ppc32_test", "PPC32_VMTEST", "PPC32_TEST", ppc32_vmtest_create_instance, ppc32_vmtest_delete_instance, ppc32_vmtest_init_instance, ppc32_vmtest_stop_instance, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }; /* Register the ppc32_vmtest platform */ int ppc32_vmtest_platform_register(void) { return(vm_platform_register(&ppc32_vmtest_platform)); } dynamips-0.2.14/stable/ppc32_vmtest.h000066400000000000000000000006121241034141600173710ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * PowerPC VM experimentations. */ #ifndef __PPC32_VMTEST_H__ #define __PPC32_VMTEST_H__ #include #include "utils.h" #include "net.h" #include "device.h" #include "pci_dev.h" #include "vm.h" /* Default parameters of the test VM */ #define PPC32_VMTEST_DEFAULT_RAM_SIZE 256 #endif dynamips-0.2.14/stable/ppc32_x86_trans.c000066400000000000000000003277171241034141600177200ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #include #include #include #include #include #include #include #include "cpu.h" #include "jit_op.h" #include "ppc32_jit.h" #include "ppc32_x86_trans.h" #include "memory.h" /* %esp adjustment (for MacOS X) */ #define STACK_ADJUST 12 /* ======================================================================= */ /* Macros for CPU structure access */ #define REG_OFFSET(reg) (OFFSET(cpu_ppc_t,gpr[(reg)])) #define MEMOP_OFFSET(op) (OFFSET(cpu_ppc_t,mem_op_fn[(op)])) #define DECLARE_INSN(name) \ static int ppc32_emit_##name(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, \ ppc_insn_t insn) /* EFLAGS to Condition Register (CR) field - signed */ static m_uint32_t eflags_to_cr_signed[64] = { 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, }; /* EFLAGS to Condition Register (CR) field - unsigned */ static m_uint32_t eflags_to_cr_unsigned[256] = { 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, }; /* Emit unhandled instruction code */ static int ppc32_emit_unknown(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, ppc_insn_t opcode); /* Load a 32 bit immediate value */ static forced_inline void ppc32_load_imm(u_char **ptr,u_int reg,m_uint32_t val) { if (val) x86_mov_reg_imm(*ptr,reg,val); else x86_alu_reg_reg(*ptr,X86_XOR,reg,reg); } /* Set the Instruction Address (IA) register */ void ppc32_set_ia(u_char **ptr,m_uint32_t new_ia) { x86_mov_membase_imm(*ptr,X86_EDI,OFFSET(cpu_ppc_t,ia),new_ia,4); } /* Set the Link Register (LR) */ static void ppc32_set_lr(jit_op_t *iop,m_uint32_t new_lr) { x86_mov_membase_imm(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,lr),new_lr,4); } /* * Try to branch directly to the specified JIT block without returning to * main loop. */ static void ppc32_try_direct_far_jump(cpu_ppc_t *cpu,jit_op_t *iop, m_uint32_t new_ia) { m_uint32_t new_page,ia_hash,ia_offset; u_char *test1,*test2,*test3; /* Indicate that we throw %esi, %edx */ ppc32_op_emit_alter_host_reg(cpu,X86_ESI); ppc32_op_emit_alter_host_reg(cpu,X86_EDX); new_page = new_ia & PPC32_MIN_PAGE_MASK; ia_offset = (new_ia & PPC32_MIN_PAGE_IMASK) >> 2; ia_hash = ppc32_jit_get_ia_hash(new_ia); /* Get JIT block info in %edx */ x86_mov_reg_membase(iop->ob_ptr,X86_EBX, X86_EDI,OFFSET(cpu_ppc_t,exec_blk_map),4); x86_mov_reg_membase(iop->ob_ptr,X86_EDX,X86_EBX,ia_hash*sizeof(void *),4); /* no JIT block found ? */ x86_test_reg_reg(iop->ob_ptr,X86_EDX,X86_EDX); test1 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_Z, 0, 1); /* Check block IA */ x86_mov_reg_imm(iop->ob_ptr,X86_ESI,new_page); x86_alu_reg_membase(iop->ob_ptr,X86_CMP,X86_ESI,X86_EDX, OFFSET(ppc32_jit_tcb_t,start_ia)); test2 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_NE, 0, 1); /* Jump to the code */ x86_mov_reg_membase(iop->ob_ptr,X86_ESI, X86_EDX,OFFSET(ppc32_jit_tcb_t,jit_insn_ptr),4); x86_mov_reg_membase(iop->ob_ptr,X86_EBX, X86_ESI,ia_offset * sizeof(void *),4); x86_test_reg_reg(iop->ob_ptr,X86_EBX,X86_EBX); test3 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_Z, 0, 1); x86_jump_reg(iop->ob_ptr,X86_EBX); /* Returns to caller... */ x86_patch(test1,iop->ob_ptr); x86_patch(test2,iop->ob_ptr); x86_patch(test3,iop->ob_ptr); ppc32_set_ia(&iop->ob_ptr,new_ia); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); } /* Set Jump */ static void ppc32_set_jump(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b,jit_op_t *iop, m_uint32_t new_ia,int local_jump) { int return_to_caller = FALSE; u_char *jump_ptr; #if 0 if (cpu->sym_trace && !local_jump) return_to_caller = TRUE; #endif if (!return_to_caller && ppc32_jit_tcb_local_addr(b,new_ia,&jump_ptr)) { ppc32_jit_tcb_record_patch(b,iop,iop->ob_ptr,new_ia); x86_jump32(iop->ob_ptr,0); } else { if (cpu->exec_blk_direct_jump) { /* Block lookup optimization */ ppc32_try_direct_far_jump(cpu,iop,new_ia); } else { ppc32_set_ia(&iop->ob_ptr,new_ia); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); } } } /* Jump to the next page */ void ppc32_set_page_jump(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b) { jit_op_t *iop,*op_list = NULL; cpu->gen->jit_op_current = &op_list; iop = ppc32_op_emit_insn_output(cpu,4,"set_page_jump"); ppc32_set_jump(cpu,b,iop,b->start_ia + PPC32_MIN_PAGE_SIZE,FALSE); ppc32_op_insn_output(b,iop); jit_op_free_list(cpu->gen,op_list); cpu->gen->jit_op_current = NULL; } /* Load a GPR into the specified host register */ static forced_inline void ppc32_load_gpr(u_char **ptr,u_int host_reg, u_int ppc_reg) { x86_mov_reg_membase(*ptr,host_reg,X86_EDI,REG_OFFSET(ppc_reg),4); } /* Store contents for a host register into a GPR register */ static forced_inline void ppc32_store_gpr(u_char **ptr,u_int ppc_reg, u_int host_reg) { x86_mov_membase_reg(*ptr,X86_EDI,REG_OFFSET(ppc_reg),host_reg,4); } /* Apply an ALU operation on a GPR register and a host register */ static forced_inline void ppc32_alu_gpr(u_char **ptr,u_int op, u_int host_reg,u_int ppc_reg) { x86_alu_reg_membase(*ptr,op,host_reg,X86_EDI,REG_OFFSET(ppc_reg)); } /* * Update CR from %eflags * %eax, %edx, %esi are modified. */ static void ppc32_update_cr(ppc32_jit_tcb_t *b,int field,int is_signed) { /* Get status bits from EFLAGS */ if (!is_signed) { x86_mov_reg_imm(b->jit_ptr,X86_EAX,0); x86_lahf(b->jit_ptr); x86_xchg_ah_al(b->jit_ptr); x86_mov_reg_imm(b->jit_ptr,X86_EDX,eflags_to_cr_unsigned); } else { x86_pushfd(b->jit_ptr); x86_pop_reg(b->jit_ptr,X86_EAX); x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_EAX,6); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_EAX,0x3F); x86_mov_reg_imm(b->jit_ptr,X86_EDX,eflags_to_cr_signed); } x86_mov_reg_memindex(b->jit_ptr,X86_EAX,X86_EDX,0,X86_EAX,2,4); /* Check XER Summary of Overflow and report it */ //x86_mov_reg_membase(b->jit_ptr,X86_EDX,X86_EDI,OFFSET(cpu_ppc_t,xer),4); //x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ESI,PPC32_XER_SO); //x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_ESI,PPC32_XER_SO_BIT); //x86_alu_reg_reg(b->jit_ptr,X86_OR,X86_EAX,X86_ESI); /* Store modified CR field */ x86_mov_membase_reg(b->jit_ptr,X86_EDI,PPC32_CR_FIELD_OFFSET(field), X86_EAX,4); } /* * Update CR0 from %eflags * %eax, %edx, %esi are modified. */ static void ppc32_update_cr0(ppc32_jit_tcb_t *b) { ppc32_update_cr(b,0,TRUE); } /* Indicate registers modified by ppc32_update_cr() functions */ void ppc32_update_cr_set_altered_hreg(cpu_ppc_t *cpu) { /* Throw %eax and %edx, which are modifed by ppc32_update_cr() */ ppc32_op_emit_alter_host_reg(cpu,X86_EAX); ppc32_op_emit_alter_host_reg(cpu,X86_EDX); } /* Basic C call */ static forced_inline void ppc32_emit_basic_c_call(u_char **ptr,void *f) { x86_mov_reg_imm(*ptr,X86_EBX,f); x86_call_reg(*ptr,X86_EBX); } /* Emit a simple call to a C function without any parameter */ static void ppc32_emit_c_call(ppc32_jit_tcb_t *b,jit_op_t *iop,void *f) { ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); ppc32_emit_basic_c_call(&iop->ob_ptr,f); } /* ======================================================================== */ /* Initialize register mapping */ void ppc32_jit_init_hreg_mapping(cpu_ppc_t *cpu) { int avail_hregs[] = { X86_ESI, X86_EAX, X86_ECX, X86_EDX, -1 }; struct hreg_map *map; int i,hreg; cpu->hreg_map_list = cpu->hreg_lru = NULL; /* Add the available registers to the map list */ for(i=0;avail_hregs[i]!=-1;i++) { hreg = avail_hregs[i]; map = &cpu->hreg_map[hreg]; /* Initialize mapping. At the beginning, no PPC reg is mapped */ map->flags = 0; map->hreg = hreg; map->vreg = -1; ppc32_jit_insert_hreg_mru(cpu,map); } /* Clear PPC registers mapping */ for(i=0;ippc_reg_map[i] = -1; } /* Allocate a specific temp register */ static int ppc32_jit_get_tmp_hreg(cpu_ppc_t *cpu) { return(X86_EBX); } /* ======================================================================== */ /* JIT operations (specific to target CPU). */ /* ======================================================================== */ /* INSN_OUTPUT */ void ppc32_op_insn_output(ppc32_jit_tcb_t *b,jit_op_t *op) { op->ob_final = b->jit_ptr; memcpy(b->jit_ptr,op->ob_data,op->ob_ptr - op->ob_data); b->jit_ptr += op->ob_ptr - op->ob_data; if ((op->ob_ptr - op->ob_data) >= jit_op_blk_sizes[op->ob_size_index]) { printf("ppc32_op_insn_output: FAILURE: count=%d, size=%d\n", op->ob_ptr - op->ob_data, jit_op_blk_sizes[op->ob_size_index]); } } /* LOAD_GPR: p[0] = %host_reg, p[1] = %ppc_reg */ void ppc32_op_load_gpr(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_load_gpr(&b->jit_ptr,op->param[0],op->param[1]); } /* STORE_GPR: p[0] = %host_reg, p[1] = %ppc_reg */ void ppc32_op_store_gpr(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_store_gpr(&b->jit_ptr,op->param[1],op->param[0]); } /* UPDATE_FLAGS: p[0] = cr_field, p[1] = is_signed */ void ppc32_op_update_flags(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_update_cr(b,op->param[0],op->param[1]); } /* MOVE_HOST_REG: p[0] = %host_dst_reg, p[1] = %host_src_reg */ void ppc32_op_move_host_reg(ppc32_jit_tcb_t *b,jit_op_t *op) { if ((op->param[0] != JIT_OP_INV_REG) && (op->param[1] != JIT_OP_INV_REG)) x86_mov_reg_reg(b->jit_ptr,op->param[0],op->param[1],4); } /* SET_HOST_REG_IMM32: p[0] = %host_reg, p[1] = imm32 */ void ppc32_op_set_host_reg_imm32(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_load_imm(&b->jit_ptr,op->param[0],op->param[1]); } /* ======================================================================== */ /* Memory operation */ static void ppc32_emit_memop(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, int op,int base,int offset,int target,int update) { m_uint32_t val = sign_extend(offset,16); jit_op_t *iop; /* * Since an exception can be triggered, clear JIT state. This allows * to use branch target tag (we can directly branch on this instruction). */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_TARGET); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); iop = ppc32_op_emit_insn_output(cpu,5,"memop"); /* Save PC for exception handling */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* EDX = sign-extended offset */ ppc32_load_imm(&iop->ob_ptr,X86_EDX,val); /* EDX = GPR[base] + sign-extended offset */ if (update || (base != 0)) ppc32_alu_gpr(&iop->ob_ptr,X86_ADD,X86_EDX,base); if (update) x86_mov_reg_reg(iop->ob_ptr,X86_ESI,X86_EDX,4); /* ECX = target register */ x86_mov_reg_imm(iop->ob_ptr,X86_ECX,target); /* EAX = CPU instance pointer */ x86_mov_reg_reg(iop->ob_ptr,X86_EAX,X86_EDI,4); /* Call memory function */ x86_alu_reg_imm(iop->ob_ptr,X86_SUB,X86_ESP,STACK_ADJUST); x86_call_membase(iop->ob_ptr,X86_EDI,MEMOP_OFFSET(op)); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,X86_ESP,STACK_ADJUST); if (update) ppc32_store_gpr(&iop->ob_ptr,base,X86_ESI); } /* Memory operation (indexed) */ static void ppc32_emit_memop_idx(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, int op,int ra,int rb,int target,int update) { jit_op_t *iop; /* * Since an exception can be triggered, clear JIT state. This allows * to use branch target tag (we can directly branch on this instruction). */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_TARGET); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); iop = ppc32_op_emit_insn_output(cpu,5,"memop_idx"); /* Save PC for exception handling */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* EDX = $rb */ ppc32_load_gpr(&iop->ob_ptr,X86_EDX,rb); /* EDX = $rb + $ra */ if (update || (ra != 0)) ppc32_alu_gpr(&iop->ob_ptr,X86_ADD,X86_EDX,ra); if (update) x86_mov_reg_reg(iop->ob_ptr,X86_ESI,X86_EDX,4); /* ECX = target register */ x86_mov_reg_imm(iop->ob_ptr,X86_ECX,target); /* EAX = CPU instance pointer */ x86_mov_reg_reg(iop->ob_ptr,X86_EAX,X86_EDI,4); /* Call memory function */ x86_alu_reg_imm(iop->ob_ptr,X86_SUB,X86_ESP,STACK_ADJUST); x86_call_membase(iop->ob_ptr,X86_EDI,MEMOP_OFFSET(op)); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,X86_ESP,STACK_ADJUST); if (update) ppc32_store_gpr(&iop->ob_ptr,ra,X86_ESI); } typedef void (*memop_fast_access)(jit_op_t *iop,int target); /* Fast LBZ */ static void ppc32_memop_fast_lbz(jit_op_t *iop,int target) { x86_clear_reg(iop->ob_ptr,X86_ECX); x86_mov_reg_memindex(iop->ob_ptr,X86_ECX,X86_EAX,0,X86_EBX,0,1); ppc32_store_gpr(&iop->ob_ptr,target,X86_ECX); } /* Fast STB */ static void ppc32_memop_fast_stb(jit_op_t *iop,int target) { ppc32_load_gpr(&iop->ob_ptr,X86_EDX,target); x86_mov_memindex_reg(iop->ob_ptr,X86_EAX,0,X86_EBX,0,X86_EDX,1); } /* Fast LWZ */ static void ppc32_memop_fast_lwz(jit_op_t *iop,int target) { x86_mov_reg_memindex(iop->ob_ptr,X86_EAX,X86_EAX,0,X86_EBX,0,4); x86_bswap(iop->ob_ptr,X86_EAX); ppc32_store_gpr(&iop->ob_ptr,target,X86_EAX); } /* Fast STW */ static void ppc32_memop_fast_stw(jit_op_t *iop,int target) { ppc32_load_gpr(&iop->ob_ptr,X86_EDX,target); x86_bswap(iop->ob_ptr,X86_EDX); x86_mov_memindex_reg(iop->ob_ptr,X86_EAX,0,X86_EBX,0,X86_EDX,4); } /* Fast memory operation */ static void ppc32_emit_memop_fast(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, int write_op,int opcode, int base,int offset,int target, memop_fast_access op_handler) { m_uint32_t val = sign_extend(offset,16); u_char *test1,*test2,*p_exit; _maybe_used u_char *p_fast_exit; jit_op_t *iop; /* * Since an exception can be triggered, clear JIT state. This allows * to use branch target tag (we can directly branch on this instruction). */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_TARGET); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); iop = ppc32_op_emit_insn_output(cpu,5,"memop_fast"); test2 = NULL; if (val != 0) { /* EBX = sign-extended offset */ ppc32_load_imm(&iop->ob_ptr,X86_EBX,val); /* EBX = GPR[base] + sign-extended offset */ if (base != 0) ppc32_alu_gpr(&iop->ob_ptr,X86_ADD,X86_EBX,base); } else { if (base != 0) ppc32_load_gpr(&iop->ob_ptr,X86_EBX,base); else ppc32_load_imm(&iop->ob_ptr,X86_EBX,0); } #if 0 /* ======= zzz ======= */ { u_char *testZ; x86_mov_reg_reg(iop->ob_ptr,X86_ESI,X86_EBX,4); x86_alu_reg_imm(iop->ob_ptr,X86_AND,X86_ESI,PPC32_MIN_PAGE_MASK); x86_alu_reg_membase(iop->ob_ptr,X86_CMP,X86_ESI,X86_EDI, OFFSET(cpu_ppc_t,vtlb[base].vaddr)); testZ = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_NZ, 0, 1); x86_alu_reg_imm(iop->ob_ptr,X86_AND,X86_EBX,PPC32_MIN_PAGE_IMASK); x86_mov_reg_membase(iop->ob_ptr,X86_EAX, X86_EDI,OFFSET(cpu_ppc_t,vtlb[base].haddr),4); /* Memory access */ op_handler(iop,target); p_fast_exit = iop->ob_ptr; x86_jump8(iop->ob_ptr,0); x86_patch(testZ,iop->ob_ptr); } #endif /* EAX = mts32_entry index */ x86_mov_reg_reg(iop->ob_ptr,X86_EAX,X86_EBX,4); x86_shift_reg_imm(iop->ob_ptr,X86_SHR,X86_EAX,MTS32_HASH_SHIFT); x86_alu_reg_imm(iop->ob_ptr,X86_AND,X86_EAX,MTS32_HASH_MASK); /* EDX = mts32_entry */ x86_mov_reg_membase(iop->ob_ptr,X86_EDX, X86_EDI,OFFSET(cpu_ppc_t,mts_cache[PPC32_MTS_DCACHE]), 4); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,X86_EAX,4); x86_alu_reg_reg(iop->ob_ptr,X86_ADD,X86_EDX,X86_EAX); /* Compare virtual page address (ESI = vpage) */ x86_mov_reg_reg(iop->ob_ptr,X86_ESI,X86_EBX,4); x86_alu_reg_imm(iop->ob_ptr,X86_AND,X86_ESI,PPC32_MIN_PAGE_MASK); x86_alu_reg_membase(iop->ob_ptr,X86_CMP,X86_ESI,X86_EDX, OFFSET(mts32_entry_t,gvpa)); test1 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_NZ, 0, 1); /* Test if we are writing to a COW page */ if (write_op) { x86_test_membase_imm(iop->ob_ptr,X86_EDX,OFFSET(mts32_entry_t,flags), MTS_FLAG_COW|MTS_FLAG_EXEC); test2 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_NZ, 0, 1); } /* EBX = offset in page, EAX = Host Page Address */ x86_alu_reg_imm(iop->ob_ptr,X86_AND,X86_EBX,PPC32_MIN_PAGE_IMASK); x86_mov_reg_membase(iop->ob_ptr,X86_EAX, X86_EDX,OFFSET(mts32_entry_t,hpa),4); #if 0 /* zzz */ { x86_mov_membase_reg(iop->ob_ptr, X86_EDI,OFFSET(cpu_ppc_t,vtlb[base].vaddr), X86_ESI,4); x86_mov_membase_reg(iop->ob_ptr, X86_EDI,OFFSET(cpu_ppc_t,vtlb[base].haddr), X86_EAX,4); } #endif /* Memory access */ op_handler(iop,target); p_exit = iop->ob_ptr; x86_jump8(iop->ob_ptr,0); /* === Slow lookup === */ x86_patch(test1,iop->ob_ptr); if (test2) x86_patch(test2,iop->ob_ptr); /* Update IA (EBX = vaddr) */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* EDX = virtual address */ x86_mov_reg_reg(iop->ob_ptr,X86_EDX,X86_EBX,4); /* ECX = target register */ x86_mov_reg_imm(iop->ob_ptr,X86_ECX,target); /* EAX = CPU instance pointer */ x86_mov_reg_reg(iop->ob_ptr,X86_EAX,X86_EDI,4); /* Call memory function */ x86_alu_reg_imm(iop->ob_ptr,X86_SUB,X86_ESP,STACK_ADJUST); x86_call_membase(iop->ob_ptr,X86_EDI,MEMOP_OFFSET(opcode)); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,X86_ESP,STACK_ADJUST); x86_patch(p_exit,iop->ob_ptr); /* zzz */ #if 0 x86_patch(p_fast_exit,iop->ob_ptr); #endif } /* Emit unhandled instruction code */ static int ppc32_emit_unknown(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, ppc_insn_t opcode) { u_char *test1; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,3,"unknown"); /* Update IA */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* Fallback to non-JIT mode */ x86_alu_reg_imm(iop->ob_ptr,X86_SUB,X86_ESP,STACK_ADJUST); x86_mov_reg_reg(iop->ob_ptr,X86_EAX,X86_EDI,4); x86_mov_reg_imm(iop->ob_ptr,X86_EDX,opcode); ppc32_emit_basic_c_call(&iop->ob_ptr,ppc32_exec_single_insn_ext); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,X86_ESP,STACK_ADJUST); x86_test_reg_reg(iop->ob_ptr,X86_EAX,X86_EAX); test1 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_Z, 0, 1); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); x86_patch(test1,iop->ob_ptr); /* Signal this as an EOB to reset JIT state */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); return(0); } /* Virtual Breakpoint */ void ppc32_emit_breakpoint(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b) { jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,2,"breakpoint"); x86_alu_reg_imm(iop->ob_ptr,X86_SUB,X86_ESP,STACK_ADJUST); x86_mov_reg_reg(iop->ob_ptr,X86_EAX,X86_EDI,4); ppc32_emit_c_call(b,iop,ppc32_run_breakpoint); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,X86_ESP,STACK_ADJUST); /* Signal this as an EOB to to reset JIT state */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); } /* Dump regs */ _unused static void ppc32_emit_dump_regs(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b) { jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,2,"dump_regs"); x86_mov_reg_membase(iop->ob_ptr,X86_EAX,X86_EDI,OFFSET(cpu_ppc_t,gen),4); x86_alu_reg_imm(iop->ob_ptr,X86_SUB,X86_ESP,STACK_ADJUST-4); x86_push_reg(iop->ob_ptr,X86_EAX); ppc32_emit_c_call(b,iop,ppc32_dump_regs); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,X86_ESP,STACK_ADJUST); /* Signal this as an EOB to to reset JIT state */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); } /* Increment the number of executed instructions (performance debugging) */ void ppc32_inc_perf_counter(cpu_ppc_t *cpu) { jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,1,"perf_cnt"); x86_inc_membase(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,perf_counter)); } /* ======================================================================== */ /* BLR - Branch to Link Register */ DECLARE_INSN(BLR) { jit_op_t *iop; int hreg; ppc32_jit_start_hreg_seq(cpu,"blr"); hreg = ppc32_jit_alloc_hreg(cpu,-1); ppc32_op_emit_alter_host_reg(cpu,hreg); iop = ppc32_op_emit_insn_output(cpu,2,"blr"); x86_mov_reg_membase(iop->ob_ptr,hreg,X86_EDI,OFFSET(cpu_ppc_t,lr),4); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,ia),hreg,4); /* set the return address */ if (insn & 1) ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); ppc32_jit_close_hreg_seq(cpu); return(0); } /* BCTR - Branch to Count Register */ DECLARE_INSN(BCTR) { jit_op_t *iop; int hreg; ppc32_jit_start_hreg_seq(cpu,"bctr"); hreg = ppc32_jit_alloc_hreg(cpu,-1); ppc32_op_emit_alter_host_reg(cpu,hreg); iop = ppc32_op_emit_insn_output(cpu,2,"bctr"); x86_mov_reg_membase(iop->ob_ptr,hreg,X86_EDI,OFFSET(cpu_ppc_t,ctr),4); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,ia),hreg,4); /* set the return address */ if (insn & 1) ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFLR - Move From Link Register */ DECLARE_INSN(MFLR) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mflr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mflr"); x86_mov_reg_membase(iop->ob_ptr,hreg_rd,X86_EDI,OFFSET(cpu_ppc_t,lr),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MTLR - Move To Link Register */ DECLARE_INSN(MTLR) { int rs = bits(insn,21,25); int hreg_rs; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mtlr"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"mtlr"); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,lr),hreg_rs,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFCTR - Move From Counter Register */ DECLARE_INSN(MFCTR) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mfctr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mfctr"); x86_mov_reg_membase(iop->ob_ptr,hreg_rd,X86_EDI,OFFSET(cpu_ppc_t,ctr),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MTCTR - Move To Counter Register */ DECLARE_INSN(MTCTR) { int rs = bits(insn,21,25); int hreg_rs; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mtctr"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"mtctr"); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,ctr),hreg_rs,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFTBU - Move from Time Base (Up) */ DECLARE_INSN(MFTBU) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mftbu"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mftbu"); x86_mov_reg_membase(iop->ob_ptr,hreg_rd,X86_EDI,OFFSET(cpu_ppc_t,tb)+4,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } #define PPC32_TB_INCREMENT 50 /* MFTBL - Move from Time Base (Lo) */ DECLARE_INSN(MFTBL) { int rd = bits(insn,21,25); int hreg_rd,hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mftbl"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); iop = ppc32_op_emit_insn_output(cpu,3,"mftbl"); x86_mov_reg_membase(iop->ob_ptr,hreg_rd,X86_EDI,OFFSET(cpu_ppc_t,tb),4); /* Increment the time base register */ x86_mov_reg_membase(iop->ob_ptr,hreg_rd,X86_EDI,OFFSET(cpu_ppc_t,tb),4); x86_mov_reg_membase(iop->ob_ptr,hreg_t0,X86_EDI,OFFSET(cpu_ppc_t,tb)+4,4); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_rd,PPC32_TB_INCREMENT); x86_alu_reg_imm(iop->ob_ptr,X86_ADC,hreg_t0,0); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,tb),hreg_rd,4); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,tb)+4,hreg_t0,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADD */ DECLARE_INSN(ADD) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rd,hreg_ra,hreg_rb; jit_op_t *iop; /* $rd = $ra + $rb */ ppc32_jit_start_hreg_seq(cpu,"add"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"add"); if (rd == ra) x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb); else if (rd == rb) x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_rd,hreg_ra); else { x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDC */ DECLARE_INSN(ADDC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rd,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $rd = $ra + $rb */ ppc32_jit_start_hreg_seq(cpu,"addc"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); /* store the carry flag */ hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"addc"); if (rd == ra) x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb); else if (rd == rb) x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_rd,hreg_ra); else { x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t0,FALSE); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x1); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,xer_ca), hreg_t0,4); if (insn & 1) { x86_test_reg_reg(iop->ob_ptr,hreg_rd,hreg_rd); ppc32_op_emit_update_flags(cpu,0,TRUE); } ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDE - Add Extended */ DECLARE_INSN(ADDE) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"adde"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,3,"adde"); /* $t0 = $ra + carry */ x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); x86_alu_reg_membase(iop->ob_ptr,X86_ADD,hreg_t0, X86_EDI,OFFSET(cpu_ppc_t,xer_ca)); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,xer_ca),hreg_t1,4); /* $t0 += $rb */ x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_t0,hreg_rb); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_alu_membase_reg(iop->ob_ptr,X86_OR,X86_EDI,OFFSET(cpu_ppc_t,xer_ca), hreg_t1); /* update cr0 */ if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_t0,hreg_t0); x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDI - ADD Immediate */ DECLARE_INSN(ADDI) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + imm */ ppc32_jit_start_hreg_seq(cpu,"addi"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); if (ra != 0) { hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,2,"addi"); if (rd != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_rd,tmp); } else { iop = ppc32_op_emit_insn_output(cpu,1,"addi"); ppc32_load_imm(&iop->ob_ptr,hreg_rd,tmp); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDIC - ADD Immediate with Carry */ DECLARE_INSN(ADDIC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + imm */ ppc32_jit_start_hreg_seq(cpu,"addic"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"addic"); if (rd != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_rd,tmp); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); x86_set_membase(iop->ob_ptr,X86_CC_C, X86_EDI,OFFSET(cpu_ppc_t,xer_ca),FALSE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDIC. */ DECLARE_INSN(ADDIC_dot) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + imm */ ppc32_jit_start_hreg_seq(cpu,"addic."); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"addic."); if (rd != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_rd,tmp); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); x86_set_membase(iop->ob_ptr,X86_CC_C, X86_EDI,OFFSET(cpu_ppc_t,xer_ca),FALSE); ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDIS - ADD Immediate Shifted */ DECLARE_INSN(ADDIS) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + (imm << 16) */ ppc32_jit_start_hreg_seq(cpu,"addis"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); if (ra != 0) { hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"addis"); if (rd != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_rd,tmp); } else { //iop = ppc32_op_emit_insn_output(cpu,1,"addis"); //x86_mov_reg_imm(iop->ob_ptr,hreg_rd,tmp); ppc32_op_emit_set_host_reg_imm32(cpu,hreg_rd,tmp); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDZE */ DECLARE_INSN(ADDZE) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rd,hreg_ra,hreg_t0; jit_op_t *iop; /* $rd = $ra + xer_ca + set_carry */ ppc32_jit_start_hreg_seq(cpu,"addze"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,2,"addze"); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,hreg_t0); if (rd != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_alu_reg_membase(iop->ob_ptr,X86_ADD,hreg_rd, X86_EDI,OFFSET(cpu_ppc_t,xer_ca)); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t0,FALSE); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,xer_ca),hreg_t0,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* AND */ DECLARE_INSN(AND) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = $rs & $rb */ ppc32_jit_start_hreg_seq(cpu,"and"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"and"); if (ra == rs) x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb); else if (ra == rb) x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_ra,hreg_rs); else { x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ANDC */ DECLARE_INSN(ANDC) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $ra = $rs & ~$rb */ ppc32_jit_start_hreg_seq(cpu,"andc"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"andc"); /* $t0 = ~$rb */ hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rb,4); x86_not_reg(iop->ob_ptr,hreg_t0); /* $ra = $rs & $t0 */ if (ra == rs) x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_ra,hreg_t0); else { x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_rs); x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* AND Immediate */ DECLARE_INSN(ANDI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs & imm */ ppc32_jit_start_hreg_seq(cpu,"andi"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"andi"); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_ra,tmp); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* AND Immediate Shifted */ DECLARE_INSN(ANDIS) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs & imm */ ppc32_jit_start_hreg_seq(cpu,"andis"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"andis"); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_ra,tmp); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* B - Branch */ DECLARE_INSN(B) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"b"); /* compute the new ia */ new_ia = b->start_ia + (b->ppc_trans_pos << 2); new_ia += sign_extend(offset << 2,26); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BA - Branch Absolute */ DECLARE_INSN(BA) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"ba"); /* compute the new ia */ new_ia = sign_extend(offset << 2,26); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BL - Branch and Link */ DECLARE_INSN(BL) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"bl"); /* compute the new ia */ new_ia = b->start_ia + (b->ppc_trans_pos << 2); new_ia += sign_extend(offset << 2,26); /* set the return address */ ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BLA - Branch and Link Absolute */ DECLARE_INSN(BLA) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"bla"); /* compute the new ia */ new_ia = sign_extend(offset << 2,26); /* set the return address */ ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BC - Branch Conditional (Condition Check only) */ DECLARE_INSN(BCC) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); jit_op_t *iop; u_int cr_field,cr_bit; m_uint32_t new_ia; u_char *jump_ptr; int local_jump; int cond; ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_JUMP); iop = ppc32_op_emit_insn_output(cpu,5,"bcc"); /* Get the wanted value for the condition bit */ cond = (bo >> 3) & 0x1; /* Set the return address */ if (insn & 1) { ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1)<<2)); } /* Compute the new ia */ new_ia = sign_extend_32(bd << 2,16); if (!(insn & 0x02)) new_ia += b->start_ia + (b->ppc_trans_pos << 2); /* Test the condition bit */ cr_field = ppc32_get_cr_field(bi); cr_bit = ppc32_get_cr_bit(bi); ppc32_op_emit_require_flags(cpu,cr_field); x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(cr_field), (1 << cr_bit)); local_jump = ppc32_jit_tcb_local_addr(b,new_ia,&jump_ptr); /* * Optimize the jump, depending if the destination is in the same * page or not. */ if (local_jump) { ppc32_jit_tcb_record_patch(b,iop,iop->ob_ptr,new_ia); x86_branch32(iop->ob_ptr,(cond) ? X86_CC_NZ : X86_CC_Z,0,FALSE); } else { jump_ptr = iop->ob_ptr; x86_branch32(iop->ob_ptr,(cond) ? X86_CC_Z : X86_CC_NZ,0,FALSE); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); x86_patch(jump_ptr,iop->ob_ptr); } ppc32_op_emit_branch_target(cpu,b,new_ia); return(0); } /* BC - Branch Conditional */ DECLARE_INSN(BC) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); int hreg_t0,hreg_t1; jit_op_t *iop; u_int cr_field,cr_bit; m_uint32_t new_ia; u_char *jump_ptr; int local_jump; int cond,ctr; ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_JUMP); iop = ppc32_op_emit_insn_output(cpu,5,"bc"); ppc32_jit_start_hreg_seq(cpu,"bc"); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); /* Get the wanted value for the condition bit and CTR value */ cond = (bo >> 3) & 0x1; ctr = (bo >> 1) & 0x1; /* Set the return address */ if (insn & 1) { ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1)<<2)); } /* Compute the new ia */ new_ia = sign_extend_32(bd << 2,16); if (!(insn & 0x02)) new_ia += b->start_ia + (b->ppc_trans_pos << 2); x86_mov_reg_imm(iop->ob_ptr,hreg_t0,1); /* Decrement the count register */ if (!(bo & 0x04)) { x86_dec_membase(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,ctr)); x86_set_reg(iop->ob_ptr,(ctr) ? X86_CC_Z : X86_CC_NZ,hreg_t1,FALSE); x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } /* Test the condition bit */ if (!((bo >> 4) & 0x01)) { cr_field = ppc32_get_cr_field(bi); cr_bit = ppc32_get_cr_bit(bi); ppc32_op_emit_require_flags(cpu,cr_field); x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(cr_field), (1 << cr_bit)); x86_set_reg(iop->ob_ptr,(cond) ? X86_CC_NZ : X86_CC_Z,hreg_t1,FALSE); x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); local_jump = ppc32_jit_tcb_local_addr(b,new_ia,&jump_ptr); /* * Optimize the jump, depending if the destination is in the same * page or not. */ if (local_jump) { ppc32_jit_tcb_record_patch(b,iop,iop->ob_ptr,new_ia); x86_branch32(iop->ob_ptr,X86_CC_NZ,0,FALSE); } else { jump_ptr = iop->ob_ptr; x86_branch32(iop->ob_ptr,X86_CC_Z,0,FALSE); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); x86_patch(jump_ptr,iop->ob_ptr); } ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_jit_close_hreg_seq(cpu); return(0); } /* BCLR - Branch Conditional to Link register */ DECLARE_INSN(BCLR) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); int hreg_t0,hreg_t1; jit_op_t *iop; u_int cr_field,cr_bit; m_uint32_t new_ia; u_char *jump_ptr; int cond,ctr; ppc32_jit_start_hreg_seq(cpu,"bclr"); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); iop = ppc32_op_emit_insn_output(cpu,5,"bclr"); /* Get the wanted value for the condition bit and CTR value */ cond = (bo >> 3) & 0x1; ctr = (bo >> 1) & 0x1; /* Compute the new ia */ new_ia = sign_extend_32(bd << 2,16); if (!(insn & 0x02)) new_ia += b->start_ia + (b->ppc_trans_pos << 2); x86_mov_reg_imm(iop->ob_ptr,hreg_t0,1); /* Decrement the count register */ if (!(bo & 0x04)) { x86_dec_membase(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,ctr)); x86_set_reg(iop->ob_ptr,(ctr) ? X86_CC_Z : X86_CC_NZ,hreg_t1,FALSE); x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } /* Test the condition bit */ if (!((bo >> 4) & 0x01)) { cr_field = ppc32_get_cr_field(bi); cr_bit = ppc32_get_cr_bit(bi); ppc32_op_emit_require_flags(cpu,cr_field); x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(cr_field), (1 << cr_bit)); x86_set_reg(iop->ob_ptr,(cond) ? X86_CC_NZ : X86_CC_Z,hreg_t1,FALSE); x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } /* Set the return address */ x86_mov_reg_membase(iop->ob_ptr,hreg_t1,X86_EDI,OFFSET(cpu_ppc_t,lr),4); if (insn & 1) { ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1)<<2)); } /* Branching */ x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); jump_ptr = iop->ob_ptr; x86_branch32(iop->ob_ptr,X86_CC_Z,0,FALSE); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t1,0xFFFFFFFC); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,ia),hreg_t1,4); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); x86_patch(jump_ptr,iop->ob_ptr); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMP - Compare */ DECLARE_INSN(CMP) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmp"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"cmp"); x86_alu_reg_reg(iop->ob_ptr,X86_CMP,hreg_ra,hreg_rb); ppc32_op_emit_update_flags(cpu,rd,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMPI - Compare Immediate */ DECLARE_INSN(CMPI) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_ra; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmpi"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"cmpi"); x86_alu_reg_imm(iop->ob_ptr,X86_CMP,hreg_ra,tmp); ppc32_op_emit_update_flags(cpu,rd,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMPL - Compare Logical */ DECLARE_INSN(CMPL) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmpl"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"cmpl"); x86_alu_reg_reg(iop->ob_ptr,X86_CMP,hreg_ra,hreg_rb); ppc32_op_emit_update_flags(cpu,rd,FALSE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMPLI - Compare Immediate */ DECLARE_INSN(CMPLI) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); int hreg_ra; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmpli"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"cmpli"); x86_alu_reg_imm(iop->ob_ptr,X86_CMP,hreg_ra,imm); ppc32_op_emit_update_flags(cpu,rd,FALSE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRAND - Condition Register AND */ DECLARE_INSN(CRAND) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"crand"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crand"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of AND between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,X86_EDX); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRANDC - Condition Register AND with Complement */ DECLARE_INSN(CRANDC) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"crandc"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crandc"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_Z,hreg_t0,FALSE); /* result of AND between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,X86_EDX); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CREQV - Condition Register EQV */ DECLARE_INSN(CREQV) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"creqv"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"creqv"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of XOR between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,X86_EDX); x86_not_reg(iop->ob_ptr,hreg_t0); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRNAND - Condition Register NAND */ DECLARE_INSN(CRNAND) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"crnand"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crnand"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of NAND between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,X86_EDX); x86_not_reg(iop->ob_ptr,hreg_t0); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRNOR - Condition Register NOR */ DECLARE_INSN(CRNOR) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"crnor"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crnor"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of NOR between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_t0,X86_EDX); x86_not_reg(iop->ob_ptr,hreg_t0); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CROR - Condition Register OR */ DECLARE_INSN(CROR) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"cror"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"cror"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of OR between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_t0,X86_EDX); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRORC - Condition Register OR with Complement */ DECLARE_INSN(CRORC) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"crorc"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crorc"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_Z,hreg_t0,FALSE); /* result of ORC between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_t0,X86_EDX); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRXOR - Condition Register XOR */ DECLARE_INSN(CRXOR) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"crxor"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crxor"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of XOR between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,X86_EDX); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* DIVWU - Divide Word Unsigned */ DECLARE_INSN(DIVWU) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"divwu"); ppc32_jit_alloc_hreg_forced(cpu,X86_EAX); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); /* $rd = $ra / $rb */ ppc32_op_emit_load_gpr(cpu,X86_EAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"divwu"); ppc32_load_imm(&iop->ob_ptr,X86_EDX,0); x86_div_reg(iop->ob_ptr,hreg_rb,0); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,X86_EAX,X86_EAX); ppc32_op_emit_store_gpr(cpu,rd,X86_EAX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,X86_EAX); ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* EQV */ DECLARE_INSN(EQV) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = ~($rs ^ $rb) */ ppc32_jit_start_hreg_seq(cpu,"eqv"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"eqv"); if (ra == rs) x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb); else if (ra == rb) x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rs); else { x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb); } x86_not_reg(iop->ob_ptr,hreg_ra); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* EXTSB - Extend Sign Byte */ DECLARE_INSN(EXTSB) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = extsb($rs) */ ppc32_jit_start_hreg_seq(cpu,"extsb"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"extsb"); if (rs != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_ra,24); x86_shift_reg_imm(iop->ob_ptr,X86_SAR,hreg_ra,24); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* EXTSH - Extend Sign Word */ DECLARE_INSN(EXTSH) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = extsh($rs) */ ppc32_jit_start_hreg_seq(cpu,"extsh"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"extsh"); if (rs != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_ra,16); x86_shift_reg_imm(iop->ob_ptr,X86_SAR,hreg_ra,16); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* LBZ - Load Byte and Zero */ DECLARE_INSN(LBZ) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(cpu,b,PPC_MEMOP_LBZ,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,0,PPC_MEMOP_LBZ,ra,offset,rs, ppc32_memop_fast_lbz); return(0); } /* LBZU - Load Byte and Zero with Update */ DECLARE_INSN(LBZU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LBZ,ra,offset,rs,1); return(0); } /* LBZUX - Load Byte and Zero with Update Indexed */ DECLARE_INSN(LBZUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LBZ,ra,rb,rs,1); return(0); } /* LBZX - Load Byte and Zero Indexed */ DECLARE_INSN(LBZX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LBZ,ra,rb,rs,0); return(0); } /* LHA - Load Half-Word Algebraic */ DECLARE_INSN(LHA) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHA,ra,offset,rs,0); return(0); } /* LHAU - Load Half-Word Algebraic with Update */ DECLARE_INSN(LHAU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHA,ra,offset,rs,1); return(0); } /* LHAUX - Load Half-Word Algebraic with Update Indexed */ DECLARE_INSN(LHAUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHA,ra,rb,rs,1); return(0); } /* LHAX - Load Half-Word Algebraic Indexed */ DECLARE_INSN(LHAX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHA,ra,rb,rs,0); return(0); } /* LHZ - Load Half-Word and Zero */ DECLARE_INSN(LHZ) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHZ,ra,offset,rs,0); return(0); } /* LHZU - Load Half-Word and Zero with Update */ DECLARE_INSN(LHZU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHZ,ra,offset,rs,1); return(0); } /* LHZUX - Load Half-Word and Zero with Update Indexed */ DECLARE_INSN(LHZUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHZ,ra,rb,rs,1); return(0); } /* LHZX - Load Half-Word and Zero Indexed */ DECLARE_INSN(LHZX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHZ,ra,rb,rs,0); return(0); } /* LWZ - Load Word and Zero */ DECLARE_INSN(LWZ) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(b,PPC_MEMOP_LWZ,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,0,PPC_MEMOP_LWZ,ra,offset,rs, ppc32_memop_fast_lwz); return(0); } /* LWZU - Load Word and Zero with Update */ DECLARE_INSN(LWZU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LWZ,ra,offset,rs,1); return(0); } /* LWZUX - Load Word and Zero with Update Indexed */ DECLARE_INSN(LWZUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LWZ,ra,rb,rs,1); return(0); } /* LWZX - Load Word and Zero Indexed */ DECLARE_INSN(LWZX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LWZ,ra,rb,rs,0); return(0); } /* MCRF - Move Condition Register Field */ DECLARE_INSN(MCRF) { int rd = bits(insn,23,25); int rs = bits(insn,18,20); int hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mcrf"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_require_flags(cpu,rs); iop = ppc32_op_emit_insn_output(cpu,1,"mcrf"); /* Load "rs" field in %edx */ x86_mov_reg_membase(iop->ob_ptr,hreg_t0, X86_EDI,PPC32_CR_FIELD_OFFSET(rs),4); /* Store it in "rd" field */ x86_mov_membase_reg(iop->ob_ptr,X86_EDI,PPC32_CR_FIELD_OFFSET(rd), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFCR - Move from Condition Register */ DECLARE_INSN(MFCR) { int rd = bits(insn,21,25); int hreg_rd,hreg_t0; jit_op_t *iop; int i; ppc32_jit_start_hreg_seq(cpu,"mfcr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_require_flags(cpu,JIT_OP_PPC_ALL_FLAGS); iop = ppc32_op_emit_insn_output(cpu,3,"mfcr"); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_rd,hreg_rd); for(i=0;i<8;i++) { /* load field in %edx */ x86_mov_reg_membase(iop->ob_ptr,hreg_t0, X86_EDI,PPC32_CR_FIELD_OFFSET(i),4); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_rd,4); x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_rd,hreg_t0); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFMSR - Move from Machine State Register */ DECLARE_INSN(MFMSR) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mfmsr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mfmsr"); x86_mov_reg_membase(iop->ob_ptr,hreg_rd,X86_EDI,OFFSET(cpu_ppc_t,msr),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFSR - Move From Segment Register */ DECLARE_INSN(MFSR) { int rd = bits(insn,21,25); int sr = bits(insn,16,19); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mfsr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mfsr"); x86_mov_reg_membase(iop->ob_ptr,hreg_rd, X86_EDI,(OFFSET(cpu_ppc_t,sr) + (sr << 2)),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MTCRF - Move to Condition Register Fields */ DECLARE_INSN(MTCRF) { int rs = bits(insn,21,25); int crm = bits(insn,12,19); int hreg_rs,hreg_t0; jit_op_t *iop; int i; ppc32_jit_start_hreg_seq(cpu,"mtcrf"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,3,"mtcrf"); for(i=0;i<8;i++) if (crm & (1 << (7 - i))) { x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); if (i != 7) x86_shift_reg_imm(iop->ob_ptr,X86_SHR,hreg_t0,28 - (i << 2)); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x0F); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,PPC32_CR_FIELD_OFFSET(i), hreg_t0,4); } ppc32_op_emit_basic_opcode(cpu,JIT_OP_TRASH_FLAGS); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULHW - Multiply High Word */ DECLARE_INSN(MULHW) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mulhw"); ppc32_jit_alloc_hreg_forced(cpu,X86_EAX); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,X86_EAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); /* rd = hi(ra * rb) */ iop = ppc32_op_emit_insn_output(cpu,2,"mulhw"); x86_mul_reg(iop->ob_ptr,hreg_rb,1); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,X86_EDX,X86_EDX); ppc32_op_emit_store_gpr(cpu,rd,X86_EDX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,X86_EAX); ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULHWU - Multiply High Word Unsigned */ DECLARE_INSN(MULHWU) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mulhwu"); ppc32_jit_alloc_hreg_forced(cpu,X86_EAX); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,X86_EAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); /* rd = hi(ra * rb) */ iop = ppc32_op_emit_insn_output(cpu,2,"mulhwu"); x86_mul_reg(iop->ob_ptr,hreg_rb,0); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,X86_EDX,X86_EDX); ppc32_op_emit_store_gpr(cpu,rd,X86_EDX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,X86_EAX); ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULLI - Multiply Low Immediate */ DECLARE_INSN(MULLI) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); int hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mulli"); ppc32_jit_alloc_hreg_forced(cpu,X86_EAX); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,X86_EAX,ra); /* rd = lo(ra * imm) */ iop = ppc32_op_emit_insn_output(cpu,2,"mulli"); ppc32_load_imm(&iop->ob_ptr,hreg_t0,sign_extend_32(imm,16)); x86_mul_reg(iop->ob_ptr,hreg_t0,1); ppc32_op_emit_store_gpr(cpu,rd,X86_EAX); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,X86_EAX); ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULLW - Multiply Low Word */ DECLARE_INSN(MULLW) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mullw"); ppc32_jit_alloc_hreg_forced(cpu,X86_EAX); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,X86_EAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); /* rd = lo(ra * rb) */ iop = ppc32_op_emit_insn_output(cpu,2,"mullw"); x86_mul_reg(iop->ob_ptr,hreg_rb,1); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,X86_EAX,X86_EAX); ppc32_op_emit_store_gpr(cpu,rd,X86_EAX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,X86_EAX); ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* NAND */ DECLARE_INSN(NAND) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = ~($rs & $rb) */ ppc32_jit_start_hreg_seq(cpu,"nand"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"nand"); if (ra == rs) x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb); else if (ra == rb) x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_ra,hreg_rs); else { x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb); } x86_not_reg(iop->ob_ptr,hreg_ra); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* NEG */ DECLARE_INSN(NEG) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = neg($ra) */ ppc32_jit_start_hreg_seq(cpu,"neg"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"neg"); if (rd != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_neg_reg(iop->ob_ptr,hreg_rd); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_rd,hreg_rd); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* NOR */ DECLARE_INSN(NOR) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = ~($rs | $rb) */ ppc32_jit_start_hreg_seq(cpu,"nor"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"nor"); if (ra == rs) x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb); else if (ra == rb) x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_rs); else { x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb); } x86_not_reg(iop->ob_ptr,hreg_ra); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR */ DECLARE_INSN(OR) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = $rs | $rb */ ppc32_jit_start_hreg_seq(cpu,"or"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); /* special optimization for move/nop operation */ if (rs == rb) { ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"or"); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"or"); if (ra == rs) { x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb); } else if (ra == rb) x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_rs); else { x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR with Complement */ DECLARE_INSN(ORC) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $ra = $rs & ~$rb */ ppc32_jit_start_hreg_seq(cpu,"orc"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"orc"); /* $t0 = ~$rb */ hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rb,4); x86_not_reg(iop->ob_ptr,hreg_t0); /* $ra = $rs | $t0 */ if (ra == rs) x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_t0); else { x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_t0,hreg_rs); x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR Immediate */ DECLARE_INSN(ORI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs | imm */ ppc32_jit_start_hreg_seq(cpu,"ori"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"ori"); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_imm(iop->ob_ptr,X86_OR,hreg_ra,tmp); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR Immediate Shifted */ DECLARE_INSN(ORIS) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs | imm */ ppc32_jit_start_hreg_seq(cpu,"oris"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"oris"); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_imm(iop->ob_ptr,X86_OR,hreg_ra,tmp); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* RLWIMI - Rotate Left Word Immediate then Mask Insert */ DECLARE_INSN(RLWIMI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t mask; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"rlwimi"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); mask = ppc32_rotate_mask(mb,me); iop = ppc32_op_emit_insn_output(cpu,2,"rlwimi"); /* Apply inverse mask to $ra */ if (mask != 0) x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_ra,~mask); /* Rotate $rs of "sh" bits and apply the mask */ x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); if (sh != 0) x86_shift_reg_imm(iop->ob_ptr,X86_ROL,hreg_t0,sh); if (mask != 0xFFFFFFFF) x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,mask); /* Store the result */ x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_t0); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* RLWINM - Rotate Left Word Immediate AND with Mask */ DECLARE_INSN(RLWINM) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t mask; int hreg_rs,hreg_ra; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"rlwinm"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"rlwinm"); /* Rotate $rs of "sh" bits and apply the mask */ mask = ppc32_rotate_mask(mb,me); if (rs != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); if (sh != 0) x86_shift_reg_imm(iop->ob_ptr,X86_ROL,hreg_ra,sh); if (mask != 0xFFFFFFFF) x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_ra,mask); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* RLWNM - Rotate Left Word then Mask Insert */ DECLARE_INSN(RLWNM) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t mask; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; /* ecx is directly modified: throw it */ ppc32_op_emit_alter_host_reg(cpu,X86_ECX); ppc32_jit_start_hreg_seq(cpu,"rlwnm"); ppc32_jit_alloc_hreg_forced(cpu,X86_ECX); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,X86_ECX,rb); iop = ppc32_op_emit_insn_output(cpu,2,"rlwnm"); /* Load the shift register ("sh") */ mask = ppc32_rotate_mask(mb,me); /* Rotate $rs and apply the mask */ x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); x86_shift_reg(iop->ob_ptr,X86_ROL,hreg_t0); if (mask != 0xFFFFFFFF) x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,mask); x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* Shift Left Word */ DECLARE_INSN(SLW) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); u_char *test1; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; /* ecx is directly modified: throw it */ ppc32_op_emit_alter_host_reg(cpu,X86_ECX); ppc32_jit_start_hreg_seq(cpu,"slw"); ppc32_jit_alloc_hreg_forced(cpu,X86_ECX); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); /* $ra = $rs << $rb. If count >= 32, then null result */ ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,X86_ECX,rb); iop = ppc32_op_emit_insn_output(cpu,3,"slw"); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,hreg_t0); x86_test_reg_imm(iop->ob_ptr,X86_ECX,0x20); test1 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_NZ, 0, 1); x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); x86_shift_reg(iop->ob_ptr,X86_SHL,hreg_t0); /* store the result */ x86_patch(test1,iop->ob_ptr); x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SRAWI - Shift Right Algebraic Word Immediate */ DECLARE_INSN(SRAWI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); register m_uint32_t mask; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"srawi"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); /* $ra = (int32)$rs >> sh */ ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,3,"srawi"); x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_shift_reg_imm(iop->ob_ptr,X86_SAR,hreg_ra,sh); /* set XER_CA depending on the result */ mask = ~(0xFFFFFFFFU << sh) | 0x80000000; x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,mask); x86_alu_reg_imm(iop->ob_ptr,X86_CMP,hreg_t0,0x80000000); x86_set_reg(iop->ob_ptr,X86_CC_A,hreg_t0,FALSE); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x1); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,xer_ca),hreg_t0,4); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* Shift Right Word */ DECLARE_INSN(SRW) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); u_char *test1; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; /* ecx is directly modified: throw it */ ppc32_op_emit_alter_host_reg(cpu,X86_ECX); ppc32_jit_start_hreg_seq(cpu,"srw"); ppc32_jit_alloc_hreg_forced(cpu,X86_ECX); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); /* $ra = $rs >> $rb. If count >= 32, then null result */ ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,X86_ECX,rb); iop = ppc32_op_emit_insn_output(cpu,3,"srw"); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,hreg_t0); x86_test_reg_imm(iop->ob_ptr,X86_ECX,0x20); test1 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_NZ, 0, 1); x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); x86_shift_reg(iop->ob_ptr,X86_SHR,hreg_t0); /* store the result */ x86_patch(test1,iop->ob_ptr); x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* STB - Store Byte */ DECLARE_INSN(STB) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(b,PPC_MEMOP_STB,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,1,PPC_MEMOP_STB,ra,offset,rs, ppc32_memop_fast_stb); return(0); } /* STBU - Store Byte with Update */ DECLARE_INSN(STBU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STB,ra,offset,rs,1); return(0); } /* STBUX - Store Byte with Update Indexed */ DECLARE_INSN(STBUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STB,ra,rb,rs,1); return(0); } /* STBUX - Store Byte Indexed */ DECLARE_INSN(STBX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STB,ra,rb,rs,0); return(0); } /* STH - Store Half-Word */ DECLARE_INSN(STH) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STH,ra,offset,rs,0); return(0); } /* STHU - Store Half-Word with Update */ DECLARE_INSN(STHU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STH,ra,offset,rs,1); return(0); } /* STHUX - Store Half-Word with Update Indexed */ DECLARE_INSN(STHUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STH,ra,rb,rs,1); return(0); } /* STHUX - Store Half-Word Indexed */ DECLARE_INSN(STHX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STH,ra,rb,rs,0); return(0); } /* STW - Store Word */ DECLARE_INSN(STW) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(b,PPC_MEMOP_STW,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,1,PPC_MEMOP_STW,ra,offset,rs, ppc32_memop_fast_stw); return(0); } /* STWU - Store Word with Update */ DECLARE_INSN(STWU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STW,ra,offset,rs,1); return(0); } /* STWUX - Store Word with Update Indexed */ DECLARE_INSN(STWUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STW,ra,rb,rs,1); return(0); } /* STWUX - Store Word Indexed */ DECLARE_INSN(STWX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STW,ra,rb,rs,0); return(0); } /* SUBF - Subtract From */ DECLARE_INSN(SUBF) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rd,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $rd = $rb - $ra */ ppc32_jit_start_hreg_seq(cpu,"subf"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"subf"); if (rd == rb) x86_alu_reg_reg(iop->ob_ptr,X86_SUB,hreg_rd,hreg_ra); else if (rd == ra) { x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rb,4); x86_alu_reg_reg(iop->ob_ptr,X86_SUB,hreg_t0,hreg_ra); x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); } else { x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_rb,4); x86_alu_reg_reg(iop->ob_ptr,X86_SUB,hreg_rd,hreg_ra); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SUBFC - Subtract From Carrying */ DECLARE_INSN(SUBFC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; /* $rd = ~$ra + 1 + $rb */ ppc32_jit_start_hreg_seq(cpu,"subfc"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,3,"subfc"); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); /* $t0 = ~$ra + 1 */ x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); x86_not_reg(iop->ob_ptr,hreg_t0); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_t0,1); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,xer_ca),hreg_t1,4); /* $t0 += $rb */ x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_t0,hreg_rb); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_alu_membase_reg(iop->ob_ptr,X86_OR,X86_EDI,OFFSET(cpu_ppc_t,xer_ca), hreg_t1); x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_rd,hreg_rd); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); /* update cr0 */ if (insn & 1) ppc32_update_cr0(b); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SUBFE - Subtract From Extended */ DECLARE_INSN(SUBFE) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; /* $rd = ~$ra + $carry (xer_ca) + $rb */ ppc32_jit_start_hreg_seq(cpu,"subfe"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,3,"subfe"); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); /* $t0 = ~$ra + $carry */ x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); x86_not_reg(iop->ob_ptr,hreg_t0); x86_alu_reg_membase(iop->ob_ptr,X86_ADD,hreg_t0, X86_EDI,OFFSET(cpu_ppc_t,xer_ca)); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,xer_ca),hreg_t1,4); /* $t0 += $rb */ x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_t0,hreg_rb); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_alu_membase_reg(iop->ob_ptr,X86_OR,X86_EDI,OFFSET(cpu_ppc_t,xer_ca), hreg_t1); x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_rd,hreg_rd); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); /* update cr0 */ if (insn & 1) ppc32_update_cr0(b); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SUBFIC - Subtract From Immediate Carrying */ DECLARE_INSN(SUBFIC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_ra,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; /* $rd = ~$ra + 1 + sign_extend(imm,16) */ ppc32_jit_start_hreg_seq(cpu,"subfic"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,3,"subfic"); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); /* $t0 = ~$ra + 1 */ x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); x86_not_reg(iop->ob_ptr,hreg_t0); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_t0,1); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,xer_ca),hreg_t1,4); /* $t0 += sign_extend(imm,16) */ x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_t0,tmp); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_alu_membase_reg(iop->ob_ptr,X86_OR,X86_EDI,OFFSET(cpu_ppc_t,xer_ca), hreg_t1); x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SYNC - Synchronize */ DECLARE_INSN(SYNC) { return(0); } /* XOR */ DECLARE_INSN(XOR) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = $rs ^ $rb */ ppc32_jit_start_hreg_seq(cpu,"xor"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"xor"); if (ra == rs) x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb); else if (ra == rb) x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rs); else { x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* XORI - XOR Immediate */ DECLARE_INSN(XORI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs ^ imm */ ppc32_jit_start_hreg_seq(cpu,"xori"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"xori"); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_imm(iop->ob_ptr,X86_XOR,hreg_ra,imm); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* XORIS - XOR Immediate Shifted */ DECLARE_INSN(XORIS) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs ^ (imm << 16) */ ppc32_jit_start_hreg_seq(cpu,"xoris"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"xoris"); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_imm(iop->ob_ptr,X86_XOR,hreg_ra,tmp); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* PPC instruction array */ struct ppc32_insn_tag ppc32_insn_tags[] = { { ppc32_emit_BLR , 0xfffffffe , 0x4e800020 }, { ppc32_emit_BCTR , 0xfffffffe , 0x4e800420 }, { ppc32_emit_MFLR , 0xfc1fffff , 0x7c0802a6 }, { ppc32_emit_MTLR , 0xfc1fffff , 0x7c0803a6 }, { ppc32_emit_MFCTR , 0xfc1fffff , 0x7c0902a6 }, { ppc32_emit_MTCTR , 0xfc1fffff , 0x7c0903a6 }, { ppc32_emit_MFTBL , 0xfc1ff7ff , 0x7c0c42e6 }, { ppc32_emit_MFTBU , 0xfc1ff7ff , 0x7c0d42e6 }, { ppc32_emit_ADD , 0xfc0007fe , 0x7c000214 }, { ppc32_emit_ADDC , 0xfc0007fe , 0x7c000014 }, { ppc32_emit_ADDE , 0xfc0007fe , 0x7c000114 }, { ppc32_emit_ADDI , 0xfc000000 , 0x38000000 }, { ppc32_emit_ADDIC , 0xfc000000 , 0x30000000 }, { ppc32_emit_ADDIC_dot , 0xfc000000 , 0x34000000 }, { ppc32_emit_ADDIS , 0xfc000000 , 0x3c000000 }, { ppc32_emit_ADDZE , 0xfc00fffe , 0x7c000194 }, { ppc32_emit_AND , 0xfc0007fe , 0x7c000038 }, { ppc32_emit_ANDC , 0xfc0007fe , 0x7c000078 }, { ppc32_emit_ANDI , 0xfc000000 , 0x70000000 }, { ppc32_emit_ANDIS , 0xfc000000 , 0x74000000 }, { ppc32_emit_B , 0xfc000003 , 0x48000000 }, { ppc32_emit_BA , 0xfc000003 , 0x48000002 }, { ppc32_emit_BL , 0xfc000003 , 0x48000001 }, { ppc32_emit_BLA , 0xfc000003 , 0x48000003 }, { ppc32_emit_BCC , 0xfe800000 , 0x40800000 }, { ppc32_emit_BC , 0xfc000000 , 0x40000000 }, { ppc32_emit_BCLR , 0xfc00fffe , 0x4c000020 }, { ppc32_emit_CMP , 0xfc6007ff , 0x7c000000 }, { ppc32_emit_CMPI , 0xfc600000 , 0x2c000000 }, { ppc32_emit_CMPL , 0xfc6007ff , 0x7c000040 }, { ppc32_emit_CMPLI , 0xfc600000 , 0x28000000 }, { ppc32_emit_CRAND , 0xfc0007ff , 0x4c000202 }, { ppc32_emit_CRANDC , 0xfc0007ff , 0x4c000102 }, { ppc32_emit_CREQV , 0xfc0007ff , 0x4c000242 }, { ppc32_emit_CRNAND , 0xfc0007ff , 0x4c0001c2 }, { ppc32_emit_CRNOR , 0xfc0007ff , 0x4c000042 }, { ppc32_emit_CROR , 0xfc0007ff , 0x4c000382 }, { ppc32_emit_CRORC , 0xfc0007ff , 0x4c000342 }, { ppc32_emit_CRXOR , 0xfc0007ff , 0x4c000182 }, { ppc32_emit_DIVWU , 0xfc0007fe , 0x7c000396 }, { ppc32_emit_EQV , 0xfc0007fe , 0x7c000238 }, { ppc32_emit_EXTSB , 0xfc00fffe , 0x7c000774 }, { ppc32_emit_EXTSH , 0xfc00fffe , 0x7c000734 }, { ppc32_emit_LBZ , 0xfc000000 , 0x88000000 }, { ppc32_emit_LBZU , 0xfc000000 , 0x8c000000 }, { ppc32_emit_LBZUX , 0xfc0007ff , 0x7c0000ee }, { ppc32_emit_LBZX , 0xfc0007ff , 0x7c0000ae }, { ppc32_emit_LHA , 0xfc000000 , 0xa8000000 }, { ppc32_emit_LHAU , 0xfc000000 , 0xac000000 }, { ppc32_emit_LHAUX , 0xfc0007ff , 0x7c0002ee }, { ppc32_emit_LHAX , 0xfc0007ff , 0x7c0002ae }, { ppc32_emit_LHZ , 0xfc000000 , 0xa0000000 }, { ppc32_emit_LHZU , 0xfc000000 , 0xa4000000 }, { ppc32_emit_LHZUX , 0xfc0007ff , 0x7c00026e }, { ppc32_emit_LHZX , 0xfc0007ff , 0x7c00022e }, { ppc32_emit_LWZ , 0xfc000000 , 0x80000000 }, { ppc32_emit_LWZU , 0xfc000000 , 0x84000000 }, { ppc32_emit_LWZUX , 0xfc0007ff , 0x7c00006e }, { ppc32_emit_LWZX , 0xfc0007ff , 0x7c00002e }, { ppc32_emit_MCRF , 0xfc63ffff , 0x4c000000 }, { ppc32_emit_MFCR , 0xfc1fffff , 0x7c000026 }, { ppc32_emit_MFMSR , 0xfc1fffff , 0x7c0000a6 }, { ppc32_emit_MFSR , 0xfc10ffff , 0x7c0004a6 }, { ppc32_emit_MTCRF , 0xfc100fff , 0x7c000120 }, { ppc32_emit_MULHW , 0xfc0007fe , 0x7c000096 }, { ppc32_emit_MULHWU , 0xfc0007fe , 0x7c000016 }, { ppc32_emit_MULLI , 0xfc000000 , 0x1c000000 }, { ppc32_emit_MULLW , 0xfc0007fe , 0x7c0001d6 }, { ppc32_emit_NAND , 0xfc0007fe , 0x7c0003b8 }, { ppc32_emit_NEG , 0xfc00fffe , 0x7c0000d0 }, { ppc32_emit_NOR , 0xfc0007fe , 0x7c0000f8 }, { ppc32_emit_OR , 0xfc0007fe , 0x7c000378 }, { ppc32_emit_ORC , 0xfc0007fe , 0x7c000338 }, { ppc32_emit_ORI , 0xfc000000 , 0x60000000 }, { ppc32_emit_ORIS , 0xfc000000 , 0x64000000 }, { ppc32_emit_RLWIMI , 0xfc000000 , 0x50000000 }, { ppc32_emit_RLWINM , 0xfc000000 , 0x54000000 }, { ppc32_emit_RLWNM , 0xfc000000 , 0x5c000000 }, { ppc32_emit_SLW , 0xfc0007fe , 0x7c000030 }, { ppc32_emit_SRAWI , 0xfc0007fe , 0x7c000670 }, { ppc32_emit_SRW , 0xfc0007fe , 0x7c000430 }, { ppc32_emit_STB , 0xfc000000 , 0x98000000 }, { ppc32_emit_STBU , 0xfc000000 , 0x9c000000 }, { ppc32_emit_STBUX , 0xfc0007ff , 0x7c0001ee }, { ppc32_emit_STBX , 0xfc0007ff , 0x7c0001ae }, { ppc32_emit_STH , 0xfc000000 , 0xb0000000 }, { ppc32_emit_STHU , 0xfc000000 , 0xb4000000 }, { ppc32_emit_STHUX , 0xfc0007ff , 0x7c00036e }, { ppc32_emit_STHX , 0xfc0007ff , 0x7c00032e }, { ppc32_emit_STW , 0xfc000000 , 0x90000000 }, { ppc32_emit_STWU , 0xfc000000 , 0x94000000 }, { ppc32_emit_STWUX , 0xfc0007ff , 0x7c00016e }, { ppc32_emit_STWX , 0xfc0007ff , 0x7c00012e }, { ppc32_emit_SUBF , 0xfc0007fe , 0x7c000050 }, { ppc32_emit_SUBFC , 0xfc0007fe , 0x7c000010 }, { ppc32_emit_SUBFE , 0xfc0007fe , 0x7c000110 }, { ppc32_emit_SUBFIC , 0xfc000000 , 0x20000000 }, { ppc32_emit_SYNC , 0xffffffff , 0x7c0004ac }, { ppc32_emit_XOR , 0xfc0007fe , 0x7c000278 }, { ppc32_emit_XORI , 0xfc000000 , 0x68000000 }, { ppc32_emit_XORIS , 0xfc000000 , 0x6c000000 }, { ppc32_emit_unknown , 0x00000000 , 0x00000000 }, { NULL , 0x00000000 , 0x00000000 }, }; dynamips-0.2.14/stable/utils.h000066400000000000000000000236261241034141600162120ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __UTILS_H__ #define __UTILS_H__ #include "dynamips_common.h" #include #include #include /* Host CPU Types */ #define CPU_x86 0 #define CPU_amd64 1 #define CPU_nojit 2 /* Number of host registers available for JIT */ #if JIT_CPU == CPU_x86 #define JIT_HOST_NREG 8 #elif JIT_CPU == CPU_amd64 #define JIT_HOST_NREG 16 #else #define JIT_HOST_NREG 0 #endif /* Host to VM (big-endian) conversion functions */ #if ARCH_BYTE_ORDER == ARCH_BIG_ENDIAN #define htovm16(x) (x) #define htovm32(x) (x) #define htovm64(x) (x) #define vmtoh16(x) (x) #define vmtoh32(x) (x) #define vmtoh64(x) (x) #else #define htovm16(x) (htons(x)) #define htovm32(x) (htonl(x)) #define htovm64(x) (swap64(x)) #define vmtoh16(x) (ntohs(x)) #define vmtoh32(x) (ntohl(x)) #define vmtoh64(x) (swap64(x)) #endif /* FD pool */ #define FD_POOL_MAX 16 typedef struct fd_pool fd_pool_t; struct fd_pool { int fd[FD_POOL_MAX]; struct fd_pool *next; }; /* Forward declarations */ typedef struct cpu_gen cpu_gen_t; typedef struct vm_instance vm_instance_t; typedef struct vm_platform vm_platform_t; typedef struct mips64_jit_tcb mips64_jit_tcb_t; typedef struct ppc32_jit_tcb ppc32_jit_tcb_t; typedef struct jit_op jit_op_t; /* Translated block function pointer */ typedef void (*insn_tblock_fptr)(void); /* Host executable page */ typedef struct insn_exec_page insn_exec_page_t; struct insn_exec_page { u_char *ptr; insn_exec_page_t *next; }; /* MIPS instruction */ typedef m_uint32_t mips_insn_t; /* PowerPC instruction */ typedef m_uint32_t ppc_insn_t; /* MMAP */ #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif /* List item */ typedef struct m_list m_list_t; struct m_list { void *data; m_list_t *next; }; /* MTS mapping info */ typedef struct { m_uint64_t vaddr; m_uint64_t paddr; m_uint64_t len; m_uint32_t cached; m_uint32_t tlb_index; m_uint32_t offset; }mts_map_t; /* Invalid VTLB entry */ #define MTS_INV_ENTRY_MASK 0x00000001 /* MTS entry flags */ #define MTS_FLAG_DEV 0x000000001 /* Virtual device used */ #define MTS_FLAG_COW 0x000000002 /* Copy-On-Write */ #define MTS_FLAG_EXEC 0x000000004 /* Exec page */ #define MTS_FLAG_RO 0x000000008 /* Read-only page */ #define MTS_FLAG_WRCATCH (MTS_FLAG_RO|MTS_FLAG_COW) /* Catch writes */ /* Virtual TLB entry (32-bit MMU) */ typedef struct mts32_entry mts32_entry_t; struct mts32_entry { m_uint32_t gvpa; /* Guest Virtual Page Address */ m_uint32_t gppa; /* Guest Physical Page Address */ m_iptr_t hpa; /* Host Page Address */ m_uint32_t flags; /* Flags */ }__attribute__ ((aligned(16))); /* Virtual TLB entry (64-bit MMU) */ typedef struct mts64_entry mts64_entry_t; struct mts64_entry { m_uint64_t gvpa; /* Guest Virtual Page Address */ m_uint64_t gppa; /* Guest Physical Page Address */ m_iptr_t hpa; /* Host Page Address */ m_uint32_t flags; /* Flags */ }__attribute__ ((aligned(16))); /* Host register allocation */ #define HREG_FLAG_ALLOC_LOCKED 1 #define HREG_FLAG_ALLOC_FORCED 2 struct hreg_map { int hreg,vreg; int flags; struct hreg_map *prev,*next; }; /* Global logfile */ extern FILE *log_file; /* Check status of a bit */ static inline int check_bit(u_int old,u_int new,u_int bit) { int mask = 1 << bit; if ((old & mask) && !(new & mask)) return(1); /* bit unset */ if (!(old & mask) && (new & mask)) return(2); /* bit set */ /* no change */ return(0); } /* Sign-extension */ static forced_inline m_int64_t sign_extend(m_int64_t x,int len) { len = 64 - len; return (x << len) >> len; } /* Sign-extension (32-bit) */ static forced_inline m_int32_t sign_extend_32(m_int32_t x,int len) { len = 32 - len; return (x << len) >> len; } /* Extract bits from a 32-bit values */ static inline int bits(m_uint32_t val,int start,int end) { return((val >> start) & ((1 << (end-start+1)) - 1)); } /* Normalize a size */ static inline u_int normalize_size(u_int val,u_int nb,int shift) { return(((val+nb-1) & ~(nb-1)) >> shift); } /* Convert a 16-bit number between little and big endian */ static forced_inline m_uint16_t swap16(m_uint16_t value) { return((value >> 8) | ((value & 0xFF) << 8)); } /* Convert a 32-bit number between little and big endian */ static forced_inline m_uint32_t swap32(m_uint32_t value) { m_uint32_t result; result = value >> 24; result |= ((value >> 16) & 0xff) << 8; result |= ((value >> 8) & 0xff) << 16; result |= (value & 0xff) << 24; return(result); } /* Convert a 64-bit number between little and big endian */ static forced_inline m_uint64_t swap64(m_uint64_t value) { m_uint64_t result; result = (m_uint64_t)swap32(value & 0xffffffff) << 32; result |= swap32(value >> 32); return(result); } /* Get current time in number of msec since epoch */ static inline m_tmcnt_t m_gettime(void) { struct timeval tvp; gettimeofday(&tvp,NULL); return(((m_tmcnt_t)tvp.tv_sec * 1000) + ((m_tmcnt_t)tvp.tv_usec / 1000)); } /* Get current time in number of usec since epoch */ static inline m_tmcnt_t m_gettime_usec(void) { struct timeval tvp; gettimeofday(&tvp,NULL); return(((m_tmcnt_t)tvp.tv_sec * 1000000) + (m_tmcnt_t)tvp.tv_usec); } #ifdef __CYGWIN__ #define GET_TIMEZONE _timezone #else #define GET_TIMEZONE timezone #endif /* Get current time in number of ms (localtime) */ static inline m_tmcnt_t m_gettime_adj(void) { struct timeval tvp; struct tm tmx; time_t gmt_adjust; time_t ct; gettimeofday(&tvp,NULL); ct = tvp.tv_sec; localtime_r(&ct,&tmx); #if defined(__CYGWIN__) || defined(SUNOS) gmt_adjust = -(tmx.tm_isdst ? GET_TIMEZONE - 3600 : GET_TIMEZONE); #else gmt_adjust = tmx.tm_gmtoff; #endif tvp.tv_sec += gmt_adjust; return(((m_tmcnt_t)tvp.tv_sec * 1000) + ((m_tmcnt_t)tvp.tv_usec / 1000)); } /* Get a byte-swapped 16-bit value on a non-aligned area */ static inline m_uint16_t m_ntoh16(m_uint8_t *ptr) { m_uint16_t val = (ptr[0] << 8) | ptr[1]; return(val); } /* Get a byte-swapped 32-bit value on a non-aligned area */ static inline m_uint32_t m_ntoh32(m_uint8_t *ptr) { m_uint32_t val = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]; return(val); } /* Set a byte-swapped 16-bit value on a non-aligned area */ static inline void m_hton16(m_uint8_t *ptr,m_uint16_t val) { ptr[0] = val >> 8; ptr[1] = val; } /* Set a byte-swapped 32-bit value on a non-aligned area */ static inline void m_hton32(m_uint8_t *ptr,m_uint32_t val) { ptr[0] = val >> 24; ptr[1] = val >> 16; ptr[2] = val >> 8; ptr[3] = val; } /* Add an element to a list */ m_list_t *m_list_add(m_list_t **head,void *data); /* Dynamic sprintf */ char *dyn_sprintf(const char *fmt,...); /* Split a string */ int m_strsplit(char *str,char delim,char **array,int max_count); /* Tokenize a string */ int m_strtok(char *str,char delim,char **array,int max_count); /* Quote a string */ char *m_strquote(char *buffer,size_t buf_len,char *str); /* Decode from hex. */ int hex_decode(unsigned char *out,const unsigned char *in,int maxlen); /* Ugly function that dumps a structure in hexa and ascii. */ void mem_dump(FILE *f_output,u_char *pkt,u_int len); /* Logging function */ void m_flog(FILE *fd,char *module,char *fmt,va_list ap); /* Logging function */ void m_log(char *module,char *fmt,...); /* Write an array of string to a logfile */ void m_flog_str_array(FILE *fd,int count,char *str[]); /* Returns a line from specified file (remove trailing '\n') */ char *m_fgets(char *buffer,int size,FILE *fd); /* Read a file and returns it in a buffer */ int m_read_file(const char *filename,u_char **buffer,size_t *length); /* Allocate aligned memory */ void *m_memalign(size_t boundary,size_t size); /* Block specified signal for calling thread */ int m_signal_block(int sig); /* Unblock specified signal for calling thread */ int m_signal_unblock(int sig); /* Set non-blocking mode on a file descriptor */ int m_fd_set_non_block(int fd); /* Sync a memory zone */ int memzone_sync(void *addr, size_t len); /* Sync all mappings of a memory zone */ int memzone_sync_all(void *addr, size_t len); /* Unmap a memory zone */ int memzone_unmap(void *addr, size_t len); /* Map a memory zone as an executable area */ u_char *memzone_map_exec_area(size_t len); /* Map a memory zone from a file */ u_char *memzone_map_file(int fd,size_t len); /* Map a memory zone from a file, with copy-on-write (COW) */ u_char *memzone_map_cow_file(int fd,size_t len); /* Create a file to serve as a memory zone */ int memzone_create_file(char *filename,size_t len,u_char **ptr); /* Open a file to serve as a COW memory zone */ int memzone_open_cow_file(char *filename,size_t len,u_char **ptr); /* Open a file and map it in memory */ int memzone_open_file(char *filename,u_char **ptr,off_t *fsize); int memzone_open_file_ro(char *filename,u_char **ptr,off_t *fsize); /* Compute NVRAM checksum */ m_uint16_t nvram_cksum(m_uint16_t *ptr,size_t count); /* Byte-swap a memory block */ void mem_bswap32(void *ptr,size_t len); /* Reverse a byte */ m_uint8_t m_reverse_u8(m_uint8_t val); /* Generate a pseudo random block of data */ void m_randomize_block(m_uint8_t *buf,size_t len); /* Free an FD pool */ void fd_pool_free(fd_pool_t *pool); /* Initialize an empty pool */ void fd_pool_init(fd_pool_t *pool); /* Get a free slot for a FD in a pool */ int fd_pool_get_free_slot(fd_pool_t *pool,int **slot); /* Fill a FD set and get the maximum FD in order to use with select */ int fd_pool_set_fds(fd_pool_t *pool,fd_set *fds); /* Send a buffer to all FDs of a pool */ int fd_pool_send(fd_pool_t *pool,void *buffer,size_t len,int flags); /* Call a function for each FD having incoming data */ int fd_pool_check_input(fd_pool_t *pool,fd_set *fds, void (*cbk)(int *fd_slot,void *opt),void *opt); /* Equivalent to fprintf, but for a posix fd */ ssize_t fd_printf(int fd,int flags,char *fmt,...); #endif dynamips-0.2.14/stable/vm.c000066400000000000000000000740371241034141600154710ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Virtual machine abstraction. */ #include #include #include #include #include #include #include #include #include #include "registry.h" #include "device.h" #include "pci_dev.h" #include "pci_io.h" #include "cpu.h" #include "vm.h" #include "mips64_jit.h" #include "dev_vtty.h" #include MIPS64_ARCH_INC_FILE #define DEBUG_VM 1 #define VM_GLOCK() pthread_mutex_lock(&vm_global_lock) #define VM_GUNLOCK() pthread_mutex_unlock(&vm_global_lock) /* Type of VM file naming (0=use VM name, 1=use instance ID) */ int vm_file_naming_type = 0; /* Platform list */ static struct vm_platform_list *vm_platforms = NULL; /* Pool of ghost images */ static vm_ghost_image_t *vm_ghost_pool = NULL; /* Global lock for VM manipulation */ static pthread_mutex_t vm_global_lock = PTHREAD_MUTEX_INITIALIZER; /* Free all chunks used by a VM */ static void vm_chunk_free_all(vm_instance_t *vm); /* Initialize a VM object */ void vm_object_init(vm_obj_t *obj) { memset(obj,0,sizeof(*obj)); } /* Add a VM object to an instance */ void vm_object_add(vm_instance_t *vm,vm_obj_t *obj) { obj->next = vm->vm_object_list; obj->pprev = &vm->vm_object_list; if (vm->vm_object_list) vm->vm_object_list->pprev = &obj->next; vm->vm_object_list = obj; } /* Remove a VM object from an instance */ void vm_object_remove(vm_instance_t *vm,vm_obj_t *obj) { if (obj->next) obj->next->pprev = obj->pprev; *(obj->pprev) = obj->next; obj->shutdown(vm,obj->data); } /* Find an object given its name */ vm_obj_t *vm_object_find(vm_instance_t *vm,char *name) { vm_obj_t *obj; for(obj=vm->vm_object_list;obj;obj=obj->next) if (!strcmp(obj->name,name)) return obj; return NULL; } /* Check that a mandatory object is present */ int vm_object_check(vm_instance_t *vm,char *name) { return(vm_object_find(vm,name) ? 0 : -1); } /* Shut down all objects of an instance */ void vm_object_free_list(vm_instance_t *vm) { vm_obj_t *obj,*next; for(obj=vm->vm_object_list;obj;obj=next) { next = obj->next; if (obj->shutdown != NULL) { #if DEBUG_VM vm_log(vm,"VM_OBJECT","Shutdown of object \"%s\"\n",obj->name); #endif obj->shutdown(vm,obj->data); } } vm->vm_object_list = NULL; } /* Rebuild the object list pointers */ _unused static void vm_object_rebuild_list(vm_instance_t *vm) { vm_obj_t **obj; for(obj=&vm->vm_object_list;*obj;obj=&(*obj)->next) (*obj)->pprev = obj; } /* Dump the object list of an instance */ void vm_object_dump(vm_instance_t *vm) { vm_obj_t *obj; printf("VM \"%s\" (%u) object list:\n",vm->name,vm->instance_id); for(obj=vm->vm_object_list;obj;obj=obj->next) { printf(" - %-15s [data=%p]\n",obj->name,obj->data); } printf("\n"); } /* Get VM type */ char *vm_get_type(vm_instance_t *vm) { return vm->platform->name; } /* Get log name */ static char *vm_get_log_name(vm_instance_t *vm) { if (vm->platform->log_name != NULL) return vm->platform->log_name; /* default value */ return "VM"; } /* Get MAC address MSB */ u_int vm_get_mac_addr_msb(vm_instance_t *vm) { if (vm->platform->get_mac_addr_msb != NULL) return(vm->platform->get_mac_addr_msb()); /* default value */ return(0xC6); } /* Generate a filename for use by the instance */ char *vm_build_filename(vm_instance_t *vm,char *name) { char *filename,*machine; machine = vm_get_type(vm); switch(vm_file_naming_type) { case 1: filename = dyn_sprintf("%s_i%u_%s",machine,vm->instance_id,name); break; case 0: default: filename = dyn_sprintf("%s_%s_%s",machine,vm->name,name); break; } assert(filename != NULL); return filename; } /* Get the amount of host virtual memory used by a VM */ size_t vm_get_vspace_size(vm_instance_t *vm) { struct vdevice *dev; size_t hsize = 0; /* Add memory used by CPU (exec area) */ /* XXX TODO */ /* Add memory used by devices */ for(dev=vm->dev_list;dev;dev=dev->next) hsize += dev_get_vspace_size(dev); return(hsize); } /* Erase lock file */ void vm_release_lock(vm_instance_t *vm,int erase) { if (vm->lock_fd != NULL) { fclose(vm->lock_fd); vm->lock_fd = NULL; } if (vm->lock_file != NULL) { if (erase) unlink(vm->lock_file); free(vm->lock_file); vm->lock_file = NULL; } } /* Check that an instance lock file doesn't already exist */ int vm_get_lock(vm_instance_t *vm) { char pid_str[32]; struct flock lock; vm->lock_file = vm_build_filename(vm,"lock"); if (!(vm->lock_fd = fopen(vm->lock_file,"w"))) { fprintf(stderr,"Unable to create lock file \"%s\".\n",vm->lock_file); return(-1); } memset(&lock,0,sizeof(lock)); lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(fileno(vm->lock_fd),F_SETLK,&lock) == -1) { if (fcntl(fileno(vm->lock_fd),F_GETLK,&lock) == 0) { snprintf(pid_str,sizeof(pid_str),"%ld",(long)lock.l_pid); } else { strcpy(pid_str,"unknown"); } fprintf(stderr, "\nAn emulator instance (PID %s) is already running with " "identifier %u.\n" "If this is not the case, please erase file \"%s\".\n\n", pid_str,vm->instance_id,vm->lock_file); vm_release_lock(vm,FALSE); return(-1); } /* write the emulator PID */ fprintf(vm->lock_fd,"%ld\n",(u_long)getpid()); return(0); } /* Log a message */ void vm_flog(vm_instance_t *vm,char *module,char *format,va_list ap) { if (vm->log_fd) m_flog(vm->log_fd,module,format,ap); } /* Log a message */ void vm_log(vm_instance_t *vm,char *module,char *format,...) { va_list ap; if (vm->log_fd) { va_start(ap,format); vm_flog(vm,module,format,ap); va_end(ap); } } /* Close the log file */ int vm_close_log(vm_instance_t *vm) { if (vm->log_fd) fclose(vm->log_fd); free(vm->log_file); vm->log_file = NULL; vm->log_fd = NULL; return(0); } /* Create the log file */ int vm_create_log(vm_instance_t *vm) { if (vm->log_file_enabled) { vm_close_log(vm); if (!(vm->log_file = vm_build_filename(vm,"log.txt"))) return(-1); if (!(vm->log_fd = fopen(vm->log_file,"w"))) { fprintf(stderr,"VM %s: unable to create log file '%s'\n", vm->name,vm->log_file); free(vm->log_file); vm->log_file = NULL; return(-1); } } return(0); } /* Reopen the log file */ int vm_reopen_log(vm_instance_t *vm) { if (vm->log_file_enabled) { vm_close_log(vm); if (!(vm->log_file = vm_build_filename(vm,"log.txt"))) return(-1); if (!(vm->log_fd = fopen(vm->log_file,"a"))) { fprintf(stderr,"VM %s: unable to reopen log file '%s'\n", vm->name,vm->log_file); free(vm->log_file); vm->log_file = NULL; return(-1); } } return(0); } /* Error message */ void vm_error(vm_instance_t *vm,char *format,...) { char buffer[2048]; va_list ap; va_start(ap,format); vsnprintf(buffer,sizeof(buffer),format,ap); va_end(ap); fprintf(stderr,"%s '%s': %s",vm_get_log_name(vm),vm->name,buffer); } /* Create a new VM instance */ static vm_instance_t *vm_create(char *name,int instance_id, vm_platform_t *platform) { vm_instance_t *vm; if (!(vm = malloc(sizeof(*vm)))) { fprintf(stderr,"VM %s: unable to create new instance!\n",name); return NULL; } memset(vm,0,sizeof(*vm)); if (!(vm->name = strdup(name))) { fprintf(stderr,"VM %s: unable to store instance name!\n",name); goto err_name; } vm->instance_id = instance_id; vm->platform = platform; vm->status = VM_STATUS_HALTED; vm->jit_use = JIT_SUPPORT; vm->exec_blk_direct_jump = TRUE; vm->vtty_con_type = VTTY_TYPE_TERM; vm->vtty_aux_type = VTTY_TYPE_NONE; vm->timer_irq_check_itv = VM_TIMER_IRQ_CHECK_ITV; vm->log_file_enabled = TRUE; vm->rommon_vars.filename = vm_build_filename(vm,"rommon_vars"); if (!vm->rommon_vars.filename) goto err_rommon; /* XXX */ rommon_load_file(&vm->rommon_vars); /* create lock file */ if (vm_get_lock(vm) == -1) goto err_lock; /* create log file */ if (vm_create_log(vm) == -1) goto err_log; if (registry_add(vm->name,OBJ_TYPE_VM,vm) == -1) { fprintf(stderr,"VM: Unable to store instance '%s' in registry!\n", vm->name); goto err_reg_add; } m_log("VM","VM %s created.\n",vm->name); return vm; err_reg_add: vm_close_log(vm); err_log: free(vm->lock_file); err_lock: free(vm->rommon_vars.filename); err_rommon: free(vm->name); err_name: free(vm); return NULL; } /* * Shutdown hardware resources used by a VM. * The CPU must have been stopped. */ int vm_hardware_shutdown(vm_instance_t *vm) { int i; if ((vm->status == VM_STATUS_HALTED) || !vm->cpu_group) { vm_log(vm,"VM","trying to shutdown an inactive VM.\n"); return(-1); } vm_log(vm,"VM","shutdown procedure engaged.\n"); /* Mark the VM as halted */ vm->status = VM_STATUS_HALTED; /* Free the object list */ vm_object_free_list(vm); /* Free resources used by PCI busses */ vm_log(vm,"VM","removing PCI busses.\n"); pci_io_data_remove(vm,vm->pci_io_space); pci_bus_remove(vm->pci_bus[0]); pci_bus_remove(vm->pci_bus[1]); vm->pci_bus[0] = vm->pci_bus[1] = NULL; /* Free the PCI bus pool */ for(i=0;ipci_bus_pool[i] != NULL) { pci_bus_remove(vm->pci_bus_pool[i]); vm->pci_bus_pool[i] = NULL; } } /* Remove the IRQ routing vectors */ vm->set_irq = NULL; vm->clear_irq = NULL; /* Delete the VTTY for Console and AUX ports */ vm_log(vm,"VM","deleting VTTY.\n"); vm_delete_vtty(vm); /* Delete system CPU group */ vm_log(vm,"VM","deleting system CPUs.\n"); cpu_group_delete(vm->cpu_group); vm->cpu_group = NULL; vm->boot_cpu = NULL; vm_log(vm,"VM","shutdown procedure completed.\n"); m_log("VM","VM %s shutdown.\n",vm->name); return(0); } /* Free resources used by a VM */ void vm_free(vm_instance_t *vm) { if (vm != NULL) { /* Free hardware resources */ vm_hardware_shutdown(vm); m_log("VM","VM %s destroyed.\n",vm->name); /* Close log file */ vm_close_log(vm); /* Remove the lock file */ vm_release_lock(vm,TRUE); /* Free all chunks */ vm_chunk_free_all(vm); /* Free various elements */ rommon_var_clear(&vm->rommon_vars); free(vm->rommon_vars.filename); free(vm->ghost_ram_filename); free(vm->sym_filename); free(vm->ios_image); free(vm->ios_startup_config); free(vm->ios_private_config); free(vm->rom_filename); free(vm->name); free(vm); } } /* Get an instance given a name */ vm_instance_t *vm_acquire(char *name) { return(registry_find(name,OBJ_TYPE_VM)); } /* Release a VM (decrement reference count) */ int vm_release(vm_instance_t *vm) { return(registry_unref(vm->name,OBJ_TYPE_VM)); } /* Initialize RAM */ int vm_ram_init(vm_instance_t *vm,m_uint64_t paddr) { m_uint32_t len; len = vm->ram_size * 1048576; if (vm->ghost_status == VM_GHOST_RAM_USE) { return(dev_ram_ghost_init(vm,"ram",vm->sparse_mem,vm->ghost_ram_filename, paddr,len)); } return(dev_ram_init(vm,"ram",vm->ram_mmap, (vm->ghost_status != VM_GHOST_RAM_GENERATE), vm->ghost_ram_filename,vm->sparse_mem,paddr,len)); } /* Initialize VTTY */ int vm_init_vtty(vm_instance_t *vm) { /* Create Console and AUX ports */ vm->vtty_con = vtty_create(vm,"Console port", vm->vtty_con_type,vm->vtty_con_tcp_port, &vm->vtty_con_serial_option); vm->vtty_aux = vtty_create(vm,"AUX port", vm->vtty_aux_type,vm->vtty_aux_tcp_port, &vm->vtty_aux_serial_option); return(0); } /* Delete VTTY */ void vm_delete_vtty(vm_instance_t *vm) { vtty_delete(vm->vtty_con); vtty_delete(vm->vtty_aux); vm->vtty_con = vm->vtty_aux = NULL; } /* Bind a device to a virtual machine */ int vm_bind_device(vm_instance_t *vm,struct vdevice *dev) { struct vdevice **cur; u_int i; /* * Add this device to the device array. The index in the device array * is used by the MTS subsystem. */ for(i=0;idev_array[i]) break; if (i == VM_DEVICE_MAX) { fprintf(stderr,"VM%u: vm_bind_device: device table full.\n", vm->instance_id); return(-1); } vm->dev_array[i] = dev; dev->id = i; /* * Add it to the linked-list (devices are ordered by physical addresses). */ for(cur=&vm->dev_list;*cur;cur=&(*cur)->next) if ((*cur)->phys_addr > dev->phys_addr) break; dev->next = *cur; if (*cur) (*cur)->pprev = &dev->next; dev->pprev = cur; *cur = dev; return(0); } /* Unbind a device from a virtual machine */ int vm_unbind_device(vm_instance_t *vm,struct vdevice *dev) { u_int i; if (!dev || !dev->pprev) return(-1); /* Remove the device from the linked list */ if (dev->next) dev->next->pprev = dev->pprev; *(dev->pprev) = dev->next; /* Remove the device from the device array */ for(i=0;idev_array[i] == dev) { vm->dev_array[i] = NULL; break; } /* Clear device list info */ dev->next = NULL; dev->pprev = NULL; return(0); } /* Map a device at the specified physical address */ int vm_map_device(vm_instance_t *vm,struct vdevice *dev,m_uint64_t base_addr) { #if 0 /* Suspend VM activity */ vm_suspend(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { fprintf(stderr,"VM%u: unable to sync with system CPUs.\n", vm->instance_id); return(-1); } #endif /* Unbind the device if it was already active */ vm_unbind_device(vm,dev); /* Map the device at the new base address and rebuild MTS */ dev->phys_addr = base_addr; vm_bind_device(vm,dev); cpu_group_rebuild_mts(vm->cpu_group); #if 0 vm_resume(vm); #endif return(0); } /* Suspend a VM instance */ int vm_suspend(vm_instance_t *vm) { if (vm->status == VM_STATUS_RUNNING) { cpu_group_save_state(vm->cpu_group); cpu_group_set_state(vm->cpu_group,CPU_STATE_SUSPENDED); vm->status = VM_STATUS_SUSPENDED; } return(0); } /* Resume a VM instance */ int vm_resume(vm_instance_t *vm) { if (vm->status == VM_STATUS_SUSPENDED) { cpu_group_restore_state(vm->cpu_group); vm->status = VM_STATUS_RUNNING; } return(0); } /* Stop an instance */ int vm_stop(vm_instance_t *vm) { cpu_group_stop_all_cpu(vm->cpu_group); vm->status = VM_STATUS_SHUTDOWN; return(0); } /* Monitor an instance periodically */ void vm_monitor(vm_instance_t *vm) { while(vm->status != VM_STATUS_SHUTDOWN) usleep(200000); } /* Create a new chunk */ static vm_chunk_t *vm_chunk_create(vm_instance_t *vm) { vm_chunk_t *chunk; size_t area_len; if (!(chunk = malloc(sizeof(*chunk)))) return NULL; area_len = VM_CHUNK_AREA_SIZE * VM_PAGE_SIZE; if (!(chunk->area = m_memalign(VM_PAGE_SIZE,area_len))) { free(chunk); return NULL; } chunk->page_alloc = 0; chunk->page_total = VM_CHUNK_AREA_SIZE; chunk->next = vm->chunks; vm->chunks = chunk; return chunk; } /* Free a chunk */ static void vm_chunk_free(vm_chunk_t *chunk) { free(chunk->area); free(chunk); } /* Free all chunks used by a VM */ static void vm_chunk_free_all(vm_instance_t *vm) { vm_chunk_t *chunk,*next; for(chunk=vm->chunks;chunk;chunk=next) { next = chunk->next; vm_chunk_free(chunk); } vm->chunks = NULL; } /* Allocate an host page */ void *vm_alloc_host_page(vm_instance_t *vm) { vm_chunk_t *chunk = vm->chunks; void *ptr; if (!chunk || (chunk->page_alloc == chunk->page_total)) { chunk = vm_chunk_create(vm); if (!chunk) return NULL; } ptr = chunk->area + (chunk->page_alloc * VM_PAGE_SIZE); chunk->page_alloc++; return(ptr); } /* Free resources used by a ghost image */ static void vm_ghost_image_free(vm_ghost_image_t *img) { if (img) { if (img->fd != -1) { close(img->fd); if (img->area_ptr != NULL) memzone_unmap(img->area_ptr,img->file_size); } free(img->filename); free(img); } } /* Find a specified ghost image in the pool */ static vm_ghost_image_t *vm_ghost_image_find(char *filename) { vm_ghost_image_t *img; for(img=vm_ghost_pool;img;img=img->next) if (!strcmp(img->filename,filename)) return img; return NULL; } /* Load a new ghost image */ static vm_ghost_image_t *vm_ghost_image_load(char *filename) { vm_ghost_image_t *img; if (!(img = calloc(1,sizeof(*img)))) return NULL; img->fd = -1; if (!(img->filename = strdup(filename))) { vm_ghost_image_free(img); return NULL; } img->fd = memzone_open_file_ro(img->filename,&img->area_ptr,&img->file_size); if (img->fd == -1) { vm_ghost_image_free(img); return NULL; } m_log("GHOST","loaded ghost image %s (fd=%d) at addr=%p (size=0x%llx)\n", img->filename,img->fd,img->area_ptr,(long long)img->file_size); return img; } /* Get a ghost image */ int vm_ghost_image_get(char *filename,u_char **ptr,int *fd) { vm_ghost_image_t *img; VM_GLOCK(); /* Do we already have this image in the pool ? */ if ((img = vm_ghost_image_find(filename)) != NULL) { img->ref_count++; *ptr = img->area_ptr; *fd = img->fd; VM_GUNLOCK(); return(0); } /* Load the ghost file and add it into the pool */ if (!(img = vm_ghost_image_load(filename))) { VM_GUNLOCK(); fprintf(stderr,"Unable to load ghost image %s\n",filename); return(-1); } img->ref_count = 1; *ptr = img->area_ptr; *fd = img->fd; img->next = vm_ghost_pool; vm_ghost_pool = img; VM_GUNLOCK(); m_log("GHOST","loaded image %s successfully.\n",filename); return(0); } /* Release a ghost image */ int vm_ghost_image_release(int fd) { vm_ghost_image_t **img,*next; VM_GLOCK(); for(img=&vm_ghost_pool;*img;img=&(*img)->next) { if ((*img)->fd == fd) { assert((*img)->ref_count > 0); (*img)->ref_count--; if ((*img)->ref_count == 0) { m_log("GHOST","unloaded ghost image %s (fd=%d) at " "addr=%p (size=0x%llx)\n", (*img)->filename,(*img)->fd,(*img)->area_ptr, (long long)(*img)->file_size); next = (*img)->next; vm_ghost_image_free(*img); *img = next; } VM_GUNLOCK(); return(0); } } VM_GUNLOCK(); return(-1); } /* Open a VM file and map it in memory */ int vm_mmap_open_file(vm_instance_t *vm,char *name, u_char **ptr,off_t *fsize) { char *filename; int fd; if (!(filename = vm_build_filename(vm,name))) { fprintf(stderr,"vm_mmap_open_file: unable to create filename (%s)\n", name); return(-1); } if ((fd = memzone_open_file(filename,ptr,fsize)) == -1) fprintf(stderr,"vm_mmap_open_file: unable to open file '%s' (%s)\n", filename,strerror(errno)); free(filename); return(fd); } /* Open/Create a VM file and map it in memory */ int vm_mmap_create_file(vm_instance_t *vm,char *name,size_t len,u_char **ptr) { char *filename; int fd; if (!(filename = vm_build_filename(vm,name))) { fprintf(stderr,"vm_mmap_create_file: unable to create filename (%s)\n", name); return(-1); } if ((fd = memzone_create_file(filename,len,ptr)) == -1) fprintf(stderr,"vm_mmap_create_file: unable to open file '%s' (%s)\n", filename,strerror(errno)); free(filename); return(fd); } /* Close a memory mapped file */ int vm_mmap_close_file(int fd,u_char *ptr,size_t len) { if (ptr != NULL) memzone_unmap(ptr,len); if (fd != -1) close(fd); return(0); } /* Save the Cisco IOS configuration from NVRAM */ int vm_ios_save_config(vm_instance_t *vm) { char *output; int res; if (!(output = vm_build_filename(vm,"ios_cfg.txt"))) return(-1); res = vm_nvram_extract_config(vm,output); free(output); return(res); } /* Set Cisco IOS image to use */ int vm_ios_set_image(vm_instance_t *vm,char *ios_image) { char *str; if (!(str = strdup(ios_image))) return(-1); if (vm->ios_image != NULL) { free(vm->ios_image); vm->ios_image = NULL; } vm->ios_image = str; return(0); } /* Unset a Cisco IOS configuration file */ void vm_ios_unset_config(vm_instance_t *vm) { free(vm->ios_startup_config); vm->ios_startup_config = NULL; free(vm->ios_private_config); vm->ios_private_config = NULL; } /* Set Cisco IOS configuration files to use (NULL to keep existing data) */ int vm_ios_set_config(vm_instance_t *vm,const char *startup_filename,const char *private_filename) { char *startup_file = NULL; char *private_file = NULL; if (startup_filename) { startup_file = strdup(startup_filename); if (startup_file == NULL) goto err_memory; } if (private_filename) { private_file = strdup(private_filename); if (private_file == NULL) goto err_memory; } vm_ios_unset_config(vm); vm->ios_startup_config = startup_file; vm->ios_private_config = private_file; return(0); err_memory: free(startup_file); free(private_file); return(-1); } /* Extract IOS configuration from NVRAM and write it to a file */ int vm_nvram_extract_config(vm_instance_t *vm,char *filename) { u_char *cfg_buffer = NULL; size_t cfg_len; FILE *fd; if (!vm->platform->nvram_extract_config) return(-1); /* Extract the IOS configuration */ if ((vm->platform->nvram_extract_config(vm,&cfg_buffer,&cfg_len,NULL,NULL)) || (cfg_buffer == NULL)) return(-1); /* Write configuration to the specified filename */ if (!(fd = fopen(filename,"w"))) { vm_error(vm,"unable to create file '%s'\n",filename); free(cfg_buffer); return(-1); } fwrite(cfg_buffer,cfg_len,1,fd); fclose(fd); free(cfg_buffer); return(0); } /* Read IOS configuraton from the files and push it to NVRAM (NULL to keep existing data) */ int vm_nvram_push_config(vm_instance_t *vm,const char *startup_filename,const char *private_filename) { u_char *startup_config = NULL; u_char *private_config = NULL; size_t startup_len = 0; size_t private_len = 0; int res = -1; /* Read configuration */ if (startup_filename) { if (m_read_file(startup_filename, &startup_config, &startup_len)) goto cleanup; } if (private_filename) { if (m_read_file(private_filename, &private_config, &private_len)) goto cleanup; } /* Push it! */ res = vm->platform->nvram_push_config(vm, startup_config, startup_len, private_config, private_len); cleanup: free(startup_config); free(private_config); return(res); } /* Save general VM configuration into the specified file */ void vm_save_config(vm_instance_t *vm,FILE *fd) { fprintf(fd,"vm create %s %u %s\n", vm->name,vm->instance_id,vm->platform->name); if (vm->ios_image) fprintf(fd,"vm set_ios %s %s\n",vm->name,vm->ios_image); fprintf(fd,"vm set_ram %s %u\n",vm->name,vm->ram_size); fprintf(fd,"vm set_nvram %s %u\n",vm->name,vm->nvram_size); fprintf(fd,"vm set_ram_mmap %s %u\n",vm->name,vm->ram_mmap); fprintf(fd,"vm set_clock_divisor %s %u\n",vm->name,vm->clock_divisor); fprintf(fd,"vm set_conf_reg %s 0x%4.4x\n",vm->name,vm->conf_reg_setup); if (vm->vtty_con_type == VTTY_TYPE_TCP) fprintf(fd,"vm set_con_tcp_port %s %d\n", vm->name,vm->vtty_con_tcp_port); if (vm->vtty_aux_type == VTTY_TYPE_TCP) fprintf(fd,"vm set_aux_tcp_port %s %d\n", vm->name,vm->vtty_aux_tcp_port); /* Save slot config */ vm_slot_save_all_config(vm,fd); } /* Find a platform */ vm_platform_t *vm_platform_find(char *name) { struct vm_platform_list *p; for(p=vm_platforms;p;p=p->next) if (!strcmp(p->platform->name,name)) return(p->platform); return NULL; } /* Find a platform given its CLI name */ vm_platform_t *vm_platform_find_cli_name(char *name) { struct vm_platform_list *p; for(p=vm_platforms;p;p=p->next) if (!strcmp(p->platform->cli_name,name)) return(p->platform); return NULL; } /* Destroy vm_platforms */ static void destroy_vm_platforms(void) { struct vm_platform_list *p, *next; for (p = vm_platforms; p ;p = next) { next = p->next; free(p); } vm_platforms = NULL; } /* Register a platform */ int vm_platform_register(vm_platform_t *platform) { struct vm_platform_list *p; if (vm_platform_find(platform->name) != NULL) { fprintf(stderr,"vm_platform_register: platform '%s' already exists.\n", platform->name); return(-1); } if (!(p = malloc(sizeof(*p)))) { fprintf(stderr,"vm_platform_register: unable to record platform.\n"); return(-1); } if (!vm_platforms) { atexit(destroy_vm_platforms); } p->platform = platform; p->next = vm_platforms; vm_platforms = p; return(0); } /* Create an instance of the specified type */ vm_instance_t *vm_create_instance(char *name,int instance_id,char *type) { vm_platform_t *platform; vm_instance_t *vm = NULL; if (!(platform = vm_platform_find(type))) { fprintf(stderr,"VM %s: unknown platform '%s'\n",name,type); goto error; } /* Create a generic VM instance */ if (!(vm = vm_create(name,instance_id,platform))) goto error; /* Initialize specific parts */ if (vm->platform->create_instance(vm) == -1) goto error; return vm; error: fprintf(stderr,"VM %s: unable to create instance!\n",name); vm_free(vm); return NULL; } /* Free resources used by a VM instance */ static int vm_reg_delete_instance(void *data,void *arg) { vm_instance_t *vm = data; return(vm->platform->delete_instance(vm)); } /* Delete a VM instance */ int vm_delete_instance(char *name) { return(registry_delete_if_unused(name,OBJ_TYPE_VM, vm_reg_delete_instance,NULL)); } /* Rename a VM instance */ int vm_rename_instance(vm_instance_t *vm, char *name) { char *old_name; char *old_lock_file = NULL; FILE *old_lock_fd = NULL; glob_t globbuf; size_t i; char *pattern = NULL; char *filename; int do_rename = 0; if (name == NULL || vm == NULL) goto err_invalid; /* invalid argument */ if (vm->status != VM_STATUS_HALTED) goto err_not_stopped; /* VM is not stopped */ if (strcmp(vm->name, name) == 0) return(0); /* same name, done */ if (registry_exists(name,OBJ_TYPE_VM)) goto err_exists; /* name already exists */ old_name = vm->name; vm->name = NULL; if(!(vm->name = strdup(name))) goto err_strdup; /* out of memory */ /* get new lock */ do_rename = ( vm_file_naming_type != 1 ); if (do_rename) { old_lock_file = vm->lock_file; old_lock_fd = vm->lock_fd; vm->lock_file = NULL; vm->lock_fd = NULL; if (vm_get_lock(vm) == -1) goto err_lock; } if (registry_rename(old_name,vm->name,OBJ_TYPE_VM)) goto err_registry; /* failed to rename */ vm_log(vm,"VM","renamed from '%s' to '%s'",old_name,vm->name); /* rename files (best effort) */ if (do_rename) { fclose(old_lock_fd); unlink(old_lock_file); free(old_lock_file); vm_close_log(vm); if ((pattern = dyn_sprintf("%s_%s_*",vm_get_type(vm),old_name)) == NULL) goto skip_rename; if (glob(pattern, GLOB_NOSORT, NULL, &globbuf) != 0) goto skip_rename; for (i = 0; i < globbuf.gl_pathc; i++) { if ((filename = dyn_sprintf("%s_%s_%s",vm_get_type(vm),vm->name,globbuf.gl_pathv[i] + strlen(pattern) - 1)) == NULL) break; /* out of memory */ rename(globbuf.gl_pathv[i], filename); free(filename); } globfree(&globbuf); skip_rename: free(pattern); vm_reopen_log(vm); } free(old_name); return(0); // done err_registry: err_lock: err_strdup: free(vm->name); vm->name = old_name; if (do_rename) { vm_release_lock(vm,TRUE); vm->lock_file = old_lock_file; vm->lock_fd = old_lock_fd; } err_exists: err_not_stopped: err_invalid: return(-1); } /* Initialize a VM instance */ int vm_init_instance(vm_instance_t *vm) { return(vm->platform->init_instance(vm)); } /* Stop a VM instance */ int vm_stop_instance(vm_instance_t *vm) { return(vm->platform->stop_instance(vm)); } /* Delete all VM instances */ int vm_delete_all_instances(void) { return(registry_delete_type(OBJ_TYPE_VM,vm_reg_delete_instance,NULL)); } /* Save configurations of all VM instances */ static void vm_reg_save_config(registry_entry_t *entry,void *opt,int *err) { vm_instance_t *vm = entry->data; FILE *fd = opt; vm_save_config(vm,fd); /* Save specific platform options */ if (vm->platform->save_config != NULL) vm->platform->save_config(vm,fd); } /* Save all VM configs */ int vm_save_config_all(FILE *fd) { registry_foreach_type(OBJ_TYPE_VM,vm_reg_save_config,fd,NULL); return(0); } /* OIR to start a slot/subslot */ int vm_oir_start(vm_instance_t *vm,u_int slot,u_int subslot) { if (vm->platform->oir_start != NULL) return(vm->platform->oir_start(vm,slot,subslot)); /* OIR not supported */ return(-1); } /* OIR to stop a slot/subslot */ int vm_oir_stop(vm_instance_t *vm,u_int slot,u_int subslot) { if (vm->platform->oir_stop != NULL) return(vm->platform->oir_stop(vm,slot,subslot)); /* OIR not supported */ return(-1); } dynamips-0.2.14/stable/vm.h000066400000000000000000000275101241034141600154700ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Virtual Machines. */ #ifndef __VM_H__ #define __VM_H__ #include #include "dynamips.h" #include "memory.h" #include "cpu.h" #include "dev_vtty.h" #include "cisco_eeprom.h" #include "cisco_card.h" #include "rommon_var.h" #define VM_PAGE_SHIFT 12 #define VM_PAGE_SIZE (1 << VM_PAGE_SHIFT) #define VM_PAGE_IMASK (VM_PAGE_SIZE - 1) #define VM_PAGE_MASK (~(VM_PAGE_IMASK)) /* Number of pages in chunk area */ #define VM_CHUNK_AREA_SIZE 256 /* VM memory chunk */ typedef struct vm_chunk vm_chunk_t; struct vm_chunk { void *area; u_int page_alloc,page_total; vm_chunk_t *next; }; /* VM ghost pool entry */ typedef struct vm_ghost_image vm_ghost_image_t; struct vm_ghost_image { char *filename; u_int ref_count; int fd; off_t file_size; u_char *area_ptr; vm_ghost_image_t *next; }; /* Maximum number of devices per VM */ #define VM_DEVICE_MAX (1 << 6) /* Size of the PCI bus pool */ #define VM_PCI_POOL_SIZE 32 /* VM instance status */ enum { VM_STATUS_HALTED = 0, /* VM is halted and no HW resources are used */ VM_STATUS_SHUTDOWN, /* Shutdown procedure engaged */ VM_STATUS_RUNNING, /* VM is running */ VM_STATUS_SUSPENDED, /* VM is suspended */ }; /* Ghost RAM status */ enum { VM_GHOST_RAM_NONE = 0, VM_GHOST_RAM_GENERATE, VM_GHOST_RAM_USE, }; /* Timer IRQ check interval */ #define VM_TIMER_IRQ_CHECK_ITV 1000 /* Max slots per VM */ #define VM_MAX_SLOTS 16 /* forward declarations */ typedef struct vm_obj vm_obj_t; /* Shutdown function prototype for an object */ typedef void *(*vm_shutdown_t)(vm_instance_t *vm,void *data); /* VM object, used to keep track of devices and various things */ struct vm_obj { char *name; void *data; struct vm_obj *next,**pprev; vm_shutdown_t shutdown; }; /* VM instance */ struct vm_instance { char *name; vm_platform_t *platform; /* Platform specific helpers */ int status; /* Instance status */ int instance_id; /* Instance Identifier */ char *lock_file; /* Lock file */ char *log_file; /* Log filename */ int log_file_enabled; /* Logging enabled */ u_int ram_size,rom_size; /* RAM and ROM size in Mb */ u_int ram_res_size; /* RAM reserved space size */ u_int iomem_size; /* IOMEM size in Mb */ u_int nvram_size; /* NVRAM size in Kb */ u_int pcmcia_disk_size[2]; /* PCMCIA disk0 and disk1 sizes (in Mb) */ u_int conf_reg,conf_reg_setup; /* Config register */ u_int clock_divisor; /* Clock Divisor (see cp0.c) */ u_int ram_mmap; /* Memory-mapped RAM ? */ u_int restart_ios; /* Restart IOS on reload ? */ u_int elf_machine_id; /* ELF machine identifier */ u_int exec_area_size; /* Size of execution area for CPU */ m_uint32_t ios_entry_point; /* IOS entry point */ char *ios_image; /* IOS image filename */ char *ios_startup_config; /* IOS configuration file for startup-config */ char *ios_private_config; /* IOS configuration file for private-config */ char *rom_filename; /* ROM filename */ char *sym_filename; /* Symbol filename */ FILE *lock_fd,*log_fd; /* Lock/Log file descriptors */ int debug_level; /* Debugging Level */ int jit_use; /* CPUs use JIT */ int sparse_mem; /* Use sparse virtual memory */ u_int nm_iomem_size; /* IO mem size to be passed to Smart Init */ /* ROMMON variables */ struct rommon_var_list rommon_vars; /* Memory chunks */ vm_chunk_t *chunks; /* Basic hardware: system CPU, PCI busses and PCI I/O space */ cpu_group_t *cpu_group; cpu_gen_t *boot_cpu; struct pci_bus *pci_bus[2]; struct pci_bus *pci_bus_pool[VM_PCI_POOL_SIZE]; struct pci_io_data *pci_io_space; /* Memory mapped devices */ struct vdevice *dev_list; struct vdevice *dev_array[VM_DEVICE_MAX]; /* IRQ routing */ void (*set_irq)(vm_instance_t *vm,u_int irq); void (*clear_irq)(vm_instance_t *vm,u_int irq); /* Slots for PA/NM/... */ u_int nr_slots; u_int slots_type; struct cisco_card *slots[VM_MAX_SLOTS]; struct cisco_card_driver **slots_drivers; struct pci_bus *slots_pci_bus[VM_MAX_SLOTS]; /* Filename for ghosted RAM */ char *ghost_ram_filename; /* Ghost RAM image handling */ int ghost_status; /* Timer IRQ interval check */ u_int timer_irq_check_itv; /* "idling" pointer counter */ m_uint64_t idle_pc; /* JIT block direct jumps */ int exec_blk_direct_jump; /* IRQ idling preemption */ u_int irq_idle_preempt[256]; /* Console and AUX port VTTY type and parameters */ int vtty_con_type,vtty_aux_type; int vtty_con_tcp_port,vtty_aux_tcp_port; vtty_serial_option_t vtty_con_serial_option,vtty_aux_serial_option; /* Virtual TTY for Console and AUX ports */ vtty_t *vtty_con,*vtty_aux; /* Space reserved in NVRAM by ROM monitor */ u_int nvram_rom_space; /* Chassis cookie (for c2600 and maybe other routers) */ m_uint16_t chassis_cookie[64]; /* Specific hardware data */ void *hw_data; /* VM objects */ struct vm_obj *vm_object_list; }; /* VM Platform definition */ struct vm_platform { char *name; char *log_name; char *cli_name; int (*create_instance)(vm_instance_t *vm); int (*delete_instance)(vm_instance_t *vm); int (*init_instance)(vm_instance_t *vm); int (*stop_instance)(vm_instance_t *vm); int (*oir_start)(vm_instance_t *vm,u_int slot_id,u_int subslot_id); int (*oir_stop)(vm_instance_t *vm,u_int slot_id,u_int subslot_id); int (*nvram_extract_config)(vm_instance_t *vm,u_char **startup_config,size_t *startup_len,u_char **private_config,size_t *private_len); int (*nvram_push_config)(vm_instance_t *vm,u_char *startup_config,size_t startup_len,u_char *private_config,size_t private_len); u_int (*get_mac_addr_msb)(void); void (*save_config)(vm_instance_t *vm,FILE *fd); int (*cli_parse_options)(vm_instance_t *vm,int option); void (*cli_show_options)(vm_instance_t *vm); void (*show_spec_drivers)(void); }; /* VM platform list item */ struct vm_platform_list { struct vm_platform_list *next; struct vm_platform *platform; }; extern int vm_file_naming_type; /* Set an IRQ for a VM */ static inline void vm_set_irq(vm_instance_t *vm,u_int irq) { if (vm->set_irq != NULL) vm->set_irq(vm,irq); } /* Clear an IRQ for a VM */ static inline void vm_clear_irq(vm_instance_t *vm,u_int irq) { if (vm->clear_irq != NULL) vm->clear_irq(vm,irq); } /* Initialize a VM object */ void vm_object_init(vm_obj_t *obj); /* Add a VM object to an instance */ void vm_object_add(vm_instance_t *vm,vm_obj_t *obj); /* Remove a VM object from an instance */ void vm_object_remove(vm_instance_t *vm,vm_obj_t *obj); /* Find an object given its name */ vm_obj_t *vm_object_find(vm_instance_t *vm,char *name); /* Check that a mandatory object is present */ int vm_object_check(vm_instance_t *vm,char *name); /* Dump the object list of an instance */ void vm_object_dump(vm_instance_t *vm); /* Get VM type */ char *vm_get_type(vm_instance_t *vm); /* Get MAC address MSB */ u_int vm_get_mac_addr_msb(vm_instance_t *vm); /* Generate a filename for use by the instance */ char *vm_build_filename(vm_instance_t *vm,char *name); /* Get the amount of host virtual memory used by a VM */ size_t vm_get_vspace_size(vm_instance_t *vm); /* Check that an instance lock file doesn't already exist */ int vm_get_lock(vm_instance_t *vm); /* Erase lock file */ void vm_release_lock(vm_instance_t *vm,int erase); /* Log a message */ void vm_flog(vm_instance_t *vm,char *module,char *format,va_list ap); /* Log a message */ void vm_log(vm_instance_t *vm,char *module,char *format,...); /* Close the log file */ int vm_close_log(vm_instance_t *vm); /* Create the log file */ int vm_create_log(vm_instance_t *vm); /* Reopen the log file */ int vm_reopen_log(vm_instance_t *vm); /* Error message */ void vm_error(vm_instance_t *vm,char *format,...); /* Shutdown hardware resources used by a VM */ int vm_hardware_shutdown(vm_instance_t *vm); /* Free resources used by a VM */ void vm_free(vm_instance_t *vm); /* Get an instance given a name */ vm_instance_t *vm_acquire(char *name); /* Release a VM (decrement reference count) */ int vm_release(vm_instance_t *vm); /* Initialize RAM */ int vm_ram_init(vm_instance_t *vm,m_uint64_t paddr); /* Initialize VTTY */ int vm_init_vtty(vm_instance_t *vm); /* Delete VTTY */ void vm_delete_vtty(vm_instance_t *vm); /* Bind a device to a virtual machine */ int vm_bind_device(vm_instance_t *vm,struct vdevice *dev); /* Unbind a device from a virtual machine */ int vm_unbind_device(vm_instance_t *vm,struct vdevice *dev); /* Map a device at the specified physical address */ int vm_map_device(vm_instance_t *vm,struct vdevice *dev,m_uint64_t base_addr); /* Set an IRQ for a VM */ void vm_set_irq(vm_instance_t *vm,u_int irq); /* Clear an IRQ for a VM */ void vm_clear_irq(vm_instance_t *vm,u_int irq); /* Suspend a VM instance */ int vm_suspend(vm_instance_t *vm); /* Resume a VM instance */ int vm_resume(vm_instance_t *vm); /* Stop an instance */ int vm_stop(vm_instance_t *vm); /* Monitor an instance periodically */ void vm_monitor(vm_instance_t *vm); /* Allocate an host page */ void *vm_alloc_host_page(vm_instance_t *vm); /* Free an host page */ void vm_free_host_page(vm_instance_t *vm,void *ptr); /* Get a ghost image */ int vm_ghost_image_get(char *filename,u_char **ptr,int *fd); /* Release a ghost image */ int vm_ghost_image_release(int fd); /* Open a VM file and map it in memory */ int vm_mmap_open_file(vm_instance_t *vm,char *name, u_char **ptr,off_t *fsize); /* Open/Create a VM file and map it in memory */ int vm_mmap_create_file(vm_instance_t *vm,char *name,size_t len,u_char **ptr); /* Close a memory mapped file */ int vm_mmap_close_file(int fd,u_char *ptr,size_t len); /* Save the Cisco IOS configuration from NVRAM */ int vm_ios_save_config(vm_instance_t *vm); /* Set Cisco IOS image to use */ int vm_ios_set_image(vm_instance_t *vm,char *ios_image); /* Unset a Cisco IOS configuration file */ void vm_ios_unset_config(vm_instance_t *vm); /* Set Cisco IOS configuration files to use (NULL to keep existing data) */ int vm_ios_set_config(vm_instance_t *vm,const char *startup_filename,const char *private_filename); /* Extract IOS configuration from NVRAM and write it to a file */ int vm_nvram_extract_config(vm_instance_t *vm,char *filename); /* Read IOS configuraton from the files and push it to NVRAM (NULL to keep existing data) */ int vm_nvram_push_config(vm_instance_t *vm,const char *startup_filename,const char *private_filename); /* Save general VM configuration into the specified file */ void vm_save_config(vm_instance_t *vm,FILE *fd); /* Find a platform */ vm_platform_t *vm_platform_find(char *name); /* Find a platform given its CLI name */ vm_platform_t *vm_platform_find_cli_name(char *name); /* Register a platform */ int vm_platform_register(vm_platform_t *platform); /* Create an instance of the specified type */ vm_instance_t *vm_create_instance(char *name,int instance_id,char *type); /* Delete a VM instance */ int vm_delete_instance(char *name); /* Rename a VM instance */ int vm_rename_instance(vm_instance_t *vm, char *name); /* Initialize a VM instance */ int vm_init_instance(vm_instance_t *vm); /* Stop a VM instance */ int vm_stop_instance(vm_instance_t *vm); /* Delete all VM instances */ int vm_delete_all_instances(void); /* Save all VM configs */ int vm_save_config_all(FILE *fd); /* OIR to start a slot/subslot */ int vm_oir_start(vm_instance_t *vm,u_int slot,u_int subslot); /* OIR to stop a slot/subslot */ int vm_oir_stop(vm_instance_t *vm,u_int slot,u_int subslot); #endif dynamips-0.2.14/stable/x86-codegen.h000066400000000000000000001354531241034141600171030ustar00rootroot00000000000000/* * x86-codegen.h: Macros for generating x86 code * * Authors: * Paolo Molaro (lupus@ximian.com) * Intel Corporation (ORP Project) * Sergey Chaban (serge@wildwestsoftware.com) * Dietmar Maurer (dietmar@ximian.com) * Patrik Torstensson * * Copyright (C) 2000 Intel Corporation. All rights reserved. * Copyright (C) 2001, 2002 Ximian, Inc. */ #ifndef X86_H #define X86_H #include /* // x86 register numbers */ typedef enum { X86_EAX = 0, X86_ECX = 1, X86_EDX = 2, X86_EBX = 3, X86_ESP = 4, X86_EBP = 5, X86_ESI = 6, X86_EDI = 7, X86_NREG } X86_Reg_No; /* // opcodes for alu instructions */ typedef enum { X86_ADD = 0, X86_OR = 1, X86_ADC = 2, X86_SBB = 3, X86_AND = 4, X86_SUB = 5, X86_XOR = 6, X86_CMP = 7, X86_NALU } X86_ALU_Opcode; /* // opcodes for shift instructions */ typedef enum { X86_SHLD, X86_SHLR, X86_ROL = 0, X86_ROR = 1, X86_RCL = 2, X86_RCR = 3, X86_SHL = 4, X86_SHR = 5, X86_SAR = 7, X86_NSHIFT = 8 } X86_Shift_Opcode; /* // opcodes for floating-point instructions */ typedef enum { X86_FADD = 0, X86_FMUL = 1, X86_FCOM = 2, X86_FCOMP = 3, X86_FSUB = 4, X86_FSUBR = 5, X86_FDIV = 6, X86_FDIVR = 7, X86_NFP = 8 } X86_FP_Opcode; /* // integer conditions codes */ typedef enum { X86_CC_EQ = 0, X86_CC_E = 0, X86_CC_Z = 0, X86_CC_NE = 1, X86_CC_NZ = 1, X86_CC_LT = 2, X86_CC_B = 2, X86_CC_C = 2, X86_CC_NAE = 2, X86_CC_LE = 3, X86_CC_BE = 3, X86_CC_NA = 3, X86_CC_GT = 4, X86_CC_A = 4, X86_CC_NBE = 4, X86_CC_GE = 5, X86_CC_AE = 5, X86_CC_NB = 5, X86_CC_NC = 5, X86_CC_LZ = 6, X86_CC_S = 6, X86_CC_GEZ = 7, X86_CC_NS = 7, X86_CC_P = 8, X86_CC_PE = 8, X86_CC_NP = 9, X86_CC_PO = 9, X86_CC_O = 10, X86_CC_NO = 11, X86_NCC } X86_CC; /* FP status */ enum { X86_FP_C0 = 0x100, X86_FP_C1 = 0x200, X86_FP_C2 = 0x400, X86_FP_C3 = 0x4000, X86_FP_CC_MASK = 0x4500 }; /* FP control word */ enum { X86_FPCW_INVOPEX_MASK = 0x1, X86_FPCW_DENOPEX_MASK = 0x2, X86_FPCW_ZERODIV_MASK = 0x4, X86_FPCW_OVFEX_MASK = 0x8, X86_FPCW_UNDFEX_MASK = 0x10, X86_FPCW_PRECEX_MASK = 0x20, X86_FPCW_PRECC_MASK = 0x300, X86_FPCW_ROUNDC_MASK = 0xc00, /* values for precision control */ X86_FPCW_PREC_SINGLE = 0, X86_FPCW_PREC_DOUBLE = 0x200, X86_FPCW_PREC_EXTENDED = 0x300, /* values for rounding control */ X86_FPCW_ROUND_NEAREST = 0, X86_FPCW_ROUND_DOWN = 0x400, X86_FPCW_ROUND_UP = 0x800, X86_FPCW_ROUND_TOZERO = 0xc00 }; /* // prefix code */ typedef enum { X86_LOCK_PREFIX = 0xF0, X86_REPNZ_PREFIX = 0xF2, X86_REPZ_PREFIX = 0xF3, X86_REP_PREFIX = 0xF3, X86_CS_PREFIX = 0x2E, X86_SS_PREFIX = 0x36, X86_DS_PREFIX = 0x3E, X86_ES_PREFIX = 0x26, X86_FS_PREFIX = 0x64, X86_GS_PREFIX = 0x65, X86_UNLIKELY_PREFIX = 0x2E, X86_LIKELY_PREFIX = 0x3E, X86_OPERAND_PREFIX = 0x66, X86_ADDRESS_PREFIX = 0x67 } X86_Prefix; static const unsigned char x86_cc_unsigned_map [X86_NCC] = { 0x74, /* eq */ 0x75, /* ne */ 0x72, /* lt */ 0x76, /* le */ 0x77, /* gt */ 0x73, /* ge */ 0x78, /* lz */ 0x79, /* gez */ 0x7a, /* p */ 0x7b, /* np */ 0x70, /* o */ 0x71, /* no */ }; static const unsigned char x86_cc_signed_map [X86_NCC] = { 0x74, /* eq */ 0x75, /* ne */ 0x7c, /* lt */ 0x7e, /* le */ 0x7f, /* gt */ 0x7d, /* ge */ 0x78, /* lz */ 0x79, /* gez */ 0x7a, /* p */ 0x7b, /* np */ 0x70, /* o */ 0x71, /* no */ }; typedef union { int val; unsigned char b [4]; } x86_imm_buf; #define X86_NOBASEREG (-1) /* // bitvector mask for callee-saved registers */ #define X86_ESI_MASK (1<> 6) #define x86_modrm_reg(modrm) (((modrm) >> 3) & 0x7) #define x86_modrm_rm(modrm) ((modrm) & 0x7) #define x86_address_byte(inst,m,o,r) do { *(inst)++ = ((((m)&0x03)<<6)|(((o)&0x07)<<3)|(((r)&0x07))); } while (0) #define x86_imm_emit32(inst,imm) \ do { \ x86_imm_buf imb; imb.val = (int) (imm); \ *(inst)++ = imb.b [0]; \ *(inst)++ = imb.b [1]; \ *(inst)++ = imb.b [2]; \ *(inst)++ = imb.b [3]; \ } while (0) #define x86_imm_emit16(inst,imm) do { *(short*)(inst) = (imm); (inst) += 2; } while (0) #define x86_imm_emit8(inst,imm) do { *(inst) = (unsigned char)((imm) & 0xff); ++(inst); } while (0) #define x86_is_imm8(imm) (((int)(imm) >= -128 && (int)(imm) <= 127)) #define x86_is_imm16(imm) (((int)(imm) >= -(1<<16) && (int)(imm) <= ((1<<16)-1))) #define x86_reg_emit(inst,r,regno) do { x86_address_byte ((inst), 3, (r), (regno)); } while (0) #define x86_reg8_emit(inst,r,regno,is_rh,is_rnoh) do {x86_address_byte ((inst), 3, (is_rh)?((r)|4):(r), (is_rnoh)?((regno)|4):(regno));} while (0) #define x86_regp_emit(inst,r,regno) do { x86_address_byte ((inst), 0, (r), (regno)); } while (0) #define x86_mem_emit(inst,r,disp) do { x86_address_byte ((inst), 0, (r), 5); x86_imm_emit32((inst), (disp)); } while (0) #define x86_membase_emit(inst,r,basereg,disp) do {\ if ((basereg) == X86_ESP) { \ if ((disp) == 0) { \ x86_address_byte ((inst), 0, (r), X86_ESP); \ x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ } else if (x86_is_imm8((disp))) { \ x86_address_byte ((inst), 1, (r), X86_ESP); \ x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ x86_imm_emit8 ((inst), (disp)); \ } else { \ x86_address_byte ((inst), 2, (r), X86_ESP); \ x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ x86_imm_emit32 ((inst), (disp)); \ } \ break; \ } \ if ((disp) == 0 && (basereg) != X86_EBP) { \ x86_address_byte ((inst), 0, (r), (basereg)); \ break; \ } \ if (x86_is_imm8((disp))) { \ x86_address_byte ((inst), 1, (r), (basereg)); \ x86_imm_emit8 ((inst), (disp)); \ } else { \ x86_address_byte ((inst), 2, (r), (basereg)); \ x86_imm_emit32 ((inst), (disp)); \ } \ } while (0) #define x86_memindex_emit(inst,r,basereg,disp,indexreg,shift) \ do { \ if ((basereg) == X86_NOBASEREG) { \ x86_address_byte ((inst), 0, (r), 4); \ x86_address_byte ((inst), (shift), (indexreg), 5); \ x86_imm_emit32 ((inst), (disp)); \ } else if ((disp) == 0 && (basereg) != X86_EBP) { \ x86_address_byte ((inst), 0, (r), 4); \ x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ } else if (x86_is_imm8((disp))) { \ x86_address_byte ((inst), 1, (r), 4); \ x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ x86_imm_emit8 ((inst), (disp)); \ } else { \ x86_address_byte ((inst), 2, (r), 4); \ x86_address_byte ((inst), (shift), (indexreg), 5); \ x86_imm_emit32 ((inst), (disp)); \ } \ } while (0) /* * target is the position in the code where to jump to: * target = code; * .. output loop code... * x86_mov_reg_imm (code, X86_EAX, 0); * loop = code; * x86_loop (code, -1); * ... finish method * * patch displacement * x86_patch (loop, target); * * ins should point at the start of the instruction that encodes a target. * the instruction is inspected for validity and the correct displacement * is inserted. */ #define x86_patch(ins,target) \ do { \ unsigned char* pos = (ins) + 1; \ int disp, size = 0; \ switch (*(unsigned char*)(ins)) { \ case 0xe8: case 0xe9: ++size; break; /* call, jump32 */ \ case 0x0f: if (!(*pos >= 0x70 && *pos <= 0x8f)) assert (0); \ ++size; ++pos; break; /* prefix for 32-bit disp */ \ case 0xe0: case 0xe1: case 0xe2: /* loop */ \ case 0xeb: /* jump8 */ \ /* conditional jump opcodes */ \ case 0x70: case 0x71: case 0x72: case 0x73: \ case 0x74: case 0x75: case 0x76: case 0x77: \ case 0x78: case 0x79: case 0x7a: case 0x7b: \ case 0x7c: case 0x7d: case 0x7e: case 0x7f: \ break; \ default: assert (0); \ } \ disp = (target) - pos; \ if (size) x86_imm_emit32 (pos, disp - 4); \ else if (x86_is_imm8 (disp - 1)) x86_imm_emit8 (pos, disp - 1); \ else assert (0); \ } while (0) #define x86_breakpoint(inst) \ do { \ *(inst)++ = 0xcc; \ } while (0) #define x86_clc(inst) do { *(inst)++ =(unsigned char)0xf8; } while (0) #define x86_cld(inst) do { *(inst)++ =(unsigned char)0xfc; } while (0) #define x86_stosb(inst) do { *(inst)++ =(unsigned char)0xaa; } while (0) #define x86_stosl(inst) do { *(inst)++ =(unsigned char)0xab; } while (0) #define x86_stosd(inst) x86_stosl((inst)) #define x86_movsb(inst) do { *(inst)++ =(unsigned char)0xa4; } while (0) #define x86_movsl(inst) do { *(inst)++ =(unsigned char)0xa5; } while (0) #define x86_movsd(inst) x86_movsl((inst)) #define x86_prefix(inst,p) do { *(inst)++ =(unsigned char) (p); } while (0) #define x86_bswap(inst,reg) \ do { \ *(inst)++ = 0x0f; \ *(inst)++ = (unsigned char)0xc8 + (reg); \ } while (0) #define x86_rdtsc(inst) \ do { \ *(inst)++ = 0x0f; \ *(inst)++ = 0x31; \ } while (0) #define x86_cmpxchg_reg_reg(inst,dreg,reg) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xb1; \ x86_reg_emit ((inst), (reg), (dreg)); \ } while (0) #define x86_cmpxchg_mem_reg(inst,mem,reg) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xb1; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_cmpxchg_membase_reg(inst,basereg,disp,reg) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xb1; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_xchg_reg_reg(inst,dreg,reg,size) \ do { \ if ((size) == 1) \ *(inst)++ = (unsigned char)0x86; \ else \ *(inst)++ = (unsigned char)0x87; \ x86_reg_emit ((inst), (reg), (dreg)); \ } while (0) #define x86_xchg_mem_reg(inst,mem,reg,size) \ do { \ if ((size) == 1) \ *(inst)++ = (unsigned char)0x86; \ else \ *(inst)++ = (unsigned char)0x87; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_xchg_membase_reg(inst,basereg,disp,reg,size) \ do { \ if ((size) == 1) \ *(inst)++ = (unsigned char)0x86; \ else \ *(inst)++ = (unsigned char)0x87; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_xadd_reg_reg(inst,dreg,reg,size) \ do { \ *(inst)++ = (unsigned char)0x0F; \ if ((size) == 1) \ *(inst)++ = (unsigned char)0xC0; \ else \ *(inst)++ = (unsigned char)0xC1; \ x86_reg_emit ((inst), (reg), (dreg)); \ } while (0) #define x86_xadd_mem_reg(inst,mem,reg,size) \ do { \ *(inst)++ = (unsigned char)0x0F; \ if ((size) == 1) \ *(inst)++ = (unsigned char)0xC0; \ else \ *(inst)++ = (unsigned char)0xC1; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_xadd_membase_reg(inst,basereg,disp,reg,size) \ do { \ *(inst)++ = (unsigned char)0x0F; \ if ((size) == 1) \ *(inst)++ = (unsigned char)0xC0; \ else \ *(inst)++ = (unsigned char)0xC1; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_inc_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_mem_emit ((inst), 0, (mem)); \ } while (0) #define x86_inc_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ } while (0) #define x86_inc_reg(inst,reg) do { *(inst)++ = (unsigned char)0x40 + (reg); } while (0) #define x86_dec_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_mem_emit ((inst), 1, (mem)); \ } while (0) #define x86_dec_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_membase_emit ((inst), 1, (basereg), (disp)); \ } while (0) #define x86_dec_reg(inst,reg) do { *(inst)++ = (unsigned char)0x48 + (reg); } while (0) #define x86_not_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_mem_emit ((inst), 2, (mem)); \ } while (0) #define x86_not_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_membase_emit ((inst), 2, (basereg), (disp)); \ } while (0) #define x86_not_reg(inst,reg) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_reg_emit ((inst), 2, (reg)); \ } while (0) #define x86_neg_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_mem_emit ((inst), 3, (mem)); \ } while (0) #define x86_neg_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_membase_emit ((inst), 3, (basereg), (disp)); \ } while (0) #define x86_neg_reg(inst,reg) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_reg_emit ((inst), 3, (reg)); \ } while (0) #define x86_nop(inst) do { *(inst)++ = (unsigned char)0x90; } while (0) #define x86_alu_reg_imm(inst,opc,reg,imm) \ do { \ if ((reg) == X86_EAX) { \ *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ x86_imm_emit32 ((inst), (imm)); \ break; \ } \ if (x86_is_imm8((imm))) { \ *(inst)++ = (unsigned char)0x83; \ x86_reg_emit ((inst), (opc), (reg)); \ x86_imm_emit8 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0x81; \ x86_reg_emit ((inst), (opc), (reg)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_alu_mem_imm(inst,opc,mem,imm) \ do { \ if (x86_is_imm8((imm))) { \ *(inst)++ = (unsigned char)0x83; \ x86_mem_emit ((inst), (opc), (mem)); \ x86_imm_emit8 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0x81; \ x86_mem_emit ((inst), (opc), (mem)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_alu_membase_imm(inst,opc,basereg,disp,imm) \ do { \ if (x86_is_imm8((imm))) { \ *(inst)++ = (unsigned char)0x83; \ x86_membase_emit ((inst), (opc), (basereg), (disp)); \ x86_imm_emit8 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0x81; \ x86_membase_emit ((inst), (opc), (basereg), (disp)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_alu_membase8_imm(inst,opc,basereg,disp,imm) \ do { \ *(inst)++ = (unsigned char)0x80; \ x86_membase_emit ((inst), (opc), (basereg), (disp)); \ x86_imm_emit8 ((inst), (imm)); \ } while (0) #define x86_alu_mem_reg(inst,opc,mem,reg) \ do { \ *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_alu_membase_reg(inst,opc,basereg,disp,reg) \ do { \ *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_alu_reg_reg(inst,opc,dreg,reg) \ do { \ *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) /** * @x86_alu_reg8_reg8: * Supports ALU operations between two 8-bit registers. * dreg := dreg opc reg * X86_Reg_No enum is used to specify the registers. * Additionally is_*_h flags are used to specify what part * of a given 32-bit register is used - high (TRUE) or low (FALSE). * For example: dreg = X86_EAX, is_dreg_h = TRUE -> use AH */ #define x86_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) \ do { \ *(inst)++ = (((unsigned char)(opc)) << 3) + 2; \ x86_reg8_emit ((inst), (dreg), (reg), (is_dreg_h), (is_reg_h)); \ } while (0) #define x86_alu_reg_mem(inst,opc,reg,mem) \ do { \ *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_alu_reg_membase(inst,opc,reg,basereg,disp) \ do { \ *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_test_reg_imm(inst,reg,imm) \ do { \ if ((reg) == X86_EAX) { \ *(inst)++ = (unsigned char)0xa9; \ } else { \ *(inst)++ = (unsigned char)0xf7; \ x86_reg_emit ((inst), 0, (reg)); \ } \ x86_imm_emit32 ((inst), (imm)); \ } while (0) #define x86_test_mem_imm(inst,mem,imm) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_mem_emit ((inst), 0, (mem)); \ x86_imm_emit32 ((inst), (imm)); \ } while (0) #define x86_test_membase_imm(inst,basereg,disp,imm) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ x86_imm_emit32 ((inst), (imm)); \ } while (0) #define x86_test_reg_reg(inst,dreg,reg) \ do { \ *(inst)++ = (unsigned char)0x85; \ x86_reg_emit ((inst), (reg), (dreg)); \ } while (0) #define x86_test_mem_reg(inst,mem,reg) \ do { \ *(inst)++ = (unsigned char)0x85; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_test_membase_reg(inst,basereg,disp,reg) \ do { \ *(inst)++ = (unsigned char)0x85; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_shift_reg_imm(inst,opc,reg,imm) \ do { \ if ((imm) == 1) { \ *(inst)++ = (unsigned char)0xd1; \ x86_reg_emit ((inst), (opc), (reg)); \ } else { \ *(inst)++ = (unsigned char)0xc1; \ x86_reg_emit ((inst), (opc), (reg)); \ x86_imm_emit8 ((inst), (imm)); \ } \ } while (0) #define x86_shift_mem_imm(inst,opc,mem,imm) \ do { \ if ((imm) == 1) { \ *(inst)++ = (unsigned char)0xd1; \ x86_mem_emit ((inst), (opc), (mem)); \ } else { \ *(inst)++ = (unsigned char)0xc1; \ x86_mem_emit ((inst), (opc), (mem)); \ x86_imm_emit8 ((inst), (imm)); \ } \ } while (0) #define x86_shift_membase_imm(inst,opc,basereg,disp,imm) \ do { \ if ((imm) == 1) { \ *(inst)++ = (unsigned char)0xd1; \ x86_membase_emit ((inst), (opc), (basereg), (disp)); \ } else { \ *(inst)++ = (unsigned char)0xc1; \ x86_membase_emit ((inst), (opc), (basereg), (disp)); \ x86_imm_emit8 ((inst), (imm)); \ } \ } while (0) #define x86_shift_reg(inst,opc,reg) \ do { \ *(inst)++ = (unsigned char)0xd3; \ x86_reg_emit ((inst), (opc), (reg)); \ } while (0) #define x86_shift_mem(inst,opc,mem) \ do { \ *(inst)++ = (unsigned char)0xd3; \ x86_mem_emit ((inst), (opc), (mem)); \ } while (0) #define x86_shift_membase(inst,opc,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xd3; \ x86_membase_emit ((inst), (opc), (basereg), (disp)); \ } while (0) /* * Multi op shift missing. */ #define x86_shrd_reg(inst,dreg,reg) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xad; \ x86_reg_emit ((inst), (reg), (dreg)); \ } while (0) #define x86_shrd_reg_imm(inst,dreg,reg,shamt) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xac; \ x86_reg_emit ((inst), (reg), (dreg)); \ x86_imm_emit8 ((inst), (shamt)); \ } while (0) #define x86_shld_reg(inst,dreg,reg) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xa5; \ x86_reg_emit ((inst), (reg), (dreg)); \ } while (0) #define x86_shld_reg_imm(inst,dreg,reg,shamt) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xa4; \ x86_reg_emit ((inst), (reg), (dreg)); \ x86_imm_emit8 ((inst), (shamt)); \ } while (0) /* * EDX:EAX = EAX * rm */ #define x86_mul_reg(inst,reg,is_signed) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_reg_emit ((inst), 4 + ((is_signed) ? 1 : 0), (reg)); \ } while (0) #define x86_mul_mem(inst,mem,is_signed) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_mem_emit ((inst), 4 + ((is_signed) ? 1 : 0), (mem)); \ } while (0) #define x86_mul_membase(inst,basereg,disp,is_signed) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_membase_emit ((inst), 4 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ } while (0) /* * r *= rm */ #define x86_imul_reg_reg(inst,dreg,reg) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xaf; \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) #define x86_imul_reg_mem(inst,reg,mem) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xaf; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_imul_reg_membase(inst,reg,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xaf; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) /* * dreg = rm * imm */ #define x86_imul_reg_reg_imm(inst,dreg,reg,imm) \ do { \ if (x86_is_imm8 ((imm))) { \ *(inst)++ = (unsigned char)0x6b; \ x86_reg_emit ((inst), (dreg), (reg)); \ x86_imm_emit8 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0x69; \ x86_reg_emit ((inst), (dreg), (reg)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_imul_reg_mem_imm(inst,reg,mem,imm) \ do { \ if (x86_is_imm8 ((imm))) { \ *(inst)++ = (unsigned char)0x6b; \ x86_mem_emit ((inst), (reg), (mem)); \ x86_imm_emit8 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0x69; \ x86_reg_emit ((inst), (reg), (mem)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_imul_reg_membase_imm(inst,reg,basereg,disp,imm) \ do { \ if (x86_is_imm8 ((imm))) { \ *(inst)++ = (unsigned char)0x6b; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ x86_imm_emit8 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0x69; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) /* * divide EDX:EAX by rm; * eax = quotient, edx = remainder */ #define x86_div_reg(inst,reg,is_signed) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_reg_emit ((inst), 6 + ((is_signed) ? 1 : 0), (reg)); \ } while (0) #define x86_div_mem(inst,mem,is_signed) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_mem_emit ((inst), 6 + ((is_signed) ? 1 : 0), (mem)); \ } while (0) #define x86_div_membase(inst,basereg,disp,is_signed) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_membase_emit ((inst), 6 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ } while (0) #define x86_mov_mem_reg(inst,mem,reg,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x88; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x89; break; \ default: assert (0); \ } \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_mov_regp_reg(inst,regp,reg,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x88; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x89; break; \ default: assert (0); \ } \ x86_regp_emit ((inst), (reg), (regp)); \ } while (0) #define x86_mov_membase_reg(inst,basereg,disp,reg,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x88; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x89; break; \ default: assert (0); \ } \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x88; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x89; break; \ default: assert (0); \ } \ x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ } while (0) #define x86_mov_reg_reg(inst,dreg,reg,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x8a; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) #define x86_mov_reg_mem(inst,reg,mem,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x8a; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_mov_reg_membase(inst,reg,basereg,disp,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x8a; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x8a; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ } while (0) /* * Note: x86_clear_reg () chacnges the condition code! */ #define x86_clear_reg(inst,reg) x86_alu_reg_reg((inst), X86_XOR, (reg), (reg)) #define x86_mov_reg_imm(inst,reg,imm) \ do { \ *(inst)++ = (unsigned char)0xb8 + (reg); \ x86_imm_emit32 ((inst), (imm)); \ } while (0) #define x86_mov_mem_imm(inst,mem,imm,size) \ do { \ if ((size) == 1) { \ *(inst)++ = (unsigned char)0xc6; \ x86_mem_emit ((inst), 0, (mem)); \ x86_imm_emit8 ((inst), (imm)); \ } else if ((size) == 2) { \ *(inst)++ = (unsigned char)0x66; \ *(inst)++ = (unsigned char)0xc7; \ x86_mem_emit ((inst), 0, (mem)); \ x86_imm_emit16 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0xc7; \ x86_mem_emit ((inst), 0, (mem)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_mov_membase_imm(inst,basereg,disp,imm,size) \ do { \ if ((size) == 1) { \ *(inst)++ = (unsigned char)0xc6; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ x86_imm_emit8 ((inst), (imm)); \ } else if ((size) == 2) { \ *(inst)++ = (unsigned char)0x66; \ *(inst)++ = (unsigned char)0xc7; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ x86_imm_emit16 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0xc7; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) \ do { \ if ((size) == 1) { \ *(inst)++ = (unsigned char)0xc6; \ x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ x86_imm_emit8 ((inst), (imm)); \ } else if ((size) == 2) { \ *(inst)++ = (unsigned char)0x66; \ *(inst)++ = (unsigned char)0xc7; \ x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ x86_imm_emit16 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0xc7; \ x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_lea_mem(inst,reg,mem) \ do { \ *(inst)++ = (unsigned char)0x8d; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_lea_membase(inst,reg,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0x8d; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_lea_memindex(inst,reg,basereg,disp,indexreg,shift) \ do { \ *(inst)++ = (unsigned char)0x8d; \ x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ } while (0) #define x86_widen_reg(inst,dreg,reg,is_signed,is_half) \ do { \ unsigned char op = 0xb6; \ assert (is_half || X86_IS_BYTE_REG (reg)); \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) op += 0x08; \ if ((is_half)) op += 0x01; \ *(inst)++ = op; \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) #define x86_widen_mem(inst,dreg,mem,is_signed,is_half) \ do { \ unsigned char op = 0xb6; \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) op += 0x08; \ if ((is_half)) op += 0x01; \ *(inst)++ = op; \ x86_mem_emit ((inst), (dreg), (mem)); \ } while (0) #define x86_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) \ do { \ unsigned char op = 0xb6; \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) op += 0x08; \ if ((is_half)) op += 0x01; \ *(inst)++ = op; \ x86_membase_emit ((inst), (dreg), (basereg), (disp)); \ } while (0) #define x86_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) \ do { \ unsigned char op = 0xb6; \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) op += 0x08; \ if ((is_half)) op += 0x01; \ *(inst)++ = op; \ x86_memindex_emit ((inst), (dreg), (basereg), (disp), (indexreg), (shift)); \ } while (0) #define x86_lahf(inst) do { *(inst)++ = (unsigned char)0x9f; } while (0) #define x86_sahf(inst) do { *(inst)++ = (unsigned char)0x9e; } while (0) #define x86_xchg_ah_al(inst) \ do { \ *(inst)++ = (unsigned char)0x86; \ *(inst)++ = (unsigned char)0xe0; \ } while (0) #define x86_cdq(inst) do { *(inst)++ = (unsigned char)0x99; } while (0) #define x86_wait(inst) do { *(inst)++ = (unsigned char)0x9b; } while (0) #define x86_fp_op_mem(inst,opc,mem,is_double) \ do { \ *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ x86_mem_emit ((inst), (opc), (mem)); \ } while (0) #define x86_fp_op_membase(inst,opc,basereg,disp,is_double) \ do { \ *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ x86_membase_emit ((inst), (opc), (basereg), (disp)); \ } while (0) #define x86_fp_op(inst,opc,index) \ do { \ *(inst)++ = (unsigned char)0xd8; \ *(inst)++ = (unsigned char)0xc0+((opc)<<3)+((index)&0x07); \ } while (0) #define x86_fp_op_reg(inst,opc,index,pop_stack) \ do { \ static const unsigned char map[] = { 0, 1, 2, 3, 5, 4, 7, 6, 8}; \ *(inst)++ = (pop_stack) ? (unsigned char)0xde : (unsigned char)0xdc; \ *(inst)++ = (unsigned char)0xc0+(map[(opc)]<<3)+((index)&0x07); \ } while (0) /** * @x86_fp_int_op_membase * Supports FPU operations between ST(0) and integer operand in memory. * Operation encoded using X86_FP_Opcode enum. * Operand is addressed by [basereg + disp]. * is_int specifies whether operand is int32 (TRUE) or int16 (FALSE). */ #define x86_fp_int_op_membase(inst,opc,basereg,disp,is_int) \ do { \ *(inst)++ = (is_int) ? (unsigned char)0xda : (unsigned char)0xde; \ x86_membase_emit ((inst), opc, (basereg), (disp)); \ } while (0) #define x86_fstp(inst,index) \ do { \ *(inst)++ = (unsigned char)0xdd; \ *(inst)++ = (unsigned char)0xd8+(index); \ } while (0) #define x86_fcompp(inst) \ do { \ *(inst)++ = (unsigned char)0xde; \ *(inst)++ = (unsigned char)0xd9; \ } while (0) #define x86_fucompp(inst) \ do { \ *(inst)++ = (unsigned char)0xda; \ *(inst)++ = (unsigned char)0xe9; \ } while (0) #define x86_fnstsw(inst) \ do { \ *(inst)++ = (unsigned char)0xdf; \ *(inst)++ = (unsigned char)0xe0; \ } while (0) #define x86_fnstcw(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xd9; \ x86_mem_emit ((inst), 7, (mem)); \ } while (0) #define x86_fnstcw_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xd9; \ x86_membase_emit ((inst), 7, (basereg), (disp)); \ } while (0) #define x86_fldcw(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xd9; \ x86_mem_emit ((inst), 5, (mem)); \ } while (0) #define x86_fldcw_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xd9; \ x86_membase_emit ((inst), 5, (basereg), (disp)); \ } while (0) #define x86_fchs(inst) \ do { \ *(inst)++ = (unsigned char)0xd9; \ *(inst)++ = (unsigned char)0xe0; \ } while (0) #define x86_frem(inst) \ do { \ *(inst)++ = (unsigned char)0xd9; \ *(inst)++ = (unsigned char)0xf8; \ } while (0) #define x86_fxch(inst,index) \ do { \ *(inst)++ = (unsigned char)0xd9; \ *(inst)++ = (unsigned char)0xc8 + ((index) & 0x07); \ } while (0) #define x86_fcomi(inst,index) \ do { \ *(inst)++ = (unsigned char)0xdb; \ *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ } while (0) #define x86_fcomip(inst,index) \ do { \ *(inst)++ = (unsigned char)0xdf; \ *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ } while (0) #define x86_fucomi(inst,index) \ do { \ *(inst)++ = (unsigned char)0xdb; \ *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ } while (0) #define x86_fucomip(inst,index) \ do { \ *(inst)++ = (unsigned char)0xdf; \ *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ } while (0) #define x86_fld(inst,mem,is_double) \ do { \ *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ x86_mem_emit ((inst), 0, (mem)); \ } while (0) #define x86_fld_membase(inst,basereg,disp,is_double) \ do { \ *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ } while (0) #define x86_fld80_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xdb; \ x86_mem_emit ((inst), 5, (mem)); \ } while (0) #define x86_fld80_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xdb; \ x86_membase_emit ((inst), 5, (basereg), (disp)); \ } while (0) #define x86_fild(inst,mem,is_long) \ do { \ if ((is_long)) { \ *(inst)++ = (unsigned char)0xdf; \ x86_mem_emit ((inst), 5, (mem)); \ } else { \ *(inst)++ = (unsigned char)0xdb; \ x86_mem_emit ((inst), 0, (mem)); \ } \ } while (0) #define x86_fild_membase(inst,basereg,disp,is_long) \ do { \ if ((is_long)) { \ *(inst)++ = (unsigned char)0xdf; \ x86_membase_emit ((inst), 5, (basereg), (disp)); \ } else { \ *(inst)++ = (unsigned char)0xdb; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ } \ } while (0) #define x86_fld_reg(inst,index) \ do { \ *(inst)++ = (unsigned char)0xd9; \ *(inst)++ = (unsigned char)0xc0 + ((index) & 0x07); \ } while (0) #define x86_fldz(inst) \ do { \ *(inst)++ = (unsigned char)0xd9; \ *(inst)++ = (unsigned char)0xee; \ } while (0) #define x86_fld1(inst) \ do { \ *(inst)++ = (unsigned char)0xd9; \ *(inst)++ = (unsigned char)0xe8; \ } while (0) #define x86_fldpi(inst) \ do { \ *(inst)++ = (unsigned char)0xd9; \ *(inst)++ = (unsigned char)0xeb; \ } while (0) #define x86_fst(inst,mem,is_double,pop_stack) \ do { \ *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ x86_mem_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (mem)); \ } while (0) #define x86_fst_membase(inst,basereg,disp,is_double,pop_stack) \ do { \ *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ x86_membase_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (basereg), (disp)); \ } while (0) #define x86_fst80_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xdb; \ x86_mem_emit ((inst), 7, (mem)); \ } while (0) #define x86_fst80_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xdb; \ x86_membase_emit ((inst), 7, (basereg), (disp)); \ } while (0) #define x86_fist_pop(inst,mem,is_long) \ do { \ if ((is_long)) { \ *(inst)++ = (unsigned char)0xdf; \ x86_mem_emit ((inst), 7, (mem)); \ } else { \ *(inst)++ = (unsigned char)0xdb; \ x86_mem_emit ((inst), 3, (mem)); \ } \ } while (0) #define x86_fist_pop_membase(inst,basereg,disp,is_long) \ do { \ if ((is_long)) { \ *(inst)++ = (unsigned char)0xdf; \ x86_membase_emit ((inst), 7, (basereg), (disp)); \ } else { \ *(inst)++ = (unsigned char)0xdb; \ x86_membase_emit ((inst), 3, (basereg), (disp)); \ } \ } while (0) #define x86_fstsw(inst) \ do { \ *(inst)++ = (unsigned char)0x9b; \ *(inst)++ = (unsigned char)0xdf; \ *(inst)++ = (unsigned char)0xe0; \ } while (0) /** * @x86_fist_membase * Converts content of ST(0) to integer and stores it at memory location * addressed by [basereg + disp]. * is_int specifies whether destination is int32 (TRUE) or int16 (FALSE). */ #define x86_fist_membase(inst,basereg,disp,is_int) \ do { \ if ((is_int)) { \ *(inst)++ = (unsigned char)0xdb; \ x86_membase_emit ((inst), 2, (basereg), (disp)); \ } else { \ *(inst)++ = (unsigned char)0xdf; \ x86_membase_emit ((inst), 2, (basereg), (disp)); \ } \ } while (0) #define x86_push_reg(inst,reg) \ do { \ *(inst)++ = (unsigned char)0x50 + (reg); \ } while (0) #define x86_push_regp(inst,reg) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_regp_emit ((inst), 6, (reg)); \ } while (0) #define x86_push_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_mem_emit ((inst), 6, (mem)); \ } while (0) #define x86_push_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_membase_emit ((inst), 6, (basereg), (disp)); \ } while (0) #define x86_push_memindex(inst,basereg,disp,indexreg,shift) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_memindex_emit ((inst), 6, (basereg), (disp), (indexreg), (shift)); \ } while (0) #define x86_push_imm_template(inst) x86_push_imm (inst, 0xf0f0f0f0) #define x86_push_imm(inst,imm) \ do { \ int _imm = (int) (imm); \ if (x86_is_imm8 (_imm)) { \ *(inst)++ = (unsigned char)0x6A; \ x86_imm_emit8 ((inst), (_imm)); \ } else { \ *(inst)++ = (unsigned char)0x68; \ x86_imm_emit32 ((inst), (_imm)); \ } \ } while (0) #define x86_pop_reg(inst,reg) \ do { \ *(inst)++ = (unsigned char)0x58 + (reg); \ } while (0) #define x86_pop_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0x87; \ x86_mem_emit ((inst), 0, (mem)); \ } while (0) #define x86_pop_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0x87; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ } while (0) #define x86_pushad(inst) do { *(inst)++ = (unsigned char)0x60; } while (0) #define x86_pushfd(inst) do { *(inst)++ = (unsigned char)0x9c; } while (0) #define x86_popad(inst) do { *(inst)++ = (unsigned char)0x61; } while (0) #define x86_popfd(inst) do { *(inst)++ = (unsigned char)0x9d; } while (0) #define x86_loop(inst,imm) \ do { \ *(inst)++ = (unsigned char)0xe2; \ x86_imm_emit8 ((inst), (imm)); \ } while (0) #define x86_loope(inst,imm) \ do { \ *(inst)++ = (unsigned char)0xe1; \ x86_imm_emit8 ((inst), (imm)); \ } while (0) #define x86_loopne(inst,imm) \ do { \ *(inst)++ = (unsigned char)0xe0; \ x86_imm_emit8 ((inst), (imm)); \ } while (0) #define x86_jump32(inst,imm) \ do { \ *(inst)++ = (unsigned char)0xe9; \ x86_imm_emit32 ((inst), (imm)); \ } while (0) #define x86_jump8(inst,imm) \ do { \ *(inst)++ = (unsigned char)0xeb; \ x86_imm_emit8 ((inst), (imm)); \ } while (0) #define x86_jump_reg(inst,reg) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_reg_emit ((inst), 4, (reg)); \ } while (0) #define x86_jump_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_mem_emit ((inst), 4, (mem)); \ } while (0) #define x86_jump_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_membase_emit ((inst), 4, (basereg), (disp)); \ } while (0) /* * target is a pointer in our buffer. */ #define x86_jump_code(inst,target) \ do { \ int t = (unsigned char*)(target) - (inst) - 2; \ if (x86_is_imm8(t)) { \ x86_jump8 ((inst), t); \ } else { \ t -= 3; \ x86_jump32 ((inst), t); \ } \ } while (0) #define x86_jump_disp(inst,disp) \ do { \ int t = (disp) - 2; \ if (x86_is_imm8(t)) { \ x86_jump8 ((inst), t); \ } else { \ t -= 3; \ x86_jump32 ((inst), t); \ } \ } while (0) #define x86_branch8(inst,cond,imm,is_signed) \ do { \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)]; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)]; \ x86_imm_emit8 ((inst), (imm)); \ } while (0) #define x86_branch32(inst,cond,imm,is_signed) \ do { \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)] + 0x10; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x10; \ x86_imm_emit32 ((inst), (imm)); \ } while (0) #define x86_branch(inst,cond,target,is_signed) \ do { \ int offset = (target) - (inst) - 2; \ if (x86_is_imm8 ((offset))) \ x86_branch8 ((inst), (cond), offset, (is_signed)); \ else { \ offset -= 4; \ x86_branch32 ((inst), (cond), offset, (is_signed)); \ } \ } while (0) #define x86_branch_disp(inst,cond,disp,is_signed) \ do { \ int offset = (disp) - 2; \ if (x86_is_imm8 ((offset))) \ x86_branch8 ((inst), (cond), offset, (is_signed)); \ else { \ offset -= 4; \ x86_branch32 ((inst), (cond), offset, (is_signed)); \ } \ } while (0) #define x86_set_reg(inst,cond,reg,is_signed) \ do { \ assert (X86_IS_BYTE_REG (reg)); \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ x86_reg_emit ((inst), 0, (reg)); \ } while (0) #define x86_set_mem(inst,cond,mem,is_signed) \ do { \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ x86_mem_emit ((inst), 0, (mem)); \ } while (0) #define x86_set_membase(inst,cond,basereg,disp,is_signed) \ do { \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ } while (0) #define x86_call_imm(inst,disp) \ do { \ *(inst)++ = (unsigned char)0xe8; \ x86_imm_emit32 ((inst), (int)(disp)); \ } while (0) #define x86_call_reg(inst,reg) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_reg_emit ((inst), 2, (reg)); \ } while (0) #define x86_call_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_mem_emit ((inst), 2, (mem)); \ } while (0) #define x86_call_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_membase_emit ((inst), 2, (basereg), (disp)); \ } while (0) #define x86_call_code(inst,target) \ do { \ int _x86_offset = (unsigned char*)(target) - (inst); \ _x86_offset -= 5; \ x86_call_imm ((inst), _x86_offset); \ } while (0) #define x86_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) #define x86_ret_imm(inst,imm) \ do { \ if ((imm) == 0) { \ x86_ret ((inst)); \ } else { \ *(inst)++ = (unsigned char)0xc2; \ x86_imm_emit16 ((inst), (imm)); \ } \ } while (0) #define x86_cmov_reg(inst,cond,is_signed,dreg,reg) \ do { \ *(inst)++ = (unsigned char) 0x0f; \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) #define x86_cmov_mem(inst,cond,is_signed,reg,mem) \ do { \ *(inst)++ = (unsigned char) 0x0f; \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_cmov_membase(inst,cond,is_signed,reg,basereg,disp) \ do { \ *(inst)++ = (unsigned char) 0x0f; \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_enter(inst,framesize) \ do { \ *(inst)++ = (unsigned char)0xc8; \ x86_imm_emit16 ((inst), (framesize)); \ *(inst)++ = 0; \ } while (0) #define x86_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) #define x86_sahf(inst) do { *(inst)++ = (unsigned char)0x9e; } while (0) #define x86_fsin(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfe; } while (0) #define x86_fcos(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xff; } while (0) #define x86_fabs(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe1; } while (0) #define x86_ftst(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe4; } while (0) #define x86_fxam(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe5; } while (0) #define x86_fpatan(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf3; } while (0) #define x86_fprem(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf8; } while (0) #define x86_fprem1(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf5; } while (0) #define x86_frndint(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfc; } while (0) #define x86_fsqrt(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfa; } while (0) #define x86_fptan(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf2; } while (0) #define x86_padding(inst,size) \ do { \ switch ((size)) { \ case 1: x86_nop ((inst)); break; \ case 2: *(inst)++ = 0x8b; \ *(inst)++ = 0xc0; break; \ case 3: *(inst)++ = 0x8d; *(inst)++ = 0x6d; \ *(inst)++ = 0x00; break; \ case 4: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ *(inst)++ = 0x24; *(inst)++ = 0x00; \ break; \ case 5: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ *(inst)++ = 0x24; *(inst)++ = 0x00; \ x86_nop ((inst)); break; \ case 6: *(inst)++ = 0x8d; *(inst)++ = 0xad; \ *(inst)++ = 0x00; *(inst)++ = 0x00; \ *(inst)++ = 0x00; *(inst)++ = 0x00; \ break; \ case 7: *(inst)++ = 0x8d; *(inst)++ = 0xa4; \ *(inst)++ = 0x24; *(inst)++ = 0x00; \ *(inst)++ = 0x00; *(inst)++ = 0x00; \ *(inst)++ = 0x00; break; \ default: assert (0); \ } \ } while (0) #define x86_prolog(inst,frame_size,reg_mask) \ do { \ unsigned i, m = 1; \ x86_enter ((inst), (frame_size)); \ for (i = 0; i < X86_NREG; ++i, m <<= 1) { \ if ((reg_mask) & m) \ x86_push_reg ((inst), i); \ } \ } while (0) #define x86_epilog(inst,reg_mask) \ do { \ unsigned i, m = 1 << X86_EDI; \ for (i = X86_EDI; m != 0; i--, m=m>>1) { \ if ((reg_mask) & m) \ x86_pop_reg ((inst), i); \ } \ x86_leave ((inst)); \ x86_ret ((inst)); \ } while (0) #endif // X86_H dynamips-0.2.14/unstable/000077500000000000000000000000001241034141600152335ustar00rootroot00000000000000dynamips-0.2.14/unstable/CMakeLists.txt000066400000000000000000000155571241034141600200100ustar00rootroot00000000000000# dynamips - unstable code set ( COMMON "${CMAKE_SOURCE_DIR}/common" ) set ( LOCAL "${CMAKE_CURRENT_SOURCE_DIR}" ) include_directories ( "${CMAKE_CURRENT_SOURCE_DIR}" "${COMMON}" "${CMAKE_CURRENT_BINARY_DIR}" ) # mips64_microcode_dump.inc set ( _input "${LOCAL}/mips64_microcode" ) set ( _output "${CMAKE_CURRENT_BINARY_DIR}/mips64_microcode_dump.inc" ) add_custom_command ( OUTPUT ${_output} COMMAND ${ROM2C_EXECUTABLE} ARGS ${_input} ${_output} 0xbfc00000 DEPENDS rom2c ${_input} ) add_custom_target ( mips64_microcode_dump_unstable DEPENDS ${_output} ) # ppc32_microcode_dump.inc set ( _input "${LOCAL}/ppc32_microcode" ) set ( _output "${CMAKE_CURRENT_BINARY_DIR}/ppc32_microcode_dump.inc" ) add_custom_command ( OUTPUT ${_output} COMMAND ${ROM2C_EXECUTABLE} ARGS ${_input} ${_output} 0xfff00000 DEPENDS rom2c ${_input} ) add_custom_target ( ppc32_microcode_dump_unstable DEPENDS ${_output} ) #-------------------------# # dynamips: unstable code # #-------------------------# if ( NOT BUILD_DYNAMIPS_UNSTABLE ) return () endif ( NOT BUILD_DYNAMIPS_UNSTABLE ) # dynamips_*_unstable add_definitions( "-DUSE_UNSTABLE" ) set ( _files "${COMMON}/mempool.c" "${COMMON}/registry.c" "${COMMON}/rbtree.c" "${COMMON}/hash.c" "${COMMON}/sbox.c" "${COMMON}/utils.c" "${COMMON}/parser.c" "${COMMON}/gen_uuid.c" "${COMMON}/plugin.c" "${COMMON}/ptask.c" "${COMMON}/timer.c" "${COMMON}/crc.c" "${COMMON}/base64.c" "${COMMON}/net.c" "${COMMON}/net_io.c" "${COMMON}/net_io_bridge.c" "${COMMON}/net_io_filter.c" "${COMMON}/atm.c" "${COMMON}/atm_vsar.c" "${COMMON}/atm_bridge.c" "${COMMON}/frame_relay.c" "${COMMON}/eth_switch.c" "${COMMON}/dynamips.c" "${COMMON}/insn_lookup.c" "${LOCAL}/vm.c" "${LOCAL}/cpu.c" "${LOCAL}/tcb.c" # only present in unstable "${COMMON}/jit_op.c" "${LOCAL}/mips64.c" "${LOCAL}/mips64_mem.c" "${LOCAL}/mips64_cp0.c" "${LOCAL}/mips64_jit.c" "${LOCAL}/mips64_exec.c" "${LOCAL}/ppc32.c" "${LOCAL}/ppc32_mem.c" "${LOCAL}/ppc32_jit.c" "${LOCAL}/ppc32_exec.c" "${LOCAL}/ppc32_vmtest.c" "${COMMON}/memory.c" "${COMMON}/device.c" "${COMMON}/nmc93cX6.c" "${COMMON}/cisco_eeprom.c" "${COMMON}/cisco_card.c" "${COMMON}/pci_dev.c" "${COMMON}/pci_io.c" "${COMMON}/dev_zero.c" "${COMMON}/dev_bswap.c" "${COMMON}/dev_vtty.c" "${COMMON}/dev_ram.c" "${COMMON}/dev_rom.c" "${COMMON}/dev_nvram.c" "${COMMON}/dev_bootflash.c" "${COMMON}/dev_flash.c" "${COMMON}/dev_mpc860.c" "${COMMON}/dev_ds1620.c" "${COMMON}/dev_remote.c" "${COMMON}/dev_clpd6729.c" "${COMMON}/dev_pcmcia_disk.c" "${COMMON}/dev_gt.c" "${COMMON}/dev_mv64460.c" "${COMMON}/dev_plx.c" "${COMMON}/dev_dec21x50.c" "${COMMON}/dev_pericom.c" "${COMMON}/dev_ti2050b.c" "${COMMON}/dev_ap1011.c" "${COMMON}/dev_plx6520cb.c" "${COMMON}/dev_ns16552.c" "${COMMON}/dev_dec21140.c" "${COMMON}/dev_am79c971.c" "${COMMON}/dev_i8254x.c" "${COMMON}/dev_i8255x.c" "${COMMON}/dev_mueslix.c" "${COMMON}/dev_wic_serial.c" "${COMMON}/dev_c3600.c" "${COMMON}/dev_c3600_bay.c" "${COMMON}/dev_c3600_iofpga.c" "${COMMON}/dev_c3600_eth.c" "${COMMON}/dev_c3600_serial.c" "${COMMON}/dev_c7200.c" "${COMMON}/dev_c7200_iofpga.c" "${COMMON}/dev_c7200_mpfpga.c" "${COMMON}/dev_c7200_sram.c" "${COMMON}/dev_c7200_eth.c" "${COMMON}/dev_c7200_serial.c" "${COMMON}/dev_c7200_pos.c" "${COMMON}/dev_c7200_bri.c" "${COMMON}/dev_c7200_jcpa.c" "${COMMON}/dev_c2691.c" "${COMMON}/dev_c2691_iofpga.c" "${COMMON}/dev_c2691_eth.c" "${COMMON}/dev_c2691_serial.c" "${COMMON}/dev_c2691_wic.c" "${COMMON}/dev_c2691_pcmod.c" "${COMMON}/dev_c3725.c" "${COMMON}/dev_c3725_iofpga.c" "${COMMON}/dev_c3725_eth.c" "${COMMON}/dev_c3725_serial.c" "${COMMON}/dev_c3725_wic.c" "${COMMON}/dev_c3725_pcmod.c" "${COMMON}/dev_c3745.c" "${COMMON}/dev_c3745_iofpga.c" "${COMMON}/dev_c3745_eth.c" "${COMMON}/dev_c3745_serial.c" "${COMMON}/dev_c3745_wic.c" "${COMMON}/dev_c3745_pcmod.c" "${COMMON}/dev_c2600.c" "${COMMON}/dev_c2600_pci.c" "${COMMON}/dev_c2600_iofpga.c" "${COMMON}/dev_c2600_eth.c" "${COMMON}/dev_c2600_pcmod.c" "${COMMON}/dev_c2600_wic.c" "${COMMON}/dev_c1700.c" "${COMMON}/dev_c1700_iofpga.c" "${COMMON}/dev_c1700_eth.c" "${COMMON}/dev_c1700_wic.c" "${COMMON}/dev_c6msfc1.c" "${COMMON}/dev_c6msfc1_iofpga.c" "${COMMON}/dev_c6msfc1_mpfpga.c" "${COMMON}/dev_c6sup1.c" "${COMMON}/dev_c6sup1_iofpga.c" "${COMMON}/dev_c6sup1_mpfpga.c" "${COMMON}/dev_nm_16esw.c" "${COMMON}/dev_pa_a1.c" "${COMMON}/dev_pa_mc8te1.c" "${COMMON}/dev_sb1.c" "${COMMON}/dev_sb1_io.c" "${COMMON}/dev_sb1_pci.c" "${LOCAL}/hypervisor.c" "${COMMON}/hv_nio.c" "${COMMON}/hv_nio_bridge.c" "${COMMON}/hv_frsw.c" "${COMMON}/hv_atmsw.c" "${COMMON}/hv_atm_bridge.c" "${COMMON}/hv_ethsw.c" "${LOCAL}/hv_vm.c" "${COMMON}/hv_vm_debug.c" "${COMMON}/hv_store.c" "${COMMON}/hv_c7200.c" "${COMMON}/hv_c3600.c" "${COMMON}/hv_c2691.c" "${COMMON}/hv_c3725.c" "${COMMON}/hv_c3745.c" "${COMMON}/hv_c2600.c" "${COMMON}/hv_c1700.c" "${COMMON}/rommon_var.c" "${COMMON}/get_cpu_time.c" "${COMMON}/fs_fat.c" "${COMMON}/fs_mbr.c" "${COMMON}/fs_nvram.c" "${COMMON}/dev_lxt970a.c" ) if ( ENABLE_LINUX_ETH ) set ( _files ${_files} "${COMMON}/linux_eth.c" ) endif ( ENABLE_LINUX_ETH ) if ( ENABLE_GEN_ETH ) set ( _files ${_files} "${COMMON}/gen_eth.c" ) endif ( ENABLE_GEN_ETH ) set ( _dependencies ppc32_microcode_dump_unstable mips64_microcode_dump_unstable ) # dynamips_amd64_unstable if ( "amd64" STREQUAL "${DYNAMIPS_ARCH}" ) add_executable ( dynamips_amd64_unstable ${_files} "${LOCAL}/mips64_amd64_trans.c" "${LOCAL}/ppc32_amd64_trans.c" ) add_dependencies ( dynamips_amd64_unstable ${_dependencies} ) target_link_libraries ( dynamips_amd64_unstable ${DYNAMIPS_LIBRARIES} ) maybe_rename_to_dynamips ( dynamips_amd64_unstable ) install_executable ( dynamips_amd64_unstable ) endif () # dynamips_x86_unstable if ( "x86" STREQUAL "${DYNAMIPS_ARCH}" ) add_executable ( dynamips_x86_unstable ${_files} "${LOCAL}/mips64_x86_trans.c" "${LOCAL}/ppc32_x86_trans.c" ) add_dependencies ( dynamips_x86_unstable ${_dependencies} ) target_link_libraries ( dynamips_x86_unstable ${DYNAMIPS_LIBRARIES} ) maybe_rename_to_dynamips ( dynamips_x86_unstable ) install_executable ( dynamips_x86_unstable ) endif () # dynamips_nojit_unstable if ( "nojit" STREQUAL "${DYNAMIPS_ARCH}" ) add_executable ( dynamips_nojit_unstable ${_files} "${LOCAL}/mips64_nojit_trans.c" "${COMMON}/ppc32_nojit_trans.c" ) add_dependencies ( dynamips_nojit_unstable ${_dependencies} ) target_link_libraries ( dynamips_nojit_unstable ${DYNAMIPS_LIBRARIES} ) maybe_rename_to_dynamips ( dynamips_nojit_unstable ) install_executable ( dynamips_nojit_unstable ) endif () dynamips-0.2.14/unstable/amd64-codegen.h000066400000000000000000001673011241034141600177310ustar00rootroot00000000000000/* * amd64-codegen.h: Macros for generating amd64 code * * Authors: * Paolo Molaro (lupus@ximian.com) * Intel Corporation (ORP Project) * Sergey Chaban (serge@wildwestsoftware.com) * Dietmar Maurer (dietmar@ximian.com) * Patrik Torstensson * Zalman Stern * * Copyright (C) 2000 Intel Corporation. All rights reserved. * Copyright (C) 2001, 2002 Ximian, Inc. */ #ifndef AMD64_H #define AMD64_H typedef enum { AMD64_RAX = 0, AMD64_RCX = 1, AMD64_RDX = 2, AMD64_RBX = 3, AMD64_RSP = 4, AMD64_RBP = 5, AMD64_RSI = 6, AMD64_RDI = 7, AMD64_R8 = 8, AMD64_R9 = 9, AMD64_R10 = 10, AMD64_R11 = 11, AMD64_R12 = 12, AMD64_R13 = 13, AMD64_R14 = 14, AMD64_R15 = 15, AMD64_RIP = 16, AMD64_NREG } AMD64_Reg_No; typedef enum { AMD64_XMM0 = 0, AMD64_XMM1 = 1, AMD64_XMM2 = 2, AMD64_XMM3 = 3, AMD64_XMM4 = 4, AMD64_XMM5 = 5, AMD64_XMM6 = 6, AMD64_XMM7 = 7, AMD64_XMM8 = 8, AMD64_XMM9 = 9, AMD64_XMM10 = 10, AMD64_XMM11 = 11, AMD64_XMM12 = 12, AMD64_XMM13 = 13, AMD64_XMM14 = 14, AMD64_XMM15 = 15, AMD64_XMM_NREG = 16, } AMD64_XMM_Reg_No; typedef enum { AMD64_REX_B = 1, /* The register in r/m field, base register in SIB byte, or reg in opcode is 8-15 rather than 0-7 */ AMD64_REX_X = 2, /* The index register in SIB byte is 8-15 rather than 0-7 */ AMD64_REX_R = 4, /* The reg field of ModRM byte is 8-15 rather than 0-7 */ AMD64_REX_W = 8 /* Opeartion is 64-bits instead of 32 (default) or 16 (with 0x66 prefix) */ } AMD64_REX_Bits; #define AMD64_CALLEE_REGS ((1< 4) ? AMD64_REX_W : 0) | \ (((reg_modrm) > 7) ? AMD64_REX_R : 0) | \ (((reg_index) > 7) ? AMD64_REX_X : 0) | \ (((reg_rm_base_opcode) > 7) ? AMD64_REX_B : 0); \ if ((_amd64_rex_bits != 0) || (((width) == 1))) *(inst)++ = AMD64_REX(_amd64_rex_bits); \ } while (0) typedef union { long val; unsigned char b [8]; } amd64_imm_buf; #include "x86-codegen.h" #define amd64_bswap32(inst,reg) \ do { \ *(inst)++ = 0x0f; \ *(inst)++ = (unsigned char)0xc8 + (reg); \ } while (0) /* In 64 bit mode, all registers have a low byte subregister */ #undef X86_IS_BYTE_REG #define X86_IS_BYTE_REG(reg) 1 #define amd64_modrm_mod(modrm) ((modrm) >> 6) #define amd64_modrm_reg(modrm) (((modrm) >> 3) & 0x7) #define amd64_modrm_rm(modrm) ((modrm) & 0x7) #define amd64_rex_r(rex) ((((rex) >> 2) & 0x1) << 3) #define amd64_rex_x(rex) ((((rex) >> 1) & 0x1) << 3) #define amd64_rex_b(rex) ((((rex) >> 0) & 0x1) << 3) #define amd64_is_imm32(val) ((glong)val >= -((glong)1<<31) && (glong)val <= (((glong)1<<31)-1)) #define x86_imm_emit64(inst,imm) \ do { \ amd64_imm_buf imb; imb.val = (long) (imm); \ *(inst)++ = imb.b [0]; \ *(inst)++ = imb.b [1]; \ *(inst)++ = imb.b [2]; \ *(inst)++ = imb.b [3]; \ *(inst)++ = imb.b [4]; \ *(inst)++ = imb.b [5]; \ *(inst)++ = imb.b [6]; \ *(inst)++ = imb.b [7]; \ } while (0) #define amd64_membase_emit(inst,reg,basereg,disp) do { \ if ((basereg) == AMD64_RIP) { \ x86_address_byte ((inst), 0, (reg)&0x7, 5); \ x86_imm_emit32 ((inst), (disp)); \ } \ else \ x86_membase_emit ((inst),(reg)&0x7, (basereg)&0x7, (disp)); \ } while (0) #define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) \ do { \ if ((reg) == X86_EAX) { \ amd64_emit_rex(inst, size, 0, 0, 0); \ *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ x86_imm_emit32 ((inst), (imm)); \ break; \ } \ if (x86_is_imm8((imm))) { \ amd64_emit_rex(inst, size, 0, 0, (reg)); \ *(inst)++ = (unsigned char)0x83; \ x86_reg_emit ((inst), (opc), (reg)); \ x86_imm_emit8 ((inst), (imm)); \ } else { \ amd64_emit_rex(inst, size, 0, 0, (reg)); \ *(inst)++ = (unsigned char)0x81; \ x86_reg_emit ((inst), (opc), (reg)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define amd64_alu_reg_imm(inst,opc,reg,imm) amd64_alu_reg_imm_size((inst),(opc),(reg),(imm),8) #define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) \ do { \ amd64_emit_rex(inst, size, (dreg), 0, (reg)); \ *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) #define amd64_alu_reg_reg(inst,opc,dreg,reg) amd64_alu_reg_reg_size ((inst),(opc),(dreg),(reg),8) #define amd64_mov_regp_reg(inst,regp,reg,size) \ do { \ if ((size) == 2) \ *(inst)++ = (unsigned char)0x66; \ amd64_emit_rex(inst, (size), (reg), 0, (regp)); \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x88; break; \ case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ default: assert (0); \ } \ x86_regp_emit ((inst), (reg), (regp)); \ } while (0) #define amd64_mov_membase_reg(inst,basereg,disp,reg,size) \ do { \ if ((size) == 2) \ *(inst)++ = (unsigned char)0x66; \ amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x88; break; \ case 2: case 4: case 8: *(inst)++ = (unsigned char)0x89; break; \ default: assert (0); \ } \ x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ } while (0) #define amd64_mov_reg_reg(inst,dreg,reg,size) \ do { \ if ((size) == 2) \ *(inst)++ = (unsigned char)0x66; \ amd64_emit_rex(inst, (size), (dreg), 0, (reg)); \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x8a; break; \ case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) #define amd64_mov_reg_mem(inst,reg,mem,size) \ do { \ if ((size) == 2) \ *(inst)++ = (unsigned char)0x66; \ amd64_emit_rex(inst, (size), (reg), 0, 0); \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x8a; break; \ case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ x86_address_byte ((inst), 0, (reg), 4); \ x86_address_byte ((inst), 0, 4, 5); \ x86_imm_emit32 ((inst), (mem)); \ } while (0) #define amd64_mov_reg_membase(inst,reg,basereg,disp,size) \ do { \ if ((size) == 2) \ *(inst)++ = (unsigned char)0x66; \ amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x8a; break; \ case 2: case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define amd64_movzx_reg_membase(inst,reg,basereg,disp,size) \ do { \ amd64_emit_rex(inst, (size), (reg), 0, (basereg)); \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb6; break; \ case 2: *(inst)++ = (unsigned char)0x0f; *(inst)++ = (unsigned char)0xb7; break; \ case 4: case 8: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ } while (0) #define amd64_movsxd_reg_membase(inst,reg,basereg,disp) \ do { \ amd64_emit_rex(inst,8,(reg),0,(basereg)); \ *(inst)++ = (unsigned char)0x63; \ x86_membase_emit ((inst), ((reg)&0x7), ((basereg)&0x7), (disp)); \ } while (0) #define amd64_movsxd_reg_reg(inst,dreg,reg) \ do { \ amd64_emit_rex(inst,8,(dreg),0,(reg)); \ *(inst)++ = (unsigned char)0x63; \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) /* Pretty much the only instruction that supports a 64-bit immediate. Optimize for common case of * 32-bit immediate. Pepper with casts to avoid warnings. */ #define amd64_mov_reg_imm_size(inst,reg,imm,size) \ do { \ amd64_emit_rex(inst, (size), 0, 0, (reg)); \ *(inst)++ = (unsigned char)0xb8 + ((reg) & 0x7); \ if ((size) == 8) \ x86_imm_emit64 ((inst), (long)(imm)); \ else \ x86_imm_emit32 ((inst), (int)(long)(imm)); \ } while (0) #define amd64_mov_reg_imm(inst,reg,imm) \ do { \ int _amd64_width_temp = ((long)(imm) == (long)(int)(long)(imm)); \ amd64_mov_reg_imm_size ((inst), (reg), (imm), (_amd64_width_temp ? 4 : 8)); \ } while (0) #define amd64_set_reg_template(inst,reg) amd64_mov_reg_imm_size ((inst),(reg), 0, 8) #define amd64_set_template(inst,reg) amd64_set_reg_template((inst),(reg)) #define amd64_mov_membase_imm(inst,basereg,disp,imm,size) \ do { \ if ((size) == 2) \ *(inst)++ = (unsigned char)0x66; \ amd64_emit_rex(inst, (size) == 1 ? 0 : (size), 0, 0, (basereg)); \ if ((size) == 1) { \ *(inst)++ = (unsigned char)0xc6; \ x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ x86_imm_emit8 ((inst), (imm)); \ } else if ((size) == 2) { \ *(inst)++ = (unsigned char)0xc7; \ x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ x86_imm_emit16 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0xc7; \ x86_membase_emit ((inst), 0, (basereg) & 0x7, (disp)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define amd64_lea_membase(inst,reg,basereg,disp) \ do { \ amd64_emit_rex(inst, 8, (reg), 0, (basereg)); \ *(inst)++ = (unsigned char)0x8d; \ amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) /* Instruction are implicitly 64-bits so don't generate REX for just the size. */ #define amd64_push_reg(inst,reg) \ do { \ amd64_emit_rex(inst, 0, 0, 0, (reg)); \ *(inst)++ = (unsigned char)0x50 + ((reg) & 0x7); \ } while (0) /* Instruction is implicitly 64-bits so don't generate REX for just the size. */ #define amd64_push_membase(inst,basereg,disp) \ do { \ amd64_emit_rex(inst, 0, 0, 0, (basereg)); \ *(inst)++ = (unsigned char)0xff; \ x86_membase_emit ((inst), 6, (basereg) & 0x7, (disp)); \ } while (0) #define amd64_pop_reg(inst,reg) \ do { \ amd64_emit_rex(inst, 0, 0, 0, (reg)); \ *(inst)++ = (unsigned char)0x58 + ((reg) & 0x7); \ } while (0) #define amd64_call_reg(inst,reg) \ do { \ amd64_emit_rex(inst, 8, 0, 0, (reg)); \ *(inst)++ = (unsigned char)0xff; \ x86_reg_emit ((inst), 2, ((reg) & 0x7)); \ } while (0) #define amd64_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) #define amd64_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) #define amd64_movsd_reg_regp(inst,reg,regp) \ do { \ *(inst)++ = (unsigned char)0xf2; \ amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x10; \ x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ } while (0) #define amd64_movsd_regp_reg(inst,regp,reg) \ do { \ *(inst)++ = (unsigned char)0xf2; \ amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x11; \ x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ } while (0) #define amd64_movss_reg_regp(inst,reg,regp) \ do { \ *(inst)++ = (unsigned char)0xf3; \ amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x10; \ x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ } while (0) #define amd64_movss_regp_reg(inst,regp,reg) \ do { \ *(inst)++ = (unsigned char)0xf3; \ amd64_emit_rex(inst, 0, (reg), 0, (regp)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x11; \ x86_regp_emit ((inst), (reg) & 0x7, (regp) & 0x7); \ } while (0) #define amd64_movsd_reg_membase(inst,reg,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xf2; \ amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x10; \ x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ } while (0) #define amd64_movss_reg_membase(inst,reg,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xf3; \ amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x10; \ x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ } while (0) #define amd64_movsd_membase_reg(inst,basereg,disp,reg) \ do { \ *(inst)++ = (unsigned char)0xf2; \ amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x11; \ x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ } while (0) #define amd64_movss_membase_reg(inst,basereg,disp,reg) \ do { \ *(inst)++ = (unsigned char)0xf3; \ amd64_emit_rex(inst, 0, (reg), 0, (basereg)); \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0x11; \ x86_membase_emit ((inst), (reg) & 0x7, (basereg) & 0x7, (disp)); \ } while (0) /* The original inc_reg opcode is used as the REX prefix */ #define amd64_inc_reg_size(inst,reg,size) \ do { \ amd64_emit_rex ((inst),(size),0,0,(reg)); \ *(inst)++ = (unsigned char)0xff; \ x86_reg_emit ((inst),0,(reg) & 0x7); \ } while (0) #define amd64_dec_reg_size(inst,reg,size) \ do { \ amd64_emit_rex ((inst),(size),0,0,(reg)); \ *(inst)++ = (unsigned char)0xff; \ x86_reg_emit ((inst),1,(reg) & 0x7); \ } while (0) #define amd64_padding_size(inst,size) \ do { if (size == 1) x86_padding ((inst),(size)); else { amd64_emit_rex ((inst),8,0,0,0); x86_padding((inst),(size) - 1); } } while (0) #define amd64_fld_membase_size(inst,basereg,disp,is_double,size) do { \ amd64_emit_rex ((inst),0,0,0,(basereg)); \ *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ amd64_membase_emit ((inst), 0, (basereg), (disp)); \ } while (0) #define amd64_call_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); *(inst)++ = (unsigned char)0xff; amd64_membase_emit ((inst),2, (basereg),(disp)); } while (0) /* * SSE */ #define emit_opcode3(inst,op1,op2,op3) do { \ *(inst)++ = (unsigned char)(op1); \ *(inst)++ = (unsigned char)(op2); \ *(inst)++ = (unsigned char)(op3); \ } while (0) #define emit_sse_reg_reg_size(inst,dreg,reg,op1,op2,op3,size) do { \ *(inst)++ = (unsigned char)(op1); \ amd64_emit_rex ((inst), size, (dreg), 0, (reg)); \ *(inst)++ = (unsigned char)(op2); \ *(inst)++ = (unsigned char)(op3); \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) #define emit_sse_reg_reg(inst,dreg,reg,op1,op2,op3) emit_sse_reg_reg_size ((inst), (dreg), (reg), (op1), (op2), (op3), 0) #define emit_sse_membase_reg(inst,basereg,disp,reg,op1,op2,op3) do { \ *(inst)++ = (unsigned char)(op1); \ amd64_emit_rex ((inst), 0, (reg), 0, (basereg)); \ *(inst)++ = (unsigned char)(op2); \ *(inst)++ = (unsigned char)(op3); \ amd64_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define emit_sse_reg_membase(inst,dreg,basereg,disp,op1,op2,op3) do { \ *(inst)++ = (unsigned char)(op1); \ amd64_emit_rex ((inst), 0, (dreg), 0, (basereg) == AMD64_RIP ? 0 : (basereg)); \ *(inst)++ = (unsigned char)(op2); \ *(inst)++ = (unsigned char)(op3); \ amd64_membase_emit ((inst), (dreg), (basereg), (disp)); \ } while (0) #define amd64_sse_xorpd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg), 0x66, 0x0f, 0x57) #define amd64_sse_xorpd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst),(dreg),(basereg), (disp), 0x66, 0x0f, 0x57) #define amd64_sse_movsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x10) #define amd64_sse_movsd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0xf2, 0x0f, 0x10) #define amd64_sse_movsd_membase_reg(inst,basereg,disp,reg) emit_sse_membase_reg ((inst), (basereg), (disp), (reg), 0xf2, 0x0f, 0x11) #define amd64_sse_movss_membase_reg(inst,basereg,disp,reg) emit_sse_membase_reg ((inst), (basereg), (disp), (reg), 0xf3, 0x0f, 0x11) #define amd64_sse_movss_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0xf3, 0x0f, 0x10) #define amd64_sse_comisd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst),(dreg),(reg),0x66,0x0f,0x2f) #define amd64_sse_comisd_reg_membase(inst,dreg,basereg,disp) emit_sse_reg_membase ((inst), (dreg), (basereg), (disp), 0x66, 0x0f, 0x2f) #define amd64_sse_cvtsd2si_reg_reg(inst,dreg,reg) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2d, 8) #define amd64_sse_cvttsd2si_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2c, (size)) #define amd64_sse_cvttsd2si_reg_reg(inst,dreg,reg) amd64_sse_cvttsd2si_reg_reg_size ((inst), (dreg), (reg), 8) #define amd64_sse_cvtsi2sd_reg_reg_size(inst,dreg,reg,size) emit_sse_reg_reg_size ((inst), (dreg), (reg), 0xf2, 0x0f, 0x2a, (size)) #define amd64_sse_cvtsi2sd_reg_reg(inst,dreg,reg) amd64_sse_cvtsi2sd_reg_reg_size ((inst), (dreg), (reg), 8) #define amd64_sse_cvtsd2ss_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5a) #define amd64_sse_cvtss2sd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf3, 0x0f, 0x5a) #define amd64_sse_addsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x58) #define amd64_sse_subsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5c) #define amd64_sse_mulsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x59) #define amd64_sse_divsd_reg_reg(inst,dreg,reg) emit_sse_reg_reg ((inst), (dreg), (reg), 0xf2, 0x0f, 0x5e) /* Generated from x86-codegen.h */ #define amd64_breakpoint_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_breakpoint(inst); } while (0) #define amd64_cld_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_cld(inst); } while (0) #define amd64_stosb_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_stosb(inst); } while (0) #define amd64_stosl_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_stosl(inst); } while (0) #define amd64_stosd_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_stosd(inst); } while (0) #define amd64_movsb_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_movsb(inst); } while (0) #define amd64_movsl_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_movsl(inst); } while (0) #define amd64_movsd_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_movsd(inst); } while (0) #define amd64_prefix_size(inst,p,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_prefix((inst), p); } while (0) #define amd64_rdtsc_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_rdtsc(inst); } while (0) #define amd64_cmpxchg_reg_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_cmpxchg_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); } while (0) #define amd64_cmpxchg_mem_reg_size(inst,mem,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_cmpxchg_mem_reg((inst),(mem),((reg)&0x7)); } while (0) #define amd64_cmpxchg_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_cmpxchg_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7)); } while (0) #define amd64_xchg_reg_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_xchg_reg_reg((inst),((dreg)&0x7),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) #define amd64_xchg_mem_reg_size(inst,mem,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_xchg_mem_reg((inst),(mem),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) #define amd64_xchg_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg))); x86_xchg_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) #define amd64_inc_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_inc_mem((inst),(mem)); } while (0) #define amd64_inc_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_inc_membase((inst),((basereg)&0x7),(disp)); } while (0) //#define amd64_inc_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_inc_reg((inst),((reg)&0x7)); } while (0) #define amd64_dec_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_dec_mem((inst),(mem)); } while (0) #define amd64_dec_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_dec_membase((inst),((basereg)&0x7),(disp)); } while (0) //#define amd64_dec_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_dec_reg((inst),((reg)&0x7)); } while (0) #define amd64_not_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_not_mem((inst),(mem)); } while (0) #define amd64_not_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_not_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_not_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_not_reg((inst),((reg)&0x7)); } while (0) #define amd64_neg_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_neg_mem((inst),(mem)); } while (0) #define amd64_neg_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_neg_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_neg_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_neg_reg((inst),((reg)&0x7)); } while (0) #define amd64_nop_size(inst,size) do { x86_nop(inst); } while (0) //#define amd64_alu_reg_imm_size(inst,opc,reg,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_reg_imm((inst),(opc),((reg)&0x7),(imm)); } while (0) #define amd64_alu_mem_imm_size(inst,opc,mem,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_alu_mem_imm((inst),(opc),(mem),(imm)); } while (0) #define amd64_alu_membase_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_alu_membase_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); } while (0) #define amd64_alu_mem_reg_size(inst,opc,mem,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_mem_reg((inst),(opc),(mem),((reg)&0x7)); } while (0) #define amd64_alu_membase_reg_size(inst,opc,basereg,disp,reg,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_alu_membase_reg((inst),(opc),((basereg)&0x7),(disp),((reg)&0x7)); } while (0) //#define amd64_alu_reg_reg_size(inst,opc,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_alu_reg_reg((inst),(opc),((dreg)&0x7),((reg)&0x7)); } while (0) #define amd64_alu_reg8_reg8_size(inst,opc,dreg,reg,is_dreg_h,is_reg_h,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_alu_reg8_reg8((inst),(opc),((dreg)&0x7),((reg)&0x7),(is_dreg_h),(is_reg_h)); } while (0) #define amd64_alu_reg_mem_size(inst,opc,reg,mem,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_alu_reg_mem((inst),(opc),((reg)&0x7),(mem)); } while (0) #define amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_alu_reg_membase((inst),(opc),((reg)&0x7),((basereg)&0x7),(disp)); } while (0) #define amd64_test_reg_imm_size(inst,reg,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_test_reg_imm((inst),((reg)&0x7),(imm)); } while (0) #define amd64_test_mem_imm_size(inst,mem,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_test_mem_imm((inst),(mem),(imm)); } while (0) #define amd64_test_membase_imm_size(inst,basereg,disp,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_test_membase_imm((inst),((basereg)&0x7),(disp),(imm)); } while (0) #define amd64_test_reg_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_test_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); } while (0) #define amd64_test_mem_reg_size(inst,mem,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_test_mem_reg((inst),(mem),((reg)&0x7)); } while (0) #define amd64_test_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_test_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7)); } while (0) #define amd64_shift_reg_imm_size(inst,opc,reg,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_reg_imm((inst),(opc),((reg)&0x7),(imm)); } while (0) #define amd64_shift_mem_imm_size(inst,opc,mem,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_mem_imm((inst),(opc),(mem),(imm)); } while (0) #define amd64_shift_membase_imm_size(inst,opc,basereg,disp,imm,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_shift_membase_imm((inst),(opc),((basereg)&0x7),(disp),(imm)); } while (0) #define amd64_shift_reg_size(inst,opc,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_reg((inst),(opc),((reg)&0x7)); } while (0) #define amd64_shift_mem_size(inst,opc,mem,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_shift_mem((inst),(opc),(mem)); } while (0) #define amd64_shift_membase_size(inst,opc,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_shift_membase((inst),(opc),((basereg)&0x7),(disp)); } while (0) #define amd64_shrd_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shrd_reg((inst),((dreg)&0x7),((reg)&0x7)); } while (0) #define amd64_shrd_reg_imm_size(inst,dreg,reg,shamt,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shrd_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(shamt)); } while (0) #define amd64_shld_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shld_reg((inst),((dreg)&0x7),((reg)&0x7)); } while (0) #define amd64_shld_reg_imm_size(inst,dreg,reg,shamt,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_shld_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(shamt)); } while (0) #define amd64_mul_reg_size(inst,reg,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mul_reg((inst),((reg)&0x7),(is_signed)); } while (0) #define amd64_mul_mem_size(inst,mem,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_mul_mem((inst),(mem),(is_signed)); } while (0) #define amd64_mul_membase_size(inst,basereg,disp,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_mul_membase((inst),((basereg)&0x7),(disp),(is_signed)); } while (0) #define amd64_imul_reg_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_imul_reg_reg((inst),((dreg)&0x7),((reg)&0x7)); } while (0) #define amd64_imul_reg_mem_size(inst,reg,mem,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_imul_reg_mem((inst),((reg)&0x7),(mem)); } while (0) #define amd64_imul_reg_membase_size(inst,reg,basereg,disp,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_imul_reg_membase((inst),((reg)&0x7),((basereg)&0x7),(disp)); } while (0) #define amd64_imul_reg_reg_imm_size(inst,dreg,reg,imm,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_imul_reg_reg_imm((inst),((dreg)&0x7),((reg)&0x7),(imm)); } while (0) #define amd64_imul_reg_mem_imm_size(inst,reg,mem,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_imul_reg_mem_imm((inst),((reg)&0x7),(mem),(imm)); } while (0) #define amd64_imul_reg_membase_imm_size(inst,reg,basereg,disp,imm,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_imul_reg_membase_imm((inst),((reg)&0x7),((basereg)&0x7),(disp),(imm)); } while (0) #define amd64_div_reg_size(inst,reg,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_div_reg((inst),((reg)&0x7),(is_signed)); } while (0) #define amd64_div_mem_size(inst,mem,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_div_mem((inst),(mem),(is_signed)); } while (0) #define amd64_div_membase_size(inst,basereg,disp,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_div_membase((inst),((basereg)&0x7),(disp),(is_signed)); } while (0) #define amd64_mov_mem_reg_size(inst,mem,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_mem_reg((inst),(mem),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) //#define amd64_mov_regp_reg_size(inst,regp,reg,size) do { amd64_emit_rex ((inst),(size),(regp),0,(reg)); x86_mov_regp_reg((inst),(regp),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) //#define amd64_mov_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_mov_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) #define amd64_mov_memindex_reg_size(inst,basereg,disp,indexreg,shift,reg,size) do { amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_mov_memindex_reg((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) #define amd64_mov_reg_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_mov_reg_reg((inst),((dreg)&0x7),((reg)&0x7),(size) == 8 ? 4 : (size)); } while (0) //#define amd64_mov_reg_mem_size(inst,reg,mem,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_reg_mem((inst),((reg)&0x7),(mem),(size) == 8 ? 4 : (size)); } while (0) //#define amd64_mov_reg_membase_size(inst,reg,basereg,disp,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_mov_reg_membase((inst),((reg)&0x7),((basereg)&0x7),(disp),(size) == 8 ? 4 : (size)); } while (0) #define amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) do { amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_mov_reg_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(size) == 8 ? 4 : (size)); } while (0) #define amd64_clear_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_clear_reg((inst),((reg)&0x7)); } while (0) //#define amd64_mov_reg_imm_size(inst,reg,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_mov_reg_imm((inst),((reg)&0x7),(imm)); } while (0) #define amd64_mov_mem_imm_size(inst,mem,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_mov_mem_imm((inst),(mem),(imm),(size) == 8 ? 4 : (size)); } while (0) //#define amd64_mov_membase_imm_size(inst,basereg,disp,imm,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_mov_membase_imm((inst),((basereg)&0x7),(disp),(imm),(size) == 8 ? 4 : (size)); } while (0) #define amd64_mov_memindex_imm_size(inst,basereg,disp,indexreg,shift,imm,size) do { amd64_emit_rex ((inst),(size),0,(indexreg),(basereg)); x86_mov_memindex_imm((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(imm),(size) == 8 ? 4 : (size)); } while (0) #define amd64_lea_mem_size(inst,reg,mem,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_lea_mem((inst),((reg)&0x7),(mem)); } while (0) //#define amd64_lea_membase_size(inst,reg,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_lea_membase((inst),((reg)&0x7),((basereg)&0x7),(disp)); } while (0) #define amd64_lea_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) do { amd64_emit_rex ((inst),(size),(reg),(indexreg),(basereg)); x86_lea_memindex((inst),((reg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift)); } while (0) #define amd64_widen_reg_size(inst,dreg,reg,is_signed,is_half,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_widen_reg((inst),((dreg)&0x7),((reg)&0x7),(is_signed),(is_half)); } while (0) #define amd64_widen_mem_size(inst,dreg,mem,is_signed,is_half,size) do { amd64_emit_rex ((inst),(size),(dreg),0,0); x86_widen_mem((inst),((dreg)&0x7),(mem),(is_signed),(is_half)); } while (0) #define amd64_widen_membase_size(inst,dreg,basereg,disp,is_signed,is_half,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(basereg)); x86_widen_membase((inst),((dreg)&0x7),((basereg)&0x7),(disp),(is_signed),(is_half)); } while (0) #define amd64_widen_memindex_size(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half,size) do { amd64_emit_rex ((inst),(size),(dreg),(indexreg),(basereg)); x86_widen_memindex((inst),((dreg)&0x7),((basereg)&0x7),(disp),((indexreg)&0x7),(shift),(is_signed),(is_half)); } while (0) #define amd64_cdq_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_cdq(inst); } while (0) #define amd64_wait_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_wait(inst); } while (0) #define amd64_fp_op_mem_size(inst,opc,mem,is_double,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fp_op_mem((inst),(opc),(mem),(is_double)); } while (0) #define amd64_fp_op_membase_size(inst,opc,basereg,disp,is_double,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fp_op_membase((inst),(opc),((basereg)&0x7),(disp),(is_double)); } while (0) #define amd64_fp_op_size(inst,opc,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fp_op((inst),(opc),(index)); } while (0) #define amd64_fp_op_reg_size(inst,opc,index,pop_stack,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fp_op_reg((inst),(opc),(index),(pop_stack)); } while (0) #define amd64_fp_int_op_membase_size(inst,opc,basereg,disp,is_int,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fp_int_op_membase((inst),(opc),((basereg)&0x7),(disp),(is_int)); } while (0) #define amd64_fstp_size(inst,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fstp((inst),(index)); } while (0) #define amd64_fcompp_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fcompp(inst); } while (0) #define amd64_fucompp_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fucompp(inst); } while (0) #define amd64_fnstsw_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fnstsw(inst); } while (0) #define amd64_fnstcw_size(inst,mem,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fnstcw((inst),(mem)); } while (0) #define amd64_fnstcw_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_fnstcw_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_fldcw_size(inst,mem,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fldcw((inst),(mem)); } while (0) #define amd64_fldcw_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fldcw_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_fchs_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fchs(inst); } while (0) #define amd64_frem_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_frem(inst); } while (0) #define amd64_fxch_size(inst,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fxch((inst),(index)); } while (0) #define amd64_fcomi_size(inst,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fcomi((inst),(index)); } while (0) #define amd64_fcomip_size(inst,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fcomip((inst),(index)); } while (0) #define amd64_fucomi_size(inst,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fucomi((inst),(index)); } while (0) #define amd64_fucomip_size(inst,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fucomip((inst),(index)); } while (0) #define amd64_fld_size(inst,mem,is_double,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fld((inst),(mem),(is_double)); } while (0) //#define amd64_fld_membase_size(inst,basereg,disp,is_double,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fld_membase((inst),((basereg)&0x7),(disp),(is_double)); } while (0) #define amd64_fld80_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fld80_mem((inst),(mem)); } while (0) #define amd64_fld80_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_fld80_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_fild_size(inst,mem,is_long,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fild((inst),(mem),(is_long)); } while (0) #define amd64_fild_membase_size(inst,basereg,disp,is_long,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fild_membase((inst),((basereg)&0x7),(disp),(is_long)); } while (0) #define amd64_fld_reg_size(inst,index,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fld_reg((inst),(index)); } while (0) #define amd64_fldz_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fldz(inst); } while (0) #define amd64_fld1_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fld1(inst); } while (0) #define amd64_fldpi_size(inst,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fldpi(inst); } while (0) #define amd64_fst_size(inst,mem,is_double,pop_stack,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fst((inst),(mem),(is_double),(pop_stack)); } while (0) #define amd64_fst_membase_size(inst,basereg,disp,is_double,pop_stack,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fst_membase((inst),((basereg)&0x7),(disp),(is_double),(pop_stack)); } while (0) #define amd64_fst80_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fst80_mem((inst),(mem)); } while (0) #define amd64_fst80_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fst80_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_fist_pop_size(inst,mem,is_long,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_fist_pop((inst),(mem),(is_long)); } while (0) #define amd64_fist_pop_membase_size(inst,basereg,disp,is_long,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fist_pop_membase((inst),((basereg)&0x7),(disp),(is_long)); } while (0) #define amd64_fstsw_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fstsw(inst); } while (0) #define amd64_fist_membase_size(inst,basereg,disp,is_int,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_fist_membase((inst),((basereg)&0x7),(disp),(is_int)); } while (0) //#define amd64_push_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_push_reg((inst),((reg)&0x7)); } while (0) #define amd64_push_regp_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_push_regp((inst),((reg)&0x7)); } while (0) #define amd64_push_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_push_mem((inst),(mem)); } while (0) //#define amd64_push_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_push_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_push_memindex_size(inst,basereg,disp,indexreg,shift,size) do { amd64_emit_rex ((inst),(size),0,(indexreg),(basereg)); x86_push_memindex((inst),((basereg)&0x7),(disp),((indexreg)&0x7),(shift)); } while (0) #define amd64_push_imm_size(inst,imm,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_push_imm((inst),(imm)); } while (0) //#define amd64_pop_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_pop_reg((inst),((reg)&0x7)); } while (0) #define amd64_pop_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_pop_mem((inst),(mem)); } while (0) #define amd64_pop_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_pop_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_pushad_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_pushad(inst); } while (0) #define amd64_pushfd_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_pushfd(inst); } while (0) #define amd64_popad_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_popad(inst); } while (0) #define amd64_popfd_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_popfd(inst); } while (0) #define amd64_loop_size(inst,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_loop((inst),(imm)); } while (0) #define amd64_loope_size(inst,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_loope((inst),(imm)); } while (0) #define amd64_loopne_size(inst,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_loopne((inst),(imm)); } while (0) #define amd64_jump32_size(inst,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_jump32((inst),(imm)); } while (0) #define amd64_jump8_size(inst,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_jump8((inst),(imm)); } while (0) #define amd64_jump_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_jump_reg((inst),((reg)&0x7)); } while (0) #define amd64_jump_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_jump_mem((inst),(mem)); } while (0) #define amd64_jump_membase_size(inst,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_jump_membase((inst),((basereg)&0x7),(disp)); } while (0) #define amd64_jump_code_size(inst,target,size) do { x86_jump_code((inst),(target)); } while (0) #define amd64_jump_disp_size(inst,disp,size) do { amd64_emit_rex ((inst),0,0,0,0); x86_jump_disp((inst),(disp)); } while (0) #define amd64_branch8_size(inst,cond,imm,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_branch8((inst),(cond),(imm),(is_signed)); } while (0) #define amd64_branch32_size(inst,cond,imm,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_branch32((inst),(cond),(imm),(is_signed)); } while (0) #define amd64_branch_size(inst,cond,target,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_branch((inst),(cond),(target),(is_signed)); } while (0) #define amd64_branch_disp_size(inst,cond,disp,is_signed,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_branch_disp((inst),(cond),(disp),(is_signed)); } while (0) #define amd64_set_reg_size(inst,cond,reg,is_signed,size) do { amd64_emit_rex((inst),1,0,0,(reg)); x86_set_reg((inst),(cond),((reg)&0x7),(is_signed)); } while (0) #define amd64_set_mem_size(inst,cond,mem,is_signed,size) do { x86_set_mem((inst),(cond),(mem),(is_signed)); } while (0) #define amd64_set_membase_size(inst,cond,basereg,disp,is_signed,size) do { amd64_emit_rex ((inst),0,0,0,(basereg)); x86_set_membase((inst),(cond),((basereg)&0x7),(disp),(is_signed)); } while (0) #define amd64_call_imm_size(inst,disp,size) do { x86_call_imm((inst),(disp)); } while (0) //#define amd64_call_reg_size(inst,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_call_reg((inst),((reg)&0x7)); } while (0) #define amd64_call_mem_size(inst,mem,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_call_mem((inst),(mem)); } while (0) #define amd64_call_code_size(inst,target,size) do { x86_call_code((inst),(target)); } while (0) //#define amd64_ret_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_ret(inst); } while (0) #define amd64_ret_imm_size(inst,imm,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_ret_imm((inst),(imm)); } while (0) #define amd64_cmov_reg_size(inst,cond,is_signed,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_cmov_reg((inst),(cond),(is_signed),((dreg)&0x7),((reg)&0x7)); } while (0) #define amd64_cmov_mem_size(inst,cond,is_signed,reg,mem,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_cmov_mem((inst),(cond),(is_signed),((reg)&0x7),(mem)); } while (0) #define amd64_cmov_membase_size(inst,cond,is_signed,reg,basereg,disp,size) do { amd64_emit_rex ((inst),(size),0,0,(basereg)); x86_cmov_membase((inst),(cond),(is_signed),((reg)&0x7),((basereg)&0x7),(disp)); } while (0) #define amd64_enter_size(inst,framesize) do { amd64_emit_rex ((inst),(size),0,0,0); x86_enter((inst),(framesize)); } while (0) //#define amd64_leave_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_leave(inst); } while (0) #define amd64_sahf_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_sahf(inst); } while (0) #define amd64_fsin_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fsin(inst); } while (0) #define amd64_fcos_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fcos(inst); } while (0) #define amd64_fabs_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fabs(inst); } while (0) #define amd64_ftst_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_ftst(inst); } while (0) #define amd64_fxam_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fxam(inst); } while (0) #define amd64_fpatan_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fpatan(inst); } while (0) #define amd64_fprem_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fprem(inst); } while (0) #define amd64_fprem1_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fprem1(inst); } while (0) #define amd64_frndint_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_frndint(inst); } while (0) #define amd64_fsqrt_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fsqrt(inst); } while (0) #define amd64_fptan_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_fptan(inst); } while (0) //#define amd64_padding_size(inst,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_padding((inst),(size)); } while (0) #define amd64_prolog_size(inst,frame_size,reg_mask,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_prolog((inst),(frame_size),(reg_mask)); } while (0) #define amd64_epilog_size(inst,reg_mask,size) do { amd64_emit_rex ((inst),(size),0,0,0); x86_epilog((inst),(reg_mask)); } while (0) #define amd64_xadd_reg_reg_size(inst,dreg,reg,size) do { amd64_emit_rex ((inst),(size),(dreg),0,(reg)); x86_xadd_reg_reg ((inst), (dreg), (reg), (size)); } while (0) #define amd64_xadd_mem_reg_size(inst,mem,reg,size) do { amd64_emit_rex ((inst),(size),0,0,(reg)); x86_xadd_mem_reg((inst),(mem),((reg)&0x7), (size)); } while (0) #define amd64_xadd_membase_reg_size(inst,basereg,disp,reg,size) do { amd64_emit_rex ((inst),(size),(reg),0,(basereg)); x86_xadd_membase_reg((inst),((basereg)&0x7),(disp),((reg)&0x7),(size)); } while (0) #define amd64_breakpoint(inst) amd64_breakpoint_size(inst,8) #define amd64_cld(inst) amd64_cld_size(inst,8) #define amd64_stosb(inst) amd64_stosb_size(inst,8) #define amd64_stosl(inst) amd64_stosl_size(inst,8) #define amd64_stosd(inst) amd64_stosd_size(inst,8) #define amd64_movsb(inst) amd64_movsb_size(inst,8) #define amd64_movsl(inst) amd64_movsl_size(inst,8) #define amd64_movsd(inst) amd64_movsd_size(inst,8) #define amd64_prefix(inst,p) amd64_prefix_size(inst,p,8) #define amd64_rdtsc(inst) amd64_rdtsc_size(inst,8) #define amd64_cmpxchg_reg_reg(inst,dreg,reg) amd64_cmpxchg_reg_reg_size(inst,dreg,reg,8) #define amd64_cmpxchg_mem_reg(inst,mem,reg) amd64_cmpxchg_mem_reg_size(inst,mem,reg,8) #define amd64_cmpxchg_membase_reg(inst,basereg,disp,reg) amd64_cmpxchg_membase_reg_size(inst,basereg,disp,reg,8) #define amd64_xchg_reg_reg(inst,dreg,reg,size) amd64_xchg_reg_reg_size(inst,dreg,reg,size) #define amd64_xchg_mem_reg(inst,mem,reg,size) amd64_xchg_mem_reg_size(inst,mem,reg,size) #define amd64_xchg_membase_reg(inst,basereg,disp,reg,size) amd64_xchg_membase_reg_size(inst,basereg,disp,reg,size) #define amd64_xadd_reg_reg(inst,dreg,reg,size) amd64_xadd_reg_reg_size(inst,dreg,reg,size) #define amd64_xadd_mem_reg(inst,mem,reg,size) amd64_xadd_mem_reg_size(inst,mem,reg,size) #define amd64_xadd_membase_reg(inst,basereg,disp,reg,size) amd64_xadd_membase_reg_size(inst,basereg,disp,reg,size) #define amd64_inc_mem(inst,mem) amd64_inc_mem_size(inst,mem,8) #define amd64_inc_membase(inst,basereg,disp) amd64_inc_membase_size(inst,basereg,disp,8) #define amd64_inc_reg(inst,reg) amd64_inc_reg_size(inst,reg,8) #define amd64_dec_mem(inst,mem) amd64_dec_mem_size(inst,mem,8) #define amd64_dec_membase(inst,basereg,disp) amd64_dec_membase_size(inst,basereg,disp,8) #define amd64_dec_reg(inst,reg) amd64_dec_reg_size(inst,reg,8) #define amd64_not_mem(inst,mem) amd64_not_mem_size(inst,mem,8) #define amd64_not_membase(inst,basereg,disp) amd64_not_membase_size(inst,basereg,disp,8) #define amd64_not_reg(inst,reg) amd64_not_reg_size(inst,reg,8) #define amd64_neg_mem(inst,mem) amd64_neg_mem_size(inst,mem,8) #define amd64_neg_membase(inst,basereg,disp) amd64_neg_membase_size(inst,basereg,disp,8) #define amd64_neg_reg(inst,reg) amd64_neg_reg_size(inst,reg,8) #define amd64_nop(inst) amd64_nop_size(inst,8) //#define amd64_alu_reg_imm(inst,opc,reg,imm) amd64_alu_reg_imm_size(inst,opc,reg,imm,8) #define amd64_alu_mem_imm(inst,opc,mem,imm) amd64_alu_mem_imm_size(inst,opc,mem,imm,8) #define amd64_alu_membase_imm(inst,opc,basereg,disp,imm) amd64_alu_membase_imm_size(inst,opc,basereg,disp,imm,8) #define amd64_alu_mem_reg(inst,opc,mem,reg) amd64_alu_mem_reg_size(inst,opc,mem,reg,8) #define amd64_alu_membase_reg(inst,opc,basereg,disp,reg) amd64_alu_membase_reg_size(inst,opc,basereg,disp,reg,8) //#define amd64_alu_reg_reg(inst,opc,dreg,reg) amd64_alu_reg_reg_size(inst,opc,dreg,reg,8) #define amd64_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) amd64_alu_reg8_reg8_size(inst,opc,dreg,reg,is_dreg_h,is_reg_h,8) #define amd64_alu_reg_mem(inst,opc,reg,mem) amd64_alu_reg_mem_size(inst,opc,reg,mem,8) #define amd64_alu_reg_membase(inst,opc,reg,basereg,disp) amd64_alu_reg_membase_size(inst,opc,reg,basereg,disp,8) #define amd64_test_reg_imm(inst,reg,imm) amd64_test_reg_imm_size(inst,reg,imm,8) #define amd64_test_mem_imm(inst,mem,imm) amd64_test_mem_imm_size(inst,mem,imm,8) #define amd64_test_membase_imm(inst,basereg,disp,imm) amd64_test_membase_imm_size(inst,basereg,disp,imm,8) #define amd64_test_reg_reg(inst,dreg,reg) amd64_test_reg_reg_size(inst,dreg,reg,8) #define amd64_test_mem_reg(inst,mem,reg) amd64_test_mem_reg_size(inst,mem,reg,8) #define amd64_test_membase_reg(inst,basereg,disp,reg) amd64_test_membase_reg_size(inst,basereg,disp,reg,8) #define amd64_shift_reg_imm(inst,opc,reg,imm) amd64_shift_reg_imm_size(inst,opc,reg,imm,8) #define amd64_shift_mem_imm(inst,opc,mem,imm) amd64_shift_mem_imm_size(inst,opc,mem,imm,8) #define amd64_shift_membase_imm(inst,opc,basereg,disp,imm) amd64_shift_membase_imm_size(inst,opc,basereg,disp,imm,8) #define amd64_shift_reg(inst,opc,reg) amd64_shift_reg_size(inst,opc,reg,8) #define amd64_shift_mem(inst,opc,mem) amd64_shift_mem_size(inst,opc,mem,8) #define amd64_shift_membase(inst,opc,basereg,disp) amd64_shift_membase_size(inst,opc,basereg,disp,8) #define amd64_shrd_reg(inst,dreg,reg) amd64_shrd_reg_size(inst,dreg,reg,8) #define amd64_shrd_reg_imm(inst,dreg,reg,shamt) amd64_shrd_reg_imm_size(inst,dreg,reg,shamt,8) #define amd64_shld_reg(inst,dreg,reg) amd64_shld_reg_size(inst,dreg,reg,8) #define amd64_shld_reg_imm(inst,dreg,reg,shamt) amd64_shld_reg_imm_size(inst,dreg,reg,shamt,8) #define amd64_mul_reg(inst,reg,is_signed) amd64_mul_reg_size(inst,reg,is_signed,8) #define amd64_mul_mem(inst,mem,is_signed) amd64_mul_mem_size(inst,mem,is_signed,8) #define amd64_mul_membase(inst,basereg,disp,is_signed) amd64_mul_membase_size(inst,basereg,disp,is_signed,8) #define amd64_imul_reg_reg(inst,dreg,reg) amd64_imul_reg_reg_size(inst,dreg,reg,8) #define amd64_imul_reg_mem(inst,reg,mem) amd64_imul_reg_mem_size(inst,reg,mem,8) #define amd64_imul_reg_membase(inst,reg,basereg,disp) amd64_imul_reg_membase_size(inst,reg,basereg,disp,8) #define amd64_imul_reg_reg_imm(inst,dreg,reg,imm) amd64_imul_reg_reg_imm_size(inst,dreg,reg,imm,8) #define amd64_imul_reg_mem_imm(inst,reg,mem,imm) amd64_imul_reg_mem_imm_size(inst,reg,mem,imm,8) #define amd64_imul_reg_membase_imm(inst,reg,basereg,disp,imm) amd64_imul_reg_membase_imm_size(inst,reg,basereg,disp,imm,8) #define amd64_div_reg(inst,reg,is_signed) amd64_div_reg_size(inst,reg,is_signed,8) #define amd64_div_mem(inst,mem,is_signed) amd64_div_mem_size(inst,mem,is_signed,8) #define amd64_div_membase(inst,basereg,disp,is_signed) amd64_div_membase_size(inst,basereg,disp,is_signed,8) #define amd64_mov_mem_reg(inst,mem,reg,size) amd64_mov_mem_reg_size(inst,mem,reg,size) //#define amd64_mov_regp_reg(inst,regp,reg,size) amd64_mov_regp_reg_size(inst,regp,reg,size) //#define amd64_mov_membase_reg(inst,basereg,disp,reg,size) amd64_mov_membase_reg_size(inst,basereg,disp,reg,size) #define amd64_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) amd64_mov_memindex_reg_size(inst,basereg,disp,indexreg,shift,reg,size) //#define amd64_mov_reg_reg(inst,dreg,reg,size) amd64_mov_reg_reg_size(inst,dreg,reg,size) //#define amd64_mov_reg_mem(inst,reg,mem,size) amd64_mov_reg_mem_size(inst,reg,mem,size) //#define amd64_mov_reg_membase(inst,reg,basereg,disp,size) amd64_mov_reg_membase_size(inst,reg,basereg,disp,size) #define amd64_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) amd64_mov_reg_memindex_size(inst,reg,basereg,disp,indexreg,shift,size) #define amd64_clear_reg(inst,reg) amd64_clear_reg_size(inst,reg,8) //#define amd64_mov_reg_imm(inst,reg,imm) amd64_mov_reg_imm_size(inst,reg,imm,8) #define amd64_mov_mem_imm(inst,mem,imm,size) amd64_mov_mem_imm_size(inst,mem,imm,size) //#define amd64_mov_membase_imm(inst,basereg,disp,imm,size) amd64_mov_membase_imm_size(inst,basereg,disp,imm,size) #define amd64_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) amd64_mov_memindex_imm_size(inst,basereg,disp,indexreg,shift,imm,size) #define amd64_lea_mem(inst,reg,mem) amd64_lea_mem_size(inst,reg,mem,8) //#define amd64_lea_membase(inst,reg,basereg,disp) amd64_lea_membase_size(inst,reg,basereg,disp,8) #define amd64_lea_memindex(inst,reg,basereg,disp,indexreg,shift) amd64_lea_memindex_size(inst,reg,basereg,disp,indexreg,shift,8) #define amd64_widen_reg(inst,dreg,reg,is_signed,is_half) amd64_widen_reg_size(inst,dreg,reg,is_signed,is_half,8) #define amd64_widen_mem(inst,dreg,mem,is_signed,is_half) amd64_widen_mem_size(inst,dreg,mem,is_signed,is_half,8) #define amd64_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) amd64_widen_membase_size(inst,dreg,basereg,disp,is_signed,is_half,8) #define amd64_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) amd64_widen_memindex_size(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half,8) #define amd64_cdq(inst) amd64_cdq_size(inst,8) #define amd64_wait(inst) amd64_wait_size(inst,8) #define amd64_fp_op_mem(inst,opc,mem,is_double) amd64_fp_op_mem_size(inst,opc,mem,is_double,8) #define amd64_fp_op_membase(inst,opc,basereg,disp,is_double) amd64_fp_op_membase_size(inst,opc,basereg,disp,is_double,8) #define amd64_fp_op(inst,opc,index) amd64_fp_op_size(inst,opc,index,8) #define amd64_fp_op_reg(inst,opc,index,pop_stack) amd64_fp_op_reg_size(inst,opc,index,pop_stack,8) #define amd64_fp_int_op_membase(inst,opc,basereg,disp,is_int) amd64_fp_int_op_membase_size(inst,opc,basereg,disp,is_int,8) #define amd64_fstp(inst,index) amd64_fstp_size(inst,index,8) #define amd64_fcompp(inst) amd64_fcompp_size(inst,8) #define amd64_fucompp(inst) amd64_fucompp_size(inst,8) #define amd64_fnstsw(inst) amd64_fnstsw_size(inst,8) #define amd64_fnstcw(inst,mem) amd64_fnstcw_size(inst,mem,8) #define amd64_fnstcw_membase(inst,basereg,disp) amd64_fnstcw_membase_size(inst,basereg,disp,8) #define amd64_fldcw(inst,mem) amd64_fldcw_size(inst,mem,8) #define amd64_fldcw_membase(inst,basereg,disp) amd64_fldcw_membase_size(inst,basereg,disp,8) #define amd64_fchs(inst) amd64_fchs_size(inst,8) #define amd64_frem(inst) amd64_frem_size(inst,8) #define amd64_fxch(inst,index) amd64_fxch_size(inst,index,8) #define amd64_fcomi(inst,index) amd64_fcomi_size(inst,index,8) #define amd64_fcomip(inst,index) amd64_fcomip_size(inst,index,8) #define amd64_fucomi(inst,index) amd64_fucomi_size(inst,index,8) #define amd64_fucomip(inst,index) amd64_fucomip_size(inst,index,8) #define amd64_fld(inst,mem,is_double) amd64_fld_size(inst,mem,is_double,8) #define amd64_fld_membase(inst,basereg,disp,is_double) amd64_fld_membase_size(inst,basereg,disp,is_double,8) #define amd64_fld80_mem(inst,mem) amd64_fld80_mem_size(inst,mem,8) #define amd64_fld80_membase(inst,basereg,disp) amd64_fld80_membase_size(inst,basereg,disp,8) #define amd64_fild(inst,mem,is_long) amd64_fild_size(inst,mem,is_long,8) #define amd64_fild_membase(inst,basereg,disp,is_long) amd64_fild_membase_size(inst,basereg,disp,is_long,8) #define amd64_fld_reg(inst,index) amd64_fld_reg_size(inst,index,8) #define amd64_fldz(inst) amd64_fldz_size(inst,8) #define amd64_fld1(inst) amd64_fld1_size(inst,8) #define amd64_fldpi(inst) amd64_fldpi_size(inst,8) #define amd64_fst(inst,mem,is_double,pop_stack) amd64_fst_size(inst,mem,is_double,pop_stack,8) #define amd64_fst_membase(inst,basereg,disp,is_double,pop_stack) amd64_fst_membase_size(inst,basereg,disp,is_double,pop_stack,8) #define amd64_fst80_mem(inst,mem) amd64_fst80_mem_size(inst,mem,8) #define amd64_fst80_membase(inst,basereg,disp) amd64_fst80_membase_size(inst,basereg,disp,8) #define amd64_fist_pop(inst,mem,is_long) amd64_fist_pop_size(inst,mem,is_long,8) #define amd64_fist_pop_membase(inst,basereg,disp,is_long) amd64_fist_pop_membase_size(inst,basereg,disp,is_long,8) #define amd64_fstsw(inst) amd64_fstsw_size(inst,8) #define amd64_fist_membase(inst,basereg,disp,is_int) amd64_fist_membase_size(inst,basereg,disp,is_int,8) //#define amd64_push_reg(inst,reg) amd64_push_reg_size(inst,reg,8) #define amd64_push_regp(inst,reg) amd64_push_regp_size(inst,reg,8) #define amd64_push_mem(inst,mem) amd64_push_mem_size(inst,mem,8) //#define amd64_push_membase(inst,basereg,disp) amd64_push_membase_size(inst,basereg,disp,8) #define amd64_push_memindex(inst,basereg,disp,indexreg,shift) amd64_push_memindex_size(inst,basereg,disp,indexreg,shift,8) #define amd64_push_imm(inst,imm) amd64_push_imm_size(inst,imm,8) //#define amd64_pop_reg(inst,reg) amd64_pop_reg_size(inst,reg,8) #define amd64_pop_mem(inst,mem) amd64_pop_mem_size(inst,mem,8) #define amd64_pop_membase(inst,basereg,disp) amd64_pop_membase_size(inst,basereg,disp,8) #define amd64_pushad(inst) amd64_pushad_size(inst,8) #define amd64_pushfd(inst) amd64_pushfd_size(inst,8) #define amd64_popad(inst) amd64_popad_size(inst,8) #define amd64_popfd(inst) amd64_popfd_size(inst,8) #define amd64_loop(inst,imm) amd64_loop_size(inst,imm,8) #define amd64_loope(inst,imm) amd64_loope_size(inst,imm,8) #define amd64_loopne(inst,imm) amd64_loopne_size(inst,imm,8) #define amd64_jump32(inst,imm) amd64_jump32_size(inst,imm,8) #define amd64_jump8(inst,imm) amd64_jump8_size(inst,imm,8) #define amd64_jump_reg(inst,reg) amd64_jump_reg_size(inst,reg,8) #define amd64_jump_mem(inst,mem) amd64_jump_mem_size(inst,mem,8) #define amd64_jump_membase(inst,basereg,disp) amd64_jump_membase_size(inst,basereg,disp,8) #define amd64_jump_code(inst,target) amd64_jump_code_size(inst,target,8) #define amd64_jump_disp(inst,disp) amd64_jump_disp_size(inst,disp,8) #define amd64_branch8(inst,cond,imm,is_signed) amd64_branch8_size(inst,cond,imm,is_signed,8) #define amd64_branch32(inst,cond,imm,is_signed) amd64_branch32_size(inst,cond,imm,is_signed,8) #define amd64_branch(inst,cond,target,is_signed) amd64_branch_size(inst,cond,target,is_signed,8) #define amd64_branch_disp(inst,cond,disp,is_signed) amd64_branch_disp_size(inst,cond,disp,is_signed,8) #define amd64_set_reg(inst,cond,reg,is_signed) amd64_set_reg_size(inst,cond,reg,is_signed,8) #define amd64_set_mem(inst,cond,mem,is_signed) amd64_set_mem_size(inst,cond,mem,is_signed,8) #define amd64_set_membase(inst,cond,basereg,disp,is_signed) amd64_set_membase_size(inst,cond,basereg,disp,is_signed,8) #define amd64_call_imm(inst,disp) amd64_call_imm_size(inst,disp,8) //#define amd64_call_reg(inst,reg) amd64_call_reg_size(inst,reg,8) #define amd64_call_mem(inst,mem) amd64_call_mem_size(inst,mem,8) #define amd64_call_membase(inst,basereg,disp) amd64_call_membase_size(inst,basereg,disp,8) #define amd64_call_code(inst,target) amd64_call_code_size(inst,target,8) //#define amd64_ret(inst) amd64_ret_size(inst,8) #define amd64_ret_imm(inst,imm) amd64_ret_imm_size(inst,imm,8) #define amd64_cmov_reg(inst,cond,is_signed,dreg,reg) amd64_cmov_reg_size(inst,cond,is_signed,dreg,reg,8) #define amd64_cmov_mem(inst,cond,is_signed,reg,mem) amd64_cmov_mem_size(inst,cond,is_signed,reg,mem,8) #define amd64_cmov_membase(inst,cond,is_signed,reg,basereg,disp) amd64_cmov_membase_size(inst,cond,is_signed,reg,basereg,disp,8) #define amd64_enter(inst,framesize) amd64_enter_size(inst,framesize) //#define amd64_leave(inst) amd64_leave_size(inst,8) #define amd64_sahf(inst) amd64_sahf_size(inst,8) #define amd64_fsin(inst) amd64_fsin_size(inst,8) #define amd64_fcos(inst) amd64_fcos_size(inst,8) #define amd64_fabs(inst) amd64_fabs_size(inst,8) #define amd64_ftst(inst) amd64_ftst_size(inst,8) #define amd64_fxam(inst) amd64_fxam_size(inst,8) #define amd64_fpatan(inst) amd64_fpatan_size(inst,8) #define amd64_fprem(inst) amd64_fprem_size(inst,8) #define amd64_fprem1(inst) amd64_fprem1_size(inst,8) #define amd64_frndint(inst) amd64_frndint_size(inst,8) #define amd64_fsqrt(inst) amd64_fsqrt_size(inst,8) #define amd64_fptan(inst) amd64_fptan_size(inst,8) #define amd64_padding(inst,size) amd64_padding_size(inst,size) #define amd64_prolog(inst,frame,reg_mask) amd64_prolog_size(inst,frame,reg_mask,8) #define amd64_epilog(inst,reg_mask) amd64_epilog_size(inst,reg_mask,8) static inline void amd64_jump_code_fn(u_char **instp,u_char *target) { amd64_jump_code(*instp,target); } #endif // AMD64_H dynamips-0.2.14/unstable/cpu.c000066400000000000000000000163151241034141600161740ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Management of CPU groups (for MP systems). */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "tcb.h" #include "memory.h" #include "device.h" #include "mips64.h" #include "mips64_cp0.h" #include "mips64_exec.h" #include "mips64_jit.h" #include "ppc32.h" #include "ppc32_exec.h" #include "ppc32_jit.h" #include "dynamips.h" /* Find a CPU in a group given its ID */ cpu_gen_t *cpu_group_find_id(cpu_group_t *group,u_int id) { cpu_gen_t *cpu; if (!group) return NULL; for(cpu=group->cpu_list;cpu;cpu=cpu->next) if (cpu->id == id) return cpu; return NULL; } /* Find the highest CPU ID in a CPU group */ int cpu_group_find_highest_id(cpu_group_t *group,u_int *highest_id) { cpu_gen_t *cpu; u_int max_id = 0; if (!group || group->cpu_list) return(-1); for(cpu=group->cpu_list;cpu;cpu=cpu->next) if (cpu->id >= max_id) max_id = cpu->id; *highest_id = max_id; return(0); } /* Add a CPU in a CPU group */ int cpu_group_add(cpu_group_t *group,cpu_gen_t *cpu) { if (!group) return(-1); /* check that we don't already have a CPU with this id */ if (cpu_group_find_id(group,cpu->id) != NULL) { fprintf(stderr,"cpu_group_add: CPU%u already present in group.\n", cpu->id); return(-1); } cpu->next = group->cpu_list; group->cpu_list = cpu; return(0); } /* Create a new CPU group */ cpu_group_t *cpu_group_create(char *name) { cpu_group_t *group; if (!(group = malloc(sizeof(*group)))) return NULL; group->name = name; group->cpu_list = NULL; return group; } /* Delete a CPU group */ void cpu_group_delete(cpu_group_t *group) { cpu_gen_t *cpu,*next; if (group != NULL) { for(cpu=group->cpu_list;cpu;cpu=next) { next = cpu->next; cpu_delete(cpu); } free(group); } } /* Rebuild the MTS subsystem for a CPU group */ int cpu_group_rebuild_mts(cpu_group_t *group) { cpu_gen_t *cpu; for(cpu=group->cpu_list;cpu;cpu=cpu->next) cpu->mts_rebuild(cpu); return(0); } /* Log a message for a CPU */ void cpu_log(cpu_gen_t *cpu,char *module,char *format,...) { char buffer[256]; va_list ap; va_start(ap,format); snprintf(buffer,sizeof(buffer),"CPU%u: %s",cpu->id,module); vm_flog(cpu->vm,buffer,format,ap); va_end(ap); } /* Create a new CPU */ cpu_gen_t *cpu_create(vm_instance_t *vm,u_int type,u_int id) { void *(*cpu_run_fn)(void *); cpu_gen_t *cpu; if (!(cpu = malloc(sizeof(*cpu)))) return NULL; memset(cpu,0,sizeof(*cpu)); cpu->vm = vm; cpu->id = id; cpu->type = type; cpu->state = CPU_STATE_SUSPENDED; cpu->tsg = vm->tsg; switch(cpu->type) { case CPU_TYPE_MIPS64: cpu->jit_op_array_size = MIPS_INSN_PER_PAGE; CPU_MIPS64(cpu)->vm = vm; CPU_MIPS64(cpu)->gen = cpu; mips64_init(CPU_MIPS64(cpu)); cpu_run_fn = (void *)mips64_jit_run_cpu; if (!cpu->vm->jit_use) cpu_run_fn = (void *)mips64_exec_run_cpu; else mips64_jit_init(CPU_MIPS64(cpu)); break; case CPU_TYPE_PPC32: cpu->jit_op_array_size = PPC32_INSN_PER_PAGE; CPU_PPC32(cpu)->vm = vm; CPU_PPC32(cpu)->gen = cpu; ppc32_init(CPU_PPC32(cpu)); cpu_run_fn = (void *)ppc32_jit_run_cpu; if (!cpu->vm->jit_use) cpu_run_fn = (void *)ppc32_exec_run_cpu; else ppc32_jit_init(CPU_PPC32(cpu)); break; default: fprintf(stderr,"CPU type %u is not supported yet\n",cpu->type); abort(); break; } /* create the CPU thread execution */ if (pthread_create(&cpu->cpu_thread,NULL,cpu_run_fn,cpu) != 0) { fprintf(stderr,"cpu_create: unable to create thread for CPU%u\n",id); free(cpu); return NULL; } return cpu; } /* Delete a CPU */ void cpu_delete(cpu_gen_t *cpu) { if (cpu) { /* Stop activity of this CPU */ cpu_stop(cpu); pthread_join(cpu->cpu_thread,NULL); /* Free resources */ switch(cpu->type) { case CPU_TYPE_MIPS64: mips64_delete(CPU_MIPS64(cpu)); break; case CPU_TYPE_PPC32: ppc32_delete(CPU_PPC32(cpu)); break; } free(cpu->jit_op_array); free(cpu); } } /* Start a CPU */ void cpu_start(cpu_gen_t *cpu) { if (cpu) { cpu_log(cpu,"CPU_STATE","Starting CPU (old state=%u)...\n",cpu->state); cpu->state = CPU_STATE_RUNNING; } } /* Stop a CPU */ void cpu_stop(cpu_gen_t *cpu) { if (cpu) { cpu_log(cpu,"CPU_STATE","Halting CPU (old state=%u)...\n",cpu->state); cpu->state = CPU_STATE_HALTED; } } /* Start all CPUs of a CPU group */ void cpu_group_start_all_cpu(cpu_group_t *group) { cpu_gen_t *cpu; for(cpu=group->cpu_list;cpu;cpu=cpu->next) cpu_start(cpu); } /* Stop all CPUs of a CPU group */ void cpu_group_stop_all_cpu(cpu_group_t *group) { cpu_gen_t *cpu; for(cpu=group->cpu_list;cpu;cpu=cpu->next) cpu_stop(cpu); } /* Set a state of all CPUs of a CPU group */ void cpu_group_set_state(cpu_group_t *group,u_int state) { cpu_gen_t *cpu; for(cpu=group->cpu_list;cpu;cpu=cpu->next) cpu->state = state; } /* Returns TRUE if all CPUs in a CPU group are inactive */ static int cpu_group_check_activity(cpu_group_t *group) { cpu_gen_t *cpu; for(cpu=group->cpu_list;cpu;cpu=cpu->next) { if (!cpu->cpu_thread_running) continue; if ((cpu->state == CPU_STATE_RUNNING) || !cpu->seq_state) return(FALSE); } return(TRUE); } /* Synchronize on CPUs (all CPUs must be inactive) */ int cpu_group_sync_state(cpu_group_t *group) { cpu_gen_t *cpu; m_tmcnt_t t1,t2; /* Check that CPU activity is really suspended */ t1 = m_gettime(); for(cpu=group->cpu_list;cpu;cpu=cpu->next) cpu->seq_state = 0; while(!cpu_group_check_activity(group)) { t2 = m_gettime(); if (t2 > (t1 + 10000)) return(-1); usleep(50000); } return(0); } /* Save state of all CPUs */ int cpu_group_save_state(cpu_group_t *group) { cpu_gen_t *cpu; for(cpu=group->cpu_list;cpu;cpu=cpu->next) cpu->prev_state = cpu->state; return(TRUE); } /* Restore state of all CPUs */ int cpu_group_restore_state(cpu_group_t *group) { cpu_gen_t *cpu; for(cpu=group->cpu_list;cpu;cpu=cpu->next) cpu->state = cpu->prev_state; return(TRUE); } /* Virtual idle loop */ void cpu_idle_loop(cpu_gen_t *cpu) { struct timespec t_spc; m_tmcnt_t expire; expire = m_gettime_usec() + cpu->idle_sleep_time; pthread_mutex_lock(&cpu->idle_mutex); t_spc.tv_sec = expire / 1000000; t_spc.tv_nsec = (expire % 1000000) * 1000; pthread_cond_timedwait(&cpu->idle_cond,&cpu->idle_mutex,&t_spc); pthread_mutex_unlock(&cpu->idle_mutex); } /* Break idle wait state */ void cpu_idle_break_wait(cpu_gen_t *cpu) { pthread_cond_signal(&cpu->idle_cond); cpu->idle_count = 0; } dynamips-0.2.14/unstable/cpu.h000066400000000000000000000134231241034141600161760ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __CPU_H__ #define __CPU_H__ #include #include #include "utils.h" #include "jit_op.h" #include "mips64.h" #include "mips64_cp0.h" #include "ppc32.h" /* Possible CPU types */ enum { CPU_TYPE_MIPS64 = 1, CPU_TYPE_PPC32, }; /* Virtual CPU states */ enum { CPU_STATE_RUNNING = 0, CPU_STATE_HALTED, CPU_STATE_SUSPENDED, }; /* Maximum results for idle pc */ #define CPU_IDLE_PC_MAX_RES 10 /* Idle PC proposed value */ struct cpu_idle_pc { m_uint64_t pc; u_int count; }; /* Number of recorded memory accesses (power of two) */ #define MEMLOG_COUNT 16 typedef struct memlog_access memlog_access_t; struct memlog_access { m_uint64_t iaddr; m_uint64_t vaddr; m_uint64_t data; m_uint32_t data_valid; m_uint32_t op_size; m_uint32_t op_type; }; /* Undefined memory access handler */ typedef int (*cpu_undefined_mem_handler_t)(cpu_gen_t *cpu,m_uint64_t vaddr, u_int op_size,u_int op_type, m_uint64_t *data); /* Generic CPU definition */ struct cpu_gen { /* CPU type and identifier for MP systems */ u_int type,id; /* CPU states */ volatile u_int state,prev_state; volatile m_uint64_t seq_state; /* Thread running this CPU */ pthread_t cpu_thread; volatile int cpu_thread_running; /* Exception restore point */ jmp_buf exec_loop_env; /* "Idle" loop management */ u_int idle_count,idle_max,idle_sleep_time; pthread_mutex_t idle_mutex; pthread_cond_t idle_cond; /* VM instance */ vm_instance_t *vm; /* Next CPU in group */ cpu_gen_t *next; /* Idle PC proposal */ struct cpu_idle_pc idle_pc_prop[CPU_IDLE_PC_MAX_RES]; u_int idle_pc_prop_count; /* Specific CPU part */ union { cpu_mips_t mips64_cpu; cpu_ppc_t ppc32_cpu; }sp; /* Methods */ void (*reg_set)(cpu_gen_t *cpu,u_int reg_index,m_uint64_t val); void (*reg_dump)(cpu_gen_t *cpu); void (*mmu_dump)(cpu_gen_t *cpu); void (*mmu_raw_dump)(cpu_gen_t *cpu); void (*add_breakpoint)(cpu_gen_t *cpu,m_uint64_t addr); void (*remove_breakpoint)(cpu_gen_t *cpu,m_uint64_t addr); void (*set_idle_pc)(cpu_gen_t *cpu,m_uint64_t addr); void (*get_idling_pc)(cpu_gen_t *cpu); void (*mts_rebuild)(cpu_gen_t *cpu); void (*mts_show_stats)(cpu_gen_t *cpu); cpu_undefined_mem_handler_t undef_mem_handler; /* Memory access log for fault debugging */ u_int memlog_pos; memlog_access_t memlog_array[MEMLOG_COUNT]; /* Statistics */ m_uint64_t dev_access_counter; /* JIT op array for current compiled pages */ u_int jit_op_array_size; jit_op_t **jit_op_array; jit_op_t **jit_op_current; /* Translation group ID and TCB descriptor local list */ int tsg; cpu_tc_t *tc_local_list; /* Current and free lists of TBs */ cpu_tb_t *tb_list,*tb_free_list; /* Virtual and Physical hash tables to retrieve TBs */ cpu_tb_t **tb_virt_hash,**tb_phys_hash; /* CPU List for a Translation Sharing Group */ cpu_gen_t **tsg_pprev,*tsg_next; /* JIT op pool */ jit_op_t *jit_op_pool[JIT_OP_POOL_NR]; }; /* CPU group definition */ typedef struct cpu_group cpu_group_t; struct cpu_group { char *name; cpu_gen_t *cpu_list; void *priv_data; }; #define CPU_MIPS64(cpu) (&(cpu)->sp.mips64_cpu) #define CPU_PPC32(cpu) (&(cpu)->sp.ppc32_cpu) /* Get CPU instruction pointer */ static forced_inline m_uint64_t cpu_get_pc(cpu_gen_t *cpu) { switch(cpu->type) { case CPU_TYPE_MIPS64: return(CPU_MIPS64(cpu)->pc); case CPU_TYPE_PPC32: return((m_uint64_t)CPU_PPC32(cpu)->ia); default: return(0); } } /* Get CPU performance counter */ static forced_inline m_uint32_t cpu_get_perf_counter(cpu_gen_t *cpu) { switch(cpu->type) { case CPU_TYPE_MIPS64: return(CPU_MIPS64(cpu)->perf_counter); case CPU_TYPE_PPC32: return(CPU_PPC32(cpu)->perf_counter); default: return(0); } } /* Find a CPU in a group given its ID */ cpu_gen_t *cpu_group_find_id(cpu_group_t *group,u_int id); /* Find the highest CPU ID in a CPU group */ int cpu_group_find_highest_id(cpu_group_t *group,u_int *highest_id); /* Add a CPU in a CPU group */ int cpu_group_add(cpu_group_t *group,cpu_gen_t *cpu); /* Create a new CPU group */ cpu_group_t *cpu_group_create(char *name); /* Delete a CPU group */ void cpu_group_delete(cpu_group_t *group); /* Rebuild the MTS subsystem for a CPU group */ int cpu_group_rebuild_mts(cpu_group_t *group); /* Log a message for a CPU */ void cpu_log(cpu_gen_t *cpu,char *module,char *format,...); /* Create a new CPU */ cpu_gen_t *cpu_create(vm_instance_t *vm,u_int type,u_int id); /* Delete a CPU */ void cpu_delete(cpu_gen_t *cpu); /* Start a CPU */ void cpu_start(cpu_gen_t *cpu); /* Stop a CPU */ void cpu_stop(cpu_gen_t *cpu); /* Start all CPUs of a CPU group */ void cpu_group_start_all_cpu(cpu_group_t *group); /* Stop all CPUs of a CPU group */ void cpu_group_stop_all_cpu(cpu_group_t *group); /* Set a state of all CPUs of a CPU group */ void cpu_group_set_state(cpu_group_t *group,u_int state); /* Synchronize on CPUs (all CPUs must be inactive) */ int cpu_group_sync_state(cpu_group_t *group); /* Save state of all CPUs */ int cpu_group_save_state(cpu_group_t *group); /* Restore state of all CPUs */ int cpu_group_restore_state(cpu_group_t *group); /* Virtual idle loop */ void cpu_idle_loop(cpu_gen_t *cpu); /* Break idle wait state */ void cpu_idle_break_wait(cpu_gen_t *cpu); /* Returns to the CPU exec loop */ static inline void cpu_exec_loop_enter(cpu_gen_t *cpu) { longjmp(cpu->exec_loop_env,1); } /* Set the exec loop entry point */ #define cpu_exec_loop_set(cpu) setjmp((cpu)->exec_loop_env) #endif dynamips-0.2.14/unstable/hv_vm.c000066400000000000000000001066471241034141600165340ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor generic VM routines. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "device.h" #include "dev_c7200.h" #include "dev_vtty.h" #include "utils.h" #include "base64.h" #include "net.h" #include "atm.h" #include "frame_relay.h" #include "crc.h" #include "net_io.h" #include "net_io_bridge.h" #ifdef GEN_ETH #include "gen_eth.h" #endif #include "registry.h" #include "hypervisor.h" #include "get_cpu_time.h" /* Find the specified CPU */ static cpu_gen_t *find_cpu(hypervisor_conn_t *conn,vm_instance_t *vm, u_int cpu_id) { cpu_gen_t *cpu; cpu = cpu_group_find_id(vm->cpu_group,cpu_id); if (!cpu) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BAD_OBJ,1,"Bad CPU specified"); return NULL; } return cpu; } /* Create a VM instance */ static int cmd_create(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = vm_create_instance(argv[0],atoi(argv[1]),argv[2]))) { hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to create VM instance '%s'", argv[0]); return(-1); } vm->vtty_con_type = VTTY_TYPE_NONE; vm->vtty_aux_type = VTTY_TYPE_NONE; vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' created",argv[0]); return(0); } /* Rename a VM instance */ static int cmd_rename(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (registry_exists(argv[1],OBJ_TYPE_VM)) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename VM instance '%s', '%s' already exists", argv[0],argv[1]); return(-1); } if (vm_rename_instance(vm,argv[1])) { hypervisor_send_reply(conn,HSC_ERR_RENAME,1, "unable to rename VM instance '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' renamed to '%s'",argv[0],argv[1]); return(0); } /* Delete a VM instance */ static int cmd_delete(hypervisor_conn_t *conn,int argc,char *argv[]) { int res; res = vm_delete_instance(argv[0]); if (res == 1) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' deleted",argv[0]); } else { hypervisor_send_reply(conn,HSC_ERR_DELETE,1, "unable to delete VM '%s'",argv[0]); } return(res); } /* Delete a VM instance and related files */ static int cmd_clean_delete(hypervisor_conn_t *conn,int argc,char *argv[]) { int res, i; glob_t globbuf; char *pattern; vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); pattern = vm_build_filename(vm, "*"); vm_release(vm); res = vm_delete_instance(argv[0]); if (res == 1) { /* delete related files (best effort) */ if (pattern != NULL && glob(pattern, GLOB_NOSORT, NULL, &globbuf) == 0) { for (i = 0; i < globbuf.gl_pathc; i++) { remove(globbuf.gl_pathv[i]); } globfree(&globbuf); } hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' and related files deleted",argv[0]); } else { hypervisor_send_reply(conn,HSC_ERR_DELETE,1, "unable to delete VM '%s'",argv[0]); } free(pattern); return(res); } /* Start a VM instance */ static int cmd_start(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (vm->vtty_con_type == VTTY_TYPE_NONE) { hypervisor_send_reply(conn,HSC_INFO_MSG,0, "Warning: no console port defined for " "VM '%s'",argv[0]); } if (vm_init_instance(vm) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_START,1, "unable to start VM instance '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' started",argv[0]); return(0); } /* Stop a VM instance */ static int cmd_stop(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (vm_stop_instance(vm) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_STOP,1, "unable to stop VM instance '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' stopped",argv[0]); return(0); } /* Get the status of a VM instance */ static int cmd_get_status(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; int status; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); status = vm->status; vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"%d",status); return(0); } /* Set translation sharing group */ static int cmd_set_tsg(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; int res; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); res = vm_set_tsg(vm,atoi(argv[1])); vm_release(vm); if (res < 0) hypervisor_send_reply(conn,HSC_ERR_BAD_PARAM,1,"unable to set group"); else hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set debugging level */ static int cmd_set_debug_level(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->debug_level = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set IOS image filename */ static int cmd_set_ios(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (vm_ios_set_image(vm,argv[1]) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to store IOS image name for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"IOS image set for '%s'",argv[0]); return(0); } /* Set IOS configuration filename to load at startup */ static int cmd_set_config(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; const char *startup_filename = argv[1]; const char *private_filename = ((argc > 2) ? argv[2] : ""); if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (0 == strlen(startup_filename)) startup_filename = NULL; // keep existing data if (0 == strlen(private_filename)) private_filename = NULL; // keep existing data if (vm_ios_set_config(vm,startup_filename,private_filename) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to store IOS config for router '%s'", argv[0]); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"IOS config file set for '%s'", argv[0]); return(0); } /* Set RAM size */ static int cmd_set_ram(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->ram_size = atoi(argv[1]); /* XXX npe-400 can split excess ram into iomem, adjusting ram_size */ if (vm->elf_machine_id == C7200_ELF_MACHINE_ID) VM_C7200(vm)->npe400_ram_size = vm->ram_size; vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set NVRAM size */ static int cmd_set_nvram(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->nvram_size = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Enable/disable use of a memory-mapped file to simulate RAM */ static int cmd_set_ram_mmap(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->ram_mmap = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Enable/disable use of sparse memory */ static int cmd_set_sparse_mem(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->sparse_mem = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the clock divisor */ static int cmd_set_clock_divisor(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_int clock_div; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if ((clock_div = atoi(argv[1])) != 0) vm->clock_divisor = clock_div; vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Enable/disable use of block direct jump (compatibility option) */ static int cmd_set_blk_direct_jump(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->exec_blk_direct_jump = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the idle PC */ static int cmd_set_idle_pc(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->idle_pc = strtoull(argv[1],NULL,0); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the idle PC value when the CPU is online */ static int cmd_set_idle_pc_online(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!(cpu = find_cpu(conn,vm,atoi(argv[1])))) return(-1); cpu->set_idle_pc(cpu,strtoull(argv[2],NULL,0)); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Get the idle PC proposals */ static int cmd_get_idle_pc_prop(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; int i; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!(cpu = find_cpu(conn,vm,atoi(argv[1])))) return(-1); cpu->get_idling_pc(cpu); for(i=0;iidle_pc_prop_count;i++) { hypervisor_send_reply(conn,HSC_INFO_MSG,0,"0x%llx [%d]", cpu->idle_pc_prop[i].pc, cpu->idle_pc_prop[i].count); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Dump the idle PC proposals */ static int cmd_show_idle_pc_prop(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; int i; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!(cpu = find_cpu(conn,vm,atoi(argv[1])))) return(-1); for(i=0;iidle_pc_prop_count;i++) { hypervisor_send_reply(conn,HSC_INFO_MSG,0,"0x%llx [%d]", cpu->idle_pc_prop[i].pc, cpu->idle_pc_prop[i].count); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set CPU idle max value */ static int cmd_set_idle_max(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!(cpu = find_cpu(conn,vm,atoi(argv[1])))) return(-1); cpu->idle_max = atoi(argv[2]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set CPU idle sleep time value */ static int cmd_set_idle_sleep_time(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!(cpu = find_cpu(conn,vm,atoi(argv[1])))) return(-1); cpu->idle_sleep_time = atoi(argv[2]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show info about potential timer drift */ static int cmd_show_timer_drift(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!(cpu = find_cpu(conn,vm,atoi(argv[1])))) return(-1); switch(cpu->type) { case CPU_TYPE_MIPS64: hypervisor_send_reply(conn,HSC_INFO_MSG,0,"Timer Drift: %u", CPU_MIPS64(cpu)->timer_drift); hypervisor_send_reply(conn,HSC_INFO_MSG,0,"Pending Timer IRQ: %u", CPU_MIPS64(cpu)->timer_irq_pending); break; case CPU_TYPE_PPC32: hypervisor_send_reply(conn,HSC_INFO_MSG,0,"Timer Drift: %u", CPU_PPC32(cpu)->timer_drift); hypervisor_send_reply(conn,HSC_INFO_MSG,0,"Pending Timer IRQ: %u", CPU_PPC32(cpu)->timer_irq_pending); break; } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the exec area size */ static int cmd_set_exec_area(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->exec_area_size = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set ghost RAM file */ static int cmd_set_ghost_file(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); free(vm->ghost_ram_filename); vm->ghost_ram_filename = strdup(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set ghost RAM status */ static int cmd_set_ghost_status(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->ghost_status = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set PCMCIA ATA disk0 size */ static int cmd_set_disk0(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->pcmcia_disk_size[0] = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set PCMCIA ATA disk1 size */ static int cmd_set_disk1(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->pcmcia_disk_size[1] = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set the config register used at startup */ static int cmd_set_conf_reg(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->conf_reg_setup = strtol(argv[1],NULL,0); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set TCP port for console */ static int cmd_set_con_tcp_port(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->vtty_con_type = VTTY_TYPE_TCP; vm->vtty_con_tcp_port = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set TCP port for AUX port */ static int cmd_set_aux_tcp_port(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm->vtty_aux_type = VTTY_TYPE_TCP; vm->vtty_aux_tcp_port = atoi(argv[1]); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Read IOS configuration files from a given router */ static int cmd_extract_config(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_char *startup_config = NULL; u_char *private_config = NULL; size_t startup_len; size_t private_len; u_char *startup_base64 = NULL; u_char *private_base64 = NULL; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!vm->platform->nvram_extract_config) goto err_no_extract_method; /* Extract the IOS configuration */ if ((vm->platform->nvram_extract_config(vm,&startup_config,&startup_len,&private_config,&private_len))) goto err_nvram_extract; /* * Convert config to base64. base64 generates 4 bytes for each group of 3 bytes. */ if (!(startup_base64 = malloc(1 + (startup_len + 2) / 3 * 4)) || !(private_base64 = malloc(1 + (private_len + 2) / 3 * 4))) goto err_alloc_base64; base64_encode(startup_base64,startup_config,startup_len); base64_encode(private_base64,private_config,private_len); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"conf '%s' '%s' '%s'",argv[0],startup_base64,private_base64); free(private_base64); free(startup_base64); free(private_config); free(startup_config); return(0); err_alloc_base64: free(private_base64); free(startup_base64); free(private_config); free(startup_config); err_nvram_extract: err_no_extract_method: vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to extract config of VM '%s'",argv[0]); return(-1); } /* Push IOS configuration to a given router */ static int cmd_push_config(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_char *startup_config = NULL; u_char *private_config = NULL; int startup_len = 0; int private_len = 0; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); if (!vm->platform->nvram_push_config) goto err_no_push_method; /* * Convert base64 input to standard text. base64 uses 4 bytes for each group of 3 bytes. */ if (strcmp(argv[1],"(keep)") != 0) { startup_len = (strlen(argv[1]) + 3) / 4 * 3; if (!(startup_config = malloc(1 + startup_len))) goto err_alloc_base64; if ((startup_len = base64_decode(startup_config,(u_char *)argv[1],startup_len)) < 0) goto err_decode_base64; startup_config[startup_len] = '\0'; } if (argc > 2 && strcmp(argv[2],"(keep)") != 0) { private_len = (strlen(argv[2]) + 3) / 4 * 3; if (!(private_config = malloc(1 + private_len))) goto err_alloc_base64; if ((private_len = base64_decode(private_config,(u_char *)argv[2],private_len)) < 0) goto err_decode_base64; private_config[private_len] = '\0'; } /* Push configuration */ if (vm->platform->nvram_push_config(vm,startup_config,(size_t)startup_len,private_config,(size_t)private_len) < 0) goto err_nvram_push; free(private_config); free(startup_config); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1, "IOS config file pushed tm VM '%s'", argv[0]); return(0); err_nvram_push: err_decode_base64: err_alloc_base64: free(private_config); free(startup_config); err_no_push_method: vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_CREATE,1, "unable to push IOS config for VM '%s'", argv[0]); return(-1); } /* Show info about the specified CPU */ static int cmd_show_cpu_info(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; cpu_gen_t *cpu; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); cpu = cpu_group_find_id(vm->cpu_group,atoi(argv[1])); if (cpu) { cpu->reg_dump(cpu); cpu->mmu_dump(cpu); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show CPU usage - experimental */ static int cmd_show_cpu_usage(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; double usage; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); usage = get_cpu_time(); if (usage == -1) return(-1); hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%u", (unsigned long)usage); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Suspend a VM instance */ static int cmd_suspend(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm_suspend(vm); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' suspended",argv[0]); return(0); } /* Resume a VM instance */ static int cmd_resume(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); vm_resume(vm); vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"VM '%s' resumed",argv[0]); return(0); } /* Send a message on the console */ static int cmd_send_con_msg(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm = NULL; const char *format = NULL; char *data = NULL; int len,written; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); format = (argc > 2)? argv[2]: "plain"; len = (int)strlen(argv[1]); written = -1; if (strcmp(format,"plain") == 0 && len >= 0) { written = vtty_store_data(vm->vtty_con,argv[1],len); } else if (strcmp(format,"base64") == 0 && len >= 0) { data = malloc(len+1); len = base64_decode((unsigned char*)data,(const unsigned char *)argv[1],len); written = vtty_store_data(vm->vtty_con,data,len); free(data); data = NULL; } vm_release(vm); if (written < 0) { hypervisor_send_reply(conn,HSC_ERR_INV_PARAM,1,"Invalid parameter"); return(-1); } else { hypervisor_send_reply(conn,HSC_INFO_OK,1,"%d byte(s) written",written); return(0); } } /* Send a message on the AUX port */ static int cmd_send_aux_msg(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm = NULL; const char *format = NULL; char *data = NULL; int len,written; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); format = (argc > 2)? argv[2]: "plain"; len = (int)strlen(argv[1]); written = -1; if (strcmp(format,"plain") == 0 && len >= 0) { written = vtty_store_data(vm->vtty_aux,argv[1],len); } else if (strcmp(format,"base64") == 0 && len >= 0) { data = malloc(len+1); len = base64_decode((unsigned char*)data,(const unsigned char *)argv[1],len); written = vtty_store_data(vm->vtty_aux,data,len); free(data); data = NULL; } vm_release(vm); if (written < 0) { hypervisor_send_reply(conn,HSC_ERR_INV_PARAM,1,"Invalid parameter"); return(-1); } else { hypervisor_send_reply(conn,HSC_INFO_OK,1,"%d byte(s) written",written); return(0); } } /* Show slot bindings */ static int cmd_slot_bindings(hypervisor_conn_t *conn,int argc,char *argv[]) { struct cisco_card *card,*sc; vm_instance_t *vm; int i,j; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); for(i=0;inr_slots;i++) { if (!(card = vm_slot_get_card_ptr(vm,i))) continue; /* main module */ hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%u/%u: %s", card->slot_id,card->subslot_id,card->dev_type); /* sub-slots */ for(j=0;jsub_slots[j])) continue; hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%u/%u: %s", card->slot_id,card->subslot_id,card->dev_type); } } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show NIO bindings for the specified slot */ static int cmd_slot_nio_bindings(hypervisor_conn_t *conn,int argc,char *argv[]) { struct cisco_nio_binding *nb; struct cisco_card *card,*sc; vm_instance_t *vm; u_int i,slot; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); if ((card = vm_slot_get_card_ptr(vm,slot))) { /* main module */ for(nb=card->nio_list;nb;nb=nb->next) { hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%u: %s", nb->port_id,nb->nio->name); } /* sub-slots */ for(i=0;isub_slots[i])) continue; for(nb=sc->nio_list;nb;nb=nb->next) { hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%u: %s", nb->port_id,nb->nio->name); } } } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Add a slot binding */ static int cmd_slot_add_binding(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_int slot,port; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); port = atoi(argv[2]); if (vm_slot_add_binding(vm,argv[3],slot,port) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "VM %s: unable to add binding for slot %u/%u", argv[0],slot,port); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Remove a slot binding */ static int cmd_slot_remove_binding(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; u_int slot,port; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); port = atoi(argv[2]); if (vm_slot_remove_binding(vm,slot,port) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "VM %s: unable to remove binding for slot %u/%u", argv[0],slot,port); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Add a NIO binding for a slot/port */ static int cmd_slot_add_nio_binding(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; u_int slot,port; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); port = atoi(argv[2]); if (vm_slot_add_nio_binding(vm,slot,port,argv[3]) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "VM %s: unable to add binding " "for slot %u/%u",argv[0],slot,port); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Remove a NIO binding for a slot/port */ static int cmd_slot_remove_nio_binding(hypervisor_conn_t *conn, int argc,char *argv[]) { vm_instance_t *vm; u_int slot,port; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); port = atoi(argv[2]); if (vm_slot_remove_nio_binding(vm,slot,port) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "VM %s: unable to remove NIO binding " "for slot %u/%u",argv[0],slot,port); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Enable NIO of the specified slot/port */ static int cmd_slot_enable_nio(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_int slot,port; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); port = atoi(argv[2]); if (vm_slot_enable_nio(vm,slot,port) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "VM %s: unable to enable NIO for slot %u/%u", argv[0],slot,port); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Disable NIO of the specified slot/port */ static int cmd_slot_disable_nio(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_int slot,port; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); port = atoi(argv[2]); if (vm_slot_disable_nio(vm,slot,port) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BINDING,1, "VM %s: unable to disable NIO for slot %u/%u", argv[0],slot,port); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* OIR to start a slot/subslot */ static int cmd_slot_oir_start(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_int slot,subslot; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); subslot = atoi(argv[2]); if (vm_oir_start(vm,slot,subslot) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_START,1, "VM %s: unable to engage OIR for slot %u/%u", argv[0],slot,subslot); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* OIR to stop a slot/subslot */ static int cmd_slot_oir_stop(hypervisor_conn_t *conn,int argc,char *argv[]) { vm_instance_t *vm; u_int slot,subslot; if (!(vm = hypervisor_find_object(conn,argv[0],OBJ_TYPE_VM))) return(-1); slot = atoi(argv[1]); subslot = atoi(argv[2]); if (vm_oir_stop(vm,slot,subslot) == -1) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_STOP,1, "VM %s: unable to engage OIR for slot %u/%u", argv[0],slot,subslot); return(-1); } vm_release(vm); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show info about VM object */ static void cmd_show_vm_list(registry_entry_t *entry,void *opt,int *err) { hypervisor_conn_t *conn = opt; vm_instance_t *vm = entry->data; hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s (%s)", entry->name,vm_get_type(vm)); } /* VM List */ static int cmd_vm_list(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_VM,cmd_show_vm_list,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show console TCP port info about VM object */ static void cmd_show_vm_list_con_ports(registry_entry_t *entry,void *opt, int *err) { hypervisor_conn_t *conn = opt; vm_instance_t *vm = entry->data; if (vm->vtty_con_type == VTTY_TYPE_TCP) hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s (%d)", vm->name,vm->vtty_con_tcp_port); } /* VM console TCP port list */ static int cmd_vm_list_con_ports(hypervisor_conn_t *conn,int argc,char *argv[]) { int err = 0; registry_foreach_type(OBJ_TYPE_VM,cmd_show_vm_list_con_ports,conn,&err); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* VM commands */ static hypervisor_cmd_t vm_cmd_array[] = { { "create", 3, 3, cmd_create, NULL }, { "rename", 2, 2, cmd_rename, NULL }, { "delete", 1, 1, cmd_delete, NULL }, { "clean_delete", 1, 1, cmd_clean_delete, NULL, }, { "start", 1, 1, cmd_start, NULL }, { "stop", 1, 1, cmd_stop, NULL }, { "get_status", 1, 1, cmd_get_status, NULL }, { "set_tsg", 2, 2, cmd_set_tsg, NULL }, { "set_debug_level", 2, 2, cmd_set_debug_level, NULL }, { "set_ios", 2, 2, cmd_set_ios, NULL }, { "set_config", 2, 3, cmd_set_config, NULL }, { "set_ram", 2, 2, cmd_set_ram, NULL }, { "set_nvram", 2, 2, cmd_set_nvram, NULL }, { "set_ram_mmap", 2, 2, cmd_set_ram_mmap, NULL }, { "set_sparse_mem", 2, 2, cmd_set_sparse_mem, NULL }, { "set_clock_divisor", 2, 2, cmd_set_clock_divisor, NULL }, { "set_blk_direct_jump", 2, 2, cmd_set_blk_direct_jump, NULL }, { "set_exec_area", 2, 2, cmd_set_exec_area, NULL }, { "set_disk0", 2, 2, cmd_set_disk0, NULL }, { "set_disk1", 2, 2, cmd_set_disk1, NULL }, { "set_conf_reg", 2, 2, cmd_set_conf_reg, NULL }, { "set_idle_pc", 2, 2, cmd_set_idle_pc, NULL }, { "set_idle_pc_online", 3, 3, cmd_set_idle_pc_online, NULL }, { "get_idle_pc_prop", 2, 2, cmd_get_idle_pc_prop, NULL }, { "show_idle_pc_prop", 2, 2, cmd_show_idle_pc_prop, NULL }, { "set_idle_max", 3, 3, cmd_set_idle_max, NULL }, { "set_idle_sleep_time", 3, 3, cmd_set_idle_sleep_time, NULL }, { "show_timer_drift", 2, 2, cmd_show_timer_drift, NULL }, { "set_ghost_file", 2, 2, cmd_set_ghost_file, NULL }, { "set_ghost_status", 2, 2, cmd_set_ghost_status, NULL }, { "set_con_tcp_port", 2, 2, cmd_set_con_tcp_port, NULL }, { "set_aux_tcp_port", 2, 2, cmd_set_aux_tcp_port, NULL }, { "extract_config", 1, 1, cmd_extract_config, NULL }, { "push_config", 2, 3, cmd_push_config, NULL }, { "cpu_info", 2, 2, cmd_show_cpu_info, NULL }, { "cpu_usage", 2, 2, cmd_show_cpu_usage, NULL }, { "suspend", 1, 1, cmd_suspend, NULL }, { "resume", 1, 1, cmd_resume, NULL }, { "send_con_msg", 2, 3, cmd_send_con_msg, NULL }, { "send_aux_msg", 2, 3, cmd_send_aux_msg, NULL }, { "slot_bindings", 1, 1, cmd_slot_bindings, NULL }, { "slot_nio_bindings", 2, 2, cmd_slot_nio_bindings, NULL }, { "slot_add_binding", 4, 4, cmd_slot_add_binding, NULL }, { "slot_remove_binding", 3, 3, cmd_slot_remove_binding, NULL }, { "slot_add_nio_binding", 4, 4, cmd_slot_add_nio_binding, NULL }, { "slot_remove_nio_binding", 3, 3, cmd_slot_remove_nio_binding, NULL }, { "slot_enable_nio", 3, 3, cmd_slot_enable_nio, NULL }, { "slot_disable_nio", 3, 3, cmd_slot_disable_nio, NULL }, { "slot_oir_start", 3, 3, cmd_slot_oir_start, NULL }, { "slot_oir_stop", 3, 3, cmd_slot_oir_stop, NULL }, { "list", 0, 0, cmd_vm_list, NULL }, { "list_con_ports", 0, 0, cmd_vm_list_con_ports, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Hypervisor VM initialization */ int hypervisor_vm_init(void) { hypervisor_module_t *module; module = hypervisor_register_module("vm",NULL); assert(module != NULL); hypervisor_register_cmd_array(module,vm_cmd_array); return(0); } dynamips-0.2.14/unstable/hypervisor.c000066400000000000000000000417611241034141600176220ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Hypervisor routines. */ #include "dynamips_common.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "gen_uuid.h" #include "parser.h" #include "net.h" #include "registry.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "tcb.h" #include "dev_c7200.h" #include "dev_c3600.h" #include "dev_c2691.h" #include "dev_c3725.h" #include "dev_c3745.h" #include "dev_c2600.h" #include "dev_c1700.h" #include "hypervisor.h" #include "net_io.h" #include "net_io_bridge.h" #include "frame_relay.h" #include "atm.h" #define DEBUG_TOKEN 0 /* Hypervisor modules */ static hypervisor_module_t *module_list = NULL; static volatile int hypervisor_running = 0; /* Hypervisor connection list */ static hypervisor_conn_t *hypervisor_conn_list = NULL; /* Show hypervisor version */ static int cmd_version(hypervisor_conn_t *conn,int argc,char *argv[]) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"%s",sw_version); return(0); } /* Show UUID */ static int cmd_uuid(hypervisor_conn_t *conn,int argc,char *argv[]) { char buffer[40]; uuid_t local_uuid; gen_uuid_get_local(local_uuid); uuid_unparse(local_uuid,buffer); hypervisor_send_reply(conn,HSC_INFO_OK,1,"%s",buffer); return(0); } /* Parser test */ static int cmd_parser_test(hypervisor_conn_t *conn,int argc,char *argv[]) { int i; for(i=0;inext) hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s",m->name); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Show module command list */ static int cmd_modcmd_list(hypervisor_conn_t *conn,int argc,char *argv[]) { hypervisor_module_t *m; hypervisor_cmd_t *cmd; if (!(m = hypervisor_find_module(argv[0]))) { hypervisor_send_reply(conn,HSC_ERR_UNK_MODULE,1,"unknown module '%s'", argv[0]); return(-1); } for(cmd=m->cmd_list;cmd;cmd=cmd->next) hypervisor_send_reply(conn,HSC_INFO_MSG,0,"%s (min/max args: %d/%d)", cmd->name,cmd->min_param,cmd->max_param); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Set working directory */ static int cmd_set_working_dir(hypervisor_conn_t *conn,int argc,char *argv[]) { if (chdir(argv[0]) == -1) { hypervisor_send_reply(conn,HSC_ERR_INV_PARAM,1, "chdir: %s",strerror(errno)); } else { m_log("GENERAL","working_dir=%s\n",argv[0]); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); } return(0); } /* Save the hypervisor configuration in the specified file */ static int cmd_save_config(hypervisor_conn_t *conn,int argc,char *argv[]) { FILE *fd; if (!(fd = fopen(argv[0],"w"))) { hypervisor_send_reply(conn,HSC_ERR_FILE,1,"fopen: %s",strerror(errno)); return(-1); } /* Save configuration for all objects */ netio_save_config_all(fd); frsw_save_config_all(fd); atmsw_save_config_all(fd); //atm_bridge_save_config_all(fd); netio_bridge_save_config_all(fd); vm_save_config_all(fd); fclose(fd); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Reset hypervisor (delete all objects) */ static int cmd_reset(hypervisor_conn_t *conn,int argc,char *argv[]) { dynamips_reset(); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Close connection */ static int cmd_close(hypervisor_conn_t *conn,int argc,char *argv[]) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); conn->active = FALSE; return(0); } /* Stop hypervisor */ static int cmd_stop(hypervisor_conn_t *conn,int argc,char *argv[]) { hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); hypervisor_running = FALSE; return(0); } /* Statistics about JIT code sharing (dumped on console) */ static int cmd_tsg_stats(hypervisor_conn_t *conn,int argc,char *argv[]) { tsg_show_stats(); hypervisor_send_reply(conn,HSC_INFO_OK,1,"OK"); return(0); } /* Hypervisor commands */ static hypervisor_cmd_t hypervisor_cmd_array[] = { { "version", 0, 0, cmd_version, NULL }, { "uuid", 0, 0, cmd_uuid, NULL }, { "parser_test", 0, 10, cmd_parser_test, NULL }, { "module_list", 0, 0, cmd_mod_list, NULL }, { "cmd_list", 1, 1, cmd_modcmd_list, NULL }, { "working_dir", 1, 1, cmd_set_working_dir, NULL }, { "save_config", 1, 1, cmd_save_config, NULL }, { "reset", 0, 0, cmd_reset, NULL }, { "close", 0, 0, cmd_close, NULL }, { "stop", 0, 0, cmd_stop, NULL }, { "tsg_stats", 0, 0, cmd_tsg_stats, NULL }, { NULL, -1, -1, NULL, NULL }, }; /* Send a reply */ int hypervisor_send_reply(hypervisor_conn_t *conn,int code,int done, char *format,...) { va_list ap; size_t n = 0; if (conn != NULL) { va_start(ap,format); n += fprintf(conn->out,"%3d%s",code,(done)?"-":" "); n += vfprintf(conn->out,format,ap); n += fprintf(conn->out,"\r\n"); fflush(conn->out); va_end(ap); } return(n); } /* Find a module */ hypervisor_module_t *hypervisor_find_module(char *name) { hypervisor_module_t *m; for(m=module_list;m;m=m->next) if (!strcmp(m->name,name)) return m; return NULL; } /* Find a command in a module */ hypervisor_cmd_t *hypervisor_find_cmd(hypervisor_module_t *module,char *name) { hypervisor_cmd_t *cmd; for(cmd=module->cmd_list;cmd;cmd=cmd->next) if (!strcmp(cmd->name,name)) return cmd; return NULL; } /* Find an object in the registry */ void *hypervisor_find_object(hypervisor_conn_t *conn,char *name,int obj_type) { void *p; if (!(p = registry_find(name,obj_type))) { hypervisor_send_reply(conn,HSC_ERR_UNK_OBJ,1, "unable to find object '%s'",name); return NULL; } return p; } /* Find a VM in the registry */ void *hypervisor_find_vm(hypervisor_conn_t *conn,char *name) { vm_platform_t *platform = conn->cur_module->opt; vm_instance_t *vm; if (!(vm = vm_acquire(name))) { hypervisor_send_reply(conn,HSC_ERR_UNK_OBJ,1, "unable to find VM '%s'",name); return NULL; } if (vm->platform != platform) { vm_release(vm); hypervisor_send_reply(conn,HSC_ERR_BAD_OBJ,1, "VM '%s' is not a VM type %s", name,platform->name); return NULL; } return vm; } /* Destroy module_list */ static void destroy_module_list(void) { hypervisor_module_t *m, *next; for (m = module_list; m; m = next) { next = m->next; free(m); } module_list = NULL; } /* Register a module */ hypervisor_module_t *hypervisor_register_module(char *name,void *opt) { hypervisor_module_t *m; if (hypervisor_find_module(name) != NULL) { fprintf(stderr,"Hypervisor: module '%s' already exists.\n",name); return NULL; } if (!(m = malloc(sizeof(*m)))) { fprintf(stderr,"Hypervisor: unable to register new module.\n"); return NULL; } if (!module_list) atexit(destroy_module_list); m->name = name; m->opt = opt; m->cmd_list = NULL; m->next = module_list; module_list = m; return m; } /* Register a list of commands */ int hypervisor_register_cmd_list(hypervisor_module_t *module, hypervisor_cmd_t *cmd_list) { hypervisor_cmd_t *cmd = cmd_list; while(cmd->next != NULL) cmd = cmd->next; cmd->next = module->cmd_list; module->cmd_list = cmd_list; return(0); } /* Register an array of commands */ int hypervisor_register_cmd_array(hypervisor_module_t *module, hypervisor_cmd_t *cmd_array) { hypervisor_cmd_t *cmd; for(cmd=cmd_array;cmd->name!=NULL;cmd++) { cmd->next = module->cmd_list; module->cmd_list = cmd; } return(0); } /* Locate the module and execute command */ static int hypervisor_exec_cmd(hypervisor_conn_t *conn, char *mod_name,char *cmd_name, int argc,char *argv[]) { hypervisor_module_t *module; hypervisor_cmd_t *cmd; if (!(module = hypervisor_find_module(mod_name))) { hypervisor_send_reply(conn,HSC_ERR_UNK_MODULE,1,"Unknown module '%s'", mod_name); return(-1); } if (!(cmd = hypervisor_find_cmd(module,cmd_name))) { hypervisor_send_reply(conn,HSC_ERR_UNK_CMD,1,"Unknown command '%s'", cmd_name); return(-1); } if ((argc < cmd->min_param) || (argc > cmd->max_param)) { hypervisor_send_reply(conn,HSC_ERR_BAD_PARAM,1, "Bad number of parameters (%d with min/max=%d/%d)", argc,cmd->min_param,cmd->max_param); return(-1); } conn->cur_module = module; return(cmd->handler(conn,argc,argv)); } /* Thread for servicing connections */ static void *hypervisor_thread(void *arg) { hypervisor_conn_t *conn = arg; char buffer[512],**tokens; parser_context_t ctx; int res; tokens = NULL; parser_context_init(&ctx); while(conn->active) { if (!fgets(buffer,sizeof(buffer),conn->in)) break; if (!*buffer) continue; /* Tokenize command line */ res = parser_scan_buffer(&ctx,buffer,strlen(buffer)); if (res != 0) { tokens = NULL; if (ctx.error != 0) { hypervisor_send_reply(conn,HSC_ERR_PARSING,1,"Parse error: %s", parser_strerror(&ctx)); goto free_tokens; } if (ctx.tok_count < 2) { hypervisor_send_reply(conn,HSC_ERR_PARSING,1, "At least a module and a command " "must be specified"); goto free_tokens; } /* Map token list to an array */ tokens = parser_map_array(&ctx); if (!tokens) { hypervisor_send_reply(conn,HSC_ERR_PARSING,1,"No memory"); goto free_tokens; } /* Execute command */ m_log("HYPERVISOR","exec_cmd: "); m_flog_str_array(log_file,ctx.tok_count,tokens); hypervisor_exec_cmd(conn,tokens[0],tokens[1],ctx. tok_count-2,&tokens[2]); free_tokens: free(tokens); tokens = NULL; parser_context_free(&ctx); } } free(tokens); parser_context_free(&ctx); return NULL; } static void sigpipe_handler(int sig) { printf("SIGPIPE received.\n"); } /* Initialize hypervisor */ int hypervisor_init(void) { hypervisor_module_t *module; module = hypervisor_register_module("hypervisor",NULL); assert(module != NULL); hypervisor_register_cmd_array(module,hypervisor_cmd_array); return(0); } /* Remove a connection from the list */ static void hypervisor_remove_conn(hypervisor_conn_t *conn) { if (conn->pprev != NULL) { if (conn->next) conn->next->pprev = conn->pprev; *(conn->pprev) = conn->next; } } /* Close a connection */ static void hypervisor_close_conn(hypervisor_conn_t *conn) { if (conn != NULL) { conn->active = FALSE; shutdown(conn->client_fd,2); pthread_join(conn->tid,NULL); fclose(conn->in); fclose(conn->out); shutdown(conn->client_fd,2); close(conn->client_fd); hypervisor_remove_conn(conn); free(conn); } } /* Close connections (dead or all) */ static void hypervisor_close_conn_list(int dead_status) { hypervisor_conn_t *conn,*next; for(conn=hypervisor_conn_list;conn;conn=next) { next = conn->next; if (dead_status && conn->active) continue; hypervisor_close_conn(conn); } } /* Add a new connection to the list */ static void hypervisor_add_conn(hypervisor_conn_t *conn) { conn->next = hypervisor_conn_list; conn->pprev = &hypervisor_conn_list; if (hypervisor_conn_list != NULL) hypervisor_conn_list->pprev = &conn->next; hypervisor_conn_list = conn; } /* Create a new connection */ static hypervisor_conn_t *hypervisor_create_conn(int client_fd) { hypervisor_conn_t *conn; if (!(conn = malloc(sizeof(*conn)))) goto err_malloc; memset(conn,0,sizeof(*conn)); conn->active = TRUE; conn->client_fd = client_fd; /* Open input buffered stream */ if (!(conn->in = fdopen(client_fd,"r"))) { perror("hypervisor_create_conn: fdopen/in"); goto err_fd_in; } /* Open output buffered stream */ if (!(conn->out = fdopen(client_fd,"w"))) { perror("hypervisor_create_conn: fdopen/out"); goto err_fd_out; } /* Set line buffering */ setlinebuf(conn->in); setlinebuf(conn->out); /* Create the managing thread */ if (pthread_create(&conn->tid,NULL,hypervisor_thread,conn) != 0) goto err_thread; /* Add it to the connection list */ hypervisor_add_conn(conn); return conn; err_thread: fclose(conn->out); err_fd_out: fclose(conn->in); err_fd_in: free(conn); err_malloc: return NULL; } /* Stop hypervisor from sighandler */ int hypervisor_stopsig(void) { hypervisor_running = FALSE; return(0); } /* Hypervisor TCP server */ int hypervisor_tcp_server(char *ip_addr,int tcp_port) { int fd_array[HYPERVISOR_MAX_FD]; struct sockaddr_storage remote_addr; socklen_t remote_len; int i,res,clnt,fd_count,fd_max; struct timeval tv; fd_set fds; /* Initialize all hypervisor modules */ hypervisor_init(); hypervisor_nio_init(); hypervisor_nio_bridge_init(); hypervisor_frsw_init(); hypervisor_atmsw_init(); hypervisor_atm_bridge_init(); hypervisor_ethsw_init(); hypervisor_vm_init(); hypervisor_vm_debug_init(); hypervisor_store_init(); signal(SIGPIPE,sigpipe_handler); if (!tcp_port) tcp_port = HYPERVISOR_TCP_PORT; fd_count = ip_listen(ip_addr,tcp_port,SOCK_STREAM, HYPERVISOR_MAX_FD,fd_array); if (fd_count <= 0) { fprintf(stderr,"Hypervisor: unable to create TCP sockets.\n"); return(-1); } /* Start accepting connections */ m_log("HYPERVISOR","Release %s/%s (tag %s)\n", sw_version,os_name,sw_version_tag); if (ip_addr != NULL) { binding_addr = ip_addr; m_log("HYPERVISOR","Started on IP = %s, TCP port = %d.\n", ip_addr, tcp_port); printf("Hypervisor TCP control server started (IP %s port %d).\n", ip_addr, tcp_port); } else { m_log("HYPERVISOR","Started on TCP port = %d.\n",tcp_port); printf("Hypervisor TCP control server started (port %d).\n",tcp_port); } hypervisor_running = TRUE; while(hypervisor_running) { FD_ZERO(&fds); fd_max = -1; for(i=0;i fd_max) fd_max = fd_array[i]; } /* Wait for incoming connections */ tv.tv_sec = 0; tv.tv_usec = 500 * 1000; /* 500 ms */ res = select(fd_max+1,&fds,NULL,NULL,&tv); if (res == -1) { if (errno == EINTR) continue; else perror("hypervisor_tcp_server: select"); } /* Accept connections on signaled sockets */ for(i=0;i #include "utils.h" /* MTS operation */ #define MTS_READ 0 #define MTS_WRITE 1 /* 0.5GB value */ #define MTS_SIZE_512M 0x20000000 /* MTS flag bits: D (device), ACC (memory access), C (chain) */ #define MTS_FLAG_BITS 4 #define MTS_FLAG_MASK 0x0000000fUL /* Masks for MTS entries */ #define MTS_CHAIN_MASK 0x00000001 #define MTS_ACC_MASK 0x00000006 #define MTS_DEV_MASK 0x00000008 #define MTS_ADDR_MASK (~MTS_FLAG_MASK) /* Device ID mask and shift, device offset mask */ #define MTS_DEVID_MASK 0xfc000000 #define MTS_DEVID_SHIFT 26 #define MTS_DEVOFF_MASK 0x03ffffff /* Memory access flags */ #define MTS_ACC_AE 0x00000002 /* Address Error */ #define MTS_ACC_T 0x00000004 /* TLB Exception */ #define MTS_ACC_U 0x00000006 /* Unexistent */ /* Macro for easy hash computing */ #define MTS_SHR(v,sr) ((v) >> (sr)) /* Hash table size for MTS64 (default: [shift:16,bits:12]) */ #define MTS64_HASH_SHIFT1 12 #define MTS64_HASH_SHIFT2 20 #define MTS64_HASH_BITS 8 #define MTS64_HASH_SIZE (1 << MTS64_HASH_BITS) #define MTS64_HASH_MASK (MTS64_HASH_SIZE - 1) /* MTS64 hash on virtual addresses */ #define MTS64_SHR(v,i) (MTS_SHR((v),MTS64_HASH_SHIFT##i)) #define MTS64_HASH(vaddr) ((MTS64_SHR(vaddr,1) ^ MTS64_SHR(vaddr,2)) & MTS64_HASH_MASK) /* Hash table size for MTS32 (default: [shift:15,bits:15]) */ #define MTS32_HASH_SHIFT1 12 #define MTS32_HASH_SHIFT2 20 #define MTS32_HASH_BITS 8 #define MTS32_HASH_SIZE (1 << MTS32_HASH_BITS) #define MTS32_HASH_MASK (MTS32_HASH_SIZE - 1) /* MTS32 hash on virtual addresses */ #define MTS32_SHR(v,i) (MTS_SHR((v),MTS32_HASH_SHIFT##i)) #define MTS32_HASH(vaddr) ((MTS32_SHR(vaddr,1) ^ MTS32_SHR(vaddr,2)) & MTS32_HASH_MASK) /* Number of entries per chunk */ #define MTS64_CHUNK_SIZE 256 #define MTS32_CHUNK_SIZE 256 /* MTS64: chunk definition */ struct mts64_chunk { mts64_entry_t entry[MTS64_CHUNK_SIZE]; struct mts64_chunk *next; u_int count; }; /* MTS32: chunk definition */ struct mts32_chunk { mts32_entry_t entry[MTS32_CHUNK_SIZE]; struct mts32_chunk *next; u_int count; }; /* Record a memory access */ void memlog_rec_access(cpu_gen_t *cpu,m_uint64_t vaddr,m_uint64_t data, m_uint32_t op_size,m_uint32_t op_type); /* Show the last memory accesses */ void memlog_dump(cpu_gen_t *cpu); /* Update the data obtained by a read access */ void memlog_update_read(cpu_gen_t *cpu,m_iptr_t raddr); /* Copy a memory block from VM physical RAM to real host */ void physmem_copy_from_vm(vm_instance_t *vm,void *real_buffer, m_uint64_t paddr,size_t len); /* Copy a memory block to VM physical RAM from real host */ void physmem_copy_to_vm(vm_instance_t *vm,void *real_buffer, m_uint64_t paddr,size_t len); /* Copy a 32-bit word from the VM physical RAM to real host */ m_uint32_t physmem_copy_u32_from_vm(vm_instance_t *vm,m_uint64_t paddr); /* Copy a 32-bit word to the VM physical RAM from real host */ void physmem_copy_u32_to_vm(vm_instance_t *vm,m_uint64_t paddr,m_uint32_t val); /* Copy a 16-bit word from the VM physical RAM to real host */ m_uint16_t physmem_copy_u16_from_vm(vm_instance_t *vm,m_uint64_t paddr); /* Copy a 16-bit word to the VM physical RAM from real host */ void physmem_copy_u16_to_vm(vm_instance_t *vm,m_uint64_t paddr,m_uint16_t val); /* Copy a byte from the VM physical RAM to real host */ m_uint8_t physmem_copy_u8_from_vm(vm_instance_t *vm,m_uint64_t paddr); /* Copy a 16-bit word to the VM physical RAM from real host */ void physmem_copy_u8_to_vm(vm_instance_t *vm,m_uint64_t paddr,m_uint8_t val); /* DMA transfer operation */ void physmem_dma_transfer(vm_instance_t *vm,m_uint64_t src,m_uint64_t dst, size_t len); /* strlen in VM physical memory */ size_t physmem_strlen(vm_instance_t *vm,m_uint64_t paddr); /* find sequence of bytes in VM cacheable physical memory interval [first,last] */ int physmem_cfind(vm_instance_t *vm,m_uint8_t *bytes,size_t len, m_uint64_t first,m_uint64_t last, m_uint64_t *paddr); /* Physical memory dump (32-bit words) */ void physmem_dump_vm(vm_instance_t *vm,m_uint64_t paddr,m_uint32_t u32_count); #endif dynamips-0.2.14/unstable/mips64.c000066400000000000000000000720601241034141600165260ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * XXX TODO: proper context save/restore for CPUs. */ #include #include #include #include #include #include #include #include #include "rbtree.h" #include "cpu.h" #include "vm.h" #include "tcb.h" #include "mips64_mem.h" #include "mips64_exec.h" #include "mips64_jit.h" #include "dynamips.h" #include "memory.h" #include "device.h" /* MIPS general purpose registers names */ char *mips64_gpr_reg_names[MIPS64_GPR_NR] = { "zr", "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", "fp", "ra", }; /* Cacheability and Coherency Attribute */ static int cca_cache_status[8] = { 1, 1, 0, 1, 0, 1, 0, 0, }; /* Get register index given its name */ int mips64_get_reg_index(char *name) { int i; for(i=0;ipc = MIPS_ROM_PC; cpu->gpr[MIPS_GPR_SP] = MIPS_ROM_SP; cpu->cp0.reg[MIPS_CP0_STATUS] = MIPS_CP0_STATUS_BEV; cpu->cp0.reg[MIPS_CP0_CAUSE] = 0; cpu->cp0.reg[MIPS_CP0_CONFIG] = 0x00c08ff0ULL; /* Clear the complete TLB */ memset(&cpu->cp0.tlb,0,MIPS64_TLB_MAX_ENTRIES*sizeof(tlb_entry_t)); /* Restart the MTS subsystem */ mips64_set_addr_mode(cpu,32/*64*/); /* zzz */ cpu->gen->mts_rebuild(cpu->gen); /* Flush JIT structures */ mips64_jit_flush(cpu,0); return(0); } /* Initialize a MIPS64 processor */ int mips64_init(cpu_mips_t *cpu) { cpu->addr_bus_mask = 0xFFFFFFFFFFFFFFFFULL; cpu->cp0.reg[MIPS_CP0_PRID] = MIPS_PRID_R4600; cpu->cp0.tlb_entries = MIPS64_TLB_STD_ENTRIES; /* Initialize idle timer */ cpu->gen->idle_max = 500; cpu->gen->idle_sleep_time = 30000; /* Timer IRQ parameters (default frequency: 250 Hz <=> 4ms period) */ cpu->timer_irq_check_itv = 1000; cpu->timer_irq_freq = 250; /* Enable fast memory operations */ cpu->fast_memop = TRUE; /* Enable/Disable direct block jump */ cpu->exec_blk_direct_jump = cpu->vm->exec_blk_direct_jump; /* Create the IRQ lock (for non-jit architectures) */ pthread_mutex_init(&cpu->irq_lock,NULL); /* Idle loop mutex and condition */ pthread_mutex_init(&cpu->gen->idle_mutex,NULL); pthread_cond_init(&cpu->gen->idle_cond,NULL); /* Set the CPU methods */ cpu->gen->reg_set = (void *)mips64_reg_set; cpu->gen->reg_dump = (void *)mips64_dump_regs; cpu->gen->mmu_dump = (void *)mips64_tlb_dump; cpu->gen->mmu_raw_dump = (void *)mips64_tlb_raw_dump; cpu->gen->add_breakpoint = (void *)mips64_add_breakpoint; cpu->gen->remove_breakpoint = (void *)mips64_remove_breakpoint; cpu->gen->set_idle_pc = (void *)mips64_set_idle_pc; cpu->gen->get_idling_pc = (void *)mips64_get_idling_pc; /* Set the startup parameters */ mips64_reset(cpu); return(0); } /* Delete a MIPS64 processor */ void mips64_delete(cpu_mips_t *cpu) { if (cpu) { mips64_mem_shutdown(cpu); mips64_jit_shutdown(cpu); } } /* Set the CPU PRID register */ void mips64_set_prid(cpu_mips_t *cpu,m_uint32_t prid) { cpu->cp0.reg[MIPS_CP0_PRID] = prid; if ((prid == MIPS_PRID_R7000) || (prid == MIPS_PRID_BCM1250)) cpu->cp0.tlb_entries = MIPS64_TLB_MAX_ENTRIES; } /* Set idle PC value */ void mips64_set_idle_pc(cpu_gen_t *cpu,m_uint64_t addr) { CPU_MIPS64(cpu)->idle_pc = addr; } /* Timer IRQ */ void *mips64_timer_irq_run(cpu_mips_t *cpu) { pthread_mutex_t umutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t ucond = PTHREAD_COND_INITIALIZER; struct timespec t_spc; m_tmcnt_t expire; u_int interval; u_int threshold; interval = 1000000 / cpu->timer_irq_freq; threshold = cpu->timer_irq_freq * 10; expire = m_gettime_usec() + interval; while(cpu->gen->state != CPU_STATE_HALTED) { pthread_mutex_lock(&umutex); t_spc.tv_sec = expire / 1000000; t_spc.tv_nsec = (expire % 1000000) * 1000; pthread_cond_timedwait(&ucond,&umutex,&t_spc); pthread_mutex_unlock(&umutex); if (likely(!cpu->irq_disable) && likely(cpu->gen->state == CPU_STATE_RUNNING)) { cpu->timer_irq_pending++; if (unlikely(cpu->timer_irq_pending > threshold)) { cpu->timer_irq_pending = 0; cpu->timer_drift++; #if 0 printf("Timer IRQ not accurate (%u pending IRQ): " "reduce the \"--timer-irq-check-itv\" parameter " "(current value: %u)\n", cpu->timer_irq_pending,cpu->timer_irq_check_itv); #endif } } expire += interval; } return NULL; } #define IDLE_HASH_SIZE 8192 /* Idle PC hash item */ struct mips64_idle_pc_hash { m_uint64_t pc; u_int count; struct mips64_idle_pc_hash *next; }; /* Determine an "idling" PC */ int mips64_get_idling_pc(cpu_gen_t *cpu) { cpu_mips_t *mcpu = CPU_MIPS64(cpu); struct mips64_idle_pc_hash **pc_hash,*p; struct cpu_idle_pc *res; u_int h_index; m_uint64_t cur_pc; int i; cpu->idle_pc_prop_count = 0; if (mcpu->idle_pc != 0) { printf("\nYou already use an idle PC, using the calibration would give " "incorrect results.\n"); return(-1); } printf("\nPlease wait while gathering statistics...\n"); pc_hash = calloc(IDLE_HASH_SIZE,sizeof(struct mips64_idle_pc_hash *)); /* Disable IRQ */ mcpu->irq_disable = TRUE; /* Take 1000 measures, each mesure every 10ms */ for(i=0;i<1000;i++) { cur_pc = mcpu->pc; h_index = (cur_pc >> 2) & (IDLE_HASH_SIZE-1); for(p=pc_hash[h_index];p;p=p->next) if (p->pc == cur_pc) { p->count++; break; } if (!p) { if ((p = malloc(sizeof(*p)))) { p->pc = cur_pc; p->count = 1; p->next = pc_hash[h_index]; pc_hash[h_index] = p; } } usleep(10000); } /* Select PCs */ for(i=0;inext) if ((p->count >= 70) && (p->count <= 180)) { res = &cpu->idle_pc_prop[cpu->idle_pc_prop_count++]; res->pc = p->pc; res->count = p->count; if (cpu->idle_pc_prop_count >= CPU_IDLE_PC_MAX_RES) goto done; } } done: /* Set idle PC */ if (cpu->idle_pc_prop_count) { printf("Done. Suggested idling PC:\n"); for(i=0;iidle_pc_prop_count;i++) { printf(" 0x%llx (count=%u)\n", cpu->idle_pc_prop[i].pc, cpu->idle_pc_prop[i].count); } printf("Restart the emulator with \"--idle-pc=0x%llx\" (for example)\n", cpu->idle_pc_prop[0].pc); } else { printf("Done. No suggestion for idling PC\n"); for(i=0;inext) { printf(" 0x%16.16llx (%3u)\n",p->pc,p->count); if (cpu->idle_pc_prop_count < CPU_IDLE_PC_MAX_RES) { res = &cpu->idle_pc_prop[cpu->idle_pc_prop_count++]; res->pc = p->pc; res->count = p->count; } } printf("\n"); } /* Re-enable IRQ */ mcpu->irq_disable = FALSE; return(0); } /* Set an IRQ (VM IRQ standard routing) */ void mips64_vm_set_irq(vm_instance_t *vm,u_int irq) { cpu_mips_t *boot_cpu; boot_cpu = CPU_MIPS64(vm->boot_cpu); if (boot_cpu->irq_disable) { boot_cpu->irq_pending = 0; return; } mips64_set_irq(boot_cpu,irq); if (boot_cpu->irq_idle_preempt[irq]) cpu_idle_break_wait(vm->boot_cpu); } /* Clear an IRQ (VM IRQ standard routing) */ void mips64_vm_clear_irq(vm_instance_t *vm,u_int irq) { cpu_mips_t *boot_cpu; boot_cpu = CPU_MIPS64(vm->boot_cpu); mips64_clear_irq(boot_cpu,irq); } /* Update the IRQ flag (inline) */ static forced_inline int mips64_update_irq_flag_fast(cpu_mips_t *cpu) { mips_cp0_t *cp0 = &cpu->cp0; m_uint32_t imask,sreg_mask; m_uint32_t cause; cpu->irq_pending = FALSE; cause = cp0->reg[MIPS_CP0_CAUSE] & ~MIPS_CP0_CAUSE_IMASK; cp0->reg[MIPS_CP0_CAUSE] = cause | cpu->irq_cause; sreg_mask = MIPS_CP0_STATUS_IE|MIPS_CP0_STATUS_EXL|MIPS_CP0_STATUS_ERL; if ((cp0->reg[MIPS_CP0_STATUS] & sreg_mask) == MIPS_CP0_STATUS_IE) { imask = cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_IMASK; if (unlikely(cp0->reg[MIPS_CP0_CAUSE] & imask)) { cpu->irq_pending = TRUE; return(TRUE); } } return(FALSE); } /* Update the IRQ flag */ void mips64_update_irq_flag(cpu_mips_t *cpu) { mips64_update_irq_flag_fast(cpu); } /* Generate a general exception */ void mips64_general_exception(cpu_mips_t *cpu,u_int exc_code) { mips_cp0_t *cp0 = &cpu->cp0; m_uint64_t cause; /* Update cause register (set BD and ExcCode) */ cause = cp0->reg[MIPS_CP0_CAUSE] & MIPS_CP0_CAUSE_IMASK; if (cpu->bd_slot) cause |= MIPS_CP0_CAUSE_BD_SLOT; else cause &= ~MIPS_CP0_CAUSE_BD_SLOT; cause |= (exc_code << MIPS_CP0_CAUSE_SHIFT); cp0->reg[MIPS_CP0_CAUSE] = cause; /* If EXL bit is 0, set EPC and BadVaddr registers */ if (likely(!(cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_EXL))) { cp0->reg[MIPS_CP0_EPC] = cpu->pc - (cpu->bd_slot << 2); } /* Set EXL bit in status register */ cp0->reg[MIPS_CP0_STATUS] |= MIPS_CP0_STATUS_EXL; /* Use bootstrap vectors ? */ if (cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_BEV) cpu->pc = 0xffffffffbfc00200ULL + 0x180; else cpu->pc = 0xffffffff80000000ULL + 0x180; /* Clear the pending IRQ flag */ cpu->irq_pending = 0; } /* Generate a general exception that updates BadVaddr */ void mips64_gen_exception_badva(cpu_mips_t *cpu,u_int exc_code, m_uint64_t bad_vaddr) { mips_cp0_t *cp0 = &cpu->cp0; m_uint64_t cause; /* Update cause register (set BD and ExcCode) */ cause = cp0->reg[MIPS_CP0_CAUSE] & MIPS_CP0_CAUSE_IMASK; if (cpu->bd_slot) cause |= MIPS_CP0_CAUSE_BD_SLOT; else cause &= ~MIPS_CP0_CAUSE_BD_SLOT; cause |= (exc_code << MIPS_CP0_CAUSE_SHIFT); cp0->reg[MIPS_CP0_CAUSE] = cause; /* If EXL bit is 0, set EPC and BadVaddr registers */ if (likely(!(cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_EXL))) { cp0->reg[MIPS_CP0_EPC] = cpu->pc - (cpu->bd_slot << 2); cp0->reg[MIPS_CP0_BADVADDR] = bad_vaddr; } /* Set EXL bit in status register */ cp0->reg[MIPS_CP0_STATUS] |= MIPS_CP0_STATUS_EXL; /* Use bootstrap vectors ? */ if (cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_BEV) cpu->pc = 0xffffffffbfc00200ULL + 0x180; else cpu->pc = 0xffffffff80000000ULL + 0x180; /* Clear the pending IRQ flag */ cpu->irq_pending = 0; } /* Generate a TLB/XTLB miss exception */ void mips64_tlb_miss_exception(cpu_mips_t *cpu,u_int exc_code, m_uint64_t bad_vaddr) { mips_cp0_t *cp0 = &cpu->cp0; m_uint64_t cause,vector; /* Update cause register (set BD and ExcCode) */ cause = cp0->reg[MIPS_CP0_CAUSE] & MIPS_CP0_CAUSE_IMASK; if (cpu->bd_slot) cause |= MIPS_CP0_CAUSE_BD_SLOT; else cause &= ~MIPS_CP0_CAUSE_BD_SLOT; cause |= (exc_code << MIPS_CP0_CAUSE_SHIFT); cp0->reg[MIPS_CP0_CAUSE] = cause; /* If EXL bit is 0, set EPC and BadVaddr registers */ if (likely(!(cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_EXL))) { cp0->reg[MIPS_CP0_EPC] = cpu->pc - (cpu->bd_slot << 2); cp0->reg[MIPS_CP0_BADVADDR] = bad_vaddr; /* * determine if TLB or XTLB exception, based on the current * addressing mode. */ if (cpu->addr_mode == 64) { vector = 0x080; } else { vector = 0x000; } } else { /* nested: handled as a general exception */ vector = 0x180; } /* Set EXL bit in status register */ cp0->reg[MIPS_CP0_STATUS] |= MIPS_CP0_STATUS_EXL; /* Use bootstrap vectors ? */ if (cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_BEV) cpu->pc = 0xffffffffbfc00200ULL + vector; else cpu->pc = 0xffffffff80000000ULL + vector; /* Clear the pending IRQ flag */ cpu->irq_pending = 0; } /* Prepare a TLB exception */ void mips64_prepare_tlb_exception(cpu_mips_t *cpu,m_uint64_t vaddr) { m_uint64_t vpn2,mask; /* Update CP0 context and xcontext registers */ mips64_cp0_update_context_reg(cpu,vaddr); mips64_cp0_update_xcontext_reg(cpu,vaddr); /* EntryHi also contains the VPN address */ mask = mips64_cp0_get_vpn2_mask(cpu); vpn2 = vaddr & mask; cpu->cp0.reg[MIPS_CP0_TLB_HI] &= ~mask; cpu->cp0.reg[MIPS_CP0_TLB_HI] |= vpn2; } /* * Increment count register and trigger the timer IRQ if value in compare * register is the same. */ fastcall void mips64_exec_inc_cp0_cnt(cpu_mips_t *cpu) { cpu->cp0_virt_cnt_reg++; #if 0 /* TIMER_IRQ */ mips_cp0_t *cp0 = &cpu->cp0; if (unlikely((cpu->cp0_virt_cnt_reg == cpu->cp0_virt_cmp_reg))) { cp0->reg[MIPS_CP0_COUNT] = (m_uint32_t)cp0->reg[MIPS_CP0_COMPARE]; mips64_set_irq(cpu,7); mips64_update_irq_flag_fast(cpu); } #endif } /* Trigger the Timer IRQ */ fastcall void mips64_trigger_timer_irq(cpu_mips_t *cpu) { mips_cp0_t *cp0 = &cpu->cp0; cpu->timer_irq_count++; cp0->reg[MIPS_CP0_COUNT] = (m_uint32_t)cp0->reg[MIPS_CP0_COMPARE]; mips64_set_irq(cpu,7); mips64_update_irq_flag_fast(cpu); } /* Execute ERET instruction */ fastcall void mips64_exec_eret(cpu_mips_t *cpu) { mips_cp0_t *cp0 = &cpu->cp0; if (cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_ERL) { cp0->reg[MIPS_CP0_STATUS] &= ~MIPS_CP0_STATUS_ERL; cpu->pc = cp0->reg[MIPS_CP0_ERR_EPC]; } else { cp0->reg[MIPS_CP0_STATUS] &= ~MIPS_CP0_STATUS_EXL; cpu->pc = cp0->reg[MIPS_CP0_EPC]; } /* We have to clear the LLbit */ cpu->ll_bit = 0; /* Update the pending IRQ flag */ mips64_update_irq_flag_fast(cpu); } /* Execute SYSCALL instruction */ fastcall void mips64_exec_syscall(cpu_mips_t *cpu) { #if DEBUG_SYSCALL printf("MIPS64: SYSCALL at PC=0x%llx (RA=0x%llx)\n" " a0=0x%llx, a1=0x%llx, a2=0x%llx, a3=0x%llx\n", cpu->pc, cpu->gpr[MIPS_GPR_RA], cpu->gpr[MIPS_GPR_A0], cpu->gpr[MIPS_GPR_A1], cpu->gpr[MIPS_GPR_A2], cpu->gpr[MIPS_GPR_A3]); #endif mips64_general_exception(cpu,MIPS_CP0_CAUSE_SYSCALL); } /* Execute BREAK instruction */ fastcall void mips64_exec_break(cpu_mips_t *cpu,u_int code) { cpu_log(cpu->gen,"MIPS64","BREAK instruction (code=%u)\n",code); mips64_general_exception(cpu,MIPS_CP0_CAUSE_BP); } /* Trigger a Trap Exception */ fastcall void mips64_trigger_trap_exception(cpu_mips_t *cpu) { cpu_log(cpu->gen,"MIPS64","TRAP exception\n"); mips64_general_exception(cpu,MIPS_CP0_CAUSE_TRAP); } /* Trigger IRQs */ fastcall void mips64_trigger_irq(cpu_mips_t *cpu) { if (unlikely(cpu->irq_disable)) { cpu->irq_pending = 0; return; } cpu->irq_count++; if (mips64_update_irq_flag_fast(cpu)) mips64_general_exception(cpu,MIPS_CP0_CAUSE_INTERRUPT); else cpu->irq_fp_count++; } /* DMFC1 */ fastcall void mips64_exec_dmfc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg) { cpu->gpr[gp_reg] = cpu->fpu.reg[cp1_reg]; } /* DMTC1 */ fastcall void mips64_exec_dmtc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg) { cpu->fpu.reg[cp1_reg] = cpu->gpr[gp_reg]; } /* MFC1 */ fastcall void mips64_exec_mfc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg) { m_int64_t val; val = cpu->fpu.reg[cp1_reg] & 0xffffffff; cpu->gpr[gp_reg] = sign_extend(val,32); } /* MTC1 */ fastcall void mips64_exec_mtc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg) { cpu->fpu.reg[cp1_reg] = cpu->gpr[gp_reg] & 0xffffffff; } /* Virtual breakpoint */ fastcall void mips64_run_breakpoint(cpu_mips_t *cpu) { cpu_log(cpu->gen,"BREAKPOINT", "Virtual breakpoint reached at PC=0x%llx\n",cpu->pc); printf("[[[ Virtual Breakpoint reached at PC=0x%llx RA=0x%llx]]]\n", cpu->pc,cpu->gpr[MIPS_GPR_RA]); mips64_dump_regs(cpu->gen); memlog_dump(cpu->gen); } /* Add a virtual breakpoint */ int mips64_add_breakpoint(cpu_gen_t *cpu,m_uint64_t pc) { cpu_mips_t *mcpu = CPU_MIPS64(cpu); int i; for(i=0;ibreakpoints[i]) break; if (i == MIPS64_MAX_BREAKPOINTS) return(-1); mcpu->breakpoints[i] = pc; mcpu->breakpoints_enabled = TRUE; return(0); } /* Remove a virtual breakpoint */ void mips64_remove_breakpoint(cpu_gen_t *cpu,m_uint64_t pc) { cpu_mips_t *mcpu = CPU_MIPS64(cpu); int i,j; for(i=0;ibreakpoints[i] == pc) { for(j=i;jbreakpoints[j] = mcpu->breakpoints[j+1]; mcpu->breakpoints[MIPS64_MAX_BREAKPOINTS-1] = 0; } for(i=0;ibreakpoints[i] != 0) return; mcpu->breakpoints_enabled = FALSE; } /* Debugging for register-jump to address 0 */ fastcall void mips64_debug_jr0(cpu_mips_t *cpu) { printf("MIPS64: cpu %p jumping to address 0...\n",cpu); mips64_dump_regs(cpu->gen); } /* Set a register */ void mips64_reg_set(cpu_gen_t *cpu,u_int reg,m_uint64_t val) { if (reg < MIPS64_GPR_NR) CPU_MIPS64(cpu)->gpr[reg] = val; } /* Dump registers of a MIPS64 processor */ void mips64_dump_regs(cpu_gen_t *cpu) { cpu_mips_t *mcpu = CPU_MIPS64(cpu); mips_insn_t *ptr,insn; char buffer[80]; int i; printf("MIPS64 Registers:\n"); for(i=0;igpr[i*2], mips64_gpr_reg_names[(i*2)+1], (i*2)+1, mcpu->gpr[(i*2)+1]); } printf(" lo = 0x%16.16llx, hi = 0x%16.16llx\n", mcpu->lo, mcpu->hi); printf(" pc = 0x%16.16llx, ll_bit = %u\n", mcpu->pc, mcpu->ll_bit); /* Fetch the current instruction */ ptr = mcpu->mem_op_lookup(mcpu,mcpu->pc); if (ptr) { insn = vmtoh32(*ptr); if (mips64_dump_insn(buffer,sizeof(buffer),1,mcpu->pc,insn) != -1) printf(" Instruction: %s\n",buffer); } printf("\nCP0 Registers:\n"); for(i=0;iirq_count,mcpu->irq_fp_count,mcpu->irq_pending); printf(" Timer IRQ count: %llu, pending: %u, timer drift: %u\n\n", mcpu->timer_irq_count,mcpu->timer_irq_pending,mcpu->timer_drift); printf(" Device access count: %llu\n",cpu->dev_access_counter); printf("\n"); } /* Dump a memory block */ void mips64_dump_memory(cpu_mips_t *cpu,m_uint64_t vaddr,u_int count) { void *haddr; u_int i; for(i=0;imem_op_lookup(cpu,vaddr); if (haddr) printf("0x%8.8x ",htovm32(*(m_uint32_t *)haddr)); else printf("XXXXXXXXXX "); } printf("\n\n"); } /* Dump the stack */ void mips64_dump_stack(cpu_mips_t *cpu,u_int count) { printf("MIPS Stack Dump at 0x%16.16llx:",cpu->gpr[MIPS_GPR_SP]); mips64_dump_memory(cpu,cpu->gpr[MIPS_GPR_SP],count); } /* Save the CPU state into a file */ int mips64_save_state(cpu_mips_t *cpu,char *filename) { FILE *fd; int i; if (!(fd = fopen(filename,"w"))) { perror("mips64_save_state: fopen"); return(-1); } /* pc, lo and hi */ fprintf(fd,"pc: %16.16llx\n",cpu->pc); fprintf(fd,"lo: %16.16llx\n",cpu->lo); fprintf(fd,"hi: %16.16llx\n",cpu->hi); /* general purpose registers */ for(i=0;igpr[i]); printf("\n"); /* cp0 registers */ for(i=0;icp0.reg[i]); printf("\n"); /* cp1 registers */ for(i=0;ifpu.reg[i]); printf("\n"); /* tlb entries */ for(i=0;icp0.tlb_entries;i++) { fprintf(fd,"tlb%d_mask: %16.16llx\n",i,cpu->cp0.tlb[i].mask); fprintf(fd,"tlb%d_hi: %16.16llx\n",i,cpu->cp0.tlb[i].hi); fprintf(fd,"tlb%d_lo0: %16.16llx\n",i,cpu->cp0.tlb[i].lo0); fprintf(fd,"tlb%d_lo1: %16.16llx\n",i,cpu->cp0.tlb[i].lo1); } fclose(fd); return(0); } /* Read a 64-bit unsigned integer */ static m_uint64_t mips64_hex_u64(char *str,int *err) { m_uint64_t res = 0; u_char c; /* remove leading spaces */ while((*str == ' ') || (*str == '\t')) str++; while(*str) { c = *str; if ((c >= '0') && (c <= '9')) res = (res << 4) + (c - '0'); if ((c >= 'a') && (c <= 'f')) res = (res << 4) + ((c - 'a') + 10); if ((c >= 'A') && (c <= 'F')) res = (res << 4) + ((c - 'A') + 10); str++; } return(res); } /* Restore the CPU state from a file */ int mips64_restore_state(cpu_mips_t *cpu,char *filename) { char buffer[4096],*sep,*value,*ep,*field; size_t len; FILE *fd; int index; if (!(fd = fopen(filename,"r"))) { perror("mips64_restore_state: fopen"); return(-1); } while(!feof(fd)) { *buffer = 0; fgets(buffer,sizeof(buffer),fd); len = strlen(buffer); if (buffer[len-1] == '\n') buffer[len-1] = 0; sep = strchr(buffer,':'); if (!sep) continue; value = sep + 1; *sep = 0; /* gpr ? */ if ((index = mips64_get_reg_index(buffer)) != -1) { cpu->gpr[index] = mips64_hex_u64(value,NULL); continue; } /* cp0 register ? */ if ((index = mips64_cp0_get_reg_index(buffer)) != -1) { cpu->cp0.reg[index] = mips64_hex_u64(value,NULL); continue; } /* cp1 register ? */ if ((len > 3) && (!strncmp(buffer,"fpu",3))) { index = atoi(buffer+3); cpu->fpu.reg[index] = mips64_hex_u64(value,NULL); } /* tlb entry ? */ if ((len > 3) && (!strncmp(buffer,"tlb",3))) { ep = strchr(buffer,'_'); if (ep) { index = atoi(buffer+3); field = ep + 1; if (!strcmp(field,"mask")) { cpu->cp0.tlb[index].mask = mips64_hex_u64(value,NULL); continue; } if (!strcmp(field,"hi")) { cpu->cp0.tlb[index].hi = mips64_hex_u64(value,NULL); continue; } if (!strcmp(field,"lo0")) { cpu->cp0.tlb[index].lo0 = mips64_hex_u64(value,NULL); continue; } if (!strcmp(field,"lo1")) { cpu->cp0.tlb[index].lo1 = mips64_hex_u64(value,NULL); continue; } } } /* pc, lo, hi ? */ if (!strcmp(buffer,"pc")) { cpu->pc = mips64_hex_u64(value,NULL); continue; } if (!strcmp(buffer,"lo")) { cpu->lo = mips64_hex_u64(value,NULL); continue; } if (!strcmp(buffer,"hi")) { cpu->hi = mips64_hex_u64(value,NULL); continue; } } mips64_dump_regs(cpu->gen); mips64_tlb_dump(cpu->gen); fclose(fd); return(0); } /* Load a raw image into the simulated memory */ int mips64_load_raw_image(cpu_mips_t *cpu,char *filename,m_uint64_t vaddr) { struct stat file_info; size_t len,clen; m_uint32_t remain; void *haddr; FILE *bfd; if (!(bfd = fopen(filename,"r"))) { perror("fopen"); return(-1); } if (fstat(fileno(bfd),&file_info) == -1) { perror("stat"); return(-1); } len = file_info.st_size; printf("Loading RAW file '%s' at virtual address 0x%llx (size=%lu)\n", filename,vaddr,(u_long)len); while(len > 0) { haddr = cpu->mem_op_lookup(cpu,vaddr); if (!haddr) { fprintf(stderr,"load_raw_image: invalid load address 0x%llx\n", vaddr); return(-1); } if (len > MIPS_MIN_PAGE_SIZE) clen = MIPS_MIN_PAGE_SIZE; else clen = len; remain = MIPS_MIN_PAGE_SIZE; remain -= (vaddr - (vaddr & MIPS_MIN_PAGE_MASK)); clen = m_min(clen,remain); if (fread((u_char *)haddr,clen,1,bfd) != 1) break; vaddr += clen; len -= clen; } fclose(bfd); return(0); } /* Load an ELF image into the simulated memory */ int mips64_load_elf_image(cpu_mips_t *cpu,char *filename,int skip_load, m_uint32_t *entry_point) { m_uint64_t vaddr; m_uint32_t remain; void *haddr; Elf32_Ehdr *ehdr; Elf32_Shdr *shdr; Elf_Scn *scn; Elf *img_elf; size_t len,clen; _maybe_used char *name; int i,fd; FILE *bfd; if (!filename) return(-1); #ifdef __CYGWIN__ fd = open(filename,O_RDONLY|O_BINARY); #else fd = open(filename,O_RDONLY); #endif if (fd == -1) { perror("load_elf_image: open"); return(-1); } if (elf_version(EV_CURRENT) == EV_NONE) { fprintf(stderr,"load_elf_image: library out of date\n"); return(-1); } if (!(img_elf = elf_begin(fd,ELF_C_READ,NULL))) { fprintf(stderr,"load_elf_image: elf_begin: %s\n", elf_errmsg(elf_errno())); return(-1); } if (!(ehdr = elf32_getehdr(img_elf))) { fprintf(stderr,"load_elf_image: invalid ELF file\n"); return(-1); } printf("Loading ELF file '%s'...\n",filename); bfd = fdopen(fd,"rb"); if (!bfd) { perror("load_elf_image: fdopen"); return(-1); } if (!skip_load) { for(i=0;ie_shnum;i++) { scn = elf_getscn(img_elf,i); shdr = elf32_getshdr(scn); name = elf_strptr(img_elf, ehdr->e_shstrndx, (size_t)shdr->sh_name); len = shdr->sh_size; if (!(shdr->sh_flags & SHF_ALLOC) || !len) continue; fseek(bfd,shdr->sh_offset,SEEK_SET); vaddr = sign_extend(shdr->sh_addr,32); if (cpu->vm->debug_level > 0) { printf(" * Adding section at virtual address 0x%8.8llx " "(len=0x%8.8lx)\n",vaddr & 0xFFFFFFFF,(u_long)len); } while(len > 0) { haddr = cpu->mem_op_lookup(cpu,vaddr); if (!haddr) { fprintf(stderr,"load_elf_image: invalid load address 0x%llx\n", vaddr); return(-1); } if (len > MIPS_MIN_PAGE_SIZE) clen = MIPS_MIN_PAGE_SIZE; else clen = len; remain = PPC32_MIN_PAGE_SIZE; remain -= (vaddr - (vaddr & PPC32_MIN_PAGE_MASK)); clen = m_min(clen,remain); if (fread((u_char *)haddr,clen,1,bfd) < 1) break; vaddr += clen; len -= clen; } } } else { printf("ELF loading skipped, using a ghost RAM file.\n"); } printf("ELF entry point: 0x%x\n",ehdr->e_entry); if (entry_point) *entry_point = ehdr->e_entry; elf_end(img_elf); fclose(bfd); return(0); } /* Symbol lookup */ struct symbol *mips64_sym_lookup(cpu_mips_t *cpu,m_uint64_t addr) { return(rbtree_lookup(cpu->sym_tree,&addr)); } /* Insert a new symbol */ struct symbol *mips64_sym_insert(cpu_mips_t *cpu,char *name,m_uint64_t addr) { struct symbol *sym; size_t len; if (!cpu->sym_tree) return NULL; len = strlen(name); if (!(sym = malloc(len+1+sizeof(*sym)))) return NULL; memcpy(sym->name,name,len+1); sym->addr = addr; if (rbtree_insert(cpu->sym_tree,sym,sym) == -1) { free(sym); return NULL; } return sym; } /* Symbol comparison function */ static int mips64_sym_compare(m_uint64_t *a1,struct symbol *sym) { if (*a1 > sym->addr) return(1); if (*a1 < sym->addr) return(-1); return(0); } /* Create the symbol tree */ int mips64_sym_create_tree(cpu_mips_t *cpu) { cpu->sym_tree = rbtree_create((tree_fcompare)mips64_sym_compare,NULL); return(cpu->sym_tree ? 0 : -1); } /* Load a symbol file */ int mips64_sym_load_file(cpu_mips_t *cpu,char *filename) { char buffer[4096],func_name[128]; m_uint64_t addr; char sym_type; FILE *fd; if (!cpu->sym_tree && (mips64_sym_create_tree(cpu) == -1)) { fprintf(stderr,"CPU%u: Unable to create symbol tree.\n",cpu->gen->id); return(-1); } if (!(fd = fopen(filename,"r"))) { perror("load_sym_file: fopen"); return(-1); } while(!feof(fd)) { fgets(buffer,sizeof(buffer),fd); if (sscanf(buffer,"%llx %c %s",&addr,&sym_type,func_name) == 3) { mips64_sym_insert(cpu,func_name,addr); } } fclose(fd); return(0); } dynamips-0.2.14/unstable/mips64.h000066400000000000000000000475171241034141600165440ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __MIPS_64_H__ #define __MIPS_64_H__ #include #include "utils.h" #include "rbtree.h" /* * MIPS General Purpose Registers */ #define MIPS_GPR_ZERO 0 /* zero */ #define MIPS_GPR_AT 1 /* at */ #define MIPS_GPR_V0 2 /* v0 */ #define MIPS_GPR_V1 3 /* v1 */ #define MIPS_GPR_A0 4 /* a0 */ #define MIPS_GPR_A1 5 /* a1 */ #define MIPS_GPR_A2 6 /* a2 */ #define MIPS_GPR_A3 7 /* a3 */ #define MIPS_GPR_T0 8 /* t0 */ #define MIPS_GPR_T1 9 /* t1 */ #define MIPS_GPR_T2 10 /* t2 */ #define MIPS_GPR_T3 11 /* t3 */ #define MIPS_GPR_T4 12 /* t4 */ #define MIPS_GPR_T5 13 /* t5 */ #define MIPS_GPR_T6 14 /* t6 */ #define MIPS_GPR_T7 15 /* t7 */ #define MIPS_GPR_S0 16 /* s0 */ #define MIPS_GPR_S1 17 /* s1 */ #define MIPS_GPR_S2 18 /* s2 */ #define MIPS_GPR_S3 19 /* s3 */ #define MIPS_GPR_S4 20 /* s4 */ #define MIPS_GPR_S5 21 /* s5 */ #define MIPS_GPR_S6 22 /* s6 */ #define MIPS_GPR_S7 23 /* s7 */ #define MIPS_GPR_T8 24 /* t8 */ #define MIPS_GPR_T9 25 /* t9 */ #define MIPS_GPR_K0 26 /* k0 */ #define MIPS_GPR_K1 27 /* k1 */ #define MIPS_GPR_GP 28 /* gp */ #define MIPS_GPR_SP 29 /* sp */ #define MIPS_GPR_FP 30 /* fp */ #define MIPS_GPR_RA 31 /* ra */ /* * Coprocessor 0 (System Coprocessor) Register definitions */ #define MIPS_CP0_INDEX 0 /* TLB Index */ #define MIPS_CP0_RANDOM 1 /* TLB Random */ #define MIPS_CP0_TLB_LO_0 2 /* TLB Entry Lo0 */ #define MIPS_CP0_TLB_LO_1 3 /* TLB Entry Lo1 */ #define MIPS_CP0_CONTEXT 4 /* Kernel PTE pointer */ #define MIPS_CP0_PAGEMASK 5 /* TLB Page Mask */ #define MIPS_CP0_WIRED 6 /* TLB Wired */ #define MIPS_CP0_INFO 7 /* Info (RM7000) */ #define MIPS_CP0_BADVADDR 8 /* Bad Virtual Address */ #define MIPS_CP0_COUNT 9 /* Count */ #define MIPS_CP0_TLB_HI 10 /* TLB Entry Hi */ #define MIPS_CP0_COMPARE 11 /* Timer Compare */ #define MIPS_CP0_STATUS 12 /* Status */ #define MIPS_CP0_CAUSE 13 /* Cause */ #define MIPS_CP0_EPC 14 /* Exception PC */ #define MIPS_CP0_PRID 15 /* Proc Rev ID */ #define MIPS_CP0_CONFIG 16 /* Configuration */ #define MIPS_CP0_LLADDR 17 /* Load/Link address */ #define MIPS_CP0_WATCHLO 18 /* Low Watch address */ #define MIPS_CP0_WATCHHI 19 /* High Watch address */ #define MIPS_CP0_XCONTEXT 20 /* Extended context */ #define MIPS_CP0_ECC 26 /* ECC and parity */ #define MIPS_CP0_CACHERR 27 /* Cache Err/Status */ #define MIPS_CP0_TAGLO 28 /* Cache Tag Lo */ #define MIPS_CP0_TAGHI 29 /* Cache Tag Hi */ #define MIPS_CP0_ERR_EPC 30 /* Error exception PC */ /* * CP0 Set 1 Registers (R7000) */ #define MIPS_CP0_S1_CONFIG 16 /* Configuration Register */ #define MIPS_CP0_S1_IPLLO 18 /* Priority level for IRQ [7:0] */ #define MIPS_CP0_S1_IPLHI 19 /* Priority level for IRQ [15:8] */ #define MIPS_CP0_S1_INTCTL 20 /* Interrupt Control */ #define MIPS_CP0_S1_DERRADDR0 26 /* Imprecise Error Address */ #define MIPS_CP0_S1_DERRADDR1 27 /* Imprecise Error Address */ /* * CP0 Status Register */ #define MIPS_CP0_STATUS_CU0 0x10000000 #define MIPS_CP0_STATUS_CU1 0x20000000 #define MIPS_CP0_STATUS_BEV 0x00400000 #define MIPS_CP0_STATUS_TS 0x00200000 #define MIPS_CP0_STATUS_SR 0x00100000 #define MIPS_CP0_STATUS_CH 0x00040000 #define MIPS_CP0_STATUS_CE 0x00020000 #define MIPS_CP0_STATUS_DE 0x00010000 #define MIPS_CP0_STATUS_RP 0x08000000 #define MIPS_CP0_STATUS_FR 0x04000000 #define MIPS_CP0_STATUS_RE 0x02000000 #define MIPS_CP0_STATUS_KX 0x00000080 #define MIPS_CP0_STATUS_SX 0x00000040 #define MIPS_CP0_STATUS_UX 0x00000020 #define MIPS_CP0_STATUS_KSU 0x00000018 #define MIPS_CP0_STATUS_ERL 0x00000004 #define MIPS_CP0_STATUS_EXL 0x00000002 #define MIPS_CP0_STATUS_IE 0x00000001 #define MIPS_CP0_STATUS_IMASK7 0x00008000 #define MIPS_CP0_STATUS_IMASK6 0x00004000 #define MIPS_CP0_STATUS_IMASK5 0x00002000 #define MIPS_CP0_STATUS_IMASK4 0x00001000 #define MIPS_CP0_STATUS_IMASK3 0x00000800 #define MIPS_CP0_STATUS_IMASK2 0x00000400 #define MIPS_CP0_STATUS_IMASK1 0x00000200 #define MIPS_CP0_STATUS_IMASK0 0x00000100 #define MIPS_CP0_STATUS_DS_MASK 0x00770000 #define MIPS_CP0_STATUS_CU_MASK 0xF0000000 #define MIPS_CP0_STATUS_IMASK 0x0000FF00 /* Addressing mode: Kernel, Supervisor and User */ #define MIPS_CP0_STATUS_KSU_SHIFT 0x03 #define MIPS_CP0_STATUS_KSU_MASK 0x03 #define MIPS_CP0_STATUS_KM 0x00 #define MIPS_CP0_STATUS_SM 0x01 #define MIPS_CP0_STATUS_UM 0x10 /* * CP0 Cause register */ #define MIPS_CP0_CAUSE_BD_SLOT 0x80000000 #define MIPS_CP0_CAUSE_MASK 0x0000007C #define MIPS_CP0_CAUSE_CEMASK 0x30000000 #define MIPS_CP0_CAUSE_IMASK 0x0000FF00 #define MIPS_CP0_CAUSE_SHIFT 2 #define MIPS_CP0_CAUSE_CESHIFT 28 #define MIPS_CP0_CAUSE_ISHIFT 8 #define MIPS_CP0_CAUSE_INTERRUPT 0 #define MIPS_CP0_CAUSE_TLB_MOD 1 #define MIPS_CP0_CAUSE_TLB_LOAD 2 #define MIPS_CP0_CAUSE_TLB_SAVE 3 #define MIPS_CP0_CAUSE_ADDR_LOAD 4 /* ADEL */ #define MIPS_CP0_CAUSE_ADDR_SAVE 5 /* ADES */ #define MIPS_CP0_CAUSE_BUS_INSTR 6 #define MIPS_CP0_CAUSE_BUS_DATA 7 #define MIPS_CP0_CAUSE_SYSCALL 8 #define MIPS_CP0_CAUSE_BP 9 #define MIPS_CP0_CAUSE_ILLOP 10 #define MIPS_CP0_CAUSE_CP_UNUSABLE 11 #define MIPS_CP0_CAUSE_OVFLW 12 #define MIPS_CP0_CAUSE_TRAP 13 #define MIPS_CP0_CAUSE_VC_INSTR 14 /* Virtual Coherency */ #define MIPS_CP0_CAUSE_FPE 15 #define MIPS_CP0_CAUSE_WATCH 23 #define MIPS_CP0_CAUSE_VC_DATA 31 /* Virtual Coherency */ #define MIPS_CP0_CAUSE_IBIT7 0x00008000 #define MIPS_CP0_CAUSE_IBIT6 0x00004000 #define MIPS_CP0_CAUSE_IBIT5 0x00002000 #define MIPS_CP0_CAUSE_IBIT4 0x00001000 #define MIPS_CP0_CAUSE_IBIT3 0x00000800 #define MIPS_CP0_CAUSE_IBIT2 0x00000400 #define MIPS_CP0_CAUSE_IBIT1 0x00000200 #define MIPS_CP0_CAUSE_IBIT0 0x00000100 /* CP0 Context register */ #define MIPS_CP0_CONTEXT_VPN2_MASK 0xffffe000ULL /* applied to addr */ #define MIPS_CP0_CONTEXT_BADVPN2_MASK 0x7fffffULL #define MIPS_CP0_CONTEXT_BADVPN2_SHIFT 4 /* CP0 XContext register */ #define MIPS_CP0_XCONTEXT_VPN2_MASK 0xffffffe000ULL #define MIPS_CP0_XCONTEXT_RBADVPN2_MASK 0x1ffffffffULL #define MIPS_CP0_XCONTEXT_BADVPN2_SHIFT 4 #define MIPS_CP0_XCONTEXT_R_SHIFT 31 /* TLB masks and shifts */ #define MIPS_TLB_PAGE_MASK 0x01ffe000ULL #define MIPS_TLB_PAGE_SHIFT 13 #define MIPS_TLB_VPN2_MASK_32 0xffffe000ULL #define MIPS_TLB_VPN2_MASK_64 0xc00000ffffffe000ULL #define MIPS_TLB_PFN_MASK 0x3fffffc0ULL #define MIPS_TLB_ASID_MASK 0x000000ff /* "asid" in EntryHi */ #define MIPS_TLB_G_MASK 0x00001000ULL /* "Global" in EntryHi */ #define MIPS_TLB_V_MASK 0x2ULL /* "Valid" in EntryLo */ #define MIPS_TLB_D_MASK 0x4ULL /* "Dirty" in EntryLo */ #define MIPS_TLB_C_MASK 0x38ULL /* Page Coherency Attribute */ #define MIPS_TLB_C_SHIFT 3 #define MIPS_CP0_LO_G_MASK 0x00000001ULL /* "Global" in Lo0/1 reg */ #define MIPS_CP0_LO_SAFE_MASK 0x3fffffffULL /* Safety mask for Lo reg */ #define MIPS_CP0_HI_SAFE_MASK 0xc00000ffffffe0ffULL /* Same for EntryHi */ /* results for TLB lookups */ enum { MIPS_TLB_LOOKUP_OK = 0, /* Entry found */ MIPS_TLB_LOOKUP_INVALID, /* Invalid entry found */ MIPS_TLB_LOOKUP_MISS, /* No matching entry found */ MIPS_TLB_LOOKUP_MOD, /* Read-only */ }; /* Exceptions vectors */ enum { MIPS_EXCVECT_RST = 0, /* Soft Reset, Reset, NMI */ MIPS_EXCVECT_TLB_REFILL, /* TLB Refill (32-bit) */ MIPS_EXCVECT_XTLB_REFILL, /* TLB Refill (64-bit) */ MIPS_EXCVECT_CACHE_ERR, /* Cache Error */ MIPS_EXCVECT_INT_IV0, /* Interrupt, IV=0 */ MIPS_EXCVECT_INT_IV1, /* Interrupt, IV=1 */ MIPS_EXCVECT_OTHERS, /* Other exceptions */ }; /* MIPS "jr ra" instruction */ #define MIPS_INSN_JR_RA 0x03e00008 /* Minimum page size: 4 Kb */ #define MIPS_MIN_PAGE_SHIFT 12 #define MIPS_MIN_PAGE_SIZE (1 << MIPS_MIN_PAGE_SHIFT) #define MIPS_MIN_PAGE_IMASK (MIPS_MIN_PAGE_SIZE - 1) #define MIPS_MIN_PAGE_MASK 0xfffffffffffff000ULL /* Addressing mode: Kernel, Supervisor and User */ #define MIPS_MODE_KERNEL 00 /* Segments in 32-bit User mode */ #define MIPS_USEG_BASE 0x00000000 #define MIPS_USEG_SIZE 0x80000000 /* Segments in 32-bit Supervisor mode */ #define MIPS_SUSEG_BASE 0x00000000 #define MIPS_SUSEG_SIZE 0x80000000 #define MIPS_SSEG_BASE 0xc0000000 #define MIPS_SSEG_SIZE 0x20000000 /* Segments in 32-bit Kernel mode */ #define MIPS_KUSEG_BASE 0x00000000 #define MIPS_KUSEG_SIZE 0x80000000 #define MIPS_KSEG0_BASE 0x80000000 #define MIPS_KSEG0_SIZE 0x20000000 #define MIPS_KSEG1_BASE 0xa0000000 #define MIPS_KSEG1_SIZE 0x20000000 #define MIPS_KSSEG_BASE 0xc0000000 #define MIPS_KSSEG_SIZE 0x20000000 #define MIPS_KSEG3_BASE 0xe0000000 #define MIPS_KSEG3_SIZE 0x20000000 /* xkphys mask (36-bit physical address) */ #define MIPS64_XKPHYS_ZONE_MASK 0xF800000000000000ULL #define MIPS64_XKPHYS_PHYS_SIZE (1ULL << 36) #define MIPS64_XKPHYS_PHYS_MASK (MIPS64_XKPHYS_PHYS_SIZE - 1) #define MIPS64_XKPHYS_CCA_SHIFT 59 /* Initial Program Counter and Stack pointer for ROM */ #define MIPS_ROM_PC 0xffffffffbfc00000ULL #define MIPS_ROM_SP 0xffffffff80004000ULL /* Number of GPR (general purpose registers) */ #define MIPS64_GPR_NR 32 /* Number of registers in CP0 */ #define MIPS64_CP0_REG_NR 32 /* Number of registers in CP1 */ #define MIPS64_CP1_REG_NR 32 /* Number of TLB entries */ #define MIPS64_TLB_STD_ENTRIES 48 #define MIPS64_TLB_MAX_ENTRIES 64 #define MIPS64_TLB_IDX_MASK 0x3f /* 6 bits */ /* Enable the 64 TLB entries for R7000 CPU */ #define MIPS64_R7000_TLB64_ENABLE 0x20000000 /* Number of instructions per page */ #define MIPS_INSN_PER_PAGE (MIPS_MIN_PAGE_SIZE/sizeof(mips_insn_t)) /* MIPS CPU Identifiers */ #define MIPS_PRID_R4600 0x00002012 #define MIPS_PRID_R4700 0x00002112 #define MIPS_PRID_R5000 0x00002312 #define MIPS_PRID_R7000 0x00002721 #define MIPS_PRID_R527x 0x00002812 #define MIPS_PRID_BCM1250 0x00040102 /* Memory operations */ enum { MIPS_MEMOP_LOOKUP = 0, MIPS_MEMOP_IFETCH, MIPS_MEMOP_LB, MIPS_MEMOP_LBU, MIPS_MEMOP_LH, MIPS_MEMOP_LHU, MIPS_MEMOP_LW, MIPS_MEMOP_LWU, MIPS_MEMOP_LD, MIPS_MEMOP_SB, MIPS_MEMOP_SH, MIPS_MEMOP_SW, MIPS_MEMOP_SD, MIPS_MEMOP_LWL, MIPS_MEMOP_LWR, MIPS_MEMOP_LDL, MIPS_MEMOP_LDR, MIPS_MEMOP_SWL, MIPS_MEMOP_SWR, MIPS_MEMOP_SDL, MIPS_MEMOP_SDR, MIPS_MEMOP_LL, MIPS_MEMOP_SC, MIPS_MEMOP_LDC1, MIPS_MEMOP_SDC1, MIPS_MEMOP_CACHE, MIPS_MEMOP_MAX, }; /* Maximum number of breakpoints */ #define MIPS64_MAX_BREAKPOINTS 8 /* MIPS CPU type */ typedef struct cpu_mips cpu_mips_t; /* Memory operation function prototype */ typedef fastcall void (*mips_memop_fn)(cpu_mips_t *cpu,m_uint64_t vaddr, u_int reg); /* TLB entry definition */ typedef struct { m_uint64_t mask; m_uint64_t hi; m_uint64_t lo0; m_uint64_t lo1; }tlb_entry_t; /* System Coprocessor (CP0) definition */ typedef struct { m_uint64_t reg[MIPS64_CP0_REG_NR]; tlb_entry_t tlb[MIPS64_TLB_MAX_ENTRIES]; /* Number of TLB entries */ u_int tlb_entries; /* Extensions for R7000 CP0 Set1 */ m_uint32_t ipl_lo,ipl_hi,int_ctl; m_uint32_t derraddr0,derraddr1; }mips_cp0_t; /* FPU Coprocessor (CP1) definition */ typedef struct { m_uint64_t reg[MIPS64_CP1_REG_NR]; }mips_cp1_t; /* MIPS CPU definition */ struct cpu_mips { /* MTS32/MTS64 caches */ union { mts32_entry_t *mts32_cache; mts64_entry_t *mts64_cache; }mts_u; /* Virtual version of CP0 Compare Register */ m_uint32_t cp0_virt_cnt_reg,cp0_virt_cmp_reg; /* General Purpose Registers, Pointer Counter, LO/HI, IRQ */ m_uint32_t irq_pending,irq_cause,ll_bit; m_uint64_t pc,gpr[MIPS64_GPR_NR]; m_uint64_t lo,hi,ret_pc; m_uint32_t exec_state; u_int bd_slot; /* Virtual address to physical page translation */ fastcall int (*translate)(cpu_mips_t *cpu,m_uint64_t vaddr, m_uint32_t *phys_page); /* Memory access functions */ mips_memop_fn mem_op_fn[MIPS_MEMOP_MAX]; /* Memory lookup function (to load ELF image,...) and instruction fetch */ void *(*mem_op_lookup)(cpu_mips_t *cpu,m_uint64_t vaddr); void *(*mem_op_ifetch)(cpu_mips_t *cpu,m_uint64_t vaddr); /* System coprocessor (CP0) */ mips_cp0_t cp0; /* FPU (CP1) */ mips_cp1_t fpu; /* Address bus mask for physical addresses */ m_uint64_t addr_bus_mask; /* IRQ counters and cause */ m_uint64_t irq_count,timer_irq_count,irq_fp_count; pthread_mutex_t irq_lock; /* Current and free lists of translated code blocks */ mips64_jit_tcb_t *tcb_list,*tcb_last,*tcb_free_list; /* Executable page area */ void *exec_page_area; size_t exec_page_area_size; size_t exec_page_count,exec_page_alloc; insn_exec_page_t *exec_page_free_list; insn_exec_page_t *exec_page_array; /* Idle PC value */ volatile m_uint64_t idle_pc; /* Timer IRQs */ volatile u_int timer_irq_pending; u_int timer_irq_freq; u_int timer_irq_check_itv; u_int timer_drift; /* IRQ disable flag */ volatile u_int irq_disable; /* IRQ idling preemption */ u_int irq_idle_preempt[8]; /* Generic CPU instance pointer */ cpu_gen_t *gen; /* VM instance */ vm_instance_t *vm; /* non-JIT mode instruction counter */ m_uint64_t insn_exec_count; /* MTS invalidate/shutdown operations */ void (*mts_invalidate)(cpu_mips_t *cpu); void (*mts_shutdown)(cpu_mips_t *cpu); /* MTS cache statistics */ m_uint64_t mts_misses,mts_lookups; /* JIT flush method */ u_int jit_flush_method; /* Number of compiled pages */ u_int compiled_pages; /* Fast memory operations use */ u_int fast_memop; /* Direct block jump */ u_int exec_blk_direct_jump; /* Address mode (32 or 64 bits) */ u_int addr_mode; /* Current exec page (non-JIT) info */ m_uint64_t njm_exec_page; mips_insn_t *njm_exec_ptr; /* Performance counter (number of instructions executed by CPU) */ m_uint32_t perf_counter; /* Breakpoints */ m_uint64_t breakpoints[MIPS64_MAX_BREAKPOINTS]; u_int breakpoints_enabled; /* Symtrace */ int sym_trace; rbtree_tree *sym_tree; /* XXX */ cpu_tb_t *current_tb; }; #define MIPS64_IRQ_LOCK(cpu) pthread_mutex_lock(&(cpu)->irq_lock) #define MIPS64_IRQ_UNLOCK(cpu) pthread_mutex_unlock(&(cpu)->irq_lock) /* Register names */ extern char *mips64_gpr_reg_names[]; /* Get cacheability info */ int mips64_cca_cached(m_uint8_t val); /* Reset a MIPS64 CPU */ int mips64_reset(cpu_mips_t *cpu); /* Initialize a MIPS64 processor */ int mips64_init(cpu_mips_t *cpu); /* Delete a MIPS64 processor */ void mips64_delete(cpu_mips_t *cpu); /* Set the CPU PRID register */ void mips64_set_prid(cpu_mips_t *cpu,m_uint32_t prid); /* Set idle PC value */ void mips64_set_idle_pc(cpu_gen_t *cpu,m_uint64_t addr); /* Timer IRQ */ void *mips64_timer_irq_run(cpu_mips_t *cpu); /* Determine an "idling" PC */ int mips64_get_idling_pc(cpu_gen_t *cpu); /* Set an IRQ (VM IRQ standard routing) */ void mips64_vm_set_irq(vm_instance_t *vm,u_int irq); /* Clear an IRQ (VM IRQ standard routing) */ void mips64_vm_clear_irq(vm_instance_t *vm,u_int irq); /* Update the IRQ flag */ void mips64_update_irq_flag(cpu_mips_t *cpu); /* Generate a general exception */ void mips64_general_exception(cpu_mips_t *cpu,u_int exc_code); /* Generate a general exception that updates BadVaddr */ void mips64_gen_exception_badva(cpu_mips_t *cpu,u_int exc_code, m_uint64_t bad_vaddr); /* Generate a TLB/XTLB exception */ void mips64_tlb_miss_exception(cpu_mips_t *cpu,u_int exc_code, m_uint64_t bad_vaddr); /* Prepare a TLB exception */ void mips64_prepare_tlb_exception(cpu_mips_t *cpu,m_uint64_t vaddr); /* * Increment count register and trigger the timer IRQ if value in compare * register is the same. */ fastcall void mips64_exec_inc_cp0_cnt(cpu_mips_t *cpu); /* Trigger the Timer IRQ */ fastcall void mips64_trigger_timer_irq(cpu_mips_t *cpu); /* Execute ERET instruction */ fastcall void mips64_exec_eret(cpu_mips_t *cpu); /* Execute SYSCALL instruction */ fastcall void mips64_exec_syscall(cpu_mips_t *cpu); /* Execute BREAK instruction */ fastcall void mips64_exec_break(cpu_mips_t *cpu,u_int code); /* Trigger a Trap Exception */ fastcall void mips64_trigger_trap_exception(cpu_mips_t *cpu); /* Trigger IRQs */ fastcall void mips64_trigger_irq(cpu_mips_t *cpu); /* Set an IRQ */ void mips64_set_irq(cpu_mips_t *cpu,m_uint8_t irq); /* Clear an IRQ */ void mips64_clear_irq(cpu_mips_t *cpu,m_uint8_t irq); /* DMFC1 */ fastcall void mips64_exec_dmfc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg); /* DMTC1 */ fastcall void mips64_exec_dmtc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg); /* MFC1 */ fastcall void mips64_exec_mfc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg); /* MTC1 */ fastcall void mips64_exec_mtc1(cpu_mips_t *cpu,u_int gp_reg,u_int cp1_reg); /* Virtual breakpoint */ fastcall void mips64_run_breakpoint(cpu_mips_t *cpu); /* Add a virtual breakpoint */ int mips64_add_breakpoint(cpu_gen_t *cpu,m_uint64_t pc); /* Remove a virtual breakpoint */ void mips64_remove_breakpoint(cpu_gen_t *cpu,m_uint64_t pc); /* Debugging for register-jump to address 0 */ fastcall void mips64_debug_jr0(cpu_mips_t *cpu); /* Set a register */ void mips64_reg_set(cpu_gen_t *cpu,u_int reg,m_uint64_t val); /* Dump registers of a MIPS64 processor */ void mips64_dump_regs(cpu_gen_t *cpu); /* Dump a memory block */ void mips64_dump_memory(cpu_mips_t *cpu,m_uint64_t vaddr,u_int count); /* Dump the stack */ void mips64_dump_stack(cpu_mips_t *cpu,u_int count); /* Save the CPU state into a file */ int mips64_save_state(cpu_mips_t *cpu,char *filename); /* Load a raw image into the simulated memory */ int mips64_load_raw_image(cpu_mips_t *cpu,char *filename,m_uint64_t vaddr); /* Load an ELF image into the simulated memory */ int mips64_load_elf_image(cpu_mips_t *cpu,char *filename,int skip_load, m_uint32_t *entry_point); /* Symbol lookup */ struct symbol *mips64_sym_lookup(cpu_mips_t *cpu,m_uint64_t addr); /* Insert a new symbol */ struct symbol *mips64_sym_insert(cpu_mips_t *cpu,char *name,m_uint64_t addr); /* Create the symbol tree */ int mips64_sym_create_tree(cpu_mips_t *cpu); /* Load a symbol file */ int mips64_sym_load_file(cpu_mips_t *cpu,char *filename); #endif dynamips-0.2.14/unstable/mips64_amd64_trans.c000066400000000000000000002352201241034141600207270ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "tcb.h" #include "mips64_jit.h" #include "mips64_amd64_trans.h" #include "mips64_cp0.h" #include "memory.h" /* Macros for CPU structure access */ #define REG_OFFSET(reg) (OFFSET(cpu_mips_t,gpr[(reg)])) #define CP0_REG_OFFSET(c0reg) (OFFSET(cpu_mips_t,cp0.reg[(c0reg)])) #define MEMOP_OFFSET(op) (OFFSET(cpu_mips_t,mem_op_fn[(op)])) #define DECLARE_INSN(name) \ static int mips64_emit_##name(cpu_mips_t *cpu,cpu_tc_t *b,\ mips_insn_t insn) /* Set an IRQ */ void mips64_set_irq(cpu_mips_t *cpu,m_uint8_t irq) { m_uint32_t m; m = (1 << (irq + MIPS_CP0_CAUSE_ISHIFT)) & MIPS_CP0_CAUSE_IMASK; atomic_or(&cpu->irq_cause,m); } /* Clear an IRQ */ void mips64_clear_irq(cpu_mips_t *cpu,m_uint8_t irq) { m_uint32_t m; m = (1 << (irq + MIPS_CP0_CAUSE_ISHIFT)) & MIPS_CP0_CAUSE_IMASK; atomic_and(&cpu->irq_cause,~m); if (!cpu->irq_cause) cpu->irq_pending = 0; } /* Load a 64 bit immediate value */ static inline void mips64_load_imm(cpu_tc_t *b,u_int reg,m_uint64_t value) { if (value > 0xffffffffULL) amd64_mov_reg_imm_size(b->jit_ptr,reg,value,8); else amd64_mov_reg_imm(b->jit_ptr,reg,value); } /* Set the Pointer Counter (PC) register */ void mips64_set_pc(cpu_tc_t *b,m_uint64_t new_pc) { mips64_load_imm(b,AMD64_RAX,new_pc); amd64_mov_membase_reg(b->jit_ptr, AMD64_R15,OFFSET(cpu_mips_t,pc), AMD64_RAX,8); } /* Set the Return Address (RA) register */ void mips64_set_ra(cpu_tc_t *b,m_uint64_t ret_pc) { mips64_load_imm(b,AMD64_RAX,ret_pc); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15, REG_OFFSET(MIPS_GPR_RA), AMD64_RAX,8); } /* * Try to branch directly to the specified JIT block without returning to * the main loop. */ static void mips64_try_direct_far_jump(cpu_mips_t *cpu,cpu_tc_t *b, m_uint64_t new_pc) { m_uint64_t new_page; m_uint32_t pc_hash,pc_offset; u_char *test1,*test2,*test3,*test4; new_page = new_pc & MIPS_MIN_PAGE_MASK; pc_offset = (new_pc & MIPS_MIN_PAGE_IMASK) >> 2; pc_hash = mips64_jit_get_virt_hash(new_pc); /* Get generic CPU pointer */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RSI, AMD64_R15,OFFSET(cpu_mips_t,gen),8); /* Get JIT block info in %rdx */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RBX, AMD64_RSI,OFFSET(cpu_gen_t,tb_virt_hash),8); amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX, AMD64_RBX,pc_hash*sizeof(void *),8); /* no JIT block found ? */ amd64_test_reg_reg(b->jit_ptr,AMD64_RDX,AMD64_RDX); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_Z, 0, 1); /* Check block virtual address */ mips64_load_imm(b,AMD64_RAX,new_page); amd64_alu_reg_membase_size(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RDX, OFFSET(cpu_tb_t,vaddr),8); test2 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Get pointer to the Translated Code block */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RBX, AMD64_RDX,OFFSET(cpu_tb_t,tc),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RBX,AMD64_RBX); test3 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_Z, 0, 1); /* Jump to the code */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RSI, AMD64_RBX,OFFSET(cpu_tc_t,jit_insn_ptr),8); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX, AMD64_RSI,pc_offset * sizeof(void *),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test4 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_Z, 0, 1); amd64_jump_reg(b->jit_ptr,AMD64_RAX); /* Returns to caller... */ amd64_patch(test1,b->jit_ptr); amd64_patch(test2,b->jit_ptr); amd64_patch(test3,b->jit_ptr); amd64_patch(test4,b->jit_ptr); mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); } /* Set Jump */ static void mips64_set_jump(cpu_mips_t *cpu,cpu_tc_t *b, m_uint64_t new_pc,int local_jump) { int return_to_caller = FALSE; u_char *jump_ptr; if (cpu->sym_trace && !local_jump) return_to_caller = TRUE; if (!return_to_caller && mips64_jit_tcb_local_addr(b,new_pc,&jump_ptr)) { if (jump_ptr) { amd64_jump_code(b->jit_ptr,jump_ptr); } else { /* Never jump directly to code in a delay slot */ if (mips64_jit_is_delay_slot(b,new_pc)) { mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); return; } mips64_jit_tcb_record_patch(cpu,b,b->jit_ptr,new_pc); amd64_jump32(b->jit_ptr,0); } } else { if (1 /*cpu->exec_blk_direct_jump*/) { /* zzz */ /* Block lookup optimization */ mips64_try_direct_far_jump(cpu,b,new_pc); } else { mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); } } } /* Basic C call */ static forced_inline void mips64_emit_basic_c_call(cpu_tc_t *b,void *f) { amd64_mov_reg_imm(b->jit_ptr,AMD64_RCX,f); amd64_call_reg(b->jit_ptr,AMD64_RCX); } /* Emit a simple call to a C function without any parameter */ static void mips64_emit_c_call(cpu_tc_t *b,void *f) { mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); amd64_mov_reg_imm(b->jit_ptr,AMD64_RCX,f); amd64_call_reg(b->jit_ptr,AMD64_RCX); } /* Single-step operation */ void mips64_emit_single_step(cpu_tc_t *b,mips_insn_t insn) { amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_mov_reg_imm(b->jit_ptr,AMD64_RSI,insn); mips64_emit_basic_c_call(b,mips64_exec_single_step); } /* Fast memory operation prototype */ typedef void (*memop_fast_access)(cpu_tc_t *b,int target); /* Fast LW */ static void mips64_memop_fast_lw(cpu_tc_t *b,int target) { amd64_mov_reg_memindex(b->jit_ptr,AMD64_RAX,AMD64_RBX,0,AMD64_RSI,0,4); amd64_bswap32(b->jit_ptr,X86_EAX); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RDX,X86_EAX); /* Save value in register */ amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(target),AMD64_RDX,8); } /* Fast SW */ static void mips64_memop_fast_sw(cpu_tc_t *b,int target) { /* Load value from register */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(target),4); amd64_bswap32(b->jit_ptr,X86_EAX); amd64_mov_memindex_reg(b->jit_ptr,AMD64_RBX,0,AMD64_RSI,0,AMD64_RAX,4); } /* Fast memory operation (64-bit) */ static void mips64_emit_memop_fast64(cpu_tc_t *b,int write_op, int opcode,int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { m_uint64_t val = sign_extend(offset,16); u_char *test1,*test2,*p_exit; test2 = NULL; /* XXX */ amd64_inc_membase(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,mts_lookups)); /* RSI = GPR[base] + sign-extended offset */ mips64_load_imm(b,AMD64_RSI,val); amd64_alu_reg_membase(b->jit_ptr,X86_ADD, AMD64_RSI,AMD64_R15,REG_OFFSET(base)); /* RBX = mts64_entry index */ amd64_mov_reg_reg_size(b->jit_ptr,X86_EBX,X86_ESI,4); amd64_mov_reg_reg_size(b->jit_ptr,X86_EAX,X86_ESI,4); amd64_shift_reg_imm_size(b->jit_ptr,X86_SHR,X86_EBX,MTS64_HASH_SHIFT1,4); amd64_shift_reg_imm_size(b->jit_ptr,X86_SHR,X86_EAX,MTS64_HASH_SHIFT2,4); amd64_alu_reg_reg(b->jit_ptr,X86_XOR,AMD64_RBX,AMD64_RAX); amd64_alu_reg_imm_size(b->jit_ptr,X86_AND,X86_EBX,MTS64_HASH_MASK,8); /* RCX = mts64 entry */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX, AMD64_R15, OFFSET(cpu_mips_t,mts_u.mts64_cache),8); amd64_shift_reg_imm(b->jit_ptr,X86_SHL,AMD64_RBX,5); /* TO FIX */ amd64_alu_reg_reg(b->jit_ptr,X86_ADD,AMD64_RCX,AMD64_RBX); /* Compare virtual page address (EAX = vpage) */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RSI,8); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RAX,MIPS_MIN_PAGE_MASK); amd64_alu_reg_membase_size(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RCX, OFFSET(mts64_entry_t,gvpa),8); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); /* Test if we are writing to a COW page */ if (write_op) { amd64_test_membase_imm_size(b->jit_ptr, AMD64_RCX,OFFSET(mts64_entry_t,flags), MTS_FLAG_WRCATCH,4); test2 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); } /* ESI = offset in page, RBX = Host Page Address */ amd64_alu_reg_imm(b->jit_ptr,X86_AND,X86_ESI,MIPS_MIN_PAGE_IMASK); amd64_mov_reg_membase(b->jit_ptr,AMD64_RBX, AMD64_RCX,OFFSET(mts64_entry_t,hpa),8); /* Memory access */ op_handler(b,target); p_exit = b->jit_ptr; amd64_jump8(b->jit_ptr,0); if (test2) amd64_patch(test2,b->jit_ptr); /* === Slow lookup === */ amd64_patch(test1,b->jit_ptr); /* Save PC for exception handling */ mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); /* Sign-extend virtual address */ //amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RSI,X86_ESI); /* RDX = target register */ amd64_mov_reg_imm(b->jit_ptr,AMD64_RDX,target); /* RDI = CPU instance */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); /* Call memory access function */ amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); amd64_call_membase(b->jit_ptr,AMD64_R15,MEMOP_OFFSET(opcode)); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); amd64_patch(p_exit,b->jit_ptr); } /* Fast memory operation (32-bit) */ static void mips64_emit_memop_fast32(cpu_tc_t *b,int write_op, int opcode,int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { m_uint32_t val = sign_extend(offset,16); u_char *test1,*test2,*p_exit; test2 = NULL; /* XXX */ amd64_inc_membase(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,mts_lookups)); /* ESI = GPR[base] + sign-extended offset */ amd64_mov_reg_imm(b->jit_ptr,X86_ESI,val); amd64_alu_reg_membase_size(b->jit_ptr,X86_ADD, X86_ESI,AMD64_R15,REG_OFFSET(base),4); /* RBX = mts32_entry index */ amd64_mov_reg_reg_size(b->jit_ptr,X86_EBX,X86_ESI,4); amd64_mov_reg_reg_size(b->jit_ptr,X86_EAX,X86_ESI,4); amd64_shift_reg_imm_size(b->jit_ptr,X86_SHR,X86_EBX,MTS32_HASH_SHIFT1,4); amd64_shift_reg_imm_size(b->jit_ptr,X86_SHR,X86_EAX,MTS32_HASH_SHIFT2,4); amd64_alu_reg_reg(b->jit_ptr,X86_XOR,AMD64_RBX,AMD64_RAX); amd64_alu_reg_imm_size(b->jit_ptr,X86_AND,X86_EBX,MTS32_HASH_MASK,4); /* RCX = mts32 entry */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX, AMD64_R15, OFFSET(cpu_mips_t,mts_u.mts32_cache),8); amd64_shift_reg_imm(b->jit_ptr,X86_SHL,AMD64_RBX,5); /* TO FIX */ amd64_alu_reg_reg(b->jit_ptr,X86_ADD,AMD64_RCX,AMD64_RBX); /* Compare virtual page address (EAX = vpage) */ amd64_mov_reg_reg(b->jit_ptr,X86_EAX,X86_ESI,4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,X86_EAX,MIPS_MIN_PAGE_MASK); amd64_alu_reg_membase_size(b->jit_ptr,X86_CMP,X86_EAX,AMD64_RCX, OFFSET(mts32_entry_t,gvpa),4); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); /* Test if we are writing to a COW page */ if (write_op) { amd64_test_membase_imm_size(b->jit_ptr, AMD64_RCX,OFFSET(mts32_entry_t,flags), MTS_FLAG_WRCATCH,4); test2 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); } /* ESI = offset in page, RBX = Host Page Address */ amd64_alu_reg_imm(b->jit_ptr,X86_AND,X86_ESI,MIPS_MIN_PAGE_IMASK); amd64_mov_reg_membase(b->jit_ptr,AMD64_RBX, AMD64_RCX,OFFSET(mts32_entry_t,hpa),8); /* Memory access */ op_handler(b,target); p_exit = b->jit_ptr; amd64_jump8(b->jit_ptr,0); /* === Slow lookup === */ amd64_patch(test1,b->jit_ptr); if (test2) amd64_patch(test2,b->jit_ptr); /* Save PC for exception handling */ mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); /* Sign-extend virtual address */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RSI,X86_ESI); /* RDX = target register */ amd64_mov_reg_imm(b->jit_ptr,AMD64_RDX,target); /* RDI = CPU instance */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); /* Call memory access function */ amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); amd64_call_membase(b->jit_ptr,AMD64_R15,MEMOP_OFFSET(opcode)); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); amd64_patch(p_exit,b->jit_ptr); } /* Fast memory operation */ static void mips64_emit_memop_fast(cpu_mips_t *cpu,cpu_tc_t *b, int write_op,int opcode, int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { switch(cpu->addr_mode) { case 32: mips64_emit_memop_fast32(b,write_op,opcode,base,offset,target, keep_ll_bit,op_handler); break; case 64: mips64_emit_memop_fast64(b,write_op,opcode,base,offset,target, keep_ll_bit,op_handler); break; } } /* Memory operation */ static void mips64_emit_memop(cpu_tc_t *b,int op,int base,int offset, int target,int keep_ll_bit) { m_uint64_t val = sign_extend(offset,16); /* Save PC for exception handling */ mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); /* RDI = CPU instance */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); if (!keep_ll_bit) { amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_mov_membase_reg(b->jit_ptr,AMD64_RDI,OFFSET(cpu_mips_t,ll_bit), X86_ECX,4); } /* RSI = GPR[base] + sign-extended offset */ mips64_load_imm(b,AMD64_RSI,val); amd64_alu_reg_membase(b->jit_ptr,X86_ADD, AMD64_RSI,AMD64_RDI,REG_OFFSET(base)); /* RDX = target register */ amd64_mov_reg_imm(b->jit_ptr,AMD64_RDX,target); /* Call memory access function */ amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); amd64_call_membase(b->jit_ptr,AMD64_RDI,MEMOP_OFFSET(op)); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); } /* Coprocessor Register transfert operation */ static void mips64_emit_cp_xfr_op(cpu_tc_t *b,int rt,int rd,void *f) { /* update pc */ mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); /* cp0 register */ amd64_mov_reg_imm(b->jit_ptr,AMD64_RDX,rd); /* gpr */ amd64_mov_reg_imm(b->jit_ptr,AMD64_RSI,rt); /* cpu instance */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_basic_c_call(b,f); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); } /* Virtual Breakpoint */ void mips64_emit_breakpoint(cpu_tc_t *b) { amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_c_call(b,mips64_run_breakpoint); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); } /* Unknown opcode handler */ static fastcall void mips64_unknown_opcode(cpu_mips_t *cpu,m_uint32_t opcode) { printf("CPU = %p\n",cpu); printf("MIPS64: unhandled opcode 0x%8.8x at 0x%llx (ra=0x%llx)\n", opcode,cpu->pc,cpu->gpr[MIPS_GPR_RA]); mips64_dump_regs(cpu->gen); } /* Emit unhandled instruction code */ static int mips64_emit_unknown(cpu_mips_t *cpu,cpu_tc_t *b, mips_insn_t opcode) { amd64_mov_reg_imm(b->jit_ptr,AMD64_RSI,opcode); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_c_call(b,mips64_unknown_opcode); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); return(0); } /* Invalid delay slot handler */ static fastcall void mips64_invalid_delay_slot(cpu_mips_t *cpu) { printf("MIPS64: invalid instruction in delay slot at 0x%llx (ra=0x%llx)\n", cpu->pc,cpu->gpr[MIPS_GPR_RA]); mips64_dump_regs(cpu->gen); /* Halt the virtual CPU */ cpu->pc = 0; } /* Emit unhandled instruction code */ int mips64_emit_invalid_delay_slot(cpu_tc_t *b) { amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_c_call(b,mips64_invalid_delay_slot); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); return(0); } /* * Increment count register and trigger the timer IRQ if value in compare * register is the same. */ void mips64_inc_cp0_count_reg(cpu_tc_t *b) { amd64_inc_membase_size(b->jit_ptr, AMD64_R15,OFFSET(cpu_mips_t,cp0_virt_cnt_reg),4); #if 0 /* TIMER_IRQ */ u_char *test1; /* increment the virtual count register */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX, AMD64_R15,OFFSET(cpu_mips_t,cp0_virt_cnt_reg),4); amd64_inc_reg_size(b->jit_ptr,AMD64_RAX,4); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15, OFFSET(cpu_mips_t,cp0_virt_cnt_reg), AMD64_RAX,4); /* check with the virtual compare register */ amd64_alu_reg_membase_size(b->jit_ptr,X86_CMP,AMD64_RAX, AMD64_R15,OFFSET(cpu_mips_t,cp0_virt_cmp_reg),4); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* we have to trigger the timer irq */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); mips64_emit_basic_c_call(b,mips64_trigger_timer_irq); amd64_patch(test1,b->jit_ptr); #endif } /* Check if there are pending IRQ */ void mips64_check_pending_irq(cpu_tc_t *b) { u_char *test1; /* Check the pending IRQ flag */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX, AMD64_R15,OFFSET(cpu_mips_t,irq_pending),4); amd64_test_reg_reg_size(b->jit_ptr,AMD64_RAX,AMD64_RAX,4); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_Z, 0, 1); /* Update PC */ mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); /* Trigger the IRQ */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_basic_c_call(b,mips64_trigger_irq); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); mips64_jit_tcb_push_epilog(b); amd64_patch(test1,b->jit_ptr); } /* Increment the number of executed instructions (performance debugging) */ void mips64_inc_perf_counter(cpu_tc_t *b) { amd64_inc_membase_size(b->jit_ptr, AMD64_R15,OFFSET(cpu_mips_t,perf_counter),4); } /* ADD */ DECLARE_INSN(ADD) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_ADD,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* ADDI */ DECLARE_INSN(ADDI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); /* TODO: Exception handling */ mips64_load_imm(b,AMD64_RAX,val); amd64_alu_reg_membase(b->jit_ptr,X86_ADD,AMD64_RAX, AMD64_R15,REG_OFFSET(rs)); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RAX,8); return(0); } /* ADDIU */ DECLARE_INSN(ADDIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); mips64_load_imm(b,AMD64_RAX,val); if (rs != 0) { amd64_alu_reg_membase(b->jit_ptr,X86_ADD,AMD64_RAX, AMD64_R15,REG_OFFSET(rs)); } amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RDX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RDX,8); return(0); } /* ADDU */ DECLARE_INSN(ADDU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_ADD,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* AND */ DECLARE_INSN(AND) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_AND,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* ANDI */ DECLARE_INSN(ANDI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); mips64_load_imm(b,AMD64_RAX,imm); amd64_alu_reg_membase(b->jit_ptr,X86_AND,AMD64_RAX, AMD64_R15,REG_OFFSET(rs)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RAX,8); return(0); } /* B (Branch, virtual instruction) */ DECLARE_INSN(B) { int offset = bits(insn,0,15); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); return(0); } /* BAL (Branch and Link, virtual instruction) */ DECLARE_INSN(BAL) { int offset = bits(insn,0,15); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->vaddr + ((b->trans_pos + 1) << 2)); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,0); return(0); } /* BEQ (Branch On Equal) */ DECLARE_INSN(BEQ) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_CMP,AMD64_RAX, AMD64_R15,REG_OFFSET(rt)); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NE, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BEQL (Branch On Equal Likely) */ DECLARE_INSN(BEQL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_CMP,AMD64_RAX, AMD64_R15,REG_OFFSET(rt)); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NE, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BEQZ (Branch On Equal Zero) */ DECLARE_INSN(BEQZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] with 0. */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NZ, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BNEZ (Branch On Not Equal Zero) */ DECLARE_INSN(BNEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] with 0. */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_Z, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZ (Branch On Greater or Equal Than Zero) */ DECLARE_INSN(BGEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* If sign bit is set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZAL (Branch On Greater or Equal Than Zero And Link) */ DECLARE_INSN(BGEZAL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->vaddr + ((b->trans_pos + 1) << 2)); /* If sign bit is set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZALL (Branch On Greater or Equal Than Zero And Link Likely) */ DECLARE_INSN(BGEZALL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->vaddr + ((b->trans_pos + 1) << 2)); /* If sign bit is set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BGEZL (Branch On Greater or Equal Than Zero Likely) */ DECLARE_INSN(BGEZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* If sign bit is set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BGTZ (Branch On Greater Than Zero) */ DECLARE_INSN(BGTZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* compare reg to zero */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RCX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_LE, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGTZL (Branch On Greater Than Zero Likely) */ DECLARE_INSN(BGTZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* compare reg to zero */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RCX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_LE, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BLEZ (Branch On Less or Equal Than Zero) */ DECLARE_INSN(BLEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* compare reg to zero */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RCX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_GT, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLEZL (Branch On Less or Equal Than Zero Likely) */ DECLARE_INSN(BLEZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* compare reg to zero */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RCX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_GT, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BLTZ (Branch On Less Than Zero) */ DECLARE_INSN(BLTZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* If sign bit isn't set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLTZAL (Branch On Less Than Zero And Link) */ DECLARE_INSN(BLTZAL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->vaddr + ((b->trans_pos + 1) << 2)); /* If sign bit isn't set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLTZALL (Branch On Less Than Zero And Link Likely) */ DECLARE_INSN(BLTZALL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->vaddr + ((b->trans_pos + 1) << 2)); /* If sign bit isn't set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BLTZL (Branch On Less Than Zero Likely) */ DECLARE_INSN(BLTZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* If sign bit isn't set, don't take the branch */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BNE (Branch On Not Equal) */ DECLARE_INSN(BNE) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_CMP,AMD64_RAX, AMD64_R15,REG_OFFSET(rt)); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_E, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BNEL (Branch On Not Equal Likely) */ DECLARE_INSN(BNEL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_CMP,AMD64_RAX, AMD64_R15,REG_OFFSET(rt)); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_E, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); amd64_patch(test1,b->jit_ptr); return(0); } /* BREAK */ DECLARE_INSN(BREAK) { u_int code = bits(insn,6,25); amd64_mov_reg_imm(b->jit_ptr,AMD64_RSI,code); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_basic_c_call(b,mips64_exec_break); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); mips64_jit_tcb_push_epilog(b); return(0); } /* CACHE */ DECLARE_INSN(CACHE) { int base = bits(insn,21,25); int op = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_CACHE,base,offset,op,0); return(0); } /* CFC0 */ DECLARE_INSN(CFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_cfc0); return(0); } /* CTC0 */ DECLARE_INSN(CTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_ctc0); return(0); } /* DADDIU */ DECLARE_INSN(DADDIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); mips64_load_imm(b,AMD64_RCX,val); amd64_alu_reg_membase(b->jit_ptr,X86_ADD,AMD64_RCX, AMD64_R15,REG_OFFSET(rs)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RCX,8); return(0); } /* DADDU: rd = rs + rt */ DECLARE_INSN(DADDU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_ADD,AMD64_RCX, AMD64_R15,REG_OFFSET(rt)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RCX,8); return(0); } /* DIV */ DECLARE_INSN(DIV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); /* eax = gpr[rs] */ amd64_clear_reg(b->jit_ptr,AMD64_RDX); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),4); /* ecx = gpr[rt] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rt),4); /* eax = quotient (LO), edx = remainder (HI) */ amd64_div_reg_size(b->jit_ptr,AMD64_RCX,1,4); /* store LO */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,lo), AMD64_RAX,8); /* store HI */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RDX,X86_EDX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,hi), AMD64_RDX,8); return(0); } /* DIVU */ DECLARE_INSN(DIVU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); /* eax = gpr[rs] */ amd64_clear_reg(b->jit_ptr,AMD64_RDX); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),4); /* ecx = gpr[rt] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rt),4); /* eax = quotient (LO), edx = remainder (HI) */ amd64_div_reg_size(b->jit_ptr,AMD64_RCX,0,4); /* store LO */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,lo), AMD64_RAX,8); /* store HI */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RDX,X86_EDX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,hi), AMD64_RDX,8); return(0); } /* DMFC0 */ DECLARE_INSN(DMFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_dmfc0); return(0); } /* DMFC1 */ DECLARE_INSN(DMFC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_dmfc1); return(0); } /* DMTC0 */ DECLARE_INSN(DMTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_dmtc0); return(0); } /* DMTC1 */ DECLARE_INSN(DMTC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_dmtc1); return(0); } /* DSLL */ DECLARE_INSN(DSLL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg_imm(b->jit_ptr,X86_SHL,AMD64_RAX,sa); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSLL32 */ DECLARE_INSN(DSLL32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg_imm(b->jit_ptr,X86_SHL,AMD64_RAX,sa+32); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSLLV */ DECLARE_INSN(DSLLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rs),4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RCX,0x3f); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg(b->jit_ptr,X86_SHL,AMD64_RAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSRA */ DECLARE_INSN(DSRA) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg_imm(b->jit_ptr,X86_SAR,AMD64_RAX,sa); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSRA32 */ DECLARE_INSN(DSRA32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg_imm(b->jit_ptr,X86_SAR,AMD64_RAX,sa+32); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSRAV */ DECLARE_INSN(DSRAV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rs),4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RCX,0x3f); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg(b->jit_ptr,X86_SAR,AMD64_RAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSRL */ DECLARE_INSN(DSRL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg_imm(b->jit_ptr,X86_SHR,AMD64_RAX,sa); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSRL32 */ DECLARE_INSN(DSRL32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg_imm(b->jit_ptr,X86_SHR,AMD64_RAX,sa+32); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSRLV */ DECLARE_INSN(DSRLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rs),4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RCX,0x3f); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_shift_reg(b->jit_ptr,X86_SHR,AMD64_RAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* DSUBU: rd = rs - rt */ DECLARE_INSN(DSUBU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_SUB,AMD64_RAX, AMD64_R15,REG_OFFSET(rt)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* ERET */ DECLARE_INSN(ERET) { mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_basic_c_call(b,mips64_exec_eret); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); mips64_jit_tcb_push_epilog(b); return(0); } /* J (Jump) */ DECLARE_INSN(J) { u_int instr_index = bits(insn,0,25); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc &= ~((1 << 28) - 1); new_pc |= instr_index << 2; /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); return(0); } /* JAL (Jump And Link) */ DECLARE_INSN(JAL) { u_int instr_index = bits(insn,0,25); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc &= ~((1 << 28) - 1); new_pc |= instr_index << 2; /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->vaddr + ((b->trans_pos + 1) << 2)); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,0); return(0); } /* JALR (Jump and Link Register) */ DECLARE_INSN(JALR) { int rs = bits(insn,21,25); int rd = bits(insn,11,15); m_uint64_t ret_pc; /* set the return pc (instruction after the delay slot) in GPR[rd] */ ret_pc = b->vaddr + ((b->trans_pos + 1) << 2); mips64_load_imm(b,AMD64_RAX,ret_pc); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); /* get the new pc */ amd64_mov_reg_membase(b->jit_ptr,AMD64_R14,AMD64_R15,REG_OFFSET(rs),8); #if DEBUG_JR0 { u_char *test1; amd64_test_reg_reg(b->jit_ptr,AMD64_R14,AMD64_R14); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_c_call(b,mips64_debug_jr0); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); amd64_patch(test1,b->jit_ptr); } #endif /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc */ amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,pc), AMD64_R14,8); /* returns to the caller which will determine the next path */ mips64_jit_tcb_push_epilog(b); return(0); } /* JR (Jump Register) */ DECLARE_INSN(JR) { int rs = bits(insn,21,25); /* get the new pc */ amd64_mov_reg_membase(b->jit_ptr,AMD64_R14,AMD64_R15,REG_OFFSET(rs),8); #if DEBUG_JR0 { u_char *test1; amd64_test_reg_reg(b->jit_ptr,AMD64_RCX,AMD64_RCX); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_c_call(b,mips64_debug_jr0); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); amd64_patch(test1,b->jit_ptr); } #endif /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc */ amd64_mov_membase_reg(b->jit_ptr, AMD64_R15,OFFSET(cpu_mips_t,pc), AMD64_R14,8); /* returns to the caller which will determine the next path */ mips64_jit_tcb_push_epilog(b); return(0); } /* LB (Load Byte) */ DECLARE_INSN(LB) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LB,base,offset,rt,TRUE); return(0); } /* LBU (Load Byte Unsigned) */ DECLARE_INSN(LBU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LBU,base,offset,rt,TRUE); return(0); } /* LD (Load Double-Word) */ DECLARE_INSN(LD) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LD,base,offset,rt,TRUE); return(0); } /* LDC1 (Load Double-Word to Coprocessor 1) */ DECLARE_INSN(LDC1) { int base = bits(insn,21,25); int ft = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDC1,base,offset,ft,TRUE); return(0); } /* LDL (Load Double-Word Left) */ DECLARE_INSN(LDL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDL,base,offset,rt,TRUE); return(0); } /* LDR (Load Double-Word Right) */ DECLARE_INSN(LDR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDR,base,offset,rt,TRUE); return(0); } /* LH (Load Half-Word) */ DECLARE_INSN(LH) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LH,base,offset,rt,TRUE); return(0); } /* LHU (Load Half-Word Unsigned) */ DECLARE_INSN(LHU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LHU,base,offset,rt,TRUE); return(0); } /* LI (virtual) */ DECLARE_INSN(LI) { int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); mips64_load_imm(b,AMD64_RCX,val); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RCX,8); return(0); } /* LL (Load Linked) */ DECLARE_INSN(LL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LL,base,offset,rt,TRUE); return(0); } /* LUI */ DECLARE_INSN(LUI) { int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16) << 16; #if 1 mips64_load_imm(b,AMD64_RCX,val); #else amd64_mov_reg_imm(b->jit_ptr,AMD64_RCX,imm); amd64_shift_reg_imm(b->jit_ptr,X86_SHL,AMD64_RCX,48); amd64_shift_reg_imm(b->jit_ptr,X86_SAR,AMD64_RCX,32); #endif amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RCX,8); return(0); } /* LW (Load Word) */ DECLARE_INSN(LW) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,0,MIPS_MEMOP_LW,base,offset,rt,TRUE, mips64_memop_fast_lw); } else { mips64_emit_memop(b,MIPS_MEMOP_LW,base,offset,rt,TRUE); } return(0); } /* LWL (Load Word Left) */ DECLARE_INSN(LWL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LWL,base,offset,rt,TRUE); return(0); } /* LWR (Load Word Right) */ DECLARE_INSN(LWR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LWR,base,offset,rt,TRUE); return(0); } /* LWU (Load Word Unsigned) */ DECLARE_INSN(LWU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LWU,base,offset,rt,TRUE); return(0); } /* MFC0 */ DECLARE_INSN(MFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_mfc0); return(0); } /* MFC1 */ DECLARE_INSN(MFC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_mfc1); return(0); } /* MFHI */ DECLARE_INSN(MFHI) { int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX, AMD64_R15,OFFSET(cpu_mips_t,hi),8); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RDX,8); return(0); } /* MFLO */ DECLARE_INSN(MFLO) { int rd = bits(insn,11,15); if (!rd) return(0); amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX, AMD64_R15,OFFSET(cpu_mips_t,lo),8); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RDX,8); return(0); } /* MOVE (virtual instruction, real: ADDU) */ DECLARE_INSN(MOVE) { int rs = bits(insn,21,25); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX,AMD64_R15,REG_OFFSET(rs),4); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RDX,X86_EDX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RDX,8); return(0); } /* MOVZ */ DECLARE_INSN(MOVZ) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); u_char *test1; amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); amd64_test_reg_reg(b->jit_ptr,AMD64_RAX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch32(b->jit_ptr, X86_CC_NZ, 0, 1); amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX,AMD64_R15,REG_OFFSET(rs),8); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RDX,8); amd64_patch(test1,b->jit_ptr); return(0); } /* MTC0 */ DECLARE_INSN(MTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_mtc0); return(0); } /* MTC1 */ DECLARE_INSN(MTC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_mtc1); return(0); } /* MTHI */ DECLARE_INSN(MTHI) { int rs = bits(insn,21,25); amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX,AMD64_R15,REG_OFFSET(rs),8); amd64_mov_membase_reg(b->jit_ptr, AMD64_R15,OFFSET(cpu_mips_t,hi),AMD64_RDX,8); return(0); } /* MTLO */ DECLARE_INSN(MTLO) { int rs = bits(insn,21,25); amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX,AMD64_R15,REG_OFFSET(rs),8); amd64_mov_membase_reg(b->jit_ptr, AMD64_R15,OFFSET(cpu_mips_t,lo),AMD64_RDX,8); return(0); } /* MUL */ DECLARE_INSN(MUL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); /* eax = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),4); /* ecx = gpr[rt] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rt),4); amd64_mul_reg_size(b->jit_ptr,AMD64_RCX,1,4); /* store result in gpr[rd] */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* MULT */ DECLARE_INSN(MULT) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); /* eax = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),4); /* ecx = gpr[rt] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rt),4); amd64_mul_reg_size(b->jit_ptr,AMD64_RCX,1,4); /* store LO */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,lo), AMD64_RAX,8); /* store HI */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RDX,X86_EDX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,hi), AMD64_RDX,8); return(0); } /* MULTU */ DECLARE_INSN(MULTU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); /* eax = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),4); /* ecx = gpr[rt] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rt),4); amd64_mul_reg_size(b->jit_ptr,AMD64_RCX,0,4); /* store LO */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,lo), AMD64_RAX,8); /* store HI */ amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RDX,X86_EDX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,OFFSET(cpu_mips_t,hi), AMD64_RDX,8); return(0); } /* NOP */ DECLARE_INSN(NOP) { return(0); } /* NOR */ DECLARE_INSN(NOR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_OR,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_not_reg(b->jit_ptr,AMD64_RAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* OR */ DECLARE_INSN(OR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_OR,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* ORI */ DECLARE_INSN(ORI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); mips64_load_imm(b,AMD64_RAX,imm); amd64_alu_reg_membase(b->jit_ptr,X86_OR,AMD64_RAX, AMD64_R15,REG_OFFSET(rs)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RAX,8); return(0); } /* PREF */ DECLARE_INSN(PREF) { amd64_nop(b->jit_ptr); return(0); } /* PREFI */ DECLARE_INSN(PREFI) { amd64_nop(b->jit_ptr); return(0); } /* SB (Store Byte) */ DECLARE_INSN(SB) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SB,base,offset,rt,FALSE); return(0); } /* SC (Store Conditional) */ DECLARE_INSN(SC) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SC,base,offset,rt,TRUE); return(0); } /* SD (Store Double-Word) */ DECLARE_INSN(SD) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SD,base,offset,rt,FALSE); return(0); } /* SDL (Store Double-Word Left) */ DECLARE_INSN(SDL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDL,base,offset,rt,FALSE); return(0); } /* SDR (Store Double-Word Right) */ DECLARE_INSN(SDR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDR,base,offset,rt,FALSE); return(0); } /* SDC1 (Store Double-Word from Coprocessor 1) */ DECLARE_INSN(SDC1) { int base = bits(insn,21,25); int ft = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDC1,base,offset,ft,FALSE); return(0); } /* SH (Store Half-Word) */ DECLARE_INSN(SH) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SH,base,offset,rt,FALSE); return(0); } /* SLL */ DECLARE_INSN(SLL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),4); amd64_shift_reg_imm(b->jit_ptr,X86_SHL,AMD64_RAX,sa); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SLLV */ DECLARE_INSN(SLLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rs),4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RCX,0x1f); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),4); amd64_shift_reg(b->jit_ptr,X86_SHL,AMD64_RAX); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SLT */ DECLARE_INSN(SLT) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); u_char *test1; /* RDX = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX,AMD64_R15,REG_OFFSET(rs),8); /* RAX = gpr[rt] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); /* we set rd to 1 when gpr[rs] < gpr[rt] */ amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RCX,8); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RDX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_GE, 0, 1); amd64_inc_membase(b->jit_ptr,AMD64_R15,REG_OFFSET(rd)); /* end */ amd64_patch(test1,b->jit_ptr); return(0); } /* SLTI */ DECLARE_INSN(SLTI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); u_char *test1; /* RDX = val */ mips64_load_imm(b,AMD64_RDX,val); /* RAX = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); /* we set rt to 1 when gpr[rs] < val */ amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RCX,8); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RDX); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_GE, 0, 1); amd64_inc_membase(b->jit_ptr,AMD64_R15,REG_OFFSET(rt)); /* end */ amd64_patch(test1,b->jit_ptr); return(0); } /* SLTU */ DECLARE_INSN(SLTU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); u_char *test1; /* RDX = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RDX,AMD64_R15,REG_OFFSET(rs),8); /* RAX = gpr[rt] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),8); /* we set rd to 1 when gpr[rs] < gpr[rt] */ amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RCX,8); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RDX,AMD64_RAX); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_AE, 0, 0); amd64_inc_membase(b->jit_ptr,AMD64_R15,REG_OFFSET(rd)); /* end */ amd64_patch(test1,b->jit_ptr); return(0); } /* SLTIU */ DECLARE_INSN(SLTIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); u_char *test1; /* RDX = val */ mips64_load_imm(b,AMD64_RDX,val); /* RAX = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); /* we set rt to 1 when gpr[rs] < val */ amd64_clear_reg(b->jit_ptr,AMD64_RCX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RCX,8); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RDX); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_AE, 0, 0); amd64_inc_membase(b->jit_ptr,AMD64_R15,REG_OFFSET(rt)); /* end */ amd64_patch(test1,b->jit_ptr); return(0); } /* SRA */ DECLARE_INSN(SRA) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),4); amd64_shift_reg_imm_size(b->jit_ptr,X86_SAR,AMD64_RAX,sa,4); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SRAV */ DECLARE_INSN(SRAV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rs),4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RCX,0x1f); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),4); amd64_shift_reg_size(b->jit_ptr,X86_SAR,AMD64_RAX,4); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SRL */ DECLARE_INSN(SRL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),4); amd64_shift_reg_imm(b->jit_ptr,X86_SHR,AMD64_RAX,sa); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SRLV */ DECLARE_INSN(SRLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX,AMD64_R15,REG_OFFSET(rs),4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RCX,0x1f); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rt),4); amd64_shift_reg(b->jit_ptr,X86_SHR,AMD64_RAX); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SUB */ DECLARE_INSN(SUB) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); /* TODO: Exception handling */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_SUB,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SUBU */ DECLARE_INSN(SUBU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_SUB,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_movsxd_reg_reg(b->jit_ptr,AMD64_RAX,X86_EAX); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* SW (Store Word) */ DECLARE_INSN(SW) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,1,MIPS_MEMOP_SW,base,offset,rt,FALSE, mips64_memop_fast_sw); } else { mips64_emit_memop(b,MIPS_MEMOP_SW,base,offset,rt,FALSE); } return(0); } /* SWL (Store Word Left) */ DECLARE_INSN(SWL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SWL,base,offset,rt,FALSE); return(0); } /* SWR (Store Word Right) */ DECLARE_INSN(SWR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SWR,base,offset,rt,FALSE); return(0); } /* SYNC */ DECLARE_INSN(SYNC) { return(0); } /* SYSCALL */ DECLARE_INSN(SYSCALL) { mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_basic_c_call(b,mips64_exec_syscall); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); mips64_jit_tcb_push_epilog(b); return(0); } /* TEQ (Trap If Equal) */ DECLARE_INSN(TEQ) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); u_char *test1; /* * compare gpr[rs] and gpr[rt]. */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_CMP,AMD64_RAX, AMD64_R15,REG_OFFSET(rt)); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Generate trap exception */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_c_call(b,mips64_trigger_trap_exception); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); mips64_jit_tcb_push_epilog(b); /* end */ amd64_patch(test1,b->jit_ptr); return(0); } /* TEQI (Trap If Equal Immediate) */ DECLARE_INSN(TEQI) { int rs = bits(insn,21,25); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); u_char *test1; /* RDX = val */ mips64_load_imm(b,AMD64_RDX,val); /* RAX = gpr[rs] */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_reg(b->jit_ptr,X86_CMP,AMD64_RAX,AMD64_RDX); test1 = b->jit_ptr; amd64_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Generate trap exception */ amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_c_call(b,mips64_trigger_trap_exception); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); mips64_jit_tcb_push_epilog(b); /* end */ amd64_patch(test1,b->jit_ptr); return(0); } /* TLBP */ DECLARE_INSN(TLBP) { mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbp); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); return(0); } /* TLBR */ DECLARE_INSN(TLBR) { mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbr); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); return(0); } /* TLBWI */ DECLARE_INSN(TLBWI) { mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbwi); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); return(0); } /* TLBWR */ DECLARE_INSN(TLBWR) { mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); amd64_mov_reg_reg(b->jit_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(b->jit_ptr,X86_SUB,AMD64_RSP,8); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbwr); amd64_alu_reg_imm(b->jit_ptr,X86_ADD,AMD64_RSP,8); return(0); } /* XOR */ DECLARE_INSN(XOR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); amd64_mov_reg_membase(b->jit_ptr,AMD64_RAX,AMD64_R15,REG_OFFSET(rs),8); amd64_alu_reg_membase(b->jit_ptr,X86_XOR,AMD64_RAX,AMD64_R15, REG_OFFSET(rt)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rd),AMD64_RAX,8); return(0); } /* XORI */ DECLARE_INSN(XORI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); mips64_load_imm(b,AMD64_RAX,imm); amd64_alu_reg_membase(b->jit_ptr,X86_XOR,AMD64_RAX, AMD64_R15,REG_OFFSET(rs)); amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,REG_OFFSET(rt),AMD64_RAX,8); return(0); } /* MIPS instruction array */ struct mips64_insn_tag mips64_insn_tags[] = { { mips64_emit_LI , 0xffe00000 , 0x24000000, 1 }, /* virtual */ { mips64_emit_MOVE , 0xfc1f07ff , 0x00000021, 1 }, /* virtual */ { mips64_emit_B , 0xffff0000 , 0x10000000, 0 }, /* virtual */ { mips64_emit_BAL , 0xffff0000 , 0x04110000, 0 }, /* virtual */ { mips64_emit_BEQZ , 0xfc1f0000 , 0x10000000, 0 }, /* virtual */ { mips64_emit_BNEZ , 0xfc1f0000 , 0x14000000, 0 }, /* virtual */ { mips64_emit_ADD , 0xfc0007ff , 0x00000020, 1 }, { mips64_emit_ADDI , 0xfc000000 , 0x20000000, 1 }, { mips64_emit_ADDIU , 0xfc000000 , 0x24000000, 1 }, { mips64_emit_ADDU , 0xfc0007ff , 0x00000021, 1 }, { mips64_emit_AND , 0xfc0007ff , 0x00000024, 1 }, { mips64_emit_ANDI , 0xfc000000 , 0x30000000, 1 }, { mips64_emit_BEQ , 0xfc000000 , 0x10000000, 0 }, { mips64_emit_BEQL , 0xfc000000 , 0x50000000, 0 }, { mips64_emit_BGEZ , 0xfc1f0000 , 0x04010000, 0 }, { mips64_emit_BGEZAL , 0xfc1f0000 , 0x04110000, 0 }, { mips64_emit_BGEZALL , 0xfc1f0000 , 0x04130000, 0 }, { mips64_emit_BGEZL , 0xfc1f0000 , 0x04030000, 0 }, { mips64_emit_BGTZ , 0xfc1f0000 , 0x1c000000, 0 }, { mips64_emit_BGTZL , 0xfc1f0000 , 0x5c000000, 0 }, { mips64_emit_BLEZ , 0xfc1f0000 , 0x18000000, 0 }, { mips64_emit_BLEZL , 0xfc1f0000 , 0x58000000, 0 }, { mips64_emit_BLTZ , 0xfc1f0000 , 0x04000000, 0 }, { mips64_emit_BLTZAL , 0xfc1f0000 , 0x04100000, 0 }, { mips64_emit_BLTZALL , 0xfc1f0000 , 0x04120000, 0 }, { mips64_emit_BLTZL , 0xfc1f0000 , 0x04020000, 0 }, { mips64_emit_BNE , 0xfc000000 , 0x14000000, 0 }, { mips64_emit_BNEL , 0xfc000000 , 0x54000000, 0 }, { mips64_emit_BREAK , 0xfc00003f , 0x0000000d, 1 }, { mips64_emit_CACHE , 0xfc000000 , 0xbc000000, 1 }, { mips64_emit_CFC0 , 0xffe007ff , 0x40400000, 1 }, { mips64_emit_CTC0 , 0xffe007ff , 0x40600000, 1 }, { mips64_emit_DADDIU , 0xfc000000 , 0x64000000, 1 }, { mips64_emit_DADDU , 0xfc0007ff , 0x0000002d, 1 }, { mips64_emit_DIV , 0xfc00ffff , 0x0000001a, 1 }, { mips64_emit_DIVU , 0xfc00ffff , 0x0000001b, 1 }, { mips64_emit_DMFC0 , 0xffe007f8 , 0x40200000, 1 }, { mips64_emit_DMFC1 , 0xffe007ff , 0x44200000, 1 }, { mips64_emit_DMTC0 , 0xffe007f8 , 0x40a00000, 1 }, { mips64_emit_DMTC1 , 0xffe007ff , 0x44a00000, 1 }, { mips64_emit_DSLL , 0xffe0003f , 0x00000038, 1 }, { mips64_emit_DSLL32 , 0xffe0003f , 0x0000003c, 1 }, { mips64_emit_DSLLV , 0xfc0007ff , 0x00000014, 1 }, { mips64_emit_DSRA , 0xffe0003f , 0x0000003b, 1 }, { mips64_emit_DSRA32 , 0xffe0003f , 0x0000003f, 1 }, { mips64_emit_DSRAV , 0xfc0007ff , 0x00000017, 1 }, { mips64_emit_DSRL , 0xffe0003f , 0x0000003a, 1 }, { mips64_emit_DSRL32 , 0xffe0003f , 0x0000003e, 1 }, { mips64_emit_DSRLV , 0xfc0007ff , 0x00000016, 1 }, { mips64_emit_DSUBU , 0xfc0007ff , 0x0000002f, 1 }, { mips64_emit_ERET , 0xffffffff , 0x42000018, 0 }, { mips64_emit_J , 0xfc000000 , 0x08000000, 0 }, { mips64_emit_JAL , 0xfc000000 , 0x0c000000, 0 }, { mips64_emit_JALR , 0xfc1f003f , 0x00000009, 0 }, { mips64_emit_JR , 0xfc1ff83f , 0x00000008, 0 }, { mips64_emit_LB , 0xfc000000 , 0x80000000, 1 }, { mips64_emit_LBU , 0xfc000000 , 0x90000000, 1 }, { mips64_emit_LD , 0xfc000000 , 0xdc000000, 1 }, { mips64_emit_LDC1 , 0xfc000000 , 0xd4000000, 1 }, { mips64_emit_LDL , 0xfc000000 , 0x68000000, 1 }, { mips64_emit_LDR , 0xfc000000 , 0x6c000000, 1 }, { mips64_emit_LH , 0xfc000000 , 0x84000000, 1 }, { mips64_emit_LHU , 0xfc000000 , 0x94000000, 1 }, { mips64_emit_LL , 0xfc000000 , 0xc0000000, 1 }, { mips64_emit_LUI , 0xffe00000 , 0x3c000000, 1 }, { mips64_emit_LW , 0xfc000000 , 0x8c000000, 1 }, { mips64_emit_LWL , 0xfc000000 , 0x88000000, 1 }, { mips64_emit_LWR , 0xfc000000 , 0x98000000, 1 }, { mips64_emit_LWU , 0xfc000000 , 0x9c000000, 1 }, { mips64_emit_MFC0 , 0xffe007ff , 0x40000000, 1 }, { mips64_emit_CFC0 , 0xffe007ff , 0x40000001, 1 }, /* MFC0 / Set 1 */ { mips64_emit_MFC1 , 0xffe007ff , 0x44000000, 1 }, { mips64_emit_MFHI , 0xffff07ff , 0x00000010, 1 }, { mips64_emit_MFLO , 0xffff07ff , 0x00000012, 1 }, { mips64_emit_MOVZ , 0xfc0007ff , 0x0000000a, 1 }, { mips64_emit_MTC0 , 0xffe007ff , 0x40800000, 1 }, { mips64_emit_MTC1 , 0xffe007ff , 0x44800000, 1 }, { mips64_emit_MTHI , 0xfc1fffff , 0x00000011, 1 }, { mips64_emit_MTLO , 0xfc1fffff , 0x00000013, 1 }, { mips64_emit_MUL , 0xfc0007ff , 0x70000002, 1 }, { mips64_emit_MULT , 0xfc00ffff , 0x00000018, 1 }, { mips64_emit_MULTU , 0xfc00ffff , 0x00000019, 1 }, { mips64_emit_NOP , 0xffffffff , 0x00000000, 1 }, { mips64_emit_NOR , 0xfc0007ff , 0x00000027, 1 }, { mips64_emit_OR , 0xfc0007ff , 0x00000025, 1 }, { mips64_emit_ORI , 0xfc000000 , 0x34000000, 1 }, { mips64_emit_PREF , 0xfc000000 , 0xcc000000, 1 }, { mips64_emit_PREFI , 0xfc0007ff , 0x4c00000f, 1 }, { mips64_emit_SB , 0xfc000000 , 0xa0000000, 1 }, { mips64_emit_SC , 0xfc000000 , 0xe0000000, 1 }, { mips64_emit_SD , 0xfc000000 , 0xfc000000, 1 }, { mips64_emit_SDC1 , 0xfc000000 , 0xf4000000, 1 }, { mips64_emit_SDL , 0xfc000000 , 0xb0000000, 1 }, { mips64_emit_SDR , 0xfc000000 , 0xb4000000, 1 }, { mips64_emit_SH , 0xfc000000 , 0xa4000000, 1 }, { mips64_emit_SLL , 0xffe0003f , 0x00000000, 1 }, { mips64_emit_SLLV , 0xfc0007ff , 0x00000004, 1 }, { mips64_emit_SLT , 0xfc0007ff , 0x0000002a, 1 }, { mips64_emit_SLTI , 0xfc000000 , 0x28000000, 1 }, { mips64_emit_SLTIU , 0xfc000000 , 0x2c000000, 1 }, { mips64_emit_SLTU , 0xfc0007ff , 0x0000002b, 1 }, { mips64_emit_SRA , 0xffe0003f , 0x00000003, 1 }, { mips64_emit_SRAV , 0xfc0007ff , 0x00000007, 1 }, { mips64_emit_SRL , 0xffe0003f , 0x00000002, 1 }, { mips64_emit_SRLV , 0xfc0007ff , 0x00000006, 1 }, { mips64_emit_SUB , 0xfc0007ff , 0x00000022, 1 }, { mips64_emit_SUBU , 0xfc0007ff , 0x00000023, 1 }, { mips64_emit_SW , 0xfc000000 , 0xac000000, 1 }, { mips64_emit_SWL , 0xfc000000 , 0xa8000000, 1 }, { mips64_emit_SWR , 0xfc000000 , 0xb8000000, 1 }, { mips64_emit_SYNC , 0xfffff83f , 0x0000000f, 1 }, { mips64_emit_SYSCALL , 0xfc00003f , 0x0000000c, 1 }, { mips64_emit_TEQ , 0xfc00003f , 0x00000034, 1 }, { mips64_emit_TEQI , 0xfc1f0000 , 0x040c0000, 1 }, { mips64_emit_TLBP , 0xffffffff , 0x42000008, 1 }, { mips64_emit_TLBR , 0xffffffff , 0x42000001, 1 }, { mips64_emit_TLBWI , 0xffffffff , 0x42000002, 1 }, { mips64_emit_TLBWR , 0xffffffff , 0x42000006, 1 }, { mips64_emit_XOR , 0xfc0007ff , 0x00000026, 1 }, { mips64_emit_XORI , 0xfc000000 , 0x38000000, 1 }, { mips64_emit_unknown , 0x00000000 , 0x00000000, 1 }, { NULL , 0x00000000 , 0x00000000, 0 }, }; dynamips-0.2.14/unstable/mips64_amd64_trans.h000066400000000000000000000041751241034141600207370ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __MIPS64_AMD64_TRANS_H__ #define __MIPS64_AMD64_TRANS_H__ #include "utils.h" #include "amd64-codegen.h" #include "cpu.h" #include "dynamips.h" #include "mips64_exec.h" #define JIT_SUPPORT 1 /* Manipulate bitmasks atomically */ static forced_inline void atomic_or(m_uint32_t *v,m_uint32_t m) { __asm__ __volatile__("lock; orl %1,%0":"=m"(*v):"ir"(m),"m"(*v)); } static forced_inline void atomic_and(m_uint32_t *v,m_uint32_t m) { __asm__ __volatile__("lock; andl %1,%0":"=m"(*v):"ir"(m),"m"(*v)); } /* Wrappers to amd64-codegen functions */ #define mips64_jit_tcb_set_patch amd64_patch #define mips64_jit_tcb_set_jump amd64_jump_code_fn /* MIPS instruction array */ extern struct mips64_insn_tag mips64_insn_tags[]; /* Push epilog for an amd64 instruction block */ static forced_inline void mips64_jit_tcb_push_epilog(cpu_tc_t *tc) { amd64_ret(tc->jit_ptr); } /* Execute JIT code */ static forced_inline void mips64_jit_tcb_exec(cpu_mips_t *cpu,cpu_tb_t *tb) { insn_tblock_fptr jit_code; m_uint32_t offset,*iarray; offset = (cpu->pc & MIPS_MIN_PAGE_IMASK) >> 2; jit_code = (insn_tblock_fptr)tb->tc->jit_insn_ptr[offset]; if (unlikely(!jit_code)) { iarray = (m_uint32_t *)tb->target_code; mips64_exec_single_step(cpu,vmtoh32(iarray[offset])); return; } asm volatile ("movq %0,%%r15"::"r"(cpu): "r14","r15","rax","rbx","rcx","rdx","rdi","rsi"); jit_code(); } static inline void amd64_patch(u_char *code,u_char *target) { /* Skip REX */ if ((code[0] >= 0x40) && (code[0] <= 0x4f)) code += 1; if ((code [0] & 0xf8) == 0xb8) { /* amd64_set_reg_template */ *(m_uint64_t *)(code + 1) = (m_uint64_t)target; } else if (code [0] == 0x8b) { /* mov 0(%rip), %dreg */ *(m_uint32_t *)(code + 2) = (m_uint32_t)(m_uint64_t)target - 7; } else if ((code [0] == 0xff) && (code [1] == 0x15)) { /* call *(%rip) */ *(m_uint32_t *)(code + 2) = ((m_uint32_t)(m_uint64_t)target) - 7; } else x86_patch(code,target); } #endif dynamips-0.2.14/unstable/mips64_cp0.c000066400000000000000000000403761241034141600172750ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * MIPS Coprocessor 0 (System Coprocessor) implementation. * We don't use the JIT here, since there is no high performance needed. */ #include #include #include #include #include #include #include #include "device.h" #include "mips64.h" #include "mips64_cp0.h" #include "dynamips.h" #include "memory.h" /* MIPS cp0 registers names */ char *mips64_cp0_reg_names[MIPS64_CP0_REG_NR] = { "index" , "random", "entry_lo0", "entry_lo1", "context", "pagemask", "wired", "info", "badvaddr", "count", "entry_hi", "compare", "status", "cause", "epc", "prid", "config", "ll_addr", "watch_lo", "watch_hi", "xcontext", "cp0_r21", "cp0_r22", "cp0_r23", "cp0_r24", "cp0_r25", "ecc", "cache_err", "tag_lo", "tag_hi", "err_epc", "cp0_r31", }; /* Get cp0 register index given its name */ int mips64_cp0_get_reg_index(char *name) { int i; for(i=0;icp0.reg[MIPS_CP0_WIRED]; return(wired + (cpu->cp0_virt_cnt_reg % (cpu->cp0.tlb_entries - wired))); } /* Get a cp0 register (fast version) */ static inline m_uint64_t mips64_cp0_get_reg_fast(cpu_mips_t *cpu,u_int cp0_reg) { mips_cp0_t *cp0 = &cpu->cp0; m_uint32_t delta,res; switch(cp0_reg) { case MIPS_CP0_COUNT: delta = cpu->cp0_virt_cmp_reg - cpu->cp0_virt_cnt_reg; res = (m_uint32_t)cp0->reg[MIPS_CP0_COMPARE]; res -= cpu->vm->clock_divisor * delta; return(sign_extend(res,32)); #if 1 case MIPS_CP0_COMPARE: return(sign_extend(cp0->reg[MIPS_CP0_COMPARE],32)); #else /* really useful and logical ? */ case MIPS_CP0_COMPARE: delta = cpu->cp0_virt_cmp_reg - cpu->cp0_virt_cnt_reg; res = (m_uint32_t)cp0->reg[MIPS_CP0_COUNT]; res += (cpu->vm->clock_divisor * delta); return(res); #endif case MIPS_CP0_INFO: return(MIPS64_R7000_TLB64_ENABLE); case MIPS_CP0_RANDOM: return(mips64_cp0_get_random_reg(cpu)); default: return(cp0->reg[cp0_reg]); } } /* Get a cp0 register */ m_uint64_t mips64_cp0_get_reg(cpu_mips_t *cpu,u_int cp0_reg) { return(mips64_cp0_get_reg_fast(cpu,cp0_reg)); } /* Set a cp0 register */ static inline void mips64_cp0_set_reg(cpu_mips_t *cpu,u_int cp0_reg, m_uint64_t val) { mips_cp0_t *cp0 = &cpu->cp0; m_uint32_t delta; switch(cp0_reg) { case MIPS_CP0_STATUS: case MIPS_CP0_CAUSE: cp0->reg[cp0_reg] = val; mips64_update_irq_flag(cpu); break; case MIPS_CP0_PAGEMASK: cp0->reg[cp0_reg] = val & MIPS_TLB_PAGE_MASK; break; case MIPS_CP0_COMPARE: mips64_clear_irq(cpu,7); mips64_update_irq_flag(cpu); cp0->reg[cp0_reg] = val; delta = val - cp0->reg[MIPS_CP0_COUNT]; cpu->cp0_virt_cnt_reg = 0; cpu->cp0_virt_cmp_reg = delta / cpu->vm->clock_divisor; break; case MIPS_CP0_COUNT: cp0->reg[cp0_reg] = val; delta = cp0->reg[MIPS_CP0_COMPARE] - val; cpu->cp0_virt_cnt_reg = 0; cpu->cp0_virt_cmp_reg = delta / cpu->vm->clock_divisor; break; case MIPS_CP0_TLB_HI: cp0->reg[cp0_reg] = val & MIPS_CP0_HI_SAFE_MASK; break; case MIPS_CP0_TLB_LO_0: case MIPS_CP0_TLB_LO_1: cp0->reg[cp0_reg] = val & MIPS_CP0_LO_SAFE_MASK; break; case MIPS_CP0_RANDOM: case MIPS_CP0_PRID: case MIPS_CP0_CONFIG: /* read only registers */ break; case MIPS_CP0_WIRED: cp0->reg[cp0_reg] = val & MIPS64_TLB_IDX_MASK; break; default: cp0->reg[cp0_reg] = val; } } /* Get a cp0 "set 1" register (R7000) */ m_uint64_t mips64_cp0_s1_get_reg(cpu_mips_t *cpu,u_int cp0_s1_reg) { switch(cp0_s1_reg) { case MIPS_CP0_S1_CONFIG: return(0x7F << 25); case MIPS_CP0_S1_IPLLO: return(cpu->cp0.ipl_lo); case MIPS_CP0_S1_IPLHI: return(cpu->cp0.ipl_hi); case MIPS_CP0_S1_INTCTL: return(cpu->cp0.int_ctl); case MIPS_CP0_S1_DERRADDR0: return(cpu->cp0.derraddr0); case MIPS_CP0_S1_DERRADDR1: return(cpu->cp0.derraddr1); default: /* undefined register */ cpu_log(cpu->gen,"CP0_S1","trying to read unknown register %u\n", cp0_s1_reg); return(0); } } /* Set a cp0 "set 1" register (R7000) */ static inline void mips64_cp0_s1_set_reg(cpu_mips_t *cpu,u_int cp0_s1_reg, m_uint64_t val) { mips_cp0_t *cp0 = &cpu->cp0; switch(cp0_s1_reg) { case MIPS_CP0_S1_IPLLO: cp0->ipl_lo = val; break; case MIPS_CP0_S1_IPLHI: cp0->ipl_hi = val; break; case MIPS_CP0_S1_INTCTL: cp0->int_ctl = val; break; case MIPS_CP0_S1_DERRADDR0: cp0->derraddr0 = val; break; case MIPS_CP0_S1_DERRADDR1: cp0->derraddr1 = val; break; default: cpu_log(cpu->gen, "CP0_S1","trying to set unknown register %u (val=0x%x)\n", cp0_s1_reg,val); } } /* DMFC0 */ fastcall void mips64_cp0_exec_dmfc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg) { cpu->gpr[gp_reg] = mips64_cp0_get_reg_fast(cpu,cp0_reg); } /* DMTC0 */ fastcall void mips64_cp0_exec_dmtc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg) { mips64_cp0_set_reg(cpu,cp0_reg,cpu->gpr[gp_reg]); } /* MFC0 */ fastcall void mips64_cp0_exec_mfc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg) { cpu->gpr[gp_reg] = sign_extend(mips64_cp0_get_reg_fast(cpu,cp0_reg),32); } /* MTC0 */ fastcall void mips64_cp0_exec_mtc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg) { mips64_cp0_set_reg(cpu,cp0_reg,cpu->gpr[gp_reg] & 0xffffffff); } /* CFC0 */ fastcall void mips64_cp0_exec_cfc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg) { cpu->gpr[gp_reg] = sign_extend(mips64_cp0_s1_get_reg(cpu,cp0_reg),32); } /* CTC0 */ fastcall void mips64_cp0_exec_ctc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg) { mips64_cp0_s1_set_reg(cpu,cp0_reg,cpu->gpr[gp_reg] & 0xffffffff); } /* Get the page size corresponding to a page mask */ static inline m_uint32_t get_page_size(m_uint32_t page_mask) { return((page_mask + 0x2000) >> 1); } /* Write page size in buffer */ static char *get_page_size_str(char *buffer,size_t len,m_uint32_t page_mask) { m_uint32_t page_size; page_size = get_page_size(page_mask); /* Mb ? */ if (page_size >= (1024*1024)) snprintf(buffer,len,"%uMB",page_size >> 20); else snprintf(buffer,len,"%uKB",page_size >> 10); return buffer; } /* Execute a callback for the specified entry */ static inline void mips64_cp0_tlb_callback(cpu_mips_t *cpu,tlb_entry_t *entry, int action) { _maybe_used m_uint64_t vaddr,paddr0,paddr1; _maybe_used m_uint32_t psize; vaddr = entry->hi & mips64_cp0_get_vpn2_mask(cpu); psize = get_page_size(entry->mask); if (entry->lo0 & MIPS_TLB_V_MASK) { paddr0 = (entry->lo0 & MIPS_TLB_PFN_MASK) << 6; /*printf("TLB: vaddr=0x%8.8llx -> paddr0=0x%10.10llx (size=0x%8.8x), " "action=%s\n", vaddr,paddr0,psize, (action == 0) ? "ADD" : "DELETE");*/ } if (entry->lo1 & MIPS_TLB_V_MASK) { paddr1 = (entry->lo1 & MIPS_TLB_PFN_MASK) << 6; /*printf("TLB: vaddr=0x%8.8llx -> paddr1=0x%10.10llx (size=0x%8.8x), " "action=%s\n", vaddr,paddr1,psize, (action == 0) ? "ADD" : "DELETE");*/ } } /* TLB lookup */ int mips64_cp0_tlb_lookup(cpu_mips_t *cpu,m_uint64_t vaddr, u_int op_type,mts_map_t *res) { mips_cp0_t *cp0 = &cpu->cp0; m_uint64_t vpn_addr,vpn2_mask; m_uint64_t page_mask,hi_addr; m_uint32_t page_size,pca; tlb_entry_t *entry; u_int asid; int i; vpn2_mask = mips64_cp0_get_vpn2_mask(cpu); vpn_addr = vaddr & vpn2_mask; asid = cp0->reg[MIPS_CP0_TLB_HI] & MIPS_TLB_ASID_MASK; for(i=0;itlb_entries;i++) { entry = &cp0->tlb[i]; page_mask = ~entry->mask; hi_addr = entry->hi & vpn2_mask & page_mask; if (((vpn_addr & page_mask) == hi_addr) && ((entry->hi & MIPS_TLB_G_MASK) || ((entry->hi & MIPS_TLB_ASID_MASK) == asid))) { page_size = get_page_size(entry->mask); if ((vaddr & page_size) == 0) { /* Even Page */ if (entry->lo0 & MIPS_TLB_V_MASK) { /* Check write protection */ if ((op_type == MTS_WRITE) && !(entry->lo0 & MIPS_TLB_D_MASK)) return MIPS_TLB_LOOKUP_MOD; res->flags = 0; res->vaddr = vaddr & MIPS_MIN_PAGE_MASK; res->paddr = (entry->lo0 & MIPS_TLB_PFN_MASK) << 6; res->paddr += (res->vaddr & (page_size-1)); res->paddr &= cpu->addr_bus_mask; res->offset = vaddr & MIPS_MIN_PAGE_IMASK; pca = (entry->lo0 & MIPS_TLB_C_MASK); pca >>= MIPS_TLB_C_SHIFT; res->cached = mips64_cca_cached(pca); if (!(entry->lo0 & MIPS_TLB_D_MASK)) res->flags |= MTS_FLAG_RO; return(MIPS_TLB_LOOKUP_OK); } } else { /* Odd Page */ if (entry->lo1 & MIPS_TLB_V_MASK) { /* Check write protection */ if ((op_type == MTS_WRITE) && !(entry->lo1 & MIPS_TLB_D_MASK)) return MIPS_TLB_LOOKUP_MOD; res->flags = 0; res->vaddr = vaddr & MIPS_MIN_PAGE_MASK; res->paddr = (entry->lo1 & MIPS_TLB_PFN_MASK) << 6; res->paddr += (res->vaddr & (page_size-1)); res->paddr &= cpu->addr_bus_mask; res->offset = vaddr & MIPS_MIN_PAGE_IMASK; pca = (entry->lo1 & MIPS_TLB_C_MASK); pca >>= MIPS_TLB_C_SHIFT; res->cached = mips64_cca_cached(pca); if (!(entry->lo0 & MIPS_TLB_D_MASK)) res->flags |= MTS_FLAG_RO; return(MIPS_TLB_LOOKUP_OK); } } /* Invalid entry */ return(MIPS_TLB_LOOKUP_INVALID); } } /* No matching entry */ return(MIPS_TLB_LOOKUP_MISS); } /* TLBP: Probe a TLB entry */ fastcall void mips64_cp0_exec_tlbp(cpu_mips_t *cpu) { mips_cp0_t *cp0 = &cpu->cp0; m_uint64_t hi_reg,asid; m_uint64_t vpn2,vpn2_mask; m_uint64_t page_mask; tlb_entry_t *entry; int i; vpn2_mask = mips64_cp0_get_vpn2_mask(cpu); hi_reg = cp0->reg[MIPS_CP0_TLB_HI]; asid = hi_reg & MIPS_TLB_ASID_MASK; vpn2 = hi_reg & vpn2_mask; cp0->reg[MIPS_CP0_INDEX] = 0xffffffff80000000ULL; for(i=0;itlb_entries;i++) { entry = &cp0->tlb[i]; page_mask = ~entry->mask; if (((entry->hi & vpn2_mask & page_mask) == (vpn2 & page_mask)) && ((entry->hi & MIPS_TLB_G_MASK) || ((entry->hi & MIPS_TLB_ASID_MASK) == asid))) { cp0->reg[MIPS_CP0_INDEX] = i; #if DEBUG_TLB_ACTIVITY printf("CPU: CP0_TLBP returned %u\n",i); tlb_dump(cpu); #endif } } } /* TLBR: Read Indexed TLB entry */ fastcall void mips64_cp0_exec_tlbr(cpu_mips_t *cpu) { mips_cp0_t *cp0 = &cpu->cp0; tlb_entry_t *entry; u_int index; index = cp0->reg[MIPS_CP0_INDEX]; #if DEBUG_TLB_ACTIVITY cpu_log(cpu,"TLB","CP0_TLBR: reading entry %u.\n",index); #endif if (index < cp0->tlb_entries) { entry = &cp0->tlb[index]; cp0->reg[MIPS_CP0_PAGEMASK] = entry->mask; cp0->reg[MIPS_CP0_TLB_HI] = entry->hi; cp0->reg[MIPS_CP0_TLB_LO_0] = entry->lo0; cp0->reg[MIPS_CP0_TLB_LO_1] = entry->lo1; /* * The G bit must be reported in both Lo0 and Lo1 registers, * and cleared in Hi register. */ if (entry->hi & MIPS_TLB_G_MASK) { cp0->reg[MIPS_CP0_TLB_LO_0] |= MIPS_CP0_LO_G_MASK; cp0->reg[MIPS_CP0_TLB_LO_1] |= MIPS_CP0_LO_G_MASK; cp0->reg[MIPS_CP0_TLB_HI] &= ~MIPS_TLB_G_MASK; } } } /* TLBW: Write a TLB entry */ static inline void mips64_cp0_exec_tlbw(cpu_mips_t *cpu,u_int index) { mips_cp0_t *cp0 = &cpu->cp0; tlb_entry_t *entry; #if DEBUG_TLB_ACTIVITY cpu_log(cpu,"TLB","CP0_TLBWI: writing entry %u " "[mask=0x%8.8llx,hi=0x%8.8llx,lo0=0x%8.8llx,lo1=0x%8.8llx]\n", index,cp0->reg[MIPS_CP0_PAGEMASK],cp0->reg[MIPS_CP0_TLB_HI], cp0->reg[MIPS_CP0_TLB_LO_0],cp0->reg[MIPS_CP0_TLB_LO_1]); #endif if (index < cp0->tlb_entries) { entry = &cp0->tlb[index]; mips64_cp0_tlb_callback(cpu,entry,TLB_ZONE_ADD); entry->mask = cp0->reg[MIPS_CP0_PAGEMASK] & MIPS_TLB_PAGE_MASK; entry->hi = cp0->reg[MIPS_CP0_TLB_HI]; entry->lo0 = cp0->reg[MIPS_CP0_TLB_LO_0]; entry->lo1 = cp0->reg[MIPS_CP0_TLB_LO_1]; /* if G bit is set in lo0 and lo1, set it in hi */ if ((entry->lo0 & entry->lo1) & MIPS_CP0_LO_G_MASK) entry->hi |= MIPS_TLB_G_MASK; else entry->hi &= ~MIPS_TLB_G_MASK; /* Clear G bit in TLB lo0 and lo1 */ entry->lo0 &= ~MIPS_CP0_LO_G_MASK; entry->lo1 &= ~MIPS_CP0_LO_G_MASK; /* Inform the MTS subsystem */ cpu->mts_invalidate(cpu); mips64_cp0_tlb_callback(cpu,entry,TLB_ZONE_DELETE); #if DEBUG_TLB_ACTIVITY mips64_tlb_dump_entry(cpu,index); #endif } } /* TLBWI: Write Indexed TLB entry */ fastcall void mips64_cp0_exec_tlbwi(cpu_mips_t *cpu) { mips64_cp0_exec_tlbw(cpu,cpu->cp0.reg[MIPS_CP0_INDEX]); } /* TLBWR: Write Random TLB entry */ fastcall void mips64_cp0_exec_tlbwr(cpu_mips_t *cpu) { mips64_cp0_exec_tlbw(cpu,mips64_cp0_get_random_reg(cpu)); } /* Raw dump of the TLB */ void mips64_tlb_raw_dump(cpu_gen_t *cpu) { cpu_mips_t *mcpu = CPU_MIPS64(cpu); tlb_entry_t *entry; u_int i; printf("TLB dump:\n"); for(i=0;icp0.tlb_entries;i++) { entry = &mcpu->cp0.tlb[i]; printf(" %2d: mask=0x%16.16llx hi=0x%16.16llx " "lo0=0x%16.16llx lo1=0x%16.16llx\n", i, entry->mask, entry->hi, entry->lo0, entry->lo1); } printf("\n"); } /* Dump the specified TLB entry */ void mips64_tlb_dump_entry(cpu_mips_t *cpu,u_int index) { tlb_entry_t *entry; char buffer[256]; entry = &cpu->cp0.tlb[index]; /* virtual Address */ printf(" %2d: vaddr=0x%8.8llx ", index, entry->hi & mips64_cp0_get_vpn2_mask(cpu)); /* global or ASID */ if (entry->hi & MIPS_TLB_G_MASK) printf("(global) "); else printf("(asid 0x%2.2llx) ",entry->hi & MIPS_TLB_ASID_MASK); /* 1st page: Lo0 */ printf("p0="); if (entry->lo0 & MIPS_TLB_V_MASK) printf("0x%9.9llx",(entry->lo0 & MIPS_TLB_PFN_MASK) << 6); else printf("(invalid) "); printf(" %c ",(entry->lo0 & MIPS_TLB_D_MASK) ? 'D' : ' '); /* 2nd page: Lo1 */ printf("p1="); if (entry->lo1 & MIPS_TLB_V_MASK) printf("0x%9.9llx",(entry->lo1 & MIPS_TLB_PFN_MASK) << 6); else printf("(invalid) "); printf(" %c ",(entry->lo1 & MIPS_TLB_D_MASK) ? 'D' : ' '); /* page size */ printf(" (%s)\n",get_page_size_str(buffer,sizeof(buffer),entry->mask)); } /* Human-Readable dump of the TLB */ void mips64_tlb_dump(cpu_gen_t *cpu) { cpu_mips_t *mcpu = CPU_MIPS64(cpu); u_int i; printf("TLB dump:\n"); for(i=0;icp0.tlb_entries;i++) mips64_tlb_dump_entry(mcpu,i); printf("\n"); } dynamips-0.2.14/unstable/mips64_cp0.h000066400000000000000000000063431241034141600172760ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __CP0_H__ #define __CP0_H__ #include "utils.h" #define TLB_ZONE_ADD 0 #define TLB_ZONE_DELETE 1 /* Update the Context register with a faulty address */ static inline void mips64_cp0_update_context_reg(cpu_mips_t *cpu,m_uint64_t addr) { m_uint64_t badvpn2; badvpn2 = addr & MIPS_CP0_CONTEXT_VPN2_MASK; badvpn2 <<= MIPS_CP0_CONTEXT_BADVPN2_SHIFT; cpu->cp0.reg[MIPS_CP0_CONTEXT] &= ~MIPS_CP0_CONTEXT_BADVPN2_MASK; cpu->cp0.reg[MIPS_CP0_CONTEXT] |= badvpn2; } /* Update the XContext register with a faulty address */ static inline void mips64_cp0_update_xcontext_reg(cpu_mips_t *cpu,m_uint64_t addr) { m_uint64_t rbadvpn2; rbadvpn2 = addr & MIPS_CP0_XCONTEXT_VPN2_MASK; rbadvpn2 <<= MIPS_CP0_XCONTEXT_BADVPN2_SHIFT; rbadvpn2 |= ((addr >> 62) & 0x03) << MIPS_CP0_XCONTEXT_R_SHIFT; cpu->cp0.reg[MIPS_CP0_XCONTEXT] &= ~MIPS_CP0_XCONTEXT_RBADVPN2_MASK; cpu->cp0.reg[MIPS_CP0_XCONTEXT] |= rbadvpn2; } /* Get the CPU operating mode (User,Supervisor or Kernel) */ static forced_inline u_int mips64_cp0_get_mode(cpu_mips_t *cpu) { mips_cp0_t *cp0 = &cpu->cp0; u_int cpu_mode; cpu_mode = cp0->reg[MIPS_CP0_STATUS] >> MIPS_CP0_STATUS_KSU_SHIFT; cpu_mode &= MIPS_CP0_STATUS_KSU_MASK; return(cpu_mode); } /* Get the VPN2 mask */ static forced_inline m_uint64_t mips64_cp0_get_vpn2_mask(cpu_mips_t *cpu) { if (cpu->addr_mode == 64) return(MIPS_TLB_VPN2_MASK_64); else return(MIPS_TLB_VPN2_MASK_32); } /* CP0 register names */ extern char *mips64_cp0_reg_names[]; /* Get cp0 register index given its name */ int mips64_cp0_get_reg_index(char *name); /* Get the CPU operating mode (User,Supervisor or Kernel) */ u_int mips64_cp0_get_mode(cpu_mips_t *cpu); /* Get a cp0 register */ m_uint64_t mips64_cp0_get_reg(cpu_mips_t *cpu,u_int cp0_reg); /* DMFC0 */ fastcall void mips64_cp0_exec_dmfc0(cpu_mips_t *cpu,u_int gp_reg, u_int cp0_reg); /* DMTC0 */ fastcall void mips64_cp0_exec_dmtc0(cpu_mips_t *cpu,u_int gp_reg, u_int cp0_reg); /* MFC0 */ fastcall void mips64_cp0_exec_mfc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg); /* MTC0 */ fastcall void mips64_cp0_exec_mtc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg); /* CFC0 */ fastcall void mips64_cp0_exec_cfc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg); /* CTC0 */ fastcall void mips64_cp0_exec_ctc0(cpu_mips_t *cpu,u_int gp_reg,u_int cp0_reg); /* TLB lookup */ int mips64_cp0_tlb_lookup(cpu_mips_t *cpu,m_uint64_t vaddr,u_int op_type, mts_map_t *res); /* TLBP: Probe a TLB entry */ fastcall void mips64_cp0_exec_tlbp(cpu_mips_t *cpu); /* TLBR: Read Indexed TLB entry */ fastcall void mips64_cp0_exec_tlbr(cpu_mips_t *cpu); /* TLBWI: Write Indexed TLB entry */ fastcall void mips64_cp0_exec_tlbwi(cpu_mips_t *cpu); /* TLBWR: Write Random TLB entry */ fastcall void mips64_cp0_exec_tlbwr(cpu_mips_t *cpu); /* Raw dump of the TLB */ void mips64_tlb_raw_dump(cpu_gen_t *cpu); /* Dump the specified TLB entry */ void mips64_tlb_dump_entry(cpu_mips_t *cpu,u_int index); /* Human-Readable dump of the TLB */ void mips64_tlb_dump(cpu_gen_t *cpu); #endif dynamips-0.2.14/unstable/mips64_exec.c000066400000000000000000001677051241034141600175450ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * MIPS64 Step-by-step execution. */ #if __GNUC__ > 2 #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "mips64_exec.h" #include "memory.h" #include "insn_lookup.h" #include "dynamips.h" /* Forward declaration of instruction array */ static struct mips64_insn_exec_tag mips64_exec_tags[]; static insn_lookup_t *ilt = NULL; /* ILT */ static forced_inline void *mips64_exec_get_insn(int index) { return(&mips64_exec_tags[index]); } static int mips64_exec_chk_lo(struct mips64_insn_exec_tag *tag,int value) { return((value & tag->mask) == (tag->value & 0xFFFF)); } static int mips64_exec_chk_hi(struct mips64_insn_exec_tag *tag,int value) { return((value & (tag->mask >> 16)) == (tag->value >> 16)); } /* Destroy instruction lookup table */ static void destroy_ilt(void) { assert(ilt); ilt_destroy(ilt); ilt = NULL; } /* Initialize instruction lookup table */ void mips64_exec_create_ilt(void) { int i,count; for(i=0,count=0;mips64_exec_tags[i].exec;i++) count++; ilt = ilt_create("mips64e",count, (ilt_get_insn_cbk_t)mips64_exec_get_insn, (ilt_check_cbk_t)mips64_exec_chk_lo, (ilt_check_cbk_t)mips64_exec_chk_hi); atexit(destroy_ilt); } /* Dump statistics */ void mips64_dump_stats(cpu_mips_t *cpu) { int i; #if NJM_STATS_ENABLE printf("\n"); for(i=0;mips64_exec_tags[i].exec;i++) printf(" * %-10s : %10llu\n", mips64_exec_tags[i].name,mips64_exec_tags[i].count); printf("%llu instructions executed since startup.\n",cpu->insn_exec_count); #else printf("Statistics support is not compiled in.\n"); #endif } /* Dump an instruction */ int mips64_dump_insn(char *buffer,size_t buf_size,size_t insn_name_size, m_uint64_t pc,mips_insn_t instruction) { char insn_name[64],insn_format[32],*name; int base,rs,rd,rt,sa,offset,imm; struct mips64_insn_exec_tag *tag; m_uint64_t new_pc; int index; /* Lookup for instruction */ index = ilt_lookup(ilt,instruction); tag = mips64_exec_get_insn(index); if (!tag) { snprintf(buffer,buf_size,"%8.8x (unknown)",instruction); return(-1); } if (!(name = tag->name)) name = "[unknown]"; if (!insn_name_size) insn_name_size = 10; snprintf(insn_format,sizeof(insn_format),"%%-%lus",(u_long)insn_name_size); snprintf(insn_name,sizeof(insn_name),insn_format,name); switch(tag->instr_type) { case 1: /* instructions without operands */ snprintf(buffer,buf_size,"%8.8x %s",instruction,insn_name); break; case 2: /* load/store instructions */ base = bits(instruction,21,25); rt = bits(instruction,16,20); offset = (m_int16_t)bits(instruction,0,15); snprintf(buffer,buf_size,"%8.8x %s %s,%d(%s)", instruction,insn_name,mips64_gpr_reg_names[rt], offset,mips64_gpr_reg_names[base]); break; case 3: /* GPR[rd] = GPR[rs] op GPR[rt] */ rs = bits(instruction,21,25); rt = bits(instruction,16,20); rd = bits(instruction,11,15); snprintf(buffer,buf_size,"%8.8x %s %s,%s,%s", instruction,insn_name,mips64_gpr_reg_names[rd], mips64_gpr_reg_names[rs],mips64_gpr_reg_names[rt]); break; case 4: /* GPR[rd] = GPR[rt] op GPR[rs] */ rs = bits(instruction,21,25); rt = bits(instruction,16,20); rd = bits(instruction,11,15); snprintf(buffer,buf_size,"%8.8x %s %s,%s,%s", instruction,insn_name,mips64_gpr_reg_names[rd], mips64_gpr_reg_names[rt],mips64_gpr_reg_names[rs]); break; case 5: /* GPR[rt] = GPR[rs] op immediate (hex) */ rs = bits(instruction,21,25); rt = bits(instruction,16,20); imm = bits(instruction,0,15); snprintf(buffer,buf_size,"%8.8x %s %s,%s,0x%x", instruction,insn_name,mips64_gpr_reg_names[rt], mips64_gpr_reg_names[rs],imm); break; case 6: /* GPR[rt] = GPR[rs] op immediate (dec) */ rs = bits(instruction,21,25); rt = bits(instruction,16,20); imm = bits(instruction,0,15); snprintf(buffer,buf_size,"%8.8x %s %s,%s,%d", instruction,insn_name,mips64_gpr_reg_names[rt], mips64_gpr_reg_names[rs],(m_int16_t)imm); break; case 7: /* GPR[rd] = GPR[rt] op sa */ rt = bits(instruction,16,20); rd = bits(instruction,11,15); sa = bits(instruction,6,10); snprintf(buffer,buf_size,"%8.8x %s %s,%s,%d", instruction,insn_name,mips64_gpr_reg_names[rd], mips64_gpr_reg_names[rt],sa); break; case 8: /* Branch with: GPR[rs] / GPR[rt] / offset */ rs = bits(instruction,21,25); rt = bits(instruction,16,20); offset = bits(instruction,0,15); new_pc = (pc + 4) + sign_extend(offset << 2,18); snprintf(buffer,buf_size,"%8.8x %s %s,%s,0x%llx", instruction,insn_name,mips64_gpr_reg_names[rs], mips64_gpr_reg_names[rt],new_pc); break; case 9: /* Branch with: GPR[rs] / offset */ rs = bits(instruction,21,25); offset = bits(instruction,0,15); new_pc = (pc + 4) + sign_extend(offset << 2,18); snprintf(buffer,buf_size,"%8.8x %s %s,0x%llx", instruction,insn_name,mips64_gpr_reg_names[rs],new_pc); break; case 10: /* Branch with: offset */ offset = bits(instruction,0,15); new_pc = (pc + 4) + sign_extend(offset << 2,18); snprintf(buffer,buf_size,"%8.8x %s 0x%llx", instruction,insn_name,new_pc); break; case 11: /* Jump */ offset = bits(instruction,0,25); new_pc = (pc & ~((1 << 28) - 1)) | (offset << 2); snprintf(buffer,buf_size,"%8.8x %s 0x%llx", instruction,insn_name,new_pc); break; case 13: /* op GPR[rs] */ rs = bits(instruction,21,25); snprintf(buffer,buf_size,"%8.8x %s %s", instruction,insn_name,mips64_gpr_reg_names[rs]); break; case 14: /* op GPR[rd] */ rd = bits(instruction,11,15); snprintf(buffer,buf_size,"%8.8x %s %s", instruction,insn_name,mips64_gpr_reg_names[rd]); break; case 15: /* op GPR[rd], GPR[rs] */ rs = bits(instruction,21,25); rd = bits(instruction,11,15); snprintf(buffer,buf_size,"%8.8x %s %s,%s", instruction,insn_name,mips64_gpr_reg_names[rd], mips64_gpr_reg_names[rs]); break; case 16: /* op GPR[rt], imm */ rt = bits(instruction,16,20); imm = bits(instruction,0,15); snprintf(buffer,buf_size,"%8.8x %s %s,0x%x", instruction,insn_name,mips64_gpr_reg_names[rt],imm); break; case 17: /* op GPR[rs], GPR[rt] */ rs = bits(instruction,21,25); rt = bits(instruction,16,20); snprintf(buffer,buf_size,"%8.8x %s %s,%s", instruction,insn_name,mips64_gpr_reg_names[rs], mips64_gpr_reg_names[rt]); break; case 18: /* op GPR[rt], CP0[rd] */ rt = bits(instruction,16,20); rd = bits(instruction,11,15); snprintf(buffer,buf_size,"%8.8x %s %s,%s", instruction,insn_name,mips64_gpr_reg_names[rt], mips64_cp0_reg_names[rd]); break; case 19: /* op GPR[rt], $rd */ rt = bits(instruction,16,20); rd = bits(instruction,11,15); snprintf(buffer,buf_size,"%8.8x %s %s,$%d", instruction,insn_name,mips64_gpr_reg_names[rt],rd); break; case 20: /* op GPR[rs], imm */ rs = bits(instruction,21,25); imm = bits(instruction,0,15); snprintf(buffer,buf_size,"%8.8x %s %s,0x%x", instruction,insn_name,mips64_gpr_reg_names[rs],imm); break; default: snprintf(buffer,buf_size,"%8.8x %s (TO DEFINE - %d)", instruction,insn_name,tag->instr_type); return(-1); } return(0); } /* Dump an instruction block */ void mips64_dump_insn_block(cpu_mips_t *cpu,m_uint64_t pc,u_int count, size_t insn_name_size) { mips_insn_t *ptr,insn; char buffer[80]; int i; for(i=0;imem_op_lookup(cpu,pc); insn = vmtoh32(*ptr); mips64_dump_insn(buffer,sizeof(buffer),insn_name_size,pc,insn); printf("0x%llx: %s\n",pc,buffer); pc += sizeof(mips_insn_t); } } /* Execute a memory operation */ _unused static forced_inline void mips64_exec_memop(cpu_mips_t *cpu,int memop, m_uint64_t vaddr,u_int dst_reg, int keep_ll_bit) { fastcall mips_memop_fn fn; if (!keep_ll_bit) cpu->ll_bit = 0; fn = cpu->mem_op_fn[memop]; fn(cpu,vaddr,dst_reg); } /* Execute a memory operation (2) */ static forced_inline void mips64_exec_memop2(cpu_mips_t *cpu,int memop, m_uint64_t base,int offset, u_int dst_reg,int keep_ll_bit) { m_uint64_t vaddr = cpu->gpr[base] + sign_extend(offset,16); fastcall mips_memop_fn fn; if (!keep_ll_bit) cpu->ll_bit = 0; fn = cpu->mem_op_fn[memop]; fn(cpu,vaddr,dst_reg); } /* Fetch an instruction */ static forced_inline int mips64_exec_fetch(cpu_mips_t *cpu,m_uint64_t pc, mips_insn_t *insn) { m_uint64_t exec_page; m_uint32_t offset; exec_page = pc & MIPS_MIN_PAGE_MASK; if (unlikely(exec_page != cpu->njm_exec_page)) { cpu->njm_exec_ptr = cpu->mem_op_ifetch(cpu,exec_page); cpu->njm_exec_page = exec_page; } offset = (pc & MIPS_MIN_PAGE_IMASK) >> 2; *insn = vmtoh32(cpu->njm_exec_ptr[offset]); return(0); } /* Unknown opcode */ static fastcall int mips64_exec_unknown(cpu_mips_t *cpu,mips_insn_t insn) { printf("MIPS64: unknown opcode 0x%8.8x at pc = 0x%llx\n",insn,cpu->pc); mips64_dump_regs(cpu->gen); return(0); } /* Execute a single instruction */ static forced_inline int mips64_exec_single_instruction(cpu_mips_t *cpu,mips_insn_t instruction) { register fastcall int (*exec)(cpu_mips_t *,mips_insn_t) = NULL; struct mips64_insn_exec_tag *tag; int index; #if DEBUG_INSN_PERF_CNT cpu->perf_counter++; #endif /* Increment CP0 count register */ mips64_exec_inc_cp0_cnt(cpu); /* Lookup for instruction */ index = ilt_lookup(ilt,instruction); tag = mips64_exec_get_insn(index); exec = tag->exec; #if NJM_STATS_ENABLE cpu->insn_exec_count++; mips64_exec_tags[index].count++; #endif #if 0 { char buffer[80]; if (mips64_dump_insn(buffer,sizeof(buffer),0,cpu->pc,instruction)!=-1) cpu_log(cpu->gen,"EXEC","0x%llx: %s\n",cpu->pc,buffer); } #endif return(exec(cpu,instruction)); } /* Single-step execution */ fastcall void mips64_exec_single_step(cpu_mips_t *cpu,mips_insn_t instruction) { int res; res = mips64_exec_single_instruction(cpu,instruction); /* Normal flow ? */ if (likely(!res)) cpu->pc += 4; } /* Execute a page */ fastcall int mips64_exec_page(cpu_mips_t *cpu) { m_uint32_t offset; mips_insn_t insn; int res; /* Check IRQ */ if (unlikely(cpu->irq_pending)) mips64_trigger_irq(cpu); cpu->njm_exec_page = cpu->pc & MIPS_MIN_PAGE_MASK; cpu->njm_exec_ptr = cpu->mem_op_ifetch(cpu,cpu->njm_exec_page); do { /* Reset "zero register" (for safety) */ cpu->gpr[0] = 0; offset = (cpu->pc & MIPS_MIN_PAGE_IMASK) >> 2; insn = vmtoh32(cpu->njm_exec_ptr[offset]); res = mips64_exec_single_instruction(cpu,insn); if (likely(!res)) cpu->pc += sizeof(mips_insn_t); }while((cpu->pc & MIPS_MIN_PAGE_MASK) == cpu->njm_exec_page); return(0); } /* Run MIPS code in step-by-step mode */ void *mips64_exec_run_cpu(cpu_gen_t *gen) { cpu_mips_t *cpu = CPU_MIPS64(gen); pthread_t timer_irq_thread; int timer_irq_check = 0; mips_insn_t insn; int res; if (pthread_create(&timer_irq_thread,NULL, (void *)mips64_timer_irq_run,cpu)) { fprintf(stderr,"VM '%s': unable to create Timer IRQ thread for CPU%u.\n", cpu->vm->name,gen->id); cpu_stop(gen); return NULL; } gen->cpu_thread_running = TRUE; cpu_exec_loop_set(gen); start_cpu: gen->idle_count = 0; for(;;) { if (unlikely(gen->state != CPU_STATE_RUNNING)) break; /* Handle virtual idle loop */ if (unlikely(cpu->pc == cpu->idle_pc)) { if (++gen->idle_count == gen->idle_max) { cpu_idle_loop(gen); gen->idle_count = 0; } } /* Handle the virtual CPU clock */ if (++timer_irq_check == cpu->timer_irq_check_itv) { timer_irq_check = 0; if (cpu->timer_irq_pending && !cpu->irq_disable) { mips64_trigger_timer_irq(cpu); mips64_trigger_irq(cpu); cpu->timer_irq_pending--; } } /* Reset "zero register" (for safety) */ cpu->gpr[0] = 0; /* Check IRQ */ if (unlikely(cpu->irq_pending)) { mips64_trigger_irq(cpu); continue; } /* Fetch and execute the instruction */ mips64_exec_fetch(cpu,cpu->pc,&insn); res = mips64_exec_single_instruction(cpu,insn); /* Normal flow ? */ if (likely(!res)) cpu->pc += sizeof(mips_insn_t); } if (!cpu->pc) { cpu_stop(gen); cpu_log(gen,"SLOW_EXEC","PC=0, halting CPU.\n"); } /* Check regularly if the CPU has been restarted */ while(gen->cpu_thread_running) { gen->seq_state++; switch(gen->state) { case CPU_STATE_RUNNING: gen->state = CPU_STATE_RUNNING; goto start_cpu; case CPU_STATE_HALTED: gen->cpu_thread_running = FALSE; pthread_join(timer_irq_thread,NULL); break; } /* CPU is paused */ usleep(200000); } return NULL; } /* Execute the instruction in delay slot */ static forced_inline void mips64_exec_bdslot(cpu_mips_t *cpu) { mips_insn_t insn; /* Set BD slot flag */ cpu->bd_slot = 1; /* Fetch the instruction in delay slot */ mips64_exec_fetch(cpu,cpu->pc+4,&insn); /* Execute the instruction */ mips64_exec_single_instruction(cpu,insn); /* Clear BD slot flag */ cpu->bd_slot = 0; } /* ADD */ static fastcall int mips64_exec_ADD(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_uint64_t res; /* TODO: Exception handling */ res = (m_uint32_t)cpu->gpr[rs] + (m_uint32_t)cpu->gpr[rt]; cpu->gpr[rd] = sign_extend(res,32); return(0); } /* ADDI */ static fastcall int mips64_exec_ADDI(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t res,val = sign_extend(imm,16); /* TODO: Exception handling */ res = (m_uint32_t)cpu->gpr[rs] + val; cpu->gpr[rt] = sign_extend(res,32); return(0); } /* ADDIU */ static fastcall int mips64_exec_ADDIU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t res,val = sign_extend(imm,16); res = (m_uint32_t)cpu->gpr[rs] + val; cpu->gpr[rt] = sign_extend(res,32); return(0); } /* ADDU */ static fastcall int mips64_exec_ADDU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_uint32_t res; res = (m_uint32_t)cpu->gpr[rs] + (m_uint32_t)cpu->gpr[rt]; cpu->gpr[rd] = sign_extend(res,32); return(0); } /* AND */ static fastcall int mips64_exec_AND(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rs] & cpu->gpr[rt]; return(0); } /* ANDI */ static fastcall int mips64_exec_ANDI(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); cpu->gpr[rt] = cpu->gpr[rs] & imm; return(0); } /* B (Branch, virtual instruction) */ static fastcall int mips64_exec_B(cpu_mips_t *cpu,mips_insn_t insn) { int offset = bits(insn,0,15); m_uint64_t new_pc; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* set the new pc in cpu structure */ cpu->pc = new_pc; return(1); } /* BAL (Branch And Link, virtual instruction) */ static fastcall int mips64_exec_BAL(cpu_mips_t *cpu,mips_insn_t insn) { int offset = bits(insn,0,15); m_uint64_t new_pc; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ cpu->gpr[MIPS_GPR_RA] = cpu->pc + 8; /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* set the new pc in cpu structure */ cpu->pc = new_pc; return(1); } /* BEQ (Branch On Equal) */ static fastcall int mips64_exec_BEQ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] == gpr[rt] */ res = (cpu->gpr[rs] == cpu->gpr[rt]); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BEQL (Branch On Equal Likely) */ static fastcall int mips64_exec_BEQL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] == gpr[rt] */ res = (cpu->gpr[rs] == cpu->gpr[rt]); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BEQZ (Branch On Equal Zero) - Virtual Instruction */ static fastcall int mips64_exec_BEQZ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] == 0 */ res = (cpu->gpr[rs] == 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BNEZ (Branch On Not Equal Zero) - Virtual Instruction */ static fastcall int mips64_exec_BNEZ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] != 0 */ res = (cpu->gpr[rs] != 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BGEZ (Branch On Greater or Equal Than Zero) */ static fastcall int mips64_exec_BGEZ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] >= 0 */ res = ((m_int64_t)cpu->gpr[rs] >= 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BGEZAL (Branch On Greater or Equal Than Zero And Link) */ static fastcall int mips64_exec_BGEZAL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ cpu->gpr[MIPS_GPR_RA] = cpu->pc + 8; /* take the branch if gpr[rs] >= 0 */ res = ((m_int64_t)cpu->gpr[rs] >= 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BGEZALL (Branch On Greater or Equal Than Zero And Link Likely) */ static fastcall int mips64_exec_BGEZALL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ cpu->gpr[MIPS_GPR_RA] = cpu->pc + 8; /* take the branch if gpr[rs] >= 0 */ res = ((m_int64_t)cpu->gpr[rs] >= 0); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BGEZL (Branch On Greater or Equal Than Zero Likely) */ static fastcall int mips64_exec_BGEZL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] >= 0 */ res = ((m_int64_t)cpu->gpr[rs] >= 0); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BGTZ (Branch On Greater Than Zero) */ static fastcall int mips64_exec_BGTZ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] > 0 */ res = ((m_int64_t)cpu->gpr[rs] > 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BGTZL (Branch On Greater Than Zero Likely) */ static fastcall int mips64_exec_BGTZL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] > 0 */ res = ((m_int64_t)cpu->gpr[rs] > 0); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BLEZ (Branch On Less or Equal Than Zero) */ static fastcall int mips64_exec_BLEZ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] <= 0 */ res = ((m_int64_t)cpu->gpr[rs] <= 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BLEZL (Branch On Less or Equal Than Zero Likely) */ static fastcall int mips64_exec_BLEZL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] <= 0 */ res = ((m_int64_t)cpu->gpr[rs] <= 0); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BLTZ (Branch On Less Than Zero) */ static fastcall int mips64_exec_BLTZ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] < 0 */ res = ((m_int64_t)cpu->gpr[rs] < 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BLTZAL (Branch On Less Than Zero And Link) */ static fastcall int mips64_exec_BLTZAL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ cpu->gpr[MIPS_GPR_RA] = cpu->pc + 8; /* take the branch if gpr[rs] < 0 */ res = ((m_int64_t)cpu->gpr[rs] < 0); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BLTZALL (Branch On Less Than Zero And Link Likely) */ static fastcall int mips64_exec_BLTZALL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ cpu->gpr[MIPS_GPR_RA] = cpu->pc + 8; /* take the branch if gpr[rs] < 0 */ res = ((m_int64_t)cpu->gpr[rs] < 0); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BLTZL (Branch On Less Than Zero Likely) */ static fastcall int mips64_exec_BLTZL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] < 0 */ res = ((m_int64_t)cpu->gpr[rs] < 0); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BNE (Branch On Not Equal) */ static fastcall int mips64_exec_BNE(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] != gpr[rt] */ res = (cpu->gpr[rs] != cpu->gpr[rt]); /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* take the branch if the test result is true */ if (res) cpu->pc = new_pc; else cpu->pc += 8; return(1); } /* BNEL (Branch On Not Equal Likely) */ static fastcall int mips64_exec_BNEL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); m_uint64_t new_pc; int res; /* compute the new pc */ new_pc = (cpu->pc + 4) + sign_extend(offset << 2,18); /* take the branch if gpr[rs] != gpr[rt] */ res = (cpu->gpr[rs] != cpu->gpr[rt]); /* take the branch if the test result is true */ if (res) { mips64_exec_bdslot(cpu); cpu->pc = new_pc; } else cpu->pc += 8; return(1); } /* BREAK */ static fastcall int mips64_exec_BREAK(cpu_mips_t *cpu,mips_insn_t insn) { u_int code = bits(insn,6,25); mips64_exec_break(cpu,code); return(1); } /* CACHE */ static fastcall int mips64_exec_CACHE(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int op = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_CACHE,base,offset,op,FALSE); return(0); } /* CFC0 */ static fastcall int mips64_exec_CFC0(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_cp0_exec_cfc0(cpu,rt,rd); return(0); } /* CTC0 */ static fastcall int mips64_exec_CTC0(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_cp0_exec_ctc0(cpu,rt,rd); return(0); } /* DADDIU */ static fastcall int mips64_exec_DADDIU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); cpu->gpr[rt] = cpu->gpr[rs] + val; return(0); } /* DADDU: rd = rs + rt */ static fastcall int mips64_exec_DADDU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rs] + cpu->gpr[rt]; return(0); } /* DIV */ static fastcall int mips64_exec_DIV(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); cpu->lo = (m_int32_t)cpu->gpr[rs] / (m_int32_t)cpu->gpr[rt]; cpu->hi = (m_int32_t)cpu->gpr[rs] % (m_int32_t)cpu->gpr[rt]; cpu->lo = sign_extend(cpu->lo,32); cpu->hi = sign_extend(cpu->hi,32); return(0); } /* DIVU */ static fastcall int mips64_exec_DIVU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); if (cpu->gpr[rt] == 0) return(0); cpu->lo = (m_uint32_t)cpu->gpr[rs] / (m_uint32_t)cpu->gpr[rt]; cpu->hi = (m_uint32_t)cpu->gpr[rs] % (m_uint32_t)cpu->gpr[rt]; cpu->lo = sign_extend(cpu->lo,32); cpu->hi = sign_extend(cpu->hi,32); return(0); } /* DMFC0 */ static fastcall int mips64_exec_DMFC0(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_cp0_exec_dmfc0(cpu,rt,rd); return(0); } /* DMFC1 */ static fastcall int mips64_exec_DMFC1(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_exec_dmfc1(cpu,rt,rd); return(0); } /* DMTC0 */ static fastcall int mips64_exec_DMTC0(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_cp0_exec_dmtc0(cpu,rt,rd); return(0); } /* DMTC1 */ static fastcall int mips64_exec_DMTC1(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_exec_dmtc1(cpu,rt,rd); return(0); } /* DSLL */ static fastcall int mips64_exec_DSLL(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); cpu->gpr[rd] = cpu->gpr[rt] << sa; return(0); } /* DSLL32 */ static fastcall int mips64_exec_DSLL32(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); cpu->gpr[rd] = cpu->gpr[rt] << (32 + sa); return(0); } /* DSLLV */ static fastcall int mips64_exec_DSLLV(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rt] << (cpu->gpr[rs] & 0x3f); return(0); } /* DSRA */ static fastcall int mips64_exec_DSRA(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); cpu->gpr[rd] = (m_int64_t)cpu->gpr[rt] >> sa; return(0); } /* DSRA32 */ static fastcall int mips64_exec_DSRA32(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); cpu->gpr[rd] = (m_int64_t)cpu->gpr[rt] >> (32 + sa); return(0); } /* DSRAV */ static fastcall int mips64_exec_DSRAV(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = (m_int64_t)cpu->gpr[rt] >> (cpu->gpr[rs] & 0x3f); return(0); } /* DSRL */ static fastcall int mips64_exec_DSRL(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); cpu->gpr[rd] = cpu->gpr[rt] >> sa; return(0); } /* DSRL32 */ static fastcall int mips64_exec_DSRL32(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); cpu->gpr[rd] = cpu->gpr[rt] >> (32 + sa); return(0); } /* DSRLV */ static fastcall int mips64_exec_DSRLV(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rt] >> (cpu->gpr[rs] & 0x3f); return(0); } /* DSUBU */ static fastcall int mips64_exec_DSUBU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rs] - cpu->gpr[rt]; return(0); } /* ERET */ static fastcall int mips64_exec_ERET(cpu_mips_t *cpu,mips_insn_t insn) { mips64_exec_eret(cpu); return(1); } /* J */ static fastcall int mips64_exec_J(cpu_mips_t *cpu,mips_insn_t insn) { u_int instr_index = bits(insn,0,25); m_uint64_t new_pc; /* compute the new pc */ new_pc = cpu->pc & ~((1 << 28) - 1); new_pc |= instr_index << 2; /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* set the new pc */ cpu->pc = new_pc; return(1); } /* JAL */ static fastcall int mips64_exec_JAL(cpu_mips_t *cpu,mips_insn_t insn) { u_int instr_index = bits(insn,0,25); m_uint64_t new_pc; /* compute the new pc */ new_pc = cpu->pc & ~((1 << 28) - 1); new_pc |= instr_index << 2; /* set the return address (instruction after the delay slot) */ cpu->gpr[MIPS_GPR_RA] = cpu->pc + 8; /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* set the new pc */ cpu->pc = new_pc; return(1); } /* JALR */ static fastcall int mips64_exec_JALR(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rd = bits(insn,11,15); m_uint64_t new_pc; /* set the return pc (instruction after the delay slot) in GPR[rd] */ cpu->gpr[rd] = cpu->pc + 8; /* get the new pc */ new_pc = cpu->gpr[rs]; /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* set the new pc */ cpu->pc = new_pc; return(1); } /* JR */ static fastcall int mips64_exec_JR(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); m_uint64_t new_pc; /* get the new pc */ new_pc = cpu->gpr[rs]; /* exec the instruction in the delay slot */ mips64_exec_bdslot(cpu); /* set the new pc */ cpu->pc = new_pc; return(1); } /* LB (Load Byte) */ static fastcall int mips64_exec_LB(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LB,base,offset,rt,TRUE); return(0); } /* LBU (Load Byte Unsigned) */ static fastcall int mips64_exec_LBU(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LBU,base,offset,rt,TRUE); return(0); } /* LD (Load Double-Word) */ static fastcall int mips64_exec_LD(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LD,base,offset,rt,TRUE); return(0); } /* LDC1 (Load Double-Word to Coprocessor 1) */ static fastcall int mips64_exec_LDC1(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int ft = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LDC1,base,offset,ft,TRUE); return(0); } /* LDL (Load Double-Word Left) */ static fastcall int mips64_exec_LDL(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LDL,base,offset,rt,TRUE); return(0); } /* LDR (Load Double-Word Right) */ static fastcall int mips64_exec_LDR(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LDR,base,offset,rt,TRUE); return(0); } /* LH (Load Half-Word) */ static fastcall int mips64_exec_LH(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LH,base,offset,rt,TRUE); return(0); } /* LHU (Load Half-Word Unsigned) */ static fastcall int mips64_exec_LHU(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LHU,base,offset,rt,TRUE); return(0); } /* LI (virtual) */ static fastcall int mips64_exec_LI(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int imm = bits(insn,0,15); cpu->gpr[rt] = sign_extend(imm,16); return(0); } /* LL (Load Linked) */ static fastcall int mips64_exec_LL(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LL,base,offset,rt,TRUE); return(0); } /* LUI */ static fastcall int mips64_exec_LUI(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int imm = bits(insn,0,15); cpu->gpr[rt] = sign_extend(imm,16) << 16; return(0); } /* LW (Load Word) */ static fastcall int mips64_exec_LW(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LW,base,offset,rt,TRUE); return(0); } /* LWL (Load Word Left) */ static fastcall int mips64_exec_LWL(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LWL,base,offset,rt,TRUE); return(0); } /* LWR (Load Word Right) */ static fastcall int mips64_exec_LWR(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LWR,base,offset,rt,TRUE); return(0); } /* LWU (Load Word Unsigned) */ static fastcall int mips64_exec_LWU(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_LWU,base,offset,rt,TRUE); return(0); } /* MFC0 */ static fastcall int mips64_exec_MFC0(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_cp0_exec_mfc0(cpu,rt,rd); return(0); } /* MFC1 */ static fastcall int mips64_exec_MFC1(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_exec_mfc1(cpu,rt,rd); return(0); } /* MFHI */ static fastcall int mips64_exec_MFHI(cpu_mips_t *cpu,mips_insn_t insn) { int rd = bits(insn,11,15); if (rd) cpu->gpr[rd] = cpu->hi; return(0); } /* MFLO */ static fastcall int mips64_exec_MFLO(cpu_mips_t *cpu,mips_insn_t insn) { int rd = bits(insn,11,15); if (rd) cpu->gpr[rd] = cpu->lo; return(0); } /* MOVE (virtual instruction, real: ADDU) */ static fastcall int mips64_exec_MOVE(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rd = bits(insn,11,15); cpu->gpr[rd] = sign_extend(cpu->gpr[rs],32); return(0); } /* MOVZ */ static fastcall int mips64_exec_MOVZ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); if (!cpu->gpr[rt]) cpu->gpr[rd] = cpu->gpr[rs]; return(0); } /* MTC0 */ static fastcall int mips64_exec_MTC0(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_cp0_exec_mtc0(cpu,rt,rd); return(0); } /* MTC1 */ static fastcall int mips64_exec_MTC1(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_exec_mtc1(cpu,rt,rd); return(0); } /* MTHI */ static fastcall int mips64_exec_MTHI(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); cpu->hi = cpu->gpr[rs]; return(0); } /* MTLO */ static fastcall int mips64_exec_MTLO(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); cpu->lo = cpu->gpr[rs]; return(0); } /* MUL */ static fastcall int mips64_exec_MUL(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_int32_t val; /* note: after this instruction, HI/LO regs are undefined */ val = (m_int32_t)cpu->gpr[rs] * (m_int32_t)cpu->gpr[rt]; cpu->gpr[rd] = sign_extend(val,32); return(0); } /* MULT */ static fastcall int mips64_exec_MULT(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); m_int64_t val; val = (m_int64_t)(m_int32_t)cpu->gpr[rs]; val *= (m_int64_t)(m_int32_t)cpu->gpr[rt]; cpu->lo = sign_extend(val,32); cpu->hi = sign_extend(val >> 32,32); return(0); } /* MULTU */ static fastcall int mips64_exec_MULTU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); m_uint64_t val; val = (m_uint64_t)(m_uint32_t)cpu->gpr[rs]; val *= (m_uint64_t)(m_uint32_t)cpu->gpr[rt]; cpu->lo = sign_extend(val,32); cpu->hi = sign_extend(val >> 32,32); return(0); } /* NOP */ static fastcall int mips64_exec_NOP(cpu_mips_t *cpu,mips_insn_t insn) { return(0); } /* NOR */ static fastcall int mips64_exec_NOR(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = ~(cpu->gpr[rs] | cpu->gpr[rt]); return(0); } /* OR */ static fastcall int mips64_exec_OR(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rs] | cpu->gpr[rt]; return(0); } /* ORI */ static fastcall int mips64_exec_ORI(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); cpu->gpr[rt] = cpu->gpr[rs] | imm; return(0); } /* PREF */ static fastcall int mips64_exec_PREF(cpu_mips_t *cpu,mips_insn_t insn) { return(0); } /* PREFI */ static fastcall int mips64_exec_PREFI(cpu_mips_t *cpu,mips_insn_t insn) { return(0); } /* SB (Store Byte) */ static fastcall int mips64_exec_SB(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SB,base,offset,rt,FALSE); return(0); } /* SC (Store Conditional) */ static fastcall int mips64_exec_SC(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SC,base,offset,rt,TRUE); return(0); } /* SD (Store Double-Word) */ static fastcall int mips64_exec_SD(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SD,base,offset,rt,FALSE); return(0); } /* SDL (Store Double-Word Left) */ static fastcall int mips64_exec_SDL(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SDL,base,offset,rt,FALSE); return(0); } /* SDR (Store Double-Word Right) */ static fastcall int mips64_exec_SDR(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SDR,base,offset,rt,FALSE); return(0); } /* SDC1 (Store Double-Word from Coprocessor 1) */ static fastcall int mips64_exec_SDC1(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int ft = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SDC1,base,offset,ft,FALSE); return(0); } /* SH (Store Half-Word) */ static fastcall int mips64_exec_SH(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SH,base,offset,rt,FALSE); return(0); } /* SLL */ static fastcall int mips64_exec_SLL(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); m_uint32_t res; res = (m_uint32_t)cpu->gpr[rt] << sa; cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SLLV */ static fastcall int mips64_exec_SLLV(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_uint32_t res; res = (m_uint32_t)cpu->gpr[rt] << (cpu->gpr[rs] & 0x1f); cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SLT */ static fastcall int mips64_exec_SLT(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); if ((m_int64_t)cpu->gpr[rs] < (m_int64_t)cpu->gpr[rt]) cpu->gpr[rd] = 1; else cpu->gpr[rd] = 0; return(0); } /* SLTI */ static fastcall int mips64_exec_SLTI(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_int64_t val = sign_extend(imm,16); if ((m_int64_t)cpu->gpr[rs] < val) cpu->gpr[rt] = 1; else cpu->gpr[rt] = 0; return(0); } /* SLTIU */ static fastcall int mips64_exec_SLTIU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); if (cpu->gpr[rs] < val) cpu->gpr[rt] = 1; else cpu->gpr[rt] = 0; return(0); } /* SLTU */ static fastcall int mips64_exec_SLTU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); if (cpu->gpr[rs] < cpu->gpr[rt]) cpu->gpr[rd] = 1; else cpu->gpr[rd] = 0; return(0); } /* SRA */ static fastcall int mips64_exec_SRA(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); m_int32_t res; res = (m_int32_t)cpu->gpr[rt] >> sa; cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SRAV */ static fastcall int mips64_exec_SRAV(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_int32_t res; res = (m_int32_t)cpu->gpr[rt] >> (cpu->gpr[rs] & 0x1f); cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SRL */ static fastcall int mips64_exec_SRL(cpu_mips_t *cpu,mips_insn_t insn) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); m_uint32_t res; res = (m_uint32_t)cpu->gpr[rt] >> sa; cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SRLV */ static fastcall int mips64_exec_SRLV(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_uint32_t res; res = (m_uint32_t)cpu->gpr[rt] >> (cpu->gpr[rs] & 0x1f); cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SUB */ static fastcall int mips64_exec_SUB(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_uint32_t res; /* TODO: Exception handling */ res = (m_uint32_t)cpu->gpr[rs] - (m_uint32_t)cpu->gpr[rt]; cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SUBU */ static fastcall int mips64_exec_SUBU(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); m_uint32_t res; res = (m_uint32_t)cpu->gpr[rs] - (m_uint32_t)cpu->gpr[rt]; cpu->gpr[rd] = sign_extend(res,32); return(0); } /* SW (Store Word) */ static fastcall int mips64_exec_SW(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SW,base,offset,rt,FALSE); return(0); } /* SWL (Store Word Left) */ static fastcall int mips64_exec_SWL(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SWL,base,offset,rt,FALSE); return(0); } /* SWR (Store Word Right) */ static fastcall int mips64_exec_SWR(cpu_mips_t *cpu,mips_insn_t insn) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_exec_memop2(cpu,MIPS_MEMOP_SWR,base,offset,rt,FALSE); return(0); } /* SYNC */ static fastcall int mips64_exec_SYNC(cpu_mips_t *cpu,mips_insn_t insn) { return(0); } /* SYSCALL */ static fastcall int mips64_exec_SYSCALL(cpu_mips_t *cpu,mips_insn_t insn) { mips64_exec_syscall(cpu); return(1); } /* TEQ (Trap if Equal) */ static fastcall int mips64_exec_TEQ(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); if (unlikely(cpu->gpr[rs] == cpu->gpr[rt])) { mips64_trigger_trap_exception(cpu); return(1); } return(0); } /* TEQI (Trap if Equal Immediate) */ static fastcall int mips64_exec_TEQI(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); if (unlikely(cpu->gpr[rs] == val)) { mips64_trigger_trap_exception(cpu); return(1); } return(0); } /* TLBP */ static fastcall int mips64_exec_TLBP(cpu_mips_t *cpu,mips_insn_t insn) { mips64_cp0_exec_tlbp(cpu); return(0); } /* TLBR */ static fastcall int mips64_exec_TLBR(cpu_mips_t *cpu,mips_insn_t insn) { mips64_cp0_exec_tlbr(cpu); return(0); } /* TLBWI */ static fastcall int mips64_exec_TLBWI(cpu_mips_t *cpu,mips_insn_t insn) { mips64_cp0_exec_tlbwi(cpu); return(0); } /* TLBWR */ static fastcall int mips64_exec_TLBWR(cpu_mips_t *cpu,mips_insn_t insn) { mips64_cp0_exec_tlbwr(cpu); return(0); } /* XOR */ static fastcall int mips64_exec_XOR(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rs] ^ cpu->gpr[rt]; return(0); } /* XORI */ static fastcall int mips64_exec_XORI(cpu_mips_t *cpu,mips_insn_t insn) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); cpu->gpr[rt] = cpu->gpr[rs] ^ imm; return(0); } /* MIPS instruction array */ static struct mips64_insn_exec_tag mips64_exec_tags[] = { { "li" , mips64_exec_LI , 0xffe00000 , 0x24000000, 1, 16 }, { "move" , mips64_exec_MOVE , 0xfc1f07ff , 0x00000021, 1, 15 }, { "b" , mips64_exec_B , 0xffff0000 , 0x10000000, 0, 10 }, { "bal" , mips64_exec_BAL , 0xffff0000 , 0x04110000, 0, 10 }, { "beqz" , mips64_exec_BEQZ , 0xfc1f0000 , 0x10000000, 0, 9 }, { "bnez" , mips64_exec_BNEZ , 0xfc1f0000 , 0x14000000, 0, 9 }, { "add" , mips64_exec_ADD , 0xfc0007ff , 0x00000020, 1, 3 }, { "addi" , mips64_exec_ADDI , 0xfc000000 , 0x20000000, 1, 6 }, { "addiu" , mips64_exec_ADDIU , 0xfc000000 , 0x24000000, 1, 6 }, { "addu" , mips64_exec_ADDU , 0xfc0007ff , 0x00000021, 1, 3 }, { "and" , mips64_exec_AND , 0xfc0007ff , 0x00000024, 1, 3 }, { "andi" , mips64_exec_ANDI , 0xfc000000 , 0x30000000, 1, 5 }, { "beq" , mips64_exec_BEQ , 0xfc000000 , 0x10000000, 0, 8 }, { "beql" , mips64_exec_BEQL , 0xfc000000 , 0x50000000, 0, 8 }, { "bgez" , mips64_exec_BGEZ , 0xfc1f0000 , 0x04010000, 0, 9 }, { "bgezal" , mips64_exec_BGEZAL , 0xfc1f0000 , 0x04110000, 0, 9 }, { "bgezall", mips64_exec_BGEZALL , 0xfc1f0000 , 0x04130000, 0, 9 }, { "bgezl" , mips64_exec_BGEZL , 0xfc1f0000 , 0x04030000, 0, 9 }, { "bgtz" , mips64_exec_BGTZ , 0xfc1f0000 , 0x1c000000, 0, 9 }, { "bgtzl" , mips64_exec_BGTZL , 0xfc1f0000 , 0x5c000000, 0, 9 }, { "blez" , mips64_exec_BLEZ , 0xfc1f0000 , 0x18000000, 0, 9 }, { "blezl" , mips64_exec_BLEZL , 0xfc1f0000 , 0x58000000, 0, 9 }, { "bltz" , mips64_exec_BLTZ , 0xfc1f0000 , 0x04000000, 0, 9 }, { "bltzal" , mips64_exec_BLTZAL , 0xfc1f0000 , 0x04100000, 0, 9 }, { "bltzall", mips64_exec_BLTZALL , 0xfc1f0000 , 0x04120000, 0, 9 }, { "bltzl" , mips64_exec_BLTZL , 0xfc1f0000 , 0x04020000, 0, 9 }, { "bne" , mips64_exec_BNE , 0xfc000000 , 0x14000000, 0, 8 }, { "bnel" , mips64_exec_BNEL , 0xfc000000 , 0x54000000, 0, 8 }, { "break" , mips64_exec_BREAK , 0xfc00003f , 0x0000000d, 1, 0 }, { "cache" , mips64_exec_CACHE , 0xfc000000 , 0xbc000000, 1, 2 }, { "cfc0" , mips64_exec_CFC0 , 0xffe007ff , 0x40400000, 1, 18 }, { "ctc0" , mips64_exec_CTC0 , 0xffe007ff , 0x40600000, 1, 18 }, { "daddiu" , mips64_exec_DADDIU , 0xfc000000 , 0x64000000, 1, 5 }, { "daddu" , mips64_exec_DADDU , 0xfc0007ff , 0x0000002d, 1, 3 }, { "div" , mips64_exec_DIV , 0xfc00ffff , 0x0000001a, 1, 17 }, { "divu" , mips64_exec_DIVU , 0xfc00ffff , 0x0000001b, 1, 17 }, { "dmfc0" , mips64_exec_DMFC0 , 0xffe007f8 , 0x40200000, 1, 18 }, { "dmfc1" , mips64_exec_DMFC1 , 0xffe007ff , 0x44200000, 1, 19 }, { "dmtc0" , mips64_exec_DMTC0 , 0xffe007f8 , 0x40a00000, 1, 18 }, { "dmtc1" , mips64_exec_DMTC1 , 0xffe007ff , 0x44a00000, 1, 19 }, { "dsll" , mips64_exec_DSLL , 0xffe0003f , 0x00000038, 1, 7 }, { "dsll32" , mips64_exec_DSLL32 , 0xffe0003f , 0x0000003c, 1, 7 }, { "dsllv" , mips64_exec_DSLLV , 0xfc0007ff , 0x00000014, 1, 4 }, { "dsra" , mips64_exec_DSRA , 0xffe0003f , 0x0000003b, 1, 7 }, { "dsra32" , mips64_exec_DSRA32 , 0xffe0003f , 0x0000003f, 1, 7 }, { "dsrav" , mips64_exec_DSRAV , 0xfc0007ff , 0x00000017, 1, 4 }, { "dsrl" , mips64_exec_DSRL , 0xffe0003f , 0x0000003a, 1, 7 }, { "dsrl32" , mips64_exec_DSRL32 , 0xffe0003f , 0x0000003e, 1, 7 }, { "dsrlv" , mips64_exec_DSRLV , 0xfc0007ff , 0x00000016, 1, 4 }, { "dsubu" , mips64_exec_DSUBU , 0xfc0007ff , 0x0000002f, 1, 3 }, { "eret" , mips64_exec_ERET , 0xffffffff , 0x42000018, 0, 1 }, { "j" , mips64_exec_J , 0xfc000000 , 0x08000000, 0, 11 }, { "jal" , mips64_exec_JAL , 0xfc000000 , 0x0c000000, 0, 11 }, { "jalr" , mips64_exec_JALR , 0xfc1f003f , 0x00000009, 0, 15 }, { "jr" , mips64_exec_JR , 0xfc1ff83f , 0x00000008, 0, 13 }, { "lb" , mips64_exec_LB , 0xfc000000 , 0x80000000, 1, 2 }, { "lbu" , mips64_exec_LBU , 0xfc000000 , 0x90000000, 1, 2 }, { "ld" , mips64_exec_LD , 0xfc000000 , 0xdc000000, 1, 2 }, { "ldc1" , mips64_exec_LDC1 , 0xfc000000 , 0xd4000000, 1, 3 }, { "ldl" , mips64_exec_LDL , 0xfc000000 , 0x68000000, 1, 2 }, { "ldr" , mips64_exec_LDR , 0xfc000000 , 0x6c000000, 1, 2 }, { "lh" , mips64_exec_LH , 0xfc000000 , 0x84000000, 1, 2 }, { "lhu" , mips64_exec_LHU , 0xfc000000 , 0x94000000, 1, 2 }, { "ll" , mips64_exec_LL , 0xfc000000 , 0xc0000000, 1, 2 }, { "lui" , mips64_exec_LUI , 0xffe00000 , 0x3c000000, 1, 16 }, { "lw" , mips64_exec_LW , 0xfc000000 , 0x8c000000, 1, 2 }, { "lwl" , mips64_exec_LWL , 0xfc000000 , 0x88000000, 1, 2 }, { "lwr" , mips64_exec_LWR , 0xfc000000 , 0x98000000, 1, 2 }, { "lwu" , mips64_exec_LWU , 0xfc000000 , 0x9c000000, 1, 2 }, { "mfc0" , mips64_exec_MFC0 , 0xffe007ff , 0x40000000, 1, 18 }, { "mfc0_1" , mips64_exec_CFC0 , 0xffe007ff , 0x40000001, 1, 19 }, { "mfc1" , mips64_exec_MFC1 , 0xffe007ff , 0x44000000, 1, 19 }, { "mfhi" , mips64_exec_MFHI , 0xffff07ff , 0x00000010, 1, 14 }, { "mflo" , mips64_exec_MFLO , 0xffff07ff , 0x00000012, 1, 14 }, { "movz" , mips64_exec_MOVZ , 0xfc0007ff , 0x0000000a, 1, 3 }, { "mtc0" , mips64_exec_MTC0 , 0xffe007ff , 0x40800000, 1, 18 }, { "mtc1" , mips64_exec_MTC1 , 0xffe007ff , 0x44800000, 1, 19 }, { "mthi" , mips64_exec_MTHI , 0xfc1fffff , 0x00000011, 1, 13 }, { "mtlo" , mips64_exec_MTLO , 0xfc1fffff , 0x00000013, 1, 13 }, { "mul" , mips64_exec_MUL , 0xfc0007ff , 0x70000002, 1, 4 }, { "mult" , mips64_exec_MULT , 0xfc00ffff , 0x00000018, 1, 17 }, { "multu" , mips64_exec_MULTU , 0xfc00ffff , 0x00000019, 1, 17 }, { "nop" , mips64_exec_NOP , 0xffffffff , 0x00000000, 1, 1 }, { "nor" , mips64_exec_NOR , 0xfc0007ff , 0x00000027, 1, 3 }, { "or" , mips64_exec_OR , 0xfc0007ff , 0x00000025, 1, 3 }, { "ori" , mips64_exec_ORI , 0xfc000000 , 0x34000000, 1, 5 }, { "pref" , mips64_exec_PREF , 0xfc000000 , 0xcc000000, 1, 0 }, { "prefi" , mips64_exec_PREFI , 0xfc0007ff , 0x4c00000f, 1, 0 }, { "sb" , mips64_exec_SB , 0xfc000000 , 0xa0000000, 1, 2 }, { "sc" , mips64_exec_SC , 0xfc000000 , 0xe0000000, 1, 2 }, { "sd" , mips64_exec_SD , 0xfc000000 , 0xfc000000, 1, 2 }, { "sdc1" , mips64_exec_SDC1 , 0xfc000000 , 0xf4000000, 1, 3 }, { "sdl" , mips64_exec_SDL , 0xfc000000 , 0xb0000000, 1, 2 }, { "sdr" , mips64_exec_SDR , 0xfc000000 , 0xb4000000, 1, 2 }, { "sh" , mips64_exec_SH , 0xfc000000 , 0xa4000000, 1, 2 }, { "sll" , mips64_exec_SLL , 0xffe0003f , 0x00000000, 1, 7 }, { "sllv" , mips64_exec_SLLV , 0xfc0007ff , 0x00000004, 1, 4 }, { "slt" , mips64_exec_SLT , 0xfc0007ff , 0x0000002a, 1, 3 }, { "slti" , mips64_exec_SLTI , 0xfc000000 , 0x28000000, 1, 5 }, { "sltiu" , mips64_exec_SLTIU , 0xfc000000 , 0x2c000000, 1, 5 }, { "sltu" , mips64_exec_SLTU , 0xfc0007ff , 0x0000002b, 1, 3 }, { "sra" , mips64_exec_SRA , 0xffe0003f , 0x00000003, 1, 7 }, { "srav" , mips64_exec_SRAV , 0xfc0007ff , 0x00000007, 1, 4 }, { "srl" , mips64_exec_SRL , 0xffe0003f , 0x00000002, 1, 7 }, { "srlv" , mips64_exec_SRLV , 0xfc0007ff , 0x00000006, 1, 4 }, { "sub" , mips64_exec_SUB , 0xfc0007ff , 0x00000022, 1, 3 }, { "subu" , mips64_exec_SUBU , 0xfc0007ff , 0x00000023, 1, 3 }, { "sw" , mips64_exec_SW , 0xfc000000 , 0xac000000, 1, 2 }, { "swl" , mips64_exec_SWL , 0xfc000000 , 0xa8000000, 1, 2 }, { "swr" , mips64_exec_SWR , 0xfc000000 , 0xb8000000, 1, 2 }, { "sync" , mips64_exec_SYNC , 0xfffff83f , 0x0000000f, 1, 1 }, { "syscall", mips64_exec_SYSCALL , 0xfc00003f , 0x0000000c, 1, 1 }, { "teq" , mips64_exec_TEQ , 0xfc00003f , 0x00000034, 1, 17 }, { "teqi" , mips64_exec_TEQI , 0xfc1f0000 , 0x040c0000, 1, 20 }, { "tlbp" , mips64_exec_TLBP , 0xffffffff , 0x42000008, 1, 1 }, { "tlbr" , mips64_exec_TLBR , 0xffffffff , 0x42000001, 1, 1 }, { "tlbwi" , mips64_exec_TLBWI , 0xffffffff , 0x42000002, 1, 1 }, { "tlbwr" , mips64_exec_TLBWR , 0xffffffff , 0x42000006, 1, 1 }, { "xor" , mips64_exec_XOR , 0xfc0007ff , 0x00000026, 1, 3 }, { "xori" , mips64_exec_XORI , 0xfc000000 , 0x38000000, 1, 5 }, { "unknown", mips64_exec_unknown , 0x00000000 , 0x00000000, 1, 0 }, { NULL , NULL , 0x00000000 , 0x00000000, 1, 0 }, }; #endif dynamips-0.2.14/unstable/mips64_exec.h000066400000000000000000000021451241034141600175340ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __MIPS64_EXEC_H__ #define __MIPS64_EXEC_H__ #include "utils.h" /* MIPS instruction recognition */ struct mips64_insn_exec_tag { char *name; fastcall int (*exec)(cpu_mips_t *,mips_insn_t); m_uint32_t mask,value; int delay_slot; int instr_type; m_uint64_t count; }; /* Initialize instruction lookup table */ void mips64_exec_create_ilt(void); /* Dump statistics */ void mips64_dump_stats(cpu_mips_t *cpu); /* Dump an instruction */ int mips64_dump_insn(char *buffer,size_t buf_size,size_t insn_name_size, m_uint64_t pc,mips_insn_t instruction); /* Dump an instruction block */ void mips64_dump_insn_block(cpu_mips_t *cpu,m_uint64_t pc,u_int count, size_t insn_name_size); /* Single-step execution */ fastcall void mips64_exec_single_step(cpu_mips_t *cpu,mips_insn_t instruction); /* Execute a page */ fastcall int mips64_exec_page(cpu_mips_t *cpu); /* Run MIPS code in step-by-step mode */ void *mips64_exec_run_cpu(cpu_gen_t *cpu); #endif dynamips-0.2.14/unstable/mips64_jit.c000066400000000000000000000341671241034141600174020ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * MIPS64 JIT compiler. */ #include #include #include #include #include #include #include #include #include #include "sbox.h" #include "cpu.h" #include "device.h" #include "tcb.h" #include "mips64.h" #include "mips64_cp0.h" #include "mips64_exec.h" #include "mips64_jit.h" #include "insn_lookup.h" #include "memory.h" #include "ptask.h" #include MIPS64_ARCH_INC_FILE #define DEBUG_JIT_SHARED 0 #if DEBUG_BLOCK_TIMESTAMP static volatile m_uint64_t jit_jiffies = 0; #endif /* MIPS jump instructions for block scan */ struct mips64_insn_jump mips64_insn_jumps[] = { { "b" , 0xffff0000, 0x10000000, 16, 1 }, { "bal" , 0xffff0000, 0x04110000, 16, 1 }, { "beq" , 0xfc000000, 0x10000000, 16, 1 }, { "beql" , 0xfc000000, 0x50000000, 16, 1 }, { "bgez" , 0xfc1f0000, 0x04010000, 16, 1 }, { "bgezl" , 0xfc1f0000, 0x04030000, 16, 1 }, { "bgezal" , 0xfc1f0000, 0x04110000, 16, 1 }, { "bgezall" , 0xfc1f0000, 0x04130000, 16, 1 }, { "bgtz" , 0xfc1f0000, 0x1c000000, 16, 1 }, { "bgtzl" , 0xfc1f0000, 0x5c000000, 16, 1 }, { "blez" , 0xfc1f0000, 0x18000000, 16, 1 }, { "blezl" , 0xfc1f0000, 0x58000000, 16, 1 }, { "bltz" , 0xfc1f0000, 0x04000000, 16, 1 }, { "bltzl" , 0xfc1f0000, 0x04020000, 16, 1 }, { "bltzal" , 0xfc1f0000, 0x04100000, 16, 1 }, { "bltzall" , 0xfc1f0000, 0x04120000, 16, 1 }, { "bne" , 0xfc000000, 0x14000000, 16, 1 }, { "bnel" , 0xfc000000, 0x54000000, 16, 1 }, { "j" , 0xfc000000, 0x08000000, 26, 0 }, { NULL , 0x00000000, 0x00000000, 0, 0 }, }; /* Instruction Lookup Table */ static insn_lookup_t *ilt = NULL; static void *mips64_jit_get_insn(int index) { return(&mips64_insn_tags[index]); } static int mips64_jit_chk_lo(struct mips64_insn_tag *tag,int value) { return((value & tag->mask) == (tag->value & 0xFFFF)); } static int mips64_jit_chk_hi(struct mips64_insn_tag *tag,int value) { return((value & (tag->mask >> 16)) == (tag->value >> 16)); } /* Destroy instruction lookup table */ static void destroy_ilt(void) { assert(ilt); ilt_destroy(ilt); ilt = NULL; } /* Initialize instruction lookup table */ void mips64_jit_create_ilt(void) { int i,count; for(i=0,count=0;mips64_insn_tags[i].emit;i++) count++; ilt = ilt_create("mips64j",count, (ilt_get_insn_cbk_t)mips64_jit_get_insn, (ilt_check_cbk_t)mips64_jit_chk_lo, (ilt_check_cbk_t)mips64_jit_chk_hi); atexit(destroy_ilt); } /* Initialize the JIT structure */ int mips64_jit_init(cpu_mips_t *cpu) { if (tsg_bind_cpu(cpu->gen) == -1) return(-1); return(cpu_jit_init(cpu->gen, MIPS_JIT_VIRT_HASH_SIZE, MIPS_JIT_PHYS_HASH_SIZE)); } /* Flush the JIT */ u_int mips64_jit_flush(cpu_mips_t *cpu,u_int threshold) { /* TO FIX / ENHANCE */ return(tsg_remove_single_desc(cpu->gen)); } /* Shutdown the JIT */ void mips64_jit_shutdown(cpu_mips_t *cpu) { cpu_jit_shutdown(cpu->gen); } /* Find the JIT code emitter for the specified MIPS instruction */ static struct mips64_insn_tag *insn_tag_find(mips_insn_t ins) { struct mips64_insn_tag *tag = NULL; int index; index = ilt_lookup(ilt,ins); tag = mips64_jit_get_insn(index); return tag; } /* Check if the specified MIPS instruction is a jump */ _unused static struct mips64_insn_jump *insn_jump_find(mips_insn_t ins) { struct mips64_insn_jump *jump = NULL; int i; for(i=0;mips64_insn_jumps[i].name;i++) if ((ins & mips64_insn_jumps[i].mask) == mips64_insn_jumps[i].value) { jump = &mips64_insn_jumps[i]; break; } return(jump); } /* Fetch a MIPS instruction */ static forced_inline mips_insn_t insn_fetch(cpu_tc_t *tc) { return(vmtoh32(((mips_insn_t *)tc->target_code)[tc->trans_pos])); } /* Emit a breakpoint if necessary */ #if BREAKPOINT_ENABLE static void insn_emit_breakpoint(cpu_mips_t *cpu,cpu_tc_t *tc) { m_uint64_t pc; int i; pc = tc->vaddr + ((tc->trans_pos-1) << 2); for(i=0;ibreakpoints[i]) { mips64_emit_breakpoint(tc); break; } } #endif /* BREAKPOINT_ENABLE */ /* Check if an instruction is in a delay slot or not */ int mips64_jit_is_delay_slot(cpu_tc_t *tc,m_uint64_t pc) { struct mips64_insn_tag *tag; m_uint32_t offset,insn; offset = (pc - tc->vaddr) >> 2; if (!offset) return(FALSE); /* Fetch the previous instruction to determine if it is a jump */ insn = vmtoh32(((m_uint32_t *)tc->target_code)[offset-1]); tag = insn_tag_find(insn); assert(tag != NULL); return(!tag->delay_slot); } /* Fetch a MIPS instruction and emit corresponding translated code */ struct mips64_insn_tag *mips64_jit_fetch_and_emit(cpu_mips_t *cpu, cpu_tc_t *tc, int delay_slot) { struct mips64_insn_tag *tag; mips_insn_t code; code = insn_fetch(tc); tag = insn_tag_find(code); assert(tag); /* Branch-delay slot is in another page: slow exec */ if ((tc->trans_pos == (MIPS_INSN_PER_PAGE-1)) && !tag->delay_slot) { tc->jit_insn_ptr[tc->trans_pos] = tc->jit_ptr; mips64_set_pc(tc,tc->vaddr + (tc->trans_pos << 2)); mips64_emit_single_step(tc,code); mips64_jit_tcb_push_epilog(tc); tc->trans_pos++; return tag; } if (delay_slot && !tag->delay_slot) { mips64_emit_invalid_delay_slot(tc); return NULL; } if (!delay_slot) tc->jit_insn_ptr[tc->trans_pos] = tc->jit_ptr; if (delay_slot != 2) tc->trans_pos++; #if DEBUG_INSN_PERF_CNT mips64_inc_perf_counter(tc); #endif if (!delay_slot) { /* Check for IRQs + Increment count register before jumps */ if (!tag->delay_slot) { mips64_inc_cp0_count_reg(tc); mips64_check_pending_irq(tc); } } #if BREAKPOINT_ENABLE if (cpu->breakpoints_enabled) insn_emit_breakpoint(cpu,tc); #endif tag->emit(cpu,tc,code); return tag; } /* Add end of JIT block */ static void mips64_jit_tcb_add_end(cpu_tc_t *tc) { mips64_set_pc(tc,tc->vaddr+(tc->trans_pos<<2)); mips64_jit_tcb_push_epilog(tc); } /* Record a patch to apply in a compiled block */ int mips64_jit_tcb_record_patch(cpu_mips_t *cpu,cpu_tc_t *tc, u_char *jit_ptr,m_uint64_t vaddr) { struct insn_patch *patch; patch = tc_record_patch(cpu->gen,tc,jit_ptr,vaddr); return((patch != NULL) ? 0 : -1); } /* Apply all patches */ static int mips64_jit_tcb_apply_patches(cpu_mips_t *cpu,cpu_tc_t *tc) { tc_apply_patches(tc,mips64_jit_tcb_set_patch); return(0); } /* Adjust the JIT buffer if its size is not sufficient */ static int mips64_jit_tcb_adjust_buffer(cpu_mips_t *cpu,cpu_tc_t *tc) { return(tc_adjust_jit_buffer(cpu->gen,tc,mips64_jit_tcb_set_jump)); } /* Produce translated code for a page. If this fails, use non-compiled mode */ static cpu_tc_t *mips64_jit_tcb_translate(cpu_mips_t *cpu,cpu_tb_t *tb) { struct mips64_insn_tag *tag; cpu_tc_t *tc; /* The page is not shared, we have to compile it */ tc = tc_alloc(cpu->gen,tb->vaddr,tb->exec_state); if (tc == NULL) return NULL; tc->target_code = tb->target_code; tc->trans_pos = 0; /* Emit native code for each instruction */ while(tc->trans_pos < MIPS_INSN_PER_PAGE) { if (unlikely(!(tag = mips64_jit_fetch_and_emit(cpu,tc,0)))) { cpu_log(cpu->gen,"JIT", "unable to fetch instruction (VA=0x%8.8llx,exec_state=%u).\n", tb->vaddr,tb->exec_state); return NULL; } #if DEBUG_BLOCK_COMPILE cpu_log(cpu->gen,"JIT","Page 0x%8.8llx: emitted tag 0x%8.8x/0x%8.8x\n", tb->vaddr,tag->mask,tag->value); #endif if (mips64_jit_tcb_adjust_buffer(cpu,tc) == -1) return NULL; } mips64_jit_tcb_add_end(tc); mips64_jit_tcb_apply_patches(cpu,tc); tc_free_patches(tc); tc->target_code = NULL; return tc; } /* Compile a MIPS instruction page */ static cpu_tb_t * mips64_jit_tcb_compile(cpu_mips_t *cpu,m_uint64_t vaddr,m_uint32_t exec_state) { cpu_tb_t *tb; cpu_tc_t *tc; m_uint64_t page_addr; mips_insn_t *mips_code; m_uint32_t phys_page; page_addr = vaddr & MIPS_MIN_PAGE_MASK; /* * Get the mips code address from the host point of view. * If there is an error (TLB,...), we return directly to the main loop. */ mips_code = cpu->mem_op_ifetch(cpu,page_addr); if (unlikely(cpu->translate(cpu,page_addr,&phys_page))) return NULL; /* Create a new translation block */ if (!(tb = tb_alloc(cpu->gen,page_addr,exec_state))) return NULL; tb->vaddr = page_addr; tb->exec_state = exec_state; tb->phys_page = phys_page; tb->phys_hash = mips64_jit_get_phys_hash(phys_page); tb->virt_hash = mips64_jit_get_virt_hash(page_addr); tb->target_code = mips_code; tb->checksum = tsg_checksum_page(tb->target_code,VM_PAGE_SIZE); /* Check if we can share this page with another virtual CPU */ if (tc_find_shared(cpu->gen,tb) == TSG_LOOKUP_SHARED) { #if DEBUG_JIT_SHARED cpu_log(cpu->gen,"JIT","Page 0x%8.8llx is shared (ref_count=%u)\n", tb->vaddr,tb->tc->ref_count); #endif return tb; } /* The page is not shared, we have to compile it */ tc = mips64_jit_tcb_translate(cpu,tb); if (tc != NULL) { tc->target_code = tb->target_code; tc->trans_pos = 0; tb_enable(cpu->gen,tb); tc_register(cpu->gen,tb,tc); } else { tb->flags |= TB_FLAG_NOJIT; tb_enable(cpu->gen,tb); } return tb; } /* Run a compiled MIPS instruction block */ static forced_inline void mips64_jit_tcb_run(cpu_mips_t *cpu,cpu_tb_t *tb) { #if DEBUG_SYM_TREE struct symbol *sym = NULL; int mark = FALSE; #endif if (unlikely(cpu->pc & 0x03)) { fprintf(stderr,"mips64_jit_tcb_run: Invalid PC 0x%llx.\n",cpu->pc); mips64_dump_regs(cpu->gen); mips64_tlb_dump(cpu->gen); cpu_stop(cpu->gen); return; } #if DEBUG_SYM_TREE if (cpu->sym_trace && cpu->sym_tree) { if ((sym = mips64_sym_lookup(cpu,cpu->pc)) != NULL) { cpu_log(cpu,"mips64_jit_tcb_run(start)", "%s (PC=0x%llx) RA = 0x%llx\na0=0x%llx, " "a1=0x%llx, a2=0x%llx, a3=0x%llx\n", sym->name, cpu->pc, cpu->gpr[MIPS_GPR_RA], cpu->gpr[MIPS_GPR_A0], cpu->gpr[MIPS_GPR_A1], cpu->gpr[MIPS_GPR_A2], cpu->gpr[MIPS_GPR_A3]); mark = TRUE; } } #endif /* Execute JIT compiled code */ mips64_jit_tcb_exec(cpu,tb); #if DEBUG_SYM_TREE if (mark) { cpu_log(cpu,"mips64_jit_tcb_run(end)","%s, v0 = 0x%llx\n", sym->name,cpu->gpr[MIPS_GPR_V0]); } #endif } /* Execute compiled MIPS code */ void *mips64_jit_run_cpu(cpu_gen_t *gen) { cpu_mips_t *cpu = CPU_MIPS64(gen); pthread_t timer_irq_thread; cpu_tb_t *tb; m_uint32_t hv,hp; m_uint32_t phys_page; int timer_irq_check = 0; if (pthread_create(&timer_irq_thread,NULL, (void *)mips64_timer_irq_run,cpu)) { fprintf(stderr, "VM '%s': unable to create Timer IRQ thread for CPU%u.\n", cpu->vm->name,gen->id); cpu_stop(cpu->gen); return NULL; } gen->cpu_thread_running = TRUE; cpu_exec_loop_set(gen); start_cpu: gen->idle_count = 0; for(;;) { if (unlikely(gen->state != CPU_STATE_RUNNING)) { /* * We are paused/halted, so free the TCB/TCD in order to allow * reallocation of exec pages for other vCPUs. */ cpu_jit_tcb_flush_all(cpu->gen); break; } #if DEBUG_BLOCK_PERF_CNT cpu->perf_counter++; #endif /* Handle virtual idle loop */ if (unlikely(cpu->pc == cpu->idle_pc)) { if (++gen->idle_count == gen->idle_max) { cpu_idle_loop(gen); gen->idle_count = 0; } } /* Handle the virtual CPU clock */ if (++timer_irq_check == cpu->timer_irq_check_itv) { timer_irq_check = 0; if (cpu->timer_irq_pending && !cpu->irq_disable) { mips64_trigger_timer_irq(cpu); mips64_trigger_irq(cpu); cpu->timer_irq_pending--; } } /* Get the JIT block corresponding to PC register */ hv = mips64_jit_get_virt_hash(cpu->pc); tb = gen->tb_virt_hash[hv]; if (unlikely(!tb) || unlikely(!mips64_jit_tcb_match(cpu,tb))) { /* slow lookup: try to find the page by physical address */ cpu->translate(cpu,cpu->pc,&phys_page); hp = mips64_jit_get_phys_hash(phys_page); for(tb=gen->tb_phys_hash[hp];tb;tb=tb->phys_next) if (mips64_jit_tcb_match(cpu,tb)) goto tb_found; /* the TB doesn't exist, compile the page */ tb = mips64_jit_tcb_compile(cpu,cpu->pc,cpu->exec_state); if (unlikely(!tb)) { fprintf(stderr, "VM '%s': unable to compile block for CPU%u PC=0x%llx\n", cpu->vm->name,gen->id,cpu->pc); cpu_stop(gen); break; } tb_found: /* update the virtual hash table */ gen->tb_virt_hash[hv] = tb; } #if DEBUG_BLOCK_TIMESTAMP tb->tm_last_use = jit_jiffies++; #endif tb->acc_count++; cpu->current_tb = tb; if (unlikely(tb->flags & TB_FLAG_NOTRANS)) mips64_exec_page(cpu); else mips64_jit_tcb_run(cpu,tb); } /* Check regularly if the CPU has been restarted */ while(gen->cpu_thread_running) { gen->seq_state++; switch(gen->state) { case CPU_STATE_RUNNING: printf("VM %s: starting CPU!\n",cpu->vm->name); goto start_cpu; case CPU_STATE_HALTED: gen->cpu_thread_running = FALSE; pthread_join(timer_irq_thread,NULL); return NULL; } /* CPU is paused */ usleep(200000); } return NULL; } dynamips-0.2.14/unstable/mips64_jit.h000066400000000000000000000113001241034141600173670ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * MIPS64 JIT compiler. */ #ifndef __MIPS64_JIT_H__ #define __MIPS64_JIT_H__ #include "utils.h" #include "sbox.h" #include "tcb.h" /* Size of hash for virtual address lookup */ #define MIPS_JIT_VIRT_HASH_BITS 16 #define MIPS_JIT_VIRT_HASH_MASK ((1 << MIPS_JIT_VIRT_HASH_BITS) - 1) #define MIPS_JIT_VIRT_HASH_SIZE (1 << MIPS_JIT_VIRT_HASH_BITS) /* Size of hash for physical lookup */ #define MIPS_JIT_PHYS_HASH_BITS 16 #define MIPS_JIT_PHYS_HASH_MASK ((1 << MIPS_JIT_PHYS_HASH_BITS) - 1) #define MIPS_JIT_PHYS_HASH_SIZE (1 << MIPS_JIT_PHYS_HASH_BITS) /* MIPS instruction recognition */ struct mips64_insn_tag { int (*emit)(cpu_mips_t *cpu,cpu_tc_t *,mips_insn_t); m_uint32_t mask,value; int delay_slot; }; /* MIPS jump instruction (for block scan) */ struct mips64_insn_jump { char *name; m_uint32_t mask,value; int offset_bits; int relative; }; /* Get the JIT instruction pointer in a translated block */ static forced_inline u_char *mips64_jit_tc_get_host_ptr(cpu_tc_t *tc,m_uint64_t vaddr) { m_uint32_t offset; offset = ((m_uint32_t)vaddr & MIPS_MIN_PAGE_IMASK) >> 2; return(tc->jit_insn_ptr[offset]); } /* Check if the specified address belongs to the specified block */ static forced_inline int mips64_jit_tcb_local_addr(cpu_tc_t *tc,m_uint64_t vaddr, u_char **jit_addr) { if ((vaddr & MIPS_MIN_PAGE_MASK) == tc->vaddr) { *jit_addr = mips64_jit_tc_get_host_ptr(tc,vaddr); return(1); } return(0); } /* Check if PC register matches the compiled block virtual address */ static forced_inline int mips64_jit_tcb_match(cpu_mips_t *cpu,cpu_tb_t *tb) { m_uint64_t vpage; vpage = cpu->pc & MIPS_MIN_PAGE_MASK; return((tb->vaddr == vpage) && (tb->exec_state == cpu->exec_state)); } /* Compute the hash index for the specified virtual address */ static forced_inline m_uint32_t mips64_jit_get_virt_hash(m_uint64_t vaddr) { m_uint32_t page_hash; page_hash = sbox_u32(vaddr >> MIPS_MIN_PAGE_SHIFT); return((page_hash ^ (page_hash >> 12)) & MIPS_JIT_VIRT_HASH_MASK); } /* Compute the hash index for the specified physical page */ static forced_inline m_uint32_t mips64_jit_get_phys_hash(m_uint32_t phys_page) { m_uint32_t page_hash; page_hash = sbox_u32(phys_page); return((page_hash ^ (page_hash >> 12)) & MIPS_JIT_PHYS_HASH_MASK); } /* Find a JIT block matching a physical page */ static inline cpu_tb_t * mips64_jit_find_by_phys_page(cpu_mips_t *cpu,m_uint32_t phys_page) { m_uint32_t page_hash = mips64_jit_get_phys_hash(phys_page); cpu_tb_t *tb; for(tb=cpu->gen->tb_phys_hash[page_hash];tb;tb=tb->phys_next) if (tb->phys_page == phys_page) return tb; return NULL; } /* Check if there are pending IRQ */ extern void mips64_check_pending_irq(cpu_tc_t *tc); /* Initialize instruction lookup table */ void mips64_jit_create_ilt(void); /* Initialize the JIT structure */ int mips64_jit_init(cpu_mips_t *cpu); /* Flush the JIT */ u_int mips64_jit_flush(cpu_mips_t *cpu,u_int threshold); /* Shutdown the JIT */ void mips64_jit_shutdown(cpu_mips_t *cpu); /* Check if an instruction is in a delay slot or not */ int mips64_jit_is_delay_slot(cpu_tc_t *tc,m_uint64_t pc); /* Fetch a MIPS instruction and emit corresponding translated code */ struct mips64_insn_tag *mips64_jit_fetch_and_emit(cpu_mips_t *cpu, cpu_tc_t *tc, int delay_slot); /* Record a patch to apply in a compiled block */ int mips64_jit_tcb_record_patch(cpu_mips_t *cpu,cpu_tc_t *tc, u_char *jit_ptr,m_uint64_t vaddr); /* Mark a block as containing self-modifying code */ void mips64_jit_mark_smc(cpu_mips_t *cpu,cpu_tb_t *tb); /* Free an instruction block */ void mips64_jit_tcb_free(cpu_mips_t *cpu,cpu_tb_t *tb,int list_removal); /* Execute compiled MIPS code */ void *mips64_jit_run_cpu(cpu_gen_t *cpu); /* Set the Pointer Counter (PC) register */ void mips64_set_pc(cpu_tc_t *tc,m_uint64_t new_pc); /* Set the Return Address (RA) register */ void mips64_set_ra(cpu_tc_t *tc,m_uint64_t ret_pc); /* Single-step operation */ void mips64_emit_single_step(cpu_tc_t *tc,mips_insn_t insn); /* Virtual Breakpoint */ void mips64_emit_breakpoint(cpu_tc_t *tc); /* Emit unhandled instruction code */ int mips64_emit_invalid_delay_slot(cpu_tc_t *tc); /* * Increment count register and trigger the timer IRQ if value in compare * register is the same. */ void mips64_inc_cp0_count_reg(cpu_tc_t *tc); /* Increment the number of executed instructions (performance debugging) */ void mips64_inc_perf_counter(cpu_tc_t *tc); #endif dynamips-0.2.14/unstable/mips64_mem.c000066400000000000000000000402771241034141600173710ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * Patched by Jeremy Grossmann for the GNS3 project (www.gns3.net) */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "tcb.h" #include "mips64_jit.h" #include "dynamips.h" #include "memory.h" #include "device.h" /* Undefined access */ static void mips64_undef_access(cpu_mips_t *cpu,m_uint64_t vaddr, u_int op_code,u_int op_type,u_int op_size, m_uint64_t *data) { if (op_type == MTS_READ) *data = 0; if (cpu->gen->undef_mem_handler != NULL) { if (cpu->gen->undef_mem_handler(cpu->gen,vaddr,op_size,op_type,data)) return; } #if DEBUG_MTS_ACC_U if (op_type == MTS_READ) cpu_log(cpu->gen, "MTS","read access to undefined address 0x%llx at " "pc=0x%llx (size=%u)\n",vaddr,cpu->pc,op_size); else cpu_log(cpu->gen, "MTS","write access to undefined address 0x%llx at " "pc=0x%llx, value=0x%8.8llx (size=%u)\n", vaddr,cpu->pc,*data,op_size); #endif } /* TLB error: throw the appropriate exception */ static void mips64_tlb_error(cpu_mips_t *cpu,m_uint64_t vaddr,int error, u_int op_code,u_int op_type,u_int op_size) { if (op_code == MIPS_MEMOP_LOOKUP) return; #if DEBUG_MTS_ACC_T cpu_log(cpu->gen,"MTS", "TLB exception for address 0x%llx at pc=0x%llx " "(%s access, size=%u)\n", vaddr,cpu->pc,(op_type == MTS_READ) ? "read":"write",op_size); #if MEMLOG_ENABLE memlog_dump(cpu->gen); #endif #endif /* Set context/xcontext and entryhi registers */ mips64_prepare_tlb_exception(cpu,vaddr); switch(error) { case MTS_ACC_T: /* Cannot be anying but _LOOKUP due to test above, but sensible */ /* to leave the test in for future reference */ if (op_code != MIPS_MEMOP_LOOKUP) { /* If the IOS tries to access memory at addr 0x0, it is probably */ /* a reload. Shut the vm down, otherwise 100% cpu and livespin */ if (vaddr == 0) { cpu_log(cpu->gen, "MTS","TLB exception suggests RELOAD for address 0x%llx at pc=0x%llx " "(%s access, size=%u)\n", vaddr,cpu->pc,(op_type == MTS_READ) ? "read":"write",op_size); vm_stop(cpu->vm); } } break; case MIPS_TLB_LOOKUP_MISS: if (op_type == MTS_READ) mips64_tlb_miss_exception(cpu,MIPS_CP0_CAUSE_TLB_LOAD,vaddr); else mips64_tlb_miss_exception(cpu,MIPS_CP0_CAUSE_TLB_SAVE,vaddr); break; case MIPS_TLB_LOOKUP_INVALID: if (op_type == MTS_READ) mips64_gen_exception_badva(cpu,MIPS_CP0_CAUSE_TLB_LOAD,vaddr); else mips64_gen_exception_badva(cpu,MIPS_CP0_CAUSE_TLB_SAVE,vaddr); break; case MIPS_TLB_LOOKUP_MOD: mips64_gen_exception_badva(cpu,MIPS_CP0_CAUSE_TLB_MOD,vaddr); break; } cpu_exec_loop_enter(cpu->gen); } /* Address Error: throw the exception */ static void mips64_ae_error(cpu_mips_t *cpu,m_uint64_t vaddr, u_int op_code,u_int op_type,u_int op_size) { if (op_code == MIPS_MEMOP_LOOKUP) return; #if DEBUG_MTS_ACC_AE cpu_log(cpu->gen, "MTS","AE exception for address 0x%llx at pc=0x%llx (%s access)\n", vaddr,cpu->pc,(op_type == MTS_READ) ? "read":"write"); #endif if (op_type == MTS_READ) mips64_gen_exception_badva(cpu,MIPS_CP0_CAUSE_ADDR_LOAD,vaddr); else mips64_gen_exception_badva(cpu,MIPS_CP0_CAUSE_ADDR_SAVE,vaddr); cpu_exec_loop_enter(cpu->gen); } /* === MTS for 64-bit address space ======================================= */ #define MTS_ADDR_SIZE 64 #define MTS_NAME(name) mts64_##name #define MTS_NAME_UP(name) MTS64_##name #define MTS_PROTO(name) mips64_mts64_##name #define MTS_PROTO_UP(name) MIPS64_MTS64_##name #include "mips_mts.c" /* === MTS for 32-bit address space ======================================= */ #define MTS_ADDR_SIZE 32 #define MTS_NAME(name) mts32_##name #define MTS_NAME_UP(name) MTS32_##name #define MTS_PROTO(name) mips64_mts32_##name #define MTS_PROTO_UP(name) MIPS64_MTS32_##name #include "mips_mts.c" /* === Specific operations for MTS64 ====================================== */ /* MTS64 slow lookup */ static mts64_entry_t * mips64_mts64_slow_lookup(cpu_mips_t *cpu,m_uint64_t vaddr, u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data, mts64_entry_t *alt_entry) { m_uint32_t hash_bucket,zone,sub_zone,cca; mts64_entry_t *entry; mts_map_t map; int tlb_res; hash_bucket = MTS64_HASH(vaddr); entry = &cpu->mts_u.mts64_cache[hash_bucket]; zone = vaddr >> 40; #if DEBUG_MTS_STATS cpu->mts_misses++; #endif switch(zone) { case 0x000000: /* xkuseg */ case 0x400000: /* xksseg */ case 0xc00000: /* xkseg */ /* trigger TLB exception if no matching entry found */ tlb_res = mips64_cp0_tlb_lookup(cpu,vaddr,op_type,&map); if (tlb_res != MIPS_TLB_LOOKUP_OK) goto err_tlb; if (!(entry = mips64_mts64_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return(entry); case 0xffffff: sub_zone = (vaddr >> 29) & 0x7FF; switch(sub_zone) { case 0x7fc: /* ckseg0 */ map.vaddr = vaddr & MIPS_MIN_PAGE_MASK; map.paddr = map.vaddr - 0xFFFFFFFF80000000ULL; map.offset = vaddr & MIPS_MIN_PAGE_IMASK; map.cached = TRUE; map.flags = 0; if (!(entry = mips64_mts64_map(cpu,op_type,&map, entry,alt_entry))) goto err_undef; return(entry); case 0x7fd: /* ckseg1 */ map.vaddr = vaddr & MIPS_MIN_PAGE_MASK; map.paddr = map.vaddr - 0xFFFFFFFFA0000000ULL; map.offset = vaddr & MIPS_MIN_PAGE_IMASK; map.cached = FALSE; map.flags = 0; if (!(entry = mips64_mts64_map(cpu,op_type,&map, entry,alt_entry))) goto err_undef; return(entry); case 0x7fe: /* cksseg */ case 0x7ff: /* ckseg3 */ /* trigger TLB exception if no matching entry found */ tlb_res = mips64_cp0_tlb_lookup(cpu,vaddr,op_type,&map); if (tlb_res != MIPS_TLB_LOOKUP_OK) goto err_tlb; if (!(entry = mips64_mts64_map(cpu,op_type, &map,entry,alt_entry))) goto err_undef; return(entry); default: /* Invalid zone: generate Address Error (AE) exception */ goto err_address; } break; /* xkphys */ case 0x800000: case 0x880000: case 0x900000: case 0x980000: case 0xa00000: case 0xa80000: case 0xb00000: case 0xb80000: cca = (vaddr >> MIPS64_XKPHYS_CCA_SHIFT) & 0x03; map.cached = mips64_cca_cached(cca); map.vaddr = vaddr & MIPS_MIN_PAGE_MASK; map.paddr = (vaddr & MIPS64_XKPHYS_PHYS_MASK); map.paddr &= MIPS_MIN_PAGE_MASK; map.offset = vaddr & MIPS_MIN_PAGE_IMASK; map.flags = 0; if (!(entry = mips64_mts64_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return(entry); default: /* Invalid zone: generate Address Error (AE) exception */ goto err_address; } err_undef: mips64_undef_access(cpu,vaddr,op_code,op_type,op_size,data); return NULL; err_address: mips64_ae_error(cpu,vaddr,op_code,op_type,op_size); return NULL; err_tlb: mips64_tlb_error(cpu,vaddr,tlb_res,op_code,op_type,op_size); return NULL; } /* MTS64 access */ static forced_inline void *mips64_mts64_access(cpu_mips_t *cpu,m_uint64_t vaddr, u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data) { mts64_entry_t *entry,alt_entry; m_uint32_t hash_bucket; m_iptr_t haddr; u_int dev_id; int wr_catch; #if MEMLOG_ENABLE /* Record the memory access */ memlog_rec_access(cpu->gen,vaddr,*data,op_size,op_type); #endif hash_bucket = MTS64_HASH(vaddr); entry = &cpu->mts_u.mts64_cache[hash_bucket]; #if DEBUG_MTS_STATS cpu->mts_lookups++; #endif /* Copy-On-Write for sparse device or Read-only page ? */ wr_catch = (op_type == MTS_WRITE) && (entry->flags & MTS_FLAG_WRCATCH); /* Slow lookup if nothing found in cache */ if (unlikely(((vaddr & MIPS_MIN_PAGE_MASK) != entry->gvpa) || wr_catch)) { entry = mips64_mts64_slow_lookup(cpu,vaddr,op_code,op_size,op_type, data,&alt_entry); if (!entry) return NULL; if (entry->flags & MTS_FLAG_DEV) { dev_id = (entry->hpa & MTS_DEVID_MASK) >> MTS_DEVID_SHIFT; haddr = entry->hpa & MTS_DEVOFF_MASK; return(dev_access_fast(cpu->gen,dev_id,haddr,op_size,op_type,data)); } } /* Invalidate JIT code for written pages */ if ((op_type == MTS_WRITE) && (entry->flags & MTS_FLAG_EXEC)) mips64_mts64_invalidate_tcb(cpu,entry); /* Raw memory access */ haddr = entry->hpa + (vaddr & MIPS_MIN_PAGE_IMASK); #if MEMLOG_ENABLE memlog_update_read(cpu->gen,haddr); #endif return((void *)haddr); } /* MTS64 virtual address to physical page translation */ static fastcall int mips64_mts64_translate(cpu_mips_t *cpu,m_uint64_t vaddr, m_uint32_t *phys_page) { mts64_entry_t *entry,alt_entry; m_uint32_t hash_bucket; m_uint64_t data = 0; hash_bucket = MTS64_HASH(vaddr); entry = &cpu->mts_u.mts64_cache[hash_bucket]; /* Slow lookup if nothing found in cache */ if (unlikely((vaddr & MIPS_MIN_PAGE_MASK) != entry->gvpa)) { entry = mips64_mts64_slow_lookup(cpu,vaddr,MIPS_MEMOP_LOOKUP,4,MTS_READ, &data,&alt_entry); if (!entry) return(-1); } *phys_page = entry->gppa >> MIPS_MIN_PAGE_SHIFT; return(0); } /* === Specific operations for MTS32 ====================================== */ /* MTS32 slow lookup */ static mts32_entry_t * mips64_mts32_slow_lookup(cpu_mips_t *cpu,m_uint64_t vaddr, u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data, mts32_entry_t *alt_entry) { m_uint32_t hash_bucket,zone; mts32_entry_t *entry; mts_map_t map; int tlb_res; hash_bucket = MTS32_HASH(vaddr); entry = &cpu->mts_u.mts32_cache[hash_bucket]; zone = (vaddr >> 29) & 0x7; #if DEBUG_MTS_STATS cpu->mts_misses++; #endif switch(zone) { case 0x00 ... 0x03: /* kuseg */ /* trigger TLB exception if no matching entry found */ tlb_res = mips64_cp0_tlb_lookup(cpu,vaddr,op_type,&map); if (tlb_res != MIPS_TLB_LOOKUP_OK) goto err_tlb; if (!(entry = mips64_mts32_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return(entry); case 0x04: /* kseg0 */ map.vaddr = vaddr & MIPS_MIN_PAGE_MASK; map.paddr = map.vaddr - 0xFFFFFFFF80000000ULL; map.offset = vaddr & MIPS_MIN_PAGE_IMASK; map.cached = TRUE; map.flags = 0; if (!(entry = mips64_mts32_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return(entry); case 0x05: /* kseg1 */ map.vaddr = vaddr & MIPS_MIN_PAGE_MASK; map.paddr = map.vaddr - 0xFFFFFFFFA0000000ULL; map.offset = vaddr & MIPS_MIN_PAGE_IMASK; map.cached = FALSE; map.flags = 0; if (!(entry = mips64_mts32_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return(entry); case 0x06: /* ksseg */ case 0x07: /* kseg3 */ /* trigger TLB exception if no matching entry found */ tlb_res = mips64_cp0_tlb_lookup(cpu,vaddr,op_type,&map); if (tlb_res != MIPS_TLB_LOOKUP_OK) goto err_tlb; if (!(entry = mips64_mts32_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return(entry); } err_undef: mips64_undef_access(cpu,vaddr,op_code,op_type,op_size,data); return NULL; #if 0 err_address: mips64_ae_error(cpu,vaddr,op_code,op_type,op_size); return NULL; #endif err_tlb: mips64_tlb_error(cpu,vaddr,tlb_res,op_code,op_type,op_size); return NULL; } /* MTS32 access */ static forced_inline void *mips64_mts32_access(cpu_mips_t *cpu,m_uint64_t vaddr, u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data) { mts32_entry_t *entry,alt_entry; m_uint32_t hash_bucket; m_iptr_t haddr; u_int dev_id; int wr_catch; #if MEMLOG_ENABLE /* Record the memory access */ memlog_rec_access(cpu->gen,vaddr,*data,op_size,op_type); #endif hash_bucket = MTS32_HASH(vaddr); entry = &cpu->mts_u.mts32_cache[hash_bucket]; #if DEBUG_MTS_STATS cpu->mts_lookups++; #endif /* Copy-On-Write for sparse device or read-only page ? */ wr_catch = (op_type == MTS_WRITE) && (entry->flags & MTS_FLAG_WRCATCH); /* Slow lookup if nothing found in cache */ if (unlikely((((m_uint32_t)vaddr & MIPS_MIN_PAGE_MASK) != entry->gvpa) || wr_catch)) { entry = mips64_mts32_slow_lookup(cpu,vaddr,op_code,op_size,op_type, data,&alt_entry); if (!entry) return NULL; if (entry->flags & MTS_FLAG_DEV) { dev_id = (entry->hpa & MTS_DEVID_MASK) >> MTS_DEVID_SHIFT; haddr = entry->hpa & MTS_DEVOFF_MASK; return(dev_access_fast(cpu->gen,dev_id,haddr,op_size,op_type,data)); } } /* Invalidate JIT code for written pages */ if ((op_type == MTS_WRITE) && (entry->flags & MTS_FLAG_EXEC)) mips64_mts32_invalidate_tcb(cpu,entry); /* Raw memory access */ haddr = entry->hpa + (vaddr & MIPS_MIN_PAGE_IMASK); #if MEMLOG_ENABLE memlog_update_read(cpu->gen,haddr); #endif return((void *)haddr); } /* MTS32 virtual address to physical page translation */ static fastcall int mips64_mts32_translate(cpu_mips_t *cpu,m_uint64_t vaddr, m_uint32_t *phys_page) { mts32_entry_t *entry,alt_entry; m_uint32_t hash_bucket; m_uint64_t data = 0; hash_bucket = MTS32_HASH(vaddr); entry = &cpu->mts_u.mts32_cache[hash_bucket]; /* Slow lookup if nothing found in cache */ if (unlikely(((m_uint32_t)vaddr & MIPS_MIN_PAGE_MASK) != entry->gvpa)) { entry = mips64_mts32_slow_lookup(cpu,vaddr,MIPS_MEMOP_LOOKUP,4,MTS_READ, &data,&alt_entry); if (!entry) return(-1); } *phys_page = entry->gppa >> MIPS_MIN_PAGE_SHIFT; return(0); } /* ======================================================================== */ /* Shutdown MTS subsystem */ void mips64_mem_shutdown(cpu_mips_t *cpu) { if (cpu->mts_shutdown != NULL) cpu->mts_shutdown(cpu); } /* Set the address mode */ int mips64_set_addr_mode(cpu_mips_t *cpu,u_int addr_mode) { if (cpu->addr_mode != addr_mode) { mips64_mem_shutdown(cpu); switch(addr_mode) { case 32: mips64_mts32_init(cpu); mips64_mts32_init_memop_vectors(cpu); break; case 64: mips64_mts64_init(cpu); mips64_mts64_init_memop_vectors(cpu); break; default: fprintf(stderr, "mts_set_addr_mode: internal error (addr_mode=%u)\n", addr_mode); exit(EXIT_FAILURE); } } return(0); } dynamips-0.2.14/unstable/mips64_microcode000077500000000000000000000373241241034141600203400ustar00rootroot00000000000000ELF¿À4= 4 (     ¿À¿À(à(àðŽ'½þÿºÐÿ»Ø'ºÿºèÿ¿ø<¿À'Zà<¿À'{0@'½þÿºÐÿ»Ø'ºÿºèÿ¿ø<¿À'Zà<¿À'{0@'½þÿºÐÿ»Ø'ºÿºèÿ¿ø<¿À'Zà<¿À'{È@'½þÿºÐÿ»Ø'ºÿºèÿ¿ø<¿À'Zà<¿À'{0@'½þÿºÐÿ»Ø'ºÿºèÿ¿ø<¿À'Zà<¿À'{0@ÿ¡ÿ¢ÿ£ÿ¤ ÿ¥(ÿ¦0ÿ§8ÿ¨@ÿ©HÿªPÿ«Xÿ¬`ÿ­hÿ®pÿ¯xÿ°€ÿ±ˆÿ²ÿ³˜ÿ´ ÿµ¨ÿ¶°ÿ·¸ÿ¸Àÿ¹Èÿ¼àÿ¾ðÿ¿ø (ÿ¤ÿ¥@`¯¤@h¯¤ @%@ÿ¥@&pÿ¦(@'ðÿ§0  -`ø ¤@„`ߤ(@¤pߥ0@¥ðߤߥ€ ß»Øß¡ߢߣߤ ߥ(ߦ0ß§8ߨ@ß©HߪPß«X߬`ß­hß®p߯xß°€ß±ˆß²ß³˜ß´ ßµ¨ß¶°ß·¸ß¸Àß¹Èß¼àß¾ðß¿øß½èB¿À°¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿À°¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà¿Àà@`à@„`à@hà@„hà@Hà@„Hà@„Xà< % ý< % ­<¿À'Zà€è-g½ÿÀ$ÿù¨è$€@-$(-0-ø < % < % Ýà<€7½@<¿À--(-0-8-@-H-P-X-`-h-p-€-ˆ--˜- -¨-°-¸-À-È-Ð-Ø-à-ð-ÿÇŒƒ <¿À0c|@$B'ØC!ŒY'½ÿÐÿ° ÿ¿( €€-ŒEß¿(ß°  '½0ŒF<¿À$¥" ðð -Ž0c`<¿ÀÞ($¥"(ðð -ŽŽ <¿Àß¿(ß° $¥"8 - ðð'½0Þ0$¥"(ðð - ðìŽ'½ÿØÿ¿ ð‰ -<ð4„B@ðu4Dß¿ ðy'½(Œ‚ '½ÿÈÿ°(ÿ¿0@€€-Ü‚(dBü‚(Ž<ÿÿ4Bý‚ $ðy4„€Ž$Ž,Ž4Ž<Þ(𲯢$<>ú@þ<€b%þß¿0ß°(à'½8<¿À$¥"`ðð$Þ(dc ðþ('½ÿÐ<¿Àÿ° $¥"ˆ€€-ÿ¿(ðv -Ž0B@<¿ÀÞ($¥"(ðð -ŽŽ <¿Àß¿(ß° $¥"8 - ðð'½0Þ0$¥"(ðð -ŽŽ <¿Àß¿(ß° $¥"8 - ðð'½0'½ÿÀÿ° €€-Ž Œ„ÿ²0ÿ±(0’ÿ0Qÿ2$0b@ÿ¿80b@A0b€@G0b@@3<¿À$¥"àðð -ŽŽ <¿À$¥"øðð -<¿À2:ß¿8ß²0ß±(ß° $¥#0 - ðð'½@ð}$þÿðD $<¿À$¥" ðð -Ž0cT`Þ0<¿ÀÞ($¥"(ðð -ŽŽ <¿Àß¿8ß²0ß±(ß° $¥"8 - ðð'½@0‚@ÿî<¿ÀÞ0<¿À$¥"(ðð - ð•Žð}$ýÿðD $<¿À ð‰$¥"Àð}<ÿÿ4„ÿðD $ð…<4„B@ß¿8ß²0ß±(ß° D ! ð'½@€¦À€-€- f$¥€¦Àÿü$càÀ€-¢ ‚€£` 8-à€-¢ b€£`$¥$çæ*@ÿø‡!€-à€‚@(-$¥…!€CT`ÿý$¥à -'½ÿÿ²8ÿ°(ÿ¿hÿ·`ÿ¶XÿµPÿ´Hÿ³@ÿ±0 €-€¥'¢€ÿ¦€ÿ§ˆ¯¢  €-<¿À$W&0$%$s$x$c³0¥ÿðl@ -&‚ ÿùß¿h-ß·`ß¶XßµPß´Hß³@ß²8ß±0ß°(à'½p&‚tW(bt@ sO@ -Tvÿè&¢ @ -E$Bðl¯¢ ð &TuÿÞ&¢ ŒQ$B',ƒ ¯¢ `$…W$…0ðl@ -0B,C `$EW$E0ðl@ -0B,C `$EW$E0ðl@ -0B,C `$EW$E0ðl@ -0B,C `$EW$E0ðl@ -0B,C `$EW$E0ðl@ -0B,C `$EW$E0ðl@ -2#,b @ÿœ$e0 ð $eWðl$% ð &¢ ŒEP à(-$B@ -ðv¯¢ ð &€‚@<¶¬bT$„€‚@ÿü<¶$¬CXŒDX€$ÿÿ$Æÿÿ-<¶ŒâT$c ¢$¥f *@  €ÿö<¶$¬CX$à€‚@<¶¬bT$„€‚@ÿü$<¶¬CXà$'½ÿ¨,‚…ÿ²Hÿ±@ÿ°8à-ÿ¿P€@- €-Àˆ-@§|<¿À$¥&°0-$¯°$¯±,ðð¯²4$ÿÿß¿Pß²Hß±@ß°8à'½X<¿À€$c b!Œd€<¶ŒC ðÈ  --<¶$@¬£LŒ¢P$c¤‚fÿû$„ ðÈ-<¿À$¥&$ðð0-‚@<¶¬bT&‚@ÿü$<¶¬CX ðÈ$ ðÈ$ÿÿ<¶ŒC$ ðÈ'<¶ŒdH0‚€@<ÿÿŒbDŒBà@ÿÃß¿P<ÿÿ4Bÿ ðÈ‚$<¶ŒCD$ ðȬeà ðÈ$€<¶ŒCD ðÈ$b<¿À0-$¥&pðð$ - (-@0-ß¿Pß²Hß±@ß°8 ð€'½X<¶$¬C4 ðÈ-<¿À ðÈ$BH< ðÄŒD  ðÈ$<  ðÈŒB < ðÄŒD  ðÈ$<  ðÈŒB <¶ ðÈŒB$<¶ŒC ðÈ<¶ŒC ðÈ€<¿À$¥&8 -ðð<¶Ž0P`$<¿À$¥&Xðð -ð¬Ž( ðÈ-<¶ ðÈŒB, ðÈ$ 0¥ÿðl - ðÈ-<  ðÈŒB <¿À ðÈŒB(Ø`ø ®4 ðÈ-€0¥ÿ<¶à¬E<<¶à¬E@¢@€<¶¬b<$¥¢T@ÿý¬b<à¬b@$¥¢T@ÿý¬b@àÈ- <¿À'½ÿÈ$¥' -ÿ¿0ÿ±(ððÿ° <¿À$ðð$¥'0<¶<”Œd4B³ß‚ <¶<¿À$¥'Hðð --`ø <¶ŒPD<¿À&€` -$¥'< ðĬC &<¿À` -$¥'˜< ¬C ðÄ&€< ðú¬P <¿À$q' <¶Ž( -ðð (-ð‘Ž(Ž0T@ÿùŽ(<¿À$¥'Àðð -ß¿0$<¶ß±(ß° ¬C4à'½8¿Àø¿Àp¿Àh¿Àø¿ÀX¿Àø¿Àè¿À\¿À¿Àø¿À¿Àø¿Àø¿À„¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿À ¿Àø¿Àø¿Àü¿Àð¿Àø¿Àø¿Àø¿Àø¿Àø¿Àä¿ÀпÀÄ¿À°¿Àø¿Àø¿Àø¿Àø¿À¤¿À¿Àh¿À˜¿ÀX¿ÀØ¿À¿À¿Àø¿Àø¿Àø¿Àø¿ÀH¿À@¿Àø¿Àø¿Àà¿À,¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àà¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿Àø¿ÀX %s PC = 0x%x, Cause = 0x%x, Status Reg = 0x%x Warning: System Call in BD Slot. Cache Exception: *** Software Interrupt 0 *** *** Software Interrupt 1 *** *** Unknown IRQ *** Stacked Cause Reg = 0x%x, Stacked Status Reg = 0x%x Current Int Cause = 0x%x, Current Int Status = 0x%x *** CPU Interrupt ****** TLB Modification Exception ****** TLB (Load/Fetch) Exception ****** TLB (Store) Exception ****** Address Error (Load/Fetch) Exception ****** Address Error (Store) Exception ****** Bus Error (Fetch) Exception ****** Bus Error (Load) Exception ****** System Call Exception ****** Break Point Exception ****** Illegal Opcode Exception ****** Coprocessor Unusable Exception ****** Arithmetic Overflow Exception ****** Trap Exception ****** Virtual Coherency (Opcode) Exception ****** Floating Point Exception ****** Reserved General Exception ****** Watch Exception ****** Virtual Coherency (Data) Exception ***(null) ROM: reload requested... ROM: Restarting IOS... trying to read bootvar '%s' trying to set bootvar '%s' unhandled syscall 0x%x at pc=0x%x (a1=0x%x,a2=0x%x,a3=0x%x) ROMMON Emulation Microcode ROMMON emulation microcode. Microcode has started. The microcode need to be upgraded to match your emulator version. IOSBOOTLDRLaunching IOS image at 0x%x... Image returned to ROM. ¿À#h¿À h¿À#€¿À#¨¿À#пÀ#ð¿À$ ¿À$H¿À$p¿À$˜¿À ¿À$¸¿À$Ø¿À%¿À%(¿À%P¿À%h¿À%˜¿À%À¿À%À¿À%À¿À%À¿À%À¿À%À¿À%À¿À%è¿À%À¿À%À¿À%À¿À%À¿À%À¿À%À¿À%À¿À&¿À&ðÿÿÿþ¿À°¿Àà¿ÀÔ¿Àä¿Àô¿À¿À¿À$¿À4¿ÀD¿À°¿À0€ÿÿÿø0¿Àè€ÿÿÿø(¿À €ÿÿÿø8¿ÀÈ€ÿÿÿø0¿À h€ÿÿÿø@¿À ¿À @¿À ˜¿À À€ÿÿÿÿøp¿À¿À¿ÀÈ€ÿÿÿøX¿À°¿ÀØ¿À(¿À8€ÿÿÿø8GCC: (GNU) 4.3.0GCC: (GNU) 4.3.0GCC: (GNU) 4.3.0GCC: (GNU) 4.3.0GCC: (GNU) 4.3.0Agnu.shstrtab.text.rodata.rodata.str1.8.data.bss.reginfo.pdr.comment.gnu.attributes ¿À ¿À 0 2¿À" 2 ¸(¿À'Ø7Ø.   3p8à<8ø`A #include #include #include #include #include #include #include "cpu.h" #include "mips64_jit.h" #include "mips64_nojit_trans.h" /* Set an IRQ */ void mips64_set_irq(cpu_mips_t *cpu,m_uint8_t irq) { m_uint32_t m; m = (1 << (irq + MIPS_CP0_CAUSE_ISHIFT)) & MIPS_CP0_CAUSE_IMASK; MIPS64_IRQ_LOCK(cpu); cpu->irq_cause |= m; MIPS64_IRQ_UNLOCK(cpu); } /* Clear an IRQ */ void mips64_clear_irq(cpu_mips_t *cpu,m_uint8_t irq) { m_uint32_t m; m = (1 << (irq + MIPS_CP0_CAUSE_ISHIFT)) & MIPS_CP0_CAUSE_IMASK; MIPS64_IRQ_LOCK(cpu); cpu->irq_cause &= ~m; MIPS64_IRQ_UNLOCK(cpu); if (!cpu->irq_cause) cpu->irq_pending = 0; } #define EMPTY(func) func { \ fprintf(stderr,"This function should not be called: "#func"\n"); \ abort(); \ } EMPTY(void mips64_jit_tcb_push_epilog(cpu_tc_t *tc)); EMPTY(void mips64_jit_tcb_exec(cpu_mips_t *cpu,cpu_tb_t *tb)); EMPTY(void mips64_set_pc(cpu_tc_t *tc,m_uint64_t new_pc)); EMPTY(void mips64_set_ra(cpu_tc_t *tc,m_uint64_t ret_pc)); EMPTY(void mips64_emit_breakpoint(cpu_tc_t *tc)); EMPTY(void mips64_emit_single_step(cpu_tc_t *tc,mips_insn_t insn)); EMPTY(int mips64_emit_invalid_delay_slot(cpu_tc_t *tc)); EMPTY(void mips64_inc_cp0_count_reg(cpu_tc_t *tc)); EMPTY(void mips64_check_pending_irq(cpu_tc_t *tc)); EMPTY(void mips64_inc_perf_counter(cpu_tc_t *tc)); /* MIPS instruction array */ struct mips64_insn_tag mips64_insn_tags[] = { { NULL, 0, 0, 0 }, }; dynamips-0.2.14/unstable/mips64_nojit_trans.h000066400000000000000000000013601241034141600211400ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * Just an empty JIT template file for architectures not supported by the JIT * code. */ #ifndef __MIPS64_NOJIT_TRANS_H__ #define __MIPS64_NOJIT_TRANS_H__ #include "utils.h" #include "cpu.h" #include "dynamips.h" #define JIT_SUPPORT 0 static inline void mips64_jit_tcb_set_patch(u_char *code,u_char *target) {} static inline void mips64_jit_tcb_set_jump(u_char **instp,u_char *target) {} /* MIPS instruction array */ extern struct mips64_insn_tag mips64_insn_tags[]; /* Push epilog for an x86 instruction block */ void mips64_jit_tcb_push_epilog(cpu_tc_t *tc); /* Execute JIT code */ void mips64_jit_tcb_exec(cpu_mips_t *cpu,cpu_tb_t *tb); #endif dynamips-0.2.14/unstable/mips64_ppc32_trans.c000066400000000000000000002532401241034141600207450ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * JIT engine for 32-bit PowerPC architecture * Copyright (c) 2006, 2007 Zhe Fang (fangzhe@msn.com) */ #include #include #include #include #include #include #include #include "cpu.h" #include "mips64_jit.h" #include "mips64_ppc32_trans.h" #include "mips64_cp0.h" #include "memory.h" /* Macros for CPU structure access */ #define REG_OFFSET(reg) (OFFSET(cpu_mips_t,gpr[(reg)])) #define CP0_REG_OFFSET(c0reg) (OFFSET(cpu_mips_t,cp0.reg[(c0reg)])) #define MEMOP_OFFSET(op) (OFFSET(cpu_mips_t,mem_op_fn[(op)])) #define DECLARE_INSN(name) \ static int mips64_emit_##name(cpu_mips_t *cpu,mips64_jit_tcb_t *b, \ mips_insn_t insn) /* Set an IRQ */ void mips64_set_irq(cpu_mips_t *cpu,m_uint8_t irq) { m_uint32_t m; m = (1 << (irq + MIPS_CP0_CAUSE_ISHIFT)) & MIPS_CP0_CAUSE_IMASK; atomic_or(&cpu->irq_cause,m); } /* Clear an IRQ */ void mips64_clear_irq(cpu_mips_t *cpu,m_uint8_t irq) { m_uint32_t m; m = (1 << (irq + MIPS_CP0_CAUSE_ISHIFT)) & MIPS_CP0_CAUSE_IMASK; atomic_and(&cpu->irq_cause,~m); if (!cpu->irq_cause) cpu->irq_pending = 0; } /* Set the Pointer Counter (PC) register */ void mips64_set_pc(mips64_jit_tcb_t *b,m_uint64_t new_pc) { ppc_load(b->jit_ptr,ppc_r0,new_pc >> 32); ppc_stw(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,pc),ppc_r3); ppc_load(b->jit_ptr,ppc_r0,new_pc & 0xffffffff); ppc_stw(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,pc)+4,ppc_r3); } /* Set the Return Address (RA) register */ void mips64_set_ra(mips64_jit_tcb_t *b,m_uint64_t ret_pc) { ppc_load(b->jit_ptr,ppc_r0,ret_pc >> 32); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(MIPS_GPR_RA),ppc_r3); ppc_load(b->jit_ptr,ppc_r0,ret_pc & 0xffffffff); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(MIPS_GPR_RA)+4,ppc_r3); } /* * Try to branch directly to the specified JIT block without returning to * main loop. */ static void mips64_try_direct_far_jump(cpu_mips_t *cpu,mips64_jit_tcb_t *b, m_uint64_t new_pc) { m_uint64_t new_page; m_uint32_t pc_hash,pc_offset; u_char *test1,*test2,*test3,*test4; new_page = new_pc & MIPS_MIN_PAGE_MASK; pc_offset = ((new_pc & MIPS_MIN_PAGE_IMASK) >> 2) * sizeof(u_char *); pc_hash = mips64_jit_get_pc_hash(new_pc) * sizeof(mips64_jit_tcb_t *); /* Get JIT block info in r4 */ ppc_lwz(b->jit_ptr,ppc_r4,OFFSET(cpu_mips_t,exec_blk_map),ppc_r3); /* Check if offset is too big for a 16-bit immediate value */ if (pc_hash > 0x7fff) ppc_addis(b->jit_ptr,ppc_r4,ppc_r4,ppc_ha16(pc_hash)); ppc_lwz(b->jit_ptr,ppc_r4,ppc_lo16(pc_hash),ppc_r4); /* no JIT block found ? */ ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r4,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); /* r5:r6 = start_pc, r7:r8 = new_page */ ppc_lwz(b->jit_ptr,ppc_r5,OFFSET(mips64_jit_tcb_t,start_pc),ppc_r4); ppc_lwz(b->jit_ptr,ppc_r6,OFFSET(mips64_jit_tcb_t,start_pc)+4,ppc_r4); ppc_load(b->jit_ptr,ppc_r7,new_page >> 32); ppc_load(b->jit_ptr,ppc_r8,new_page & 0xffffffff); /* Check block PC (lower 32-bits first) */ ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r6,ppc_r8); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); /* Check higher bits... */ ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r5,ppc_r7); test3 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); /* Jump to the code */ ppc_lwz(b->jit_ptr,ppc_r4,OFFSET(mips64_jit_tcb_t,jit_insn_ptr),ppc_r4); ppc_lwz(b->jit_ptr,ppc_r12,pc_offset,ppc_r4); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r12,0); test4 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); ppc_mtlr(b->jit_ptr,ppc_r12); ppc_blr(b->jit_ptr); /* Returns to caller... */ ppc_patch(test1,b->jit_ptr); ppc_patch(test2,b->jit_ptr); ppc_patch(test3,b->jit_ptr); ppc_patch(test4,b->jit_ptr); mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); } /* Set Jump */ static void mips64_set_jump(cpu_mips_t *cpu,mips64_jit_tcb_t *b, m_uint64_t new_pc,int local_jump) { int return_to_caller = FALSE; u_char *jump_ptr; if (cpu->sym_trace && !local_jump) return_to_caller = TRUE; if (!return_to_caller && mips64_jit_tcb_local_addr(b,new_pc,&jump_ptr)) { if (jump_ptr) { ppc_emit_jump_code(b->jit_ptr, jump_ptr, 0); } else { /* To check if the target is in a delay slot (previous instruction's delay_slot == 0) */ if (mips64_jit_is_delay_slot(b,new_pc)) { mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); return; } /* When using ppc_patch from mono, the following code will be invalid if cpu->vm->exec_area_size, or MIPS_EXEC_AREA_SIZE > 32, so here's a hacked way - ppc_emit_jump_code has been used to support 32-bit addressing. Also notice that += 16 hack depends on insn_block_record_patch is called for unconditional branch only. */ mips64_jit_tcb_record_patch(b,b->jit_ptr,new_pc); b->jit_ptr += 16; /*ppc_nop(b->jit_ptr); ppc_nop(b->jit_ptr); ppc_nop(b->jit_ptr); ppc_nop(b->jit_ptr);*/ } } else { if (cpu->exec_blk_direct_jump) { /* Block lookup optimization */ mips64_try_direct_far_jump(cpu,b,new_pc); } else { mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); } } } /* Basic C call */ static forced_inline void mips64_emit_basic_c_call(mips64_jit_tcb_t *b,void *f) { ppc_emit_jump_code(b->jit_ptr, (u_char *)f, 1/*linked*/); /* Restore the volatile r3 */ ppc_lwz(b->jit_ptr,ppc_r3,PPC_STACK_DECREMENTER+PPC_STACK_PARAM_OFFSET,ppc_r1); } /* Emit a simple call to a C function without any parameter */ static void mips64_emit_c_call(mips64_jit_tcb_t *b,void *f) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); mips64_emit_basic_c_call(b,f); } /* Single-step operation */ void mips64_emit_single_step(mips64_jit_tcb_t *b,mips_insn_t insn) { ppc_load(b->jit_ptr,ppc_r4,insn); // mips64_emit_basic_c_call(b,mips64_exec_single_step); /* Restore link register */ ppc_lwz(b->jit_ptr,ppc_r0,PPC_STACK_DECREMENTER+PPC_RET_ADDR_OFFSET,ppc_r1); ppc_mtlr(b->jit_ptr,ppc_r0); /* Trick: let callee return directly to the caller */ ppc_emit_jump_code(b->jit_ptr, (u_char *)mips64_exec_single_step, 0); } /* Fast memory operation prototype */ typedef void (*memop_fast_access)(mips64_jit_tcb_t *b,int target); /* Fast LB */ static void mips64_memop_fast_lb(mips64_jit_tcb_t *b,int target) { ppc_lbzx(b->jit_ptr,ppc_r10,ppc_r7,ppc_r8); ppc_extsb(b->jit_ptr,ppc_r10,ppc_r10); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(target),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(target)+4,ppc_r3); } /* Fast LBU */ static void mips64_memop_fast_lbu(mips64_jit_tcb_t *b,int target) { ppc_lbzx(b->jit_ptr,ppc_r10,ppc_r7,ppc_r8); ppc_li(b->jit_ptr,ppc_r0,0); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(target),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(target)+4,ppc_r3); } /* Fast LD */ static void mips64_memop_fast_ld(mips64_jit_tcb_t *b,int target) { ppc_lwzux(b->jit_ptr,ppc_r9,ppc_r7,ppc_r8); ppc_lwz(b->jit_ptr,ppc_r10,4,ppc_r7); ppc_stw(b->jit_ptr,ppc_r9,REG_OFFSET(target),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(target)+4,ppc_r3); } /* Fast LH */ static void mips64_memop_fast_lh(mips64_jit_tcb_t *b,int target) { ppc_lhax(b->jit_ptr,ppc_r10,ppc_r7,ppc_r8); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(target),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(target)+4,ppc_r3); } /* Fast LHU */ static void mips64_memop_fast_lhu(mips64_jit_tcb_t *b,int target) { ppc_lhzx(b->jit_ptr,ppc_r10,ppc_r7,ppc_r8); ppc_li(b->jit_ptr,ppc_r0,0); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(target),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(target)+4,ppc_r3); } /* Fast LW */ static void mips64_memop_fast_lw(mips64_jit_tcb_t *b,int target) { ppc_lwzx(b->jit_ptr,ppc_r10,ppc_r7,ppc_r8); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(target),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(target)+4,ppc_r3); } /* Fast LWU */ static void mips64_memop_fast_lwu(mips64_jit_tcb_t *b,int target) { ppc_lwzx(b->jit_ptr,ppc_r10,ppc_r7,ppc_r8); ppc_li(b->jit_ptr,ppc_r0,0); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(target),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(target)+4,ppc_r3); } /* Fast SB */ static void mips64_memop_fast_sb(mips64_jit_tcb_t *b,int target) { ppc_lwz(b->jit_ptr,ppc_r10,REG_OFFSET(target)+4,ppc_r3); ppc_stbx(b->jit_ptr,ppc_r10,ppc_r7,ppc_r8); } /* Fast SD */ static void mips64_memop_fast_sd(mips64_jit_tcb_t *b,int target) { ppc_lwz(b->jit_ptr,ppc_r9,REG_OFFSET(target),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r10,REG_OFFSET(target)+4,ppc_r3); ppc_stwux(b->jit_ptr,ppc_r9,ppc_r7,ppc_r8); ppc_stw(b->jit_ptr,ppc_r10,4,ppc_r7); } /* Fast SH */ static void mips64_memop_fast_sh(mips64_jit_tcb_t *b,int target) { ppc_lwz(b->jit_ptr,ppc_r10,REG_OFFSET(target)+4,ppc_r3); ppc_sthx(b->jit_ptr,ppc_r10,ppc_r7,ppc_r8); } /* Fast SW */ static void mips64_memop_fast_sw(mips64_jit_tcb_t *b,int target) { ppc_lwz(b->jit_ptr,ppc_r10,REG_OFFSET(target)+4,ppc_r3); ppc_stwx(b->jit_ptr,ppc_r10,ppc_r7,ppc_r8); } /* Fast memory operation (64-bit) */ static void mips64_emit_memop_fast64(mips64_jit_tcb_t *b,int write_op, int opcode,int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { u_char *test1,*test2,*test3,*p_exit; test3 = NULL; /* r4:r5 = GPR[base] + sign-extended offset */ ppc_lwz(b->jit_ptr,ppc_r4,REG_OFFSET(base),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(base)+4,ppc_r3); ppc_addic(b->jit_ptr,ppc_r5,ppc_r5,offset); if (offset & 0x8000 /* < 0 */) ppc_addme(b->jit_ptr,ppc_r4,ppc_r4); else ppc_addze(b->jit_ptr,ppc_r4,ppc_r4); /* r7 = offset in mts cache */ ppc_srwi(b->jit_ptr,ppc_r7,ppc_r5,MTS64_HASH_SHIFT1); ppc_srwi(b->jit_ptr,ppc_r6,ppc_r5,MTS64_HASH_SHIFT2); ppc_xor(b->jit_ptr,ppc_r8,ppc_r7,ppc_r6); ppc_rlwinm(b->jit_ptr,ppc_r7,ppc_r8, MTS64_HASH_BITS+5, 32-(MTS64_HASH_BITS+5), 31-MTS64_HASH_BITS); /* r8 = mts64_cache */ ppc_lwz(b->jit_ptr,ppc_r8,OFFSET(cpu_mips_t,mts_u.mts64_cache),ppc_r3); /* r6 = mts64_entry */ ppc_add(b->jit_ptr,ppc_r6,ppc_r8,ppc_r7); /* r7, r8 are temporary */ /* Compare virtual page address */ ppc_lwz(b->jit_ptr,ppc_r7,OFFSET(mts64_entry_t,gvpa),ppc_r6); ppc_lwz(b->jit_ptr,ppc_r8,OFFSET(mts64_entry_t,gvpa)+4,ppc_r6); /* Compare the high part of the vaddr */ ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r4,ppc_r7); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); /* Compare the low part of the vaddr & MIPS_MIN_PAGE_MASK (vpage) */ ppc_rlwinm(b->jit_ptr,ppc_r0,ppc_r5,0,0,19); ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r0,ppc_r8); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); /* Test if we are writing to a COW page */ if (write_op) { ppc_lwz(b->jit_ptr,ppc_r0,OFFSET(mts64_entry_t,flags),ppc_r6); ppc_mtcrf(b->jit_ptr,0x01,ppc_r0); /* MTS_FLAG_COW is moved to EQ bit of cr7 */ test3 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); } /* r7 = Host Page Address, r8 = offset in page */ ppc_lwz(b->jit_ptr,ppc_r7,OFFSET(mts64_entry_t,hpa),ppc_r6); //ppc_rlwinm(b->jit_ptr,ppc_r8,ppc_r5,0,32-MIPS_MIN_PAGE_SHIFT,31); ppc_andid(b->jit_ptr,ppc_r8,ppc_r5,MIPS_MIN_PAGE_IMASK); /* Memory access */ op_handler(b,target); p_exit = b->jit_ptr; ppc_b(b->jit_ptr,0); /* === Slow lookup === */ ppc_patch(test1,b->jit_ptr); ppc_patch(test2,b->jit_ptr); if (test3) ppc_patch(test3,b->jit_ptr); /* The following codes are copied from mips64_emit_memop */ /* lr = r12 = address of memory function */ ppc_lwz(b->jit_ptr,ppc_r12,MEMOP_OFFSET(opcode),ppc_r3); ppc_mtlr(b->jit_ptr,ppc_r12); /* Save PC for exception handling */ mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); /* r6 = target register */ ppc_li(b->jit_ptr,ppc_r6,target); /* Call memory function */ ppc_blrl(b->jit_ptr); /* Restore the volatile r3 */ ppc_lwz(b->jit_ptr,ppc_r3,PPC_STACK_DECREMENTER+PPC_STACK_PARAM_OFFSET,ppc_r1); ppc_patch(p_exit,b->jit_ptr); } /* Fast memory operation (32-bit) */ static void mips64_emit_memop_fast32(mips64_jit_tcb_t *b,int write_op, int opcode,int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { u_char *test2,*test3,*p_exit; test3 = NULL; /* r5 = GPR[base] + sign-extended offset */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(base)+4,ppc_r3); ppc_addi(b->jit_ptr,ppc_r5,ppc_r5,offset); /* r7 = offset in mts cache */ ppc_srwi(b->jit_ptr,ppc_r7,ppc_r5,MTS32_HASH_SHIFT1); ppc_srwi(b->jit_ptr,ppc_r6,ppc_r5,MTS32_HASH_SHIFT2); ppc_xor(b->jit_ptr,ppc_r8,ppc_r7,ppc_r6); ppc_rlwinm(b->jit_ptr,ppc_r7,ppc_r8, MTS32_HASH_BITS+4, 32-(MTS32_HASH_BITS+4), 31-MTS32_HASH_BITS); /* r8 = mts32_cache */ ppc_lwz(b->jit_ptr,ppc_r8,OFFSET(cpu_mips_t,mts_u.mts32_cache),ppc_r3); /* r6 = mts32_entry */ ppc_add(b->jit_ptr,ppc_r6,ppc_r8,ppc_r7); /* r7, r8 are temporary */ /* Compare virtual page address (vaddr & MIPS_MIN_PAGE_MASK) */ ppc_lwz(b->jit_ptr,ppc_r8,OFFSET(mts32_entry_t,gvpa),ppc_r6); ppc_rlwinm(b->jit_ptr,ppc_r0,ppc_r5,0,0,19); ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r0,ppc_r8); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); /* Test if we are writing to a COW page */ if (write_op) { ppc_lwz(b->jit_ptr,ppc_r0,OFFSET(mts32_entry_t,flags),ppc_r6); ppc_mtcrf(b->jit_ptr,0x01,ppc_r0); /* MTS_FLAG_COW is moved to EQ bit of cr7 */ test3 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); } /* r7 = Host Page Address, r8 = offset in page */ ppc_lwz(b->jit_ptr,ppc_r7,OFFSET(mts32_entry_t,hpa),ppc_r6); //ppc_rlwinm(b->jit_ptr,ppc_r8,ppc_r5,0,32-MIPS_MIN_PAGE_SHIFT,31); ppc_andid(b->jit_ptr,ppc_r8,ppc_r5,MIPS_MIN_PAGE_IMASK); /* Memory access */ op_handler(b,target); p_exit = b->jit_ptr; ppc_b(b->jit_ptr,0); /* === Slow lookup === */ ppc_patch(test2,b->jit_ptr); if (test3) ppc_patch(test3,b->jit_ptr); /* The following codes are copied from mips64_emit_memop */ /* lr = r12 = address of memory function */ ppc_lwz(b->jit_ptr,ppc_r12,MEMOP_OFFSET(opcode),ppc_r3); ppc_mtlr(b->jit_ptr,ppc_r12); /* Save PC for exception handling */ mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); /* r4 = sign extention of r5 */ ppc_srawi(b->jit_ptr,ppc_r4,ppc_r5,31); /* r6 = target register */ ppc_li(b->jit_ptr,ppc_r6,target); /* Call memory function */ ppc_blrl(b->jit_ptr); /* Restore the volatile r3 */ ppc_lwz(b->jit_ptr,ppc_r3,PPC_STACK_DECREMENTER+PPC_STACK_PARAM_OFFSET,ppc_r1); ppc_patch(p_exit,b->jit_ptr); } /* Fast memory operation */ static void mips64_emit_memop_fast(cpu_mips_t *cpu,mips64_jit_tcb_t *b, int write_op,int opcode, int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { switch(cpu->addr_mode) { case 32: mips64_emit_memop_fast32(b,write_op,opcode,base,offset,target, keep_ll_bit,op_handler); break; case 64: mips64_emit_memop_fast64(b,write_op,opcode,base,offset,target, keep_ll_bit,op_handler); break; } } /* Memory operation */ static void mips64_emit_memop(mips64_jit_tcb_t *b,int opcode,int base,int offset, int target,int keep_ll_bit) { /* lr = r12 = address of memory function */ ppc_lwz(b->jit_ptr,ppc_r12,MEMOP_OFFSET(opcode),ppc_r3); ppc_mtlr(b->jit_ptr,ppc_r12); /* Save PC for exception handling */ mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); if (!keep_ll_bit) { ppc_li(b->jit_ptr,ppc_r0,0); ppc_stw(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,ll_bit),ppc_r3); } /* r3 = CPU instance pointer */ /* r4:r5 = GPR[base] + sign-extended offset */ ppc_lwz(b->jit_ptr,ppc_r4,REG_OFFSET(base),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(base)+4,ppc_r3); ppc_addic(b->jit_ptr,ppc_r5,ppc_r5,offset); if (offset & 0x8000 /* < 0 */) ppc_addme(b->jit_ptr,ppc_r4,ppc_r4); else ppc_addze(b->jit_ptr,ppc_r4,ppc_r4); /* r6 = target register */ ppc_li(b->jit_ptr,ppc_r6,target); /* Call memory function */ ppc_blrl(b->jit_ptr); /* Restore the volatile r3 */ ppc_lwz(b->jit_ptr,ppc_r3,PPC_STACK_DECREMENTER+PPC_STACK_PARAM_OFFSET,ppc_r1); } /* Coprocessor Register transfert operation */ static void mips64_emit_cp_xfr_op(mips64_jit_tcb_t *b,int rt,int rd,void *f) { /* update pc */ mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); /* r3 = CPU instance pointer */ /* r4 = gpr */ ppc_load(b->jit_ptr,ppc_r4,rt); /* r5 = cp0 register */ ppc_load(b->jit_ptr,ppc_r5,rd); mips64_emit_basic_c_call(b,f); } /* Virtual Breakpoint */ void mips64_emit_breakpoint(mips64_jit_tcb_t *b) { mips64_emit_c_call(b,mips64_run_breakpoint); } /* Unknown opcode handler */ static asmlinkage void mips64_unknown_opcode(cpu_mips_t *cpu,m_uint32_t opcode) { printf("MIPS64: unhandled opcode 0x%8.8x at 0x%llx (ra=0x%llx)\n", opcode,cpu->pc,cpu->gpr[MIPS_GPR_RA]); mips64_dump_regs(cpu->gen); } /* Emit unhandled instruction code */ static int mips64_emit_unknown(cpu_mips_t *cpu,mips64_jit_tcb_t *b, mips_insn_t opcode) { ppc_load(b->jit_ptr,ppc_r4,opcode); mips64_emit_c_call(b,mips64_unknown_opcode); return(0); } /* Invalid delay slot handler */ static fastcall void mips64_invalid_delay_slot(cpu_mips_t *cpu) { printf("MIPS64: invalid instruction in delay slot at 0x%llx (ra=0x%llx)\n", cpu->pc,cpu->gpr[MIPS_GPR_RA]); mips64_dump_regs(cpu->gen); /* Halt the virtual CPU */ cpu->pc = 0; } /* Emit invalid delay slot */ int mips64_emit_invalid_delay_slot(mips64_jit_tcb_t *b) { /* Restore link register */ ppc_lwz(b->jit_ptr,ppc_r0,PPC_STACK_DECREMENTER+PPC_RET_ADDR_OFFSET,ppc_r1); ppc_mtlr(b->jit_ptr,ppc_r0); /* Trick: let callee return directly to the caller */ ppc_emit_jump_code(b->jit_ptr, (u_char *)mips64_invalid_delay_slot, 0); return(0); } /* * Increment count register and trigger the timer IRQ if value in compare * register is the same. */ void mips64_inc_cp0_count_reg(mips64_jit_tcb_t *b) { ppc_lwz(b->jit_ptr,ppc_r4,OFFSET(cpu_mips_t,cp0_virt_cnt_reg),ppc_r3); ppc_addi(b->jit_ptr,ppc_r4,ppc_r4,1); // addi takes 0 instead of r0 ppc_stw(b->jit_ptr,ppc_r4,OFFSET(cpu_mips_t,cp0_virt_cnt_reg),ppc_r3); } /* Check if there are pending IRQ */ void mips64_check_pending_irq(mips64_jit_tcb_t *b) { u_char *test1; /* Check the pending IRQ flag */ ppc_lwz(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,irq_pending),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r0,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); /* Save PC */ mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); /* Trigger the IRQ */ mips64_emit_basic_c_call(b,mips64_trigger_irq); mips64_jit_tcb_push_epilog(b); ppc_patch(test1,b->jit_ptr); } /* Increment the number of executed instructions (performance debugging) */ void mips64_inc_perf_counter(mips64_jit_tcb_t *b) { ppc_lwz(b->jit_ptr,ppc_r4,OFFSET(cpu_mips_t,perf_counter),ppc_r3); ppc_addi(b->jit_ptr,ppc_r4,ppc_r4,1); // addi takes 0 instead of r0 ppc_stw(b->jit_ptr,ppc_r4,OFFSET(cpu_mips_t,perf_counter),ppc_r3); } /* ADD */ DECLARE_INSN(ADD) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r9,REG_OFFSET(rt)+4,ppc_r3); ppc_addo(b->jit_ptr,ppc_r10,ppc_r8,ppc_r9); /* TODO: Exception handling */ ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); return(0); } /* ADDI */ DECLARE_INSN(ADDI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rs)+4,ppc_r3); ppc_addi(b->jit_ptr,ppc_r9,ppc_r8,imm); /* TODO: Exception handling */ ppc_stw(b->jit_ptr,ppc_r9,REG_OFFSET(rt)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r9,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rt),ppc_r3); return(0); } /* ADDIU */ DECLARE_INSN(ADDIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rs)+4,ppc_r3); ppc_addi(b->jit_ptr,ppc_r9,ppc_r8,imm); ppc_stw(b->jit_ptr,ppc_r9,REG_OFFSET(rt)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r9,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rt),ppc_r3); return(0); } /* ADDU */ DECLARE_INSN(ADDU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r9,REG_OFFSET(rt)+4,ppc_r3); ppc_add(b->jit_ptr,ppc_r10,ppc_r8,ppc_r9); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); return(0); } /* AND */ DECLARE_INSN(AND) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r7,REG_OFFSET(rt),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_and(b->jit_ptr,ppc_r9,ppc_r5,ppc_r7); ppc_and(b->jit_ptr,ppc_r10,ppc_r6,ppc_r8); ppc_stw(b->jit_ptr,ppc_r9,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* ANDI */ DECLARE_INSN(ANDI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_andid(b->jit_ptr,ppc_r8,ppc_r6,imm); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_li(b->jit_ptr,ppc_r0,0); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rt),ppc_r3); return(0); } /* B (virtual) */ DECLARE_INSN(B) { int offset = bits(insn,0,15); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); return(0); } /* BAL (virtual) */ DECLARE_INSN(BAL) { int offset = bits(insn,0,15); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,0); return(0); } /* BEQ */ DECLARE_INSN(BEQ) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. * compare the low 32 bits first (higher probability). */ ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r6,ppc_r8); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r7,REG_OFFSET(rt),ppc_r3); ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r5,ppc_r7); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test1,b->jit_ptr); ppc_patch(test2,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BEQL (Branch On Equal Likely) */ DECLARE_INSN(BEQL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. * compare the low 32 bits first (higher probability). */ ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r6,ppc_r8); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r7,REG_OFFSET(rt),ppc_r3); ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r5,ppc_r7); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test1,b->jit_ptr); ppc_patch(test2,b->jit_ptr); return(0); } /* BEQZ (virtual) */ DECLARE_INSN(BEQZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. * compare the low 32 bits first (higher probability). */ ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r6,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,0); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test1,b->jit_ptr); ppc_patch(test2,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BNEZ (virtual) */ DECLARE_INSN(BNEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. * compare the low 32 bits first (higher probability). */ ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r6,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,0); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); ppc_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test2,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZ */ DECLARE_INSN(BGEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* If sign bit is set, don't take the branch */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE,ppc_crbf(ppc_cr7,PPC_BR_LT),0); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZAL */ DECLARE_INSN(BGEZAL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* If sign bit is set, don't take the branch */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE,ppc_crbf(ppc_cr7,PPC_BR_LT),0); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZALL */ DECLARE_INSN(BGEZALL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* if sign bit is set, don't take the branch */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_LT),0); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test1,b->jit_ptr); return(0); } /* BGEZL */ DECLARE_INSN(BGEZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* If sign bit is set, don't take the branch */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_LT),0); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test1,b->jit_ptr); return(0); } /* BGTZ */ DECLARE_INSN(BGTZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2,*test3; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the hi word of gpr[rs] */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE,ppc_crbf(ppc_cr7,PPC_BR_GT),0); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE,ppc_crbf(ppc_cr7,PPC_BR_LT),0); /* test the lo word of gpr[rs] (here hi word == 0) */ ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r6,0); test3 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); ppc_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test2,b->jit_ptr); ppc_patch(test3,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGTZL */ DECLARE_INSN(BGTZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2,*test3; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the hi word of gpr[rs] */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE,ppc_crbf(ppc_cr7,PPC_BR_GT),0); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_LT),0); /* test the lo word of gpr[rs] (here hi word == 0) */ ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r6,0); test3 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); ppc_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test2,b->jit_ptr); ppc_patch(test3,b->jit_ptr); return(0); } /* BLEZ */ DECLARE_INSN(BLEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2,*test3; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the hi word of gpr[rs] */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE,ppc_crbf(ppc_cr7,PPC_BR_LT),0); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE,ppc_crbf(ppc_cr7,PPC_BR_GT),0); /* test the lo word of gpr[rs] (here hi word == 0) */ ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r6,0); test3 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); ppc_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test2,b->jit_ptr); ppc_patch(test3,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLEZL */ DECLARE_INSN(BLEZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2,*test3; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the hi word of gpr[rs] */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE,ppc_crbf(ppc_cr7,PPC_BR_LT),0); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_GT),0); /* test the lo word of gpr[rs] (here hi word == 0) */ ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r6,0); test3 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); ppc_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test2,b->jit_ptr); ppc_patch(test3,b->jit_ptr); return(0); } /* BLTZ */ DECLARE_INSN(BLTZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the sign bit of gpr[rs], if set, take the branch. */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr7,PPC_BR_LT),0); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLTZAL */ DECLARE_INSN(BLTZAL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* * test the sign bit of gpr[rs], if set, take the branch. */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr7,PPC_BR_LT),0); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLTZALL */ DECLARE_INSN(BLTZALL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->start_pc + ((b->mips_trans_pos + 1) << 2)); /* * test the sign bit of gpr[rs], if set, take the branch. */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_LT),0); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test1,b->jit_ptr); return(0); } /* BLTZL */ DECLARE_INSN(BLTZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the sign bit of gpr[rs], if set, take the branch. */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,0); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_LT),0); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test1,b->jit_ptr); return(0); } /* BNE */ DECLARE_INSN(BNE) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. * compare the low 32 bits first (higher probability). */ ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r6,ppc_r8); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r7,REG_OFFSET(rt),ppc_r3); ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r5,ppc_r7); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); ppc_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test2,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BNEL */ DECLARE_INSN(BNEL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. * compare the low 32 bits first (higher probability). */ ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r6,ppc_r8); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r7,REG_OFFSET(rt),ppc_r3); ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r5,ppc_r7); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_TRUE_UNLIKELY,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); ppc_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); ppc_patch(test2,b->jit_ptr); return(0); } /* BREAK */ DECLARE_INSN(BREAK) { u_int code = bits(insn,6,25); /* r3 = CPU instance pointer */ /* r4 = code */ ppc_load(b->jit_ptr,ppc_r4,code); mips64_emit_basic_c_call(b,mips64_exec_break); mips64_jit_tcb_push_epilog(b); return(0); } /* CACHE */ DECLARE_INSN(CACHE) { int base = bits(insn,21,25); int op = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_CACHE,base,offset,op,FALSE); return(0); } /* CFC0 */ DECLARE_INSN(CFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_cfc0); return(0); } /* CTC0 */ DECLARE_INSN(CTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_ctc0); return(0); } /* DADDIU */ DECLARE_INSN(DADDIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_addic(b->jit_ptr,ppc_r6,ppc_r6,imm); if (imm & 0x8000 /* < 0 */) ppc_addme(b->jit_ptr,ppc_r5,ppc_r5); else ppc_addze(b->jit_ptr,ppc_r5,ppc_r5); ppc_stw(b->jit_ptr,ppc_r6,REG_OFFSET(rt)+4,ppc_r3); ppc_stw(b->jit_ptr,ppc_r5,REG_OFFSET(rt),ppc_r3); return(0); } /* DADDU */ DECLARE_INSN(DADDU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r7,REG_OFFSET(rt),ppc_r3); ppc_addc(b->jit_ptr,ppc_r10,ppc_r6,ppc_r8); ppc_adde(b->jit_ptr,ppc_r9,ppc_r5,ppc_r7); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); ppc_stw(b->jit_ptr,ppc_r9,REG_OFFSET(rd),ppc_r3); return(0); } /* DIV */ DECLARE_INSN(DIV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_divw(b->jit_ptr,ppc_r10,ppc_r6,ppc_r8); /* store LO (quotient) */ ppc_stw(b->jit_ptr,ppc_r10,OFFSET(cpu_mips_t,lo)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,lo),ppc_r3); ppc_mullw(b->jit_ptr,ppc_r9,ppc_r10,ppc_r8); ppc_subf(b->jit_ptr,ppc_r9,ppc_r9,ppc_r6); /* store HI (remainder) */ ppc_stw(b->jit_ptr,ppc_r9,OFFSET(cpu_mips_t,hi)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r9,31); ppc_stw(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,hi),ppc_r3); return(0); } /* DIVU */ DECLARE_INSN(DIVU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_divwu(b->jit_ptr,ppc_r10,ppc_r6,ppc_r8); /* store LO (quotient) */ ppc_stw(b->jit_ptr,ppc_r10,OFFSET(cpu_mips_t,lo)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,lo),ppc_r3); ppc_mullw(b->jit_ptr,ppc_r9,ppc_r10,ppc_r8); ppc_subf(b->jit_ptr,ppc_r9,ppc_r9,ppc_r6); /* store HI (remainder) */ ppc_stw(b->jit_ptr,ppc_r9,OFFSET(cpu_mips_t,hi)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r9,31); ppc_stw(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,hi),ppc_r3); return(0); } /* DMFC0 */ DECLARE_INSN(DMFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_dmfc0); return(0); } /* DMFC1 */ DECLARE_INSN(DMFC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_dmfc1); return(0); } /* DMTC0 */ DECLARE_INSN(DMTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_dmtc0); return(0); } /* DMTC1 */ DECLARE_INSN(DMTC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_dmtc1); return(0); } /* DSLL */ DECLARE_INSN(DSLL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rt),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rt)+4,ppc_r3); ppc_slwi(b->jit_ptr,ppc_r7,ppc_r5,sa); if (sa > 0) ppc_rlwimi(b->jit_ptr,ppc_r7,ppc_r6,sa,32-sa,31); ppc_slwi(b->jit_ptr,ppc_r8,ppc_r6,sa); ppc_stw(b->jit_ptr,ppc_r7,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* DSLL32 */ DECLARE_INSN(DSLL32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rt)+4,ppc_r3); ppc_slwi(b->jit_ptr,ppc_r7,ppc_r6,sa); ppc_li(b->jit_ptr,ppc_r8,0); ppc_stw(b->jit_ptr,ppc_r7,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* DSLLV */ DECLARE_INSN(DSLLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r10,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rt),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rt)+4,ppc_r3); ppc_andid(b->jit_ptr,ppc_r10,ppc_r10,0x3f); // ppc_rlwinm(b->jit_ptr,ppc_r10,ppc_r10,0,26,31); ppc_subfic(b->jit_ptr,ppc_r9,ppc_r10,32); ppc_slw(b->jit_ptr,ppc_r7,ppc_r5,ppc_r10); ppc_srw(b->jit_ptr,ppc_r9,ppc_r6,ppc_r9); ppc_or(b->jit_ptr,ppc_r7,ppc_r7,ppc_r9); ppc_subi(b->jit_ptr,ppc_r9,ppc_r10,32); ppc_slw(b->jit_ptr,ppc_r9,ppc_r6,ppc_r9); ppc_or(b->jit_ptr,ppc_r7,ppc_r7,ppc_r9); ppc_slw(b->jit_ptr,ppc_r8,ppc_r6,ppc_r10); ppc_stw(b->jit_ptr,ppc_r7,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* DSRA */ DECLARE_INSN(DSRA) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rt),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rt)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r7,ppc_r5,sa); ppc_srwi(b->jit_ptr,ppc_r8,ppc_r6,sa); if (sa > 0) ppc_rlwimi(b->jit_ptr,ppc_r8,ppc_r5,32-sa,0,sa-1); ppc_stw(b->jit_ptr,ppc_r7,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* DSRA32 */ DECLARE_INSN(DSRA32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rt),ppc_r3); ppc_srawi(b->jit_ptr,ppc_r7,ppc_r5,31); ppc_srawi(b->jit_ptr,ppc_r8,ppc_r5,sa); ppc_stw(b->jit_ptr,ppc_r7,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* DSRAV */ DECLARE_INSN(DSRAV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); u_char *test1; ppc_lwz(b->jit_ptr,ppc_r10,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rt),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rt)+4,ppc_r3); ppc_andid(b->jit_ptr,ppc_r10,ppc_r10,0x3f); // ppc_rlwinm(b->jit_ptr,ppc_r10,ppc_r10,0,26,31); ppc_subfic(b->jit_ptr,ppc_r9,ppc_r10,32); ppc_srw(b->jit_ptr,ppc_r8,ppc_r6,ppc_r10); ppc_slw(b->jit_ptr,ppc_r7,ppc_r5,ppc_r9); ppc_or(b->jit_ptr,ppc_r8,ppc_r8,ppc_r7); ppc_subicd(b->jit_ptr,ppc_r9,ppc_r10,32); ppc_sraw(b->jit_ptr,ppc_r7,ppc_r5,ppc_r9); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr0,PPC_BR_GT),0); ppc_mr(b->jit_ptr,ppc_r8,ppc_r7); ppc_sraw(b->jit_ptr,ppc_r7,ppc_r5,ppc_r10); ppc_patch(test1,b->jit_ptr); ppc_stw(b->jit_ptr,ppc_r7,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* DSRL */ DECLARE_INSN(DSRL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rt),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rt)+4,ppc_r3); ppc_srwi(b->jit_ptr,ppc_r7,ppc_r5,sa); ppc_srwi(b->jit_ptr,ppc_r8,ppc_r6,sa); if (sa > 0) ppc_rlwimi(b->jit_ptr,ppc_r8,ppc_r5,32-sa,0,sa-1); ppc_stw(b->jit_ptr,ppc_r7,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* DSRL32 */ DECLARE_INSN(DSRL32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rt),ppc_r3); ppc_li(b->jit_ptr,ppc_r7,0); ppc_srwi(b->jit_ptr,ppc_r8,ppc_r5,sa); ppc_stw(b->jit_ptr,ppc_r7,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* DSRLV */ DECLARE_INSN(DSRLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r10,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rt),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rt)+4,ppc_r3); ppc_andid(b->jit_ptr,ppc_r10,ppc_r10,0x3f); // ppc_rlwinm(b->jit_ptr,ppc_r10,ppc_r10,0,26,31); ppc_subfic(b->jit_ptr,ppc_r9,ppc_r10,32); ppc_srw(b->jit_ptr,ppc_r8,ppc_r6,ppc_r10); ppc_slw(b->jit_ptr,ppc_r9,ppc_r5,ppc_r9); ppc_or(b->jit_ptr,ppc_r8,ppc_r8,ppc_r9); ppc_subi(b->jit_ptr,ppc_r9,ppc_r10,32); ppc_srw(b->jit_ptr,ppc_r9,ppc_r5,ppc_r9); ppc_or(b->jit_ptr,ppc_r8,ppc_r8,ppc_r9); ppc_srw(b->jit_ptr,ppc_r7,ppc_r5,ppc_r10); ppc_stw(b->jit_ptr,ppc_r7,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* DSUBU */ DECLARE_INSN(DSUBU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r7,REG_OFFSET(rt),ppc_r3); ppc_subc(b->jit_ptr,ppc_r10,ppc_r6,ppc_r8); ppc_subfe(b->jit_ptr,ppc_r9,ppc_r7,ppc_r5); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); ppc_stw(b->jit_ptr,ppc_r9,REG_OFFSET(rd),ppc_r3); return(0); } /* ERET */ DECLARE_INSN(ERET) { /* Restore link register */ ppc_lwz(b->jit_ptr,ppc_r0,PPC_STACK_DECREMENTER+PPC_RET_ADDR_OFFSET,ppc_r1); ppc_mtlr(b->jit_ptr,ppc_r0); mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); /* Trick: let callee return directly to the caller */ ppc_emit_jump_code(b->jit_ptr, (u_char *)mips64_exec_eret, 0); return(0); } /* J */ DECLARE_INSN(J) { u_int instr_index = bits(insn,0,25); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc &= ~((1 << 28) - 1); new_pc |= instr_index << 2; /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); return(0); } /* JAL */ DECLARE_INSN(JAL) { u_int instr_index = bits(insn,0,25); m_uint64_t new_pc,ret_pc; /* compute the new pc */ new_pc = b->start_pc + (b->mips_trans_pos << 2); new_pc &= ~((1 << 28) - 1); new_pc |= instr_index << 2; /* set the return address (instruction after the delay slot) */ ret_pc = b->start_pc + ((b->mips_trans_pos + 1) << 2); mips64_set_ra(b,ret_pc); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,0); return(0); } /* JALR */ DECLARE_INSN(JALR) { int rs = bits(insn,21,25); int rd = bits(insn,11,15); m_uint64_t ret_pc; /* set the return pc (instruction after the delay slot) in GPR[rd] */ ret_pc = b->start_pc + ((b->mips_trans_pos + 1) << 2); ppc_load(b->jit_ptr,ppc_r7,ret_pc >> 32); ppc_load(b->jit_ptr,ppc_r8,ret_pc & 0xffffffff); ppc_stw(b->jit_ptr,ppc_r7,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); /* get the new pc */ ppc_lwz(b->jit_ptr,ppc_r9,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r10,REG_OFFSET(rs)+4,ppc_r3); ppc_stw(b->jit_ptr,ppc_r9,OFFSET(cpu_mips_t,ret_pc),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,OFFSET(cpu_mips_t,ret_pc)+4,ppc_r3); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc */ ppc_lwz(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,ret_pc),ppc_r3); ppc_stw(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,pc),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,ret_pc)+4,ppc_r3); ppc_stw(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,pc)+4,ppc_r3); /* returns to the caller which will determine the next path */ mips64_jit_tcb_push_epilog(b); return(0); } /* JR */ DECLARE_INSN(JR) { int rs = bits(insn,21,25); /* get the new pc */ ppc_lwz(b->jit_ptr,ppc_r9,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r10,REG_OFFSET(rs)+4,ppc_r3); ppc_stw(b->jit_ptr,ppc_r9,OFFSET(cpu_mips_t,ret_pc),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,OFFSET(cpu_mips_t,ret_pc)+4,ppc_r3); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc */ ppc_lwz(b->jit_ptr,ppc_r9,OFFSET(cpu_mips_t,ret_pc),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r10,OFFSET(cpu_mips_t,ret_pc)+4,ppc_r3); ppc_stw(b->jit_ptr,ppc_r9,OFFSET(cpu_mips_t,pc),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,OFFSET(cpu_mips_t,pc)+4,ppc_r3); /* returns to the caller which will determine the next path */ mips64_jit_tcb_push_epilog(b); return(0); } /* LB */ DECLARE_INSN(LB) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,0,MIPS_MEMOP_LB,base,offset,rt,TRUE, mips64_memop_fast_lb); } else { mips64_emit_memop(b,MIPS_MEMOP_LB,base,offset,rt,TRUE); } return(0); } /* LBU */ DECLARE_INSN(LBU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,0,MIPS_MEMOP_LBU,base,offset,rt,TRUE, mips64_memop_fast_lbu); } else { mips64_emit_memop(b,MIPS_MEMOP_LBU,base,offset,rt,TRUE); } return(0); } /* LD */ DECLARE_INSN(LD) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,0,MIPS_MEMOP_LD,base,offset,rt,TRUE, mips64_memop_fast_ld); } else { mips64_emit_memop(b,MIPS_MEMOP_LD,base,offset,rt,TRUE); } return(0); } /* LDC1 */ DECLARE_INSN(LDC1) { int base = bits(insn,21,25); int ft = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDC1,base,offset,ft,TRUE); return(0); } /* LDL */ DECLARE_INSN(LDL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDL,base,offset,rt,TRUE); return(0); } /* LDR */ DECLARE_INSN(LDR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDR,base,offset,rt,TRUE); return(0); } /* LH */ DECLARE_INSN(LH) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,0,MIPS_MEMOP_LH,base,offset,rt,TRUE, mips64_memop_fast_lh); } else { mips64_emit_memop(b,MIPS_MEMOP_LH,base,offset,rt,TRUE); } return(0); } /* LHU */ DECLARE_INSN(LHU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,0,MIPS_MEMOP_LHU,base,offset,rt,TRUE, mips64_memop_fast_lhu); } else { mips64_emit_memop(b,MIPS_MEMOP_LHU,base,offset,rt,TRUE); } return(0); } /* LI (virtual) */ DECLARE_INSN(LI) { int rt = bits(insn,16,20); int imm = bits(insn,0,15); ppc_li(b->jit_ptr,ppc_r9,imm); ppc_stw(b->jit_ptr,ppc_r9,REG_OFFSET(rt)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r9,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rt),ppc_r3); return(0); } /* LL */ DECLARE_INSN(LL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LL,base,offset,rt,TRUE); return(0); } /* LUI */ DECLARE_INSN(LUI) { int rt = bits(insn,16,20); int imm = bits(insn,0,15); ppc_lis(b->jit_ptr,ppc_r10,imm); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rt)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rt),ppc_r3); return(0); } /* LW */ DECLARE_INSN(LW) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,0,MIPS_MEMOP_LW,base,offset,rt,TRUE, mips64_memop_fast_lw); } else { mips64_emit_memop(b,MIPS_MEMOP_LW,base,offset,rt,TRUE); } return(0); } /* LWL */ DECLARE_INSN(LWL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LWL,base,offset,rt,TRUE); return(0); } /* LWR */ DECLARE_INSN(LWR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LWR,base,offset,rt,TRUE); return(0); } /* LWU */ DECLARE_INSN(LWU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,0,MIPS_MEMOP_LWU,base,offset,rt,TRUE, mips64_memop_fast_lwu); } else { mips64_emit_memop(b,MIPS_MEMOP_LWU,base,offset,rt,TRUE); } return(0); } /* MFC0 */ DECLARE_INSN(MFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_mfc0); return(0); } /* MFC1 */ DECLARE_INSN(MFC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_mfc1); return(0); } /* MFHI */ DECLARE_INSN(MFHI) { int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r9,OFFSET(cpu_mips_t,hi),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r10,OFFSET(cpu_mips_t,hi)+4,ppc_r3); ppc_stw(b->jit_ptr,ppc_r9,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* MFLO */ DECLARE_INSN(MFLO) { int rd = bits(insn,11,15); /* Optimization for "mflo zr" */ if (!rd) return(0); ppc_lwz(b->jit_ptr,ppc_r9,OFFSET(cpu_mips_t,lo),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r10,OFFSET(cpu_mips_t,lo)+4,ppc_r3); ppc_stw(b->jit_ptr,ppc_r9,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* MOVE (virtual) */ DECLARE_INSN(MOVE) { int rs = bits(insn,21,25); int rd = bits(insn,11,15); if (rs != 0) { ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rs)+4,ppc_r3); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r8,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); } else { ppc_li(b->jit_ptr,ppc_r0,0); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd)+4,ppc_r3); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); } return(0); } /* MTC0 */ DECLARE_INSN(MTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_mtc0); return(0); } /* MTC1 */ DECLARE_INSN(MTC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_mtc1); return(0); } /* MTHI */ DECLARE_INSN(MTHI) { int rs = bits(insn,21,25); ppc_lwz(b->jit_ptr,ppc_r9,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r10,REG_OFFSET(rs)+4,ppc_r3); ppc_stw(b->jit_ptr,ppc_r9,OFFSET(cpu_mips_t,hi),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,OFFSET(cpu_mips_t,hi)+4,ppc_r3); return(0); } /* MTLO */ DECLARE_INSN(MTLO) { int rs = bits(insn,21,25); ppc_lwz(b->jit_ptr,ppc_r9,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r10,REG_OFFSET(rs)+4,ppc_r3); ppc_stw(b->jit_ptr,ppc_r9,OFFSET(cpu_mips_t,lo),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,OFFSET(cpu_mips_t,lo)+4,ppc_r3); return(0); } /* MUL */ DECLARE_INSN(MUL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r9,REG_OFFSET(rt)+4,ppc_r3); ppc_mullw(b->jit_ptr,ppc_r10,ppc_r8,ppc_r9); /* store result in gpr[rd] */ ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); return(0); } /* MULT */ DECLARE_INSN(MULT) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r9,REG_OFFSET(rt)+4,ppc_r3); ppc_mullw(b->jit_ptr,ppc_r10,ppc_r8,ppc_r9); /* store LO */ ppc_stw(b->jit_ptr,ppc_r10,OFFSET(cpu_mips_t,lo)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,lo),ppc_r3); ppc_mulhw(b->jit_ptr,ppc_r10,ppc_r8,ppc_r9); /* store HI */ ppc_stw(b->jit_ptr,ppc_r10,OFFSET(cpu_mips_t,hi)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,hi),ppc_r3); return(0); } /* MULTU */ DECLARE_INSN(MULTU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r9,REG_OFFSET(rt)+4,ppc_r3); ppc_mullw(b->jit_ptr,ppc_r10,ppc_r8,ppc_r9); /* store LO */ ppc_stw(b->jit_ptr,ppc_r10,OFFSET(cpu_mips_t,lo)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,lo),ppc_r3); ppc_mulhwu(b->jit_ptr,ppc_r10,ppc_r8,ppc_r9); /* store HI */ ppc_stw(b->jit_ptr,ppc_r10,OFFSET(cpu_mips_t,hi)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,OFFSET(cpu_mips_t,hi),ppc_r3); return(0); } /* NOP */ DECLARE_INSN(NOP) { return(0); } /* NOR */ DECLARE_INSN(NOR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r7,REG_OFFSET(rt),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_nor(b->jit_ptr,ppc_r9,ppc_r5,ppc_r7); ppc_nor(b->jit_ptr,ppc_r10,ppc_r6,ppc_r8); ppc_stw(b->jit_ptr,ppc_r9,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* OR */ DECLARE_INSN(OR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r7,REG_OFFSET(rt),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_or(b->jit_ptr,ppc_r9,ppc_r5,ppc_r7); ppc_or(b->jit_ptr,ppc_r10,ppc_r6,ppc_r8); ppc_stw(b->jit_ptr,ppc_r9,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* ORI */ DECLARE_INSN(ORI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_ori(b->jit_ptr,ppc_r8,ppc_r6,imm); ppc_stw(b->jit_ptr,ppc_r5,REG_OFFSET(rt),ppc_r3); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); return(0); } /* PREF */ DECLARE_INSN(PREF) { /* int base = bits(insn,21,25); int hint = bits(insn,16,20); int offset = bits(insn,0,15); */ return(0); } /* PREFX */ DECLARE_INSN(PREFX) { /* int base = bits(insn,21,25); int index = bits(insn,16,20); int hint = bits(insn,11,15); */ return(0); } /* SB */ DECLARE_INSN(SB) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,1,MIPS_MEMOP_SB,base,offset,rt,FALSE, mips64_memop_fast_sb); } else { mips64_emit_memop(b,MIPS_MEMOP_SB,base,offset,rt,FALSE); } return(0); } /* SC */ DECLARE_INSN(SC) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SC,base,offset,rt,TRUE); return(0); } /* SD */ DECLARE_INSN(SD) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,1,MIPS_MEMOP_SD,base,offset,rt,FALSE, mips64_memop_fast_sd); } else { mips64_emit_memop(b,MIPS_MEMOP_SD,base,offset,rt,FALSE); } return(0); } /* SDL */ DECLARE_INSN(SDL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDL,base,offset,rt,FALSE); return(0); } /* SDR */ DECLARE_INSN(SDR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDR,base,offset,rt,FALSE); return(0); } /* SDC1 */ DECLARE_INSN(SDC1) { int base = bits(insn,21,25); int ft = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDC1,base,offset,ft,FALSE); return(0); } /* SH */ DECLARE_INSN(SH) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,1,MIPS_MEMOP_SH,base,offset,rt,FALSE, mips64_memop_fast_sh); } else { mips64_emit_memop(b,MIPS_MEMOP_SH,base,offset,rt,FALSE); } return(0); } /* SLL */ DECLARE_INSN(SLL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rt)+4,ppc_r3); ppc_slwi(b->jit_ptr,ppc_r8,ppc_r6,sa); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r8,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); return(0); } /* SLLV */ DECLARE_INSN(SLLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r10,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rt)+4,ppc_r3); ppc_andid(b->jit_ptr,ppc_r10,ppc_r10,0x1f); // ppc_rlwinm(b->jit_ptr,ppc_r10,ppc_r10,0,27,31); ppc_slw(b->jit_ptr,ppc_r8,ppc_r6,ppc_r10); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r8,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); return(0); } /* SLT */ DECLARE_INSN(SLT) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r7,REG_OFFSET(rt),ppc_r3); ppc_subc(b->jit_ptr,ppc_r0,ppc_r6,ppc_r8); ppc_subfe(b->jit_ptr,ppc_r0,ppc_r7,ppc_r5); ppc_eqv(b->jit_ptr,ppc_r0,ppc_r7,ppc_r5); ppc_srwi(b->jit_ptr,ppc_r0,ppc_r0,31); ppc_addze(b->jit_ptr,ppc_r0,ppc_r0); ppc_andid(b->jit_ptr,ppc_r10,ppc_r0,0x1); //ppc_rlwinm(b->jit_ptr,ppc_r10,ppc_r0,0,31,31); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); ppc_li(b->jit_ptr,ppc_r0,0); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); return(0); } /* SLTI */ DECLARE_INSN(SLTI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_subic(b->jit_ptr,ppc_r0,ppc_r6,imm); if (imm & 0x8000 /* < 0 */) { ppc_addze(b->jit_ptr,ppc_r0,ppc_r5); ppc_srwi(b->jit_ptr,ppc_r0,ppc_r5,31); } else { ppc_addme(b->jit_ptr,ppc_r0,ppc_r5); ppc_srwi(b->jit_ptr,ppc_r0,ppc_r5,31); ppc_xori(b->jit_ptr,ppc_r0,ppc_r0,0x1); } ppc_addze(b->jit_ptr,ppc_r0,ppc_r0); ppc_andid(b->jit_ptr,ppc_r10,ppc_r0,0x1); //ppc_rlwinm(b->jit_ptr,ppc_r10,ppc_r0,0,31,31); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rt)+4,ppc_r3); ppc_li(b->jit_ptr,ppc_r0,0); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rt),ppc_r3); return(0); } /* SLTIU */ DECLARE_INSN(SLTIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_subic(b->jit_ptr,ppc_r0,ppc_r6,imm); if (imm & 0x8000 /* < 0 */) ppc_addze(b->jit_ptr,ppc_r0,ppc_r5); else ppc_addme(b->jit_ptr,ppc_r0,ppc_r5); ppc_subfe(b->jit_ptr,ppc_r0,ppc_r0,ppc_r0); ppc_neg(b->jit_ptr,ppc_r10,ppc_r0); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rt)+4,ppc_r3); ppc_li(b->jit_ptr,ppc_r0,0); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rt),ppc_r3); return(0); } /* SLTU */ DECLARE_INSN(SLTU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r7,REG_OFFSET(rt),ppc_r3); ppc_subc(b->jit_ptr,ppc_r0,ppc_r6,ppc_r8); ppc_subfe(b->jit_ptr,ppc_r0,ppc_r7,ppc_r5); ppc_subfe(b->jit_ptr,ppc_r0,ppc_r0,ppc_r0); ppc_neg(b->jit_ptr,ppc_r10,ppc_r0); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); ppc_li(b->jit_ptr,ppc_r0,0); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); return(0); } /* SRA */ DECLARE_INSN(SRA) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rt)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r8,ppc_r6,sa); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r8,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); return(0); } /* SRAV */ DECLARE_INSN(SRAV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r10,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rt)+4,ppc_r3); ppc_andid(b->jit_ptr,ppc_r10,ppc_r10,0x1f); // ppc_rlwinm(b->jit_ptr,ppc_r10,ppc_r10,0,27,31); ppc_sraw(b->jit_ptr,ppc_r8,ppc_r6,ppc_r10); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r8,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); return(0); } /* SRL */ DECLARE_INSN(SRL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rt)+4,ppc_r3); ppc_srwi(b->jit_ptr,ppc_r8,ppc_r6,sa); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r8,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); return(0); } /* SRLV */ DECLARE_INSN(SRLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r10,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rt)+4,ppc_r3); ppc_andid(b->jit_ptr,ppc_r10,ppc_r10,0x1f); // ppc_rlwinm(b->jit_ptr,ppc_r10,ppc_r10,0,27,31); ppc_srw(b->jit_ptr,ppc_r8,ppc_r6,ppc_r10); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rd)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r8,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); return(0); } /* SUB */ DECLARE_INSN(SUB) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r9,REG_OFFSET(rt)+4,ppc_r3); ppc_subfo(b->jit_ptr,ppc_r10,ppc_r9,ppc_r8); /* TODO: Exception handling */ ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); return(0); } /* SUBU */ DECLARE_INSN(SUBU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r9,REG_OFFSET(rt)+4,ppc_r3); ppc_sub(b->jit_ptr,ppc_r10,ppc_r8,ppc_r9); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); ppc_srawi(b->jit_ptr,ppc_r0,ppc_r10,31); ppc_stw(b->jit_ptr,ppc_r0,REG_OFFSET(rd),ppc_r3); return(0); } /* SW */ DECLARE_INSN(SW) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,1,MIPS_MEMOP_SW,base,offset,rt,FALSE, mips64_memop_fast_sw); } else { mips64_emit_memop(b,MIPS_MEMOP_SW,base,offset,rt,FALSE); } return(0); } /* SWL */ DECLARE_INSN(SWL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SWL,base,offset,rt,FALSE); return(0); } /* SWR */ DECLARE_INSN(SWR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SWR,base,offset,rt,FALSE); return(0); } /* SYNC */ DECLARE_INSN(SYNC) { return(0); } /* SYSCALL */ DECLARE_INSN(SYSCALL) { /* Restore link register */ ppc_lwz(b->jit_ptr,ppc_r0,PPC_STACK_DECREMENTER+PPC_RET_ADDR_OFFSET,ppc_r1); ppc_mtlr(b->jit_ptr,ppc_r0); mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); /* Trick: let callee return directly to the caller */ ppc_emit_jump_code(b->jit_ptr, (u_char *)mips64_exec_syscall, 0); return(0); } /* TEQ */ DECLARE_INSN(TEQ) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); u_char *test1,*test2; /* Compare low part */ ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r6,ppc_r8); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); /* Compare high part */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r7,REG_OFFSET(rt),ppc_r3); ppc_cmpw(b->jit_ptr,ppc_cr7,ppc_r5,ppc_r7); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); /* Generate trap exception */ mips64_emit_c_call(b,mips64_trigger_trap_exception); mips64_jit_tcb_push_epilog(b); /* end */ ppc_patch(test1,b->jit_ptr); ppc_patch(test2,b->jit_ptr); return(0); } /* TEQI (Trap If Equal Immediate) */ DECLARE_INSN(TEQI) { int rs = bits(insn,21,25); int imm = bits(insn,0,15); u_char *test1,*test2; /* Compare low part */ ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r6,imm); test1 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); /* Compare high part */ ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_cmpwi(b->jit_ptr,ppc_cr7,ppc_r5,(imm << 16) >> 31); test2 = b->jit_ptr; ppc_bc(b->jit_ptr,PPC_BR_FALSE,ppc_crbf(ppc_cr7,PPC_BR_EQ),0); /* Generate trap exception */ mips64_emit_c_call(b,mips64_trigger_trap_exception); mips64_jit_tcb_push_epilog(b); /* end */ ppc_patch(test1,b->jit_ptr); ppc_patch(test2,b->jit_ptr); return(0); } /* TLBP */ DECLARE_INSN(TLBP) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbp); return(0); } /* TLBR */ DECLARE_INSN(TLBR) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbr); return(0); } /* TLBWI */ DECLARE_INSN(TLBWI) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbwi); return(0); } /* TLBWR */ DECLARE_INSN(TLBWR) { mips64_set_pc(b,b->start_pc+((b->mips_trans_pos-1)<<2)); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbwr); return(0); } /* XOR */ DECLARE_INSN(XOR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r7,REG_OFFSET(rt),ppc_r3); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); ppc_xor(b->jit_ptr,ppc_r9,ppc_r5,ppc_r7); ppc_xor(b->jit_ptr,ppc_r10,ppc_r6,ppc_r8); ppc_stw(b->jit_ptr,ppc_r9,REG_OFFSET(rd),ppc_r3); ppc_stw(b->jit_ptr,ppc_r10,REG_OFFSET(rd)+4,ppc_r3); return(0); } /* XORI */ DECLARE_INSN(XORI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); ppc_lwz(b->jit_ptr,ppc_r6,REG_OFFSET(rs)+4,ppc_r3); ppc_lwz(b->jit_ptr,ppc_r5,REG_OFFSET(rs),ppc_r3); ppc_xori(b->jit_ptr,ppc_r8,ppc_r6,imm); ppc_stw(b->jit_ptr,ppc_r5,REG_OFFSET(rt),ppc_r3); ppc_stw(b->jit_ptr,ppc_r8,REG_OFFSET(rt)+4,ppc_r3); return(0); } /* MIPS instruction array */ struct mips64_insn_tag mips64_insn_tags[] = { { mips64_emit_LI , 0xffe00000 , 0x24000000, 1 }, /* virtual */ { mips64_emit_MOVE , 0xfc1f07ff , 0x00000021, 1 }, /* virtual */ { mips64_emit_B , 0xffff0000 , 0x10000000, 0 }, /* virtual */ { mips64_emit_BAL , 0xffff0000 , 0x04110000, 0 }, /* virtual */ { mips64_emit_BEQZ , 0xfc1f0000 , 0x10000000, 0 }, /* virtual */ { mips64_emit_BNEZ , 0xfc1f0000 , 0x14000000, 0 }, /* virtual */ { mips64_emit_ADD , 0xfc0007ff , 0x00000020, 1 }, { mips64_emit_ADDI , 0xfc000000 , 0x20000000, 1 }, { mips64_emit_ADDIU , 0xfc000000 , 0x24000000, 1 }, { mips64_emit_ADDU , 0xfc0007ff , 0x00000021, 1 }, { mips64_emit_AND , 0xfc0007ff , 0x00000024, 1 }, { mips64_emit_ANDI , 0xfc000000 , 0x30000000, 1 }, { mips64_emit_BEQ , 0xfc000000 , 0x10000000, 0 }, { mips64_emit_BEQL , 0xfc000000 , 0x50000000, 0 }, { mips64_emit_BGEZ , 0xfc1f0000 , 0x04010000, 0 }, { mips64_emit_BGEZAL , 0xfc1f0000 , 0x04110000, 0 }, { mips64_emit_BGEZALL , 0xfc1f0000 , 0x04130000, 0 }, { mips64_emit_BGEZL , 0xfc1f0000 , 0x04030000, 0 }, { mips64_emit_BGTZ , 0xfc1f0000 , 0x1c000000, 0 }, { mips64_emit_BGTZL , 0xfc1f0000 , 0x5c000000, 0 }, { mips64_emit_BLEZ , 0xfc1f0000 , 0x18000000, 0 }, { mips64_emit_BLEZL , 0xfc1f0000 , 0x58000000, 0 }, { mips64_emit_BLTZ , 0xfc1f0000 , 0x04000000, 0 }, { mips64_emit_BLTZAL , 0xfc1f0000 , 0x04100000, 0 }, { mips64_emit_BLTZALL , 0xfc1f0000 , 0x04120000, 0 }, { mips64_emit_BLTZL , 0xfc1f0000 , 0x04020000, 0 }, { mips64_emit_BNE , 0xfc000000 , 0x14000000, 0 }, { mips64_emit_BNEL , 0xfc000000 , 0x54000000, 0 }, { mips64_emit_BREAK , 0xfc00003f , 0x0000000d, 1 }, { mips64_emit_CACHE , 0xfc000000 , 0xbc000000, 1 }, { mips64_emit_CFC0 , 0xffe007ff , 0x40400000, 1 }, { mips64_emit_CTC0 , 0xffe007ff , 0x40600000, 1 }, { mips64_emit_DADDIU , 0xfc000000 , 0x64000000, 1 }, { mips64_emit_DADDU , 0xfc0007ff , 0x0000002d, 1 }, { mips64_emit_DIV , 0xfc00ffff , 0x0000001a, 1 }, { mips64_emit_DIVU , 0xfc00ffff , 0x0000001b, 1 }, { mips64_emit_DMFC0 , 0xffe007f8 , 0x40200000, 1 }, { mips64_emit_DMFC1 , 0xffe007ff , 0x44200000, 1 }, { mips64_emit_DMTC0 , 0xffe007f8 , 0x40a00000, 1 }, { mips64_emit_DMTC1 , 0xffe007ff , 0x44a00000, 1 }, { mips64_emit_DSLL , 0xffe0003f , 0x00000038, 1 }, { mips64_emit_DSLL32 , 0xffe0003f , 0x0000003c, 1 }, { mips64_emit_DSLLV , 0xfc0007ff , 0x00000014, 1 }, { mips64_emit_DSRA , 0xffe0003f , 0x0000003b, 1 }, { mips64_emit_DSRA32 , 0xffe0003f , 0x0000003f, 1 }, { mips64_emit_DSRAV , 0xfc0007ff , 0x00000017, 1 }, { mips64_emit_DSRL , 0xffe0003f , 0x0000003a, 1 }, { mips64_emit_DSRL32 , 0xffe0003f , 0x0000003e, 1 }, { mips64_emit_DSRLV , 0xfc0007ff , 0x00000016, 1 }, { mips64_emit_DSUBU , 0xfc0007ff , 0x0000002f, 1 }, { mips64_emit_ERET , 0xffffffff , 0x42000018, 0 }, { mips64_emit_J , 0xfc000000 , 0x08000000, 0 }, { mips64_emit_JAL , 0xfc000000 , 0x0c000000, 0 }, { mips64_emit_JALR , 0xfc1f003f , 0x00000009, 0 }, { mips64_emit_JR , 0xfc1ff83f , 0x00000008, 0 }, { mips64_emit_LB , 0xfc000000 , 0x80000000, 1 }, { mips64_emit_LBU , 0xfc000000 , 0x90000000, 1 }, { mips64_emit_LD , 0xfc000000 , 0xdc000000, 1 }, { mips64_emit_LDC1 , 0xfc000000 , 0xd4000000, 1 }, { mips64_emit_LDL , 0xfc000000 , 0x68000000, 1 }, { mips64_emit_LDR , 0xfc000000 , 0x6c000000, 1 }, { mips64_emit_LH , 0xfc000000 , 0x84000000, 1 }, { mips64_emit_LHU , 0xfc000000 , 0x94000000, 1 }, { mips64_emit_LL , 0xfc000000 , 0xc0000000, 1 }, { mips64_emit_LUI , 0xffe00000 , 0x3c000000, 1 }, { mips64_emit_LW , 0xfc000000 , 0x8c000000, 1 }, { mips64_emit_LWL , 0xfc000000 , 0x88000000, 1 }, { mips64_emit_LWR , 0xfc000000 , 0x98000000, 1 }, { mips64_emit_LWU , 0xfc000000 , 0x9c000000, 1 }, { mips64_emit_MFC0 , 0xffe007ff , 0x40000000, 1 }, { mips64_emit_CFC0 , 0xffe007ff , 0x40000001, 1 }, /* MFC0 / Set 1 */ { mips64_emit_MFC1 , 0xffe007ff , 0x44000000, 1 }, { mips64_emit_MFHI , 0xffff07ff , 0x00000010, 1 }, { mips64_emit_MFLO , 0xffff07ff , 0x00000012, 1 }, { mips64_emit_MTC0 , 0xffe007ff , 0x40800000, 1 }, { mips64_emit_MTC1 , 0xffe007ff , 0x44800000, 1 }, { mips64_emit_MTHI , 0xfc1fffff , 0x00000011, 1 }, { mips64_emit_MTLO , 0xfc1fffff , 0x00000013, 1 }, { mips64_emit_MUL , 0xfc0007ff , 0x70000002, 1 }, { mips64_emit_MULT , 0xfc00ffff , 0x00000018, 1 }, { mips64_emit_MULTU , 0xfc00ffff , 0x00000019, 1 }, { mips64_emit_NOP , 0xffffffff , 0x00000000, 1 }, { mips64_emit_NOR , 0xfc0007ff , 0x00000027, 1 }, { mips64_emit_OR , 0xfc0007ff , 0x00000025, 1 }, { mips64_emit_ORI , 0xfc000000 , 0x34000000, 1 }, { mips64_emit_PREF , 0xfc000000 , 0xcc000000, 1 }, { mips64_emit_PREFX , 0xfc0007ff , 0x4c00000f, 1 }, { mips64_emit_SB , 0xfc000000 , 0xa0000000, 1 }, { mips64_emit_SC , 0xfc000000 , 0xe0000000, 1 }, { mips64_emit_SD , 0xfc000000 , 0xfc000000, 1 }, { mips64_emit_SDC1 , 0xfc000000 , 0xf4000000, 1 }, { mips64_emit_SDL , 0xfc000000 , 0xb0000000, 1 }, { mips64_emit_SDR , 0xfc000000 , 0xb4000000, 1 }, { mips64_emit_SH , 0xfc000000 , 0xa4000000, 1 }, { mips64_emit_SLL , 0xffe0003f , 0x00000000, 1 }, { mips64_emit_SLLV , 0xfc0007ff , 0x00000004, 1 }, { mips64_emit_SLT , 0xfc0007ff , 0x0000002a, 1 }, { mips64_emit_SLTI , 0xfc000000 , 0x28000000, 1 }, { mips64_emit_SLTIU , 0xfc000000 , 0x2c000000, 1 }, { mips64_emit_SLTU , 0xfc0007ff , 0x0000002b, 1 }, { mips64_emit_SRA , 0xffe0003f , 0x00000003, 1 }, { mips64_emit_SRAV , 0xfc0007ff , 0x00000007, 1 }, { mips64_emit_SRL , 0xffe0003f , 0x00000002, 1 }, { mips64_emit_SRLV , 0xfc0007ff , 0x00000006, 1 }, { mips64_emit_SUB , 0xfc0007ff , 0x00000022, 1 }, { mips64_emit_SUBU , 0xfc0007ff , 0x00000023, 1 }, { mips64_emit_SW , 0xfc000000 , 0xac000000, 1 }, { mips64_emit_SWL , 0xfc000000 , 0xa8000000, 1 }, { mips64_emit_SWR , 0xfc000000 , 0xb8000000, 1 }, { mips64_emit_SYNC , 0xfffff83f , 0x0000000f, 1 }, { mips64_emit_SYSCALL , 0xfc00003f , 0x0000000c, 1 }, { mips64_emit_TEQ , 0xfc00003f , 0x00000034, 1 }, { mips64_emit_TEQI , 0xfc1f0000 , 0x040c0000, 1 }, { mips64_emit_TLBP , 0xffffffff , 0x42000008, 1 }, { mips64_emit_TLBR , 0xffffffff , 0x42000001, 1 }, { mips64_emit_TLBWI , 0xffffffff , 0x42000002, 1 }, { mips64_emit_TLBWR , 0xffffffff , 0x42000006, 1 }, { mips64_emit_XOR , 0xfc0007ff , 0x00000026, 1 }, { mips64_emit_XORI , 0xfc000000 , 0x38000000, 1 }, { mips64_emit_unknown , 0x00000000 , 0x00000000, 1 }, { NULL , 0x00000000 , 0x00000000, 0 }, }; dynamips-0.2.14/unstable/mips64_ppc32_trans.h000066400000000000000000000106011241034141600207420ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * JIT engine for 32-bit PowerPC architecture * Copyright (c) 2006, 2007 Zhe Fang (fangzhe@msn.com) */ #ifndef __MIPS64_PPC32_TRANS_H__ #define __MIPS64_PPC32_TRANS_H__ #include "utils.h" #include "cpu.h" #include "mips64_exec.h" #include "dynamips.h" #include "ppc-codegen.h" #define JIT_SUPPORT 1 /* Manipulate bitmasks synchronically */ static forced_inline void atomic_or(m_uint32_t *v,m_uint32_t m) { __asm__ __volatile__("lwarx r0, 0, %0 \n" "or r0, r0, %1 \n" "stwcx. r0, 0, %0 \n" "bne- $-12":"+p"(v): "r"(m): "r0", "memory"); } static forced_inline void atomic_and(m_uint32_t *v,m_uint32_t m) { __asm__ __volatile__("lwarx r0, 0, %0 \n" "and r0, r0, %1 \n" "stwcx. r0, 0, %0 \n" "bne- $-12":"+p"(v): "r"(m): "r0", "memory"); } /* Made from ppc_patch in ppc-codegen.h */ #define ppc_emit_jump_code(code,target,lk) do {\ \ /* prefer relative branches, they are more position independent (e.g. for AOT compilation). */\ if ((target) - (code) >= 0){ \ if ((target) - (code) <= 33554431){ \ ppc_emit32 ((code), (18 << 26) | ((target) - (code)) | lk); \ break; \ } \ } else { \ /* diff between 0 and -33554432 */ \ if ((target) - (code) >= -33554432){ \ ppc_emit32 ((code), (18 << 26) | (((target) - (code)) & ~0xfc000000) | lk); \ break; \ } \ } \ \ if ((long)(target) >= 0){ \ if ((long)(target) <= 33554431){ \ ppc_emit32 ((code), (18 << 26) | (unsigned int)(target) | 2 | lk); \ break; \ } \ } else { \ if ((long)(target) >= -33554432){ \ ppc_emit32 ((code), (18 << 26) | ((unsigned int)(target) & ~0xfc000000) | 2 | lk); \ break; \ } \ } \ \ /* The last way... */ \ ppc_lis ((code), ppc_r12, (unsigned int)(target) >> 16); \ ppc_ori ((code), ppc_r12, ppc_r12, (unsigned int)(target) & 0xffff); \ ppc_mtlr((code), ppc_r12); \ ppc_bclrx((code), PPC_BR_ALWAYS, 0, lk); \ } while (0) /* Here's a hack, see comments in mips64_set_jump for more info */ //#define mips64_jit_tcb_set_patch ppc_patch #define mips64_jit_tcb_set_patch(a,b) ppc_emit_jump_code(a,b,0) #define mips64_jit_tcb_set_jump(a,b) ppc_emit_jump_code(a,b,0) /* MIPS instruction array */ extern struct mips64_insn_tag mips64_insn_tags[]; #define PPC_STACK_DECREMENTER 114 /* Push epilog for a ppc instruction block */ static forced_inline void mips64_jit_tcb_push_epilog(mips64_jit_tcb_t *b) { /* Restore link register */ ppc_lwz(b->jit_ptr,ppc_r0,PPC_STACK_DECREMENTER+PPC_RET_ADDR_OFFSET,ppc_r1); ppc_mtlr(b->jit_ptr,ppc_r0); ppc_blr(b->jit_ptr); } /* Execute JIT code */ static forced_inline void mips64_jit_tcb_exec(cpu_mips_t *cpu,mips64_jit_tcb_t *block) { register insn_tblock_fptr jit_code __asm__("r12"); m_uint32_t offset; offset = (cpu->pc & MIPS_MIN_PAGE_IMASK) >> 2; jit_code = (insn_tblock_fptr)block->jit_insn_ptr[offset]; if (unlikely(!jit_code)) { mips64_exec_single_step(cpu,vmtoh32(block->mips_code[offset])); return; } /* Same as C call of jit_code(cpu_mips_t *cpu) except establish and destroy caller's stack frame here. r0, r3 - r10, r11, r12, ctr, xer, cr0 - cr5, cr6 - cr7 are volatile according to the ABI. CPU instance pointer passed through r3, also preserved onto stack. */ __asm__ __volatile__( "mtlr r12 \n" "mr r3, %1 \n" "lis r0, hi16(jit_ret) \n" "ori r0, r0, lo16(jit_ret)\n" "stw r3, %2(r1) \n" "stw r0, %3(r1) \n" "stwu r1, %4(r1) \n" "blr \n" "jit_ret: \n" "lwz r1, 0(r1) \n" :"+r"(jit_code):"r"(cpu),"i"(PPC_STACK_PARAM_OFFSET), "i"(PPC_RET_ADDR_OFFSET),"i"(-PPC_STACK_DECREMENTER) :"r0","r3","r4","r5","r6","r7","r8","r9","r10", "r11",/*"r12",*/"lr","ctr","xer","cr0","cr1","cr5", "cr6","cr7","memory"); } #endif dynamips-0.2.14/unstable/mips64_x86_trans.c000066400000000000000000002574551241034141600204570ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "tcb.h" #include "mips64_jit.h" #include "mips64_x86_trans.h" #include "mips64_cp0.h" #include "memory.h" /* Macros for CPU structure access */ #define REG_OFFSET(reg) (OFFSET(cpu_mips_t,gpr[(reg)])) #define CP0_REG_OFFSET(c0reg) (OFFSET(cpu_mips_t,cp0.reg[(c0reg)])) #define MEMOP_OFFSET(op) (OFFSET(cpu_mips_t,mem_op_fn[(op)])) #define DECLARE_INSN(name) \ static int mips64_emit_##name(cpu_mips_t *cpu,cpu_tc_t *b, \ mips_insn_t insn) /* Set an IRQ */ void mips64_set_irq(cpu_mips_t *cpu,m_uint8_t irq) { m_uint32_t m; m = (1 << (irq + MIPS_CP0_CAUSE_ISHIFT)) & MIPS_CP0_CAUSE_IMASK; atomic_or(&cpu->irq_cause,m); } /* Clear an IRQ */ void mips64_clear_irq(cpu_mips_t *cpu,m_uint8_t irq) { m_uint32_t m; m = (1 << (irq + MIPS_CP0_CAUSE_ISHIFT)) & MIPS_CP0_CAUSE_IMASK; atomic_and(&cpu->irq_cause,~m); if (!cpu->irq_cause) cpu->irq_pending = 0; } /* Load a 64 bit immediate value */ static inline void mips64_load_imm(cpu_tc_t *b, u_int hi_reg,u_int lo_reg, m_uint64_t value) { m_uint32_t hi_val = value >> 32; m_uint32_t lo_val = value & 0xffffffff; if (lo_val) x86_mov_reg_imm(b->jit_ptr,lo_reg,lo_val); else x86_alu_reg_reg(b->jit_ptr,X86_XOR,lo_reg,lo_reg); if (hi_val) x86_mov_reg_imm(b->jit_ptr,hi_reg,hi_val); else x86_alu_reg_reg(b->jit_ptr,X86_XOR,hi_reg,hi_reg); } /* Set the Pointer Counter (PC) register */ void mips64_set_pc(cpu_tc_t *b,m_uint64_t new_pc) { x86_mov_membase_imm(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,pc), new_pc & 0xFFFFFFFF,4); x86_mov_membase_imm(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,pc)+4, new_pc >> 32,4); } /* Set the Return Address (RA) register */ void mips64_set_ra(cpu_tc_t *b,m_uint64_t ret_pc) { mips64_load_imm(b,X86_EDX,X86_EAX,ret_pc); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(MIPS_GPR_RA), X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(MIPS_GPR_RA)+4, X86_EDX,4); } #if 0 /* * Try to branch directly to the specified JIT block without returning to * the main loop. */ static void mips64_try_direct_far_jump(cpu_mips_t *cpu,cpu_tc_t *b, m_uint64_t new_pc) { m_uint64_t new_page; m_uint32_t pc_hash,pc_offset; u_char *test1,*test2,*test3,*test4,*test5; new_page = new_pc & MIPS_MIN_PAGE_MASK; pc_offset = (new_pc & MIPS_MIN_PAGE_IMASK) >> 2; pc_hash = mips64_jit_get_virt_hash(new_pc); /* Get generic CPU pointer */ x86_mov_reg_membase(b->jit_ptr,X86_ESI,X86_EDI,OFFSET(cpu_mips_t,gen),4); /* Get JIT block info in %edx */ x86_mov_reg_membase(b->jit_ptr,X86_EBX, X86_ESI,OFFSET(cpu_gen_t,tb_virt_hash),4); x86_mov_reg_membase(b->jit_ptr,X86_EDX,X86_EBX,pc_hash*sizeof(void *),4); /* no JIT block found ? */ x86_test_reg_reg(b->jit_ptr,X86_EDX,X86_EDX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_Z, 0, 1); /* Check block virtual address (lower 32-bits first) */ x86_mov_reg_imm(b->jit_ptr,X86_EAX,(m_uint32_t)new_page); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EAX,X86_EDX, OFFSET(cpu_tb_t,vaddr)); test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Check higher bits... */ x86_mov_reg_imm(b->jit_ptr,X86_ECX,new_page >> 32); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_ECX,X86_EDX, OFFSET(cpu_tb_t,vaddr)+4); test3 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Get pointer to the Translated Code block */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDX,OFFSET(cpu_tb_t,tc),4); x86_test_reg_reg(b->jit_ptr,X86_EBX,X86_EBX); test4 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_Z, 0, 1); /* Jump to the code */ x86_mov_reg_membase(b->jit_ptr,X86_ESI, X86_EBX,OFFSET(cpu_tc_t,jit_insn_ptr),4); x86_mov_reg_membase(b->jit_ptr,X86_EAX, X86_ESI,pc_offset * sizeof(void *),4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test5 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_Z, 0, 1); x86_jump_reg(b->jit_ptr,X86_EAX); /* Returns to caller... */ x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); x86_patch(test3,b->jit_ptr); x86_patch(test4,b->jit_ptr); x86_patch(test5,b->jit_ptr); mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); } #endif /* Set Jump */ static void mips64_set_jump(cpu_mips_t *cpu,cpu_tc_t *b, m_uint64_t new_pc,int local_jump) { int return_to_caller = FALSE; u_char *jump_ptr; if (cpu->sym_trace && !local_jump) return_to_caller = TRUE; if (!return_to_caller && mips64_jit_tcb_local_addr(b,new_pc,&jump_ptr)) { if (jump_ptr) { x86_jump_code(b->jit_ptr,jump_ptr); } else { /* Never jump directly to code in a delay slot */ if (mips64_jit_is_delay_slot(b,new_pc)) { mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); return; } mips64_jit_tcb_record_patch(cpu,b,b->jit_ptr,new_pc); x86_jump32(b->jit_ptr,0); } } else { if (0/*cpu->exec_blk_direct_jump*/) { /* Block lookup optimization */ //mips64_try_direct_far_jump(cpu,b,new_pc); } else { mips64_set_pc(b,new_pc); mips64_jit_tcb_push_epilog(b); } } } /* Basic C call */ static forced_inline void mips64_emit_basic_c_call(cpu_tc_t *b,void *f) { x86_mov_reg_imm(b->jit_ptr,X86_EBX,f); x86_call_reg(b->jit_ptr,X86_EBX); } /* Emit a simple call to a C function without any parameter */ static void mips64_emit_c_call(cpu_tc_t *b,void *f) { mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); mips64_emit_basic_c_call(b,f); } /* Single-step operation */ void mips64_emit_single_step(cpu_tc_t *b,mips_insn_t insn) { x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); x86_mov_reg_imm(b->jit_ptr,X86_EDX,insn); mips64_emit_basic_c_call(b,mips64_exec_single_step); } /* Fast memory operation prototype */ typedef void (*memop_fast_access)(cpu_tc_t *b,int target); /* Fast LW */ static void mips64_memop_fast_lw(cpu_tc_t *b,int target) { x86_mov_reg_memindex(b->jit_ptr,X86_EAX,X86_EAX,0,X86_EBX,0,4); x86_bswap(b->jit_ptr,X86_EAX); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(target),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(target)+4,X86_EDX,4); } /* Fast SW */ static void mips64_memop_fast_sw(cpu_tc_t *b,int target) { x86_mov_reg_membase(b->jit_ptr,X86_EDX,X86_EDI,REG_OFFSET(target),4); x86_bswap(b->jit_ptr,X86_EDX); x86_mov_memindex_reg(b->jit_ptr,X86_EAX,0,X86_EBX,0,X86_EDX,4); } /* Fast memory operation (64-bit) */ static void mips64_emit_memop_fast64(cpu_tc_t *b,int write_op, int opcode,int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { m_uint64_t val = sign_extend(offset,16); u_char *test1,*test2,*test3,*p_exit; test3 = NULL; /* ECX:EBX = sign-extended offset */ mips64_load_imm(b,X86_ECX,X86_EBX,val); /* ECX:EBX = GPR[base] + sign-extended offset */ x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EBX,X86_EDI,REG_OFFSET(base)); x86_alu_reg_membase(b->jit_ptr,X86_ADC,X86_ECX,X86_EDI,REG_OFFSET(base)+4); /* EAX = mts64_entry index */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EBX,4); x86_mov_reg_reg(b->jit_ptr,X86_ESI,X86_EBX,4); x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_EAX,MTS64_HASH_SHIFT1); x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_ESI,MTS64_HASH_SHIFT2); x86_alu_reg_reg(b->jit_ptr,X86_XOR,X86_EAX,X86_ESI); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_EAX,MTS64_HASH_MASK); /* EDX = mts64_entry */ x86_mov_reg_membase(b->jit_ptr,X86_EDX, X86_EDI,OFFSET(cpu_mips_t,mts_u.mts64_cache), 4); x86_shift_reg_imm(b->jit_ptr,X86_SHL,X86_EAX,5); x86_alu_reg_reg(b->jit_ptr,X86_ADD,X86_EDX,X86_EAX); /* Compare virtual page address (ESI = vpage) */ x86_mov_reg_reg(b->jit_ptr,X86_ESI,X86_EBX,4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ESI,MIPS_MIN_PAGE_MASK); /* Compare the high part of the vaddr */ x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_ECX,X86_EDX, OFFSET(mts64_entry_t,gvpa)+4); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); /* Compare the low part of the vaddr */ x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_ESI,X86_EDX, OFFSET(mts64_entry_t,gvpa)); test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); /* Test if we are writing to a COW page */ if (write_op) { x86_test_membase_imm(b->jit_ptr,X86_EDX,OFFSET(mts64_entry_t,flags), MTS_FLAG_WRCATCH); test3 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); } /* EBX = offset in page, EAX = Host Page Address */ x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_EBX,MIPS_MIN_PAGE_IMASK); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDX,OFFSET(mts64_entry_t,hpa),4); /* Memory access */ op_handler(b,target); p_exit = b->jit_ptr; x86_jump8(b->jit_ptr,0); /* === Slow lookup === */ x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); if (test3) x86_patch(test3,b->jit_ptr); /* Update PC (ECX:EBX = vaddr) */ x86_mov_reg_reg(b->jit_ptr,X86_ESI,X86_EBX,4); mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); x86_mov_reg_reg(b->jit_ptr,X86_EDX,X86_ESI,4); /* EBX = target register */ x86_mov_reg_imm(b->jit_ptr,X86_EBX,target); /* EAX = CPU instance pointer */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); /* * Push parameters on stack and call memory function. * Keep the stack aligned on a 16-byte boundary for Darwin/x86. */ x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,8); x86_push_reg(b->jit_ptr,X86_EBX); x86_call_membase(b->jit_ptr,X86_EDI,MEMOP_OFFSET(opcode)); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); x86_patch(p_exit,b->jit_ptr); } /* Fast memory operation (32-bit) */ static void mips64_emit_memop_fast32(cpu_tc_t *b,int write_op, int opcode,int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { m_uint32_t val = sign_extend(offset,16); u_char *test1,*test2,*p_exit; test2 = NULL; /* EBX = sign-extended offset */ x86_mov_reg_imm(b->jit_ptr,X86_EBX,val); /* EBX = GPR[base] + sign-extended offset */ x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EBX,X86_EDI,REG_OFFSET(base)); /* EAX = mts32_entry index */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EBX,4); x86_mov_reg_reg(b->jit_ptr,X86_ESI,X86_EBX,4); x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_EAX,MTS32_HASH_SHIFT1); x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_ESI,MTS32_HASH_SHIFT2); x86_alu_reg_reg(b->jit_ptr,X86_XOR,X86_EAX,X86_ESI); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_EAX,MTS32_HASH_MASK); /* EDX = mts32_entry */ x86_mov_reg_membase(b->jit_ptr,X86_EDX, X86_EDI,OFFSET(cpu_mips_t,mts_u.mts32_cache), 4); x86_shift_reg_imm(b->jit_ptr,X86_SHL,X86_EAX,4); x86_alu_reg_reg(b->jit_ptr,X86_ADD,X86_EDX,X86_EAX); /* Compare virtual page address (ESI = vpage) */ x86_mov_reg_reg(b->jit_ptr,X86_ESI,X86_EBX,4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ESI,PPC32_MIN_PAGE_MASK); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_ESI,X86_EDX, OFFSET(mts32_entry_t,gvpa)); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); /* Test if we are writing to a COW page */ if (write_op) { x86_test_membase_imm(b->jit_ptr,X86_EDX,OFFSET(mts32_entry_t,flags), MTS_FLAG_WRCATCH); test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); } /* EBX = offset in page, EAX = Host Page Address */ x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_EBX,PPC32_MIN_PAGE_IMASK); x86_mov_reg_membase(b->jit_ptr,X86_EAX, X86_EDX,OFFSET(mts32_entry_t,hpa),4); /* Memory access */ op_handler(b,target); p_exit = b->jit_ptr; x86_jump8(b->jit_ptr,0); /* === Slow lookup === */ x86_patch(test1,b->jit_ptr); if (test2) x86_patch(test2,b->jit_ptr); /* Update PC (EBX = vaddr) */ mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); /* Sign-extend virtual address and put vaddr in ECX:EDX */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EBX,4); x86_cdq(b->jit_ptr); x86_mov_reg_reg(b->jit_ptr,X86_ECX,X86_EDX,4); x86_mov_reg_reg(b->jit_ptr,X86_EDX,X86_EAX,4); /* EBX = target register */ x86_mov_reg_imm(b->jit_ptr,X86_EBX,target); /* EAX = CPU instance pointer */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); /* * Push parameters on stack and call memory function. * Keep the stack aligned on a 16-byte boundary for Darwin/x86. */ x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,8); x86_push_reg(b->jit_ptr,X86_EBX); x86_call_membase(b->jit_ptr,X86_EDI,MEMOP_OFFSET(opcode)); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); x86_patch(p_exit,b->jit_ptr); } /* Fast memory operation */ static void mips64_emit_memop_fast(cpu_mips_t *cpu,cpu_tc_t *b, int write_op,int opcode, int base,int offset, int target,int keep_ll_bit, memop_fast_access op_handler) { switch(cpu->addr_mode) { case 32: mips64_emit_memop_fast32(b,write_op,opcode,base,offset,target, keep_ll_bit,op_handler); break; case 64: mips64_emit_memop_fast64(b,write_op,opcode,base,offset,target, keep_ll_bit,op_handler); break; } } /* Memory operation */ static void mips64_emit_memop(cpu_tc_t *b,int op,int base,int offset, int target,int keep_ll_bit) { m_uint64_t val = sign_extend(offset,16); /* Save PC for exception handling */ mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); if (!keep_ll_bit) { x86_clear_reg(b->jit_ptr,X86_EAX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,ll_bit), X86_EAX,4); } /* ECX:EDX = sign-extended offset */ mips64_load_imm(b,X86_ECX,X86_EDX,val); /* ECX:EDX = GPR[base] + sign-extended offset */ x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EDX,X86_EDI,REG_OFFSET(base)); x86_alu_reg_membase(b->jit_ptr,X86_ADC,X86_ECX,X86_EDI,REG_OFFSET(base)+4); /* EBX = target register */ x86_mov_reg_imm(b->jit_ptr,X86_EBX,target); /* EAX = CPU instance pointer */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); /* * Push parameters on stack and call memory function. * Keep the stack aligned on a 16-byte boundary for Darwin/x86. */ x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,8); x86_push_reg(b->jit_ptr,X86_EBX); x86_call_membase(b->jit_ptr,X86_EDI,MEMOP_OFFSET(op)); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); } /* Coprocessor Register transfert operation */ static void mips64_emit_cp_xfr_op(cpu_tc_t *b,int rt,int rd,void *f) { /* update pc */ mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); /* cp0 register */ x86_mov_reg_imm(b->jit_ptr,X86_ECX,rd); /* gpr */ x86_mov_reg_imm(b->jit_ptr,X86_EDX,rt); /* cpu instance */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,f); } /* Virtual Breakpoint */ void mips64_emit_breakpoint(cpu_tc_t *b) { x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,12); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_c_call(b,mips64_run_breakpoint); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); } /* Unknown opcode handler */ static asmlinkage void mips64_unknown_opcode(cpu_mips_t *cpu,m_uint32_t opcode) { printf("MIPS64: unhandled opcode 0x%8.8x at 0x%llx (ra=0x%llx)\n", opcode,cpu->pc,cpu->gpr[MIPS_GPR_RA]); mips64_dump_regs(cpu->gen); } /* Emit unhandled instruction code */ static int mips64_emit_unknown(cpu_mips_t *cpu,cpu_tc_t *b, mips_insn_t opcode) { x86_mov_reg_imm(b->jit_ptr,X86_EAX,opcode); x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,4); x86_push_reg(b->jit_ptr,X86_EAX); x86_push_reg(b->jit_ptr,X86_EDI); mips64_emit_c_call(b,mips64_unknown_opcode); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); return(0); } /* Invalid delay slot handler */ static fastcall void mips64_invalid_delay_slot(cpu_mips_t *cpu) { printf("MIPS64: invalid instruction in delay slot at 0x%llx (ra=0x%llx)\n", cpu->pc,cpu->gpr[MIPS_GPR_RA]); mips64_dump_regs(cpu->gen); /* Halt the virtual CPU */ cpu->pc = 0; } /* Emit unhandled instruction code */ int mips64_emit_invalid_delay_slot(cpu_tc_t *b) { x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,12); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_c_call(b,mips64_invalid_delay_slot); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); mips64_jit_tcb_push_epilog(b); return(0); } /* * Increment count register and trigger the timer IRQ if value in compare * register is the same. */ void mips64_inc_cp0_count_reg(cpu_tc_t *b) { x86_inc_membase(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,cp0_virt_cnt_reg)); } /* Check if there are pending IRQ */ void mips64_check_pending_irq(cpu_tc_t *b) { u_char *test1; /* Check the pending IRQ flag */ x86_mov_reg_membase(b->jit_ptr,X86_EAX, X86_EDI,OFFSET(cpu_mips_t,irq_pending),4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_Z, 0, 1); /* Save PC */ mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); /* Trigger the IRQ */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_trigger_irq); mips64_jit_tcb_push_epilog(b); x86_patch(test1,b->jit_ptr); } /* Increment the number of executed instructions (performance debugging) */ void mips64_inc_perf_counter(cpu_tc_t *b) { x86_inc_membase(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,perf_counter)); } /* ADD */ DECLARE_INSN(ADD) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); /* TODO: Exception handling */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* ADDI */ DECLARE_INSN(ADDI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); /* TODO: Exception handling */ x86_mov_reg_imm(b->jit_ptr,X86_EAX,val & 0xffffffff); x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EAX,X86_EDI,REG_OFFSET(rs)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_EDX,4); return(0); } /* ADDIU */ DECLARE_INSN(ADDIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); x86_mov_reg_imm(b->jit_ptr,X86_EAX,val & 0xffffffff); x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EAX,X86_EDI,REG_OFFSET(rs)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_EDX,4); return(0); } /* ADDU */ DECLARE_INSN(ADDU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* AND */ DECLARE_INSN(AND) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_AND,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_alu_reg_membase(b->jit_ptr,X86_AND,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* ANDI */ DECLARE_INSN(ANDI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); x86_mov_reg_imm(b->jit_ptr,X86_EAX,imm); x86_alu_reg_reg(b->jit_ptr,X86_XOR,X86_EBX,X86_EBX); x86_alu_reg_membase(b->jit_ptr,X86_AND,X86_EAX,X86_EDI,REG_OFFSET(rs)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_EBX,4); return(0); } /* B (Branch, virtual instruction) */ DECLARE_INSN(B) { int offset = bits(insn,0,15); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); return(0); } /* BAL (Branch and Link, virtual instruction) */ DECLARE_INSN(BAL) { int offset = bits(insn,0,15); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->vaddr + ((b->trans_pos + 1) << 2)); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,0); return(0); } /* BEQ (Branch On Equal) */ DECLARE_INSN(BEQ) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. * compare the low 32 bits first (higher probability). */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EAX,X86_EDI,REG_OFFSET(rt)); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NE, 0, 1); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NE, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BEQL (Branch On Equal Likely) */ DECLARE_INSN(BEQL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. * compare the low 32 bits first (higher probability). */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EAX,X86_EDI,REG_OFFSET(rt)); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NE, 0, 1); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NE, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); return(0); } /* BEQZ (Branch On Equal Zero - optimization) */ DECLARE_INSN(BEQZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] with 0. * compare the low 32 bits first (higher probability). */ x86_alu_membase_imm(b->jit_ptr,X86_CMP,X86_EDI,REG_OFFSET(rs),0); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NE, 0, 1); x86_alu_membase_imm(b->jit_ptr,X86_CMP,X86_EDI,REG_OFFSET(rs)+4,0); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NE, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BNEZ (Branch On Not Equal Zero - optimization) */ DECLARE_INSN(BNEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] with 0. * compare the low 32 bits first (higher probability). */ x86_alu_membase_imm(b->jit_ptr,X86_CMP,X86_EDI,REG_OFFSET(rs),0); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); x86_alu_membase_imm(b->jit_ptr,X86_CMP,X86_EDI,REG_OFFSET(rs)+4,0); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_E, 0, 1); x86_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test2,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZ (Branch On Greater or Equal Than Zero) */ DECLARE_INSN(BGEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* If sign bit is set, don't take the branch */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZAL (Branch On Greater or Equal Than Zero And Link) */ DECLARE_INSN(BGEZAL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->vaddr + ((b->trans_pos + 1) << 2)); /* If sign bit is set, don't take the branch */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGEZALL (Branch On Greater or Equal Than Zero and Link Likely) */ DECLARE_INSN(BGEZALL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->vaddr + ((b->trans_pos + 1) << 2)); /* if sign bit is set, don't take the branch */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); return(0); } /* BGEZL (Branch On Greater or Equal Than Zero Likely) */ DECLARE_INSN(BGEZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* if sign bit is set, don't take the branch */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_S, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); return(0); } /* BGTZ (Branch On Greater Than Zero) */ DECLARE_INSN(BGTZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2,*test3; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the hi word of gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_S, 0, 1); test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); /* test the lo word of gpr[rs] (here hi word = 0) */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs),4); x86_test_reg_reg(b->jit_ptr,X86_EBX,X86_EBX); test3 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_Z, 0, 1); /* here, we take the branch */ x86_patch(test2,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); x86_patch(test3,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BGTZL (Branch On Greater Than Zero Likely) */ DECLARE_INSN(BGTZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2,*test3; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the hi word of gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_S, 0, 1); test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NZ, 0, 1); /* test the lo word of gpr[rs] (here hi word = 0) */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs),4); x86_test_reg_reg(b->jit_ptr,X86_EBX,X86_EBX); test3 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_Z, 0, 1); /* here, we take the branch */ x86_patch(test2,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); x86_patch(test3,b->jit_ptr); return(0); } /* BLEZ (Branch On Less or Equal Than Zero) */ DECLARE_INSN(BLEZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2,*test3; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the hi word of gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_S, 0, 1); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NZ, 0, 1); /* test the lo word of gpr[rs] (here hi word = 0) */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs),4); x86_test_reg_reg(b->jit_ptr,X86_EBX,X86_EBX); test3 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NZ, 0, 1); /* here, we take the branch */ x86_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test2,b->jit_ptr); x86_patch(test3,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLEZL (Branch On Less or Equal Than Zero Likely) */ DECLARE_INSN(BLEZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1,*test2,*test3; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the hi word of gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_S, 0, 1); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NZ, 0, 1); /* test the lo word of gpr[rs] (here hi word = 0) */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs),4); x86_test_reg_reg(b->jit_ptr,X86_EBX,X86_EBX); test3 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NZ, 0, 1); /* here, we take the branch */ x86_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test2,b->jit_ptr); x86_patch(test3,b->jit_ptr); return(0); } /* BLTZ (Branch On Less Than Zero) */ DECLARE_INSN(BLTZ) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the sign bit of gpr[rs], if set, take the branch. */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLTZAL (Branch On Less Than Zero And Link) */ DECLARE_INSN(BLTZAL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->vaddr + ((b->trans_pos + 1) << 2)); /* * test the sign bit of gpr[rs], if set, take the branch. */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BLTZALL (Branch On Less Than Zero And Link Likely) */ DECLARE_INSN(BLTZALL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* set the return address (instruction after the delay slot) */ mips64_set_ra(b,b->vaddr + ((b->trans_pos + 1) << 2)); /* * test the sign bit of gpr[rs], if set, take the branch. */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); return(0); } /* BLTZL (Branch On Less Than Zero Likely) */ DECLARE_INSN(BLTZL) { int rs = bits(insn,21,25); int offset = bits(insn,0,15); u_char *test1; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * test the sign bit of gpr[rs], if set, take the branch. */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs)+4,4); x86_test_reg_reg(b->jit_ptr,X86_EAX,X86_EAX); test1 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NS, 0, 1); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test1,b->jit_ptr); return(0); } /* BNE (Branch On Not Equal) */ DECLARE_INSN(BNE) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. * compare the low 32 bits first (higher probability). */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EAX,X86_EDI,REG_OFFSET(rt)); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_E, 0, 1); x86_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,2); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test2,b->jit_ptr); /* if the branch is not taken, we have to execute the delay slot too */ mips64_jit_fetch_and_emit(cpu,b,1); return(0); } /* BNEL (Branch On Not Equal Likely) */ DECLARE_INSN(BNEL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); u_char *test1,*test2; m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc += sign_extend(offset << 2,18); /* * compare gpr[rs] and gpr[rt]. * compare the low 32 bits first (higher probability). */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EAX,X86_EDI,REG_OFFSET(rt)); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_E, 0, 1); x86_patch(test1,b->jit_ptr); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); x86_patch(test2,b->jit_ptr); return(0); } /* BREAK */ DECLARE_INSN(BREAK) { u_int code = bits(insn,6,25); x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,12); x86_mov_reg_imm(b->jit_ptr,X86_EDX,code); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_exec_break); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); mips64_jit_tcb_push_epilog(b); return(0); } /* CACHE */ DECLARE_INSN(CACHE) { int base = bits(insn,21,25); int op = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_CACHE,base,offset,op,FALSE); return(0); } /* CFC0 */ DECLARE_INSN(CFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_cfc0); return(0); } /* CTC0 */ DECLARE_INSN(CTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_ctc0); return(0); } /* DADDIU */ DECLARE_INSN(DADDIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); mips64_load_imm(b,X86_EBX,X86_EAX,val); x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EAX,X86_EDI,REG_OFFSET(rs)); x86_alu_reg_membase(b->jit_ptr,X86_ADC,X86_EBX,X86_EDI,REG_OFFSET(rs)+4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_EBX,4); return(0); } /* DADDU: rd = rs + rt */ DECLARE_INSN(DADDU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_ADD,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_alu_reg_membase(b->jit_ptr,X86_ADC,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* DIV */ DECLARE_INSN(DIV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); /* eax = gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_cdq(b->jit_ptr); /* ebx = gpr[rt] */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt),4); /* eax = quotient (LO), edx = remainder (HI) */ x86_div_reg(b->jit_ptr,X86_EBX,1); /* store LO */ x86_mov_reg_reg(b->jit_ptr,X86_ECX,X86_EDX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo)+4,X86_EDX,4); /* store HI */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi)+4,X86_EDX,4); return(0); } /* DIVU */ DECLARE_INSN(DIVU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); /* eax = gpr[rs] */ x86_clear_reg(b->jit_ptr,X86_EDX); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); /* ebx = gpr[rt] */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt),4); /* eax = quotient (LO), edx = remainder (HI) */ x86_div_reg(b->jit_ptr,X86_EBX,0); /* store LO */ x86_mov_reg_reg(b->jit_ptr,X86_ECX,X86_EDX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo)+4,X86_EDX,4); /* store HI */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi)+4,X86_EDX,4); return(0); } /* DMFC0 */ DECLARE_INSN(DMFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_dmfc0); return(0); } /* DMFC1 */ DECLARE_INSN(DMFC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_dmfc1); return(0); } /* DMTC0 */ DECLARE_INSN(DMTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_dmtc0); return(0); } /* DMTC1 */ DECLARE_INSN(DMTC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_dmtc1); return(0); } /* DSLL */ DECLARE_INSN(DSLL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt)+4,4); x86_shld_reg_imm(b->jit_ptr,X86_EBX,X86_EAX,sa); x86_shift_reg_imm(b->jit_ptr,X86_SHL,X86_EAX,sa); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* DSLL32 */ DECLARE_INSN(DSLL32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_shift_reg_imm(b->jit_ptr,X86_SHL,X86_EAX,sa); x86_clear_reg(b->jit_ptr,X86_EDX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EDX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EAX,4); return(0); } /* DSLLV */ DECLARE_INSN(DSLLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt)+4,4); x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ECX,0x3f); x86_shld_reg(b->jit_ptr,X86_EBX,X86_EAX); x86_shift_reg(b->jit_ptr,X86_SHL,X86_EAX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* DSRA */ DECLARE_INSN(DSRA) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt)+4,4); x86_shrd_reg_imm(b->jit_ptr,X86_EAX,X86_EBX,sa); x86_shift_reg_imm(b->jit_ptr,X86_SAR,X86_EBX,sa); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* DSRA32 */ DECLARE_INSN(DSRA32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt)+4,4); x86_shift_reg_imm(b->jit_ptr,X86_SAR,X86_EAX,sa); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* DSRAV */ DECLARE_INSN(DSRAV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt)+4,4); x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ECX,0x3f); x86_shrd_reg(b->jit_ptr,X86_EAX,X86_EBX); x86_shift_reg(b->jit_ptr,X86_SAR,X86_EBX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* DSRL */ DECLARE_INSN(DSRL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt)+4,4); x86_shrd_reg_imm(b->jit_ptr,X86_EAX,X86_EBX,sa); x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_EBX,sa); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* DSRL32 */ DECLARE_INSN(DSRL32) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt)+4,4); x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_EAX,sa); x86_clear_reg(b->jit_ptr,X86_EDX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* DSRLV */ DECLARE_INSN(DSRLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt)+4,4); x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ECX,0x3f); x86_shrd_reg(b->jit_ptr,X86_EAX,X86_EBX); x86_shift_reg(b->jit_ptr,X86_SHR,X86_EBX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* DSUBU: rd = rs - rt */ DECLARE_INSN(DSUBU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_SUB,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_alu_reg_membase(b->jit_ptr,X86_SBB,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* ERET */ DECLARE_INSN(ERET) { mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_exec_eret); mips64_jit_tcb_push_epilog(b); return(0); } /* J (Jump) */ DECLARE_INSN(J) { u_int instr_index = bits(insn,0,25); m_uint64_t new_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc &= ~((1 << 28) - 1); new_pc |= instr_index << 2; /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,1); return(0); } /* JAL (Jump And Link) */ DECLARE_INSN(JAL) { u_int instr_index = bits(insn,0,25); m_uint64_t new_pc,ret_pc; /* compute the new pc */ new_pc = b->vaddr + (b->trans_pos << 2); new_pc &= ~((1 << 28) - 1); new_pc |= instr_index << 2; /* set the return address (instruction after the delay slot) */ ret_pc = b->vaddr + ((b->trans_pos + 1) << 2); mips64_set_ra(b,ret_pc); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc in cpu structure */ mips64_set_jump(cpu,b,new_pc,0); return(0); } /* JALR (Jump and Link Register) */ DECLARE_INSN(JALR) { int rs = bits(insn,21,25); int rd = bits(insn,11,15); m_uint64_t ret_pc; /* set the return pc (instruction after the delay slot) in GPR[rd] */ ret_pc = b->vaddr + ((b->trans_pos + 1) << 2); mips64_load_imm(b,X86_EBX,X86_EAX,ret_pc); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); /* get the new pc */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EDX,X86_EDI,REG_OFFSET(rs)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,ret_pc),X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,ret_pc)+4, X86_EDX,4); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc */ x86_mov_reg_membase(b->jit_ptr,X86_ECX, X86_EDI,OFFSET(cpu_mips_t,ret_pc),4); x86_mov_reg_membase(b->jit_ptr,X86_EDX, X86_EDI,OFFSET(cpu_mips_t,ret_pc)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,pc),X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,pc)+4,X86_EDX,4); /* returns to the caller which will determine the next path */ mips64_jit_tcb_push_epilog(b); return(0); } /* JR (Jump Register) */ DECLARE_INSN(JR) { int rs = bits(insn,21,25); /* get the new pc */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EDX,X86_EDI,REG_OFFSET(rs)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,ret_pc),X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,ret_pc)+4, X86_EDX,4); /* insert the instruction in the delay slot */ mips64_jit_fetch_and_emit(cpu,b,1); /* set the new pc */ x86_mov_reg_membase(b->jit_ptr,X86_ECX, X86_EDI,OFFSET(cpu_mips_t,ret_pc),4); x86_mov_reg_membase(b->jit_ptr,X86_EDX, X86_EDI,OFFSET(cpu_mips_t,ret_pc)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,pc),X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,pc)+4,X86_EDX,4); /* returns to the caller which will determine the next path */ mips64_jit_tcb_push_epilog(b); return(0); } /* LB (Load Byte) */ DECLARE_INSN(LB) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LB,base,offset,rt,TRUE); return(0); } /* LBU (Load Byte Unsigned) */ DECLARE_INSN(LBU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LBU,base,offset,rt,TRUE); return(0); } /* LD (Load Double-Word) */ DECLARE_INSN(LD) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LD,base,offset,rt,TRUE); return(0); } /* LDC1 (Load Double-Word to Coprocessor 1) */ DECLARE_INSN(LDC1) { int base = bits(insn,21,25); int ft = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDC1,base,offset,ft,TRUE); return(0); } /* LDL (Load Double-Word Left) */ DECLARE_INSN(LDL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDL,base,offset,rt,TRUE); return(0); } /* LDR (Load Double-Word Right) */ DECLARE_INSN(LDR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LDR,base,offset,rt,TRUE); return(0); } /* LH (Load Half-Word) */ DECLARE_INSN(LH) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LH,base,offset,rt,TRUE); return(0); } /* LHU (Load Half-Word Unsigned) */ DECLARE_INSN(LHU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LHU,base,offset,rt,TRUE); return(0); } /* LI (virtual) */ DECLARE_INSN(LI) { int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); x86_mov_membase_imm(b->jit_ptr,X86_EDI,REG_OFFSET(rt),val & 0xffffffff,4); x86_mov_membase_imm(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,val >> 32,4); return(0); } /* LL (Load Linked) */ DECLARE_INSN(LL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LL,base,offset,rt,TRUE); return(0); } /* LUI */ DECLARE_INSN(LUI) { int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16) << 16; mips64_load_imm(b,X86_EBX,X86_EAX,val); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_EBX,4); return(0); } /* LW (Load Word) */ DECLARE_INSN(LW) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,0,MIPS_MEMOP_LW,base,offset,rt,TRUE, mips64_memop_fast_lw); } else { mips64_emit_memop(b,MIPS_MEMOP_LW,base,offset,rt,TRUE); } return(0); } /* LWL (Load Word Left) */ DECLARE_INSN(LWL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LWL,base,offset,rt,TRUE); return(0); } /* LWR (Load Word Right) */ DECLARE_INSN(LWR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LWR,base,offset,rt,TRUE); return(0); } /* LWU (Load Word Unsigned) */ DECLARE_INSN(LWU) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_LWU,base,offset,rt,TRUE); return(0); } /* MFC0 */ DECLARE_INSN(MFC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_mfc0); return(0); } /* MFC1 */ DECLARE_INSN(MFC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_mfc1); return(0); } /* MFHI */ DECLARE_INSN(MFHI) { int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,OFFSET(cpu_mips_t,hi),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,OFFSET(cpu_mips_t,hi)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* MFLO */ DECLARE_INSN(MFLO) { int rd = bits(insn,11,15); if (!rd) return(0); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,OFFSET(cpu_mips_t,lo),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,OFFSET(cpu_mips_t,lo)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* MOVE (virtual instruction, real: ADDU) */ DECLARE_INSN(MOVE) { int rs = bits(insn,21,25); int rd = bits(insn,11,15); if (rs != 0) { x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); } else { x86_alu_reg_reg(b->jit_ptr,X86_XOR,X86_EBX,X86_EBX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EBX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); } return(0); } /* MOVZ */ DECLARE_INSN(MOVZ) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); u_char *test1,*test2; /* * compare gpr[rt] to 0. */ x86_alu_membase_imm(b->jit_ptr,X86_CMP,X86_EDI,REG_OFFSET(rt),0); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); x86_alu_membase_imm(b->jit_ptr,X86_CMP,X86_EDI,REG_OFFSET(rt)+4,0); test2 = b->jit_ptr; x86_branch32(b->jit_ptr, X86_CC_NE, 0, 1); /* gpr[rd] <- gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EBX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_ECX,4); x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); return(0); } /* MTC0 */ DECLARE_INSN(MTC0) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_cp0_exec_mtc0); return(0); } /* MTC1 */ DECLARE_INSN(MTC1) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); mips64_emit_cp_xfr_op(b,rt,rd,mips64_exec_mtc1); return(0); } /* MTHI */ DECLARE_INSN(MTHI) { int rs = bits(insn,21,25); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi)+4,X86_EBX,4); return(0); } /* MTLO */ DECLARE_INSN(MTLO) { int rs = bits(insn,21,25); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo)+4,X86_EBX,4); return(0); } /* MUL */ DECLARE_INSN(MUL) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt),4); x86_mul_reg(b->jit_ptr,X86_EBX,1); /* store result in gpr[rd] */ x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* MULT */ DECLARE_INSN(MULT) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt),4); x86_mul_reg(b->jit_ptr,X86_EBX,1); /* store LO */ x86_mov_reg_reg(b->jit_ptr,X86_ECX,X86_EDX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo)+4,X86_EDX,4); /* store HI */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi)+4,X86_EDX,4); return(0); } /* MULTU */ DECLARE_INSN(MULTU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rt),4); x86_mul_reg(b->jit_ptr,X86_EBX,0); /* store LO */ x86_mov_reg_reg(b->jit_ptr,X86_ECX,X86_EDX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,lo)+4,X86_EDX,4); /* store HI */ x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_ECX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,OFFSET(cpu_mips_t,hi)+4,X86_EDX,4); return(0); } /* NOP */ DECLARE_INSN(NOP) { //x86_nop(b->jit_ptr); return(0); } /* NOR */ DECLARE_INSN(NOR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_OR,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_alu_reg_membase(b->jit_ptr,X86_OR,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); x86_not_reg(b->jit_ptr,X86_EAX); x86_not_reg(b->jit_ptr,X86_EBX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* OR */ DECLARE_INSN(OR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_OR,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_alu_reg_membase(b->jit_ptr,X86_OR,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* ORI */ DECLARE_INSN(ORI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = imm; x86_mov_reg_imm(b->jit_ptr,X86_EAX,val & 0xffff); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_OR,X86_EAX,X86_EDI,REG_OFFSET(rs)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_EBX,4); return(0); } /* PREF */ DECLARE_INSN(PREF) { x86_nop(b->jit_ptr); return(0); } /* PREFI */ DECLARE_INSN(PREFI) { x86_nop(b->jit_ptr); return(0); } /* SB (Store Byte) */ DECLARE_INSN(SB) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SB,base,offset,rt,FALSE); return(0); } /* SC (Store Conditional) */ DECLARE_INSN(SC) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SC,base,offset,rt,TRUE); return(0); } /* SD (Store Double-Word) */ DECLARE_INSN(SD) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SD,base,offset,rt,FALSE); return(0); } /* SDL (Store Double-Word Left) */ DECLARE_INSN(SDL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDL,base,offset,rt,FALSE); return(0); } /* SDR (Store Double-Word Right) */ DECLARE_INSN(SDR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDR,base,offset,rt,FALSE); return(0); } /* SDC1 (Store Double-Word from Coprocessor 1) */ DECLARE_INSN(SDC1) { int base = bits(insn,21,25); int ft = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SDC1,base,offset,ft,FALSE); return(0); } /* SH (Store Half-Word) */ DECLARE_INSN(SH) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SH,base,offset,rt,FALSE); return(0); } /* SLL */ DECLARE_INSN(SLL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_shift_reg_imm(b->jit_ptr,X86_SHL,X86_EAX,sa); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SLLV */ DECLARE_INSN(SLLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ECX,0x1f); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_shift_reg(b->jit_ptr,X86_SHL,X86_EAX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SLT */ DECLARE_INSN(SLT) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); u_char *test1,*test2,*test3; /* edx:eax = gpr[rt] */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EDX,X86_EDI,REG_OFFSET(rt)+4,4); /* ebx:ecx = gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); /* we set rd to 1 when gpr[rs] < gpr[rt] */ x86_clear_reg(b->jit_ptr,X86_ESI); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_ESI,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_ESI,4); /* rs(high) > rt(high) => end */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_EBX,X86_EDX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_GT, 0, 1); /* rs(high) < rt(high) => set rd to 1 */ test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_LT, 0, 1); /* rs(high) == rt(high), rs(low) >= rt(low) => end */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_ECX,X86_EAX); test3 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_AE, 0, 1); /* set rd to 1 */ x86_patch(test2,b->jit_ptr); x86_inc_membase(b->jit_ptr,X86_EDI,REG_OFFSET(rd)); /* end */ x86_patch(test1,b->jit_ptr); x86_patch(test3,b->jit_ptr); return(0); } /* SLTI */ DECLARE_INSN(SLTI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); u_char *test1,*test2,*test3; /* we set rt to 1 when gpr[rs] < val, rt to 0 when gpr[rs] >= val */ /* edx:eax = val */ mips64_load_imm(b,X86_EDX,X86_EAX,val); /* ebx:ecx = gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); /* we set rt to 1 when gpr[rs] < val */ x86_clear_reg(b->jit_ptr,X86_ESI); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_ESI,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_ESI,4); /* rs(high) > val(high) => end */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_EBX,X86_EDX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_GT, 0, 1); /* rs(high) < val(high) => set rt to 1 */ test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_LT, 0, 1); /* rs(high) == val(high), rs(low) >= val(low) => set rt to 0 */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_ECX,X86_EAX); test3 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_AE, 0, 1); /* set rt to 1 */ x86_patch(test2,b->jit_ptr); x86_inc_membase(b->jit_ptr,X86_EDI,REG_OFFSET(rt)); /* end */ x86_patch(test1,b->jit_ptr); x86_patch(test3,b->jit_ptr); return(0); } /* SLTIU */ DECLARE_INSN(SLTIU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); u_char *test1,*test2,*test3; /* edx:eax = val */ mips64_load_imm(b,X86_EDX,X86_EAX,val); /* ebx:ecx = gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); /* we set rt to 1 when gpr[rs] < val */ x86_clear_reg(b->jit_ptr,X86_ESI); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_ESI,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_ESI,4); /* rs(high) > val(high) => end */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_EBX,X86_EDX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_A, 0, 0); /* rs(high) < val(high) => set rt to 1 */ test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_B, 0, 0); /* rs(high) == val(high), rs(low) >= val(low) => end */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_ECX,X86_EAX); test3 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_AE, 0, 1); /* set rt to 1 */ x86_patch(test2,b->jit_ptr); x86_inc_membase(b->jit_ptr,X86_EDI,REG_OFFSET(rt)); /* end */ x86_patch(test1,b->jit_ptr); x86_patch(test3,b->jit_ptr); return(0); } /* SLTU */ DECLARE_INSN(SLTU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); u_char *test1,*test2,*test3; /* edx:eax = gpr[rt] */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_mov_reg_membase(b->jit_ptr,X86_EDX,X86_EDI,REG_OFFSET(rt)+4,4); /* ebx:ecx = gpr[rs] */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); /* we set rd to 1 when gpr[rs] < gpr[rt] */ x86_clear_reg(b->jit_ptr,X86_ESI); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_ESI,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_ESI,4); /* rs(high) > rt(high) => end */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_EBX,X86_EDX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_A, 0, 0); /* rs(high) < rt(high) => set rd to 1 */ test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_B, 0, 0); /* rs(high) == rt(high), rs(low) >= rt(low) => end */ x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_ECX,X86_EAX); test3 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_AE, 0, 1); /* set rd to 1 */ x86_patch(test2,b->jit_ptr); x86_inc_membase(b->jit_ptr,X86_EDI,REG_OFFSET(rd)); /* end */ x86_patch(test1,b->jit_ptr); x86_patch(test3,b->jit_ptr); return(0); } /* SRA */ DECLARE_INSN(SRA) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_shift_reg_imm(b->jit_ptr,X86_SAR,X86_EAX,sa); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SRAV */ DECLARE_INSN(SRAV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ECX,0x1f); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_shift_reg(b->jit_ptr,X86_SAR,X86_EAX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SRL */ DECLARE_INSN(SRL) { int rt = bits(insn,16,20); int rd = bits(insn,11,15); int sa = bits(insn,6,10); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_EAX,sa); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_clear_reg(b->jit_ptr,X86_EDX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SRLV */ DECLARE_INSN(SRLV) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ECX,0x1f); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rt),4); x86_shift_reg(b->jit_ptr,X86_SHR,X86_EAX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_clear_reg(b->jit_ptr,X86_EDX); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SUB */ DECLARE_INSN(SUB) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); /* TODO: Exception handling */ x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_SUB,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SUBU */ DECLARE_INSN(SUBU) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_SUB,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_cdq(b->jit_ptr); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EDX,4); return(0); } /* SW (Store Word) */ DECLARE_INSN(SW) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); if (cpu->fast_memop) { mips64_emit_memop_fast(cpu,b,1,MIPS_MEMOP_SW,base,offset,rt,FALSE, mips64_memop_fast_sw); } else { mips64_emit_memop(b,MIPS_MEMOP_SW,base,offset,rt,FALSE); } return(0); } /* SWL (Store Word Left) */ DECLARE_INSN(SWL) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SWL,base,offset,rt,FALSE); return(0); } /* SWR (Store Word Right) */ DECLARE_INSN(SWR) { int base = bits(insn,21,25); int rt = bits(insn,16,20); int offset = bits(insn,0,15); mips64_emit_memop(b,MIPS_MEMOP_SWR,base,offset,rt,FALSE); return(0); } /* SYNC */ DECLARE_INSN(SYNC) { return(0); } /* SYSCALL */ DECLARE_INSN(SYSCALL) { mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_exec_syscall); mips64_jit_tcb_push_epilog(b); return(0); } /* TEQ (Trap If Equal) */ DECLARE_INSN(TEQ) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); u_char *test1,*test2; /* Compare low part */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_ECX,X86_EDI,REG_OFFSET(rt)); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Compare high part */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_CMP,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Generate trap exception */ x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,12); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_c_call(b,mips64_trigger_trap_exception); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); mips64_jit_tcb_push_epilog(b); /* end */ x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); return(0); } /* TEQI (Trap If Equal Immediate) */ DECLARE_INSN(TEQI) { int rs = bits(insn,21,25); int imm = bits(insn,0,15); m_uint64_t val = sign_extend(imm,16); u_char *test1,*test2; /* edx:eax = val */ mips64_load_imm(b,X86_EDX,X86_EAX,val); /* Compare low part */ x86_mov_reg_membase(b->jit_ptr,X86_ECX,X86_EDI,REG_OFFSET(rs),4); x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_ECX,X86_EAX); test1 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Compare high part */ x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_reg(b->jit_ptr,X86_CMP,X86_EBX,X86_EDX); test2 = b->jit_ptr; x86_branch8(b->jit_ptr, X86_CC_NE, 0, 1); /* Generate trap exception */ x86_alu_reg_imm(b->jit_ptr,X86_SUB,X86_ESP,12); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_c_call(b,mips64_trigger_trap_exception); x86_alu_reg_imm(b->jit_ptr,X86_ADD,X86_ESP,12); mips64_jit_tcb_push_epilog(b); /* end */ x86_patch(test1,b->jit_ptr); x86_patch(test2,b->jit_ptr); return(0); } /* TLBP */ DECLARE_INSN(TLBP) { mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbp); return(0); } /* TLBR */ DECLARE_INSN(TLBR) { mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbr); return(0); } /* TLBWI */ DECLARE_INSN(TLBWI) { mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbwi); return(0); } /* TLBWR */ DECLARE_INSN(TLBWR) { mips64_set_pc(b,b->vaddr+((b->trans_pos-1)<<2)); x86_mov_reg_reg(b->jit_ptr,X86_EAX,X86_EDI,4); mips64_emit_basic_c_call(b,mips64_cp0_exec_tlbwr); return(0); } /* XOR */ DECLARE_INSN(XOR) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int rd = bits(insn,11,15); x86_mov_reg_membase(b->jit_ptr,X86_EAX,X86_EDI,REG_OFFSET(rs),4); x86_mov_reg_membase(b->jit_ptr,X86_EBX,X86_EDI,REG_OFFSET(rs)+4,4); x86_alu_reg_membase(b->jit_ptr,X86_XOR,X86_EAX,X86_EDI,REG_OFFSET(rt)); x86_alu_reg_membase(b->jit_ptr,X86_XOR,X86_EBX,X86_EDI,REG_OFFSET(rt)+4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rd)+4,X86_EBX,4); return(0); } /* XORI */ DECLARE_INSN(XORI) { int rs = bits(insn,21,25); int rt = bits(insn,16,20); int imm = bits(insn,0,15); m_uint64_t val = imm; mips64_load_imm(b,X86_EBX,X86_EAX,val); x86_alu_reg_membase(b->jit_ptr,X86_XOR,X86_EAX,X86_EDI,REG_OFFSET(rs)); x86_alu_reg_membase(b->jit_ptr,X86_XOR,X86_EBX,X86_EDI,REG_OFFSET(rs)+4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt),X86_EAX,4); x86_mov_membase_reg(b->jit_ptr,X86_EDI,REG_OFFSET(rt)+4,X86_EBX,4); return(0); } /* MIPS instruction array */ struct mips64_insn_tag mips64_insn_tags[] = { { mips64_emit_LI , 0xffe00000 , 0x24000000, 1 }, /* virtual */ { mips64_emit_MOVE , 0xfc1f07ff , 0x00000021, 1 }, /* virtual */ { mips64_emit_B , 0xffff0000 , 0x10000000, 0 }, /* virtual */ { mips64_emit_BAL , 0xffff0000 , 0x04110000, 0 }, /* virtual */ { mips64_emit_BEQZ , 0xfc1f0000 , 0x10000000, 0 }, /* virtual */ { mips64_emit_BNEZ , 0xfc1f0000 , 0x14000000, 0 }, /* virtual */ { mips64_emit_ADD , 0xfc0007ff , 0x00000020, 1 }, { mips64_emit_ADDI , 0xfc000000 , 0x20000000, 1 }, { mips64_emit_ADDIU , 0xfc000000 , 0x24000000, 1 }, { mips64_emit_ADDU , 0xfc0007ff , 0x00000021, 1 }, { mips64_emit_AND , 0xfc0007ff , 0x00000024, 1 }, { mips64_emit_ANDI , 0xfc000000 , 0x30000000, 1 }, { mips64_emit_BEQ , 0xfc000000 , 0x10000000, 0 }, { mips64_emit_BEQL , 0xfc000000 , 0x50000000, 0 }, { mips64_emit_BGEZ , 0xfc1f0000 , 0x04010000, 0 }, { mips64_emit_BGEZAL , 0xfc1f0000 , 0x04110000, 0 }, { mips64_emit_BGEZALL , 0xfc1f0000 , 0x04130000, 0 }, { mips64_emit_BGEZL , 0xfc1f0000 , 0x04030000, 0 }, { mips64_emit_BGTZ , 0xfc1f0000 , 0x1c000000, 0 }, { mips64_emit_BGTZL , 0xfc1f0000 , 0x5c000000, 0 }, { mips64_emit_BLEZ , 0xfc1f0000 , 0x18000000, 0 }, { mips64_emit_BLEZL , 0xfc1f0000 , 0x58000000, 0 }, { mips64_emit_BLTZ , 0xfc1f0000 , 0x04000000, 0 }, { mips64_emit_BLTZAL , 0xfc1f0000 , 0x04100000, 0 }, { mips64_emit_BLTZALL , 0xfc1f0000 , 0x04120000, 0 }, { mips64_emit_BLTZL , 0xfc1f0000 , 0x04020000, 0 }, { mips64_emit_BNE , 0xfc000000 , 0x14000000, 0 }, { mips64_emit_BNEL , 0xfc000000 , 0x54000000, 0 }, { mips64_emit_BREAK , 0xfc00003f , 0x0000000d, 1 }, { mips64_emit_CACHE , 0xfc000000 , 0xbc000000, 1 }, { mips64_emit_CFC0 , 0xffe007ff , 0x40400000, 1 }, { mips64_emit_CTC0 , 0xffe007ff , 0x40600000, 1 }, { mips64_emit_DADDIU , 0xfc000000 , 0x64000000, 1 }, { mips64_emit_DADDU , 0xfc0007ff , 0x0000002d, 1 }, { mips64_emit_DIV , 0xfc00ffff , 0x0000001a, 1 }, { mips64_emit_DIVU , 0xfc00ffff , 0x0000001b, 1 }, { mips64_emit_DMFC0 , 0xffe007f8 , 0x40200000, 1 }, { mips64_emit_DMFC1 , 0xffe007ff , 0x44200000, 1 }, { mips64_emit_DMTC0 , 0xffe007f8 , 0x40a00000, 1 }, { mips64_emit_DMTC1 , 0xffe007ff , 0x44a00000, 1 }, { mips64_emit_DSLL , 0xffe0003f , 0x00000038, 1 }, { mips64_emit_DSLL32 , 0xffe0003f , 0x0000003c, 1 }, { mips64_emit_DSLLV , 0xfc0007ff , 0x00000014, 1 }, { mips64_emit_DSRA , 0xffe0003f , 0x0000003b, 1 }, { mips64_emit_DSRA32 , 0xffe0003f , 0x0000003f, 1 }, { mips64_emit_DSRAV , 0xfc0007ff , 0x00000017, 1 }, { mips64_emit_DSRL , 0xffe0003f , 0x0000003a, 1 }, { mips64_emit_DSRL32 , 0xffe0003f , 0x0000003e, 1 }, { mips64_emit_DSRLV , 0xfc0007ff , 0x00000016, 1 }, { mips64_emit_DSUBU , 0xfc0007ff , 0x0000002f, 1 }, { mips64_emit_ERET , 0xffffffff , 0x42000018, 0 }, { mips64_emit_J , 0xfc000000 , 0x08000000, 0 }, { mips64_emit_JAL , 0xfc000000 , 0x0c000000, 0 }, { mips64_emit_JALR , 0xfc1f003f , 0x00000009, 0 }, { mips64_emit_JR , 0xfc1ff83f , 0x00000008, 0 }, { mips64_emit_LB , 0xfc000000 , 0x80000000, 1 }, { mips64_emit_LBU , 0xfc000000 , 0x90000000, 1 }, { mips64_emit_LD , 0xfc000000 , 0xdc000000, 1 }, { mips64_emit_LDC1 , 0xfc000000 , 0xd4000000, 1 }, { mips64_emit_LDL , 0xfc000000 , 0x68000000, 1 }, { mips64_emit_LDR , 0xfc000000 , 0x6c000000, 1 }, { mips64_emit_LH , 0xfc000000 , 0x84000000, 1 }, { mips64_emit_LHU , 0xfc000000 , 0x94000000, 1 }, { mips64_emit_LL , 0xfc000000 , 0xc0000000, 1 }, { mips64_emit_LUI , 0xffe00000 , 0x3c000000, 1 }, { mips64_emit_LW , 0xfc000000 , 0x8c000000, 1 }, { mips64_emit_LWL , 0xfc000000 , 0x88000000, 1 }, { mips64_emit_LWR , 0xfc000000 , 0x98000000, 1 }, { mips64_emit_LWU , 0xfc000000 , 0x9c000000, 1 }, { mips64_emit_MFC0 , 0xffe007ff , 0x40000000, 1 }, { mips64_emit_CFC0 , 0xffe007ff , 0x40000001, 1 }, /* MFC0 / Set 1 */ { mips64_emit_MFC1 , 0xffe007ff , 0x44000000, 1 }, { mips64_emit_MFHI , 0xffff07ff , 0x00000010, 1 }, { mips64_emit_MFLO , 0xffff07ff , 0x00000012, 1 }, { mips64_emit_MOVZ , 0xfc0007ff , 0x0000000a, 1 }, { mips64_emit_MTC0 , 0xffe007ff , 0x40800000, 1 }, { mips64_emit_MTC1 , 0xffe007ff , 0x44800000, 1 }, { mips64_emit_MTHI , 0xfc1fffff , 0x00000011, 1 }, { mips64_emit_MTLO , 0xfc1fffff , 0x00000013, 1 }, { mips64_emit_MUL , 0xfc0007ff , 0x70000002, 1 }, { mips64_emit_MULT , 0xfc00ffff , 0x00000018, 1 }, { mips64_emit_MULTU , 0xfc00ffff , 0x00000019, 1 }, { mips64_emit_NOP , 0xffffffff , 0x00000000, 1 }, { mips64_emit_NOR , 0xfc0007ff , 0x00000027, 1 }, { mips64_emit_OR , 0xfc0007ff , 0x00000025, 1 }, { mips64_emit_ORI , 0xfc000000 , 0x34000000, 1 }, { mips64_emit_PREF , 0xfc000000 , 0xcc000000, 1 }, { mips64_emit_PREFI , 0xfc0007ff , 0x4c00000f, 1 }, { mips64_emit_SB , 0xfc000000 , 0xa0000000, 1 }, { mips64_emit_SC , 0xfc000000 , 0xe0000000, 1 }, { mips64_emit_SD , 0xfc000000 , 0xfc000000, 1 }, { mips64_emit_SDC1 , 0xfc000000 , 0xf4000000, 1 }, { mips64_emit_SDL , 0xfc000000 , 0xb0000000, 1 }, { mips64_emit_SDR , 0xfc000000 , 0xb4000000, 1 }, { mips64_emit_SH , 0xfc000000 , 0xa4000000, 1 }, { mips64_emit_SLL , 0xffe0003f , 0x00000000, 1 }, { mips64_emit_SLLV , 0xfc0007ff , 0x00000004, 1 }, { mips64_emit_SLT , 0xfc0007ff , 0x0000002a, 1 }, { mips64_emit_SLTI , 0xfc000000 , 0x28000000, 1 }, { mips64_emit_SLTIU , 0xfc000000 , 0x2c000000, 1 }, { mips64_emit_SLTU , 0xfc0007ff , 0x0000002b, 1 }, { mips64_emit_SRA , 0xffe0003f , 0x00000003, 1 }, { mips64_emit_SRAV , 0xfc0007ff , 0x00000007, 1 }, { mips64_emit_SRL , 0xffe0003f , 0x00000002, 1 }, { mips64_emit_SRLV , 0xfc0007ff , 0x00000006, 1 }, { mips64_emit_SUB , 0xfc0007ff , 0x00000022, 1 }, { mips64_emit_SUBU , 0xfc0007ff , 0x00000023, 1 }, { mips64_emit_SW , 0xfc000000 , 0xac000000, 1 }, { mips64_emit_SWL , 0xfc000000 , 0xa8000000, 1 }, { mips64_emit_SWR , 0xfc000000 , 0xb8000000, 1 }, { mips64_emit_SYNC , 0xfffff83f , 0x0000000f, 1 }, { mips64_emit_SYSCALL , 0xfc00003f , 0x0000000c, 1 }, { mips64_emit_TEQ , 0xfc00003f , 0x00000034, 1 }, { mips64_emit_TEQI , 0xfc1f0000 , 0x040c0000, 1 }, { mips64_emit_TLBP , 0xffffffff , 0x42000008, 1 }, { mips64_emit_TLBR , 0xffffffff , 0x42000001, 1 }, { mips64_emit_TLBWI , 0xffffffff , 0x42000002, 1 }, { mips64_emit_TLBWR , 0xffffffff , 0x42000006, 1 }, { mips64_emit_XOR , 0xfc0007ff , 0x00000026, 1 }, { mips64_emit_XORI , 0xfc000000 , 0x38000000, 1 }, { mips64_emit_unknown , 0x00000000 , 0x00000000, 1 }, { NULL , 0x00000000 , 0x00000000, 0 }, }; dynamips-0.2.14/unstable/mips64_x86_trans.h000066400000000000000000000027701241034141600204500ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __MIPS64_X86_TRANS_H__ #define __MIPS64_X86_TRANS_H__ #include "utils.h" #include "x86-codegen.h" #include "cpu.h" #include "mips64_exec.h" #include "dynamips.h" #define JIT_SUPPORT 1 /* Manipulate bitmasks atomically */ static forced_inline void atomic_or(m_uint32_t *v,m_uint32_t m) { __asm__ __volatile__("lock; orl %1,%0":"=m"(*v):"ir"(m),"m"(*v)); } static forced_inline void atomic_and(m_uint32_t *v,m_uint32_t m) { __asm__ __volatile__("lock; andl %1,%0":"=m"(*v):"ir"(m),"m"(*v)); } /* Wrappers to x86-codegen functions */ #define mips64_jit_tcb_set_patch x86_patch_fn #define mips64_jit_tcb_set_jump x86_jump_code_fn /* MIPS instruction array */ extern struct mips64_insn_tag mips64_insn_tags[]; /* Push epilog for an x86 instruction block */ static forced_inline void mips64_jit_tcb_push_epilog(cpu_tc_t *tc) { x86_ret(tc->jit_ptr); } /* Execute JIT code */ static forced_inline void mips64_jit_tcb_exec(cpu_mips_t *cpu,cpu_tb_t *tb) { insn_tblock_fptr jit_code; m_uint32_t offset,*iarray; offset = (cpu->pc & MIPS_MIN_PAGE_IMASK) >> 2; jit_code = (insn_tblock_fptr)tb->tc->jit_insn_ptr[offset]; if (unlikely(!jit_code)) { iarray = (m_uint32_t *)tb->target_code; mips64_exec_single_step(cpu,vmtoh32(iarray[offset])); return; } asm volatile ("movl %0,%%edi"::"r"(cpu): "esi","edi","eax","ebx","ecx","edx"); jit_code(); } #endif dynamips-0.2.14/unstable/mips_mts.c000066400000000000000000000421101241034141600172300ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Template code for MTS. */ #define MTS_ENTRY MTS_NAME(entry_t) #define MTS_CACHE(cpu) ( cpu->mts_u. MTS_NAME(cache) ) /* Forward declarations */ static forced_inline void *MTS_PROTO(access)(cpu_mips_t *cpu,m_uint64_t vaddr, u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data); static fastcall int MTS_PROTO(translate)(cpu_mips_t *cpu,m_uint64_t vaddr, m_uint32_t *phys_page); /* Initialize the MTS subsystem for the specified CPU */ int MTS_PROTO(init)(cpu_mips_t *cpu) { size_t len; /* Initialize the cache entries to 0 (empty) */ len = MTS_NAME_UP(HASH_SIZE) * sizeof(MTS_ENTRY); if (!(MTS_CACHE(cpu) = malloc(len))) return(-1); memset(MTS_CACHE(cpu),0xFF,len); cpu->mts_lookups = 0; cpu->mts_misses = 0; return(0); } /* Free memory used by MTS */ void MTS_PROTO(shutdown)(cpu_mips_t *cpu) { /* Free the cache itself */ free(MTS_CACHE(cpu)); MTS_CACHE(cpu) = NULL; } /* Show MTS detailed information (debugging only!) */ void MTS_PROTO(show_stats)(cpu_gen_t *gen_cpu) { cpu_mips_t *cpu = CPU_MIPS64(gen_cpu); #if DEBUG_MTS_MAP_VIRT MTS_ENTRY *entry; u_int i,count; #endif printf("\nCPU%u: MTS%d statistics:\n",cpu->gen->id,MTS_ADDR_SIZE); #if DEBUG_MTS_MAP_VIRT /* Valid hash entries */ for(count=0,i=0;igvpa & MTS_INV_ENTRY_MASK)) { printf(" %4u: vaddr=0x%8.8llx, paddr=0x%8.8llx, hpa=%p\n", i,(m_uint64_t)entry->gvpa,(m_uint64_t)entry->gppa, (void *)entry->hpa); count++; } } printf(" %u/%u valid hash entries.\n",count,MTS_NAME_UP(HASH_SIZE)); #endif printf(" Total lookups: %llu, misses: %llu, efficiency: %g%%\n", cpu->mts_lookups, cpu->mts_misses, 100 - ((double)(cpu->mts_misses*100)/ (double)cpu->mts_lookups)); } /* Invalidate the complete MTS cache */ void MTS_PROTO(invalidate_cache)(cpu_mips_t *cpu) { size_t len; len = MTS_NAME_UP(HASH_SIZE) * sizeof(MTS_ENTRY); memset(MTS_CACHE(cpu),0xFF,len); } /* * MTS mapping. * * It is NOT inlined since it triggers a GCC bug on my config (x86, GCC 3.3.5) */ static no_inline MTS_ENTRY * MTS_PROTO(map)(cpu_mips_t *cpu,u_int op_type,mts_map_t *map, MTS_ENTRY *entry,MTS_ENTRY *alt_entry) { cpu_tb_t *tb; struct vdevice *dev; m_uint32_t offset; m_iptr_t host_ptr; m_uint32_t exec_flag = 0; int cow; if (!(dev = dev_lookup(cpu->vm,map->paddr,map->cached))) return NULL; if (cpu->gen->tb_phys_hash != NULL) { tb = mips64_jit_find_by_phys_page(cpu,map->paddr >> VM_PAGE_SHIFT); if ((tb != NULL) && !(tb->flags & TB_FLAG_SMC)) exec_flag = MTS_FLAG_EXEC; } if (dev->flags & VDEVICE_FLAG_SPARSE) { host_ptr = dev_sparse_get_host_addr(cpu->vm,dev,map->paddr,op_type,&cow); entry->gvpa = map->vaddr; entry->gppa = map->paddr; entry->hpa = host_ptr; entry->flags = (cow) ? MTS_FLAG_COW : 0; entry->flags |= exec_flag | map->flags; return entry; } if (!dev->host_addr || (dev->flags & VDEVICE_FLAG_NO_MTS_MMAP)) { offset = (map->paddr + map->offset) - dev->phys_addr; /* device entries are never stored in virtual TLB */ alt_entry->hpa = (dev->id << MTS_DEVID_SHIFT) + offset; alt_entry->flags = MTS_FLAG_DEV | map->flags; return alt_entry; } entry->gvpa = map->vaddr; entry->gppa = map->paddr; entry->hpa = dev->host_addr + (map->paddr - dev->phys_addr); entry->flags = exec_flag | map->flags; return entry; } /* Invalidate TCB related to a physical page marked as executable */ static void MTS_PROTO(invalidate_tcb)(cpu_mips_t *cpu,MTS_ENTRY *entry) { m_uint32_t hp,phys_page,pc_phys_page; if (cpu->gen->tb_phys_hash == NULL) return; phys_page = entry->gppa >> VM_PAGE_SHIFT; hp = mips64_jit_get_phys_hash(phys_page); cpu->translate(cpu,cpu->pc,&pc_phys_page); entry->flags &= ~MTS_FLAG_EXEC; cpu_jit_write_on_exec_page(cpu->gen,phys_page,hp,pc_phys_page); } /* MTS lookup */ static void *MTS_PROTO(lookup)(cpu_mips_t *cpu,m_uint64_t vaddr) { m_uint64_t data; return(MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LOOKUP,4,MTS_READ,&data)); } /* MTS instruction fetch */ static void *MTS_PROTO(ifetch)(cpu_mips_t *cpu,m_uint64_t vaddr) { m_uint64_t data; return(MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_IFETCH,4,MTS_READ,&data)); } /* === MIPS Memory Operations ============================================= */ /* LB: Load Byte */ fastcall void MTS_PROTO(lb)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LB,1,MTS_READ,&data); if (likely(haddr != NULL)) data = *(m_uint8_t *)haddr; cpu->gpr[reg] = sign_extend(data,8); } /* LBU: Load Byte Unsigned */ fastcall void MTS_PROTO(lbu)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LBU,1,MTS_READ,&data); if (likely(haddr != NULL)) data = *(m_uint8_t *)haddr; cpu->gpr[reg] = data & 0xff; } /* LH: Load Half-Word */ fastcall void MTS_PROTO(lh)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LH,2,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh16(*(m_uint16_t *)haddr); cpu->gpr[reg] = sign_extend(data,16); } /* LHU: Load Half-Word Unsigned */ fastcall void MTS_PROTO(lhu)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LHU,2,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh16(*(m_uint16_t *)haddr); cpu->gpr[reg] = data & 0xffff; } /* LW: Load Word */ fastcall void MTS_PROTO(lw)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LW,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); cpu->gpr[reg] = sign_extend(data,32); } /* LWU: Load Word Unsigned */ fastcall void MTS_PROTO(lwu)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LWU,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); cpu->gpr[reg] = data & 0xffffffff; } /* LD: Load Double-Word */ fastcall void MTS_PROTO(ld)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LD,8,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); cpu->gpr[reg] = data; } /* SB: Store Byte */ fastcall void MTS_PROTO(sb)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->gpr[reg] & 0xff; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_SB,1,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint8_t *)haddr = data; } /* SH: Store Half-Word */ fastcall void MTS_PROTO(sh)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->gpr[reg] & 0xffff; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_SH,2,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint16_t *)haddr = htovm16(data); } /* SW: Store Word */ fastcall void MTS_PROTO(sw)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->gpr[reg] & 0xffffffff; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_SW,4,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint32_t *)haddr = htovm32(data); } /* SD: Store Double-Word */ fastcall void MTS_PROTO(sd)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->gpr[reg]; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_SD,8,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint64_t *)haddr = htovm64(data); } /* LDC1: Load Double-Word To Coprocessor 1 */ fastcall void MTS_PROTO(ldc1)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LDC1,8,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); cpu->fpu.reg[reg] = data; } /* LWL: Load Word Left */ fastcall void MTS_PROTO(lwl)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t r_mask,naddr; m_uint64_t data; u_int m_shift; void *haddr; naddr = vaddr & ~(0x03); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_LWL,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); m_shift = (vaddr & 0x03) << 3; r_mask = (1ULL << m_shift) - 1; data <<= m_shift; cpu->gpr[reg] &= r_mask; cpu->gpr[reg] |= data; cpu->gpr[reg] = sign_extend(cpu->gpr[reg],32); } /* LWR: Load Word Right */ fastcall void MTS_PROTO(lwr)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t r_mask,naddr; m_uint64_t data; u_int m_shift; void *haddr; naddr = vaddr & ~(0x03); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_LWR,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); m_shift = ((vaddr & 0x03) + 1) << 3; r_mask = (1ULL << m_shift) - 1; data = sign_extend(data >> (32 - m_shift),32); r_mask = sign_extend(r_mask,32); cpu->gpr[reg] &= ~r_mask; cpu->gpr[reg] |= data; } /* LDL: Load Double-Word Left */ fastcall void MTS_PROTO(ldl)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t r_mask,naddr; m_uint64_t data; u_int m_shift; void *haddr; naddr = vaddr & ~(0x07); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_LDL,8,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); m_shift = (vaddr & 0x07) << 3; r_mask = (1ULL << m_shift) - 1; data <<= m_shift; cpu->gpr[reg] &= r_mask; cpu->gpr[reg] |= data; } /* LDR: Load Double-Word Right */ fastcall void MTS_PROTO(ldr)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t r_mask,naddr; m_uint64_t data; u_int m_shift; void *haddr; naddr = vaddr & ~(0x07); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_LDR,8,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); m_shift = ((vaddr & 0x07) + 1) << 3; r_mask = (1ULL << m_shift) - 1; data >>= (64 - m_shift); cpu->gpr[reg] &= ~r_mask; cpu->gpr[reg] |= data; } /* SWL: Store Word Left */ fastcall void MTS_PROTO(swl)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t d_mask,naddr; m_uint64_t data; u_int r_shift; void *haddr; naddr = vaddr & ~(0x03ULL); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SWL,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); r_shift = (vaddr & 0x03) << 3; d_mask = 0xffffffff >> r_shift; data &= ~d_mask; data |= (cpu->gpr[reg] & 0xffffffff) >> r_shift; haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SWL,4,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint32_t *)haddr = htovm32(data); } /* SWR: Store Word Right */ fastcall void MTS_PROTO(swr)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t d_mask,naddr; m_uint64_t data; u_int r_shift; void *haddr; naddr = vaddr & ~(0x03); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SWR,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); r_shift = ((vaddr & 0x03) + 1) << 3; d_mask = 0xffffffff >> r_shift; data &= d_mask; data |= (cpu->gpr[reg] << (32 - r_shift)) & 0xffffffff; haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SWR,4,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint32_t *)haddr = htovm32(data); } /* SDL: Store Double-Word Left */ fastcall void MTS_PROTO(sdl)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t d_mask,naddr; m_uint64_t data; u_int r_shift; void *haddr; naddr = vaddr & ~(0x07); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SDL,8,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); r_shift = (vaddr & 0x07) << 3; d_mask = 0xffffffffffffffffULL >> r_shift; data &= ~d_mask; data |= cpu->gpr[reg] >> r_shift; haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SDL,8,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint64_t *)haddr = htovm64(data); } /* SDR: Store Double-Word Right */ fastcall void MTS_PROTO(sdr)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t d_mask,naddr; m_uint64_t data; u_int r_shift; void *haddr; naddr = vaddr & ~(0x07); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SDR,8,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); r_shift = ((vaddr & 0x07) + 1) << 3; d_mask = 0xffffffffffffffffULL >> r_shift; data &= d_mask; data |= cpu->gpr[reg] << (64 - r_shift); haddr = MTS_PROTO(access)(cpu,naddr,MIPS_MEMOP_SDR,8,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint64_t *)haddr = htovm64(data); } /* LL: Load Linked */ fastcall void MTS_PROTO(ll)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_LL,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); cpu->gpr[reg] = sign_extend(data,32); cpu->ll_bit = 1; } /* SC: Store Conditional */ fastcall void MTS_PROTO(sc)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; if (cpu->ll_bit) { data = cpu->gpr[reg] & 0xffffffff; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_SC,4,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint32_t *)haddr = htovm32(data); } cpu->gpr[reg] = cpu->ll_bit; } /* SDC1: Store Double-Word from Coprocessor 1 */ fastcall void MTS_PROTO(sdc1)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->fpu.reg[reg]; haddr = MTS_PROTO(access)(cpu,vaddr,MIPS_MEMOP_SDC1,8,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint64_t *)haddr = htovm64(data); } /* CACHE: Cache operation */ fastcall void MTS_PROTO(cache)(cpu_mips_t *cpu,m_uint64_t vaddr,u_int op) { #if DEBUG_CACHE cpu_log(cpu->gen, "MTS","CACHE: PC=0x%llx, vaddr=0x%llx, cache=%u, code=%u\n", cpu->pc, vaddr, op & 0x3, op >> 2); #endif } /* === MTS Cache Management ============================================= */ void MTS_PROTO(api_rebuild)(cpu_gen_t *cpu) { MTS_PROTO(invalidate_cache)(CPU_MIPS64(cpu)); } /* ======================================================================== */ /* Initialize memory access vectors */ void MTS_PROTO(init_memop_vectors)(cpu_mips_t *cpu) { /* XXX TODO: * - LD/SD forbidden in Supervisor/User modes with 32-bit addresses. */ cpu->addr_mode = MTS_ADDR_SIZE; /* Memory lookup and Instruction fetch operations */ cpu->mem_op_lookup = MTS_PROTO(lookup); cpu->mem_op_ifetch = MTS_PROTO(ifetch); /* Translation operation */ cpu->translate = MTS_PROTO(translate); /* Invalidate and Shutdown operations */ cpu->mts_invalidate = MTS_PROTO(invalidate_cache); cpu->mts_shutdown = MTS_PROTO(shutdown); /* Rebuild MTS data structures */ cpu->gen->mts_rebuild = MTS_PROTO(api_rebuild); /* Show statistics */ cpu->gen->mts_show_stats = MTS_PROTO(show_stats); /* Load Operations */ cpu->mem_op_fn[MIPS_MEMOP_LB] = MTS_PROTO(lb); cpu->mem_op_fn[MIPS_MEMOP_LBU] = MTS_PROTO(lbu); cpu->mem_op_fn[MIPS_MEMOP_LH] = MTS_PROTO(lh); cpu->mem_op_fn[MIPS_MEMOP_LHU] = MTS_PROTO(lhu); cpu->mem_op_fn[MIPS_MEMOP_LW] = MTS_PROTO(lw); cpu->mem_op_fn[MIPS_MEMOP_LWU] = MTS_PROTO(lwu); cpu->mem_op_fn[MIPS_MEMOP_LD] = MTS_PROTO(ld); cpu->mem_op_fn[MIPS_MEMOP_LDL] = MTS_PROTO(ldl); cpu->mem_op_fn[MIPS_MEMOP_LDR] = MTS_PROTO(ldr); /* Store Operations */ cpu->mem_op_fn[MIPS_MEMOP_SB] = MTS_PROTO(sb); cpu->mem_op_fn[MIPS_MEMOP_SH] = MTS_PROTO(sh); cpu->mem_op_fn[MIPS_MEMOP_SW] = MTS_PROTO(sw); cpu->mem_op_fn[MIPS_MEMOP_SD] = MTS_PROTO(sd); /* Load Left/Right operations */ cpu->mem_op_fn[MIPS_MEMOP_LWL] = MTS_PROTO(lwl); cpu->mem_op_fn[MIPS_MEMOP_LWR] = MTS_PROTO(lwr); cpu->mem_op_fn[MIPS_MEMOP_LDL] = MTS_PROTO(ldl); cpu->mem_op_fn[MIPS_MEMOP_LDR] = MTS_PROTO(ldr); /* Store Left/Right operations */ cpu->mem_op_fn[MIPS_MEMOP_SWL] = MTS_PROTO(swl); cpu->mem_op_fn[MIPS_MEMOP_SWR] = MTS_PROTO(swr); cpu->mem_op_fn[MIPS_MEMOP_SDL] = MTS_PROTO(sdl); cpu->mem_op_fn[MIPS_MEMOP_SDR] = MTS_PROTO(sdr); /* LL/SC - Load Linked / Store Conditional */ cpu->mem_op_fn[MIPS_MEMOP_LL] = MTS_PROTO(ll); cpu->mem_op_fn[MIPS_MEMOP_SC] = MTS_PROTO(sc); /* Coprocessor 1 memory access functions */ cpu->mem_op_fn[MIPS_MEMOP_LDC1] = MTS_PROTO(ldc1); cpu->mem_op_fn[MIPS_MEMOP_SDC1] = MTS_PROTO(sdc1); /* Cache Operation */ cpu->mem_op_fn[MIPS_MEMOP_CACHE] = MTS_PROTO(cache); } #undef MTS_ADDR_SIZE #undef MTS_NAME #undef MTS_NAME_UP #undef MTS_PROTO #undef MTS_PROTO_UP #undef MTS_ENTRY #undef MTS_CHUNK dynamips-0.2.14/unstable/ppc-codegen.h000066400000000000000000001221431241034141600175730ustar00rootroot00000000000000/* The following codes come from ppc-codegen.h: Copyright (C) 2001 Radek Doulik for testing do the following: ./test | as -o test.o */ #ifndef __MONO_PPC_CODEGEN_H__ #define __MONO_PPC_CODEGEN_H__ #include typedef enum { ppc_r0 = 0, ppc_r1, ppc_sp = ppc_r1, ppc_r2, ppc_r3, ppc_r4, ppc_r5, ppc_r6, ppc_r7, ppc_r8, ppc_r9, ppc_r10, ppc_r11, ppc_r12, ppc_r13, ppc_r14, ppc_r15, ppc_r16, ppc_r17, ppc_r18, ppc_r19, ppc_r20, ppc_r21, ppc_r22, ppc_r23, ppc_r24, ppc_r25, ppc_r26, ppc_r27, ppc_r28, ppc_r29, ppc_r30, ppc_r31 } PPCIntRegister; typedef enum { ppc_f0 = 0, ppc_f1, ppc_f2, ppc_f3, ppc_f4, ppc_f5, ppc_f6, ppc_f7, ppc_f8, ppc_f9, ppc_f10, ppc_f11, ppc_f12, ppc_f13, ppc_f14, ppc_f15, ppc_f16, ppc_f17, ppc_f18, ppc_f19, ppc_f20, ppc_f21, ppc_f22, ppc_f23, ppc_f24, ppc_f25, ppc_f26, ppc_f27, ppc_f28, ppc_f29, ppc_f30, ppc_f31 } PPCFloatRegister; typedef enum { ppc_lr = 256, ppc_ctr = 256 + 32, ppc_xer = 32 } PPCSpecialRegister; enum { /* B0 operand for branches */ PPC_BR_DEC_CTR_NONZERO_FALSE = 0, PPC_BR_LIKELY = 1, /* can be or'ed with the conditional variants */ PPC_BR_DEC_CTR_ZERO_FALSE = 2, PPC_BR_FALSE = 4, PPC_BR_DEC_CTR_NONZERO_TRUE = 8, PPC_BR_DEC_CTR_ZERO_TRUE = 10, PPC_BR_TRUE = 12, PPC_BR_DEC_CTR_NONZERO = 16, PPC_BR_DEC_CTR_ZERO = 18, PPC_BR_ALWAYS = 20, /* B1 operand for branches */ PPC_BR_LT = 0, PPC_BR_GT = 1, PPC_BR_EQ = 2, PPC_BR_SO = 3, }; enum { PPC_TRAP_LT = 1, PPC_TRAP_GT = 2, PPC_TRAP_EQ = 4, PPC_TRAP_LT_UN = 8, PPC_TRAP_GT_UN = 16, PPC_TRAP_LE = 1 + PPC_TRAP_EQ, PPC_TRAP_GE = 2 + PPC_TRAP_EQ, PPC_TRAP_LE_UN = 8 + PPC_TRAP_EQ, PPC_TRAP_GE_UN = 16 + PPC_TRAP_EQ }; #define ppc_emit32(c,x) do { *((unsigned int *) (c)) = x; (c) = (unsigned char *)(c) + sizeof (unsigned int);} while (0) #define ppc_is_imm16(val) ((int)(val) >= (int)-(1<<15) && (int)(val) <= (int)((1<<15)-1)) #define ppc_is_uimm16(val) ((int)(val) >= 0 && (int)(val) <= 65535) #define ppc_load(c,D,v) do { \ if (ppc_is_imm16 ((v))) { \ ppc_li ((c), (D), (unsigned short)(v)); \ } else { \ ppc_lis ((c), (D), (unsigned int)(v) >> 16); \ ppc_ori ((c), (D), (D), (unsigned int)(v) & 0xffff); \ } \ } while (0) #define ppc_break(c) ppc_tw((c),31,0,0) #define ppc_addi(c,D,A,d) ppc_emit32 (c, (14 << 26) | ((D) << 21) | ((A) << 16) | (unsigned short)(d)) #define ppc_addis(c,D,A,d) ppc_emit32 (c, (15 << 26) | ((D) << 21) | ((A) << 16) | (unsigned short)(d)) #define ppc_li(c,D,v) ppc_addi (c, D, 0, (unsigned short)(v)); #define ppc_lis(c,D,v) ppc_addis (c, D, 0, (unsigned short)(v)); #define ppc_lwz(c,D,d,a) ppc_emit32 (c, (32 << 26) | ((D) << 21) | ((a) << 16) | (unsigned short)(d)) #define ppc_lhz(c,D,d,a) ppc_emit32 (c, (40 << 26) | ((D) << 21) | ((a) << 16) | (unsigned short)(d)) #define ppc_lbz(c,D,d,a) ppc_emit32 (c, (34 << 26) | ((D) << 21) | ((a) << 16) | (unsigned short)(d)) #define ppc_stw(c,S,d,a) ppc_emit32 (c, (36 << 26) | ((S) << 21) | ((a) << 16) | (unsigned short)(d)) #define ppc_sth(c,S,d,a) ppc_emit32 (c, (44 << 26) | ((S) << 21) | ((a) << 16) | (unsigned short)(d)) #define ppc_stb(c,S,d,a) ppc_emit32 (c, (38 << 26) | ((S) << 21) | ((a) << 16) | (unsigned short)(d)) #define ppc_stwu(c,s,d,a) ppc_emit32 (c, (37 << 26) | ((s) << 21) | ((a) << 16) | (unsigned short)(d)) #define ppc_or(c,a,s,b) ppc_emit32 (c, (31 << 26) | ((s) << 21) | ((a) << 16) | ((b) << 11) | 888) /* ori updated as pem.fm & MPCFPE32B - Zhe */ #define ppc_ori(c,A,S,UIMM) ppc_emit32 (c, (24 << 26) | ((S) << 21) | ((A) << 16) | (unsigned short)(UIMM)) #define ppc_mr(c,a,s) ppc_or (c, a, s, s) #define ppc_mfspr(c,D,spr) ppc_emit32 (c, (31 << 26) | ((D) << 21) | ((spr) << 11) | (339 << 1)) #define ppc_mflr(c,D) ppc_mfspr (c, D, ppc_lr) #define ppc_mtspr(c,spr,S) ppc_emit32 (c, (31 << 26) | ((S) << 21) | ((spr) << 11) | (467 << 1)) #define ppc_mtlr(c,S) ppc_mtspr (c, ppc_lr, S) #define ppc_mtctr(c,S) ppc_mtspr (c, ppc_ctr, S) #define ppc_mtxer(c,S) ppc_mtspr (c, ppc_xer, S) #define ppc_b(c,li) ppc_emit32 (c, (18 << 26) | ((li) << 2)) #define ppc_bl(c,li) ppc_emit32 (c, (18 << 26) | ((li) << 2) | 1) #define ppc_ba(c,li) ppc_emit32 (c, (18 << 26) | ((li) << 2) | 2) #define ppc_bla(c,li) ppc_emit32 (c, (18 << 26) | ((li) << 2) | 3) #define ppc_blrl(c) ppc_emit32 (c, 0x4e800021) #define ppc_blr(c) ppc_emit32 (c, 0x4e800020) #define ppc_lfs(c,D,d,A) ppc_emit32 (c, (48 << 26) | ((D) << 21) | ((A) << 16) | (unsigned short)(d)) #define ppc_lfd(c,D,d,A) ppc_emit32 (c, (50 << 26) | ((D) << 21) | ((A) << 16) | (unsigned short)(d)) #define ppc_stfs(c,S,d,a) ppc_emit32 (c, (52 << 26) | ((S) << 21) | ((a) << 16) | (unsigned short)(d)) #define ppc_stfd(c,S,d,a) ppc_emit32 (c, (54 << 26) | ((S) << 21) | ((a) << 16) | (unsigned short)(d)) /*********************************************************************** The macros below were tapped out by Christopher Taylor from 18 November 2002 to 19 December 2002. Special thanks to rodo, lupus, dietmar, miguel, and duncan for patience, and motivation. The macros found in this file are based on the assembler instructions found in Motorola and Digital DNA's: "Programming Enviornments Manual For 32-bit Implementations of the PowerPC Architecture" MPCFPE32B/AD 12/2001 REV2 see pages 326 - 524 for detailed information regarding each instruction Also see the "Ximian Copyright Agreement, 2002" for more information regarding my and Ximian's copyright to this code. ;) *************************************************************************/ #define ppc_addx(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | (OE << 10) | (266 << 1) | Rc) #define ppc_add(c,D,A,B) ppc_addx(c,D,A,B,0,0) #define ppc_addd(c,D,A,B) ppc_addx(c,D,A,B,0,1) #define ppc_addo(c,D,A,B) ppc_addx(c,D,A,B,1,0) #define ppc_addod(c,D,A,B) ppc_addx(c,D,A,B,1,1) #define ppc_addcx(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | (OE << 10) | (10 << 1) | Rc) #define ppc_addc(c,D,A,B) ppc_addcx(c,D,A,B,0,0) #define ppc_addcd(c,D,A,B) ppc_addcx(c,D,A,B,0,1) #define ppc_addco(c,D,A,B) ppc_addcx(c,D,A,B,1,0) #define ppc_addcod(c,D,A,B) ppc_addcx(c,D,A,B,1,1) #define ppc_addex(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | ((D) << 21) | ((A) << 16) | ((B) << 11) | (OE << 10) | (138 << 1) | Rc) #define ppc_adde(c,D,A,B) ppc_addex(c,D,A,B,0,0) #define ppc_added(c,D,A,B) ppc_addex(c,D,A,B,0,1) #define ppc_addeo(c,D,A,B) ppc_addex(c,D,A,B,1,0) #define ppc_addeod(c,D,A,B) ppc_addex(c,D,A,B,1,1) #define ppc_addic(c,D,A,d) ppc_emit32(c, (12 << 26) | ((D) << 21) | ((A) << 16) | (unsigned short)(d)) #define ppc_addicd(c,D,A,d) ppc_emit32(c, (13 << 26) | ((D) << 21) | ((A) << 16) | (unsigned short)(d)) #define ppc_addmex(c,D,A,OE,RC) ppc_emit32(c, (31 << 26) | ((D) << 21 ) | ((A) << 16) | (0 << 11) | ((OE) << 10) | (234 << 1) | RC) #define ppc_addme(c,D,A) ppc_addmex(c,D,A,0,0) #define ppc_addmed(c,D,A) ppc_addmex(c,D,A,0,1) #define ppc_addmeo(c,D,A) ppc_addmex(c,D,A,1,0) #define ppc_addmeod(c,D,A) ppc_addmex(c,D,A,1,1) #define ppc_addzex(c,D,A,OE,RC) ppc_emit32(c, (31 << 26) | ((D) << 21 ) | ((A) << 16) | (0 << 11) | ((OE) << 10) | (202 << 1) | RC) #define ppc_addze(c,D,A) ppc_addzex(c,D,A,0,0) #define ppc_addzed(c,D,A) ppc_addzex(c,D,A,0,1) #define ppc_addzeo(c,D,A) ppc_addzex(c,D,A,1,0) #define ppc_addzeod(c,D,A) ppc_addzex(c,D,A,1,1) /* and* updated as pem.fm & MPCFPE32B - Zhe */ #define ppc_andx(c,A,S,B,RC) ppc_emit32(c, (31 << 26) | ((S) << 21 ) | ((A) << 16) | ((B) << 11) | (28 << 1) | RC) #define ppc_and(c,A,S,B) ppc_andx(c,A,S,B,0) #define ppc_andd(c,A,S,B) ppc_andx(c,A,S,B,1) #define ppc_andcx(c,A,S,B,RC) ppc_emit32(c, (31 << 26) | ((S) << 21 ) | ((A) << 16) | ((B) << 11) | (60 << 1) | RC) #define ppc_andc(c,A,S,B) ppc_andcx(c,A,S,B,0) #define ppc_andcd(c,A,S,B) ppc_andcx(c,A,S,B,1) #define ppc_andid(c,A,S,d) ppc_emit32(c, (28 << 26) | ((S) << 21 ) | ((A) << 16) | ((unsigned short)(d))) #define ppc_andisd(c,A,S,d) ppc_emit32(c, (29 << 26) | ((S) << 21 ) | ((A) << 16) | ((unsigned short)(d))) #define ppc_bcx(c,BO,BI,BD,AA,LK) ppc_emit32(c, (16 << 26) | (BO << 21 )| (BI << 16) | ((BD) << 2) | ((AA) << 1) | LK) #define ppc_bc(c,BO,BI,BD) ppc_bcx(c,BO,BI,BD,0,0) #define ppc_bca(c,BO,BI,BD) ppc_bcx(c,BO,BI,BD,1,0) #define ppc_bcl(c,BO,BI,BD) ppc_bcx(c,BO,BI,BD,0,1) #define ppc_bcla(c,BO,BI,BD) ppc_bcx(c,BO,BI,BD,1,1) #define ppc_bcctrx(c,BO,BI,LK) ppc_emit32(c, (19 << 26) | (BO << 21 )| (BI << 16) | (0 << 11) | (528 << 1) | LK) #define ppc_bcctr(c,BO,BI) ppc_bcctrx(c,BO,BI,0) #define ppc_bcctrl(c,BO,BI) ppc_bcctrx(c,BO,BI,1) #define ppc_bnectrp(c,BO,BI) ppc_bcctr(c,BO,BI) #define ppc_bnectrlp(c,BO,BI) ppc_bcctr(c,BO,BI) #define ppc_bclrx(c,BO,BI,LK) ppc_emit32(c, (19 << 26) | (BO << 21 )| (BI << 16) | (0 << 11) | (16 << 1) | LK) #define ppc_bclr(c,BO,BI) ppc_bclrx(c,BO,BI,0) #define ppc_bclrl(c,BO,BI) ppc_bclrx(c,BO,BI,1) #define ppc_bnelrp(c,BO,BI) ppc_bclr(c,BO,BI) #define ppc_bnelrlp(c,BO,BI) ppc_bclr(c,BO,BI) #define ppc_cmp(c,cfrD,L,A,B) ppc_emit32(c, (31 << 26) | (cfrD << 23) | (0 << 22) | (L << 21) | (A << 16) | (B << 11) | (0x00000 << 1) | 0 ) #define ppc_cmpi(c,cfrD,L,A,B) ppc_emit32(c, (11 << 26) | (cfrD << 23) | (0 << 22) | (L << 21) | (A << 16) | (unsigned short)(B)) #define ppc_cmpl(c,cfrD,L,A,B) ppc_emit32(c, (31 << 26) | (cfrD << 23) | (0 << 22) | (L << 21) | (A << 16) | (B << 11) | (32 << 1) | 0 ) #define ppc_cmpli(c,cfrD,L,A,B) ppc_emit32(c, (10 << 26) | (cfrD << 23) | (0 << 22) | (L << 21) | (A << 16) | (unsigned short)(B)) /* cntlzw* updated as pem.fm & MPCFPE32B - Zhe */ #define ppc_cntlzwx(c,A,S,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (0 << 11) | (26 << 1) | Rc) #define ppc_cntlzw(c,A,S) ppc_cntlzwx(c,A,S,0) #define ppc_cntlzwd(c,A,S) ppc_cntlzwx(c,A,S,1) #define ppc_crand(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (257 << 1) | 0) #define ppc_crandc(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (129 << 1) | 0) #define ppc_creqv(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (289 << 1) | 0) #define ppc_crnand(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (225 << 1) | 0) #define ppc_crnor(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (33 << 1) | 0) #define ppc_cror(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (449 << 1) | 0) #define ppc_crorc(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (417 << 1) | 0) #define ppc_crxor(c,D,A,B) ppc_emit32(c, (19 << 26) | (D << 21) | (A << 16) | (B << 11) | (193 << 1) | 0) #define ppc_dcba(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (758 << 1) | 0) #define ppc_dcbf(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (86 << 1) | 0) #define ppc_dcbi(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (470 << 1) | 0) #define ppc_dcbst(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (54 << 1) | 0) /* dcbt updated as pem.fm.2.3 - Zhe */ #define ppc_dcbt(c,A,B,TH) ppc_emit32(c, (31 << 26) | (TH << 21) | (A << 16) | (B << 11) | (278 << 1) | 0) #define ppc_dcbtst(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (246 << 1) | 0) #define ppc_dcbz(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (1014 << 1) | 0) #define ppc_divwx(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (OE << 10) | (491 << 1) | Rc) #define ppc_divw(c,D,A,B) ppc_divwx(c,D,A,B,0,0) #define ppc_divwd(c,D,A,B) ppc_divwx(c,D,A,B,0,1) #define ppc_divwo(c,D,A,B) ppc_divwx(c,D,A,B,1,0) #define ppc_divwod(c,D,A,B) ppc_divwx(c,D,A,B,1,1) #define ppc_divwux(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (OE << 10) | (459 << 1) | Rc) #define ppc_divwu(c,D,A,B) ppc_divwux(c,D,A,B,0,0) #define ppc_divwud(c,D,A,B) ppc_divwux(c,D,A,B,0,1) #define ppc_divwuo(c,D,A,B) ppc_divwux(c,D,A,B,1,0) #define ppc_divwuod(c,D,A,B) ppc_divwux(c,D,A,B,1,1) #define ppc_eciwx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (310 << 1) | 0) #define ppc_ecowx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (438 << 1) | 0) #define ppc_eieio(c) ppc_emit32(c, (31 << 26) | (0 << 21) | (0 << 16) | (0 << 11) | (854 << 1) | 0) #define ppc_eqvx(c,A,S,B,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (284 << 1) | Rc) #define ppc_eqv(c,A,S,B) ppc_eqvx(c,A,S,B,0) #define ppc_eqvd(c,A,S,B) ppc_eqvx(c,A,S,B,1) #define ppc_extsbx(c,A,S,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (0 << 11) | (954 << 1) | Rc) #define ppc_extsb(c,A,S) ppc_extsbx(c,A,S,0) #define ppc_extsbd(c,A,S) ppc_extsbx(c,A,S,1) #define ppc_extshx(c,A,S,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (0 << 11) | (922 << 1) | Rc) #define ppc_extsh(c,A,S) ppc_extshx(c,A,S,0) #define ppc_extshd(c,A,S) ppc_extshx(c,A,S,1) #define ppc_fabsx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (264 << 1) | Rc) #define ppc_fabs(c,D,B) ppc_fabsx(c,D,B,0) #define ppc_fabsd(c,D,B) ppc_fabsx(c,D,B,1) #define ppc_faddx(c,D,A,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 6) | (21 << 1) | Rc) #define ppc_fadd(c,D,A,B) ppc_faddx(c,D,A,B,0) #define ppc_faddd(c,D,A,B) ppc_faddx(c,D,A,B,1) #define ppc_faddsx(c,D,A,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 6) | (21 << 1) | Rc) #define ppc_fadds(c,D,A,B) ppc_faddsx(c,D,A,B,0) #define ppc_faddsd(c,D,A,B) ppc_faddsx(c,D,A,B,1) #define ppc_fcmpo(c,crfD,A,B) ppc_emit32(c, (63 << 26) | (crfD << 23) | (0 << 21) | (A << 16) | (B << 11) | (32 << 1) | 0) #define ppc_fcmpu(c,crfD,A,B) ppc_emit32(c, (63 << 26) | (crfD << 23) | (0 << 21) | (A << 16) | (B << 11) | (0 << 1) | 0) #define ppc_fctiwx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (14 << 1) | Rc) #define ppc_fctiw(c,D,B) ppc_fctiwx(c,D,B,0) #define ppc_fctiwd(c,D,B) ppc_fctiwx(c,D,B,1) #define ppc_fctiwzx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (15 << 1) | Rc) #define ppc_fctiwz(c,D,B) ppc_fctiwzx(c,D,B,0) #define ppc_fctiwzd(c,D,B) ppc_fctiwzx(c,D,B,1) #define ppc_fdivx(c,D,A,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 6) | (18 << 1) | Rc) #define ppc_fdiv(c,D,A,B) ppc_fdivx(c,D,A,B,0) #define ppc_fdivd(c,D,A,B) ppc_fdivx(c,D,A,B,1) #define ppc_fdivsx(c,D,A,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 6) | (18 << 1) | Rc) #define ppc_fdivs(c,D,A,B) ppc_fdivsx(c,D,A,B,0) #define ppc_fdivsd(c,D,A,B) ppc_fdivsx(c,D,A,B,1) #define ppc_fmaddx(c,D,A,B,C,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (29 << 1) | Rc) #define ppc_fmadd(c,D,A,B,C) ppc_fmaddx(c,D,A,B,C,0) #define ppc_fmaddd(c,D,A,B,C) ppc_fmaddx(c,D,A,B,C,1) #define ppc_fmaddsx(c,D,A,B,C,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (29 << 1) | Rc) #define ppc_fmadds(c,D,A,B,C) ppc_fmaddsx(c,D,A,B,C,0) #define ppc_fmaddsd(c,D,A,B,C) ppc_fmaddsx(c,D,A,B,C,1) #define ppc_fmrx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (72 << 1) | Rc) #define ppc_fmr(c,D,B) ppc_fmrx(c,D,B,0) #define ppc_fmrd(c,D,B) ppc_fmrx(c,D,B,1) #define ppc_fmsubx(c,D,A,C,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (28 << 1) | Rc) #define ppc_fmsub(c,D,A,C,B) ppc_fmsubx(c,D,A,C,B,0) #define ppc_fmsubd(c,D,A,C,B) ppc_fmsubx(c,D,A,C,B,1) #define ppc_fmsubsx(c,D,A,C,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (28 << 1) | Rc) #define ppc_fmsubs(c,D,A,C,B) ppc_fmsubsx(c,D,A,C,B,0) #define ppc_fmsubsd(c,D,A,C,B) ppc_fmsubsx(c,D,A,C,B,1) #define ppc_fmulx(c,D,A,C,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (0 << 11) | (C << 6) | (25 << 1) | Rc) #define ppc_fmul(c,D,A,C) ppc_fmulx(c,D,A,C,0) #define ppc_fmuld(c,D,A,C) ppc_fmulx(c,D,A,C,1) #define ppc_fmulsx(c,D,A,C,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (0 << 11) | (C << 6) | (25 << 1) | Rc) #define ppc_fmuls(c,D,A,C) ppc_fmulsx(c,D,A,C,0) #define ppc_fmulsd(c,D,A,C) ppc_fmulsx(c,D,A,C,1) #define ppc_fnabsx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (136 << 1) | Rc) #define ppc_fnabs(c,D,B) ppc_fnabsx(c,D,B,0) #define ppc_fnabsd(c,D,B) ppc_fnabsx(c,D,B,1) #define ppc_fnegx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (40 << 1) | Rc) #define ppc_fneg(c,D,B) ppc_fnegx(c,D,B,0) #define ppc_fnegd(c,D,B) ppc_fnegx(c,D,B,1) #define ppc_fnmaddx(c,D,A,C,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (31 << 1) | Rc) #define ppc_fnmadd(c,D,A,C,B) ppc_fnmaddx(c,D,A,C,B,0) #define ppc_fnmaddd(c,D,A,C,B) ppc_fnmaddx(c,D,A,C,B,1) #define ppc_fnmaddsx(c,D,A,C,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (31 << 1) | Rc) #define ppc_fnmadds(c,D,A,C,B) ppc_fnmaddsx(c,D,A,C,B,0) #define ppc_fnmaddsd(c,D,A,C,B) ppc_fnmaddsx(c,D,A,C,B,1) #define ppc_fnmsubx(c,D,A,C,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (30 << 1) | Rc) #define ppc_fnmsub(c,D,A,C,B) ppc_fnmsubx(c,D,A,C,B,0) #define ppc_fnmsubd(c,D,A,C,B) ppc_fnmsubx(c,D,A,C,B,1) #define ppc_fnmsubsx(c,D,A,C,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (30 << 1) | Rc) #define ppc_fnmsubs(c,D,A,C,B) ppc_fnmsubsx(c,D,A,C,B,0) #define ppc_fnmsubsd(c,D,A,C,B) ppc_fnmsubsx(c,D,A,C,B,1) #define ppc_fresx(c,D,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (0 << 16) | (B << 11) | (0 << 6) | (24 << 1) | Rc) #define ppc_fres(c,D,B) ppc_fresx(c,D,B,0) #define ppc_fresd(c,D,B) ppc_fresx(c,D,B,1) #define ppc_frspx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (12 << 1) | Rc) #define ppc_frsp(c,D,B) ppc_frspx(c,D,B,0) #define ppc_frspd(c,D,B) ppc_frspx(c,D,B,1) #define ppc_frsqrtex(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (0 << 6) | (26 << 1) | Rc) #define ppc_frsqrte(c,D,B) ppc_frsqrtex(c,D,B,0) #define ppc_frsqrted(c,D,B) ppc_frsqrtex(c,D,B,1) #define ppc_fselx(c,D,A,C,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (C << 6) | (23 << 1) | Rc) #define ppc_fsel(c,D,A,C,B) ppc_fselx(c,D,A,C,B,0) #define ppc_fseld(c,D,A,C,B) ppc_fselx(c,D,A,C,B,1) #define ppc_fsqrtx(c,D,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (B << 11) | (0 << 6) | (22 << 1) | Rc) #define ppc_fsqrt(c,D,B) ppc_fsqrtx(c,D,B,0) #define ppc_fsqrtd(c,D,B) ppc_fsqrtx(c,D,B,1) #define ppc_fsqrtsx(c,D,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (0 << 16) | (B << 11) | (0 << 6) | (22 << 1) | Rc) #define ppc_fsqrts(c,D,B) ppc_fsqrtsx(c,D,B,0) #define ppc_fsqrtsd(c,D,B) ppc_fsqrtsx(c,D,B,1) #define ppc_fsubx(c,D,A,B,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 6) | (20 << 1) | Rc) #define ppc_fsub(c,D,A,B) ppc_fsubx(c,D,A,B,0) #define ppc_fsubd(c,D,A,B) ppc_fsubx(c,D,A,B,1) #define ppc_fsubsx(c,D,A,B,Rc) ppc_emit32(c, (59 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 6) | (20 << 1) | Rc) #define ppc_fsubs(c,D,A,B) ppc_fsubsx(c,D,A,B,0) #define ppc_fsubsd(c,D,A,B) ppc_fsubsx(c,D,A,B,1) #define ppc_icbi(c,A,B) ppc_emit32(c, (31 << 26) | (0 << 21) | (A << 16) | (B << 11) | (982 << 1) | 0) #define ppc_isync(c) ppc_emit32(c, (19 << 26) | (0 << 11) | (150 << 1) | 0) /* lbzu updated as pem.fm & MPCFPE32B - Zhe */ #define ppc_lbzu(c,D,d,A) ppc_emit32(c, (35 << 26) | (D << 21) | (A << 16) | (unsigned short)d) #define ppc_lbzux(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (119 << 1) | 0) #define ppc_lbzx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (87 << 1) | 0) /* lfdu updated as pem.fm & MPCFPE32B - Zhe */ #define ppc_lfdu(c,D,d,A) ppc_emit32(c, (51 << 26) | (D << 21) | (A << 16) | (unsigned short)d) #define ppc_lfdux(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (631 << 1) | 0) #define ppc_lfdx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (599 << 1) | 0) /* lfs* updated as pem.fm & MPCFPE32B - Zhe */ #define ppc_lfsu(c,D,d,A) ppc_emit32(c, (49 << 26) | (D << 21) | (A << 16) | (unsigned short)d) #define ppc_lfsux(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (567 << 1) | 0) #define ppc_lfsx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (535 << 1) | 0) /* lha, lhau, lhzu updated as pem.fm & MPCFPE32B - Zhe */ #define ppc_lha(c,D,d,A) ppc_emit32(c, (42 << 26) | (D << 21) | (A << 16) | (unsigned short)d) #define ppc_lhau(c,D,d,A) ppc_emit32(c, (43 << 26) | (D << 21) | (A << 16) | (unsigned short)d) #define ppc_lhaux(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (375 << 1) | 0) #define ppc_lhax(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (343 << 1) | 0) #define ppc_lhbrx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (790 << 1) | 0) #define ppc_lhzu(c,D,d,A) ppc_emit32(c, (41 << 26) | (D << 21) | (A << 16) | (unsigned short)d) #define ppc_lhzux(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (311 << 1) | 0) #define ppc_lhzx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (279 << 1) | 0) /* lmw updated as pem.fm & MPCFPE32B - Zhe */ #define ppc_lmw(c,D,d,A) ppc_emit32(c, (46 << 26) | (D << 21) | (A << 16) | (unsigned short)d) #define ppc_lswi(c,D,A,NB) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (NB << 11) | (597 << 1) | 0) #define ppc_lswx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (533 << 1) | 0) #define ppc_lwarx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (20 << 1) | 0) #define ppc_lwbrx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (534 << 1) | 0) /* lwzu updated as pem.fm & MPCFPE32B - Zhe */ #define ppc_lwzu(c,D,d,A) ppc_emit32(c, (33 << 26) | (D << 21) | (A << 16) | (unsigned short)d) #define ppc_lwzux(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (55 << 1) | 0) #define ppc_lwzx(c,D,A,B) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (23 << 1) | 0) #define ppc_mcrf(c,crfD,crfS) ppc_emit32(c, (19 << 26) | (crfD << 23) | (0 << 21) | (crfS << 18) | 0) #define ppc_mcrfs(c,crfD,crfS) ppc_emit32(c, (63 << 26) | (crfD << 23) | (0 << 21) | (crfS << 18) | (0 << 16) | (64 << 1) | 0) #define ppc_mcrxr(c,crfD) ppc_emit32(c, (31 << 26) | (crfD << 23) | (0 << 16) | (512 << 1) | 0) #define ppc_mfcr(c,D) ppc_emit32(c, (31 << 26) | (D << 21) | (0 << 16) | (19 << 1) | 0) #define ppc_mffsx(c,D,Rc) ppc_emit32(c, (63 << 26) | (D << 21) | (0 << 16) | (583 << 1) | Rc) #define ppc_mffs(c,D) ppc_mffsx(c,D,0) #define ppc_mffsd(c,D) ppc_mffsx(c,D,1) #define ppc_mfmsr(c,D) ppc_emit32(c, (31 << 26) | (D << 21) | (0 << 16) | (83 << 1) | 0) #define ppc_mfsr(c,D,SR) ppc_emit32(c, (31 << 26) | (D << 21) | (0 << 20) | (SR << 16) | (0 << 11) | (595 << 1) | 0) #define ppc_mfsrin(c,D,B) ppc_emit32(c, (31 << 26) | (D << 21) | (0 << 16) | (B << 11) | (659 << 1) | 0) #define ppc_mftb(c,D,TBR) ppc_emit32(c, (31 << 26) | (D << 21) | (TBR << 11) | (371 << 1) | 0) #define ppc_mtcrf(c,CRM,S) ppc_emit32(c, (31 << 26) | (S << 21) | (0 << 20) | (CRM << 12) | (0 << 11) | (144 << 1) | 0) #define ppc_mtfsb0x(c,CRB,Rc) ppc_emit32(c, (63 << 26) | (CRB << 21) | (0 << 11) | (70 << 1) | Rc) #define ppc_mtfsb0(c,CRB) ppc_mtfsb0x(c,CRB,0) #define ppc_mtfsb0d(c,CRB) ppc_mtfsb0x(c,CRB,1) #define ppc_mtfsb1x(c,CRB,Rc) ppc_emit32(c, (63 << 26) | (CRB << 21) | (0 << 11) | (38 << 1) | Rc) #define ppc_mtfsb1(c,CRB) ppc_mtfsb1x(c,CRB,0) #define ppc_mtfsb1d(c,CRB) ppc_mtfsb1x(c,CRB,1) #define ppc_mtfsfx(c,FM,B,Rc) ppc_emit32(c, (63 << 26) | (0 << 25) | (FM << 22) | (0 << 21) | (B << 11) | (711 << 1) | Rc) #define ppc_mtfsf(c,FM,B) ppc_mtfsfx(c,FM,B,0) #define ppc_mtfsfd(c,FM,B) ppc_mtfsfx(c,FM,B,1) #define ppc_mtfsfix(c,crfD,IMM,Rc) ppc_emit32(c, (63 << 26) | (crfD << 23) | (0 << 16) | (IMM << 12) | (0 << 11) | (134 << 1) | Rc) #define ppc_mtfsfi(c,crfD,IMM) ppc_mtfsfix(c,crfD,IMM,0) #define ppc_mtfsfid(c,crfD,IMM) ppc_mtfsfix(c,crfD,IMM,1) #define ppc_mtmsr(c, S) ppc_emit32(c, (31 << 26) | (S << 21) | (0 << 11) | (146 << 1) | 0) #define ppc_mtsr(c,SR,S) ppc_emit32(c, (31 << 26) | (S << 21) | (0 << 20) | (SR << 16) | (0 << 11) | (210 << 1) | 0) #define ppc_mtsrin(c,S,B) ppc_emit32(c, (31 << 26) | (S << 21) | (0 << 16) | (B << 11) | (242 << 1) | 0) #define ppc_mulhwx(c,D,A,B,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 10) | (75 << 1) | Rc) #define ppc_mulhw(c,D,A,B) ppc_mulhwx(c,D,A,B,0) #define ppc_mulhwd(c,D,A,B) ppc_mulhwx(c,D,A,B,1) #define ppc_mulhwux(c,D,A,B,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (0 << 10) | (11 << 1) | Rc) #define ppc_mulhwu(c,D,A,B) ppc_mulhwux(c,D,A,B,0) #define ppc_mulhwud(c,D,A,B) ppc_mulhwux(c,D,A,B,1) #define ppc_mulli(c,D,A,SIMM) ppc_emit32(c, ((07) << 26) | (D << 21) | (A << 16) | (unsigned short)(SIMM)) #define ppc_mullwx(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (OE << 10) | (235 << 1) | Rc) #define ppc_mullw(c,D,A,B) ppc_mullwx(c,D,A,B,0,0) #define ppc_mullwd(c,D,A,B) ppc_mullwx(c,D,A,B,0,1) #define ppc_mullwo(c,D,A,B) ppc_mullwx(c,D,A,B,1,0) #define ppc_mullwod(c,D,A,B) ppc_mullwx(c,D,A,B,1,1) #define ppc_nandx(c,A,S,B,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (476 << 1) | Rc) #define ppc_nand(c,A,S,B) ppc_nandx(c,A,S,B,0) #define ppc_nandd(c,A,S,B) ppc_nandx(c,A,S,B,1) #define ppc_negx(c,D,A,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (0 << 11) | (OE << 10) | (104 << 1) | Rc) #define ppc_neg(c,D,A) ppc_negx(c,D,A,0,0) #define ppc_negd(c,D,A) ppc_negx(c,D,A,0,1) #define ppc_nego(c,D,A) ppc_negx(c,D,A,1,0) #define ppc_negod(c,D,A) ppc_negx(c,D,A,1,1) #define ppc_norx(c,A,S,B,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (124 << 1) | Rc) #define ppc_nor(c,A,S,B) ppc_norx(c,A,S,B,0) #define ppc_nord(c,A,S,B) ppc_norx(c,A,S,B,1) #define ppc_not(c,A,S) ppc_norx(c,A,S,S,0) #define ppc_orx(c,A,S,B,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (444 << 1) | Rc) #define ppc_ord(c,A,S,B) ppc_orx(c,A,S,B,1) #define ppc_orcx(c,A,S,B,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (412 << 1) | Rc) #define ppc_orc(c,A,S,B) ppc_orcx(c,A,S,B,0) #define ppc_orcd(c,A,S,B) ppc_orcx(c,A,S,B,1) #define ppc_oris(c,A,S,UIMM) ppc_emit32(c, (25 << 26) | (S << 21) | (A << 16) | (unsigned short)(UIMM)) #define ppc_rfi(c) ppc_emit32(c, (19 << 26) | (0 << 11) | (50 << 1) | 0) /* rlw* updated to avoid gcc warnings - Zhe */ #define ppc_rlwimix(c,A,S,SH,MB,ME,Rc) ppc_emit32(c, (20 << 26) | (S << 21) | (A << 16) | ((SH) << 11) | ((MB) << 6) | ((ME) << 1) | Rc) #define ppc_rlwimi(c,A,S,SH,MB,ME) ppc_rlwimix(c,A,S,SH,MB,ME,0) #define ppc_rlwimid(c,A,S,SH,MB,ME) ppc_rlwimix(c,A,S,SH,MB,ME,1) #define ppc_rlwinmx(c,A,S,SH,MB,ME,Rc) ppc_emit32(c, (21 << 26) | (S << 21) | (A << 16) | ((SH) << 11) | ((MB) << 6) | ((ME) << 1) | Rc) #define ppc_rlwinm(c,A,S,SH,MB,ME) ppc_rlwinmx(c,A,S,SH,MB,ME,0) #define ppc_rlwinmd(c,A,S,SH,MB,ME) ppc_rlwinmx(c,A,S,SH,MB,ME,1) #define ppc_rlwnmx(c,A,S,SH,MB,ME,Rc) ppc_emit32(c, (23 << 26) | (S << 21) | (A << 16) | ((SH) << 11) | ((MB) << 6) | ((ME) << 1) | Rc) #define ppc_rlwnm(c,A,S,SH,MB,ME) ppc_rlwnmx(c,A,S,SH,MB,ME,0) #define ppc_rlwnmd(c,A,S,SH,MB,ME) ppc_rlwnmx(c,A,S,SH,MB,ME,1) #define ppc_sc(c) ppc_emit32(c, (17 << 26) | (0 << 2) | (1 << 1) | 0) /* slw* updated as pem.fm & MPCFPE32B - Zhe */ #define ppc_slwx(c,A,S,B,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (24 << 1) | Rc) #define ppc_slw(c,A,S,B) ppc_slwx(c,A,S,B,0) #define ppc_slwd(c,A,S,B) ppc_slwx(c,A,S,B,1) #define ppc_srawx(c,A,S,B,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (792 << 1) | Rc) #define ppc_sraw(c,A,S,B) ppc_srawx(c,A,S,B,0) #define ppc_srawd(c,A,S,B) ppc_srawx(c,A,S,B,1) #define ppc_srawix(c,A,S,SH,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (SH << 11) | (824 << 1) | Rc) #define ppc_srawi(c,A,S,B) ppc_srawix(c,A,S,B,0) #define ppc_srawid(c,A,S,B) ppc_srawix(c,A,S,B,1) #define ppc_srwx(c,A,S,SH,Rc) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (SH << 11) | (536 << 1) | Rc) #define ppc_srw(c,A,S,B) ppc_srwx(c,A,S,B,0) #define ppc_srwd(c,A,S,B) ppc_srwx(c,A,S,B,1) /* stbu updated as pem.fm & MPCFPE32B - Zhe */ #define ppc_stbu(c,S,d,A) ppc_emit32(c, (39 << 26) | (S << 21) | (A << 16) | (unsigned short)(d)) #define ppc_stbux(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (247 << 1) | 0) #define ppc_stbx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (215 << 1) | 0) /* stfdu updated as pem.fm & MPCFPE32B - Zhe */ #define ppc_stfdu(c,S,d,A) ppc_emit32(c, (55 << 26) | (S << 21) | (A << 16) | (unsigned short)(d)) #define ppc_stfdx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (727 << 1) | 0) #define ppc_stfiwx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (983 << 1) | 0) /* stfsu, sthu, stmw updated as pem.fm & MPCFPE32B - Zhe */ #define ppc_stfsu(c,S,d,A) ppc_emit32(c, (53 << 26) | (S << 21) | (A << 16) | (unsigned short)(d)) #define ppc_stfsux(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (695 << 1) | 0) #define ppc_stfsx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (663 << 1) | 0) #define ppc_sthbrx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (918 << 1) | 0) #define ppc_sthu(c,S,d,A) ppc_emit32(c, (45 << 26) | (S << 21) | (A << 16) | (unsigned short)(d)) #define ppc_sthux(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (439 << 1) | 0) #define ppc_sthx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (407 << 1) | 0) #define ppc_stmw(c,S,d,A) ppc_emit32(c, (47 << 26) | (S << 21) | (A << 16) | (unsigned short)(d)) #define ppc_stswi(c,S,A,NB) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (NB << 11) | (725 << 1) | 0) #define ppc_stswx(c,S,A,NB) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (NB << 11) | (661 << 1) | 0) #define ppc_stwbrx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (662 << 1) | 0) #define ppc_stwcxd(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (150 << 1) | 1) #define ppc_stwux(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (183 << 1) | 0) #define ppc_stwx(c,S,A,B) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (151 << 1) | 0) #define ppc_subfx(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (OE << 10) | (40 << 1) | Rc) #define ppc_subf(c,D,A,B) ppc_subfx(c,D,A,B,0,0) #define ppc_subfd(c,D,A,B) ppc_subfx(c,D,A,B,0,1) #define ppc_subfo(c,D,A,B) ppc_subfx(c,D,A,B,1,0) #define ppc_subfod(c,D,A,B) ppc_subfx(c,D,A,B,1,1) #define ppc_sub(c,D,A,B) ppc_subf(c,D,B,A) #define ppc_subfcx(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (OE << 10) | (8 << 1) | Rc) #define ppc_subfc(c,D,A,B) ppc_subfcx(c,D,A,B,0,0) #define ppc_subfcd(c,D,A,B) ppc_subfcx(c,D,A,B,0,1) #define ppc_subfco(c,D,A,B) ppc_subfcx(c,D,A,B,1,0) #define ppc_subfcod(c,D,A,B) ppc_subfcx(c,D,A,B,1,1) #define ppc_subfex(c,D,A,B,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (B << 11) | (OE << 10) | (136 << 1) | Rc) #define ppc_subfe(c,D,A,B) ppc_subfex(c,D,A,B,0,0) #define ppc_subfed(c,D,A,B) ppc_subfex(c,D,A,B,0,1) #define ppc_subfeo(c,D,A,B) ppc_subfex(c,D,A,B,1,0) #define ppc_subfeod(c,D,A,B) ppc_subfex(c,D,A,B,1,1) #define ppc_subfic(c,D,A,SIMM) ppc_emit32(c, (8 << 26) | (D << 21) | (A << 16) | (unsigned short)(SIMM)) #define ppc_subfmex(c,D,A,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (0 << 11) | (OE << 10) | (232 << 1) | Rc) #define ppc_subfme(c,D,A) ppc_subfmex(c,D,A,0,0) #define ppc_subfmed(c,D,A) ppc_subfmex(c,D,A,0,1) #define ppc_subfmeo(c,D,A) ppc_subfmex(c,D,A,1,0) #define ppc_subfmeod(c,D,A) ppc_subfmex(c,D,A,1,1) #define ppc_subfzex(c,D,A,OE,Rc) ppc_emit32(c, (31 << 26) | (D << 21) | (A << 16) | (0 << 11) | (OE << 10) | (200 << 1) | Rc) #define ppc_subfze(c,D,A) ppc_subfzex(c,D,A,0,0) #define ppc_subfzed(c,D,A) ppc_subfzex(c,D,A,0,1) #define ppc_subfzeo(c,D,A) ppc_subfzex(c,D,A,1,0) #define ppc_subfzeod(c,D,A) ppc_subfzex(c,D,A,1,1) #define ppc_sync(c) ppc_emit32(c, (31 << 26) | (0 << 11) | (598 << 1) | 0) #define ppc_tlbia(c) ppc_emit32(c, (31 << 26) | (0 << 11) | (370 << 1) | 0) #define ppc_tlbie(c,B) ppc_emit32(c, (31 << 26) | (0 << 16) | (B << 11) | (306 << 1) | 0) #define ppc_tlbsync(c) ppc_emit32(c, (31 << 26) | (0 << 11) | (566 << 1) | 0) #define ppc_tw(c,TO,A,B) ppc_emit32(c, (31 << 26) | (TO << 21) | (A << 16) | (B << 11) | (4 << 1) | 0) #define ppc_twi(c,TO,A,SIMM) ppc_emit32(c, (3 << 26) | (TO << 21) | (A << 16) | (unsigned short)(SIMM)) #define ppc_xorx(c,A,S,B,RC) ppc_emit32(c, (31 << 26) | (S << 21) | (A << 16) | (B << 11) | (316 << 1) | RC) #define ppc_xor(c,A,S,B) ppc_xorx(c,A,S,B,0) #define ppc_xord(c,A,S,B) ppc_xorx(c,A,S,B,1) /* xori* updated as pem.fm & MPCFPE32B - Zhe */ #define ppc_xori(c,A,S,UIMM) ppc_emit32(c, (26 << 26) | (S << 21) | (A << 16) | (unsigned short)(UIMM)) #define ppc_xoris(c,A,S,UIMM) ppc_emit32(c, (27 << 26) | (S << 21) | (A << 16) | (unsigned short)(UIMM)) /* this marks the end of my work, ct */ /* * The following codes come from mini-ppc.c: * PowerPC backend for the Mono code generator * * Authors: * Paolo Molaro (lupus@ximian.com) * Dietmar Maurer (dietmar@ximian.com) * * (C) 2003 Ximian, Inc. */ /* deal with some of the ABI differences here */ #ifdef __APPLE__ #define PPC_RET_ADDR_OFFSET 8 #define PPC_STACK_ALIGNMENT 16 #define PPC_STACK_PARAM_OFFSET 24 #define PPC_MINIMAL_STACK_SIZE 24 #define PPC_FIRST_ARG_REG ppc_r3 #define PPC_LAST_ARG_REG ppc_r10 #define PPC_FIRST_FPARG_REG ppc_f1 #define PPC_LAST_FPARG_REG ppc_f13 #define PPC_PASS_STRUCTS_BY_VALUE 1 #else /* SVR4 - Linux, NetBSD, etc. */ #define PPC_RET_ADDR_OFFSET 4 #define PPC_STACK_ALIGNMENT 16 #define PPC_STACK_PARAM_OFFSET 8 #define PPC_MINIMAL_STACK_SIZE 8 #define PPC_FIRST_ARG_REG ppc_r3 #define PPC_LAST_ARG_REG ppc_r10 #define PPC_FIRST_FPARG_REG ppc_f1 #define PPC_LAST_FPARG_REG ppc_f8 /* set the next to 0 once inssel-ppc.brg is updated */ #define PPC_PASS_STRUCTS_BY_VALUE 1 #define PPC_SMALL_RET_STRUCT_IN_REG 1 #endif // code from ppc/tramp.c, try to keep in sync #define MIN_CACHE_LINE 8 static inline void mono_ppc_flush_icache (unsigned char *code, int size) { unsigned int i; unsigned char *p; p = code; /* use dcbf for smp support, later optimize for UP, see pem._64bit.d20030611.pdf page 211 */ if (1) { for (i = 0; i < size; i += MIN_CACHE_LINE, p += MIN_CACHE_LINE) { asm ("dcbf 0,%0;" : : "r"(p) : "memory"); } } else { for (i = 0; i < size; i += MIN_CACHE_LINE, p += MIN_CACHE_LINE) { asm ("dcbst 0,%0;" : : "r"(p) : "memory"); } } asm ("sync"); p = code; for (i = 0; i < size; i += MIN_CACHE_LINE, p += MIN_CACHE_LINE) { asm ("icbi 0,%0; sync;" : : "r"(p) : "memory"); } asm ("sync"); asm ("isync"); } static inline void ppc_patch (unsigned char *code, unsigned char *target) { unsigned int ins = *(unsigned int*)code; unsigned int prim = ins >> 26; unsigned int ovf; //g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target); if (prim == 18) { // prefer relative branches, they are more position independent (e.g. for AOT compilation). int diff = target - code; if (diff >= 0){ if (diff <= 33554431){ ins = (18 << 26) | (diff) | (ins & 1); *(unsigned int*)code = ins; return; } } else { /* diff between 0 and -33554432 */ if (diff >= -33554432){ ins = (18 << 26) | (diff & ~0xfc000000) | (ins & 1); *(unsigned int*)code = ins; return; } } if ((long)target >= 0){ if ((long)target <= 33554431){ ins = (18 << 26) | ((unsigned int) target) | (ins & 1) | 2; *(unsigned int*)code = ins; return; } } else { if ((long)target >= -33554432){ ins = (18 << 26) | (((unsigned int)target) & ~0xfc000000) | (ins & 1) | 2; *(unsigned int*)code = ins; return; } } // handle_thunk (TRUE, code, target); return; assert (0); } if (prim == 16) { // absolute address if (ins & 2) { unsigned int li = (unsigned int)target; ins = (ins & 0xffff0000) | (ins & 3); ovf = li & 0xffff0000; if (ovf != 0 && ovf != 0xffff0000) assert (0); li &= 0xffff; ins |= li; // FIXME: assert the top bits of li are 0 } else { int diff = target - code; ins = (ins & 0xffff0000) | (ins & 3); ovf = diff & 0xffff0000; if (ovf != 0 && ovf != 0xffff0000) assert (0); diff &= 0xffff; ins |= diff; } *(unsigned int*)code = ins; return; } if (prim == 15 || ins == 0x4e800021) { unsigned int *seq; /* the trampoline code will try to patch the blrl */ if (ins == 0x4e800021) { code -= 12; } /* this is the lis/ori/mtlr/blrl sequence */ seq = (unsigned int*)code; assert ((seq [0] >> 26) == 15); assert ((seq [1] >> 26) == 24); assert ((seq [2] >> 26) == 31); assert (seq [3] == 0x4e800021); /* FIXME: make this thread safe */ ppc_lis (code, ppc_r0, (unsigned int)(target) >> 16); ppc_ori (code, ppc_r0, ppc_r0, (unsigned int)(target) & 0xffff); mono_ppc_flush_icache (code - 8, 8); } else { assert (0); } // g_print ("patched with 0x%08x\n", ins); } /* Works done by Zhe Fang (fangzhe@msn.com) Most macros here are based on the assembler instructions can be founded in "Appendix F. Simplified Mnemonics" of IBM's "PowerPC Microprocessor Family: The Programming Enviornments for 32-Bit Microprocessors" G522-0290-01 02/21/2000 also referenced in "Appendix E. Simplified Mnemonics" of IBM's "PowerPC Microprocessor Family: The Programming Enviornments for 32 and 64-Bit Microprocessors" pem.fm.2.3 March 31, 2005 and "Appendix E. Simplified Mnemonics for PowerPC Instructions" of Freescale Semiconductor's "Programming Enviornments Manual for 32-Bit Implementations of the PowerPC Architecture" MPCFPE32B Rev. 3, 9/2005 The Branch-Conditional-Prediction relative codes are followed the descriptions in "4.2.4.2 Conditional Branch Control" of IBM's "PowerPC Microprocessor Family: The Programming Enviornments Manual for 32 and 64-bit Microprocessors" pem.fm.2.3 March 31, 2005 and also compatiable with other manuals above */ typedef enum { ppc_cr0 = 0, ppc_cr1, ppc_cr2, ppc_cr3, ppc_cr4, ppc_cr5, ppc_cr6, ppc_cr7 } PPCCRField; #define ppc_crbf(f,b) (4*f+b) enum { /* for BO */ PPC_BR_FALSE_UNLIKELY = 6, PPC_BR_FALSE_LIKELY = 7, PPC_BR_TRUE_UNLIKELY = 14, PPC_BR_TRUE_LIKELY = 15, PPC_BR_DEC_CTR_NONZERO_UNLIKELY = 24, PPC_BR_DEC_CTR_NONZERO_LIKELY = 25, PPC_BR_DEC_CTR_ZERO_UNLIKELY = 26, PPC_BR_DEC_CTR_ZERO_LIKELY = 27, /* for BH */ PPC_BR_BCLR_SUB_RETURN = 0, PPC_BR_BCCTR_NOT_SUB_RETURN = 0, PPC_BR_BCLR_NOT_SUB_RETURN = 1, PPC_BR_BCLR_NOT_PREDICTABLE = 3, PPC_BR_BCCTR_NOT_PREDICTABLE = 3 }; /*#define ppc_bcctrx(c,BO,BI,BH,LK) ppc_emit32(c, (19 << 26) | (BO << 21 )| (BI << 16) | (BH << 11) | (528 << 1) | LK) #define ppc_bcctr(c,BO,BI,BH) ppc_bcctrx(c,BO,BI,BH,0) #define ppc_bcctrl(c,BO,BI,BH) ppc_bcctrx(c,BO,BI,BH,1) #define ppc_bclrx(c,BO,BI,BH,LK) ppc_emit32(c, (19 << 26) | (BO << 21 )| (BI << 16) | (BH << 11) | (16 << 1) | LK) #define ppc_bclr(c,BO,BI,BH) ppc_bclrx(c,BO,BI,BH,0) #define ppc_bclrl(c,BO,BI,BH) ppc_bclrx(c,BO,BI,BH,1)*/ #define ppc_subi(c,D,A,d) ppc_addi(c,D,A,-(d)) #define ppc_subis(c,D,A,d) ppc_addis(c,D,A,-(d)) #define ppc_subic(c,D,A,d) ppc_addic(c,D,A,-(d)) #define ppc_subicd(c,D,A,d) ppc_addicd(c,D,A,-(d)) #define ppc_sub(c,D,A,B) ppc_subf(c,D,B,A) #define ppc_subc(c,D,A,B) ppc_subfc(c,D,B,A) #define ppc_cmpwi(c,cfrD,A,B) ppc_cmpi(c,cfrD,0,A,B) #define ppc_cmpw(c,cfrD,A,B) ppc_cmp(c,cfrD,0,A,B) #define ppc_cmplwi(c,cfrD,A,B) ppc_cmpli(c,cfrD,0,A,B) #define ppc_cmplw(c,cfrD,A,B) ppc_cmpl(c,cfrD,0,A,B) #define ppc_extlwi(c,A,S,n,b) ppc_rlwinm(c,A,S,b,0,((n-1) & 0x1f)) #define ppc_extrwi(c,A,S,n,b) ppc_rlwinm(c,A,S,((b+n) & 0x1f),((32-(n)) & 0x1f),31) #define ppc_inslwi(c,A,S,n,b) ppc_rlwimi(c,A,S,(32-(b) & 0x1f),b,((b+n-1) & 0x1f)) #define ppc_insrwi(c,A,S,n,b) ppc_rlwimi(c,A,S,((32-(b)-(n)) & 0x1f),b,((b+n-1) & 0x1f)) #define ppc_rotlwi(c,A,S,n) ppc_rlwinm(c,A,S,n,0,31) #define ppc_rotrwi(c,A,S,n) ppc_rlwinm(c,A,S,((32-(n)) & 0x1f),0,31) #define ppc_rotlw(c,A,S,B) ppc_rlwinm(c,A,S,B,0,31) #define ppc_slwi(c,A,S,n) ppc_rlwinm(c,A,S,n,0,31-(n)) #define ppc_srwi(c,A,S,n) ppc_rlwinm(c,A,S,((32-(n)) & 0x1f),n,31) #define ppc_clrlwi(c,A,S,n) ppc_rlwinm(c,A,S,0,n,31) #define ppc_clrrwi(c,A,S,n) ppc_rlwinm(c,A,S,0,0,31-(n)) #define ppc_clrlslwi(c,A,S,b,n) ppc_rlwinm(c,A,S,n,((b-(n)) & 0x1f),31-(n)) #define ppc_bctr(c) ppc_emit32(c,0x4e800420) #define ppc_bctrl(c) ppc_emit32(c,0x4e800421) #define ppc_crset(c,bx) ppc_creqv(c,bx,bx,bx) #define ppc_crclr(c,bx) ppc_crxor(c,bx,bx,bx) #define ppc_crmove(c,bx,by) ppc_cror(c,bx,by,by) #define ppc_crnot(c,bx,by) ppc_crnor(c,bx,by,by) #define ppc_nop(c) ppc_ori(c,0,0,0) //#define ppc_la(c,D,d,A) ppc_addi(c,D,A,d) #define ppc_la(c,D,v) ppc_addi(c,D,v,v) #define ppc_mtcr(c,S) ppc_mtcrf(c,0xff,S) /* Helper macros for assembler */ #define ppc_hi16(v) ((unsigned int)(v) >> 16) #define ppc_ha16(v) (ppc_hi16(v) + ((v & 0x8000) >> 15)) #define ppc_lo16(v) ((unsigned int)(v) & 0xffff) #endif dynamips-0.2.14/unstable/ppc32.c000066400000000000000000000366051241034141600163400ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * PowerPC (32-bit) generic routines. */ #include #include #include #include #include #include #include #include #include "rbtree.h" #include "cpu.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "ppc32_mem.h" #include "ppc32_exec.h" #include "ppc32_jit.h" /* Reset a PowerPC CPU */ int ppc32_reset(cpu_ppc_t *cpu) { cpu->ia = PPC32_ROM_START; cpu->gpr[1] = PPC32_ROM_SP; cpu->msr = PPC32_MSR_IP; /* Restart the MTS subsystem */ ppc32_mem_restart(cpu); /* Flush JIT structures */ ppc32_jit_flush(cpu,0); return(0); } /* Initialize a PowerPC processor */ int ppc32_init(cpu_ppc_t *cpu) { /* Initialize JIT operations */ jit_op_init_cpu(cpu->gen); /* Initialize idle timer */ cpu->gen->idle_max = 500; cpu->gen->idle_sleep_time = 30000; /* Timer IRQ parameters (default frequency: 250 Hz <=> 4ms period) */ cpu->timer_irq_check_itv = 1000; cpu->timer_irq_freq = 250; /* Enable/disable direct block jump */ cpu->exec_blk_direct_jump = cpu->vm->exec_blk_direct_jump; /* Idle loop mutex and condition */ pthread_mutex_init(&cpu->gen->idle_mutex,NULL); pthread_cond_init(&cpu->gen->idle_cond,NULL); /* Set the CPU methods */ cpu->gen->reg_set = (void *)ppc32_reg_set; cpu->gen->reg_dump = (void *)ppc32_dump_regs; cpu->gen->mmu_dump = (void *)ppc32_dump_mmu; cpu->gen->mmu_raw_dump = (void *)ppc32_dump_mmu; cpu->gen->add_breakpoint = (void *)ppc32_add_breakpoint; cpu->gen->remove_breakpoint = (void *)ppc32_remove_breakpoint; cpu->gen->set_idle_pc = (void *)ppc32_set_idle_pc; cpu->gen->get_idling_pc = (void *)ppc32_get_idling_pc; /* Set the startup parameters */ ppc32_reset(cpu); return(0); } /* Delete a PowerPC processor */ void ppc32_delete(cpu_ppc_t *cpu) { if (cpu) { ppc32_mem_shutdown(cpu); ppc32_jit_shutdown(cpu); } } /* Set the processor version register (PVR) */ void ppc32_set_pvr(cpu_ppc_t *cpu,m_uint32_t pvr) { cpu->pvr = pvr; ppc32_mem_restart(cpu); } /* Set idle PC value */ void ppc32_set_idle_pc(cpu_gen_t *cpu,m_uint64_t addr) { CPU_PPC32(cpu)->idle_pc = (m_uint32_t)addr; } /* Timer IRQ */ void *ppc32_timer_irq_run(cpu_ppc_t *cpu) { pthread_mutex_t umutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t ucond = PTHREAD_COND_INITIALIZER; struct timespec t_spc; m_tmcnt_t expire; u_int interval; u_int threshold; #if 0 while(!cpu->timer_irq_armed) sleep(1); #endif interval = 1000000 / cpu->timer_irq_freq; threshold = cpu->timer_irq_freq * 10; expire = m_gettime_usec() + interval; while(cpu->gen->state != CPU_STATE_HALTED) { pthread_mutex_lock(&umutex); t_spc.tv_sec = expire / 1000000; t_spc.tv_nsec = (expire % 1000000) * 1000; pthread_cond_timedwait(&ucond,&umutex,&t_spc); pthread_mutex_unlock(&umutex); if (likely(!cpu->irq_disable) && likely(cpu->gen->state == CPU_STATE_RUNNING) && likely(cpu->msr & PPC32_MSR_EE)) { cpu->timer_irq_pending++; if (unlikely(cpu->timer_irq_pending > threshold)) { cpu->timer_irq_pending = 0; cpu->timer_drift++; #if 0 printf("Timer IRQ not accurate (%u pending IRQ): " "reduce the \"--timer-irq-check-itv\" parameter " "(current value: %u)\n", cpu->timer_irq_pending,cpu->timer_irq_check_itv); #endif } } expire += interval; } return NULL; } #define IDLE_HASH_SIZE 8192 /* Idle PC hash item */ struct ppc32_idle_pc_hash { m_uint32_t ia; u_int count; struct ppc32_idle_pc_hash *next; }; /* Determine an "idling" PC */ int ppc32_get_idling_pc(cpu_gen_t *cpu) { cpu_ppc_t *pcpu = CPU_PPC32(cpu); struct ppc32_idle_pc_hash **pc_hash,*p; struct cpu_idle_pc *res; u_int h_index; m_uint32_t cur_ia; int i; cpu->idle_pc_prop_count = 0; if (pcpu->idle_pc != 0) { printf("\nYou already use an idle PC, using the calibration would give " "incorrect results.\n"); return(-1); } printf("\nPlease wait while gathering statistics...\n"); pc_hash = calloc(IDLE_HASH_SIZE,sizeof(struct ppc32_idle_pc_hash *)); /* Disable IRQ */ pcpu->irq_disable = TRUE; /* Take 1000 measures, each mesure every 10ms */ for(i=0;i<1000;i++) { cur_ia = pcpu->ia; h_index = (cur_ia >> 2) & (IDLE_HASH_SIZE-1); for(p=pc_hash[h_index];p;p=p->next) if (p->ia == cur_ia) { p->count++; break; } if (!p) { if ((p = malloc(sizeof(*p)))) { p->ia = cur_ia; p->count = 1; p->next = pc_hash[h_index]; pc_hash[h_index] = p; } } usleep(10000); } /* Select PCs */ for(i=0;inext) if ((p->count >= 20) && (p->count <= 80)) { res = &cpu->idle_pc_prop[cpu->idle_pc_prop_count++]; res->pc = p->ia; res->count = p->count; if (cpu->idle_pc_prop_count >= CPU_IDLE_PC_MAX_RES) goto done; } } done: /* Set idle PC */ if (cpu->idle_pc_prop_count) { printf("Done. Suggested idling PC:\n"); for(i=0;iidle_pc_prop_count;i++) { printf(" 0x%llx (count=%u)\n", cpu->idle_pc_prop[i].pc, cpu->idle_pc_prop[i].count); } printf("Restart the emulator with \"--idle-pc=0x%llx\" (for example)\n", cpu->idle_pc_prop[0].pc); } else { printf("Done. No suggestion for idling PC, dumping the full table:\n"); for(i=0;inext) { printf(" 0x%8.8x (%3u)\n",p->ia,p->count); if (cpu->idle_pc_prop_count < CPU_IDLE_PC_MAX_RES) { res = &cpu->idle_pc_prop[cpu->idle_pc_prop_count++]; res->pc = p->ia; res->count = p->count; } } printf("\n"); } /* Re-enable IRQ */ pcpu->irq_disable = FALSE; return(0); } #if 0 /* Set an IRQ (VM IRQ standard routing) */ void ppc32_vm_set_irq(vm_instance_t *vm,u_int irq) { cpu_ppc_t *boot_cpu; boot_cpu = CPU_PPC32(vm->boot_cpu); if (boot_cpu->irq_disable) { boot_cpu->irq_pending = 0; return; } ppc32_set_irq(boot_cpu,irq); if (boot_cpu->irq_idle_preempt[irq]) cpu_idle_break_wait(vm->boot_cpu); } /* Clear an IRQ (VM IRQ standard routing) */ void ppc32_vm_clear_irq(vm_instance_t *vm,u_int irq) { cpu_ppc_t *boot_cpu; boot_cpu = CPU_PPC32(vm->boot_cpu); ppc32_clear_irq(boot_cpu,irq); } #endif /* Generate an exception */ void ppc32_trigger_exception(cpu_ppc_t *cpu,u_int exc_vector) { //printf("TRIGGER_EXCEPTION: saving cpu->ia=0x%8.8x, msr=0x%8.8x\n", // cpu->ia,cpu->msr); /* Save the return instruction address */ cpu->srr0 = cpu->ia; if (exc_vector == PPC32_EXC_SYSCALL) cpu->srr0 += sizeof(ppc_insn_t); //printf("SRR0 = 0x%8.8x\n",cpu->srr0); /* Save Machine State Register (MSR) */ cpu->srr1 = cpu->msr & PPC32_EXC_SRR1_MASK; //printf("SRR1 = 0x%8.8x\n",cpu->srr1); /* Set the new SRR value */ cpu->msr &= ~PPC32_EXC_MSR_MASK; cpu->irq_check = FALSE; //printf("MSR = 0x%8.8x\n",cpu->msr); /* Use bootstrap vectors ? */ if (cpu->msr & PPC32_MSR_IP) cpu->ia = 0xFFF00000 + exc_vector; else cpu->ia = exc_vector; } /* Trigger IRQs */ fastcall void ppc32_trigger_irq(cpu_ppc_t *cpu) { if (unlikely(cpu->irq_disable)) { cpu->irq_pending = FALSE; cpu->irq_check = FALSE; return; } /* Clear the IRQ check flag */ cpu->irq_check = FALSE; if (cpu->irq_pending && (cpu->msr & PPC32_MSR_EE)) { cpu->irq_count++; cpu->irq_pending = FALSE; ppc32_trigger_exception(cpu,PPC32_EXC_EXT); } } /* Trigger the decrementer exception */ void ppc32_trigger_timer_irq(cpu_ppc_t *cpu) { cpu->timer_irq_count++; if (cpu->msr & PPC32_MSR_EE) ppc32_trigger_exception(cpu,PPC32_EXC_DEC); } /* Virtual breakpoint */ fastcall void ppc32_run_breakpoint(cpu_ppc_t *cpu) { cpu_log(cpu->gen,"BREAKPOINT", "Virtual breakpoint reached at IA=0x%8.8x\n",cpu->ia); printf("[[[ Virtual Breakpoint reached at IA=0x%8.8x LR=0x%8.8x]]]\n", cpu->ia,cpu->lr); ppc32_dump_regs(cpu->gen); } /* Add a virtual breakpoint */ int ppc32_add_breakpoint(cpu_gen_t *cpu,m_uint64_t ia) { cpu_ppc_t *pcpu = CPU_PPC32(cpu); int i; for(i=0;ibreakpoints[i]) break; if (i == PPC32_MAX_BREAKPOINTS) return(-1); pcpu->breakpoints[i] = ia; pcpu->breakpoints_enabled = TRUE; return(0); } /* Remove a virtual breakpoint */ void ppc32_remove_breakpoint(cpu_gen_t *cpu,m_uint64_t ia) { cpu_ppc_t *pcpu = CPU_PPC32(cpu); int i,j; for(i=0;ibreakpoints[i] == ia) { for(j=i;jbreakpoints[j] = pcpu->breakpoints[j+1]; pcpu->breakpoints[PPC32_MAX_BREAKPOINTS-1] = 0; } for(i=0;ibreakpoints[i] != 0) return; pcpu->breakpoints_enabled = FALSE; } /* Set a register */ void ppc32_reg_set(cpu_gen_t *cpu,u_int reg,m_uint64_t val) { if (reg < PPC32_GPR_NR) CPU_PPC32(cpu)->gpr[reg] = (m_uint32_t)val; } /* Dump registers of a PowerPC processor */ void ppc32_dump_regs(cpu_gen_t *cpu) { cpu_ppc_t *pcpu = CPU_PPC32(cpu); int i; printf("PowerPC Registers:\n"); for(i=0;igpr[i*4], (i*4)+1, pcpu->gpr[(i*4)+1], (i*4)+2, pcpu->gpr[(i*4)+2], (i*4)+3, pcpu->gpr[(i*4)+3]); } printf("\n"); printf(" ia = 0x%8.8x, lr = 0x%8.8x\n", pcpu->ia, pcpu->lr); printf(" cr = 0x%8.8x, msr = 0x%8.8x, xer = 0x%8.8x, dec = 0x%8.8x\n", ppc32_get_cr(pcpu), pcpu->msr, pcpu->xer | (pcpu->xer_ca << PPC32_XER_CA_BIT), pcpu->dec); printf(" sprg[0] = 0x%8.8x, sprg[1] = 0x%8.8x\n", pcpu->sprg[0],pcpu->sprg[1]); printf(" sprg[2] = 0x%8.8x, sprg[3] = 0x%8.8x\n", pcpu->sprg[2],pcpu->sprg[3]); printf("\n IRQ count: %llu, IRQ false positives: %llu, " "IRQ Pending: %u, IRQ Check: %s\n", pcpu->irq_count,pcpu->irq_fp_count,pcpu->irq_pending, pcpu->irq_check ? "yes" : "no"); printf(" Timer IRQ count: %llu, pending: %u, timer drift: %u\n\n", pcpu->timer_irq_count,pcpu->timer_irq_pending,pcpu->timer_drift); printf(" Device access count: %llu\n",cpu->dev_access_counter); printf("\n"); } /* Dump BAT registers */ static void ppc32_dump_bat(cpu_ppc_t *cpu,int index) { int i; for(i=0;ibat[index][i].reg[0],cpu->bat[index][i].reg[1]); } /* Dump MMU registers */ void ppc32_dump_mmu(cpu_gen_t *cpu) { cpu_ppc_t *pcpu = CPU_PPC32(cpu); int i; printf("PowerPC MMU Registers:\n"); printf(" - IBAT Registers:\n"); ppc32_dump_bat(pcpu,PPC32_IBAT_IDX); printf(" - DBAT Registers:\n"); ppc32_dump_bat(pcpu,PPC32_DBAT_IDX); printf(" - Segment Registers:\n"); for(i=0;isr[i]); printf(" - SDR1: 0x%8.8x\n",pcpu->sdr1); } /* Load a raw image into the simulated memory */ int ppc32_load_raw_image(cpu_ppc_t *cpu,char *filename,m_uint32_t vaddr) { struct stat file_info; size_t len,clen; m_uint32_t remain; void *haddr; FILE *bfd; if (!(bfd = fopen(filename,"r"))) { perror("fopen"); return(-1); } if (fstat(fileno(bfd),&file_info) == -1) { perror("stat"); return(-1); } len = file_info.st_size; printf("Loading RAW file '%s' at virtual address 0x%8.8x (size=%lu)\n", filename,vaddr,(u_long)len); while(len > 0) { haddr = cpu->mem_op_lookup(cpu,vaddr,PPC32_MTS_DCACHE); if (!haddr) { fprintf(stderr,"load_raw_image: invalid load address 0x%8.8x\n", vaddr); return(-1); } if (len > PPC32_MIN_PAGE_SIZE) clen = PPC32_MIN_PAGE_SIZE; else clen = len; remain = MIPS_MIN_PAGE_SIZE; remain -= (vaddr - (vaddr & MIPS_MIN_PAGE_MASK)); clen = m_min(clen,remain); if (fread((u_char *)haddr,clen,1,bfd) != 1) break; vaddr += clen; len -= clen; } fclose(bfd); return(0); } /* Load an ELF image into the simulated memory */ int ppc32_load_elf_image(cpu_ppc_t *cpu,char *filename,int skip_load, m_uint32_t *entry_point) { m_uint32_t vaddr,remain; void *haddr; Elf32_Ehdr *ehdr; Elf32_Shdr *shdr; Elf_Scn *scn; Elf *img_elf; size_t len,clen; _maybe_used char *name; int i,fd; FILE *bfd; if (!filename) return(-1); #ifdef __CYGWIN__ fd = open(filename,O_RDONLY|O_BINARY); #else fd = open(filename,O_RDONLY); #endif if (fd == -1) { perror("load_elf_image: open"); return(-1); } if (elf_version(EV_CURRENT) == EV_NONE) { fprintf(stderr,"load_elf_image: library out of date\n"); return(-1); } if (!(img_elf = elf_begin(fd,ELF_C_READ,NULL))) { fprintf(stderr,"load_elf_image: elf_begin: %s\n", elf_errmsg(elf_errno())); return(-1); } if (!(ehdr = elf32_getehdr(img_elf))) { fprintf(stderr,"load_elf_image: invalid ELF file\n"); return(-1); } printf("Loading ELF file '%s'...\n",filename); bfd = fdopen(fd,"rb"); if (!bfd) { perror("load_elf_image: fdopen"); return(-1); } if (!skip_load) { for(i=0;ie_shnum;i++) { scn = elf_getscn(img_elf,i); shdr = elf32_getshdr(scn); name = elf_strptr(img_elf, ehdr->e_shstrndx, (size_t)shdr->sh_name); len = shdr->sh_size; if (!(shdr->sh_flags & SHF_ALLOC) || !len) continue; fseek(bfd,shdr->sh_offset,SEEK_SET); vaddr = shdr->sh_addr; if (cpu->vm->debug_level > 0) { printf(" * Adding section at virtual address 0x%8.8x " "(len=0x%8.8lx)\n",vaddr,(u_long)len); } while(len > 0) { haddr = cpu->mem_op_lookup(cpu,vaddr,PPC32_MTS_DCACHE); if (!haddr) { fprintf(stderr,"load_elf_image: invalid load address 0x%x\n", vaddr); return(-1); } if (len > PPC32_MIN_PAGE_SIZE) clen = PPC32_MIN_PAGE_SIZE; else clen = len; remain = PPC32_MIN_PAGE_SIZE; remain -= (vaddr - (vaddr & PPC32_MIN_PAGE_MASK)); clen = m_min(clen,remain); if (fread((u_char *)haddr,clen,1,bfd) < 1) break; vaddr += clen; len -= clen; } } } else { printf("ELF loading skipped, using a ghost RAM file.\n"); } printf("ELF entry point: 0x%x\n",ehdr->e_entry); if (entry_point) *entry_point = ehdr->e_entry; elf_end(img_elf); fclose(bfd); return(0); } dynamips-0.2.14/unstable/ppc32.h000066400000000000000000000412161241034141600163370ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) */ #ifndef __PPC_32_H__ #define __PPC_32_H__ #include #include "utils.h" #include "rbtree.h" /* CPU identifiers */ #define PPC32_PVR_405 0x40110000 /* Number of GPR (general purpose registers) */ #define PPC32_GPR_NR 32 /* Number of registers in FPU */ #define PPC32_FPU_REG_NR 32 /* Minimum page size: 4 Kb */ #define PPC32_MIN_PAGE_SHIFT 12 #define PPC32_MIN_PAGE_SIZE (1 << PPC32_MIN_PAGE_SHIFT) #define PPC32_MIN_PAGE_IMASK (PPC32_MIN_PAGE_SIZE - 1) #define PPC32_MIN_PAGE_MASK 0xFFFFF000 /* Number of instructions per page */ #define PPC32_INSN_PER_PAGE (PPC32_MIN_PAGE_SIZE/sizeof(ppc_insn_t)) /* Starting point for ROM */ #define PPC32_ROM_START 0xfff00100 #define PPC32_ROM_SP 0x00006000 /* Special Purpose Registers (SPR) */ #define PPC32_SPR_XER 1 #define PPC32_SPR_LR 8 /* Link Register */ #define PPC32_SPR_CTR 9 /* Count Register */ #define PPC32_SPR_DSISR 18 #define PPC32_SPR_DAR 19 #define PPC32_SPR_DEC 22 /* Decrementer */ #define PPC32_SPR_SDR1 25 /* Page Table Address */ #define PPC32_SPR_SRR0 26 #define PPC32_SPR_SRR1 27 #define PPC32_SPR_TBL_READ 268 /* Time Base Low (read) */ #define PPC32_SPR_TBU_READ 269 /* Time Base Up (read) */ #define PPC32_SPR_SPRG0 272 #define PPC32_SPR_SPRG1 273 #define PPC32_SPR_SPRG2 274 #define PPC32_SPR_SPRG3 275 #define PPC32_SPR_TBL_WRITE 284 /* Time Base Low (write) */ #define PPC32_SPR_TBU_WRITE 285 /* Time Base Up (write) */ #define PPC32_SPR_PVR 287 /* Processor Version Register */ #define PPC32_SPR_HID0 1008 #define PPC32_SPR_HID1 1009 #define PPC405_SPR_PID 945 /* Process Identifier */ /* Exception vectors */ #define PPC32_EXC_SYS_RST 0x00000100 /* System Reset */ #define PPC32_EXC_MC_CHK 0x00000200 /* Machine Check */ #define PPC32_EXC_DSI 0x00000300 /* Data memory access failure */ #define PPC32_EXC_ISI 0x00000400 /* Instruction fetch failure */ #define PPC32_EXC_EXT 0x00000500 /* External Interrupt */ #define PPC32_EXC_ALIGN 0x00000600 /* Alignment */ #define PPC32_EXC_PROG 0x00000700 /* FPU, Illegal instruction, ... */ #define PPC32_EXC_NO_FPU 0x00000800 /* FPU unavailable */ #define PPC32_EXC_DEC 0x00000900 /* Decrementer */ #define PPC32_EXC_SYSCALL 0x00000C00 /* System Call */ #define PPC32_EXC_TRACE 0x00000D00 /* Trace */ #define PPC32_EXC_FPU_HLP 0x00000E00 /* Floating-Point Assist */ /* Condition Register (CR) is accessed through 8 fields of 4 bits */ #define ppc32_get_cr_field(n) ((n) >> 2) #define ppc32_get_cr_bit(n) (~(n) & 0x03) /* Positions of LT, GT, EQ and SO bits in CR fields */ #define PPC32_CR_LT_BIT 3 #define PPC32_CR_GT_BIT 2 #define PPC32_CR_EQ_BIT 1 #define PPC32_CR_SO_BIT 0 /* CR0 (Condition Register Field 0) bits */ #define PPC32_CR0_LT_BIT 31 #define PPC32_CR0_LT (1 << PPC32_CR0_LT_BIT) /* Negative */ #define PPC32_CR0_GT_BIT 30 #define PPC32_CR0_GT (1 << PPC32_CR0_GT_BIT) /* Positive */ #define PPC32_CR0_EQ_BIT 29 #define PPC32_CR0_EQ (1 << PPC32_CR0_EQ_BIT) /* Zero */ #define PPC32_CR0_SO_BIT 28 #define PPC32_CR0_SO (1 << PPC32_CR0_SO_BIT) /* Summary overflow */ /* XER register */ #define PPC32_XER_SO_BIT 31 #define PPC32_XER_SO (1 << PPC32_XER_SO_BIT) /* Summary Overflow */ #define PPC32_XER_OV 0x40000000 /* Overflow */ #define PPC32_XER_CA_BIT 29 #define PPC32_XER_CA (1 << PPC32_XER_CA_BIT) /* Carry */ #define PPC32_XER_BC_MASK 0x0000007F /* Byte cnt (lswx/stswx) */ /* MSR (Machine State Register) */ #define PPC32_MSR_POW_MASK 0x00060000 /* Power Management */ #define PPC32_MSR_ILE 0x00010000 /* Exception Little-Endian Mode */ #define PPC32_MSR_EE 0x00008000 /* External Interrupt Enable */ #define PPC32_MSR_PR 0x00004000 /* Privilege Level (0=supervisor) */ #define PPC32_MSR_PR_SHIFT 14 #define PPC32_MSR_FP 0x00002000 /* Floating-Point Available */ #define PPC32_MSR_ME 0x00001000 /* Machine Check Enable */ #define PPC32_MSR_FE0 0x00000800 /* Floating-Point Exception Mode 0 */ #define PPC32_MSR_SE 0x00000400 /* Single-step trace enable */ #define PPC32_MSR_BE 0x00000200 /* Branch Trace Enable */ #define PPC32_MSR_FE1 0x00000100 /* Floating-Point Exception Mode 1 */ #define PPC32_MSR_IP 0x00000040 /* Exception Prefix */ #define PPC32_MSR_IR 0x00000020 /* Instruction address translation */ #define PPC32_MSR_DR 0x00000010 /* Data address translation */ #define PPC32_MSR_RI 0x00000002 /* Recoverable Exception */ #define PPC32_MSR_LE 0x00000001 /* Little-Endian mode enable */ #define PPC32_RFI_MSR_MASK 0x87c0ff73 #define PPC32_EXC_SRR1_MASK 0x0000ff73 #define PPC32_EXC_MSR_MASK 0x0006ef32 /* Number of BAT registers (8 for PowerPC 7448) */ #define PPC32_BAT_NR 8 /* Number of segment registers */ #define PPC32_SR_NR 16 /* Upper BAT register */ #define PPC32_UBAT_BEPI_MASK 0xFFFE0000 /* Block Effective Page Index */ #define PPC32_UBAT_BEPI_SHIFT 17 #define PPC32_UBAT_BL_MASK 0x00001FFC /* Block Length */ #define PPC32_UBAT_BL_SHIFT 2 #define PPC32_UBAT_XBL_MASK 0x0001FFFC /* Block Length */ #define PPC32_UBAT_XBL_SHIFT 2 #define PPC32_UBAT_VS 0x00000002 /* Supervisor mode valid bit */ #define PPC32_UBAT_VP 0x00000001 /* User mode valid bit */ #define PPC32_UBAT_PROT_MASK (PPC32_UBAT_VS|PPC32_UBAT_VP) /* Lower BAT register */ #define PPC32_LBAT_BRPN_MASK 0xFFFE0000 /* Physical address */ #define PPC32_LBAT_BRPN_SHIFT 17 #define PPC32_LBAT_WIMG_MASK 0x00000078 /* Memory/cache access mode bits */ #define PPC32_LBAT_PP_MASK 0x00000003 /* Protection bits */ #define PPC32_BAT_ADDR_SHIFT 17 /* Segment Descriptor */ #define PPC32_SD_T 0x80000000 #define PPC32_SD_KS 0x40000000 /* Supervisor-state protection key */ #define PPC32_SD_KP 0x20000000 /* User-state protection key */ #define PPC32_SD_N 0x10000000 /* No-execute protection bit */ #define PPC32_SD_VSID_MASK 0x00FFFFFF /* Virtual Segment ID */ /* SDR1 Register */ #define PPC32_SDR1_HTABORG_MASK 0xFFFF0000 /* Physical base address */ #define PPC32_SDR1_HTABEXT_MASK 0x0000E000 /* Extended base address */ #define PPC32_SDR1_HTABMASK 0x000001FF /* Mask for page table address */ #define PPC32_SDR1_HTMEXT_MASK 0x00001FFF /* Extended mask */ /* Page Table Entry (PTE) size: 64-bits */ #define PPC32_PTE_SIZE 8 /* PTE entry (Up and Lo) */ #define PPC32_PTEU_V 0x80000000 /* Valid entry */ #define PPC32_PTEU_VSID_MASK 0x7FFFFF80 /* Virtual Segment ID */ #define PPC32_PTEU_VSID_SHIFT 7 #define PPC32_PTEU_H 0x00000040 /* Hash function */ #define PPC32_PTEU_API_MASK 0x0000003F /* Abbreviated Page index */ #define PPC32_PTEL_RPN_MASK 0xFFFFF000 /* Physical Page Number */ #define PPC32_PTEL_XPN_MASK 0x00000C00 /* Extended Page Number (0-2) */ #define PPC32_PTEL_XPN_SHIFT 9 #define PPC32_PTEL_R 0x00000100 /* Referenced bit */ #define PPC32_PTEL_C 0x00000080 /* Changed bit */ #define PPC32_PTEL_WIMG_MASK 0x00000078 /* Mem/cache access mode bits */ #define PPC32_PTEL_WIMG_SHIFT 3 #define PPC32_PTEL_X_MASK 0x00000004 /* Extended Page Number (3) */ #define PPC32_PTEL_X_SHIFT 2 #define PPC32_PTEL_PP_MASK 0x00000003 /* Page Protection bits */ /* DSISR register */ #define PPC32_DSISR_NOTRANS 0x40000000 /* No valid translation */ #define PPC32_DSISR_STORE 0x02000000 /* Store operation */ /* PowerPC 405 TLB definitions */ #define PPC405_TLBHI_EPN_MASK 0xFFFFFC00 /* Effective Page Number */ #define PPC405_TLBHI_SIZE_MASK 0x00000380 /* Page Size */ #define PPC405_TLBHI_SIZE_SHIFT 7 #define PPC405_TLBHI_V 0x00000040 /* Valid TLB entry */ #define PPC405_TLBHI_E 0x00000020 /* Endianness */ #define PPC405_TLBHI_U0 0x00000010 /* User-Defined Attribute */ #define PPC405_TLBLO_RPN_MASK 0xFFFFFC00 /* Real Page Number */ #define PPC405_TLBLO_EX 0x00000200 /* Execute Enable */ #define PPC405_TLBLO_WR 0x00000100 /* Write Enable */ #define PPC405_TLBLO_ZSEL_MASK 0x000000F0 /* Zone Select */ #define PPC405_TLBLO_ZSEL_SHIFT 4 #define PPC405_TLBLO_W 0x00000008 /* Write-Through */ #define PPC405_TLBLO_I 0x00000004 /* Caching Inhibited */ #define PPC405_TLBLO_M 0x00000002 /* Memory Coherent */ #define PPC405_TLBLO_G 0x00000001 /* Guarded */ /* Number of TLB entries for PPC405 */ #define PPC405_TLB_ENTRIES 64 struct ppc405_tlb_entry { m_uint32_t tlb_hi,tlb_lo,tid; }; /* Memory operations */ enum { PPC_MEMOP_LOOKUP = 0, PPC_MEMOP_IFETCH, /* Load operations */ PPC_MEMOP_LBZ, PPC_MEMOP_LHZ, PPC_MEMOP_LWZ, /* Load operation with sign-extend */ PPC_MEMOP_LHA, /* Store operations */ PPC_MEMOP_STB, PPC_MEMOP_STH, PPC_MEMOP_STW, /* Byte-Reversed operations */ PPC_MEMOP_LWBR, PPC_MEMOP_STWBR, /* String operations */ PPC_MEMOP_LSW, PPC_MEMOP_STSW, /* FPU operations */ PPC_MEMOP_LFD, PPC_MEMOP_STFD, /* ICBI - Instruction Cache Block Invalidate */ PPC_MEMOP_ICBI, PPC_MEMOP_MAX, }; /* PowerPC CPU type */ typedef struct cpu_ppc cpu_ppc_t; /* Memory operation function prototype */ typedef fastcall void (*ppc_memop_fn)(cpu_ppc_t *cpu,m_uint32_t vaddr, u_int reg); /* BAT type indexes */ enum { PPC32_IBAT_IDX = 0, PPC32_DBAT_IDX, }; /* BAT register */ struct ppc32_bat_reg { m_uint32_t reg[2]; }; /* BAT register programming */ struct ppc32_bat_prog { int type,index; m_uint32_t hi,lo; }; /* MTS Instruction Cache and Data Cache */ #define PPC32_MTS_ICACHE PPC32_IBAT_IDX #define PPC32_MTS_DCACHE PPC32_DBAT_IDX /* FPU Coprocessor definition */ typedef struct { m_uint64_t reg[PPC32_FPU_REG_NR]; }ppc_fpu_t; /* Maximum number of breakpoints */ #define PPC32_MAX_BREAKPOINTS 8 /* PowerPC CPU definition */ struct cpu_ppc { /* Execution state */ m_uint32_t exec_state; /* Instruction address */ m_uint32_t ia; /* General Purpose registers */ m_uint32_t gpr[PPC32_GPR_NR]; /* Pending IRQ */ volatile m_uint32_t irq_pending,irq_check; /* XER, Condition Register, Link Register, Count Register */ m_uint32_t xer,lr,ctr,reserve; m_uint32_t xer_ca; /* Condition Register (CR) fields */ u_int cr_fields[8]; /* MTS caches (Instruction+Data) */ mts32_entry_t *mts_cache[2]; /* Code page translation cache and physical page mapping */ ppc32_jit_tcb_t **tcb_virt_hash,**tcb_phys_hash; /* Virtual address to physical page translation */ fastcall int (*translate)(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int cid, m_uint32_t *phys_page); /* Memory access functions */ ppc_memop_fn mem_op_fn[PPC_MEMOP_MAX]; /* Memory lookup function (to load ELF image,...) and Instruction fetch */ void *(*mem_op_lookup)(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int cid); void *(*mem_op_ifetch)(cpu_ppc_t *cpu,m_uint32_t vaddr); /* MTS slow lookup function */ mts32_entry_t *(*mts_slow_lookup)(cpu_ppc_t *cpu,m_uint32_t vaddr, u_int cid,u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data, mts32_entry_t *alt_entry); /* IRQ counters */ m_uint64_t irq_count,timer_irq_count,irq_fp_count; pthread_mutex_t irq_lock; /* Current and free lists of translated code blocks */ ppc32_jit_tcb_t *tcb_list,*tcb_last,*tcb_free_list; /* Executable page area */ void *exec_page_area; size_t exec_page_area_size; size_t exec_page_count,exec_page_alloc; insn_exec_page_t *exec_page_free_list; insn_exec_page_t *exec_page_array; /* Idle PC value */ volatile m_uint32_t idle_pc; /* Timer IRQs */ volatile u_int timer_irq_pending,timer_irq_armed; u_int timer_irq_freq; u_int timer_irq_check_itv; u_int timer_drift; /* IRQ disable flag */ volatile u_int irq_disable; /* IBAT (Instruction) and DBAT (Data) registers */ struct ppc32_bat_reg bat[2][PPC32_BAT_NR]; /* Segment registers */ m_uint32_t sr[PPC32_SR_NR]; /* Page Table Address */ m_uint32_t sdr1; void *sdr1_hptr; /* MSR (Machine state register) */ m_uint32_t msr; /* Interrupt Registers (SRR0/SRR1) */ m_uint32_t srr0,srr1,dsisr,dar; /* SPRG registers */ m_uint32_t sprg[4]; /* PVR (Processor Version Register) */ m_uint32_t pvr; /* Time-Base register */ m_uint64_t tb; /* Decrementer */ m_uint32_t dec; /* Hardware Implementation Dependent Registers */ m_uint32_t hid0,hid1; /* String instruction position (lswi/stswi) */ u_int sw_pos; /* PowerPC 405 TLB */ struct ppc405_tlb_entry ppc405_tlb[PPC405_TLB_ENTRIES]; m_uint32_t ppc405_pid; /* MPC860 IMMR register */ m_uint32_t mpc860_immr; /* FPU */ ppc_fpu_t fpu; /* Generic CPU instance pointer */ cpu_gen_t *gen; /* VM instance */ vm_instance_t *vm; /* MTS cache statistics */ m_uint64_t mts_misses,mts_lookups; /* JIT flush method */ u_int jit_flush_method; /* Number of compiled pages */ u_int compiled_pages; /* Fast memory operations use */ u_int fast_memop; /* Direct block jump */ u_int exec_blk_direct_jump; /* Current exec page (non-JIT) info */ m_uint64_t njm_exec_page; mips_insn_t *njm_exec_ptr; /* Performance counter (non-JIT) */ m_uint32_t perf_counter; /* non-JIT mode instruction counter */ m_uint64_t insn_exec_count; /* Breakpoints */ m_uint32_t breakpoints[PPC32_MAX_BREAKPOINTS]; u_int breakpoints_enabled; /* JIT host register allocation */ char *jit_hreg_seq_name; int ppc_reg_map[PPC32_GPR_NR]; struct hreg_map *hreg_map_list,*hreg_lru; struct hreg_map hreg_map[JIT_HOST_NREG]; }; #define PPC32_CR_FIELD_OFFSET(f) \ (OFFSET(cpu_ppc_t,cr_fields)+((f) * sizeof(u_int))) /* Get the full CR register */ static forced_inline m_uint32_t ppc32_get_cr(cpu_ppc_t *cpu) { m_uint32_t cr = 0; int i; for(i=0;i<8;i++) cr |= cpu->cr_fields[i] << (28 - (i << 2)); return(cr); } /* Set the CR fields given a CR value */ static forced_inline void ppc32_set_cr(cpu_ppc_t *cpu,m_uint32_t cr) { int i; for(i=0;i<8;i++) cpu->cr_fields[i] = (cr >> (28 - (i << 2))) & 0x0F; } /* Get a CR bit */ static forced_inline m_uint32_t ppc32_read_cr_bit(cpu_ppc_t *cpu,u_int bit) { m_uint32_t res; res = cpu->cr_fields[ppc32_get_cr_field(bit)] >> ppc32_get_cr_bit(bit); return(res & 0x01); } /* Set a CR bit */ static forced_inline void ppc32_set_cr_bit(cpu_ppc_t *cpu,u_int bit) { cpu->cr_fields[ppc32_get_cr_field(bit)] |= 1 << ppc32_get_cr_bit(bit); } /* Clear a CR bit */ static forced_inline void ppc32_clear_cr_bit(cpu_ppc_t *cpu,u_int bit) { cpu->cr_fields[ppc32_get_cr_field(bit)] &= ~(1 << ppc32_get_cr_bit(bit)); } /* Reset a PowerPC CPU */ int ppc32_reset(cpu_ppc_t *cpu); /* Initialize a PowerPC processor */ int ppc32_init(cpu_ppc_t *cpu); /* Delete a PowerPC processor */ void ppc32_delete(cpu_ppc_t *cpu); /* Set the processor version register (PVR) */ void ppc32_set_pvr(cpu_ppc_t *cpu,m_uint32_t pvr); /* Set idle PC value */ void ppc32_set_idle_pc(cpu_gen_t *cpu,m_uint64_t addr); /* Timer IRQ */ void *ppc32_timer_irq_run(cpu_ppc_t *cpu); /* Determine an "idling" PC */ int ppc32_get_idling_pc(cpu_gen_t *cpu); /* Generate an exception */ void ppc32_trigger_exception(cpu_ppc_t *cpu,u_int exc_vector); /* Trigger the decrementer exception */ void ppc32_trigger_timer_irq(cpu_ppc_t *cpu); /* Trigger IRQs */ fastcall void ppc32_trigger_irq(cpu_ppc_t *cpu); /* Virtual breakpoint */ fastcall void ppc32_run_breakpoint(cpu_ppc_t *cpu); /* Add a virtual breakpoint */ int ppc32_add_breakpoint(cpu_gen_t *cpu,m_uint64_t ia); /* Remove a virtual breakpoint */ void ppc32_remove_breakpoint(cpu_gen_t *cpu,m_uint64_t ia); /* Set a register */ void ppc32_reg_set(cpu_gen_t *cpu,u_int reg,m_uint64_t val); /* Dump registers of a PowerPC processor */ void ppc32_dump_regs(cpu_gen_t *cpu); /* Dump MMU registers */ void ppc32_dump_mmu(cpu_gen_t *cpu); /* Load a raw image into the simulated memory */ int ppc32_load_raw_image(cpu_ppc_t *cpu,char *filename,m_uint32_t vaddr); /* Load an ELF image into the simulated memory */ int ppc32_load_elf_image(cpu_ppc_t *cpu,char *filename,int skip_load, m_uint32_t *entry_point); /* Run PowerPC code in step-by-step mode */ void *ppc32_exec_run_cpu(cpu_gen_t *gen); #endif dynamips-0.2.14/unstable/ppc32_amd64_trans.c000066400000000000000000003316741241034141600205460ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #include #include #include #include #include #include #include #include "cpu.h" #include "jit_op.h" #include "ppc32_jit.h" #include "ppc32_amd64_trans.h" #include "memory.h" /* Macros for CPU structure access */ #define REG_OFFSET(reg) (OFFSET(cpu_ppc_t,gpr[(reg)])) #define MEMOP_OFFSET(op) (OFFSET(cpu_ppc_t,mem_op_fn[(op)])) #define DECLARE_INSN(name) \ static int ppc32_emit_##name(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, \ ppc_insn_t insn) /* EFLAGS to Condition Register (CR) field - signed */ static m_uint32_t eflags_to_cr_signed[64] = { 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, }; /* EFLAGS to Condition Register (CR) field - unsigned */ static m_uint32_t eflags_to_cr_unsigned[256] = { 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, }; /* Load a 32 bit immediate value */ static inline void ppc32_load_imm(u_char **ptr,u_int reg,m_uint32_t val) { if (val) amd64_mov_reg_imm_size(*ptr,reg,val,4); else amd64_alu_reg_reg_size(*ptr,X86_XOR,reg,reg,4); } /* Set the Instruction Address (IA) register */ void ppc32_set_ia(u_char **ptr,m_uint32_t new_ia) { amd64_mov_membase_imm(*ptr,AMD64_R15,OFFSET(cpu_ppc_t,ia),new_ia,4); } /* Set the Link Register (LR) */ static void ppc32_set_lr(jit_op_t *iop,m_uint32_t new_lr) { amd64_mov_membase_imm(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,lr),new_lr,4); } /* * Try to branch directly to the specified JIT block without returning to * main loop. */ static void ppc32_try_direct_far_jump(cpu_ppc_t *cpu,jit_op_t *iop, m_uint32_t new_ia) { m_uint32_t new_page,ia_hash,ia_offset; u_char *test1,*test2,*test3,*test4; /* Indicate that we throw %rbx, %rdx */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RBX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RSI); new_page = new_ia & PPC32_MIN_PAGE_MASK; ia_offset = (new_ia & PPC32_MIN_PAGE_IMASK) >> 2; ia_hash = ppc32_jit_get_virt_hash(new_ia); /* Get JIT block info in %rdx */ amd64_mov_reg_membase(iop->ob_ptr,AMD64_RBX, AMD64_R15,OFFSET(cpu_ppc_t,tcb_virt_hash),8); amd64_mov_reg_membase(iop->ob_ptr,AMD64_RDX, AMD64_RBX,ia_hash*sizeof(void *),8); /* no JIT block found ? */ amd64_test_reg_reg(iop->ob_ptr,AMD64_RDX,AMD64_RDX); test1 = iop->ob_ptr; amd64_branch8(iop->ob_ptr, X86_CC_Z, 0, 1); /* Check block IA */ ppc32_load_imm(&iop->ob_ptr,AMD64_RSI,new_page); amd64_alu_reg_membase_size(iop->ob_ptr,X86_CMP,AMD64_RAX,AMD64_RDX, OFFSET(ppc32_jit_tcb_t,start_ia),4); test2 = iop->ob_ptr; amd64_branch8(iop->ob_ptr, X86_CC_NE, 0, 1); /* Jump to the code */ amd64_mov_reg_membase(iop->ob_ptr,AMD64_RSI, AMD64_RDX,OFFSET(ppc32_jit_tcb_t,jit_insn_ptr),8); amd64_test_reg_reg(iop->ob_ptr,AMD64_RSI,AMD64_RSI); test3 = iop->ob_ptr; amd64_branch8(iop->ob_ptr, X86_CC_Z, 0, 1); amd64_mov_reg_membase(iop->ob_ptr,AMD64_RBX, AMD64_RSI,ia_offset * sizeof(void *),8); amd64_test_reg_reg(iop->ob_ptr,AMD64_RBX,AMD64_RBX); test4 = iop->ob_ptr; amd64_branch8(iop->ob_ptr, X86_CC_Z, 0, 1); amd64_jump_reg(iop->ob_ptr,AMD64_RBX); /* Returns to caller... */ amd64_patch(test1,iop->ob_ptr); amd64_patch(test2,iop->ob_ptr); amd64_patch(test3,iop->ob_ptr); amd64_patch(test4,iop->ob_ptr); ppc32_set_ia(&iop->ob_ptr,new_ia); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); } /* Set Jump */ static void ppc32_set_jump(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b,jit_op_t *iop, m_uint32_t new_ia,int local_jump) { int return_to_caller = FALSE; u_char *jump_ptr; #if 0 if (cpu->sym_trace && !local_jump) return_to_caller = TRUE; #endif if (!return_to_caller && ppc32_jit_tcb_local_addr(b,new_ia,&jump_ptr)) { ppc32_jit_tcb_record_patch(b,iop,iop->ob_ptr,new_ia); amd64_jump32(iop->ob_ptr,0); } else { if (cpu->exec_blk_direct_jump) { /* Block lookup optimization */ ppc32_try_direct_far_jump(cpu,iop,new_ia); } else { ppc32_set_ia(&iop->ob_ptr,new_ia); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); } } } /* Jump to the next page */ void ppc32_set_page_jump(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b) { jit_op_t *iop,*op_list = NULL; cpu->gen->jit_op_current = &op_list; iop = ppc32_op_emit_insn_output(cpu,4,"set_page_jump"); ppc32_set_jump(cpu,b,iop,b->start_ia + PPC32_MIN_PAGE_SIZE,FALSE); ppc32_op_insn_output(b,iop); jit_op_free_list(cpu->gen,op_list); cpu->gen->jit_op_current = NULL; } /* Load a GPR into the specified host register */ static forced_inline void ppc32_load_gpr(u_char **ptr,u_int host_reg, u_int ppc_reg) { amd64_mov_reg_membase(*ptr,host_reg,AMD64_R15,REG_OFFSET(ppc_reg),4); } /* Store contents for a host register into a GPR register */ static forced_inline void ppc32_store_gpr(u_char **ptr,u_int ppc_reg, u_int host_reg) { amd64_mov_membase_reg(*ptr,AMD64_R15,REG_OFFSET(ppc_reg),host_reg,4); } /* Apply an ALU operation on a GPR register and a host register */ static forced_inline void ppc32_alu_gpr(u_char **ptr,u_int op, u_int host_reg,u_int ppc_reg) { amd64_alu_reg_membase_size(*ptr,op,host_reg, AMD64_R15,REG_OFFSET(ppc_reg),4); } /* * Update CR from %eflags * %rax, %rdx, %rsi are modified. */ static void ppc32_update_cr(ppc32_jit_tcb_t *b,int field,int is_signed) { /* Get status bits from EFLAGS */ amd64_pushfd_size(b->jit_ptr,8); amd64_pop_reg(b->jit_ptr,AMD64_RAX); if (!is_signed) { amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RAX,0xFF); amd64_mov_reg_imm_size(b->jit_ptr,AMD64_RDX,eflags_to_cr_unsigned,8); } else { amd64_shift_reg_imm(b->jit_ptr,X86_SHR,AMD64_RAX,6); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RAX,0x3F); amd64_mov_reg_imm_size(b->jit_ptr,AMD64_RDX,eflags_to_cr_signed,8); } amd64_mov_reg_memindex(b->jit_ptr,AMD64_RAX,AMD64_RDX,0,AMD64_RAX,2,4); #if 0 /* Check XER Summary of Overflow and report it */ amd64_mov_reg_membase(b->jit_ptr,AMD64_RCX, AMD64_R15,OFFSET(cpu_ppc_t,xer),4); amd64_alu_reg_imm(b->jit_ptr,X86_AND,AMD64_RCX,PPC32_XER_SO); amd64_shift_reg_imm(b->jit_ptr,X86_SHR,AMD64_RCX,(field << 2) + 3); amd64_alu_reg_reg(b->jit_ptr,X86_OR,AMD64_RDX,AMD64_RCX); #endif /* Store modified CR field */ amd64_mov_membase_reg(b->jit_ptr,AMD64_R15,PPC32_CR_FIELD_OFFSET(field), AMD64_RAX,4); } /* * Update CR0 from %eflags * %eax, %ecx, %edx, %esi are modified. */ static void ppc32_update_cr0(ppc32_jit_tcb_t *b) { ppc32_update_cr(b,0,TRUE); } /* Indicate registers modified by ppc32_update_cr() functions */ void ppc32_update_cr_set_altered_hreg(cpu_ppc_t *cpu) { ppc32_op_emit_alter_host_reg(cpu,AMD64_RAX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); } /* Basic C call */ static forced_inline void ppc32_emit_basic_c_call(u_char **ptr,void *f) { amd64_mov_reg_imm(*ptr,AMD64_RBX,f); amd64_call_reg(*ptr,AMD64_RBX); } /* Emit a simple call to a C function without any parameter */ static void ppc32_emit_c_call(ppc32_jit_tcb_t *b,jit_op_t *iop,void *f) { ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); ppc32_emit_basic_c_call(&iop->ob_ptr,f); } /* ======================================================================== */ /* Initialize register mapping */ void ppc32_jit_init_hreg_mapping(cpu_ppc_t *cpu) { int avail_hregs[] = { AMD64_RSI, AMD64_RAX, AMD64_RCX, AMD64_RDX, AMD64_R13, AMD64_R14, AMD64_RDI, -1 }; struct hreg_map *map; int i,hreg; cpu->hreg_map_list = cpu->hreg_lru = NULL; /* Add the available registers to the map list */ for(i=0;avail_hregs[i]!=-1;i++) { hreg = avail_hregs[i]; map = &cpu->hreg_map[hreg]; /* Initialize mapping. At the beginning, no PPC reg is mapped */ map->flags = 0; map->hreg = hreg; map->vreg = -1; ppc32_jit_insert_hreg_mru(cpu,map); } /* Clear PPC registers mapping */ for(i=0;ippc_reg_map[i] = -1; } /* Allocate a specific temp register */ static int ppc32_jit_get_tmp_hreg(cpu_ppc_t *cpu) { return(AMD64_RBX); } /* ======================================================================== */ /* JIT operations (specific to target CPU). */ /* ======================================================================== */ /* INSN_OUTPUT */ void ppc32_op_insn_output(ppc32_jit_tcb_t *b,jit_op_t *op) { op->ob_final = b->jit_ptr; memcpy(b->jit_ptr,op->ob_data,op->ob_ptr - op->ob_data); b->jit_ptr += op->ob_ptr - op->ob_data; } /* LOAD_GPR: p[0] = %host_reg, p[1] = %ppc_reg */ void ppc32_op_load_gpr(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_load_gpr(&b->jit_ptr,op->param[0],op->param[1]); } /* STORE_GPR: p[0] = %host_reg, p[1] = %ppc_reg */ void ppc32_op_store_gpr(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_store_gpr(&b->jit_ptr,op->param[1],op->param[0]); } /* UPDATE_FLAGS: p[0] = cr_field, p[1] = is_signed */ void ppc32_op_update_flags(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_update_cr(b,op->param[0],op->param[1]); } /* MOVE_HOST_REG: p[0] = %host_dst_reg, p[1] = %host_src_reg */ void ppc32_op_move_host_reg(ppc32_jit_tcb_t *b,jit_op_t *op) { if ((op->param[0] != JIT_OP_INV_REG) && (op->param[1] != JIT_OP_INV_REG)) amd64_mov_reg_reg(b->jit_ptr,op->param[0],op->param[1],4); } /* SET_HOST_REG_IMM32: p[0] = %host_reg, p[1] = imm32 */ void ppc32_op_set_host_reg_imm32(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_load_imm(&b->jit_ptr,op->param[0],op->param[1]); } /* ======================================================================== */ /* Memory operation */ static void ppc32_emit_memop(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, int op,int base,int offset,int target,int update) { m_uint32_t val = sign_extend(offset,16); jit_op_t *iop; /* * Since an exception can be triggered, clear JIT state. This allows * to use branch target tag (we can directly branch on this instruction). */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_TARGET); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); iop = ppc32_op_emit_insn_output(cpu,5,"memop"); /* Save PC for exception handling */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* RSI = sign-extended offset */ ppc32_load_imm(&iop->ob_ptr,AMD64_RSI,val); /* RSI = GPR[base] + sign-extended offset */ if (update || (base != 0)) ppc32_alu_gpr(&iop->ob_ptr,X86_ADD,AMD64_RSI,base); if (update) amd64_mov_reg_reg(iop->ob_ptr,AMD64_R14,AMD64_RSI,4); /* RDX = target register */ amd64_mov_reg_imm(iop->ob_ptr,AMD64_RDX,target); /* RDI = CPU instance pointer */ amd64_mov_reg_reg(iop->ob_ptr,AMD64_RDI,AMD64_R15,8); /* Call memory function */ amd64_alu_reg_imm(iop->ob_ptr,X86_SUB,AMD64_RSP,8); amd64_call_membase(iop->ob_ptr,AMD64_R15,MEMOP_OFFSET(op)); amd64_alu_reg_imm(iop->ob_ptr,X86_ADD,AMD64_RSP,8); if (update) ppc32_store_gpr(&iop->ob_ptr,base,AMD64_R14); } /* Memory operation (indexed) */ static void ppc32_emit_memop_idx(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, int op,int ra,int rb,int target,int update) { jit_op_t *iop; /* * Since an exception can be triggered, clear JIT state. This allows * to use branch target tag (we can directly branch on this instruction). */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_TARGET); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); iop = ppc32_op_emit_insn_output(cpu,5,"memop_idx"); /* Save PC for exception handling */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* RSI = $rb */ ppc32_load_gpr(&iop->ob_ptr,AMD64_RSI,rb); /* RSI = GPR[base] + sign-extended offset */ if (update || (ra != 0)) ppc32_alu_gpr(&iop->ob_ptr,X86_ADD,AMD64_RSI,ra); if (update) amd64_mov_reg_reg(iop->ob_ptr,AMD64_R14,AMD64_RSI,4); /* RDX = target register */ amd64_mov_reg_imm(iop->ob_ptr,AMD64_RDX,target); /* RDI = CPU instance pointer */ amd64_mov_reg_reg(iop->ob_ptr,AMD64_RDI,AMD64_R15,8); /* Call memory function */ amd64_alu_reg_imm(iop->ob_ptr,X86_SUB,AMD64_RSP,8); amd64_call_membase(iop->ob_ptr,AMD64_R15,MEMOP_OFFSET(op)); amd64_alu_reg_imm(iop->ob_ptr,X86_ADD,AMD64_RSP,8); if (update) ppc32_store_gpr(&iop->ob_ptr,ra,AMD64_R14); } typedef void (*memop_fast_access)(jit_op_t *iop,int target); /* Fast LBZ */ static void ppc32_memop_fast_lbz(jit_op_t *iop,int target) { amd64_clear_reg(iop->ob_ptr,AMD64_RCX); amd64_mov_reg_memindex(iop->ob_ptr,AMD64_RCX,AMD64_RBX,0,AMD64_RSI,0,1); ppc32_store_gpr(&iop->ob_ptr,target,AMD64_RCX); } /* Fast STB */ static void ppc32_memop_fast_stb(jit_op_t *iop,int target) { ppc32_load_gpr(&iop->ob_ptr,AMD64_RDX,target); amd64_mov_memindex_reg(iop->ob_ptr,AMD64_RBX,0,AMD64_RSI,0,AMD64_RDX,1); } /* Fast LWZ */ static void ppc32_memop_fast_lwz(jit_op_t *iop,int target) { amd64_mov_reg_memindex(iop->ob_ptr,AMD64_RAX,AMD64_RBX,0,AMD64_RSI,0,4); amd64_bswap32(iop->ob_ptr,AMD64_RAX); ppc32_store_gpr(&iop->ob_ptr,target,AMD64_RAX); } /* Fast STW */ static void ppc32_memop_fast_stw(jit_op_t *iop,int target) { ppc32_load_gpr(&iop->ob_ptr,AMD64_RDX,target); amd64_bswap32(iop->ob_ptr,AMD64_RDX); amd64_mov_memindex_reg(iop->ob_ptr,AMD64_RBX,0,AMD64_RSI,0,AMD64_RDX,4); } /* Fast memory operation */ static void ppc32_emit_memop_fast(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, int write_op,int opcode, int base,int offset,int target, memop_fast_access op_handler) { m_uint32_t val = sign_extend(offset,16); u_char *test1,*test2,*p_exit; jit_op_t *iop; /* * Since an exception can be triggered, clear JIT state. This allows * to use branch target tag (we can directly branch on this instruction). */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_TARGET); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); iop = ppc32_op_emit_insn_output(cpu,5,"memop_fast"); test2 = NULL; /* XXX */ amd64_inc_membase(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,mts_lookups)); /* RSI = GPR[base] + sign-extended offset */ ppc32_load_imm(&iop->ob_ptr,AMD64_RSI,val); if (base != 0) ppc32_alu_gpr(&iop->ob_ptr,X86_ADD,AMD64_RSI,base); /* RBX = mts32_entry index */ amd64_mov_reg_reg_size(iop->ob_ptr,X86_EBX,X86_ESI,4); amd64_mov_reg_reg_size(iop->ob_ptr,X86_EAX,X86_ESI,4); amd64_shift_reg_imm_size(iop->ob_ptr,X86_SHR,X86_EBX,MTS32_HASH_SHIFT1,4); amd64_shift_reg_imm_size(iop->ob_ptr,X86_SHR,X86_EAX,MTS32_HASH_SHIFT2,4); amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,AMD64_RBX,AMD64_RAX); amd64_alu_reg_imm_size(iop->ob_ptr,X86_AND,X86_EBX,MTS32_HASH_MASK,4); /* RCX = mts32 entry */ amd64_mov_reg_membase(iop->ob_ptr,AMD64_RCX, AMD64_R15, OFFSET(cpu_ppc_t,mts_cache[PPC32_MTS_DCACHE]),8); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,AMD64_RBX,5); /* TO FIX */ amd64_alu_reg_reg(iop->ob_ptr,X86_ADD,AMD64_RCX,AMD64_RBX); /* Compare virtual page address (EAX = vpage) */ amd64_mov_reg_reg(iop->ob_ptr,X86_EAX,X86_ESI,4); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,X86_EAX,PPC32_MIN_PAGE_MASK); amd64_alu_reg_membase_size(iop->ob_ptr,X86_CMP,X86_EAX,AMD64_RCX, OFFSET(mts32_entry_t,gvpa),4); test1 = iop->ob_ptr; amd64_branch8(iop->ob_ptr, X86_CC_NZ, 0, 1); /* Test if we are writing to a COW page */ if (write_op) { amd64_test_membase_imm_size(iop->ob_ptr, AMD64_RCX,OFFSET(mts32_entry_t,flags), MTS_FLAG_COW|MTS_FLAG_EXEC,4); test2 = iop->ob_ptr; amd64_branch8(iop->ob_ptr, X86_CC_NZ, 0, 1); } /* ESI = offset in page, RBX = Host Page Address */ amd64_alu_reg_imm(iop->ob_ptr,X86_AND,X86_ESI,PPC32_MIN_PAGE_IMASK); amd64_mov_reg_membase(iop->ob_ptr,AMD64_RBX, AMD64_RCX,OFFSET(mts32_entry_t,hpa),8); /* Memory access */ op_handler(iop,target); p_exit = iop->ob_ptr; amd64_jump8(iop->ob_ptr,0); /* === Slow lookup === */ amd64_patch(test1,iop->ob_ptr); if (test2) amd64_patch(test2,iop->ob_ptr); /* Save IA for exception handling */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* RDX = target register */ amd64_mov_reg_imm(iop->ob_ptr,AMD64_RDX,target); /* RDI = CPU instance */ amd64_mov_reg_reg(iop->ob_ptr,AMD64_RDI,AMD64_R15,8); /* Call memory access function */ amd64_alu_reg_imm(iop->ob_ptr,X86_SUB,AMD64_RSP,8); amd64_call_membase(iop->ob_ptr,AMD64_R15,MEMOP_OFFSET(opcode)); amd64_alu_reg_imm(iop->ob_ptr,X86_ADD,AMD64_RSP,8); amd64_patch(p_exit,iop->ob_ptr); } /* Emit unhandled instruction code */ static int ppc32_emit_unknown(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, ppc_insn_t opcode) { u_char *test1; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,3,"unknown"); /* Update IA */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* Fallback to non-JIT mode */ amd64_mov_reg_reg(iop->ob_ptr,AMD64_RDI,AMD64_R15,8); amd64_mov_reg_imm(iop->ob_ptr,AMD64_RSI,opcode); amd64_alu_reg_imm(iop->ob_ptr,X86_SUB,AMD64_RSP,8); ppc32_emit_c_call(b,iop,ppc32_exec_single_insn_ext); amd64_alu_reg_imm(iop->ob_ptr,X86_ADD,AMD64_RSP,8); amd64_test_reg_reg_size(iop->ob_ptr,AMD64_RAX,AMD64_RAX,4); test1 = iop->ob_ptr; amd64_branch8(iop->ob_ptr, X86_CC_Z, 0, 1); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); amd64_patch(test1,iop->ob_ptr); /* Signal this as an EOB to reset JIT state */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); return(0); } /* Virtual Breakpoint */ void ppc32_emit_breakpoint(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b) { jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,2,"breakpoint"); amd64_mov_reg_reg(iop->ob_ptr,AMD64_RDI,AMD64_R15,8); amd64_alu_reg_imm(iop->ob_ptr,X86_SUB,AMD64_RSP,8); ppc32_emit_c_call(b,iop,ppc32_run_breakpoint); amd64_alu_reg_imm(iop->ob_ptr,X86_ADD,AMD64_RSP,8); /* Signal this as an EOB to to reset JIT state */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); } /* Increment the number of executed instructions (performance debugging) */ void ppc32_inc_perf_counter(cpu_ppc_t *cpu) { jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,1,"perf_cnt"); amd64_inc_membase_size(iop->ob_ptr, AMD64_R15,OFFSET(cpu_ppc_t,perf_counter),4); } /* ======================================================================== */ /* BLR - Branch to Link Register */ DECLARE_INSN(BLR) { jit_op_t *iop; int hreg; ppc32_jit_start_hreg_seq(cpu,"blr"); hreg = ppc32_jit_alloc_hreg(cpu,-1); ppc32_op_emit_alter_host_reg(cpu,hreg); iop = ppc32_op_emit_insn_output(cpu,2,"blr"); amd64_mov_reg_membase(iop->ob_ptr,hreg,AMD64_R15,OFFSET(cpu_ppc_t,lr),4); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,ia),hreg,4); /* set the return address */ if (insn & 1) ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); ppc32_jit_close_hreg_seq(cpu); return(0); } /* BCTR - Branch to Count Register */ DECLARE_INSN(BCTR) { jit_op_t *iop; int hreg; ppc32_jit_start_hreg_seq(cpu,"bctr"); hreg = ppc32_jit_alloc_hreg(cpu,-1); ppc32_op_emit_alter_host_reg(cpu,hreg); iop = ppc32_op_emit_insn_output(cpu,2,"bctr"); amd64_mov_reg_membase(iop->ob_ptr,hreg,AMD64_R15,OFFSET(cpu_ppc_t,ctr),4); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,ia),hreg,4); /* set the return address */ if (insn & 1) ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFLR - Move From Link Register */ DECLARE_INSN(MFLR) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mflr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mflr"); amd64_mov_reg_membase(iop->ob_ptr,hreg_rd,AMD64_R15,OFFSET(cpu_ppc_t,lr),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MTLR - Move To Link Register */ DECLARE_INSN(MTLR) { int rs = bits(insn,21,25); int hreg_rs; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mtlr"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"mtlr"); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,lr),hreg_rs,4); return(0); } /* MFCTR - Move From Counter Register */ DECLARE_INSN(MFCTR) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mfctr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mfctr"); amd64_mov_reg_membase(iop->ob_ptr,hreg_rd, AMD64_R15,OFFSET(cpu_ppc_t,ctr),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MTCTR - Move To Counter Register */ DECLARE_INSN(MTCTR) { int rs = bits(insn,21,25); int hreg_rs; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mtctr"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"mtctr"); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,ctr), hreg_rs,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFTBU - Move from Time Base (Up) */ DECLARE_INSN(MFTBU) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mftbu"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mftbu"); amd64_mov_reg_membase(iop->ob_ptr,hreg_rd, AMD64_R15,OFFSET(cpu_ppc_t,tb)+4,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } #define PPC32_TB_INCREMENT 50 /* MFTBL - Move from Time Base (Lo) */ DECLARE_INSN(MFTBL) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mftbl"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,3,"mftbl"); amd64_mov_reg_membase(iop->ob_ptr,hreg_rd, AMD64_R15,OFFSET(cpu_ppc_t,tb),8); amd64_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_rd,PPC32_TB_INCREMENT); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,tb), hreg_rd,8); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADD */ DECLARE_INSN(ADD) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rd,hreg_ra,hreg_rb; jit_op_t *iop; /* $rd = $ra + $rb */ ppc32_jit_start_hreg_seq(cpu,"add"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"add"); if (rd == ra) amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb,4); else if (rd == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_rd,hreg_ra,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb,4); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDC */ DECLARE_INSN(ADDC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rd,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $rd = $ra + $rb */ ppc32_jit_start_hreg_seq(cpu,"addc"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); /* store the carry flag */ hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"addc"); if (rd == ra) amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb,4); else if (rd == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_rd,hreg_ra,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb,4); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); /* store the carry flag */ amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t0,FALSE); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x1); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t0,4); if (insn & 1) { amd64_test_reg_reg_size(iop->ob_ptr,hreg_rd,hreg_rd,4); ppc32_op_emit_update_flags(cpu,0,TRUE); } ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDE - Add Extended */ DECLARE_INSN(ADDE) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"adde"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,3,"adde"); /* $t0 = $ra + carry */ amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); amd64_alu_reg_membase_size(iop->ob_ptr,X86_ADD,hreg_t0, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca),4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); /* $t0 += $rb */ amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_t0,hreg_rb,4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); /* update cr0 */ if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_t0,hreg_t0,4); amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDI - ADD Immediate */ DECLARE_INSN(ADDI) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + imm */ ppc32_jit_start_hreg_seq(cpu,"addi"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); if (ra != 0) { hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,2,"addi"); if (rd != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_ADD,hreg_rd,tmp,4); } else { iop = ppc32_op_emit_insn_output(cpu,1,"addi"); ppc32_load_imm(&iop->ob_ptr,hreg_rd,tmp); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDIC - ADD Immediate with Carry */ DECLARE_INSN(ADDIC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + imm */ ppc32_jit_start_hreg_seq(cpu,"addic"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"addic"); if (rd != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_ADD,hreg_rd,tmp,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); amd64_set_membase(iop->ob_ptr,X86_CC_C, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca),FALSE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDIC. */ DECLARE_INSN(ADDIC_dot) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + imm */ ppc32_jit_start_hreg_seq(cpu,"addic."); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"addic."); if (rd != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_ADD,hreg_rd,tmp,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); amd64_set_membase(iop->ob_ptr,X86_CC_C, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca),FALSE); ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDIS - ADD Immediate Shifted */ DECLARE_INSN(ADDIS) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + (imm << 16) */ ppc32_jit_start_hreg_seq(cpu,"addis"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); if (ra != 0) { hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"addis"); if (rd != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_ADD,hreg_rd,tmp,4); } else { iop = ppc32_op_emit_insn_output(cpu,1,"addis"); amd64_mov_reg_imm(iop->ob_ptr,hreg_rd,tmp); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDZE */ DECLARE_INSN(ADDZE) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rd,hreg_ra,hreg_t0; jit_op_t *iop; /* $rd = $ra + xer_ca + set_carry */ ppc32_jit_start_hreg_seq(cpu,"addze"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,2,"addze"); amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,hreg_t0); if (rd != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_alu_reg_membase_size(iop->ob_ptr,X86_ADD,hreg_rd, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca),4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t0,FALSE); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t0,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* AND */ DECLARE_INSN(AND) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = $rs & $rb */ ppc32_jit_start_hreg_seq(cpu,"and"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"and"); if (ra == rs) amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb,4); else if (ra == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_ra,hreg_rs,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb,4); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ANDC */ DECLARE_INSN(ANDC) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $ra = $rs & ~$rb */ ppc32_jit_start_hreg_seq(cpu,"andc"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"andc"); /* $t0 = ~$rb */ hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rb,4); amd64_not_reg(iop->ob_ptr,hreg_t0); /* $ra = $rs & $t0 */ if (ra == rs) amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_ra,hreg_t0,4); else { amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_t0,hreg_rs,4); amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* AND Immediate */ DECLARE_INSN(ANDI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs & imm */ ppc32_jit_start_hreg_seq(cpu,"andi"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"andi"); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_AND,hreg_ra,tmp,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* AND Immediate Shifted */ DECLARE_INSN(ANDIS) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs & imm */ ppc32_jit_start_hreg_seq(cpu,"andis"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"andis"); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_AND,hreg_ra,tmp,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* B - Branch */ DECLARE_INSN(B) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"b"); /* compute the new ia */ new_ia = b->start_ia + (b->ppc_trans_pos << 2); new_ia += sign_extend(offset << 2,26); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BA - Branch Absolute */ DECLARE_INSN(BA) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"ba"); /* compute the new ia */ new_ia = sign_extend(offset << 2,26); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BL - Branch and Link */ DECLARE_INSN(BL) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"bl"); /* compute the new ia */ new_ia = b->start_ia + (b->ppc_trans_pos << 2); new_ia += sign_extend(offset << 2,26); /* set the return address */ ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BLA - Branch and Link Absolute */ DECLARE_INSN(BLA) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"bla"); /* compute the new ia */ new_ia = sign_extend(offset << 2,26); /* set the return address */ ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BC - Branch Conditional (Condition Check only) */ DECLARE_INSN(BCC) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); jit_op_t *iop; u_int cr_field,cr_bit; m_uint32_t new_ia; u_char *jump_ptr; int local_jump; int cond; ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_JUMP); iop = ppc32_op_emit_insn_output(cpu,5,"bcc"); /* Get the wanted value for the condition bit */ cond = (bo >> 3) & 0x1; /* Set the return address */ if (insn & 1) { ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1)<<2)); } /* Compute the new ia */ new_ia = sign_extend_32(bd << 2,16); if (!(insn & 0x02)) new_ia += b->start_ia + (b->ppc_trans_pos << 2); /* Test the condition bit */ cr_field = ppc32_get_cr_field(bi); cr_bit = ppc32_get_cr_bit(bi); ppc32_op_emit_require_flags(cpu,cr_field); amd64_test_membase_imm_size(iop->ob_ptr, AMD64_R15,PPC32_CR_FIELD_OFFSET(cr_field), (1 << cr_bit),4); local_jump = ppc32_jit_tcb_local_addr(b,new_ia,&jump_ptr); /* * Optimize the jump, depending if the destination is in the same * page or not. */ if (local_jump) { ppc32_jit_tcb_record_patch(b,iop,iop->ob_ptr,new_ia); amd64_branch32(iop->ob_ptr,(cond) ? X86_CC_NZ : X86_CC_Z,0,FALSE); } else { jump_ptr = iop->ob_ptr; amd64_branch32(iop->ob_ptr,(cond) ? X86_CC_Z : X86_CC_NZ,0,FALSE); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); amd64_patch(jump_ptr,iop->ob_ptr); } ppc32_op_emit_branch_target(cpu,b,new_ia); return(0); } /* BC - Branch Conditional */ DECLARE_INSN(BC) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); int hreg_t0,hreg_t1; jit_op_t *iop; u_int cr_field,cr_bit; m_uint32_t new_ia; u_char *jump_ptr; int local_jump; int cond,ctr; ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_JUMP); iop = ppc32_op_emit_insn_output(cpu,5,"bc"); ppc32_jit_start_hreg_seq(cpu,"bc"); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); /* Get the wanted value for the condition bit and CTR value */ cond = (bo >> 3) & 0x1; ctr = (bo >> 1) & 0x1; /* Set the return address */ if (insn & 1) { ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1)<<2)); } /* Compute the new ia */ new_ia = sign_extend_32(bd << 2,16); if (!(insn & 0x02)) new_ia += b->start_ia + (b->ppc_trans_pos << 2); amd64_mov_reg_imm(iop->ob_ptr,hreg_t0,1); /* Decrement the count register */ if (!(bo & 0x04)) { amd64_dec_membase_size(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,ctr),4); amd64_set_reg(iop->ob_ptr,(ctr) ? X86_CC_Z : X86_CC_NZ,hreg_t1,FALSE); amd64_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } /* Test the condition bit */ if (!((bo >> 4) & 0x01)) { cr_field = ppc32_get_cr_field(bi); cr_bit = ppc32_get_cr_bit(bi); ppc32_op_emit_require_flags(cpu,cr_field); amd64_test_membase_imm_size(iop->ob_ptr, AMD64_R15,PPC32_CR_FIELD_OFFSET(cr_field), (1 << cr_bit),4); amd64_set_reg(iop->ob_ptr,(cond) ? X86_CC_NZ : X86_CC_Z,hreg_t1,FALSE); amd64_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); local_jump = ppc32_jit_tcb_local_addr(b,new_ia,&jump_ptr); /* * Optimize the jump, depending if the destination is in the same * page or not. */ if (local_jump) { ppc32_jit_tcb_record_patch(b,iop,iop->ob_ptr,new_ia); amd64_branch32(iop->ob_ptr,X86_CC_NZ,0,FALSE); } else { jump_ptr = iop->ob_ptr; amd64_branch32(iop->ob_ptr,X86_CC_Z,0,FALSE); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); amd64_patch(jump_ptr,iop->ob_ptr); } ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_jit_close_hreg_seq(cpu); return(0); } /* BCLR - Branch Conditional to Link register */ DECLARE_INSN(BCLR) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); int hreg_t0,hreg_t1; jit_op_t *iop; u_int cr_field,cr_bit; m_uint32_t new_ia; u_char *jump_ptr; int cond,ctr; ppc32_jit_start_hreg_seq(cpu,"bclr"); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); iop = ppc32_op_emit_insn_output(cpu,5,"bclr"); /* Get the wanted value for the condition bit and CTR value */ cond = (bo >> 3) & 0x1; ctr = (bo >> 1) & 0x1; /* Compute the new ia */ new_ia = sign_extend_32(bd << 2,16); if (!(insn & 0x02)) new_ia += b->start_ia + (b->ppc_trans_pos << 2); amd64_mov_reg_imm(iop->ob_ptr,hreg_t0,1); /* Decrement the count register */ if (!(bo & 0x04)) { amd64_dec_membase_size(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,ctr),4); amd64_set_reg(iop->ob_ptr,(ctr) ? X86_CC_Z : X86_CC_NZ,hreg_t1,FALSE); amd64_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } /* Test the condition bit */ if (!((bo >> 4) & 0x01)) { cr_field = ppc32_get_cr_field(bi); cr_bit = ppc32_get_cr_bit(bi); ppc32_op_emit_require_flags(cpu,cr_field); amd64_test_membase_imm_size(iop->ob_ptr, AMD64_R15,PPC32_CR_FIELD_OFFSET(cr_field), (1 << cr_bit),4); amd64_set_reg(iop->ob_ptr,(cond) ? X86_CC_NZ : X86_CC_Z,hreg_t1,FALSE); amd64_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } /* Set the return address */ amd64_mov_reg_membase(iop->ob_ptr,hreg_t1,AMD64_R15,OFFSET(cpu_ppc_t,lr),4); if (insn & 1) { ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1)<<2)); } /* Branching */ amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); jump_ptr = iop->ob_ptr; amd64_branch32(iop->ob_ptr,X86_CC_Z,0,FALSE); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t1,0xFFFFFFFC); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,ia),hreg_t1,4); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); amd64_patch(jump_ptr,iop->ob_ptr); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMP - Compare */ DECLARE_INSN(CMP) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmp"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"cmp"); amd64_alu_reg_reg_size(iop->ob_ptr,X86_CMP,hreg_ra,hreg_rb,4); ppc32_op_emit_update_flags(cpu,rd,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMPI - Compare Immediate */ DECLARE_INSN(CMPI) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_ra; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmpi"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"cmpi"); amd64_alu_reg_imm_size(iop->ob_ptr,X86_CMP,hreg_ra,tmp,4); ppc32_op_emit_update_flags(cpu,rd,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMPL - Compare Logical */ DECLARE_INSN(CMPL) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmpl"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"cmpl"); amd64_alu_reg_reg_size(iop->ob_ptr,X86_CMP,hreg_ra,hreg_rb,4); ppc32_op_emit_update_flags(cpu,rd,FALSE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMPLI - Compare Immediate */ DECLARE_INSN(CMPLI) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); int hreg_ra; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmpli"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"cmpli"); amd64_alu_reg_imm_size(iop->ob_ptr,X86_CMP,hreg_ra,imm,4); ppc32_op_emit_update_flags(cpu,rd,FALSE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRAND - Condition Register AND */ DECLARE_INSN(CRAND) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"crand"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crand"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of AND between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,AMD64_RDX); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRANDC - Condition Register AND with Complement */ DECLARE_INSN(CRANDC) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"crandc"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crandc"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_Z,hreg_t0,FALSE); /* result of AND between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,AMD64_RDX); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CREQV - Condition Register EQV */ DECLARE_INSN(CREQV) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"creqv"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"creqv"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of XOR between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,AMD64_RDX); amd64_not_reg(iop->ob_ptr,hreg_t0); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRNAND - Condition Register NAND */ DECLARE_INSN(CRNAND) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"crnand"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crnand"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of NAND between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,AMD64_RDX); amd64_not_reg(iop->ob_ptr,hreg_t0); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRNOR - Condition Register NOR */ DECLARE_INSN(CRNOR) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"crnor"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crnor"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of NOR between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_t0,AMD64_RDX); amd64_not_reg(iop->ob_ptr,hreg_t0); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CROR - Condition Register OR */ DECLARE_INSN(CROR) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"cror"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"cror"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of NOR between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_t0,AMD64_RDX); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRORC - Condition Register OR with Complement */ DECLARE_INSN(CRORC) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"crorc"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crorc"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_Z,hreg_t0,FALSE); /* result of ORC between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_t0,AMD64_RDX); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRXOR - Condition Register XOR */ DECLARE_INSN(CRXOR) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_start_hreg_seq(cpu,"crxor"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crxor"); /* test $ba bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,AMD64_RDX,FALSE); /* test $bb bit */ amd64_test_membase_imm(iop->ob_ptr, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); amd64_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of XOR between $ba and $bb */ amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,AMD64_RDX); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ amd64_alu_membase_imm_size(iop->ob_ptr,X86_AND, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd)),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15, PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* DIVWU - Divide Word Unsigned */ DECLARE_INSN(DIVWU) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"divwu"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RAX); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); /* $rd = $ra / $rb */ ppc32_op_emit_load_gpr(cpu,AMD64_RAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"divwu"); ppc32_load_imm(&iop->ob_ptr,AMD64_RDX,0); amd64_div_reg_size(iop->ob_ptr,hreg_rb,0,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,AMD64_RAX,AMD64_RAX,4); ppc32_op_emit_store_gpr(cpu,rd,AMD64_RAX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RAX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* EQV */ DECLARE_INSN(EQV) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = ~($rs ^ $rb) */ ppc32_jit_start_hreg_seq(cpu,"eqv"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"eqv"); if (ra == rs) amd64_alu_reg_reg_size(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb,4); else if (ra == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rs,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb,4); } amd64_not_reg(iop->ob_ptr,hreg_ra); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* EXTSB - Extend Sign Byte */ DECLARE_INSN(EXTSB) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = extsb($rs) */ ppc32_jit_start_hreg_seq(cpu,"extsb"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"extsb"); if (rs != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_shift_reg_imm_size(iop->ob_ptr,X86_SHL,hreg_ra,24,4); amd64_shift_reg_imm_size(iop->ob_ptr,X86_SAR,hreg_ra,24,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* EXTSH - Extend Sign Word */ DECLARE_INSN(EXTSH) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = extsh($rs) */ ppc32_jit_start_hreg_seq(cpu,"extsh"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"extsh"); if (rs != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_shift_reg_imm_size(iop->ob_ptr,X86_SHL,hreg_ra,16,4); amd64_shift_reg_imm_size(iop->ob_ptr,X86_SAR,hreg_ra,16,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* LBZ - Load Byte and Zero */ DECLARE_INSN(LBZ) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(b,PPC_MEMOP_LBZ,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,0,PPC_MEMOP_LBZ,ra,offset,rs, ppc32_memop_fast_lbz); return(0); } /* LBZU - Load Byte and Zero with Update */ DECLARE_INSN(LBZU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LBZ,ra,offset,rs,1); return(0); } /* LBZUX - Load Byte and Zero with Update Indexed */ DECLARE_INSN(LBZUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LBZ,ra,rb,rs,1); return(0); } /* LBZX - Load Byte and Zero Indexed */ DECLARE_INSN(LBZX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LBZ,ra,rb,rs,0); return(0); } /* LHA - Load Half-Word Algebraic */ DECLARE_INSN(LHA) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHA,ra,offset,rs,0); return(0); } /* LHAU - Load Half-Word Algebraic with Update */ DECLARE_INSN(LHAU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHA,ra,offset,rs,1); return(0); } /* LHAUX - Load Half-Word Algebraic with Update Indexed */ DECLARE_INSN(LHAUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHA,ra,rb,rs,1); return(0); } /* LHAX - Load Half-Word Algebraic Indexed */ DECLARE_INSN(LHAX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHA,ra,rb,rs,0); return(0); } /* LHZ - Load Half-Word and Zero */ DECLARE_INSN(LHZ) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHZ,ra,offset,rs,0); return(0); } /* LHZU - Load Half-Word and Zero with Update */ DECLARE_INSN(LHZU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHZ,ra,offset,rs,1); return(0); } /* LHZUX - Load Half-Word and Zero with Update Indexed */ DECLARE_INSN(LHZUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHZ,ra,rb,rs,1); return(0); } /* LHZX - Load Half-Word and Zero Indexed */ DECLARE_INSN(LHZX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHZ,ra,rb,rs,0); return(0); } /* LWZ - Load Word and Zero */ DECLARE_INSN(LWZ) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(b,PPC_MEMOP_LWZ,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,0,PPC_MEMOP_LWZ,ra,offset,rs, ppc32_memop_fast_lwz); return(0); } /* LWZU - Load Word and Zero with Update */ DECLARE_INSN(LWZU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LWZ,ra,offset,rs,1); return(0); } /* LWZUX - Load Word and Zero with Update Indexed */ DECLARE_INSN(LWZUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LWZ,ra,rb,rs,1); return(0); } /* LWZX - Load Word and Zero Indexed */ DECLARE_INSN(LWZX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LWZ,ra,rb,rs,0); return(0); } /* MCRF - Move Condition Register Field */ DECLARE_INSN(MCRF) { int rd = bits(insn,23,25); int rs = bits(insn,18,20); int hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mcrf"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_require_flags(cpu,rs); iop = ppc32_op_emit_insn_output(cpu,1,"mcrf"); /* Load "rs" field in %edx */ amd64_mov_reg_membase(iop->ob_ptr,hreg_t0, AMD64_R15,PPC32_CR_FIELD_OFFSET(rs),4); /* Store it in "rd" field */ amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,PPC32_CR_FIELD_OFFSET(rd), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFCR - Move from Condition Register */ DECLARE_INSN(MFCR) { int rd = bits(insn,21,25); int hreg_rd,hreg_t0; jit_op_t *iop; int i; ppc32_jit_start_hreg_seq(cpu,"mfcr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_require_flags(cpu,JIT_OP_PPC_ALL_FLAGS); iop = ppc32_op_emit_insn_output(cpu,3,"mfcr"); amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_rd,hreg_rd); for(i=0;i<8;i++) { /* load field in %edx */ amd64_mov_reg_membase(iop->ob_ptr,hreg_t0, AMD64_R15,PPC32_CR_FIELD_OFFSET(i),4); amd64_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_rd,4); amd64_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_rd,hreg_t0); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFMSR - Move from Machine State Register */ DECLARE_INSN(MFMSR) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mfmsr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mfmsr"); amd64_mov_reg_membase(iop->ob_ptr,hreg_rd, AMD64_R15,OFFSET(cpu_ppc_t,msr),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFSR - Move From Segment Register */ DECLARE_INSN(MFSR) { int rd = bits(insn,21,25); int sr = bits(insn,16,19); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mfsr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mfsr"); amd64_mov_reg_membase(iop->ob_ptr,hreg_rd, AMD64_R15,(OFFSET(cpu_ppc_t,sr) + (sr << 2)),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MTCRF - Move to Condition Register Fields */ DECLARE_INSN(MTCRF) { int rs = bits(insn,21,25); int crm = bits(insn,12,19); int hreg_rs,hreg_t0; jit_op_t *iop; int i; ppc32_jit_start_hreg_seq(cpu,"mtcrf"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,4,"mtcrf"); for(i=0;i<8;i++) if (crm & (1 << (7 - i))) { amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); if (i != 7) amd64_shift_reg_imm(iop->ob_ptr,X86_SHR,hreg_t0,28 - (i << 2)); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x0F); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,PPC32_CR_FIELD_OFFSET(i), hreg_t0,4); } ppc32_op_emit_basic_opcode(cpu,JIT_OP_TRASH_FLAGS); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULHW - Multiply High Word */ DECLARE_INSN(MULHW) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mulhw"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RAX); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,AMD64_RAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); /* rd = hi(ra * rb) */ iop = ppc32_op_emit_insn_output(cpu,2,"mulhw"); amd64_mul_reg_size(iop->ob_ptr,hreg_rb,1,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,AMD64_RDX,AMD64_RDX,4); ppc32_op_emit_store_gpr(cpu,rd,AMD64_RDX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RAX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULHWU - Multiply High Word Unsigned */ DECLARE_INSN(MULHWU) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mulhwu"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RAX); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,AMD64_RAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); /* rd = hi(ra * rb) */ iop = ppc32_op_emit_insn_output(cpu,2,"mulhwu"); amd64_mul_reg_size(iop->ob_ptr,hreg_rb,0,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,AMD64_RDX,AMD64_RDX,4); ppc32_op_emit_store_gpr(cpu,rd,AMD64_RDX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RAX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULLI - Multiply Low Immediate */ DECLARE_INSN(MULLI) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); int hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mulli"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RAX); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,AMD64_RAX,ra); /* rd = lo(ra * imm) */ iop = ppc32_op_emit_insn_output(cpu,2,"mulli"); ppc32_load_imm(&iop->ob_ptr,hreg_t0,sign_extend_32(imm,16)); amd64_mul_reg_size(iop->ob_ptr,hreg_t0,1,4); ppc32_op_emit_store_gpr(cpu,rd,AMD64_RAX); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RAX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULLW - Multiply Low Word */ DECLARE_INSN(MULLW) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mullw"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RAX); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,AMD64_RAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); /* rd = lo(ra * rb) */ iop = ppc32_op_emit_insn_output(cpu,2,"mullw"); amd64_mul_reg_size(iop->ob_ptr,hreg_rb,1,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,AMD64_RAX,AMD64_RAX,4); ppc32_op_emit_store_gpr(cpu,rd,AMD64_RAX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RAX); ppc32_op_emit_alter_host_reg(cpu,AMD64_RDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* NAND */ DECLARE_INSN(NAND) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = ~($rs & $rb) */ ppc32_jit_start_hreg_seq(cpu,"nand"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"nand"); if (ra == rs) amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb,4); else if (ra == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_ra,hreg_rs,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb,4); } amd64_not_reg(iop->ob_ptr,hreg_ra); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* NEG */ DECLARE_INSN(NEG) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = neg($ra) */ ppc32_jit_start_hreg_seq(cpu,"neg"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"neg"); if (rd != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); amd64_neg_reg(iop->ob_ptr,hreg_rd); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_rd,hreg_rd,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* NOR */ DECLARE_INSN(NOR) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = ~($rs | $rb) */ ppc32_jit_start_hreg_seq(cpu,"nor"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"nor"); if (ra == rs) amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb,4); else if (ra == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_rs,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb,4); } amd64_not_reg(iop->ob_ptr,hreg_ra); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR */ DECLARE_INSN(OR) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = $rs | $rb */ ppc32_jit_start_hreg_seq(cpu,"or"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); /* special optimization for move/nop operation */ if (rs == rb) { ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"or"); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"or"); if (ra == rs) { amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb,4); } else if (ra == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_rs,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb,4); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR with Complement */ DECLARE_INSN(ORC) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $ra = $rs & ~$rb */ ppc32_jit_start_hreg_seq(cpu,"orc"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"orc"); /* $t0 = ~$rb */ hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rb,4); amd64_not_reg(iop->ob_ptr,hreg_t0); /* $ra = $rs | $t0 */ if (ra == rs) amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_t0,4); else { amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_t0,hreg_rs,4); amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR Immediate */ DECLARE_INSN(ORI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs | imm */ ppc32_jit_start_hreg_seq(cpu,"ori"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"ori"); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_OR,hreg_ra,tmp,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR Immediate Shifted */ DECLARE_INSN(ORIS) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs | imm */ ppc32_jit_start_hreg_seq(cpu,"oris"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"oris"); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_imm_size(iop->ob_ptr,X86_OR,hreg_ra,tmp,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* RLWIMI - Rotate Left Word Immediate then Mask Insert */ DECLARE_INSN(RLWIMI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t mask; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"rlwimi"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); mask = ppc32_rotate_mask(mb,me); iop = ppc32_op_emit_insn_output(cpu,2,"rlwimi"); /* Apply inverse mask to $ra */ if (mask != 0) amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_ra,~mask); /* Rotate $rs of "sh" bits and apply the mask */ amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); if (sh != 0) amd64_shift_reg_imm_size(iop->ob_ptr,X86_ROL,hreg_t0,sh,4); if (mask != 0xFFFFFFFF) amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,mask); /* Store the result */ amd64_alu_reg_reg_size(iop->ob_ptr,X86_OR,hreg_ra,hreg_t0,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* RLWINM - Rotate Left Word Immediate AND with Mask */ DECLARE_INSN(RLWINM) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t mask; int hreg_rs,hreg_ra; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"rlwinm"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"rlwinm"); /* Rotate $rs of "sh" bits and apply the mask */ mask = ppc32_rotate_mask(mb,me); if (rs != ra) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); if (sh != 0) amd64_shift_reg_imm_size(iop->ob_ptr,X86_ROL,hreg_ra,sh,4); if (mask != 0xFFFFFFFF) amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_ra,mask); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* RLWNM - Rotate Left Word then Mask Insert */ DECLARE_INSN(RLWNM) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t mask; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; /* ecx is directly modified: throw it */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RCX); ppc32_jit_start_hreg_seq(cpu,"rlwnm"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RCX); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,AMD64_RCX,rb); iop = ppc32_op_emit_insn_output(cpu,2,"rlwnm"); /* Load the shift register ("sh") */ mask = ppc32_rotate_mask(mb,me); /* Rotate $rs and apply the mask */ amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); amd64_shift_reg_size(iop->ob_ptr,X86_ROL,hreg_t0,4); if (mask != 0xFFFFFFFF) amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,mask); amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* Shift Left Word */ DECLARE_INSN(SLW) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra; jit_op_t *iop; /* ecx is directly modified: throw it */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RCX); ppc32_jit_start_hreg_seq(cpu,"slw"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RCX); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); /* $ra = $rs << $rb. If count >= 32, then null result */ ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,AMD64_RCX,rb); iop = ppc32_op_emit_insn_output(cpu,3,"slw"); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,AMD64_RCX,0x3f); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_shift_reg(iop->ob_ptr,X86_SHL,hreg_ra); /* store the result */ if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SRAWI - Shift Right Algebraic Word Immediate */ DECLARE_INSN(SRAWI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); register m_uint32_t mask; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"srawi"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); /* $ra = (int32)$rs >> sh */ ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,3,"srawi"); amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_shift_reg_imm_size(iop->ob_ptr,X86_SAR,hreg_ra,sh,4); /* set XER_CA depending on the result */ mask = ~(0xFFFFFFFFU << sh) | 0x80000000; amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,mask); amd64_alu_reg_imm_size(iop->ob_ptr,X86_CMP,hreg_t0,0x80000000,4); amd64_set_reg(iop->ob_ptr,X86_CC_A,hreg_t0,FALSE); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x1); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t0,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* Shift Right Word */ DECLARE_INSN(SRW) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra; jit_op_t *iop; /* ecx is directly modified: throw it */ ppc32_op_emit_alter_host_reg(cpu,AMD64_RCX); ppc32_jit_start_hreg_seq(cpu,"srw"); ppc32_jit_alloc_hreg_forced(cpu,AMD64_RCX); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); /* $ra = $rs >> $rb. If count >= 32, then null result */ ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,AMD64_RCX,rb); iop = ppc32_op_emit_insn_output(cpu,3,"srw"); amd64_alu_reg_imm(iop->ob_ptr,X86_AND,AMD64_RCX,0x3f); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_shift_reg(iop->ob_ptr,X86_SHR,hreg_ra); /* store the result */ if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_ra,hreg_ra,4); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* STB - Store Byte */ DECLARE_INSN(STB) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(b,PPC_MEMOP_STB,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,1,PPC_MEMOP_STB,ra,offset,rs, ppc32_memop_fast_stb); return(0); } /* STBU - Store Byte with Update */ DECLARE_INSN(STBU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STB,ra,offset,rs,1); return(0); } /* STBUX - Store Byte with Update Indexed */ DECLARE_INSN(STBUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STB,ra,rb,rs,1); return(0); } /* STBUX - Store Byte Indexed */ DECLARE_INSN(STBX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STB,ra,rb,rs,0); return(0); } /* STH - Store Half-Word */ DECLARE_INSN(STH) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STH,ra,offset,rs,0); return(0); } /* STHU - Store Half-Word with Update */ DECLARE_INSN(STHU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STH,ra,offset,rs,1); return(0); } /* STHUX - Store Half-Word with Update Indexed */ DECLARE_INSN(STHUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STH,ra,rb,rs,1); return(0); } /* STHUX - Store Half-Word Indexed */ DECLARE_INSN(STHX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STH,ra,rb,rs,0); return(0); } /* STW - Store Word */ DECLARE_INSN(STW) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(b,PPC_MEMOP_STW,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,1,PPC_MEMOP_STW,ra,offset,rs, ppc32_memop_fast_stw); return(0); } /* STWU - Store Word with Update */ DECLARE_INSN(STWU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STW,ra,offset,rs,1); return(0); } /* STWUX - Store Word with Update Indexed */ DECLARE_INSN(STWUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STW,ra,rb,rs,1); return(0); } /* STWUX - Store Word Indexed */ DECLARE_INSN(STWX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STW,ra,rb,rs,0); return(0); } /* SUBF - Subtract From */ DECLARE_INSN(SUBF) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rd,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $rd = $rb - $ra */ ppc32_jit_start_hreg_seq(cpu,"subf"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"subf"); if (rd == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_SUB,hreg_rd,hreg_ra,4); else if (rd == ra) { amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rb,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_SUB,hreg_t0,hreg_ra,4); amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); } else { amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_rb,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_SUB,hreg_rd,hreg_ra,4); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SUBFC - Subtract From Carrying */ DECLARE_INSN(SUBFC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; /* $rd = ~$ra + 1 + $rb */ ppc32_jit_start_hreg_seq(cpu,"subfc"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,3,"subfc"); amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); /* $t0 = ~$ra + 1 */ amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); amd64_not_reg(iop->ob_ptr,hreg_t0); amd64_alu_reg_imm_size(iop->ob_ptr,X86_ADD,hreg_t0,1,4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); /* $t0 += $rb */ amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_t0,hreg_rb,4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_rd,hreg_rd,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); /* update cr0 */ if (insn & 1) ppc32_update_cr0(b); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SUBFE - Subtract From Extended */ DECLARE_INSN(SUBFE) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; /* $rd = ~$ra + $carry (xer_ca) + $rb */ ppc32_jit_start_hreg_seq(cpu,"subfe"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,3,"subfe"); amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); /* $t0 = ~$ra + $carry */ amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); amd64_not_reg(iop->ob_ptr,hreg_t0); amd64_alu_reg_membase_size(iop->ob_ptr,X86_ADD,hreg_t0, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca),4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); /* $t0 += $rb */ amd64_alu_reg_reg_size(iop->ob_ptr,X86_ADD,hreg_t0,hreg_rb,4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); if (insn & 1) amd64_test_reg_reg_size(iop->ob_ptr,hreg_rd,hreg_rd,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); /* update cr0 */ if (insn & 1) ppc32_update_cr0(b); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SUBFIC - Subtract From Immediate Carrying */ DECLARE_INSN(SUBFIC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_ra,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; /* $rd = ~$ra + 1 + sign_extend(imm,16) */ ppc32_jit_start_hreg_seq(cpu,"subfic"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,3,"subfic"); amd64_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); /* $t0 = ~$ra + 1 */ amd64_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); amd64_not_reg(iop->ob_ptr,hreg_t0); amd64_alu_reg_imm_size(iop->ob_ptr,X86_ADD,hreg_t0,1,4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_mov_membase_reg(iop->ob_ptr,AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); /* $t0 += sign_extend(imm,16) */ amd64_alu_reg_imm_size(iop->ob_ptr,X86_ADD,hreg_t0,tmp,4); amd64_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); amd64_alu_membase_reg_size(iop->ob_ptr,X86_OR, AMD64_R15,OFFSET(cpu_ppc_t,xer_ca), hreg_t1,4); amd64_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SYNC - Synchronize */ DECLARE_INSN(SYNC) { return(0); } /* XOR */ DECLARE_INSN(XOR) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = $rs ^ $rb */ ppc32_jit_start_hreg_seq(cpu,"xor"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"xor"); if (ra == rs) amd64_alu_reg_reg_size(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb,4); else if (ra == rb) amd64_alu_reg_reg_size(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rs,4); else { amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_reg_size(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb,4); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* XORI - XOR Immediate */ DECLARE_INSN(XORI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs ^ imm */ ppc32_jit_start_hreg_seq(cpu,"xori"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"xori"); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_imm(iop->ob_ptr,X86_XOR,hreg_ra,imm); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* XORIS - XOR Immediate Shifted */ DECLARE_INSN(XORIS) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs ^ (imm << 16) */ ppc32_jit_start_hreg_seq(cpu,"xoris"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"xoris"); if (ra != rs) amd64_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); amd64_alu_reg_imm(iop->ob_ptr,X86_XOR,hreg_ra,tmp); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* PPC instruction array */ struct ppc32_insn_tag ppc32_insn_tags[] = { { ppc32_emit_BLR , 0xfffffffe , 0x4e800020 }, { ppc32_emit_BCTR , 0xfffffffe , 0x4e800420 }, { ppc32_emit_MFLR , 0xfc1fffff , 0x7c0802a6 }, { ppc32_emit_MTLR , 0xfc1fffff , 0x7c0803a6 }, { ppc32_emit_MFCTR , 0xfc1fffff , 0x7c0902a6 }, { ppc32_emit_MTCTR , 0xfc1fffff , 0x7c0903a6 }, { ppc32_emit_MFTBL , 0xfc1ff7ff , 0x7c0c42e6 }, { ppc32_emit_MFTBU , 0xfc1ff7ff , 0x7c0d42e6 }, { ppc32_emit_ADD , 0xfc0007fe , 0x7c000214 }, { ppc32_emit_ADDC , 0xfc0007fe , 0x7c000014 }, { ppc32_emit_ADDE , 0xfc0007fe , 0x7c000114 }, { ppc32_emit_ADDI , 0xfc000000 , 0x38000000 }, { ppc32_emit_ADDIC , 0xfc000000 , 0x30000000 }, { ppc32_emit_ADDIC_dot , 0xfc000000 , 0x34000000 }, { ppc32_emit_ADDIS , 0xfc000000 , 0x3c000000 }, { ppc32_emit_ADDZE , 0xfc00fffe , 0x7c000194 }, { ppc32_emit_AND , 0xfc0007fe , 0x7c000038 }, { ppc32_emit_ANDC , 0xfc0007fe , 0x7c000078 }, { ppc32_emit_ANDI , 0xfc000000 , 0x70000000 }, { ppc32_emit_ANDIS , 0xfc000000 , 0x74000000 }, { ppc32_emit_B , 0xfc000003 , 0x48000000 }, { ppc32_emit_BA , 0xfc000003 , 0x48000002 }, { ppc32_emit_BL , 0xfc000003 , 0x48000001 }, { ppc32_emit_BLA , 0xfc000003 , 0x48000003 }, { ppc32_emit_BCC , 0xfe800000 , 0x40800000 }, { ppc32_emit_BC , 0xfc000000 , 0x40000000 }, { ppc32_emit_BCLR , 0xfc00fffe , 0x4c000020 }, { ppc32_emit_CMP , 0xfc6007ff , 0x7c000000 }, { ppc32_emit_CMPI , 0xfc600000 , 0x2c000000 }, { ppc32_emit_CMPL , 0xfc6007ff , 0x7c000040 }, { ppc32_emit_CMPLI , 0xfc600000 , 0x28000000 }, { ppc32_emit_CRAND , 0xfc0007ff , 0x4c000202 }, { ppc32_emit_CRANDC , 0xfc0007ff , 0x4c000102 }, { ppc32_emit_CREQV , 0xfc0007ff , 0x4c000242 }, { ppc32_emit_CRNAND , 0xfc0007ff , 0x4c0001c2 }, { ppc32_emit_CRNOR , 0xfc0007ff , 0x4c000042 }, { ppc32_emit_CROR , 0xfc0007ff , 0x4c000382 }, { ppc32_emit_CRORC , 0xfc0007ff , 0x4c000342 }, { ppc32_emit_CRXOR , 0xfc0007ff , 0x4c000182 }, { ppc32_emit_DIVWU , 0xfc0007fe , 0x7c000396 }, { ppc32_emit_EQV , 0xfc0007fe , 0x7c000238 }, { ppc32_emit_EXTSB , 0xfc00fffe , 0x7c000774 }, { ppc32_emit_EXTSH , 0xfc00fffe , 0x7c000734 }, { ppc32_emit_LBZ , 0xfc000000 , 0x88000000 }, { ppc32_emit_LBZU , 0xfc000000 , 0x8c000000 }, { ppc32_emit_LBZUX , 0xfc0007ff , 0x7c0000ee }, { ppc32_emit_LBZX , 0xfc0007ff , 0x7c0000ae }, { ppc32_emit_LHA , 0xfc000000 , 0xa8000000 }, { ppc32_emit_LHAU , 0xfc000000 , 0xac000000 }, { ppc32_emit_LHAUX , 0xfc0007ff , 0x7c0002ee }, { ppc32_emit_LHAX , 0xfc0007ff , 0x7c0002ae }, { ppc32_emit_LHZ , 0xfc000000 , 0xa0000000 }, { ppc32_emit_LHZU , 0xfc000000 , 0xa4000000 }, { ppc32_emit_LHZUX , 0xfc0007ff , 0x7c00026e }, { ppc32_emit_LHZX , 0xfc0007ff , 0x7c00022e }, { ppc32_emit_LWZ , 0xfc000000 , 0x80000000 }, { ppc32_emit_LWZU , 0xfc000000 , 0x84000000 }, { ppc32_emit_LWZUX , 0xfc0007ff , 0x7c00006e }, { ppc32_emit_LWZX , 0xfc0007ff , 0x7c00002e }, { ppc32_emit_MCRF , 0xfc63ffff , 0x4c000000 }, { ppc32_emit_MFCR , 0xfc1fffff , 0x7c000026 }, { ppc32_emit_MFMSR , 0xfc1fffff , 0x7c0000a6 }, { ppc32_emit_MFSR , 0xfc10ffff , 0x7c0004a6 }, { ppc32_emit_MTCRF , 0xfc100fff , 0x7c000120 }, { ppc32_emit_MULHW , 0xfc0007fe , 0x7c000096 }, { ppc32_emit_MULHWU , 0xfc0007fe , 0x7c000016 }, { ppc32_emit_MULLI , 0xfc000000 , 0x1c000000 }, { ppc32_emit_MULLW , 0xfc0007fe , 0x7c0001d6 }, { ppc32_emit_NAND , 0xfc0007fe , 0x7c0003b8 }, { ppc32_emit_NEG , 0xfc00fffe , 0x7c0000d0 }, { ppc32_emit_NOR , 0xfc0007fe , 0x7c0000f8 }, { ppc32_emit_OR , 0xfc0007fe , 0x7c000378 }, { ppc32_emit_ORC , 0xfc0007fe , 0x7c000338 }, { ppc32_emit_ORI , 0xfc000000 , 0x60000000 }, { ppc32_emit_ORIS , 0xfc000000 , 0x64000000 }, { ppc32_emit_RLWIMI , 0xfc000000 , 0x50000000 }, { ppc32_emit_RLWINM , 0xfc000000 , 0x54000000 }, { ppc32_emit_RLWNM , 0xfc000000 , 0x5c000000 }, { ppc32_emit_SLW , 0xfc0007fe , 0x7c000030 }, { ppc32_emit_SRAWI , 0xfc0007fe , 0x7c000670 }, { ppc32_emit_SRW , 0xfc0007fe , 0x7c000430 }, { ppc32_emit_STB , 0xfc000000 , 0x98000000 }, { ppc32_emit_STBU , 0xfc000000 , 0x9c000000 }, { ppc32_emit_STBUX , 0xfc0007ff , 0x7c0001ee }, { ppc32_emit_STBX , 0xfc0007ff , 0x7c0001ae }, { ppc32_emit_STH , 0xfc000000 , 0xb0000000 }, { ppc32_emit_STHU , 0xfc000000 , 0xb4000000 }, { ppc32_emit_STHUX , 0xfc0007ff , 0x7c00036e }, { ppc32_emit_STHX , 0xfc0007ff , 0x7c00032e }, { ppc32_emit_STW , 0xfc000000 , 0x90000000 }, { ppc32_emit_STWU , 0xfc000000 , 0x94000000 }, { ppc32_emit_STWUX , 0xfc0007ff , 0x7c00016e }, { ppc32_emit_STWX , 0xfc0007ff , 0x7c00012e }, { ppc32_emit_SUBF , 0xfc0007fe , 0x7c000050 }, { ppc32_emit_SUBFC , 0xfc0007fe , 0x7c000010 }, { ppc32_emit_SUBFE , 0xfc0007fe , 0x7c000110 }, { ppc32_emit_SUBFIC , 0xfc000000 , 0x20000000 }, { ppc32_emit_SYNC , 0xffffffff , 0x7c0004ac }, { ppc32_emit_XOR , 0xfc0007fe , 0x7c000278 }, { ppc32_emit_XORI , 0xfc000000 , 0x68000000 }, { ppc32_emit_XORIS , 0xfc000000 , 0x6c000000 }, { ppc32_emit_unknown , 0x00000000 , 0x00000000 }, { NULL , 0x00000000 , 0x00000000 }, }; dynamips-0.2.14/unstable/ppc32_exec.c000066400000000000000000002733121241034141600173420ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * PowerPC (32-bit) step-by-step execution. */ #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "ppc32_exec.h" #include "ppc32_mem.h" #include "memory.h" #include "insn_lookup.h" #include "dynamips.h" /* Forward declaration of instruction array */ static struct ppc32_insn_exec_tag ppc32_exec_tags[]; static insn_lookup_t *ilt = NULL; /* ILT */ static forced_inline void *ppc32_exec_get_insn(int index) { return(&ppc32_exec_tags[index]); } static int ppc32_exec_chk_lo(struct ppc32_insn_exec_tag *tag,int value) { return((value & tag->mask) == (tag->value & 0xFFFF)); } static int ppc32_exec_chk_hi(struct ppc32_insn_exec_tag *tag,int value) { return((value & (tag->mask >> 16)) == (tag->value >> 16)); } /* Destroy instruction lookup table */ static void destroy_ilt(void) { assert(ilt); ilt_destroy(ilt); ilt = NULL; } /* Initialize instruction lookup table */ void ppc32_exec_create_ilt(void) { int i,count; for(i=0,count=0;ppc32_exec_tags[i].exec;i++) count++; ilt = ilt_create("ppc32e",count, (ilt_get_insn_cbk_t)ppc32_exec_get_insn, (ilt_check_cbk_t)ppc32_exec_chk_lo, (ilt_check_cbk_t)ppc32_exec_chk_hi); atexit(destroy_ilt); } /* Dump statistics */ void ppc32_dump_stats(cpu_ppc_t *cpu) { int i; #if NJM_STATS_ENABLE printf("\n"); for(i=0;ppc32_exec_tags[i].exec;i++) printf(" * %-10s : %10llu\n", ppc32_exec_tags[i].name,ppc32_exec_tags[i].count); printf("%llu instructions executed since startup.\n",cpu->insn_exec_count); #else printf("Statistics support is not compiled in.\n"); #endif } /* Execute a memory operation */ static forced_inline void ppc32_exec_memop(cpu_ppc_t *cpu,int memop, m_uint32_t vaddr,u_int dst_reg) { fastcall ppc_memop_fn fn; fn = cpu->mem_op_fn[memop]; fn(cpu,vaddr,dst_reg); } /* Fetch an instruction */ static forced_inline int ppc32_exec_fetch(cpu_ppc_t *cpu,m_uint32_t ia, ppc_insn_t *insn) { m_uint32_t exec_page,offset; exec_page = ia & ~PPC32_MIN_PAGE_IMASK; if (unlikely(exec_page != cpu->njm_exec_page)) { cpu->njm_exec_ptr = cpu->mem_op_ifetch(cpu,exec_page); cpu->njm_exec_page = exec_page; } offset = (ia & PPC32_MIN_PAGE_IMASK) >> 2; *insn = vmtoh32(cpu->njm_exec_ptr[offset]); return(0); } /* Unknown opcode */ static fastcall int ppc32_exec_unknown(cpu_ppc_t *cpu,ppc_insn_t insn) { printf("PPC32: unknown opcode 0x%8.8x at ia = 0x%x\n",insn,cpu->ia); ppc32_dump_regs(cpu->gen); return(0); } /* Execute a single instruction */ static forced_inline int ppc32_exec_single_instruction(cpu_ppc_t *cpu,ppc_insn_t instruction) { register fastcall int (*exec)(cpu_ppc_t *,ppc_insn_t) = NULL; struct ppc32_insn_exec_tag *tag; int index; #if DEBUG_INSN_PERF_CNT cpu->perf_counter++; #endif /* Lookup for instruction */ index = ilt_lookup(ilt,instruction); tag = ppc32_exec_get_insn(index); exec = tag->exec; #if NJM_STATS_ENABLE cpu->insn_exec_count++; ppc32_exec_tags[index].count++; #endif return(exec(cpu,instruction)); } /* Execute a single instruction (external) */ fastcall int ppc32_exec_single_insn_ext(cpu_ppc_t *cpu,ppc_insn_t insn) { int res; res = ppc32_exec_single_instruction(cpu,insn); if (likely(!res)) cpu->ia += sizeof(ppc_insn_t); return(res); } /* Execute a page */ fastcall int ppc32_exec_page(cpu_ppc_t *cpu) { m_uint32_t exec_page,offset; ppc_insn_t insn; int res; exec_page = cpu->ia & PPC32_MIN_PAGE_MASK; cpu->njm_exec_page = exec_page; cpu->njm_exec_ptr = cpu->mem_op_lookup(cpu,exec_page,PPC32_MTS_ICACHE); do { offset = (cpu->ia & PPC32_MIN_PAGE_IMASK) >> 2; insn = vmtoh32(cpu->njm_exec_ptr[offset]); res = ppc32_exec_single_instruction(cpu,insn); if (likely(!res)) cpu->ia += sizeof(ppc_insn_t); }while((cpu->ia & PPC32_MIN_PAGE_MASK) == exec_page); return(0); } /* Run PowerPC code in step-by-step mode */ void *ppc32_exec_run_cpu(cpu_gen_t *gen) { cpu_ppc_t *cpu = CPU_PPC32(gen); pthread_t timer_irq_thread; int timer_irq_check = 0; ppc_insn_t insn; int res; if (pthread_create(&timer_irq_thread,NULL, (void *)ppc32_timer_irq_run,cpu)) { fprintf(stderr,"VM '%s': unable to create Timer IRQ thread for CPU%u.\n", cpu->vm->name,gen->id); cpu_stop(gen); return NULL; } gen->cpu_thread_running = TRUE; cpu_exec_loop_set(gen); start_cpu: for(;;) { if (unlikely(gen->state != CPU_STATE_RUNNING)) break; /* Check IRQ */ if (unlikely(cpu->irq_check)) ppc32_trigger_irq(cpu); /* Handle virtual idle loop */ if (unlikely(cpu->ia == cpu->idle_pc)) { if (++gen->idle_count == gen->idle_max) { cpu_idle_loop(gen); gen->idle_count = 0; } } /* Handle the virtual CPU clock */ if (++timer_irq_check == cpu->timer_irq_check_itv) { timer_irq_check = 0; if (cpu->timer_irq_pending && !cpu->irq_disable && (cpu->msr & PPC32_MSR_EE)) { cpu->timer_irq_armed = 0; cpu->timer_irq_pending--; vm_set_irq(cpu->vm,0); //ppc32_trigger_timer_irq(cpu); } } /* Increment the time base */ cpu->tb += 100; /* Fetch and execute the instruction */ ppc32_exec_fetch(cpu,cpu->ia,&insn); res = ppc32_exec_single_instruction(cpu,insn); /* Normal flow ? */ if (likely(!res)) cpu->ia += sizeof(ppc_insn_t); } /* Check regularly if the CPU has been restarted */ while(gen->cpu_thread_running) { gen->seq_state++; switch(gen->state) { case CPU_STATE_RUNNING: gen->state = CPU_STATE_RUNNING; goto start_cpu; case CPU_STATE_HALTED: gen->cpu_thread_running = FALSE; pthread_join(timer_irq_thread,NULL); break; } /* CPU is paused */ usleep(200000); } return NULL; } /* ========================================================================= */ /* Update CR0 */ static forced_inline void ppc32_exec_update_cr0(cpu_ppc_t *cpu,m_uint32_t val) { m_uint32_t res; if (val & 0x80000000) res = 1 << PPC32_CR_LT_BIT; else { if (val > 0) res = 1 << PPC32_CR_GT_BIT; else res = 1 << PPC32_CR_EQ_BIT; } if (cpu->xer & PPC32_XER_SO) res |= 1 << PPC32_CR_SO_BIT; cpu->cr_fields[0] = res; } /* * Update Overflow bit from a sum result (r = a + b) * * (a > 0) && (b > 0) => r > 0, otherwise overflow * (a < 0) && (a < 0) => r < 0, otherwise overflow. */ static forced_inline void ppc32_exec_ov_sum(cpu_ppc_t *cpu,m_uint32_t r, m_uint32_t a,m_uint32_t b) { register m_uint32_t sc; sc = (~(a ^ b) & (a ^ r) & 0x80000000); if (unlikely(sc)) cpu->xer |= PPC32_XER_SO | PPC32_XER_OV; else cpu->xer &= ~PPC32_XER_OV; } /* * Update Overflow bit from a substraction result (r = a - b) * * (a > 0) && (b < 0) => r > 0, otherwise overflow * (a < 0) && (a > 0) => r < 0, otherwise overflow. */ static forced_inline void ppc32_exec_ov_sub(cpu_ppc_t *cpu,m_uint32_t r, m_uint32_t a,m_uint32_t b) { register m_uint32_t sc; sc = ((a ^ b) & (a ^ r) & 0x80000000); if (unlikely(sc)) cpu->xer |= PPC32_XER_SO | PPC32_XER_OV; else cpu->xer &= ~PPC32_XER_OV; } /* * Update CA bit from a sum result (r = a + b) */ static forced_inline void ppc32_exec_ca_sum(cpu_ppc_t *cpu,m_uint32_t r, m_uint32_t a,m_uint32_t b) { cpu->xer_ca = (r < a) ? 1 : 0; } /* * Update CA bit from a substraction result (r = a - b) */ static forced_inline void ppc32_exec_ca_sub(cpu_ppc_t *cpu,m_uint32_t r, m_uint32_t a,m_uint32_t b) { cpu->xer_ca = (b > a) ? 1 : 0; } /* Check condition code */ static forced_inline int ppc32_check_cond(cpu_ppc_t *cpu,m_uint32_t bo, m_uint32_t bi) { u_int ctr_ok = TRUE; u_int cond_ok; u_int cr_bit; if (!(bo & 0x04)) { cpu->ctr--; ctr_ok = (cpu->ctr != 0) ^ ((bo >> 1) & 0x1); } cr_bit = ppc32_read_cr_bit(cpu,bi); cond_ok = (bo >> 4) | ((cr_bit ^ (~bo >> 3)) & 0x1); return(ctr_ok & cond_ok); } /* MFLR - Move From Link Register */ static fastcall int ppc32_exec_MFLR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); cpu->gpr[rd] = cpu->lr; return(0); } /* MTLR - Move To Link Register */ static fastcall int ppc32_exec_MTLR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); cpu->lr = cpu->gpr[rs]; return(0); } /* MFCTR - Move From Counter Register */ static fastcall int ppc32_exec_MFCTR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); cpu->gpr[rd] = cpu->ctr; return(0); } /* MTCTR - Move To Counter Register */ static fastcall int ppc32_exec_MTCTR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); cpu->ctr = cpu->gpr[rs]; return(0); } /* ADD */ static fastcall int ppc32_exec_ADD(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[ra] + cpu->gpr[rb]; return(0); } /* ADD. */ static fastcall int ppc32_exec_ADD_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t tmp; tmp = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[rd] = tmp; return(0); } /* ADDO - Add with Overflow */ static fastcall int ppc32_exec_ADDO(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b; ppc32_exec_ov_sum(cpu,d,a,b); cpu->gpr[rd] = d; return(0); } /* ADDO. */ static fastcall int ppc32_exec_ADDO_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b; ppc32_exec_ov_sum(cpu,d,a,b); ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* ADDC - Add Carrying */ static fastcall int ppc32_exec_ADDC(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b; ppc32_exec_ca_sum(cpu,d,a,b); cpu->gpr[rd] = d; return(0); } /* ADDC. */ static fastcall int ppc32_exec_ADDC_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b; ppc32_exec_ca_sum(cpu,d,a,b); ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* ADDCO - Add Carrying with Overflow */ static fastcall int ppc32_exec_ADDCO(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b; ppc32_exec_ca_sum(cpu,d,a,b); ppc32_exec_ov_sum(cpu,d,a,b); cpu->gpr[rd] = d; return(0); } /* ADDCO. */ static fastcall int ppc32_exec_ADDCO_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b; ppc32_exec_ca_sum(cpu,d,a,b); ppc32_exec_ov_sum(cpu,d,a,b); ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* ADDE - Add Extended */ static fastcall int ppc32_exec_ADDE(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b + carry; if (((b + carry) < b) || (d < a)) cpu->xer_ca = 1; cpu->gpr[rd] = d; return(0); } /* ADDE. */ static fastcall int ppc32_exec_ADDE_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b + carry; if (((b + carry) < b) || (d < a)) cpu->xer_ca = 1; ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* ADDEO - Add Extended with Overflow */ static fastcall int ppc32_exec_ADDEO(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b + carry; if (((b + carry) < b) || (d < a)) cpu->xer_ca = 1; ppc32_exec_ov_sum(cpu,d,a,b); cpu->gpr[rd] = d; return(0); } /* ADDEO. */ static fastcall int ppc32_exec_ADDEO_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = a + b + carry; if (((b + carry) < b) || (d < a)) cpu->xer_ca = 1; ppc32_exec_ov_sum(cpu,d,a,b); ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* ADDI - ADD Immediate */ static fastcall int ppc32_exec_ADDI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); register m_uint32_t tmp; tmp = sign_extend_32(imm,16); if (ra != 0) tmp += cpu->gpr[ra]; cpu->gpr[rd] = tmp; return(0); } /* ADDIC - ADD Immediate with Carry */ static fastcall int ppc32_exec_ADDIC(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); register m_uint32_t a,d; a = cpu->gpr[ra]; d = a + sign_extend_32(imm,16); ppc32_exec_ca_sum(cpu,d,a,0); cpu->gpr[rd] = d; return(0); } /* ADDIC. */ static fastcall int ppc32_exec_ADDIC_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); register m_uint32_t a,d; a = cpu->gpr[ra]; d = a + sign_extend_32(imm,16); ppc32_exec_ca_sum(cpu,d,a,0); ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* ADDIS - ADD Immediate Shifted */ static fastcall int ppc32_exec_ADDIS(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); register m_uint32_t tmp; tmp = imm << 16; if (ra != 0) tmp += cpu->gpr[ra]; cpu->gpr[rd] = tmp; return(0); } /* ADDME - Add to Minus One Extended */ static fastcall int ppc32_exec_ADDME(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t a,b,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; b = 0xFFFFFFFF; d = a + b + carry; if (((b + carry) < b) || (d < a)) cpu->xer_ca = 1; cpu->gpr[rd] = d; return(0); } /* ADDME. */ static fastcall int ppc32_exec_ADDME_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t a,b,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; b = 0xFFFFFFFF; d = a + b + carry; if (((b + carry) < b) || (d < a)) cpu->xer_ca = 1; ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* ADDZE - Add to Zero Extended */ static fastcall int ppc32_exec_ADDZE(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t a,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; d = a + carry; if (d < a) cpu->xer_ca = 1; cpu->gpr[rd] = d; return(0); } /* ADDZE. */ static fastcall int ppc32_exec_ADDZE_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t a,d; m_uint32_t carry; carry = cpu->xer_ca; cpu->xer_ca = 0; a = cpu->gpr[ra]; d = a + carry; if (d < a) cpu->xer_ca = 1; ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* AND */ static fastcall int ppc32_exec_AND(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = cpu->gpr[rs] & cpu->gpr[rb]; return(0); } /* AND. */ static fastcall int ppc32_exec_AND_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t tmp; tmp = cpu->gpr[rs] & cpu->gpr[rb]; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* ANDC - AND with Complement */ static fastcall int ppc32_exec_ANDC(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = cpu->gpr[rs] & (~cpu->gpr[rb]); return(0); } /* ANDC. - AND with Complement */ static fastcall int ppc32_exec_ANDC_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t tmp; tmp = cpu->gpr[rs] & (~cpu->gpr[rb]); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* ANDI. - AND Immediate */ static fastcall int ppc32_exec_ANDI_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); register m_uint32_t tmp; tmp = cpu->gpr[rs] & imm; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* ANDIS. - AND Immediate Shifted */ static fastcall int ppc32_exec_ANDIS_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); register m_uint32_t tmp; tmp = cpu->gpr[rs] & (imm << 16); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* B - Branch */ static fastcall int ppc32_exec_B(cpu_ppc_t *cpu,ppc_insn_t insn) { m_uint32_t offset = bits(insn,2,25); cpu->ia += sign_extend_32(offset << 2,26); return(1); } /* BA - Branch Absolute */ static fastcall int ppc32_exec_BA(cpu_ppc_t *cpu,ppc_insn_t insn) { m_uint32_t offset = bits(insn,2,25); cpu->ia = sign_extend_32(offset << 2,26); return(1); } /* BL - Branch and Link */ static fastcall int ppc32_exec_BL(cpu_ppc_t *cpu,ppc_insn_t insn) { m_uint32_t offset = bits(insn,2,25); cpu->lr = cpu->ia + 4; cpu->ia += sign_extend_32(offset << 2,26); return(1); } /* BLA - Branch and Link Absolute */ static fastcall int ppc32_exec_BLA(cpu_ppc_t *cpu,ppc_insn_t insn) { m_uint32_t offset = bits(insn,2,25); cpu->lr = cpu->ia + 4; cpu->ia = sign_extend_32(offset << 2,26); return(1); } /* BC - Branch Conditional */ static fastcall int ppc32_exec_BC(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia += sign_extend_32(bd << 2,16); return(1); } return(0); } /* BCA - Branch Conditional (absolute) */ static fastcall int ppc32_exec_BCA(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia = sign_extend_32(bd << 2,16); return(1); } return(0); } /* BCL - Branch Conditional and Link */ static fastcall int ppc32_exec_BCL(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); cpu->lr = cpu->ia + 4; if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia += sign_extend_32(bd << 2,16); return(1); } return(0); } /* BCLA - Branch Conditional and Link (absolute) */ static fastcall int ppc32_exec_BCLA(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); cpu->lr = cpu->ia + 4; if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia = sign_extend_32(bd << 2,16); return(1); } return(0); } /* BCLR - Branch Conditional to Link register */ static fastcall int ppc32_exec_BCLR(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia = cpu->lr & ~0x3; return(1); } return(0); } /* BCLRL - Branch Conditional to Link register */ static fastcall int ppc32_exec_BCLRL(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); m_uint32_t new_ia; new_ia = cpu->lr & ~0x03; cpu->lr = cpu->ia + 4; if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia = new_ia; return(1); } return(0); } /* BCCTR - Branch Conditional to Count register */ static fastcall int ppc32_exec_BCCTR(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia = cpu->ctr & ~0x3; return(1); } return(0); } /* BCCTRL - Branch Conditional to Count register and Link */ static fastcall int ppc32_exec_BCCTRL(cpu_ppc_t *cpu,ppc_insn_t insn) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); cpu->lr = cpu->ia + 4; if (ppc32_check_cond(cpu,bo,bi)) { cpu->ia = cpu->ctr & ~0x3; return(1); } return(0); } /* CMP - Compare */ static fastcall int ppc32_exec_CMP(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t res; m_int32_t a,b; a = (m_int32_t)cpu->gpr[ra]; b = (m_int32_t)cpu->gpr[rb]; if (a < b) res = 0x08; else { if (a > b) res = 0x04; else res = 0x02; } if (cpu->xer & PPC32_XER_SO) res |= 0x01; cpu->cr_fields[rd] = res; return(0); } /* CMPI - Compare Immediate */ static fastcall int ppc32_exec_CMPI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t res; m_int32_t a,b; a = (m_int32_t)cpu->gpr[ra]; b = sign_extend_32(imm,16); if (a < b) res = 0x08; else { if (a > b) res = 0x04; else res = 0x02; } if (cpu->xer & PPC32_XER_SO) res |= 0x01; cpu->cr_fields[rd] = res; return(0); } /* CMPL - Compare Logical */ static fastcall int ppc32_exec_CMPL(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t res,a,b; a = cpu->gpr[ra]; b = cpu->gpr[rb]; if (a < b) res = 0x08; else { if (a > b) res = 0x04; else res = 0x02; } if (cpu->xer & PPC32_XER_SO) res |= 0x01; cpu->cr_fields[rd] = res; return(0); } /* CMPLI - Compare Logical Immediate */ static fastcall int ppc32_exec_CMPLI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); m_uint32_t res,a; a = cpu->gpr[ra]; if (a < imm) res = 0x08; else { if (a > imm) res = 0x04; else res = 0x02; } if (cpu->xer & PPC32_XER_SO) res |= 0x01; cpu->cr_fields[rd] = res; return(0); } /* CNTLZW - Count Leading Zeros Word */ static fastcall int ppc32_exec_CNTLZW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t val,mask; int i; val = cpu->gpr[rs]; mask = 0x80000000; for(i=0;i<32;i++) { if (val & mask) break; mask >>= 1; } cpu->gpr[ra] = i; return(0); } /* CRAND - Condition Register AND */ static fastcall int ppc32_exec_CRAND(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp &= ppc32_read_cr_bit(cpu,bb); if (tmp & 0x1) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* CREQV - Condition Register Equivalent */ static fastcall int ppc32_exec_CREQV(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp ^= ppc32_read_cr_bit(cpu,bb); if (!(tmp & 0x1)) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* CRANDC - Condition Register AND with Complement */ static fastcall int ppc32_exec_CRANDC(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp &= ~ppc32_read_cr_bit(cpu,bb); if (tmp & 0x1) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* CRNAND - Condition Register NAND */ static fastcall int ppc32_exec_CRNAND(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp &= ppc32_read_cr_bit(cpu,bb); if (!(tmp & 0x1)) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* CRNOR - Condition Register NOR */ static fastcall int ppc32_exec_CRNOR(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp |= ppc32_read_cr_bit(cpu,bb); if (!(tmp & 0x1)) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* CROR - Condition Register OR */ static fastcall int ppc32_exec_CROR(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp |= ppc32_read_cr_bit(cpu,bb); if (tmp & 0x1) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* CRORC - Condition Register OR with complement */ static fastcall int ppc32_exec_CRORC(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp |= ~ppc32_read_cr_bit(cpu,bb); if (tmp & 0x1) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* CRXOR - Condition Register XOR */ static fastcall int ppc32_exec_CRXOR(cpu_ppc_t *cpu,ppc_insn_t insn) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); m_uint32_t tmp; tmp = ppc32_read_cr_bit(cpu,ba); tmp ^= ppc32_read_cr_bit(cpu,bb); if (tmp & 0x1) ppc32_set_cr_bit(cpu,bd); else ppc32_clear_cr_bit(cpu,bd); return(0); } /* DCBF - Data Cache Block Flush */ static fastcall int ppc32_exec_DCBF(cpu_ppc_t *cpu,ppc_insn_t insn) { int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; //printf("PPC32: DBCF: vaddr=0x%8.8x\n",vaddr); return(0); } /* DCBI - Data Cache Block Invalidate */ static fastcall int ppc32_exec_DCBI(cpu_ppc_t *cpu,ppc_insn_t insn) { int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; //printf("PPC32: DBCI: vaddr=0x%8.8x\n",vaddr); return(0); } /* DCBT - Data Cache Block Touch */ static fastcall int ppc32_exec_DCBT(cpu_ppc_t *cpu,ppc_insn_t insn) { int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; //printf("PPC32: DBCT: vaddr=0x%8.8x\n",vaddr); return(0); } /* DCBST - Data Cache Block Store */ static fastcall int ppc32_exec_DCBST(cpu_ppc_t *cpu,ppc_insn_t insn) { int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; //printf("PPC32: DBCST: vaddr=0x%8.8x\n",vaddr); return(0); } /* DIVW - Divide Word */ static fastcall int ppc32_exec_DIVW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_int32_t a,b; a = (m_int32_t)cpu->gpr[ra]; b = (m_int32_t)cpu->gpr[rb]; if (!((b == 0) || ((cpu->gpr[ra] == 0x80000000) && (b == -1)))) cpu->gpr[rd] = a / b; return(0); } /* DIVW. - Divide Word */ static fastcall int ppc32_exec_DIVW_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_int32_t a,b,d; a = (m_int32_t)cpu->gpr[ra]; b = (m_int32_t)cpu->gpr[rb]; d = 0; if (!((b == 0) || ((cpu->gpr[ra] == 0x80000000) && (b == -1)))) d = a / b; ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* DIVWU - Divide Word Unsigned */ static fastcall int ppc32_exec_DIVWU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b; a = cpu->gpr[ra]; b = cpu->gpr[rb]; if (b != 0) cpu->gpr[rd] = a / b; return(0); } /* DIVWU. - Divide Word Unsigned */ static fastcall int ppc32_exec_DIVWU_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,d; a = cpu->gpr[ra]; b = cpu->gpr[rb]; d = 0; if (b != 0) d = a / b; ppc32_exec_update_cr0(cpu,d); cpu->gpr[rd] = d; return(0); } /* EIEIO - Enforce In-order Execution of I/O */ static fastcall int ppc32_exec_EIEIO(cpu_ppc_t *cpu,ppc_insn_t insn) { return(0); } /* EQV */ static fastcall int ppc32_exec_EQV(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = ~(cpu->gpr[rs] ^ cpu->gpr[rb]); return(0); } /* EXTSB - Extend Sign Byte */ static fastcall int ppc32_exec_EXTSB(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); cpu->gpr[ra] = sign_extend_32(cpu->gpr[rs],8); return(0); } /* EXTSB. */ static fastcall int ppc32_exec_EXTSB_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t tmp; tmp = sign_extend_32(cpu->gpr[rs],8); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* EXTSH - Extend Sign Word */ static fastcall int ppc32_exec_EXTSH(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); cpu->gpr[ra] = sign_extend_32(cpu->gpr[rs],16); return(0); } /* EXTSH. */ static fastcall int ppc32_exec_EXTSH_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t tmp; tmp = sign_extend_32(cpu->gpr[rs],16); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* ICBI - Instruction Cache Block Invalidate */ static fastcall int ppc32_exec_ICBI(cpu_ppc_t *cpu,ppc_insn_t insn) { int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_ICBI,vaddr,0); return(0); } /* ISYNC - Instruction Synchronize */ static fastcall int ppc32_exec_ISYNC(cpu_ppc_t *cpu,ppc_insn_t insn) { return(0); } /* LBZ - Load Byte and Zero */ static fastcall int ppc32_exec_LBZ(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LBZ,vaddr,rd); return(0); } /* LBZU - Load Byte and Zero with Update */ static fastcall int ppc32_exec_LBZU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_LBZ,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LBZUX - Load Byte and Zero with Update Indexed */ static fastcall int ppc32_exec_LBZUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_LBZ,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LBZX - Load Byte and Zero Indexed */ static fastcall int ppc32_exec_LBZX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LBZ,vaddr,rd); return(0); } /* LHA - Load Half-Word Algebraic */ static fastcall int ppc32_exec_LHA(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LHA,vaddr,rd); return(0); } /* LHAU - Load Half-Word Algebraic with Update */ static fastcall int ppc32_exec_LHAU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_LHA,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LHAUX - Load Half-Word Algebraic with Update Indexed */ static fastcall int ppc32_exec_LHAUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_LHA,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LHAX - Load Half-Word Algebraic ndexed */ static fastcall int ppc32_exec_LHAX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LHA,vaddr,rd); return(0); } /* LHZ - Load Half-Word and Zero */ static fastcall int ppc32_exec_LHZ(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LHZ,vaddr,rd); return(0); } /* LHZU - Load Half-Word and Zero with Update */ static fastcall int ppc32_exec_LHZU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_LHZ,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LHZUX - Load Half-Word and Zero with Update Indexed */ static fastcall int ppc32_exec_LHZUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_LHZ,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LHZX - Load Half-Word and Zero Indexed */ static fastcall int ppc32_exec_LHZX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LHZ,vaddr,rd); return(0); } /* LMW - Load Multiple Word */ static fastcall int ppc32_exec_LMW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; int r; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; for(r=rd;r<=31;r++) { ppc32_exec_memop(cpu,PPC_MEMOP_LWZ,vaddr,r); vaddr += sizeof(m_uint32_t); } return(0); } /* LWBRX - Load Word Byte-Reverse Indexed */ static fastcall int ppc32_exec_LWBRX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LWBR,vaddr,rd); return(0); } /* LWZ - Load Word and Zero */ static fastcall int ppc32_exec_LWZ(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LWZ,vaddr,rd); return(0); } /* LWZU - Load Word and Zero with Update */ static fastcall int ppc32_exec_LWZU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_LWZ,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LWZUX - Load Word and Zero with Update Indexed */ static fastcall int ppc32_exec_LWZUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_LWZ,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LWZX - Load Word and Zero Indexed */ static fastcall int ppc32_exec_LWZX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LWZ,vaddr,rd); return(0); } /* LWARX - Load Word and Reserve Indexed */ static fastcall int ppc32_exec_LWARX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; cpu->reserve = 1; ppc32_exec_memop(cpu,PPC_MEMOP_LWZ,vaddr,rd); return(0); } /* LFD - Load Floating-Point Double */ static fastcall int ppc32_exec_LFD(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LFD,vaddr,rd); return(0); } /* LFDU - Load Floating-Point Double with Update */ static fastcall int ppc32_exec_LFDU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_LFD,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LFDUX - Load Floating-Point Double with Update Indexed */ static fastcall int ppc32_exec_LFDUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_LFD,vaddr,rd); cpu->gpr[ra] = vaddr; return(0); } /* LFDX - Load Floating-Point Double Indexed */ static fastcall int ppc32_exec_LFDX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_LFD,vaddr,rd); return(0); } /* LSWI - Load String Word Immediate */ static fastcall int ppc32_exec_LSWI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int nb = bits(insn,11,15); m_uint32_t vaddr = 0; int r; if (ra != 0) vaddr += cpu->gpr[ra]; if (nb == 0) nb = 32; r = rd - 1; cpu->sw_pos = 0; while(nb > 0) { if (cpu->sw_pos == 0) { r = (r + 1) & 0x1F; cpu->gpr[r] = 0; } ppc32_exec_memop(cpu,PPC_MEMOP_LSW,vaddr,r); cpu->sw_pos += 8; if (cpu->sw_pos == 32) cpu->sw_pos = 0; vaddr++; nb--; } return(0); } /* LSWX - Load String Word Indexed */ static fastcall int ppc32_exec_LSWX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; int r,nb; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; nb = cpu->xer & PPC32_XER_BC_MASK; r = rd - 1; cpu->sw_pos = 0; while(nb > 0) { if (cpu->sw_pos == 0) { r = (r + 1) & 0x1F; cpu->gpr[r] = 0; } ppc32_exec_memop(cpu,PPC_MEMOP_LSW,vaddr,r); cpu->sw_pos += 8; if (cpu->sw_pos == 32) cpu->sw_pos = 0; vaddr++; nb--; } return(0); } /* MCRF - Move Condition Register Field */ static fastcall int ppc32_exec_MCRF(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,23,25); int rs = bits(insn,18,20); cpu->cr_fields[rd] = cpu->cr_fields[rs]; return(0); } /* MFCR - Move from Condition Register */ static fastcall int ppc32_exec_MFCR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); cpu->gpr[rd] = ppc32_get_cr(cpu); return(0); } /* MFMSR - Move from Machine State Register */ static fastcall int ppc32_exec_MFMSR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); cpu->gpr[rd] = cpu->msr; return(0); } /* MFTBU - Move from Time Base (Up) */ static fastcall int ppc32_exec_MFTBU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); cpu->gpr[rd] = cpu->tb >> 32; return(0); } /* MFTBL - Move from Time Base (Lo) */ static fastcall int ppc32_exec_MFTBL(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); cpu->tb += 50; cpu->gpr[rd] = cpu->tb & 0xFFFFFFFF; return(0); } /* MFSPR - Move from Special-Purpose Register */ static fastcall int ppc32_exec_MFSPR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int spr0 = bits(insn,16,20); int spr1 = bits(insn,11,15); u_int spr; spr = (spr1 << 5) | spr0; cpu->gpr[rd] = 0; //cpu_log(cpu->gen,"SPR","reading SPR=%d at cpu->ia=0x%8.8x\n",spr,cpu->ia); if ((spr1 == 0x10) || (spr1 == 0x11)) { cpu->gpr[rd] = ppc32_get_bat_spr(cpu,spr); return(0); } switch(spr) { case PPC32_SPR_XER: cpu->gpr[rd] = cpu->xer | (cpu->xer_ca << PPC32_XER_CA_BIT); break; case PPC32_SPR_DSISR: cpu->gpr[rd] = cpu->dsisr; break; case PPC32_SPR_DAR: cpu->gpr[rd] = cpu->dar; break; case PPC32_SPR_DEC: cpu->gpr[rd] = cpu->dec; break; case PPC32_SPR_SDR1: cpu->gpr[rd] = cpu->sdr1; break; case PPC32_SPR_SRR0: cpu->gpr[rd] = cpu->srr0; break; case PPC32_SPR_SRR1: cpu->gpr[rd] = cpu->srr1; break; case PPC32_SPR_TBL_READ: cpu->gpr[rd] = cpu->tb & 0xFFFFFFFF; break; case PPC32_SPR_TBU_READ: cpu->gpr[rd] = cpu->tb >> 32; break; case PPC32_SPR_SPRG0: cpu->gpr[rd] = cpu->sprg[0]; break; case PPC32_SPR_SPRG1: cpu->gpr[rd] = cpu->sprg[1]; break; case PPC32_SPR_SPRG2: cpu->gpr[rd] = cpu->sprg[2]; break; case PPC32_SPR_SPRG3: cpu->gpr[rd] = cpu->sprg[3]; break; case PPC32_SPR_PVR: cpu->gpr[rd] = cpu->pvr; break; case PPC32_SPR_HID0: cpu->gpr[rd] = cpu->hid0; break; case PPC32_SPR_HID1: cpu->gpr[rd] = cpu->hid1; break; case PPC405_SPR_PID: cpu->gpr[rd] = cpu->ppc405_pid; break; /* MPC860 IMMR */ case 638: cpu->gpr[rd] = cpu->mpc860_immr; break; default: cpu->gpr[rd] = 0x0; //printf("READING SPR = %d\n",spr); } return(0); } /* MFSR - Move From Segment Register */ static fastcall int ppc32_exec_MFSR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int sr = bits(insn,16,19); cpu->gpr[rd] = cpu->sr[sr]; return(0); } /* MFSRIN - Move From Segment Register Indirect */ static fastcall int ppc32_exec_MFSRIN(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int rb = bits(insn,11,15); cpu->gpr[rd] = cpu->sr[cpu->gpr[rb] >> 28]; return(0); } /* MTCRF - Move to Condition Register Fields */ static fastcall int ppc32_exec_MTCRF(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int crm = bits(insn,12,19); int i; for(i=0;i<8;i++) if (crm & (1 << (7 - i))) cpu->cr_fields[i] = (cpu->gpr[rs] >> (28 - (i << 2))) & 0x0F; return(0); } /* MTMSR - Move to Machine State Register */ static fastcall int ppc32_exec_MTMSR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); cpu->msr = cpu->gpr[rs]; cpu->irq_check = (cpu->msr & PPC32_MSR_EE) && cpu->irq_pending; //printf("New MSR = 0x%8.8x at cpu->ia=0x%8.8x\n",cpu->msr,cpu->ia); return(0); } /* MTSPR - Move to Special-Purpose Register */ static fastcall int ppc32_exec_MTSPR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int spr0 = bits(insn,16,20); int spr1 = bits(insn,11,15); u_int spr; spr = (spr1 << 5) | spr0; //cpu_log(cpu->gen,"SPR","writing SPR=%d, val=0x%8.8x at cpu->ia=0x%8.8x\n", // spr,cpu->ia,cpu->gpr[rd]); if ((spr1 == 0x10) || (spr1 == 0x11)) { ppc32_set_bat_spr(cpu,spr,cpu->gpr[rd]); return(0); } switch(spr) { case PPC32_SPR_XER: cpu->xer = cpu->gpr[rd] & ~PPC32_XER_CA; cpu->xer_ca = (cpu->gpr[rd] >> PPC32_XER_CA_BIT) & 0x1; break; case PPC32_SPR_DEC: //printf("WRITING DECR 0x%8.8x AT IA=0x%8.8x\n",cpu->gpr[rd],cpu->ia); cpu->dec = cpu->gpr[rd]; cpu->timer_irq_armed = TRUE; break; case PPC32_SPR_SDR1: ppc32_set_sdr1(cpu,cpu->gpr[rd]); break; case PPC32_SPR_SRR0: cpu->srr0 = cpu->gpr[rd]; break; case PPC32_SPR_SRR1: cpu->srr1 = cpu->gpr[rd]; break; case PPC32_SPR_SPRG0: cpu->sprg[0] = cpu->gpr[rd]; break; case PPC32_SPR_SPRG1: cpu->sprg[1] = cpu->gpr[rd]; break; case PPC32_SPR_SPRG2: cpu->sprg[2] = cpu->gpr[rd]; break; case PPC32_SPR_SPRG3: cpu->sprg[3] = cpu->gpr[rd]; break; case PPC32_SPR_HID0: cpu->hid0 = cpu->gpr[rd]; break; case PPC32_SPR_HID1: cpu->hid1 = cpu->gpr[rd]; break; case PPC405_SPR_PID: cpu->ppc405_pid = cpu->gpr[rd]; break; #if 0 default: printf("WRITING SPR=%d, data=0x%8.8x\n",spr,cpu->gpr[rd]); #endif } return(0); } /* MTSR - Move To Segment Register */ static fastcall int ppc32_exec_MTSR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int sr = bits(insn,16,19); cpu->sr[sr] = cpu->gpr[rs]; ppc32_mem_invalidate_cache(cpu); return(0); } /* MULHW - Multiply High Word */ static fastcall int ppc32_exec_MULHW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_int64_t tmp; m_uint32_t res; tmp = (m_int64_t)(m_int32_t)cpu->gpr[ra]; tmp *= (m_int64_t)(m_int32_t)cpu->gpr[rb]; res = tmp >> 32; cpu->gpr[rd] = res; return(0); } /* MULHW. */ static fastcall int ppc32_exec_MULHW_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_int64_t tmp; m_uint32_t res; tmp = (m_int64_t)(m_int32_t)cpu->gpr[ra]; tmp *= (m_int64_t)(m_int32_t)cpu->gpr[rb]; res = tmp >> 32; ppc32_exec_update_cr0(cpu,res); cpu->gpr[rd] = res; return(0); } /* MULHWU - Multiply High Word Unsigned */ static fastcall int ppc32_exec_MULHWU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint64_t tmp; m_uint32_t res; tmp = (m_uint64_t)cpu->gpr[ra]; tmp *= (m_uint64_t)cpu->gpr[rb]; res = tmp >> 32; cpu->gpr[rd] = res; return(0); } /* MULHWU. */ static fastcall int ppc32_exec_MULHWU_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint64_t tmp; m_uint32_t res; tmp = (m_uint64_t)cpu->gpr[ra]; tmp *= (m_uint64_t)cpu->gpr[rb]; res = tmp >> 32; ppc32_exec_update_cr0(cpu,res); cpu->gpr[rd] = res; return(0); } /* MULLI - Multiply Low Immediate */ static fastcall int ppc32_exec_MULLI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); cpu->gpr[rd] = (m_int32_t)cpu->gpr[ra] * sign_extend_32(imm,16); return(0); } /* MULLW - Multiply Low Word */ static fastcall int ppc32_exec_MULLW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_int64_t tmp; tmp = (m_int64_t)(m_int32_t)cpu->gpr[ra]; tmp *= (m_int64_t)(m_int32_t)cpu->gpr[rb]; cpu->gpr[rd] = (m_uint32_t)tmp; return(0); } /* MULLW. */ static fastcall int ppc32_exec_MULLW_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t res; m_int64_t tmp; tmp = (m_int64_t)(m_int32_t)cpu->gpr[ra]; tmp *= (m_int64_t)(m_int32_t)cpu->gpr[rb]; res = (m_uint32_t)tmp; ppc32_exec_update_cr0(cpu,res); cpu->gpr[rd] = res; return(0); } /* MULLWO - Multiply Low Word with Overflow */ static fastcall int ppc32_exec_MULLWO(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_int64_t tmp; tmp = (m_int64_t)(m_int32_t)cpu->gpr[ra]; tmp *= (m_int64_t)(m_int32_t)cpu->gpr[rb]; cpu->xer &= ~PPC32_XER_OV; if (unlikely(tmp != (m_int64_t)(m_int32_t)tmp)) cpu->xer |= PPC32_XER_OV|PPC32_XER_SO; cpu->gpr[rd] = (m_uint32_t)tmp; return(0); } /* MULLWO. */ static fastcall int ppc32_exec_MULLWO_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t res; m_int64_t tmp; tmp = (m_int64_t)(m_int32_t)cpu->gpr[ra]; tmp *= (m_int64_t)(m_int32_t)cpu->gpr[rb]; cpu->xer &= ~PPC32_XER_OV; if (unlikely(tmp != (m_int64_t)(m_int32_t)tmp)) cpu->xer |= PPC32_XER_OV|PPC32_XER_SO; res = (m_uint32_t)tmp; ppc32_exec_update_cr0(cpu,res); cpu->gpr[rd] = res; return(0); } /* NAND */ static fastcall int ppc32_exec_NAND(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = ~(cpu->gpr[rs] & cpu->gpr[rb]); return(0); } /* NAND. */ static fastcall int ppc32_exec_NAND_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t tmp; tmp = ~(cpu->gpr[rs] & cpu->gpr[rb]); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* NEG - Negate */ static fastcall int ppc32_exec_NEG(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); cpu->gpr[rd] = ~cpu->gpr[ra] + 1; return(0); } /* NEG. */ static fastcall int ppc32_exec_NEG_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t tmp; tmp = ~cpu->gpr[ra] + 1; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[rd] = tmp; return(0); } /* NEGO */ static fastcall int ppc32_exec_NEGO(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t tmp; tmp = ~cpu->gpr[ra] + 1; cpu->gpr[rd] = tmp; cpu->xer &= ~PPC32_XER_OV; if (unlikely(tmp == 0x80000000)) cpu->xer |= PPC32_XER_OV|PPC32_XER_SO; ppc32_exec_update_cr0(cpu,tmp); return(0); } /* NEGO. */ static fastcall int ppc32_exec_NEGO_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t tmp; tmp = ~cpu->gpr[ra] + 1; cpu->gpr[rd] = tmp; cpu->xer &= ~PPC32_XER_OV; if (unlikely(tmp == 0x80000000)) cpu->xer |= PPC32_XER_OV|PPC32_XER_SO; ppc32_exec_update_cr0(cpu,tmp); return(0); } /* NOR */ static fastcall int ppc32_exec_NOR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = ~(cpu->gpr[rs] | cpu->gpr[rb]); return(0); } /* NOR. */ static fastcall int ppc32_exec_NOR_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t tmp; tmp = ~(cpu->gpr[rs] | cpu->gpr[rb]); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* OR */ static fastcall int ppc32_exec_OR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = cpu->gpr[rs] | cpu->gpr[rb]; return(0); } /* OR. */ static fastcall int ppc32_exec_OR_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t tmp; tmp = cpu->gpr[rs] | cpu->gpr[rb]; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* ORC - OR with Complement */ static fastcall int ppc32_exec_ORC(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = cpu->gpr[rs] | ~cpu->gpr[rb]; return(0); } /* ORC. */ static fastcall int ppc32_exec_ORC_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t tmp; tmp = cpu->gpr[rs] | ~cpu->gpr[rb]; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* ORI - OR Immediate */ static fastcall int ppc32_exec_ORI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); cpu->gpr[ra] = cpu->gpr[rs] | imm; return(0); } /* ORIS - OR Immediate Shifted */ static fastcall int ppc32_exec_ORIS(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); cpu->gpr[ra] = cpu->gpr[rs] | (imm << 16); return(0); } /* RFI - Return From Interrupt */ static fastcall int ppc32_exec_RFI(cpu_ppc_t *cpu,ppc_insn_t insn) { //printf("RFI: srr0=0x%8.8x, srr1=0x%8.8x\n",cpu->srr0,cpu->srr1); cpu->msr &= ~PPC32_RFI_MSR_MASK; cpu->msr |= cpu->srr1 & PPC32_RFI_MSR_MASK; cpu->msr &= ~(1 << 13); cpu->ia = cpu->srr0 & ~0x03; cpu->irq_check = (cpu->msr & PPC32_MSR_EE) && cpu->irq_pending; //printf("NEW IA=0x%8.8x, NEW MSR=0x%8.8x\n",cpu->ia,cpu->msr); return(1); } /* RLWIMI - Rotate Left Word Immediate then Mask Insert */ static fastcall int ppc32_exec_RLWIMI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t r,mask; r = (cpu->gpr[rs] << sh) | (cpu->gpr[rs] >> (32 - sh)); mask = ppc32_rotate_mask(mb,me); cpu->gpr[ra] = (r & mask) | (cpu->gpr[ra] & ~mask); return(0); } /* RLWIMI. - Rotate Left Word Immediate then Mask Insert */ static fastcall int ppc32_exec_RLWIMI_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t r,mask,tmp; r = (cpu->gpr[rs] << sh) | (cpu->gpr[rs] >> (32 - sh)); mask = ppc32_rotate_mask(mb,me); tmp = (r & mask) | (cpu->gpr[ra] & ~mask); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* RLWINM - Rotate Left Word Immediate AND with Mask */ static fastcall int ppc32_exec_RLWINM(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t r,mask; r = (cpu->gpr[rs] << sh) | (cpu->gpr[rs] >> (32 - sh)); mask = ppc32_rotate_mask(mb,me); cpu->gpr[ra] = r & mask; return(0); } /* RLWINM. - Rotate Left Word Immediate AND with Mask */ static fastcall int ppc32_exec_RLWINM_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t r,mask,tmp; r = (cpu->gpr[rs] << sh) | (cpu->gpr[rs] >> (32 - sh)); mask = ppc32_rotate_mask(mb,me); tmp = r & mask; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* RLWNM - Rotate Left Word then Mask Insert */ static fastcall int ppc32_exec_RLWNM(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t r,sh,mask; sh = cpu->gpr[rb] & 0x1f; r = (cpu->gpr[rs] << sh) | (cpu->gpr[rs] >> (32 - sh)); mask = ppc32_rotate_mask(mb,me); cpu->gpr[ra] = r & mask; return(0); } /* RLWNM. - Rotate Left Word then Mask Insert */ static fastcall int ppc32_exec_RLWNM_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t r,sh,mask,tmp; sh = cpu->gpr[rb] & 0x1f; r = (cpu->gpr[rs] << sh) | (cpu->gpr[rs] >> (32 - sh)); mask = ppc32_rotate_mask(mb,me); tmp = r & mask; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* SC - System Call */ static fastcall int ppc32_exec_SC(cpu_ppc_t *cpu,ppc_insn_t insn) { ppc32_trigger_exception(cpu,PPC32_EXC_SYSCALL); return(1); } /* SLW - Shift Left Word */ static fastcall int ppc32_exec_SLW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t s; s = cpu->gpr[rb] & 0x3f; if (likely(!(s & 0x20))) cpu->gpr[ra] = cpu->gpr[rs] << s; else cpu->gpr[ra] = 0; return(0); } /* SLW. */ static fastcall int ppc32_exec_SLW_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t s,tmp; s = cpu->gpr[rb] & 0x3f; if (likely(!(s & 0x20))) tmp = cpu->gpr[rs] << s; else tmp = 0; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* SRAW - Shift Right Algebraic Word */ static fastcall int ppc32_exec_SRAW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t s,mask; int sh; cpu->xer_ca = 0; s = cpu->gpr[rs]; sh = cpu->gpr[rb]; if (unlikely(sh & 0x20)) { cpu->gpr[ra] = (m_int32_t)s >> 31; cpu->xer_ca = cpu->gpr[ra] & 0x1; return(0); } cpu->gpr[ra] = (m_int32_t)s >> sh; mask = ~(0xFFFFFFFFU << sh); if ((s & 0x80000000) && ((s & mask) != 0)) cpu->xer_ca = 1; return(0); } /* SRAWI - Shift Right Algebraic Word Immediate */ static fastcall int ppc32_exec_SRAWI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); register m_uint32_t s,mask; cpu->xer_ca = 0; s = cpu->gpr[rs]; cpu->gpr[ra] = (m_int32_t)s >> sh; mask = ~(0xFFFFFFFFU << sh); if ((s & 0x80000000) && ((s & mask) != 0)) cpu->xer_ca = 1; return(0); } /* SRAWI. */ static fastcall int ppc32_exec_SRAWI_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); register m_uint32_t s,r,mask; cpu->xer_ca = 0; s = cpu->gpr[rs]; r = (m_int32_t)s >> sh; mask = ~(0xFFFFFFFFU << sh); if ((s & 0x80000000) && ((s & mask) != 0)) cpu->xer_ca = 1; ppc32_exec_update_cr0(cpu,r); cpu->gpr[ra] = r; return(0); } /* SRW - Shift Right Word */ static fastcall int ppc32_exec_SRW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t s; s = cpu->gpr[rb] & 0x3f; if (likely(!(s & 0x20))) cpu->gpr[ra] = cpu->gpr[rs] >> s; else cpu->gpr[ra] = 0; return(0); } /* SRW. */ static fastcall int ppc32_exec_SRW_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t s,tmp; s = cpu->gpr[rb] & 0x3f; if (likely(!(s & 0x20))) tmp = cpu->gpr[rs] >> s; else tmp = 0; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* STB - Store Byte */ static fastcall int ppc32_exec_STB(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STB,vaddr,rs); return(0); } /* STBU - Store Byte with Update */ static fastcall int ppc32_exec_STBU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_STB,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STBUX - Store Byte with Update Indexed */ static fastcall int ppc32_exec_STBUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_STB,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STBX - Store Byte Indexed */ static fastcall int ppc32_exec_STBX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STB,vaddr,rs); return(0); } /* STH - Store Half-Word */ static fastcall int ppc32_exec_STH(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STH,vaddr,rs); return(0); } /* STHU - Store Half-Word with Update */ static fastcall int ppc32_exec_STHU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_STH,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STHUX - Store Half-Word with Update Indexed */ static fastcall int ppc32_exec_STHUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_STH,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STHX - Store Half-Word Indexed */ static fastcall int ppc32_exec_STHX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STH,vaddr,rs); return(0); } /* STMW - Store Multiple Word */ static fastcall int ppc32_exec_STMW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; int r; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; for(r=rs;r<=31;r++) { ppc32_exec_memop(cpu,PPC_MEMOP_STW,vaddr,r); vaddr += sizeof(m_uint32_t); } return(0); } /* STW - Store Word */ static fastcall int ppc32_exec_STW(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STW,vaddr,rs); return(0); } /* STWU - Store Word with Update */ static fastcall int ppc32_exec_STWU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_STW,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STWUX - Store Word with Update Indexed */ static fastcall int ppc32_exec_STWUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_STW,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STWX - Store Word Indexed */ static fastcall int ppc32_exec_STWX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STW,vaddr,rs); return(0); } /* STWBRX - Store Word Byte-Reverse Indexed */ static fastcall int ppc32_exec_STWBRX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STWBR,vaddr,rs); return(0); } /* STWCX. - Store Word Conditional Indexed */ static fastcall int ppc32_exec_STWCX_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; if (cpu->reserve) { ppc32_exec_memop(cpu,PPC_MEMOP_STW,vaddr,rs); cpu->cr_fields[0] = 1 << PPC32_CR_EQ_BIT; if (cpu->xer & PPC32_XER_SO) cpu->cr_fields[0] |= 1 << PPC32_CR_SO_BIT; cpu->reserve = 0; } else { cpu->cr_fields[0] = 0; if (cpu->xer & PPC32_XER_SO) cpu->cr_fields[0] |= 1 << PPC32_CR_SO_BIT; } return(0); } /* STFD - Store Floating-Point Double */ static fastcall int ppc32_exec_STFD(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = sign_extend_32(imm,16); if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STFD,vaddr,rs); return(0); } /* STFDU - Store Floating-Point Double with Update */ static fastcall int ppc32_exec_STFDU(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + sign_extend_32(imm,16); ppc32_exec_memop(cpu,PPC_MEMOP_STFD,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STFDUX - Store Floating-Point Double with Update Indexed */ static fastcall int ppc32_exec_STFDUX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[ra] + cpu->gpr[rb]; ppc32_exec_memop(cpu,PPC_MEMOP_STFD,vaddr,rs); cpu->gpr[ra] = vaddr; return(0); } /* STFDX - Store Floating-Point Double Indexed */ static fastcall int ppc32_exec_STFDX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; ppc32_exec_memop(cpu,PPC_MEMOP_STFD,vaddr,rs); return(0); } /* STSWI - Store String Word Immediate */ static fastcall int ppc32_exec_STSWI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int nb = bits(insn,11,15); m_uint32_t vaddr = 0; int r; if (ra != 0) vaddr += cpu->gpr[ra]; if (nb == 0) nb = 32; r = rs - 1; cpu->sw_pos = 0; while(nb > 0) { if (cpu->sw_pos == 0) r = (r + 1) & 0x1F; ppc32_exec_memop(cpu,PPC_MEMOP_STSW,vaddr,r); cpu->sw_pos += 8; if (cpu->sw_pos == 32) cpu->sw_pos = 0; vaddr++; nb--; } return(0); } /* STSWX - Store String Word Indexed */ static fastcall int ppc32_exec_STSWX(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; int r,nb; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; nb = cpu->xer & PPC32_XER_BC_MASK; r = rs - 1; cpu->sw_pos = 0; while(nb > 0) { if (cpu->sw_pos == 0) r = (r + 1) & 0x1F; ppc32_exec_memop(cpu,PPC_MEMOP_STSW,vaddr,r); cpu->sw_pos += 8; if (cpu->sw_pos == 32) cpu->sw_pos = 0; vaddr++; nb--; } return(0); } /* SUBF - Subtract From */ static fastcall int ppc32_exec_SUBF(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[rd] = cpu->gpr[rb] - cpu->gpr[ra]; return(0); } /* SUBF. */ static fastcall int ppc32_exec_SUBF_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t tmp; tmp = cpu->gpr[rb] - cpu->gpr[ra]; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[rd] = tmp; return(0); } /* SUBFO - Subtract From with Overflow */ static fastcall int ppc32_exec_SUBFO(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,tmp; a = cpu->gpr[ra]; b = cpu->gpr[rb]; tmp = b - a; ppc32_exec_ov_sub(cpu,tmp,b,a); cpu->gpr[rd] = tmp; return(0); } /* SUBFO. */ static fastcall int ppc32_exec_SUBFO_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,tmp; a = cpu->gpr[ra]; b = cpu->gpr[rb]; tmp = b - a; ppc32_exec_ov_sub(cpu,tmp,b,a); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[rd] = tmp; return(0); } /* SUBFC - Subtract From Carrying */ static fastcall int ppc32_exec_SUBFC(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,r,tmp; a = ~cpu->gpr[ra]; b = cpu->gpr[rb]; tmp = a + 1; r = b + tmp; ppc32_exec_ca_sum(cpu,tmp,a,1); if (r < tmp) cpu->xer_ca = 1; cpu->gpr[rd] = r; return(0); } /* SUBFC. */ static fastcall int ppc32_exec_SUBFC_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,r,tmp; a = ~cpu->gpr[ra]; b = cpu->gpr[rb]; tmp = a + 1; r = b + tmp; ppc32_exec_ca_sum(cpu,tmp,a,1); if (r < tmp) cpu->xer_ca = 1; ppc32_exec_update_cr0(cpu,r); cpu->gpr[rd] = r; return(0); } /* SUBFCO - Subtract From with Overflow */ _unused static fastcall int ppc32_exec_SUBFCO(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,tmp; a = cpu->gpr[ra]; b = cpu->gpr[rb]; tmp = b - a; ppc32_exec_ca_sub(cpu,tmp,b,a); ppc32_exec_ov_sub(cpu,tmp,b,a); cpu->gpr[rd] = tmp; return(0); } /* SUBFCO. */ _unused static fastcall int ppc32_exec_SUBFCO_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,tmp; a = cpu->gpr[ra]; b = cpu->gpr[rb]; tmp = b - a; ppc32_exec_ca_sub(cpu,tmp,b,a); ppc32_exec_ov_sub(cpu,tmp,b,a); ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[rd] = tmp; return(0); } /* SUBFE - Subtract From Carrying */ static fastcall int ppc32_exec_SUBFE(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); register m_uint32_t a,b,r,tmp; m_uint32_t carry; carry = cpu->xer_ca; a = ~cpu->gpr[ra]; b = cpu->gpr[rb]; tmp = a + carry; r = b + tmp; ppc32_exec_ca_sum(cpu,tmp,a,carry); if (r < tmp) cpu->xer_ca = 1; cpu->gpr[rd] = r; return(0); } /* SUBFIC - Subtract From Immediate Carrying */ static fastcall int ppc32_exec_SUBFIC(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); register m_uint32_t a,b,r,tmp; a = ~cpu->gpr[ra]; b = sign_extend_32(imm,16); tmp = a + 1; r = b + tmp; ppc32_exec_ca_sum(cpu,tmp,a,1); if (r < tmp) cpu->xer_ca = 1; cpu->gpr[rd] = r; return(0); } /* SUBFZE - Subtract From Zero extended */ static fastcall int ppc32_exec_SUBFZE(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t a,r; m_uint32_t carry; carry = cpu->xer_ca; a = ~cpu->gpr[ra]; r = a + carry; if (r < a) cpu->xer_ca = 1; cpu->gpr[rd] = r; return(0); } /* SUBFZE. */ static fastcall int ppc32_exec_SUBFZE_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); register m_uint32_t a,r; m_uint32_t carry; carry = cpu->xer_ca; a = ~cpu->gpr[ra]; r = a + carry; if (r < a) cpu->xer_ca = 1; ppc32_exec_update_cr0(cpu,r); cpu->gpr[rd] = r; return(0); } /* SYNC - Synchronize */ static fastcall int ppc32_exec_SYNC(cpu_ppc_t *cpu,ppc_insn_t insn) { return(0); } /* TLBIA - TLB Invalidate All */ static fastcall int ppc32_exec_TLBIA(cpu_ppc_t *cpu,ppc_insn_t insn) { ppc32_mem_invalidate_cache(cpu); return(0); } /* TLBIE - TLB Invalidate Entry */ static fastcall int ppc32_exec_TLBIE(cpu_ppc_t *cpu,ppc_insn_t insn) { ppc32_mem_invalidate_cache(cpu); return(0); } /* TLBSYNC - TLB Synchronize */ static fastcall int ppc32_exec_TLBSYNC(cpu_ppc_t *cpu,ppc_insn_t insn) { return(0); } /* TW - Trap Word */ static fastcall int ppc32_exec_TW(cpu_ppc_t *cpu,ppc_insn_t insn) { int to = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_int32_t a,b; a = cpu->gpr[ra]; b = cpu->gpr[rb]; if (((a < b) && (to & 0x10)) || ((a > b) && (to & 0x08)) || ((a == b) && (to & 0x04)) || (((m_uint32_t)a < (m_uint32_t)b) && (to & 0x02)) || (((m_uint32_t)a > (m_uint32_t)b) && (to & 0x01))) { ppc32_trigger_exception(cpu,PPC32_EXC_PROG); cpu->srr1 |= 1 << 17; return(1); } return(0); } /* TWI - Trap Word Immediate */ static fastcall int ppc32_exec_TWI(cpu_ppc_t *cpu,ppc_insn_t insn) { int to = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_int32_t a,b; a = cpu->gpr[ra]; b = sign_extend(imm,16); if (((a < b) && (to & 0x10)) || ((a > b) && (to & 0x08)) || ((a == b) && (to & 0x04)) || (((m_uint32_t)a < (m_uint32_t)b) && (to & 0x02)) || (((m_uint32_t)a > (m_uint32_t)b) && (to & 0x01))) { ppc32_trigger_exception(cpu,PPC32_EXC_PROG); cpu->srr1 |= 1 << 17; return(1); } return(0); } /* XOR */ static fastcall int ppc32_exec_XOR(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); cpu->gpr[ra] = cpu->gpr[rs] ^ cpu->gpr[rb]; return(0); } /* XOR. */ static fastcall int ppc32_exec_XOR_dot(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t tmp; tmp = cpu->gpr[rs] ^ cpu->gpr[rb]; ppc32_exec_update_cr0(cpu,tmp); cpu->gpr[ra] = tmp; return(0); } /* XORI - XOR Immediate */ static fastcall int ppc32_exec_XORI(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); cpu->gpr[ra] = cpu->gpr[rs] ^ imm; return(0); } /* XORIS - XOR Immediate Shifted */ static fastcall int ppc32_exec_XORIS(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); cpu->gpr[ra] = cpu->gpr[rs] ^ (imm << 16); return(0); } /* DCCCI - Data Cache Congruence Class Invalidate (PowerPC 405) */ static fastcall int ppc32_exec_DCCCI(cpu_ppc_t *cpu,ppc_insn_t insn) { int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; return(0); } /* ICCCI - Instruction Cache Congruence Class Invalidate (PowerPC 405) */ static fastcall int ppc32_exec_ICCCI(cpu_ppc_t *cpu,ppc_insn_t insn) { int ra = bits(insn,16,20); int rb = bits(insn,11,15); m_uint32_t vaddr; vaddr = cpu->gpr[rb]; if (ra != 0) vaddr += cpu->gpr[ra]; return(0); } /* MFDCR - Move From Device Control Register (PowerPC 405) */ static fastcall int ppc32_exec_MFDCR(cpu_ppc_t *cpu,ppc_insn_t insn) { int UNUSED(rt) = bits(insn,21,25); return(0); } /* MTDCR - Move To Device Control Register (PowerPC 405) */ static fastcall int ppc32_exec_MTDCR(cpu_ppc_t *cpu,ppc_insn_t insn) { int UNUSED(rt) = bits(insn,21,25); return(0); } /* TLBRE - TLB Read Entry (PowerPC 405) */ static fastcall int ppc32_exec_TLBRE(cpu_ppc_t *cpu,ppc_insn_t insn) { int rt = bits(insn,21,25); int ra = bits(insn,16,20); int ws = bits(insn,11,15); m_uint32_t index; index = cpu->gpr[ra] & 0x3F; if (ws == 1) { cpu->gpr[rt] = cpu->ppc405_tlb[index].tlb_lo; } else { cpu->gpr[rt] = cpu->ppc405_tlb[index].tlb_hi; cpu->ppc405_pid = cpu->ppc405_tlb[index].tid; } return(0); } /* TLBWE - TLB Write Entry (PowerPC 405) */ static fastcall int ppc32_exec_TLBWE(cpu_ppc_t *cpu,ppc_insn_t insn) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int ws = bits(insn,11,15); m_uint32_t index; index = cpu->gpr[ra] & 0x3F; if (ws == 1) { cpu->ppc405_tlb[index].tlb_lo = cpu->gpr[rs]; } else { cpu->ppc405_tlb[index].tlb_hi = cpu->gpr[rs]; cpu->ppc405_tlb[index].tid = cpu->ppc405_pid; } return(0); } /* PowerPC instruction array */ static struct ppc32_insn_exec_tag ppc32_exec_tags[] = { { "mflr" , ppc32_exec_MFLR , 0xfc1fffff , 0x7c0802a6, 0 }, { "mtlr" , ppc32_exec_MTLR , 0xfc1fffff , 0x7c0803a6, 0 }, { "mfctr" , ppc32_exec_MFCTR , 0xfc1fffff , 0x7c0902a6, 0 }, { "mtctr" , ppc32_exec_MTCTR , 0xfc1fffff , 0x7c0903a6, 0 }, { "add" , ppc32_exec_ADD , 0xfc0007ff , 0x7c000214, 0 }, { "add." , ppc32_exec_ADD_dot , 0xfc0007ff , 0x7c000215, 0 }, { "addo" , ppc32_exec_ADDO , 0xfc0007ff , 0x7c000614, 0 }, { "addo." , ppc32_exec_ADDO_dot , 0xfc0007ff , 0x7c000615, 0 }, { "addc" , ppc32_exec_ADDC , 0xfc0007ff , 0x7c000014, 0 }, { "addc." , ppc32_exec_ADDC_dot , 0xfc0007ff , 0x7c000015, 0 }, { "addco" , ppc32_exec_ADDCO , 0xfc0007ff , 0x7c000414, 0 }, { "addco." , ppc32_exec_ADDCO_dot , 0xfc0007ff , 0x7c000415, 0 }, { "adde" , ppc32_exec_ADDE , 0xfc0007ff , 0x7c000114, 0 }, { "adde." , ppc32_exec_ADDE_dot , 0xfc0007ff , 0x7c000115, 0 }, { "addeo" , ppc32_exec_ADDEO , 0xfc0007ff , 0x7c000514, 0 }, { "addeo." , ppc32_exec_ADDEO_dot , 0xfc0007ff , 0x7c000515, 0 }, { "addi" , ppc32_exec_ADDI , 0xfc000000 , 0x38000000, 0 }, { "addic" , ppc32_exec_ADDIC , 0xfc000000 , 0x30000000, 0 }, { "addic." , ppc32_exec_ADDIC_dot , 0xfc000000 , 0x34000000, 0 }, { "addis" , ppc32_exec_ADDIS , 0xfc000000 , 0x3c000000, 0 }, { "addme" , ppc32_exec_ADDME , 0xfc00ffff , 0x7c0001d4, 0 }, { "addme." , ppc32_exec_ADDME_dot , 0xfc00ffff , 0x7c0001d5, 0 }, { "addze" , ppc32_exec_ADDZE , 0xfc00ffff , 0x7c000194, 0 }, { "addze." , ppc32_exec_ADDZE_dot , 0xfc00ffff , 0x7c000195, 0 }, { "and" , ppc32_exec_AND , 0xfc0007ff , 0x7c000038, 0 }, { "and." , ppc32_exec_AND_dot , 0xfc0007ff , 0x7c000039, 0 }, { "andc" , ppc32_exec_ANDC , 0xfc0007ff , 0x7c000078, 0 }, { "andc." , ppc32_exec_ANDC_dot , 0xfc0007ff , 0x7c000079, 0 }, { "andi." , ppc32_exec_ANDI_dot , 0xfc000000 , 0x70000000, 0 }, { "andis." , ppc32_exec_ANDIS_dot , 0xfc000000 , 0x74000000, 0 }, { "b" , ppc32_exec_B , 0xfc000003 , 0x48000000, 0 }, { "ba" , ppc32_exec_BA , 0xfc000003 , 0x48000002, 0 }, { "bl" , ppc32_exec_BL , 0xfc000003 , 0x48000001, 0 }, { "bla" , ppc32_exec_BLA , 0xfc000003 , 0x48000003, 0 }, { "bc" , ppc32_exec_BC , 0xfc000003 , 0x40000000, 0 }, { "bca" , ppc32_exec_BCA , 0xfc000003 , 0x40000002, 0 }, { "bcl" , ppc32_exec_BCL , 0xfc000003 , 0x40000001, 0 }, { "bcla" , ppc32_exec_BCLA , 0xfc000003 , 0x40000003, 0 }, { "bclr" , ppc32_exec_BCLR , 0xfc00ffff , 0x4c000020, 0 }, { "bclrl" , ppc32_exec_BCLRL , 0xfc00ffff , 0x4c000021, 0 }, { "bcctr" , ppc32_exec_BCCTR , 0xfc00ffff , 0x4c000420, 0 }, { "bcctrl" , ppc32_exec_BCCTRL , 0xfc00ffff , 0x4c000421, 0 }, { "cmp" , ppc32_exec_CMP , 0xfc6007ff , 0x7c000000, 0 }, { "cmpi" , ppc32_exec_CMPI , 0xfc600000 , 0x2c000000, 0 }, { "cmpl" , ppc32_exec_CMPL , 0xfc6007ff , 0x7c000040, 0 }, { "cmpli" , ppc32_exec_CMPLI , 0xfc600000 , 0x28000000, 0 }, { "cntlzw" , ppc32_exec_CNTLZW , 0xfc00ffff , 0x7c000034, 0 }, { "crand" , ppc32_exec_CRAND , 0xfc0007ff , 0x4c000202, 0 }, { "crandc" , ppc32_exec_CRANDC , 0xfc0007ff , 0x4c000102, 0 }, { "creqv" , ppc32_exec_CREQV , 0xfc0007ff , 0x4c000242, 0 }, { "crnand" , ppc32_exec_CRNAND , 0xfc0007ff , 0x4c0001c2, 0 }, { "crnor" , ppc32_exec_CRNOR , 0xfc0007ff , 0x4c000042, 0 }, { "cror" , ppc32_exec_CROR , 0xfc0007ff , 0x4c000382, 0 }, { "crorc" , ppc32_exec_CRORC , 0xfc0007ff , 0x4c000342, 0 }, { "crxor" , ppc32_exec_CRXOR , 0xfc0007ff , 0x4c000182, 0 }, { "dcbf" , ppc32_exec_DCBF , 0xffe007ff , 0x7c0000ac, 0 }, { "dcbi" , ppc32_exec_DCBI , 0xffe007ff , 0x7c0003ac, 0 }, { "dcbt" , ppc32_exec_DCBT , 0xffe007ff , 0x7c00022c, 0 }, { "dcbst" , ppc32_exec_DCBST , 0xffe007ff , 0x7c00006c, 0 }, { "divw" , ppc32_exec_DIVW , 0xfc0007ff , 0x7c0003d6, 0 }, { "divw." , ppc32_exec_DIVW_dot , 0xfc0007ff , 0x7c0003d7, 0 }, { "divwu" , ppc32_exec_DIVWU , 0xfc0007ff , 0x7c000396, 0 }, { "divwu." , ppc32_exec_DIVWU_dot , 0xfc0007ff , 0x7c000397, 0 }, { "eieio" , ppc32_exec_EIEIO , 0xffffffff , 0x7c0006ac, 0 }, { "eqv" , ppc32_exec_EQV , 0xfc0007ff , 0x7c000238, 0 }, { "extsb" , ppc32_exec_EXTSB , 0xfc00ffff , 0x7c000774, 0 }, { "extsb." , ppc32_exec_EXTSB_dot , 0xfc00ffff , 0x7c000775, 0 }, { "extsh" , ppc32_exec_EXTSH , 0xfc00ffff , 0x7c000734, 0 }, { "extsh." , ppc32_exec_EXTSH_dot , 0xfc00ffff , 0x7c000735, 0 }, { "icbi" , ppc32_exec_ICBI , 0xffe007ff , 0x7c0007ac, 0 }, { "isync" , ppc32_exec_ISYNC , 0xffffffff , 0x4c00012c, 0 }, { "lbz" , ppc32_exec_LBZ , 0xfc000000 , 0x88000000, 0 }, { "lbzu" , ppc32_exec_LBZU , 0xfc000000 , 0x8c000000, 0 }, { "lbzux" , ppc32_exec_LBZUX , 0xfc0007ff , 0x7c0000ee, 0 }, { "lbzx" , ppc32_exec_LBZX , 0xfc0007ff , 0x7c0000ae, 0 }, { "lha" , ppc32_exec_LHA , 0xfc000000 , 0xa8000000, 0 }, { "lhau" , ppc32_exec_LHAU , 0xfc000000 , 0xac000000, 0 }, { "lhaux" , ppc32_exec_LHAUX , 0xfc0007ff , 0x7c0002ee, 0 }, { "lhax" , ppc32_exec_LHAX , 0xfc0007ff , 0x7c0002ae, 0 }, { "lhz" , ppc32_exec_LHZ , 0xfc000000 , 0xa0000000, 0 }, { "lhzu" , ppc32_exec_LHZU , 0xfc000000 , 0xa4000000, 0 }, { "lhzux" , ppc32_exec_LHZUX , 0xfc0007ff , 0x7c00026e, 0 }, { "lhzx" , ppc32_exec_LHZX , 0xfc0007ff , 0x7c00022e, 0 }, { "lmw" , ppc32_exec_LMW , 0xfc000000 , 0xb8000000, 0 }, { "lwbrx" , ppc32_exec_LWBRX , 0xfc0007ff , 0x7c00042c, 0 }, { "lwz" , ppc32_exec_LWZ , 0xfc000000 , 0x80000000, 0 }, { "lwzu" , ppc32_exec_LWZU , 0xfc000000 , 0x84000000, 0 }, { "lwzux" , ppc32_exec_LWZUX , 0xfc0007ff , 0x7c00006e, 0 }, { "lwzx" , ppc32_exec_LWZX , 0xfc0007ff , 0x7c00002e, 0 }, { "lwarx" , ppc32_exec_LWARX , 0xfc0007ff , 0x7c000028, 0 }, { "lfd" , ppc32_exec_LFD , 0xfc000000 , 0xc8000000, 0 }, { "lfdu" , ppc32_exec_LFDU , 0xfc000000 , 0xcc000000, 0 }, { "lfdux" , ppc32_exec_LFDUX , 0xfc0007ff , 0x7c0004ee, 0 }, { "lfdx" , ppc32_exec_LFDX , 0xfc0007ff , 0x7c0004ae, 0 }, { "lswi" , ppc32_exec_LSWI , 0xfc0007ff , 0x7c0004aa, 0 }, { "lswx" , ppc32_exec_LSWX , 0xfc0007ff , 0x7c00042a, 0 }, { "mcrf" , ppc32_exec_MCRF , 0xfc63ffff , 0x4c000000, 0 }, { "mfcr" , ppc32_exec_MFCR , 0xfc1fffff , 0x7c000026, 0 }, { "mfmsr" , ppc32_exec_MFMSR , 0xfc1fffff , 0x7c0000a6, 0 }, { "mfspr" , ppc32_exec_MFSPR , 0xfc0007ff , 0x7c0002a6, 0 }, { "mfsr" , ppc32_exec_MFSR , 0xfc10ffff , 0x7c0004a6, 0 }, { "mfsrin" , ppc32_exec_MFSRIN , 0xfc1f07ff , 0x7c000526, 0 }, { "mftbl" , ppc32_exec_MFTBL , 0xfc1ff7ff , 0x7c0c42e6, 0 }, { "mftbu" , ppc32_exec_MFTBU , 0xfc1ff7ff , 0x7c0d42e6, 0 }, { "mtcrf" , ppc32_exec_MTCRF , 0xfc100fff , 0x7c000120, 0 }, { "mtmsr" , ppc32_exec_MTMSR , 0xfc1fffff , 0x7c000124, 0 }, { "mtspr" , ppc32_exec_MTSPR , 0xfc0007ff , 0x7c0003a6, 0 }, { "mtsr" , ppc32_exec_MTSR , 0xfc10ffff , 0x7c0001a4, 0 }, { "mulhw" , ppc32_exec_MULHW , 0xfc0007ff , 0x7c000096, 0 }, { "mulhw." , ppc32_exec_MULHW_dot , 0xfc0007ff , 0x7c000097, 0 }, { "mulhwu" , ppc32_exec_MULHWU , 0xfc0007ff , 0x7c000016, 0 }, { "mulhwu." , ppc32_exec_MULHWU_dot , 0xfc0007ff , 0x7c000017, 0 }, { "mulli" , ppc32_exec_MULLI , 0xfc000000 , 0x1c000000, 0 }, { "mullw" , ppc32_exec_MULLW , 0xfc0007ff , 0x7c0001d6, 0 }, { "mullw." , ppc32_exec_MULLW_dot , 0xfc0007ff , 0x7c0001d7, 0 }, { "mullwo" , ppc32_exec_MULLWO , 0xfc0007ff , 0x7c0005d6, 0 }, { "mullwo." , ppc32_exec_MULLWO_dot , 0xfc0007ff , 0x7c0005d7, 0 }, { "nand" , ppc32_exec_NAND , 0xfc0007ff , 0x7c0003b8, 0 }, { "nand." , ppc32_exec_NAND_dot , 0xfc0007ff , 0x7c0003b9, 0 }, { "neg" , ppc32_exec_NEG , 0xfc00ffff , 0x7c0000d0, 0 }, { "neg." , ppc32_exec_NEG_dot , 0xfc00ffff , 0x7c0000d1, 0 }, { "nego" , ppc32_exec_NEGO , 0xfc00ffff , 0x7c0004d0, 0 }, { "nego." , ppc32_exec_NEGO_dot , 0xfc00ffff , 0x7c0004d1, 0 }, { "nor" , ppc32_exec_NOR , 0xfc0007ff , 0x7c0000f8, 0 }, { "nor." , ppc32_exec_NOR_dot , 0xfc0007ff , 0x7c0000f9, 0 }, { "or" , ppc32_exec_OR , 0xfc0007ff , 0x7c000378, 0 }, { "or." , ppc32_exec_OR_dot , 0xfc0007ff , 0x7c000379, 0 }, { "orc" , ppc32_exec_ORC , 0xfc0007ff , 0x7c000338, 0 }, { "orc." , ppc32_exec_ORC_dot , 0xfc0007ff , 0x7c000339, 0 }, { "ori" , ppc32_exec_ORI , 0xfc000000 , 0x60000000, 0 }, { "oris" , ppc32_exec_ORIS , 0xfc000000 , 0x64000000, 0 }, { "rfi" , ppc32_exec_RFI , 0xffffffff , 0x4c000064, 0 }, { "rlwimi" , ppc32_exec_RLWIMI , 0xfc000001 , 0x50000000, 0 }, { "rlwimi." , ppc32_exec_RLWIMI_dot , 0xfc000001 , 0x50000001, 0 }, { "rlwinm" , ppc32_exec_RLWINM , 0xfc000001 , 0x54000000, 0 }, { "rlwinm." , ppc32_exec_RLWINM_dot , 0xfc000001 , 0x54000001, 0 }, { "rlwnm" , ppc32_exec_RLWNM , 0xfc000001 , 0x5c000000, 0 }, { "rlwnm." , ppc32_exec_RLWNM_dot , 0xfc000001 , 0x5c000001, 0 }, { "sc" , ppc32_exec_SC , 0xffffffff , 0x44000002, 0 }, { "slw" , ppc32_exec_SLW , 0xfc0007ff , 0x7c000030, 0 }, { "slw." , ppc32_exec_SLW_dot , 0xfc0007ff , 0x7c000031, 0 }, { "sraw" , ppc32_exec_SRAW , 0xfc0007ff , 0x7c000630, 0 }, { "srawi" , ppc32_exec_SRAWI , 0xfc0007ff , 0x7c000670, 0 }, { "srawi." , ppc32_exec_SRAWI_dot , 0xfc0007ff , 0x7c000671, 0 }, { "srw" , ppc32_exec_SRW , 0xfc0007ff , 0x7c000430, 0 }, { "srw." , ppc32_exec_SRW_dot , 0xfc0007ff , 0x7c000431, 0 }, { "stb" , ppc32_exec_STB , 0xfc000000 , 0x98000000, 0 }, { "stbu" , ppc32_exec_STBU , 0xfc000000 , 0x9c000000, 0 }, { "stbux" , ppc32_exec_STBUX , 0xfc0007ff , 0x7c0001ee, 0 }, { "stbx" , ppc32_exec_STBX , 0xfc0007ff , 0x7c0001ae, 0 }, { "sth" , ppc32_exec_STH , 0xfc000000 , 0xb0000000, 0 }, { "sthu" , ppc32_exec_STHU , 0xfc000000 , 0xb4000000, 0 }, { "sthux" , ppc32_exec_STHUX , 0xfc0007ff , 0x7c00036e, 0 }, { "sthx" , ppc32_exec_STHX , 0xfc0007ff , 0x7c00032e, 0 }, { "stmw" , ppc32_exec_STMW , 0xfc000000 , 0xbc000000, 0 }, { "stw" , ppc32_exec_STW , 0xfc000000 , 0x90000000, 0 }, { "stwu" , ppc32_exec_STWU , 0xfc000000 , 0x94000000, 0 }, { "stwux" , ppc32_exec_STWUX , 0xfc0007ff , 0x7c00016e, 0 }, { "stwx" , ppc32_exec_STWX , 0xfc0007ff , 0x7c00012e, 0 }, { "stwbrx" , ppc32_exec_STWBRX , 0xfc0007ff , 0x7c00052c, 0 }, { "stwcx." , ppc32_exec_STWCX_dot , 0xfc0007ff , 0x7c00012d, 0 }, { "stfd" , ppc32_exec_STFD , 0xfc000000 , 0xd8000000, 0 }, { "stfdu" , ppc32_exec_STFDU , 0xfc000000 , 0xdc000000, 0 }, { "stfdux" , ppc32_exec_STFDUX , 0xfc0007ff , 0x7c0005ee, 0 }, { "stfdx" , ppc32_exec_STFDX , 0xfc0007ff , 0x7c0005ae, 0 }, { "stswi" , ppc32_exec_STSWI , 0xfc0007ff , 0x7c0005aa, 0 }, { "stswx" , ppc32_exec_STSWX , 0xfc0007ff , 0x7c00052a, 0 }, { "subf" , ppc32_exec_SUBF , 0xfc0007ff , 0x7c000050, 0 }, { "subf." , ppc32_exec_SUBF_dot , 0xfc0007ff , 0x7c000051, 0 }, { "subfo" , ppc32_exec_SUBFO , 0xfc0007ff , 0x7c000450, 0 }, { "subfo." , ppc32_exec_SUBFO_dot , 0xfc0007ff , 0x7c000451, 0 }, { "subfc" , ppc32_exec_SUBFC , 0xfc0007ff , 0x7c000010, 0 }, { "subfc." , ppc32_exec_SUBFC_dot , 0xfc0007ff , 0x7c000011, 0 }, //{ "subfco" , ppc32_exec_SUBFCO , 0xfc0007ff , 0x7c000410, 0 }, //{ "subfco." , ppc32_exec_SUBFCO_dot , 0xfc0007ff , 0x7c000411, 0 }, { "subfe" , ppc32_exec_SUBFE , 0xfc0007ff , 0x7c000110, 0 }, { "subfic" , ppc32_exec_SUBFIC , 0xfc000000 , 0x20000000, 0 }, { "subfze" , ppc32_exec_SUBFZE , 0xfc00ffff , 0x7c000190, 0 }, { "subfze." , ppc32_exec_SUBFZE_dot , 0xfc00ffff , 0x7c000191, 0 }, { "sync" , ppc32_exec_SYNC , 0xffffffff , 0x7c0004ac, 0 }, { "tlbia" , ppc32_exec_TLBIA , 0xffffffff , 0x7c0002e4, 0 }, { "tlbie" , ppc32_exec_TLBIE , 0xffff07ff , 0x7c000264, 0 }, { "tlbsync" , ppc32_exec_TLBSYNC , 0xffffffff , 0x7c00046c, 0 }, { "tw" , ppc32_exec_TW , 0xfc0007ff , 0x7c000008, 0 }, { "twi" , ppc32_exec_TWI , 0xfc000000 , 0x0c000000, 0 }, { "xor" , ppc32_exec_XOR , 0xfc0007ff , 0x7c000278, 0 }, { "xor." , ppc32_exec_XOR_dot , 0xfc0007ff , 0x7c000279, 0 }, { "xori" , ppc32_exec_XORI , 0xfc000000 , 0x68000000, 0 }, { "xoris" , ppc32_exec_XORIS , 0xfc000000 , 0x6c000000, 0 }, /* PowerPC 405 specific instructions */ { "dccci" , ppc32_exec_DCCCI , 0xfc0007ff , 0x7c00038c, 0 }, { "iccci" , ppc32_exec_ICCCI , 0xfc0007ff , 0x7c00078c, 0 }, { "mfdcr" , ppc32_exec_MFDCR , 0xfc0007ff , 0x7c000286, 0 }, { "mtdcr" , ppc32_exec_MTDCR , 0xfc0007ff , 0x7c000386, 0 }, { "tlbre" , ppc32_exec_TLBRE , 0xfc0007ff , 0x7c000764, 0 }, { "tlbwe" , ppc32_exec_TLBWE , 0xfc0007ff , 0x7c0007a4, 0 }, /* Unknown opcode fallback */ { "unknown" , ppc32_exec_unknown , 0x00000000 , 0x00000000, 0 }, { NULL , NULL, 0, 0, 0 }, }; dynamips-0.2.14/unstable/ppc32_jit.c000066400000000000000000001072701241034141600172030ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * PPC32 JIT compiler. */ #include #include #include #include #include #include #include #include #include #include "cpu.h" #include "device.h" #include "ppc32.h" #include "ppc32_exec.h" #include "ppc32_jit.h" #include "insn_lookup.h" #include "memory.h" #include "ptask.h" #include PPC32_ARCH_INC_FILE /* Instruction Lookup Table */ static insn_lookup_t *ilt = NULL; static void *ppc32_jit_get_insn(int index) { return(&ppc32_insn_tags[index]); } static int ppc32_jit_chk_lo(struct ppc32_insn_tag *tag,int value) { return((value & tag->mask) == (tag->value & 0xFFFF)); } static int ppc32_jit_chk_hi(struct ppc32_insn_tag *tag,int value) { return((value & (tag->mask >> 16)) == (tag->value >> 16)); } /* Destroy instruction lookup table */ static void destroy_ilt(void) { assert(ilt); ilt_destroy(ilt); ilt = NULL; } /* Initialize instruction lookup table */ void ppc32_jit_create_ilt(void) { int i,count; for(i=0,count=0;ppc32_insn_tags[i].emit;i++) count++; ilt = ilt_create("ppc32j",count, (ilt_get_insn_cbk_t)ppc32_jit_get_insn, (ilt_check_cbk_t)ppc32_jit_chk_lo, (ilt_check_cbk_t)ppc32_jit_chk_hi); atexit(destroy_ilt); } /* Initialize the JIT structure */ int ppc32_jit_init(cpu_ppc_t *cpu) { insn_exec_page_t *cp; u_char *cp_addr; u_int area_size; size_t len; int i; /* Virtual address mapping for TCB */ len = PPC_JIT_VIRT_HASH_SIZE * sizeof(void *); cpu->tcb_virt_hash = m_memalign(4096,len); memset(cpu->tcb_virt_hash,0,len); /* Physical address mapping for TCB */ len = PPC_JIT_PHYS_HASH_SIZE * sizeof(void *); cpu->tcb_phys_hash = m_memalign(4096,len); memset(cpu->tcb_phys_hash,0,len); /* Get area size */ if (!(area_size = cpu->vm->exec_area_size)) area_size = PPC_EXEC_AREA_SIZE; /* Create executable page area */ cpu->exec_page_area_size = area_size * 1048576; cpu->exec_page_area = memzone_map_exec_area(cpu->exec_page_area_size); if (!cpu->exec_page_area) { fprintf(stderr, "ppc32_jit_init: unable to create exec area (size %lu)\n", (u_long)cpu->exec_page_area_size); return(-1); } /* Carve the executable page area */ cpu->exec_page_count = cpu->exec_page_area_size / PPC_JIT_BUFSIZE; cpu->exec_page_array = calloc(cpu->exec_page_count, sizeof(insn_exec_page_t)); if (!cpu->exec_page_array) { fprintf(stderr,"ppc32_jit_init: unable to create exec page array\n"); return(-1); } for(i=0,cp_addr=cpu->exec_page_area;iexec_page_count;i++) { cp = &cpu->exec_page_array[i]; cp->ptr = cp_addr; cp_addr += PPC_JIT_BUFSIZE; cp->next = cpu->exec_page_free_list; cpu->exec_page_free_list = cp; } printf("CPU%u: carved JIT exec zone of %lu Mb into %lu pages of %u Kb.\n", cpu->gen->id, (u_long)(cpu->exec_page_area_size / 1048576), (u_long)cpu->exec_page_count,PPC_JIT_BUFSIZE / 1024); return(0); } /* Flush the JIT */ u_int ppc32_jit_flush(cpu_ppc_t *cpu,u_int threshold) { ppc32_jit_tcb_t *p,*next; m_uint32_t hv; u_int count = 0; if (!threshold) threshold = (u_int)(-1); /* UINT_MAX not defined everywhere */ for(p=cpu->tcb_list;p;p=next) { next = p->next; /* flushing not allowed */ if (p->flags & PPC32_JIT_TCB_FLAG_NO_FLUSH) continue; if (p->acc_count <= threshold) { hv = ppc32_jit_get_virt_hash(p->start_ia); ppc32_jit_tcb_free(cpu,p,TRUE); if (cpu->tcb_virt_hash[hv] == p) cpu->tcb_virt_hash[hv] = NULL; count++; } } cpu->compiled_pages -= count; return(count); } /* Shutdown the JIT */ void ppc32_jit_shutdown(cpu_ppc_t *cpu) { ppc32_jit_tcb_t *p,*next; /* Flush the JIT */ ppc32_jit_flush(cpu,0); /* Free the instruction blocks */ for(p=cpu->tcb_free_list;p;p=next) { next = p->next; free(p); } /* Unmap the executable page area */ if (cpu->exec_page_area) memzone_unmap(cpu->exec_page_area,cpu->exec_page_area_size); /* Free the exec page array */ free(cpu->exec_page_array); /* Free virtual and physical hash tables */ free(cpu->tcb_virt_hash); free(cpu->tcb_phys_hash); } /* Allocate an exec page */ static inline insn_exec_page_t *exec_page_alloc(cpu_ppc_t *cpu) { insn_exec_page_t *p; u_int count; /* If the free list is empty, flush JIT */ if (unlikely(!cpu->exec_page_free_list)) { if (cpu->jit_flush_method) { cpu_log(cpu->gen, "JIT","flushing data structures (compiled pages=%u)\n", cpu->compiled_pages); ppc32_jit_flush(cpu,0); } else { count = ppc32_jit_flush(cpu,100); cpu_log(cpu->gen,"JIT","partial JIT flush (count=%u)\n",count); if (!cpu->exec_page_free_list) ppc32_jit_flush(cpu,0); } /* Use both methods alternatively */ cpu->jit_flush_method = 1 - cpu->jit_flush_method; } if (unlikely(!(p = cpu->exec_page_free_list))) return NULL; cpu->exec_page_free_list = p->next; cpu->exec_page_alloc++; return p; } /* Free an exec page and returns it to the pool */ static inline void exec_page_free(cpu_ppc_t *cpu,insn_exec_page_t *p) { if (p) { p->next = cpu->exec_page_free_list; cpu->exec_page_free_list = p; cpu->exec_page_alloc--; } } /* Find the JIT code emitter for the specified PowerPC instruction */ static struct ppc32_insn_tag *insn_tag_find(ppc_insn_t ins) { struct ppc32_insn_tag *tag = NULL; int index; index = ilt_lookup(ilt,ins); tag = ppc32_jit_get_insn(index); return tag; } /* Fetch a PowerPC instruction */ static forced_inline ppc_insn_t insn_fetch(ppc32_jit_tcb_t *tcb) { return(vmtoh32(tcb->ppc_code[tcb->ppc_trans_pos])); } #define DEBUG_HREG 0 /* Show register allocation status */ _unused static void ppc32_jit_show_hreg_status(cpu_ppc_t *cpu) { struct hreg_map *map; printf("PPC32-JIT: reg status for insn '%s'\n",cpu->jit_hreg_seq_name); for(map=cpu->hreg_map_list;map;map=map->next) { switch(map->flags) { case 0: printf(" hreg %d is free, mapped to vreg %d\n", map->hreg,map->vreg); break; case HREG_FLAG_ALLOC_LOCKED: printf(" hreg %d is locked, mapped to vreg %d\n", map->hreg,map->vreg); break; case HREG_FLAG_ALLOC_FORCED: printf(" hreg %d is in forced alloc\n",map->hreg); break; } } } /* Extract an host reg mapping from the register list */ static void ppc32_jit_extract_hreg(cpu_ppc_t *cpu,struct hreg_map *map) { if (map->prev != NULL) map->prev->next = map->next; else cpu->hreg_map_list = map->next; if (map->next != NULL) map->next->prev = map->prev; else cpu->hreg_lru = map->prev; } /* Insert a reg map as head of list (as MRU element) */ void ppc32_jit_insert_hreg_mru(cpu_ppc_t *cpu,struct hreg_map *map) { map->next = cpu->hreg_map_list; map->prev = NULL; if (map->next == NULL) { cpu->hreg_lru = map; } else { map->next->prev = map; } cpu->hreg_map_list = map; } /* Start register allocation sequence */ void ppc32_jit_start_hreg_seq(cpu_ppc_t *cpu,char *insn) { struct hreg_map *map; #if DEBUG_HREG printf("Starting hreg_seq insn='%s'\n",insn); #endif /* Reset the allocation state of all host registers */ for(map=cpu->hreg_map_list;map;map=map->next) map->flags = 0; /* Save the instruction name for debugging/error analysis */ cpu->jit_hreg_seq_name = insn; } /* Close register allocation sequence */ void ppc32_jit_close_hreg_seq(cpu_ppc_t *cpu) { #if DEBUG_HREG ppc32_show_hreg_status(cpu); #endif } /* Find a free host register to use */ static struct hreg_map *ppc32_jit_get_free_hreg(cpu_ppc_t *cpu) { struct hreg_map *map,*oldest_free = NULL; for(map=cpu->hreg_lru;map;map=map->prev) { if ((map->vreg == -1) && (map->flags == 0)) return map; if ((map->flags == 0) && !oldest_free) oldest_free = map; } if (!oldest_free) { fprintf(stderr, "ppc32_get_free_hreg: unable to find free reg for insn %s\n", cpu->jit_hreg_seq_name); } return oldest_free; } /* Allocate an host register */ int ppc32_jit_alloc_hreg(cpu_ppc_t *cpu,int ppc_reg) { struct hreg_map *map; int hreg; /* * If PPC reg is invalid, the caller requested for a temporary register. */ if (ppc_reg == -1) { if ((map = ppc32_jit_get_free_hreg(cpu)) == NULL) return(-1); /* Allocate the register and invalidate its PPC mapping if present */ map->flags = HREG_FLAG_ALLOC_LOCKED; if (map->vreg != -1) { cpu->ppc_reg_map[map->vreg] = -1; map->vreg = -1; } return(map->hreg); } hreg = cpu->ppc_reg_map[ppc_reg]; /* * If the PPC register is already mapped to an host register, re-use this * mapping and put this as MRU mapping. */ if (hreg != -1) { map = &cpu->hreg_map[hreg]; } else { /* * This PPC register has no mapping to host register. Find a free * register. */ if ((map = ppc32_jit_get_free_hreg(cpu)) == NULL) return(-1); /* Remove the old PPC mapping if present */ if (map->vreg != -1) cpu->ppc_reg_map[map->vreg] = -1; /* Establish the new mapping */ cpu->ppc_reg_map[ppc_reg] = map->hreg; map->vreg = ppc_reg; } /* Prevent this register from further allocation in this instruction */ map->flags = HREG_FLAG_ALLOC_LOCKED; ppc32_jit_extract_hreg(cpu,map); ppc32_jit_insert_hreg_mru(cpu,map); return(map->hreg); } /* Force allocation of an host register */ int ppc32_jit_alloc_hreg_forced(cpu_ppc_t *cpu,int hreg) { int ppc_reg; ppc_reg = cpu->hreg_map[hreg].vreg; /* Check that this register is not already allocated */ if (cpu->hreg_map[hreg].flags != 0) { fprintf(stderr,"ppc32_alloc_hreg_forced: trying to force allocation " "of hreg %d (insn %s)\n", hreg,cpu->jit_hreg_seq_name); return(-1); } cpu->hreg_map[hreg].flags = HREG_FLAG_ALLOC_FORCED; cpu->hreg_map[hreg].vreg = -1; if (ppc_reg != -1) cpu->ppc_reg_map[ppc_reg] = -1; return(0); } /* Emit a breakpoint if necessary */ #if BREAKPOINT_ENABLE static void insn_emit_breakpoint(cpu_ppc_t *cpu,ppc32_jit_tcb_t *tcb) { m_uint32_t ia; int i; ia = tcb->start_ia + ((tcb->ppc_trans_pos-1)<<2); for(i=0;ibreakpoints[i]) { ppc32_emit_breakpoint(cpu,tcb); break; } } #endif /* BREAKPOINT_ENABLE */ /* Fetch a PowerPC instruction and emit corresponding translated code */ struct ppc32_insn_tag *ppc32_jit_fetch_and_emit(cpu_ppc_t *cpu, ppc32_jit_tcb_t *tcb) { struct ppc32_insn_tag *tag; ppc_insn_t code; code = insn_fetch(tcb); tag = insn_tag_find(code); assert(tag); tag->emit(cpu,tcb,code); return tag; } /* Add end of JIT block */ _unused static void ppc32_jit_tcb_add_end(ppc32_jit_tcb_t *tcb) { ppc32_set_ia(&tcb->jit_ptr,tcb->start_ia+(tcb->ppc_trans_pos<<2)); ppc32_jit_tcb_push_epilog(&tcb->jit_ptr); } /* Record a patch to apply in a compiled block */ int ppc32_jit_tcb_record_patch(ppc32_jit_tcb_t *tcb,jit_op_t *iop, u_char *jit_ptr,m_uint32_t vaddr) { struct ppc32_jit_patch_table *ipt = tcb->patch_table; struct ppc32_insn_patch *patch; /* pc must be 32-bit aligned */ if (vaddr & 0x03) { fprintf(stderr, "TCB 0x%8.8x: trying to record an invalid IA (0x%8.8x)\n", tcb->start_ia,vaddr); return(-1); } if (!ipt || (ipt->cur_patch >= PPC32_INSN_PATCH_TABLE_SIZE)) { /* full table or no table, create a new one */ ipt = malloc(sizeof(*ipt)); if (!ipt) { fprintf(stderr,"Block 0x%8.8x: unable to create patch table.\n", tcb->start_ia); return(-1); } memset(ipt,0,sizeof(*ipt)); ipt->next = tcb->patch_table; tcb->patch_table = ipt; } #if DEBUG_BLOCK_PATCH printf("TCB 0x%8.8x: recording patch [JIT:%p->ppc:0x%8.8x], " "MTP=%d\n",tcb->start_ia,jit_ptr,vaddr,tcb->ppc_trans_pos); #endif patch = &ipt->patches[ipt->cur_patch]; patch->jit_insn = jit_ptr; patch->ppc_ia = vaddr; ipt->cur_patch++; patch->next = iop->arg_ptr; iop->arg_ptr = patch; return(0); } /* Apply patches for a JIT instruction block */ static int ppc32_jit_tcb_apply_patches(cpu_ppc_t *cpu, ppc32_jit_tcb_t *tcb, jit_op_t *iop) { struct ppc32_insn_patch *patch; u_char *jit_ptr,*jit_dst; u_int pos; for(patch=iop->arg_ptr;patch;patch=patch->next) { jit_ptr = (patch->jit_insn - iop->ob_data) + iop->ob_final; pos = (patch->ppc_ia & PPC32_MIN_PAGE_IMASK) >> 2; jit_dst = tcb->jit_insn_ptr[pos]; if (jit_dst) { #if DEBUG_BLOCK_PATCH printf("TCB 0x%8.8x: applying patch " "[JIT:%p->ppc:0x%8.8x=JIT:%p, ]\n", tcb->start_ia,patch->jit_insn,patch->ppc_ia,jit_dst); #endif ppc32_jit_tcb_set_patch(jit_ptr,jit_dst); } else { printf("TCB 0x%8.8x: null dst for patch!\n",tcb->start_ia); } } return(0); } /* Free the patch table */ static void ppc32_jit_tcb_free_patches(ppc32_jit_tcb_t *tcb) { struct ppc32_jit_patch_table *p,*next; for(p=tcb->patch_table;p;p=next) { next = p->next; free(p); } tcb->patch_table = NULL; } /* Adjust the JIT buffer if its size is not sufficient */ static int ppc32_jit_tcb_adjust_buffer(cpu_ppc_t *cpu,ppc32_jit_tcb_t *tcb) { insn_exec_page_t *new_buffer; if ((tcb->jit_ptr - tcb->jit_buffer->ptr) <= (PPC_JIT_BUFSIZE - 512)) return(0); #if DEBUG_BLOCK_CHUNK printf("TCB 0x%8.8x: adjusting JIT buffer...\n",tcb->start_ia); #endif if (tcb->jit_chunk_pos >= PPC_JIT_MAX_CHUNKS) { fprintf(stderr,"TCB 0x%8.8x: too many JIT chunks.\n",tcb->start_ia); return(-1); } if (!(new_buffer = exec_page_alloc(cpu))) return(-1); /* record the new exec page */ tcb->jit_chunks[tcb->jit_chunk_pos++] = tcb->jit_buffer; tcb->jit_buffer = new_buffer; /* jump to the new exec page (link) */ ppc32_jit_tcb_set_jump(tcb->jit_ptr,new_buffer->ptr); tcb->jit_ptr = new_buffer->ptr; return(0); } /* Allocate an instruction block */ static inline ppc32_jit_tcb_t *ppc32_jit_tcb_alloc(cpu_ppc_t *cpu) { ppc32_jit_tcb_t *tcb; if (cpu->tcb_free_list) { tcb = cpu->tcb_free_list; cpu->tcb_free_list = tcb->next; } else { if (!(tcb = malloc(sizeof(*tcb)))) return NULL; } memset(tcb,0,sizeof(*tcb)); return tcb; } /* Free the code chunks */ static void ppc32_jit_tcb_free_code_chunks(cpu_ppc_t *cpu,ppc32_jit_tcb_t *tcb) { int i; /* Free code pages */ for(i=0;ijit_chunks[i]); tcb->jit_chunks[i] = NULL; } /* Free the current JIT buffer */ exec_page_free(cpu,tcb->jit_buffer); tcb->jit_buffer = NULL; } /* Free the generated code stuff */ static void ppc32_jit_flush_gen_code(cpu_ppc_t *cpu,ppc32_jit_tcb_t *tcb) { /* Free the patch tables */ ppc32_jit_tcb_free_patches(tcb); /* Free code pages */ ppc32_jit_tcb_free_code_chunks(cpu,tcb); /* Free the PowerPC-to-native code mapping */ free(tcb->jit_insn_ptr); tcb->jit_insn_ptr = NULL; } /* Mark a block as containing self-modifying code */ void ppc32_jit_mark_smc(cpu_ppc_t *cpu,ppc32_jit_tcb_t *tcb) { if (tcb->flags & PPC32_JIT_TCB_FLAG_SMC) return; /* already done */ tcb->flags |= PPC32_JIT_TCB_FLAG_SMC; ppc32_jit_flush_gen_code(cpu,tcb); } /* Free an instruction block */ void ppc32_jit_tcb_free(cpu_ppc_t *cpu,ppc32_jit_tcb_t *tcb,int list_removal) { if (tcb != NULL) { if (list_removal) { /* Remove the block from the linked list */ if (tcb->next) tcb->next->prev = tcb->prev; else cpu->tcb_last = tcb->prev; if (tcb->prev) tcb->prev->next = tcb->next; else cpu->tcb_list = tcb->next; /* Remove the block from the physical mapping hash table */ if (tcb->phys_pprev) { if (tcb->phys_next) tcb->phys_next->phys_pprev = tcb->phys_pprev; *(tcb->phys_pprev) = tcb->phys_next; tcb->phys_pprev = NULL; tcb->phys_next = NULL; } } /* Free generated code information */ ppc32_jit_flush_gen_code(cpu,tcb); tcb->next = cpu->tcb_free_list; cpu->tcb_free_list = tcb; } } /* Create an instruction block */ static ppc32_jit_tcb_t * ppc32_jit_tcb_create(cpu_ppc_t *cpu,m_uint32_t vaddr,m_uint32_t exec_state) { ppc32_jit_tcb_t *tcb = NULL; m_uint32_t phys_page; ppc_insn_t *ppc_code; /* * Get the powerpc code address from the host point of view. * If there is an error (TLB,...), we return directly to the main loop. */ ppc_code = cpu->mem_op_ifetch(cpu,vaddr); if (unlikely(cpu->translate(cpu,cpu->ia,PPC32_MTS_ICACHE,&phys_page))) return NULL; if (!(tcb = ppc32_jit_tcb_alloc(cpu))) goto err_block_alloc; tcb->start_ia = vaddr; tcb->exec_state = exec_state; tcb->phys_page = phys_page; tcb->phys_hash = ppc32_jit_get_phys_hash(phys_page); /* Allocate the first JIT buffer */ if (!(tcb->jit_buffer = exec_page_alloc(cpu))) goto err_jit_alloc; tcb->jit_ptr = tcb->jit_buffer->ptr; tcb->ppc_code = ppc_code; if (!tcb->ppc_code) { fprintf(stderr,"%% No memory map for code execution at 0x%8.8x\n", tcb->start_ia); goto err_lookup; } #if DEBUG_BLOCK_TIMESTAMP tcb->tm_first_use = tcb->tm_last_use = jit_jiffies; #endif return tcb; err_lookup: err_jit_alloc: ppc32_jit_tcb_free(cpu,tcb,FALSE); err_block_alloc: fprintf(stderr,"%% Unable to create instruction block for vaddr=0x%8.8x\n", vaddr); return NULL; } /* ======================================================================== */ /* Dump a JIT opcode */ static void ppc32_op_dump_opcode(jit_op_t *op) { switch(op->opcode) { case JIT_OP_BRANCH_TARGET: printf("branch_target"); break; case JIT_OP_BRANCH_JUMP: printf("branch_jump"); break; case JIT_OP_EOB: printf("eob"); break; case JIT_OP_LOAD_GPR: printf("load_gpr(%d,$%d,r:%d)", op->param[0],op->param[1],op->param[2]); break; case JIT_OP_STORE_GPR: printf("store_gpr(%d,$%d,r:%d)", op->param[0],op->param[1],op->param[2]); break; case JIT_OP_ALTER_HOST_REG: printf("alter_host_reg(%d)",op->param[0]); break; case JIT_OP_UPDATE_FLAGS: printf("update_flags(%d,%s)", op->param[0],(op->param[1] ? "signed" : "unsigned")); break; case JIT_OP_REQUIRE_FLAGS: printf("require_flags(%d)",op->param[0]); break; case JIT_OP_TRASH_FLAGS: printf("trash_flags(%d)",op->param[0]); break; case JIT_OP_INSN_OUTPUT: printf("insn_out(\"%s\")",op->insn_name); break; case JIT_OP_SET_HOST_REG_IMM32: printf("set_host_reg_imm32(%d,0x%8.8x)",op->param[0],op->param[1]); break; default: printf("op(%u)",op->opcode); } } /* Dump JIT operations (debugging) */ _unused static void ppc32_op_dump(cpu_gen_t *cpu,ppc32_jit_tcb_t *tcb) { m_uint32_t ia = tcb->start_ia; jit_op_t *op; int i; printf("PPC32-JIT: dump of page 0x%8.8x\n",ia); for(i=0;ijit_op_array[i];op;op=op->next) { ppc32_op_dump_opcode(op); printf(" "); } printf("\n"); } printf("\n"); } /* PPC register mapping */ typedef struct { int host_reg; jit_op_t *last_store; m_uint32_t last_store_ia; }ppc_reg_map_t; /* Clear register mapping (with PPC register) */ static void ppc32_clear_ppc_reg_map(ppc_reg_map_t *ppc_map,int *host_map, int reg) { int i,hreg; if (reg == JIT_OP_ALL_REGS) { for(i=0;ihost_reg != JIT_OP_INV_REG) && (host_map[map->host_reg] != i)) goto error; } for(i=0;ijit_op_array[i];op;op=op->next) { //ppc32_check_reg_map(ppc_map,host_map); cur_ia = tcb->start_ia + (i << 2); switch(op->opcode) { /* Clear mapping if end of block or branch target */ case JIT_OP_BRANCH_TARGET: case JIT_OP_EOB: ppc32_clear_ppc_reg_map(ppc_map,host_map,JIT_OP_ALL_REGS); for(j=0;j<8;j++) last_cr_update[j] = NULL; break; /* Branch jump: clear "store" operation status */ case JIT_OP_BRANCH_JUMP: for(j=0;jparam[0]; if (reg != JIT_OP_ALL_REGS) { if (host_map[reg] != JIT_OP_INV_REG) ppc32_clear_ppc_reg_map(ppc_map,host_map,host_map[reg]); } else { ppc32_clear_ppc_reg_map(ppc_map,host_map,JIT_OP_ALL_REGS); } break; /* Save reg mapping and last operation */ case JIT_OP_STORE_GPR: reg = op->param[0]; map = &ppc_map[op->param[1]]; /* clear old mapping */ if (reg != map->host_reg) { ppc32_clear_host_reg_map(ppc_map,host_map,reg); ppc32_clear_ppc_reg_map(ppc_map,host_map,op->param[1]); } /* cancel previous store op for this PPC register */ if (map->last_store) { map->last_store->param[0] = JIT_OP_INV_REG; map->last_store = NULL; } map->host_reg = reg; map->last_store = op; map->last_store_ia = cur_ia; host_map[reg] = op->param[1]; break; /* Load reg: check if can avoid it */ case JIT_OP_LOAD_GPR: reg = op->param[0]; map = &ppc_map[op->param[1]]; if (reg == map->host_reg) { /* Cancel this load */ op->param[0] = JIT_OP_INV_REG; } else { /* clear old mapping */ ppc32_clear_host_reg_map(ppc_map,host_map,reg); ppc32_clear_ppc_reg_map(ppc_map,host_map,op->param[1]); /* Save this reg mapping */ map->host_reg = op->param[0]; map->last_store = NULL; host_map[op->param[0]] = op->param[1]; } break; /* Trash flags */ case JIT_OP_TRASH_FLAGS: for(j=0;j<8;j++) last_cr_update[j] = NULL; break; /* Flags required */ case JIT_OP_REQUIRE_FLAGS: if (op->param[0] != JIT_OP_PPC_ALL_FLAGS) { last_cr_update[op->param[0]] = NULL; } else { for(j=0;j<8;j++) last_cr_update[j] = NULL; } break; /* Update flags */ case JIT_OP_UPDATE_FLAGS: opx = last_cr_update[op->param[0]]; if (opx != NULL) opx->param[0] = JIT_OP_INV_REG; last_cr_update[op->param[0]] = op; break; } } } } /* Generate the JIT code for the specified JIT op list */ static void ppc32_op_gen_list(ppc32_jit_tcb_t *tcb,int ipos,jit_op_t *op_list, u_char *jit_start) { jit_op_t *op; for(op=op_list;op;op=op->next) { switch(op->opcode) { case JIT_OP_INSN_OUTPUT: ppc32_op_insn_output(tcb,op); break; case JIT_OP_LOAD_GPR: ppc32_op_load_gpr(tcb,op); break; case JIT_OP_STORE_GPR: ppc32_op_store_gpr(tcb,op); break; case JIT_OP_UPDATE_FLAGS: ppc32_op_update_flags(tcb,op); break; case JIT_OP_BRANCH_TARGET: tcb->jit_insn_ptr[ipos] = jit_start; break; case JIT_OP_MOVE_HOST_REG: ppc32_op_move_host_reg(tcb,op); break; case JIT_OP_SET_HOST_REG_IMM32: ppc32_op_set_host_reg_imm32(tcb,op); break; } } } /* Opcode emit start */ static inline void ppc32_op_emit_start(cpu_ppc_t *cpu,ppc32_jit_tcb_t *tcb) { cpu_gen_t *c = cpu->gen; jit_op_t *op; if (c->jit_op_array[tcb->ppc_trans_pos] == NULL) c->jit_op_current = &c->jit_op_array[tcb->ppc_trans_pos]; else { for(op=c->jit_op_array[tcb->ppc_trans_pos];op;op=op->next) c->jit_op_current = &op->next; } } /* Generate the JIT code for the current page, given an op list */ static int ppc32_op_gen_page(cpu_ppc_t *cpu,ppc32_jit_tcb_t *tcb) { struct ppc32_insn_tag *tag; cpu_gen_t *gcpu = cpu->gen; jit_op_t *iop; m_uint32_t cur_ia; u_char *jit_ptr; int i; /* Generate JIT opcodes */ for(tcb->ppc_trans_pos=0; tcb->ppc_trans_posppc_trans_pos++) { ppc32_op_emit_start(cpu,tcb); cur_ia = tcb->start_ia + (tcb->ppc_trans_pos << 2); if (ppc32_jit_tcb_get_target_bit(tcb,cur_ia)) ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_TARGET); #if DEBUG_INSN_PERF_CNT ppc32_inc_perf_counter(cpu); #endif #if BREAKPOINT_ENABLE if (cpu->breakpoints_enabled) insn_emit_breakpoint(cpu,tcb); #endif if (unlikely(!(tag = ppc32_jit_fetch_and_emit(cpu,tcb)))) { fprintf(stderr,"ppc32_op_gen_page: unable to fetch instruction.\n"); return(-1); } } /* * Mark the first instruction as a potential target, as well as the * current IA value. */ ppc32_op_emit_branch_target(cpu,tcb,tcb->start_ia); ppc32_op_emit_branch_target(cpu,tcb,cpu->ia); /* Optimize condition register and general registers */ ppc32_op_optimize(gcpu,tcb); /* Generate JIT code for each instruction in page */ for(i=0;ijit_ptr; /* Generate output code */ ppc32_op_gen_list(tcb,i,gcpu->jit_op_array[i],jit_ptr); /* Adjust the JIT buffer if its size is not sufficient */ ppc32_jit_tcb_adjust_buffer(cpu,tcb); } /* Apply patches and free opcodes */ for(i=0;ijit_op_array[i];iop;iop=iop->next) if (iop->opcode == JIT_OP_INSN_OUTPUT) ppc32_jit_tcb_apply_patches(cpu,tcb,iop); jit_op_free_list(gcpu,gcpu->jit_op_array[i]); gcpu->jit_op_array[i] = NULL; } /* Add end of page (returns to caller) */ ppc32_set_page_jump(cpu,tcb); /* Free patch tables */ ppc32_jit_tcb_free_patches(tcb); return(0); } /* ======================================================================== */ /* Compile a PowerPC instruction page */ static ppc32_jit_tcb_t * ppc32_jit_tcb_compile(cpu_ppc_t *cpu,m_uint32_t vaddr,m_uint32_t exec_state) { ppc32_jit_tcb_t *tcb; m_uint32_t page_addr; page_addr = vaddr & PPC32_MIN_PAGE_MASK; if (unlikely(!(tcb = ppc32_jit_tcb_create(cpu,page_addr,exec_state)))) { fprintf(stderr,"insn_page_compile: unable to create JIT block.\n"); return NULL; } /* Allocate the array used to convert PPC code ptr to native code ptr */ if (!(tcb->jit_insn_ptr = calloc(PPC32_INSN_PER_PAGE,sizeof(u_char *)))) { fprintf(stderr,"insn_page_compile: unable to create JIT mappings.\n"); goto error; } /* Compile the page */ if (ppc32_op_gen_page(cpu,tcb) == -1) { fprintf(stderr,"insn_page_compile: unable to compile page.\n"); goto error; } /* Add the block to the linked list */ tcb->next = cpu->tcb_list; tcb->prev = NULL; if (cpu->tcb_list) cpu->tcb_list->prev = tcb; else cpu->tcb_last = tcb; cpu->tcb_list = tcb; /* Add the block to the physical mapping hash table */ tcb->phys_next = cpu->tcb_phys_hash[tcb->phys_hash]; tcb->phys_pprev = &cpu->tcb_phys_hash[tcb->phys_hash]; if (cpu->tcb_phys_hash[tcb->phys_hash] != NULL) cpu->tcb_phys_hash[tcb->phys_hash]->phys_pprev = &tcb->phys_next; cpu->tcb_phys_hash[tcb->phys_hash] = tcb; cpu->compiled_pages++; return tcb; error: ppc32_jit_tcb_free(cpu,tcb,FALSE); return NULL; } /* Recompile a page */ int ppc32_jit_tcb_recompile(cpu_ppc_t *cpu,ppc32_jit_tcb_t *tcb) { #if 0 printf("PPC32-JIT: recompiling page 0x%8.8x\n",tcb->start_ia); #endif /* Free old code chunks */ ppc32_jit_tcb_free_code_chunks(cpu,tcb); /* Reset code ptr array */ memset(tcb->jit_insn_ptr,0,PPC32_INSN_PER_PAGE * sizeof(u_char *)); /* Allocate the first JIT buffer */ if (!(tcb->jit_buffer = exec_page_alloc(cpu))) return(-1); /* Disable flushing to avoid dangling pointers */ tcb->flags |= PPC32_JIT_TCB_FLAG_NO_FLUSH; /* Recompile the page */ if (ppc32_op_gen_page(cpu,tcb) == -1) { fprintf(stderr,"insn_page_compile: unable to recompile page.\n"); return(-1); } tcb->flags &= ~PPC32_JIT_TCB_FLAG_NO_FLUSH; tcb->target_undef_cnt = 0; return(0); } /* Run a compiled PowerPC instruction block */ static forced_inline void ppc32_jit_tcb_run(cpu_ppc_t *cpu,ppc32_jit_tcb_t *tcb) { if (unlikely(cpu->ia & 0x03)) { fprintf(stderr,"ppc32_jit_tcb_run: Invalid IA 0x%8.8x.\n",cpu->ia); ppc32_dump_regs(cpu->gen); ppc32_dump_mmu(cpu->gen); cpu_stop(cpu->gen); return; } /* Execute JIT compiled code */ ppc32_jit_tcb_exec(cpu,tcb); } /* Execute compiled PowerPC code */ void *ppc32_jit_run_cpu(cpu_gen_t *gen) { cpu_ppc_t *cpu = CPU_PPC32(gen); pthread_t timer_irq_thread; ppc32_jit_tcb_t *tcb; m_uint32_t hv,hp; m_uint32_t phys_page; int timer_irq_check = 0; ppc32_jit_init_hreg_mapping(cpu); if (pthread_create(&timer_irq_thread,NULL,(void *)ppc32_timer_irq_run,cpu)) { fprintf(stderr, "VM '%s': unable to create Timer IRQ thread for CPU%u.\n", cpu->vm->name,gen->id); cpu_stop(cpu->gen); return NULL; } gen->cpu_thread_running = TRUE; cpu_exec_loop_set(gen); start_cpu: gen->idle_count = 0; for(;;) { if (unlikely(gen->state != CPU_STATE_RUNNING)) break; #if DEBUG_BLOCK_PERF_CNT cpu->perf_counter++; #endif /* Handle virtual idle loop */ if (unlikely(cpu->ia == cpu->idle_pc)) { if (++gen->idle_count == gen->idle_max) { cpu_idle_loop(gen); gen->idle_count = 0; } } /* Handle the virtual CPU clock */ if (++timer_irq_check == cpu->timer_irq_check_itv) { timer_irq_check = 0; if (cpu->timer_irq_pending && !cpu->irq_disable && (cpu->msr & PPC32_MSR_EE)) { cpu->timer_irq_armed = 0; cpu->timer_irq_pending--; vm_set_irq(cpu->vm,0); } } /* Check IRQs */ if (unlikely(cpu->irq_check)) ppc32_trigger_irq(cpu); /* Get the JIT block corresponding to IA register */ hv = ppc32_jit_get_virt_hash(cpu->ia); tcb = cpu->tcb_virt_hash[hv]; if (unlikely(!tcb) || unlikely(!ppc32_jit_tcb_match(cpu,tcb))) { /* slow lookup: try to find the page by physical address */ cpu->translate(cpu,cpu->ia,PPC32_MTS_ICACHE,&phys_page); hp = ppc32_jit_get_phys_hash(phys_page); for(tcb=cpu->tcb_phys_hash[hp];tcb;tcb=tcb->phys_next) if (ppc32_jit_tcb_match(cpu,tcb)) goto tcb_found; /* the tcb doesn't exist, compile the page */ tcb = ppc32_jit_tcb_compile(cpu,cpu->ia,cpu->exec_state); if (unlikely(!tcb)) { fprintf(stderr, "VM '%s': unable to compile block for CPU%u IA=0x%8.8x\n", cpu->vm->name,gen->id,cpu->ia); cpu_stop(gen); break; } tcb_found: /* update the virtual hash table */ cpu->tcb_virt_hash[hv] = tcb; } #if DEBUG_BLOCK_TIMESTAMP tcb->tm_last_use = jit_jiffies++; #endif tcb->acc_count++; if (unlikely(tcb->flags & PPC32_JIT_TCB_FLAG_SMC)) ppc32_exec_page(cpu); else ppc32_jit_tcb_run(cpu,tcb); } if (!cpu->ia) { cpu_stop(gen); cpu_log(gen,"JIT","IA=0, halting CPU.\n"); } /* Check regularly if the CPU has been restarted */ while(gen->cpu_thread_running) { gen->seq_state++; switch(gen->state) { case CPU_STATE_RUNNING: gen->state = CPU_STATE_RUNNING; goto start_cpu; case CPU_STATE_HALTED: gen->cpu_thread_running = FALSE; pthread_join(timer_irq_thread,NULL); break; } /* CPU is paused */ usleep(200000); } return NULL; } dynamips-0.2.14/unstable/ppc32_jit.h000066400000000000000000000252561241034141600172130ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * PPC32 JIT compiler. */ #ifndef __PPC32_JIT_H__ #define __PPC32_JIT_H__ #include "utils.h" #include "sbox.h" /* Size of executable page area (in Mb) */ #ifndef __CYGWIN__ #define PPC_EXEC_AREA_SIZE 64 #else #define PPC_EXEC_AREA_SIZE 16 #endif /* Buffer size for JIT code generation */ #define PPC_JIT_BUFSIZE 32768 /* Maximum number of X86 chunks */ #define PPC_JIT_MAX_CHUNKS 64 /* Size of hash for virtual address lookup */ #define PPC_JIT_VIRT_HASH_BITS 17 #define PPC_JIT_VIRT_HASH_MASK ((1 << PPC_JIT_VIRT_HASH_BITS) - 1) #define PPC_JIT_VIRT_HASH_SIZE (1 << PPC_JIT_VIRT_HASH_BITS) /* Size of hash for physical lookup */ #define PPC_JIT_PHYS_HASH_BITS 16 #define PPC_JIT_PHYS_HASH_MASK ((1 << PPC_JIT_PHYS_HASH_BITS) - 1) #define PPC_JIT_PHYS_HASH_SIZE (1 << PPC_JIT_PHYS_HASH_BITS) #define PPC_JIT_TARGET_BITMAP_INDEX(x) (((x) >> 7) & 0x1F) #define PPC_JIT_TARGET_BITMAP_POS(x) (((x) >> 2) & 0x1F) /* Instruction jump patch */ struct ppc32_insn_patch { struct ppc32_insn_patch *next; u_char *jit_insn; m_uint32_t ppc_ia; }; /* Instruction patch table */ #define PPC32_INSN_PATCH_TABLE_SIZE 32 struct ppc32_jit_patch_table { struct ppc32_jit_patch_table *next; struct ppc32_insn_patch patches[PPC32_INSN_PATCH_TABLE_SIZE]; u_int cur_patch; }; #define PPC32_JIT_TCB_FLAG_SMC 0x1 /* Self-modifying code */ #define PPC32_JIT_TCB_FLAG_NO_FLUSH 0x2 /* No flushing */ /* PPC32 translated code block */ struct ppc32_jit_tcb { u_int flags; m_uint32_t start_ia; m_uint32_t exec_state; u_char **jit_insn_ptr; m_uint64_t acc_count; ppc_insn_t *ppc_code; u_int ppc_trans_pos; u_int jit_chunk_pos; u_char *jit_ptr; insn_exec_page_t *jit_buffer; insn_exec_page_t *jit_chunks[PPC_JIT_MAX_CHUNKS]; struct ppc32_jit_patch_table *patch_table; ppc32_jit_tcb_t *prev,*next; /* Physical page information */ m_uint32_t phys_page; m_uint32_t phys_hash; ppc32_jit_tcb_t **phys_pprev,*phys_next; /* 1024 instructions per page, one bit per instruction */ m_uint32_t target_bitmap[32]; m_uint32_t target_undef_cnt; #if DEBUG_BLOCK_TIMESTAMP m_uint64_t tm_first_use,tm_last_use; #endif }; /* PPC instruction recognition */ struct ppc32_insn_tag { int (*emit)(cpu_ppc_t *cpu,ppc32_jit_tcb_t *,ppc_insn_t); m_uint32_t mask,value; }; /* Mark the specified IA as a target for further recompiling */ static inline void ppc32_jit_tcb_set_target_bit(ppc32_jit_tcb_t *b,m_uint32_t ia) { int index,pos; index = PPC_JIT_TARGET_BITMAP_INDEX(ia); pos = PPC_JIT_TARGET_BITMAP_POS(ia); b->target_bitmap[index] |= 1 << pos; } /* Returns TRUE if the specified IA is in the target bitmap */ static inline int ppc32_jit_tcb_get_target_bit(ppc32_jit_tcb_t *b,m_uint32_t ia) { int index,pos; index = PPC_JIT_TARGET_BITMAP_INDEX(ia); pos = PPC_JIT_TARGET_BITMAP_POS(ia); return(b->target_bitmap[index] & (1 << pos)); } /* Get the JIT instruction pointer in a translated block */ static forced_inline u_char *ppc32_jit_tcb_get_host_ptr(ppc32_jit_tcb_t *tcb,m_uint32_t vaddr) { m_uint32_t offset; offset = (vaddr & PPC32_MIN_PAGE_IMASK) >> 2; return(tcb->jit_insn_ptr[offset]); } /* Check if the specified address belongs to the specified block */ static forced_inline int ppc32_jit_tcb_local_addr(ppc32_jit_tcb_t *tcb,m_uint32_t vaddr, u_char **jit_addr) { if ((vaddr & PPC32_MIN_PAGE_MASK) == tcb->start_ia) { *jit_addr = ppc32_jit_tcb_get_host_ptr(tcb,vaddr); return(1); } return(0); } /* Check if PC register matches the compiled block virtual address */ static forced_inline int ppc32_jit_tcb_match(cpu_ppc_t *cpu,ppc32_jit_tcb_t *tcb) { m_uint32_t vpage; vpage = cpu->ia & PPC32_MIN_PAGE_MASK; return((tcb->start_ia == vpage) && (tcb->exec_state == cpu->exec_state)); } /* Compute the hash index for the specified virtual address */ static forced_inline m_uint32_t ppc32_jit_get_virt_hash(m_uint32_t vaddr) { m_uint32_t page_hash; page_hash = sbox_u32(vaddr >> PPC32_MIN_PAGE_SHIFT); return((page_hash ^ (page_hash >> 14)) & PPC_JIT_VIRT_HASH_MASK); } /* Compute the hash index for the specified physical page */ static forced_inline m_uint32_t ppc32_jit_get_phys_hash(m_uint32_t phys_page) { m_uint32_t page_hash; page_hash = sbox_u32(phys_page); return((page_hash ^ (page_hash >> 12)) & PPC_JIT_PHYS_HASH_MASK); } /* Find a JIT block matching a physical page */ static inline ppc32_jit_tcb_t * ppc32_jit_find_by_phys_page(cpu_ppc_t *cpu,m_uint32_t phys_page) { m_uint32_t page_hash = ppc32_jit_get_phys_hash(phys_page); ppc32_jit_tcb_t *tcb; for(tcb=cpu->tcb_phys_hash[page_hash];tcb;tcb=tcb->phys_next) if (tcb->phys_page == phys_page) return tcb; return NULL; } /* ======================================================================== */ /* JIT emit operations (generic). */ /* ======================================================================== */ /* Indicate registers modified by ppc32_update_cr() functions */ extern void ppc32_update_cr_set_altered_hreg(cpu_ppc_t *cpu); /* Set opcode */ static inline void ppc32_op_set(cpu_ppc_t *cpu,jit_op_t *op) { cpu_gen_t *c = cpu->gen; *c->jit_op_current = op; c->jit_op_current = &op->next; } /* EMIT_BASIC_OPCODE */ static inline void ppc32_op_emit_basic_opcode(cpu_ppc_t *cpu,u_int opcode) { jit_op_t *op = jit_op_get(cpu->gen,0,opcode); ppc32_op_set(cpu,op); } /* Trash the specified host register */ static inline void ppc32_op_emit_alter_host_reg(cpu_ppc_t *cpu,int host_reg) { jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_ALTER_HOST_REG); op->param[0] = host_reg; ppc32_op_set(cpu,op); } /* EMIT_INSN_OUTPUT */ static inline jit_op_t * ppc32_op_emit_insn_output(cpu_ppc_t *cpu,u_int size_index,char *insn_name) { jit_op_t *op = jit_op_get(cpu->gen,size_index,JIT_OP_INSN_OUTPUT); op->arg_ptr = NULL; op->insn_name = insn_name; ppc32_op_set(cpu,op); return op; } /* EMIT_LOAD_GPR */ static inline void ppc32_op_emit_load_gpr(cpu_ppc_t *cpu,int host_reg,int ppc_reg) { jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_LOAD_GPR); op->param[0] = host_reg; op->param[1] = ppc_reg; op->param[2] = host_reg; ppc32_op_set(cpu,op); } /* EMIT_STORE_GPR */ static inline void ppc32_op_emit_store_gpr(cpu_ppc_t *cpu,int ppc_reg,int host_reg) { jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_STORE_GPR); op->param[0] = host_reg; op->param[1] = ppc_reg; op->param[2] = host_reg; ppc32_op_set(cpu,op); } /* EMIT_UPDATE_FLAGS */ static inline void ppc32_op_emit_update_flags(cpu_ppc_t *cpu,int field,int is_signed) { jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_UPDATE_FLAGS); op->param[0] = field; op->param[1] = is_signed; ppc32_op_set(cpu,op); ppc32_update_cr_set_altered_hreg(cpu); } /* EMIT_REQUIRE_FLAGS */ static inline void ppc32_op_emit_require_flags(cpu_ppc_t *cpu,int field) { jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_REQUIRE_FLAGS); op->param[0] = field; ppc32_op_set(cpu,op); } /* EMIT_BRANCH_TARGET */ static inline void ppc32_op_emit_branch_target(cpu_ppc_t *cpu, ppc32_jit_tcb_t *b, m_uint32_t ia) { if ((ia & PPC32_MIN_PAGE_MASK) == b->start_ia) { cpu_gen_t *c = cpu->gen; jit_op_t *op = jit_op_get(c,0,JIT_OP_BRANCH_TARGET); u_int pos = (ia & PPC32_MIN_PAGE_IMASK) >> 2; /* Insert in head */ op->next = c->jit_op_array[pos]; c->jit_op_array[pos] = op; } } /* EMIT_SET_HOST_REG_IMM32 */ static inline void ppc32_op_emit_set_host_reg_imm32(cpu_ppc_t *cpu,int reg,m_uint32_t val) { jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_SET_HOST_REG_IMM32); op->param[0] = reg; op->param[1] = val; ppc32_op_set(cpu,op); } /* ======================================================================== */ /* JIT operations with implementations specific to target CPU */ void ppc32_op_insn_output(ppc32_jit_tcb_t *b,jit_op_t *op); void ppc32_op_load_gpr(ppc32_jit_tcb_t *b,jit_op_t *op); void ppc32_op_store_gpr(ppc32_jit_tcb_t *b,jit_op_t *op); void ppc32_op_update_flags(ppc32_jit_tcb_t *b,jit_op_t *op); void ppc32_op_move_host_reg(ppc32_jit_tcb_t *b,jit_op_t *op); void ppc32_op_set_host_reg_imm32(ppc32_jit_tcb_t *b,jit_op_t *op); /* Set the Instruction Address (IA) register */ void ppc32_set_ia(u_char **ptr,m_uint32_t new_ia); /* Jump to the next page */ void ppc32_set_page_jump(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b); /* Increment the number of executed instructions (performance debugging) */ void ppc32_inc_perf_counter(cpu_ppc_t *cpu); /* ======================================================================== */ /* Virtual Breakpoint */ void ppc32_emit_breakpoint(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b); /* Initialize instruction lookup table */ void ppc32_jit_create_ilt(void); /* Initialize the JIT structure */ int ppc32_jit_init(cpu_ppc_t *cpu); /* Flush the JIT */ u_int ppc32_jit_flush(cpu_ppc_t *cpu,u_int threshold); /* Shutdown the JIT */ void ppc32_jit_shutdown(cpu_ppc_t *cpu); /* Fetch a PowerPC instruction and emit corresponding translated code */ struct ppc32_insn_tag *ppc32_jit_fetch_and_emit(cpu_ppc_t *cpu, ppc32_jit_tcb_t *block); /* Record a patch to apply in a compiled block */ int ppc32_jit_tcb_record_patch(ppc32_jit_tcb_t *block,jit_op_t *iop, u_char *jit_ptr,m_uint32_t vaddr); /* Mark a block as containing self-modifying code */ void ppc32_jit_mark_smc(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block); /* Free an instruction block */ void ppc32_jit_tcb_free(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block, int list_removal); /* Check if the specified address belongs to the specified block */ int ppc32_jit_tcb_local_addr(ppc32_jit_tcb_t *block,m_uint32_t vaddr, u_char **jit_addr); /* Recompile a page */ int ppc32_jit_tcb_recompile(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block); /* Execute compiled PowerPC code */ void *ppc32_jit_run_cpu(cpu_gen_t *gen); /* Start register allocation sequence */ void ppc32_jit_start_hreg_seq(cpu_ppc_t *cpu,char *insn); /* Close register allocation sequence */ void ppc32_jit_close_hreg_seq(cpu_ppc_t *cpu); /* Insert a reg map as head of list (as MRU element) */ void ppc32_jit_insert_hreg_mru(cpu_ppc_t *cpu,struct hreg_map *map); /* Allocate an host register */ int ppc32_jit_alloc_hreg(cpu_ppc_t *cpu,int ppc_reg); /* Force allocation of an host register */ int ppc32_jit_alloc_hreg_forced(cpu_ppc_t *cpu,int hreg); /* Initialize register mapping */ void ppc32_jit_init_hreg_mapping(cpu_ppc_t *cpu); #endif dynamips-0.2.14/unstable/ppc32_mem.c000066400000000000000000000720101241034141600171640ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * PowerPC MMU. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "ppc32_jit.h" #define DEBUG_ICBI 0 /* Memory access with special access mask */ void ppc32_access_special(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int cid, m_uint32_t mask,u_int op_code,u_int op_type, u_int op_size,m_uint64_t *data) { switch(mask) { case MTS_ACC_T: if (op_code != PPC_MEMOP_LOOKUP) { #if DEBUG_MTS_ACC_T cpu_log(cpu->gen, "MTS","MMU exception for address 0x%8.8x at ia=0x%8.8x " "(%s access, size=%u)\n", vaddr,cpu->ia,(op_type == MTS_READ) ? "read":"write",op_size); //ppc32_dump_regs(cpu->gen); #if MEMLOG_ENABLE memlog_dump(cpu->gen); #endif #endif if (cid == PPC32_MTS_DCACHE) { cpu->dsisr = PPC32_DSISR_NOTRANS; if (op_type == MTS_WRITE) cpu->dsisr |= PPC32_DSISR_STORE; cpu->dar = vaddr; ppc32_trigger_exception(cpu,PPC32_EXC_DSI); cpu_exec_loop_enter(cpu->gen); } else { ppc32_trigger_exception(cpu,PPC32_EXC_ISI); cpu_exec_loop_enter(cpu->gen); } } break; case MTS_ACC_U: if (op_type == MTS_READ) *data = 0; if (cpu->gen->undef_mem_handler != NULL) { if (cpu->gen->undef_mem_handler(cpu->gen,(m_uint64_t)vaddr, op_size,op_type,data)) return; } #if DEBUG_MTS_ACC_U if (op_type == MTS_READ) cpu_log(cpu->gen, "MTS","read access to undefined address 0x%8.8x at " "ia=0x%8.8x (size=%u)\n",vaddr,cpu->ia,op_size); else cpu_log(cpu->gen, "MTS","write access to undefined address 0x%8.8x at " "ia=0x%8.8x, value=0x%8.8llx (size=%u)\n", vaddr,cpu->ia,*data,op_size); #endif break; } } /* Initialize the MTS subsystem for the specified CPU */ int ppc32_mem_init(cpu_ppc_t *cpu) { size_t len; /* Initialize the cache entries to 0 (empty) */ len = MTS32_HASH_SIZE * sizeof(mts32_entry_t); if (!(cpu->mts_cache[PPC32_MTS_ICACHE] = malloc(len))) return(-1); if (!(cpu->mts_cache[PPC32_MTS_DCACHE] = malloc(len))) return(-1); memset(cpu->mts_cache[PPC32_MTS_ICACHE],0xFF,len); memset(cpu->mts_cache[PPC32_MTS_DCACHE],0xFF,len); cpu->mts_lookups = 0; cpu->mts_misses = 0; return(0); } /* Free memory used by MTS */ void ppc32_mem_shutdown(cpu_ppc_t *cpu) { if (cpu != NULL) { /* Free the caches themselves */ free(cpu->mts_cache[PPC32_MTS_ICACHE]); free(cpu->mts_cache[PPC32_MTS_DCACHE]); cpu->mts_cache[PPC32_MTS_ICACHE] = NULL; cpu->mts_cache[PPC32_MTS_DCACHE] = NULL; } } /* Show MTS detailed information (debugging only!) */ void ppc32_mem_show_stats(cpu_gen_t *gen_cpu) { cpu_ppc_t *cpu = CPU_PPC32(gen_cpu); #if DEBUG_MTS_MAP_VIRT mts32_entry_t *entry; u_int i,count; #endif printf("\nCPU%u: MTS statistics:\n",cpu->gen->id); #if DEBUG_MTS_MAP_VIRT printf("Instruction cache:\n"); /* Valid hash entries for Instruction Cache */ for(count=0,i=0;imts_cache[PPC32_MTS_ICACHE][i]; if (!(entry->gvpa & MTS_INV_ENTRY_MASK)) { printf(" %4u: vaddr=0x%8.8x, paddr=0x%8.8x, hpa=%p\n", i,entry->gvpa,entry->gppa,(void *)entry->hpa); count++; } } printf(" %u/%u valid hash entries for icache.\n",count,MTS32_HASH_SIZE); printf("Data cache:\n"); /* Valid hash entries for Instruction Cache */ for(count=0,i=0;imts_cache[PPC32_MTS_DCACHE][i]; if (!(entry->gvpa & MTS_INV_ENTRY_MASK)) { printf(" %4u: vaddr=0x%8.8x, paddr=0x%8.8x, hpa=%p\n", i,entry->gvpa,entry->gppa,(void *)entry->hpa); count++; } } printf(" %u/%u valid hash entries for dcache.\n",count,MTS32_HASH_SIZE); #endif printf("\n Total lookups: %llu, misses: %llu, efficiency: %g%%\n", cpu->mts_lookups, cpu->mts_misses, 100 - ((double)(cpu->mts_misses*100)/ (double)cpu->mts_lookups)); } /* Invalidate the MTS caches (instruction and data) */ void ppc32_mem_invalidate_cache(cpu_ppc_t *cpu) { size_t len; len = MTS32_HASH_SIZE * sizeof(mts32_entry_t); memset(cpu->mts_cache[PPC32_MTS_ICACHE],0xFF,len); memset(cpu->mts_cache[PPC32_MTS_DCACHE],0xFF,len); } /* * MTS mapping. * * It is NOT inlined since it triggers a GCC bug on my config (x86, GCC 3.3.5) */ static no_inline struct mts32_entry * ppc32_mem_map(cpu_ppc_t *cpu,u_int op_type,mts_map_t *map, mts32_entry_t *entry,mts32_entry_t *alt_entry) { ppc32_jit_tcb_t *tcb; struct vdevice *dev; m_uint32_t offset; m_iptr_t host_ptr; m_uint32_t exec_flag = 0; int cow; if (!(dev = dev_lookup(cpu->vm,map->paddr+map->offset,map->cached))) return NULL; if (cpu->tcb_phys_hash != NULL) { tcb = ppc32_jit_find_by_phys_page(cpu,map->paddr >> VM_PAGE_SHIFT); if ((tcb != NULL) && !(tcb->flags & PPC32_JIT_TCB_FLAG_SMC)) exec_flag = MTS_FLAG_EXEC; } if (dev->flags & VDEVICE_FLAG_SPARSE) { host_ptr = dev_sparse_get_host_addr(cpu->vm,dev,map->paddr, op_type,&cow); entry->gvpa = map->vaddr; entry->gppa = map->paddr; entry->hpa = host_ptr; entry->flags = (cow) ? MTS_FLAG_COW : 0; entry->flags |= exec_flag; return entry; } if (!dev->host_addr || (dev->flags & VDEVICE_FLAG_NO_MTS_MMAP)) { offset = (map->paddr + map->offset) - dev->phys_addr; /* device entries are never stored in virtual TLB */ alt_entry->gppa = dev->id; alt_entry->hpa = offset; alt_entry->flags = MTS_FLAG_DEV; return alt_entry; } entry->gvpa = map->vaddr; entry->gppa = map->paddr; entry->hpa = dev->host_addr + (map->paddr - dev->phys_addr); entry->flags = exec_flag; return entry; } /* BAT lookup */ static forced_inline int ppc32_bat_lookup(cpu_ppc_t *cpu,m_uint32_t vaddr, u_int cid,mts_map_t *map) { m_uint32_t bepi,mask,bl,pr,ubat; int i; pr = (cpu->msr & PPC32_MSR_PR) >> PPC32_MSR_PR_SHIFT; pr = ((~pr << 1) | pr) & 0x03; for(i=0;ibat[cid][i].reg[0]; if (!(ubat & pr)) continue; //bl = (ubat & PPC32_UBAT_BL_MASK) >> PPC32_UBAT_BL_SHIFT; bl = (ubat & PPC32_UBAT_XBL_MASK) >> PPC32_UBAT_XBL_SHIFT; mask = ~bl << PPC32_BAT_ADDR_SHIFT; bepi = ubat & PPC32_UBAT_BEPI_MASK; if (bepi == (vaddr & mask)) { map->vaddr = vaddr & PPC32_MIN_PAGE_MASK; map->paddr = cpu->bat[cid][i].reg[1] & PPC32_LBAT_BRPN_MASK; map->paddr += map->vaddr - bepi; map->offset = vaddr & PPC32_MIN_PAGE_IMASK; map->cached = FALSE; return(TRUE); } } return(FALSE); } /* Memory slow lookup */ static mts32_entry_t *ppc32_slow_lookup(cpu_ppc_t *cpu,m_uint32_t vaddr, u_int cid,u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data, mts32_entry_t *alt_entry) { m_uint32_t hash_bucket,segment,vsid; m_uint32_t hash,tmp,pteg_offset,pte_key,key,pte2; mts32_entry_t *entry; m_uint8_t *pte_haddr; m_uint64_t paddr; mts_map_t map; int i; #if DEBUG_MTS_STATS cpu->mts_misses++; #endif hash_bucket = MTS32_HASH(vaddr); entry = &cpu->mts_cache[cid][hash_bucket]; /* No translation - cover the 4GB space */ if (((cid == PPC32_MTS_ICACHE) && !(cpu->msr & PPC32_MSR_IR)) || ((cid == PPC32_MTS_DCACHE) && !(cpu->msr & PPC32_MSR_DR))) { map.vaddr = vaddr & PPC32_MIN_PAGE_MASK; map.paddr = vaddr & PPC32_MIN_PAGE_MASK; map.offset = vaddr & PPC32_MIN_PAGE_IMASK; map.cached = FALSE; if (!(entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return entry; } /* Walk through the BAT registers */ if (ppc32_bat_lookup(cpu,vaddr,cid,&map)) { if (!(entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return entry; } if (unlikely(!cpu->sdr1)) goto no_pte; /* Get the virtual segment identifier */ segment = vaddr >> 28; vsid = cpu->sr[segment] & PPC32_SD_VSID_MASK; /* Compute the first hash value */ hash = (vaddr >> PPC32_MIN_PAGE_SHIFT) & 0xFFFF; hash ^= vsid; hash &= 0x7FFFFF; tmp = (hash >> 10) & (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK); pteg_offset = (hash & 0x3FF) << 6; pteg_offset |= tmp << 16; pte_haddr = cpu->sdr1_hptr + pteg_offset; pte_key = 0x80000000 | (vsid << 7); pte_key |= (vaddr >> 22) & 0x3F; for(i=0;i<8;i++,pte_haddr+=PPC32_PTE_SIZE) { key = vmtoh32(*(m_uint32_t *)pte_haddr); if (key == pte_key) goto pte_lookup_done; } /* Secondary hash value */ hash = (~hash) & 0x7FFFFF; tmp = (hash >> 10) & (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK); pteg_offset = (hash & 0x3FF) << 6; pteg_offset |= tmp << 16; pte_haddr = cpu->sdr1_hptr + pteg_offset; pte_key = 0x80000040 | (vsid << 7); pte_key |= (vaddr >> 22) & 0x3F; for(i=0;i<8;i++,pte_haddr+=PPC32_PTE_SIZE) { key = vmtoh32(*(m_uint32_t *)pte_haddr); if (key == pte_key) goto pte_lookup_done; } no_pte: /* No matching PTE for this virtual address */ ppc32_access_special(cpu,vaddr,cid,MTS_ACC_T,op_code,op_type,op_size,data); return NULL; pte_lookup_done: pte2 = vmtoh32(*(m_uint32_t *)(pte_haddr + sizeof(m_uint32_t))); paddr = pte2 & PPC32_PTEL_RPN_MASK; paddr |= (pte2 & PPC32_PTEL_XPN_MASK) << (33 - PPC32_PTEL_XPN_SHIFT); paddr |= (pte2 & PPC32_PTEL_X_MASK) << (32 - PPC32_PTEL_X_SHIFT); map.vaddr = vaddr & ~PPC32_MIN_PAGE_IMASK; map.paddr = paddr; map.offset = vaddr & PPC32_MIN_PAGE_IMASK; map.cached = FALSE; if ((entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) return entry; err_undef: ppc32_access_special(cpu,vaddr,cid,MTS_ACC_U,op_code,op_type,op_size,data); return NULL; } /* Invalidate TCB related to a physical page marked as executable */ static void ppc32_mem_invalidate_tcb(cpu_ppc_t *cpu,mts32_entry_t *entry) { ppc32_jit_tcb_t *tcb,**tcbp,*tcb_next; m_uint32_t hp,hv,phys_page,ia_phys_page; if (cpu->tcb_phys_hash == NULL) return; phys_page = entry->gppa >> VM_PAGE_SHIFT; hp = ppc32_jit_get_phys_hash(phys_page); cpu->translate(cpu,cpu->ia,PPC32_MTS_ICACHE,&ia_phys_page); if (phys_page != ia_phys_page) { /* Clear all TCB matching the physical page being modified */ for(tcbp=&cpu->tcb_phys_hash[hp];*tcbp;) if ((*tcbp)->phys_page == phys_page) { hv = ppc32_jit_get_virt_hash((*tcbp)->start_ia); if (cpu->tcb_virt_hash[hv] == *tcbp) cpu->tcb_virt_hash[hv] = NULL; tcb_next = (*tcbp)->phys_next; ppc32_jit_tcb_free(cpu,(*tcbp),TRUE); *tcbp = tcb_next; } else { tcbp = &(*tcbp)->phys_next; } entry->flags &= ~MTS_FLAG_EXEC; } else { /* Self-modifying page */ for(tcb=cpu->tcb_phys_hash[hp];tcb;tcb=tcb->phys_next) if (tcb->phys_page == phys_page) ppc32_jit_mark_smc(cpu,tcb); entry->flags &= ~MTS_FLAG_EXEC; cpu_exec_loop_enter(cpu->gen); } } /* Memory access */ static inline void *ppc32_mem_access(cpu_ppc_t *cpu,m_uint32_t vaddr, u_int cid,u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data) { mts32_entry_t *entry,alt_entry; m_uint32_t hash_bucket; m_iptr_t haddr; u_int dev_id; int cow; #if MEMLOG_ENABLE /* Record the memory access */ memlog_rec_access(cpu->gen,vaddr,*data,op_size,op_type); #endif hash_bucket = MTS32_HASH(vaddr); entry = &cpu->mts_cache[cid][hash_bucket]; #if DEBUG_MTS_STATS cpu->mts_lookups++; #endif /* Copy-On-Write for sparse device ? */ cow = (op_type == MTS_WRITE) && (entry->flags & MTS_FLAG_COW); /* Slow lookup if nothing found in cache */ if (unlikely(((vaddr & PPC32_MIN_PAGE_MASK) != entry->gvpa) || cow)) { entry = cpu->mts_slow_lookup(cpu,vaddr,cid,op_code,op_size,op_type, data,&alt_entry); if (!entry) return NULL; if (entry->flags & MTS_FLAG_DEV) { dev_id = entry->gppa; haddr = entry->hpa; return(dev_access_fast(cpu->gen,dev_id,haddr,op_size,op_type,data)); } } /* Invalidate JIT code for written pages */ if ((op_type == MTS_WRITE) && (entry->flags & MTS_FLAG_EXEC)) ppc32_mem_invalidate_tcb(cpu,entry); /* Raw memory access */ haddr = entry->hpa + (vaddr & PPC32_MIN_PAGE_IMASK); #if MEMLOG_ENABLE memlog_update_read(cpu->gen,haddr); #endif return((void *)haddr); } /* Memory data access */ #define PPC32_MEM_DACCESS(cpu,vaddr,op_code,op_size,op_type,data) \ ppc32_mem_access((cpu),(vaddr),PPC32_MTS_DCACHE,(op_code),(op_size),\ (op_type),(data)) /* Virtual address to physical page translation */ static fastcall int ppc32_translate(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int cid, m_uint32_t *phys_page) { mts32_entry_t *entry,alt_entry; m_uint32_t hash_bucket; m_uint64_t data = 0; hash_bucket = MTS32_HASH(vaddr); entry = &cpu->mts_cache[cid][hash_bucket]; /* Slow lookup if nothing found in cache */ if (unlikely((vaddr & PPC32_MIN_PAGE_MASK) != entry->gvpa)) { entry = cpu->mts_slow_lookup(cpu,vaddr,cid,PPC_MEMOP_LOOKUP,4,MTS_READ, &data,&alt_entry); if (!entry) return(-1); } *phys_page = entry->gppa >> PPC32_MIN_PAGE_SHIFT; return(0); } /* Virtual address lookup */ static void *ppc32_mem_lookup(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int cid) { m_uint64_t data; return(ppc32_mem_access(cpu,vaddr,cid,PPC_MEMOP_LOOKUP,4,MTS_READ,&data)); } /* Instruction fetch */ static void *ppc32_mem_ifetch(cpu_ppc_t *cpu,m_uint32_t vaddr) { m_uint64_t data; return(ppc32_mem_access(cpu,vaddr,PPC32_MTS_ICACHE, PPC_MEMOP_IFETCH,4,MTS_READ,&data)); } /* Set a BAT register */ int ppc32_set_bat(cpu_ppc_t *cpu,struct ppc32_bat_prog *bp) { struct ppc32_bat_reg *bat; if ((bp->type != PPC32_IBAT_IDX) && (bp->type != PPC32_DBAT_IDX)) return(-1); if (bp->index >= PPC32_BAT_NR) return(-1); bat = &cpu->bat[bp->type][bp->index]; bat->reg[0] = bp->hi; bat->reg[1] = bp->lo; return(0); } /* Load BAT registers from a BAT array */ void ppc32_load_bat_array(cpu_ppc_t *cpu,struct ppc32_bat_prog *bp) { while(bp->index != -1) { ppc32_set_bat(cpu,bp); bp++; } } /* Get the host address for SDR1 */ int ppc32_set_sdr1(cpu_ppc_t *cpu,m_uint32_t sdr1) { struct vdevice *dev; m_uint64_t pt_addr; cpu->sdr1 = sdr1; pt_addr = sdr1 & PPC32_SDR1_HTABORG_MASK; pt_addr |= ((m_uint64_t)(sdr1 & PPC32_SDR1_HTABEXT_MASK) << 20); if (!(dev = dev_lookup(cpu->vm,pt_addr,TRUE))) { vm_error(cpu->vm, "ppc32_set_sdr1: unable to find haddr for SDR1=0x%8.8x\n", sdr1); return(-1); } cpu->sdr1_hptr = (char *)dev->host_addr + (pt_addr - dev->phys_addr); /* Flush the virtual TLB */ ppc32_mem_invalidate_cache(cpu); return(0); } /* Initialize the page table */ int ppc32_init_page_table(cpu_ppc_t *cpu) { m_uint32_t pt_size; if (!cpu->sdr1_hptr) return(-1); pt_size = (1 + (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK)) << 16; memset(cpu->sdr1_hptr,0,pt_size); return(0); } /* Map a page */ int ppc32_map_page(cpu_ppc_t *cpu,u_int vsid,m_uint32_t vaddr,m_uint64_t paddr, u_int wimg,u_int pp) { m_uint32_t hash,tmp,pteg_offset,key; m_uint8_t *pte_haddr; int i; /* Compute the first hash value */ hash = (vaddr >> PPC32_MIN_PAGE_SHIFT) & 0xFFFF; hash ^= vsid; hash &= 0x7FFFFF; tmp = (hash >> 10) & (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK); pteg_offset = (hash & 0x3FF) << 6; pteg_offset |= tmp << 16; pte_haddr = cpu->sdr1_hptr + pteg_offset; for(i=0;i<8;i++,pte_haddr+=PPC32_PTE_SIZE) { key = vmtoh32(*(m_uint32_t *)pte_haddr); if (!(key & PPC32_PTEU_V)) { hash = 0; goto free_pte_found; } } /* Secondary hash value */ hash = (~hash) & 0x7FFFFF; tmp = (hash >> 10) & (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK); pteg_offset = (hash & 0x3FF) << 6; pteg_offset |= tmp << 16; pte_haddr = cpu->sdr1_hptr + pteg_offset; for(i=0;i<8;i++,pte_haddr+=PPC32_PTE_SIZE) { key = vmtoh32(*(m_uint32_t *)pte_haddr); if (!(key & PPC32_PTEU_V)) { hash = PPC32_PTEU_H; goto free_pte_found; } } /* No free PTE found */ return(-1); free_pte_found: tmp = PPC32_PTEU_V | (vsid << PPC32_PTEU_VSID_SHIFT) | hash; tmp |= (vaddr >> 22) & 0x3F; *(m_uint32_t *)pte_haddr = htovm32(tmp); tmp = vaddr & PPC32_PTEL_RPN_MASK; tmp |= (vaddr >> (32 - PPC32_PTEL_X_SHIFT)) & PPC32_PTEL_X_MASK; tmp |= (vaddr >> (33 - PPC32_PTEL_XPN_SHIFT)) & PPC32_PTEL_XPN_MASK; tmp |= (wimg << PPC32_PTEL_WIMG_SHIFT) + pp; *(m_uint32_t *)(pte_haddr+sizeof(m_uint32_t)) = htovm32(tmp); return(0); } /* Map a memory zone */ int ppc32_map_zone(cpu_ppc_t *cpu,u_int vsid,m_uint32_t vaddr,m_uint64_t paddr, m_uint32_t size,u_int wimg,u_int pp) { while(size > 0) { if (ppc32_map_page(cpu,vsid,vaddr,paddr,wimg,pp) == -1) return(-1); size -= PPC32_MIN_PAGE_SIZE; vaddr += PPC32_MIN_PAGE_SIZE; paddr += PPC32_MIN_PAGE_SIZE; } return(0); } /* PowerPC 405 TLB masks */ static m_uint32_t ppc405_tlb_masks[8] = { 0xFFFFFC00, 0xFFFFF000, 0xFFFFC000, 0xFFFF0000, 0xFFFC0000, 0xFFF00000, 0xFFC00000, 0xFF000000, }; /* PowerPC 405 slow lookup */ static mts32_entry_t *ppc405_slow_lookup(cpu_ppc_t *cpu,m_uint32_t vaddr, u_int cid,u_int op_code,u_int op_size, u_int op_type,m_uint64_t *data, mts32_entry_t *alt_entry) { struct ppc405_tlb_entry *tlb_entry; m_uint32_t hash_bucket,mask; m_uint32_t page_size; mts32_entry_t *entry; mts_map_t map; int i; #if DEBUG_MTS_STATS cpu->mts_misses++; #endif hash_bucket = MTS32_HASH(vaddr); entry = &cpu->mts_cache[cid][hash_bucket]; /* No translation - cover the 4GB space */ if (((cid == PPC32_MTS_ICACHE) && !(cpu->msr & PPC32_MSR_IR)) || ((cid == PPC32_MTS_DCACHE) && !(cpu->msr & PPC32_MSR_DR))) { map.vaddr = vaddr & PPC32_MIN_PAGE_MASK; map.paddr = vaddr & PPC32_MIN_PAGE_MASK; map.cached = FALSE; if (!(entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return entry; } /* Walk through the unified TLB */ for(i=0;ippc405_tlb[i]; /* We want a valid entry with TID = PID */ if (!(tlb_entry->tlb_hi & PPC405_TLBHI_V) || (tlb_entry->tid != cpu->ppc405_pid)) continue; /* Get the address mask corresponding to this entry */ page_size = tlb_entry->tlb_hi & PPC405_TLBHI_SIZE_MASK; page_size >>= PPC405_TLBHI_SIZE_SHIFT; mask = ppc405_tlb_masks[page_size]; /* Matching entry ? */ if ((vaddr & mask) == (tlb_entry->tlb_hi & mask)) { map.vaddr = vaddr & mask; map.paddr = tlb_entry->tlb_lo & mask; map.cached = FALSE; if (!(entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) goto err_undef; return entry; } } /* No matching TLB entry for this virtual address */ ppc32_access_special(cpu,vaddr,cid,MTS_ACC_T,op_code,op_type,op_size,data); return NULL; err_undef: ppc32_access_special(cpu,vaddr,cid,MTS_ACC_U,op_code,op_type,op_size,data); return NULL; } /* Dump a PowerPC 405 TLB entry */ static void ppc405_dump_tlb_entry(cpu_ppc_t *cpu,u_int index) { struct ppc405_tlb_entry *entry; entry = &cpu->ppc405_tlb[index]; printf(" %2d: hi=0x%8.8x lo=0x%8.8x tid=0x%2.2x\n", index,entry->tlb_hi,entry->tlb_lo,entry->tid); } /* Dump the PowerPC 405 TLB */ static void ppc405_dump_tlb(cpu_gen_t *cpu) { cpu_ppc_t *pcpu = CPU_PPC32(cpu); u_int i; for(i=0;igpr[reg] = data & 0xFF; } /* LHZ: Load Half-Word Zero */ fastcall void ppc32_lhz(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LHZ,2,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh16(*(m_uint16_t *)haddr); cpu->gpr[reg] = data & 0xFFFF; } /* LWZ: Load Word Zero */ fastcall void ppc32_lwz(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LWZ,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); cpu->gpr[reg] = data; } /* LWBR: Load Word Byte Reverse */ fastcall void ppc32_lwbr(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LWBR,4,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); cpu->gpr[reg] = swap32(data); } /* LHA: Load Half-Word Algebraic */ fastcall void ppc32_lha(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LHZ,2,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh16(*(m_uint16_t *)haddr); cpu->gpr[reg] = sign_extend_32(data,16); } /* STB: Store Byte */ fastcall void ppc32_stb(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->gpr[reg] & 0xff; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STB,1,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint8_t *)haddr = data; } /* STH: Store Half-Word */ fastcall void ppc32_sth(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->gpr[reg] & 0xffff; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STH,2,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint16_t *)haddr = htovm16(data); } /* STW: Store Word */ fastcall void ppc32_stw(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->gpr[reg]; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STW,4,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint32_t *)haddr = htovm32(data); } /* STWBR: Store Word Byte Reversed */ fastcall void ppc32_stwbr(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = swap32(cpu->gpr[reg]); haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STWBR,4,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint32_t *)haddr = htovm32(data); } /* LSW: Load String Word */ fastcall void ppc32_lsw(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LSW,1,MTS_READ,&data); if (likely(haddr != NULL)) data = *(m_uint8_t *)haddr; cpu->gpr[reg] |= (data & 0xFF) << (24 - cpu->sw_pos); } /* STW: Store String Word */ fastcall void ppc32_stsw(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = (cpu->gpr[reg] >> (24 - cpu->sw_pos)) & 0xFF; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STSW,1,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint8_t *)haddr = data; } /* LFD: Load Floating-Point Double */ fastcall void ppc32_lfd(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LWZ,8,MTS_READ,&data); if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); cpu->fpu.reg[reg] = data; } /* STFD: Store Floating-Point Double */ fastcall void ppc32_stfd(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) { m_uint64_t data; void *haddr; data = cpu->fpu.reg[reg]; haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STW,8,MTS_WRITE,&data); if (likely(haddr != NULL)) *(m_uint64_t *)haddr = htovm64(data); } /* ICBI: Instruction Cache Block Invalidate */ fastcall void ppc32_icbi(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int op) { ppc32_jit_tcb_t *tcb; m_uint32_t phys_page; #if DEBUG_ICBI cpu_log(cpu->gen,"MTS","ICBI: ia=0x%8.8x, vaddr=0x%8.8x\n",cpu->ia,vaddr); #endif if (!cpu->translate(cpu,vaddr,PPC32_MTS_ICACHE,&phys_page)) { if (cpu->tcb_phys_hash) { tcb = ppc32_jit_find_by_phys_page(cpu,phys_page); if (tcb && (tcb->start_ia == (vaddr & PPC32_MIN_PAGE_MASK))) { #if DEBUG_ICBI cpu_log(cpu->gen,"MTS", "ICBI: removing compiled page at 0x%8.8x, pc=0x%8.8x\n", tcb->start_ia,cpu->ia); #endif ppc32_jit_tcb_free(cpu,tcb,TRUE); cpu->tcb_virt_hash[ppc32_jit_get_virt_hash(vaddr)] = NULL; } else { #if DEBUG_ICBI cpu_log(cpu->gen,"MTS", "ICBI: trying to remove page 0x%llx with pc=0x%llx\n", block->start_ia,cpu->ia); #endif } } } } /* ======================================================================== */ /* Get a BAT register pointer given a SPR index */ static inline m_uint32_t *ppc32_get_bat_spr_ptr(cpu_ppc_t *cpu,u_int spr) { m_uint32_t spr_cat,cid,index; spr_cat = spr >> 5; if ((spr_cat != 0x10) && (spr_cat != 0x11)) return NULL; cid = (spr >> 3) & 0x1; index = (spr >> 1) & 0x3; if (spr & 0x20) index += 4; //printf("GET_BAT_SPR: SPR=%u => cid=%u, index=%u\n",spr,cid,index); return(&cpu->bat[cid][index].reg[spr & 0x1]); } /* Get a BAT SPR */ m_uint32_t ppc32_get_bat_spr(cpu_ppc_t *cpu,u_int spr) { m_uint32_t *p; if (!(p = ppc32_get_bat_spr_ptr(cpu,spr))) return(0); return(*p); } /* Set a BAT SPR */ void ppc32_set_bat_spr(cpu_ppc_t *cpu,u_int spr,m_uint32_t val) { m_uint32_t *p; if ((p = ppc32_get_bat_spr_ptr(cpu,spr))) { *p = val; ppc32_mem_invalidate_cache(cpu); } } /* ======================================================================== */ /* Rebuild MTS data structures */ static void ppc32_mem_rebuild_mts(cpu_gen_t *gen_cpu) { ppc32_mem_invalidate_cache(CPU_PPC32(gen_cpu)); } /* Initialize memory access vectors */ void ppc32_init_memop_vectors(cpu_ppc_t *cpu) { /* MTS slow lookup */ cpu->mts_slow_lookup = ppc32_slow_lookup; /* MTS rebuild */ cpu->gen->mts_rebuild = ppc32_mem_rebuild_mts; /* MTS statistics */ cpu->gen->mts_show_stats = ppc32_mem_show_stats; /* Memory lookup and instruction fetch operations */ cpu->mem_op_lookup = ppc32_mem_lookup; cpu->mem_op_ifetch = ppc32_mem_ifetch; /* Translation operation */ cpu->translate = ppc32_translate; /* Load Operations */ cpu->mem_op_fn[PPC_MEMOP_LBZ] = ppc32_lbz; cpu->mem_op_fn[PPC_MEMOP_LHZ] = ppc32_lhz; cpu->mem_op_fn[PPC_MEMOP_LWZ] = ppc32_lwz; /* Load Operation with sign-extension */ cpu->mem_op_fn[PPC_MEMOP_LHA] = ppc32_lha; /* Store Operations */ cpu->mem_op_fn[PPC_MEMOP_STB] = ppc32_stb; cpu->mem_op_fn[PPC_MEMOP_STH] = ppc32_sth; cpu->mem_op_fn[PPC_MEMOP_STW] = ppc32_stw; /* Byte-Reversed operations */ cpu->mem_op_fn[PPC_MEMOP_LWBR] = ppc32_lwbr; cpu->mem_op_fn[PPC_MEMOP_STWBR] = ppc32_stwbr; /* String operations */ cpu->mem_op_fn[PPC_MEMOP_LSW] = ppc32_lsw; cpu->mem_op_fn[PPC_MEMOP_STSW] = ppc32_stsw; /* FPU operations */ cpu->mem_op_fn[PPC_MEMOP_LFD] = ppc32_lfd; cpu->mem_op_fn[PPC_MEMOP_STFD] = ppc32_stfd; /* ICBI - Instruction Cache Block Invalidate */ cpu->mem_op_fn[PPC_MEMOP_ICBI] = ppc32_icbi; } /* Restart the memory subsystem */ int ppc32_mem_restart(cpu_ppc_t *cpu) { m_uint32_t family; ppc32_mem_shutdown(cpu); ppc32_mem_init(cpu); ppc32_init_memop_vectors(cpu); /* Override the MTS lookup vector depending on the cpu type */ family = cpu->pvr & 0xFFFF0000; if (family == PPC32_PVR_405) { cpu->mts_slow_lookup = ppc405_slow_lookup; cpu->gen->mmu_dump = ppc405_dump_tlb; cpu->gen->mmu_raw_dump = ppc405_dump_tlb; } return(0); } dynamips-0.2.14/unstable/ppc32_microcode000077500000000000000000002427141241034141600201460ustar00rootroot00000000000000ELFÿð4Dd4 ( t@@ÿðÿðCÀCÀ8` <€ÿð8„ 8 tH.ñ8`<€ÿð8„8 H.Ý8` <€ÿð8„ 8 H.ÉH6•`LdLd}QC¦=@9J@‘ ‘*}B¦‘ }(¦‘* }¦‘ } &‘*=ÿð94D}¦N€!=@9J@ }ñ *}!¦ }¦ *JLd‹Ä›Ã8c8„8¥ÿÿ,@‚ÿèN€ |i¦8`8€8 |ñ |¦N€!N€ ˆ|uM‚ 9`|Y®}$Z9kˆ |u@‚ÿìN€ | +y| ¦L ˆ9`˜ˆ/€@¾N€ |X®|Y®}$X®/‰Mž 9kBÿèN€ ˆ|ix8`/€Mž 8c| ®/€@žÿôN€ |¦”!ÿÀ“¡4|}x“Á8|ž#xD“0“á<ˆ‘!(9!H|u8˜8¡Áá ‘$‘A,‘! A‚Œ= ÿð;‰B HT„>£ëxHùŒ|uA‚h/„%@žÿäŒ|t/€sAž|Ap/€%Až¨/€c@žÿ̉!+‰@œ´9i€U):™a} Jˆ‰£ëxH•Œ|u@‚ÿ €D8`ƒ0|¦ƒ¡4ƒÁ8ƒá<8!@N€ /€x@žÿh‰!+‰@œ@9i€U):™a} JƒéWé'>+‰ 8‰WA8‰0£ëxHWéG>+‰ 8‰WA8‰0£ëxHWég>+‰ 8‰WA8‰0£ëxHåWé‡>+‰ 8‰WA8‰0£ëxHÉWé§>+‰ 8‰WA8‰0£ëxH­WéÇ>+‰ 8‰WA8‰0£ëxH‘Wéç>+‰ 8‰WA8‰0£ëxHuWé>+‰ 8‰0@½þh8‰WKÿþ`‰!+‰@œ`9i€U):™a|€J€„/„AžX£ëxHIKÿþ0£ëx8€%HKÿþ ! 8  KÿþÌ! 8  KÿþX€ 8 €„/„@žÿ°„ãxKÿÿ¨ˆ|uA‚= ö TŒ|u@‚ÿô= ö8 X9`8`ÿÿ€ X/€Lž 4ÿÿ=ö9@| ¦@<€T|‰#x|t/€| Yî9k™IAžBÿà8= ö X8`N€ 8| ¦KÿÿÀˆ|uA‚= ö TŒ|u@‚ÿô8= ö X8`N€ +ƒ„|¦”!ÿà“¡|Ý3x“Á|æ;x“á|¾+x$|Ÿ#x@H<€ÿð|ex8„B˜çûxÈóx©ëx8`LÆ1‚KÿüI8`ÿÿ€$ƒ¡|¦ƒÁƒá8! N€ = ÿðT`:9)@}i.}kJ}i¦N€ = ö€ T Kÿÿ¼8@9`| ¦=@ö‘jLUi<9k€ P| û.Bÿì€$8`ƒ¡|¦ƒÁƒá8! N€ <€ÿð8`8„B|åûxLÆ1‚Kÿûˆ|uA‚= ö TŒ|u@‚ÿô8= ö X8`Kÿÿ(8`ÿÿKÿÿ = ÿð€iC¸Kÿÿ= €i@$Kÿú­8`Kÿÿ<€ÿðåûx8„B\8`LÆ1‚Kÿû)€$ãûxÄóx|¦¥ëxƒÁƒ¡ƒá8! Kÿýœ= ö€ $|øKÿþ°<€ÿð8`8„B(?àöLÆ1‚KÿúÙ?0/‰Až <€ÿð8`8„BDLÆ1‚Kÿú¹€(Kÿú8`Kÿþh= ö€i,Kÿþ\8` KÿþT8`T„>Hõ8`Kÿþ@= ö€ Hp €T^@¢þ,)D€ià/ƒ@¾þT^Kÿþ= ö8`iD‹àKÿþ= €i@ Kÿýô= €i@ Kÿù8`Kÿýà= €i@$KÿýÔ= ö€i$KÿýÈ= ö€ T Kÿý¸= ö€ TP*Kÿý¨= €i@(Kÿýœ= öiD8kKÿýŒ8= ö 48`Kÿýx8`€Kÿýp8})¦4N€!8`KÿýX/ƒAž= ö‰ #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "dev_rom.h" #include "pci_io.h" #include "dev_vtty.h" #include "registry.h" #include "net.h" #include "ppc32_mem.h" #include "ppc32_vmtest.h" static struct ppc32_bat_prog bat_array[] = { { PPC32_IBAT_IDX, 0, 0xfff0001e, 0xfff00001 }, { PPC32_IBAT_IDX, 1, 0x00001ffe, 0x00000001 }, { PPC32_IBAT_IDX, 2, 0x00000000, 0xee3e0072 }, { PPC32_IBAT_IDX, 3, 0x80001ffe, 0x80000001 }, { PPC32_DBAT_IDX, 0, 0x80001ffe, 0x80000042 }, { PPC32_DBAT_IDX, 1, 0x00001ffe, 0x0000002a }, { PPC32_DBAT_IDX, 2, 0x60001ffe, 0x6000002a }, { PPC32_DBAT_IDX, 3, 0xfc0007fe, 0xfc00002a }, { -1, -1, 0, 0 }, }; /* Create a new router instance */ static int ppc32_vmtest_create_instance(vm_instance_t *vm) { vm->ram_size = PPC32_VMTEST_DEFAULT_RAM_SIZE; return(0); } /* Free resources used by a test instance */ static int ppc32_vmtest_delete_instance(vm_instance_t *vm) { /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(FALSE); } } /* Free all resources used by VM */ vm_free(vm); return(TRUE); } /* Set IRQ line */ static void ppc32_vmtest_set_irq(vm_instance_t *vm,u_int irq) { cpu_ppc_t *cpu = CPU_PPC32(vm->boot_cpu); cpu->irq_check = cpu->irq_pending = TRUE; } /* Clear IRQ line */ static void ppc32_vmtest_clear_irq(vm_instance_t *vm,u_int irq) { cpu_ppc_t *cpu = CPU_PPC32(vm->boot_cpu); cpu->irq_check = cpu->irq_pending = FALSE; } /* Initialize the PPC32 VM test Platform */ static int ppc32_vmtest_init_platform(vm_instance_t *vm) { _maybe_used cpu_ppc_t *cpu0; cpu_gen_t *gen0; /* Create Console and AUX ports */ vm_init_vtty(vm); /* Create a CPU group */ vm->cpu_group = cpu_group_create("System CPU"); /* Initialize the virtual PowerPC processor */ if (!(gen0 = cpu_create(vm,CPU_TYPE_PPC32,0))) { vm_error(vm,"unable to create CPU0!\n"); return(-1); } cpu0 = CPU_PPC32(gen0); /* Enable as PowerPC 405 */ //ppc32_set_pvr(cpu0,PPC32_PVR_405 | 0x0102); /* Add this CPU to the system CPU group */ cpu_group_add(vm->cpu_group,gen0); vm->boot_cpu = gen0; /* Set IRQ vectors */ vm->set_irq = ppc32_vmtest_set_irq; vm->clear_irq = ppc32_vmtest_clear_irq; #if 0 { vm_obj_t *obj; /* Initialize ROM (as a Flash) */ if (!(obj = dev_flash_init(vm,"rom",0xFF000000,16*1048576))) return(-1); dev_flash_copy_data(obj,0x0F00000,ppc32_microcode,ppc32_microcode_len); } #endif //dev_bootflash_init(vm,"bootflash",0xFF000000,8*1048576); #if 1 /* Initialize ROM */ if (!vm->rom_filename) { /* use embedded ROM */ dev_rom_init(vm,"rom",0xFFF00000,512*1024, ppc32_microcode,ppc32_microcode_len); } else { /* use alternate ROM */ dev_ram_init(vm,"rom",TRUE,TRUE,NULL,FALSE, 0xFFF00000,512*1024); } #endif /* Remote emulator control */ dev_remote_control_init(vm,0xf6000000,0x1000); /* Initialize RAM */ vm_ram_init(vm,0x00000000); /* RAM aliasing */ dev_create_ram_alias(vm,"ram_alias","ram",0x80000000,vm->ram_size*1048576); /* Display the device list */ dev_show_list(vm); return(0); } /* Boot the RAW image */ _unused static int ppc32_vmtest_boot_raw(vm_instance_t *vm) { cpu_ppc_t *cpu; if (!vm->boot_cpu) return(-1); /* Suspend CPU activity since we will restart directly from ROM */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Reset the boot CPU */ cpu = CPU_PPC32(vm->boot_cpu); ppc32_reset(cpu); /* Load RAW image */ if (ppc32_load_raw_image(cpu,vm->ios_image,0xFFF00000) < 0) { vm_error(vm,"failed to load RAW image '%s'.\n",vm->ios_image); return(-1); } cpu->ia = 0xFFF00100; cpu->gpr[1] = 0x2000; /* Launch the simulation */ printf("\nPPC32_VMTEST '%s': starting simulation (CPU0 IA=0x%8.8x), " "JIT %sabled.\n", vm->name,cpu->ia,vm->jit_use ? "en":"dis"); vm_log(vm,"PPC32_VMTEST_BOOT", "starting instance (CPU0 IA=0x%8.8x,JIT %s)\n", cpu->ia,vm->jit_use ? "on":"off"); /* Start main CPU */ if (vm->ghost_status != VM_GHOST_RAM_GENERATE) { vm->status = VM_STATUS_RUNNING; cpu_start(vm->boot_cpu); } else { vm->status = VM_STATUS_SHUTDOWN; } return(0); } /* Boot the ELF image */ static int ppc32_vmtest_boot_elf(vm_instance_t *vm) { m_uint32_t rom_entry_point; cpu_ppc_t *cpu; if (!vm->boot_cpu) return(-1); /* Suspend CPU activity since we will restart directly from ROM */ vm_suspend(vm); /* Check that CPU activity is really suspended */ if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } /* Reset the boot CPU */ cpu = CPU_PPC32(vm->boot_cpu); ppc32_reset(cpu); /* Load ROM (ELF image or embedded) */ cpu = CPU_PPC32(vm->boot_cpu); rom_entry_point = (m_uint32_t)PPC32_ROM_START; if ((vm->rom_filename != NULL) && (ppc32_load_elf_image(cpu,vm->rom_filename,0,&rom_entry_point) < 0)) { vm_error(vm,"unable to load alternate ROM '%s', " "fallback to embedded ROM.\n\n",vm->rom_filename); vm->rom_filename = NULL; } /* Load ELF image */ if (ppc32_load_elf_image(cpu,vm->ios_image, (vm->ghost_status == VM_GHOST_RAM_USE), &vm->ios_entry_point) < 0) { vm_error(vm,"failed to load ELF image '%s'.\n",vm->ios_image); return(-1); } /* Launch the simulation */ printf("\nPPC32_VMTEST '%s': starting simulation (CPU0 IA=0x%8.8x), " "JIT %sabled.\n", vm->name,cpu->ia,vm->jit_use ? "en":"dis"); vm_log(vm,"PPC32_VMTEST_BOOT", "starting instance (CPU0 IA=0x%8.8x,JIT %s)\n", cpu->ia,vm->jit_use ? "on":"off"); /* Start main CPU */ if (vm->ghost_status != VM_GHOST_RAM_GENERATE) { vm->status = VM_STATUS_RUNNING; cpu_start(vm->boot_cpu); } else { vm->status = VM_STATUS_SHUTDOWN; } return(0); } /* Initialize a test instance */ static int ppc32_vmtest_init_instance(vm_instance_t *vm) { /* Initialize the test platform */ if (ppc32_vmtest_init_platform(vm) == -1) { vm_error(vm,"unable to initialize the platform hardware.\n"); return(-1); } /* Load BAT registers */ ppc32_load_bat_array(CPU_PPC32(vm->boot_cpu),bat_array); return(ppc32_vmtest_boot_elf(vm)); } /* Stop a test instance */ static int ppc32_vmtest_stop_instance(vm_instance_t *vm) { printf("\nPPC32_VMTEST '%s': stopping simulation.\n",vm->name); vm_log(vm,"PPC32_VMTEST_STOP","stopping simulation.\n"); /* Stop all CPUs */ if (vm->cpu_group != NULL) { vm_stop(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { vm_error(vm,"unable to sync with system CPUs.\n"); return(-1); } } /* Free resources that were used during execution to emulate hardware */ vm_hardware_shutdown(vm); return(0); } /* Platform definition */ static vm_platform_t ppc32_vmtest_platform = { "ppc32_test", "PPC32_VMTEST", "PPC32_TEST", ppc32_vmtest_create_instance, ppc32_vmtest_delete_instance, ppc32_vmtest_init_instance, ppc32_vmtest_stop_instance, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }; /* Register the ppc32_vmtest platform */ int ppc32_vmtest_platform_register(void) { return(vm_platform_register(&ppc32_vmtest_platform)); } dynamips-0.2.14/unstable/ppc32_vmtest.h000066400000000000000000000007361241034141600177430ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2006 Christophe Fillot (cf@utc.fr) * * PowerPC VM experimentations. */ #ifndef __PPC32_VMTEST_H__ #define __PPC32_VMTEST_H__ #include #include "utils.h" #include "net.h" #include "device.h" #include "pci_dev.h" #include "vm.h" /* Default parameters of the test VM */ #define PPC32_VMTEST_DEFAULT_RAM_SIZE 256 /* Register the ppc32_vmtest platform */ int ppc32_vmtest_platform_register(void); #endif dynamips-0.2.14/unstable/ppc32_x86_trans.c000066400000000000000000003262011241034141600202460ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #include #include #include #include #include #include #include #include "cpu.h" #include "jit_op.h" #include "ppc32_jit.h" #include "ppc32_x86_trans.h" #include "memory.h" /* %esp adjustment (for MacOS X) */ #define STACK_ADJUST 12 /* ======================================================================= */ /* Macros for CPU structure access */ #define REG_OFFSET(reg) (OFFSET(cpu_ppc_t,gpr[(reg)])) #define MEMOP_OFFSET(op) (OFFSET(cpu_ppc_t,mem_op_fn[(op)])) #define DECLARE_INSN(name) \ static int ppc32_emit_##name(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, \ ppc_insn_t insn) /* EFLAGS to Condition Register (CR) field - signed */ static m_uint32_t eflags_to_cr_signed[64] = { 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, 0x08, 0x02, 0x04, 0x02, }; /* EFLAGS to Condition Register (CR) field - unsigned */ static m_uint32_t eflags_to_cr_unsigned[256] = { 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, }; /* Emit unhandled instruction code */ static int ppc32_emit_unknown(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, ppc_insn_t opcode); /* Load a 32 bit immediate value */ static forced_inline void ppc32_load_imm(u_char **ptr,u_int reg,m_uint32_t val) { if (val) x86_mov_reg_imm(*ptr,reg,val); else x86_alu_reg_reg(*ptr,X86_XOR,reg,reg); } /* Set the Instruction Address (IA) register */ void ppc32_set_ia(u_char **ptr,m_uint32_t new_ia) { x86_mov_membase_imm(*ptr,X86_EDI,OFFSET(cpu_ppc_t,ia),new_ia,4); } /* Set the Link Register (LR) */ static void ppc32_set_lr(jit_op_t *iop,m_uint32_t new_lr) { x86_mov_membase_imm(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,lr),new_lr,4); } /* * Try to branch directly to the specified JIT block without returning to * main loop. */ static void ppc32_try_direct_far_jump(cpu_ppc_t *cpu,jit_op_t *iop, m_uint32_t new_ia) { m_uint32_t new_page,ia_hash,ia_offset; u_char *test1,*test2,*test3,*test4; /* Indicate that we throw %esi, %edx */ ppc32_op_emit_alter_host_reg(cpu,X86_ESI); ppc32_op_emit_alter_host_reg(cpu,X86_EDX); new_page = new_ia & PPC32_MIN_PAGE_MASK; ia_offset = (new_ia & PPC32_MIN_PAGE_IMASK) >> 2; ia_hash = ppc32_jit_get_virt_hash(new_ia); /* Get JIT block info in %edx */ x86_mov_reg_membase(iop->ob_ptr,X86_EBX, X86_EDI,OFFSET(cpu_ppc_t,tcb_virt_hash),4); x86_mov_reg_membase(iop->ob_ptr,X86_EDX,X86_EBX,ia_hash*sizeof(void *),4); /* no JIT block found ? */ x86_test_reg_reg(iop->ob_ptr,X86_EDX,X86_EDX); test1 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_Z, 0, 1); /* Check block IA */ x86_mov_reg_imm(iop->ob_ptr,X86_ESI,new_page); x86_alu_reg_membase(iop->ob_ptr,X86_CMP,X86_ESI,X86_EDX, OFFSET(ppc32_jit_tcb_t,start_ia)); test2 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_NE, 0, 1); /* Jump to the code */ x86_mov_reg_membase(iop->ob_ptr,X86_ESI, X86_EDX,OFFSET(ppc32_jit_tcb_t,jit_insn_ptr),4); x86_test_reg_reg(iop->ob_ptr,X86_ESI,X86_ESI); test3 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_Z, 0, 1); x86_mov_reg_membase(iop->ob_ptr,X86_EBX, X86_ESI,ia_offset * sizeof(void *),4); x86_test_reg_reg(iop->ob_ptr,X86_EBX,X86_EBX); test4 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_Z, 0, 1); x86_jump_reg(iop->ob_ptr,X86_EBX); /* Returns to caller... */ x86_patch(test1,iop->ob_ptr); x86_patch(test2,iop->ob_ptr); x86_patch(test3,iop->ob_ptr); x86_patch(test4,iop->ob_ptr); ppc32_set_ia(&iop->ob_ptr,new_ia); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); } /* Set Jump */ static void ppc32_set_jump(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b,jit_op_t *iop, m_uint32_t new_ia,int local_jump) { int return_to_caller = FALSE; u_char *jump_ptr; #if 0 if (cpu->sym_trace && !local_jump) return_to_caller = TRUE; #endif if (!return_to_caller && ppc32_jit_tcb_local_addr(b,new_ia,&jump_ptr)) { ppc32_jit_tcb_record_patch(b,iop,iop->ob_ptr,new_ia); x86_jump32(iop->ob_ptr,0); } else { if (cpu->exec_blk_direct_jump) { /* Block lookup optimization */ ppc32_try_direct_far_jump(cpu,iop,new_ia); } else { ppc32_set_ia(&iop->ob_ptr,new_ia); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); } } } /* Jump to the next page */ void ppc32_set_page_jump(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b) { jit_op_t *iop,*op_list = NULL; cpu->gen->jit_op_current = &op_list; iop = ppc32_op_emit_insn_output(cpu,4,"set_page_jump"); ppc32_set_jump(cpu,b,iop,b->start_ia + PPC32_MIN_PAGE_SIZE,FALSE); ppc32_op_insn_output(b,iop); jit_op_free_list(cpu->gen,op_list); cpu->gen->jit_op_current = NULL; } /* Load a GPR into the specified host register */ static forced_inline void ppc32_load_gpr(u_char **ptr,u_int host_reg, u_int ppc_reg) { x86_mov_reg_membase(*ptr,host_reg,X86_EDI,REG_OFFSET(ppc_reg),4); } /* Store contents for a host register into a GPR register */ static forced_inline void ppc32_store_gpr(u_char **ptr,u_int ppc_reg, u_int host_reg) { x86_mov_membase_reg(*ptr,X86_EDI,REG_OFFSET(ppc_reg),host_reg,4); } /* Apply an ALU operation on a GPR register and a host register */ static forced_inline void ppc32_alu_gpr(u_char **ptr,u_int op, u_int host_reg,u_int ppc_reg) { x86_alu_reg_membase(*ptr,op,host_reg,X86_EDI,REG_OFFSET(ppc_reg)); } /* * Update CR from %eflags * %eax, %edx, %esi are modified. */ static void ppc32_update_cr(ppc32_jit_tcb_t *b,int field,int is_signed) { /* Get status bits from EFLAGS */ if (!is_signed) { x86_mov_reg_imm(b->jit_ptr,X86_EAX,0); x86_lahf(b->jit_ptr); x86_xchg_ah_al(b->jit_ptr); x86_mov_reg_imm(b->jit_ptr,X86_EDX,eflags_to_cr_unsigned); } else { x86_pushfd(b->jit_ptr); x86_pop_reg(b->jit_ptr,X86_EAX); x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_EAX,6); x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_EAX,0x3F); x86_mov_reg_imm(b->jit_ptr,X86_EDX,eflags_to_cr_signed); } x86_mov_reg_memindex(b->jit_ptr,X86_EAX,X86_EDX,0,X86_EAX,2,4); /* Check XER Summary of Overflow and report it */ //x86_mov_reg_membase(b->jit_ptr,X86_EDX,X86_EDI,OFFSET(cpu_ppc_t,xer),4); //x86_alu_reg_imm(b->jit_ptr,X86_AND,X86_ESI,PPC32_XER_SO); //x86_shift_reg_imm(b->jit_ptr,X86_SHR,X86_ESI,PPC32_XER_SO_BIT); //x86_alu_reg_reg(b->jit_ptr,X86_OR,X86_EAX,X86_ESI); /* Store modified CR field */ x86_mov_membase_reg(b->jit_ptr,X86_EDI,PPC32_CR_FIELD_OFFSET(field), X86_EAX,4); } /* * Update CR0 from %eflags * %eax, %edx, %esi are modified. */ static void ppc32_update_cr0(ppc32_jit_tcb_t *b) { ppc32_update_cr(b,0,TRUE); } /* Indicate registers modified by ppc32_update_cr() functions */ void ppc32_update_cr_set_altered_hreg(cpu_ppc_t *cpu) { /* Throw %eax and %edx, which are modifed by ppc32_update_cr() */ ppc32_op_emit_alter_host_reg(cpu,X86_EAX); ppc32_op_emit_alter_host_reg(cpu,X86_EDX); } /* Basic C call */ static forced_inline void ppc32_emit_basic_c_call(u_char **ptr,void *f) { x86_mov_reg_imm(*ptr,X86_EBX,f); x86_call_reg(*ptr,X86_EBX); } /* Emit a simple call to a C function without any parameter */ static void ppc32_emit_c_call(ppc32_jit_tcb_t *b,jit_op_t *iop,void *f) { ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); ppc32_emit_basic_c_call(&iop->ob_ptr,f); } /* ======================================================================== */ /* Initialize register mapping */ void ppc32_jit_init_hreg_mapping(cpu_ppc_t *cpu) { int avail_hregs[] = { X86_ESI, X86_EAX, X86_ECX, X86_EDX, -1 }; struct hreg_map *map; int i,hreg; cpu->hreg_map_list = cpu->hreg_lru = NULL; /* Add the available registers to the map list */ for(i=0;avail_hregs[i]!=-1;i++) { hreg = avail_hregs[i]; map = &cpu->hreg_map[hreg]; /* Initialize mapping. At the beginning, no PPC reg is mapped */ map->flags = 0; map->hreg = hreg; map->vreg = -1; ppc32_jit_insert_hreg_mru(cpu,map); } /* Clear PPC registers mapping */ for(i=0;ippc_reg_map[i] = -1; } /* Allocate a specific temp register */ static int ppc32_jit_get_tmp_hreg(cpu_ppc_t *cpu) { return(X86_EBX); } /* ======================================================================== */ /* JIT operations (specific to target CPU). */ /* ======================================================================== */ /* INSN_OUTPUT */ void ppc32_op_insn_output(ppc32_jit_tcb_t *b,jit_op_t *op) { op->ob_final = b->jit_ptr; memcpy(b->jit_ptr,op->ob_data,op->ob_ptr - op->ob_data); b->jit_ptr += op->ob_ptr - op->ob_data; if ((op->ob_ptr - op->ob_data) >= jit_op_blk_sizes[op->ob_size_index]) { printf("ppc32_op_insn_output: FAILURE: count=%d, size=%d\n", op->ob_ptr - op->ob_data, jit_op_blk_sizes[op->ob_size_index]); } } /* LOAD_GPR: p[0] = %host_reg, p[1] = %ppc_reg */ void ppc32_op_load_gpr(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_load_gpr(&b->jit_ptr,op->param[0],op->param[1]); } /* STORE_GPR: p[0] = %host_reg, p[1] = %ppc_reg */ void ppc32_op_store_gpr(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_store_gpr(&b->jit_ptr,op->param[1],op->param[0]); } /* UPDATE_FLAGS: p[0] = cr_field, p[1] = is_signed */ void ppc32_op_update_flags(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_update_cr(b,op->param[0],op->param[1]); } /* MOVE_HOST_REG: p[0] = %host_dst_reg, p[1] = %host_src_reg */ void ppc32_op_move_host_reg(ppc32_jit_tcb_t *b,jit_op_t *op) { if ((op->param[0] != JIT_OP_INV_REG) && (op->param[1] != JIT_OP_INV_REG)) x86_mov_reg_reg(b->jit_ptr,op->param[0],op->param[1],4); } /* SET_HOST_REG_IMM32: p[0] = %host_reg, p[1] = imm32 */ void ppc32_op_set_host_reg_imm32(ppc32_jit_tcb_t *b,jit_op_t *op) { if (op->param[0] != JIT_OP_INV_REG) ppc32_load_imm(&b->jit_ptr,op->param[0],op->param[1]); } /* ======================================================================== */ /* Memory operation */ static void ppc32_emit_memop(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, int op,int base,int offset,int target,int update) { m_uint32_t val = sign_extend(offset,16); jit_op_t *iop; /* * Since an exception can be triggered, clear JIT state. This allows * to use branch target tag (we can directly branch on this instruction). */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_TARGET); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); iop = ppc32_op_emit_insn_output(cpu,5,"memop"); /* Save PC for exception handling */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* EDX = sign-extended offset */ ppc32_load_imm(&iop->ob_ptr,X86_EDX,val); /* EDX = GPR[base] + sign-extended offset */ if (update || (base != 0)) ppc32_alu_gpr(&iop->ob_ptr,X86_ADD,X86_EDX,base); if (update) x86_mov_reg_reg(iop->ob_ptr,X86_ESI,X86_EDX,4); /* ECX = target register */ x86_mov_reg_imm(iop->ob_ptr,X86_ECX,target); /* EAX = CPU instance pointer */ x86_mov_reg_reg(iop->ob_ptr,X86_EAX,X86_EDI,4); /* Call memory function */ x86_alu_reg_imm(iop->ob_ptr,X86_SUB,X86_ESP,STACK_ADJUST); x86_call_membase(iop->ob_ptr,X86_EDI,MEMOP_OFFSET(op)); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,X86_ESP,STACK_ADJUST); if (update) ppc32_store_gpr(&iop->ob_ptr,base,X86_ESI); } /* Memory operation (indexed) */ static void ppc32_emit_memop_idx(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, int op,int ra,int rb,int target,int update) { jit_op_t *iop; /* * Since an exception can be triggered, clear JIT state. This allows * to use branch target tag (we can directly branch on this instruction). */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_TARGET); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); iop = ppc32_op_emit_insn_output(cpu,5,"memop_idx"); /* Save PC for exception handling */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* EDX = $rb */ ppc32_load_gpr(&iop->ob_ptr,X86_EDX,rb); /* EDX = $rb + $ra */ if (update || (ra != 0)) ppc32_alu_gpr(&iop->ob_ptr,X86_ADD,X86_EDX,ra); if (update) x86_mov_reg_reg(iop->ob_ptr,X86_ESI,X86_EDX,4); /* ECX = target register */ x86_mov_reg_imm(iop->ob_ptr,X86_ECX,target); /* EAX = CPU instance pointer */ x86_mov_reg_reg(iop->ob_ptr,X86_EAX,X86_EDI,4); /* Call memory function */ x86_alu_reg_imm(iop->ob_ptr,X86_SUB,X86_ESP,STACK_ADJUST); x86_call_membase(iop->ob_ptr,X86_EDI,MEMOP_OFFSET(op)); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,X86_ESP,STACK_ADJUST); if (update) ppc32_store_gpr(&iop->ob_ptr,ra,X86_ESI); } typedef void (*memop_fast_access)(jit_op_t *iop,int target); /* Fast LBZ */ static void ppc32_memop_fast_lbz(jit_op_t *iop,int target) { x86_clear_reg(iop->ob_ptr,X86_ECX); x86_mov_reg_memindex(iop->ob_ptr,X86_ECX,X86_EAX,0,X86_EBX,0,1); ppc32_store_gpr(&iop->ob_ptr,target,X86_ECX); } /* Fast STB */ static void ppc32_memop_fast_stb(jit_op_t *iop,int target) { ppc32_load_gpr(&iop->ob_ptr,X86_EDX,target); x86_mov_memindex_reg(iop->ob_ptr,X86_EAX,0,X86_EBX,0,X86_EDX,1); } /* Fast LWZ */ static void ppc32_memop_fast_lwz(jit_op_t *iop,int target) { x86_mov_reg_memindex(iop->ob_ptr,X86_EAX,X86_EAX,0,X86_EBX,0,4); x86_bswap(iop->ob_ptr,X86_EAX); ppc32_store_gpr(&iop->ob_ptr,target,X86_EAX); } /* Fast STW */ static void ppc32_memop_fast_stw(jit_op_t *iop,int target) { ppc32_load_gpr(&iop->ob_ptr,X86_EDX,target); x86_bswap(iop->ob_ptr,X86_EDX); x86_mov_memindex_reg(iop->ob_ptr,X86_EAX,0,X86_EBX,0,X86_EDX,4); } /* Fast memory operation */ static void ppc32_emit_memop_fast(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, int write_op,int opcode, int base,int offset,int target, memop_fast_access op_handler) { m_uint32_t val = sign_extend(offset,16); u_char *test1,*test2,*p_exit; jit_op_t *iop; /* * Since an exception can be triggered, clear JIT state. This allows * to use branch target tag (we can directly branch on this instruction). */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_TARGET); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); iop = ppc32_op_emit_insn_output(cpu,5,"memop_fast"); test2 = NULL; if (val != 0) { /* EBX = sign-extended offset */ ppc32_load_imm(&iop->ob_ptr,X86_EBX,val); /* EBX = GPR[base] + sign-extended offset */ if (base != 0) ppc32_alu_gpr(&iop->ob_ptr,X86_ADD,X86_EBX,base); } else { if (base != 0) ppc32_load_gpr(&iop->ob_ptr,X86_EBX,base); else ppc32_load_imm(&iop->ob_ptr,X86_EBX,0); } /* EAX = mts32_entry index */ x86_mov_reg_reg(iop->ob_ptr,X86_EAX,X86_EBX,4); x86_mov_reg_reg(iop->ob_ptr,X86_ESI,X86_EBX,4); x86_shift_reg_imm(iop->ob_ptr,X86_SHR,X86_EAX,MTS32_HASH_SHIFT1); x86_shift_reg_imm(iop->ob_ptr,X86_SHR,X86_ESI,MTS32_HASH_SHIFT2); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,X86_EAX,X86_ESI); x86_alu_reg_imm(iop->ob_ptr,X86_AND,X86_EAX,MTS32_HASH_MASK); /* EDX = mts32_entry */ x86_mov_reg_membase(iop->ob_ptr,X86_EDX, X86_EDI,OFFSET(cpu_ppc_t,mts_cache[PPC32_MTS_DCACHE]), 4); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,X86_EAX,4); x86_alu_reg_reg(iop->ob_ptr,X86_ADD,X86_EDX,X86_EAX); /* Compare virtual page address (ESI = vpage) */ x86_mov_reg_reg(iop->ob_ptr,X86_ESI,X86_EBX,4); x86_alu_reg_imm(iop->ob_ptr,X86_AND,X86_ESI,PPC32_MIN_PAGE_MASK); x86_alu_reg_membase(iop->ob_ptr,X86_CMP,X86_ESI,X86_EDX, OFFSET(mts32_entry_t,gvpa)); test1 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_NZ, 0, 1); /* Test if we are writing to a COW page */ if (write_op) { x86_test_membase_imm(iop->ob_ptr,X86_EDX,OFFSET(mts32_entry_t,flags), MTS_FLAG_COW|MTS_FLAG_EXEC); test2 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_NZ, 0, 1); } /* EBX = offset in page, EAX = Host Page Address */ x86_alu_reg_imm(iop->ob_ptr,X86_AND,X86_EBX,PPC32_MIN_PAGE_IMASK); x86_mov_reg_membase(iop->ob_ptr,X86_EAX, X86_EDX,OFFSET(mts32_entry_t,hpa),4); /* Memory access */ op_handler(iop,target); p_exit = iop->ob_ptr; x86_jump8(iop->ob_ptr,0); /* === Slow lookup === */ x86_patch(test1,iop->ob_ptr); if (test2) x86_patch(test2,iop->ob_ptr); /* Update IA (EBX = vaddr) */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* EDX = virtual address */ x86_mov_reg_reg(iop->ob_ptr,X86_EDX,X86_EBX,4); /* ECX = target register */ x86_mov_reg_imm(iop->ob_ptr,X86_ECX,target); /* EAX = CPU instance pointer */ x86_mov_reg_reg(iop->ob_ptr,X86_EAX,X86_EDI,4); /* Call memory function */ x86_alu_reg_imm(iop->ob_ptr,X86_SUB,X86_ESP,STACK_ADJUST); x86_call_membase(iop->ob_ptr,X86_EDI,MEMOP_OFFSET(opcode)); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,X86_ESP,STACK_ADJUST); x86_patch(p_exit,iop->ob_ptr); } /* Emit unhandled instruction code */ static int ppc32_emit_unknown(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b, ppc_insn_t opcode) { u_char *test1; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,3,"unknown"); /* Update IA */ ppc32_set_ia(&iop->ob_ptr,b->start_ia+(b->ppc_trans_pos << 2)); /* Fallback to non-JIT mode */ x86_alu_reg_imm(iop->ob_ptr,X86_SUB,X86_ESP,STACK_ADJUST); x86_mov_reg_reg(iop->ob_ptr,X86_EAX,X86_EDI,4); x86_mov_reg_imm(iop->ob_ptr,X86_EDX,opcode); ppc32_emit_basic_c_call(&iop->ob_ptr,ppc32_exec_single_insn_ext); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,X86_ESP,STACK_ADJUST); x86_test_reg_reg(iop->ob_ptr,X86_EAX,X86_EAX); test1 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_Z, 0, 1); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); x86_patch(test1,iop->ob_ptr); /* Signal this as an EOB to reset JIT state */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); return(0); } /* Virtual Breakpoint */ void ppc32_emit_breakpoint(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b) { jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,2,"breakpoint"); x86_alu_reg_imm(iop->ob_ptr,X86_SUB,X86_ESP,STACK_ADJUST); x86_mov_reg_reg(iop->ob_ptr,X86_EAX,X86_EDI,4); ppc32_emit_c_call(b,iop,ppc32_run_breakpoint); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,X86_ESP,STACK_ADJUST); /* Signal this as an EOB to to reset JIT state */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); } /* Dump regs */ _unused static void ppc32_emit_dump_regs(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b) { jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,2,"dump_regs"); x86_mov_reg_membase(iop->ob_ptr,X86_EAX,X86_EDI,OFFSET(cpu_ppc_t,gen),4); x86_alu_reg_imm(iop->ob_ptr,X86_SUB,X86_ESP,STACK_ADJUST-4); x86_push_reg(iop->ob_ptr,X86_EAX); ppc32_emit_c_call(b,iop,ppc32_dump_regs); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,X86_ESP,STACK_ADJUST); /* Signal this as an EOB to to reset JIT state */ ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); } /* Increment the number of executed instructions (performance debugging) */ void ppc32_inc_perf_counter(cpu_ppc_t *cpu) { jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,1,"perf_cnt"); x86_inc_membase(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,perf_counter)); } /* ======================================================================== */ /* BLR - Branch to Link Register */ DECLARE_INSN(BLR) { jit_op_t *iop; int hreg; ppc32_jit_start_hreg_seq(cpu,"blr"); hreg = ppc32_jit_alloc_hreg(cpu,-1); ppc32_op_emit_alter_host_reg(cpu,hreg); iop = ppc32_op_emit_insn_output(cpu,2,"blr"); x86_mov_reg_membase(iop->ob_ptr,hreg,X86_EDI,OFFSET(cpu_ppc_t,lr),4); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,ia),hreg,4); /* set the return address */ if (insn & 1) ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); ppc32_jit_close_hreg_seq(cpu); return(0); } /* BCTR - Branch to Count Register */ DECLARE_INSN(BCTR) { jit_op_t *iop; int hreg; ppc32_jit_start_hreg_seq(cpu,"bctr"); hreg = ppc32_jit_alloc_hreg(cpu,-1); ppc32_op_emit_alter_host_reg(cpu,hreg); iop = ppc32_op_emit_insn_output(cpu,2,"bctr"); x86_mov_reg_membase(iop->ob_ptr,hreg,X86_EDI,OFFSET(cpu_ppc_t,ctr),4); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,ia),hreg,4); /* set the return address */ if (insn & 1) ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFLR - Move From Link Register */ DECLARE_INSN(MFLR) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mflr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mflr"); x86_mov_reg_membase(iop->ob_ptr,hreg_rd,X86_EDI,OFFSET(cpu_ppc_t,lr),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MTLR - Move To Link Register */ DECLARE_INSN(MTLR) { int rs = bits(insn,21,25); int hreg_rs; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mtlr"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"mtlr"); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,lr),hreg_rs,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFCTR - Move From Counter Register */ DECLARE_INSN(MFCTR) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mfctr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mfctr"); x86_mov_reg_membase(iop->ob_ptr,hreg_rd,X86_EDI,OFFSET(cpu_ppc_t,ctr),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MTCTR - Move To Counter Register */ DECLARE_INSN(MTCTR) { int rs = bits(insn,21,25); int hreg_rs; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mtctr"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"mtctr"); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,ctr),hreg_rs,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFTBU - Move from Time Base (Up) */ DECLARE_INSN(MFTBU) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mftbu"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mftbu"); x86_mov_reg_membase(iop->ob_ptr,hreg_rd,X86_EDI,OFFSET(cpu_ppc_t,tb)+4,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } #define PPC32_TB_INCREMENT 50 /* MFTBL - Move from Time Base (Lo) */ DECLARE_INSN(MFTBL) { int rd = bits(insn,21,25); int hreg_rd,hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mftbl"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); iop = ppc32_op_emit_insn_output(cpu,3,"mftbl"); x86_mov_reg_membase(iop->ob_ptr,hreg_rd,X86_EDI,OFFSET(cpu_ppc_t,tb),4); /* Increment the time base register */ x86_mov_reg_membase(iop->ob_ptr,hreg_rd,X86_EDI,OFFSET(cpu_ppc_t,tb),4); x86_mov_reg_membase(iop->ob_ptr,hreg_t0,X86_EDI,OFFSET(cpu_ppc_t,tb)+4,4); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_rd,PPC32_TB_INCREMENT); x86_alu_reg_imm(iop->ob_ptr,X86_ADC,hreg_t0,0); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,tb),hreg_rd,4); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,tb)+4,hreg_t0,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADD */ DECLARE_INSN(ADD) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rd,hreg_ra,hreg_rb; jit_op_t *iop; /* $rd = $ra + $rb */ ppc32_jit_start_hreg_seq(cpu,"add"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"add"); if (rd == ra) x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb); else if (rd == rb) x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_rd,hreg_ra); else { x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDC */ DECLARE_INSN(ADDC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rd,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $rd = $ra + $rb */ ppc32_jit_start_hreg_seq(cpu,"addc"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); /* store the carry flag */ hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"addc"); if (rd == ra) x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb); else if (rd == rb) x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_rd,hreg_ra); else { x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_rd,hreg_rb); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t0,FALSE); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x1); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,xer_ca), hreg_t0,4); if (insn & 1) { x86_test_reg_reg(iop->ob_ptr,hreg_rd,hreg_rd); ppc32_op_emit_update_flags(cpu,0,TRUE); } ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDE - Add Extended */ DECLARE_INSN(ADDE) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"adde"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,3,"adde"); /* $t0 = $ra + carry */ x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); x86_alu_reg_membase(iop->ob_ptr,X86_ADD,hreg_t0, X86_EDI,OFFSET(cpu_ppc_t,xer_ca)); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,xer_ca),hreg_t1,4); /* $t0 += $rb */ x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_t0,hreg_rb); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_alu_membase_reg(iop->ob_ptr,X86_OR,X86_EDI,OFFSET(cpu_ppc_t,xer_ca), hreg_t1); /* update cr0 */ if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_t0,hreg_t0); x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDI - ADD Immediate */ DECLARE_INSN(ADDI) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + imm */ ppc32_jit_start_hreg_seq(cpu,"addi"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); if (ra != 0) { hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,2,"addi"); if (rd != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_rd,tmp); } else { iop = ppc32_op_emit_insn_output(cpu,1,"addi"); ppc32_load_imm(&iop->ob_ptr,hreg_rd,tmp); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDIC - ADD Immediate with Carry */ DECLARE_INSN(ADDIC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + imm */ ppc32_jit_start_hreg_seq(cpu,"addic"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"addic"); if (rd != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_rd,tmp); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); x86_set_membase(iop->ob_ptr,X86_CC_C, X86_EDI,OFFSET(cpu_ppc_t,xer_ca),FALSE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDIC. */ DECLARE_INSN(ADDIC_dot) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + imm */ ppc32_jit_start_hreg_seq(cpu,"addic."); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"addic."); if (rd != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_rd,tmp); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); x86_set_membase(iop->ob_ptr,X86_CC_C, X86_EDI,OFFSET(cpu_ppc_t,xer_ca),FALSE); ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDIS - ADD Immediate Shifted */ DECLARE_INSN(ADDIS) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = $ra + (imm << 16) */ ppc32_jit_start_hreg_seq(cpu,"addis"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); if (ra != 0) { hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"addis"); if (rd != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_rd,tmp); } else { //iop = ppc32_op_emit_insn_output(cpu,1,"addis"); //x86_mov_reg_imm(iop->ob_ptr,hreg_rd,tmp); ppc32_op_emit_set_host_reg_imm32(cpu,hreg_rd,tmp); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ADDZE */ DECLARE_INSN(ADDZE) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rd,hreg_ra,hreg_t0; jit_op_t *iop; /* $rd = $ra + xer_ca + set_carry */ ppc32_jit_start_hreg_seq(cpu,"addze"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,2,"addze"); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,hreg_t0); if (rd != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_alu_reg_membase(iop->ob_ptr,X86_ADD,hreg_rd, X86_EDI,OFFSET(cpu_ppc_t,xer_ca)); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t0,FALSE); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,xer_ca),hreg_t0,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* AND */ DECLARE_INSN(AND) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = $rs & $rb */ ppc32_jit_start_hreg_seq(cpu,"and"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"and"); if (ra == rs) x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb); else if (ra == rb) x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_ra,hreg_rs); else { x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* ANDC */ DECLARE_INSN(ANDC) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $ra = $rs & ~$rb */ ppc32_jit_start_hreg_seq(cpu,"andc"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"andc"); /* $t0 = ~$rb */ hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rb,4); x86_not_reg(iop->ob_ptr,hreg_t0); /* $ra = $rs & $t0 */ if (ra == rs) x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_ra,hreg_t0); else { x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_rs); x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* AND Immediate */ DECLARE_INSN(ANDI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs & imm */ ppc32_jit_start_hreg_seq(cpu,"andi"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"andi"); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_ra,tmp); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* AND Immediate Shifted */ DECLARE_INSN(ANDIS) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs & imm */ ppc32_jit_start_hreg_seq(cpu,"andis"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"andis"); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_ra,tmp); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* B - Branch */ DECLARE_INSN(B) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"b"); /* compute the new ia */ new_ia = b->start_ia + (b->ppc_trans_pos << 2); new_ia += sign_extend(offset << 2,26); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BA - Branch Absolute */ DECLARE_INSN(BA) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"ba"); /* compute the new ia */ new_ia = sign_extend(offset << 2,26); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BL - Branch and Link */ DECLARE_INSN(BL) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"bl"); /* compute the new ia */ new_ia = b->start_ia + (b->ppc_trans_pos << 2); new_ia += sign_extend(offset << 2,26); /* set the return address */ ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BLA - Branch and Link Absolute */ DECLARE_INSN(BLA) { m_uint32_t offset = bits(insn,2,25); m_uint32_t new_ia; jit_op_t *iop; iop = ppc32_op_emit_insn_output(cpu,4,"bla"); /* compute the new ia */ new_ia = sign_extend(offset << 2,26); /* set the return address */ ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1) << 2)); return(0); } /* BC - Branch Conditional (Condition Check only) */ DECLARE_INSN(BCC) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); jit_op_t *iop; u_int cr_field,cr_bit; m_uint32_t new_ia; u_char *jump_ptr; int local_jump; int cond; ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_JUMP); iop = ppc32_op_emit_insn_output(cpu,5,"bcc"); /* Get the wanted value for the condition bit */ cond = (bo >> 3) & 0x1; /* Set the return address */ if (insn & 1) { ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1)<<2)); } /* Compute the new ia */ new_ia = sign_extend_32(bd << 2,16); if (!(insn & 0x02)) new_ia += b->start_ia + (b->ppc_trans_pos << 2); /* Test the condition bit */ cr_field = ppc32_get_cr_field(bi); cr_bit = ppc32_get_cr_bit(bi); ppc32_op_emit_require_flags(cpu,cr_field); x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(cr_field), (1 << cr_bit)); local_jump = ppc32_jit_tcb_local_addr(b,new_ia,&jump_ptr); /* * Optimize the jump, depending if the destination is in the same * page or not. */ if (local_jump) { ppc32_jit_tcb_record_patch(b,iop,iop->ob_ptr,new_ia); x86_branch32(iop->ob_ptr,(cond) ? X86_CC_NZ : X86_CC_Z,0,FALSE); } else { jump_ptr = iop->ob_ptr; x86_branch32(iop->ob_ptr,(cond) ? X86_CC_Z : X86_CC_NZ,0,FALSE); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); x86_patch(jump_ptr,iop->ob_ptr); } ppc32_op_emit_branch_target(cpu,b,new_ia); return(0); } /* BC - Branch Conditional */ DECLARE_INSN(BC) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); int hreg_t0,hreg_t1; jit_op_t *iop; u_int cr_field,cr_bit; m_uint32_t new_ia; u_char *jump_ptr; int local_jump; int cond,ctr; ppc32_op_emit_basic_opcode(cpu,JIT_OP_BRANCH_JUMP); iop = ppc32_op_emit_insn_output(cpu,5,"bc"); ppc32_jit_start_hreg_seq(cpu,"bc"); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); /* Get the wanted value for the condition bit and CTR value */ cond = (bo >> 3) & 0x1; ctr = (bo >> 1) & 0x1; /* Set the return address */ if (insn & 1) { ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1)<<2)); } /* Compute the new ia */ new_ia = sign_extend_32(bd << 2,16); if (!(insn & 0x02)) new_ia += b->start_ia + (b->ppc_trans_pos << 2); x86_mov_reg_imm(iop->ob_ptr,hreg_t0,1); /* Decrement the count register */ if (!(bo & 0x04)) { x86_dec_membase(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,ctr)); x86_set_reg(iop->ob_ptr,(ctr) ? X86_CC_Z : X86_CC_NZ,hreg_t1,FALSE); x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } /* Test the condition bit */ if (!((bo >> 4) & 0x01)) { cr_field = ppc32_get_cr_field(bi); cr_bit = ppc32_get_cr_bit(bi); ppc32_op_emit_require_flags(cpu,cr_field); x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(cr_field), (1 << cr_bit)); x86_set_reg(iop->ob_ptr,(cond) ? X86_CC_NZ : X86_CC_Z,hreg_t1,FALSE); x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); local_jump = ppc32_jit_tcb_local_addr(b,new_ia,&jump_ptr); /* * Optimize the jump, depending if the destination is in the same * page or not. */ if (local_jump) { ppc32_jit_tcb_record_patch(b,iop,iop->ob_ptr,new_ia); x86_branch32(iop->ob_ptr,X86_CC_NZ,0,FALSE); } else { jump_ptr = iop->ob_ptr; x86_branch32(iop->ob_ptr,X86_CC_Z,0,FALSE); ppc32_set_jump(cpu,b,iop,new_ia,TRUE); x86_patch(jump_ptr,iop->ob_ptr); } ppc32_op_emit_branch_target(cpu,b,new_ia); ppc32_jit_close_hreg_seq(cpu); return(0); } /* BCLR - Branch Conditional to Link register */ DECLARE_INSN(BCLR) { int bo = bits(insn,21,25); int bi = bits(insn,16,20); int bd = bits(insn,2,15); int hreg_t0,hreg_t1; jit_op_t *iop; u_int cr_field,cr_bit; m_uint32_t new_ia; u_char *jump_ptr; int cond,ctr; ppc32_jit_start_hreg_seq(cpu,"bclr"); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); iop = ppc32_op_emit_insn_output(cpu,5,"bclr"); /* Get the wanted value for the condition bit and CTR value */ cond = (bo >> 3) & 0x1; ctr = (bo >> 1) & 0x1; /* Compute the new ia */ new_ia = sign_extend_32(bd << 2,16); if (!(insn & 0x02)) new_ia += b->start_ia + (b->ppc_trans_pos << 2); x86_mov_reg_imm(iop->ob_ptr,hreg_t0,1); /* Decrement the count register */ if (!(bo & 0x04)) { x86_dec_membase(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,ctr)); x86_set_reg(iop->ob_ptr,(ctr) ? X86_CC_Z : X86_CC_NZ,hreg_t1,FALSE); x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } /* Test the condition bit */ if (!((bo >> 4) & 0x01)) { cr_field = ppc32_get_cr_field(bi); cr_bit = ppc32_get_cr_bit(bi); ppc32_op_emit_require_flags(cpu,cr_field); x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(cr_field), (1 << cr_bit)); x86_set_reg(iop->ob_ptr,(cond) ? X86_CC_NZ : X86_CC_Z,hreg_t1,FALSE); x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,hreg_t1); } /* Set the return address */ x86_mov_reg_membase(iop->ob_ptr,hreg_t1,X86_EDI,OFFSET(cpu_ppc_t,lr),4); if (insn & 1) { ppc32_set_lr(iop,b->start_ia + ((b->ppc_trans_pos+1) << 2)); ppc32_op_emit_branch_target(cpu,b,b->start_ia+((b->ppc_trans_pos+1)<<2)); } /* Branching */ x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); jump_ptr = iop->ob_ptr; x86_branch32(iop->ob_ptr,X86_CC_Z,0,FALSE); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t1,0xFFFFFFFC); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,ia),hreg_t1,4); ppc32_jit_tcb_push_epilog(&iop->ob_ptr); x86_patch(jump_ptr,iop->ob_ptr); ppc32_op_emit_basic_opcode(cpu,JIT_OP_EOB); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMP - Compare */ DECLARE_INSN(CMP) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmp"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"cmp"); x86_alu_reg_reg(iop->ob_ptr,X86_CMP,hreg_ra,hreg_rb); ppc32_op_emit_update_flags(cpu,rd,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMPI - Compare Immediate */ DECLARE_INSN(CMPI) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_ra; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmpi"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"cmpi"); x86_alu_reg_imm(iop->ob_ptr,X86_CMP,hreg_ra,tmp); ppc32_op_emit_update_flags(cpu,rd,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMPL - Compare Logical */ DECLARE_INSN(CMPL) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmpl"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"cmpl"); x86_alu_reg_reg(iop->ob_ptr,X86_CMP,hreg_ra,hreg_rb); ppc32_op_emit_update_flags(cpu,rd,FALSE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CMPLI - Compare Immediate */ DECLARE_INSN(CMPLI) { int rd = bits(insn,23,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); int hreg_ra; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"cmpli"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"cmpli"); x86_alu_reg_imm(iop->ob_ptr,X86_CMP,hreg_ra,imm); ppc32_op_emit_update_flags(cpu,rd,FALSE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRAND - Condition Register AND */ DECLARE_INSN(CRAND) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"crand"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crand"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of AND between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,X86_EDX); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRANDC - Condition Register AND with Complement */ DECLARE_INSN(CRANDC) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"crandc"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crandc"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_Z,hreg_t0,FALSE); /* result of AND between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,X86_EDX); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CREQV - Condition Register EQV */ DECLARE_INSN(CREQV) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"creqv"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"creqv"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of XOR between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,X86_EDX); x86_not_reg(iop->ob_ptr,hreg_t0); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRNAND - Condition Register NAND */ DECLARE_INSN(CRNAND) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"crnand"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crnand"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of NAND between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_t0,X86_EDX); x86_not_reg(iop->ob_ptr,hreg_t0); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRNOR - Condition Register NOR */ DECLARE_INSN(CRNOR) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"crnor"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crnor"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of NOR between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_t0,X86_EDX); x86_not_reg(iop->ob_ptr,hreg_t0); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CROR - Condition Register OR */ DECLARE_INSN(CROR) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"cror"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"cror"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of OR between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_t0,X86_EDX); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRORC - Condition Register OR with Complement */ DECLARE_INSN(CRORC) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"crorc"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crorc"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_Z,hreg_t0,FALSE); /* result of ORC between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_t0,X86_EDX); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* CRXOR - Condition Register XOR */ DECLARE_INSN(CRXOR) { int bd = bits(insn,21,25); int bb = bits(insn,16,20); int ba = bits(insn,11,15); int hreg_t0; jit_op_t *iop; ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_start_hreg_seq(cpu,"crxor"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(ba)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bb)); ppc32_op_emit_require_flags(cpu,ppc32_get_cr_field(bd)); iop = ppc32_op_emit_insn_output(cpu,3,"crxor"); /* test $ba bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(ba)), (1 << ppc32_get_cr_bit(ba))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,X86_EDX,FALSE); /* test $bb bit */ x86_test_membase_imm(iop->ob_ptr, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bb)), (1 << ppc32_get_cr_bit(bb))); x86_set_reg(iop->ob_ptr,X86_CC_NZ,hreg_t0,FALSE); /* result of XOR between $ba and $bb */ x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,X86_EDX); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x01); /* set/clear $bd bit depending on the result */ x86_alu_membase_imm(iop->ob_ptr,X86_AND, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), ~(1 << ppc32_get_cr_bit(bd))); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_t0,ppc32_get_cr_bit(bd)); x86_alu_membase_reg(iop->ob_ptr,X86_OR, X86_EDI,PPC32_CR_FIELD_OFFSET(ppc32_get_cr_field(bd)), hreg_t0); ppc32_jit_close_hreg_seq(cpu); return(0); } /* DIVWU - Divide Word Unsigned */ DECLARE_INSN(DIVWU) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"divwu"); ppc32_jit_alloc_hreg_forced(cpu,X86_EAX); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); /* $rd = $ra / $rb */ ppc32_op_emit_load_gpr(cpu,X86_EAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"divwu"); ppc32_load_imm(&iop->ob_ptr,X86_EDX,0); x86_div_reg(iop->ob_ptr,hreg_rb,0); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,X86_EAX,X86_EAX); ppc32_op_emit_store_gpr(cpu,rd,X86_EAX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,X86_EAX); ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* EQV */ DECLARE_INSN(EQV) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = ~($rs ^ $rb) */ ppc32_jit_start_hreg_seq(cpu,"eqv"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"eqv"); if (ra == rs) x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb); else if (ra == rb) x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rs); else { x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb); } x86_not_reg(iop->ob_ptr,hreg_ra); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* EXTSB - Extend Sign Byte */ DECLARE_INSN(EXTSB) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = extsb($rs) */ ppc32_jit_start_hreg_seq(cpu,"extsb"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"extsb"); if (rs != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_ra,24); x86_shift_reg_imm(iop->ob_ptr,X86_SAR,hreg_ra,24); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* EXTSH - Extend Sign Word */ DECLARE_INSN(EXTSH) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = extsh($rs) */ ppc32_jit_start_hreg_seq(cpu,"extsh"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"extsh"); if (rs != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_ra,16); x86_shift_reg_imm(iop->ob_ptr,X86_SAR,hreg_ra,16); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* LBZ - Load Byte and Zero */ DECLARE_INSN(LBZ) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(cpu,b,PPC_MEMOP_LBZ,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,0,PPC_MEMOP_LBZ,ra,offset,rs, ppc32_memop_fast_lbz); return(0); } /* LBZU - Load Byte and Zero with Update */ DECLARE_INSN(LBZU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LBZ,ra,offset,rs,1); return(0); } /* LBZUX - Load Byte and Zero with Update Indexed */ DECLARE_INSN(LBZUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LBZ,ra,rb,rs,1); return(0); } /* LBZX - Load Byte and Zero Indexed */ DECLARE_INSN(LBZX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LBZ,ra,rb,rs,0); return(0); } /* LHA - Load Half-Word Algebraic */ DECLARE_INSN(LHA) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHA,ra,offset,rs,0); return(0); } /* LHAU - Load Half-Word Algebraic with Update */ DECLARE_INSN(LHAU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHA,ra,offset,rs,1); return(0); } /* LHAUX - Load Half-Word Algebraic with Update Indexed */ DECLARE_INSN(LHAUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHA,ra,rb,rs,1); return(0); } /* LHAX - Load Half-Word Algebraic Indexed */ DECLARE_INSN(LHAX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHA,ra,rb,rs,0); return(0); } /* LHZ - Load Half-Word and Zero */ DECLARE_INSN(LHZ) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHZ,ra,offset,rs,0); return(0); } /* LHZU - Load Half-Word and Zero with Update */ DECLARE_INSN(LHZU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LHZ,ra,offset,rs,1); return(0); } /* LHZUX - Load Half-Word and Zero with Update Indexed */ DECLARE_INSN(LHZUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHZ,ra,rb,rs,1); return(0); } /* LHZX - Load Half-Word and Zero Indexed */ DECLARE_INSN(LHZX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LHZ,ra,rb,rs,0); return(0); } /* LWZ - Load Word and Zero */ DECLARE_INSN(LWZ) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(b,PPC_MEMOP_LWZ,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,0,PPC_MEMOP_LWZ,ra,offset,rs, ppc32_memop_fast_lwz); return(0); } /* LWZU - Load Word and Zero with Update */ DECLARE_INSN(LWZU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_LWZ,ra,offset,rs,1); return(0); } /* LWZUX - Load Word and Zero with Update Indexed */ DECLARE_INSN(LWZUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LWZ,ra,rb,rs,1); return(0); } /* LWZX - Load Word and Zero Indexed */ DECLARE_INSN(LWZX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_LWZ,ra,rb,rs,0); return(0); } /* MCRF - Move Condition Register Field */ DECLARE_INSN(MCRF) { int rd = bits(insn,23,25); int rs = bits(insn,18,20); int hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mcrf"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_require_flags(cpu,rs); iop = ppc32_op_emit_insn_output(cpu,1,"mcrf"); /* Load "rs" field in %edx */ x86_mov_reg_membase(iop->ob_ptr,hreg_t0, X86_EDI,PPC32_CR_FIELD_OFFSET(rs),4); /* Store it in "rd" field */ x86_mov_membase_reg(iop->ob_ptr,X86_EDI,PPC32_CR_FIELD_OFFSET(rd), hreg_t0,4); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFCR - Move from Condition Register */ DECLARE_INSN(MFCR) { int rd = bits(insn,21,25); int hreg_rd,hreg_t0; jit_op_t *iop; int i; ppc32_jit_start_hreg_seq(cpu,"mfcr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_require_flags(cpu,JIT_OP_PPC_ALL_FLAGS); iop = ppc32_op_emit_insn_output(cpu,3,"mfcr"); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_rd,hreg_rd); for(i=0;i<8;i++) { /* load field in %edx */ x86_mov_reg_membase(iop->ob_ptr,hreg_t0, X86_EDI,PPC32_CR_FIELD_OFFSET(i),4); x86_shift_reg_imm(iop->ob_ptr,X86_SHL,hreg_rd,4); x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_rd,hreg_t0); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFMSR - Move from Machine State Register */ DECLARE_INSN(MFMSR) { int rd = bits(insn,21,25); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mfmsr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mfmsr"); x86_mov_reg_membase(iop->ob_ptr,hreg_rd,X86_EDI,OFFSET(cpu_ppc_t,msr),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MFSR - Move From Segment Register */ DECLARE_INSN(MFSR) { int rd = bits(insn,21,25); int sr = bits(insn,16,19); int hreg_rd; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mfsr"); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); iop = ppc32_op_emit_insn_output(cpu,1,"mfsr"); x86_mov_reg_membase(iop->ob_ptr,hreg_rd, X86_EDI,(OFFSET(cpu_ppc_t,sr) + (sr << 2)),4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MTCRF - Move to Condition Register Fields */ DECLARE_INSN(MTCRF) { int rs = bits(insn,21,25); int crm = bits(insn,12,19); int hreg_rs,hreg_t0; jit_op_t *iop; int i; ppc32_jit_start_hreg_seq(cpu,"mtcrf"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,3,"mtcrf"); for(i=0;i<8;i++) if (crm & (1 << (7 - i))) { x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); if (i != 7) x86_shift_reg_imm(iop->ob_ptr,X86_SHR,hreg_t0,28 - (i << 2)); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x0F); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,PPC32_CR_FIELD_OFFSET(i), hreg_t0,4); } ppc32_op_emit_basic_opcode(cpu,JIT_OP_TRASH_FLAGS); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULHW - Multiply High Word */ DECLARE_INSN(MULHW) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mulhw"); ppc32_jit_alloc_hreg_forced(cpu,X86_EAX); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,X86_EAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); /* rd = hi(ra * rb) */ iop = ppc32_op_emit_insn_output(cpu,2,"mulhw"); x86_mul_reg(iop->ob_ptr,hreg_rb,1); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,X86_EDX,X86_EDX); ppc32_op_emit_store_gpr(cpu,rd,X86_EDX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,X86_EAX); ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULHWU - Multiply High Word Unsigned */ DECLARE_INSN(MULHWU) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mulhwu"); ppc32_jit_alloc_hreg_forced(cpu,X86_EAX); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,X86_EAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); /* rd = hi(ra * rb) */ iop = ppc32_op_emit_insn_output(cpu,2,"mulhwu"); x86_mul_reg(iop->ob_ptr,hreg_rb,0); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,X86_EDX,X86_EDX); ppc32_op_emit_store_gpr(cpu,rd,X86_EDX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,X86_EAX); ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULLI - Multiply Low Immediate */ DECLARE_INSN(MULLI) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); int hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mulli"); ppc32_jit_alloc_hreg_forced(cpu,X86_EAX); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_load_gpr(cpu,X86_EAX,ra); /* rd = lo(ra * imm) */ iop = ppc32_op_emit_insn_output(cpu,2,"mulli"); ppc32_load_imm(&iop->ob_ptr,hreg_t0,sign_extend_32(imm,16)); x86_mul_reg(iop->ob_ptr,hreg_t0,1); ppc32_op_emit_store_gpr(cpu,rd,X86_EAX); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,X86_EAX); ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* MULLW - Multiply Low Word */ DECLARE_INSN(MULLW) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rb; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"mullw"); ppc32_jit_alloc_hreg_forced(cpu,X86_EAX); ppc32_jit_alloc_hreg_forced(cpu,X86_EDX); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,X86_EAX,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); /* rd = lo(ra * rb) */ iop = ppc32_op_emit_insn_output(cpu,2,"mullw"); x86_mul_reg(iop->ob_ptr,hreg_rb,1); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,X86_EAX,X86_EAX); ppc32_op_emit_store_gpr(cpu,rd,X86_EAX); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); /* edx:eax are directly modified: throw them */ ppc32_op_emit_alter_host_reg(cpu,X86_EAX); ppc32_op_emit_alter_host_reg(cpu,X86_EDX); ppc32_jit_close_hreg_seq(cpu); return(0); } /* NAND */ DECLARE_INSN(NAND) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = ~($rs & $rb) */ ppc32_jit_start_hreg_seq(cpu,"nand"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"nand"); if (ra == rs) x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb); else if (ra == rb) x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_ra,hreg_rs); else { x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_reg(iop->ob_ptr,X86_AND,hreg_ra,hreg_rb); } x86_not_reg(iop->ob_ptr,hreg_ra); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* NEG */ DECLARE_INSN(NEG) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int hreg_rd,hreg_ra; jit_op_t *iop; /* $rd = neg($ra) */ ppc32_jit_start_hreg_seq(cpu,"neg"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,1,"neg"); if (rd != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_ra,4); x86_neg_reg(iop->ob_ptr,hreg_rd); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_rd,hreg_rd); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* NOR */ DECLARE_INSN(NOR) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = ~($rs | $rb) */ ppc32_jit_start_hreg_seq(cpu,"nor"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"nor"); if (ra == rs) x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb); else if (ra == rb) x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_rs); else { x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb); } x86_not_reg(iop->ob_ptr,hreg_ra); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR */ DECLARE_INSN(OR) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = $rs | $rb */ ppc32_jit_start_hreg_seq(cpu,"or"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); /* special optimization for move/nop operation */ if (rs == rb) { ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"or"); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"or"); if (ra == rs) { x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb); } else if (ra == rb) x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_rs); else { x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_rb); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR with Complement */ DECLARE_INSN(ORC) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $ra = $rs & ~$rb */ ppc32_jit_start_hreg_seq(cpu,"orc"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"orc"); /* $t0 = ~$rb */ hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rb,4); x86_not_reg(iop->ob_ptr,hreg_t0); /* $ra = $rs | $t0 */ if (ra == rs) x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_t0); else { x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_t0,hreg_rs); x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR Immediate */ DECLARE_INSN(ORI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs | imm */ ppc32_jit_start_hreg_seq(cpu,"ori"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"ori"); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_imm(iop->ob_ptr,X86_OR,hreg_ra,tmp); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* OR Immediate Shifted */ DECLARE_INSN(ORIS) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs | imm */ ppc32_jit_start_hreg_seq(cpu,"oris"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"oris"); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_imm(iop->ob_ptr,X86_OR,hreg_ra,tmp); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* RLWIMI - Rotate Left Word Immediate then Mask Insert */ DECLARE_INSN(RLWIMI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t mask; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"rlwimi"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); mask = ppc32_rotate_mask(mb,me); iop = ppc32_op_emit_insn_output(cpu,2,"rlwimi"); /* Apply inverse mask to $ra */ if (mask != 0) x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_ra,~mask); /* Rotate $rs of "sh" bits and apply the mask */ x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); if (sh != 0) x86_shift_reg_imm(iop->ob_ptr,X86_ROL,hreg_t0,sh); if (mask != 0xFFFFFFFF) x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,mask); /* Store the result */ x86_alu_reg_reg(iop->ob_ptr,X86_OR,hreg_ra,hreg_t0); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* RLWINM - Rotate Left Word Immediate AND with Mask */ DECLARE_INSN(RLWINM) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t mask; int hreg_rs,hreg_ra; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"rlwinm"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,2,"rlwinm"); /* Rotate $rs of "sh" bits and apply the mask */ mask = ppc32_rotate_mask(mb,me); if (rs != ra) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); if (sh != 0) x86_shift_reg_imm(iop->ob_ptr,X86_ROL,hreg_ra,sh); if (mask != 0xFFFFFFFF) x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_ra,mask); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* RLWNM - Rotate Left Word then Mask Insert */ DECLARE_INSN(RLWNM) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int mb = bits(insn,6,10); int me = bits(insn,1,5); register m_uint32_t mask; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; /* ecx is directly modified: throw it */ ppc32_op_emit_alter_host_reg(cpu,X86_ECX); ppc32_jit_start_hreg_seq(cpu,"rlwnm"); ppc32_jit_alloc_hreg_forced(cpu,X86_ECX); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,X86_ECX,rb); iop = ppc32_op_emit_insn_output(cpu,2,"rlwnm"); /* Load the shift register ("sh") */ mask = ppc32_rotate_mask(mb,me); /* Rotate $rs and apply the mask */ x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); x86_shift_reg(iop->ob_ptr,X86_ROL,hreg_t0); if (mask != 0xFFFFFFFF) x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,mask); x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* Shift Left Word */ DECLARE_INSN(SLW) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); u_char *test1; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; /* ecx is directly modified: throw it */ ppc32_op_emit_alter_host_reg(cpu,X86_ECX); ppc32_jit_start_hreg_seq(cpu,"slw"); ppc32_jit_alloc_hreg_forced(cpu,X86_ECX); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); /* $ra = $rs << $rb. If count >= 32, then null result */ ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,X86_ECX,rb); iop = ppc32_op_emit_insn_output(cpu,3,"slw"); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,hreg_t0); x86_test_reg_imm(iop->ob_ptr,X86_ECX,0x20); test1 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_NZ, 0, 1); x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); x86_shift_reg(iop->ob_ptr,X86_SHL,hreg_t0); /* store the result */ x86_patch(test1,iop->ob_ptr); x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SRAWI - Shift Right Algebraic Word Immediate */ DECLARE_INSN(SRAWI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int sh = bits(insn,11,15); register m_uint32_t mask; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; ppc32_jit_start_hreg_seq(cpu,"srawi"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); /* $ra = (int32)$rs >> sh */ ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,3,"srawi"); x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_shift_reg_imm(iop->ob_ptr,X86_SAR,hreg_ra,sh); /* set XER_CA depending on the result */ mask = ~(0xFFFFFFFFU << sh) | 0x80000000; x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,mask); x86_alu_reg_imm(iop->ob_ptr,X86_CMP,hreg_t0,0x80000000); x86_set_reg(iop->ob_ptr,X86_CC_A,hreg_t0,FALSE); x86_alu_reg_imm(iop->ob_ptr,X86_AND,hreg_t0,0x1); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,xer_ca),hreg_t0,4); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* Shift Right Word */ DECLARE_INSN(SRW) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); u_char *test1; int hreg_rs,hreg_ra,hreg_t0; jit_op_t *iop; /* ecx is directly modified: throw it */ ppc32_op_emit_alter_host_reg(cpu,X86_ECX); ppc32_jit_start_hreg_seq(cpu,"srw"); ppc32_jit_alloc_hreg_forced(cpu,X86_ECX); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); /* $ra = $rs >> $rb. If count >= 32, then null result */ ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,X86_ECX,rb); iop = ppc32_op_emit_insn_output(cpu,3,"srw"); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t0,hreg_t0); x86_test_reg_imm(iop->ob_ptr,X86_ECX,0x20); test1 = iop->ob_ptr; x86_branch8(iop->ob_ptr, X86_CC_NZ, 0, 1); x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rs,4); x86_shift_reg(iop->ob_ptr,X86_SHR,hreg_t0); /* store the result */ x86_patch(test1,iop->ob_ptr); x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_t0,4); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_ra,hreg_ra); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* STB - Store Byte */ DECLARE_INSN(STB) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(b,PPC_MEMOP_STB,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,1,PPC_MEMOP_STB,ra,offset,rs, ppc32_memop_fast_stb); return(0); } /* STBU - Store Byte with Update */ DECLARE_INSN(STBU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STB,ra,offset,rs,1); return(0); } /* STBUX - Store Byte with Update Indexed */ DECLARE_INSN(STBUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STB,ra,rb,rs,1); return(0); } /* STBUX - Store Byte Indexed */ DECLARE_INSN(STBX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STB,ra,rb,rs,0); return(0); } /* STH - Store Half-Word */ DECLARE_INSN(STH) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STH,ra,offset,rs,0); return(0); } /* STHU - Store Half-Word with Update */ DECLARE_INSN(STHU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STH,ra,offset,rs,1); return(0); } /* STHUX - Store Half-Word with Update Indexed */ DECLARE_INSN(STHUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STH,ra,rb,rs,1); return(0); } /* STHUX - Store Half-Word Indexed */ DECLARE_INSN(STHX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STH,ra,rb,rs,0); return(0); } /* STW - Store Word */ DECLARE_INSN(STW) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); //ppc32_emit_memop(b,PPC_MEMOP_STW,ra,offset,rs,0); ppc32_emit_memop_fast(cpu,b,1,PPC_MEMOP_STW,ra,offset,rs, ppc32_memop_fast_stw); return(0); } /* STWU - Store Word with Update */ DECLARE_INSN(STWU) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t offset = bits(insn,0,15); ppc32_emit_memop(cpu,b,PPC_MEMOP_STW,ra,offset,rs,1); return(0); } /* STWUX - Store Word with Update Indexed */ DECLARE_INSN(STWUX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STW,ra,rb,rs,1); return(0); } /* STWUX - Store Word Indexed */ DECLARE_INSN(STWX) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); ppc32_emit_memop_idx(cpu,b,PPC_MEMOP_STW,ra,rb,rs,0); return(0); } /* SUBF - Subtract From */ DECLARE_INSN(SUBF) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rd,hreg_ra,hreg_rb,hreg_t0; jit_op_t *iop; /* $rd = $rb - $ra */ ppc32_jit_start_hreg_seq(cpu,"subf"); hreg_t0 = ppc32_jit_get_tmp_hreg(cpu); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,2,"subf"); if (rd == rb) x86_alu_reg_reg(iop->ob_ptr,X86_SUB,hreg_rd,hreg_ra); else if (rd == ra) { x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_rb,4); x86_alu_reg_reg(iop->ob_ptr,X86_SUB,hreg_t0,hreg_ra); x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); } else { x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_rb,4); x86_alu_reg_reg(iop->ob_ptr,X86_SUB,hreg_rd,hreg_ra); } ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SUBFC - Subtract From Carrying */ DECLARE_INSN(SUBFC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; /* $rd = ~$ra + 1 + $rb */ ppc32_jit_start_hreg_seq(cpu,"subfc"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,3,"subfc"); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); /* $t0 = ~$ra + 1 */ x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); x86_not_reg(iop->ob_ptr,hreg_t0); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_t0,1); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,xer_ca),hreg_t1,4); /* $t0 += $rb */ x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_t0,hreg_rb); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_alu_membase_reg(iop->ob_ptr,X86_OR,X86_EDI,OFFSET(cpu_ppc_t,xer_ca), hreg_t1); x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_rd,hreg_rd); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); /* update cr0 */ if (insn & 1) ppc32_update_cr0(b); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SUBFE - Subtract From Extended */ DECLARE_INSN(SUBFE) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_ra,hreg_rb,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; /* $rd = ~$ra + $carry (xer_ca) + $rb */ ppc32_jit_start_hreg_seq(cpu,"subfe"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,3,"subfe"); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); /* $t0 = ~$ra + $carry */ x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); x86_not_reg(iop->ob_ptr,hreg_t0); x86_alu_reg_membase(iop->ob_ptr,X86_ADD,hreg_t0, X86_EDI,OFFSET(cpu_ppc_t,xer_ca)); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,xer_ca),hreg_t1,4); /* $t0 += $rb */ x86_alu_reg_reg(iop->ob_ptr,X86_ADD,hreg_t0,hreg_rb); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_alu_membase_reg(iop->ob_ptr,X86_OR,X86_EDI,OFFSET(cpu_ppc_t,xer_ca), hreg_t1); x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); if (insn & 1) x86_test_reg_reg(iop->ob_ptr,hreg_rd,hreg_rd); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); /* update cr0 */ if (insn & 1) ppc32_update_cr0(b); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SUBFIC - Subtract From Immediate Carrying */ DECLARE_INSN(SUBFIC) { int rd = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = sign_extend_32(imm,16); int hreg_ra,hreg_rd,hreg_t0,hreg_t1; jit_op_t *iop; /* $rd = ~$ra + 1 + sign_extend(imm,16) */ ppc32_jit_start_hreg_seq(cpu,"subfic"); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rd = ppc32_jit_alloc_hreg(cpu,rd); hreg_t0 = ppc32_jit_alloc_hreg(cpu,-1); hreg_t1 = ppc32_jit_get_tmp_hreg(cpu); ppc32_op_emit_alter_host_reg(cpu,hreg_t0); ppc32_op_emit_load_gpr(cpu,hreg_ra,ra); iop = ppc32_op_emit_insn_output(cpu,3,"subfic"); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_t1,hreg_t1); /* $t0 = ~$ra + 1 */ x86_mov_reg_reg(iop->ob_ptr,hreg_t0,hreg_ra,4); x86_not_reg(iop->ob_ptr,hreg_t0); x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_t0,1); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_mov_membase_reg(iop->ob_ptr,X86_EDI,OFFSET(cpu_ppc_t,xer_ca),hreg_t1,4); /* $t0 += sign_extend(imm,16) */ x86_alu_reg_imm(iop->ob_ptr,X86_ADD,hreg_t0,tmp); x86_set_reg(iop->ob_ptr,X86_CC_C,hreg_t1,FALSE); x86_alu_membase_reg(iop->ob_ptr,X86_OR,X86_EDI,OFFSET(cpu_ppc_t,xer_ca), hreg_t1); x86_mov_reg_reg(iop->ob_ptr,hreg_rd,hreg_t0,4); ppc32_op_emit_store_gpr(cpu,rd,hreg_rd); ppc32_jit_close_hreg_seq(cpu); return(0); } /* SYNC - Synchronize */ DECLARE_INSN(SYNC) { return(0); } /* XOR */ DECLARE_INSN(XOR) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); int rb = bits(insn,11,15); int hreg_rs,hreg_ra,hreg_rb; jit_op_t *iop; /* $ra = $rs ^ $rb */ ppc32_jit_start_hreg_seq(cpu,"xor"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); hreg_rb = ppc32_jit_alloc_hreg(cpu,rb); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); ppc32_op_emit_load_gpr(cpu,hreg_rb,rb); iop = ppc32_op_emit_insn_output(cpu,1,"xor"); if (ra == rs) x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb); else if (ra == rb) x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rs); else { x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_reg(iop->ob_ptr,X86_XOR,hreg_ra,hreg_rb); } ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); if (insn & 1) ppc32_op_emit_update_flags(cpu,0,TRUE); ppc32_jit_close_hreg_seq(cpu); return(0); } /* XORI - XOR Immediate */ DECLARE_INSN(XORI) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint32_t imm = bits(insn,0,15); int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs ^ imm */ ppc32_jit_start_hreg_seq(cpu,"xori"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"xori"); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_imm(iop->ob_ptr,X86_XOR,hreg_ra,imm); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* XORIS - XOR Immediate Shifted */ DECLARE_INSN(XORIS) { int rs = bits(insn,21,25); int ra = bits(insn,16,20); m_uint16_t imm = bits(insn,0,15); m_uint32_t tmp = imm << 16; int hreg_rs,hreg_ra; jit_op_t *iop; /* $ra = $rs ^ (imm << 16) */ ppc32_jit_start_hreg_seq(cpu,"xoris"); hreg_rs = ppc32_jit_alloc_hreg(cpu,rs); hreg_ra = ppc32_jit_alloc_hreg(cpu,ra); ppc32_op_emit_load_gpr(cpu,hreg_rs,rs); iop = ppc32_op_emit_insn_output(cpu,1,"xoris"); if (ra != rs) x86_mov_reg_reg(iop->ob_ptr,hreg_ra,hreg_rs,4); x86_alu_reg_imm(iop->ob_ptr,X86_XOR,hreg_ra,tmp); ppc32_op_emit_store_gpr(cpu,ra,hreg_ra); ppc32_jit_close_hreg_seq(cpu); return(0); } /* PPC instruction array */ struct ppc32_insn_tag ppc32_insn_tags[] = { { ppc32_emit_BLR , 0xfffffffe , 0x4e800020 }, { ppc32_emit_BCTR , 0xfffffffe , 0x4e800420 }, { ppc32_emit_MFLR , 0xfc1fffff , 0x7c0802a6 }, { ppc32_emit_MTLR , 0xfc1fffff , 0x7c0803a6 }, { ppc32_emit_MFCTR , 0xfc1fffff , 0x7c0902a6 }, { ppc32_emit_MTCTR , 0xfc1fffff , 0x7c0903a6 }, { ppc32_emit_MFTBL , 0xfc1ff7ff , 0x7c0c42e6 }, { ppc32_emit_MFTBU , 0xfc1ff7ff , 0x7c0d42e6 }, { ppc32_emit_ADD , 0xfc0007fe , 0x7c000214 }, { ppc32_emit_ADDC , 0xfc0007fe , 0x7c000014 }, { ppc32_emit_ADDE , 0xfc0007fe , 0x7c000114 }, { ppc32_emit_ADDI , 0xfc000000 , 0x38000000 }, { ppc32_emit_ADDIC , 0xfc000000 , 0x30000000 }, { ppc32_emit_ADDIC_dot , 0xfc000000 , 0x34000000 }, { ppc32_emit_ADDIS , 0xfc000000 , 0x3c000000 }, { ppc32_emit_ADDZE , 0xfc00fffe , 0x7c000194 }, { ppc32_emit_AND , 0xfc0007fe , 0x7c000038 }, { ppc32_emit_ANDC , 0xfc0007fe , 0x7c000078 }, { ppc32_emit_ANDI , 0xfc000000 , 0x70000000 }, { ppc32_emit_ANDIS , 0xfc000000 , 0x74000000 }, { ppc32_emit_B , 0xfc000003 , 0x48000000 }, { ppc32_emit_BA , 0xfc000003 , 0x48000002 }, { ppc32_emit_BL , 0xfc000003 , 0x48000001 }, { ppc32_emit_BLA , 0xfc000003 , 0x48000003 }, { ppc32_emit_BCC , 0xfe800000 , 0x40800000 }, { ppc32_emit_BC , 0xfc000000 , 0x40000000 }, { ppc32_emit_BCLR , 0xfc00fffe , 0x4c000020 }, { ppc32_emit_CMP , 0xfc6007ff , 0x7c000000 }, { ppc32_emit_CMPI , 0xfc600000 , 0x2c000000 }, { ppc32_emit_CMPL , 0xfc6007ff , 0x7c000040 }, { ppc32_emit_CMPLI , 0xfc600000 , 0x28000000 }, { ppc32_emit_CRAND , 0xfc0007ff , 0x4c000202 }, { ppc32_emit_CRANDC , 0xfc0007ff , 0x4c000102 }, { ppc32_emit_CREQV , 0xfc0007ff , 0x4c000242 }, { ppc32_emit_CRNAND , 0xfc0007ff , 0x4c0001c2 }, { ppc32_emit_CRNOR , 0xfc0007ff , 0x4c000042 }, { ppc32_emit_CROR , 0xfc0007ff , 0x4c000382 }, { ppc32_emit_CRORC , 0xfc0007ff , 0x4c000342 }, { ppc32_emit_CRXOR , 0xfc0007ff , 0x4c000182 }, { ppc32_emit_DIVWU , 0xfc0007fe , 0x7c000396 }, { ppc32_emit_EQV , 0xfc0007fe , 0x7c000238 }, { ppc32_emit_EXTSB , 0xfc00fffe , 0x7c000774 }, { ppc32_emit_EXTSH , 0xfc00fffe , 0x7c000734 }, { ppc32_emit_LBZ , 0xfc000000 , 0x88000000 }, { ppc32_emit_LBZU , 0xfc000000 , 0x8c000000 }, { ppc32_emit_LBZUX , 0xfc0007ff , 0x7c0000ee }, { ppc32_emit_LBZX , 0xfc0007ff , 0x7c0000ae }, { ppc32_emit_LHA , 0xfc000000 , 0xa8000000 }, { ppc32_emit_LHAU , 0xfc000000 , 0xac000000 }, { ppc32_emit_LHAUX , 0xfc0007ff , 0x7c0002ee }, { ppc32_emit_LHAX , 0xfc0007ff , 0x7c0002ae }, { ppc32_emit_LHZ , 0xfc000000 , 0xa0000000 }, { ppc32_emit_LHZU , 0xfc000000 , 0xa4000000 }, { ppc32_emit_LHZUX , 0xfc0007ff , 0x7c00026e }, { ppc32_emit_LHZX , 0xfc0007ff , 0x7c00022e }, { ppc32_emit_LWZ , 0xfc000000 , 0x80000000 }, { ppc32_emit_LWZU , 0xfc000000 , 0x84000000 }, { ppc32_emit_LWZUX , 0xfc0007ff , 0x7c00006e }, { ppc32_emit_LWZX , 0xfc0007ff , 0x7c00002e }, { ppc32_emit_MCRF , 0xfc63ffff , 0x4c000000 }, { ppc32_emit_MFCR , 0xfc1fffff , 0x7c000026 }, { ppc32_emit_MFMSR , 0xfc1fffff , 0x7c0000a6 }, { ppc32_emit_MFSR , 0xfc10ffff , 0x7c0004a6 }, { ppc32_emit_MTCRF , 0xfc100fff , 0x7c000120 }, { ppc32_emit_MULHW , 0xfc0007fe , 0x7c000096 }, { ppc32_emit_MULHWU , 0xfc0007fe , 0x7c000016 }, { ppc32_emit_MULLI , 0xfc000000 , 0x1c000000 }, { ppc32_emit_MULLW , 0xfc0007fe , 0x7c0001d6 }, { ppc32_emit_NAND , 0xfc0007fe , 0x7c0003b8 }, { ppc32_emit_NEG , 0xfc00fffe , 0x7c0000d0 }, { ppc32_emit_NOR , 0xfc0007fe , 0x7c0000f8 }, { ppc32_emit_OR , 0xfc0007fe , 0x7c000378 }, { ppc32_emit_ORC , 0xfc0007fe , 0x7c000338 }, { ppc32_emit_ORI , 0xfc000000 , 0x60000000 }, { ppc32_emit_ORIS , 0xfc000000 , 0x64000000 }, { ppc32_emit_RLWIMI , 0xfc000000 , 0x50000000 }, { ppc32_emit_RLWINM , 0xfc000000 , 0x54000000 }, { ppc32_emit_RLWNM , 0xfc000000 , 0x5c000000 }, { ppc32_emit_SLW , 0xfc0007fe , 0x7c000030 }, { ppc32_emit_SRAWI , 0xfc0007fe , 0x7c000670 }, { ppc32_emit_SRW , 0xfc0007fe , 0x7c000430 }, { ppc32_emit_STB , 0xfc000000 , 0x98000000 }, { ppc32_emit_STBU , 0xfc000000 , 0x9c000000 }, { ppc32_emit_STBUX , 0xfc0007ff , 0x7c0001ee }, { ppc32_emit_STBX , 0xfc0007ff , 0x7c0001ae }, { ppc32_emit_STH , 0xfc000000 , 0xb0000000 }, { ppc32_emit_STHU , 0xfc000000 , 0xb4000000 }, { ppc32_emit_STHUX , 0xfc0007ff , 0x7c00036e }, { ppc32_emit_STHX , 0xfc0007ff , 0x7c00032e }, { ppc32_emit_STW , 0xfc000000 , 0x90000000 }, { ppc32_emit_STWU , 0xfc000000 , 0x94000000 }, { ppc32_emit_STWUX , 0xfc0007ff , 0x7c00016e }, { ppc32_emit_STWX , 0xfc0007ff , 0x7c00012e }, { ppc32_emit_SUBF , 0xfc0007fe , 0x7c000050 }, { ppc32_emit_SUBFC , 0xfc0007fe , 0x7c000010 }, { ppc32_emit_SUBFE , 0xfc0007fe , 0x7c000110 }, { ppc32_emit_SUBFIC , 0xfc000000 , 0x20000000 }, { ppc32_emit_SYNC , 0xffffffff , 0x7c0004ac }, { ppc32_emit_XOR , 0xfc0007fe , 0x7c000278 }, { ppc32_emit_XORI , 0xfc000000 , 0x68000000 }, { ppc32_emit_XORIS , 0xfc000000 , 0x6c000000 }, { ppc32_emit_unknown , 0x00000000 , 0x00000000 }, { NULL , 0x00000000 , 0x00000000 }, }; dynamips-0.2.14/unstable/tcb.c000066400000000000000000000543011241034141600161520ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2008 Christophe Fillot (cf@utc.fr) * * Translation Sharing Groups. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "device.h" #include "pci_dev.h" #include "pci_io.h" #include "cpu.h" #include "vm.h" #include "tcb.h" #define DEBUG_JIT_FLUSH 0 #define DEBUG_JIT_BUFFER_ADJUST 0 #define DEBUG_JIT_PATCH 0 /* Size of a JIT page */ #define TC_JIT_PAGE_SIZE 32768 /* CPU provisionning */ #ifndef __CYGWIN__ #define TSG_EXEC_AREA_SINGLE_CPU 64 #else #define TSG_EXEC_AREA_SINGLE_CPU 16 #endif #define TSG_EXEC_AREA_SHARED 64 /* Minimal number of exec pages to have to satisfy an allocation request */ #define TCB_MIN_EXEC_PAGES (2 * TCB_DESC_MAX_CHUNKS) /* Maximum number of translation sharing groups */ #define TSG_MAX_GROUPS 128 /* Hash table to retrieve Translated Code descriptors from their checksums */ #define TC_HASH_BITS 16 #define TC_HASH_SIZE (1 << TC_HASH_BITS) #define TC_HASH_MASK (TC_HASH_SIZE - 1) typedef struct tsg tsg_t; struct tsg { /* Lock to synchronize multiple CPU accesses */ pthread_mutex_t lock; pthread_mutexattr_t lock_attr; /* Hash table to retrieve Translated Code */ cpu_tc_t **tc_hash; /* Free list of TC descriptors */ cpu_tc_t *tc_free_list; /* List of CPUs attached to this group */ cpu_gen_t *cpu_list; /* Exec page allocator */ void *exec_area; insn_exec_page_t *exec_page_array; insn_exec_page_t *exec_page_free_list; size_t exec_area_alloc_size; u_int exec_page_alloc,exec_page_total; u_int exec_area_full; }; #define TSG_LOCK(g) pthread_mutex_lock(&(g)->lock) #define TSG_UNLOCK(g) pthread_mutex_unlock(&(g)->lock) /* TCB groups */ static tsg_t *tsg_array[TSG_MAX_GROUPS]; /* forward prototype declarations */ int tsg_remove_single_desc(cpu_gen_t *cpu); static int tc_free(tsg_t *tsg,cpu_tc_t *tc); /* Create a new exec area */ static int exec_page_create_area(tsg_t *tsg) { size_t area_size,page_count; insn_exec_page_t *cp; u_char *cp_addr; int i; /* Area already created */ if (tsg->exec_area != NULL) return(0); /* Allocate an executable area through MMAP */ area_size = tsg->exec_area_alloc_size * 1048756; tsg->exec_area = memzone_map_exec_area(area_size); if (!tsg->exec_area) { perror("exec_page_create_area: mmap"); goto err_mmap; } /* Create the page array */ page_count = area_size / TC_JIT_PAGE_SIZE; tsg->exec_page_array = calloc(page_count,sizeof(insn_exec_page_t)); if (!tsg->exec_page_array) goto err_array; for(i=0,cp_addr=tsg->exec_area;iexec_page_array[i]; cp->ptr = cp_addr; cp_addr += TC_JIT_PAGE_SIZE; tsg->exec_page_total++; cp->next = tsg->exec_page_free_list; tsg->exec_page_free_list = cp; } return(0); err_array: memzone_unmap(tsg->exec_area,area_size); err_mmap: return(-1); } /* Allocate an exec page */ static insn_exec_page_t *exec_page_alloc(cpu_gen_t *cpu) { tsg_t *tsg = tsg_array[cpu->tsg]; insn_exec_page_t *p; _maybe_used int count; TSG_LOCK(tsg); //tcb_desc_check_consistency(tcbg); /* * If the free list is empty, try to increase exec area capacity, then * flush JIT for the requesting CPU. */ if (unlikely(!(p = tsg->exec_page_free_list))) { count = tsg_remove_single_desc(cpu); #if DEBUG_JIT_FLUSH cpu_log(cpu,"JIT","flushed %d TCB\n",count); #endif if (unlikely(!(p = tsg->exec_page_free_list))) tsg->exec_area_full = TRUE; } /* If the area is full, stop allocating pages and free TCB */ if (tsg->exec_area_full) { cpu_jit_tcb_flush_all(cpu); /* if we get >= 25% of free pages, we can reallocate */ if (tsg->exec_page_total >= (tsg->exec_page_alloc * 4)) { tsg->exec_area_full = FALSE; } TSG_UNLOCK(tsg); return NULL; } tsg->exec_page_free_list = p->next; tsg->exec_page_alloc++; TSG_UNLOCK(tsg); return p; } /* * Free an exec page and returns it to the pool. * Note: the lock is already taken when exec_page_free() is called. */ static inline void exec_page_free(tsg_t *tcbg,insn_exec_page_t *p) { if (p != NULL) { p->next = tcbg->exec_page_free_list; tcbg->exec_page_free_list = p; tcbg->exec_page_alloc--; } } /* Allocate a free TCB group */ int tsg_alloc(void) { int i; for(i=0;iexec_area_full = FALSE; tsg->exec_area_alloc_size = alloc_size; /* Create the TC hash table */ if (!(tsg->tc_hash = calloc(sizeof(cpu_tc_t *),TC_HASH_SIZE))) goto err_hash; /* Create the exec page area */ if (exec_page_create_area(tsg) == -1) goto err_area; tsg_array[id] = tsg; pthread_mutexattr_init(&tsg->lock_attr); pthread_mutexattr_settype(&tsg->lock_attr,PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&tsg->lock,&tsg->lock_attr); return(0); err_area: free(tsg->tc_hash); err_hash: free(tsg); return(-1); } /* Bind a CPU to a TSG - If the group isn't specified, create one */ int tsg_bind_cpu(cpu_gen_t *cpu) { tsg_t *tsg; ssize_t alloc_size; if (cpu->tsg == -1) { cpu->tsg = tsg_alloc(); if (cpu->tsg == -1) return(-1); alloc_size = TSG_EXEC_AREA_SINGLE_CPU; } else { alloc_size = TSG_EXEC_AREA_SHARED; } if (tsg_create(cpu->tsg,alloc_size) == -1) return(-1); tsg = tsg_array[cpu->tsg]; M_LIST_ADD(cpu,tsg->cpu_list,tsg); return(0); } /* Unbind a CPU from a TSG - release all resources used */ int tsg_unbind_cpu(cpu_gen_t *cpu) { tsg_t *tsg = tsg_array[cpu->tsg]; cpu_tb_t *tb,*next; if (cpu->tsg == -1) return(-1); /* Free all descriptors in free list */ for(tb=cpu->tb_free_list;tb;tb=next) { next = tb->tb_next; free(tb); } /* Free all descriptors currently in use */ for(tb=cpu->tb_list;tb;tb=next) { next = tb->tb_next; tb_free(cpu,tb); free(tb); } cpu->tb_list = NULL; cpu->tb_free_list = NULL; TSG_LOCK(tsg); M_LIST_REMOVE(cpu,tsg); TSG_UNLOCK(tsg); return(0); } /* Create a JIT chunk */ int tc_alloc_jit_chunk(cpu_gen_t *cpu,cpu_tc_t *tc) { insn_exec_page_t *chunk; if (tc->jit_chunk_pos >= TC_MAX_CHUNKS) { cpu_log(cpu,"JIT","TC 0x%8.8llx: too many chunks.\n",tc->vaddr); return(-1); } if (!(chunk = exec_page_alloc(cpu))) return(-1); tc->jit_chunks[tc->jit_chunk_pos++] = chunk; tc->jit_buffer = chunk; return(0); } /* Free JIT chunks allocated for a TC descriptor */ static void tc_free_jit_chunks(tsg_t *tsg,cpu_tc_t *tc) { int i; for(i=0;ijit_chunk_pos;i++) { exec_page_free(tsg,tc->jit_chunks[i]); tc->jit_chunks[i] = NULL; } } /* Remove the TC descriptor from the TSG hash table */ static inline void tc_remove_from_hash(cpu_tc_t *tc) { M_LIST_REMOVE(tc,hash); } /* * Add a TC descriptor as local to the specified CPU. * This occurs when the descriptor has just been created and is not shared. * It allows to free pages easily in case of contention. */ static inline void tc_add_cpu_local(cpu_gen_t *cpu,cpu_tc_t *tc) { M_LIST_ADD(tc,cpu->tc_local_list,sc); } /* * Remove a TC descriptor from a local list of a CPU. It happens when the TC * becomes shared between different virtual CPUs. */ static inline void tc_remove_cpu_local(cpu_tc_t *tc) { M_LIST_REMOVE(tc,sc); } /* Free a TC descriptor */ static int tc_free(tsg_t *tsg,cpu_tc_t *tc) { TSG_LOCK(tsg); tc->ref_count--; assert(tc->ref_count >= 0); if (tc->ref_count == 0) { tc->flags &= ~TC_FLAG_VALID; tc_free_patches(tc); tc_remove_from_hash(tc); tc_remove_cpu_local(tc); tc_free_jit_chunks(tsg,tc); free(tc->jit_insn_ptr); tc->sc_next = tsg->tc_free_list; tsg->tc_free_list = tc; TSG_UNLOCK(tsg); return(TRUE); } /* not yet deleted */ TSG_UNLOCK(tsg); return(FALSE); } /* Allocate a new TC descriptor */ cpu_tc_t *tc_alloc(cpu_gen_t *cpu,m_uint64_t vaddr,m_uint32_t exec_state) { tsg_t *tsg = tsg_array[cpu->tsg]; cpu_tc_t *tc; size_t len; TSG_LOCK(tsg); if (tsg->tc_free_list) { tc = tsg->tc_free_list; tsg->tc_free_list = tc->sc_next; } else { if (!(tc = malloc(sizeof(*tc)))) return NULL; } TSG_UNLOCK(tsg); memset(tc,0,sizeof(*tc)); tc->vaddr = vaddr; tc->exec_state = exec_state; tc->ref_count = 1; /* * Allocate the array used to convert target code ptr to native code ptr, * and create the first JIT buffer. */ len = VM_PAGE_SIZE / sizeof(m_uint32_t); if (!(tc->jit_insn_ptr = calloc(len,sizeof(u_char *))) || (tc_alloc_jit_chunk(cpu,tc) == -1)) { tc_free(tsg,tc); return NULL; } tc->jit_ptr = tc->jit_buffer->ptr; return tc; } /* Compute a checksum on a page */ tsg_checksum_t tsg_checksum_page(void *page,ssize_t size) { tsg_checksum_t cksum = 0; m_uint64_t *ptr = page; while(size > 0) { cksum ^= *ptr; ptr++; size -= sizeof(m_uint64_t); } return(cksum); } /* Compute a hash on the specified checksum */ static inline u_int tsg_cksum_hash(tsg_checksum_t cksum) { tsg_checksum_t tmp; tmp = cksum ^ (cksum >> 17) ^ (cksum >> 23); return((u_int)(tmp & TC_HASH_MASK)); } /* Compare physical pages */ static inline int tb_compare_page(cpu_tb_t *tb1,cpu_tb_t *tb2) { if (tb1->target_code == tb2->target_code) return(0); return(memcmp(tb1->target_code,tb2->target_code,VM_PAGE_SIZE)); } /* Compare 2 TBs */ static forced_inline int tb_compare(cpu_tb_t *tb1,cpu_tb_t *tb2) { return((tb1->vaddr == tb2->vaddr) && (tb1->exec_state == tb2->exec_state)); } /* Try to find a TC descriptor to share generated code */ int tc_find_shared(cpu_gen_t *cpu,cpu_tb_t *tb) { tsg_t *tsg = tsg_array[cpu->tsg]; cpu_tb_t *p; cpu_tc_t *tc; u_int hash_bucket; TSG_LOCK(tsg); assert(tb->target_code != NULL); hash_bucket = tsg_cksum_hash(tb->checksum); for(tc=tsg->tc_hash[hash_bucket];tc;tc=tc->hash_next) { assert(tc->flags & TC_FLAG_VALID); if (tc->checksum == tb->checksum) { for(p=tc->tb_list;p;p=p->tb_dl_next) { //assert(p->flags & TCB_FLAG_VALID); if (!(p->flags & TB_FLAG_VALID)) { tb_dump(tb); abort(); } if (tb_compare(tb,p) && !tb_compare_page(tb,p)) { /* matching page, we can share the code */ tc->ref_count++; tb->tc = tc; tc_remove_cpu_local(tc); M_LIST_ADD(tb,tc->tb_list,tb_dl); tb_enable(cpu,tb); TSG_UNLOCK(tsg); return(TSG_LOOKUP_SHARED); } } } } /* A new TCB descriptor must be created */ TSG_UNLOCK(tsg); return(TSG_LOOKUP_NEW); } /* Register a newly compiled TCB descriptor */ void tc_register(cpu_gen_t *cpu,cpu_tb_t *tb,cpu_tc_t *tc) { tsg_t *tsg = tsg_array[cpu->tsg]; u_int hash_bucket = tsg_cksum_hash(tb->checksum); tb->tc = tc; tc->checksum = tb->checksum; TSG_LOCK(tsg); tc_add_cpu_local(cpu,tc); M_LIST_ADD(tb,tc->tb_list,tb_dl); M_LIST_ADD(tc,tsg->tc_hash[hash_bucket],hash); tc->flags |= TC_FLAG_VALID; TSG_UNLOCK(tsg); } /* Remove all TC descriptors belonging to a single CPU (ie not shared) */ int tsg_remove_single_desc(cpu_gen_t *cpu) { cpu_tc_t *tc,*next; int count = 0; for(tc=cpu->tc_local_list;tc;tc=next) { next = tc->sc_next; assert(tc->ref_count == 1); assert(tc->tb_list->tb_dl_next == NULL); tb_free(cpu,tc->tb_list); count++; } cpu->tc_local_list = NULL; return(count); } /* Dump a TCB */ void tb_dump(cpu_tb_t *tb) { printf("TB 0x%8.8llx:\n",tb->vaddr); printf(" - flags : 0x%4.4x\n",tb->flags); printf(" - checksum : 0x%16.16llx\n",tb->checksum); printf(" - target_code : %p\n",tb->target_code); printf(" - virt_hash : 0x%8.8x\n",tb->virt_hash); printf(" - phys_hash : 0x%8.8x\n",tb->phys_hash); printf(" - phys_page : 0x%8.8x\n",tb->phys_page); printf(" - tc : %p\n",tb->tc); printf(" - tb_LIST : (%p,%p)\n",tb->tb_pprev,tb->tb_next); printf(" - tcb_dl_LIST : (%p,%p)\n",tb->tb_dl_pprev,tb->tb_dl_next); printf(" - phys_LIST : (%p,%p)\n",tb->phys_pprev,tb->phys_next); } /* Dump a TCB descriptor */ void tc_dump(cpu_tc_t *tc) { printf("TC 0x%8.8llx:\n",tc->vaddr); printf(" - flags : 0x%4.4x\n",tc->flags); printf(" - checksum : 0x%8.8llx\n",tc->checksum); printf(" - ref_count : %u\n",tc->ref_count); printf(" - tb_list : %p\n",tc->tb_list); printf(" - hash_LIST : (%p,%p)\n",tc->hash_pprev,tc->hash_next); printf(" - sc_LIST : (%p,%p)\n",tc->sc_pprev,tc->sc_next); } /* Consistency check */ int tc_check_consistency(cpu_gen_t *cpu) { tsg_t *tsg = tsg_array[cpu->tsg]; cpu_tb_t *tb; cpu_tc_t *tc; int i,err=0; TSG_LOCK(tsg); for(i=0;itc_hash[i];tc;tc=tc->hash_next) { if (!(tc->flags & TC_FLAG_VALID)) { cpu_log(cpu,"JIT", "consistency error: TC 0x%8.8llx (flags=0x%x)\n", tc->vaddr,tc->flags); tc_dump(tc); err++; } for(tb=tc->tb_list;tb;tb=tb->tb_dl_next) { if (!(tb->flags & TB_FLAG_VALID)) { cpu_log(cpu,"JIT", "consistency error: TB 0x%8.8llx (flags=0x%x)\n", tb->vaddr,tb->flags); err++; tb_dump(tb); } } } } TSG_UNLOCK(tsg); if (err > 0) { printf("TSG %d: internal consistency error (%d pb detected)\n", cpu->tsg,err); } return(err); } /* Statistics: compute number of shared pages in a translation group */ static int tsg_get_stats(tsg_t *tsg,struct tsg_stats *s) { cpu_tc_t *tc; int i; s->shared_tc = s->total_tc = 0; s->shared_pages = 0; if (!tsg) return(-1); for(i=0;itc_hash[i];tc;tc=tc->hash_next) { if (tc->ref_count > 1) { s->shared_pages += tc->jit_chunk_pos; s->shared_tc++; } s->total_tc++; } } return(0); } /* Show statistics about all translation groups */ void tsg_show_stats(void) { struct tsg_stats s; int i; printf("\nTSG statistics:\n\n"); printf(" ID Shared TC Total TC Alloc.Pages Shared Pages Total Pages\n"); for(i=0;iexec_page_alloc, s.shared_pages, tsg_array[i]->exec_page_total); } } printf("\n"); } /* Adjust the JIT buffer if its size is not sufficient */ int tc_adjust_jit_buffer(cpu_gen_t *cpu,cpu_tc_t *tc, void (*set_jump)(u_char **insn,u_char *dst)) { assert((tc->jit_ptr - tc->jit_buffer->ptr) < TC_JIT_PAGE_SIZE); if ((tc->jit_ptr - tc->jit_buffer->ptr) <= (TC_JIT_PAGE_SIZE - 512)) return(0); #if DEBUG_JIT_BUFFER_ADJUST cpu_log(cpu,"JIT", "TC 0x%8.8llx: adjusting JIT buffer (cur=%p,start=%p,delta=%u)\n", tc->vaddr,tc->jit_ptr,tc->jit_buffer->ptr, TC_JIT_PAGE_SIZE - (tc->jit_ptr-tc->jit_buffer->ptr)); #endif /* * If we cannot allocate a new chunk, free the complete descriptor and * return an error so that the caller uses non-JIT mode for this TCB. */ if (tc_alloc_jit_chunk(cpu,tc) == -1) { tc_free(tsg_array[cpu->tsg],tc); return(-1); } /* jump to the new exec page (link) */ set_jump(&tc->jit_ptr,tc->jit_buffer->ptr); tc->jit_ptr = tc->jit_buffer->ptr; return(0); } /* Record a patch to apply in a compiled block */ struct insn_patch *tc_record_patch(cpu_gen_t *cpu,cpu_tc_t *tc, u_char *jit_ptr,m_uint64_t vaddr) { struct insn_patch_table *ipt = tc->patch_table; struct insn_patch *patch; /* vaddr must be 32-bit aligned */ if (vaddr & 0x03) { cpu_log(cpu,"JIT", "TC 0x%8.8llx: trying to record an invalid vaddr (0x%8.8llx)\n", tc->vaddr,vaddr); return NULL; } if (!ipt || (ipt->cur_patch >= INSN_PATCH_TABLE_SIZE)) { /* full table or no table, create a new one */ ipt = malloc(sizeof(*ipt)); if (!ipt) { cpu_log(cpu,"JIT","TC 0x%8.8llx: unable to create patch table.\n", tc->vaddr); return NULL; } memset(ipt,0,sizeof(*ipt)); ipt->next = tc->patch_table; tc->patch_table = ipt; } #if DEBUG_JIT_PATCH printf("TC 0x%8.8llx: recording patch [host %p -> target:0x%8.8llx], " "TP=%d\n",tc->vaddr,jit_ptr,vaddr,tc->trans_pos); #endif patch = &ipt->patches[ipt->cur_patch]; patch->jit_insn = jit_ptr; patch->vaddr = vaddr; ipt->cur_patch++; return patch; } /* Apply all patches */ int tc_apply_patches(cpu_tc_t *tc,void (*set_patch)(u_char *insn,u_char *dst)) { struct insn_patch_table *ipt; struct insn_patch *patch; u_char *jit_dst; int i; for(ipt=tc->patch_table;ipt;ipt=ipt->next) for(i=0;icur_patch;i++) { patch = &ipt->patches[i]; jit_dst = tc_get_host_ptr(tc,patch->vaddr); if (jit_dst) { #if DEBUG_JIT_PATCH printf("TC 0x%8.8llx: applying patch " "[host %p -> target 0x%8.8llx=JIT:%p]\n", tc->vaddr,patch->jit_insn,patch->vaddr,jit_dst); #endif set_patch(patch->jit_insn,jit_dst); } } return(0); } /* Free the patch table */ void tc_free_patches(cpu_tc_t *tc) { struct insn_patch_table *p,*next; for(p=tc->patch_table;p;p=next) { next = p->next; free(p); } tc->patch_table = NULL; } /* Initialize the JIT structures of a CPU */ int cpu_jit_init(cpu_gen_t *cpu,size_t virt_hash_size,size_t phys_hash_size) { size_t len; /* Virtual address mapping for TCB */ len = virt_hash_size * sizeof(void *); cpu->tb_virt_hash = m_memalign(4096,len); memset(cpu->tb_virt_hash,0,len); /* Physical address mapping for TCB */ len = phys_hash_size * sizeof(void *); cpu->tb_phys_hash = m_memalign(4096,len); memset(cpu->tb_phys_hash,0,len); return(0); } /* Shutdown the JIT structures of a CPU */ void cpu_jit_shutdown(cpu_gen_t *cpu) { tsg_unbind_cpu(cpu); /* Free virtual and physical hash tables */ free(cpu->tb_virt_hash); free(cpu->tb_phys_hash); cpu->tb_virt_hash = NULL; cpu->tb_phys_hash = NULL; } /* Allocate a new TB */ cpu_tb_t *tb_alloc(cpu_gen_t *cpu,m_uint64_t vaddr,u_int exec_state) { cpu_tb_t *tb; if (cpu->tb_free_list) { tb = cpu->tb_free_list; cpu->tb_free_list = tb->tb_next; } else { if (!(tb = malloc(sizeof(*tb)))) return NULL; } memset(tb,0,sizeof(*tb)); tb->vaddr = vaddr; tb->exec_state = exec_state; return tb; } /* Free a Translation Block */ void tb_free(cpu_gen_t *cpu,cpu_tb_t *tb) { tsg_t *tsg = tsg_array[cpu->tsg]; /* Remove this TB from the TB list bound to a TC descriptor */ TSG_LOCK(tsg); M_LIST_REMOVE(tb,tb_dl); TSG_UNLOCK(tsg); /* Release the TC descriptor first */ if (tb->tc != NULL) tc_free(tsg,tb->tc); /* Remove the block from the CPU TCB list */ M_LIST_REMOVE(tb,tb); /* Remove the block from the CPU physical mapping hash table */ M_LIST_REMOVE(tb,phys); /* Invalidate the entry in hash table for virtual addresses */ if (cpu->tb_virt_hash[tb->virt_hash] == tb) cpu->tb_virt_hash[tb->virt_hash] = NULL; /* Make the block return to the free list */ memset(tb,0,sizeof(*tb)); tb->tb_next = cpu->tb_free_list; cpu->tb_free_list = tb; } /* Enable a Tranlsation Block */ void tb_enable(cpu_gen_t *cpu,cpu_tb_t *tb) { M_LIST_ADD(tb,cpu->tb_list,tb); M_LIST_ADD(tb,cpu->tb_phys_hash[tb->phys_hash],phys); tb->flags |= TB_FLAG_VALID; } /* Flush all TCB of a virtual CPU */ void cpu_jit_tcb_flush_all(cpu_gen_t *cpu) { cpu_tb_t *tb,*next; for(tb=cpu->tb_list;tb;tb=next) { next = tb->tb_next; tb_free(cpu,tb); } assert(cpu->tb_list == NULL); } /* Mark a TB as containing self-modifying code */ void tb_mark_smc(cpu_gen_t *cpu,cpu_tb_t *tb) { if (tb->flags & TB_FLAG_SMC) return; /* already done */ tb->flags |= TB_FLAG_SMC; if (tb->tc != NULL) { tc_free(tsg_array[cpu->tsg],tb->tc); tb->tc = NULL; } } /* Handle write access on an executable page */ void cpu_jit_write_on_exec_page(cpu_gen_t *cpu, m_uint32_t wr_phys_page, m_uint32_t wr_hp, m_uint32_t ip_phys_page) { cpu_tb_t *tb,**tbp,*tb_next; if (wr_phys_page != ip_phys_page) { /* Clear all TCB matching the physical page being modified */ for(tbp=&cpu->tb_phys_hash[wr_hp];*tbp;) if ((*tbp)->phys_page == wr_phys_page) { tb_next = (*tbp)->phys_next; tb_free(cpu,(*tbp)); *tbp = tb_next; } else { tbp = &(*tbp)->phys_next; } } else { /* Self-modifying page */ for(tb=cpu->tb_phys_hash[wr_hp];tb;tb=tb->phys_next) if (tb->phys_page == wr_phys_page) tb_mark_smc(cpu,tb); cpu_exec_loop_enter(cpu); } } dynamips-0.2.14/unstable/tcb.h000066400000000000000000000150501241034141600161550ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2008 Christophe Fillot (cf@utc.fr) * * Translation Sharing Groups. */ #ifndef __TSG_H__ #define __TSG_H__ #include "utils.h" #include "vm.h" /* Checksum type */ typedef m_uint64_t tsg_checksum_t; /* Instruction jump patch */ struct insn_patch { struct insn_patch *next; u_char *jit_insn; m_uint64_t vaddr; }; /* Instruction patch table */ #define INSN_PATCH_TABLE_SIZE 32 struct insn_patch_table { struct insn_patch_table *next; struct insn_patch patches[INSN_PATCH_TABLE_SIZE]; u_int cur_patch; }; /* Flags for CPU Tranlation Blocks (TB) */ #define TB_FLAG_SMC 0x01 /* Self-modifying code */ #define TB_FLAG_RECOMP 0x02 /* Page being recompiled */ #define TB_FLAG_NOJIT 0x04 /* Page not supported for JIT */ #define TB_FLAG_VALID 0x08 /* Don't use translated code to execute the page */ #define TB_FLAG_NOTRANS (TB_FLAG_SMC|TB_FLAG_RECOMP|TB_FLAG_NOJIT) /* CPU Translation Block */ struct cpu_tb { u_int flags; m_uint64_t vaddr; m_uint32_t exec_state; tsg_checksum_t checksum; m_uint64_t acc_count; void *target_code; cpu_tb_t **tb_pprev,*tb_next; /* Translated Code (can be shared among multiple CPUs) */ cpu_tc_t *tc; cpu_tb_t **tb_dl_pprev,*tb_dl_next; /* Virtual page hash */ m_uint32_t virt_hash; /* Physical page information */ m_uint32_t phys_page; m_uint32_t phys_hash; cpu_tb_t **phys_pprev,*phys_next; #if DEBUG_BLOCK_TIMESTAMP m_uint64_t tm_first_use,tm_last_use; #endif }; /* Maximum exec pages per TC descriptor */ #define TC_MAX_CHUNKS 32 /* TC descriptor flags */ #define TC_FLAG_REMOVAL 0x01 /* Descriptor marked for removal */ #define TC_FLAG_VALID 0x02 /* CPU Translated Code */ struct cpu_tc { tsg_checksum_t checksum; m_uint64_t vaddr; m_uint32_t exec_state; u_int flags; /* Temporarily used during the translation */ void *target_code; u_char **jit_insn_ptr; u_int jit_chunk_pos; insn_exec_page_t *jit_chunks[TC_MAX_CHUNKS]; /* Current JIT buffer */ insn_exec_page_t *jit_buffer; u_char *jit_ptr; /* Patch table */ struct insn_patch_table *patch_table; /* Translation position in target code */ u_int trans_pos; /* 1024 instructions per page, one bit per instruction */ m_uint32_t target_bitmap[32]; m_uint32_t target_undef_cnt; /* Reference count */ int ref_count; /* TB list referring to this translated code / exec pages */ cpu_tb_t *tb_list; /* Linked list for hash table referencing */ cpu_tc_t **hash_pprev,*hash_next; /* Linked list for single-CPU referencement (ref_count=1) */ cpu_tc_t **sc_pprev,*sc_next; }; #define TC_TARGET_BITMAP_INDEX(x) (((x) >> 7) & 0x1F) #define TC_TARGET_BITMAP_POS(x) (((x) >> 2) & 0x1F) /* Mark the specified vaddr as a target for further recompiling */ static inline void tc_set_target_bit(cpu_tc_t *tc,m_uint32_t vaddr) { int index,pos; index = TC_TARGET_BITMAP_INDEX(vaddr); pos = TC_TARGET_BITMAP_POS(vaddr); tc->target_bitmap[index] |= 1 << pos; } /* Returns TRUE if the specified vaddr is in the target bitmap */ static inline int tc_get_target_bit(cpu_tc_t *tc,m_uint32_t vaddr) { int index,pos; index = TC_TARGET_BITMAP_INDEX(vaddr); pos = TC_TARGET_BITMAP_POS(vaddr); return(tc->target_bitmap[index] & (1 << pos)); } /* Get the JIT instruction pointer in a translated code */ static forced_inline u_char *tc_get_host_ptr(cpu_tc_t *tc,m_uint64_t vaddr) { m_uint32_t offset; offset = (vaddr & VM_PAGE_IMASK) >> 2; return(tc->jit_insn_ptr[offset]); } /* Get the JIT instruction pointer in a translated block */ static forced_inline u_char *tb_get_host_ptr(cpu_tb_t *tb,m_uint64_t vaddr) { return(tc_get_host_ptr(tb->tc,vaddr)); } /* Lookup return codes */ #define TSG_LOOKUP_NEW 0 #define TSG_LOOKUP_SHARED 1 /* Translation sharing group statistics */ struct tsg_stats { u_int total_tc; u_int shared_tc; u_int shared_pages; }; enum { CPU_JIT_DISABLE_CPU = 0, CPU_JIT_ENABLE_CPU, }; /* Allocate a new TCB descriptor */ cpu_tc_t *tc_alloc(cpu_gen_t *cpu,m_uint64_t vaddr,m_uint32_t exec_state); /* Bind a CPU to a TSG - If the group isn't specified, create one */ int tsg_bind_cpu(cpu_gen_t *cpu); /* Unbind a CPU from a TSG - release all resources used */ int tsg_unbind_cpu(cpu_gen_t *cpu); /* Create a JIT chunk */ int tc_alloc_jit_chunk(cpu_gen_t *cpu,cpu_tc_t *tc); /* Allocate a new TC descriptor */ cpu_tc_t *tc_alloc(cpu_gen_t *cpu,m_uint64_t vaddr,m_uint32_t exec_state); /* Compute a checksum on a page */ tsg_checksum_t tsg_checksum_page(void *page,ssize_t size); /* Try to find a TC descriptor to share generated code */ int tc_find_shared(cpu_gen_t *cpu,cpu_tb_t *tb); /* Register a newly compiled TCB descriptor */ void tc_register(cpu_gen_t *cpu,cpu_tb_t *tb,cpu_tc_t *tc); /* Remove all TC descriptors belonging to a single CPU (ie not shared) */ int tsg_remove_single_desc(cpu_gen_t *cpu); /* Dump a TCB */ void tb_dump(cpu_tb_t *tb); /* Dump a TCB descriptor */ void tc_dump(cpu_tc_t *tc); /* Show statistics about all translation groups */ void tsg_show_stats(void); /* Adjust the JIT buffer if its size is not sufficient */ int tc_adjust_jit_buffer(cpu_gen_t *cpu,cpu_tc_t *tc, void (*set_jump)(u_char **insn,u_char *dst)); /* Record a patch to apply in a compiled block */ struct insn_patch *tc_record_patch(cpu_gen_t *cpu,cpu_tc_t *tc, u_char *jit_ptr,m_uint64_t vaddr); /* Apply all patches */ int tc_apply_patches(cpu_tc_t *tc,void (*set_patch)(u_char *insn,u_char *dst)); /* Free the patch table */ void tc_free_patches(cpu_tc_t *tc); /* Initialize the JIT structures of a CPU */ int cpu_jit_init(cpu_gen_t *cpu,size_t virt_hash_size,size_t phys_hash_size); /* Shutdown the JIT structures of a CPU */ void cpu_jit_shutdown(cpu_gen_t *cpu); /* Allocate a new Translation Block */ cpu_tb_t *tb_alloc(cpu_gen_t *cpu,m_uint64_t vaddr,u_int exec_state); /* Free a Translation Block */ void tb_free(cpu_gen_t *cpu,cpu_tb_t *tb); /* Enable a Tranlsation Block */ void tb_enable(cpu_gen_t *cpu,cpu_tb_t *tb); /* Flush all TCB of a virtual CPU */ void cpu_jit_tcb_flush_all(cpu_gen_t *cpu); /* Mark a TB as containing self-modifying code */ void tb_mark_smc(cpu_gen_t *cpu,cpu_tb_t *tb); /* Handle write access on an executable page */ void cpu_jit_write_on_exec_page(cpu_gen_t *cpu, m_uint32_t wr_phys_page, m_uint32_t wr_hp, m_uint32_t ip_phys_page); #endif dynamips-0.2.14/unstable/utils.h000066400000000000000000000252611241034141600165520ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ #ifndef __UTILS_H__ #define __UTILS_H__ #include "dynamips_common.h" #include #include #include /* Host CPU Types */ #define CPU_x86 0 #define CPU_amd64 1 #define CPU_nojit 2 /* Number of host registers available for JIT */ #if JIT_CPU == CPU_x86 #define JIT_HOST_NREG 8 #elif JIT_CPU == CPU_amd64 #define JIT_HOST_NREG 16 #else #define JIT_HOST_NREG 0 #endif /* Host to VM (big-endian) conversion functions */ #if ARCH_BYTE_ORDER == ARCH_BIG_ENDIAN #define htovm16(x) (x) #define htovm32(x) (x) #define htovm64(x) (x) #define vmtoh16(x) (x) #define vmtoh32(x) (x) #define vmtoh64(x) (x) #else #define htovm16(x) (htons(x)) #define htovm32(x) (htonl(x)) #define htovm64(x) (swap64(x)) #define vmtoh16(x) (ntohs(x)) #define vmtoh32(x) (ntohl(x)) #define vmtoh64(x) (swap64(x)) #endif /* FD pool */ #define FD_POOL_MAX 16 typedef struct fd_pool fd_pool_t; struct fd_pool { int fd[FD_POOL_MAX]; struct fd_pool *next; }; /* Forward declarations */ typedef struct cpu_gen cpu_gen_t; typedef struct vm_instance vm_instance_t; typedef struct vm_platform vm_platform_t; typedef struct mips64_jit_tcb mips64_jit_tcb_t; typedef struct ppc32_jit_tcb ppc32_jit_tcb_t; typedef struct jit_op jit_op_t; typedef struct cpu_tb cpu_tb_t; typedef struct cpu_tc cpu_tc_t; /* Translated block function pointer */ typedef void (*insn_tblock_fptr)(void); /* Host executable page */ typedef struct insn_exec_page insn_exec_page_t; struct insn_exec_page { u_char *ptr; insn_exec_page_t *next; int flags; }; /* MIPS instruction */ typedef m_uint32_t mips_insn_t; /* PowerPC instruction */ typedef m_uint32_t ppc_insn_t; /* MMAP */ #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif /* Macros for double linked list */ #define M_LIST_ADD(item,head,prefix) \ do { \ (item)->prefix##_next = (head); \ (item)->prefix##_pprev = &(head); \ \ if ((head) != NULL) \ (head)->prefix##_pprev = &(item)->prefix##_next; \ \ (head) = (item); \ }while(0) #define M_LIST_REMOVE(item,prefix) \ do { \ if ((item)->prefix##_pprev != NULL) { \ if ((item)->prefix##_next != NULL) \ (item)->prefix##_next->prefix##_pprev = (item)->prefix##_pprev; \ \ *((item)->prefix##_pprev) = (item)->prefix##_next; \ \ (item)->prefix##_pprev = NULL; \ (item)->prefix##_next = NULL; \ } \ }while(0) /* List item */ typedef struct m_list m_list_t; struct m_list { void *data; m_list_t *next; }; /* MTS mapping info */ typedef struct { m_uint64_t vaddr; m_uint64_t paddr; m_uint64_t len; m_uint32_t cached; m_uint32_t offset; m_uint32_t flags; }mts_map_t; /* Invalid VTLB entry */ #define MTS_INV_ENTRY_MASK 0x00000001 /* MTS entry flags */ #define MTS_FLAG_DEV 0x000000001 /* Virtual device used */ #define MTS_FLAG_COW 0x000000002 /* Copy-On-Write */ #define MTS_FLAG_EXEC 0x000000004 /* Exec page */ #define MTS_FLAG_RO 0x000000008 /* Read-only page */ #define MTS_FLAG_WRCATCH (MTS_FLAG_RO|MTS_FLAG_COW) /* Catch writes */ /* Virtual TLB entry (32-bit MMU) */ typedef struct mts32_entry mts32_entry_t; struct mts32_entry { m_uint32_t gvpa; /* Guest Virtual Page Address */ m_uint32_t gppa; /* Guest Physical Page Address */ m_iptr_t hpa; /* Host Page Address */ m_uint32_t flags; /* Flags */ }__attribute__ ((aligned(16))); /* Virtual TLB entry (64-bit MMU) */ typedef struct mts64_entry mts64_entry_t; struct mts64_entry { m_uint64_t gvpa; /* Guest Virtual Page Address */ m_uint64_t gppa; /* Guest Physical Page Address */ m_iptr_t hpa; /* Host Page Address */ m_uint32_t flags; /* Flags */ }__attribute__ ((aligned(16))); /* Host register allocation */ #define HREG_FLAG_ALLOC_LOCKED 1 #define HREG_FLAG_ALLOC_FORCED 2 struct hreg_map { int hreg,vreg; int flags; struct hreg_map *prev,*next; }; /* Global logfile */ extern FILE *log_file; /* Check status of a bit */ static inline int check_bit(u_int old,u_int new,u_int bit) { int mask = 1 << bit; if ((old & mask) && !(new & mask)) return(1); /* bit unset */ if (!(old & mask) && (new & mask)) return(2); /* bit set */ /* no change */ return(0); } /* Sign-extension */ static forced_inline m_int64_t sign_extend(m_int64_t x,int len) { len = 64 - len; return (x << len) >> len; } /* Sign-extension (32-bit) */ static forced_inline m_int32_t sign_extend_32(m_int32_t x,int len) { len = 32 - len; return (x << len) >> len; } /* Extract bits from a 32-bit values */ static inline int bits(m_uint32_t val,int start,int end) { return((val >> start) & ((1 << (end-start+1)) - 1)); } /* Normalize a size */ static inline u_int normalize_size(u_int val,u_int nb,int shift) { return(((val+nb-1) & ~(nb-1)) >> shift); } /* Convert a 16-bit number between little and big endian */ static forced_inline m_uint16_t swap16(m_uint16_t value) { return((value >> 8) | ((value & 0xFF) << 8)); } /* Convert a 32-bit number between little and big endian */ static forced_inline m_uint32_t swap32(m_uint32_t value) { m_uint32_t result; result = value >> 24; result |= ((value >> 16) & 0xff) << 8; result |= ((value >> 8) & 0xff) << 16; result |= (value & 0xff) << 24; return(result); } /* Convert a 64-bit number between little and big endian */ static forced_inline m_uint64_t swap64(m_uint64_t value) { m_uint64_t result; result = (m_uint64_t)swap32(value & 0xffffffff) << 32; result |= swap32(value >> 32); return(result); } /* Get current time in number of msec since epoch */ static inline m_tmcnt_t m_gettime(void) { struct timeval tvp; gettimeofday(&tvp,NULL); return(((m_tmcnt_t)tvp.tv_sec * 1000) + ((m_tmcnt_t)tvp.tv_usec / 1000)); } /* Get current time in number of usec since epoch */ static inline m_tmcnt_t m_gettime_usec(void) { struct timeval tvp; gettimeofday(&tvp,NULL); return(((m_tmcnt_t)tvp.tv_sec * 1000000) + (m_tmcnt_t)tvp.tv_usec); } #ifdef __CYGWIN__ #define GET_TIMEZONE _timezone #else #define GET_TIMEZONE timezone #endif /* Get current time in number of ms (localtime) */ static inline m_tmcnt_t m_gettime_adj(void) { struct timeval tvp; struct tm tmx; time_t gmt_adjust; time_t ct; gettimeofday(&tvp,NULL); ct = tvp.tv_sec; localtime_r(&ct,&tmx); #if defined(__CYGWIN__) || defined(SUNOS) gmt_adjust = -(tmx.tm_isdst ? GET_TIMEZONE - 3600 : GET_TIMEZONE); #else gmt_adjust = tmx.tm_gmtoff; #endif tvp.tv_sec += gmt_adjust; return(((m_tmcnt_t)tvp.tv_sec * 1000) + ((m_tmcnt_t)tvp.tv_usec / 1000)); } /* Get a byte-swapped 16-bit value on a non-aligned area */ static inline m_uint16_t m_ntoh16(m_uint8_t *ptr) { m_uint16_t val = (ptr[0] << 8) | ptr[1]; return(val); } /* Get a byte-swapped 32-bit value on a non-aligned area */ static inline m_uint32_t m_ntoh32(m_uint8_t *ptr) { m_uint32_t val = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]; return(val); } /* Set a byte-swapped 16-bit value on a non-aligned area */ static inline void m_hton16(m_uint8_t *ptr,m_uint16_t val) { ptr[0] = val >> 8; ptr[1] = val; } /* Set a byte-swapped 32-bit value on a non-aligned area */ static inline void m_hton32(m_uint8_t *ptr,m_uint32_t val) { ptr[0] = val >> 24; ptr[1] = val >> 16; ptr[2] = val >> 8; ptr[3] = val; } /* Add an element to a list */ m_list_t *m_list_add(m_list_t **head,void *data); /* Dynamic sprintf */ char *dyn_sprintf(const char *fmt,...); /* Split a string */ int m_strsplit(char *str,char delim,char **array,int max_count); /* Tokenize a string */ int m_strtok(char *str,char delim,char **array,int max_count); /* Quote a string */ char *m_strquote(char *buffer,size_t buf_len,char *str); /* Decode from hex. */ int hex_decode(unsigned char *out,const unsigned char *in,int maxlen); /* Ugly function that dumps a structure in hexa and ascii. */ void mem_dump(FILE *f_output,u_char *pkt,u_int len); /* Logging function */ void m_flog(FILE *fd,char *module,char *fmt,va_list ap); /* Logging function */ void m_log(char *module,char *fmt,...); /* Write an array of string to a logfile */ void m_flog_str_array(FILE *fd,int count,char *str[]); /* Returns a line from specified file (remove trailing '\n') */ char *m_fgets(char *buffer,int size,FILE *fd); /* Read a file and returns it in a buffer */ int m_read_file(const char *filename,u_char **buffer,size_t *length); /* Allocate aligned memory */ void *m_memalign(size_t boundary,size_t size); /* Block specified signal for calling thread */ int m_signal_block(int sig); /* Unblock specified signal for calling thread */ int m_signal_unblock(int sig); /* Set non-blocking mode on a file descriptor */ int m_fd_set_non_block(int fd); /* Sync a memory zone */ int memzone_sync(void *addr, size_t len); /* Sync all mappings of a memory zone */ int memzone_sync_all(void *addr, size_t len); /* Unmap a memory zone */ int memzone_unmap(void *addr, size_t len); /* Map a memory zone as an executable area */ u_char *memzone_map_exec_area(size_t len); /* Map a memory zone from a file */ u_char *memzone_map_file(int fd,size_t len); /* Map a memory zone from a file, with copy-on-write (COW) */ u_char *memzone_map_cow_file(int fd,size_t len); /* Create a file to serve as a memory zone */ int memzone_create_file(char *filename,size_t len,u_char **ptr); /* Open a file to serve as a COW memory zone */ int memzone_open_cow_file(char *filename,size_t len,u_char **ptr); /* Open a file and map it in memory */ int memzone_open_file(char *filename,u_char **ptr,off_t *fsize); int memzone_open_file_ro(char *filename,u_char **ptr,off_t *fsize); /* Compute NVRAM checksum */ m_uint16_t nvram_cksum(m_uint16_t *ptr,size_t count); /* Byte-swap a memory block */ void mem_bswap32(void *ptr,size_t len); /* Reverse a byte */ m_uint8_t m_reverse_u8(m_uint8_t val); /* Generate a pseudo random block of data */ void m_randomize_block(m_uint8_t *buf,size_t len); /* Free an FD pool */ void fd_pool_free(fd_pool_t *pool); /* Initialize an empty pool */ void fd_pool_init(fd_pool_t *pool); /* Get a free slot for a FD in a pool */ int fd_pool_get_free_slot(fd_pool_t *pool,int **slot); /* Fill a FD set and get the maximum FD in order to use with select */ int fd_pool_set_fds(fd_pool_t *pool,fd_set *fds); /* Send a buffer to all FDs of a pool */ int fd_pool_send(fd_pool_t *pool,void *buffer,size_t len,int flags); /* Call a function for each FD having incoming data */ int fd_pool_check_input(fd_pool_t *pool,fd_set *fds, void (*cbk)(int *fd_slot,void *opt),void *opt); /* Equivalent to fprintf, but for a posix fd */ ssize_t fd_printf(int fd,int flags,char *fmt,...); #endif dynamips-0.2.14/unstable/vm.c000066400000000000000000000743021241034141600160270ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Virtual machine abstraction. */ #include #include #include #include #include #include #include #include #include #include "registry.h" #include "device.h" #include "pci_dev.h" #include "pci_io.h" #include "cpu.h" #include "vm.h" #include "tcb.h" #include "mips64_jit.h" #include "dev_vtty.h" #include MIPS64_ARCH_INC_FILE #define DEBUG_VM 1 #define VM_GLOCK() pthread_mutex_lock(&vm_global_lock) #define VM_GUNLOCK() pthread_mutex_unlock(&vm_global_lock) /* Type of VM file naming (0=use VM name, 1=use instance ID) */ int vm_file_naming_type = 0; /* Platform list */ static struct vm_platform_list *vm_platforms = NULL; /* Pool of ghost images */ static vm_ghost_image_t *vm_ghost_pool = NULL; /* Global lock for VM manipulation */ static pthread_mutex_t vm_global_lock = PTHREAD_MUTEX_INITIALIZER; /* Free all chunks used by a VM */ static void vm_chunk_free_all(vm_instance_t *vm); /* Initialize a VM object */ void vm_object_init(vm_obj_t *obj) { memset(obj,0,sizeof(*obj)); } /* Add a VM object to an instance */ void vm_object_add(vm_instance_t *vm,vm_obj_t *obj) { obj->next = vm->vm_object_list; obj->pprev = &vm->vm_object_list; if (vm->vm_object_list) vm->vm_object_list->pprev = &obj->next; vm->vm_object_list = obj; } /* Remove a VM object from an instance */ void vm_object_remove(vm_instance_t *vm,vm_obj_t *obj) { if (obj->next) obj->next->pprev = obj->pprev; *(obj->pprev) = obj->next; obj->shutdown(vm,obj->data); } /* Find an object given its name */ vm_obj_t *vm_object_find(vm_instance_t *vm,char *name) { vm_obj_t *obj; for(obj=vm->vm_object_list;obj;obj=obj->next) if (!strcmp(obj->name,name)) return obj; return NULL; } /* Check that a mandatory object is present */ int vm_object_check(vm_instance_t *vm,char *name) { return(vm_object_find(vm,name) ? 0 : -1); } /* Shut down all objects of an instance */ void vm_object_free_list(vm_instance_t *vm) { vm_obj_t *obj,*next; for(obj=vm->vm_object_list;obj;obj=next) { next = obj->next; if (obj->shutdown != NULL) { #if DEBUG_VM vm_log(vm,"VM_OBJECT","Shutdown of object \"%s\"\n",obj->name); #endif obj->shutdown(vm,obj->data); } } vm->vm_object_list = NULL; } /* Rebuild the object list pointers */ _unused static void vm_object_rebuild_list(vm_instance_t *vm) { vm_obj_t **obj; for(obj=&vm->vm_object_list;*obj;obj=&(*obj)->next) (*obj)->pprev = obj; } /* Dump the object list of an instance */ void vm_object_dump(vm_instance_t *vm) { vm_obj_t *obj; printf("VM \"%s\" (%u) object list:\n",vm->name,vm->instance_id); for(obj=vm->vm_object_list;obj;obj=obj->next) { printf(" - %-15s [data=%p]\n",obj->name,obj->data); } printf("\n"); } /* Get VM type */ char *vm_get_type(vm_instance_t *vm) { return vm->platform->name; } /* Get log name */ static char *vm_get_log_name(vm_instance_t *vm) { if (vm->platform->log_name != NULL) return vm->platform->log_name; /* default value */ return "VM"; } /* Get MAC address MSB */ u_int vm_get_mac_addr_msb(vm_instance_t *vm) { if (vm->platform->get_mac_addr_msb != NULL) return(vm->platform->get_mac_addr_msb()); /* default value */ return(0xC6); } /* Generate a filename for use by the instance */ char *vm_build_filename(vm_instance_t *vm,char *name) { char *filename,*machine; machine = vm_get_type(vm); switch(vm_file_naming_type) { case 1: filename = dyn_sprintf("%s_i%u_%s",machine,vm->instance_id,name); break; case 0: default: filename = dyn_sprintf("%s_%s_%s",machine,vm->name,name); break; } assert(filename != NULL); return filename; } /* Get the amount of host virtual memory used by a VM */ size_t vm_get_vspace_size(vm_instance_t *vm) { struct vdevice *dev; size_t hsize = 0; /* Add memory used by CPU (exec area) */ /* XXX TODO */ /* Add memory used by devices */ for(dev=vm->dev_list;dev;dev=dev->next) hsize += dev_get_vspace_size(dev); return(hsize); } /* Erase lock file */ void vm_release_lock(vm_instance_t *vm,int erase) { if (vm->lock_fd != NULL) { fclose(vm->lock_fd); vm->lock_fd = NULL; } if (vm->lock_file != NULL) { if (erase) unlink(vm->lock_file); free(vm->lock_file); vm->lock_file = NULL; } } /* Check that an instance lock file doesn't already exist */ int vm_get_lock(vm_instance_t *vm) { char pid_str[32]; struct flock lock; vm->lock_file = vm_build_filename(vm,"lock"); if (!(vm->lock_fd = fopen(vm->lock_file,"w"))) { fprintf(stderr,"Unable to create lock file \"%s\".\n",vm->lock_file); return(-1); } memset(&lock,0,sizeof(lock)); lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(fileno(vm->lock_fd),F_SETLK,&lock) == -1) { if (fcntl(fileno(vm->lock_fd),F_GETLK,&lock) == 0) { snprintf(pid_str,sizeof(pid_str),"%ld",(long)lock.l_pid); } else { strcpy(pid_str,"unknown"); } fprintf(stderr, "\nAn emulator instance (PID %s) is already running with " "identifier %u.\n" "If this is not the case, please erase file \"%s\".\n\n", pid_str,vm->instance_id,vm->lock_file); vm_release_lock(vm,FALSE); return(-1); } /* write the emulator PID */ fprintf(vm->lock_fd,"%ld\n",(u_long)getpid()); return(0); } /* Log a message */ void vm_flog(vm_instance_t *vm,char *module,char *format,va_list ap) { if (vm->log_fd) m_flog(vm->log_fd,module,format,ap); } /* Log a message */ void vm_log(vm_instance_t *vm,char *module,char *format,...) { va_list ap; if (vm->log_fd) { va_start(ap,format); vm_flog(vm,module,format,ap); va_end(ap); } } /* Close the log file */ int vm_close_log(vm_instance_t *vm) { if (vm->log_fd) fclose(vm->log_fd); free(vm->log_file); vm->log_file = NULL; vm->log_fd = NULL; return(0); } /* Create the log file */ int vm_create_log(vm_instance_t *vm) { if (vm->log_file_enabled) { vm_close_log(vm); if (!(vm->log_file = vm_build_filename(vm,"log.txt"))) return(-1); if (!(vm->log_fd = fopen(vm->log_file,"w"))) { fprintf(stderr,"VM %s: unable to create log file '%s'\n", vm->name,vm->log_file); free(vm->log_file); vm->log_file = NULL; return(-1); } } return(0); } /* Reopen the log file */ int vm_reopen_log(vm_instance_t *vm) { if (vm->log_file_enabled) { vm_close_log(vm); if (!(vm->log_file = vm_build_filename(vm,"log.txt"))) return(-1); if (!(vm->log_fd = fopen(vm->log_file,"a"))) { fprintf(stderr,"VM %s: unable to reopen log file '%s'\n", vm->name,vm->log_file); free(vm->log_file); vm->log_file = NULL; return(-1); } } return(0); } /* Error message */ void vm_error(vm_instance_t *vm,char *format,...) { char buffer[2048]; va_list ap; va_start(ap,format); vsnprintf(buffer,sizeof(buffer),format,ap); va_end(ap); fprintf(stderr,"%s '%s': %s",vm_get_log_name(vm),vm->name,buffer); } /* Create a new VM instance */ static vm_instance_t *vm_create(char *name,int instance_id, vm_platform_t *platform) { vm_instance_t *vm; if (!(vm = malloc(sizeof(*vm)))) { fprintf(stderr,"VM %s: unable to create new instance!\n",name); return NULL; } memset(vm,0,sizeof(*vm)); if (!(vm->name = strdup(name))) { fprintf(stderr,"VM %s: unable to store instance name!\n",name); goto err_name; } vm->instance_id = instance_id; vm->platform = platform; vm->status = VM_STATUS_HALTED; vm->jit_use = JIT_SUPPORT; vm->exec_blk_direct_jump = TRUE; vm->vtty_con_type = VTTY_TYPE_TERM; vm->vtty_aux_type = VTTY_TYPE_NONE; vm->timer_irq_check_itv = VM_TIMER_IRQ_CHECK_ITV; vm->log_file_enabled = TRUE; vm->rommon_vars.filename = vm_build_filename(vm,"rommon_vars"); if (!vm->rommon_vars.filename) goto err_rommon; /* XXX */ rommon_load_file(&vm->rommon_vars); /* create lock file */ if (vm_get_lock(vm) == -1) goto err_lock; /* create log file */ if (vm_create_log(vm) == -1) goto err_log; if (registry_add(vm->name,OBJ_TYPE_VM,vm) == -1) { fprintf(stderr,"VM: Unable to store instance '%s' in registry!\n", vm->name); goto err_reg_add; } m_log("VM","VM %s created.\n",vm->name); return vm; err_reg_add: vm_close_log(vm); err_log: free(vm->lock_file); err_lock: free(vm->rommon_vars.filename); err_rommon: free(vm->name); err_name: free(vm); return NULL; } /* * Shutdown hardware resources used by a VM. * The CPU must have been stopped. */ int vm_hardware_shutdown(vm_instance_t *vm) { int i; if ((vm->status == VM_STATUS_HALTED) || !vm->cpu_group) { vm_log(vm,"VM","trying to shutdown an inactive VM.\n"); return(-1); } vm_log(vm,"VM","shutdown procedure engaged.\n"); /* Mark the VM as halted */ vm->status = VM_STATUS_HALTED; /* Free the object list */ vm_object_free_list(vm); /* Free resources used by PCI busses */ vm_log(vm,"VM","removing PCI busses.\n"); pci_io_data_remove(vm,vm->pci_io_space); pci_bus_remove(vm->pci_bus[0]); pci_bus_remove(vm->pci_bus[1]); vm->pci_bus[0] = vm->pci_bus[1] = NULL; /* Free the PCI bus pool */ for(i=0;ipci_bus_pool[i] != NULL) { pci_bus_remove(vm->pci_bus_pool[i]); vm->pci_bus_pool[i] = NULL; } } /* Remove the IRQ routing vectors */ vm->set_irq = NULL; vm->clear_irq = NULL; /* Delete the VTTY for Console and AUX ports */ vm_log(vm,"VM","deleting VTTY.\n"); vm_delete_vtty(vm); /* Delete system CPU group */ vm_log(vm,"VM","deleting system CPUs.\n"); cpu_group_delete(vm->cpu_group); vm->cpu_group = NULL; vm->boot_cpu = NULL; vm_log(vm,"VM","shutdown procedure completed.\n"); m_log("VM","VM %s shutdown.\n",vm->name); return(0); } /* Free resources used by a VM */ void vm_free(vm_instance_t *vm) { if (vm != NULL) { /* Free hardware resources */ vm_hardware_shutdown(vm); m_log("VM","VM %s destroyed.\n",vm->name); /* Close log file */ vm_close_log(vm); /* Remove the lock file */ vm_release_lock(vm,TRUE); /* Free all chunks */ vm_chunk_free_all(vm); /* Free various elements */ free(vm->rommon_vars.filename); free(vm->ghost_ram_filename); free(vm->sym_filename); free(vm->ios_image); free(vm->ios_startup_config); free(vm->ios_private_config); free(vm->rom_filename); free(vm->name); free(vm); } } /* Get an instance given a name */ vm_instance_t *vm_acquire(char *name) { return(registry_find(name,OBJ_TYPE_VM)); } /* Release a VM (decrement reference count) */ int vm_release(vm_instance_t *vm) { return(registry_unref(vm->name,OBJ_TYPE_VM)); } /* Initialize RAM */ int vm_ram_init(vm_instance_t *vm,m_uint64_t paddr) { m_uint32_t len; len = vm->ram_size * 1048576; if (vm->ghost_status == VM_GHOST_RAM_USE) { return(dev_ram_ghost_init(vm,"ram",vm->sparse_mem,vm->ghost_ram_filename, paddr,len)); } return(dev_ram_init(vm,"ram",vm->ram_mmap, (vm->ghost_status != VM_GHOST_RAM_GENERATE), vm->ghost_ram_filename,vm->sparse_mem,paddr,len)); } /* Initialize VTTY */ int vm_init_vtty(vm_instance_t *vm) { /* Create Console and AUX ports */ vm->vtty_con = vtty_create(vm,"Console port", vm->vtty_con_type,vm->vtty_con_tcp_port, &vm->vtty_con_serial_option); vm->vtty_aux = vtty_create(vm,"AUX port", vm->vtty_aux_type,vm->vtty_aux_tcp_port, &vm->vtty_aux_serial_option); return(0); } /* Delete VTTY */ void vm_delete_vtty(vm_instance_t *vm) { vtty_delete(vm->vtty_con); vtty_delete(vm->vtty_aux); vm->vtty_con = vm->vtty_aux = NULL; } /* Bind a device to a virtual machine */ int vm_bind_device(vm_instance_t *vm,struct vdevice *dev) { struct vdevice **cur; u_int i; /* * Add this device to the device array. The index in the device array * is used by the MTS subsystem. */ for(i=0;idev_array[i]) break; if (i == VM_DEVICE_MAX) { fprintf(stderr,"VM%u: vm_bind_device: device table full.\n", vm->instance_id); return(-1); } vm->dev_array[i] = dev; dev->id = i; /* * Add it to the linked-list (devices are ordered by physical addresses). */ for(cur=&vm->dev_list;*cur;cur=&(*cur)->next) if ((*cur)->phys_addr > dev->phys_addr) break; dev->next = *cur; if (*cur) (*cur)->pprev = &dev->next; dev->pprev = cur; *cur = dev; return(0); } /* Unbind a device from a virtual machine */ int vm_unbind_device(vm_instance_t *vm,struct vdevice *dev) { u_int i; if (!dev || !dev->pprev) return(-1); /* Remove the device from the linked list */ if (dev->next) dev->next->pprev = dev->pprev; *(dev->pprev) = dev->next; /* Remove the device from the device array */ for(i=0;idev_array[i] == dev) { vm->dev_array[i] = NULL; break; } /* Clear device list info */ dev->next = NULL; dev->pprev = NULL; return(0); } /* Map a device at the specified physical address */ int vm_map_device(vm_instance_t *vm,struct vdevice *dev,m_uint64_t base_addr) { #if 0 /* Suspend VM activity */ vm_suspend(vm); if (cpu_group_sync_state(vm->cpu_group) == -1) { fprintf(stderr,"VM%u: unable to sync with system CPUs.\n", vm->instance_id); return(-1); } #endif /* Unbind the device if it was already active */ vm_unbind_device(vm,dev); /* Map the device at the new base address and rebuild MTS */ dev->phys_addr = base_addr; vm_bind_device(vm,dev); cpu_group_rebuild_mts(vm->cpu_group); #if 0 vm_resume(vm); #endif return(0); } /* Suspend a VM instance */ int vm_suspend(vm_instance_t *vm) { if (vm->status == VM_STATUS_RUNNING) { cpu_group_save_state(vm->cpu_group); cpu_group_set_state(vm->cpu_group,CPU_STATE_SUSPENDED); vm->status = VM_STATUS_SUSPENDED; } return(0); } /* Resume a VM instance */ int vm_resume(vm_instance_t *vm) { if (vm->status == VM_STATUS_SUSPENDED) { cpu_group_restore_state(vm->cpu_group); vm->status = VM_STATUS_RUNNING; } return(0); } /* Stop an instance */ int vm_stop(vm_instance_t *vm) { cpu_group_stop_all_cpu(vm->cpu_group); vm->status = VM_STATUS_SHUTDOWN; return(0); } /* Monitor an instance periodically */ void vm_monitor(vm_instance_t *vm) { while(vm->status != VM_STATUS_SHUTDOWN) usleep(200000); } /* Create a new chunk */ static vm_chunk_t *vm_chunk_create(vm_instance_t *vm) { vm_chunk_t *chunk; size_t area_len; if (!(chunk = malloc(sizeof(*chunk)))) return NULL; area_len = VM_CHUNK_AREA_SIZE * VM_PAGE_SIZE; if (!(chunk->area = m_memalign(VM_PAGE_SIZE,area_len))) { free(chunk); return NULL; } chunk->page_alloc = 0; chunk->page_total = VM_CHUNK_AREA_SIZE; chunk->next = vm->chunks; vm->chunks = chunk; return chunk; } /* Free a chunk */ static void vm_chunk_free(vm_chunk_t *chunk) { free(chunk->area); free(chunk); } /* Free all chunks used by a VM */ static void vm_chunk_free_all(vm_instance_t *vm) { vm_chunk_t *chunk,*next; for(chunk=vm->chunks;chunk;chunk=next) { next = chunk->next; vm_chunk_free(chunk); } vm->chunks = NULL; } /* Allocate an host page */ void *vm_alloc_host_page(vm_instance_t *vm) { vm_chunk_t *chunk = vm->chunks; void *ptr; if (!chunk || (chunk->page_alloc == chunk->page_total)) { chunk = vm_chunk_create(vm); if (!chunk) return NULL; } ptr = chunk->area + (chunk->page_alloc * VM_PAGE_SIZE); chunk->page_alloc++; return(ptr); } /* Free resources used by a ghost image */ static void vm_ghost_image_free(vm_ghost_image_t *img) { if (img) { if (img->fd != -1) { close(img->fd); if (img->area_ptr != NULL) memzone_unmap(img->area_ptr,img->file_size); } free(img->filename); free(img); } } /* Find a specified ghost image in the pool */ static vm_ghost_image_t *vm_ghost_image_find(char *filename) { vm_ghost_image_t *img; for(img=vm_ghost_pool;img;img=img->next) if (!strcmp(img->filename,filename)) return img; return NULL; } /* Load a new ghost image */ static vm_ghost_image_t *vm_ghost_image_load(char *filename) { vm_ghost_image_t *img; if (!(img = calloc(1,sizeof(*img)))) return NULL; img->fd = -1; if (!(img->filename = strdup(filename))) { vm_ghost_image_free(img); return NULL; } img->fd = memzone_open_file_ro(img->filename,&img->area_ptr,&img->file_size); if (img->fd == -1) { vm_ghost_image_free(img); return NULL; } m_log("GHOST","loaded ghost image %s (fd=%d) at addr=%p (size=0x%llx)\n", img->filename,img->fd,img->area_ptr,(long long)img->file_size); return img; } /* Get a ghost image */ int vm_ghost_image_get(char *filename,u_char **ptr,int *fd) { vm_ghost_image_t *img; VM_GLOCK(); /* Do we already have this image in the pool ? */ if ((img = vm_ghost_image_find(filename)) != NULL) { img->ref_count++; *ptr = img->area_ptr; *fd = img->fd; VM_GUNLOCK(); return(0); } /* Load the ghost file and add it into the pool */ if (!(img = vm_ghost_image_load(filename))) { VM_GUNLOCK(); fprintf(stderr,"Unable to load ghost image %s\n",filename); return(-1); } img->ref_count = 1; *ptr = img->area_ptr; *fd = img->fd; img->next = vm_ghost_pool; vm_ghost_pool = img; VM_GUNLOCK(); m_log("GHOST","loaded image %s successfully.\n",filename); return(0); } /* Release a ghost image */ int vm_ghost_image_release(int fd) { vm_ghost_image_t **img,*next; VM_GLOCK(); for(img=&vm_ghost_pool;*img;img=&(*img)->next) { if ((*img)->fd == fd) { assert((*img)->ref_count > 0); (*img)->ref_count--; if ((*img)->ref_count == 0) { m_log("GHOST","unloaded ghost image %s (fd=%d) at " "addr=%p (size=0x%llx)\n", (*img)->filename,(*img)->fd,(*img)->area_ptr, (long long)(*img)->file_size); next = (*img)->next; vm_ghost_image_free(*img); *img = next; } VM_GUNLOCK(); return(0); } } VM_GUNLOCK(); return(-1); } /* Open a VM file and map it in memory */ int vm_mmap_open_file(vm_instance_t *vm,char *name, u_char **ptr,off_t *fsize) { char *filename; int fd; if (!(filename = vm_build_filename(vm,name))) { fprintf(stderr,"vm_mmap_open_file: unable to create filename (%s)\n", name); return(-1); } if ((fd = memzone_open_file(filename,ptr,fsize)) == -1) fprintf(stderr,"vm_mmap_open_file: unable to open file '%s' (%s)\n", filename,strerror(errno)); free(filename); return(fd); } /* Open/Create a VM file and map it in memory */ int vm_mmap_create_file(vm_instance_t *vm,char *name,size_t len,u_char **ptr) { char *filename; int fd; if (!(filename = vm_build_filename(vm,name))) { fprintf(stderr,"vm_mmap_create_file: unable to create filename (%s)\n", name); return(-1); } if ((fd = memzone_create_file(filename,len,ptr)) == -1) fprintf(stderr,"vm_mmap_create_file: unable to open file '%s' (%s)\n", filename,strerror(errno)); free(filename); return(fd); } /* Close a memory mapped file */ int vm_mmap_close_file(int fd,u_char *ptr,size_t len) { if (ptr != NULL) memzone_unmap(ptr,len); if (fd != -1) close(fd); return(0); } /* Save the Cisco IOS configuration from NVRAM */ int vm_ios_save_config(vm_instance_t *vm) { char *output; int res; if (!(output = vm_build_filename(vm,"ios_cfg.txt"))) return(-1); res = vm_nvram_extract_config(vm,output); free(output); return(res); } /* Set Cisco IOS image to use */ int vm_ios_set_image(vm_instance_t *vm,char *ios_image) { char *str; if (!(str = strdup(ios_image))) return(-1); if (vm->ios_image != NULL) { free(vm->ios_image); vm->ios_image = NULL; } vm->ios_image = str; return(0); } /* Unset a Cisco IOS configuration file */ void vm_ios_unset_config(vm_instance_t *vm) { free(vm->ios_startup_config); vm->ios_startup_config = NULL; free(vm->ios_private_config); vm->ios_private_config = NULL; } /* Set Cisco IOS configuration files to use (NULL to keep existing data) */ int vm_ios_set_config(vm_instance_t *vm,const char *startup_filename,const char *private_filename) { char *startup_file = NULL; char *private_file = NULL; if (startup_filename) { startup_file = strdup(startup_filename); if (startup_file == NULL) goto err_memory; } if (private_filename) { private_file = strdup(private_filename); if (private_file == NULL) goto err_memory; } vm_ios_unset_config(vm); vm->ios_startup_config = startup_file; vm->ios_private_config = private_file; return(0); err_memory: free(startup_file); free(private_file); return(-1); } /* Extract IOS configuration from NVRAM and write it to a file */ int vm_nvram_extract_config(vm_instance_t *vm,char *filename) { u_char *cfg_buffer = NULL; size_t cfg_len; FILE *fd; if (!vm->platform->nvram_extract_config) return(-1); /* Extract the IOS configuration */ if ((vm->platform->nvram_extract_config(vm,&cfg_buffer,&cfg_len,NULL,NULL)) || (cfg_buffer == NULL)) return(-1); /* Write configuration to the specified filename */ if (!(fd = fopen(filename,"w"))) { vm_error(vm,"unable to create file '%s'\n",filename); free(cfg_buffer); return(-1); } fwrite(cfg_buffer,cfg_len,1,fd); fclose(fd); free(cfg_buffer); return(0); } /* Read IOS configuraton from the files and push it to NVRAM (NULL to keep existing data) */ int vm_nvram_push_config(vm_instance_t *vm,const char *startup_filename,const char *private_filename) { u_char *startup_config = NULL; u_char *private_config = NULL; size_t startup_len = 0; size_t private_len = 0; int res = -1; /* Read configuration */ if (startup_filename) { if (m_read_file(startup_filename, &startup_config, &startup_len)) goto cleanup; } if (private_filename) { if (m_read_file(private_filename, &private_config, &private_len)) goto cleanup; } /* Push it! */ res = vm->platform->nvram_push_config(vm, startup_config, startup_len, private_config, private_len); cleanup: free(startup_config); free(private_config); return(res); } /* Save general VM configuration into the specified file */ void vm_save_config(vm_instance_t *vm,FILE *fd) { fprintf(fd,"vm create %s %u %s\n", vm->name,vm->instance_id,vm->platform->name); if (vm->ios_image) fprintf(fd,"vm set_ios %s %s\n",vm->name,vm->ios_image); fprintf(fd,"vm set_ram %s %u\n",vm->name,vm->ram_size); fprintf(fd,"vm set_nvram %s %u\n",vm->name,vm->nvram_size); fprintf(fd,"vm set_ram_mmap %s %u\n",vm->name,vm->ram_mmap); fprintf(fd,"vm set_clock_divisor %s %u\n",vm->name,vm->clock_divisor); fprintf(fd,"vm set_conf_reg %s 0x%4.4x\n",vm->name,vm->conf_reg_setup); if (vm->vtty_con_type == VTTY_TYPE_TCP) fprintf(fd,"vm set_con_tcp_port %s %d\n", vm->name,vm->vtty_con_tcp_port); if (vm->vtty_aux_type == VTTY_TYPE_TCP) fprintf(fd,"vm set_aux_tcp_port %s %d\n", vm->name,vm->vtty_aux_tcp_port); /* Save slot config */ vm_slot_save_all_config(vm,fd); } /* Find a platform */ vm_platform_t *vm_platform_find(char *name) { struct vm_platform_list *p; for(p=vm_platforms;p;p=p->next) if (!strcmp(p->platform->name,name)) return(p->platform); return NULL; } /* Find a platform given its CLI name */ vm_platform_t *vm_platform_find_cli_name(char *name) { struct vm_platform_list *p; for(p=vm_platforms;p;p=p->next) if (!strcmp(p->platform->cli_name,name)) return(p->platform); return NULL; } /* Destroy vm_platforms */ static void destroy_vm_platforms(void) { struct vm_platform_list *p, *next; for (p = vm_platforms; p ;p = next) { next = p->next; free(p); } vm_platforms = NULL; } /* Register a platform */ int vm_platform_register(vm_platform_t *platform) { struct vm_platform_list *p; if (vm_platform_find(platform->name) != NULL) { fprintf(stderr,"vm_platform_register: platform '%s' already exists.\n", platform->name); return(-1); } if (!(p = malloc(sizeof(*p)))) { fprintf(stderr,"vm_platform_register: unable to record platform.\n"); return(-1); } if (!vm_platforms) { atexit(destroy_vm_platforms); } p->platform = platform; p->next = vm_platforms; vm_platforms = p; return(0); } /* Create an instance of the specified type */ vm_instance_t *vm_create_instance(char *name,int instance_id,char *type) { vm_platform_t *platform; vm_instance_t *vm = NULL; if (!(platform = vm_platform_find(type))) { fprintf(stderr,"VM %s: unknown platform '%s'\n",name,type); goto error; } /* Create a generic VM instance */ if (!(vm = vm_create(name,instance_id,platform))) goto error; /* Initialize specific parts */ if (vm->platform->create_instance(vm) == -1) goto error; return vm; error: fprintf(stderr,"VM %s: unable to create instance!\n",name); vm_free(vm); return NULL; } /* Free resources used by a VM instance */ static int vm_reg_delete_instance(void *data,void *arg) { vm_instance_t *vm = data; return(vm->platform->delete_instance(vm)); } /* Delete a VM instance */ int vm_delete_instance(char *name) { return(registry_delete_if_unused(name,OBJ_TYPE_VM, vm_reg_delete_instance,NULL)); } /* Rename a VM instance */ int vm_rename_instance(vm_instance_t *vm, char *name) { char *old_name; char *old_lock_file = NULL; FILE *old_lock_fd = NULL; glob_t globbuf; size_t i; char *pattern = NULL; char *filename; int do_rename = 0; if (name == NULL || vm == NULL) goto err_invalid; /* invalid argument */ if (vm->status != VM_STATUS_HALTED) goto err_not_stopped; /* VM is not stopped */ if (strcmp(vm->name, name) == 0) return(0); /* same name, done */ if (registry_exists(name,OBJ_TYPE_VM)) goto err_exists; /* name already exists */ old_name = vm->name; vm->name = NULL; if(!(vm->name = strdup(name))) goto err_strdup; /* out of memory */ /* get new lock */ do_rename = ( vm_file_naming_type != 1 ); if (do_rename) { old_lock_file = vm->lock_file; old_lock_fd = vm->lock_fd; vm->lock_file = NULL; vm->lock_fd = NULL; if (vm_get_lock(vm) == -1) goto err_lock; } if (registry_rename(old_name,vm->name,OBJ_TYPE_VM)) goto err_registry; /* failed to rename */ vm_log(vm,"VM","renamed from '%s' to '%s'",old_name,vm->name); /* rename files (best effort) */ if (do_rename) { fclose(old_lock_fd); unlink(old_lock_file); free(old_lock_file); vm_close_log(vm); if ((pattern = dyn_sprintf("%s_%s_*",vm_get_type(vm),old_name)) == NULL) goto skip_rename; if (glob(pattern, GLOB_NOSORT, NULL, &globbuf) != 0) goto skip_rename; for (i = 0; i < globbuf.gl_pathc; i++) { if ((filename = dyn_sprintf("%s_%s_%s",vm_get_type(vm),vm->name,globbuf.gl_pathv[i] + strlen(pattern) - 1)) == NULL) break; /* out of memory */ rename(globbuf.gl_pathv[i], filename); free(filename); } globfree(&globbuf); skip_rename: free(pattern); vm_reopen_log(vm); } free(old_name); return(0); // done err_registry: err_lock: err_strdup: free(vm->name); vm->name = old_name; if (do_rename) { vm_release_lock(vm,TRUE); vm->lock_file = old_lock_file; vm->lock_fd = old_lock_fd; } err_exists: err_not_stopped: err_invalid: return(-1); } /* Initialize a VM instance */ int vm_init_instance(vm_instance_t *vm) { return(vm->platform->init_instance(vm)); } /* Stop a VM instance */ int vm_stop_instance(vm_instance_t *vm) { return(vm->platform->stop_instance(vm)); } /* Delete all VM instances */ int vm_delete_all_instances(void) { return(registry_delete_type(OBJ_TYPE_VM,vm_reg_delete_instance,NULL)); } /* Save configurations of all VM instances */ static void vm_reg_save_config(registry_entry_t *entry,void *opt,int *err) { vm_instance_t *vm = entry->data; FILE *fd = opt; vm_save_config(vm,fd); /* Save specific platform options */ if (vm->platform->save_config != NULL) vm->platform->save_config(vm,fd); } /* Save all VM configs */ int vm_save_config_all(FILE *fd) { registry_foreach_type(OBJ_TYPE_VM,vm_reg_save_config,fd,NULL); return(0); } /* OIR to start a slot/subslot */ int vm_oir_start(vm_instance_t *vm,u_int slot,u_int subslot) { if (vm->platform->oir_start != NULL) return(vm->platform->oir_start(vm,slot,subslot)); /* OIR not supported */ return(-1); } /* OIR to stop a slot/subslot */ int vm_oir_stop(vm_instance_t *vm,u_int slot,u_int subslot) { if (vm->platform->oir_stop != NULL) return(vm->platform->oir_stop(vm,slot,subslot)); /* OIR not supported */ return(-1); } /* Set the JIT translation sharing group */ int vm_set_tsg(vm_instance_t *vm,int group) { if (vm->status == VM_STATUS_RUNNING) return(-1); vm->tsg = group; return(0); } dynamips-0.2.14/unstable/vm.h000066400000000000000000000277221241034141600160400ustar00rootroot00000000000000/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Virtual Machines. */ #ifndef __VM_H__ #define __VM_H__ #include #include "dynamips.h" #include "memory.h" #include "cpu.h" #include "dev_vtty.h" #include "cisco_eeprom.h" #include "cisco_card.h" #include "rommon_var.h" #define VM_PAGE_SHIFT 12 #define VM_PAGE_SIZE (1 << VM_PAGE_SHIFT) #define VM_PAGE_IMASK (VM_PAGE_SIZE - 1) #define VM_PAGE_MASK (~(VM_PAGE_IMASK)) /* Number of pages in chunk area */ #define VM_CHUNK_AREA_SIZE 256 /* VM memory chunk */ typedef struct vm_chunk vm_chunk_t; struct vm_chunk { void *area; u_int page_alloc,page_total; vm_chunk_t *next; }; /* VM ghost pool entry */ typedef struct vm_ghost_image vm_ghost_image_t; struct vm_ghost_image { char *filename; u_int ref_count; int fd; off_t file_size; u_char *area_ptr; vm_ghost_image_t *next; }; /* Maximum number of devices per VM */ #define VM_DEVICE_MAX (1 << 6) /* Size of the PCI bus pool */ #define VM_PCI_POOL_SIZE 32 /* VM instance status */ enum { VM_STATUS_HALTED = 0, /* VM is halted and no HW resources are used */ VM_STATUS_SHUTDOWN, /* Shutdown procedure engaged */ VM_STATUS_RUNNING, /* VM is running */ VM_STATUS_SUSPENDED, /* VM is suspended */ }; /* Ghost RAM status */ enum { VM_GHOST_RAM_NONE = 0, VM_GHOST_RAM_GENERATE, VM_GHOST_RAM_USE, }; /* Timer IRQ check interval */ #define VM_TIMER_IRQ_CHECK_ITV 1000 /* Max slots per VM */ #define VM_MAX_SLOTS 16 /* forward declarations */ typedef struct vm_obj vm_obj_t; /* Shutdown function prototype for an object */ typedef void *(*vm_shutdown_t)(vm_instance_t *vm,void *data); /* VM object, used to keep track of devices and various things */ struct vm_obj { char *name; void *data; struct vm_obj *next,**pprev; vm_shutdown_t shutdown; }; /* VM instance */ struct vm_instance { char *name; vm_platform_t *platform; /* Platform specific helpers */ int status; /* Instance status */ int instance_id; /* Instance Identifier */ char *lock_file; /* Lock file */ char *log_file; /* Log filename */ int log_file_enabled; /* Logging enabled */ u_int ram_size,rom_size; /* RAM and ROM size in Mb */ u_int ram_res_size; /* RAM reserved space size */ u_int iomem_size; /* IOMEM size in Mb */ u_int nvram_size; /* NVRAM size in Kb */ u_int pcmcia_disk_size[2]; /* PCMCIA disk0 and disk1 sizes (in Mb) */ u_int conf_reg,conf_reg_setup; /* Config register */ u_int clock_divisor; /* Clock Divisor (see cp0.c) */ u_int ram_mmap; /* Memory-mapped RAM ? */ u_int restart_ios; /* Restart IOS on reload ? */ u_int elf_machine_id; /* ELF machine identifier */ u_int exec_area_size; /* Size of execution area for CPU */ m_uint32_t ios_entry_point; /* IOS entry point */ char *ios_image; /* IOS image filename */ char *ios_startup_config; /* IOS configuration file for startup-config */ char *ios_private_config; /* IOS configuration file for private-config */ char *rom_filename; /* ROM filename */ char *sym_filename; /* Symbol filename */ FILE *lock_fd,*log_fd; /* Lock/Log file descriptors */ int debug_level; /* Debugging Level */ int jit_use; /* CPUs use JIT */ int sparse_mem; /* Use sparse virtual memory */ u_int nm_iomem_size; /* IO mem size to be passed to Smart Init */ /* ROMMON variables */ struct rommon_var_list rommon_vars; /* Memory chunks */ vm_chunk_t *chunks; /* Basic hardware: system CPU, PCI busses and PCI I/O space */ cpu_group_t *cpu_group; cpu_gen_t *boot_cpu; struct pci_bus *pci_bus[2]; struct pci_bus *pci_bus_pool[VM_PCI_POOL_SIZE]; struct pci_io_data *pci_io_space; /* Memory mapped devices */ struct vdevice *dev_list; struct vdevice *dev_array[VM_DEVICE_MAX]; /* IRQ routing */ void (*set_irq)(vm_instance_t *vm,u_int irq); void (*clear_irq)(vm_instance_t *vm,u_int irq); /* Slots for PA/NM/... */ u_int nr_slots; u_int slots_type; struct cisco_card *slots[VM_MAX_SLOTS]; struct cisco_card_driver **slots_drivers; struct pci_bus *slots_pci_bus[VM_MAX_SLOTS]; /* Filename for ghosted RAM */ char *ghost_ram_filename; /* Ghost RAM image handling */ int ghost_status; /* Timer IRQ interval check */ u_int timer_irq_check_itv; /* Translation sharing group */ int tsg; /* "idling" pointer counter */ m_uint64_t idle_pc; /* JIT block direct jumps */ int exec_blk_direct_jump; /* IRQ idling preemption */ u_int irq_idle_preempt[256]; /* Console and AUX port VTTY type and parameters */ int vtty_con_type,vtty_aux_type; int vtty_con_tcp_port,vtty_aux_tcp_port; vtty_serial_option_t vtty_con_serial_option,vtty_aux_serial_option; /* Virtual TTY for Console and AUX ports */ vtty_t *vtty_con,*vtty_aux; /* Space reserved in NVRAM by ROM monitor */ u_int nvram_rom_space; /* Chassis cookie (for c2600 and maybe other routers) */ m_uint16_t chassis_cookie[64]; /* Specific hardware data */ void *hw_data; /* VM objects */ struct vm_obj *vm_object_list; }; /* VM Platform definition */ struct vm_platform { char *name; char *log_name; char *cli_name; int (*create_instance)(vm_instance_t *vm); int (*delete_instance)(vm_instance_t *vm); int (*init_instance)(vm_instance_t *vm); int (*stop_instance)(vm_instance_t *vm); int (*oir_start)(vm_instance_t *vm,u_int slot_id,u_int subslot_id); int (*oir_stop)(vm_instance_t *vm,u_int slot_id,u_int subslot_id); int (*nvram_extract_config)(vm_instance_t *vm,u_char **startup_config,size_t *startup_len,u_char **private_config,size_t *private_len); int (*nvram_push_config)(vm_instance_t *vm,u_char *startup_config,size_t startup_len,u_char *private_config,size_t private_len); u_int (*get_mac_addr_msb)(void); void (*save_config)(vm_instance_t *vm,FILE *fd); int (*cli_parse_options)(vm_instance_t *vm,int option); void (*cli_show_options)(vm_instance_t *vm); void (*show_spec_drivers)(void); }; /* VM platform list item */ struct vm_platform_list { struct vm_platform_list *next; struct vm_platform *platform; }; extern int vm_file_naming_type; /* Set an IRQ for a VM */ static inline void vm_set_irq(vm_instance_t *vm,u_int irq) { if (vm->set_irq != NULL) vm->set_irq(vm,irq); } /* Clear an IRQ for a VM */ static inline void vm_clear_irq(vm_instance_t *vm,u_int irq) { if (vm->clear_irq != NULL) vm->clear_irq(vm,irq); } /* Initialize a VM object */ void vm_object_init(vm_obj_t *obj); /* Add a VM object to an instance */ void vm_object_add(vm_instance_t *vm,vm_obj_t *obj); /* Remove a VM object from an instance */ void vm_object_remove(vm_instance_t *vm,vm_obj_t *obj); /* Find an object given its name */ vm_obj_t *vm_object_find(vm_instance_t *vm,char *name); /* Check that a mandatory object is present */ int vm_object_check(vm_instance_t *vm,char *name); /* Dump the object list of an instance */ void vm_object_dump(vm_instance_t *vm); /* Get VM type */ char *vm_get_type(vm_instance_t *vm); /* Get MAC address MSB */ u_int vm_get_mac_addr_msb(vm_instance_t *vm); /* Generate a filename for use by the instance */ char *vm_build_filename(vm_instance_t *vm,char *name); /* Get the amount of host virtual memory used by a VM */ size_t vm_get_vspace_size(vm_instance_t *vm); /* Check that an instance lock file doesn't already exist */ int vm_get_lock(vm_instance_t *vm); /* Erase lock file */ void vm_release_lock(vm_instance_t *vm,int erase); /* Log a message */ void vm_flog(vm_instance_t *vm,char *module,char *format,va_list ap); /* Log a message */ void vm_log(vm_instance_t *vm,char *module,char *format,...); /* Close the log file */ int vm_close_log(vm_instance_t *vm); /* Create the log file */ int vm_create_log(vm_instance_t *vm); /* Reopen the log file */ int vm_reopen_log(vm_instance_t *vm); /* Error message */ void vm_error(vm_instance_t *vm,char *format,...); /* Shutdown hardware resources used by a VM */ int vm_hardware_shutdown(vm_instance_t *vm); /* Free resources used by a VM */ void vm_free(vm_instance_t *vm); /* Get an instance given a name */ vm_instance_t *vm_acquire(char *name); /* Release a VM (decrement reference count) */ int vm_release(vm_instance_t *vm); /* Initialize RAM */ int vm_ram_init(vm_instance_t *vm,m_uint64_t paddr); /* Initialize VTTY */ int vm_init_vtty(vm_instance_t *vm); /* Delete VTTY */ void vm_delete_vtty(vm_instance_t *vm); /* Bind a device to a virtual machine */ int vm_bind_device(vm_instance_t *vm,struct vdevice *dev); /* Unbind a device from a virtual machine */ int vm_unbind_device(vm_instance_t *vm,struct vdevice *dev); /* Map a device at the specified physical address */ int vm_map_device(vm_instance_t *vm,struct vdevice *dev,m_uint64_t base_addr); /* Set an IRQ for a VM */ void vm_set_irq(vm_instance_t *vm,u_int irq); /* Clear an IRQ for a VM */ void vm_clear_irq(vm_instance_t *vm,u_int irq); /* Suspend a VM instance */ int vm_suspend(vm_instance_t *vm); /* Resume a VM instance */ int vm_resume(vm_instance_t *vm); /* Stop an instance */ int vm_stop(vm_instance_t *vm); /* Monitor an instance periodically */ void vm_monitor(vm_instance_t *vm); /* Allocate an host page */ void *vm_alloc_host_page(vm_instance_t *vm); /* Free an host page */ void vm_free_host_page(vm_instance_t *vm,void *ptr); /* Get a ghost image */ int vm_ghost_image_get(char *filename,u_char **ptr,int *fd); /* Release a ghost image */ int vm_ghost_image_release(int fd); /* Open a VM file and map it in memory */ int vm_mmap_open_file(vm_instance_t *vm,char *name, u_char **ptr,off_t *fsize); /* Open/Create a VM file and map it in memory */ int vm_mmap_create_file(vm_instance_t *vm,char *name,size_t len,u_char **ptr); /* Close a memory mapped file */ int vm_mmap_close_file(int fd,u_char *ptr,size_t len); /* Save the Cisco IOS configuration from NVRAM */ int vm_ios_save_config(vm_instance_t *vm); /* Set Cisco IOS image to use */ int vm_ios_set_image(vm_instance_t *vm,char *ios_image); /* Unset a Cisco IOS configuration file */ void vm_ios_unset_config(vm_instance_t *vm); /* Set Cisco IOS configuration files to use (NULL to keep existing data) */ int vm_ios_set_config(vm_instance_t *vm,const char *startup_filename,const char *private_filename); /* Extract IOS configuration from NVRAM and write it to a file */ int vm_nvram_extract_config(vm_instance_t *vm,char *filename); /* Read IOS configuraton from the files and push it to NVRAM (NULL to keep existing data) */ int vm_nvram_push_config(vm_instance_t *vm,const char *startup_filename,const char *private_filename); /* Save general VM configuration into the specified file */ void vm_save_config(vm_instance_t *vm,FILE *fd); /* Find a platform */ vm_platform_t *vm_platform_find(char *name); /* Find a platform given its CLI name */ vm_platform_t *vm_platform_find_cli_name(char *name); /* Register a platform */ int vm_platform_register(vm_platform_t *platform); /* Create an instance of the specified type */ vm_instance_t *vm_create_instance(char *name,int instance_id,char *type); /* Delete a VM instance */ int vm_delete_instance(char *name); /* Rename a VM instance */ int vm_rename_instance(vm_instance_t *vm, char *name); /* Initialize a VM instance */ int vm_init_instance(vm_instance_t *vm); /* Stop a VM instance */ int vm_stop_instance(vm_instance_t *vm); /* Delete all VM instances */ int vm_delete_all_instances(void); /* Save all VM configs */ int vm_save_config_all(FILE *fd); /* OIR to start a slot/subslot */ int vm_oir_start(vm_instance_t *vm,u_int slot,u_int subslot); /* OIR to stop a slot/subslot */ int vm_oir_stop(vm_instance_t *vm,u_int slot,u_int subslot); /* Set the JIT translation sharing group */ int vm_set_tsg(vm_instance_t *vm,int group); #endif dynamips-0.2.14/unstable/x86-codegen.h000066400000000000000000001357641241034141600174530ustar00rootroot00000000000000/* * x86-codegen.h: Macros for generating x86 code * * Authors: * Paolo Molaro (lupus@ximian.com) * Intel Corporation (ORP Project) * Sergey Chaban (serge@wildwestsoftware.com) * Dietmar Maurer (dietmar@ximian.com) * Patrik Torstensson * * Copyright (C) 2000 Intel Corporation. All rights reserved. * Copyright (C) 2001, 2002 Ximian, Inc. */ #ifndef X86_H #define X86_H #include /* // x86 register numbers */ typedef enum { X86_EAX = 0, X86_ECX = 1, X86_EDX = 2, X86_EBX = 3, X86_ESP = 4, X86_EBP = 5, X86_ESI = 6, X86_EDI = 7, X86_NREG } X86_Reg_No; /* // opcodes for alu instructions */ typedef enum { X86_ADD = 0, X86_OR = 1, X86_ADC = 2, X86_SBB = 3, X86_AND = 4, X86_SUB = 5, X86_XOR = 6, X86_CMP = 7, X86_NALU } X86_ALU_Opcode; /* // opcodes for shift instructions */ typedef enum { X86_SHLD, X86_SHLR, X86_ROL = 0, X86_ROR = 1, X86_RCL = 2, X86_RCR = 3, X86_SHL = 4, X86_SHR = 5, X86_SAR = 7, X86_NSHIFT = 8 } X86_Shift_Opcode; /* // opcodes for floating-point instructions */ typedef enum { X86_FADD = 0, X86_FMUL = 1, X86_FCOM = 2, X86_FCOMP = 3, X86_FSUB = 4, X86_FSUBR = 5, X86_FDIV = 6, X86_FDIVR = 7, X86_NFP = 8 } X86_FP_Opcode; /* // integer conditions codes */ typedef enum { X86_CC_EQ = 0, X86_CC_E = 0, X86_CC_Z = 0, X86_CC_NE = 1, X86_CC_NZ = 1, X86_CC_LT = 2, X86_CC_B = 2, X86_CC_C = 2, X86_CC_NAE = 2, X86_CC_LE = 3, X86_CC_BE = 3, X86_CC_NA = 3, X86_CC_GT = 4, X86_CC_A = 4, X86_CC_NBE = 4, X86_CC_GE = 5, X86_CC_AE = 5, X86_CC_NB = 5, X86_CC_NC = 5, X86_CC_LZ = 6, X86_CC_S = 6, X86_CC_GEZ = 7, X86_CC_NS = 7, X86_CC_P = 8, X86_CC_PE = 8, X86_CC_NP = 9, X86_CC_PO = 9, X86_CC_O = 10, X86_CC_NO = 11, X86_NCC } X86_CC; /* FP status */ enum { X86_FP_C0 = 0x100, X86_FP_C1 = 0x200, X86_FP_C2 = 0x400, X86_FP_C3 = 0x4000, X86_FP_CC_MASK = 0x4500 }; /* FP control word */ enum { X86_FPCW_INVOPEX_MASK = 0x1, X86_FPCW_DENOPEX_MASK = 0x2, X86_FPCW_ZERODIV_MASK = 0x4, X86_FPCW_OVFEX_MASK = 0x8, X86_FPCW_UNDFEX_MASK = 0x10, X86_FPCW_PRECEX_MASK = 0x20, X86_FPCW_PRECC_MASK = 0x300, X86_FPCW_ROUNDC_MASK = 0xc00, /* values for precision control */ X86_FPCW_PREC_SINGLE = 0, X86_FPCW_PREC_DOUBLE = 0x200, X86_FPCW_PREC_EXTENDED = 0x300, /* values for rounding control */ X86_FPCW_ROUND_NEAREST = 0, X86_FPCW_ROUND_DOWN = 0x400, X86_FPCW_ROUND_UP = 0x800, X86_FPCW_ROUND_TOZERO = 0xc00 }; /* // prefix code */ typedef enum { X86_LOCK_PREFIX = 0xF0, X86_REPNZ_PREFIX = 0xF2, X86_REPZ_PREFIX = 0xF3, X86_REP_PREFIX = 0xF3, X86_CS_PREFIX = 0x2E, X86_SS_PREFIX = 0x36, X86_DS_PREFIX = 0x3E, X86_ES_PREFIX = 0x26, X86_FS_PREFIX = 0x64, X86_GS_PREFIX = 0x65, X86_UNLIKELY_PREFIX = 0x2E, X86_LIKELY_PREFIX = 0x3E, X86_OPERAND_PREFIX = 0x66, X86_ADDRESS_PREFIX = 0x67 } X86_Prefix; static const unsigned char x86_cc_unsigned_map [X86_NCC] = { 0x74, /* eq */ 0x75, /* ne */ 0x72, /* lt */ 0x76, /* le */ 0x77, /* gt */ 0x73, /* ge */ 0x78, /* lz */ 0x79, /* gez */ 0x7a, /* p */ 0x7b, /* np */ 0x70, /* o */ 0x71, /* no */ }; static const unsigned char x86_cc_signed_map [X86_NCC] = { 0x74, /* eq */ 0x75, /* ne */ 0x7c, /* lt */ 0x7e, /* le */ 0x7f, /* gt */ 0x7d, /* ge */ 0x78, /* lz */ 0x79, /* gez */ 0x7a, /* p */ 0x7b, /* np */ 0x70, /* o */ 0x71, /* no */ }; typedef union { int val; unsigned char b [4]; } x86_imm_buf; #define X86_NOBASEREG (-1) /* // bitvector mask for callee-saved registers */ #define X86_ESI_MASK (1<> 6) #define x86_modrm_reg(modrm) (((modrm) >> 3) & 0x7) #define x86_modrm_rm(modrm) ((modrm) & 0x7) #define x86_address_byte(inst,m,o,r) do { *(inst)++ = ((((m)&0x03)<<6)|(((o)&0x07)<<3)|(((r)&0x07))); } while (0) #define x86_imm_emit32(inst,imm) \ do { \ x86_imm_buf imb; imb.val = (int) (imm); \ *(inst)++ = imb.b [0]; \ *(inst)++ = imb.b [1]; \ *(inst)++ = imb.b [2]; \ *(inst)++ = imb.b [3]; \ } while (0) #define x86_imm_emit16(inst,imm) do { *(short*)(inst) = (imm); (inst) += 2; } while (0) #define x86_imm_emit8(inst,imm) do { *(inst) = (unsigned char)((imm) & 0xff); ++(inst); } while (0) #define x86_is_imm8(imm) (((int)(imm) >= -128 && (int)(imm) <= 127)) #define x86_is_imm16(imm) (((int)(imm) >= -(1<<16) && (int)(imm) <= ((1<<16)-1))) #define x86_reg_emit(inst,r,regno) do { x86_address_byte ((inst), 3, (r), (regno)); } while (0) #define x86_reg8_emit(inst,r,regno,is_rh,is_rnoh) do {x86_address_byte ((inst), 3, (is_rh)?((r)|4):(r), (is_rnoh)?((regno)|4):(regno));} while (0) #define x86_regp_emit(inst,r,regno) do { x86_address_byte ((inst), 0, (r), (regno)); } while (0) #define x86_mem_emit(inst,r,disp) do { x86_address_byte ((inst), 0, (r), 5); x86_imm_emit32((inst), (disp)); } while (0) #define x86_membase_emit(inst,r,basereg,disp) do {\ if ((basereg) == X86_ESP) { \ if ((disp) == 0) { \ x86_address_byte ((inst), 0, (r), X86_ESP); \ x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ } else if (x86_is_imm8((disp))) { \ x86_address_byte ((inst), 1, (r), X86_ESP); \ x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ x86_imm_emit8 ((inst), (disp)); \ } else { \ x86_address_byte ((inst), 2, (r), X86_ESP); \ x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ x86_imm_emit32 ((inst), (disp)); \ } \ break; \ } \ if ((disp) == 0 && (basereg) != X86_EBP) { \ x86_address_byte ((inst), 0, (r), (basereg)); \ break; \ } \ if (x86_is_imm8((disp))) { \ x86_address_byte ((inst), 1, (r), (basereg)); \ x86_imm_emit8 ((inst), (disp)); \ } else { \ x86_address_byte ((inst), 2, (r), (basereg)); \ x86_imm_emit32 ((inst), (disp)); \ } \ } while (0) #define x86_memindex_emit(inst,r,basereg,disp,indexreg,shift) \ do { \ if ((basereg) == X86_NOBASEREG) { \ x86_address_byte ((inst), 0, (r), 4); \ x86_address_byte ((inst), (shift), (indexreg), 5); \ x86_imm_emit32 ((inst), (disp)); \ } else if ((disp) == 0 && (basereg) != X86_EBP) { \ x86_address_byte ((inst), 0, (r), 4); \ x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ } else if (x86_is_imm8((disp))) { \ x86_address_byte ((inst), 1, (r), 4); \ x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ x86_imm_emit8 ((inst), (disp)); \ } else { \ x86_address_byte ((inst), 2, (r), 4); \ x86_address_byte ((inst), (shift), (indexreg), 5); \ x86_imm_emit32 ((inst), (disp)); \ } \ } while (0) /* * target is the position in the code where to jump to: * target = code; * .. output loop code... * x86_mov_reg_imm (code, X86_EAX, 0); * loop = code; * x86_loop (code, -1); * ... finish method * * patch displacement * x86_patch (loop, target); * * ins should point at the start of the instruction that encodes a target. * the instruction is inspected for validity and the correct displacement * is inserted. */ #define x86_patch(ins,target) \ do { \ unsigned char* pos = (ins) + 1; \ int disp, size = 0; \ switch (*(unsigned char*)(ins)) { \ case 0xe8: case 0xe9: ++size; break; /* call, jump32 */ \ case 0x0f: if (!(*pos >= 0x70 && *pos <= 0x8f)) assert (0); \ ++size; ++pos; break; /* prefix for 32-bit disp */ \ case 0xe0: case 0xe1: case 0xe2: /* loop */ \ case 0xeb: /* jump8 */ \ /* conditional jump opcodes */ \ case 0x70: case 0x71: case 0x72: case 0x73: \ case 0x74: case 0x75: case 0x76: case 0x77: \ case 0x78: case 0x79: case 0x7a: case 0x7b: \ case 0x7c: case 0x7d: case 0x7e: case 0x7f: \ break; \ default: assert (0); \ } \ disp = (target) - pos; \ if (size) x86_imm_emit32 (pos, disp - 4); \ else if (x86_is_imm8 (disp - 1)) x86_imm_emit8 (pos, disp - 1); \ else assert (0); \ } while (0) #define x86_breakpoint(inst) \ do { \ *(inst)++ = 0xcc; \ } while (0) #define x86_clc(inst) do { *(inst)++ =(unsigned char)0xf8; } while (0) #define x86_cld(inst) do { *(inst)++ =(unsigned char)0xfc; } while (0) #define x86_stosb(inst) do { *(inst)++ =(unsigned char)0xaa; } while (0) #define x86_stosl(inst) do { *(inst)++ =(unsigned char)0xab; } while (0) #define x86_stosd(inst) x86_stosl((inst)) #define x86_movsb(inst) do { *(inst)++ =(unsigned char)0xa4; } while (0) #define x86_movsl(inst) do { *(inst)++ =(unsigned char)0xa5; } while (0) #define x86_movsd(inst) x86_movsl((inst)) #define x86_prefix(inst,p) do { *(inst)++ =(unsigned char) (p); } while (0) #define x86_bswap(inst,reg) \ do { \ *(inst)++ = 0x0f; \ *(inst)++ = (unsigned char)0xc8 + (reg); \ } while (0) #define x86_rdtsc(inst) \ do { \ *(inst)++ = 0x0f; \ *(inst)++ = 0x31; \ } while (0) #define x86_cmpxchg_reg_reg(inst,dreg,reg) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xb1; \ x86_reg_emit ((inst), (reg), (dreg)); \ } while (0) #define x86_cmpxchg_mem_reg(inst,mem,reg) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xb1; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_cmpxchg_membase_reg(inst,basereg,disp,reg) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xb1; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_xchg_reg_reg(inst,dreg,reg,size) \ do { \ if ((size) == 1) \ *(inst)++ = (unsigned char)0x86; \ else \ *(inst)++ = (unsigned char)0x87; \ x86_reg_emit ((inst), (reg), (dreg)); \ } while (0) #define x86_xchg_mem_reg(inst,mem,reg,size) \ do { \ if ((size) == 1) \ *(inst)++ = (unsigned char)0x86; \ else \ *(inst)++ = (unsigned char)0x87; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_xchg_membase_reg(inst,basereg,disp,reg,size) \ do { \ if ((size) == 1) \ *(inst)++ = (unsigned char)0x86; \ else \ *(inst)++ = (unsigned char)0x87; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_xadd_reg_reg(inst,dreg,reg,size) \ do { \ *(inst)++ = (unsigned char)0x0F; \ if ((size) == 1) \ *(inst)++ = (unsigned char)0xC0; \ else \ *(inst)++ = (unsigned char)0xC1; \ x86_reg_emit ((inst), (reg), (dreg)); \ } while (0) #define x86_xadd_mem_reg(inst,mem,reg,size) \ do { \ *(inst)++ = (unsigned char)0x0F; \ if ((size) == 1) \ *(inst)++ = (unsigned char)0xC0; \ else \ *(inst)++ = (unsigned char)0xC1; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_xadd_membase_reg(inst,basereg,disp,reg,size) \ do { \ *(inst)++ = (unsigned char)0x0F; \ if ((size) == 1) \ *(inst)++ = (unsigned char)0xC0; \ else \ *(inst)++ = (unsigned char)0xC1; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_inc_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_mem_emit ((inst), 0, (mem)); \ } while (0) #define x86_inc_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ } while (0) #define x86_inc_reg(inst,reg) do { *(inst)++ = (unsigned char)0x40 + (reg); } while (0) #define x86_dec_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_mem_emit ((inst), 1, (mem)); \ } while (0) #define x86_dec_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_membase_emit ((inst), 1, (basereg), (disp)); \ } while (0) #define x86_dec_reg(inst,reg) do { *(inst)++ = (unsigned char)0x48 + (reg); } while (0) #define x86_not_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_mem_emit ((inst), 2, (mem)); \ } while (0) #define x86_not_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_membase_emit ((inst), 2, (basereg), (disp)); \ } while (0) #define x86_not_reg(inst,reg) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_reg_emit ((inst), 2, (reg)); \ } while (0) #define x86_neg_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_mem_emit ((inst), 3, (mem)); \ } while (0) #define x86_neg_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_membase_emit ((inst), 3, (basereg), (disp)); \ } while (0) #define x86_neg_reg(inst,reg) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_reg_emit ((inst), 3, (reg)); \ } while (0) #define x86_nop(inst) do { *(inst)++ = (unsigned char)0x90; } while (0) #define x86_alu_reg_imm(inst,opc,reg,imm) \ do { \ if ((reg) == X86_EAX) { \ *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ x86_imm_emit32 ((inst), (imm)); \ break; \ } \ if (x86_is_imm8((imm))) { \ *(inst)++ = (unsigned char)0x83; \ x86_reg_emit ((inst), (opc), (reg)); \ x86_imm_emit8 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0x81; \ x86_reg_emit ((inst), (opc), (reg)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_alu_mem_imm(inst,opc,mem,imm) \ do { \ if (x86_is_imm8((imm))) { \ *(inst)++ = (unsigned char)0x83; \ x86_mem_emit ((inst), (opc), (mem)); \ x86_imm_emit8 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0x81; \ x86_mem_emit ((inst), (opc), (mem)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_alu_membase_imm(inst,opc,basereg,disp,imm) \ do { \ if (x86_is_imm8((imm))) { \ *(inst)++ = (unsigned char)0x83; \ x86_membase_emit ((inst), (opc), (basereg), (disp)); \ x86_imm_emit8 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0x81; \ x86_membase_emit ((inst), (opc), (basereg), (disp)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_alu_membase8_imm(inst,opc,basereg,disp,imm) \ do { \ *(inst)++ = (unsigned char)0x80; \ x86_membase_emit ((inst), (opc), (basereg), (disp)); \ x86_imm_emit8 ((inst), (imm)); \ } while (0) #define x86_alu_mem_reg(inst,opc,mem,reg) \ do { \ *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_alu_membase_reg(inst,opc,basereg,disp,reg) \ do { \ *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_alu_reg_reg(inst,opc,dreg,reg) \ do { \ *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) /** * @x86_alu_reg8_reg8: * Supports ALU operations between two 8-bit registers. * dreg := dreg opc reg * X86_Reg_No enum is used to specify the registers. * Additionally is_*_h flags are used to specify what part * of a given 32-bit register is used - high (TRUE) or low (FALSE). * For example: dreg = X86_EAX, is_dreg_h = TRUE -> use AH */ #define x86_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) \ do { \ *(inst)++ = (((unsigned char)(opc)) << 3) + 2; \ x86_reg8_emit ((inst), (dreg), (reg), (is_dreg_h), (is_reg_h)); \ } while (0) #define x86_alu_reg_mem(inst,opc,reg,mem) \ do { \ *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_alu_reg_membase(inst,opc,reg,basereg,disp) \ do { \ *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_test_reg_imm(inst,reg,imm) \ do { \ if ((reg) == X86_EAX) { \ *(inst)++ = (unsigned char)0xa9; \ } else { \ *(inst)++ = (unsigned char)0xf7; \ x86_reg_emit ((inst), 0, (reg)); \ } \ x86_imm_emit32 ((inst), (imm)); \ } while (0) #define x86_test_mem_imm(inst,mem,imm) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_mem_emit ((inst), 0, (mem)); \ x86_imm_emit32 ((inst), (imm)); \ } while (0) #define x86_test_membase_imm(inst,basereg,disp,imm) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ x86_imm_emit32 ((inst), (imm)); \ } while (0) #define x86_test_reg_reg(inst,dreg,reg) \ do { \ *(inst)++ = (unsigned char)0x85; \ x86_reg_emit ((inst), (reg), (dreg)); \ } while (0) #define x86_test_mem_reg(inst,mem,reg) \ do { \ *(inst)++ = (unsigned char)0x85; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_test_membase_reg(inst,basereg,disp,reg) \ do { \ *(inst)++ = (unsigned char)0x85; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_shift_reg_imm(inst,opc,reg,imm) \ do { \ if ((imm) == 1) { \ *(inst)++ = (unsigned char)0xd1; \ x86_reg_emit ((inst), (opc), (reg)); \ } else { \ *(inst)++ = (unsigned char)0xc1; \ x86_reg_emit ((inst), (opc), (reg)); \ x86_imm_emit8 ((inst), (imm)); \ } \ } while (0) #define x86_shift_mem_imm(inst,opc,mem,imm) \ do { \ if ((imm) == 1) { \ *(inst)++ = (unsigned char)0xd1; \ x86_mem_emit ((inst), (opc), (mem)); \ } else { \ *(inst)++ = (unsigned char)0xc1; \ x86_mem_emit ((inst), (opc), (mem)); \ x86_imm_emit8 ((inst), (imm)); \ } \ } while (0) #define x86_shift_membase_imm(inst,opc,basereg,disp,imm) \ do { \ if ((imm) == 1) { \ *(inst)++ = (unsigned char)0xd1; \ x86_membase_emit ((inst), (opc), (basereg), (disp)); \ } else { \ *(inst)++ = (unsigned char)0xc1; \ x86_membase_emit ((inst), (opc), (basereg), (disp)); \ x86_imm_emit8 ((inst), (imm)); \ } \ } while (0) #define x86_shift_reg(inst,opc,reg) \ do { \ *(inst)++ = (unsigned char)0xd3; \ x86_reg_emit ((inst), (opc), (reg)); \ } while (0) #define x86_shift_mem(inst,opc,mem) \ do { \ *(inst)++ = (unsigned char)0xd3; \ x86_mem_emit ((inst), (opc), (mem)); \ } while (0) #define x86_shift_membase(inst,opc,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xd3; \ x86_membase_emit ((inst), (opc), (basereg), (disp)); \ } while (0) /* * Multi op shift missing. */ #define x86_shrd_reg(inst,dreg,reg) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xad; \ x86_reg_emit ((inst), (reg), (dreg)); \ } while (0) #define x86_shrd_reg_imm(inst,dreg,reg,shamt) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xac; \ x86_reg_emit ((inst), (reg), (dreg)); \ x86_imm_emit8 ((inst), (shamt)); \ } while (0) #define x86_shld_reg(inst,dreg,reg) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xa5; \ x86_reg_emit ((inst), (reg), (dreg)); \ } while (0) #define x86_shld_reg_imm(inst,dreg,reg,shamt) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xa4; \ x86_reg_emit ((inst), (reg), (dreg)); \ x86_imm_emit8 ((inst), (shamt)); \ } while (0) /* * EDX:EAX = EAX * rm */ #define x86_mul_reg(inst,reg,is_signed) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_reg_emit ((inst), 4 + ((is_signed) ? 1 : 0), (reg)); \ } while (0) #define x86_mul_mem(inst,mem,is_signed) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_mem_emit ((inst), 4 + ((is_signed) ? 1 : 0), (mem)); \ } while (0) #define x86_mul_membase(inst,basereg,disp,is_signed) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_membase_emit ((inst), 4 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ } while (0) /* * r *= rm */ #define x86_imul_reg_reg(inst,dreg,reg) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xaf; \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) #define x86_imul_reg_mem(inst,reg,mem) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xaf; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_imul_reg_membase(inst,reg,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0x0f; \ *(inst)++ = (unsigned char)0xaf; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) /* * dreg = rm * imm */ #define x86_imul_reg_reg_imm(inst,dreg,reg,imm) \ do { \ if (x86_is_imm8 ((imm))) { \ *(inst)++ = (unsigned char)0x6b; \ x86_reg_emit ((inst), (dreg), (reg)); \ x86_imm_emit8 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0x69; \ x86_reg_emit ((inst), (dreg), (reg)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_imul_reg_mem_imm(inst,reg,mem,imm) \ do { \ if (x86_is_imm8 ((imm))) { \ *(inst)++ = (unsigned char)0x6b; \ x86_mem_emit ((inst), (reg), (mem)); \ x86_imm_emit8 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0x69; \ x86_reg_emit ((inst), (reg), (mem)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_imul_reg_membase_imm(inst,reg,basereg,disp,imm) \ do { \ if (x86_is_imm8 ((imm))) { \ *(inst)++ = (unsigned char)0x6b; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ x86_imm_emit8 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0x69; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) /* * divide EDX:EAX by rm; * eax = quotient, edx = remainder */ #define x86_div_reg(inst,reg,is_signed) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_reg_emit ((inst), 6 + ((is_signed) ? 1 : 0), (reg)); \ } while (0) #define x86_div_mem(inst,mem,is_signed) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_mem_emit ((inst), 6 + ((is_signed) ? 1 : 0), (mem)); \ } while (0) #define x86_div_membase(inst,basereg,disp,is_signed) \ do { \ *(inst)++ = (unsigned char)0xf7; \ x86_membase_emit ((inst), 6 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ } while (0) #define x86_mov_mem_reg(inst,mem,reg,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x88; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x89; break; \ default: assert (0); \ } \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_mov_regp_reg(inst,regp,reg,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x88; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x89; break; \ default: assert (0); \ } \ x86_regp_emit ((inst), (reg), (regp)); \ } while (0) #define x86_mov_membase_reg(inst,basereg,disp,reg,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x88; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x89; break; \ default: assert (0); \ } \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x88; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x89; break; \ default: assert (0); \ } \ x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ } while (0) #define x86_mov_reg_reg(inst,dreg,reg,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x8a; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) #define x86_mov_reg_mem(inst,reg,mem,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x8a; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_mov_reg_membase(inst,reg,basereg,disp,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x8a; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) \ do { \ switch ((size)) { \ case 1: *(inst)++ = (unsigned char)0x8a; break; \ case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ case 4: *(inst)++ = (unsigned char)0x8b; break; \ default: assert (0); \ } \ x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ } while (0) /* * Note: x86_clear_reg () chacnges the condition code! */ #define x86_clear_reg(inst,reg) x86_alu_reg_reg((inst), X86_XOR, (reg), (reg)) #define x86_mov_reg_imm(inst,reg,imm) \ do { \ *(inst)++ = (unsigned char)0xb8 + (reg); \ x86_imm_emit32 ((inst), (imm)); \ } while (0) #define x86_mov_mem_imm(inst,mem,imm,size) \ do { \ if ((size) == 1) { \ *(inst)++ = (unsigned char)0xc6; \ x86_mem_emit ((inst), 0, (mem)); \ x86_imm_emit8 ((inst), (imm)); \ } else if ((size) == 2) { \ *(inst)++ = (unsigned char)0x66; \ *(inst)++ = (unsigned char)0xc7; \ x86_mem_emit ((inst), 0, (mem)); \ x86_imm_emit16 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0xc7; \ x86_mem_emit ((inst), 0, (mem)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_mov_membase_imm(inst,basereg,disp,imm,size) \ do { \ if ((size) == 1) { \ *(inst)++ = (unsigned char)0xc6; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ x86_imm_emit8 ((inst), (imm)); \ } else if ((size) == 2) { \ *(inst)++ = (unsigned char)0x66; \ *(inst)++ = (unsigned char)0xc7; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ x86_imm_emit16 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0xc7; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) \ do { \ if ((size) == 1) { \ *(inst)++ = (unsigned char)0xc6; \ x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ x86_imm_emit8 ((inst), (imm)); \ } else if ((size) == 2) { \ *(inst)++ = (unsigned char)0x66; \ *(inst)++ = (unsigned char)0xc7; \ x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ x86_imm_emit16 ((inst), (imm)); \ } else { \ *(inst)++ = (unsigned char)0xc7; \ x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ x86_imm_emit32 ((inst), (imm)); \ } \ } while (0) #define x86_lea_mem(inst,reg,mem) \ do { \ *(inst)++ = (unsigned char)0x8d; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_lea_membase(inst,reg,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0x8d; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_lea_memindex(inst,reg,basereg,disp,indexreg,shift) \ do { \ *(inst)++ = (unsigned char)0x8d; \ x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ } while (0) #define x86_widen_reg(inst,dreg,reg,is_signed,is_half) \ do { \ unsigned char op = 0xb6; \ assert (is_half || X86_IS_BYTE_REG (reg)); \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) op += 0x08; \ if ((is_half)) op += 0x01; \ *(inst)++ = op; \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) #define x86_widen_mem(inst,dreg,mem,is_signed,is_half) \ do { \ unsigned char op = 0xb6; \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) op += 0x08; \ if ((is_half)) op += 0x01; \ *(inst)++ = op; \ x86_mem_emit ((inst), (dreg), (mem)); \ } while (0) #define x86_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) \ do { \ unsigned char op = 0xb6; \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) op += 0x08; \ if ((is_half)) op += 0x01; \ *(inst)++ = op; \ x86_membase_emit ((inst), (dreg), (basereg), (disp)); \ } while (0) #define x86_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) \ do { \ unsigned char op = 0xb6; \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) op += 0x08; \ if ((is_half)) op += 0x01; \ *(inst)++ = op; \ x86_memindex_emit ((inst), (dreg), (basereg), (disp), (indexreg), (shift)); \ } while (0) #define x86_lahf(inst) do { *(inst)++ = (unsigned char)0x9f; } while (0) #define x86_sahf(inst) do { *(inst)++ = (unsigned char)0x9e; } while (0) #define x86_xchg_ah_al(inst) \ do { \ *(inst)++ = (unsigned char)0x86; \ *(inst)++ = (unsigned char)0xe0; \ } while (0) #define x86_cdq(inst) do { *(inst)++ = (unsigned char)0x99; } while (0) #define x86_wait(inst) do { *(inst)++ = (unsigned char)0x9b; } while (0) #define x86_fp_op_mem(inst,opc,mem,is_double) \ do { \ *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ x86_mem_emit ((inst), (opc), (mem)); \ } while (0) #define x86_fp_op_membase(inst,opc,basereg,disp,is_double) \ do { \ *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ x86_membase_emit ((inst), (opc), (basereg), (disp)); \ } while (0) #define x86_fp_op(inst,opc,index) \ do { \ *(inst)++ = (unsigned char)0xd8; \ *(inst)++ = (unsigned char)0xc0+((opc)<<3)+((index)&0x07); \ } while (0) #define x86_fp_op_reg(inst,opc,index,pop_stack) \ do { \ static const unsigned char map[] = { 0, 1, 2, 3, 5, 4, 7, 6, 8}; \ *(inst)++ = (pop_stack) ? (unsigned char)0xde : (unsigned char)0xdc; \ *(inst)++ = (unsigned char)0xc0+(map[(opc)]<<3)+((index)&0x07); \ } while (0) /** * @x86_fp_int_op_membase * Supports FPU operations between ST(0) and integer operand in memory. * Operation encoded using X86_FP_Opcode enum. * Operand is addressed by [basereg + disp]. * is_int specifies whether operand is int32 (TRUE) or int16 (FALSE). */ #define x86_fp_int_op_membase(inst,opc,basereg,disp,is_int) \ do { \ *(inst)++ = (is_int) ? (unsigned char)0xda : (unsigned char)0xde; \ x86_membase_emit ((inst), opc, (basereg), (disp)); \ } while (0) #define x86_fstp(inst,index) \ do { \ *(inst)++ = (unsigned char)0xdd; \ *(inst)++ = (unsigned char)0xd8+(index); \ } while (0) #define x86_fcompp(inst) \ do { \ *(inst)++ = (unsigned char)0xde; \ *(inst)++ = (unsigned char)0xd9; \ } while (0) #define x86_fucompp(inst) \ do { \ *(inst)++ = (unsigned char)0xda; \ *(inst)++ = (unsigned char)0xe9; \ } while (0) #define x86_fnstsw(inst) \ do { \ *(inst)++ = (unsigned char)0xdf; \ *(inst)++ = (unsigned char)0xe0; \ } while (0) #define x86_fnstcw(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xd9; \ x86_mem_emit ((inst), 7, (mem)); \ } while (0) #define x86_fnstcw_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xd9; \ x86_membase_emit ((inst), 7, (basereg), (disp)); \ } while (0) #define x86_fldcw(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xd9; \ x86_mem_emit ((inst), 5, (mem)); \ } while (0) #define x86_fldcw_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xd9; \ x86_membase_emit ((inst), 5, (basereg), (disp)); \ } while (0) #define x86_fchs(inst) \ do { \ *(inst)++ = (unsigned char)0xd9; \ *(inst)++ = (unsigned char)0xe0; \ } while (0) #define x86_frem(inst) \ do { \ *(inst)++ = (unsigned char)0xd9; \ *(inst)++ = (unsigned char)0xf8; \ } while (0) #define x86_fxch(inst,index) \ do { \ *(inst)++ = (unsigned char)0xd9; \ *(inst)++ = (unsigned char)0xc8 + ((index) & 0x07); \ } while (0) #define x86_fcomi(inst,index) \ do { \ *(inst)++ = (unsigned char)0xdb; \ *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ } while (0) #define x86_fcomip(inst,index) \ do { \ *(inst)++ = (unsigned char)0xdf; \ *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ } while (0) #define x86_fucomi(inst,index) \ do { \ *(inst)++ = (unsigned char)0xdb; \ *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ } while (0) #define x86_fucomip(inst,index) \ do { \ *(inst)++ = (unsigned char)0xdf; \ *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ } while (0) #define x86_fld(inst,mem,is_double) \ do { \ *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ x86_mem_emit ((inst), 0, (mem)); \ } while (0) #define x86_fld_membase(inst,basereg,disp,is_double) \ do { \ *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ } while (0) #define x86_fld80_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xdb; \ x86_mem_emit ((inst), 5, (mem)); \ } while (0) #define x86_fld80_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xdb; \ x86_membase_emit ((inst), 5, (basereg), (disp)); \ } while (0) #define x86_fild(inst,mem,is_long) \ do { \ if ((is_long)) { \ *(inst)++ = (unsigned char)0xdf; \ x86_mem_emit ((inst), 5, (mem)); \ } else { \ *(inst)++ = (unsigned char)0xdb; \ x86_mem_emit ((inst), 0, (mem)); \ } \ } while (0) #define x86_fild_membase(inst,basereg,disp,is_long) \ do { \ if ((is_long)) { \ *(inst)++ = (unsigned char)0xdf; \ x86_membase_emit ((inst), 5, (basereg), (disp)); \ } else { \ *(inst)++ = (unsigned char)0xdb; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ } \ } while (0) #define x86_fld_reg(inst,index) \ do { \ *(inst)++ = (unsigned char)0xd9; \ *(inst)++ = (unsigned char)0xc0 + ((index) & 0x07); \ } while (0) #define x86_fldz(inst) \ do { \ *(inst)++ = (unsigned char)0xd9; \ *(inst)++ = (unsigned char)0xee; \ } while (0) #define x86_fld1(inst) \ do { \ *(inst)++ = (unsigned char)0xd9; \ *(inst)++ = (unsigned char)0xe8; \ } while (0) #define x86_fldpi(inst) \ do { \ *(inst)++ = (unsigned char)0xd9; \ *(inst)++ = (unsigned char)0xeb; \ } while (0) #define x86_fst(inst,mem,is_double,pop_stack) \ do { \ *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ x86_mem_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (mem)); \ } while (0) #define x86_fst_membase(inst,basereg,disp,is_double,pop_stack) \ do { \ *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ x86_membase_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (basereg), (disp)); \ } while (0) #define x86_fst80_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xdb; \ x86_mem_emit ((inst), 7, (mem)); \ } while (0) #define x86_fst80_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xdb; \ x86_membase_emit ((inst), 7, (basereg), (disp)); \ } while (0) #define x86_fist_pop(inst,mem,is_long) \ do { \ if ((is_long)) { \ *(inst)++ = (unsigned char)0xdf; \ x86_mem_emit ((inst), 7, (mem)); \ } else { \ *(inst)++ = (unsigned char)0xdb; \ x86_mem_emit ((inst), 3, (mem)); \ } \ } while (0) #define x86_fist_pop_membase(inst,basereg,disp,is_long) \ do { \ if ((is_long)) { \ *(inst)++ = (unsigned char)0xdf; \ x86_membase_emit ((inst), 7, (basereg), (disp)); \ } else { \ *(inst)++ = (unsigned char)0xdb; \ x86_membase_emit ((inst), 3, (basereg), (disp)); \ } \ } while (0) #define x86_fstsw(inst) \ do { \ *(inst)++ = (unsigned char)0x9b; \ *(inst)++ = (unsigned char)0xdf; \ *(inst)++ = (unsigned char)0xe0; \ } while (0) /** * @x86_fist_membase * Converts content of ST(0) to integer and stores it at memory location * addressed by [basereg + disp]. * is_int specifies whether destination is int32 (TRUE) or int16 (FALSE). */ #define x86_fist_membase(inst,basereg,disp,is_int) \ do { \ if ((is_int)) { \ *(inst)++ = (unsigned char)0xdb; \ x86_membase_emit ((inst), 2, (basereg), (disp)); \ } else { \ *(inst)++ = (unsigned char)0xdf; \ x86_membase_emit ((inst), 2, (basereg), (disp)); \ } \ } while (0) #define x86_push_reg(inst,reg) \ do { \ *(inst)++ = (unsigned char)0x50 + (reg); \ } while (0) #define x86_push_regp(inst,reg) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_regp_emit ((inst), 6, (reg)); \ } while (0) #define x86_push_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_mem_emit ((inst), 6, (mem)); \ } while (0) #define x86_push_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_membase_emit ((inst), 6, (basereg), (disp)); \ } while (0) #define x86_push_memindex(inst,basereg,disp,indexreg,shift) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_memindex_emit ((inst), 6, (basereg), (disp), (indexreg), (shift)); \ } while (0) #define x86_push_imm_template(inst) x86_push_imm (inst, 0xf0f0f0f0) #define x86_push_imm(inst,imm) \ do { \ int _imm = (int) (imm); \ if (x86_is_imm8 (_imm)) { \ *(inst)++ = (unsigned char)0x6A; \ x86_imm_emit8 ((inst), (_imm)); \ } else { \ *(inst)++ = (unsigned char)0x68; \ x86_imm_emit32 ((inst), (_imm)); \ } \ } while (0) #define x86_pop_reg(inst,reg) \ do { \ *(inst)++ = (unsigned char)0x58 + (reg); \ } while (0) #define x86_pop_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0x87; \ x86_mem_emit ((inst), 0, (mem)); \ } while (0) #define x86_pop_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0x87; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ } while (0) #define x86_pushad(inst) do { *(inst)++ = (unsigned char)0x60; } while (0) #define x86_pushfd(inst) do { *(inst)++ = (unsigned char)0x9c; } while (0) #define x86_popad(inst) do { *(inst)++ = (unsigned char)0x61; } while (0) #define x86_popfd(inst) do { *(inst)++ = (unsigned char)0x9d; } while (0) #define x86_loop(inst,imm) \ do { \ *(inst)++ = (unsigned char)0xe2; \ x86_imm_emit8 ((inst), (imm)); \ } while (0) #define x86_loope(inst,imm) \ do { \ *(inst)++ = (unsigned char)0xe1; \ x86_imm_emit8 ((inst), (imm)); \ } while (0) #define x86_loopne(inst,imm) \ do { \ *(inst)++ = (unsigned char)0xe0; \ x86_imm_emit8 ((inst), (imm)); \ } while (0) #define x86_jump32(inst,imm) \ do { \ *(inst)++ = (unsigned char)0xe9; \ x86_imm_emit32 ((inst), (imm)); \ } while (0) #define x86_jump8(inst,imm) \ do { \ *(inst)++ = (unsigned char)0xeb; \ x86_imm_emit8 ((inst), (imm)); \ } while (0) #define x86_jump_reg(inst,reg) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_reg_emit ((inst), 4, (reg)); \ } while (0) #define x86_jump_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_mem_emit ((inst), 4, (mem)); \ } while (0) #define x86_jump_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_membase_emit ((inst), 4, (basereg), (disp)); \ } while (0) /* * target is a pointer in our buffer. */ #define x86_jump_code(inst,target) \ do { \ int t = (unsigned char*)(target) - (inst) - 2; \ if (x86_is_imm8(t)) { \ x86_jump8 ((inst), t); \ } else { \ t -= 3; \ x86_jump32 ((inst), t); \ } \ } while (0) #define x86_jump_disp(inst,disp) \ do { \ int t = (disp) - 2; \ if (x86_is_imm8(t)) { \ x86_jump8 ((inst), t); \ } else { \ t -= 3; \ x86_jump32 ((inst), t); \ } \ } while (0) #define x86_branch8(inst,cond,imm,is_signed) \ do { \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)]; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)]; \ x86_imm_emit8 ((inst), (imm)); \ } while (0) #define x86_branch32(inst,cond,imm,is_signed) \ do { \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)] + 0x10; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x10; \ x86_imm_emit32 ((inst), (imm)); \ } while (0) #define x86_branch(inst,cond,target,is_signed) \ do { \ int offset = (target) - (inst) - 2; \ if (x86_is_imm8 ((offset))) \ x86_branch8 ((inst), (cond), offset, (is_signed)); \ else { \ offset -= 4; \ x86_branch32 ((inst), (cond), offset, (is_signed)); \ } \ } while (0) #define x86_branch_disp(inst,cond,disp,is_signed) \ do { \ int offset = (disp) - 2; \ if (x86_is_imm8 ((offset))) \ x86_branch8 ((inst), (cond), offset, (is_signed)); \ else { \ offset -= 4; \ x86_branch32 ((inst), (cond), offset, (is_signed)); \ } \ } while (0) #define x86_set_reg(inst,cond,reg,is_signed) \ do { \ assert (X86_IS_BYTE_REG (reg)); \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ x86_reg_emit ((inst), 0, (reg)); \ } while (0) #define x86_set_mem(inst,cond,mem,is_signed) \ do { \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ x86_mem_emit ((inst), 0, (mem)); \ } while (0) #define x86_set_membase(inst,cond,basereg,disp,is_signed) \ do { \ *(inst)++ = (unsigned char)0x0f; \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ x86_membase_emit ((inst), 0, (basereg), (disp)); \ } while (0) #define x86_call_imm(inst,disp) \ do { \ *(inst)++ = (unsigned char)0xe8; \ x86_imm_emit32 ((inst), (int)(disp)); \ } while (0) #define x86_call_reg(inst,reg) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_reg_emit ((inst), 2, (reg)); \ } while (0) #define x86_call_mem(inst,mem) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_mem_emit ((inst), 2, (mem)); \ } while (0) #define x86_call_membase(inst,basereg,disp) \ do { \ *(inst)++ = (unsigned char)0xff; \ x86_membase_emit ((inst), 2, (basereg), (disp)); \ } while (0) #define x86_call_code(inst,target) \ do { \ int _x86_offset = (unsigned char*)(target) - (inst); \ _x86_offset -= 5; \ x86_call_imm ((inst), _x86_offset); \ } while (0) #define x86_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) #define x86_ret_imm(inst,imm) \ do { \ if ((imm) == 0) { \ x86_ret ((inst)); \ } else { \ *(inst)++ = (unsigned char)0xc2; \ x86_imm_emit16 ((inst), (imm)); \ } \ } while (0) #define x86_cmov_reg(inst,cond,is_signed,dreg,reg) \ do { \ *(inst)++ = (unsigned char) 0x0f; \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ x86_reg_emit ((inst), (dreg), (reg)); \ } while (0) #define x86_cmov_mem(inst,cond,is_signed,reg,mem) \ do { \ *(inst)++ = (unsigned char) 0x0f; \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ x86_mem_emit ((inst), (reg), (mem)); \ } while (0) #define x86_cmov_membase(inst,cond,is_signed,reg,basereg,disp) \ do { \ *(inst)++ = (unsigned char) 0x0f; \ if ((is_signed)) \ *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ else \ *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ x86_membase_emit ((inst), (reg), (basereg), (disp)); \ } while (0) #define x86_enter(inst,framesize) \ do { \ *(inst)++ = (unsigned char)0xc8; \ x86_imm_emit16 ((inst), (framesize)); \ *(inst)++ = 0; \ } while (0) #define x86_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) #define x86_sahf(inst) do { *(inst)++ = (unsigned char)0x9e; } while (0) #define x86_fsin(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfe; } while (0) #define x86_fcos(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xff; } while (0) #define x86_fabs(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe1; } while (0) #define x86_ftst(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe4; } while (0) #define x86_fxam(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe5; } while (0) #define x86_fpatan(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf3; } while (0) #define x86_fprem(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf8; } while (0) #define x86_fprem1(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf5; } while (0) #define x86_frndint(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfc; } while (0) #define x86_fsqrt(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfa; } while (0) #define x86_fptan(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf2; } while (0) #define x86_padding(inst,size) \ do { \ switch ((size)) { \ case 1: x86_nop ((inst)); break; \ case 2: *(inst)++ = 0x8b; \ *(inst)++ = 0xc0; break; \ case 3: *(inst)++ = 0x8d; *(inst)++ = 0x6d; \ *(inst)++ = 0x00; break; \ case 4: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ *(inst)++ = 0x24; *(inst)++ = 0x00; \ break; \ case 5: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ *(inst)++ = 0x24; *(inst)++ = 0x00; \ x86_nop ((inst)); break; \ case 6: *(inst)++ = 0x8d; *(inst)++ = 0xad; \ *(inst)++ = 0x00; *(inst)++ = 0x00; \ *(inst)++ = 0x00; *(inst)++ = 0x00; \ break; \ case 7: *(inst)++ = 0x8d; *(inst)++ = 0xa4; \ *(inst)++ = 0x24; *(inst)++ = 0x00; \ *(inst)++ = 0x00; *(inst)++ = 0x00; \ *(inst)++ = 0x00; break; \ default: assert (0); \ } \ } while (0) #define x86_prolog(inst,frame_size,reg_mask) \ do { \ unsigned i, m = 1; \ x86_enter ((inst), (frame_size)); \ for (i = 0; i < X86_NREG; ++i, m <<= 1) { \ if ((reg_mask) & m) \ x86_push_reg ((inst), i); \ } \ } while (0) #define x86_epilog(inst,reg_mask) \ do { \ unsigned i, m = 1 << X86_EDI; \ for (i = X86_EDI; m != 0; i--, m=m>>1) { \ if ((reg_mask) & m) \ x86_pop_reg ((inst), i); \ } \ x86_leave ((inst)); \ x86_ret ((inst)); \ } while (0) static inline void x86_jump_code_fn(u_char **instp,u_char *target) { x86_jump_code(*instp,target); } static inline void x86_patch_fn(u_char *instp,u_char *target) { x86_patch(instp,target); } #endif // X86_H