diff options
93 files changed, 25116 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..79755bb8 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Dominic Rath <Dominic.Rath@gmx.de> diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..5b6e7c66 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + + <signature of Ty Coon>, 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 Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..66c2887f --- /dev/null +++ b/ChangeLog @@ -0,0 +1,7 @@ +2005-07-03 Dominic Rath <Dominic.Rath@gmx.net> + + * First public release + +2005-10-27 Dominic Rath <Dominic.Rath@gmx.net> + + * First release of new codebase diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..f2008737 --- /dev/null +++ b/INSTALL @@ -0,0 +1,194 @@ +Prerequisites +============= + +When building with support for FTDI FT2232 based devices, you need at least +one of the following libraries: + +- libftdi (http://www.intra2net.com/opensource/ftdi/) +- libftd2xx (http://www.ftdichip.com/Drivers/D2XX.htm) + +Basic Installation +================== + + OpenOCD is distributed without autotools generated files, i.e. without a +configure script. Run ./bootstrap in the openocd directory to have all +necessary files generated. + + You have to explicitly enable desired JTAG interfaces during configure: + +./configure --enable-parport --enable-ftdi2232 --enable-ftd2xx \ + --enable-amtjtagaccel + + Under Windows/Cygwin, only the ftd2xx driver is supported for FT2232 based +devices. You have to specify the location of the FTDI driver package with the +--with-ftd2xx=/full/path/name option. + +Under Linux you can choose to build the parport driver with support for +/dev/parportN instead of the default access with direct port I/O using +--enable-parport_ppdev. This has the advantage of running OpenOCD without root +privileges at the expense of a slight performance decrease. + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes a while. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Type `make install' to install the programs and any data files and + documentation. + + 4. You can remove the program binaries and object files from the + source code directory by typing `make clean'. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..02520f20 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,5 @@ +# not a GNU package. You can remove this line, if +# have all needed files, that a GNU package needs +AUTOMAKE_OPTIONS = foreign 1.4 + +SUBDIRS = src @@ -0,0 +1,49 @@ + openocd + + Free and Open On-Chip Debugging, In-System Programming + and Boundary-Scan Testing + Copyright (c) 2004, 2005 Dominic Rath + +The debugger uses an IEEE 1149-1 compliant JTAG TAP bus master to access on-chip +debug functionality available on ARM7 and ARM9 based microcontrollers / +system-on-chip solutions. + +User interaction is realized through a telnet command line interface and a gdb +(The GNU Debugger) remote protocol server. + +Initially, support for two JTAG TAP bus master interfaces with public hardware +schematics will be included, but support of additional hardware is an expressed +goal. + +1. JTAG hardware + +Currently, openocd contains support for Wiggler-compatible paralell port +dongles and a USB interface based on the FTDI FT2232, called USBJTAG-1. +A new version of the USB interface, USB-JTAG v1.2, is available with complete +schematics (http://www.fh-augsburg.de/~hhoegl/proj/volksmikro/usb-jtag/050910/). + +It was tested using Amontec's (www.amontec.com) Chameleon POD in it's +Wiggler configuration, but homemade wigglers should work just as well. +In order to use the reset functionality (warm-reset, debug from reset, reset +and init), the choosen Wiggler has to connect the nSRST line. + +USBJTAG-1 is based on a FTDI DLP2232M module and a few additional parts. +Schematics are freely available. USB-JTAG v1.2 doesn't use the DLP2232M, but +has the FTDI chip soldered directly on the PCB. There are two drivers for these +modules implemented, one using the open source libftdi, the other using FTDI's +proprietary FTD2XX library. + +2. Supported cores + +This version of openocd supports the following cores: + +- ARM7TDMI +- ARM9TDMI + +Support for cores with MMUs (ARM720t, ARM920t) is currently being merged. + +3. Licensing + +openocd is licensed under the terms of the GNU General Public License, see the +file COPYING for details. + @@ -0,0 +1,7 @@ +- Additional cores. ARM9E(J)-S, ARM7TDMI-S, TI925, ... +- Testing. +- Additional jtag interfaces. Currently, only Wiggler style interfaces and + USBJTAG-1 are supported. +- Testing. +- Handle endianess. The configuration variable is there, but that's about it. + Currently, only little-endian targets and little-endian hosts are supported. diff --git a/bootstrap b/bootstrap new file mode 100755 index 00000000..f7c4c0fa --- /dev/null +++ b/bootstrap @@ -0,0 +1,4 @@ +aclocal \ +&& autoheader \ +&& automake --gnu --add-missing \ +&& autoconf diff --git a/configure.in b/configure.in new file mode 100644 index 00000000..db378068 --- /dev/null +++ b/configure.in @@ -0,0 +1,112 @@ +AC_INIT(configure.in) + +AC_SEARCH_LIBS([ioperm], [ioperm]) + +AC_CANONICAL_HOST + +build_bitbang=no +is_cygwin=no + +AC_ARG_ENABLE(parport, + AS_HELP_STRING([--enable-parport], [Enable building the pc parallel port driver]), + [build_parport=$enableval], [build_parport=no]) + +AC_ARG_ENABLE(parport_ppdev, + AS_HELP_STRING([--enable-parport_ppdev], [Enable use of ppdev (/dev/parportN) for parport]), + [parport_use_ppdev=$enableval], [parport_use_ppdev=no]) + +AC_ARG_ENABLE(ftdi2232, + AS_HELP_STRING([--enable-ftdi2232], [Enable building the libftdi ft2232c driver]), + [build_ftdi2232=$enableval], [build_ftdi2232=no]) + +AC_ARG_ENABLE(ftd2xx, + AS_HELP_STRING([--enable-ftd2xx], [Enable building the ftd2xx ft2232c driver]), + [build_ftd2xx=$enableval], [build_ftd2xx=no]) + +AC_ARG_ENABLE(amtjtagaccel, + AS_HELP_STRING([--enable-amtjtagaccel], [Enable building the Amontec JTAG-Accelerator driver]), + [build_amtjtagaccel=$enableval], [build_amtjtagaccel=no]) + +AC_ARG_ENABLE(ep93xx, + AS_HELP_STRING([--enable-ep93xx], [Enable building support for EP93xx based SBCs]), + [build_ep93xx=$enableval], [build_ep93xx=no]) + +AC_ARG_WITH(ftd2xx, + [AS_HELP_STRING(--with-ftd2xx, + [Where libftd2xx can be found <default=search>])], + [], + with_ftd2xx=search) + +if test $build_parport = yes; then + build_bitbang=yes + AC_DEFINE(BUILD_PARPORT, 1, [1 if you want parport.]) +else + AC_DEFINE(BUILD_PARPORT, 0, [0 if you don't want parport.]) +fi + +if test $build_ep93xx = yes; then + build_bitbang=yes + AC_DEFINE(BUILD_EP93XX, 1, [1 if you want ep93xx.]) +else + AC_DEFINE(BUILD_EP93XX, 0, [0 if you don't want ep93xx.]) +fi + +if test $parport_use_ppdev = yes; then + AC_DEFINE(PARPORT_USE_PPDEV, 1, [1 if you want parport to use ppdev.]) +else + AC_DEFINE(PARPORT_USE_PPDEV, 0, [0 if you don't want parport to use ppdev.]) +fi + +if test $build_bitbang = yes; then + AC_DEFINE(BUILD_BITBANG, 1, [1 if you want a bitbang interface.]) +else + AC_DEFINE(BUILD_BITBANG, 0, [0 if you don't want a bitbang interface.]) +fi + +if test $build_ftdi2232 = yes; then + AC_DEFINE(BUILD_FTDI2232, 1, [1 if you want libftdi ft2232.]) +else + AC_DEFINE(BUILD_FTDI2232, 0, [0 if you don't want libftdi ft2232.]) +fi + +if test $build_ftd2xx = yes; then + AC_DEFINE(BUILD_FTD2XX, 1, [1 if you want ftd2xx ft2232.]) +else + AC_DEFINE(BUILD_FTD2XX, 0, [0 if you don't want ftd2xx ft2232.]) +fi + +if test $build_amtjtagaccel = yes; then + AC_DEFINE(BUILD_AMTJTAGACCEL, 1, [1 if you want the Amontec JTAG-Accelerator driver.]) +else + AC_DEFINE(BUILD_AMTJTAGACCEL, 0, [0 if you don't want the Amontec JTAG-Accelerator driver.]) +fi + +case $host in + *-*-cygwin*) + is_cygwin=yes + AC_DEFINE(IS_CYGWIN, 1, [1 if building for Cygwin.]) + ;; + *) + AC_DEFINE(IS_CYGWIN, 0, [0 if not building for Cygwin.]) + ;; +esac + +AM_CONFIG_HEADER(config.h) +AM_INIT_AUTOMAKE(openocd, 0.1) + +AM_CONDITIONAL(PARPORT, test $build_parport = yes) +AM_CONDITIONAL(EP93XX, test $build_ep93xx = yes) +AM_CONDITIONAL(BITBANG, test $build_bitbang = yes) +AM_CONDITIONAL(FTDI2232, test $build_ftdi2232 = yes) +AM_CONDITIONAL(FTD2XX, test $build_ftd2xx = yes) +AM_CONDITIONAL(AMTJTAGACCEL, test $build_amtjtagaccel = yes) +AM_CONDITIONAL(IS_CYGWIN, test $is_cygwin = yes) +AM_CONDITIONAL(FTD2XXDIR, test $with_ftd2xx != search) + +AC_LANG_C +AC_PROG_CC +AC_PROG_RANLIB + +AC_SUBST(WITH_FTD2XX, $with_ftd2xx) + +AC_OUTPUT(Makefile src/Makefile src/helper/Makefile src/jtag/Makefile src/xsvf/Makefile src/target/Makefile src/server/Makefile src/flash/Makefile) diff --git a/doc/configs/arm7_ft2232.cfg b/doc/configs/arm7_ft2232.cfg new file mode 100644 index 00000000..369d09b1 --- /dev/null +++ b/doc/configs/arm7_ft2232.cfg @@ -0,0 +1,26 @@ +#daemon configuration +telnet_port 4444 +gdb_port 3333 + +#interface +interface ftdi2232 +jtag_speed 0 +#use combined on interfaces or targets that can't set TRST/SRST separately +reset_config trst_and_srst srst_pulls_trst + +#jtag scan chain +#format L IRC IRCM IDCODE (Length, IR Capture, IR Capture Mask, IDCODE) +jtag_device 4 0x1 0xf 0xe + +#target configuration +daemon_startup reset +#target <type> <startup mode> +#target arm7tdmi <reset mode> <chainpos> <endianness> <variant> +target arm7tdmi little run_and_halt 0 arm7tdmi-s_r4 +target_script 0 reset h2294_init.script +run_and_halt_time 0 30 +working_area 0 0x40000000 0x40000 nobackup + +#flash configuration +flash bank lpc2000 0x0 0x40000 0 0 lpc2000_v1 0 14765 calc_checksum +flash bank cfi 0x80000000 0x400000 2 2 0 diff --git a/doc/configs/arm7_ftd2xx.cfg b/doc/configs/arm7_ftd2xx.cfg new file mode 100644 index 00000000..59dad02f --- /dev/null +++ b/doc/configs/arm7_ftd2xx.cfg @@ -0,0 +1,29 @@ +#daemon configuration +telnet_port 4444 +gdb_port 3333 + +#interface +interface ftd2xx +ftd2xx_device_desc "Amontec JTAGkey A" +ftd2xx_layout jtagkey +ftd2xx_vid_pid 0x0403 0xcff8 +jtag_speed 2 +#use combined on interfaces or targets that can't set TRST/SRST separately +reset_config trst_and_srst srst_pulls_trst + +#jtag scan chain +#format L IRC IRCM IDCODE (Length, IR Capture, IR Capture Mask, IDCODE) +jtag_device 4 0x1 0xf 0xe + +#target configuration +daemon_startup reset +#target <type> <startup mode> +#target arm7tdmi <reset mode> <chainpos> <endianness> <variant> +target arm7tdmi little run_and_halt 0 arm7tdmi-s_r4 +target_script 0 reset h2294_init.script +run_and_halt_time 0 30 +working_area 0 0x40000000 0x40000 nobackup + +#flash configuration +flash bank lpc2000 0x0 0x40000 0 0 lpc2000_v1 0 14765 calc_checksum +flash bank cfi 0x80000000 0x400000 2 2 0 diff --git a/doc/configs/arm7_wig.cfg b/doc/configs/arm7_wig.cfg new file mode 100644 index 00000000..c1e6bf9e --- /dev/null +++ b/doc/configs/arm7_wig.cfg @@ -0,0 +1,28 @@ +#daemon configuration +telnet_port 4444 +gdb_port 3333 + +#interface +interface parport +parport_port 0x378 +parport_cable wiggler +jtag_speed 0 +#use combined on interfaces or targets that can't set TRST/SRST separately +reset_config trst_and_srst srst_pulls_trst + +#jtag scan chain +#format L IRC IRCM IDCODE (Length, IR Capture, IR Capture Mask, IDCODE) +jtag_device 4 0x1 0xf 0xe + +#target configuration +daemon_startup reset +#target <type> <startup mode> +#target arm7tdmi <reset mode> <chainpos> <endianness> <variant> +target arm7tdmi little run_and_halt 0 arm7tdmi-s_r4 +target_script 0 reset h2294_init.script +run_and_halt_time 0 30 +working_area 0 0x40000000 0x40000 nobackup + +#flash configuration +flash bank lpc2000 0x0 0x40000 0 0 lpc2000_v1 0 14765 calc_checksum +flash bank cfi 0x80000000 0x400000 2 2 0 diff --git a/doc/configs/arm9_ftd2xx.cfg b/doc/configs/arm9_ftd2xx.cfg new file mode 100644 index 00000000..84885dd2 --- /dev/null +++ b/doc/configs/arm9_ftd2xx.cfg @@ -0,0 +1,28 @@ +#daemon configuration +telnet_port 4444 +gdb_port 3333 + +#interface +interface ftd2xx +ftd2xx_device_desc "Amontec JTAGkey A" +ftd2xx_layout "jtagkey" +ftd2xx_vid_pid 0x0403 0xcff8 +jtag_speed 1 +#use combined on interfaces or targets that can't set TRST/SRST separately +reset_config trst_and_srst + +#jtag scan chain +#format L IRC IRCM IDCODE (Length, IR Capture, IR Capture Mask, IDCODE) +jtag_device 4 0x1 0xf 0xe + +#target configuration +daemon_startup reset +#target <type> <endianess> <reset mode> +target arm9tdmi little reset_halt 0 arm920t +working_area 0 0x200000 0x4000 backup +run_and_halt_time 0 5000 + +#flash configuration +#flash bank <driver> <base> <size> <chip_width> <bus_width> [driver_options ...] +flash bank cfi 0x10000000 0x800000 2 2 0 + diff --git a/doc/configs/chameleon.cfg b/doc/configs/chameleon.cfg new file mode 100644 index 00000000..94d581c6 --- /dev/null +++ b/doc/configs/chameleon.cfg @@ -0,0 +1,12 @@ +#daemon configuration +telnet_port 4444 +gdb_port 3333 + +#interface +interface parport +parport_cable chameleon +jtag_speed 0 + +#jtag scan chain +# format L IRC IRCM IDCODE (Length, IR Capture, IR Capture Mask, IDCODE) +jtag_device 5 0x01 0x1f 0x01 diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..e1973827 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,40 @@ +bin_PROGRAMS = openocd +openocd_SOURCES = openocd.c + +# set the include path found by configure +INCLUDES = -I$(top_srcdir)/src/helper \ + -I$(top_srcdir)/src/jtag -I$(top_srcdir)/src/target -I$(top_srcdir)/src/xsvf -I$(top_srcdir)/src/server \ + -I$(top_srcdir)/src/flash $(all_includes) + +# the library search path. +openocd_LDFLAGS = $(all_libraries) +SUBDIRS = helper jtag xsvf target server flash + +if FTDI2232 +FTDI2232LIB = -lftdi +else +FTDI2232LIB = +endif + +if IS_CYGWIN +if FTD2XXDIR +FTD2XXLDADD = @WITH_FTD2XX@/FTD2XX.lib +else +FTD2XXLDADD = -lftd2xx +endif +else +FTD2XXLDADD = -lftd2xx +endif + +if FTD2XX +FTD2XXLIB = $(FTD2XXLDADD) +else +FTD2XXLIB = +endif + +openocd_LDADD = $(top_builddir)/src/xsvf/libxsvf.a \ + $(top_builddir)/src/target/libtarget.a $(top_builddir)/src/jtag/libjtag.a \ + $(top_builddir)/src/helper/libhelper.a \ + $(top_builddir)/src/server/libserver.a $(top_builddir)/src/helper/libhelper.a \ + $(top_builddir)/src/flash/libflash.a $(top_builddir)/src/target/libtarget.a \ + $(FTDI2232LIB) $(FTD2XXLIB) diff --git a/src/flash/Makefile.am b/src/flash/Makefile.am new file mode 100644 index 00000000..61e363c9 --- /dev/null +++ b/src/flash/Makefile.am @@ -0,0 +1,5 @@ +INCLUDES = -I$(top_srcdir)/src/helper -I$(top_srcdir)/src/jtag -I$(top_srcdir)/src/target $(all_includes) +METASOURCES = AUTO +noinst_LIBRARIES = libflash.a +libflash_a_SOURCES = flash.c lpc2000.c cfi.c at91sam7.c str7x.c +noinst_HEADERS = flash.h lpc2000.h cfi.h at91sam7.h str7x.h diff --git a/src/flash/at91sam7.c b/src/flash/at91sam7.c new file mode 100644 index 00000000..8a602a3f --- /dev/null +++ b/src/flash/at91sam7.c @@ -0,0 +1,632 @@ +/*************************************************************************** + * Copyright (C) 2006 by Magnus Lundin * + * lundin@mlu.mine.nu * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/*************************************************************************** +There are some things to notice + +* AT91SAM7S64 is tested +* All AT91SAM7Sxx and AT91SAM7Xxx should work but is not tested +* All parameters are identified from onchip configuartion registers +* +* The flash controller handles erases automatically on a page (128/265 byte) basis +* Only an EraseAll command is supported by the controller +* Partial erases can be implemented in software by writing one 0xFFFFFFFF word to +* some location in every page in the region to be erased +* +* Lock regions (sectors) are 32 or 64 pages +* + ***************************************************************************/ + +#include "at91sam7.h" + +#include "flash.h" +#include "target.h" +#include "log.h" +#include "binarybuffer.h" +#include "types.h" + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int at91sam7_register_commands(struct command_context_s *cmd_ctx); +int at91sam7_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank); +int at91sam7_erase(struct flash_bank_s *bank, int first, int last); +int at91sam7_protect(struct flash_bank_s *bank, int set, int first, int last); +int at91sam7_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count); +int at91sam7_probe(struct flash_bank_s *bank); +int at91sam7_erase_check(struct flash_bank_s *bank); +int at91sam7_protect_check(struct flash_bank_s *bank); +int at91sam7_info(struct flash_bank_s *bank, char *buf, int buf_size); + +u32 at91sam7_get_flash_status(flash_bank_t *bank); +void at91sam7_set_flash_mode(flash_bank_t *bank,int mode); +u8 at91sam7_wait_status_busy(flash_bank_t *bank, int timeout); +int at91sam7_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +flash_driver_t at91sam7_flash = +{ + .name = "at91sam7", + .register_commands = at91sam7_register_commands, + .flash_bank_command = at91sam7_flash_bank_command, + .erase = at91sam7_erase, + .protect = at91sam7_protect, + .write = at91sam7_write, + .probe = at91sam7_probe, + .erase_check = at91sam7_erase_check, + .protect_check = at91sam7_protect_check, + .info = at91sam7_info +}; + + +char * EPROC[8]= {"Unknown","ARM946-E","ARM7TDMI","Unknown","ARM920T","ARM926EJ-S","Unknown","Unknown"}; +long NVPSIZ[16] = { + 0, + 0x2000, /* 8K */ + 0x4000, /* 16K */ + 0x8000, /* 32K */ + -1, + 0x10000, /* 64K */ + -1, + 0x20000, /* 128K */ + -1, + 0x40000, /* 256K */ + 0x80000, /* 512K */ + -1, + 0x100000, /* 1024K */ + -1, + 0x200000, /* 2048K */ + -1 +}; + +long SRAMSIZ[16] = { + -1, + 0x0400, /* 1K */ + 0x0800, /* 2K */ + -1, + 0x1c000, /* 112K */ + 0x1000, /* 4K */ + 0x14000, /* 80K */ + 0x28000, /* 160K */ + 0x2000, /* 8K */ + 0x4000, /* 16K */ + 0x8000, /* 32K */ + 0x10000, /* 64K */ + 0x20000, /* 128K */ + 0x40000, /* 256K */ + 0x18000, /* 96K */ + 0x80000, /* 512K */ +}; + +u32 at91sam7_get_flash_status(flash_bank_t *bank) +{ + at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv; + target_t *target = at91sam7_info->target; + long fsr; + + target->type->read_memory(target, MC_FSR, 4, 1, (u8 *)&fsr); + + return fsr; +} + +/* Setup the timimg registers for nvbits or normal flash */ +void at91sam7_set_flash_mode(flash_bank_t *bank,int mode) +{ + u32 fmcn, fmr; + at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv; + target_t *target = at91sam7_info->target; + + if (mode != at91sam7_info->flashmode) { + /* mainf contains the number of main clocks in approx 500uS */ + if (mode==1) + /* main clocks in 1uS */ + fmcn = (at91sam7_info->mainf>>9)+1; + else + /* main clocks in 1.5uS */ + fmcn = (at91sam7_info->mainf>>9)+(at91sam7_info->mainf>>10)+1; + DEBUG("fmcn: %i", fmcn); + fmr = fmcn<<16; + target->type->write_memory(target, MC_FSR, 4, 1, (u8 *)&fmr); + at91sam7_info->flashmode = mode; + } +} + +u8 at91sam7_wait_status_busy(flash_bank_t *bank, int timeout) +{ + u32 status; + + while ((!((status = at91sam7_get_flash_status(bank)) & 0x01)) && (timeout-- > 0)) + { + DEBUG("status: 0x%x", status); + usleep(1000); + } + + DEBUG("status: 0x%x", status); + + if (status&0x0C) + { + ERROR("status register: 0x%x", status); + if (status & 0x4) + ERROR("Lock Error Bit Detected, Operation Abort"); + if (status & 0x8) + ERROR("Invalid command and/or bad keyword, Operation Abort"); + if (status & 0x10) + ERROR("Security Bit Set, Operation Abort"); + } + + return status; +} + +int at91sam7_flash_command(struct flash_bank_s *bank,u8 cmd,u16 pagen) +{ + u32 fcr; + at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv; + target_t *target = at91sam7_info->target; + + fcr = (0x5A<<24) | (pagen<<8) | cmd; + target->type->write_memory(target, MC_FCR, 4, 1, (u8 *)&fcr); + DEBUG("Flash command: 0x%x, pagenumber:", fcr, pagen); + + if (at91sam7_wait_status_busy(bank, 10)&0x0C) + { + return ERROR_FLASH_OPERATION_FAILED; + } + return ERROR_OK; +} + +/* Read device id register, main clock frequency register and fill in driver info structure */ +int at91sam7_read_part_info(struct flash_bank_s *bank) +{ + at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv; + target_t *target = at91sam7_info->target; + unsigned long cidr, mcfr, status; + + if (at91sam7_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + /* Read and parse chip identification register */ + target->type->read_memory(target, DBGU_CIDR, 4, 1, (u8 *)&cidr); + + if (cidr == 0) + { + WARNING("Cannot identify target as an AT91SAM"); + return ERROR_FLASH_OPERATION_FAILED; + } + + at91sam7_info->cidr = cidr; + at91sam7_info->cidr_ext = (cidr>>31)&0x0001; + at91sam7_info->cidr_nvptyp = (cidr>>28)&0x0007; + at91sam7_info->cidr_arch = (cidr>>20)&0x00FF; + at91sam7_info->cidr_sramsiz = (cidr>>16)&0x000F; + at91sam7_info->cidr_nvpsiz2 = (cidr>>12)&0x000F; + at91sam7_info->cidr_nvpsiz = (cidr>>8)&0x000F; + at91sam7_info->cidr_eproc = (cidr>>5)&0x0007; + at91sam7_info->cidr_version = cidr&0x001F; + bank->size = NVPSIZ[at91sam7_info->cidr_nvpsiz]; + + DEBUG("nvptyp: 0x%3.3x, arch: 0x%4.4x, alt_id: 0x%4.4x, alt_addr: 0x%4.4x", at91sam7_info->cidr_nvptyp, at91sam7_info->cidr_arch ); + + /* Read main clock freqency register */ + target->type->read_memory(target, CKGR_MCFR, 4, 1, (u8 *)&mcfr); + if (mcfr&0x10000) + { + at91sam7_info->mainrdy = 1; + at91sam7_info->mainf = mcfr&0xFFFF; + at91sam7_info->usec_clocks = mcfr>>9; + } + else + { + at91sam7_info->mainrdy = 0; + at91sam7_info->mainf = 0; + at91sam7_info->usec_clocks = 0; + } + + status = at91sam7_get_flash_status(bank); + at91sam7_info->lockbits = status>>16; + at91sam7_info->securitybit = (status>>4)&0x01; + + if (at91sam7_info->cidr_arch == 0x70 ) { + at91sam7_info->num_nvmbits = 2; + at91sam7_info->nvmbits = (status>>8)&0x03; + bank->base = 0x100000; + bank->bus_width = 4; + if (bank->size==0x40000) /* AT91SAM7S256 */ + { + at91sam7_info->num_lockbits = 16; + at91sam7_info->pagesize = 256; + at91sam7_info->pages_in_lockregion = 64; + at91sam7_info->num_pages = 16*64; + } + if (bank->size==0x20000) /* AT91SAM7S128 */ + { + at91sam7_info->num_lockbits = 8; + at91sam7_info->pagesize = 256; + at91sam7_info->pages_in_lockregion = 64; + at91sam7_info->num_pages = 8*64; + } + if (bank->size==0x10000) /* AT91SAM7S64 */ + { + at91sam7_info->num_lockbits = 16; + at91sam7_info->pagesize = 128; + at91sam7_info->pages_in_lockregion = 32; + at91sam7_info->num_pages = 16*32; + } + if (bank->size==0x08000) /* AT91SAM7S321/32 */ + { + at91sam7_info->num_lockbits = 8; + at91sam7_info->pagesize = 128; + at91sam7_info->pages_in_lockregion = 32; + at91sam7_info->num_pages = 8*32; + } + + return ERROR_OK; + } + + if (at91sam7_info->cidr_arch == 0x71 ) { + at91sam7_info->num_nvmbits = 2; + at91sam7_info->nvmbits = (status>>8)&0x03; + bank->base = 0x100000; + bank->bus_width = 4; + if (bank->size==0x40000) /* AT91SAM7XC256 */ + { + at91sam7_info->num_lockbits = 16; + at91sam7_info->pagesize = 256; + at91sam7_info->pages_in_lockregion = 64; + at91sam7_info->num_pages = 16*64; + } + if (bank->size==0x20000) /* AT91SAM7XC128 */ + { + at91sam7_info->num_lockbits = 8; + at91sam7_info->pagesize = 256; + at91sam7_info->pages_in_lockregion = 64; + at91sam7_info->num_pages = 8*64; + } + + return ERROR_OK; + } + + if (at91sam7_info->cidr_arch == 0x75 ) { + at91sam7_info->num_nvmbits = 3; + at91sam7_info->nvmbits = (status>>8)&0x07; + bank->base = 0x100000; + bank->bus_width = 4; + if (bank->size==0x40000) /* AT91SAM7X256 */ + { + at91sam7_info->num_lockbits = 16; + at91sam7_info->pagesize = 256; + at91sam7_info->pages_in_lockregion = 64; + at91sam7_info->num_pages = 16*64; + } + if (bank->size==0x20000) /* AT91SAM7X128 */ + { + at91sam7_info->num_lockbits = 8; + at91sam7_info->pagesize = 256; + at91sam7_info->pages_in_lockregion = 64; + at91sam7_info->num_pages = 8*64; + } + + return ERROR_OK; + } + + if (at91sam7_info->cidr_arch != 0x70 ) + { + WARNING("at91sam7 flash only tested for AT91SAM7Sxx series"); + } + return ERROR_OK; +} + +int at91sam7_erase_check(struct flash_bank_s *bank) +{ + at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv; + target_t *target = at91sam7_info->target; + int i; + + if (!at91sam7_info->working_area_size) + { + } + else + { + } + + return ERROR_OK; +} + +int at91sam7_protect_check(struct flash_bank_s *bank) +{ + u32 status; + + at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv; + target_t *target = at91sam7_info->target; + + if (at91sam7_info->cidr == 0) + { + at91sam7_read_part_info(bank); + } + + if (at91sam7_info->cidr == 0) + { + WARNING("Cannot identify target as an AT91SAM"); + return ERROR_FLASH_OPERATION_FAILED; + } + + status = at91sam7_get_flash_status(bank); + at91sam7_info->lockbits = status>>16; + + return ERROR_OK; +} + + +int at91sam7_register_commands(struct command_context_s *cmd_ctx) +{ + command_t *at91sam7_cmd = register_command(cmd_ctx, NULL, "cfi", NULL, COMMAND_ANY, NULL); + + return ERROR_OK; +} + +int at91sam7_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank) +{ + at91sam7_flash_bank_t *at91sam7_info; + + if (argc < 6) + { + WARNING("incomplete flash_bank at91sam7 configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + at91sam7_info = malloc(sizeof(at91sam7_flash_bank_t)); + bank->driver_priv = at91sam7_info; + + at91sam7_info->target = get_target_by_num(strtoul(args[5], NULL, 0)); + if (!at91sam7_info->target) + { + ERROR("no target '%i' configured", args[5]); + exit(-1); + } + + + /* part wasn't probed for info yet */ + at91sam7_info->cidr = 0; + + return ERROR_OK; +} + +int at91sam7_erase(struct flash_bank_s *bank, int first, int last) +{ + at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv; + + if (at91sam7_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + if (at91sam7_info->cidr == 0) + { + at91sam7_read_part_info(bank); + } + + if (at91sam7_info->cidr == 0) + { + WARNING("Cannot identify target as an AT91SAM"); + return ERROR_FLASH_OPERATION_FAILED; + } + + if ((first < 0) || (last < first) || (last >= at91sam7_info->num_lockbits)) + { + return ERROR_FLASH_SECTOR_INVALID; + } + + if ((first == 0) && (last == (at91sam7_info->num_lockbits-1))) + { + return at91sam7_flash_command(bank, EA, 0); + } + + WARNING("Can only erase the whole flash area, pages are autoerased on write"); + return ERROR_FLASH_OPERATION_FAILED; +} + +int at91sam7_protect(struct flash_bank_s *bank, int set, int first, int last) +{ + u32 cmd, pagen, status; + int lockregion; + + at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv; + target_t *target = at91sam7_info->target; + + if (at91sam7_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + if ((first < 0) || (last < first) || (last >= at91sam7_info->num_lockbits)) + { + return ERROR_FLASH_SECTOR_INVALID; + } + + if (at91sam7_info->cidr == 0) + { + at91sam7_read_part_info(bank); + } + + if (at91sam7_info->cidr == 0) + { + WARNING("Cannot identify target as an AT91SAM"); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Configure the flash controller timing */ + at91sam7_set_flash_mode(bank,1); + + for (lockregion=first;lockregion<=last;lockregion++) + { + pagen = lockregion*at91sam7_info->pages_in_lockregion; + if (set) + cmd = SLB; + else + cmd = CLB; + if (at91sam7_flash_command(bank, cmd, pagen) != ERROR_OK) + { + return ERROR_FLASH_OPERATION_FAILED; + } + } + + status = at91sam7_get_flash_status(bank); + at91sam7_info->lockbits = status>>16; + + return ERROR_OK; +} + + +int at91sam7_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count) +{ + at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv; + target_t *target = at91sam7_info->target; + u32 dst_min_alignment, wcount, bytes_remaining = count; + u32 first_page, last_page, pagen, buffer_pos; + u32 fcr; + + if (at91sam7_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + if (at91sam7_info->cidr == 0) + { + at91sam7_read_part_info(bank); + } + + if (at91sam7_info->cidr == 0) + { + WARNING("Cannot identify target as an AT91SAM"); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (offset + count > bank->size) + return ERROR_FLASH_DST_OUT_OF_BANK; + + dst_min_alignment = at91sam7_info->pagesize; + + if (offset % dst_min_alignment) + { + WARNING("offset 0x%x breaks required alignment 0x%x", offset, dst_min_alignment); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + if (offset + count > bank->size) + return ERROR_FLASH_DST_OUT_OF_BANK; + + if (at91sam7_info->cidr_arch == 0) + return ERROR_FLASH_BANK_NOT_PROBED; + + first_page = offset/dst_min_alignment; + last_page = CEIL(offset + count, dst_min_alignment); + + DEBUG("first_page: %i, last_page: %i, count %i", first_page, last_page, count); + + /* Configure the flash controller timing */ + at91sam7_set_flash_mode(bank,2); + + for (pagen=first_page; pagen<last_page; pagen++) { + if (bytes_remaining<dst_min_alignment) + count = bytes_remaining; + else + count = dst_min_alignment; + bytes_remaining -= count; + + /* Write one block to the PageWriteBuffer */ + buffer_pos = (pagen-first_page)*dst_min_alignment; + wcount = CEIL(count,4); + target->type->write_memory(target, bank->base, 4, wcount, buffer+buffer_pos); + + /* Send Write Page command to Flash Controller */ + if (at91sam7_flash_command(bank, WP, pagen) != ERROR_OK) + { + return ERROR_FLASH_OPERATION_FAILED; + } + DEBUG("Flash command: 0x%x, pagenumber:", fcr, pagen); + } + + return ERROR_OK; +} + + +int at91sam7_probe(struct flash_bank_s *bank) +{ + /* we can't probe on an at91sam7 + * if this is an at91sam7, it has the configured flash + */ + at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv; + + if (at91sam7_info->cidr == 0) + { + at91sam7_read_part_info(bank); + } + + if (at91sam7_info->cidr == 0) + { + WARNING("Cannot identify target as an AT91SAM"); + return ERROR_FLASH_OPERATION_FAILED; + } + return ERROR_OK; +} + +int at91sam7_info(struct flash_bank_s *bank, char *buf, int buf_size) +{ + int printed; + at91sam7_flash_bank_t *at91sam7_info = bank->driver_priv; + + if (at91sam7_info->cidr == 0) + { + at91sam7_read_part_info(bank); + } + + if (at91sam7_info->cidr == 0) + { + printed = snprintf(buf, buf_size, "Cannot identify target as an AT91SAM\n"); + buf += printed; + buf_size -= printed; + return ERROR_FLASH_OPERATION_FAILED; + } + + printed = snprintf(buf, buf_size, "\nat91sam7 information:\n"); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "cidr: 0x%8.8x, arch: 0x%4.4x, eproc: %s, version:0x%3.3x, flashsize: 0x%8.8x\n", at91sam7_info->cidr, at91sam7_info->cidr_arch, EPROC[at91sam7_info->cidr_eproc], at91sam7_info->cidr_version, bank->size); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "main clock(estimated): %ikHz \n", at91sam7_info->mainf*2); + buf += printed; + buf_size -= printed; + + if (at91sam7_info->num_lockbits>0) { + printed = snprintf(buf, buf_size, "pagesize: %i, lockbits: %i 0x%4.4x, pages in lock region: %i \n", at91sam7_info->pagesize, at91sam7_info->num_lockbits, at91sam7_info->lockbits,at91sam7_info->num_pages/at91sam7_info->num_lockbits); + buf += printed; + buf_size -= printed; + } + + printed = snprintf(buf, buf_size, "securitybit: %i, nvmbits: 0x%1.1x\n", at91sam7_info->securitybit, at91sam7_info->nvmbits); + buf += printed; + buf_size -= printed; + + return ERROR_OK; +} diff --git a/src/flash/at91sam7.h b/src/flash/at91sam7.h new file mode 100644 index 00000000..8f9e3db7 --- /dev/null +++ b/src/flash/at91sam7.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (C) 2006 by Magnus Lundin * + * lundinªmlu.mine.nu * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef AT91SAM7_H +#define AT91SAM7_H + +#include "flash.h" +#include "target.h" + +typedef struct at91sam7_flash_bank_s +{ + struct target_s *target; + u32 working_area; + u32 working_area_size; + + /* chip id register */ + u32 cidr; + u16 cidr_ext; + u16 cidr_nvptyp; + u16 cidr_arch; + u16 cidr_sramsiz; + u16 cidr_nvpsiz; + u16 cidr_nvpsiz2; + u16 cidr_eproc; + u16 cidr_version; + + /* flash geometry */ + u16 num_pages; + u16 pagesize; + u16 pages_in_lockregion; + u8 num_erase_regions; + u32 *erase_region_info; + + /* nv memory bits */ + u16 num_lockbits; + u16 lockbits; + u16 num_nvmbits; + u16 nvmbits; + u8 securitybit; + u8 flashmode; /* 0: not init, 1: fmcn for nvbits (1uS), 2: fmcn for flash (1.5uS) */ + + /* main clock status */ + u8 mainrdy; + u16 mainf; + u16 usec_clocks; + +} at91sam7_flash_bank_t; + +/* AT91SAM7 control registers */ +#define DBGU_CIDR 0xFFFFF240 +#define CKGR_MCFR 0xFFFFFC24 +#define MC_FMR 0xFFFFFF60 +#define MC_FCR 0xFFFFFF64 +#define MC_FSR 0xFFFFFF68 + +/* Flash Controller Commands */ +#define WP 0x01 +#define SLB 0x02 +#define WPL 0x03 +#define CLB 0x04 +#define EA 0x08 +#define SGPB 0x0B +#define CGPB 0x0D +#define SSB 0x0F + + +#endif /* AT91SAM7_H */ diff --git a/src/flash/cfi.c b/src/flash/cfi.c new file mode 100644 index 00000000..5e943676 --- /dev/null +++ b/src/flash/cfi.c @@ -0,0 +1,1194 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "cfi.h" + +#include "flash.h" +#include "target.h" +#include "log.h" +#include "armv4_5.h" +#include "algorithm.h" +#include "binarybuffer.h" +#include "types.h" + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int cfi_register_commands(struct command_context_s *cmd_ctx); +int cfi_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank); +int cfi_erase(struct flash_bank_s *bank, int first, int last); +int cfi_protect(struct flash_bank_s *bank, int set, int first, int last); +int cfi_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count); +int cfi_probe(struct flash_bank_s *bank); +int cfi_erase_check(struct flash_bank_s *bank); +int cfi_protect_check(struct flash_bank_s *bank); +int cfi_info(struct flash_bank_s *bank, char *buf, int buf_size); + +int cfi_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +#define CFI_MAX_BUS_WIDTH 4 + +flash_driver_t cfi_flash = +{ + .name = "cfi", + .register_commands = cfi_register_commands, + .flash_bank_command = cfi_flash_bank_command, + .erase = cfi_erase, + .protect = cfi_protect, + .write = cfi_write, + .probe = cfi_probe, + .erase_check = cfi_erase_check, + .protect_check = cfi_protect_check, + .info = cfi_info +}; + +inline u32 flash_address(flash_bank_t *bank, int sector, u32 offset) +{ + /* while the sector list isn't built, only accesses to sector 0 work */ + if (sector == 0) + return bank->base + offset * bank->bus_width; + else + { + if (!bank->sectors) + { + ERROR("BUG: sector list not yet built"); + exit(-1); + } + return bank->base + bank->sectors[sector].offset + offset * bank->bus_width; + } + +} + +void cfi_command(flash_bank_t *bank, u8 cmd, u8 *cmd_buf) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + int i; + + if (cfi_info->target->endianness == TARGET_LITTLE_ENDIAN) + { + for (i = bank->bus_width; i > 0; i--) + { + *cmd_buf++ = (i & (bank->chip_width - 1)) ? 0x0 : cmd; + } + } + else + { + for (i = 1; i <= bank->bus_width; i++) + { + *cmd_buf++ = (i & (bank->chip_width - 1)) ? 0x0 : cmd; + } + } +} + +/* read unsigned 8-bit value from the bank + * flash banks are expected to be made of similar chips + * the query result should be the same for all + */ +u8 cfi_query_u8(flash_bank_t *bank, int sector, u32 offset) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + target_t *target = cfi_info->target; + u8 data[CFI_MAX_BUS_WIDTH]; + + target->type->read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 1, data); + + if (cfi_info->target->endianness == TARGET_LITTLE_ENDIAN) + return data[0]; + else + return data[bank->bus_width - 1]; +} + +/* read unsigned 8-bit value from the bank + * in case of a bank made of multiple chips, + * the individual values are ORed + */ +u8 cfi_get_u8(flash_bank_t *bank, int sector, u32 offset) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + target_t *target = cfi_info->target; + u8 data[CFI_MAX_BUS_WIDTH]; + int i; + + target->type->read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 1, data); + + if (cfi_info->target->endianness == TARGET_LITTLE_ENDIAN) + { + for (i = 0; i < bank->bus_width / bank->chip_width; i++) + data[0] |= data[i]; + + return data[0]; + } + else + { + u8 value = 0; + for (i = 0; i < bank->bus_width / bank->chip_width; i++) + value |= data[bank->bus_width - 1 - i]; + + return value; + } +} + +u16 cfi_query_u16(flash_bank_t *bank, int sector, u32 offset) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + target_t *target = cfi_info->target; + u8 data[CFI_MAX_BUS_WIDTH * 2]; + + target->type->read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 2, data); + + if (cfi_info->target->endianness == TARGET_LITTLE_ENDIAN) + return data[0] | data[bank->bus_width] << 8; + else + return data[bank->bus_width - 1] | data[(2 * bank->bus_width) - 1] << 8; +} + +u32 cfi_query_u32(flash_bank_t *bank, int sector, u32 offset) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + target_t *target = cfi_info->target; + u8 data[CFI_MAX_BUS_WIDTH * 4]; + + target->type->read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 4, data); + + if (cfi_info->target->endianness == TARGET_LITTLE_ENDIAN) + return data[0] | data[bank->bus_width] << 8 | data[bank->bus_width * 2] << 16 | data[bank->bus_width * 3] << 24; + else + return data[bank->bus_width - 1] | data[(2* bank->bus_width) - 1] << 8 | + data[(3 * bank->bus_width) - 1] << 16 | data[(4 * bank->bus_width) - 1] << 24; +} + +void cfi_intel_clear_status_register(flash_bank_t *bank) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + target_t *target = cfi_info->target; + u8 command[8]; + + if (target->state != TARGET_HALTED) + { + ERROR("BUG: attempted to clear status register while target wasn't halted"); + exit(-1); + } + + cfi_command(bank, 0x50, command); + target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); +} + +u8 cfi_intel_wait_status_busy(flash_bank_t *bank, int timeout) +{ + u8 status; + + while ((!((status = cfi_get_u8(bank, 0, 0x0)) & 0x80)) && (timeout-- > 0)) + { + DEBUG("status: 0x%x", status); + usleep(1000); + } + + DEBUG("status: 0x%x", status); + + if (status != 0x80) + { + ERROR("status register: 0x%x", status); + if (status & 0x2) + ERROR("Block Lock-Bit Detected, Operation Abort"); + if (status & 0x4) + ERROR("Program suspended"); + if (status & 0x8) + ERROR("Low Programming Voltage Detected, Operation Aborted"); + if (status & 0x10) + ERROR("Program Error / Error in Setting Lock-Bit"); + if (status & 0x20) + ERROR("Error in Block Erasure or Clear Lock-Bits"); + if (status & 0x40) + ERROR("Block Erase Suspended"); + + cfi_intel_clear_status_register(bank); + } + + return status; +} +int cfi_read_intel_pri_ext(flash_bank_t *bank) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + cfi_intel_pri_ext_t *pri_ext = malloc(sizeof(cfi_intel_pri_ext_t)); + target_t *target = cfi_info->target; + u8 command[8]; + + cfi_info->pri_ext = pri_ext; + + pri_ext->pri[0] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0); + pri_ext->pri[1] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 1); + pri_ext->pri[2] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 2); + + if ((pri_ext->pri[0] != 'P') || (pri_ext->pri[1] != 'R') || (pri_ext->pri[2] != 'I')) + { + cfi_command(bank, 0xf0, command); + target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); + cfi_command(bank, 0xff, command); + target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); + return ERROR_FLASH_BANK_INVALID; + } + + pri_ext->major_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 3); + pri_ext->minor_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 4); + + DEBUG("pri: '%c%c%c', version: %c.%c", pri_ext->pri[0], pri_ext->pri[1], pri_ext->pri[2], pri_ext->major_version, pri_ext->minor_version); + + pri_ext->feature_support = cfi_query_u32(bank, 0, cfi_info->pri_addr + 5); + pri_ext->suspend_cmd_support = cfi_query_u8(bank, 0, cfi_info->pri_addr + 9); + pri_ext->blk_status_reg_mask = cfi_query_u16(bank, 0, cfi_info->pri_addr + 0xa); + + DEBUG("feature_support: 0x%x, suspend_cmd_support: 0x%x, blk_status_reg_mask: 0x%x", pri_ext->feature_support, pri_ext->suspend_cmd_support, pri_ext->blk_status_reg_mask); + + pri_ext->vcc_optimal = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0xc); + pri_ext->vpp_optimal = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0xd); + + DEBUG("Vcc opt: %1.1x.%1.1x, Vpp opt: %1.1x.%1.1x", + (pri_ext->vcc_optimal & 0xf0) >> 4, pri_ext->vcc_optimal & 0x0f, + (pri_ext->vpp_optimal & 0xf0) >> 4, pri_ext->vpp_optimal & 0x0f); + + pri_ext->num_protection_fields = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0xe); + if (pri_ext->num_protection_fields != 1) + { + WARNING("expected one protection register field, but found %i", pri_ext->num_protection_fields); + } + + pri_ext->prot_reg_addr = cfi_query_u16(bank, 0, cfi_info->pri_addr + 0xf); + pri_ext->fact_prot_reg_size = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0x11); + pri_ext->user_prot_reg_size = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0x12); + + DEBUG("protection_fields: %i, prot_reg_addr: 0x%x, factory pre-programmed: %i, user programmable: %i", pri_ext->num_protection_fields, pri_ext->prot_reg_addr, 1 << pri_ext->fact_prot_reg_size, 1 << pri_ext->user_prot_reg_size); + + return ERROR_OK; +} + +int cfi_intel_info(struct flash_bank_s *bank, char *buf, int buf_size) +{ + int printed; + cfi_flash_bank_t *cfi_info = bank->driver_priv; + cfi_intel_pri_ext_t *pri_ext = cfi_info->pri_ext; + + printed = snprintf(buf, buf_size, "\nintel primary algorithm extend information:\n"); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "pri: '%c%c%c', version: %c.%c\n", pri_ext->pri[0], pri_ext->pri[1], pri_ext->pri[2], pri_ext->major_version, pri_ext->minor_version); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "feature_support: 0x%x, suspend_cmd_support: 0x%x, blk_status_reg_mask: 0x%x\n", pri_ext->feature_support, pri_ext->suspend_cmd_support, pri_ext->blk_status_reg_mask); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "Vcc opt: %1.1x.%1.1x, Vpp opt: %1.1x.%1.1x\n", + (pri_ext->vcc_optimal & 0xf0) >> 4, pri_ext->vcc_optimal & 0x0f, + (pri_ext->vpp_optimal & 0xf0) >> 4, pri_ext->vpp_optimal & 0x0f); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "protection_fields: %i, prot_reg_addr: 0x%x, factory pre-programmed: %i, user programmable: %i\n", pri_ext->num_protection_fields, pri_ext->prot_reg_addr, 1 << pri_ext->fact_prot_reg_size, 1 << pri_ext->user_prot_reg_size); + + return ERROR_OK; +} + +int cfi_register_commands(struct command_context_s *cmd_ctx) +{ + command_t *cfi_cmd = register_command(cmd_ctx, NULL, "cfi", NULL, COMMAND_ANY, NULL); + /* + register_command(cmd_ctx, cfi_cmd, "part_id", cfi_handle_part_id_command, COMMAND_EXEC, + "print part id of cfi flash bank <num>"); + */ + return ERROR_OK; +} + +/* flash_bank cfi <base> <size> <chip_width> <bus_width> <target#> + */ +int cfi_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank) +{ + cfi_flash_bank_t *cfi_info; + + if (argc < 6) + { + WARNING("incomplete flash_bank cfi configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + cfi_info = malloc(sizeof(cfi_flash_bank_t)); + bank->driver_priv = cfi_info; + + cfi_info->target = get_target_by_num(strtoul(args[5], NULL, 0)); + if (!cfi_info->target) + { + ERROR("no target '%i' configured", args[5]); + exit(-1); + } + + /* bank wasn't probed yet */ + cfi_info->qry[0] = -1; + + return ERROR_OK; +} + +int cfi_intel_erase(struct flash_bank_s *bank, int first, int last) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + cfi_intel_pri_ext_t *pri_ext = cfi_info->pri_ext; + target_t *target = cfi_info->target; + u8 command[8]; + int i; + + cfi_intel_clear_status_register(bank); + + for (i = first; i <= last; i++) + { + cfi_command(bank, 0x20, command); + target->type->write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command); + + cfi_command(bank, 0xd0, command); + target->type->write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command); + + if (cfi_intel_wait_status_busy(bank, 1000 * (1 << cfi_info->block_erase_timeout_typ)) == 0x80) + bank->sectors[i].is_erased = 1; + else + { + cfi_command(bank, 0xff, command); + target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); + + ERROR("couldn't erase block %i of flash bank at base 0x%x", i, bank->base); + return ERROR_FLASH_OPERATION_FAILED; + } + } + + cfi_command(bank, 0xff, command); + target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); + + return ERROR_OK; +} + +int cfi_erase(struct flash_bank_s *bank, int first, int last) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + + if (cfi_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + { + return ERROR_FLASH_SECTOR_INVALID; + } + + if (cfi_info->qry[0] != 'Q') + return ERROR_FLASH_BANK_NOT_PROBED; + + switch(cfi_info->pri_id) + { + case 1: + case 3: + return cfi_intel_erase(bank, first, last); + break; + default: + ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + + return ERROR_OK; +} + +int cfi_intel_protect(struct flash_bank_s *bank, int set, int first, int last) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + cfi_intel_pri_ext_t *pri_ext = cfi_info->pri_ext; + target_t *target = cfi_info->target; + u8 command[8]; + int i; + + if (!(pri_ext->feature_support & 0x28)) + return ERROR_FLASH_OPERATION_FAILED; + + cfi_intel_clear_status_register(bank); + + for (i = first; i <= last; i++) + { + cfi_command(bank, 0x60, command); + target->type->write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command); + if (set) + { + cfi_command(bank, 0x01, command); + target->type->write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command); + bank->sectors[i].is_protected = 1; + } + else + { + cfi_command(bank, 0xd0, command); + target->type->write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command); + bank->sectors[i].is_protected = 0; + } + + cfi_intel_wait_status_busy(bank, 100); + } + + /* if the device doesn't support individual block lock bits set/clear, + * all blocks have been unlocked in parallel, so we set those that should be protected + */ + if ((!set) && (!(pri_ext->feature_support & 0x20))) + { + for (i = 0; i < bank->num_sectors; i++) + { + cfi_intel_clear_status_register(bank); + cfi_command(bank, 0x60, command); + target->type->write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command); + if (bank->sectors[i].is_protected == 1) + { + cfi_command(bank, 0x01, command); + target->type->write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command); + } + + cfi_intel_wait_status_busy(bank, 100); + } + } + + cfi_command(bank, 0xff, command); + target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); + + return ERROR_OK; +} + +int cfi_protect(struct flash_bank_s *bank, int set, int first, int last) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + + if (cfi_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + { + return ERROR_FLASH_SECTOR_INVALID; + } + + if (cfi_info->qry[0] != 'Q') + return ERROR_FLASH_BANK_NOT_PROBED; + + switch(cfi_info->pri_id) + { + case 1: + case 3: + cfi_intel_protect(bank, set, first, last); + break; + default: + ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + + return ERROR_OK; +} + +void cfi_add_byte(struct flash_bank_s *bank, u8 *word, u8 byte) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + target_t *target = cfi_info->target; + + int i; + + if (target->endianness == TARGET_LITTLE_ENDIAN) + { + /* shift bytes */ + for (i = 0; i < bank->bus_width - 1; i++) + word[i] = word[i + 1]; + word[bank->bus_width - 1] = byte; + } + else + { + /* shift bytes */ + for (i = bank->bus_width - 1; i > 0; i--) + word[i] = word[i - 1]; + word[0] = byte; + } +} + +int cfi_intel_write_block(struct flash_bank_s *bank, u8 *buffer, u32 address, u32 count) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + cfi_intel_pri_ext_t *pri_ext = cfi_info->pri_ext; + target_t *target = cfi_info->target; + reg_param_t reg_params[5]; + armv4_5_algorithm_t armv4_5_info; + working_area_t *source; + u32 buffer_size = 32768; + u8 write_command[CFI_MAX_BUS_WIDTH]; + int i; + int retval; + + u32 word_32_code[] = { + 0xe4904004, /* loop: ldr r4, [r0], #4 */ + 0xe5813000, /* str r3, [r1] */ + 0xe5814000, /* str r4, [r1] */ + 0xe5914000, /* busy ldr r4, [r1] */ + 0xe3140080, /* tst r4, #0x80 */ + 0x0afffffc, /* beq busy */ + 0xe314007f, /* tst r4, #0x7f */ + 0x1a000003, /* bne done */ + 0xe2522001, /* subs r2, r2, #1 */ + 0x0a000001, /* beq done */ + 0xe2811004, /* add r1, r1 #4 */ + 0xeafffff3, /* b loop */ + 0xeafffffe, /* done: b -2 */ + }; + + u32 word_16_code[] = { + 0xe0d040b2, /* loop: ldrh r4, [r0], #2 */ + 0xe1c130b0, /* strh r3, [r1] */ + 0xe1c140b0, /* strh r4, [r1] */ + 0xe1d140b0, /* busy ldrh r4, [r1] */ + 0xe3140080, /* tst r4, #0x80 */ + 0x0afffffc, /* beq busy */ + 0xe314007f, /* tst r4, #0x7f */ + 0x1a000003, /* bne done */ + 0xe2522001, /* subs r2, r2, #1 */ + 0x0a000001, /* beq done */ + 0xe2811002, /* add r1, r1 #2 */ + 0xeafffff3, /* b loop */ + 0xeafffffe, /* done: b -2 */ + }; + + u32 word_8_code[] = { + 0xe4d04001, /* loop: ldrb r4, [r0], #1 */ + 0xe5c13000, /* strb r3, [r1] */ + 0xe5c14000, /* strb r4, [r1] */ + 0xe5d14000, /* busy ldrb r4, [r1] */ + 0xe3140080, /* tst r4, #0x80 */ + 0x0afffffc, /* beq busy */ + 0xe314007f, /* tst r4, #0x7f */ + 0x1a000003, /* bne done */ + 0xe2522001, /* subs r2, r2, #1 */ + 0x0a000001, /* beq done */ + 0xe2811001, /* add r1, r1 #1 */ + 0xeafffff3, /* b loop */ + 0xeafffffe, /* done: b -2 */ + }; + + cfi_intel_clear_status_register(bank); + + armv4_5_info.common_magic = ARMV4_5_COMMON_MAGIC; + armv4_5_info.core_mode = ARMV4_5_MODE_SVC; + armv4_5_info.core_state = ARMV4_5_STATE_ARM; + + /* flash write code */ + if (!cfi_info->write_algorithm) + { + if (target_alloc_working_area(target, 4 * 13, &cfi_info->write_algorithm) != ERROR_OK) + { + WARNING("no working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + }; + + /* write algorithm code to working area */ + if (bank->bus_width == 1) + { + target_write_buffer(target, cfi_info->write_algorithm->address, 13 * 4, (u8*)word_8_code); + } + else if (bank->bus_width == 2) + { + target_write_buffer(target, cfi_info->write_algorithm->address, 13 * 4, (u8*)word_16_code); + } + else if (bank->bus_width == 4) + { + target_write_buffer(target, cfi_info->write_algorithm->address, 13 * 4, (u8*)word_32_code); + } + else + { + return ERROR_FLASH_OPERATION_FAILED; + } + } + + while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK) + { + buffer_size /= 2; + if (buffer_size <= 256) + { + /* if we already allocated the writing code, but failed to get a buffer, free the algorithm */ + if (cfi_info->write_algorithm) + target_free_working_area(target, cfi_info->write_algorithm); + + WARNING("no large enough working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + }; + + init_reg_param(®_params[0], "r0", 32, PARAM_OUT); + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); + init_reg_param(®_params[2], "r2", 32, PARAM_OUT); + init_reg_param(®_params[3], "r3", 32, PARAM_OUT); + init_reg_param(®_params[4], "r4", 32, PARAM_IN); + + while (count > 0) + { + u32 thisrun_count = (count > buffer_size) ? buffer_size : count; + + target_write_buffer(target, source->address, thisrun_count, buffer); + + buf_set_u32(reg_params[0].value, 0, 32, source->address); + buf_set_u32(reg_params[1].value, 0, 32, address); + buf_set_u32(reg_params[2].value, 0, 32, thisrun_count / bank->bus_width); + cfi_command(bank, 0x40, write_command); + buf_set_u32(reg_params[3].value, 0, 32, buf_get_u32(write_command, 0, 32)); + + if ((retval = target->type->run_algorithm(target, 0, NULL, 5, reg_params, cfi_info->write_algorithm->address, cfi_info->write_algorithm->address + (12 * 4), 10000, &armv4_5_info)) != ERROR_OK) + { + cfi_intel_clear_status_register(bank); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (buf_get_u32(reg_params[4].value, 0, 32) != 0x80) + { + /* read status register (outputs debug inforation) */ + cfi_intel_wait_status_busy(bank, 100); + cfi_intel_clear_status_register(bank); + return ERROR_FLASH_OPERATION_FAILED; + } + + buffer += thisrun_count; + address += thisrun_count; + count -= thisrun_count; + } + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + destroy_reg_param(®_params[4]); + + return ERROR_OK; +} + +int cfi_intel_write_word(struct flash_bank_s *bank, u8 *word, u32 address) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + cfi_intel_pri_ext_t *pri_ext = cfi_info->pri_ext; + target_t *target = cfi_info->target; + u8 command[8]; + + cfi_intel_clear_status_register(bank); + cfi_command(bank, 0x40, command); + target->type->write_memory(target, address, bank->bus_width, 1, command); + + target->type->write_memory(target, address, bank->bus_width, 1, word); + + if (cfi_intel_wait_status_busy(bank, 1000 * (1 << cfi_info->word_write_timeout_max)) != 0x80) + { + cfi_command(bank, 0xff, command); + target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); + + ERROR("couldn't write word at base 0x%x, address %x", bank->base, address); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + +int cfi_write_word(struct flash_bank_s *bank, u8 *word, u32 address) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + target_t *target = cfi_info->target; + + switch(cfi_info->pri_id) + { + case 1: + case 3: + return cfi_intel_write_word(bank, word, address); + break; + default: + ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + + return ERROR_FLASH_OPERATION_FAILED; +} + +int cfi_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + target_t *target = cfi_info->target; + u32 address = bank->base + offset; /* address of first byte to be programmed */ + u32 write_p, copy_p; + int align; /* number of unaligned bytes */ + u8 current_word[CFI_MAX_BUS_WIDTH * 4]; /* word (bus_width size) currently being programmed */ + int i; + int retval; + + if (cfi_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + if (offset + count > bank->size) + return ERROR_FLASH_DST_OUT_OF_BANK; + + if (cfi_info->qry[0] != 'Q') + return ERROR_FLASH_BANK_NOT_PROBED; + + /* start at the first byte of the first word (bus_width size) */ + write_p = address & ~(bank->bus_width - 1); + if ((align = address - write_p) != 0) + { + for (i = 0; i < bank->bus_width; i++) + current_word[i] = 0; + copy_p = write_p; + + /* copy bytes before the first write address */ + for (i = 0; i < align; ++i, ++copy_p) + { + u8 byte; + target->type->read_memory(target, copy_p, 1, 1, &byte); + cfi_add_byte(bank, current_word, byte); + } + + /* add bytes from the buffer */ + for (; (i < bank->bus_width) && (count > 0); i++) + { + cfi_add_byte(bank, current_word, *buffer++); + count--; + copy_p++; + } + + /* if the buffer is already finished, copy bytes after the last write address */ + for (; (count == 0) && (i < bank->bus_width); ++i, ++copy_p) + { + u8 byte; + target->type->read_memory(target, copy_p, 1, 1, &byte); + cfi_add_byte(bank, current_word, byte); + } + + retval = cfi_write_word(bank, current_word, write_p); + if (retval != ERROR_OK) + return retval; + write_p = copy_p; + } + + /* handle blocks of bus_size aligned bytes */ + switch(cfi_info->pri_id) + { + /* try block writes (fails without working area) */ + case 1: + case 3: + retval = cfi_intel_write_block(bank, buffer, write_p, count); + break; + default: + ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + if (retval != ERROR_OK) + { + if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + { + /* fall back to memory writes */ + while (count > bank->bus_width) + { + for (i = 0; i < bank->bus_width; i++) + current_word[i] = 0; + + for (i = 0; i < bank->bus_width; i++) + { + cfi_add_byte(bank, current_word, *buffer++); + } + + retval = cfi_write_word(bank, current_word, write_p); + if (retval != ERROR_OK) + return retval; + write_p += bank->bus_width; + count -= bank->bus_width; + } + } + else + return retval; + } + + /* handle unaligned tail bytes */ + if (count > 0) + { + copy_p = write_p; + for (i = 0; i < bank->bus_width; i++) + current_word[i] = 0; + + for (i = 0; (i < bank->bus_width) && (count > 0); ++i, ++copy_p) + { + cfi_add_byte(bank, current_word, *buffer++); + count--; + } + for (; i < bank->bus_width; ++i, ++copy_p) + { + u8 byte; + target->type->read_memory(target, copy_p, 1, 1, &byte); + cfi_add_byte(bank, current_word, byte); + } + retval = cfi_write_word(bank, current_word, write_p); + if (retval != ERROR_OK) + return retval; + } + + /* return to read array mode */ + cfi_command(bank, 0xf0, current_word); + target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, current_word); + cfi_command(bank, 0xff, current_word); + target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, current_word); + + return ERROR_OK; +} + +int cfi_probe(struct flash_bank_s *bank) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + target_t *target = cfi_info->target; + u8 command[8]; + + + cfi_command(bank, 0x98, command); + target->type->write_memory(target, flash_address(bank, 0, 0x55), bank->bus_width, 1, command); + + cfi_info->qry[0] = cfi_query_u8(bank, 0, 0x10); + cfi_info->qry[1] = cfi_query_u8(bank, 0, 0x11); + cfi_info->qry[2] = cfi_query_u8(bank, 0, 0x12); + + if ((cfi_info->qry[0] != 'Q') || (cfi_info->qry[1] != 'R') || (cfi_info->qry[2] != 'Y')) + { + cfi_command(bank, 0xf0, command); + target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); + cfi_command(bank, 0xff, command); + target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); + return ERROR_FLASH_BANK_INVALID; + } + + cfi_info->pri_id = cfi_query_u16(bank, 0, 0x13); + cfi_info->pri_addr = cfi_query_u16(bank, 0, 0x15); + cfi_info->alt_id = cfi_query_u16(bank, 0, 0x17); + cfi_info->alt_addr = cfi_query_u16(bank, 0, 0x19); + + DEBUG("qry: '%c%c%c', pri_id: 0x%4.4x, pri_addr: 0x%4.4x, alt_id: 0x%4.4x, alt_addr: 0x%4.4x", cfi_info->qry[0], cfi_info->qry[1], cfi_info->qry[2], cfi_info->pri_id, cfi_info->pri_addr, cfi_info->alt_id, cfi_info->alt_addr); + + cfi_info->vcc_min = cfi_query_u8(bank, 0, 0x1b); + cfi_info->vcc_max = cfi_query_u8(bank, 0, 0x1c); + cfi_info->vpp_min = cfi_query_u8(bank, 0, 0x1d); + cfi_info->vpp_max = cfi_query_u8(bank, 0, 0x1e); + cfi_info->word_write_timeout_typ = cfi_query_u8(bank, 0, 0x1f); + cfi_info->buf_write_timeout_typ = cfi_query_u8(bank, 0, 0x20); + cfi_info->block_erase_timeout_typ = cfi_query_u8(bank, 0, 0x21); + cfi_info->chip_erase_timeout_typ = cfi_query_u8(bank, 0, 0x22); + cfi_info->word_write_timeout_max = cfi_query_u8(bank, 0, 0x23); + cfi_info->buf_write_timeout_max = cfi_query_u8(bank, 0, 0x24); + cfi_info->block_erase_timeout_max = cfi_query_u8(bank, 0, 0x25); + cfi_info->chip_erase_timeout_max = cfi_query_u8(bank, 0, 0x26); + + DEBUG("Vcc min: %1.1x.%1.1x, Vcc max: %1.1x.%1.1x, Vpp min: %1.1x.%1.1x, Vpp max: %1.1x.%1.1x", + (cfi_info->vcc_min & 0xf0) >> 4, cfi_info->vcc_min & 0x0f, + (cfi_info->vcc_max & 0xf0) >> 4, cfi_info->vcc_max & 0x0f, + (cfi_info->vpp_min & 0xf0) >> 4, cfi_info->vpp_min & 0x0f, + (cfi_info->vpp_max & 0xf0) >> 4, cfi_info->vpp_max & 0x0f); + DEBUG("typ. word write timeout: %u, typ. buf write timeout: %u, typ. block erase timeout: %u, typ. chip erase timeout: %u", 1 << cfi_info->word_write_timeout_typ, 1 << cfi_info->buf_write_timeout_typ, + 1 << cfi_info->block_erase_timeout_typ, 1 << cfi_info->chip_erase_timeout_typ); + DEBUG("max. word write timeout: %u, max. buf write timeout: %u, max. block erase timeout: %u, max. chip erase timeout: %u", (1 << cfi_info->word_write_timeout_max) * (1 << cfi_info->word_write_timeout_typ), + (1 << cfi_info->buf_write_timeout_max) * (1 << cfi_info->buf_write_timeout_typ), + (1 << cfi_info->block_erase_timeout_max) * (1 << cfi_info->block_erase_timeout_typ), + (1 << cfi_info->chip_erase_timeout_max) * (1 << cfi_info->chip_erase_timeout_typ)); + + cfi_info->dev_size = cfi_query_u8(bank, 0, 0x27); + cfi_info->interface_desc = cfi_query_u16(bank, 0, 0x28); + cfi_info->max_buf_write_size = cfi_query_u16(bank, 0, 0x2a); + cfi_info->num_erase_regions = cfi_query_u8(bank, 0, 0x2c); + + DEBUG("size: 0x%x, interface desc: %i, max buffer write size: %x", 1 << cfi_info->dev_size, cfi_info->interface_desc, cfi_info->max_buf_write_size); + + if (1 << cfi_info->dev_size != bank->size) + { + WARNING("configuration specifies 0x%x size, but a 0x%x size flash was found", bank->size, 1 << cfi_info->dev_size); + } + + if (cfi_info->num_erase_regions) + { + int i; + int num_sectors = 0; + int sector = 0; + u32 offset = 0; + cfi_info->erase_region_info = malloc(4 * cfi_info->num_erase_regions); + + for (i = 0; i < cfi_info->num_erase_regions; i++) + { + cfi_info->erase_region_info[i] = cfi_query_u32(bank, 0, 0x2d + (4 * i)); + DEBUG("erase region[%i]: %i blocks of size 0x%x", i, (cfi_info->erase_region_info[i] & 0xffff) + 1, (cfi_info->erase_region_info[i] >> 16) * 256); + + num_sectors += (cfi_info->erase_region_info[i] & 0xffff) + 1; + } + + bank->num_sectors = num_sectors; + bank->sectors = malloc(sizeof(flash_sector_t) * num_sectors); + for (i = 0; i < cfi_info->num_erase_regions; i++) + { + int j; + for (j = 0; j < (cfi_info->erase_region_info[i] & 0xffff) + 1; j++) + { + bank->sectors[sector].offset = offset; + bank->sectors[sector].size = (cfi_info->erase_region_info[i] >> 16) * 256; + offset += bank->sectors[sector].size; + bank->sectors[sector].is_erased = -1; + bank->sectors[sector].is_protected = -1; + sector++; + } + } + } + else + { + cfi_info->erase_region_info = NULL; + } + + switch(cfi_info->pri_id) + { + case 1: + case 3: + cfi_read_intel_pri_ext(bank); + break; + default: + ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + + /* return to read array mode */ + cfi_command(bank, 0xf0, command); + target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); + cfi_command(bank, 0xff, command); + target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); + + return ERROR_OK; +} + +int cfi_erase_check(struct flash_bank_s *bank) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + target_t *target = cfi_info->target; + int i; + int retval; + + if (!cfi_info->erase_check_algorithm) + { + u32 erase_check_code[] = + { + 0xe4d03001, + 0xe0022003, + 0xe2511001, + 0x1afffffb, + 0xeafffffe + }; + + /* make sure we have a working area */ + if (target_alloc_working_area(target, 20, &cfi_info->erase_check_algorithm) != ERROR_OK) + { + WARNING("no working area available, falling back to slow memory reads"); + } + else + { + /* write algorithm code to working area */ + target->type->write_memory(target, cfi_info->erase_check_algorithm->address, 4, 5, (u8*)erase_check_code); + } + } + + if (!cfi_info->erase_check_algorithm) + { + u32 *buffer = malloc(4096); + + for (i = 0; i < bank->num_sectors; i++) + { + u32 address = bank->base + bank->sectors[i].offset; + u32 size = bank->sectors[i].size; + u32 check = 0xffffffffU; + int erased = 1; + + while (size > 0) + { + u32 thisrun_size = (size > 4096) ? 4096 : size; + int j; + + target->type->read_memory(target, address, 4, thisrun_size / 4, (u8*)buffer); + + for (j = 0; j < thisrun_size / 4; j++) + check &= buffer[j]; + + if (check != 0xffffffff) + { + erased = 0; + break; + } + + size -= thisrun_size; + address += thisrun_size; + } + + bank->sectors[i].is_erased = erased; + } + + free(buffer); + } + else + { + for (i = 0; i < bank->num_sectors; i++) + { + u32 address = bank->base + bank->sectors[i].offset; + u32 size = bank->sectors[i].size; + + reg_param_t reg_params[3]; + armv4_5_algorithm_t armv4_5_info; + + armv4_5_info.common_magic = ARMV4_5_COMMON_MAGIC; + armv4_5_info.core_mode = ARMV4_5_MODE_SVC; + armv4_5_info.core_state = ARMV4_5_STATE_ARM; + + init_reg_param(®_params[0], "r0", 32, PARAM_OUT); + buf_set_u32(reg_params[0].value, 0, 32, address); + + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); + buf_set_u32(reg_params[1].value, 0, 32, size); + + init_reg_param(®_params[2], "r2", 32, PARAM_IN_OUT); + buf_set_u32(reg_params[2].value, 0, 32, 0xff); + + if ((retval = target->type->run_algorithm(target, 0, NULL, 3, reg_params, cfi_info->erase_check_algorithm->address, cfi_info->erase_check_algorithm->address + 0x10, 10000, &armv4_5_info)) != ERROR_OK) + return ERROR_FLASH_OPERATION_FAILED; + + if (buf_get_u32(reg_params[2].value, 0, 32) == 0xff) + bank->sectors[i].is_erased = 1; + else + bank->sectors[i].is_erased = 0; + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + } + } + + return ERROR_OK; +} + +int cfi_intel_protect_check(struct flash_bank_s *bank) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + cfi_intel_pri_ext_t *pri_ext = cfi_info->pri_ext; + target_t *target = cfi_info->target; + u8 command[8]; + int i; + + /* check if block lock bits are supported on this device */ + if (!(pri_ext->blk_status_reg_mask & 0x1)) + return ERROR_FLASH_OPERATION_FAILED; + + cfi_command(bank, 0x90, command); + target->type->write_memory(target, flash_address(bank, 0, 0x55), bank->bus_width, 1, command); + + for (i = 0; i < bank->num_sectors; i++) + { + u8 block_status = cfi_get_u8(bank, i, 0x2); + + if (block_status & 1) + bank->sectors[i].is_protected = 1; + else + bank->sectors[i].is_protected = 0; + } + + cfi_command(bank, 0xff, command); + target->type->write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command); + + return ERROR_OK; +} + +int cfi_protect_check(struct flash_bank_s *bank) +{ + cfi_flash_bank_t *cfi_info = bank->driver_priv; + target_t *target = cfi_info->target; + + if (cfi_info->qry[0] != 'Q') + return ERROR_FLASH_BANK_NOT_PROBED; + + switch(cfi_info->pri_id) + { + case 1: + case 3: + return cfi_intel_protect_check(bank); + break; + default: + ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + + return ERROR_OK; +} + +int cfi_info(struct flash_bank_s *bank, char *buf, int buf_size) +{ + int printed; + cfi_flash_bank_t *cfi_info = bank->driver_priv; + + if (cfi_info->qry[0] == -1) + { + printed = snprintf(buf, buf_size, "\ncfi flash bank not probed yet\n"); + return ERROR_OK; + } + + printed = snprintf(buf, buf_size, "\ncfi information:\n"); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "qry: '%c%c%c', pri_id: 0x%4.4x, pri_addr: 0x%4.4x, alt_id: 0x%4.4x, alt_addr: 0x%4.4x\n", cfi_info->qry[0], cfi_info->qry[1], cfi_info->qry[2], cfi_info->pri_id, cfi_info->pri_addr, cfi_info->alt_id, cfi_info->alt_addr); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "Vcc min: %1.1x.%1.1x, Vcc max: %1.1x.%1.1x, Vpp min: %1.1x.%1.1x, Vpp max: %1.1x.%1.1x\n", (cfi_info->vcc_min & 0xf0) >> 4, cfi_info->vcc_min & 0x0f, + (cfi_info->vcc_max & 0xf0) >> 4, cfi_info->vcc_max & 0x0f, + (cfi_info->vpp_min & 0xf0) >> 4, cfi_info->vpp_min & 0x0f, + (cfi_info->vpp_max & 0xf0) >> 4, cfi_info->vpp_max & 0x0f); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "typ. word write timeout: %u, typ. buf write timeout: %u, typ. block erase timeout: %u, typ. chip erase timeout: %u\n", 1 << cfi_info->word_write_timeout_typ, 1 << cfi_info->buf_write_timeout_typ, + 1 << cfi_info->block_erase_timeout_typ, 1 << cfi_info->chip_erase_timeout_typ); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "max. word write timeout: %u, max. buf write timeout: %u, max. block erase timeout: %u, max. chip erase timeout: %u\n", (1 << cfi_info->word_write_timeout_max) * (1 << cfi_info->word_write_timeout_typ), + (1 << cfi_info->buf_write_timeout_max) * (1 << cfi_info->buf_write_timeout_typ), + (1 << cfi_info->block_erase_timeout_max) * (1 << cfi_info->block_erase_timeout_typ), + (1 << cfi_info->chip_erase_timeout_max) * (1 << cfi_info->chip_erase_timeout_typ)); + buf += printed; + buf_size -= printed; + + printed = snprintf(buf, buf_size, "size: 0x%x, interface desc: %i, max buffer write size: %x\n", 1 << cfi_info->dev_size, cfi_info->interface_desc, cfi_info->max_buf_write_size); + buf += printed; + buf_size -= printed; + + switch(cfi_info->pri_id) + { + case 1: + case 3: + cfi_intel_info(bank, buf, buf_size); + break; + default: + ERROR("cfi primary command set %i unsupported", cfi_info->pri_id); + break; + } + + return ERROR_OK; +} diff --git a/src/flash/cfi.h b/src/flash/cfi.h new file mode 100644 index 00000000..d9700be2 --- /dev/null +++ b/src/flash/cfi.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CFI_H +#define CFI_H + +#include "flash.h" +#include "target.h" + +typedef struct cfi_flash_bank_s +{ + struct target_s *target; + working_area_t *write_algorithm; + working_area_t *erase_check_algorithm; + + char qry[3]; + + /* identification string */ + u16 pri_id; + u16 pri_addr; + u16 alt_id; + u16 alt_addr; + + /* device-system interface */ + u8 vcc_min; + u8 vcc_max; + u8 vpp_min; + u8 vpp_max; + u8 word_write_timeout_typ; + u8 buf_write_timeout_typ; + u8 block_erase_timeout_typ; + u8 chip_erase_timeout_typ; + u8 word_write_timeout_max; + u8 buf_write_timeout_max; + u8 block_erase_timeout_max; + u8 chip_erase_timeout_max; + + /* flash geometry */ + u8 dev_size; + u16 interface_desc; + u16 max_buf_write_size; + u8 num_erase_regions; + u32 *erase_region_info; + + void *pri_ext; + void *alt_ext; +} cfi_flash_bank_t; + +/* Intel primary extended query table + * as defined for the Advanced+ Boot Block Flash Memory (C3) + * and used by the linux kernel cfi driver (as of 2.6.14) + */ +typedef struct cfi_intel_pri_ext_s +{ + char pri[3]; + u8 major_version; + u8 minor_version; + u32 feature_support; + u8 suspend_cmd_support; + u16 blk_status_reg_mask; + u8 vcc_optimal; + u8 vpp_optimal; + u8 num_protection_fields; + u16 prot_reg_addr; + u8 fact_prot_reg_size; + u8 user_prot_reg_size; + u8 extra[0]; +} cfi_intel_pri_ext_t; + +#endif /* CFI_H */ diff --git a/src/flash/flash.c b/src/flash/flash.c new file mode 100644 index 00000000..a5067cd2 --- /dev/null +++ b/src/flash/flash.c @@ -0,0 +1,556 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "flash.h" +#include "command.h" +#include "log.h" +#include "target.h" + +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + +/* command handlers */ +int handle_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_flash_banks_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_flash_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_flash_probe_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_flash_erase_check_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_flash_protect_check_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_flash_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_flash_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_flash_protect_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +/* flash drivers + */ +extern flash_driver_t lpc2000_flash; +extern flash_driver_t cfi_flash; +extern flash_driver_t at91sam7_flash; +extern flash_driver_t str7x_flash; + +flash_driver_t *flash_drivers[] = +{ + &lpc2000_flash, + &cfi_flash, + &at91sam7_flash, + &str7x_flash, + NULL, +}; + +flash_bank_t *flash_banks; +static command_t *flash_cmd; + +int flash_register_commands(struct command_context_s *cmd_ctx) +{ + flash_cmd = register_command(cmd_ctx, NULL, "flash", NULL, COMMAND_ANY, NULL); + + register_command(cmd_ctx, flash_cmd, "bank", handle_flash_bank_command, COMMAND_CONFIG, NULL); + + return ERROR_OK; +} + +int flash_init(struct command_context_s *cmd_ctx) +{ + if (flash_banks) + { + register_command(cmd_ctx, flash_cmd, "banks", handle_flash_banks_command, COMMAND_EXEC, + "list configured flash banks "); + register_command(cmd_ctx, flash_cmd, "info", handle_flash_info_command, COMMAND_EXEC, + "print info about flash bank <num>"); + register_command(cmd_ctx, flash_cmd, "probe", handle_flash_probe_command, COMMAND_EXEC, + "identify flash bank <num>"); + register_command(cmd_ctx, flash_cmd, "erase_check", handle_flash_erase_check_command, COMMAND_EXEC, + "check erase state of sectors in flash bank <num>"); + register_command(cmd_ctx, flash_cmd, "protect_check", handle_flash_protect_check_command, COMMAND_EXEC, + "check protection state of sectors in flash bank <num>"); + register_command(cmd_ctx, flash_cmd, "erase", handle_flash_erase_command, COMMAND_EXEC, + "erase sectors at <bank> <first> <last>"); + register_command(cmd_ctx, flash_cmd, "write", handle_flash_write_command, COMMAND_EXEC, + "write binary <bank> <file> <offset>"); + register_command(cmd_ctx, flash_cmd, "protect", handle_flash_protect_command, COMMAND_EXEC, + "set protection of sectors at <bank> <first> <last> <on|off>"); + } + + return ERROR_OK; +} + +flash_bank_t *get_flash_bank_by_num(int num) +{ + flash_bank_t *p; + int i = 0; + + for (p = flash_banks; p; p = p->next) + { + if (i++ == num) + { + return p; + } + } + + return NULL; +} + +/* flash_bank <driver> <base> <size> <chip_width> <bus_width> [driver_options ...] + */ +int handle_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int i; + int found = 0; + + if (argc < 5) + { + WARNING("incomplete flash_bank configuration"); + return ERROR_OK; + } + + for (i = 0; flash_drivers[i]; i++) + { + if (strcmp(args[0], flash_drivers[i]->name) == 0) + { + flash_bank_t *p, *c; + + /* register flash specific commands */ + if (flash_drivers[i]->register_commands(cmd_ctx) != ERROR_OK) + { + ERROR("couldn't register '%s' commands", args[0]); + exit(-1); + } + + c = malloc(sizeof(flash_bank_t)); + c->driver = flash_drivers[i]; + c->driver_priv = NULL; + c->base = strtoul(args[1], NULL, 0); + c->size = strtoul(args[2], NULL, 0); + c->chip_width = strtoul(args[3], NULL, 0); + c->bus_width = strtoul(args[4], NULL, 0); + c->next = NULL; + + if (flash_drivers[i]->flash_bank_command(cmd_ctx, cmd, args, argc, c) != ERROR_OK) + { + ERROR("'%s' driver rejected flash bank at 0x%8.8x", args[0], c->base); + free(c); + return ERROR_OK; + } + + /* put flash bank in linked list */ + if (flash_banks) + { + /* find last flash bank */ + for (p = flash_banks; p && p->next; p = p->next); + if (p) + p->next = c; + } + else + { + flash_banks = c; + } + + found = 1; + } + } + + /* no matching flash driver found */ + if (!found) + { + ERROR("flash driver '%s' not found", args[0]); + exit(-1); + } + + return ERROR_OK; +} + +int handle_flash_banks_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + flash_bank_t *p; + int i = 0; + + if (!flash_banks) + { + command_print(cmd_ctx, "no flash banks configured"); + return ERROR_OK; + } + + for (p = flash_banks; p; p = p->next) + { + command_print(cmd_ctx, "#%i: %s at 0x%8.8x, size 0x%8.8x, buswidth %i, chipwidth %i", + i++, p->driver->name, p->base, p->size, p->bus_width, p->chip_width); + } + + return ERROR_OK; +} + +int handle_flash_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + flash_bank_t *p; + int i = 0; + int j = 0; + + if (argc != 1) + { + command_print(cmd_ctx, "usage: flash info <num>"); + return ERROR_OK; + } + + for (p = flash_banks; p; p = p->next) + { + if (i++ == strtoul(args[0], NULL, 0)) + { + char buf[1024]; + + command_print(cmd_ctx, "#%i: %s at 0x%8.8x, size 0x%8.8x, buswidth %i, chipwidth %i", + i, p->driver->name, p->base, p->size, p->bus_width, p->chip_width); + for (j = 0; j < p->num_sectors; j++) + { + char *erase_state, *protect_state; + + if (p->sectors[j].is_erased == 0) + erase_state = "not erased"; + else if (p->sectors[j].is_erased == 1) + erase_state = "erased"; + else + erase_state = "erase state unknown"; + + if (p->sectors[j].is_protected == 0) + protect_state = "not protected"; + else if (p->sectors[j].is_protected == 1) + protect_state = "protected"; + else + protect_state = "protection state unknown"; + + command_print(cmd_ctx, "\t#%i: 0x%8.8x (0x%xkB) %s, %s", + j, p->sectors[j].offset, p->sectors[j].size, + erase_state, protect_state); + } + + p->driver->info(p, buf, 1024); + command_print(cmd_ctx, "%s", buf); + } + } + + return ERROR_OK; +} + +int handle_flash_probe_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + flash_bank_t *p; + int retval; + + if (argc != 1) + { + command_print(cmd_ctx, "usage: flash probe <num>"); + return ERROR_OK; + } + + p = get_flash_bank_by_num(strtoul(args[0], NULL, 0)); + if (p) + { + if ((retval = p->driver->probe(p)) == ERROR_OK) + { + command_print(cmd_ctx, "flash '%s' found at 0x%8.8x", p->driver->name, p->base); + } + else if (retval == ERROR_FLASH_BANK_INVALID) + { + command_print(cmd_ctx, "probing failed for flash bank '#%s' at 0x%8.8x", + args[0], p->base); + } + else + { + command_print(cmd_ctx, "unknown error when probing flash bank '#%s' at 0x%8.8x", + args[0], p->base); + } + } + else + { + command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]); + } + + return ERROR_OK; +} + +int handle_flash_erase_check_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + flash_bank_t *p; + int retval; + + if (argc != 1) + { + command_print(cmd_ctx, "usage: flash erase_check <num>"); + return ERROR_OK; + } + + p = get_flash_bank_by_num(strtoul(args[0], NULL, 0)); + if (p) + { + if ((retval = p->driver->erase_check(p)) == ERROR_OK) + { + command_print(cmd_ctx, "successfully checked erase state", p->driver->name, p->base); + } + else + { + command_print(cmd_ctx, "unknown error when checking erase state of flash bank #%s at 0x%8.8x", + args[0], p->base); + } + } + else + { + command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]); + } + + return ERROR_OK; +} + +int handle_flash_protect_check_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + flash_bank_t *p; + int retval; + + if (argc != 1) + { + command_print(cmd_ctx, "usage: flash protect_check <num>"); + return ERROR_OK; + } + + p = get_flash_bank_by_num(strtoul(args[0], NULL, 0)); + if (p) + { + if ((retval = p->driver->protect_check(p)) == ERROR_OK) + { + command_print(cmd_ctx, "successfully checked protect state"); + } + else if (retval == ERROR_FLASH_OPERATION_FAILED) + { + command_print(cmd_ctx, "checking protection state failed (possibly unsupported) by flash #%s at 0x%8.8x", args[0], p->base); + } + else + { + command_print(cmd_ctx, "unknown error when checking protection state of flash bank '#%s' at 0x%8.8x", args[0], p->base); + } + } + else + { + command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]); + } + + return ERROR_OK; +} + +int handle_flash_erase_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc > 2) + { + int first = strtoul(args[1], NULL, 0); + int last = strtoul(args[2], NULL, 0); + int retval; + flash_bank_t *p = get_flash_bank_by_num(strtoul(args[0], NULL, 0)); + if (!p) + { + command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]); + return ERROR_OK; + } + + if ((retval = p->driver->erase(p, first, last)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_NOT_HALTED: + command_print(cmd_ctx, "can't work with this flash while target is running"); + break; + case ERROR_INVALID_ARGUMENTS: + command_print(cmd_ctx, "usage: flash_erase <bank> <first> <last>"); + break; + case ERROR_FLASH_BANK_INVALID: + command_print(cmd_ctx, "no '%s' flash found at 0x%8.8x", p->driver->name, p->base); + break; + case ERROR_FLASH_OPERATION_FAILED: + command_print(cmd_ctx, "flash erase error"); + break; + case ERROR_FLASH_SECTOR_INVALID: + command_print(cmd_ctx, "sector number(s) invalid"); + break; + case ERROR_OK: + command_print(cmd_ctx, "erased flash sectors %i to %i", first, last); + break; + default: + command_print(cmd_ctx, "unknown error"); + } + } + } + else + { + command_print(cmd_ctx, "usage: flash erase <bank> <first> <last>"); + } + + return ERROR_OK; +} + +int handle_flash_protect_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc > 3) + { + int first = strtoul(args[1], NULL, 0); + int last = strtoul(args[2], NULL, 0); + int set; + int retval; + flash_bank_t *p = get_flash_bank_by_num(strtoul(args[0], NULL, 0)); + if (!p) + { + command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]); + return ERROR_OK; + } + + if (strcmp(args[3], "on") == 0) + set = 1; + else if (strcmp(args[3], "off") == 0) + set = 0; + else + { + command_print(cmd_ctx, "usage: flash protect <bank> <first> <last> <on|off>"); + return ERROR_OK; + } + + if ((retval = p->driver->protect(p, set, first, last)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_NOT_HALTED: + command_print(cmd_ctx, "can't work with this flash while target is running"); + break; + case ERROR_INVALID_ARGUMENTS: + command_print(cmd_ctx, "usage: flash protect <bank> <first> <last> <on|off>"); + break; + case ERROR_FLASH_BANK_INVALID: + command_print(cmd_ctx, "no '%s' flash found at 0x%8.8x", p->driver->name, p->base); + break; + case ERROR_FLASH_OPERATION_FAILED: + command_print(cmd_ctx, "flash program error"); + break; + case ERROR_FLASH_SECTOR_INVALID: + command_print(cmd_ctx, "sector number(s) invalid"); + break; + case ERROR_OK: + command_print(cmd_ctx, "protection of flash sectors %i to %i turned %s", first, last, args[3]); + break; + default: + command_print(cmd_ctx, "unknown error"); + } + } + } + else + { + command_print(cmd_ctx, "usage: flash protect <bank> <first> <last> <on|off>"); + } + + return ERROR_OK; +} + +int handle_flash_write_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + FILE *binary; + u32 offset; + struct stat binary_stat; + u32 binary_size; + u8 *buffer; + u32 buf_cnt; + int retval; + flash_bank_t *p; + + if (argc < 3) + { + command_print(cmd_ctx, "usage: flash write <bank> <file> <offset>"); + return ERROR_OK; + } + + offset = strtoul(args[2], NULL, 0); + p = get_flash_bank_by_num(strtoul(args[0], NULL, 0)); + if (!p) + { + command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]); + return ERROR_OK; + } + + if (stat(args[1], &binary_stat) == -1) + { + ERROR("couldn't stat() %s: %s", args[1], strerror(errno)); + return ERROR_OK; + } + + if (S_ISDIR(binary_stat.st_mode)) + { + ERROR("%s is a directory", args[1]); + command_print(cmd_ctx,"%s is a directory", args[1]); + return ERROR_OK; + } + + if (binary_stat.st_size == 0){ + ERROR("Empty file %s", args[1]); + command_print(cmd_ctx,"Empty file %s", args[1]); + return ERROR_OK; + } + + if (!(binary = fopen(args[1], "r"))) + { + ERROR("couldn't open %s: %s", args[1], strerror(errno)); + command_print(cmd_ctx, "couldn't open %s", args[1]); + return ERROR_OK; + } + + binary_size = binary_stat.st_size; + buffer = malloc(binary_size); + buf_cnt = fread(buffer, 1, binary_size, binary); + + if ((retval = p->driver->write(p, buffer, offset, buf_cnt)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_NOT_HALTED: + command_print(cmd_ctx, "can't work with this flash while target is running"); + break; + case ERROR_INVALID_ARGUMENTS: + command_print(cmd_ctx, "usage: flash write <bank> <file> <offset>"); + break; + case ERROR_FLASH_BANK_INVALID: + command_print(cmd_ctx, "no '%s' flash found at 0x%8.8x", p->driver->name, p->base); + break; + case ERROR_FLASH_OPERATION_FAILED: + command_print(cmd_ctx, "flash program error"); + break; + case ERROR_FLASH_DST_BREAKS_ALIGNMENT: + command_print(cmd_ctx, "offset breaks required alignment"); + break; + case ERROR_FLASH_DST_OUT_OF_BANK: + command_print(cmd_ctx, "destination is out of flash bank (offset and/or file too large)"); + break; + case ERROR_FLASH_SECTOR_NOT_ERASED: + command_print(cmd_ctx, "destination sector(s) not erased"); + break; + default: + command_print(cmd_ctx, "unknown error"); + } + } + free(buffer); + fclose(binary); + + command_print(cmd_ctx, "wrote file %s to flash bank %i at offset 0x%8.8x", args[1], strtoul(args[0], NULL, 0), strtoul(args[2], NULL, 0)); + + return ERROR_OK; + +} diff --git a/src/flash/flash.h b/src/flash/flash.h new file mode 100644 index 00000000..a8cc1869 --- /dev/null +++ b/src/flash/flash.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef FLASH_H +#define FLASH_H + +#include "target.h" + +typedef struct flash_sector_s +{ + u32 offset; + u32 size; + int is_erased; + int is_protected; +} flash_sector_t; + +struct flash_bank_s; + +typedef struct flash_driver_s +{ + char *name; + int (*register_commands)(struct command_context_s *cmd_ctx); + int (*flash_bank_command)(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank); + int (*erase)(struct flash_bank_s *bank, int first, int last); + int (*protect)(struct flash_bank_s *bank, int set, int first, int last); + int (*write)(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count); + int (*probe)(struct flash_bank_s *bank); + int (*erase_check)(struct flash_bank_s *bank); + int (*protect_check)(struct flash_bank_s *bank); + int (*info)(struct flash_bank_s *bank, char *buf, int buf_size); +} flash_driver_t; + +typedef struct flash_bank_s +{ + flash_driver_t *driver; + void *driver_priv; + u32 base; + u32 size; + int chip_width; + int bus_width; + int num_sectors; + flash_sector_t *sectors; + struct flash_bank_s *next; +} flash_bank_t; + +extern int flash_register_commands(struct command_context_s *cmd_ctx); +extern int flash_init(struct command_context_s *cmd_ctx); + +extern flash_bank_t *get_flash_bank_by_num(int num); + +#define ERROR_FLASH_BANK_INVALID (-900) +#define ERROR_FLASH_SECTOR_INVALID (-901) +#define ERROR_FLASH_OPERATION_FAILED (-902) +#define ERROR_FLASH_DST_OUT_OF_BANK (-903) +#define ERROR_FLASH_DST_BREAKS_ALIGNMENT (-904) +#define ERROR_FLASH_BUSY (-905) +#define ERROR_FLASH_SECTOR_NOT_ERASED (-906) +#define ERROR_FLASH_BANK_NOT_PROBED (-907) + +#endif /* FLASH_H */ diff --git a/src/flash/lpc2000.c b/src/flash/lpc2000.c new file mode 100644 index 00000000..b6fcb30b --- /dev/null +++ b/src/flash/lpc2000.c @@ -0,0 +1,685 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "lpc2000.h" + +#include "flash.h" +#include "target.h" +#include "log.h" +#include "armv4_5.h" +#include "algorithm.h" +#include "binarybuffer.h" + +#include <stdlib.h> +#include <string.h> + +/* flash programming support for Philips LPC2xxx devices + * currently supported devices: + * variant 1 (lpc2000_v1): + * - 2104|5|6 + * - 2114|9 + * - 2124|9 + * - 2194 + * - 2212|4 + * - 2292|4 + * + * variant 2 (lpc2000_v2): + * - 213x + * - 214x + */ + +int lpc2000_register_commands(struct command_context_s *cmd_ctx); +int lpc2000_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank); +int lpc2000_erase(struct flash_bank_s *bank, int first, int last); +int lpc2000_protect(struct flash_bank_s *bank, int set, int first, int last); +int lpc2000_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count); +int lpc2000_probe(struct flash_bank_s *bank); +int lpc2000_erase_check(struct flash_bank_s *bank); +int lpc2000_protect_check(struct flash_bank_s *bank); +int lpc2000_info(struct flash_bank_s *bank, char *buf, int buf_size); + +int lpc2000_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +flash_driver_t lpc2000_flash = +{ + .name = "lpc2000", + .register_commands = lpc2000_register_commands, + .flash_bank_command = lpc2000_flash_bank_command, + .erase = lpc2000_erase, + .protect = lpc2000_protect, + .write = lpc2000_write, + .probe = lpc2000_probe, + .erase_check = lpc2000_erase_check, + .protect_check = lpc2000_protect_check, + .info = lpc2000_info +}; + +int lpc2000_register_commands(struct command_context_s *cmd_ctx) +{ + command_t *lpc2000_cmd = register_command(cmd_ctx, NULL, "lpc2000", NULL, COMMAND_ANY, NULL); + + register_command(cmd_ctx, lpc2000_cmd, "part_id", lpc2000_handle_part_id_command, COMMAND_EXEC, + "print part id of lpc2000 flash bank <num>"); + + return ERROR_OK; +} + +int lpc2000_build_sector_list(struct flash_bank_s *bank) +{ + lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv; + + if (lpc2000_info->variant == 1) + { + int i = 0; + u32 offset = 0; + + /* variant 1 has different layout for 128kb and 256kb flashes */ + if (bank->size == 128 * 1024) + { + bank->num_sectors = 16; + bank->sectors = malloc(sizeof(flash_sector_t) * 16); + for (i = 0; i < 16; i++) + { + bank->sectors[i].offset = offset; + bank->sectors[i].size = 8 * 1024; + offset += bank->sectors[i].size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + } + else if (bank->size == 256 * 1024) + { + bank->num_sectors = 18; + bank->sectors = malloc(sizeof(flash_sector_t) * 18); + + for (i = 0; i < 8; i++) + { + bank->sectors[i].offset = offset; + bank->sectors[i].size = 8 * 1024; + offset += bank->sectors[i].size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + for (i = 8; i < 10; i++) + { + bank->sectors[i].offset = offset; + bank->sectors[i].size = 64 * 1024; + offset += bank->sectors[i].size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + for (i = 10; i < 18; i++) + { + bank->sectors[i].offset = offset; + bank->sectors[i].size = 8 * 1024; + offset += bank->sectors[i].size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + } + else + { + ERROR("BUG: unknown bank->size encountered"); + exit(-1); + } + } + else if (lpc2000_info->variant == 2) + { + int num_sectors; + int i; + u32 offset = 0; + + /* variant 2 has a uniform layout, only number of sectors differs */ + switch (bank->size) + { + case 32 * 1024: + num_sectors = 8; + break; + case 64 * 1024: + num_sectors = 9; + break; + case 128 * 1024: + num_sectors = 11; + break; + case 256 * 1024: + num_sectors = 15; + break; + case 500 * 1024: + num_sectors = 27; + break; + default: + ERROR("BUG: unknown bank->size encountered"); + exit(-1); + break; + } + + bank->num_sectors = num_sectors; + bank->sectors = malloc(sizeof(flash_sector_t) * num_sectors); + + for (i = 0; i < num_sectors; i++) + { + if ((i >= 0) && (i < 8)) + { + bank->sectors[i].offset = offset; + bank->sectors[i].size = 4 * 1024; + offset += bank->sectors[i].size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + if ((i >= 8) && (i < 22)) + { + bank->sectors[i].offset = offset; + bank->sectors[i].size = 32 * 1024; + offset += bank->sectors[i].size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + if ((i >= 22) && (i < 27)) + { + bank->sectors[i].offset = offset; + bank->sectors[i].size = 4 * 1024; + offset += bank->sectors[i].size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + } + } + else + { + ERROR("BUG: unknown lpc2000_info->variant encountered"); + exit(-1); + } + + return ERROR_OK; +} + +/* call LPC2000 IAP function + * uses 172 bytes working area + * 0x0 to 0x7: jump gate (BX to thumb state, b -2 to wait) + * 0x8 to 0x1f: command parameter table + * 0x20 to 0x2b: command result table + * 0x2c to 0xac: stack (only 128b needed) + */ +int lpc2000_iap_call(flash_bank_t *bank, int code, u32 param_table[5], u32 result_table[2]) +{ + lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv; + target_t *target = lpc2000_info->target; + mem_param_t mem_params[2]; + reg_param_t reg_params[5]; + armv4_5_algorithm_t armv4_5_info; + u32 status_code; + + /* regrab previously allocated working_area, or allocate a new one */ + if (!lpc2000_info->iap_working_area) + { + u8 jump_gate[8]; + + /* make sure we have a working area */ + if (target_alloc_working_area(target, 172, &lpc2000_info->iap_working_area) != ERROR_OK) + { + ERROR("no working area specified, can't write LPC2000 internal flash"); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* write IAP code to working area */ + buf_set_u32(jump_gate, 0, 32, ARMV4_5_BX(12)); + buf_set_u32(jump_gate, 32, 32, 0xeafffffe); + target->type->write_memory(target, lpc2000_info->iap_working_area->address, 4, 2, (u8*)jump_gate); + } + + armv4_5_info.common_magic = ARMV4_5_COMMON_MAGIC; + armv4_5_info.core_mode = ARMV4_5_MODE_SVC; + armv4_5_info.core_state = ARMV4_5_STATE_ARM; + + /* command parameter table */ + init_mem_param(&mem_params[0], lpc2000_info->iap_working_area->address + 8, 4 * 6, PARAM_OUT); + buf_set_u32(mem_params[0].value, 0, 32, code); + buf_set_u32(mem_params[0].value, 32, 32, param_table[0]); + buf_set_u32(mem_params[0].value, 64, 32, param_table[1]); + buf_set_u32(mem_params[0].value, 96, 32, param_table[2]); + buf_set_u32(mem_params[0].value, 128, 32, param_table[3]); + buf_set_u32(mem_params[0].value, 160, 32, param_table[4]); + + init_reg_param(®_params[0], "r0", 32, PARAM_OUT); + buf_set_u32(reg_params[0].value, 0, 32, lpc2000_info->iap_working_area->address + 0x8); + + /* command result table */ + init_mem_param(&mem_params[1], lpc2000_info->iap_working_area->address + 0x20, 4 * 3, PARAM_IN); + + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); + buf_set_u32(reg_params[1].value, 0, 32, lpc2000_info->iap_working_area->address + 0x20); + + /* IAP entry point */ + init_reg_param(®_params[2], "r12", 32, PARAM_OUT); + buf_set_u32(reg_params[2].value, 0, 32, 0x7ffffff1); + + /* IAP stack */ + init_reg_param(®_params[3], "r13_svc", 32, PARAM_OUT); + buf_set_u32(reg_params[3].value, 0, 32, lpc2000_info->iap_working_area->address + 0xac); + + /* return address */ + init_reg_param(®_params[4], "lr_svc", 32, PARAM_OUT); + buf_set_u32(reg_params[4].value, 0, 32, lpc2000_info->iap_working_area->address + 0x4); + + target->type->run_algorithm(target, 2, mem_params, 5, reg_params, lpc2000_info->iap_working_area->address, lpc2000_info->iap_working_area->address + 0x4, 10000, &armv4_5_info); + + status_code = buf_get_u32(mem_params[1].value, 0, 32); + result_table[0] = buf_get_u32(mem_params[1].value, 32, 32); + result_table[1] = buf_get_u32(mem_params[1].value, 64, 32); + + destroy_mem_param(&mem_params[0]); + destroy_mem_param(&mem_params[1]); + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + destroy_reg_param(®_params[4]); + + return status_code; +} + +int lpc2000_iap_blank_check(struct flash_bank_s *bank, int first, int last) +{ + u32 param_table[5]; + u32 result_table[2]; + int status_code; + int i; + + if ((first < 0) || (last > bank->num_sectors)) + return ERROR_FLASH_SECTOR_INVALID; + + for (i = first; i <= last; i++) + { + /* check single sector */ + param_table[0] = param_table[1] = i; + status_code = lpc2000_iap_call(bank, 53, param_table, result_table); + + switch (status_code) + { + case ERROR_FLASH_OPERATION_FAILED: + return ERROR_FLASH_OPERATION_FAILED; + case LPC2000_CMD_SUCCESS: + bank->sectors[i].is_erased = 1; + break; + case LPC2000_SECTOR_NOT_BLANK: + bank->sectors[i].is_erased = 0; + break; + case LPC2000_INVALID_SECTOR: + bank->sectors[i].is_erased = 0; + break; + case LPC2000_BUSY: + return ERROR_FLASH_BUSY; + break; + default: + ERROR("BUG: unknown LPC2000 status code"); + exit(-1); + } + } + + return ERROR_OK; +} + +/* flash_bank lpc2000 <base> <size> 0 0 <lpc_variant> <target#> <cclk> [calc_checksum] + */ +int lpc2000_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank) +{ + lpc2000_flash_bank_t *lpc2000_info; + + if (argc < 8) + { + WARNING("incomplete flash_bank lpc2000 configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + lpc2000_info = malloc(sizeof(lpc2000_flash_bank_t)); + bank->driver_priv = lpc2000_info; + + if (strcmp(args[5], "lpc2000_v1") == 0) + { + lpc2000_info->variant = 1; + lpc2000_info->cmd51_dst_boundary = 512; + lpc2000_info->cmd51_can_256b = 0; + lpc2000_info->cmd51_can_8192b = 1; + } + else if (strcmp(args[5], "lpc2000_v2") == 0) + { + lpc2000_info->variant = 2; + lpc2000_info->cmd51_dst_boundary = 256; + lpc2000_info->cmd51_can_256b = 1; + lpc2000_info->cmd51_can_8192b = 0; + } + else + { + ERROR("unknown LPC2000 variant"); + free(lpc2000_info); + return ERROR_FLASH_BANK_INVALID; + } + + lpc2000_info->target = get_target_by_num(strtoul(args[6], NULL, 0)); + if (!lpc2000_info->target) + { + ERROR("no target '%s' configured", args[6]); + exit(-1); + } + lpc2000_info->iap_working_area = NULL; + lpc2000_info->cclk = strtoul(args[7], NULL, 0); + lpc2000_info->calc_checksum = 0; + lpc2000_build_sector_list(bank); + + + if (argc >= 9) + { + if (strcmp(args[8], "calc_checksum") == 0) + lpc2000_info->calc_checksum = 1; + } + + return ERROR_OK; +} + +int lpc2000_erase(struct flash_bank_s *bank, int first, int last) +{ + lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv; + u32 param_table[5]; + u32 result_table[2]; + int status_code; + + if (lpc2000_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) + { + return ERROR_FLASH_SECTOR_INVALID; + } + + param_table[0] = first; + param_table[1] = last; + param_table[2] = lpc2000_info->cclk; + + /* Prepare sectors */ + status_code = lpc2000_iap_call(bank, 50, param_table, result_table); + switch (status_code) + { + case ERROR_FLASH_OPERATION_FAILED: + return ERROR_FLASH_OPERATION_FAILED; + case LPC2000_CMD_SUCCESS: + break; + case LPC2000_INVALID_SECTOR: + return ERROR_FLASH_SECTOR_INVALID; + break; + default: + WARNING("lpc2000 prepare sectors returned %i", status_code); + return ERROR_FLASH_OPERATION_FAILED; + } + + /* Erase sectors */ + status_code = lpc2000_iap_call(bank, 52, param_table, result_table); + switch (status_code) + { + case ERROR_FLASH_OPERATION_FAILED: + return ERROR_FLASH_OPERATION_FAILED; + case LPC2000_CMD_SUCCESS: + break; + case LPC2000_INVALID_SECTOR: + return ERROR_FLASH_SECTOR_INVALID; + break; + default: + WARNING("lpc2000 erase sectors returned %i", status_code); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + +int lpc2000_protect(struct flash_bank_s *bank, int set, int first, int last) +{ + /* can't protect/unprotect on the lpc2000 */ + return ERROR_OK; +} + +int lpc2000_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count) +{ + lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv; + target_t *target = lpc2000_info->target; + u32 dst_min_alignment; + u32 bytes_remaining = count; + u32 bytes_written = 0; + int first_sector = 0; + int last_sector = 0; + u32 param_table[5]; + u32 result_table[2]; + int status_code; + int i; + working_area_t *download_area; + + if (lpc2000_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + /* allocate a working area */ + if (target_alloc_working_area(target, 4096, &download_area) != ERROR_OK) + { + ERROR("no working area specified, can't write LPC2000 internal flash"); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (offset + count > bank->size) + return ERROR_FLASH_DST_OUT_OF_BANK; + + if (lpc2000_info->cmd51_can_256b) + dst_min_alignment = 256; + else + dst_min_alignment = 512; + + if (offset % dst_min_alignment) + { + WARNING("offset 0x%x breaks required alignment 0x%x", offset, dst_min_alignment); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + for (i = 0; i < bank->num_sectors; i++) + { + if (offset >= bank->sectors[i].offset) + first_sector = i; + if (offset + CEIL(count, dst_min_alignment) * dst_min_alignment > bank->sectors[i].offset) + last_sector = i; + } + + DEBUG("first_sector: %i, last_sector: %i", first_sector, last_sector); + + /* check if exception vectors should be flashed */ + if ((offset == 0) && (count >= 0x20) && lpc2000_info->calc_checksum) + { + u32 checksum = 0; + int i = 0; + for (i = 0; i < 8; i++) + { + DEBUG("0x%2.2x: 0x%8.8x", i * 4, buf_get_u32(buffer + (i * 4), 0, 32)); + if (i != 5) + checksum += buf_get_u32(buffer + (i * 4), 0, 32); + } + checksum = 0 - checksum; + DEBUG("checksum: 0x%8.8x", checksum); + buf_set_u32(buffer + 0x14, 0, 32, checksum); + } + + while (bytes_remaining > 0) + { + u32 thisrun_bytes; + if (bytes_remaining >= 4096) + thisrun_bytes = 4096; + else if (bytes_remaining >= 1024) + thisrun_bytes = 1024; + else if ((bytes_remaining >= 512) || (!lpc2000_info->cmd51_can_256b)) + thisrun_bytes = 512; + else + thisrun_bytes = 256; + + /* Prepare sectors */ + param_table[0] = first_sector; + param_table[1] = last_sector; + status_code = lpc2000_iap_call(bank, 50, param_table, result_table); + switch (status_code) + { + case ERROR_FLASH_OPERATION_FAILED: + return ERROR_FLASH_OPERATION_FAILED; + case LPC2000_CMD_SUCCESS: + break; + case LPC2000_INVALID_SECTOR: + return ERROR_FLASH_SECTOR_INVALID; + break; + default: + WARNING("lpc2000 prepare sectors returned %i", status_code); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (bytes_remaining >= thisrun_bytes) + { + if (target_write_buffer(lpc2000_info->target, download_area->address, thisrun_bytes, buffer + bytes_written) != ERROR_OK) + { + target_free_working_area(target, download_area); + return ERROR_FLASH_OPERATION_FAILED; + } + } + else + { + u8 *last_buffer = malloc(thisrun_bytes); + int i; + memcpy(last_buffer, buffer + bytes_written, bytes_remaining); + for (i = bytes_remaining; i < thisrun_bytes; i++) + last_buffer[i] = 0xff; + target_write_buffer(lpc2000_info->target, download_area->address, thisrun_bytes, last_buffer); + free(last_buffer); + } + + DEBUG("writing 0x%x bytes to address 0x%x", thisrun_bytes, bank->base + offset + bytes_written); + + /* Write data */ + param_table[0] = bank->base + offset + bytes_written; + param_table[1] = download_area->address; + param_table[2] = thisrun_bytes; + param_table[3] = lpc2000_info->cclk; + status_code = lpc2000_iap_call(bank, 51, param_table, result_table); + switch (status_code) + { + case ERROR_FLASH_OPERATION_FAILED: + return ERROR_FLASH_OPERATION_FAILED; + case LPC2000_CMD_SUCCESS: + break; + case LPC2000_INVALID_SECTOR: + return ERROR_FLASH_SECTOR_INVALID; + break; + default: + WARNING("lpc2000 returned %i", status_code); + return ERROR_FLASH_OPERATION_FAILED; + } + + if (bytes_remaining > thisrun_bytes) + bytes_remaining -= thisrun_bytes; + else + bytes_remaining = 0; + bytes_written += thisrun_bytes; + } + + target_free_working_area(target, download_area); + + return ERROR_OK; +} + +int lpc2000_probe(struct flash_bank_s *bank) +{ + /* we can't probe on an lpc2000 + * if this is an lpc2xxx, it has the configured flash + */ + return ERROR_OK; +} + +int lpc2000_erase_check(struct flash_bank_s *bank) +{ + lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv; + + if (lpc2000_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + return lpc2000_iap_blank_check(bank, 0, bank->num_sectors - 1); +} + +int lpc2000_protect_check(struct flash_bank_s *bank) +{ + /* sectors are always protected */ + return ERROR_OK; +} + +int lpc2000_info(struct flash_bank_s *bank, char *buf, int buf_size) +{ + lpc2000_flash_bank_t *lpc2000_info = bank->driver_priv; + + snprintf(buf, buf_size, "lpc2000 flash driver variant: %i, clk: %i", lpc2000_info->variant, lpc2000_info->cclk); + + return ERROR_OK; +} + +int lpc2000_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + flash_bank_t *bank; + u32 param_table[5]; + u32 result_table[2]; + int status_code; + lpc2000_flash_bank_t *lpc2000_info; + + if (argc < 1) + { + command_print(cmd_ctx, "usage: lpc2000 part_id <num>"); + return ERROR_OK; + } + + bank = get_flash_bank_by_num(strtoul(args[0], NULL, 0)); + if (!bank) + { + command_print(cmd_ctx, "flash bank '#%s' is out of bounds", args[0]); + return ERROR_OK; + } + + lpc2000_info = bank->driver_priv; + if (lpc2000_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + if ((status_code = lpc2000_iap_call(bank, 54, param_table, result_table)) != 0x0) + { + if (status_code == ERROR_FLASH_OPERATION_FAILED) + { + command_print(cmd_ctx, "no sufficient working area specified, can't access LPC2000 IAP interface"); + return ERROR_OK; + } + command_print(cmd_ctx, "lpc2000 IAP returned status code %i", status_code); + } + else + { + command_print(cmd_ctx, "lpc2000 part id: 0x%8.8x", result_table[0]); + } + + return ERROR_OK; +} diff --git a/src/flash/lpc2000.h b/src/flash/lpc2000.h new file mode 100644 index 00000000..dbbe4b6a --- /dev/null +++ b/src/flash/lpc2000.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef LPC2000_H +#define LPC2000_H + +#include "flash.h" +#include "target.h" + +typedef struct lpc2000_flash_bank_s +{ + int variant; + struct target_s *target; + struct working_area_s *iap_working_area; + u32 cclk; + int cmd51_dst_boundary; + int cmd51_can_256b; + int cmd51_can_8192b; + int calc_checksum; +} lpc2000_flash_bank_t; + +enum lpc2000_status_codes +{ + LPC2000_CMD_SUCCESS = 0, + LPC2000_INVALID_COMMAND = 1, + LPC2000_SRC_ADDR_ERROR = 2, + LPC2000_DST_ADDR_ERROR = 3, + LPC2000_SRC_ADDR_NOT_MAPPED = 4, + LPC2000_DST_ADDR_NOT_MAPPED = 5, + LPC2000_COUNT_ERROR = 6, + LPC2000_INVALID_SECTOR = 7, + LPC2000_SECTOR_NOT_BLANK = 8, + LPC2000_SECTOR_NOT_PREPARED = 9, + LPC2000_COMPARE_ERROR = 10, + LPC2000_BUSY = 11 +}; + +#endif /* LPC2000_H */ diff --git a/src/flash/str7x.c b/src/flash/str7x.c new file mode 100644 index 00000000..2e3a6c8c --- /dev/null +++ b/src/flash/str7x.c @@ -0,0 +1,469 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "str7x.h" +#include "flash.h" +#include "target.h" +#include "log.h" +#include "armv4_5.h" +#include "algorithm.h" +#include "binarybuffer.h" + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +str7x_mem_layout_t mem_layout[] = { + {0x00000000, 0x02000, 0x01}, + {0x00002000, 0x02000, 0x01}, + {0x00004000, 0x02000, 0x01}, + {0x00006000, 0x02000, 0x01}, + {0x00008000, 0x08000, 0x01}, + {0x00010000, 0x10000, 0x01}, + {0x00020000, 0x10000, 0x01}, + {0x00030000, 0x10000, 0x01}, + {0x000C0000, 0x02000, 0x10}, + {0x000C2000, 0x02000, 0x10}, + {0,0}, +}; + +int str7x_register_commands(struct command_context_s *cmd_ctx); +int str7x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank); +int str7x_erase(struct flash_bank_s *bank, int first, int last); +int str7x_protect(struct flash_bank_s *bank, int set, int first, int last); +int str7x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count); +int str7x_probe(struct flash_bank_s *bank); +int str7x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int str7x_protect_check(struct flash_bank_s *bank); +int str7x_erase_check(struct flash_bank_s *bank); +int str7x_info(struct flash_bank_s *bank, char *buf, int buf_size); + +flash_driver_t str7x_flash = +{ + .name = "str7x", + .register_commands = str7x_register_commands, + .flash_bank_command = str7x_flash_bank_command, + .erase = str7x_erase, + .protect = str7x_protect, + .write = str7x_write, + .probe = str7x_probe, + .erase_check = str7x_erase_check, + .protect_check = str7x_protect_check, + .info = str7x_info +}; + +int str7x_register_commands(struct command_context_s *cmd_ctx) +{ + + return ERROR_OK; +} + +int str7x_get_flash_adr(struct flash_bank_s *bank, u32 reg) +{ + str7x_flash_bank_t *str7x_info = bank->driver_priv; + return (str7x_info->flash_base|reg); +} + +int str7x_build_block_list(struct flash_bank_s *bank) +{ + str7x_flash_bank_t *str7x_info = bank->driver_priv; + + int i; + int num_sectors; + + switch (bank->size) + { + case 16 * 1024: + num_sectors = 2; + break; + case 64 * 1024: + num_sectors = 5; + break; + case 128 * 1024: + num_sectors = 6; + break; + case 256 * 1024: + num_sectors = 8; + break; + default: + ERROR("BUG: unknown bank->size encountered"); + exit(-1); + } + + if( str7x_info->bank1 == 1 ) + { + num_sectors += 2; + } + + bank->num_sectors = num_sectors; + bank->sectors = malloc(sizeof(flash_sector_t) * num_sectors); + + for (i = 0; i < num_sectors; i++) + { + bank->sectors[i].offset = mem_layout[i].sector_start; + bank->sectors[i].size = mem_layout[i].sector_size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 1; + } + + return ERROR_OK; +} + +/* flash bank str7x <base> <size> 0 0 <str71_variant> <target#> + */ +int str7x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank) +{ + str7x_flash_bank_t *str7x_info; + + if (argc < 7) + { + WARNING("incomplete flash_bank str7x configuration"); + return ERROR_FLASH_BANK_INVALID; + } + + str7x_info = malloc(sizeof(str7x_flash_bank_t)); + bank->driver_priv = str7x_info; + + if (strcmp(args[5], "STR71x") == 0) + { + str7x_info->bank1 = 1; + str7x_info->flash_base = 0x40000000; + } + else if (strcmp(args[5], "STR73x") == 0) + { + str7x_info->bank1 = 0; + str7x_info->flash_base = 0x80000000; + } + else + { + ERROR("unknown STR7x variant"); + free(str7x_info); + return ERROR_FLASH_BANK_INVALID; + } + + str7x_info->target = get_target_by_num(strtoul(args[6], NULL, 0)); + if (!str7x_info->target) + { + ERROR("no target '%i' configured", args[6]); + exit(-1); + } + + str7x_build_block_list(bank); + + return ERROR_OK; +} + +u32 str7x_status(struct flash_bank_s *bank) +{ + str7x_flash_bank_t *str7x_info = bank->driver_priv; + target_t *target = str7x_info->target; + u32 retval; + + target->type->read_memory(target, str7x_get_flash_adr(bank, FLASH_CR0), 4, 1, (u8*)&retval); + + return retval; +} + +u32 str7x_result(struct flash_bank_s *bank) +{ + str7x_flash_bank_t *str7x_info = bank->driver_priv; + target_t *target = str7x_info->target; + u32 retval; + + target->type->read_memory(target, str7x_get_flash_adr(bank, FLASH_ER), 4, 1, (u8*)&retval); + + return retval; +} + +int str7x_blank_check(struct flash_bank_s *bank, int first, int last) +{ + str7x_flash_bank_t *str7x_info = bank->driver_priv; + target_t *target = str7x_info->target; + u8 *buffer; + int i; + int nBytes; + + if ((first < 0) || (last > bank->num_sectors)) + return ERROR_FLASH_SECTOR_INVALID; + + if (str7x_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + buffer = malloc(256); + + for (i = first; i <= last; i++) + { + bank->sectors[i].is_erased = 1; + + target->type->read_memory(target, bank->base + bank->sectors[i].offset, 4, 256/4, buffer); + + for (nBytes = 0; nBytes < 256; nBytes++) + { + if (buffer[nBytes] != 0xFF) + { + bank->sectors[i].is_erased = 0; + break; + } + } + } + + free(buffer); + + return ERROR_OK; +} + +int str7x_protect_check(struct flash_bank_s *bank) +{ + str7x_flash_bank_t *str7x_info = bank->driver_priv; + target_t *target = str7x_info->target; + + int i; + int retval; + + if (str7x_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + target->type->read_memory(target, str7x_get_flash_adr(bank, FLASH_NVWPAR), 4, 1, (u8*)&retval); + + for (i = 0; i < bank->num_sectors; i++) + { + if (retval & (mem_layout[i].reg_offset << i)) + bank->sectors[i].is_protected = 0; + else + bank->sectors[i].is_protected = 1; + } + + return ERROR_OK; +} + +int str7x_erase(struct flash_bank_s *bank, int first, int last) +{ + str7x_flash_bank_t *str7x_info = bank->driver_priv; + target_t *target = str7x_info->target; + + int i; + u32 cmd; + u32 retval; + u32 erase_blocks; + + if (str7x_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + erase_blocks = 0; + + for (i = first; i <= last; i++) + erase_blocks |= (mem_layout[i].reg_offset << i); + + cmd = FLASH_SER; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_CR0), 4, 1, (u8*)&cmd); + + cmd = erase_blocks; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_CR1), 4, 1, (u8*)&cmd); + + cmd = FLASH_SER|FLASH_WMS; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_CR0), 4, 1, (u8*)&cmd); + + while (((retval = str7x_status(bank)) & (FLASH_BSYA1|FLASH_BSYA2))){ + usleep(1000); + } + + retval = str7x_result(bank); + + if (retval & FLASH_ERER) + return ERROR_FLASH_SECTOR_NOT_ERASED; + else if (retval & FLASH_WPF) + return ERROR_FLASH_OPERATION_FAILED; + + for (i = first; i <= last; i++) + bank->sectors[i].is_erased = 1; + + return ERROR_OK; +} + +int str7x_protect(struct flash_bank_s *bank, int set, int first, int last) +{ + str7x_flash_bank_t *str7x_info = bank->driver_priv; + target_t *target = str7x_info->target; + int i; + u32 cmd; + u32 retval; + u32 protect_blocks; + + if (str7x_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + protect_blocks = 0xFFFFFFFF; + + if( set ) + { + for (i = first; i <= last; i++) + protect_blocks &= ~(mem_layout[i].reg_offset << i); + } + + cmd = FLASH_SPR; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_CR0), 4, 1, (u8*)&cmd); + + cmd = str7x_get_flash_adr(bank, FLASH_NVWPAR); + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_AR), 4, 1, (u8*)&cmd); + + cmd = protect_blocks; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0), 4, 1, (u8*)&cmd); + + cmd = FLASH_SPR|FLASH_WMS; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_CR0), 4, 1, (u8*)&cmd); + + while (((retval = str7x_status(bank)) & (FLASH_BSYA1|FLASH_BSYA2))){ + usleep(1000); + } + + retval = str7x_result(bank); + + if (retval & FLASH_ERER) + return ERROR_FLASH_SECTOR_NOT_ERASED; + else if (retval & FLASH_WPF) + return ERROR_FLASH_OPERATION_FAILED; + + return ERROR_OK; +} + +int str7x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count) +{ + str7x_flash_bank_t *str7x_info = bank->driver_priv; + target_t *target = str7x_info->target; + u32 dwords_remaining = (count / 8); + u32 bytes_remaining = (count & 0x00000007); + u32 address = bank->base + offset; + u32 *wordbuffer = (u32*)buffer; + u32 bytes_written = 0; + u32 cmd; + u32 retval; + + if (str7x_info->target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + if (offset + count > bank->size) + return ERROR_FLASH_DST_OUT_OF_BANK; + + while (dwords_remaining > 0) + { + // command + cmd = FLASH_DWPG; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_CR0), 4, 1, (u8*)&cmd); + + // address + cmd = address; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_AR), 4, 1, (u8*)&cmd); + + // data byte 1 + cmd = wordbuffer[bytes_written/4]; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0), 4, 1, (u8*)&cmd); + bytes_written += 4; + + // data byte 2 + cmd = wordbuffer[bytes_written/4]; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_DR1), 4, 1, (u8*)&cmd); + bytes_written += 4; + + /* start programming cycle */ + cmd = FLASH_DWPG|FLASH_WMS; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_CR0), 4, 1, (u8*)&cmd); + + while (((retval = str7x_status(bank)) & (FLASH_BSYA1|FLASH_BSYA2))){ + usleep(1000); + } + + retval = str7x_result(bank); + + if (retval & FLASH_PGER) + return ERROR_FLASH_OPERATION_FAILED; + else if (retval & FLASH_WPF) + return ERROR_FLASH_OPERATION_FAILED; + + dwords_remaining--; + address += 8; + } + + while( bytes_remaining > 0 ) + { + // command + cmd = FLASH_WPG; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_CR0), 4, 1, (u8*)&cmd); + + // address + cmd = address; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_AR), 4, 1, (u8*)&cmd); + + // data byte + cmd = buffer[bytes_written]; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0), 4, 1, (u8*)&cmd); + + /* start programming cycle */ + cmd = FLASH_WPG|FLASH_WMS; + target->type->write_memory(target, str7x_get_flash_adr(bank, FLASH_CR0), 4, 1, (u8*)&cmd); + + while (((retval = str7x_status(bank)) & (FLASH_BSYA1|FLASH_BSYA2))){ + usleep(1000); + } + + retval = str7x_result(bank); + + if (retval & FLASH_PGER) + return ERROR_FLASH_OPERATION_FAILED; + else if (retval & FLASH_WPF) + return ERROR_FLASH_OPERATION_FAILED; + + address++; + bytes_remaining--; + bytes_written++; + } + + return ERROR_OK; +} + +int str7x_probe(struct flash_bank_s *bank) +{ + return ERROR_OK; +} + +int str7x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + return ERROR_OK; +} + +int str7x_erase_check(struct flash_bank_s *bank) +{ + return str7x_blank_check(bank, 0, bank->num_sectors - 1); +} + +int str7x_info(struct flash_bank_s *bank, char *buf, int buf_size) +{ + snprintf(buf, buf_size, "str7x flash driver info" ); + return ERROR_OK; +} diff --git a/src/flash/str7x.h b/src/flash/str7x.h new file mode 100644 index 00000000..fe63b5e5 --- /dev/null +++ b/src/flash/str7x.h @@ -0,0 +1,106 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef STR7X_H +#define STR7X_H + +#include "flash.h" +#include "target.h" + +typedef struct str7x_flash_bank_s +{ + int bank1; + struct target_s *target; + u32 flash_base; +} str7x_flash_bank_t; + +enum str7x_status_codes +{ + STR7X_CMD_SUCCESS = 0, + STR7X_INVALID_COMMAND = 1, + STR7X_SRC_ADDR_ERROR = 2, + STR7X_DST_ADDR_ERROR = 3, + STR7X_SRC_ADDR_NOT_MAPPED = 4, + STR7X_DST_ADDR_NOT_MAPPED = 5, + STR7X_COUNT_ERROR = 6, + STR7X_INVALID_SECTOR = 7, + STR7X_SECTOR_NOT_BLANK = 8, + STR7X_SECTOR_NOT_PREPARED = 9, + STR7X_COMPARE_ERROR = 10, + STR7X_BUSY = 11 +}; + +/* Flash registers */ + +#define FLASH_CR0 0x00100000 +#define FLASH_CR1 0x00100004 +#define FLASH_DR0 0x00100008 +#define FLASH_DR1 0x0010000C +#define FLASH_AR 0x00100010 +#define FLASH_ER 0x00100014 +#define FLASH_NVWPAR 0x0010DFB0 +#define FLASH_NVAPR0 0x0010DFB8 +#define FLASH_NVAPR1 0x0010DFBC + +/* FLASH_CR0 register bits */ + +#define FLASH_WMS 0x80000000 +#define FLASH_SUSP 0x40000000 +#define FLASH_WPG 0x20000000 +#define FLASH_DWPG 0x10000000 +#define FLASH_SER 0x08000000 +#define FLASH_SPR 0x01000000 +#define FLASH_BER 0x04000000 +#define FLASH_MER 0x02000000 +#define FLASH_BSYA1 0x00000002 +#define FLASH_BSYA2 0x00000004 + +/* FLASH_CR1 regsiter bits */ + +#define FLASH_B1S 0x02000000 +#define FLASH_B0S 0x01000000 +#define FLASH_B1F1 0x00020000 +#define FLASH_B1F0 0x00010000 +#define FLASH_B0F7 0x00000080 +#define FLASH_B0F6 0x00000040 +#define FLASH_B0F5 0x00000020 +#define FLASH_B0F4 0x00000010 +#define FLASH_B0F3 0x00000008 +#define FLASH_B0F2 0x00000004 +#define FLASH_B0F1 0x00000002 +#define FLASH_B0F0 0x00000001 + +/* FLASH_ER register bits */ + +#define FLASH_WPF 0x00000100 +#define FLASH_RESER 0x00000080 +#define FLASH_SEQER 0x00000040 +#define FLASH_10ER 0x00000008 +#define FLASH_PGER 0x00000004 +#define FLASH_ERER 0x00000002 +#define FLASH_ERR 0x00000001 + +typedef struct str7x_mem_layout_s { + u32 sector_start; + u32 sector_size; + u32 reg_offset; +} str7x_mem_layout_t; + +#endif /* STR7X_H */ + diff --git a/src/helper/Makefile.am b/src/helper/Makefile.am new file mode 100644 index 00000000..5fb2241d --- /dev/null +++ b/src/helper/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = $(all_includes) +METASOURCES = AUTO +noinst_LIBRARIES = libhelper.a +libhelper_a_SOURCES = binarybuffer.c configuration.c log.c interpreter.c command.c time_support.c +noinst_HEADERS = binarybuffer.h configuration.h types.h log.h command.h \ + interpreter.h time_support.h diff --git a/src/helper/binarybuffer.c b/src/helper/binarybuffer.c new file mode 100644 index 00000000..357d05c3 --- /dev/null +++ b/src/helper/binarybuffer.c @@ -0,0 +1,246 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include <stdlib.h> +#include <string.h> + +#include "types.h" +#include "log.h" + +#include "binarybuffer.h" + +int buf_set_u32(u8* buffer, unsigned int first, unsigned int num, u32 value); +u32 buf_get_u32(u8* buffer, unsigned int first, unsigned int num); +u32 flip_u32(u32 value, unsigned int num); + +const unsigned char bit_reverse_table256[] = +{ + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + +int buf_set_u32(u8* buffer, unsigned int first, unsigned int num, u32 value) +{ + unsigned int i; + + if (!buffer) + return ERROR_INVALID_ARGUMENTS; + + for (i=first; i<first+num; i++) + { + if (((value >> (i-first))&1) == 1) + buffer[i/8] |= 1 << (i%8); + else + buffer[i/8] &= ~(1 << (i%8)); + } + + return ERROR_OK; +} + +u32 buf_get_u32(u8* buffer, unsigned int first, unsigned int num) +{ + u32 result = 0; + unsigned int i; + + if (!buffer) + { + ERROR("buffer not initialized"); + return 0; + } + + for (i=first; i<first+num; i++) + { + if (((buffer[i/8]>>(i%8))&1) == 1) + result |= 1 << (i-first); + } + + return result; +} + +u8* buf_cpy(u8 *from, u8 *to, int size) +{ + int num_bytes = CEIL(size, 8); + unsigned int i; + + if (from == NULL) + return NULL; + + for (i = 0; i < num_bytes; i++) + to[i] = from[i]; + + return to; +} + +int buf_cmp(u8 *buf1, u8 *buf2, int size) +{ + int num_bytes = CEIL(size, 8); + int i; + + if (!buf1 || !buf2) + return 1; + + for (i = 0; i < num_bytes; i++) + { + if (buf1[i] != buf2[i]) + return 1; + } + + return 0; +} + +int buf_cmp_mask(u8 *buf1, u8 *buf2, u8 *mask, int size) +{ + int num_bytes = CEIL(size, 8); + int i; + + for (i = 0; i < num_bytes; i++) + { + if ((buf1[i] & mask[i]) != (buf2[i] & mask[i])) + return 1; + } + + return 0; +} + +u8* buf_set_ones(u8 *buf, int count) +{ + int num_bytes = CEIL(count, 8); + int i; + + for (i = 0; i < num_bytes; i++) + { + if (count >= 8) + buf[i] = 0xff; + else + buf[i] = (1 << count) - 1; + + count -= 8; + } + + return buf; +} + +u8* buf_set_buf(u8 *src, int src_start, u8 *dst, int dst_start, int len) +{ + int src_idx = src_start, dst_idx = dst_start; + int i; + + for (i = 0; i < len; i++) + { + if (((src[src_idx/8] >> (src_idx % 8)) & 1) == 1) + dst[dst_idx/8] |= 1 << (dst_idx%8); + else + dst[dst_idx/8] &= ~(1 << (dst_idx%8)); + dst_idx++; + src_idx++; + } + + return dst; +} + +u32 flip_u32(u32 value, unsigned int num) +{ + u32 c; + + c = (bit_reverse_table256[value & 0xff] << 24) | + (bit_reverse_table256[(value >> 8) & 0xff] << 16) | + (bit_reverse_table256[(value >> 16) & 0xff] << 8) | + (bit_reverse_table256[(value >> 24) & 0xff]); + + if (num < 32) + c = c >> (32 - num); + + return c; +} + +char* buf_to_char(u8 *buf, int size) +{ + int char_len = CEIL(size, 8) * 2; + char *char_buf = malloc(char_len + 1); + int i; + int bits_left = size; + + char_buf[char_len] = 0; + + for (i = 0; i < CEIL(size, 8); i++) + { + if (bits_left < 8) + { + buf[i] &= ((1 << bits_left) - 1); + } + + if (((buf[i] & 0x0f) >= 0) && ((buf[i] & 0x0f) <= 9)) + char_buf[char_len - 2*i - 1] = '0' + (buf[i] & 0xf); + else + char_buf[char_len - 2*i - 1] = 'a' + (buf[i] & 0xf) - 10; + + if (((buf[i] & 0xf0) >> 4 >= 0) && ((buf[i] & 0xf0) >> 4 <= 9)) + char_buf[char_len - 2*i - 2] = '0' + ((buf[i] & 0xf0) >> 4); + else + char_buf[char_len - 2*i - 2] = 'a' + ((buf[i] & 0xf0) >> 4) - 10; + + } + + return char_buf; +} + +int char_to_buf(char *buf, int len, u8 *bin_buf, int buf_size) +{ + int bin_len = CEIL(len, 2); + int i; + + if (buf_size < CEIL(bin_len, 8)) + return 0; + + if (len % 2) + return 0; + + for (i = 0; i < strlen(buf); i++) + { + u32 tmp; + sscanf(buf + 2*i, "%2x", &tmp); + bin_buf[i] = tmp & 0xff; + } + + return bin_len * 8; +} + +int buf_to_u32_handler(u8 *in_buf, void *priv) +{ + u32 *dest = priv; + + *dest = buf_get_u32(in_buf, 0, 32); + + return ERROR_OK; +} diff --git a/src/helper/binarybuffer.h b/src/helper/binarybuffer.h new file mode 100644 index 00000000..0a688945 --- /dev/null +++ b/src/helper/binarybuffer.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef BINARYBUFFER_H +#define BINARYBUFFER_H + +#include "types.h" + +/* support functions to access arbitrary bits in a byte array + * flip_u32 inverses the bit order inside a 32-bit word (31..0 -> 0..31) + */ + +extern int buf_set_u32(u8* buffer, unsigned int first, unsigned int num, u32 value); +extern u32 buf_get_u32(u8* buffer, unsigned int first, unsigned int num); + +extern u32 flip_u32(u32 value, unsigned int num); + +extern int buf_cmp(u8 *buf1, u8 *buf2, int size); +extern int buf_cmp_mask(u8 *buf1, u8 *buf2, u8 *mask, int size); +extern u8* buf_cpy(u8 *from, u8 *to, int size); + +extern u8* buf_set_ones(u8 *buf, int count); +extern u8* buf_set_buf(u8 *src, int src_start, u8 *dst, int dst_start, int len); + +extern char* buf_to_char(u8 *buf, int size); +extern int char_to_buf(char *buf, int len, u8 *bin_buf, int buf_size); + +extern int buf_to_u32_handler(u8 *in_buf, void *priv); + +#define CEIL(m, n) ((m + n - 1) / n) + +#endif /* BINARYBUFFER_H */ diff --git a/src/helper/command.c b/src/helper/command.c new file mode 100644 index 00000000..26eada62 --- /dev/null +++ b/src/helper/command.c @@ -0,0 +1,508 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * part of this file is taken from libcli (libcli.sourceforge.net) * + * Copyright (C) David Parrish (david@dparrish.com) * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "command.h" + +#include "log.h" + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <unistd.h> + +int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +int build_unique_lenghts(command_context_t *context, command_t *commands) +{ + command_t *c, *p; + + /* iterate through all commands */ + for (c = commands; c; c = c->next) + { + /* find out how many characters are required to uniquely identify a command */ + for (c->unique_len = 1; c->unique_len <= strlen(c->name); c->unique_len++) + { + int foundmatch = 0; + + /* for every command, see if the current length is enough */ + for (p = commands; p; p = p->next) + { + /* ignore the command itself */ + if (c == p) + continue; + + /* compare commands up to the current length */ + if (strncmp(p->name, c->name, c->unique_len) == 0) + foundmatch++; + } + + /* when none of the commands matched, we've found the minimum length required */ + if (!foundmatch) + break; + } + + /* if the current command has children, build the unique lengths for them */ + if (c->children) + build_unique_lenghts(context, c->children); + } + + return ERROR_OK; +} + +command_t* register_command(command_context_t *context, command_t *parent, char *name, int (*handler)(struct command_context_s *context, char* name, char** args, int argc), enum command_mode mode, char *help) +{ + command_t *c, *p; + + if (!context || !name) + return NULL; + + c = malloc(sizeof(command_t)); + + c->name = strdup(name); + c->parent = parent; + c->children = NULL; + c->handler = handler; + c->mode = mode; + if (help) + c->help = strdup(help); + else + c->help = NULL; + c->unique_len = 0; + c->next = NULL; + + /* place command in tree */ + if (parent) + { + if (parent->children) + { + /* find last child */ + for (p = parent->children; p && p->next; p = p->next); + if (p) + p->next = c; + } + else + { + parent->children = c; + } + } + else + { + if (context->commands) + { + /* find last command */ + for (p = context->commands; p && p->next; p = p->next); + if (p) + p->next = c; + } + else + { + context->commands = c; + } + } + + /* update unique lengths */ + build_unique_lenghts(context, (parent) ? parent : context->commands); + + return c; +} + +int unregister_command(command_context_t *context, char *name) +{ + command_t *c, *p = NULL, *c2; + + if ((!context) || (!name)) + return ERROR_INVALID_ARGUMENTS; + + /* find command */ + for (c = context->commands; c; c = c->next) + { + if (strcmp(name, c->name) == 0) + { + /* unlink command */ + if (p) + { + p->next = c->next; + } + else + { + context->commands = c->next; + } + + /* unregister children */ + if (c->children) + { + for (c2 = c->children; c2; c2 = c2->next) + { + free(c2->name); + if (c2->help) + free(c2->help); + free(c2); + } + } + + /* delete command */ + free(c->name); + if (c->help) + free(c->help); + free(c); + } + + /* remember the last command for unlinking */ + p = c; + } + + return ERROR_OK; +} + +int parse_line(char *line, char *words[], int max_words) +{ + int nwords = 0; + char *p = line; + char *word_start = line; + int inquote = 0; + + while (nwords < max_words - 1) + { + /* check if we reached + * a terminating NUL + * a matching closing quote character " or ' + * we're inside a word but not a quote, and the current character is whitespace + */ + if (!*p || *p == inquote || (word_start && !inquote && isspace(*p))) + { + /* we're inside a word or quote, and reached its end*/ + if (word_start) + { + int len = p - word_start; + + /* copy the word */ + memcpy(words[nwords] = malloc(len + 1), word_start, len); + /* add terminating NUL */ + words[nwords++][len] = 0; + } + + /* we're done parsing the line */ + if (!*p) + break; + + /* skip over trailing quote or whitespace*/ + if (inquote || isspace(*p)) + p++; + + inquote = 0; + word_start = 0; + } + else if (*p == '"' || *p == '\'') + { + /* we've reached the beginning of a quote */ + inquote = *p++; + word_start = p; + } + else + { + /* we've reached the beginning of a new word */ + if (!word_start) + word_start = p; + + /* normal character, skip */ + p++; + } + } + + return nwords; +} + +void command_print(command_context_t *context, char *format, ...) +{ + va_list ap; + char *buffer = NULL; + int n, size = 0; + char *p; + + va_start(ap, format); + + /* process format string */ + /* TODO: possible bug. va_list is undefined after the first call to vsnprintf */ + while (!buffer || (n = vsnprintf(buffer, size, format, ap)) >= size) + { + /* increase buffer until it fits the whole string */ + if (!(p = realloc(buffer, size += 4096))) + return; + + buffer = p; + } + + /* vsnprintf failed */ + if (n < 0) + return; + + p = buffer; + + /* process lines in buffer */ + do { + char *next = strchr(p, '\n'); + + if (next) + *next++ = 0; + + if (context->output_handler) + context->output_handler(context, p); + + p = next; + } while (p); + + if (buffer) + free(buffer); + + va_end(ap); +} + +int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word) +{ + command_t *c; + + for (c = commands; c; c = c->next) + { + if (strncasecmp(c->name, words[start_word], c->unique_len)) + continue; + + if (strncasecmp(c->name, words[start_word], strlen(words[start_word]))) + continue; + + if ((c->mode == context->mode) || (c->mode == COMMAND_ANY)) + { + if (!c->children) + { + if (!c->handler) + { + command_print(context, "No handler for command"); + break; + } + else + { + return c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1); + } + } + else + { + if (start_word == num_words - 1) + { + command_print(context, "Incomplete command"); + break; + } + return find_and_run_command(context, c->children, words, num_words, start_word + 1); + } + } + } + + command_print(context, "Command %s not found", words[start_word]); + return ERROR_OK; +} + +int command_run_line(command_context_t *context, char *line) +{ + int nwords; + char *words[128] = {0}; + int retval; + int i; + + if ((!context) || (!line)) + return ERROR_INVALID_ARGUMENTS; + + /* skip preceding whitespace */ + while (isspace(*line)) + line++; + + /* empty line, ignore */ + if (!*line) + return ERROR_OK; + + if (context->echo) + { + command_print(context, "%s", line); + } + + nwords = parse_line(line, words, sizeof(words) / sizeof(words[0])); + + if (nwords > 0) + retval = find_and_run_command(context, context->commands, words, nwords, 0); + else + return ERROR_INVALID_ARGUMENTS; + + for (i = 0; i < nwords; i++) + free(words[i]); + + return retval; +} + +int command_run_file(command_context_t *context, FILE *file, enum command_mode mode) +{ + int retval; + int old_command_mode; + char buffer[4096]; + + old_command_mode = context->mode; + context->mode = mode; + + while (fgets(buffer, 4096, file)) + { + char *p; + char *cmd, *end; + + /* stop processing line after a comment (#, !) or a LF, CR were encountered */ + if ((p = strpbrk(buffer, "#!\r\n"))) + *p = 0; + + /* skip over leading whitespace */ + cmd = buffer; + while (isspace(*cmd)) + cmd++; + + /* empty (all whitespace) line? */ + if (!*cmd) + continue; + + /* search the end of the current line, ignore trailing whitespace */ + for (p = end = cmd; *p; p++) + if (!isspace(*p)) + end = p; + + /* terminate end */ + *++end = 0; + if (strcasecmp(cmd, "quit") == 0) + break; + + /* run line */ + if (command_run_line(context, cmd) == ERROR_COMMAND_CLOSE_CONNECTION) + break; + } + + context->mode = old_command_mode; + + return retval; +} + +void command_print_help_line(command_context_t* context, struct command_s *command, int indent) +{ + command_t *c; + char indents[32] = {0}; + char *help = "no help available"; + char name_buf[64]; + int i; + + for (i = 0; i < indent; i+=2) + { + indents[i*2] = ' '; + indents[i*2+1] = '-'; + } + indents[i*2] = 0; + + if ((command->mode == COMMAND_EXEC) || (command->mode == COMMAND_ANY)) + { + if (command->help) + help = command->help; + + snprintf(name_buf, 64, command->name); + strncat(name_buf, indents, 64); + command_print(context, "%20s\t%s", name_buf, help); + } + + if (command->children) + { + for (c = command->children; c; c = c->next) + { + command_print_help_line(context, c, indent + 1); + } + } +} + +int command_print_help(command_context_t* context, char* name, char** args, int argc) +{ + command_t *c; + + for (c = context->commands; c; c = c->next) + { + command_print_help_line(context, c, 0); + } + + return ERROR_OK; +} + +void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv) +{ + context->output_handler = output_handler; + context->output_handler_priv = priv; +} + +command_context_t* copy_command_context(command_context_t* context) +{ + command_context_t* copy_context = malloc(sizeof(command_context_t)); + + *copy_context = *context; + + return copy_context; +} + +int command_done(command_context_t *context) +{ + free(context); + + return ERROR_OK; +} + +command_context_t* command_init() +{ + command_context_t* context = malloc(sizeof(command_context_t)); + + context->mode = COMMAND_EXEC; + context->commands = NULL; + context->current_target = 0; + context->echo = 0; + context->output_handler = NULL; + context->output_handler_priv = NULL; + + register_command(context, NULL, "help", command_print_help, + COMMAND_EXEC, "display this help"); + + register_command(context, NULL, "sleep", handle_sleep_command, + COMMAND_ANY, "sleep for <n> milliseconds"); + + return context; +} + +/* sleep command sleeps for <n> miliseconds + * this is useful in target startup scripts + */ +int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + unsigned long duration = 0; + + if (argc == 1) + { + duration = strtoul(args[0], NULL, 0); + usleep(duration * 1000); + } + + return ERROR_OK; +} diff --git a/src/helper/command.h b/src/helper/command.h new file mode 100644 index 00000000..262786a8 --- /dev/null +++ b/src/helper/command.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef COMMAND_H +#define COMMAND_H + +#include <stdio.h> + +enum command_mode +{ + COMMAND_EXEC, + COMMAND_CONFIG, + COMMAND_ANY, +}; + +typedef struct command_context_s +{ + enum command_mode mode; + struct command_s *commands; + int current_target; + int echo; + int (*output_handler)(struct command_context_s *context, char* line); + void *output_handler_priv; +} command_context_t; + +typedef struct command_s +{ + char *name; + struct command_s *parent; + struct command_s *children; + int (*handler)(struct command_context_s *context, char* name, char** args, int argc); + enum command_mode mode; + char *help; + int unique_len; + struct command_s *next; +} command_t; + +extern command_t* register_command(command_context_t *context, command_t *parent, char *name, int (*handler)(struct command_context_s *context, char* name, char** args, int argc), enum command_mode mode, char *help); +extern int unregister_command(command_context_t *context, char *name); +extern void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv); +extern command_context_t* copy_command_context(command_context_t* context); +extern command_context_t* command_init(); +extern int command_done(command_context_t *context); +extern void command_print(command_context_t *context, char *format, ...); +extern int command_run_line(command_context_t *context, char *line); +extern int command_run_file(command_context_t *context, FILE *file, enum command_mode mode); + + +#define ERROR_COMMAND_CLOSE_CONNECTION (-600) + +#endif /* COMMAND_H */ diff --git a/src/helper/configuration.c b/src/helper/configuration.c new file mode 100644 index 00000000..e59ca6d1 --- /dev/null +++ b/src/helper/configuration.c @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "types.h" +#include "command.h" +#include "configuration.h" +#include "log.h" + +#include <stdio.h> +#include <stdlib.h> +#include <getopt.h> + +char* config_file_name; + +static int help_flag; + +static struct option long_options[] = +{ + {"help", no_argument, &help_flag, 1}, + + {"debug", optional_argument, 0, 'd'}, + {"file", required_argument, 0, 'f'}, + {"log_output", required_argument, 0, 'l'}, + + {0, 0, 0, 0} +}; + +int configuration_output_handler(struct command_context_s *context, char* line) +{ + INFO(line); + + return ERROR_OK; +} + +int parse_cmdline_args(struct command_context_s *cmd_ctx, int argc, char *argv[]) +{ + int c; + char command_buffer[128]; + + while (1) + { + /* getopt_long stores the option index here. */ + int option_index = 0; + + c = getopt_long(argc, argv, "hd::l:f:", long_options, &option_index); + + /* Detect the end of the options. */ + if (c == -1) + break; + + switch (c) + { + case 0: + break; + case 'h': /* --help | -h */ + help_flag = 1; + break; + case 'f': /* --file | -f */ + config_file_name = optarg; + break; + case 'd': /* --debug | -d */ + if (optarg) + snprintf(command_buffer, 128, "debug_level %s", optarg); + else + snprintf(command_buffer, 128, "debug_level 3"); + command_run_line(cmd_ctx, command_buffer); + break; + case 'l': /* --log_output | -l */ + if (optarg) + { + snprintf(command_buffer, 128, "log_output %s", optarg); + command_run_line(cmd_ctx, command_buffer); + } + break; + } + } + + if (help_flag) + { + printf("Open On-Chip Debugger\n(c) 2005 by Dominic Rath\n\n"); + printf("--help | -h\tdisplay this help\n"); + printf("--file | -f\tuse configuration file <name>\n"); + printf("--debug | -d\tset debug level <0-3>\n"); + printf("--log_output | -l\tredirect log output to file <name>\n"); + exit(-1); + } + + return ERROR_OK; +} + +int parse_config_file(struct command_context_s *cmd_ctx) +{ + FILE *config_file; + + if (!config_file_name) + config_file_name = "openocd.cfg"; + + config_file = fopen(config_file_name, "r"); + if (!config_file) + { + ERROR("couldn't open config file"); + return ERROR_NO_CONFIG_FILE; + } + + command_run_file(cmd_ctx, config_file, COMMAND_CONFIG); + + fclose(config_file); + + return ERROR_OK; +} + diff --git a/src/helper/configuration.h b/src/helper/configuration.h new file mode 100644 index 00000000..cd96e2f7 --- /dev/null +++ b/src/helper/configuration.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CONFIGURATION_H +#define CONFIGURATION_H + +#include "command.h" +#include "types.h" + +extern int parse_cmdline_args(struct command_context_s *cmd_ctx, int argc, char *argv[]); +extern int parse_config_file(struct command_context_s *cmd_ctx); +extern int configuration_output_handler(struct command_context_s *context, char* line); + +extern char* config_file_name; +#endif /* CONFIGURATION_H */ diff --git a/src/helper/interpreter.c b/src/helper/interpreter.c new file mode 100644 index 00000000..7e88263b --- /dev/null +++ b/src/helper/interpreter.c @@ -0,0 +1,237 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "interpreter.h" + +#include "binarybuffer.h" +#include <stdlib.h> +#include <string.h> + +var_t *variables = NULL; + +int handle_var_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_field_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_script_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +int interpreter_register_commands(struct command_context_s *cmd_ctx) +{ + register_command(cmd_ctx, NULL, "var", handle_var_command, + COMMAND_ANY, "allocate, display or delete variable <name> [num_fields|'del'] [size1] ..."); + register_command(cmd_ctx, NULL, "field", handle_field_command, + COMMAND_ANY, "display/modify variable field <var> <field> [value|'flip']"); + register_command(cmd_ctx, NULL, "script", handle_script_command, + COMMAND_ANY, "execute commands from <file>"); + + return ERROR_OK; +} + +var_t* get_var_by_num(int num) +{ + int count = 0; + var_t *var = variables; + + if (var) + { + if (num == count) + return var; + while (var->next) + { + var = var->next; + count++; + if (num == count) + return var; + } + } + return NULL; +} + +var_t* get_var_by_name(char *name) +{ + var_t *var = variables; + + if (var) + { + if (strcmp(var->name, name) == 0) + return var; + while (var->next) + { + var = var->next; + if (strcmp(var->name, name) == 0) + return var; + } + } + return NULL; +} + +var_t* get_var_by_namenum(char *namenum) +{ + if ((namenum[0] >= '0') && (namenum[0] <= '9')) + return get_var_by_num(strtol(namenum, NULL, 0)); + else + return get_var_by_name(namenum); + +} + +int field_le_to_host(u8 *buffer, void *priv) +{ + var_field_t *field = priv; + field->value = buf_get_u32(buffer, 0, field->num_bits); + + return ERROR_OK; +} + +int handle_var_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + var_t **last_var_p = &variables; + int i; + + if (argc >= 2) + { + while (*last_var_p) + { + if (strcmp((*last_var_p)->name, args[0]) == 0) + { + if (strcmp(args[1], "del") == 0) + { + var_t *next = (*last_var_p)->next; + free ((*last_var_p)->fields); + free (*last_var_p); + *last_var_p = next; + command_print(cmd_ctx, "variable %s deleted", args[0]); + } + else + command_print(cmd_ctx, "variable of that name already exists"); + return ERROR_OK; + } + last_var_p = &((*last_var_p)->next); + } + + if ((args[0][0] >= 0) && (args[0][0] <= 9)) + { + command_print(cmd_ctx, "invalid name specified (first character may not be a number)"); + return ERROR_OK; + } + + *last_var_p = malloc(sizeof(var_t)); + (*last_var_p)->name = strdup(args[0]); + (*last_var_p)->num_fields = argc - 1; + (*last_var_p)->next = NULL; + + (*last_var_p)->fields = malloc(sizeof(var_field_t) * (*last_var_p)->num_fields); + for (i = 0; i < (*last_var_p)->num_fields; i++) + { + (*last_var_p)->fields[i].num_bits = strtol(args[1+i], NULL, 0); + (*last_var_p)->fields[i].value = 0x0; + } + return ERROR_OK; + } + + if (argc == 1) + { + var_t *var = get_var_by_namenum(args[0]); + if (var) + { + int i; + command_print(cmd_ctx, "%s (%i fields):", var->name, var->num_fields); + for (i = 0; i < (var->num_fields); i++) + { + command_print(cmd_ctx, "0x%x (/%i)", var->fields[i].value, var->fields[i].num_bits); + } + } + else + { + command_print(cmd_ctx, "variable %s doesn't exist", args[0]); + } + } + + if (argc == 0) + { + var_t *var = variables; + int count = 0; + while (var) + { + command_print(cmd_ctx, "%i: %s (%i fields)", count, var->name, var->num_fields); + var = var->next; + count++; + } + } + + return ERROR_OK; +} + +int handle_field_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + + if (argc < 2) + command_print(cmd_ctx, "usage: field <var> <field> [value|'flip']"); + + if (argc >= 2) + { + var_t *var = get_var_by_namenum(args[0]); + int field_num = strtol(args[1], NULL, 0); + if (!var) + { + command_print(cmd_ctx, "variable %s doesn't exist", args[0]); + return ERROR_OK; + } + if (field_num >= var->num_fields) + command_print(cmd_ctx, "variable field %i is out of bounds (max. %i)", field_num, var->num_fields - 1); + if ((var) && (field_num < var->num_fields)) + { + if (argc > 2) + { + if (strcmp(args[2], "flip") == 0) + var->fields[field_num].value = flip_u32(var->fields[field_num].value, var->fields[field_num].num_bits); + else + var->fields[field_num].value = strtoul(args[2], NULL, 0); + } + + command_print(cmd_ctx, "%s(%i): 0x%x (/%i)", var->name, field_num, var->fields[field_num].value, var->fields[field_num].num_bits); + } + } + + return ERROR_OK; +} + +int handle_script_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + FILE *script_file; + int echo; + + if (argc != 1) + command_print(cmd_ctx, "usage: script <file>"); + + script_file = fopen(args[0], "r"); + if (!script_file) + { + command_print(cmd_ctx, "couldn't open script file %s", args[0]); + return ERROR_OK; + } + + echo = cmd_ctx->echo; + cmd_ctx->echo = 1; + + command_run_file(cmd_ctx, script_file, COMMAND_EXEC); + + cmd_ctx->echo = echo; + + fclose(script_file); + + return ERROR_OK; +} diff --git a/src/helper/interpreter.h b/src/helper/interpreter.h new file mode 100644 index 00000000..93e8d39d --- /dev/null +++ b/src/helper/interpreter.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef INTERPRETER_H +#define INTERPRETER_H + +#include "types.h" +#include "command.h" +#include "log.h" + +typedef struct var_field_s +{ + int num_bits; + u32 value; +} var_field_t; + +typedef struct var_s +{ + char *name; + int num_fields; + var_field_t *fields; + struct var_s *next; +} var_t; + +extern var_t *variables; + +extern int field_le_to_host(u8 *buffer, void *priv); + +extern var_t* get_var_by_namenum(char *namenum); +extern int interpreter_register_commands(struct command_context_s *cmd_ctx); + +#endif /* INTERPRETER_H */ diff --git a/src/helper/log.c b/src/helper/log.c new file mode 100644 index 00000000..60ba80bd --- /dev/null +++ b/src/helper/log.c @@ -0,0 +1,134 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "log.h" +#include "configuration.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +int debug_level = -1; + +static FILE* log_output; + +static char *log_strings[4] = +{ + "Error: ", + "Warning:", + "Info: ", + "Debug: ", +}; + +void log_printf(enum log_levels level, const char *file, int line, const char *function, const char *format, ...) +{ + va_list args; + char buffer[512]; + + if (level > debug_level) + return; + + va_start(args, format); + vsnprintf(buffer, 512, format, args); + + fprintf(log_output, "%s %s:%d %s(): %s\n", log_strings[level], file, line, function, buffer); + fflush(log_output); + + va_end(args); +} + +void short_log_printf(enum log_levels level, const char *format, ...) +{ + va_list args; + char buffer[512]; + + if (level > debug_level) + return; + + va_start(args, format); + vsnprintf(buffer, 512, format, args); + + fprintf(log_output, "%s %s\n", log_strings[level], buffer); + fflush(log_output); + + va_end(args); +} + +/* change the current debug level on the fly + * 0: only ERRORS + * 1: + WARNINGS + * 2: + INFORMATIONAL MSGS + * 3: + DEBUG MSGS + */ +int handle_debug_level_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 0) + command_print(cmd_ctx, "debug_level: %i", debug_level); + + if (argc > 0) + debug_level = strtoul(args[0], NULL, 0); + + if (debug_level < 0) + debug_level = 0; + + if (debug_level > 3) + debug_level = 3; + + return ERROR_OK; +} + +int handle_log_output_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 1) + { + FILE* file = fopen(args[0], "w"); + + if (file) + { + log_output = file; + } + } + + return ERROR_OK; +} + +int log_register_commands(struct command_context_s *cmd_ctx) +{ + register_command(cmd_ctx, NULL, "log_output", handle_log_output_command, + COMMAND_ANY, "redirect logging to <file> (default: stderr)"); + register_command(cmd_ctx, NULL, "debug_level", handle_debug_level_command, + COMMAND_ANY, "adjust debug level <0-3>"); + + return ERROR_OK; +} + +int log_init(struct command_context_s *cmd_ctx) +{ + /* set defaults for daemon configuration, if not set by cmdline or cfgfile */ + if (debug_level == -1) + debug_level = LOG_INFO; + + if (log_output == NULL) + { + log_output = stderr; + } + + return ERROR_OK; +} diff --git a/src/helper/log.h b/src/helper/log.h new file mode 100644 index 00000000..c495524c --- /dev/null +++ b/src/helper/log.h @@ -0,0 +1,96 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ERROR_H +#define ERROR_H + +#include "command.h" + +#include <stdarg.h> + +/* logging priorities + * LOG_ERROR - fatal errors, that are likely to cause program abort + * LOG_WARNING - non-fatal errors, that may be resolved later + * LOG_INFO - state information, etc. + * LOG_DEBUG - debug statements, execution trace + */ +enum log_levels +{ + LOG_ERROR = 0, + LOG_WARNING = 1, + LOG_INFO = 2, + LOG_DEBUG = 3 +}; + +extern void log_printf(enum log_levels level, const char *file, int line, + const char *function, const char *format, ...); +extern int log_register_commands(struct command_context_s *cmd_ctx); +extern int log_init(struct command_context_s *cmd_ctx); + +extern int debug_level; + +#define DEBUG(expr ...) \ + do { \ + log_printf (LOG_DEBUG, __FILE__, __LINE__, __FUNCTION__, expr); \ + } while(0) + +#define INFO(expr ...) \ + do { \ + log_printf (LOG_INFO, __FILE__, __LINE__, __FUNCTION__, expr); \ + } while(0) + +#define WARNING(expr ...) \ + do { \ + log_printf (LOG_WARNING, __FILE__, __LINE__, __FUNCTION__, expr); \ + } while(0) + +#define ERROR(expr ...) \ + do { \ + log_printf (LOG_ERROR, __FILE__, __LINE__, __FUNCTION__, expr); \ + } while(0) + +#define SDEBUG(expr ...) \ + do { \ + short_log_printf (LOG_DEBUG, expr); \ + } while(0) + +#define SINFO(expr ...) \ + do { \ + short_log_printf (LOG_INFO, expr); \ + } while(0) + +#define SWARNING(expr ...) \ + do { \ + short_log_printf (LOG_WARNING, expr); \ + } while(0) + +#define SERROR(expr ...) \ + do { \ + short_log_printf (LOG_ERROR, expr); \ + } while(0) + +/* general failures + * error codes < 100 + */ +#define ERROR_OK (0) +#define ERROR_INVALID_ARGUMENTS (-1) +#define ERROR_NO_CONFIG_FILE (-2) +#define ERROR_BUF_TOO_SMALL (-3) + +#endif /* ERROR_H */ diff --git a/src/helper/time_support.c b/src/helper/time_support.c new file mode 100644 index 00000000..5a7869d9 --- /dev/null +++ b/src/helper/time_support.c @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (C) 2006 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" + +#include "time_support.h" + +#include <sys/time.h> +#include <time.h> + +int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y); +int timeval_add(struct timeval *result, struct timeval *x, struct timeval *y); +int timeval_add_time(struct timeval *result, int sec, int usec); + +/* calculate difference between two struct timeval values */ +int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) +{ + if (x->tv_usec < y->tv_usec) + { + int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; + y->tv_usec -= 1000000 * nsec; + y->tv_sec += nsec; + } + if (x->tv_usec - y->tv_usec > 1000000) { + int nsec = (x->tv_usec - y->tv_usec) / 1000000; + y->tv_usec += 1000000 * nsec; + y->tv_sec -= nsec; + } + + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_usec = x->tv_usec - y->tv_usec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + +/* add two struct timeval values */ +int timeval_add(struct timeval *result, struct timeval *x, struct timeval *y) +{ + result->tv_sec = x->tv_sec + y->tv_sec; + + result->tv_usec = x->tv_usec + y->tv_usec; + + while (result->tv_usec > 1000000) + { + result->tv_usec -= 1000000; + result->tv_sec++; + } + + return 0; +} + +int timeval_add_time(struct timeval *result, int sec, int usec) +{ + result->tv_sec += sec; + result->tv_usec += usec; + + while (result->tv_usec > 1000000) + { + result->tv_usec -= 1000000; + result->tv_sec++; + } + + return 0; +} + diff --git a/src/helper/time_support.h b/src/helper/time_support.h new file mode 100644 index 00000000..d8b7fe5c --- /dev/null +++ b/src/helper/time_support.h @@ -0,0 +1,30 @@ +/*************************************************************************** + * Copyright (C) 2006 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef TIME_SUPPORT_H +#define TIME_SUPPORT_H + +#include <sys/time.h> +#include <time.h> + +extern int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y); +extern int timeval_add(struct timeval *result, struct timeval *x, struct timeval *y); +extern int timeval_add_time(struct timeval *result, int sec, int usec); + +#endif /* TIME_SUPPORT_H */ diff --git a/src/helper/types.h b/src/helper/types.h new file mode 100644 index 00000000..6d49bbb0 --- /dev/null +++ b/src/helper/types.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef TYPES_H +#define TYPES_H + +#ifndef u8 +typedef unsigned char u8; +#endif + +#ifndef u16 +typedef unsigned short u16; +#endif + +#ifndef u32 +typedef unsigned int u32; +#endif + +#endif /* TYPES_H */ diff --git a/src/jtag/Makefile.am b/src/jtag/Makefile.am new file mode 100644 index 00000000..a3a06606 --- /dev/null +++ b/src/jtag/Makefile.am @@ -0,0 +1,50 @@ + +if FTD2XXDIR +FTD2XXINC = -I@WITH_FTD2XX@/ +else +FTD2XXINC = +endif + +INCLUDES = -I$(top_srcdir)/src/helper $(FTD2XXINC) $(all_includes) +METASOURCES = AUTO +noinst_LIBRARIES = libjtag.a + +if BITBANG +BITBANGFILES = bitbang.c +else +BITBANGFILES = +endif + +if PARPORT +PARPORTFILES = parport.c +else +PARPORTFILES = +endif + +if FTDI2232 +FTDI2232FILES = ftdi2232.c +else +FTDI2232FILES = +endif + +if FTD2XX +FTD2XXFILES = ftd2xx.c +else +FTD2XXFILES = +endif + +if AMTJTAGACCEL +AMTJTAGACCELFILES = amt_jtagaccel.c +else +AMTJTAGACCELFILES = +endif + +if EP93XX +EP93XXFILES = ep93xx.c +else +EP93XXFILES = +endif + +libjtag_a_SOURCES = jtag.c $(BITBANGFILES) $(PARPORTFILES) $(FTDI2232FILES) $(FTD2XXFILES) $(AMTJTAGACCELFILES) $(EP93XXFILES) + +noinst_HEADERS = bitbang.h jtag.h diff --git a/src/jtag/amt_jtagaccel.c b/src/jtag/amt_jtagaccel.c new file mode 100644 index 00000000..42f8bc36 --- /dev/null +++ b/src/jtag/amt_jtagaccel.c @@ -0,0 +1,510 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" +#include "log.h" +#include "jtag.h" + +/* system includes */ +#include <sys/io.h> +#include <string.h> +#include <stdlib.h> + +#include <sys/time.h> +#include <time.h> + +#if PARPORT_USE_PPDEV == 1 +#include <linux/parport.h> +#include <linux/ppdev.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <unistd.h> +#endif + +/* configuration */ +unsigned long amt_jtagaccel_port; + +/* interface variables + */ +static u8 aw_control_rst = 0x00; +static u8 aw_control_fsm = 0x10; +static u8 aw_control_baudrate = 0x20; + +static int rtck_enabled = 0; + +#if PARPORT_USE_PPDEV == 1 +static int device_handle; +int addr_mode = IEEE1284_MODE_EPP | IEEE1284_ADDR ; +int data_mode = IEEE1284_MODE_EPP | IEEE1284_DATA ; +#define AMT_AW(val) do { ioctl(device_handle, PPSETMODE, &addr_mode); write(device_handle, &val, 1); } while (0) +#define AMT_AR(val) do { ioctl(device_handle, PPSETMODE, &addr_mode); read(device_handle, &val, 1); } while (0) +#define AMT_DW(val) do { ioctl(device_handle, PPSETMODE, &data_mode); write(device_handle, &val, 1); } while (0) +#define AMT_DR(val) do { ioctl(device_handle, PPSETMODE, &data_mode); read(device_handle, &val, 1); } while (0) +#else +#define AMT_AW(val) do { outb(val, amt_jtagaccel_port + 3); } while (0) +#define AMT_AR(val) do { val = inb(amt_jtagaccel_port + 3); } while (0) +#define AMT_DW(val) do { outb(val, amt_jtagaccel_port + 4); } while (0) +#define AMT_DR(val) do { val = inb(amt_jtagaccel_port + 4); } while (0) +#endif + +int amt_jtagaccel_execute_queue(void); +int amt_jtagaccel_register_commands(struct command_context_s *cmd_ctx); +int amt_jtagaccel_speed(int speed); +int amt_jtagaccel_init(void); +int amt_jtagaccel_quit(void); + +int amt_jtagaccel_handle_parport_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int amt_jtagaccel_handle_rtck_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +/* tap_move[i][j]: tap movement command to go from state i to state j + * 0: Test-Logic-Reset + * 1: Run-Test/Idle + * 2: Shift-DR + * 3: Pause-DR + * 4: Shift-IR + * 5: Pause-IR + */ +u8 amt_jtagaccel_tap_move[6][6][2] = +{ + /* TLR RTI SD PD SI PI */ + {{0x1f, 0x00}, {0x0f, 0x00}, {0x8a, 0x04}, {0x0a, 0x00}, {0x06, 0x00}, {0x96, 0x00}}, /* TLR */ + {{0x1f, 0x00}, {0x00, 0x00}, {0x85, 0x08}, {0x05, 0x00}, {0x8b, 0x08}, {0x0b, 0x00}}, /* RTI */ + {{0x1f, 0x00}, {0x0d, 0x00}, {0x00, 0x00}, {0x01, 0x00}, {0x8f, 0x09}, {0x8f, 0x01}}, /* SD */ + {{0x1f, 0x00}, {0x0c, 0x00}, {0x08, 0x00}, {0x00, 0x00}, {0x8f, 0x09}, {0x8f, 0x01}}, /* PD */ + {{0x1f, 0x00}, {0x0d, 0x00}, {0x07, 0x00}, {0x97, 0x00}, {0x00, 0x00}, {0x01, 0x00}}, /* SI */ + {{0x1f, 0x00}, {0x0c, 0x00}, {0x07, 0x00}, {0x97, 0x00}, {0x08, 0x00}, {0x00, 0x00}}, /* PI */ +}; + +jtag_interface_t amt_jtagaccel_interface = +{ + .name = "amt_jtagaccel", + + .execute_queue = amt_jtagaccel_execute_queue, + + .support_statemove = 0, + + .speed = amt_jtagaccel_speed, + .register_commands = amt_jtagaccel_register_commands, + .init = amt_jtagaccel_init, + .quit = amt_jtagaccel_quit, +}; + +int amt_jtagaccel_register_commands(struct command_context_s *cmd_ctx) +{ + register_command(cmd_ctx, NULL, "parport_port", amt_jtagaccel_handle_parport_port_command, + COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "rtck", amt_jtagaccel_handle_rtck_command, + COMMAND_CONFIG, NULL); + + return ERROR_OK; +} + +void amt_jtagaccel_reset(int trst, int srst) +{ + if (trst == 1) + aw_control_rst |= 0x4; + else if (trst == 0) + aw_control_rst &= ~0x4; + + if (srst == 1) + aw_control_rst |= 0x1; + else if (srst == 0) + aw_control_rst &= ~0x1; + + AMT_AW(aw_control_rst); +} + +int amt_jtagaccel_speed(int speed) +{ + aw_control_baudrate &= 0xf0; + aw_control_baudrate |= speed & 0x0f; + AMT_AW(aw_control_baudrate); + + return ERROR_OK; +} + +void amt_jtagaccel_end_state(state) +{ + if (tap_move_map[state] != -1) + end_state = state; + else + { + ERROR("BUG: %i is not a valid end state", state); + exit(-1); + } +} + +void amt_wait_scan_busy() +{ + int timeout = 4096; + u8 ar_status; + + AMT_AR(ar_status); + while (((ar_status) & 0x80) && (timeout-- > 0)) + AMT_AR(ar_status); + + if (ar_status & 0x80) + { + ERROR("amt_jtagaccel timed out while waiting for end of scan, rtck was %s", (rtck_enabled) ? "enabled" : "disabled"); + exit(-1); + } +} + +void amt_jtagaccel_state_move(void) +{ + u8 aw_scan_tms_5; + u8 tms_scan[2]; + + tms_scan[0] = amt_jtagaccel_tap_move[tap_move_map[cur_state]][tap_move_map[end_state]][0]; + tms_scan[1] = amt_jtagaccel_tap_move[tap_move_map[cur_state]][tap_move_map[end_state]][1]; + + aw_scan_tms_5 = 0x40 | (tms_scan[0] & 0x1f); + AMT_AW(aw_scan_tms_5); + if (jtag_speed > 3 || rtck_enabled) + amt_wait_scan_busy(); + + if (tms_scan[0] & 0x80) + { + aw_scan_tms_5 = 0x40 | (tms_scan[1] & 0x1f); + AMT_AW(aw_scan_tms_5); + if (jtag_speed > 3 || rtck_enabled) + amt_wait_scan_busy(); + } + + cur_state = end_state; +} + +void amt_jtagaccel_runtest(int num_cycles) +{ + int i = 0; + u8 aw_scan_tms_5; + u8 aw_scan_tms_1to4; + + enum tap_state saved_end_state = end_state; + + /* only do a state_move when we're not already in RTI */ + if (cur_state != TAP_RTI) + { + amt_jtagaccel_end_state(TAP_RTI); + amt_jtagaccel_state_move(); + } + + while (num_cycles - i >= 5) + { + aw_scan_tms_5 = 0x40; + AMT_AW(aw_scan_tms_5); + i += 5; + } + + if (num_cycles - i > 0) + { + aw_scan_tms_1to4 = 0x80 | ((num_cycles - i - 1) & 0x3) << 4; + AMT_AW(aw_scan_tms_1to4); + } + + amt_jtagaccel_end_state(saved_end_state); + if (cur_state != end_state) + amt_jtagaccel_state_move(); +} + +void amt_jtagaccel_scan(int ir_scan, enum scan_type type, u8 *buffer, int scan_size) +{ + int bits_left = scan_size; + int bit_count = 0; + enum tap_state saved_end_state = end_state; + u8 aw_tdi_option; + u8 dw_tdi_scan; + u8 dr_tdo; + u8 aw_tms_scan; + u8 tms_scan[2]; + + if (ir_scan) + amt_jtagaccel_end_state(TAP_SI); + else + amt_jtagaccel_end_state(TAP_SD); + + amt_jtagaccel_state_move(); + amt_jtagaccel_end_state(saved_end_state); + + /* handle unaligned bits at the beginning */ + if ((scan_size - 1) % 8) + { + aw_tdi_option = 0x30 | (((scan_size - 1) % 8) - 1); + AMT_AW(aw_tdi_option); + + dw_tdi_scan = buf_get_u32(buffer, bit_count, (scan_size - 1) % 8) & 0xff; + AMT_DW(dw_tdi_scan); + if (jtag_speed > 3 || rtck_enabled) + amt_wait_scan_busy(); + + if ((type == SCAN_IN) || (type == SCAN_IO)) + { + AMT_DR(dr_tdo); + dr_tdo = dr_tdo >> (8 - ((scan_size - 1) % 8)); + buf_set_u32(buffer, bit_count, (scan_size - 1) % 8, dr_tdo); + } + + bit_count += (scan_size - 1) % 8; + bits_left -= (scan_size - 1) % 8; + } + + while (bits_left - 1 >= 8) + { + dw_tdi_scan = buf_get_u32(buffer, bit_count, 8) & 0xff; + AMT_DW(dw_tdi_scan); + if (jtag_speed > 3 || rtck_enabled) + amt_wait_scan_busy(); + + if ((type == SCAN_IN) || (type == SCAN_IO)) + { + AMT_DR(dr_tdo); + buf_set_u32(buffer, bit_count, 8, dr_tdo); + } + + bit_count += 8; + bits_left -= 8; + } + + tms_scan[0] = amt_jtagaccel_tap_move[tap_move_map[cur_state]][tap_move_map[end_state]][0]; + tms_scan[1] = amt_jtagaccel_tap_move[tap_move_map[cur_state]][tap_move_map[end_state]][1]; + aw_tms_scan = 0x40 | (tms_scan[0] & 0x1f) | (buf_get_u32(buffer, bit_count, 1) << 5); + AMT_AW(aw_tms_scan); + if (jtag_speed > 3 || rtck_enabled) + amt_wait_scan_busy(); + + if ((type == SCAN_IN) || (type == SCAN_IO)) + { + AMT_DR(dr_tdo); + dr_tdo = dr_tdo >> 7; + buf_set_u32(buffer, bit_count, 1, dr_tdo); + } + + if (tms_scan[0] & 0x80) + { + aw_tms_scan = 0x40 | (tms_scan[1] & 0x1f); + AMT_AW(aw_tms_scan); + if (jtag_speed > 3 || rtck_enabled) + amt_wait_scan_busy(); + } + cur_state = end_state; +} + +int amt_jtagaccel_execute_queue(void) +{ + jtag_command_t *cmd = jtag_command_queue; /* currently processed command */ + int scan_size; + enum scan_type type; + u8 *buffer; + + while (cmd) + { + switch (cmd->type) + { + case JTAG_END_STATE: +#ifdef _DEBUG_JTAG_IO_ + DEBUG("end_state: %i", cmd->cmd.end_state->end_state); +#endif + if (cmd->cmd.end_state->end_state != -1) + amt_jtagaccel_end_state(cmd->cmd.end_state->end_state); + break; + case JTAG_RESET: +#ifdef _DEBUG_JTAG_IO_ + DEBUG("reset trst: %i srst %i", cmd->cmd.reset->trst, cmd->cmd.reset->srst); +#endif + if (cmd->cmd.reset->trst == 1) + { + cur_state = TAP_TLR; + } + amt_jtagaccel_reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst); + break; + case JTAG_RUNTEST: +#ifdef _DEBUG_JTAG_IO_ + DEBUG("runtest %i cycles, end in %i", cmd->cmd.runtest->num_cycles, cmd->cmd.runtest->end_state); +#endif + if (cmd->cmd.runtest->end_state != -1) + amt_jtagaccel_end_state(cmd->cmd.runtest->end_state); + amt_jtagaccel_runtest(cmd->cmd.runtest->num_cycles); + break; + case JTAG_STATEMOVE: +#ifdef _DEBUG_JTAG_IO_ + DEBUG("statemove end in %i", cmd->cmd.statemove->end_state); +#endif + if (cmd->cmd.statemove->end_state != -1) + amt_jtagaccel_end_state(cmd->cmd.statemove->end_state); + amt_jtagaccel_state_move(); + break; + case JTAG_SCAN: +#ifdef _DEBUG_JTAG_IO_ + DEBUG("scan end in %i", cmd->cmd.scan->end_state); +#endif + if (cmd->cmd.scan->end_state != -1) + amt_jtagaccel_end_state(cmd->cmd.scan->end_state); + scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer); + type = jtag_scan_type(cmd->cmd.scan); + amt_jtagaccel_scan(cmd->cmd.scan->ir_scan, type, buffer, scan_size); + if (jtag_read_buffer(buffer, cmd->cmd.scan) != ERROR_OK) + return ERROR_JTAG_QUEUE_FAILED; + if (buffer) + free(buffer); + break; + case JTAG_SLEEP: +#ifdef _DEBUG_JTAG_IO_ + DEBUG("sleep", cmd->cmd.sleep->us); +#endif + jtag_sleep(cmd->cmd.sleep->us); + break; + default: + ERROR("BUG: unknown JTAG command type encountered"); + exit(-1); + } + cmd = cmd->next; + } + + return ERROR_OK; +} + +int amt_jtagaccel_init(void) +{ +#if PARPORT_USE_PPDEV == 1 + char buffer[256]; + int i = 0; + u8 control_port; +#else + u8 status_port; +#endif + +#if PARPORT_USE_PPDEV == 1 + if (device_handle > 0) + { + ERROR("device is already opened"); + return ERROR_JTAG_INIT_FAILED; + } + + snprintf(buffer, 256, "/dev/parport%d", amt_jtagaccel_port); + device_handle = open(buffer, O_RDWR); + + if (device_handle < 0) + { + ERROR("cannot open device. check it exists and that user read and write rights are set"); + return ERROR_JTAG_INIT_FAILED; + } + + i = ioctl(device_handle, PPCLAIM); + if (i < 0) + { + ERROR("cannot claim device"); + return ERROR_JTAG_INIT_FAILED; + } + + i = IEEE1284_MODE_EPP; + i = ioctl(device_handle, PPSETMODE, & i); + if (i < 0) + { + ERROR(" cannot set compatible mode to device"); + return ERROR_JTAG_INIT_FAILED; + } + + control_port = 0x00; + i = ioctl(device_handle, PPWCONTROL, &control_port); + + control_port = 0x04; + i = ioctl(device_handle, PPWCONTROL, &control_port); + +#else + if (amt_jtagaccel_port == 0) + { + amt_jtagaccel_port = 0x378; + WARNING("No parport port specified, using default '0x378' (LPT1)"); + } + + if (ioperm(amt_jtagaccel_port, 5, 1) != 0) { + ERROR("missing privileges for direct i/o"); + return ERROR_JTAG_INIT_FAILED; + } + + /* prepare epp port */ + /* clear timeout */ + status_port = inb(amt_jtagaccel_port + 1); + outb(status_port | 0x1, amt_jtagaccel_port + 1); + + /* reset epp port */ + outb(0x00, amt_jtagaccel_port + 2); + outb(0x04, amt_jtagaccel_port + 2); +#endif + + /* enable JTAG port */ + aw_control_fsm |= 0x04; + AMT_AW(aw_control_fsm); + + amt_jtagaccel_speed(jtag_speed); + + if (jtag_reset_config & RESET_TRST_OPEN_DRAIN) + aw_control_rst &= ~0x8; + else + aw_control_rst |= 0x8; + + if (jtag_reset_config & RESET_SRST_PUSH_PULL) + aw_control_rst &= ~0x2; + else + aw_control_rst |= 0x2; + + amt_jtagaccel_reset(0, 0); + + return ERROR_OK; +} + +int amt_jtagaccel_quit(void) +{ + + return ERROR_OK; +} + +int amt_jtagaccel_handle_parport_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 0) + return ERROR_OK; + + /* only if the port wasn't overwritten by cmdline */ + if (amt_jtagaccel_port == 0) + amt_jtagaccel_port = strtoul(args[0], NULL, 0); + + return ERROR_OK; +} + +int amt_jtagaccel_handle_rtck_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 0) + { + command_print(cmd_ctx, "amt_jtagaccel RTCK feature %s", (rtck_enabled) ? "enabled" : "disabled"); + return ERROR_OK; + } + else + { + if (strcmp(args[0], "enabled") == 0) + { + rtck_enabled = 1; + + /* set RTCK enable bit */ + aw_control_fsm |= 0x02; + AMT_AW(aw_control_fsm); + } + } + + return ERROR_OK; +} diff --git a/src/jtag/bitbang.c b/src/jtag/bitbang.c new file mode 100644 index 00000000..d6ff2898 --- /dev/null +++ b/src/jtag/bitbang.c @@ -0,0 +1,219 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "bitbang.h" + +/* project specific includes */ +#include "log.h" +#include "types.h" +#include "jtag.h" +#include "configuration.h" + +/* system includes */ +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include <sys/time.h> +#include <time.h> + +bitbang_interface_t *bitbang_interface; + +int bitbang_execute_queue(void); + +void bitbang_end_state(enum tap_state state) +{ + if (tap_move_map[state] != -1) + end_state = state; + else + { + ERROR("BUG: %i is not a valid end state", state); + exit(-1); + } +} + +void bitbang_state_move(void) { + + int i=0, tms=0; + u8 tms_scan = TAP_MOVE(cur_state, end_state); + + for (i = 0; i < 7; i++) + { + tms = (tms_scan >> i) & 1; + bitbang_interface->write(0, tms, 0); + bitbang_interface->write(1, tms, 0); + } + bitbang_interface->write(0, tms, 0); + + cur_state = end_state; +} + +void bitbang_runtest(int num_cycles) +{ + int i; + + enum tap_state saved_end_state = end_state; + + /* only do a state_move when we're not already in RTI */ + if (cur_state != TAP_RTI) + { + bitbang_end_state(TAP_RTI); + bitbang_state_move(); + } + + /* execute num_cycles */ + bitbang_interface->write(0, 0, 0); + for (i = 0; i < num_cycles; i++) + { + bitbang_interface->write(1, 0, 0); + bitbang_interface->write(0, 0, 0); + } + + /* finish in end_state */ + bitbang_end_state(saved_end_state); + if (cur_state != end_state) + bitbang_state_move(); +} + +void bitbang_scan(int ir_scan, enum scan_type type, u8 *buffer, int scan_size) +{ + enum tap_state saved_end_state = end_state; + int bit_cnt; + + if (ir_scan) + bitbang_end_state(TAP_SI); + else + bitbang_end_state(TAP_SD); + + bitbang_state_move(); + bitbang_end_state(saved_end_state); + + for (bit_cnt = 0; bit_cnt < scan_size; bit_cnt++) + { + if ((buffer[bit_cnt/8] >> (bit_cnt % 8)) & 0x1) { + bitbang_interface->write(0, (bit_cnt==scan_size-1) ? 1 : 0, 1); + bitbang_interface->write(1, (bit_cnt==scan_size-1) ? 1 : 0, 1); + } else { + bitbang_interface->write(0, (bit_cnt==scan_size-1) ? 1 : 0, 0); + bitbang_interface->write(1, (bit_cnt==scan_size-1) ? 1 : 0, 0); + } + + if (type != SCAN_OUT) + { + if (bitbang_interface->read()) + buffer[(bit_cnt)/8] |= 1 << ((bit_cnt) % 8); + else + buffer[(bit_cnt)/8] &= ~(1 << ((bit_cnt) % 8)); + } + } + + /* Exit1 -> Pause */ + bitbang_interface->write(0, 0, 0); + bitbang_interface->write(1, 0, 0); + + if (ir_scan) + cur_state = TAP_PI; + else + cur_state = TAP_PD; + + if (cur_state != end_state) + bitbang_state_move(); +} + +int bitbang_execute_queue(void) +{ + jtag_command_t *cmd = jtag_command_queue; /* currently processed command */ + int scan_size; + enum scan_type type; + u8 *buffer; + + if (!bitbang_interface) + { + ERROR("BUG: Bitbang interface called, but not yet initialized"); + exit(-1); + } + + while (cmd) + { + switch (cmd->type) + { + case JTAG_END_STATE: +#ifdef _DEBUG_JTAG_IO_ + DEBUG("end_state: %i", cmd->cmd.end_state->end_state); +#endif + if (cmd->cmd.end_state->end_state != -1) + bitbang_end_state(cmd->cmd.end_state->end_state); + break; + case JTAG_RESET: +#ifdef _DEBUG_JTAG_IO_ + DEBUG("reset trst: %i srst %i", cmd->cmd.reset->trst, cmd->cmd.reset->srst); +#endif + if (cmd->cmd.reset->trst == 1) + { + cur_state = TAP_TLR; + } + bitbang_interface->reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst); + break; + case JTAG_RUNTEST: +#ifdef _DEBUG_JTAG_IO_ + DEBUG("runtest %i cycles, end in %i", cmd->cmd.runtest->num_cycles, cmd->cmd.runtest->end_state); +#endif + if (cmd->cmd.runtest->end_state != -1) + bitbang_end_state(cmd->cmd.runtest->end_state); + bitbang_runtest(cmd->cmd.runtest->num_cycles); + break; + case JTAG_STATEMOVE: +#ifdef _DEBUG_JTAG_IO_ + DEBUG("statemove end in %i", cmd->cmd.statemove->end_state); +#endif + if (cmd->cmd.statemove->end_state != -1) + bitbang_end_state(cmd->cmd.statemove->end_state); + bitbang_state_move(); + break; + case JTAG_SCAN: +#ifdef _DEBUG_JTAG_IO_ + DEBUG("scan end in %i", cmd->cmd.scan->end_state); +#endif + if (cmd->cmd.scan->end_state != -1) + bitbang_end_state(cmd->cmd.scan->end_state); + scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer); + type = jtag_scan_type(cmd->cmd.scan); + bitbang_scan(cmd->cmd.scan->ir_scan, type, buffer, scan_size); + if (jtag_read_buffer(buffer, cmd->cmd.scan) != ERROR_OK) + return ERROR_JTAG_QUEUE_FAILED; + if (buffer) + free(buffer); + break; + case JTAG_SLEEP: +#ifdef _DEBUG_JTAG_IO_ + DEBUG("sleep", cmd->cmd.sleep->us); +#endif + jtag_sleep(cmd->cmd.sleep->us); + break; + default: + ERROR("BUG: unknown JTAG command type encountered"); + exit(-1); + } + cmd = cmd->next; + } + + return ERROR_OK; +} + diff --git a/src/jtag/bitbang.h b/src/jtag/bitbang.h new file mode 100644 index 00000000..7049f435 --- /dev/null +++ b/src/jtag/bitbang.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef BITBANG_H +#define BITBANG_H + +typedef struct bitbang_interface_s +{ + /* low level callbacks (for bitbang) + */ + int (*read)(void); + void (*write)(int tck, int tms, int tdi); + void (*reset)(int trst, int srst); +} bitbang_interface_t; + +extern bitbang_interface_t *bitbang_interface; + +extern int bitbang_execute_queue(void); + +#endif /* BITBANG_H */ diff --git a/src/jtag/ep93xx.c b/src/jtag/ep93xx.c new file mode 100644 index 00000000..9c24dba4 --- /dev/null +++ b/src/jtag/ep93xx.c @@ -0,0 +1,236 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" +#include "log.h" +#include "jtag.h" +#include "bitbang.h" + +#define TDO_BIT 1 +#define TDI_BIT 2 +#define TCK_BIT 4 +#define TMS_BIT 8 +#define TRST_BIT 16 +#define SRST_BIT 32 +#define VCC_BIT 64 + +/* system includes */ +#include <sys/io.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/mman.h> +#include <unistd.h> +#include <fcntl.h> + +static u8 output_value = 0x0; +static int dev_mem_fd; +static void *gpio_controller; +static volatile u8 *gpio_data_register; +static volatile u8 *gpio_data_direction_register; + +/* low level command set + */ +int ep93xx_read(void); +void ep93xx_write(int tck, int tms, int tdi); +void ep93xx_reset(int trst, int srst); + +int ep93xx_speed(int speed); +int ep93xx_register_commands(struct command_context_s *cmd_ctx); +int ep93xx_init(void); +int ep93xx_quit(void); + +struct timespec ep93xx_zzzz; + +jtag_interface_t ep93xx_interface = +{ + .name = "ep93xx", + + .execute_queue = bitbang_execute_queue, + + .support_statemove = 0, + + .speed = ep93xx_speed, + .register_commands = ep93xx_register_commands, + .init = ep93xx_init, + .quit = ep93xx_quit, +}; + +bitbang_interface_t ep93xx_bitbang = +{ + .read = ep93xx_read, + .write = ep93xx_write, + .reset = ep93xx_reset +}; + +int ep93xx_read(void) +{ + return !!(*gpio_data_register & TDO_BIT); +} + +void ep93xx_write(int tck, int tms, int tdi) +{ + if (tck) + output_value |= TCK_BIT; + else + output_value &= TCK_BIT; + + if (tms) + output_value |= TMS_BIT; + else + output_value &= TMS_BIT; + + if (tdi) + output_value |= TDI_BIT; + else + output_value &= TDI_BIT; + + *gpio_data_register = output_value; + nanosleep(ep93xx_zzzz); +} + +/* (1) assert or (0) deassert reset lines */ +void ep93xx_reset(int trst, int srst) +{ + if (trst == 0) + output_value |= TRST_BIT; + else if (trst == 1) + output_value &= TRST_BIT; + + if (srst == 0) + output_value |= SRST_BIT; + else if (srst == 1) + output_value &= SRST_BIT; + + *gpio_data_register = output_value; + nanosleep(ep93xx_zzzz); +} + +int ep93xx_speed(int speed) +{ + + return ERROR_OK; +} + +int ep93xx_register_commands(struct command_context_s *cmd_ctx) +{ + + return ERROR_OK; +} + +static int set_gonk_mode(void) +{ + void *syscon; + u32 devicecfg; + + syscon = mmap(NULL, 4096, PROT_READ | PROT_WRITE, + MAP_SHARED, dev_mem_fd, 0x80930000); + if (syscon == MAP_FAILED) { + perror("mmap"); + return ERROR_JTAG_INIT_FAILED; + } + + devicecfg = *((volatile int *)(syscon + 0x80)); + *((volatile int *)(syscon + 0xc0)) = 0xaa; + *((volatile int *)(syscon + 0x80)) = devicecfg | 0x08000000; + + munmap(syscon, 4096); + + return ERROR_OK; +} + +int ep93xx_init(void) +{ + int ret; + + bitbang_interface = &ep93xx_bitbang; + + ep93xx_zzzz.tv_sec = 0; + ep93xx_zzzz.tv_nsec = 10000000; + + dev_mem_fd = open("/dev/mem", O_RDWR | O_SYNC); + if (dev_mem_fd < 0) { + perror("open"); + return ERROR_JTAG_INIT_FAILED; + } + + gpio_controller = mmap(NULL, 4096, PROT_READ | PROT_WRITE, + MAP_SHARED, dev_mem_fd, 0x80840000); + if (gpio_controller == MAP_FAILED) { + perror("mmap"); + close(dev_mem_fd); + return ERROR_JTAG_INIT_FAILED; + } + + ret = set_gonk_mode(); + if (ret != ERROR_OK) { + munmap(gpio_controller, 4096); + close(dev_mem_fd); + return ret; + } + +#if 0 + /* Use GPIO port A. */ + gpio_data_register = gpio_controller + 0x00; + gpio_data_direction_register = gpio_controller + 0x10; + + + /* Use GPIO port B. */ + gpio_data_register = gpio_controller + 0x04; + gpio_data_direction_register = gpio_controller + 0x14; + + /* Use GPIO port C. */ + gpio_data_register = gpio_controller + 0x08; + gpio_data_direction_register = gpio_controller + 0x18; + + /* Use GPIO port D. */ + gpio_data_register = gpio_controller + 0x0c; + gpio_data_direction_register = gpio_controller + 0x1c; +#endif + + /* Use GPIO port C. */ + gpio_data_register = gpio_controller + 0x08; + gpio_data_direction_register = gpio_controller + 0x18; + + printf("gpio_data_register = %08x\n", gpio_data_register); + printf("gpio_data_direction_reg = %08x\n", gpio_data_direction_register); + /* + * Configure bit 0 (TDO) as an input, and bits 1-5 (TDI, TCK + * TMS, TRST, SRST) as outputs. Drive TDI and TCK low, and + * TMS/TRST/SRST high. + */ + output_value = TMS_BIT | TRST_BIT | SRST_BIT | VCC_BIT; + *gpio_data_register = output_value; + nanosleep(ep93xx_zzzz); + + /* + * Configure the direction register. 1 = output, 0 = input. + */ + *gpio_data_direction_register = + TDI_BIT | TCK_BIT | TMS_BIT | TRST_BIT | SRST_BIT | VCC_BIT; + + nanosleep(ep93xx_zzzz); + return ERROR_OK; +} + +int ep93xx_quit(void) +{ + + return ERROR_OK; +} diff --git a/src/jtag/ftd2xx.c b/src/jtag/ftd2xx.c new file mode 100644 index 00000000..c73c8d58 --- /dev/null +++ b/src/jtag/ftd2xx.c @@ -0,0 +1,1004 @@ +/*************************************************************************** + * Copyright (C) 2004 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" +#if IS_CYGWIN == 1 +#include "windows.h" +#undef ERROR +#endif + +/* project specific includes */ +#include "log.h" +#include "types.h" +#include "jtag.h" +#include "configuration.h" + +/* system includes */ +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <ftd2xx.h> + +#include <sys/time.h> +#include <time.h> + +/* enable this to debug io latency + */ +#if 0 +#define _DEBUG_USB_IO_ +#endif + +/* enable this to debug communication + */ +#if 0 +#define _DEBUG_USB_COMMS_ +#endif + +/* enable this to work around ftd2xx deadlock + */ +#if 0 +#define _FTD2XX_QUEUE_DELAY_ +#endif + +int ftd2xx_execute_queue(void); + +int ftd2xx_speed(int speed); +int ftd2xx_register_commands(struct command_context_s *cmd_ctx); +int ftd2xx_init(void); +int ftd2xx_quit(void); + +int ftd2xx_handle_device_desc_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int ftd2xx_handle_layout_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int ftd2xx_handle_vid_pid_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +char *ftd2xx_device_desc = NULL; +char *ftd2xx_layout = NULL; +u16 ftd2xx_vid = 0x0403; +u16 ftd2xx_pid = 0x6010; + +typedef struct ftd2xx_layout_s +{ + char* name; + int(*init)(void); + void(*reset)(int trst, int srst); +} ftd2xx_layout_t; + +int usbjtag_init(void); +int jtagkey_init(void); +void usbjtag_reset(int trst, int srst); +void jtagkey_reset(int trst, int srst); + +ftd2xx_layout_t ftd2xx_layouts[] = +{ + {"usbjtag", usbjtag_init, usbjtag_reset}, + {"jtagkey", jtagkey_init, jtagkey_reset}, + {"jtagkey_prototype_v1", jtagkey_init, jtagkey_reset}, + {NULL, NULL, NULL}, +}; + +static u8 nTRST, nTRSTnOE, nSRST, nSRSTnOE; + +static ftd2xx_layout_t *layout; +static u8 low_output = 0x0; +static u8 low_direction = 0x0; +static u8 high_output = 0x0; +static u8 high_direction = 0x0; +static FT_HANDLE ftdih = NULL; + +static u8 *ftd2xx_buffer = NULL; +static int ftd2xx_buffer_size = 0; +static int ftd2xx_read_pointer = 0; +static int ftd2xx_expect_read = 0; +#define FTD2XX_BUFFER_SIZE 131072 +#define BUFFER_ADD ftd2xx_buffer[ftd2xx_buffer_size++] +#define BUFFER_READ ftd2xx_buffer[ftd2xx_read_pointer++] + +jtag_interface_t ftd2xx_interface = +{ + + .name = "ftd2xx", + + .execute_queue = ftd2xx_execute_queue, + + .support_statemove = 1, + + .speed = ftd2xx_speed, + .register_commands = ftd2xx_register_commands, + .init = ftd2xx_init, + .quit = ftd2xx_quit, +}; + +int ftd2xx_speed(int speed) +{ + u8 buf[3]; + FT_STATUS status; + DWORD bytes_written; + + buf[0] = 0x86; /* command "set divisor" */ + buf[1] = speed & 0xff; /* valueL (0=6MHz, 1=3MHz, 2=1.5MHz, ...*/ + buf[2] = (speed >> 8) & 0xff; /* valueH */ + + DEBUG("%2.2x %2.2x %2.2x", buf[0], buf[1], buf[2]); + if (((status = FT_Write(ftdih, buf, 3, &bytes_written)) != FT_OK) || (bytes_written != 3)) + { + ERROR("couldn't write to ftdi device: %i", status); + return status; + } + + return ERROR_OK; +} + +int ftd2xx_register_commands(struct command_context_s *cmd_ctx) +{ + register_command(cmd_ctx, NULL, "ftd2xx_device_desc", ftd2xx_handle_device_desc_command, + COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "ftd2xx_layout", ftd2xx_handle_layout_command, + COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "ftd2xx_vid_pid", ftd2xx_handle_vid_pid_command, + COMMAND_CONFIG, NULL); + return ERROR_OK; +} + +void ftd2xx_end_state(state) +{ + if (tap_move_map[state] != -1) + end_state = state; + else + { + ERROR("BUG: %i is not a valid end state", state); + exit(-1); + } +} + +void ftd2xx_read_scan(enum scan_type type, u8* buffer, int scan_size) +{ + int num_bytes = ((scan_size + 7) / 8); + int bits_left = scan_size; + int cur_byte = 0; + + while(num_bytes-- > 1) + { + buffer[cur_byte] = BUFFER_READ; + cur_byte++; + bits_left -= 8; + } + + buffer[cur_byte] = 0x0; + + if (bits_left > 1) + { + buffer[cur_byte] = BUFFER_READ >> 1; + } + + buffer[cur_byte] = (buffer[cur_byte] | ((BUFFER_READ & 0x02) << 6)) >> (8 - bits_left); + +} + +void ftd2xx_debug_dump_buffer(void) +{ + int i; + char line[256]; + char *line_p = line; + + for (i = 0; i < ftd2xx_buffer_size; i++) + { + line_p += snprintf(line_p, 256 - (line_p - line), "%2.2x ", ftd2xx_buffer[i]); + if (i % 16 == 15) + { + DEBUG("%s", line); + line_p = line; + } + } + + if (line_p != line) + DEBUG("%s", line); +} + +int ftd2xx_send_and_recv(jtag_command_t *first, jtag_command_t *last) +{ + jtag_command_t *cmd; + u8 *buffer; + int scan_size; + enum scan_type type; + FT_STATUS status; + DWORD bytes_written; + DWORD bytes_read; + +#ifdef _DEBUG_USB_IO_ + struct timeval start, inter, inter2, end; +#endif + +#ifdef _DEBUG_USB_COMMS_ + DEBUG("write buffer (size %i):", ftd2xx_buffer_size); + ftd2xx_debug_dump_buffer(); +#endif + +#ifdef _DEBUG_USB_IO_ + gettimeofday(&start, NULL); +#endif + + if ((status = FT_Write(ftdih, ftd2xx_buffer, ftd2xx_buffer_size, &bytes_written)) != FT_OK) + { + ERROR("couldn't write to ftdi device: %i", status); + exit(-1); + } + +#ifdef _DEBUG_USB_IO_ + gettimeofday(&inter, NULL); +#endif + + if (ftd2xx_expect_read) + { + int timeout = 100; + ftd2xx_buffer_size = 0; + +#ifdef _FTD2XX_QUEUE_DELAY_ + DWORD inrxqueue = 0; + while (inrxqueue < ftd2xx_expect_read) + { + FT_GetQueueStatus(ftdih, &inrxqueue); + if (inrxqueue >= ftd2xx_expect_read) + break; + usleep(1000); + }; +#endif + +#ifdef _DEBUG_USB_IO_ + gettimeofday(&inter2, NULL); +#endif + + if ((status = FT_Read(ftdih, ftd2xx_buffer, ftd2xx_expect_read, &bytes_read)) != FT_OK) + { + ERROR("couldn't read from ftdi device: %i", status); + exit(-1); + } + +#ifdef _DEBUG_USB_IO_ + gettimeofday(&end, NULL); + + INFO("inter: %i.%i, inter2: %i.%i end: %i.%i", inter.tv_sec - start.tv_sec, inter.tv_usec - start.tv_usec, + inter2.tv_sec - start.tv_sec, inter2.tv_usec - start.tv_usec, + end.tv_sec - start.tv_sec, end.tv_usec - start.tv_usec); +#endif + + + ftd2xx_buffer_size = bytes_read; + + if (ftd2xx_expect_read != ftd2xx_buffer_size) + { + ERROR("ftd2xx_expect_read (%i) != ftd2xx_buffer_size (%i) (%i retries)", ftd2xx_expect_read, ftd2xx_buffer_size, 100 - timeout); + ftd2xx_debug_dump_buffer(); + + exit(-1); + } + +#ifdef _DEBUG_USB_COMMS_ + DEBUG("read buffer (%i retries): %i bytes", 100 - timeout, ftd2xx_buffer_size); + ftd2xx_debug_dump_buffer(); +#endif + } + + ftd2xx_expect_read = 0; + ftd2xx_read_pointer = 0; + + cmd = first; + while (cmd != last) + { + switch (cmd->type) + { + case JTAG_SCAN: + type = jtag_scan_type(cmd->cmd.scan); + if (type != SCAN_OUT) + { + scan_size = jtag_scan_size(cmd->cmd.scan); + buffer = calloc(CEIL(scan_size, 8), 1); + ftd2xx_read_scan(type, buffer, scan_size); + jtag_read_buffer(buffer, cmd->cmd.scan); + free(buffer); + } + break; + default: + break; + } + cmd = cmd->next; + } + + ftd2xx_buffer_size = 0; + + return ERROR_OK; +} + +void ftd2xx_add_scan(int ir_scan, enum scan_type type, u8 *buffer, int scan_size) +{ + int num_bytes = (scan_size + 7) / 8; + int bits_left = scan_size; + int cur_byte = 0; + int last_bit; + + /* command "Clock Data to TMS/CS Pin (no Read)" */ + BUFFER_ADD = 0x4b; + /* scan 7 bit */ + BUFFER_ADD = 0x6; + /* TMS data bits */ + if (ir_scan) + { + BUFFER_ADD = TAP_MOVE(cur_state, TAP_SI); + cur_state = TAP_SI; + } + else + { + BUFFER_ADD = TAP_MOVE(cur_state, TAP_SD); + cur_state = TAP_SD; + } + //DEBUG("added TMS scan (no read)"); + + /* add command for complete bytes */ + if (num_bytes > 1) + { + if (type == SCAN_IO) + { + /* Clock Data Bytes In and Out LSB First */ + BUFFER_ADD = 0x39; + //DEBUG("added TDI bytes (io %i)", num_bytes); + } + else if (type == SCAN_OUT) + { + /* Clock Data Bytes Out on -ve Clock Edge LSB First (no Read) */ + BUFFER_ADD = 0x19; + //DEBUG("added TDI bytes (o)"); + } + else if (type == SCAN_IN) + { + /* Clock Data Bytes In on +ve Clock Edge LSB First (no Write) */ + BUFFER_ADD = 0x28; + //DEBUG("added TDI bytes (i %i)", num_bytes); + } + BUFFER_ADD = (num_bytes-2) & 0xff; + BUFFER_ADD = (num_bytes >> 8) & 0xff; + } + if (type != SCAN_IN) + { + /* add complete bytes */ + while(num_bytes-- > 1) + { + BUFFER_ADD = buffer[cur_byte]; + cur_byte++; + bits_left -= 8; + } + } + if (type == SCAN_IN) + { + bits_left -= 8 * (num_bytes - 1); + } + + /* the most signifcant bit is scanned during TAP movement */ + if (type != SCAN_IN) + last_bit = (buffer[cur_byte] >> (bits_left - 1)) & 0x1; + else + last_bit = 0; + + /* process remaining bits but the last one */ + if (bits_left > 1) + { + if (type == SCAN_IO) + { + /* Clock Data Bits In and Out LSB First */ + BUFFER_ADD = 0x3b; + //DEBUG("added TDI bits (io) %i", bits_left - 1); + } + else if (type == SCAN_OUT) + { + /* Clock Data Bits Out on -ve Clock Edge LSB First (no Read) */ + BUFFER_ADD = 0x1b; + //DEBUG("added TDI bits (o)"); + } + else if (type == SCAN_IN) + { + /* Clock Data Bits In on +ve Clock Edge LSB First (no Write) */ + BUFFER_ADD = 0x2a; + //DEBUG("added TDI bits (i %i)", bits_left - 1); + } + BUFFER_ADD = bits_left - 2; + if (type != SCAN_IN) + BUFFER_ADD = buffer[cur_byte]; + } + + /* move from Shift-IR/DR to end state */ + if (type != SCAN_OUT) + { + /* Clock Data to TMS/CS Pin with Read */ + BUFFER_ADD = 0x6b; + //DEBUG("added TMS scan (read)"); + } + else + { + /* Clock Data to TMS/CS Pin (no Read) */ + BUFFER_ADD = 0x4b; + //DEBUG("added TMS scan (no read)"); + } + BUFFER_ADD = 0x6; + BUFFER_ADD = TAP_MOVE(cur_state, end_state) | (last_bit << 7); + cur_state = end_state; + +} + +int ftd2xx_predict_scan_out(int scan_size, enum scan_type type) +{ + int predicted_size = 6; + if (type == SCAN_IN) /* only from device to host */ + { + predicted_size += (CEIL(scan_size, 8) > 1) ? 3 : 0; + predicted_size += ((scan_size - 1) % 8) ? 2 : 0; + } + else /* host to device, or bidirectional */ + { + predicted_size += (CEIL(scan_size, 8) > 1) ? (CEIL(scan_size, 8) + 3 - 1) : 0; + predicted_size += ((scan_size - 1) % 8) ? 3 : 0; + } + + return predicted_size; +} + +int ftd2xx_predict_scan_in(int scan_size, enum scan_type type) +{ + int predicted_size = 0; + + if (type != SCAN_OUT) + { + /* complete bytes */ + predicted_size += (CEIL(scan_size, 8) > 1) ? (CEIL(scan_size, 8) - 1) : 0; + /* remaining bits - 1 */ + predicted_size += ((scan_size - 1) % 8) ? 1 : 0; + /* last bit (from TMS scan) */ + predicted_size += 1; + } + + //DEBUG("scan_size: %i, predicted_size: %i", scan_size, predicted_size); + + return predicted_size; +} + +void usbjtag_reset(int trst, int srst) +{ + if (trst == 1) + { + cur_state = TAP_TLR; + if (jtag_reset_config & RESET_TRST_OPEN_DRAIN) + low_direction |= nTRSTnOE; /* switch to output pin (output is low) */ + else + low_output &= ~nTRST; /* switch output low */ + } + else if (trst == 0) + { + if (jtag_reset_config & RESET_TRST_OPEN_DRAIN) + low_direction &= ~nTRSTnOE; /* switch to input pin (high-Z + internal and external pullup) */ + else + low_output |= nTRST; /* switch output high */ + } + + if (srst == 1) + { + if (jtag_reset_config & RESET_SRST_PUSH_PULL) + low_output &= ~nSRST; /* switch output low */ + else + low_direction |= nSRSTnOE; /* switch to output pin (output is low) */ + } + else if (srst == 0) + { + if (jtag_reset_config & RESET_SRST_PUSH_PULL) + low_output |= nSRST; /* switch output high */ + else + low_direction &= ~nSRSTnOE; /* switch to input pin (high-Z) */ + } + + /* command "set data bits low byte" */ + BUFFER_ADD = 0x80; + BUFFER_ADD = low_output; + BUFFER_ADD = low_direction; + +} + +void jtagkey_reset(int trst, int srst) +{ + if (trst == 1) + { + cur_state = TAP_TLR; + if (jtag_reset_config & RESET_TRST_OPEN_DRAIN) + high_output &= ~nTRSTnOE; + else + high_output &= ~nTRST; + } + else if (trst == 0) + { + if (jtag_reset_config & RESET_TRST_OPEN_DRAIN) + high_output |= nTRSTnOE; + else + high_output |= nTRST; + } + + if (srst == 1) + { + if (jtag_reset_config & RESET_SRST_PUSH_PULL) + high_output &= ~nSRST; + else + high_output &= ~nSRSTnOE; + } + else if (srst == 0) + { + if (jtag_reset_config & RESET_SRST_PUSH_PULL) + high_output |= nSRST; + else + high_output |= nSRSTnOE; + } + + /* command "set data bits high byte" */ + BUFFER_ADD = 0x82; + BUFFER_ADD = high_output; + BUFFER_ADD = high_direction; + DEBUG("trst: %i, srst: %i, high_output: 0x%2.2x, high_direction: 0x%2.2x", trst, srst, high_output, high_direction); +} + +int ftd2xx_execute_queue() +{ + jtag_command_t *cmd = jtag_command_queue; /* currently processed command */ + jtag_command_t *first_unsent = cmd; /* next command that has to be sent */ + u8 *buffer; + int scan_size; /* size of IR or DR scan */ + enum scan_type type; + int i; + int predicted_size = 0; + int require_send = 0; + + ftd2xx_buffer_size = 0; + ftd2xx_expect_read = 0; + + while (cmd) + { + switch(cmd->type) + { + case JTAG_END_STATE: + if (cmd->cmd.end_state->end_state != -1) + ftd2xx_end_state(cmd->cmd.end_state->end_state); + break; + case JTAG_RESET: + /* only send the maximum buffer size that FT2232C can handle */ + predicted_size = 3; + if (ftd2xx_buffer_size + predicted_size + 1 > FTD2XX_BUFFER_SIZE) + { + ftd2xx_send_and_recv(first_unsent, cmd); + require_send = 0; + first_unsent = cmd; + } + + layout->reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst); + require_send = 1; + + break; + case JTAG_RUNTEST: + /* only send the maximum buffer size that FT2232C can handle */ + predicted_size = 0; + if (cur_state != TAP_RTI) + predicted_size += 3; + predicted_size += 3 * CEIL(cmd->cmd.runtest->num_cycles, 7); + if ((cmd->cmd.runtest->end_state != -1) && (cmd->cmd.runtest->end_state != TAP_RTI)) + predicted_size += 3; + if ((cmd->cmd.runtest->end_state == -1) && (end_state != TAP_RTI)) + predicted_size += 3; + if (ftd2xx_buffer_size + predicted_size + 1 > FTD2XX_BUFFER_SIZE) + { + ftd2xx_send_and_recv(first_unsent, cmd); + require_send = 0; + first_unsent = cmd; + } + if (cur_state != TAP_RTI) + { + /* command "Clock Data to TMS/CS Pin (no Read)" */ + BUFFER_ADD = 0x4b; + /* scan 7 bit */ + BUFFER_ADD = 0x6; + /* TMS data bits */ + BUFFER_ADD = TAP_MOVE(cur_state, TAP_RTI); + cur_state = TAP_RTI; + require_send = 1; + } + i = cmd->cmd.runtest->num_cycles; + while (i > 0) + { + /* command "Clock Data to TMS/CS Pin (no Read)" */ + BUFFER_ADD = 0x4b; + /* scan 7 bit */ + BUFFER_ADD = (i > 7) ? 6 : (i - 1); + /* TMS data bits */ + BUFFER_ADD = 0x0; + cur_state = TAP_RTI; + i -= (i > 7) ? 7 : i; + //DEBUG("added TMS scan (no read)"); + } + if (cmd->cmd.runtest->end_state != -1) + ftd2xx_end_state(cmd->cmd.runtest->end_state); + if (cur_state != end_state) + { + /* command "Clock Data to TMS/CS Pin (no Read)" */ + BUFFER_ADD = 0x4b; + /* scan 7 bit */ + BUFFER_ADD = 0x6; + /* TMS data bits */ + BUFFER_ADD = TAP_MOVE(cur_state, end_state); + cur_state = end_state; + //DEBUG("added TMS scan (no read)"); + } + require_send = 1; + break; + case JTAG_STATEMOVE: + /* only send the maximum buffer size that FT2232C can handle */ + predicted_size = 3; + if (ftd2xx_buffer_size + predicted_size + 1 > FTD2XX_BUFFER_SIZE) + { + ftd2xx_send_and_recv(first_unsent, cmd); + require_send = 0; + first_unsent = cmd; + } + if (cmd->cmd.statemove->end_state != -1) + ftd2xx_end_state(cmd->cmd.statemove->end_state); + /* command "Clock Data to TMS/CS Pin (no Read)" */ + BUFFER_ADD = 0x4b; + /* scan 7 bit */ + BUFFER_ADD = 0x6; + /* TMS data bits */ + BUFFER_ADD = TAP_MOVE(cur_state, end_state); + //DEBUG("added TMS scan (no read)"); + cur_state = end_state; + require_send = 1; + break; + case JTAG_SCAN: + scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer); + type = jtag_scan_type(cmd->cmd.scan); + predicted_size = ftd2xx_predict_scan_out(scan_size, type); + if (ftd2xx_buffer_size + predicted_size + 1 > FTD2XX_BUFFER_SIZE) + { + DEBUG("ftd2xx buffer size reached, sending queued commands (first_unsent: %x, cmd: %x)", first_unsent, cmd); + ftd2xx_send_and_recv(first_unsent, cmd); + require_send = 0; + first_unsent = cmd; + } + ftd2xx_expect_read += ftd2xx_predict_scan_in(scan_size, type); + //DEBUG("new read size: %i", ftd2xx_expect_read); + if (cmd->cmd.scan->end_state != -1) + ftd2xx_end_state(cmd->cmd.scan->end_state); + ftd2xx_add_scan(cmd->cmd.scan->ir_scan, type, buffer, scan_size); + require_send = 1; + if (buffer) + free(buffer); + break; + case JTAG_SLEEP: + ftd2xx_send_and_recv(first_unsent, cmd); + first_unsent = cmd->next; + jtag_sleep(cmd->cmd.sleep->us); + break; + default: + ERROR("BUG: unknown JTAG command type encountered"); + exit(-1); + } + cmd = cmd->next; + } + + if (require_send > 0) + ftd2xx_send_and_recv(first_unsent, cmd); + + return ERROR_OK; +} + +int ftd2xx_init(void) +{ + u8 latency_timer; + FT_STATUS status; + DWORD num_devices; + + ftd2xx_layout_t *cur_layout = ftd2xx_layouts; + + if ((ftd2xx_layout == NULL) || (ftd2xx_layout[0] == 0)) + { + ftd2xx_layout = "usbjtag"; + WARNING("No ftd2xx layout specified, using default 'usbjtag'"); + } + + while (cur_layout->name) + { + if (strcmp(cur_layout->name, ftd2xx_layout) == 0) + { + layout = cur_layout; + break; + } + cur_layout++; + } + + if (!layout) + { + ERROR("No matching layout found for %s", ftd2xx_layout); + return ERROR_JTAG_INIT_FAILED; + } + + if (ftd2xx_device_desc == NULL) + { + WARNING("no ftd2xx device description specified, using default 'Dual RS232'"); + ftd2xx_device_desc = "Dual RS232"; + } + +#if IS_CYGWIN != 1 + /* Add JTAGkey Vid/Pid to the linux driver */ + if ((status = FT_SetVIDPID(ftd2xx_vid, ftd2xx_pid)) != FT_OK) + { + WARNING("couldn't add %4.4x:%4.4x", ftd2xx_vid, ftd2xx_pid); + } +#endif + + if ((status = FT_OpenEx(ftd2xx_device_desc, FT_OPEN_BY_DESCRIPTION, &ftdih)) != FT_OK) + { + ERROR("unable to open ftdi device: %i", status); + status = FT_ListDevices(&num_devices, NULL, FT_LIST_NUMBER_ONLY); + if (status == FT_OK) + { + char **desc_array = malloc(sizeof(char*) * (num_devices + 1)); + int i; + + for (i = 0; i < num_devices; i++) + desc_array[i] = malloc(64); + desc_array[num_devices] = NULL; + + status = FT_ListDevices(desc_array, &num_devices, FT_LIST_ALL | FT_OPEN_BY_DESCRIPTION); + + if (status == FT_OK) + { + ERROR("ListDevices: %d\n", num_devices); + for (i = 0; i < num_devices; i++) + ERROR("%i: %s", i, desc_array[i]); + } + + for (i = 0; i < num_devices; i++) + free(desc_array[i]); + free(desc_array); + } + else + { + printf("ListDevices: NONE\n"); + } + return ERROR_JTAG_INIT_FAILED; + } + + if ((status = FT_SetLatencyTimer(ftdih, 2)) != FT_OK) + { + ERROR("unable to set latency timer: %i", status); + return ERROR_JTAG_INIT_FAILED; + } + + if ((status = FT_GetLatencyTimer(ftdih, &latency_timer)) != FT_OK) + { + ERROR("unable to get latency timer: %i", status); + return ERROR_JTAG_INIT_FAILED; + } + else + { + DEBUG("current latency timer: %i", latency_timer); + } + + if ((status = FT_SetBitMode(ftdih, 0x0b, 2)) != FT_OK) + { + ERROR("unable to enable bit i/o mode: %i", status); + return ERROR_JTAG_INIT_FAILED; + } + + ftd2xx_buffer_size = 0; + ftd2xx_buffer = malloc(FTD2XX_BUFFER_SIZE); + + if (layout->init() != ERROR_OK) + return ERROR_JTAG_INIT_FAILED; + + ftd2xx_speed(jtag_speed); + + if ((status = FT_Purge(ftdih, FT_PURGE_RX | FT_PURGE_TX)) != FT_OK) + { + ERROR("error purging ftd2xx device: %i", status); + return ERROR_JTAG_INIT_FAILED; + } + + return ERROR_OK; +} + +int usbjtag_init(void) +{ + u8 buf[3]; + FT_STATUS status; + DWORD bytes_written; + + low_output = 0x08; + low_direction = 0x0b; + + nTRST = 0x10; + nTRSTnOE = 0x10; + nSRST = 0x40; + nSRSTnOE = 0x40; + + if (jtag_reset_config & RESET_TRST_OPEN_DRAIN) + { + low_direction &= ~nTRSTnOE; /* nTRST input */ + low_output &= ~nTRST; /* nTRST = 0 */ + } + else + { + low_direction |= nTRSTnOE; /* nTRST output */ + low_output |= nTRST; /* nTRST = 1 */ + } + + if (jtag_reset_config & RESET_SRST_PUSH_PULL) + { + low_direction |= nSRSTnOE; /* nSRST output */ + low_output |= nSRST; /* nSRST = 1 */ + } + else + { + low_direction &= ~nSRSTnOE; /* nSRST input */ + low_output &= ~nSRST; /* nSRST = 0 */ + } + + /* initialize low byte for jtag */ + buf[0] = 0x80; /* command "set data bits low byte" */ + buf[1] = low_output; /* value (TMS=1,TCK=0, TDI=0, xRST high) */ + buf[2] = low_direction; /* dir (output=1), TCK/TDI/TMS=out, TDO=in */ + DEBUG("%2.2x %2.2x %2.2x", buf[0], buf[1], buf[2]); + + if (((FT_Write(ftdih, buf, 3, &bytes_written)) != FT_OK) || (bytes_written != 3)) + { + ERROR("couldn't write to ftdi device: %i", status); + return ERROR_JTAG_INIT_FAILED; + } + + return ERROR_OK; +} + +int jtagkey_init(void) +{ + u8 buf[3]; + FT_STATUS status; + DWORD bytes_written; + + low_output = 0x08; + low_direction = 0x1b; + + /* initialize low byte for jtag */ + buf[0] = 0x80; /* command "set data bits low byte" */ + buf[1] = low_output; /* value (TMS=1,TCK=0, TDI=0, nOE=0) */ + buf[2] = low_direction; /* dir (output=1), TCK/TDI/TMS=out, TDO=in, nOE=out */ + DEBUG("%2.2x %2.2x %2.2x", buf[0], buf[1], buf[2]); + + if (((FT_Write(ftdih, buf, 3, &bytes_written)) != FT_OK) || (bytes_written != 3)) + { + ERROR("couldn't write to ftdi device: %i", status); + return ERROR_JTAG_INIT_FAILED; + } + + if (strcmp(layout->name, "jtagkey") == 0) + { + nTRST = 0x01; + nTRSTnOE = 0x4; + nSRST = 0x02; + nSRSTnOE = 0x08; + } + else if (strcmp(layout->name, "jtagkey_prototype_v1") == 0) + { + nTRST = 0x02; + nTRSTnOE = 0x1; + nSRST = 0x08; + nSRSTnOE = 0x04; + } + else + { + ERROR("BUG: jtagkey_init called for non jtagkey layout"); + exit(-1); + } + + high_output = 0x0; + high_direction = 0x0f; + + if (jtag_reset_config & RESET_TRST_OPEN_DRAIN) + { + high_output |= nTRSTnOE; + high_output &= ~nTRST; + } + else + { + high_output &= ~nTRSTnOE; + high_output |= nTRST; + } + + if (jtag_reset_config & RESET_SRST_PUSH_PULL) + { + high_output &= ~nSRSTnOE; + high_output |= nSRST; + } + else + { + high_output |= nSRSTnOE; + high_output &= ~nSRST; + } + + /* initialize high port */ + buf[0] = 0x82; /* command "set data bits low byte" */ + buf[1] = high_output; /* value */ + buf[2] = high_direction; /* all outputs (xRST and xRSTnOE) */ + DEBUG("%2.2x %2.2x %2.2x", buf[0], buf[1], buf[2]); + + if (((FT_Write(ftdih, buf, 3, &bytes_written)) != FT_OK) || (bytes_written != 3)) + { + ERROR("couldn't write to ftdi device: %i", status); + return ERROR_JTAG_INIT_FAILED; + } + + return ERROR_OK; +} + +int ftd2xx_quit(void) +{ + FT_STATUS status; + + status = FT_Close(ftdih); + + free(ftd2xx_buffer); + + return ERROR_OK; +} + +int ftd2xx_handle_device_desc_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 1) + { + ftd2xx_device_desc = strdup(args[0]); + } + else + { + ERROR("expected exactly one argument to ftd2xx_device_desc <description>"); + } + + return ERROR_OK; +} + +int ftd2xx_handle_layout_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 0) + return ERROR_OK; + + ftd2xx_layout = malloc(strlen(args[0])); + strcpy(ftd2xx_layout, args[0]); + + return ERROR_OK; +} + +int ftd2xx_handle_vid_pid_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc >= 2) + { + ftd2xx_vid = strtol(args[0], NULL, 0); + ftd2xx_pid = strtol(args[1], NULL, 0); + } + else + { + WARNING("incomplete ftd2xx_vid_pid configuration directive"); + } + + return ERROR_OK; +} diff --git a/src/jtag/ftdi2232.c b/src/jtag/ftdi2232.c new file mode 100644 index 00000000..efd528c3 --- /dev/null +++ b/src/jtag/ftdi2232.c @@ -0,0 +1,630 @@ +/*************************************************************************** + * Copyright (C) 2004 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/* project specific includes */ +#include "log.h" +#include "types.h" +#include "jtag.h" +#include "configuration.h" +#include "command.h" + +/* system includes */ +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <usb.h> +#include <ftdi.h> + +#include <sys/time.h> +#include <time.h> + +/* enable this to debug io latency + */ +#if 0 +#define _DEBUG_USB_IO_ +#endif + +int ftdi2232_execute_queue(void); + +int ftdi2232_speed(int speed); +int ftdi2232_register_commands(struct command_context_s *cmd_ctx); +int ftdi2232_init(void); +int ftdi2232_quit(void); + +enum { FTDI2232_TRST = 0x10, FTDI2232_SRST = 0x40 }; +static u8 discrete_output = 0x0 | FTDI2232_TRST | FTDI2232_SRST; +static struct ftdi_context ftdic; + +static u8 *ftdi2232_buffer = NULL; +static int ftdi2232_buffer_size = 0; +static int ftdi2232_read_pointer = 0; +static int ftdi2232_expect_read = 0; +#define FTDI2232_BUFFER_SIZE 131072 +#define BUFFER_ADD ftdi2232_buffer[ftdi2232_buffer_size++] +#define BUFFER_READ ftdi2232_buffer[ftdi2232_read_pointer++] + +#define FTDI2232_SAVE_SIZE 1024 + +int ftdi2232_handle_vid_pid_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +static u16 ftdi2232_vid = 0x0403; +static u16 ftdi2232_pid = 0x6010; + +jtag_interface_t ftdi2232_interface = +{ + + .name = "ftdi2232", + + .execute_queue = ftdi2232_execute_queue, + + .support_statemove = 1, + + .speed = ftdi2232_speed, + .register_commands = ftdi2232_register_commands, + .init = ftdi2232_init, + .quit = ftdi2232_quit, +}; + +int ftdi2232_speed(int speed) +{ + u8 buf[3]; + + buf[0] = 0x86; /* command "set divisor" */ + buf[1] = speed & 0xff; /* valueL (0=6MHz, 1=3MHz, 2=1.5MHz, ...*/ + buf[2] = (speed >> 8) & 0xff; /* valueH */ + + DEBUG("%2.2x %2.2x %2.2x", buf[0], buf[1], buf[2]); + ftdi_write_data(&ftdic, buf, 3); + + return ERROR_OK; +} + +int ftdi2232_register_commands(struct command_context_s *cmd_ctx) +{ + register_command(cmd_ctx, NULL, "ftdi2232_vid_pid", ftdi2232_handle_vid_pid_command, + COMMAND_CONFIG, NULL); + + return ERROR_OK; +} + +void ftdi2232_end_state(state) +{ + if (tap_move_map[state] != -1) + end_state = state; + else + { + ERROR("BUG: %i is not a valid end state", state); + exit(-1); + } +} + +void ftdi2232_read_scan(enum scan_type type, u8* buffer, int scan_size) +{ + int num_bytes = ((scan_size + 7) / 8); + int bits_left = scan_size; + int cur_byte = 0; + + while(num_bytes-- > 1) + { + buffer[cur_byte] = BUFFER_READ; + cur_byte++; + bits_left -= 8; + } + + buffer[cur_byte] = 0x0; + + if (bits_left > 1) + { + buffer[cur_byte] = BUFFER_READ >> 1; + } + + buffer[cur_byte] = (buffer[cur_byte] | ((BUFFER_READ & 0x02) << 6)) >> (8 - bits_left); + +} + +void ftdi2232_debug_dump_buffer(void) +{ + int i; + for (i = 0; i < ftdi2232_buffer_size; i++) + { + printf("%2.2x ", ftdi2232_buffer[i]); + if (i % 16 == 15) + printf("\n"); + } + printf("\n"); + fflush(stdout); +} + +int ftdi2232_send_and_recv(jtag_command_t *first, jtag_command_t *last) +{ + jtag_command_t *cmd; + u8 *buffer; + int scan_size; + enum scan_type type; + int retval; + + BUFFER_ADD = 0x87; /* send immediate command */ + + if (ftdi2232_buffer_size > FTDI2232_SAVE_SIZE) + { + ERROR("BUG: ftdi2232_buffer grew beyond %i byte (%i) - this is going to fail", FTDI2232_SAVE_SIZE, ftdi2232_buffer_size); + } + +#ifdef _DEBUG_USB_IO_ + DEBUG("write buffer (size %i):", ftdi2232_buffer_size); + ftdi2232_debug_dump_buffer(); +#endif + + if ((retval = ftdi_write_data(&ftdic, ftdi2232_buffer, ftdi2232_buffer_size)) < 0) + { + ERROR("ftdi_write_data returned %i", retval); + exit(-1); + } + + if (ftdi2232_expect_read) + { + int timeout = 100; + ftdi2232_buffer_size = 0; + + while ((ftdi2232_buffer_size < ftdi2232_expect_read) && timeout) + { + ftdi2232_buffer_size += ftdi_read_data(&ftdic, ftdi2232_buffer + ftdi2232_buffer_size, FTDI2232_BUFFER_SIZE - ftdi2232_buffer_size); + timeout--; + } + + if (ftdi2232_expect_read != ftdi2232_buffer_size) + { + ERROR("ftdi2232_expect_read (%i) != ftdi2232_buffer_size (%i) (%i retries)", ftdi2232_expect_read, ftdi2232_buffer_size, 100 - timeout); + ftdi2232_debug_dump_buffer(); + + exit(-1); + } + +#ifdef _DEBUG_USB_IO_ + DEBUG("read buffer (%i retries): %i bytes", 100 - timeout, ftdi2232_buffer_size); + ftdi2232_debug_dump_buffer(); +#endif + } + + ftdi2232_expect_read = 0; + ftdi2232_read_pointer = 0; + + cmd = first; + while (cmd != last) + { + switch (cmd->type) + { + case JTAG_SCAN: + type = jtag_scan_type(cmd->cmd.scan); + if (type != SCAN_OUT) + { + scan_size = jtag_scan_size(cmd->cmd.scan); + buffer = calloc(CEIL(scan_size, 8), 1); + ftdi2232_read_scan(type, buffer, scan_size); + jtag_read_buffer(buffer, cmd->cmd.scan); + free(buffer); + } + break; + default: + break; + } + cmd = cmd->next; + } + + ftdi2232_buffer_size = 0; + + return ERROR_OK; +} + +void ftdi2232_add_scan(int ir_scan, enum scan_type type, u8 *buffer, int scan_size) +{ + int num_bytes = (scan_size + 7) / 8; + int bits_left = scan_size; + int cur_byte = 0; + int last_bit; + + /* command "Clock Data to TMS/CS Pin (no Read)" */ + BUFFER_ADD = 0x4b; + /* scan 7 bit */ + BUFFER_ADD = 0x6; + /* TMS data bits */ + if (ir_scan) + { + BUFFER_ADD = TAP_MOVE(cur_state, TAP_SI); + cur_state = TAP_SI; + } + else + { + BUFFER_ADD = TAP_MOVE(cur_state, TAP_SD); + cur_state = TAP_SD; + } + //DEBUG("added TMS scan (no read)"); + + /* add command for complete bytes */ + if (num_bytes > 1) + { + if (type == SCAN_IO) + { + /* Clock Data Bytes In and Out LSB First */ + BUFFER_ADD = 0x39; + //DEBUG("added TDI bytes (io %i)", num_bytes); + } + else if (type == SCAN_OUT) + { + /* Clock Data Bytes Out on -ve Clock Edge LSB First (no Read) */ + BUFFER_ADD = 0x19; + //DEBUG("added TDI bytes (o)"); + } + else if (type == SCAN_IN) + { + /* Clock Data Bytes In on +ve Clock Edge LSB First (no Write) */ + BUFFER_ADD = 0x28; + //DEBUG("added TDI bytes (i %i)", num_bytes); + } + BUFFER_ADD = (num_bytes-2) & 0xff; + BUFFER_ADD = ((num_bytes-2) >> 8) & 0xff; + } + if (type != SCAN_IN) + { + /* add complete bytes */ + while(num_bytes-- > 1) + { + BUFFER_ADD = buffer[cur_byte]; + cur_byte++; + bits_left -= 8; + } + } + if (type == SCAN_IN) + { + bits_left -= 8 * (num_bytes - 1); + } + + /* the most signifcant bit is scanned during TAP movement */ + if (type != SCAN_IN) + last_bit = (buffer[cur_byte] >> (bits_left - 1)) & 0x1; + else + last_bit = 0; + + /* process remaining bits but the last one */ + if (bits_left > 1) + { + if (type == SCAN_IO) + { + /* Clock Data Bits In and Out LSB First */ + BUFFER_ADD = 0x3b; + //DEBUG("added TDI bits (io) %i", bits_left - 1); + } + else if (type == SCAN_OUT) + { + /* Clock Data Bits Out on -ve Clock Edge LSB First (no Read) */ + BUFFER_ADD = 0x1b; + //DEBUG("added TDI bits (o)"); + } + else if (type == SCAN_IN) + { + /* Clock Data Bits In on +ve Clock Edge LSB First (no Write) */ + BUFFER_ADD = 0x2a; + //DEBUG("added TDI bits (i %i)", bits_left - 1); + } + BUFFER_ADD = bits_left - 2; + if (type != SCAN_IN) + BUFFER_ADD = buffer[cur_byte]; + } + + /* move from Shift-IR/DR to end state */ + if (type != SCAN_OUT) + { + /* Clock Data to TMS/CS Pin with Read */ + BUFFER_ADD = 0x6b; + //DEBUG("added TMS scan (read)"); + } + else + { + /* Clock Data to TMS/CS Pin (no Read) */ + BUFFER_ADD = 0x4b; + //DEBUG("added TMS scan (no read)"); + } + BUFFER_ADD = 0x6; + BUFFER_ADD = TAP_MOVE(cur_state, end_state) | (last_bit << 7); + cur_state = end_state; + +} + +int ftdi2232_predict_scan_out(int scan_size, enum scan_type type) +{ + int predicted_size = 6; + if (type == SCAN_IN) /* only from device to host */ + { + predicted_size += (CEIL(scan_size, 8) > 1) ? 3 : 0; + predicted_size += ((scan_size - 1) % 8) ? 2 : 0; + } + else /* host to device, or bidirectional */ + { + predicted_size += (CEIL(scan_size, 8) > 1) ? (CEIL(scan_size, 8) + 3 - 1) : 0; + predicted_size += ((scan_size - 1) % 8) ? 3 : 0; + } + + return predicted_size; +} + +int ftdi2232_predict_scan_in(int scan_size, enum scan_type type) +{ + int predicted_size = 0; + + if (type != SCAN_OUT) + { + /* complete bytes */ + predicted_size += (CEIL(scan_size, 8) > 1) ? (CEIL(scan_size, 8) - 1) : 0; + /* remaining bits - 1 */ + predicted_size += ((scan_size - 1) % 8) ? 1 : 0; + /* last bit (from TMS scan) */ + predicted_size += 1; + } + + //DEBUG("scan_size: %i, predicted_size: %i", scan_size, predicted_size); + + return predicted_size; +} + +int ftdi2232_execute_queue() +{ + jtag_command_t *cmd = jtag_command_queue; /* currently processed command */ + jtag_command_t *first_unsent = cmd; /* next command that has to be sent */ + u8 *buffer; + int scan_size; /* size of IR or DR scan */ + enum scan_type type; + int i; + int predicted_size = 0; + int require_send = 0; + + ftdi2232_buffer_size = 0; + ftdi2232_expect_read = 0; + + while (cmd) + { + switch(cmd->type) + { + case JTAG_END_STATE: + if (cmd->cmd.end_state->end_state != -1) + ftdi2232_end_state(cmd->cmd.end_state->end_state); + break; + case JTAG_RESET: + /* only send the maximum buffer size that FT2232C can handle */ + predicted_size = 3; + if (ftdi2232_buffer_size + predicted_size + 1 > FTDI2232_SAVE_SIZE) + { + ftdi2232_send_and_recv(first_unsent, cmd); + require_send = 0; + first_unsent = cmd; + } + + if (cmd->cmd.reset->trst == 1) + { + cur_state = TAP_TLR; + discrete_output &= ~FTDI2232_TRST; + } + else if (cmd->cmd.reset->trst == 0) + { + discrete_output |= FTDI2232_TRST; + } + + if (cmd->cmd.reset->srst == 1) + discrete_output &= ~FTDI2232_SRST; + else if (cmd->cmd.reset->srst == 0) + discrete_output |= FTDI2232_SRST; + /* command "set data bits low byte" */ + BUFFER_ADD = 0x80; + /* value (TMS=1,TCK=0, TDI=0, TRST/SRST */ + BUFFER_ADD = 0x08 | discrete_output; + /* dir (output=1), TCK/TDI/TMS=out, TDO=in, TRST/SRST=out */ + BUFFER_ADD = 0x0b | FTDI2232_SRST | FTDI2232_TRST; + require_send = 1; + break; + case JTAG_RUNTEST: + /* only send the maximum buffer size that FT2232C can handle */ + predicted_size = 0; + if (cur_state != TAP_RTI) + predicted_size += 3; + predicted_size += 3 * CEIL(cmd->cmd.runtest->num_cycles, 7); + if ((cmd->cmd.runtest->end_state != -1) && (cmd->cmd.runtest->end_state != TAP_RTI)) + predicted_size += 3; + if ((cmd->cmd.runtest->end_state == -1) && (end_state != TAP_RTI)) + predicted_size += 3; + if (ftdi2232_buffer_size + predicted_size + 1 > FTDI2232_SAVE_SIZE) + { + ftdi2232_send_and_recv(first_unsent, cmd); + require_send = 0; + first_unsent = cmd; + } + if (cur_state != TAP_RTI) + { + /* command "Clock Data to TMS/CS Pin (no Read)" */ + BUFFER_ADD = 0x4b; + /* scan 7 bit */ + BUFFER_ADD = 0x6; + /* TMS data bits */ + BUFFER_ADD = TAP_MOVE(cur_state, TAP_RTI); + cur_state = TAP_RTI; + require_send = 1; + } + i = cmd->cmd.runtest->num_cycles; + while (i > 0) + { + /* command "Clock Data to TMS/CS Pin (no Read)" */ + BUFFER_ADD = 0x4b; + /* scan 7 bit */ + BUFFER_ADD = (i > 7) ? 6 : (i - 1); + /* TMS data bits */ + BUFFER_ADD = 0x0; + cur_state = TAP_RTI; + i -= (i > 7) ? 7 : i; + //DEBUG("added TMS scan (no read)"); + } + if (cmd->cmd.runtest->end_state != -1) + ftdi2232_end_state(cmd->cmd.runtest->end_state); + if (cur_state != end_state) + { + /* command "Clock Data to TMS/CS Pin (no Read)" */ + BUFFER_ADD = 0x4b; + /* scan 7 bit */ + BUFFER_ADD = 0x6; + /* TMS data bits */ + BUFFER_ADD = TAP_MOVE(cur_state, end_state); + cur_state = end_state; + //DEBUG("added TMS scan (no read)"); + } + require_send = 1; + break; + case JTAG_STATEMOVE: + /* only send the maximum buffer size that FT2232C can handle */ + predicted_size = 3; + if (ftdi2232_buffer_size + predicted_size + 1 > FTDI2232_SAVE_SIZE) + { + ftdi2232_send_and_recv(first_unsent, cmd); + require_send = 0; + first_unsent = cmd; + } + if (cmd->cmd.statemove->end_state != -1) + ftdi2232_end_state(cmd->cmd.statemove->end_state); + /* command "Clock Data to TMS/CS Pin (no Read)" */ + BUFFER_ADD = 0x4b; + /* scan 7 bit */ + BUFFER_ADD = 0x6; + /* TMS data bits */ + BUFFER_ADD = TAP_MOVE(cur_state, end_state); + //DEBUG("added TMS scan (no read)"); + cur_state = end_state; + require_send = 1; + break; + case JTAG_SCAN: + scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer); + type = jtag_scan_type(cmd->cmd.scan); + predicted_size = ftdi2232_predict_scan_out(scan_size, type); + if (ftdi2232_buffer_size + predicted_size + 1 > FTDI2232_SAVE_SIZE) + { + ftdi2232_send_and_recv(first_unsent, cmd); + require_send = 0; + first_unsent = cmd; + } + ftdi2232_expect_read += ftdi2232_predict_scan_in(scan_size, type); + //DEBUG("new read size: %i", ftdi2232_expect_read); + if (cmd->cmd.scan->end_state != -1) + ftdi2232_end_state(cmd->cmd.scan->end_state); + ftdi2232_add_scan(cmd->cmd.scan->ir_scan, type, buffer, scan_size); + require_send = 1; + if (buffer) + free(buffer); + break; + case JTAG_SLEEP: + jtag_sleep(cmd->cmd.sleep->us); + break; + default: + ERROR("BUG: unknown JTAG command type encountered"); + exit(-1); + } + cmd = cmd->next; + } + + if (require_send > 0) + ftdi2232_send_and_recv(first_unsent, cmd); + + return ERROR_OK; +} + +int ftdi2232_init(void) +{ + if (ftdi_init(&ftdic) < 0) + return ERROR_JTAG_INIT_FAILED; + + /* context, vendor id, product id */ + if (ftdi_usb_open(&ftdic, ftdi2232_vid, ftdi2232_pid) < 0) + { + ERROR("unable to open ftdi device: %s", ftdic.error_str); + return ERROR_JTAG_INIT_FAILED; + } + + if (ftdi_usb_reset(&ftdic) < 0) + { + ERROR("unable to reset ftdi device"); + return ERROR_JTAG_INIT_FAILED; + } + + if (ftdi_set_latency_timer(&ftdic, 1) < 0) + { + ERROR("unable to set latency timer"); + return ERROR_JTAG_INIT_FAILED; + } + + ftdi2232_buffer_size = 0; + ftdi2232_buffer = malloc(FTDI2232_BUFFER_SIZE); + + ftdic.bitbang_mode = 0; /* Reset controller */ + ftdi_enable_bitbang(&ftdic, 0x0b | FTDI2232_SRST | FTDI2232_TRST); /* ctx, i/o mask (out=1, in=0) */ + + ftdic.bitbang_mode = 2; /* MPSSE mode */ + ftdi_enable_bitbang(&ftdic, 0x0b | FTDI2232_SRST | FTDI2232_TRST); /* ctx, i/o mask (out=1, in=0) */ + + if (ftdi_usb_purge_buffers(&ftdic) < 0) + { + ERROR("ftdi_purge_buffers: %s", ftdic.error_str); + return ERROR_JTAG_INIT_FAILED; + } + + /* initialize low byte for jtag */ + BUFFER_ADD = 0x80; /* command "set data bits low byte" */ + BUFFER_ADD = 0x08 | FTDI2232_SRST | FTDI2232_TRST; /* value (TMS=1,TCK=0, TDI=0, xRST high) */ + BUFFER_ADD = 0x0b | FTDI2232_SRST | FTDI2232_TRST; /* dir (output=1), TCK/TDI/TMS=out, TDO=in */ + BUFFER_ADD = 0x85; /* command "Disconnect TDI/DO to TDO/DI for Loopback" */ + ftdi2232_debug_dump_buffer(); + if (ftdi_write_data(&ftdic, ftdi2232_buffer, ftdi2232_buffer_size) != 4) + return ERROR_JTAG_INIT_FAILED; + + ftdi2232_speed(jtag_speed); + + return ERROR_OK; +} + +int ftdi2232_quit(void) +{ + ftdi_disable_bitbang(&ftdic); + + ftdi_usb_close(&ftdic); + + ftdi_deinit(&ftdic); + + free(ftdi2232_buffer); + + return ERROR_OK; +} + +int ftdi2232_handle_vid_pid_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc >= 2) + { + ftdi2232_vid = strtol(args[0], NULL, 0); + ftdi2232_pid = strtol(args[1], NULL, 0); + } + else + { + WARNING("incomplete ftdi2232_vid_pid configuration directive"); + } + + return ERROR_OK; +} diff --git a/src/jtag/jtag.c b/src/jtag/jtag.c new file mode 100644 index 00000000..e306b0a2 --- /dev/null +++ b/src/jtag/jtag.c @@ -0,0 +1,1585 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" +#include "jtag.h" + +#include "command.h" +#include "log.h" +#include "interpreter.h" + +#include "stdlib.h" +#include "string.h" +#include <unistd.h> + +char* tap_state_strings[16] = +{ + "tlr", + "sds", "cd", "sd", "e1d", "pd", "e2d", "ud", + "rti", + "sis", "ci", "si", "e1i", "pi", "e2i", "ui" +}; + +typedef struct cmd_queue_page_s +{ + void *address; + size_t used; + struct cmd_queue_page_s *next; +} cmd_queue_page_t; + +#define CMD_QUEUE_PAGE_SIZE (1024 * 1024) +static cmd_queue_page_t *cmd_queue_pages = NULL; + +/* tap_move[i][j]: tap movement command to go from state i to state j + * 0: Test-Logic-Reset + * 1: Run-Test/Idle + * 2: Shift-DR + * 3: Pause-DR + * 4: Shift-IR + * 5: Pause-IR + */ +u8 tap_move[6][6] = +{ +/* TLR RTI SD PD SI PI */ + {0x7f, 0x00, 0x17, 0x0a, 0x1b, 0x16}, /* TLR */ + {0x7f, 0x00, 0x25, 0x05, 0x2b, 0x0b}, /* RTI */ + {0x7f, 0x31, 0x00, 0x01, 0x0f, 0x2f}, /* SD */ + {0x7f, 0x30, 0x20, 0x17, 0x1e, 0x2f}, /* PD */ + {0x7f, 0x31, 0x07, 0x17, 0x00, 0x01}, /* SI */ + {0x7f, 0x30, 0x1c, 0x17, 0x20, 0x2f} /* PI */ +}; + +int tap_move_map[16] = { + 0, -1, -1, 2, -1, 3, -1, -1, + 1, -1, -1, 4, -1, 5, -1, -1 +}; + +tap_transition_t tap_transitions[16] = +{ + {TAP_TLR, TAP_RTI}, /* TLR */ + {TAP_SIS, TAP_CD}, /* SDS */ + {TAP_E1D, TAP_SD}, /* CD */ + {TAP_E1D, TAP_SD}, /* SD */ + {TAP_UD, TAP_PD}, /* E1D */ + {TAP_E2D, TAP_PD}, /* PD */ + {TAP_UD, TAP_SD}, /* E2D */ + {TAP_SDS, TAP_RTI}, /* UD */ + {TAP_SDS, TAP_RTI}, /* RTI */ + {TAP_TLR, TAP_CI}, /* SIS */ + {TAP_E1I, TAP_SI}, /* CI */ + {TAP_E1I, TAP_SI}, /* SI */ + {TAP_UI, TAP_PI}, /* E1I */ + {TAP_E2I, TAP_PI}, /* PI */ + {TAP_UI, TAP_SI}, /* E2I */ + {TAP_SDS, TAP_RTI} /* UI */ +}; + +enum tap_state end_state = TAP_TLR; +enum tap_state cur_state = TAP_TLR; +int jtag_trst = 0; +int jtag_srst = 0; + +jtag_command_t *jtag_command_queue = NULL; +jtag_command_t **last_comand_pointer = &jtag_command_queue; +jtag_device_t *jtag_devices = NULL; +int jtag_num_devices = 0; +int jtag_ir_scan_size = 0; +enum reset_types jtag_reset_config = RESET_NONE; +enum tap_state cmd_queue_end_state = TAP_TLR; +enum tap_state cmd_queue_cur_state = TAP_TLR; + +int jtag_verify_capture_ir = 1; + +/* callbacks to inform high-level handlers about JTAG state changes */ +jtag_event_callback_t *jtag_event_callbacks; + +/* jtag interfaces (parport, FTDI-USB, TI-USB, ...) + */ +#if BUILD_PARPORT == 1 + extern jtag_interface_t parport_interface; +#endif + +#if BUILD_FTDI2232 == 1 + extern jtag_interface_t ftdi2232_interface; +#endif + +#if BUILD_FTD2XX == 1 + extern jtag_interface_t ftd2xx_interface; +#endif + +#if BUILD_AMTJTAGACCEL == 1 + extern jtag_interface_t amt_jtagaccel_interface; +#endif + +#if BUILD_EP93XX == 1 + extern jtag_interface_t ep93xx_interface; +#endif + +jtag_interface_t *jtag_interfaces[] = { +#if BUILD_PARPORT == 1 + &parport_interface, +#endif +#if BUILD_FTDI2232 == 1 + &ftdi2232_interface, +#endif +#if BUILD_FTD2XX == 1 + &ftd2xx_interface, +#endif +#if BUILD_AMTJTAGACCEL == 1 + &amt_jtagaccel_interface, +#endif +#if BUILD_EP93XX == 1 + &ep93xx_interface, +#endif + NULL, +}; + +jtag_interface_t *jtag = NULL; + +/* configuration */ +char* jtag_interface = NULL; +int jtag_speed = -1; + +/* forward declarations */ + +/* jtag commands */ +int handle_interface_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_jtag_speed_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_jtag_device_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_reset_config_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +int handle_scan_chain_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +int handle_endstate_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_jtag_reset_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_runtest_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_statemove_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_irscan_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_drscan_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +int handle_verify_ircapture_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +int jtag_register_event_callback(int (*callback)(enum jtag_event event, void *priv), void *priv) +{ + jtag_event_callback_t **callbacks_p = &jtag_event_callbacks; + + if (callback == NULL) + { + return ERROR_INVALID_ARGUMENTS; + } + + if (*callbacks_p) + { + while ((*callbacks_p)->next) + callbacks_p = &((*callbacks_p)->next); + callbacks_p = &((*callbacks_p)->next); + } + + (*callbacks_p) = malloc(sizeof(jtag_event_callback_t)); + (*callbacks_p)->callback = callback; + (*callbacks_p)->priv = priv; + (*callbacks_p)->next = NULL; + + return ERROR_OK; +} + +int jtag_unregister_event_callback(int (*callback)(enum jtag_event event, void *priv)) +{ + jtag_event_callback_t **callbacks_p = &jtag_event_callbacks; + + if (callback == NULL) + { + return ERROR_INVALID_ARGUMENTS; + } + + while (*callbacks_p) + { + jtag_event_callback_t **next = &((*callbacks_p)->next); + if ((*callbacks_p)->callback == callback) + { + free(*callbacks_p); + *callbacks_p = *next; + } + callbacks_p = next; + } + + return ERROR_OK; +} + +int jtag_call_event_callbacks(enum jtag_event event) +{ + jtag_event_callback_t *callback = jtag_event_callbacks; + + DEBUG("jtag event: %i", event); + + while (callback) + { + callback->callback(event, callback->priv); + callback = callback->next; + } + + return ERROR_OK; +} + +/* returns a pointer to the pointer of the last command in queue + * this may be a pointer to the root pointer (jtag_command_queue) + * or to the next member of the last but one command + */ +jtag_command_t** jtag_get_last_command_p(void) +{ +/* jtag_command_t *cmd = jtag_command_queue; + + if (cmd) + while (cmd->next) + cmd = cmd->next; + else + return &jtag_command_queue; + + return &cmd->next;*/ + + return last_comand_pointer; +} + +/* returns a pointer to the n-th device in the scan chain */ +jtag_device_t* jtag_get_device(int num) +{ + jtag_device_t *device = jtag_devices; + int i = 0; + + while (device) + { + if (num == i) + return device; + device = device->next; + i++; + } + + return NULL; +} + +void* cmd_queue_alloc(size_t size) +{ + cmd_queue_page_t **p_page = &cmd_queue_pages; + int offset; + + if (*p_page) + { + while ((*p_page)->next) + p_page = &((*p_page)->next); + if (CMD_QUEUE_PAGE_SIZE - (*p_page)->used < size) + p_page = &((*p_page)->next); + } + + if (!*p_page) + { + *p_page = malloc(sizeof(cmd_queue_page_t)); + (*p_page)->used = 0; + (*p_page)->address = malloc(CMD_QUEUE_PAGE_SIZE); + (*p_page)->next = NULL; + } + + offset = (*p_page)->used; + (*p_page)->used += size; + + return ((*p_page)->address) + offset; +} + +void cmd_queue_free() +{ + cmd_queue_page_t *page = cmd_queue_pages; + + while (page) + { + cmd_queue_page_t *last = page; + free(page->address); + page = page->next; + free(last); + } + + cmd_queue_pages = NULL; +} + +int jtag_add_ir_scan(int num_fields, scan_field_t *fields, enum tap_state state) +{ + jtag_command_t **last_cmd; + jtag_device_t *device; + int i, j; + int scan_size = 0; + /* int changed = 0; */ + + if (jtag_trst == 1) + { + WARNING("JTAG command queued, while TRST is low (TAP in reset)"); + return ERROR_JTAG_TRST_ASSERTED; + } + + /* + for (i=0; i<num_fields; i++) + { + device = jtag_get_device(fields[i].device); + if (device) + { + if (buf_cmp(device->cur_instr, fields[i].out_value, device->ir_length)) + changed = 1; + } + else + { + ERROR("inexistant device specified for ir scan"); + return ERROR_INVALID_ARGUMENTS; + } + } + + if (!changed) + return ERROR_OK; + */ + + last_cmd = jtag_get_last_command_p(); + + /* allocate memory for a new list member */ + *last_cmd = cmd_queue_alloc(sizeof(jtag_command_t)); + (*last_cmd)->next = NULL; + last_comand_pointer = &((*last_cmd)->next); + (*last_cmd)->type = JTAG_SCAN; + + /* allocate memory for ir scan command */ + (*last_cmd)->cmd.scan = cmd_queue_alloc(sizeof(scan_command_t)); + (*last_cmd)->cmd.scan->ir_scan = 1; + (*last_cmd)->cmd.scan->num_fields = jtag_num_devices; /* one field per device */ + (*last_cmd)->cmd.scan->fields = cmd_queue_alloc(jtag_num_devices * sizeof(scan_field_t)); + (*last_cmd)->cmd.scan->end_state = state; + + if (state != -1) + cmd_queue_end_state = state; + + if (cmd_queue_cur_state == TAP_TLR && cmd_queue_end_state != TAP_TLR) + jtag_call_event_callbacks(JTAG_TRST_RELEASED); + + if (cmd_queue_end_state == TAP_TLR) + jtag_call_event_callbacks(JTAG_TRST_ASSERTED); + + cmd_queue_cur_state = cmd_queue_end_state; + + for (i=0; i < jtag_num_devices; i++) + { + int found = 0; + device = jtag_get_device(i); + scan_size = device->ir_length; + (*last_cmd)->cmd.scan->fields[i].device = i; + (*last_cmd)->cmd.scan->fields[i].num_bits = scan_size; + (*last_cmd)->cmd.scan->fields[i].in_value = NULL; + if (jtag_verify_capture_ir) + { + (*last_cmd)->cmd.scan->fields[i].in_check_value = buf_cpy(device->expected, cmd_queue_alloc(CEIL(scan_size, 8)), scan_size); + (*last_cmd)->cmd.scan->fields[i].in_check_mask = buf_cpy(device->expected_mask, cmd_queue_alloc(CEIL(scan_size, 8)), scan_size); + } + else + { + (*last_cmd)->cmd.scan->fields[i].in_check_value = NULL; + (*last_cmd)->cmd.scan->fields[i].in_check_mask = NULL; + } + (*last_cmd)->cmd.scan->fields[i].in_handler = NULL; + (*last_cmd)->cmd.scan->fields[i].in_handler_priv = NULL; + + /* search the list */ + for (j=0; j < num_fields; j++) + { + if (i == fields[j].device) + { + found = 1; + (*last_cmd)->cmd.scan->fields[i].out_value = buf_cpy(fields[j].out_value, cmd_queue_alloc(CEIL(scan_size, 8)), scan_size); + (*last_cmd)->cmd.scan->fields[i].out_mask = buf_cpy(fields[j].out_mask, cmd_queue_alloc(CEIL(scan_size, 8)), scan_size); + + device->bypass = 0; + break; + } + } + + if (!found) + { + /* if a device isn't listed, set it to BYPASS */ + (*last_cmd)->cmd.scan->fields[i].out_value = buf_set_ones(cmd_queue_alloc(CEIL(scan_size, 8)), scan_size); + (*last_cmd)->cmd.scan->fields[i].out_mask = NULL; + device->bypass = 1; + + } + + /* update device information */ + buf_cpy((*last_cmd)->cmd.scan->fields[i].out_value, jtag_get_device(i)->cur_instr, scan_size); + } + + return ERROR_OK; +} + +int jtag_add_plain_ir_scan(int num_fields, scan_field_t *fields, enum tap_state state) +{ + jtag_command_t **last_cmd; + int i; + + if (jtag_trst == 1) + { + WARNING("JTAG command queued, while TRST is low (TAP in reset)"); + return ERROR_JTAG_TRST_ASSERTED; + } + + last_cmd = jtag_get_last_command_p(); + + /* allocate memory for a new list member */ + *last_cmd = cmd_queue_alloc(sizeof(jtag_command_t)); + (*last_cmd)->next = NULL; + last_comand_pointer = &((*last_cmd)->next); + (*last_cmd)->type = JTAG_SCAN; + + /* allocate memory for ir scan command */ + (*last_cmd)->cmd.scan = cmd_queue_alloc(sizeof(scan_command_t)); + (*last_cmd)->cmd.scan->ir_scan = 1; + (*last_cmd)->cmd.scan->num_fields = num_fields; + (*last_cmd)->cmd.scan->fields = cmd_queue_alloc(num_fields * sizeof(scan_field_t)); + (*last_cmd)->cmd.scan->end_state = state; + + if (state != -1) + cmd_queue_end_state = state; + + if (cmd_queue_cur_state == TAP_TLR && cmd_queue_end_state != TAP_TLR) + jtag_call_event_callbacks(JTAG_TRST_RELEASED); + + if (cmd_queue_end_state == TAP_TLR) + jtag_call_event_callbacks(JTAG_TRST_ASSERTED); + + cmd_queue_cur_state = cmd_queue_end_state; + + for (i = 0; i < num_fields; i++) + { + int num_bits = fields[i].num_bits; + int num_bytes = CEIL(fields[i].num_bits, 8); + (*last_cmd)->cmd.scan->fields[i].device = fields[i].device; + (*last_cmd)->cmd.scan->fields[i].num_bits = num_bits; + (*last_cmd)->cmd.scan->fields[i].out_value = buf_cpy(fields[i].out_value, cmd_queue_alloc(num_bytes), num_bits); + (*last_cmd)->cmd.scan->fields[i].out_mask = buf_cpy(fields[i].out_mask, cmd_queue_alloc(num_bytes), num_bits); + (*last_cmd)->cmd.scan->fields[i].in_value = fields[i].in_value; + (*last_cmd)->cmd.scan->fields[i].in_check_value = buf_cpy(fields[i].in_check_value, cmd_queue_alloc(num_bytes), num_bits); + (*last_cmd)->cmd.scan->fields[i].in_check_mask = buf_cpy(fields[i].in_check_mask, cmd_queue_alloc(num_bytes), num_bits); + (*last_cmd)->cmd.scan->fields[i].in_handler = NULL; + (*last_cmd)->cmd.scan->fields[i].in_handler_priv = NULL; + } + return ERROR_OK; +} + +int jtag_add_dr_scan(int num_fields, scan_field_t *fields, enum tap_state state) +{ + int i, j; + int bypass_devices = 0; + int field_count = 0; + jtag_command_t **last_cmd = jtag_get_last_command_p(); + jtag_device_t *device = jtag_devices; + int scan_size; + + if (jtag_trst == 1) + { + WARNING("JTAG command queued, while TRST is low (TAP in reset)"); + return ERROR_JTAG_TRST_ASSERTED; + } + + /* count devices in bypass */ + while (device) + { + if (device->bypass) + bypass_devices++; + device = device->next; + } + + /* allocate memory for a new list member */ + *last_cmd = cmd_queue_alloc(sizeof(jtag_command_t)); + last_comand_pointer = &((*last_cmd)->next); + (*last_cmd)->next = NULL; + (*last_cmd)->type = JTAG_SCAN; + + /* allocate memory for dr scan command */ + (*last_cmd)->cmd.scan = cmd_queue_alloc(sizeof(scan_command_t)); + (*last_cmd)->cmd.scan->ir_scan = 0; + (*last_cmd)->cmd.scan->num_fields = num_fields + bypass_devices; + (*last_cmd)->cmd.scan->fields = cmd_queue_alloc((num_fields + bypass_devices) * sizeof(scan_field_t)); + (*last_cmd)->cmd.scan->end_state = state; + + if (state != -1) + cmd_queue_end_state = state; + + if (cmd_queue_cur_state == TAP_TLR && cmd_queue_end_state != TAP_TLR) + jtag_call_event_callbacks(JTAG_TRST_RELEASED); + + if (cmd_queue_end_state == TAP_TLR) + jtag_call_event_callbacks(JTAG_TRST_ASSERTED); + + cmd_queue_cur_state = cmd_queue_end_state; + + for (i=0; i < jtag_num_devices; i++) + { + int found = 0; + (*last_cmd)->cmd.scan->fields[field_count].device = i; + + for (j=0; j < num_fields; j++) + { + if (i == fields[j].device) + { + found = 1; + scan_size = fields[j].num_bits; + (*last_cmd)->cmd.scan->fields[field_count].num_bits = scan_size; + (*last_cmd)->cmd.scan->fields[field_count].out_value = buf_cpy(fields[j].out_value, cmd_queue_alloc(CEIL(scan_size, 8)), scan_size); + (*last_cmd)->cmd.scan->fields[field_count].out_mask = buf_cpy(fields[j].out_mask, cmd_queue_alloc(CEIL(scan_size, 8)), scan_size); + (*last_cmd)->cmd.scan->fields[field_count].in_value = fields[j].in_value; + (*last_cmd)->cmd.scan->fields[field_count].in_check_value = buf_cpy(fields[j].in_check_value, cmd_queue_alloc(CEIL(scan_size, 8)), scan_size); + (*last_cmd)->cmd.scan->fields[field_count].in_check_mask = buf_cpy(fields[j].in_check_mask, cmd_queue_alloc(CEIL(scan_size, 8)), scan_size); + (*last_cmd)->cmd.scan->fields[field_count].in_handler = fields[j].in_handler; + (*last_cmd)->cmd.scan->fields[field_count++].in_handler_priv = fields[j].in_handler_priv; + } + } + if (!found) + { + /* if a device isn't listed, the BYPASS register should be selected */ + if (!jtag_get_device(i)->bypass) + { + ERROR("BUG: no scan data for a device not in BYPASS"); + exit(-1); + } + + /* program the scan field to 1 bit length, and ignore it's value */ + (*last_cmd)->cmd.scan->fields[field_count].num_bits = 1; + (*last_cmd)->cmd.scan->fields[field_count].out_value = NULL; + (*last_cmd)->cmd.scan->fields[field_count].out_mask = NULL; + (*last_cmd)->cmd.scan->fields[field_count].in_value = NULL; + (*last_cmd)->cmd.scan->fields[field_count].in_check_value = NULL; + (*last_cmd)->cmd.scan->fields[field_count].in_check_mask = NULL; + (*last_cmd)->cmd.scan->fields[field_count].in_handler = NULL; + (*last_cmd)->cmd.scan->fields[field_count++].in_handler_priv = NULL; + } + else + { + /* if a device is listed, the BYPASS register must not be selected */ + if (jtag_get_device(i)->bypass) + { + ERROR("BUG: scan data for a device in BYPASS"); + exit(-1); + } + } + } + return ERROR_OK; +} + +int jtag_add_plain_dr_scan(int num_fields, scan_field_t *fields, enum tap_state state) +{ + int i; + jtag_command_t **last_cmd = jtag_get_last_command_p(); + + if (jtag_trst == 1) + { + WARNING("JTAG command queued, while TRST is low (TAP in reset)"); + return ERROR_JTAG_TRST_ASSERTED; + } + + /* allocate memory for a new list member */ + *last_cmd = cmd_queue_alloc(sizeof(jtag_command_t)); + last_comand_pointer = &((*last_cmd)->next); + (*last_cmd)->next = NULL; + (*last_cmd)->type = JTAG_SCAN; + + /* allocate memory for scan command */ + (*last_cmd)->cmd.scan = cmd_queue_alloc(sizeof(scan_command_t)); + (*last_cmd)->cmd.scan->ir_scan = 0; + (*last_cmd)->cmd.scan->num_fields = num_fields; + (*last_cmd)->cmd.scan->fields = cmd_queue_alloc(num_fields * sizeof(scan_field_t)); + (*last_cmd)->cmd.scan->end_state = state; + + if (state != -1) + cmd_queue_end_state = state; + + if (cmd_queue_cur_state == TAP_TLR && cmd_queue_end_state != TAP_TLR) + jtag_call_event_callbacks(JTAG_TRST_RELEASED); + + if (cmd_queue_end_state == TAP_TLR) + jtag_call_event_callbacks(JTAG_TRST_ASSERTED); + + cmd_queue_cur_state = cmd_queue_end_state; + + for (i = 0; i < num_fields; i++) + { + int num_bits = fields[i].num_bits; + int num_bytes = CEIL(fields[i].num_bits, 8); + (*last_cmd)->cmd.scan->fields[i].device = fields[i].device; + (*last_cmd)->cmd.scan->fields[i].num_bits = num_bits; + (*last_cmd)->cmd.scan->fields[i].out_value = buf_cpy(fields[i].out_value, cmd_queue_alloc(num_bytes), num_bits); + (*last_cmd)->cmd.scan->fields[i].out_mask = buf_cpy(fields[i].out_mask, cmd_queue_alloc(num_bytes), num_bits); + (*last_cmd)->cmd.scan->fields[i].in_value = fields[i].in_value; + (*last_cmd)->cmd.scan->fields[i].in_check_value = buf_cpy(fields[i].in_check_value, cmd_queue_alloc(num_bytes), num_bits); + (*last_cmd)->cmd.scan->fields[i].in_check_mask = buf_cpy(fields[i].in_check_mask, cmd_queue_alloc(num_bytes), num_bits); + (*last_cmd)->cmd.scan->fields[i].in_handler = fields[i].in_handler; + (*last_cmd)->cmd.scan->fields[i].in_handler_priv = fields[i].in_handler_priv; + } + + return ERROR_OK; +} +int jtag_add_statemove(enum tap_state state) +{ + jtag_command_t **last_cmd = jtag_get_last_command_p(); + + if (jtag_trst == 1) + { + WARNING("JTAG command queued, while TRST is low (TAP in reset)"); + return ERROR_JTAG_TRST_ASSERTED; + } + + /* allocate memory for a new list member */ + *last_cmd = cmd_queue_alloc(sizeof(jtag_command_t)); + last_comand_pointer = &((*last_cmd)->next); + (*last_cmd)->next = NULL; + (*last_cmd)->type = JTAG_STATEMOVE; + + (*last_cmd)->cmd.statemove = cmd_queue_alloc(sizeof(statemove_command_t)); + (*last_cmd)->cmd.statemove->end_state = state; + + if (state != -1) + cmd_queue_end_state = state; + + if (cmd_queue_cur_state == TAP_TLR && cmd_queue_end_state != TAP_TLR) + jtag_call_event_callbacks(JTAG_TRST_RELEASED); + + if (cmd_queue_end_state == TAP_TLR) + jtag_call_event_callbacks(JTAG_TRST_ASSERTED); + + cmd_queue_cur_state = cmd_queue_end_state; + + return ERROR_OK; +} + +int jtag_add_pathmove(int num_states, enum tap_state *path) +{ + jtag_command_t **last_cmd = jtag_get_last_command_p(); + int i; + + if (jtag_trst == 1) + { + WARNING("JTAG command queued, while TRST is low (TAP in reset)"); + return ERROR_JTAG_TRST_ASSERTED; + } + + /* the last state has to be a stable state */ + if (tap_move_map[path[num_states - 1]] == -1) + { + ERROR("TAP path doesn't finish in a stable state"); + return ERROR_JTAG_NOT_IMPLEMENTED; + } + + if (jtag->support_statemove) + { + /* allocate memory for a new list member */ + *last_cmd = cmd_queue_alloc(sizeof(jtag_command_t)); + last_comand_pointer = &((*last_cmd)->next); + (*last_cmd)->next = NULL; + (*last_cmd)->type = JTAG_RUNTEST; + + (*last_cmd)->cmd.pathmove = cmd_queue_alloc(sizeof(pathmove_command_t)); + (*last_cmd)->cmd.pathmove->num_states = num_states; + (*last_cmd)->cmd.pathmove->path = cmd_queue_alloc(sizeof(enum tap_state) * num_states); + + for (i = 0; i < num_states; i++) + (*last_cmd)->cmd.pathmove->path[i] = path[i]; + } + else + { + /* validate the desired path, and see if it fits a default path */ + int begin = 0; + int end = 0; + int j; + + for (i = 0; i < num_states; i++) + { + for (j = i; j < num_states; j++) + { + if (tap_move_map[path[j]] != -1) + { + end = j; + break; + } + } + + if (begin - end <= 7) /* a default path spans no more than 7 states */ + { + jtag_add_statemove(path[end]); + } + else + { + ERROR("encountered a TAP path that can't be fulfilled by default paths"); + return ERROR_JTAG_NOT_IMPLEMENTED; + } + + i = end; + } + } + + if (cmd_queue_cur_state == TAP_TLR && cmd_queue_end_state != TAP_TLR) + jtag_call_event_callbacks(JTAG_TRST_RELEASED); + + if (cmd_queue_end_state == TAP_TLR) + jtag_call_event_callbacks(JTAG_TRST_ASSERTED); + + cmd_queue_cur_state = path[num_states - 1]; + + return ERROR_OK; +} + +int jtag_add_runtest(int num_cycles, enum tap_state state) +{ + jtag_command_t **last_cmd = jtag_get_last_command_p(); + + if (jtag_trst == 1) + { + WARNING("JTAG command queued, while TRST is low (TAP in reset)"); + return ERROR_JTAG_TRST_ASSERTED; + } + + /* allocate memory for a new list member */ + *last_cmd = cmd_queue_alloc(sizeof(jtag_command_t)); + (*last_cmd)->next = NULL; + last_comand_pointer = &((*last_cmd)->next); + (*last_cmd)->type = JTAG_RUNTEST; + + (*last_cmd)->cmd.runtest = cmd_queue_alloc(sizeof(runtest_command_t)); + (*last_cmd)->cmd.runtest->num_cycles = num_cycles; + (*last_cmd)->cmd.runtest->end_state = state; + + if (state != -1) + cmd_queue_end_state = state; + + if (cmd_queue_cur_state == TAP_TLR && cmd_queue_end_state != TAP_TLR) + jtag_call_event_callbacks(JTAG_TRST_RELEASED); + + if (cmd_queue_end_state == TAP_TLR) + jtag_call_event_callbacks(JTAG_TRST_ASSERTED); + + cmd_queue_cur_state = cmd_queue_end_state; + + return ERROR_OK; +} + +int jtag_add_reset(int req_trst, int req_srst) +{ + int trst_with_tms = 0; + + jtag_command_t **last_cmd = jtag_get_last_command_p(); + + if (req_trst == -1) + req_trst = jtag_trst; + + if (req_srst == -1) + req_srst = jtag_srst; + + /* Make sure that jtag_reset_config allows the requested reset */ + /* if SRST pulls TRST, we can't fulfill srst == 1 with trst == 0 */ + if (((jtag_reset_config & RESET_SRST_PULLS_TRST) && (req_srst == 1)) && (req_trst == 0)) + return ERROR_JTAG_RESET_WOULD_ASSERT_TRST; + + /* if TRST pulls SRST, we reset with TAP T-L-R */ + if (((jtag_reset_config & RESET_TRST_PULLS_SRST) && (req_trst == 1)) && (req_srst == 0)) + { + req_trst = 0; + trst_with_tms = 1; + } + + if (req_srst && !(jtag_reset_config & RESET_HAS_SRST)) + return ERROR_JTAG_RESET_CANT_SRST; + + if (req_trst && !(jtag_reset_config & RESET_HAS_TRST)) + { + req_trst = 0; + trst_with_tms = 1; + } + + /* allocate memory for a new list member */ + *last_cmd = cmd_queue_alloc(sizeof(jtag_command_t)); + (*last_cmd)->next = NULL; + last_comand_pointer = &((*last_cmd)->next); + (*last_cmd)->type = JTAG_RESET; + + (*last_cmd)->cmd.reset = cmd_queue_alloc(sizeof(reset_command_t)); + (*last_cmd)->cmd.reset->trst = req_trst; + (*last_cmd)->cmd.reset->srst = req_srst; + + jtag_trst = req_trst; + jtag_srst = req_srst; + + if (jtag_srst) + jtag_call_event_callbacks(JTAG_SRST_ASSERTED); + else + jtag_call_event_callbacks(JTAG_SRST_RELEASED); + + if (trst_with_tms) + { + last_cmd = &((*last_cmd)->next); + + /* allocate memory for a new list member */ + *last_cmd = cmd_queue_alloc(sizeof(jtag_command_t)); + (*last_cmd)->next = NULL; + last_comand_pointer = &((*last_cmd)->next); + (*last_cmd)->type = JTAG_STATEMOVE; + + (*last_cmd)->cmd.statemove = cmd_queue_alloc(sizeof(statemove_command_t)); + (*last_cmd)->cmd.statemove->end_state = TAP_TLR; + + jtag_call_event_callbacks(JTAG_TRST_ASSERTED); + cmd_queue_cur_state = TAP_TLR; + cmd_queue_end_state = TAP_TLR; + + return ERROR_OK; + } + else + { + if (jtag_trst) + { + cmd_queue_cur_state = TAP_TLR; + jtag_call_event_callbacks(JTAG_TRST_ASSERTED); + } + } + + return ERROR_OK; +} + +int jtag_add_end_state(enum tap_state state) +{ + jtag_command_t **last_cmd = jtag_get_last_command_p(); + + /* allocate memory for a new list member */ + *last_cmd = cmd_queue_alloc(sizeof(jtag_command_t)); + (*last_cmd)->next = NULL; + last_comand_pointer = &((*last_cmd)->next); + (*last_cmd)->type = JTAG_END_STATE; + + (*last_cmd)->cmd.end_state = cmd_queue_alloc(sizeof(end_state_command_t)); + (*last_cmd)->cmd.end_state->end_state = state; + + if (state != -1) + cmd_queue_end_state = state; + + return ERROR_OK; +} + +int jtag_add_sleep(u32 us) +{ + jtag_command_t **last_cmd = jtag_get_last_command_p(); + + /* allocate memory for a new list member */ + *last_cmd = cmd_queue_alloc(sizeof(jtag_command_t)); + (*last_cmd)->next = NULL; + last_comand_pointer = &((*last_cmd)->next); + (*last_cmd)->type = JTAG_SLEEP; + + (*last_cmd)->cmd.sleep = cmd_queue_alloc(sizeof(sleep_command_t)); + (*last_cmd)->cmd.sleep->us = us; + + return ERROR_OK; +} + +int jtag_scan_size(scan_command_t *cmd) +{ + int bit_count = 0; + int i; + + /* count bits in scan command */ + for (i=0; i<cmd->num_fields; i++) + { + bit_count += cmd->fields[i].num_bits; + } + + return bit_count; +} + +int jtag_build_buffer(scan_command_t *cmd, u8 **buffer) +{ + int bit_count = 0; + int i; + + bit_count = jtag_scan_size(cmd); + *buffer = malloc(CEIL(bit_count, 8)); + + bit_count = 0; + + for (i = 0; i < cmd->num_fields; i++) + { + if (cmd->fields[i].out_value) + { + char* char_buf = buf_to_char(cmd->fields[i].out_value, cmd->fields[i].num_bits); + buf_set_buf(cmd->fields[i].out_value, 0, *buffer, bit_count, cmd->fields[i].num_bits); +#ifdef _DEBUG_JTAG_IO_ + DEBUG("fields[%i].out_value: %s", i, char_buf); +#endif + free(char_buf); + } + + bit_count += cmd->fields[i].num_bits; + } + + return bit_count; + +} + +int jtag_read_buffer(u8 *buffer, scan_command_t *cmd) +{ + int i; + int bit_count = 0; + int retval = ERROR_OK; + + for (i=0; i < cmd->num_fields; i++) + { + /* if neither in_value nor in_check_value are specified we don't have to examine this field */ + if (cmd->fields[i].in_value || cmd->fields[i].in_check_value) + { + int num_bits = cmd->fields[i].num_bits; + + if (cmd->fields[i].in_value) + { + char *char_buf; + buf_set_buf(buffer, bit_count, cmd->fields[i].in_value, 0, num_bits); + char_buf = buf_to_char(cmd->fields[i].in_value, num_bits); +#ifdef _DEBUG_JTAG_IO_ + DEBUG("fields[%i].in_value: %s", i, char_buf); +#endif + free(char_buf); + if (cmd->fields[i].in_handler) + { + if (cmd->fields[i].in_handler(cmd->fields[i].in_value, cmd->fields[i].in_handler_priv) != ERROR_OK) + { + /* TODO: error reporting */ + WARNING("in_handler reported a failed check"); + retval = ERROR_JTAG_QUEUE_FAILED; + } + } + } + + if (cmd->fields[i].in_check_value) + { + u8 *captured = buf_set_buf(buffer, bit_count, malloc(CEIL(num_bits, 8)), 0, num_bits); + if ((cmd->fields[i].in_check_mask && buf_cmp_mask(captured, cmd->fields[i].in_check_value, cmd->fields[i].in_check_mask, num_bits)) + || (!cmd->fields[i].in_check_mask && buf_cmp(captured, cmd->fields[i].in_check_mask, num_bits))) + { + char *captured_char = buf_to_char(captured, num_bits); + char *in_check_value_char = buf_to_char(cmd->fields[i].in_check_value, num_bits); + char *in_check_mask_char = buf_to_char(cmd->fields[i].in_check_mask, num_bits); + /* TODO: error reporting */ + WARNING("value captured during scan didn't pass the requested check: captured: %s check_value: %s check_mask: %s", captured_char, in_check_value_char, in_check_mask_char); + retval = ERROR_JTAG_QUEUE_FAILED; + free(captured_char); + free(in_check_value_char); + free(in_check_mask_char); + } + free(captured); + } + } + bit_count += cmd->fields[i].num_bits; + } + + return retval; +} + +enum scan_type jtag_scan_type(scan_command_t *cmd) +{ + int i; + int type = 0; + + for (i=0; i < cmd->num_fields; i++) + { + if (cmd->fields[i].in_check_value || cmd->fields[i].in_value) + type |= SCAN_IN; + if (cmd->fields[i].out_value) + type |= SCAN_OUT; + } + + return type; +} + +int jtag_execute_queue(void) +{ + int retval; + + retval = jtag->execute_queue(); + + cmd_queue_free(); + + jtag_command_queue = NULL; + last_comand_pointer = &jtag_command_queue; + + return retval; +} + +int jtag_cancel_queue(void) +{ + cmd_queue_free(); + jtag_command_queue = NULL; + last_comand_pointer = &jtag_command_queue; + + return ERROR_OK; +} + +int jtag_reset_callback(enum jtag_event event, void *priv) +{ + jtag_device_t *device = priv; + + DEBUG(""); + + if (event == JTAG_TRST_ASSERTED) + { + buf_set_ones(device->cur_instr, device->ir_length); + device->bypass = 1; + } + + return ERROR_OK; +} + +void jtag_sleep(u32 us) +{ + usleep(us); +} + +int jtag_validate_chain() +{ + jtag_device_t *device = jtag_devices; + int total_ir_length = 0; + u8 *ir_test = NULL; + scan_field_t field; + int chain_pos = 0; + + while (device) + { + total_ir_length += device->ir_length; + device = device->next; + } + + total_ir_length += 2; + ir_test = malloc(CEIL(total_ir_length, 8)); + buf_set_ones(ir_test, total_ir_length); + + field.device = 0; + field.num_bits = total_ir_length; + field.out_value = ir_test; + field.out_mask = NULL; + field.in_value = ir_test; + field.in_check_value = NULL; + field.in_check_mask = NULL; + field.in_handler = NULL; + field.in_handler_priv = NULL; + + jtag_add_plain_ir_scan(1, &field, TAP_TLR); + jtag_execute_queue(); + + device = jtag_devices; + while (device) + { + if (buf_get_u32(ir_test, chain_pos, 2) != 0x1) + { + ERROR("Error validating JTAG scan chain, IR mismatch"); + exit(-1); + } + chain_pos += device->ir_length; + device = device->next; + } + + if (buf_get_u32(ir_test, chain_pos, 2) != 0x3) + { + ERROR("Error validating JTAG scan chain, IR mismatch"); + exit(-1); + } + + free(ir_test); + + return ERROR_OK; +} + +int jtag_register_commands(struct command_context_s *cmd_ctx) +{ + register_command(cmd_ctx, NULL, "interface", handle_interface_command, + COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "jtag_speed", handle_jtag_speed_command, + COMMAND_ANY, "set jtag speed (if supported) <speed>"); + register_command(cmd_ctx, NULL, "jtag_device", handle_jtag_device_command, + COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "reset_config", handle_reset_config_command, + COMMAND_CONFIG, NULL); + + register_command(cmd_ctx, NULL, "scan_chain", handle_scan_chain_command, + COMMAND_EXEC, "print current scan chain configuration"); + + register_command(cmd_ctx, NULL, "endstate", handle_endstate_command, + COMMAND_EXEC, "finish JTAG operations in <tap_state>"); + register_command(cmd_ctx, NULL, "jtag_reset", handle_jtag_reset_command, + COMMAND_EXEC, "toggle reset lines <trst> <srst>"); + register_command(cmd_ctx, NULL, "runtest", handle_runtest_command, + COMMAND_EXEC, "move to Run-Test/Idle, and execute <num_cycles>"); + register_command(cmd_ctx, NULL, "statemove", handle_statemove_command, + COMMAND_EXEC, "move to current endstate or [tap_state]"); + register_command(cmd_ctx, NULL, "irscan", handle_irscan_command, + COMMAND_EXEC, "execute IR scan <device> <instr> [dev2] [instr2] ..."); + register_command(cmd_ctx, NULL, "drscan", handle_drscan_command, + COMMAND_EXEC, "execute DR scan <device> <var> [dev2] [var2] ..."); + + register_command(cmd_ctx, NULL, "verify_ircapture", handle_verify_ircapture_command, + COMMAND_ANY, "verify value captured during Capture-IR <enable|disable>"); + return ERROR_OK; +} + +int jtag_init(struct command_context_s *cmd_ctx) +{ + int i; + + DEBUG(""); + + if (jtag_speed == -1) + jtag_speed = 0; + + if (jtag_interface && (jtag_interface[0] != 0)) + /* configuration var 'jtag_interface' is set, and not empty */ + for (i = 0; jtag_interfaces[i]; i++) + { + if (strcmp(jtag_interface, jtag_interfaces[i]->name) == 0) + { + jtag_device_t *device; + device = jtag_devices; + + if (jtag_interfaces[i]->init() != ERROR_OK) + return ERROR_JTAG_INIT_FAILED; + jtag = jtag_interfaces[i]; + + jtag_ir_scan_size = 0; + jtag_num_devices = 0; + while (device != NULL) + { + jtag_ir_scan_size += device->ir_length; + jtag_num_devices++; + device = device->next; + } + + jtag_add_statemove(TAP_TLR); + jtag_execute_queue(); + + jtag_validate_chain(); + + return ERROR_OK; + } + } + + /* no valid interface was found (i.e. the configuration option, + * didn't match one of the compiled-in interfaces + */ + ERROR("No valid jtag interface found (%s)", jtag_interface); + jtag = NULL; + return ERROR_JTAG_INVALID_INTERFACE; +} + +int handle_interface_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int i; + + /* only if the configuration var isn't overwritten from cmdline */ + if (!jtag_interface) + { + if (args[0] && (args[0][0] != 0)) + { + for (i=0; jtag_interfaces[i]; i++) + { + if (strcmp(args[0], jtag_interfaces[i]->name) == 0) + { + if (jtag_interfaces[i]->register_commands(cmd_ctx) != ERROR_OK) + exit(-1); + + jtag_interface = jtag_interfaces[i]->name; + + return ERROR_OK; + } + } + } + + /* remember the requested interface name, so we can complain about it later */ + jtag_interface = strdup(args[0]); + DEBUG("'interface' command didn't specify a valid interface"); + } + + return ERROR_OK; +} + +int handle_jtag_device_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + jtag_device_t **last_device_p = &jtag_devices; + + if (*last_device_p) + { + while ((*last_device_p)->next) + last_device_p = &((*last_device_p)->next); + last_device_p = &((*last_device_p)->next); + } + + if (argc < 3) + return ERROR_OK; + + *last_device_p = malloc(sizeof(jtag_device_t)); + (*last_device_p)->ir_length = strtoul(args[0], NULL, 0); + + (*last_device_p)->expected = malloc((*last_device_p)->ir_length); + buf_set_u32((*last_device_p)->expected, 0, (*last_device_p)->ir_length, strtoul(args[1], NULL, 0)); + (*last_device_p)->expected_mask = malloc((*last_device_p)->ir_length); + buf_set_u32((*last_device_p)->expected_mask, 0, (*last_device_p)->ir_length, strtoul(args[2], NULL, 0)); + + (*last_device_p)->cur_instr = malloc((*last_device_p)->ir_length); + (*last_device_p)->bypass = 1; + buf_set_ones((*last_device_p)->cur_instr, (*last_device_p)->ir_length); + + (*last_device_p)->next = NULL; + + jtag_register_event_callback(jtag_reset_callback, (*last_device_p)); + + jtag_num_devices++; + + return ERROR_OK; +} + +int handle_scan_chain_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + jtag_device_t *device = jtag_devices; + int device_count = 0; + + while (device) + { + u32 expected, expected_mask, cur_instr; + expected = buf_get_u32(device->expected, 0, device->ir_length); + expected_mask = buf_get_u32(device->expected_mask, 0, device->ir_length); + cur_instr = buf_get_u32(device->cur_instr, 0, device->ir_length); + command_print(cmd_ctx, "%i: idcode: 0x%8.8x ir length %i, ir capture 0x%x, ir mask 0x%x, current instruction 0x%x", device_count, device->idcode, device->ir_length, expected, expected_mask, cur_instr); + device = device->next; + device_count++; + } + + return ERROR_OK; +} + +int handle_reset_config_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc >= 1) + { + if (strcmp(args[0], "none") == 0) + jtag_reset_config = RESET_NONE; + else if (strcmp(args[0], "trst_only") == 0) + jtag_reset_config = RESET_HAS_TRST; + else if (strcmp(args[0], "srst_only") == 0) + jtag_reset_config = RESET_HAS_SRST; + else if (strcmp(args[0], "trst_and_srst") == 0) + jtag_reset_config = RESET_TRST_AND_SRST; + else + { + ERROR("invalid reset_config argument"); + exit(-1); + } + } + + if (argc >= 2) + { + if (strcmp(args[1], "srst_pulls_trst") == 0) + jtag_reset_config |= RESET_SRST_PULLS_TRST; + else if (strcmp(args[1], "trst_pulls_srst") == 0) + jtag_reset_config |= RESET_TRST_PULLS_SRST; + else if (strcmp(args[1], "combined") == 0) + jtag_reset_config |= RESET_SRST_PULLS_TRST | RESET_TRST_PULLS_SRST; + else if (strcmp(args[1], "separate") == 0) + jtag_reset_config &= ~(RESET_SRST_PULLS_TRST | RESET_TRST_PULLS_SRST); + else + { + ERROR("invalid reset_config argument"); + exit(-1); + } + } + + if (argc >= 3) + { + if (strcmp(args[2], "trst_open_drain") == 0) + jtag_reset_config |= RESET_TRST_OPEN_DRAIN; + else if (strcmp(args[2], "trst_push_pull") == 0) + jtag_reset_config &= ~RESET_TRST_OPEN_DRAIN; + else + { + ERROR("invalid reset_config argument"); + exit(-1); + } + } + + if (argc >= 4) + { + if (strcmp(args[3], "srst_push_pull") == 0) + jtag_reset_config |= RESET_SRST_PUSH_PULL; + else if (strcmp(args[3], "srst_open_drain") == 0) + jtag_reset_config &= ~RESET_SRST_PUSH_PULL; + else + { + ERROR("invalid reset_config argument"); + exit(-1); + } + } + + return ERROR_OK; +} + +int handle_jtag_speed_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 0) + command_print(cmd_ctx, "jtag_speed: %i", jtag_speed); + + if (argc > 0) + { + /* this command can be called during CONFIG, + * in which case jtag isn't initialized */ + if (jtag) + jtag->speed(strtoul(args[0], NULL, 0)); + else + jtag_speed = strtoul(args[0], NULL, 0); + } + + return ERROR_OK; +} + +int handle_endstate_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + enum tap_state state; + + if (argc < 1) + { + command_print(cmd_ctx, "usage: endstate <tap_state>"); + return ERROR_OK; + } + + for (state = 0; state < 16; state++) + { + if (strcmp(args[0], tap_state_strings[state]) == 0) + { + jtag_add_end_state(state); + jtag_execute_queue(); + } + } + + return ERROR_OK; +} + +int handle_jtag_reset_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int trst = -1; + int srst = -1; + char *usage = "usage: jtag_reset <trst> <srst>"; + int retval; + + if (argc < 1) + { + command_print(cmd_ctx, usage); + return ERROR_OK; + } + + if (args[0][0] == '1') + trst = 1; + else if (args[0][0] == '0') + trst = 0; + else + { + command_print(cmd_ctx, usage); + return ERROR_OK; + } + + if (args[1][0] == '1') + srst = 1; + else if (args[1][0] == '0') + srst = 0; + else + { + command_print(cmd_ctx, usage); + return ERROR_OK; + } + + if ((retval = jtag_add_reset(trst, srst)) != ERROR_OK) + { + switch (retval) + { + case ERROR_JTAG_RESET_WOULD_ASSERT_TRST: + command_print(cmd_ctx, "requested reset would assert trst\nif this is acceptable, use jtag_reset 1 %c", args[1][0]); + break; + case ERROR_JTAG_RESET_CANT_SRST: + command_print(cmd_ctx, "can't assert srst because the current reset_config doesn't support it"); + break; + default: + command_print(cmd_ctx, "unknown error"); + } + } + jtag_execute_queue(); + + return ERROR_OK; +} + +int handle_runtest_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc < 1) + { + command_print(cmd_ctx, "usage: runtest <num_cycles>"); + return ERROR_OK; + } + + jtag_add_runtest(strtol(args[0], NULL, 0), -1); + jtag_execute_queue(); + + return ERROR_OK; + +} + +int handle_statemove_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + enum tap_state state; + + state = -1; + if (argc == 1) + { + for (state = 0; state < 16; state++) + { + if (strcmp(args[0], tap_state_strings[state]) == 0) + { + break; + } + } + } + + jtag_add_statemove(state); + jtag_execute_queue(); + + return ERROR_OK; + +} + +int handle_irscan_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int i; + scan_field_t *fields; + + if ((argc < 2) || (argc % 2)) + { + command_print(cmd_ctx, "usage: irscan <device> <instr> [dev2] [instr2] ..."); + return ERROR_OK; + } + + fields = malloc(sizeof(scan_field_t) * argc / 2); + + for (i = 0; i < argc / 2; i++) + { + int device = strtoul(args[i*2], NULL, 0); + int field_size = jtag_get_device(device)->ir_length; + fields[i].device = device; + fields[i].out_value = malloc(CEIL(field_size, 8)); + buf_set_u32(fields[i].out_value, 0, field_size, strtoul(args[i*2+1], NULL, 0)); + fields[i].out_mask = NULL; + fields[i].in_value = NULL; + fields[i].in_check_mask = NULL; + fields[i].in_handler = NULL; + fields[i].in_handler_priv = NULL; + } + + jtag_add_ir_scan(argc / 2, fields, -1); + jtag_execute_queue(); + + for (i = 0; i < argc / 2; i++) + free(fields[i].out_value); + + free (fields); + + return ERROR_OK; +} + +int handle_drscan_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + scan_field_t *fields; + int num_fields = 0; + int field_count = 0; + var_t *var; + int i, j; + + if ((argc < 2) || (argc % 2)) + { + command_print(cmd_ctx, "usage: drscan <device> <var> [dev2] [var2]"); + return ERROR_OK; + } + + for (i = 0; i < argc; i+=2) + { + var = get_var_by_namenum(args[i+1]); + if (var) + { + num_fields += var->num_fields; + } + else + { + command_print(cmd_ctx, "variable %s doesn't exist", args[i+1]); + return ERROR_OK; + } + } + + fields = malloc(sizeof(scan_field_t) * num_fields); + + for (i = 0; i < argc; i+=2) + { + var = get_var_by_namenum(args[i+1]); + + for (j = 0; j < var->num_fields; j++) + { + fields[field_count].device = strtol(args[i], NULL, 0); + fields[field_count].num_bits = var->fields[j].num_bits; + fields[field_count].out_value = malloc(CEIL(var->fields[j].num_bits, 8)); + buf_set_u32(fields[field_count].out_value, 0, var->fields[j].num_bits, var->fields[j].value); + fields[field_count].out_mask = NULL; + fields[field_count].in_value = fields[field_count].out_value; + fields[field_count].in_check_mask = NULL; + fields[field_count].in_check_value = NULL; + fields[field_count].in_handler = field_le_to_host; + fields[field_count++].in_handler_priv = &(var->fields[j]); + } + } + + jtag_add_dr_scan(num_fields, fields, -1); + jtag_execute_queue(); + + for (i = 0; i < argc / 2; i++) + free(fields[i].out_value); + + free(fields); + + return ERROR_OK; +} + +int handle_verify_ircapture_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 0) + { + command_print(cmd_ctx, "verify Capture-IR is %s", (jtag_verify_capture_ir) ? "enabled": "disabled"); + return ERROR_OK; + } + + if (strcmp(args[0], "enable") == 0) + { + jtag_verify_capture_ir = 1; + } + else if (strcmp(args[0], "disable") == 0) + { + jtag_verify_capture_ir = 0; + } + + return ERROR_OK; +} diff --git a/src/jtag/jtag.h b/src/jtag/jtag.h new file mode 100644 index 00000000..124150ce --- /dev/null +++ b/src/jtag/jtag.h @@ -0,0 +1,270 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef JTAG_H +#define JTAG_H + +#include "types.h" +#include "binarybuffer.h" + +#include "command.h" + +#if 0 +#define _DEBUG_JTAG_IO_ +#endif + +/* Tap States + * TLR - Test-Logic-Reset, RTI - Run-Test/Idle, + * SDS - Select-DR-Scan, CD - Capture-DR, SD - Shift-DR, E1D - Exit1-DR, + * PD - Pause-DR, E2D - Exit2-DR, UD - Update-DR, + * SIS - Select-IR-Scan, CI - Capture-IR, SI - Shift-IR, E1I - Exit1-IR, + * PI - Pause-IR, E2I - Exit2-IR, UI - Update-IR + */ +enum tap_state +{ + TAP_TLR = 0x0, TAP_RTI = 0x8, + TAP_SDS = 0x1, TAP_CD = 0x2, TAP_SD = 0x3, TAP_E1D = 0x4, + TAP_PD = 0x5, TAP_E2D = 0x6, TAP_UD = 0x7, + TAP_SIS = 0x9, TAP_CI = 0xa, TAP_SI = 0xb, TAP_E1I = 0xc, + TAP_PI = 0xd, TAP_E2I = 0xe, TAP_UI = 0xf +}; + +typedef struct tap_transition_s +{ + enum tap_state high; + enum tap_state low; +} tap_transition_t; + +extern char* tap_state_strings[16]; +extern int tap_move_map[16]; /* map 16 TAP states to 6 stable states */ +extern u8 tap_move[6][6]; /* value scanned to TMS to move from one of six stable states to another */ +extern tap_transition_t tap_transitions[16]; /* describe the TAP state diagram */ + +extern enum tap_state end_state; /* finish DR scans in dr_end_state */ +extern enum tap_state cur_state; /* current TAP state */ + +#define TAP_MOVE(from, to) tap_move[tap_move_map[from]][tap_move_map[to]] + +typedef struct scan_field_s +{ + int device; /* ordinal device number this instruction refers to */ + int num_bits; /* number of bits this field specifies (up to 32) */ + u8 *out_value; /* value to be scanned into the device */ + u8 *out_mask; /* only masked bits care */ + u8 *in_value; /* pointer to a 32-bit memory location to take data scanned out */ + u8 *in_check_value; /* used to validate scan results */ + u8 *in_check_mask; /* check specified bits against check_value */ + int (*in_handler)(u8 *in_value, void *priv); /* process received buffer using this handler */ + void *in_handler_priv; /* additional information for the in_handler */ +} scan_field_t; + +enum scan_type +{ + /* IN: from device to host, OUT: from host to device */ + SCAN_IN = 1, SCAN_OUT = 2, SCAN_IO = 3 +}; + +typedef struct scan_command_s +{ + int ir_scan; /* instruction/not data scan */ + int num_fields; /* number of fields in *fields array */ + scan_field_t *fields; /* pointer to an array of data scan fields */ + enum tap_state end_state; /* TAP state in which JTAG commands should finish */ +} scan_command_t; + +typedef struct statemove_command_s +{ + enum tap_state end_state; /* TAP state in which JTAG commands should finish */ +} statemove_command_t; + +typedef struct pathmove_command_s +{ + int num_states; /* number of states in *path */ + enum tap_state *path; /* states that have to be passed */ +} pathmove_command_t; + +typedef struct runtest_command_s +{ + int num_cycles; /* number of cycles that should be spent in Run-Test/Idle */ + enum tap_state end_state; /* TAP state in which JTAG commands should finish */ +} runtest_command_t; + +typedef struct reset_command_s +{ + int trst; /* trst/srst 0: deassert, 1: assert, -1: don't change */ + int srst; +} reset_command_t; + +typedef struct end_state_command_s +{ + enum tap_state end_state; /* TAP state in which JTAG commands should finish */ +} end_state_command_t; + +typedef struct sleep_command_s +{ + u32 us; /* number of microseconds to sleep */ +} sleep_command_t; + +typedef union jtag_command_container_u +{ + scan_command_t *scan; + statemove_command_t *statemove; + pathmove_command_t *pathmove; + runtest_command_t *runtest; + reset_command_t *reset; + end_state_command_t *end_state; + sleep_command_t *sleep; +} jtag_command_container_t; + +enum jtag_command_type +{ + JTAG_SCAN = 1, + JTAG_STATEMOVE = 2, JTAG_RUNTEST = 3, + JTAG_RESET = 4, JTAG_END_STATE = 5, + JTAG_PATHMOVE = 6, JTAG_SLEEP = 7 +}; + +typedef struct jtag_command_s +{ + jtag_command_container_t cmd; + enum jtag_command_type type; + struct jtag_command_s *next; +} jtag_command_t; + +extern jtag_command_t *jtag_command_queue; + +typedef struct jtag_device_s +{ + int ir_length; /* size of instruction register */ + u8 *expected; /* Capture-IR expected value */ + u8 *expected_mask; /* Capture-IR expected mask */ + u32 idcode; /* device identification code */ + u8 *cur_instr; /* current instruction */ + int bypass; /* bypass register selected */ + struct jtag_device_s *next; +} jtag_device_t; + +extern jtag_device_t *jtag_devices; +extern int jtag_num_devices; +extern int jtag_ir_scan_size; + +enum reset_line_mode +{ + LINE_OPEN_DRAIN = 0x0, + LINE_PUSH_PULL = 0x1, +}; + +typedef struct jtag_interface_s +{ + char* name; + + /* queued command execution + */ + int (*execute_queue)(void); + + /* optional command support + */ + int support_statemove; + + /* interface initalization + */ + int (*speed)(int speed); + int (*register_commands)(struct command_context_s *cmd_ctx); + int (*init)(void); + int (*quit)(void); + +} jtag_interface_t; + +enum jtag_event +{ + JTAG_SRST_ASSERTED, + JTAG_TRST_ASSERTED, + JTAG_SRST_RELEASED, + JTAG_TRST_RELEASED, +}; + +typedef struct jtag_event_callback_s +{ + int (*callback)(enum jtag_event event, void *priv); + void *priv; + struct jtag_event_callback_s *next; +} jtag_event_callback_t; + +extern jtag_event_callback_t *jtag_event_callbacks; + +extern jtag_interface_t *jtag; /* global pointer to configured JTAG interface */ +extern enum tap_state end_state; +extern enum tap_state cur_state; + +extern char* jtag_interface; +extern int jtag_speed; + +enum reset_types +{ + RESET_NONE = 0x0, + RESET_HAS_TRST = 0x1, + RESET_HAS_SRST = 0x2, + RESET_TRST_AND_SRST = 0x3, + RESET_SRST_PULLS_TRST = 0x4, + RESET_TRST_PULLS_SRST = 0x8, + RESET_TRST_OPEN_DRAIN = 0x10, + RESET_SRST_PUSH_PULL = 0x20, +}; + +extern enum reset_types jtag_reset_config; + +/* JTAG subsystem */ +extern int jtag_init(struct command_context_s *cmd_ctx); +extern int jtag_register_commands(struct command_context_s *cmd_ctx); + +/* JTAG interface */ +extern int jtag_add_ir_scan(int num_fields, scan_field_t *fields, enum tap_state endstate); +extern int jtag_add_dr_scan(int num_fields, scan_field_t *fields, enum tap_state endstate); +extern int jtag_add_plain_ir_scan(int num_fields, scan_field_t *fields, enum tap_state endstate); +extern int jtag_add_plain_dr_scan(int num_fields, scan_field_t *fields, enum tap_state endstate); +extern int jtag_add_statemove(enum tap_state endstate); +extern int jtag_add_pathmove(int num_states, enum tap_state *path); +extern int jtag_add_runtest(int num_cycles, enum tap_state endstate); +extern int jtag_add_reset(int trst, int srst); +extern int jtag_add_end_state(enum tap_state endstate); +extern int jtag_add_sleep(u32 us); +extern int jtag_execute_queue(void); +extern int jtag_cancel_queue(void); + +/* JTAG support functions */ +extern enum scan_type jtag_scan_type(scan_command_t *cmd); +extern int jtag_scan_size(scan_command_t *cmd); +extern int jtag_read_buffer(u8 *buffer, scan_command_t *cmd); +extern int jtag_build_buffer(scan_command_t *cmd, u8 **buffer); +extern jtag_device_t* jtag_get_device(int num); +extern void jtag_sleep(u32 us); +extern int jtag_call_event_callbacks(enum jtag_event event); +extern int jtag_register_event_callback(int (*callback)(enum jtag_event event, void *priv), void *priv); + +/* error codes + * JTAG subsystem uses codes between -100 and -199 */ + +#define ERROR_JTAG_INIT_FAILED (-100) +#define ERROR_JTAG_INVALID_INTERFACE (-101) +#define ERROR_JTAG_NOT_IMPLEMENTED (-102) +#define ERROR_JTAG_TRST_ASSERTED (-103) +#define ERROR_JTAG_QUEUE_FAILED (-104) +#define ERROR_JTAG_RESET_WOULD_ASSERT_TRST (-105) +#define ERROR_JTAG_RESET_CANT_SRST (-106) +#endif /* JTAG_H */ diff --git a/src/jtag/parport.c b/src/jtag/parport.c new file mode 100644 index 00000000..8265ada8 --- /dev/null +++ b/src/jtag/parport.c @@ -0,0 +1,351 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" +#include "log.h" +#include "jtag.h" +#include "bitbang.h" + +/* system includes */ +// -ino: 060521-1036 +#ifdef __FreeBSD__ +#include <sys/types.h> +#include <machine/sysarch.h> +#include <machine/cpufunc.h> +#define ioperm(startport,length,enable)\ + i386_set_ioperm((startport), (length), (enable)) +#else +#include <sys/io.h> +#endif + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#if PARPORT_USE_PPDEV == 1 +#include <linux/parport.h> +#include <linux/ppdev.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#endif + +/* parallel port cable description + */ +typedef struct cable_s +{ + char* name; + u8 TDO_MASK; /* status port bit containing current TDO value */ + u8 TRST_MASK; /* data port bit for TRST */ + u8 TMS_MASK; /* data port bit for TMS */ + u8 TCK_MASK; /* data port bit for TCK */ + u8 TDI_MASK; /* data port bit for TDI */ + u8 SRST_MASK; /* data port bit for SRST */ + u8 OUTPUT_INVERT; /* data port bits that should be inverted */ + u8 INPUT_INVERT; /* status port that should be inverted */ + u8 PORT_INIT; /* initialize data port with this value */ +} cable_t; + +cable_t cables[] = +{ + /* name tdo trst tms tck tdi srst o_inv i_inv init */ + { "wiggler", 0x80, 0x10, 0x02, 0x04, 0x08, 0x01, 0x01, 0x80, 0x80 }, + { "old_amt_wiggler", 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x11, 0x80, 0x80 }, + { "chameleon", 0x80, 0x00, 0x04, 0x01, 0x02, 0x00, 0x00, 0x80, 0x00 }, + { "dlc5", 0x10, 0x00, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x10 }, + { "triton", 0x80, 0x08, 0x04, 0x01, 0x02, 0x00, 0x00, 0x80, 0x00 }, + { NULL, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } +}; + +/* configuration */ +char* parport_cable; +unsigned long parport_port; + +/* interface variables + */ +static cable_t* cable; +static u8 dataport_value = 0x0; + +#if PARPORT_USE_PPDEV == 1 +static int device_handle; +#else +static unsigned long dataport; +static unsigned long statusport; +#endif + +/* low level command set + */ +int parport_read(void); +void parport_write(int tck, int tms, int tdi); +void parport_reset(int trst, int srst); + +int parport_speed(int speed); +int parport_register_commands(struct command_context_s *cmd_ctx); +int parport_init(void); +int parport_quit(void); + +/* interface commands */ +int parport_handle_parport_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int parport_handle_parport_cable_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +jtag_interface_t parport_interface = +{ + .name = "parport", + + .execute_queue = bitbang_execute_queue, + + .support_statemove = 0, + + .speed = parport_speed, + .register_commands = parport_register_commands, + .init = parport_init, + .quit = parport_quit, +}; + +bitbang_interface_t parport_bitbang = +{ + .read = parport_read, + .write = parport_write, + .reset = parport_reset +}; + +int parport_read(void) +{ + int data = 0; + +#if PARPORT_USE_PPDEV == 1 + ioctl(device_handle, PPRSTATUS, & data); +#else + data = inb(statusport); +#endif + + if ((data ^ cable->INPUT_INVERT) & cable->TDO_MASK) + return 1; + else + return 0; +} + +void parport_write(int tck, int tms, int tdi) +{ + u8 output; + int i = jtag_speed + 1; + + if (tck) + dataport_value |= cable->TCK_MASK; + else + dataport_value &= ~cable->TCK_MASK; + + if (tms) + dataport_value |= cable->TMS_MASK; + else + dataport_value &= ~cable->TMS_MASK; + + if (tdi) + dataport_value |= cable->TDI_MASK; + else + dataport_value &= ~cable->TDI_MASK; + + output = dataport_value ^ cable->OUTPUT_INVERT; + + while (i-- > 0) +#if PARPORT_USE_PPDEV == 1 + ioctl(device_handle, PPWDATA, &output); +#else +#ifdef __FreeBSD__ + outb(dataport, output); +#else + outb(output, dataport); +#endif +#endif +} + +/* (1) assert or (0) deassert reset lines */ +void parport_reset(int trst, int srst) +{ + u8 output; + DEBUG("trst: %i, srst: %i", trst, srst); + + if (trst == 0) + dataport_value |= cable->TRST_MASK; + else if (trst == 1) + dataport_value &= ~cable->TRST_MASK; + + if (srst == 0) + dataport_value |= cable->SRST_MASK; + else if (srst == 1) + dataport_value &= ~cable->SRST_MASK; + + output = dataport_value ^ cable->OUTPUT_INVERT; + +#if PARPORT_USE_PPDEV == 1 + ioctl(device_handle, PPWDATA, &output); +#else +#ifdef __FreeBSD__ + outb(dataport, output); +#else + outb(output, dataport); +#endif +#endif + +} + +int parport_speed(int speed) +{ + jtag_speed = speed; + + return ERROR_OK; +} + +int parport_register_commands(struct command_context_s *cmd_ctx) +{ + register_command(cmd_ctx, NULL, "parport_port", parport_handle_parport_port_command, + COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "parport_cable", parport_handle_parport_cable_command, + COMMAND_CONFIG, NULL); + + return ERROR_OK; +} + +int parport_init(void) +{ + cable_t *cur_cable; +#if PARPORT_USE_PPDEV == 1 + char buffer[256]; + int i = 0; +#endif + + cur_cable = cables; + + if ((parport_cable == NULL) || (parport_cable[0] == 0)) + { + parport_cable = "wiggler"; + WARNING("No parport cable specified, using default 'wiggler'"); + } + + while (cur_cable->name) + { + if (strcmp(cur_cable->name, parport_cable) == 0) + { + cable = cur_cable; + break; + } + cur_cable++; + } + + if (!cable) + { + ERROR("No matching cable found for %s", parport_cable); + return ERROR_JTAG_INIT_FAILED; + } + + dataport_value = cable->PORT_INIT; + +#if PARPORT_USE_PPDEV == 1 + if (device_handle>0) + { + ERROR("device is already opened"); + return ERROR_JTAG_INIT_FAILED; + } + + snprintf(buffer, 256, "/dev/parport%d", parport_port); + device_handle = open(buffer, O_WRONLY); + + if (device_handle<0) + { + ERROR("cannot open device. check it exists and that user read and write rights are set"); + return ERROR_JTAG_INIT_FAILED; + } + + i=ioctl(device_handle, PPCLAIM); + if (i<0) + { + ERROR("cannot claim device"); + return ERROR_JTAG_INIT_FAILED; + } + + i = PARPORT_MODE_COMPAT; + i= ioctl(device_handle, PPSETMODE, & i); + if (i<0) + { + ERROR(" cannot set compatible mode to device"); + return ERROR_JTAG_INIT_FAILED; + } + + i = IEEE1284_MODE_COMPAT; + i = ioctl(device_handle, PPNEGOT, & i); + if (i<0) + { + ERROR("cannot set compatible 1284 mode to device"); + return ERROR_JTAG_INIT_FAILED; + } +#else + if (parport_port == 0) + { + parport_port = 0x378; + WARNING("No parport port specified, using default '0x378' (LPT1)"); + } + + dataport = parport_port; + statusport = parport_port + 1; + + if (ioperm(dataport, 3, 1) != 0) { + ERROR("missing privileges for direct i/o"); + return ERROR_JTAG_INIT_FAILED; + } +#endif + + parport_reset(0, 0); + parport_write(0, 0, 0); + + bitbang_interface = &parport_bitbang; + + return ERROR_OK; +} + +int parport_quit(void) +{ + + return ERROR_OK; +} + +int parport_handle_parport_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 0) + return ERROR_OK; + + /* only if the port wasn't overwritten by cmdline */ + if (parport_port == 0) + parport_port = strtoul(args[0], NULL, 0); + + return ERROR_OK; +} + +int parport_handle_parport_cable_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 0) + return ERROR_OK; + + /* only if the cable name wasn't overwritten by cmdline */ + if (parport_cable == 0) + { + parport_cable = malloc(strlen(args[0]) + sizeof(char)); + strcpy(parport_cable, args[0]); + } + + return ERROR_OK; +} diff --git a/src/openocd.c b/src/openocd.c new file mode 100644 index 00000000..71b111a0 --- /dev/null +++ b/src/openocd.c @@ -0,0 +1,113 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "log.h" +#include "types.h" +#include "jtag.h" +#include "configuration.h" +#include "interpreter.h" +#include "xsvf.h" +#include "target.h" +#include "flash.h" + +#include "command.h" +#include "server.h" +#include "telnet_server.h" +#include "gdb_server.h" + +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/poll.h> +#include <strings.h> +#include <netinet/in.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +int main(int argc, char *argv[]) +{ + /* initialize commandline interface */ + command_context_t *cmd_ctx, *cfg_cmd_ctx; + cmd_ctx = command_init(); + + /* register subsystem commands */ + server_register_commands(cmd_ctx); + telnet_register_commands(cmd_ctx); + gdb_register_commands(cmd_ctx); + log_register_commands(cmd_ctx); + jtag_register_commands(cmd_ctx); + interpreter_register_commands(cmd_ctx); + xsvf_register_commands(cmd_ctx); + target_register_commands(cmd_ctx); + flash_register_commands(cmd_ctx); + + if (log_init(cmd_ctx) != ERROR_OK) + return EXIT_FAILURE; + DEBUG("log init complete"); + + INFO("Open On-Chip Debugger (Revision 63)"); + + cfg_cmd_ctx = copy_command_context(cmd_ctx); + cfg_cmd_ctx->mode = COMMAND_CONFIG; + command_set_output_handler(cfg_cmd_ctx, configuration_output_handler, NULL); + + if (parse_cmdline_args(cfg_cmd_ctx, argc, argv) != ERROR_OK) + return EXIT_FAILURE; + + if (parse_config_file(cfg_cmd_ctx) != ERROR_OK) + return EXIT_FAILURE; + + command_done(cfg_cmd_ctx); + + if (jtag_init(cmd_ctx) != ERROR_OK) + return EXIT_FAILURE; + DEBUG("jtag init complete"); + + if (target_init(cmd_ctx) != ERROR_OK) + return EXIT_FAILURE; + DEBUG("target init complete"); + + if (flash_init(cmd_ctx) != ERROR_OK) + return EXIT_FAILURE; + DEBUG("flash init complete"); + + /* initialize tcp server */ + server_init(); + + /* initialize telnet subsystem */ + telnet_init("Open On-Chip Debugger"); + gdb_init(); + + /* handle network connections */ + server_loop(cmd_ctx); + + /* free commandline interface */ + command_done(cmd_ctx); + + return EXIT_SUCCESS; +} diff --git a/src/server/Makefile.am b/src/server/Makefile.am new file mode 100644 index 00000000..2397a7f0 --- /dev/null +++ b/src/server/Makefile.am @@ -0,0 +1,5 @@ +INCLUDES = -I$(top_srcdir)/src/helper -I$(top_srcdir)/src/target $(all_includes) +METASOURCES = AUTO +noinst_LIBRARIES = libserver.a +noinst_HEADERS = server.h telnet_server.h gdb_server.h +libserver_a_SOURCES = server.c telnet_server.c gdb_server.c diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c new file mode 100644 index 00000000..125206dc --- /dev/null +++ b/src/server/gdb_server.c @@ -0,0 +1,1108 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "gdb_server.h" + +#include "server.h" +#include "log.h" +#include "binarybuffer.h" +#include "breakpoints.h" + +#define __USE_GNU +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + +// -ino: 060521-1116 +#ifdef __FreeBSD__ +#include <stdio.h> +char * strndup(char * str, int n) { + unsigned char * tmp = malloc((size_t)n+1); + if (! tmp) perror("gdb_server malloc failed"); + if (strlcpy(tmp, str, n) > n) perror("gdb_server strndup: too long"); + return tmp; +} +#endif +#if 0 +#define _DEBUG_GDB_IO_ +#endif + +static unsigned short gdb_port; + +int gdb_last_signal(target_t *target) +{ + switch (target->debug_reason) + { + case DBG_REASON_DBGRQ: + return 0x2; /* SIGINT */ + case DBG_REASON_BREAKPOINT: + case DBG_REASON_WATCHPOINT: + case DBG_REASON_WPTANDBKPT: + return 0x05; /* SIGTRAP */ + case DBG_REASON_SINGLESTEP: + return 0x05; /* SIGTRAP */ + case DBG_REASON_NOTHALTED: + return 0x0; /* no signal... shouldn't happen */ + default: + ERROR("BUG: undefined debug reason"); + exit(-1); + } +} + +int gdb_get_char(connection_t *connection, int* next_char) +{ + gdb_connection_t *gdb_con = connection->priv; + char *debug_buffer; + + if (gdb_con->buf_cnt-- > 0) + { + *next_char = *(gdb_con->buf_p++); + if (gdb_con->buf_cnt > 0) + connection->input_pending = 1; + else + connection->input_pending = 0; + +#ifdef _DEBUG_GDB_IO_ + DEBUG("returned char '%c' (0x%2.2x)", *next_char, *next_char); +#endif + + return ERROR_OK; + } + + while ((gdb_con->buf_cnt = read(connection->fd, gdb_con->buffer, GDB_BUFFER_SIZE)) <= 0) + { + if (gdb_con->buf_cnt == 0) + return ERROR_SERVER_REMOTE_CLOSED; + + switch(errno) + { + case EAGAIN: + usleep(1000); + break; + case ECONNABORTED: + return ERROR_SERVER_REMOTE_CLOSED; + case ECONNRESET: + return ERROR_SERVER_REMOTE_CLOSED; + default: + ERROR("read: %s", strerror(errno)); + exit(-1); + } + } + + debug_buffer = malloc(gdb_con->buf_cnt + 1); + memcpy(debug_buffer, gdb_con->buffer, gdb_con->buf_cnt); + debug_buffer[gdb_con->buf_cnt] = 0; + DEBUG("received '%s'", debug_buffer); + free(debug_buffer); + + gdb_con->buf_p = gdb_con->buffer; + gdb_con->buf_cnt--; + *next_char = *(gdb_con->buf_p++); + if (gdb_con->buf_cnt > 0) + connection->input_pending = 1; + else + connection->input_pending = 0; +#ifdef _DEBUG_GDB_IO_ + DEBUG("returned char '%c' (0x%2.2x)", *next_char, *next_char); +#endif + + return ERROR_OK; +} + +int gdb_put_packet(connection_t *connection, char *buffer, int len) +{ + int i; + unsigned char my_checksum = 0; + char checksum[3]; + char *debug_buffer; + int reply; + int retval; + gdb_connection_t *gdb_con = connection->priv; + + for (i = 0; i < len; i++) + my_checksum += buffer[i]; + + while (1) + { + + debug_buffer = malloc(len + 1); + memcpy(debug_buffer, buffer, len); + debug_buffer[len] = 0; + DEBUG("sending packet '$%s#%2.2x'", debug_buffer, my_checksum); + free(debug_buffer); + + write(connection->fd, "$", 1); + if (len > 0) + write(connection->fd, buffer, len); + write(connection->fd, "#", 1); + + snprintf(checksum, 3, "%2.2x", my_checksum); + + write(connection->fd, checksum, 2); + + if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK) + return retval; + + if (reply == '+') + break; + else if (reply == '-') + WARNING("negative reply, retrying"); + else if (reply == 0x3) + { + gdb_con->ctrl_c = 1; + if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK) + return retval; + if (reply == '+') + break; + else if (reply == '-') + WARNING("negative reply, retrying"); + else + { + ERROR("unknown character 0x%2.2x in reply, dropping connection", reply); + return ERROR_SERVER_REMOTE_CLOSED; + } + } + else + { + ERROR("unknown character 0x%2.2x in reply, dropping connection", reply); + return ERROR_SERVER_REMOTE_CLOSED; + } + } + + return ERROR_OK; +} + +int gdb_get_packet(connection_t *connection, char *buffer, int *len) +{ + int character; + int count = 0; + int retval; + int first_char = 0; + int packet_type; + char checksum[3]; + unsigned char my_checksum = 0; + gdb_connection_t *gdb_con = connection->priv; + + while (1) + { + do + { + if ((retval = gdb_get_char(connection, &character)) != ERROR_OK) + return retval; + + switch (character) + { + case '$': + break; + case '+': + WARNING("acknowledgment received, but no packet pending"); + break; + case '-': + WARNING("negative acknowledgment, but no packet pending"); + break; + case 0x3: + gdb_con->ctrl_c = 1; + *len = 0; + return ERROR_OK; + default: + WARNING("ignoring character 0x%x", character); + break; + } + } while (character != '$'); + + my_checksum = 0; + + do + { + if ((retval = gdb_get_char(connection, &character)) != ERROR_OK) + return retval; + + if( !first_char ) { + packet_type = character; + first_char = 1; + } + + if( packet_type == 'X' ) + { + switch (character) + { + case '#': + break; + case 0x7d: + /* data transmitted in binary mode (X packet) + * uses 0x7d as escape character */ + my_checksum += character & 0xff; + gdb_get_char(connection, &character); + my_checksum += character & 0xff; + buffer[count++] = (character ^ 0x20) & 0xff; + if (count > *len) + { + ERROR("packet buffer too small"); + return ERROR_GDB_BUFFER_TOO_SMALL; + } + break; + default: + buffer[count++] = character & 0xff; + my_checksum += character & 0xff; + if (count > *len) + { + ERROR("packet buffer too small"); + return ERROR_GDB_BUFFER_TOO_SMALL; + } + break; + } + } + else + { + switch (character) + { + case '#': + break; + case 0x3: + gdb_con->ctrl_c = 1; + break; + default: + buffer[count++] = character & 0xff; + my_checksum += character & 0xff; + if (count > *len) + { + ERROR("packet buffer too small"); + return ERROR_GDB_BUFFER_TOO_SMALL; + } + break; + } + } + } while (character != '#'); + + *len = count; + + if ((retval = gdb_get_char(connection, &character)) != ERROR_OK) + return retval; + checksum[0] = character; + if ((retval = gdb_get_char(connection, &character)) != ERROR_OK) + return retval; + checksum[1] = character; + checksum[2] = 0; + + if (my_checksum == strtoul(checksum, NULL, 16)) + { + write (connection->fd, "+", 1); + break; + } + + WARNING("checksum error, requesting retransmission"); + write(connection->fd, "-", 1); + } + + return ERROR_OK; +} + +int gdb_output(struct command_context_s *context, char* line) +{ + connection_t *connection = context->output_handler_priv; + char *hex_buffer; + int i, bin_size; + + bin_size = strlen(line); + + hex_buffer = malloc(bin_size*2 + 4); + + hex_buffer[0] = 'O'; + for (i=0; i<bin_size; i++) + snprintf(hex_buffer + 1 + i*2, 3, "%2.2x", line[i]); + hex_buffer[bin_size*2+1] = '0'; + hex_buffer[bin_size*2+2] = 'a'; + hex_buffer[bin_size*2+3] = 0x0; + + gdb_put_packet(connection, hex_buffer, bin_size*2 + 3); + + free(hex_buffer); + return ERROR_OK; +} + +int gdb_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv) +{ + connection_t *connection = priv; + gdb_connection_t *gdb_connection = connection->priv; + char sig_reply[4]; + int signal; + + switch (event) + { + case TARGET_EVENT_HALTED: + if (gdb_connection->frontend_state == TARGET_RUNNING) + { + if (gdb_connection->ctrl_c) + { + signal = 0x2; + gdb_connection->ctrl_c = 0; + } + else + { + signal = gdb_last_signal(target); + } + + snprintf(sig_reply, 4, "T%2.2x", signal); + gdb_put_packet(connection, sig_reply, 3); + gdb_connection->frontend_state = TARGET_HALTED; + } + break; + case TARGET_EVENT_RESUMED: + if (gdb_connection->frontend_state == TARGET_HALTED) + { + gdb_connection->frontend_state = TARGET_RUNNING; + } + break; + default: + break; + } + + return ERROR_OK; +} + +int gdb_new_connection(connection_t *connection) +{ + gdb_connection_t *gdb_connection = malloc(sizeof(gdb_connection_t)); + gdb_service_t *gdb_service = connection->service->priv; + int retval; + int initial_ack; + + connection->priv = gdb_connection; + + /* initialize gdb connection information */ + gdb_connection->buf_p = gdb_connection->buffer; + gdb_connection->buf_cnt = 0; + gdb_connection->ctrl_c = 0; + gdb_connection->frontend_state = TARGET_HALTED; + + /* output goes through gdb connection */ + command_set_output_handler(connection->cmd_ctx, gdb_output, connection); + + /* register callback to be informed about target events */ + target_register_event_callback(gdb_target_callback_event_handler, connection); + + /* a gdb session just attached, put the target in halt mode */ + if (((retval = gdb_service->target->type->halt(gdb_service->target)) != ERROR_OK) && + (retval != ERROR_TARGET_ALREADY_HALTED)) + { + ERROR("error when trying to halt target"); + exit(-1); + } + + while (gdb_service->target->state != TARGET_HALTED) + { + gdb_service->target->type->poll(gdb_service->target); + } + + /* remove the initial ACK from the incoming buffer */ + if ((retval = gdb_get_char(connection, &initial_ack)) != ERROR_OK) + return retval; + + return ERROR_OK; +} + +int gdb_connection_closed(connection_t *connection) +{ + if (connection->priv) + free(connection->priv); + else + ERROR("BUG: connection->priv == NULL"); + + target_unregister_event_callback(gdb_target_callback_event_handler, connection); + + return ERROR_OK; +} + +int gdb_last_signal_packet(connection_t *connection, target_t *target, char* packet, int packet_size) +{ + char sig_reply[4]; + int signal; + + signal = gdb_last_signal(target); + + snprintf(sig_reply, 4, "S%2.2x", signal); + gdb_put_packet(connection, sig_reply, 3); + + return ERROR_OK; +} + +void gdb_get_registers_packet(connection_t *connection, target_t *target, char* packet, int packet_size) +{ + reg_t **reg_list; + int reg_list_size; + int retval; + int reg_packet_size = 0; + char *reg_packet; + char *reg_packet_p; + int i; + + DEBUG(""); + + if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_NOT_HALTED: + ERROR("gdb requested registers, but we're not halted"); + exit(-1); + default: + ERROR("BUG: unexpected error returned by get_gdb_reg_list()"); + exit(-1); + } + } + + for (i = 0; i < reg_list_size; i++) + { + reg_packet_size += reg_list[i]->size; + } + + reg_packet = malloc(CEIL(reg_packet_size, 8) * 2); + reg_packet_p = reg_packet; + + for (i = 0; i < reg_list_size; i++) + { + int j; + char *hex_buf = buf_to_char(reg_list[i]->value, reg_list[i]->size); + DEBUG("hex_buf: %s", hex_buf); + for (j = CEIL(reg_list[i]->size, 8) * 2; j > 0; j -= 2) + { + *reg_packet_p++ = hex_buf[j - 2]; + *reg_packet_p++ = hex_buf[j - 1]; + } + free(hex_buf); + } + + reg_packet_p = strndup(reg_packet, CEIL(reg_packet_size, 8) * 2); + DEBUG("reg_packet: %s", reg_packet_p); + free(reg_packet_p); + + gdb_put_packet(connection, reg_packet, CEIL(reg_packet_size, 8) * 2); + free(reg_packet); + +} + +void gdb_set_registers_packet(connection_t *connection, target_t *target, char *packet, int packet_size) +{ + int i; + reg_t **reg_list; + int reg_list_size; + int retval; + char *packet_p; + + DEBUG(""); + + /* skip command character */ + packet++; + packet_size--; + + if (packet_size % 2) + { + WARNING("GDB set_registers packet with uneven characters received"); + return; + } + + if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_NOT_HALTED: + ERROR("gdb requested registers, but we're not halted"); + exit(-1); + default: + ERROR("BUG: unexpected error returned by get_gdb_reg_list()"); + exit(-1); + } + } + + packet_p = packet; + for (i = 0; i < reg_list_size; i++) + { + char_to_buf(packet, CEIL(reg_list[i]->size, 8) * 2, reg_list[i]->value, reg_list[i]->size); + reg_list[i]->dirty = 1; + } + + gdb_put_packet(connection, "OK", 2); +} + +void gdb_get_register_packet(connection_t *connection, target_t *target, char *packet, int packet_size) +{ + char *hex_buf; + char *reg_packet; + char *reg_packet_p; + int reg_num = strtoul(packet + 1, NULL, 16); + reg_t **reg_list; + int reg_list_size; + int retval; + int i; + + DEBUG(""); + + if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_NOT_HALTED: + ERROR("gdb requested registers, but we're not halted"); + exit(-1); + default: + ERROR("BUG: unexpected error returned by get_gdb_reg_list()"); + exit(-1); + } + } + + if (reg_list_size <= reg_num) + { + ERROR("gdb requested a non-existing register"); + exit(-1); + } + + hex_buf = buf_to_char(reg_list[reg_num]->value, reg_list[reg_num]->size); + reg_packet = reg_packet_p = malloc(CEIL(reg_list[reg_num]->size, 8) * 2); + + for (i = CEIL(reg_list[reg_num]->size, 8) * 2; i > 0; i -= 2) + { + *reg_packet_p++ = hex_buf[i - 2]; + *reg_packet_p++ = hex_buf[i - 1]; + } + + gdb_put_packet(connection, reg_packet, CEIL(reg_list[reg_num]->size, 8) * 2); + + free(reg_packet); + free(hex_buf); + +} + +void gdb_set_register_packet(connection_t *connection, target_t *target, char *packet, int packet_size) +{ + char *separator; + int reg_num = strtoul(packet + 1, &separator, 16); + reg_t **reg_list; + int reg_list_size; + int retval; + + DEBUG(""); + + if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_NOT_HALTED: + ERROR("gdb requested registers, but we're not halted"); + exit(-1); + default: + ERROR("BUG: unexpected error returned by get_gdb_reg_list()"); + exit(-1); + } + } + + if (reg_list_size < reg_num) + { + ERROR("gdb requested a non-existing register"); + exit(-1); + } + + if (*separator != '=') + { + ERROR("GDB set register packet, but no '=' following the register number"); + exit(-1); + } + + char_to_buf(separator + 1, CEIL(reg_list[reg_num]->size, 8) * 2, reg_list[reg_num]->value, reg_list[reg_num]->size); + reg_list[reg_num]->dirty = 1; + + gdb_put_packet(connection, "OK", 2); + +} + +void gdb_read_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size) +{ + char *separator; + u32 addr = 0; + u32 len = 0; + + u8 *buffer; + char *hex_buffer; + + int i; + + /* skip command character */ + packet++; + + addr = strtoul(packet, &separator, 16); + + if (*separator != ',') + return; + + len = strtoul(separator+1, NULL, 16); + + buffer = malloc(len); + + DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len); + + switch (len) + { + case 4: + if ((addr % 4) == 0) + target->type->read_memory(target, addr, 4, 1, buffer); + else + target->type->read_memory(target, addr, 1, len, buffer); + break; + case 2: + if ((addr % 2) == 0) + target->type->read_memory(target, addr, 2, 1, buffer); + else + target->type->read_memory(target, addr, 1, len, buffer); + break; + default: + if (((addr % 4) == 0) && ((len % 4) == 0)) + target->type->read_memory(target, addr, 4, len / 4, buffer); + else + target->type->read_memory(target, addr, 1, len, buffer); + } + + hex_buffer = malloc(len * 2 + 1); + + for (i=0; i<len; i++) + snprintf(hex_buffer + 2*i, 3, "%2.2x", buffer[i]); + + gdb_put_packet(connection, hex_buffer, len * 2); + + free(hex_buffer); + free(buffer); +} + +void gdb_write_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size) +{ + char *separator; + u32 addr = 0; + u32 len = 0; + + u8 *buffer; + + int i; + + /* skip command character */ + packet++; + + addr = strtoul(packet, &separator, 16); + + if (*separator != ',') + return; + + len = strtoul(separator+1, &separator, 16); + + if (*(separator++) != ':') + return; + + buffer = malloc(len); + + DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len); + + for (i=0; i<len; i++) + { + u32 tmp; + sscanf(separator + 2*i, "%2x", &tmp); + buffer[i] = tmp; + } + + switch (len) + { + /* handle sized writes */ + case 4: + if ((addr % 4) == 0) + target->type->write_memory(target, addr, 4, 1, buffer); + else + target->type->write_memory(target, addr, 1, len, buffer); + break; + case 2: + if ((addr % 2) == 0) + target->type->write_memory(target, addr, 2, 1, buffer); + else + target->type->write_memory(target, addr, 1, len, buffer); + break; + case 3: + case 1: + target->type->write_memory(target, addr, 1, len, buffer); + break; + /* handle bulk writes */ + default: + target_write_buffer(target, addr, len, buffer); + break; + } + + gdb_put_packet(connection, "OK", 2); + + free(buffer); +} + +void gdb_write_memory_binary_packet(connection_t *connection, target_t *target, char *packet, int packet_size) +{ + char *separator; + u32 addr = 0; + u32 len = 0; + + u8 *buffer; + + /* skip command character */ + packet++; + + addr = strtoul(packet, &separator, 16); + + if (*separator != ',') + return; + + len = strtoul(separator+1, &separator, 16); + + if (*(separator++) != ':') + return; + + if( len ) { + + buffer = malloc(len); + + DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len); + + memcpy( buffer, separator, len ); + + switch (len) + { + case 4: + if ((addr % 4) == 0) + target->type->write_memory(target, addr, 4, 1, buffer); + else + target->type->write_memory(target, addr, 1, len, buffer); + break; + case 2: + if ((addr % 2) == 0) + target->type->write_memory(target, addr, 2, 1, buffer); + else + target->type->write_memory(target, addr, 1, len, buffer); + break; + case 3: + case 1: + target->type->write_memory(target, addr, 1, len, buffer); + break; + default: + target_write_buffer(target, addr, len, buffer); + break; + } + + free(buffer); + } + + gdb_put_packet(connection, "OK", 2); +} + +void gdb_step_continue_packet(connection_t *connection, target_t *target, char *packet, int packet_size) +{ + int current = 0; + u32 address = 0x0; + + DEBUG(""); + + if (packet_size > 1) + { + u32 address = 0; + packet[packet_size] = 0; + address = strtoul(packet + 1, NULL, 16); + } + else + { + current = 1; + } + + if (packet[0] == 'c') + { + DEBUG("continue"); + target->type->resume(target, current, address, 0, 0); /* resume at current address, don't handle breakpoints, not debugging */ + } + else if (packet[0] == 's') + { + DEBUG("step"); + target->type->step(target, current, address, 0); /* step at current or address, don't handle breakpoints */ + } +} + +void gdb_breakpoint_watchpoint_packet(connection_t *connection, target_t *target, char *packet, int packet_size) +{ + int type; + enum breakpoint_type bp_type; + enum watchpoint_rw wp_type; + u32 address; + u32 size; + char *separator; + int retval; + + DEBUG(""); + + type = strtoul(packet + 1, &separator, 16); + + if (type == 0) /* memory breakpoint */ + bp_type = BKPT_SOFT; + else if (type == 1) /* hardware breakpoint */ + bp_type = BKPT_HARD; + else if (type == 2) /* write watchpoint */ + wp_type = WPT_WRITE; + else if (type == 3) /* read watchpoint */ + wp_type = WPT_READ; + else if (type == 4) /* access watchpoint */ + wp_type = WPT_ACCESS; + + if (*separator != ',') + return; + + address = strtoul(separator+1, &separator, 16); + + if (*separator != ',') + return; + + size = strtoul(separator+1, &separator, 16); + + switch (type) + { + case 0: + case 1: + if (packet[0] == 'Z') + { + if ((retval = breakpoint_add(target, address, size, bp_type)) != ERROR_OK) + { + if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + { + gdb_put_packet(connection, "E00", 3); + break; + } + } + } + else + { + breakpoint_remove(target, address); + } + gdb_put_packet(connection, "OK", 2); + break; + case 2: + case 3: + case 4: + { + if (packet[0] == 'Z') + watchpoint_add(target, address, size, type-2, 0, 0xffffffffu); + else + watchpoint_remove(target, address); + gdb_put_packet(connection, "OK", 2); + break; + } + default: + break; + } + +} + +void gdb_query_packet(connection_t *connection, char *packet, int packet_size) +{ + command_context_t *cmd_ctx = connection->cmd_ctx; + gdb_service_t *gdb_service = connection->service->priv; + target_t *target = gdb_service->target; + + if (strstr(packet, "qRcmd,")) + { + if (packet_size > 6) + { + char *cmd; + int i; + cmd = malloc((packet_size - 6)/2 + 1); + for (i=0; i < (packet_size - 6)/2; i++) + { + u32 tmp; + sscanf(packet + 6 + 2*i, "%2x", &tmp); + cmd[i] = tmp; + } + cmd[(packet_size - 6)/2] = 0x0; + command_run_line(cmd_ctx, cmd); + free(cmd); + } + gdb_put_packet(connection, "OK", 2); + return; + } + + gdb_put_packet(connection, "", 0); +} + +int gdb_input(connection_t *connection) +{ + gdb_service_t *gdb_service = connection->service->priv; + target_t *target = gdb_service->target; + char packet[GDB_BUFFER_SIZE]; + int packet_size; + int retval; + gdb_connection_t *gdb_con = connection->priv; + + /* drain input buffer */ + do + { + packet_size = GDB_BUFFER_SIZE-1; + if ((retval = gdb_get_packet(connection, packet, &packet_size)) != ERROR_OK) + { + switch (retval) + { + case ERROR_GDB_BUFFER_TOO_SMALL: + ERROR("BUG: buffer supplied for gdb packet was too small"); + exit(-1); + case ERROR_SERVER_REMOTE_CLOSED: + return ERROR_SERVER_REMOTE_CLOSED; + default: + ERROR("unexpected error"); + exit(-1); + } + } + + /* terminate with zero */ + packet[packet_size] = 0; + + DEBUG("recevied packet: '%s'", packet); + + if (packet_size > 0) + { + switch (packet[0]) + { + case 'H': + /* Hct... -- set thread + * we don't have threads, send empty reply */ + gdb_put_packet(connection, NULL, 0); + break; + case 'q': + gdb_query_packet(connection, packet, packet_size); + break; + case 'g': + gdb_get_registers_packet(connection, target, packet, packet_size); + break; + case 'G': + gdb_set_registers_packet(connection, target, packet, packet_size); + break; + case 'p': + gdb_get_register_packet(connection, target, packet, packet_size); + break; + case 'P': + gdb_set_register_packet(connection, target, packet, packet_size); + break; + case 'm': + gdb_read_memory_packet(connection, target, packet, packet_size); + break; + case 'M': + gdb_write_memory_packet(connection, target, packet, packet_size); + break; + case 'z': + case 'Z': + gdb_breakpoint_watchpoint_packet(connection, target, packet, packet_size); + break; + case '?': + gdb_last_signal_packet(connection, target, packet, packet_size); + break; + case 'c': + case 's': + gdb_step_continue_packet(connection, target, packet, packet_size); + break; + case 'D': + target->type->resume(target, 1, 0, 1, 0); + gdb_put_packet(connection, "OK", 2); + break; + case 'X': + gdb_write_memory_binary_packet(connection, target, packet, packet_size); + break; + case 'k': + gdb_put_packet(connection, "OK", 2); + return ERROR_SERVER_REMOTE_CLOSED; + default: + /* ignore unkown packets */ + DEBUG("ignoring 0x%2.2x packet", packet[0]); + gdb_put_packet(connection, NULL, 0); + break; + } + } + + if (gdb_con->ctrl_c) + { + if (target->state == TARGET_RUNNING) + { + target->type->halt(target); + gdb_con->ctrl_c = 0; + } + } + + } while (gdb_con->buf_cnt > 0); + + return ERROR_OK; +} + +int gdb_init() +{ + gdb_service_t *gdb_service; + target_t *target = targets; + int i = 0; + + if (!target) + { + WARNING("no gdb ports allocated as no target has been specified"); + return ERROR_OK; + } + + if (gdb_port == 0) + { + WARNING("no gdb port specified, using default port 3333"); + gdb_port = 3333; + } + + while (target) + { + char service_name[8]; + + snprintf(service_name, 8, "gdb-%2.2i", i); + + gdb_service = malloc(sizeof(gdb_service_t)); + gdb_service->target = target; + + add_service("gdb", CONNECTION_GDB, gdb_port + i, 1, gdb_new_connection, gdb_input, gdb_connection_closed, gdb_service); + + DEBUG("gdb service for target %s at port %i", target->type->name, gdb_port + i); + + target = target->next; + } + + return ERROR_OK; +} + +/* daemon configuration command gdb_port */ +int handle_gdb_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 0) + return ERROR_OK; + + /* only if the port wasn't overwritten by cmdline */ + if (gdb_port == 0) + gdb_port = strtoul(args[0], NULL, 0); + + return ERROR_OK; +} + +int gdb_register_commands(command_context_t *command_context) +{ + register_command(command_context, NULL, "gdb_port", handle_gdb_port_command, + COMMAND_CONFIG, ""); + + return ERROR_OK; +} diff --git a/src/server/gdb_server.h b/src/server/gdb_server.h new file mode 100644 index 00000000..860b29ca --- /dev/null +++ b/src/server/gdb_server.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef GDB_SERVER_H +#define GDB_SERVER_H + +#include "target.h" +#include "server.h" + +#define GDB_BUFFER_SIZE 2048 + +typedef struct gdb_connection_s +{ + char buffer[GDB_BUFFER_SIZE]; + char *buf_p; + int buf_cnt; + int ctrl_c; + enum target_state frontend_state; +} gdb_connection_t; + +typedef struct gdb_service_s +{ + struct target_s *target; +} gdb_service_t; + +extern int gdb_init(); +extern int gdb_register_commands(command_context_t *command_context); + +#define ERROR_GDB_BUFFER_TOO_SMALL (-800) + +#endif /* GDB_SERVER_H */ diff --git a/src/server/server.c b/src/server/server.c new file mode 100644 index 00000000..a034da79 --- /dev/null +++ b/src/server/server.c @@ -0,0 +1,368 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "server.h" + +#include "log.h" +#include "telnet_server.h" +#include "target.h" + +#include <command.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <signal.h> + +service_t *services = NULL; + +/* shutdown_openocd == 1: exit the main event loop, and quit the debugger */ +static int shutdown_openocd = 0; +int handle_shutdown_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +int add_connection(service_t *service, command_context_t *cmd_ctx) +{ + unsigned int address_size; + connection_t *c, *p; + int retval; + + c = malloc(sizeof(connection_t)); + c->fd = -1; + memset(&c->sin, 0, sizeof(c->sin)); + c->cmd_ctx = copy_command_context(cmd_ctx); + c->service = service; + c->input_pending = 0; + c->priv = NULL; + c->next = NULL; + + address_size = sizeof(c->sin); + c->fd = accept(service->fd, (struct sockaddr *)&service->sin, &address_size); + + if ((retval = service->new_connection(c)) == ERROR_OK) + { + INFO("accepted '%s' connection from %i", service->name, c->sin.sin_port); + } + else + { + close(c->fd); + INFO("attempted '%s' connection rejected", service->name); + free(c); + } + + if (service->connections) + { + for (p = service->connections; p && p->next; p = p->next); + if (p) + p->next = c; + } + else + { + service->connections = c; + } + + service->max_connections--; + + return ERROR_OK; +} + +int remove_connection(service_t *service, connection_t *connection) +{ + connection_t *c, *p = NULL; + + /* find connection */ + for (c = service->connections; c; c = c->next) + { + if (c->fd == connection->fd) + { + /* unlink connection */ + if (p) + p->next = c->next; + else + service->connections = c->next; + + service->connection_closed(c); + close(c->fd); + + command_done(c->cmd_ctx); + + /* delete connection */ + free(c); + + service->max_connections++; + break; + } + + /* remember the last connection for unlinking */ + p = c; + } + + return ERROR_OK; +} + +int add_service(char *name, enum connection_type type, unsigned short port, int max_connections, new_connection_handler_t new_connection_handler, input_handler_t input_handler, connection_closed_handler_t connection_closed_handler, void *priv) +{ + service_t *c, *p; + int so_reuseaddr_option = 1; + int oldopts; + + c = malloc(sizeof(service_t)); + + c->name = strdup(name); + c->type = type; + c->port = port; + c->max_connections = max_connections; + c->fd = -1; + c->connections = NULL; + c->new_connection = new_connection_handler; + c->input = input_handler; + c->connection_closed = connection_closed_handler; + c->priv = priv; + c->next = NULL; + + if ((c->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + ERROR("error creating socket: %s", strerror(errno)); + exit(-1); + } + + setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &so_reuseaddr_option, sizeof(int)); + + oldopts = fcntl(c->fd, F_GETFL, 0); + fcntl(c->fd, F_SETFL, oldopts | O_NONBLOCK); + + memset(&c->sin, 0, sizeof(c->sin)); + c->sin.sin_family = AF_INET; + c->sin.sin_addr.s_addr = INADDR_ANY; + c->sin.sin_port = htons(port); + + if (bind(c->fd, (struct sockaddr *)&c->sin, sizeof(c->sin)) == -1) + { + ERROR("couldn't bind to socket: %s", strerror(errno)); + exit(-1); + } + + if (listen(c->fd, 1) == -1) + { + ERROR("couldn't listen on socket: %s", strerror(errno)); + exit(-1); + } + + if (services) + { + for (p = services; p && p->next; p = p->next); + if (p) + p->next = c; + } + else + { + services = c; + } + + return ERROR_OK; +} + +int remove_service(unsigned short port) +{ + service_t *c, *p = NULL; + + /* find service */ + for (c = services; c; c = c->next) + { + if (c->port == port) + { + /* unlink service */ + if (p) + p->next = c->next; + else + services = c->next; + + if (c->name) + free(c->name); + + /* delete service */ + free(c); + } + + /* remember the last service for unlinking */ + p = c; + } + + return ERROR_OK; +} + +int server_loop(command_context_t *command_context) +{ + service_t *service; + + /* used in select() */ + fd_set read_fds; + struct timeval tv; + int fd_max; + + /* used in accept() */ + int retval; + + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + ERROR("couldn't set SIGPIPE to SIG_IGN"); + + /* do regular tasks after at most 10ms */ + tv.tv_sec = 0; + tv.tv_usec = 10000; + + while(!shutdown_openocd) + { + /* monitor sockets for acitvity */ + fd_max = 0; + FD_ZERO(&read_fds); + + /* add service and connection fds to read_fds */ + for (service = services; service; service = service->next) + { + if (service->fd != -1) + { + /* listen for new connections */ + FD_SET(service->fd, &read_fds); + + if (service->fd > fd_max) + fd_max = service->fd; + } + + if (service->connections) + { + connection_t *c; + + for (c = service->connections; c; c = c->next) + { + /* check for activity on the connection */ + FD_SET(c->fd, &read_fds); + if (c->fd > fd_max) + fd_max = c->fd; + } + } + } + + /* add STDIN to read_fds */ + FD_SET(fileno(stdin), &read_fds); + + if ((retval = select(fd_max + 1, &read_fds, NULL, NULL, &tv)) == -1) + { + if (errno == EINTR) + FD_ZERO(&read_fds); + else + { + ERROR("error during select: %s", strerror(errno)); + exit(-1); + } + } + + target_call_timer_callbacks(); + + if (retval == 0) + { + /* do regular tasks after at most 100ms */ + tv.tv_sec = 0; + tv.tv_usec = 10000; + +#if 0 + if (shutdown_openocd) + return ERROR_COMMAND_CLOSE_CONNECTION; + + handle_target(); +#endif + } + + for (service = services; service; service = service->next) + { + /* handle new connections on listeners */ + if ((service->fd != -1) + && (FD_ISSET(service->fd, &read_fds))) + { + if (service->max_connections > 0) + add_connection(service, command_context); + else + { + struct sockaddr_in sin; + unsigned int address_size = sizeof(sin); + int tmp_fd; + tmp_fd = accept(service->fd, (struct sockaddr *)&service->sin, &address_size); + close(tmp_fd); + INFO("rejected '%s' connection, no more connections allowed", service->name); + } + } + + /* handle activity on connections */ + if (service->connections) + { + connection_t *c; + + for (c = service->connections; c;) + { + if ((FD_ISSET(c->fd, &read_fds)) || c->input_pending) + { + if (service->input(c) != ERROR_OK) + { + connection_t *next = c->next; + remove_connection(service, c); + INFO("dropped '%s' connection", service->name); + c = next; + continue; + } + } + c = c->next; + } + } + } + + if (FD_ISSET(fileno(stdin), &read_fds)) + { + if (getc(stdin) == 'x') + { + shutdown_openocd = 1; + } + } + } + + return ERROR_OK; +} + +int server_init() +{ + + return ERROR_OK; +} + +int server_register_commands(command_context_t *context) +{ + register_command(context, NULL, "shutdown", handle_shutdown_command, + COMMAND_ANY, "shut the server down"); + + return ERROR_OK; +} + +/* tell the server we want to shut down */ +int handle_shutdown_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + shutdown_openocd = 1; + + return ERROR_COMMAND_CLOSE_CONNECTION; +} + diff --git a/src/server/server.h b/src/server/server.h new file mode 100644 index 00000000..625c364e --- /dev/null +++ b/src/server/server.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SERVER_H +#define SERVER_H + +#include "command.h" +#include "binarybuffer.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +enum connection_type +{ + CONNECTION_GDB, + CONNECTION_TELNET, +}; + +typedef struct connection_s +{ + int fd; + struct sockaddr_in sin; + command_context_t *cmd_ctx; + struct service_s *service; + int input_pending; + void *priv; + struct connection_s *next; +} connection_t; + +typedef int (*new_connection_handler_t)(connection_t *connection); +typedef int (*input_handler_t)(connection_t *connection); +typedef int (*connection_closed_handler_t)(connection_t *connection); + +typedef struct service_s +{ + char *name; + enum connection_type type; + unsigned short port; + int fd; + struct sockaddr_in sin; + int max_connections; + connection_t *connections; + new_connection_handler_t new_connection; + input_handler_t input; + connection_closed_handler_t connection_closed; + void *priv; + struct service_s *next; +} service_t; + +extern int add_service(char *name, enum connection_type type, unsigned short port, int max_connections, new_connection_handler_t new_connection_handler, input_handler_t input_handler, connection_closed_handler_t connection_closed_handler, void *priv); +extern int server_init(); +extern int server_loop(command_context_t *command_context); +extern int server_register_commands(command_context_t *context); + +#define ERROR_SERVER_REMOTE_CLOSED (-400) +#define ERROR_CONNECTION_REJECTED (-401) + +#endif /* SERVER_H */ diff --git a/src/server/telnet_server.c b/src/server/telnet_server.c new file mode 100644 index 00000000..d7933293 --- /dev/null +++ b/src/server/telnet_server.c @@ -0,0 +1,570 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "telnet_server.h" + +#include "server.h" +#include "log.h" +#include "command.h" +#include "target.h" + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> + +static unsigned short telnet_port = 0; + +int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +static char *negotiate = + "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */ + "\xFF\xFB\x01" /* IAC WILL Echo */ + "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */ + "\xFF\xFE\x01"; /* IAC DON'T Echo */ + +#define CTRL(c) (c - '@') + +void telnet_prompt(connection_t *connection) +{ + telnet_connection_t *t_con = connection->priv; + + write(connection->fd, t_con->prompt, strlen(t_con->prompt)); +} + +int telnet_output(struct command_context_s *cmd_ctx, char* line) +{ + connection_t *connection = cmd_ctx->output_handler_priv; + + write(connection->fd, line, strlen(line)); + write(connection->fd, "\r\n\0", 3); + + return ERROR_OK; +} + +int telnet_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv) +{ + struct command_context_s *cmd_ctx = priv; + connection_t *connection = cmd_ctx->output_handler_priv; + telnet_connection_t *t_con = connection->priv; + char buffer[512]; + + switch (event) + { + case TARGET_EVENT_HALTED: + command_print(cmd_ctx, "Target %i halted", get_num_by_target(target)); + target->type->arch_state(target, buffer, 512); + buffer[511] = 0; + command_print(cmd_ctx, "%s", buffer); + telnet_prompt(connection); + t_con->surpress_prompt = 1; + break; + case TARGET_EVENT_RESUMED: + command_print(cmd_ctx, "Target %i resumed", get_num_by_target(target)); + telnet_prompt(connection); + t_con->surpress_prompt = 1; + break; + default: + break; + } + + return ERROR_OK; +} + +int telnet_new_connection(connection_t *connection) +{ + telnet_connection_t *telnet_connection = malloc(sizeof(telnet_connection_t)); + telnet_service_t *telnet_service = connection->service->priv; + int i; + + connection->priv = telnet_connection; + + /* initialize telnet connection information */ + telnet_connection->line_size = 0; + telnet_connection->line_cursor = 0; + telnet_connection->option_size = 0; + telnet_connection->prompt = strdup("> "); + telnet_connection->surpress_prompt = 0; + telnet_connection->state = TELNET_STATE_DATA; + + /* output goes through telnet connection */ + command_set_output_handler(connection->cmd_ctx, telnet_output, connection); + + /* negotiate telnet options */ + write(connection->fd, negotiate, strlen(negotiate)); + + /* print connection banner */ + if (telnet_service->banner) + { + write(connection->fd, telnet_service->banner, strlen(telnet_service->banner)); + write(connection->fd, "\r\n\0", 3); + } + + telnet_prompt(connection); + + /* initialize history */ + for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) + { + telnet_connection->history[i] = NULL; + } + telnet_connection->next_history = 0; + telnet_connection->current_history = 0; + + target_register_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx); + + return ERROR_OK; +} + +void telnet_clear_line(connection_t *connection, telnet_connection_t *t_con) +{ + /* move to end of line */ + if (t_con->line_cursor < t_con->line_size) + { + write(connection->fd, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor); + } + + /* backspace, overwrite with space, backspace */ + while (t_con->line_size > 0) + { + write(connection->fd, "\b \b", 3); + t_con->line_size--; + } + t_con->line_cursor = 0; +} + +int telnet_input(connection_t *connection) +{ + int bytes_read; + char buffer[TELNET_BUFFER_SIZE]; + char *buf_p; + telnet_connection_t *t_con = connection->priv; + command_context_t *command_context = connection->cmd_ctx; + + bytes_read = read(connection->fd, buffer, TELNET_BUFFER_SIZE); + + if (bytes_read == 0) + return ERROR_SERVER_REMOTE_CLOSED; + else if (bytes_read == -1) + { + ERROR("error during read: %s", strerror(errno)); + return ERROR_SERVER_REMOTE_CLOSED; + } + + buf_p = buffer; + while (bytes_read) + { + switch (t_con->state) + { + case TELNET_STATE_DATA: + if (*buf_p == '\xff') + { + t_con->state = TELNET_STATE_IAC; + } + else + { + if (isprint(*buf_p)) /* printable character */ + { + write(connection->fd, buf_p, 1); + if (t_con->line_cursor == t_con->line_size) + { + t_con->line[t_con->line_size++] = *buf_p; + t_con->line_cursor++; + } + else + { + int i; + memmove(t_con->line + t_con->line_cursor + 1, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor); + t_con->line[t_con->line_cursor++] = *buf_p; + t_con->line_size++; + write(connection->fd, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor); + for (i = t_con->line_cursor; i < t_con->line_size; i++) + { + write(connection->fd, "\b", 1); + } + } + } + else /* non-printable */ + { + if (*buf_p == 0x1b) /* escape */ + { + t_con->state = TELNET_STATE_ESCAPE; + t_con->last_escape = '\x00'; + } + else if ((*buf_p == 0xd) || (*buf_p == 0xa)) /* CR/LF */ + { + int retval; + + /* skip over combinations with CR/LF + NUL */ + if (((*(buf_p + 1) == 0xa) || (*(buf_p + 1) == 0xd)) && (bytes_read > 1)) + { + buf_p++; + bytes_read--; + } + if ((*(buf_p + 1) == 0) && (bytes_read > 1)) + { + buf_p++; + bytes_read--; + } + t_con->line[t_con->line_size] = 0; + + write(connection->fd, "\r\n\x00", 3); + + if (strcmp(t_con->line, "history") == 0) + { + int i; + for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) + { + if (t_con->history[i]) + { + write(connection->fd, t_con->history[i], strlen(t_con->history[i])); + write(connection->fd, "\r\n\x00", 3); + } + } + telnet_prompt(connection); + t_con->line_size = 0; + t_con->line_cursor = 0; + continue; + } + + /* we're running a command, so we need a prompt + * if the output handler is called, this gets set again */ + t_con->surpress_prompt = 0; + if ((retval = command_run_line(command_context, t_con->line)) != ERROR_OK) + { + if (retval == ERROR_COMMAND_CLOSE_CONNECTION) + { + return ERROR_SERVER_REMOTE_CLOSED; + } + } + + /* if the history slot is already taken, free it */ + if (t_con->history[t_con->next_history]) + { + free(t_con->history[t_con->next_history]); + } + + /* add line to history */ + t_con->history[t_con->next_history++] = strdup(t_con->line); + + /* current history line starts at the new entry */ + t_con->current_history = t_con->next_history; + + if (t_con->history[t_con->current_history]) + { + free(t_con->history[t_con->current_history]); + } + t_con->history[t_con->current_history] = strdup(""); + + /* wrap history at TELNET_LINE_HISTORY_SIZE */ + if (t_con->next_history > TELNET_LINE_HISTORY_SIZE - 1) + t_con->next_history = 0; + + if (!t_con->surpress_prompt) + { + telnet_prompt(connection); + } + else + { + t_con->surpress_prompt = 0; + } + + t_con->line_size = 0; + t_con->line_cursor = 0; + } + else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) /* delete character */ + { + if (t_con->line_cursor > 0) + { + if (t_con->line_cursor != t_con->line_size) + { + int i; + write(connection->fd, "\b", 1); + t_con->line_cursor--; + t_con->line_size--; + memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor); + + write(connection->fd, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor); + write(connection->fd, " \b", 2); + for (i = t_con->line_cursor; i < t_con->line_size; i++) + { + write(connection->fd, "\b", 1); + } + } + else + { + t_con->line_size--; + t_con->line_cursor--; + /* back space: move the 'printer' head one char back, overwrite with space, move back again */ + write(connection->fd, "\b \b", 3); + } + } + } + else if (*buf_p == 0x15) /* clear line */ + { + telnet_clear_line(connection, t_con); + } + else if (*buf_p == CTRL('B')) /* cursor left */ + { + if (t_con->line_cursor > 0) + { + write(connection->fd, "\b", 1); + t_con->line_cursor--; + } + t_con->state = TELNET_STATE_DATA; + } + else if (*buf_p == CTRL('F')) /* cursor right */ + { + if (t_con->line_cursor < t_con->line_size) + { + write(connection->fd, t_con->line + t_con->line_cursor++, 1); + } + t_con->state = TELNET_STATE_DATA; + } + else + { + DEBUG("unhandled nonprintable: %2.2x", *buf_p); + } + } + } + break; + case TELNET_STATE_IAC: + switch (*buf_p) + { + case '\xfe': + t_con->state = TELNET_STATE_DONT; + break; + case '\xfd': + t_con->state = TELNET_STATE_DO; + break; + case '\xfc': + t_con->state = TELNET_STATE_WONT; + break; + case '\xfb': + t_con->state = TELNET_STATE_WILL; + break; + } + break; + case TELNET_STATE_SB: + break; + case TELNET_STATE_SE: + break; + case TELNET_STATE_WILL: + case TELNET_STATE_WONT: + case TELNET_STATE_DO: + case TELNET_STATE_DONT: + t_con->state = TELNET_STATE_DATA; + break; + case TELNET_STATE_ESCAPE: + if (t_con->last_escape == '[') + { + if (*buf_p == 'D') /* cursor left */ + { + if (t_con->line_cursor > 0) + { + write(connection->fd, "\b", 1); + t_con->line_cursor--; + } + t_con->state = TELNET_STATE_DATA; + } + else if (*buf_p == 'C') /* cursor right */ + { + if (t_con->line_cursor < t_con->line_size) + { + write(connection->fd, t_con->line + t_con->line_cursor++, 1); + } + t_con->state = TELNET_STATE_DATA; + } + else if (*buf_p == 'A') /* cursor up */ + { + int last_history = (t_con->current_history - 1 >= 0) ? t_con->current_history - 1 : 127; + if (t_con->history[last_history]) + { + telnet_clear_line(connection, t_con); + t_con->line_size = strlen(t_con->history[last_history]); + t_con->line_cursor = t_con->line_size; + memcpy(t_con->line, t_con->history[last_history], t_con->line_size + 1); + write(connection->fd, t_con->line, t_con->line_size); + t_con->current_history = last_history; + } + t_con->state = TELNET_STATE_DATA; + } + else if (*buf_p == 'B') /* cursor down */ + { + int next_history = (t_con->current_history + 1 < 128) ? t_con->current_history + 1 : 0; + if (t_con->history[next_history]) + { + telnet_clear_line(connection, t_con); + t_con->line_size = strlen(t_con->history[next_history]); + t_con->line_cursor = t_con->line_size; + memcpy(t_con->line, t_con->history[next_history], t_con->line_size + 1); + write(connection->fd, t_con->line, t_con->line_size); + t_con->current_history = next_history; + } + t_con->state = TELNET_STATE_DATA; + } + else if (*buf_p == '3') + { + t_con->last_escape = *buf_p; + } + else + { + t_con->state = TELNET_STATE_DATA; + } + } + else if (t_con->last_escape == '3') + { + /* Remove character */ + if (*buf_p == '~') + { + if (t_con->line_cursor < t_con->line_size) + { + int i; + t_con->line_size--; + /* remove char from line buffer */ + memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor); + + /* print remainder of buffer */ + write(connection->fd, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor); + /* overwrite last char with whitespace */ + write(connection->fd, " \b", 2); + + /* move back to cursor position*/ + for (i = t_con->line_cursor; i < t_con->line_size; i++) + { + write(connection->fd, "\b", 1); + } + } + + t_con->state = TELNET_STATE_DATA; + } + else + { + t_con->state = TELNET_STATE_DATA; + } + } + else if (t_con->last_escape == '\x00') + { + if (*buf_p == '[') + { + t_con->last_escape = *buf_p; + } + else + { + t_con->state = TELNET_STATE_DATA; + } + } + else + { + ERROR("BUG: unexpected value in t_con->last_escape"); + t_con->state = TELNET_STATE_DATA; + } + + break; + default: + ERROR("unknown telnet state"); + exit(-1); + } + + bytes_read--; + buf_p++; + } + + return ERROR_OK; +} + +int telnet_connection_closed(connection_t *connection) +{ + telnet_connection_t *t_con = connection->priv; + int i; + + if (t_con->prompt) + free(t_con->prompt); + + for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) + { + if (t_con->history[i]) + free(t_con->history[i]); + } + + if (connection->priv) + free(connection->priv); + else + ERROR("BUG: connection->priv == NULL"); + + target_unregister_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx); + + return ERROR_OK; +} + +int telnet_set_prompt(connection_t *connection, char *prompt) +{ + telnet_connection_t *t_con = connection->priv; + + t_con->prompt = strdup(prompt); + + return ERROR_OK; +} + +int telnet_init(char *banner) +{ + telnet_service_t *telnet_service = malloc(sizeof(telnet_service_t)); + + if (telnet_port == 0) + { + WARNING("no telnet port specified, using default port 4444"); + telnet_port = 4444; + } + + telnet_service->banner = banner; + + add_service("telnet", CONNECTION_TELNET, telnet_port, 1, telnet_new_connection, telnet_input, telnet_connection_closed, telnet_service); + + return ERROR_OK; +} + +int telnet_register_commands(command_context_t *command_context) +{ + register_command(command_context, NULL, "exit", handle_exit_command, + COMMAND_EXEC, "exit telnet session"); + + register_command(command_context, NULL, "telnet_port", handle_telnet_port_command, + COMMAND_CONFIG, ""); + + return ERROR_OK; +} + +/* daemon configuration command telnet_port */ +int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 0) + return ERROR_OK; + + /* only if the port wasn't overwritten by cmdline */ + if (telnet_port == 0) + telnet_port = strtoul(args[0], NULL, 0); + + return ERROR_OK; +} + +int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + return ERROR_COMMAND_CLOSE_CONNECTION; +} diff --git a/src/server/telnet_server.h b/src/server/telnet_server.h new file mode 100644 index 00000000..d6ca0e86 --- /dev/null +++ b/src/server/telnet_server.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef TELNET_SERVER_H +#define TELNET_SERVER_H + +#include "server.h" + +#define TELNET_BUFFER_SIZE (1024) + +#define TELNET_OPTION_MAX_SIZE (128) +#define TELNET_LINE_HISTORY_SIZE (128) +#define TELNET_LINE_MAX_SIZE (256) + +enum telnet_states +{ + TELNET_STATE_DATA, + TELNET_STATE_IAC, + TELNET_STATE_SB, + TELNET_STATE_SE, + TELNET_STATE_WILL, + TELNET_STATE_WONT, + TELNET_STATE_DO, + TELNET_STATE_DONT, + TELNET_STATE_ESCAPE, +}; + +typedef struct telnet_connection_s +{ + char *prompt; + int surpress_prompt; + enum telnet_states state; + char line[TELNET_LINE_MAX_SIZE]; + int line_size; + int line_cursor; + char option[TELNET_OPTION_MAX_SIZE]; + int option_size; + char last_escape; + char *history[TELNET_LINE_HISTORY_SIZE]; + int next_history; + int current_history; +} telnet_connection_t; + +typedef struct telnet_service_s +{ + char *banner; +} telnet_service_t; + +extern int telnet_init(char *banner); +extern int telnet_register_commands(command_context_t *command_context); + +#endif /* TELNET_SERVER_H */ diff --git a/src/target/Makefile.am b/src/target/Makefile.am new file mode 100644 index 00000000..7e26762b --- /dev/null +++ b/src/target/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = -I$(top_srcdir)/src/gdb -I$(top_srcdir)/src/helper -I$(top_srcdir)/src/jtag -I$(top_srcdir)/src/xsvf $(all_includes) +METASOURCES = AUTO +noinst_LIBRARIES = libtarget.a +libtarget_a_SOURCES = target.c register.c breakpoints.c armv4_5.c embeddedice.c etm.c arm7tdmi.c arm9tdmi.c \ + arm_jtag.c arm7_9_common.c algorithm.c arm920t.c arm720t.c armv4_5_mmu.c armv4_5_cache.c +noinst_HEADERS = target.h register.h armv4_5.h embeddedice.h etm.h arm7tdmi.h arm9tdmi.h \ + arm_jtag.h arm7_9_common.h arm920t.h arm720t.h armv4_5_mmu.h armv4_5_cache.h breakpoints.h algorithm.h diff --git a/src/target/algorithm.c b/src/target/algorithm.c new file mode 100644 index 00000000..fdebfc58 --- /dev/null +++ b/src/target/algorithm.c @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" +#include "algorithm.h" + +#include "log.h" +#include "configuration.h" +#include "binarybuffer.h" + +#include <stdlib.h> + + +void init_mem_param(mem_param_t *param, u32 address, u32 size, enum param_direction direction) +{ + param->address = address; + param->size = size; + param->value = malloc(size); + param->direction = direction; +} + +void destroy_mem_param(mem_param_t *param) +{ + free(param->value); +} + +void init_reg_param(reg_param_t *param, char *reg_name, u32 size, enum param_direction direction) +{ + param->reg_name = reg_name; + param->size = size; + param->value = malloc(CEIL(size, 8)); + param->direction = direction; +} + +void destroy_reg_param(reg_param_t *param) +{ + free(param->value); +} diff --git a/src/target/algorithm.h b/src/target/algorithm.h new file mode 100644 index 00000000..e248ba5a --- /dev/null +++ b/src/target/algorithm.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ALGORITHM_H +#define ALGORITHM_H + +#include "types.h" + +enum param_direction +{ + PARAM_IN, + PARAM_OUT, + PARAM_IN_OUT +}; + +typedef struct mem_param_s +{ + u32 address; + u32 size; + u8 *value; + enum param_direction direction; +} mem_param_t; + +typedef struct reg_param_s +{ + char *reg_name; + u32 size; + u8 *value; + enum param_direction direction; +} reg_param_t; + +extern void init_mem_param(mem_param_t *param, u32 address, u32 size, enum param_direction direction); +extern void destroy_mem_param(mem_param_t *param); +extern void init_reg_param(reg_param_t *param, char *reg_name, u32 size, enum param_direction direction); +extern void destroy_reg_param(reg_param_t *param); + +#endif /* ALGORITHM_H */ diff --git a/src/target/arm720t.c b/src/target/arm720t.c new file mode 100644 index 00000000..68ea3d1c --- /dev/null +++ b/src/target/arm720t.c @@ -0,0 +1,625 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" + +#include "arm720t.h" +#include "jtag.h" +#include "log.h" + +#include <stdlib.h> +#include <string.h> + +#if 1 +#define _DEBUG_INSTRUCTION_EXECUTION_ +#endif + +/* cli handling */ +int arm720t_register_commands(struct command_context_s *cmd_ctx); + +int arm720t_handle_cp15_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm720t_handle_virt2phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm720t_handle_md_phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm720t_handle_mw_phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +/* forward declarations */ +int arm720t_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target); +int arm720t_init_target(struct command_context_s *cmd_ctx, struct target_s *target); +int arm720t_quit(); +int arm720t_arch_state(struct target_s *target, char *buf, int buf_size); +int arm720t_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); +int arm720t_write_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); +int arm720t_soft_reset_halt(struct target_s *target); + +target_type_t arm720t_target = +{ + .name = "arm720t", + + .poll = arm7_9_poll, + .arch_state = arm720t_arch_state, + + .halt = arm7_9_halt, + .resume = arm7_9_resume, + .step = arm7_9_step, + + .assert_reset = arm7_9_assert_reset, + .deassert_reset = arm7_9_deassert_reset, + .soft_reset_halt = arm720t_soft_reset_halt, + + .get_gdb_reg_list = armv4_5_get_gdb_reg_list, + + .read_memory = arm720t_read_memory, + .write_memory = arm720t_write_memory, + .bulk_write_memory = arm7_9_bulk_write_memory, + + .run_algorithm = armv4_5_run_algorithm, + + .add_breakpoint = arm7_9_add_breakpoint, + .remove_breakpoint = arm7_9_remove_breakpoint, + .add_watchpoint = arm7_9_add_watchpoint, + .remove_watchpoint = arm7_9_remove_watchpoint, + + .register_commands = arm720t_register_commands, + .target_command = arm720t_target_command, + .init_target = arm720t_init_target, + .quit = arm720t_quit +}; + +int arm720t_scan_cp15(target_t *target, u32 out, u32 *in, int instruction, int clock) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + scan_field_t fields[2]; + u8 out_buf[4]; + u8 instruction_buf = instruction; + + out = flip_u32(out, 32); + buf_set_u32(out_buf, 0, 32, out); + + jtag_add_end_state(TAP_PD); + arm_jtag_scann(jtag_info, 0xf); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = &instruction_buf; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = out_buf; + fields[1].out_mask = NULL; + if (in) + { + fields[1].in_value = (u8*)in; + fields[1].in_handler = arm_jtag_buf_to_u32_flip; + fields[1].in_handler_priv = in; + } else + { + fields[1].in_value = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + } + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + + jtag_add_dr_scan(2, fields, -1); + + if (clock) + jtag_add_runtest(0, -1); + +#ifdef _DEBUG_INSTRUCTION_EXECUTION_ + jtag_execute_queue(); + + if (in) + DEBUG("out: %8.8x, in: %8.8x, instruction: %i, clock: %i", out, *in, instruction, clock); + else + DEBUG("out: %8.8x, instruction: %i, clock: %i", out, instruction, clock); +#else + DEBUG("out: %8.8x, instruction: %i, clock: %i", in, out, instruction, clock); +#endif + + return ERROR_OK; +} + +int arm720t_read_cp15(target_t *target, u32 opcode, u32 *value) +{ + /* fetch CP15 opcode */ + arm720t_scan_cp15(target, opcode, NULL, 1, 1); + /* "DECODE" stage */ + arm720t_scan_cp15(target, ARMV4_5_NOP, NULL, 1, 1); + /* "EXECUTE" stage (1) */ + arm720t_scan_cp15(target, ARMV4_5_NOP, NULL, 1, 0); + arm720t_scan_cp15(target, 0x0, NULL, 0, 1); + /* "EXECUTE" stage (2) */ + arm720t_scan_cp15(target, 0x0, NULL, 0, 1); + /* "EXECUTE" stage (3), CDATA is read */ + arm720t_scan_cp15(target, ARMV4_5_NOP, value, 1, 1); + + return ERROR_OK; +} + +int arm720t_write_cp15(target_t *target, u32 opcode, u32 value) +{ + /* fetch CP15 opcode */ + arm720t_scan_cp15(target, opcode, NULL, 1, 1); + /* "DECODE" stage */ + arm720t_scan_cp15(target, ARMV4_5_NOP, NULL, 1, 1); + /* "EXECUTE" stage (1) */ + arm720t_scan_cp15(target, ARMV4_5_NOP, NULL, 1, 0); + arm720t_scan_cp15(target, 0x0, NULL, 0, 1); + /* "EXECUTE" stage (2) */ + arm720t_scan_cp15(target, value, NULL, 0, 1); + arm720t_scan_cp15(target, ARMV4_5_NOP, NULL, 1, 1); + + return ERROR_OK; +} + +u32 arm720t_get_ttb(target_t *target) +{ + u32 ttb = 0x0; + + arm720t_read_cp15(target, 0xee120f10, &ttb); + jtag_execute_queue(); + + ttb &= 0xffffc000; + + return ttb; +} + +void arm720t_disable_mmu_caches(target_t *target, int mmu, int d_u_cache, int i_cache) +{ + u32 cp15_control; + + /* read cp15 control register */ + arm720t_read_cp15(target, 0xee110f10, &cp15_control); + jtag_execute_queue(); + + if (mmu) + cp15_control &= ~0x1U; + + if (d_u_cache || i_cache) + cp15_control &= ~0x4U; + + arm720t_write_cp15(target, 0xee010f10, cp15_control); +} + +void arm720t_enable_mmu_caches(target_t *target, int mmu, int d_u_cache, int i_cache) +{ + u32 cp15_control; + + /* read cp15 control register */ + arm720t_read_cp15(target, 0xee110f10, &cp15_control); + jtag_execute_queue(); + + if (mmu) + cp15_control |= 0x1U; + + if (d_u_cache || i_cache) + cp15_control |= 0x4U; + + arm720t_write_cp15(target, 0xee010f10, cp15_control); +} + +void arm720t_post_debug_entry(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm7tdmi_common_t *arm7tdmi = arm7_9->arch_info; + arm720t_common_t *arm720t = arm7tdmi->arch_info; + + /* examine cp15 control reg */ + arm720t_read_cp15(target, 0xee110f10, &arm720t->cp15_control_reg); + jtag_execute_queue(); + DEBUG("cp15_control_reg: %8.8x", arm720t->cp15_control_reg); + + arm720t->armv4_5_mmu.mmu_enabled = (arm720t->cp15_control_reg & 0x1U) ? 1 : 0; + arm720t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled = (arm720t->cp15_control_reg & 0x4U) ? 1 : 0; + arm720t->armv4_5_mmu.armv4_5_cache.i_cache_enabled = 0; + + /* save i/d fault status and address register */ + arm720t_read_cp15(target, 0xee150f10, &arm720t->fsr); + arm720t_read_cp15(target, 0xee160f10, &arm720t->far); + jtag_execute_queue(); +} + +void arm720t_pre_restore_context(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm7tdmi_common_t *arm7tdmi = arm7_9->arch_info; + arm720t_common_t *arm720t = arm7tdmi->arch_info; + + /* restore i/d fault status and address register */ + arm720t_write_cp15(target, 0xee050f10, arm720t->fsr); + arm720t_write_cp15(target, 0xee060f10, arm720t->far); +} + +int arm720t_get_arch_pointers(target_t *target, armv4_5_common_t **armv4_5_p, arm7_9_common_t **arm7_9_p, arm7tdmi_common_t **arm7tdmi_p, arm720t_common_t **arm720t_p) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9; + arm7tdmi_common_t *arm7tdmi; + arm720t_common_t *arm720t; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + return -1; + } + + arm7_9 = armv4_5->arch_info; + if (arm7_9->common_magic != ARM7_9_COMMON_MAGIC) + { + return -1; + } + + arm7tdmi = arm7_9->arch_info; + if (arm7tdmi->common_magic != ARM7TDMI_COMMON_MAGIC) + { + return -1; + } + + arm720t = arm7tdmi->arch_info; + if (arm720t->common_magic != ARM720T_COMMON_MAGIC) + { + return -1; + } + + *armv4_5_p = armv4_5; + *arm7_9_p = arm7_9; + *arm7tdmi_p = arm7tdmi; + *arm720t_p = arm720t; + + return ERROR_OK; +} + +int arm720t_arch_state(struct target_s *target, char *buf, int buf_size) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm7tdmi_common_t *arm7tdmi = arm7_9->arch_info; + arm720t_common_t *arm720t = arm7tdmi->arch_info; + + char *state[] = + { + "disabled", "enabled" + }; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + ERROR("BUG: called for a non-ARMv4/5 target"); + exit(-1); + } + + snprintf(buf, buf_size, + "target halted in %s state due to %s, current mode: %s\n" + "cpsr: 0x%8.8x pc: 0x%8.8x\n" + "MMU: %s, Cache: %s", + armv4_5_state_strings[armv4_5->core_state], + target_debug_reason_strings[target->debug_reason], + armv4_5_mode_strings[armv4_5_mode_to_number(armv4_5->core_mode)], + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32), + state[arm720t->armv4_5_mmu.mmu_enabled], + state[arm720t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled]); + + return ERROR_OK; +} + +int arm720t_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer) +{ + int retval; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm7tdmi_common_t *arm7tdmi = arm7_9->arch_info; + arm720t_common_t *arm720t = arm7tdmi->arch_info; + + /* disable cache, but leave MMU enabled */ + if (arm720t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled) + arm720t_disable_mmu_caches(target, 0, 1, 0); + + retval = arm7_9_read_memory(target, address, size, count, buffer); + + if (arm720t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled) + arm720t_enable_mmu_caches(target, 0, 1, 0); + + return retval; +} + +int arm720t_write_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer) +{ + int retval; + + if ((retval = arm7_9_write_memory(target, address, size, count, buffer)) != ERROR_OK) + return retval; + + return retval; +} + +int arm720t_soft_reset_halt(struct target_s *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm7tdmi_common_t *arm7tdmi = arm7_9->arch_info; + arm720t_common_t *arm720t = arm7tdmi->arch_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + + if (target->state == TARGET_RUNNING) + { + target->type->halt(target); + } + + while (buf_get_u32(dbg_stat->value, EICE_DBG_CONTROL_DBGACK, 1) == 0) + { + embeddedice_read_reg(dbg_stat); + jtag_execute_queue(); + } + + target->state = TARGET_HALTED; + + /* SVC, ARM state, IRQ and FIQ disabled */ + buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8, 0xd3); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + + /* start fetching from 0x0 */ + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, 0x0); + armv4_5->core_cache->reg_list[15].dirty = 1; + armv4_5->core_cache->reg_list[15].valid = 1; + + armv4_5->core_mode = ARMV4_5_MODE_SVC; + armv4_5->core_state = ARMV4_5_STATE_ARM; + + arm720t_disable_mmu_caches(target, 1, 1, 1); + arm720t->armv4_5_mmu.mmu_enabled = 0; + arm720t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled = 0; + arm720t->armv4_5_mmu.armv4_5_cache.i_cache_enabled = 0; + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + + return ERROR_OK; +} + +int arm720t_init_target(struct command_context_s *cmd_ctx, struct target_s *target) +{ + arm7tdmi_init_target(cmd_ctx, target); + + return ERROR_OK; + +} + +int arm720t_quit() +{ + + return ERROR_OK; +} + +int arm720t_init_arch_info(target_t *target, arm720t_common_t *arm720t, int chain_pos, char *variant) +{ + arm7tdmi_common_t *arm7tdmi = &arm720t->arm7tdmi_common; + arm7_9_common_t *arm7_9 = &arm7tdmi->arm7_9_common; + + arm7tdmi_init_arch_info(target, arm7tdmi, chain_pos, variant); + + arm7tdmi->arch_info = arm720t; + arm720t->common_magic = ARM720T_COMMON_MAGIC; + + arm7_9->post_debug_entry = arm720t_post_debug_entry; + arm7_9->pre_restore_context = arm720t_pre_restore_context; + + arm720t->armv4_5_mmu.armv4_5_cache.ctype = -1; + arm720t->armv4_5_mmu.get_ttb = arm720t_get_ttb; + arm720t->armv4_5_mmu.read_memory = arm7_9_read_memory; + arm720t->armv4_5_mmu.write_memory = arm7_9_write_memory; + arm720t->armv4_5_mmu.disable_mmu_caches = arm720t_disable_mmu_caches; + arm720t->armv4_5_mmu.enable_mmu_caches = arm720t_enable_mmu_caches; + arm720t->armv4_5_mmu.has_tiny_pages = 0; + arm720t->armv4_5_mmu.mmu_enabled = 0; + + return ERROR_OK; +} + +int arm720t_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target) +{ + int chain_pos; + char *variant = NULL; + arm720t_common_t *arm720t = malloc(sizeof(arm720t_common_t)); + + if (argc < 4) + { + ERROR("'target arm720t' requires at least one additional argument"); + exit(-1); + } + + chain_pos = strtoul(args[3], NULL, 0); + + if (argc >= 5) + variant = strdup(args[4]); + + DEBUG("chain_pos: %i, variant: %s", chain_pos, variant); + + arm720t_init_arch_info(target, arm720t, chain_pos, variant); + + return ERROR_OK; +} + +int arm720t_register_commands(struct command_context_s *cmd_ctx) +{ + int retval; + command_t *arm720t_cmd; + + + retval = arm7tdmi_register_commands(cmd_ctx); + + arm720t_cmd = register_command(cmd_ctx, NULL, "arm720t", NULL, COMMAND_ANY, NULL); + + register_command(cmd_ctx, arm720t_cmd, "cp15", arm720t_handle_cp15_command, COMMAND_EXEC, "display/modify cp15 register <opcode> [value]"); + register_command(cmd_ctx, arm720t_cmd, "virt2phys", arm720t_handle_virt2phys_command, COMMAND_EXEC, "translate va to pa <va>"); + + register_command(cmd_ctx, arm720t_cmd, "mdw_phys", arm720t_handle_md_phys_command, COMMAND_EXEC, "display memory words <physical addr> [count]"); + register_command(cmd_ctx, arm720t_cmd, "mdh_phys", arm720t_handle_md_phys_command, COMMAND_EXEC, "display memory half-words <physical addr> [count]"); + register_command(cmd_ctx, arm720t_cmd, "mdb_phys", arm720t_handle_md_phys_command, COMMAND_EXEC, "display memory bytes <physical addr> [count]"); + + register_command(cmd_ctx, arm720t_cmd, "mww_phys", arm720t_handle_mw_phys_command, COMMAND_EXEC, "write memory word <physical addr> <value>"); + register_command(cmd_ctx, arm720t_cmd, "mwh_phys", arm720t_handle_mw_phys_command, COMMAND_EXEC, "write memory half-word <physical addr> <value>"); + register_command(cmd_ctx, arm720t_cmd, "mwb_phys", arm720t_handle_mw_phys_command, COMMAND_EXEC, "write memory byte <physical addr> <value>"); + + return ERROR_OK; +} + +int arm720t_handle_cp15_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int retval; + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm7tdmi_common_t *arm7tdmi; + arm720t_common_t *arm720t; + arm_jtag_t *jtag_info; + + if (arm720t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm7tdmi, &arm720t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM720t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + /* one or more argument, access a single register (write if second argument is given */ + if (argc >= 1) + { + u32 opcode = strtoul(args[0], NULL, 0); + + if (argc == 1) + { + u32 value; + if ((retval = arm720t_read_cp15(target, opcode, &value)) != ERROR_OK) + { + command_print(cmd_ctx, "couldn't access cp15 with opcode 0x%8.8x", opcode); + return ERROR_OK; + } + jtag_execute_queue(); + + command_print(cmd_ctx, "0x%8.8x: 0x%8.8x", opcode, value); + } + else if (argc == 2) + { + u32 value = strtoul(args[1], NULL, 0); + if ((retval = arm720t_write_cp15(target, opcode, value)) != ERROR_OK) + { + command_print(cmd_ctx, "couldn't access cp15 with opcode 0x%8.8x", opcode); + return ERROR_OK; + } + command_print(cmd_ctx, "0x%8.8x: 0x%8.8x", opcode, value); + } + } + + return ERROR_OK; +} + +int arm720t_handle_virt2phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm7tdmi_common_t *arm7tdmi; + arm720t_common_t *arm720t; + arm_jtag_t *jtag_info; + + if (arm720t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm7tdmi, &arm720t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM720t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + return armv4_5_mmu_handle_virt2phys_command(cmd_ctx, cmd, args, argc, target, &arm720t->armv4_5_mmu); +} + +int arm720t_handle_md_phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm7tdmi_common_t *arm7tdmi; + arm720t_common_t *arm720t; + arm_jtag_t *jtag_info; + + if (arm720t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm7tdmi, &arm720t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM720t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + return armv4_5_mmu_handle_md_phys_command(cmd_ctx, cmd, args, argc, target, &arm720t->armv4_5_mmu); +} + +int arm720t_handle_mw_phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm7tdmi_common_t *arm7tdmi; + arm720t_common_t *arm720t; + arm_jtag_t *jtag_info; + + if (arm720t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm7tdmi, &arm720t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM720t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + return armv4_5_mmu_handle_mw_phys_command(cmd_ctx, cmd, args, argc, target, &arm720t->armv4_5_mmu); +} + diff --git a/src/target/arm720t.h b/src/target/arm720t.h new file mode 100644 index 00000000..2479b548 --- /dev/null +++ b/src/target/arm720t.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ARM720T_H +#define ARM720T_H + +#include "target.h" +#include "register.h" +#include "embeddedice.h" +#include "arm_jtag.h" +#include "arm7tdmi.h" +#include "armv4_5_mmu.h" +#include "armv4_5_cache.h" + +#define ARM720T_COMMON_MAGIC 0xa720a720 + +typedef struct arm720t_common_s +{ + int common_magic; + armv4_5_mmu_common_t armv4_5_mmu; + arm7tdmi_common_t arm7tdmi_common; + u32 cp15_control_reg; + u32 fsr; + u32 far; +} arm720t_common_t; + +#endif /* ARM720T_H */ diff --git a/src/target/arm7_9_common.c b/src/target/arm7_9_common.c new file mode 100644 index 00000000..d167041f --- /dev/null +++ b/src/target/arm7_9_common.c @@ -0,0 +1,2339 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" + +#include "embeddedice.h" +#include "target.h" +#include "armv4_5.h" +#include "arm_jtag.h" +#include "jtag.h" +#include "log.h" +#include "arm7_9_common.h" +#include "breakpoints.h" + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <errno.h> + +int arm7_9_debug_entry(target_t *target); +int arm7_9_enable_sw_bkpts(struct target_s *target); + +/* command handler forward declarations */ +int handle_arm7_9_write_xpsr_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_write_xpsr_im8_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_read_core_reg_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_write_core_reg_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_sw_bkpts_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_force_hw_bkpts_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_dbgrq_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_fast_writes_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_arm7_9_dcc_downloads_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +int arm7_9_reinit_embeddedice(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + breakpoint_t *breakpoint = target->breakpoints; + + arm7_9->wp_available = 2; + arm7_9->wp0_used = 0; + arm7_9->wp1_used = 0; + + /* mark all hardware breakpoints as unset */ + while (breakpoint) + { + if (breakpoint->type == BKPT_HARD) + { + breakpoint->set = 0; + } + breakpoint = breakpoint->next; + } + + if (arm7_9->sw_bkpts_enabled && arm7_9->sw_bkpts_use_wp) + { + arm7_9->sw_bkpts_enabled = 0; + arm7_9_enable_sw_bkpts(target); + } + + arm7_9->reinit_embeddedice = 0; + + return ERROR_OK; +} + +int arm7_9_jtag_callback(enum jtag_event event, void *priv) +{ + target_t *target = priv; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + /* a test-logic reset occured + * the EmbeddedICE registers have been reset + * hardware breakpoints have been cleared + */ + if (event == JTAG_TRST_ASSERTED) + { + arm7_9->reinit_embeddedice = 1; + } + + return ERROR_OK; +} + +int arm7_9_get_arch_pointers(target_t *target, armv4_5_common_t **armv4_5_p, arm7_9_common_t **arm7_9_p) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + return -1; + } + + if (arm7_9->common_magic != ARM7_9_COMMON_MAGIC) + { + return -1; + } + + *armv4_5_p = armv4_5; + *arm7_9_p = arm7_9; + + return ERROR_OK; +} + +int arm7_9_set_breakpoint(struct target_s *target, breakpoint_t *breakpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (arm7_9->force_hw_bkpts) + breakpoint->type = BKPT_HARD; + + if (breakpoint->set) + { + WARNING("breakpoint already set"); + return ERROR_OK; + } + + if (breakpoint->type == BKPT_HARD) + { + /* either an ARM (4 byte) or Thumb (2 byte) breakpoint */ + u32 mask = (breakpoint->length == 4) ? 0x3u : 0x1u; + if (!arm7_9->wp0_used) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_VALUE], breakpoint->address); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK], mask); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK], 0xffffffffu); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK], ~EICE_W_CTRL_nOPC & 0xff); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], EICE_W_CTRL_ENABLE); + + jtag_execute_queue(); + arm7_9->wp0_used = 1; + breakpoint->set = 1; + } + else if (!arm7_9->wp1_used) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_VALUE], breakpoint->address); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_MASK], mask); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_MASK], 0xffffffffu); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_MASK], ~EICE_W_CTRL_nOPC & 0xff); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], EICE_W_CTRL_ENABLE); + + jtag_execute_queue(); + arm7_9->wp1_used = 1; + breakpoint->set = 2; + } + else + { + ERROR("BUG: no hardware comparator available"); + return ERROR_OK; + } + } + else if (breakpoint->type == BKPT_SOFT) + { + if (breakpoint->length == 4) + { + target->type->read_memory(target, breakpoint->address, 4, 1, breakpoint->orig_instr); + target->type->write_memory(target, breakpoint->address, 4, 1, (u8*)(&arm7_9->arm_bkpt)); + } + else + { + target->type->read_memory(target, breakpoint->address, 2, 1, breakpoint->orig_instr); + target->type->read_memory(target, breakpoint->address, 2, 1, (u8*)(&arm7_9->arm_bkpt)); + } + breakpoint->set = 1; + } + + return ERROR_OK; + +} + +int arm7_9_unset_breakpoint(struct target_s *target, breakpoint_t *breakpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!breakpoint->set) + { + WARNING("breakpoint not set"); + return ERROR_OK; + } + + if (breakpoint->type == BKPT_HARD) + { + if (breakpoint->set == 1) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], 0x0); + jtag_execute_queue(); + arm7_9->wp0_used = 0; + } + else if (breakpoint->set == 2) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], 0x0); + jtag_execute_queue(); + arm7_9->wp1_used = 0; + } + breakpoint->set = 0; + } + else + { + if (breakpoint->length == 4) + { + target->type->write_memory(target, breakpoint->address, 4, 1, breakpoint->orig_instr); + } + else + { + target->type->write_memory(target, breakpoint->address, 2, 1, breakpoint->orig_instr); + } + breakpoint->set = 0; + } + + return ERROR_OK; +} + +int arm7_9_add_breakpoint(struct target_s *target, u32 address, u32 length, enum breakpoint_type type) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (arm7_9->force_hw_bkpts) + { + type = BKPT_HARD; + } + + if ((type == BKPT_SOFT) && (arm7_9->sw_bkpts_enabled == 0)) + { + INFO("sw breakpoint requested, but software breakpoints not enabled"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if ((type == BKPT_HARD) && (arm7_9->wp_available < 1)) + { + INFO("no watchpoint unit available for hardware breakpoint"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if (type == BKPT_HARD) + arm7_9->wp_available--; + + if ((length != 2) && (length != 4)) + { + INFO("only breakpoints of two (Thumb) or four (ARM) bytes length supported"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + return ERROR_OK; +} + +int arm7_9_remove_breakpoint(struct target_s *target, breakpoint_t *breakpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (breakpoint->set) + { + arm7_9_unset_breakpoint(target, breakpoint); + } + + arm7_9->wp_available++; + + return ERROR_OK; +} + +int arm7_9_set_watchpoint(struct target_s *target, watchpoint_t *watchpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + int rw_mask = 1; + u32 mask; + + mask = watchpoint->length - 1; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (watchpoint->rw == WPT_ACCESS) + rw_mask = 0; + else + rw_mask = 1; + + if (!arm7_9->wp0_used) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_VALUE], watchpoint->address); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK], mask); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK], watchpoint->mask); + if( watchpoint->mask != 0xffffffffu ) + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_VALUE], watchpoint->value); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK], 0xff & ~EICE_W_CTRL_nOPC & ~rw_mask); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], EICE_W_CTRL_ENABLE | EICE_W_CTRL_nOPC | (watchpoint->rw & 1)); + + jtag_execute_queue(); + watchpoint->set = 1; + arm7_9->wp0_used = 2; + } + else if (!arm7_9->wp1_used) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_VALUE], watchpoint->address); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_MASK], mask); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_MASK], watchpoint->mask); + if( watchpoint->mask != 0xffffffffu ) + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_VALUE], watchpoint->value); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_MASK], 0xff & ~EICE_W_CTRL_nOPC & ~rw_mask); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], EICE_W_CTRL_ENABLE | EICE_W_CTRL_nOPC | (watchpoint->rw & 1)); + + jtag_execute_queue(); + watchpoint->set = 2; + arm7_9->wp1_used = 2; + } + else + { + ERROR("BUG: no hardware comparator available"); + return ERROR_OK; + } + + return ERROR_OK; +} + +int arm7_9_unset_watchpoint(struct target_s *target, watchpoint_t *watchpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!watchpoint->set) + { + WARNING("breakpoint not set"); + return ERROR_OK; + } + + if (watchpoint->set == 1) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], 0x0); + jtag_execute_queue(); + arm7_9->wp0_used = 0; + } + else if (watchpoint->set == 2) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], 0x0); + jtag_execute_queue(); + arm7_9->wp1_used = 0; + } + watchpoint->set = 0; + + return ERROR_OK; +} + +int arm7_9_add_watchpoint(struct target_s *target, u32 address, u32 length, enum watchpoint_rw rw) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (arm7_9->wp_available < 1) + { + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if ((length != 1) && (length != 2) && (length != 4)) + { + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + arm7_9->wp_available--; + + return ERROR_OK; +} + +int arm7_9_remove_watchpoint(struct target_s *target, watchpoint_t *watchpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (watchpoint->set) + { + arm7_9_unset_watchpoint(target, watchpoint); + } + + arm7_9->wp_available++; + + return ERROR_OK; +} + +int arm7_9_enable_sw_bkpts(struct target_s *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + int retval; + + if (arm7_9->sw_bkpts_enabled) + return ERROR_OK; + + if (arm7_9->wp_available-- < 1) + { + WARNING("can't enable sw breakpoints with no watchpoint unit available"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if (!arm7_9->wp0_used) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_VALUE], arm7_9->arm_bkpt); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK], 0x0); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK], 0xffffffffu); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK], ~EICE_W_CTRL_nOPC & 0xff); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], EICE_W_CTRL_ENABLE); + arm7_9->sw_bkpts_enabled = 1; + arm7_9->wp0_used = 3; + } + else if (!arm7_9->wp1_used) + { + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_VALUE], arm7_9->arm_bkpt); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_MASK], 0x0); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_MASK], 0xffffffffu); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_MASK], ~EICE_W_CTRL_nOPC & 0xff); + embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], EICE_W_CTRL_ENABLE); + arm7_9->sw_bkpts_enabled = 2; + arm7_9->wp1_used = 3; + } + else + { + ERROR("BUG: both watchpoints used, but wp_available >= 1"); + exit(-1); + } + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("error writing EmbeddedICE registers to enable sw breakpoints"); + exit(-1); + }; + + return ERROR_OK; +} + +int arm7_9_disable_sw_bkpts(struct target_s *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + if (!arm7_9->sw_bkpts_enabled) + return ERROR_OK; + + if (arm7_9->sw_bkpts_enabled == 1) + { + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], 0x0); + arm7_9->sw_bkpts_enabled = 0; + arm7_9->wp0_used = 0; + arm7_9->wp_available++; + } + else if (arm7_9->sw_bkpts_enabled == 2) + { + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], 0x0); + arm7_9->sw_bkpts_enabled = 0; + arm7_9->wp1_used = 0; + arm7_9->wp_available++; + } + + return ERROR_OK; +} + +int arm7_9_execute_sys_speed(struct target_s *target) +{ + int timeout; + int retval; + + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + + /* set RESTART instruction */ + jtag_add_end_state(TAP_RTI); + arm_jtag_set_instr(jtag_info, 0x4); + + for (timeout=0; timeout<50; timeout++) + { + /* read debug status register */ + embeddedice_read_reg(dbg_stat); + if ((retval = jtag_execute_queue()) != ERROR_OK) + return retval; + if ((buf_get_u32(dbg_stat->value, EICE_DBG_STATUS_DBGACK, 1)) + && (buf_get_u32(dbg_stat->value, EICE_DBG_STATUS_SYSCOMP, 1))) + break; + usleep(100000); + } + if (timeout == 50) + { + ERROR("timeout waiting for SYSCOMP & DBGACK, last DBG_STATUS: %x", buf_get_u32(dbg_stat->value, 0, dbg_stat->size)); + return ERROR_TARGET_TIMEOUT; + } + + return ERROR_OK; +} + +int arm7_9_execute_fast_sys_speed(struct target_s *target) +{ + u8 check_value[4], check_mask[4]; + + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + + /* set RESTART instruction */ + jtag_add_end_state(TAP_RTI); + arm_jtag_set_instr(jtag_info, 0x4); + + /* check for DBGACK and SYSCOMP set (others don't care) */ + buf_set_u32(check_value, 0, 32, 0x9); + buf_set_u32(check_mask, 0, 32, 0x9); + + /* read debug status register */ + embeddedice_read_reg_w_check(dbg_stat, check_value, check_value); + + return ERROR_OK; +} + +enum target_state arm7_9_poll(target_t *target) +{ + int retval; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + reg_t *dbg_ctrl = &arm7_9->eice_cache->reg_list[EICE_DBG_CTRL]; + + if (arm7_9->reinit_embeddedice) + { + arm7_9_reinit_embeddedice(target); + } + + /* read debug status register */ + embeddedice_read_reg(dbg_stat); + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + switch (retval) + { + case ERROR_JTAG_QUEUE_FAILED: + ERROR("JTAG queue failed while reading EmbeddedICE status register"); + exit(-1); + break; + default: + break; + } + } + + if (buf_get_u32(dbg_stat->value, EICE_DBG_STATUS_DBGACK, 1)) + { + DEBUG("DBGACK set, dbg_state->value: 0x%x", buf_get_u32(dbg_stat->value, 0, 32)); + if ((target->state == TARGET_UNKNOWN)) + { + WARNING("DBGACK set while target was in unknown state. Reset or initialize target before resuming"); + target->state = TARGET_RUNNING; + } + if ((target->state == TARGET_RUNNING) || (target->state == TARGET_RESET)) + { + target->state = TARGET_HALTED; + if ((retval = arm7_9_debug_entry(target)) != ERROR_OK) + return retval; + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + } + if (target->state == TARGET_DEBUG_RUNNING) + { + target->state = TARGET_HALTED; + if ((retval = arm7_9_debug_entry(target)) != ERROR_OK) + return retval; + + target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED); + } + } + else + { + if (target->state != TARGET_DEBUG_RUNNING) + target->state = TARGET_RUNNING; + } + + return target->state; +} + +int arm7_9_assert_reset(target_t *target) +{ + int retval; + + DEBUG("target->state: %s", target_state_strings[target->state]); + + if (target->state == TARGET_HALTED || target->state == TARGET_UNKNOWN) + { + /* assert SRST and TRST */ + /* system would get ouf sync if we didn't reset test-logic, too */ + if ((retval = jtag_add_reset(1, 1)) != ERROR_OK) + { + if (retval == ERROR_JTAG_RESET_CANT_SRST) + { + WARNING("can't assert srst"); + return retval; + } + else + { + ERROR("unknown error"); + exit(-1); + } + } + jtag_add_sleep(5000); + if ((retval = jtag_add_reset(0, 1)) != ERROR_OK) + { + if (retval == ERROR_JTAG_RESET_WOULD_ASSERT_TRST) + { + WARNING("srst resets test logic, too"); + retval = jtag_add_reset(1, 1); + } + } + } + else + { + if ((retval = jtag_add_reset(0, 1)) != ERROR_OK) + { + if (retval == ERROR_JTAG_RESET_WOULD_ASSERT_TRST) + { + WARNING("srst resets test logic, too"); + retval = jtag_add_reset(1, 1); + } + + if (retval == ERROR_JTAG_RESET_CANT_SRST) + { + WARNING("can't assert srst"); + return retval; + } + else if (retval != ERROR_OK) + { + ERROR("unknown error"); + exit(-1); + } + } + } + + target->state = TARGET_RESET; + jtag_add_sleep(50000); + + armv4_5_invalidate_core_regs(target); + + return ERROR_OK; + +} + +int arm7_9_deassert_reset(target_t *target) +{ + DEBUG("target->state: %s", target_state_strings[target->state]); + + /* deassert reset lines */ + jtag_add_reset(0, 0); + + return ERROR_OK; + +} + +int arm7_9_soft_reset_halt(struct target_s *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + int i; + + if (target->state == TARGET_RUNNING) + { + target->type->halt(target); + } + + while (buf_get_u32(dbg_stat->value, EICE_DBG_CONTROL_DBGACK, 1) == 0) + { + embeddedice_read_reg(dbg_stat); + jtag_execute_queue(); + } + target->state = TARGET_HALTED; + + /* all register content is now invalid */ + armv4_5_invalidate_core_regs(target); + + /* SVC, ARM state, IRQ and FIQ disabled */ + buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8, 0xd3); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + + /* start fetching from 0x0 */ + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, 0x0); + armv4_5->core_cache->reg_list[15].dirty = 1; + armv4_5->core_cache->reg_list[15].valid = 1; + + armv4_5->core_mode = ARMV4_5_MODE_SVC; + armv4_5->core_state = ARMV4_5_STATE_ARM; + + /* reset registers */ + for (i = 0; i <= 14; i++) + { + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).value, 0, 32, 0xffffffff); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).dirty = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).valid = 1; + } + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + + return ERROR_OK; +} + +int arm7_9_halt(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + reg_t *dbg_ctrl = &arm7_9->eice_cache->reg_list[EICE_DBG_CTRL]; + + DEBUG("target->state: %s", target_state_strings[target->state]); + + if (target->state == TARGET_HALTED) + { + WARNING("target was already halted"); + return ERROR_TARGET_ALREADY_HALTED; + } + + if (target->state == TARGET_UNKNOWN) + { + WARNING("target was in unknown state when halt was requested"); + } + + if (arm7_9->use_dbgrq) + { + /* program EmbeddedICE Debug Control Register to assert DBGRQ + */ + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_DBGRQ, 1, 1); + embeddedice_store_reg(dbg_ctrl); + } + else + { + /* program watchpoint unit to match on any address + */ + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK], 0xffffffff); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK], 0xffffffff); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], 0x100); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK], 0xf7); + } + + target->debug_reason = DBG_REASON_DBGRQ; + + return ERROR_OK; +} + +int arm7_9_clear_halt(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + reg_t *dbg_ctrl = &arm7_9->eice_cache->reg_list[EICE_DBG_CTRL]; + + if (arm7_9->use_dbgrq) + { + /* program EmbeddedICE Debug Control Register to deassert DBGRQ + */ + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_DBGRQ, 1, 0); + embeddedice_store_reg(dbg_ctrl); + } + else + { + /* restore registers if watchpoint unit 0 was in use + */ + if (arm7_9->wp0_used) + { + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK]); + } + /* control value always has to be restored, as it was either disabled, + * or enabled with possibly different bits + */ + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE]); + } + + return ERROR_OK; +} + +int arm7_9_debug_entry(target_t *target) +{ + int i; + u32 context[16]; + u32* context_p[16]; + u32 r0_thumb, pc_thumb; + u32 cpsr; + int retval; + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + reg_t *dbg_ctrl = &arm7_9->eice_cache->reg_list[EICE_DBG_CTRL]; + +#ifdef _DEBUG_ARM7_9_ + DEBUG(""); +#endif + + if (arm7_9->pre_debug_entry) + arm7_9->pre_debug_entry(target); + + /* program EmbeddedICE Debug Control Register to assert DBGACK and INTDIS + * ensure that DBGRQ is cleared + */ + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_DBGACK, 1, 1); + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_DBGRQ, 1, 0); + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_INTDIS, 1, 1); + embeddedice_store_reg(dbg_ctrl); + + arm7_9_clear_halt(target); + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + switch (retval) + { + case ERROR_JTAG_QUEUE_FAILED: + ERROR("JTAG queue failed while writing EmbeddedICE control register"); + exit(-1); + break; + default: + break; + } + } + + if ((retval = arm7_9->examine_debug_reason(target)) != ERROR_OK) + return retval; + + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* if the target is in Thumb state, change to ARM state */ + if (buf_get_u32(dbg_stat->value, EICE_DBG_STATUS_ITBIT, 1)) + { + DEBUG("target entered debug from Thumb state"); + /* Entered debug from Thumb mode */ + armv4_5->core_state = ARMV4_5_STATE_THUMB; + arm7_9->change_to_arm(target, &r0_thumb, &pc_thumb); + DEBUG("r0_thumb: 0x%8.8x, pc_thumb: 0x%8.8x", r0_thumb, pc_thumb); + } + else + { + DEBUG("target entered debug from ARM state"); + /* Entered debug from ARM mode */ + armv4_5->core_state = ARMV4_5_STATE_ARM; + } + + for (i = 0; i < 16; i++) + context_p[i] = &context[i]; + /* save core registers (r0 - r15 of current core mode) */ + arm7_9->read_core_regs(target, 0xffff, context_p); + + arm7_9->read_xpsr(target, &cpsr, 0); + + if ((retval = jtag_execute_queue()) != ERROR_OK) + return retval; + buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32, cpsr); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 0; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + + armv4_5->core_mode = cpsr & 0x1f; + DEBUG("target entered debug state in %s mode", armv4_5_mode_strings[armv4_5_mode_to_number(armv4_5->core_mode)]); + + if (armv4_5_mode_to_number(armv4_5->core_mode) == -1) + { + target->state = TARGET_UNKNOWN; + ERROR("cpsr contains invalid mode value - communication failure"); + return ERROR_TARGET_FAILURE; + } + + if (armv4_5->core_state == ARMV4_5_STATE_THUMB) + { + DEBUG("thumb state, applying fixups"); + context[0] = r0_thumb; + context[15] = pc_thumb; + } else if (armv4_5->core_state == ARMV4_5_STATE_ARM) + { + /* adjust value stored by STM */ + context[15] -= 3 * 4; + } + + if ((target->debug_reason == DBG_REASON_BREAKPOINT) + || (target->debug_reason == DBG_REASON_SINGLESTEP) + || (target->debug_reason == DBG_REASON_WATCHPOINT) + || (target->debug_reason == DBG_REASON_WPTANDBKPT) + || ((target->debug_reason == DBG_REASON_DBGRQ) && (arm7_9->use_dbgrq == 0))) + context[15] -= 3 * ((armv4_5->core_state == ARMV4_5_STATE_ARM) ? 4 : 2); + else if (target->debug_reason == DBG_REASON_DBGRQ) + context[15] -= arm7_9->dbgreq_adjust_pc * ((armv4_5->core_state == ARMV4_5_STATE_ARM) ? 4 : 2); + else + { + ERROR("unknown debug reason: %i", target->debug_reason); + } + + + for (i=0; i<=15; i++) + { + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).value, 0, 32, context[i]); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).dirty = 0; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).valid = 1; + } + + DEBUG("entered debug state at PC 0x%x", context[15]); + + /* exceptions other than USR & SYS have a saved program status register */ + if ((armv4_5_mode_to_number(armv4_5->core_mode) != ARMV4_5_MODE_USR) && (armv4_5_mode_to_number(armv4_5->core_mode) != ARMV4_5_MODE_SYS)) + { + u32 spsr; + arm7_9->read_xpsr(target, &spsr, 1); + jtag_execute_queue(); + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 16).value, 0, 32, spsr); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 16).dirty = 0; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 16).valid = 1; + } + + /* r0 and r15 (pc) have to be restored later */ + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 0).dirty = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 15).dirty = 1; + + if ((retval = jtag->execute_queue()) != ERROR_OK) + return retval; + + if (arm7_9->post_debug_entry) + arm7_9->post_debug_entry(target); + + return ERROR_OK; +} + +int arm7_9_full_context(target_t *target) +{ + int i; + int retval; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + DEBUG(""); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* iterate through processor modes (User, FIQ, IRQ, SVC, ABT, UND) + * SYS shares registers with User, so we don't touch SYS + */ + for(i = 0; i < 6; i++) + { + u32 mask = 0; + u32* reg_p[16]; + int j; + int valid = 1; + + /* check if there are invalid registers in the current mode + */ + for (j = 0; j <= 16; j++) + { + if (ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).valid == 0) + valid = 0; + } + + if (!valid) + { + u32 tmp_cpsr; + + /* change processor mode */ + tmp_cpsr = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8) & 0xE0; + tmp_cpsr |= armv4_5_number_to_mode(i); + arm7_9->write_xpsr_im8(target, tmp_cpsr & 0xff, 0, 0); + + for (j = 0; j < 15; j++) + { + if (ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).valid == 0) + { + reg_p[j] = (u32*)ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).value; + mask |= 1 << j; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).valid = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).dirty = 0; + } + } + + /* if only the PSR is invalid, mask is all zeroes */ + if (mask) + arm7_9->read_core_regs(target, mask, reg_p); + + /* check if the PSR has to be read */ + if (ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), 16).valid == 0) + { + arm7_9->read_xpsr(target, (u32*)ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), 16).value, 1); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), 16).valid = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), 16).dirty = 0; + } + } + } + + /* restore processor mode */ + arm7_9->write_xpsr_im8(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8), 0, 0); + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG failure"); + exit(-1); + } + return ERROR_OK; +} + +int arm7_9_restore_context(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + reg_t *reg; + armv4_5_core_reg_t *reg_arch_info; + enum armv4_5_mode current_mode = armv4_5->core_mode; + int i, j; + int dirty; + int mode_change; + + DEBUG(""); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (arm7_9->pre_restore_context) + arm7_9->pre_restore_context(target); + + /* iterate through processor modes (User, FIQ, IRQ, SVC, ABT, UND) + * SYS shares registers with User, so we don't touch SYS + */ + for (i = 0; i < 6; i++) + { + DEBUG("examining %s mode", armv4_5_mode_strings[i]); + dirty = 0; + mode_change = 0; + /* check if there are dirty registers in the current mode + */ + for (j = 0; j <= 16; j++) + { + reg = &ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j); + reg_arch_info = reg->arch_info; + if (reg->dirty == 1) + { + if (reg->valid == 1) + { + dirty = 1; + DEBUG("examining dirty reg: %s", reg->name); + if ((reg_arch_info->mode != ARMV4_5_MODE_ANY) + && (reg_arch_info->mode != current_mode) + && !((reg_arch_info->mode == ARMV4_5_MODE_USR) && (armv4_5->core_mode == ARMV4_5_MODE_SYS)) + && !((reg_arch_info->mode == ARMV4_5_MODE_SYS) && (armv4_5->core_mode == ARMV4_5_MODE_USR))) + { + mode_change = 1; + DEBUG("require mode change"); + } + } + else + { + ERROR("BUG: dirty register '%s', but no valid data", reg->name); + exit(-1); + } + } + } + + if (dirty) + { + u32 mask = 0x0; + int num_regs = 0; + u32 regs[16]; + + if (mode_change) + { + u32 tmp_cpsr; + + /* change processor mode */ + tmp_cpsr = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8) & 0xE0; + tmp_cpsr |= armv4_5_number_to_mode(i); + arm7_9->write_xpsr_im8(target, tmp_cpsr & 0xff, 0, 0); + current_mode = armv4_5_number_to_mode(i); + } + + for (j = 0; j <= 14; j++) + { + reg = &ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j); + reg_arch_info = reg->arch_info; + + + if (reg->dirty == 1) + { + regs[j] = buf_get_u32(reg->value, 0, 32); + mask |= 1 << j; + num_regs++; + reg->dirty = 0; + reg->valid = 1; + DEBUG("writing register %i of mode %s with value 0x%8.8x", j, armv4_5_mode_strings[i], regs[j]); + } + } + + if (mask) + { + arm7_9->write_core_regs(target, mask, regs); + } + + reg = &ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), 16); + reg_arch_info = reg->arch_info; + if ((reg->dirty) && (reg_arch_info->mode != ARMV4_5_MODE_ANY)) + { + DEBUG("writing SPSR of mode %i with value 0x%8.8x", i, buf_get_u32(reg->value, 0, 32)); + arm7_9->write_xpsr(target, buf_get_u32(reg->value, 0, 32), 1); + } + } + } + + if ((armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty == 0) && (armv4_5->core_mode != current_mode)) + { + /* restore processor mode */ + u32 tmp_cpsr; + + tmp_cpsr = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8) & 0xE0; + tmp_cpsr |= armv4_5_number_to_mode(i); + DEBUG("writing lower 8 bit of cpsr with value 0x%2.2x", tmp_cpsr); + arm7_9->write_xpsr_im8(target, tmp_cpsr & 0xff, 0, 0); + } + else if (armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty == 1) + { + /* CPSR has been changed, full restore necessary */ + DEBUG("writing cpsr with value 0x%8.8x", buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32)); + arm7_9->write_xpsr(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), 0); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 0; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + } + + /* restore PC */ + DEBUG("writing PC with value 0x%8.8x", buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + arm7_9->write_pc(target, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + armv4_5->core_cache->reg_list[15].dirty = 0; + + if (arm7_9->post_restore_context) + arm7_9->post_restore_context(target); + + return ERROR_OK; +} + +int arm7_9_restart_core(struct target_s *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* set RESTART instruction */ + jtag_add_end_state(TAP_RTI); + arm_jtag_set_instr(jtag_info, 0x4); + + jtag_add_runtest(1, TAP_RTI); + if ((jtag_execute_queue()) != ERROR_OK) + { + exit(-1); + } + + return ERROR_OK; +} + +void arm7_9_enable_watchpoints(struct target_s *target) +{ + watchpoint_t *watchpoint = target->watchpoints; + + while (watchpoint) + { + if (watchpoint->set == 0) + arm7_9_set_watchpoint(target, watchpoint); + watchpoint = watchpoint->next; + } +} + +void arm7_9_enable_breakpoints(struct target_s *target) +{ + breakpoint_t *breakpoint = target->breakpoints; + + /* set any pending breakpoints */ + while (breakpoint) + { + if (breakpoint->set == 0) + arm7_9_set_breakpoint(target, breakpoint); + breakpoint = breakpoint->next; + } +} + +void arm7_9_disable_bkpts_and_wpts(struct target_s *target) +{ + breakpoint_t *breakpoint = target->breakpoints; + watchpoint_t *watchpoint = target->watchpoints; + + /* set any pending breakpoints */ + while (breakpoint) + { + if (breakpoint->set != 0) + arm7_9_unset_breakpoint(target, breakpoint); + breakpoint = breakpoint->next; + } + + while (watchpoint) + { + if (watchpoint->set != 0) + arm7_9_unset_watchpoint(target, watchpoint); + watchpoint = watchpoint->next; + } +} + +int arm7_9_resume(struct target_s *target, int current, u32 address, int handle_breakpoints, int debug_execution) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + breakpoint_t *breakpoint = target->breakpoints; + reg_t *dbg_ctrl = &arm7_9->eice_cache->reg_list[EICE_DBG_CTRL]; + + DEBUG(""); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!debug_execution) + { + target_free_all_working_areas(target); + } + + /* current = 1: continue on current pc, otherwise continue at <address> */ + if (!current) + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, address); + + /* the front-end may request us not to handle breakpoints */ + if (handle_breakpoints) + { + if ((breakpoint = breakpoint_find(target, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)))) + { + DEBUG("unset breakpoint at 0x%8.8x", breakpoint->address); + arm7_9_unset_breakpoint(target, breakpoint); + + DEBUG("enable single-step"); + arm7_9->enable_single_step(target); + + target->debug_reason = DBG_REASON_SINGLESTEP; + + arm7_9_restore_context(target); + + if (armv4_5->core_state == ARMV4_5_STATE_ARM) + arm7_9->branch_resume(target); + else if (armv4_5->core_state == ARMV4_5_STATE_THUMB) + { + arm7_9->branch_resume_thumb(target); + } + else + { + ERROR("unhandled core state"); + exit(-1); + } + + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_DBGACK, 1, 0); + embeddedice_write_reg(dbg_ctrl, buf_get_u32(dbg_ctrl->value, 0, dbg_ctrl->size)); + arm7_9_execute_sys_speed(target); + + DEBUG("disable single-step"); + arm7_9->disable_single_step(target); + + arm7_9_debug_entry(target); + DEBUG("new PC after step: 0x%8.8x", buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + + DEBUG("set breakpoint at 0x%8.8x", breakpoint->address); + arm7_9_set_breakpoint(target, breakpoint); + } + } + + /* enable any pending breakpoints and watchpoints */ + arm7_9_enable_breakpoints(target); + arm7_9_enable_watchpoints(target); + + arm7_9_restore_context(target); + + if (armv4_5->core_state == ARMV4_5_STATE_ARM) + { + arm7_9->branch_resume(target); + } + else if (armv4_5->core_state == ARMV4_5_STATE_THUMB) + { + arm7_9->branch_resume_thumb(target); + } + else + { + ERROR("unhandled core state"); + exit(-1); + } + + /* deassert DBGACK and INTDIS */ + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_DBGACK, 1, 0); + /* INTDIS only when we really resume, not during debug execution */ + if (!debug_execution) + buf_set_u32(dbg_ctrl->value, EICE_DBG_CONTROL_INTDIS, 1, 0); + embeddedice_write_reg(dbg_ctrl, buf_get_u32(dbg_ctrl->value, 0, dbg_ctrl->size)); + + arm7_9_restart_core(target); + + target->debug_reason = DBG_REASON_NOTHALTED; + + if (!debug_execution) + { + /* registers are now invalid */ + armv4_5_invalidate_core_regs(target); + target->state = TARGET_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + } + else + { + target->state = TARGET_DEBUG_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED); + } + + DEBUG("target resumed"); + + return ERROR_OK; +} + +void arm7_9_enable_eice_step(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + /* setup an inverse breakpoint on the current PC + * - comparator 1 matches the current address + * - rangeout from comparator 1 is connected to comparator 0 rangein + * - comparator 0 matches any address, as long as rangein is low */ + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK], 0xffffffff); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK], 0xffffffff); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], 0x100); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK], 0x77); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_VALUE], buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_MASK], 0); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_MASK], 0xffffffff); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], 0x0); + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_MASK], 0xf7); +} + +void arm7_9_disable_eice_step(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_VALUE]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_MASK]); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE]); +} + +int arm7_9_step(struct target_s *target, int current, u32 address, int handle_breakpoints) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + breakpoint_t *breakpoint = target->breakpoints; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* current = 1: continue on current pc, otherwise continue at <address> */ + if (!current) + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, address); + + /* the front-end may request us not to handle breakpoints */ + if (handle_breakpoints) + if ((breakpoint = breakpoint_find(target, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)))) + arm7_9_unset_breakpoint(target, breakpoint); + + target->debug_reason = DBG_REASON_SINGLESTEP; + + arm7_9_restore_context(target); + + arm7_9->enable_single_step(target); + + if (armv4_5->core_state == ARMV4_5_STATE_ARM) + { + arm7_9->branch_resume(target); + } + else if (armv4_5->core_state == ARMV4_5_STATE_THUMB) + { + arm7_9->branch_resume_thumb(target); + } + else + { + ERROR("unhandled core state"); + exit(-1); + } + + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + + arm7_9_execute_sys_speed(target); + arm7_9->disable_single_step(target); + + /* registers are now invalid */ + armv4_5_invalidate_core_regs(target); + + arm7_9_debug_entry(target); + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + + if (breakpoint) + arm7_9_set_breakpoint(target, breakpoint); + + DEBUG("target stepped"); + + return ERROR_OK; + +} + +int arm7_9_read_core_reg(struct target_s *target, int num, enum armv4_5_mode mode) +{ + u32* reg_p[16]; + int retval; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + enum armv4_5_mode reg_mode = ((armv4_5_core_reg_t*)ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).arch_info)->mode; + + if ((num < 0) || (num > 16)) + return ERROR_INVALID_ARGUMENTS; + + if ((mode != ARMV4_5_MODE_ANY) + && (mode != armv4_5->core_mode) + && (reg_mode != ARMV4_5_MODE_ANY)) + { + u32 tmp_cpsr; + + /* change processor mode */ + tmp_cpsr = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8) & 0xE0; + tmp_cpsr |= mode; + arm7_9->write_xpsr_im8(target, tmp_cpsr & 0xff, 0, 0); + } + + if ((num >= 0) && (num <= 15)) + { + /* read a normal core register */ + reg_p[num] = (u32*)ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).value; + + arm7_9->read_core_regs(target, 1 << num, reg_p); + } + else + { + /* read a program status register + * if the register mode is MODE_ANY, we read the cpsr, otherwise a spsr + */ + armv4_5_core_reg_t *arch_info = ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).arch_info; + int spsr = (arch_info->mode == ARMV4_5_MODE_ANY) ? 0 : 1; + + arm7_9->read_xpsr(target, (u32*)ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).value, spsr); + } + + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).valid = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).dirty = 0; + + if ((mode != ARMV4_5_MODE_ANY) + && (mode != armv4_5->core_mode) + && (reg_mode != ARMV4_5_MODE_ANY)) { + /* restore processor mode */ + arm7_9->write_xpsr_im8(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8), 0, 0); + } + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG failure"); + exit(-1); + } + + return ERROR_OK; + +} + +int arm7_9_write_core_reg(struct target_s *target, int num, enum armv4_5_mode mode, u32 value) +{ + u32 reg[16]; + int retval; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + enum armv4_5_mode reg_mode = ((armv4_5_core_reg_t*)ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).arch_info)->mode; + + if ((num < 0) || (num > 16)) + return ERROR_INVALID_ARGUMENTS; + + if ((mode != ARMV4_5_MODE_ANY) + && (mode != armv4_5->core_mode) + && (reg_mode != ARMV4_5_MODE_ANY)) { + u32 tmp_cpsr; + + /* change processor mode */ + tmp_cpsr = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8) & 0xE0; + tmp_cpsr |= mode; + arm7_9->write_xpsr_im8(target, tmp_cpsr & 0xff, 0, 0); + } + + if ((num >= 0) && (num <= 15)) + { + /* write a normal core register */ + reg[num] = value; + + arm7_9->write_core_regs(target, 1 << num, reg); + } + else + { + /* write a program status register + * if the register mode is MODE_ANY, we write the cpsr, otherwise a spsr + */ + armv4_5_core_reg_t *arch_info = ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).arch_info; + int spsr = (arch_info->mode == ARMV4_5_MODE_ANY) ? 0 : 1; + + arm7_9->write_xpsr(target, value, spsr); + } + + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).valid = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, num).dirty = 0; + + if ((mode != ARMV4_5_MODE_ANY) + && (mode != armv4_5->core_mode) + && (reg_mode != ARMV4_5_MODE_ANY)) { + /* restore processor mode */ + arm7_9->write_xpsr_im8(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8), 0, 0); + } + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG failure"); + exit(-1); + } + + return ERROR_OK; + +} + +int arm7_9_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + u32 reg[16]; + u32 *reg_p[16]; + int num_accesses = 0; + int thisrun_accesses; + u32 *buf32; + u16 *buf16; + u8 *buf8; + int i; + u32 cpsr; + int retval; + int last_reg = 0; + + DEBUG("address: 0x%8.8x, size: 0x%8.8x, count: 0x%8.8x", address, size, count); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* sanitize arguments */ + if (((size != 4) && (size != 2) && (size != 1)) || (count == 0) || !(buffer)) + return ERROR_INVALID_ARGUMENTS; + + if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u))) + return ERROR_TARGET_UNALIGNED_ACCESS; + + for (i = 0; i < 16; i++) + { + reg_p[i] = ®[i]; + } + + /* load the base register with the address of the first word */ + reg[0] = address; + arm7_9->write_core_regs(target, 0x1, reg); + + switch (size) + { + case 4: + buf32 = (u32*)buffer; + while (num_accesses < count) + { + u32 reg_list; + thisrun_accesses = ((count - num_accesses) >= 14) ? 14 : (count - num_accesses); + reg_list = (0xffff >> (15 - thisrun_accesses)) & 0xfffe; + + arm7_9->load_word_regs(target, reg_list); + arm7_9_execute_sys_speed(target); + + arm7_9->read_core_regs(target, reg_list, reg_p); + jtag_execute_queue(); + + for (i = 1; i <= thisrun_accesses; i++) + { + if (i > last_reg) + last_reg = i; + *(buf32++) = reg[i]; + } + num_accesses += thisrun_accesses; + } + break; + case 2: + buf16 = (u16*)buffer; + while (num_accesses < count) + { + u32 reg_list; + thisrun_accesses = ((count - num_accesses) >= 14) ? 14 : (count - num_accesses); + reg_list = (0xffff >> (15 - thisrun_accesses)) & 0xfffe; + + for (i = 1; i <= thisrun_accesses; i++) + { + if (i > last_reg) + last_reg = i; + arm7_9->load_hword_reg(target, i); + arm7_9_execute_sys_speed(target); + } + + arm7_9->read_core_regs(target, reg_list, reg_p); + jtag_execute_queue(); + + for (i = 1; i <= thisrun_accesses; i++) + { + *(buf16++) = reg[i] & 0xffff; + } + num_accesses += thisrun_accesses; + } + break; + case 1: + buf8 = buffer; + while (num_accesses < count) + { + u32 reg_list; + thisrun_accesses = ((count - num_accesses) >= 14) ? 14 : (count - num_accesses); + reg_list = (0xffff >> (15 - thisrun_accesses)) & 0xfffe; + + for (i = 1; i <= thisrun_accesses; i++) + { + if (i > last_reg) + last_reg = i; + arm7_9->load_byte_reg(target, i); + arm7_9_execute_sys_speed(target); + } + + arm7_9->read_core_regs(target, reg_list, reg_p); + jtag_execute_queue(); + + for (i = 1; i <= thisrun_accesses; i++) + { + *(buf8++) = reg[i] & 0xff; + } + num_accesses += thisrun_accesses; + } + break; + default: + ERROR("BUG: we shouldn't get here"); + exit(-1); + break; + } + + for (i=0; i<=last_reg; i++) + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).dirty = 1; + + arm7_9->read_xpsr(target, &cpsr, 0); + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while reading cpsr"); + exit(-1); + } + + if (((cpsr & 0x1f) == ARMV4_5_MODE_ABT) && (armv4_5->core_mode != ARMV4_5_MODE_ABT)) + { + ERROR("memory read caused data abort"); + + arm7_9->write_xpsr_im8(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8), 0, 0); + + return ERROR_TARGET_DATA_ABORT; + } + + return ERROR_OK; +} + +int arm7_9_write_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + u32 reg[16]; + int num_accesses = 0; + int thisrun_accesses; + u32 *buf32; + u16 *buf16; + u8 *buf8; + int i; + u32 cpsr; + int retval; + int last_reg = 0; + + DEBUG("address: 0x%8.8x, size: 0x%8.8x, count: 0x%8.8x", address, size, count); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* sanitize arguments */ + if (((size != 4) && (size != 2) && (size != 1)) || (count == 0) || !(buffer)) + return ERROR_INVALID_ARGUMENTS; + + if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u))) + return ERROR_TARGET_UNALIGNED_ACCESS; + + /* load the base register with the address of the first word */ + reg[0] = address; + arm7_9->write_core_regs(target, 0x1, reg); + + switch (size) + { + case 4: + buf32 = (u32*)buffer; + while (num_accesses < count) + { + u32 reg_list; + thisrun_accesses = ((count - num_accesses) >= 14) ? 14 : (count - num_accesses); + reg_list = (0xffff >> (15 - thisrun_accesses)) & 0xfffe; + + for (i = 1; i <= thisrun_accesses; i++) + { + if (i > last_reg) + last_reg = i; + reg[i] = *buf32++; + } + + arm7_9->write_core_regs(target, reg_list, reg); + + arm7_9->store_word_regs(target, reg_list); + + /* fast memory writes are only safe when the target is running + * from a sufficiently high clock (32 kHz is usually too slow) + */ + if (arm7_9->fast_memory_writes) + arm7_9_execute_fast_sys_speed(target); + else + arm7_9_execute_sys_speed(target); + + num_accesses += thisrun_accesses; + } + break; + case 2: + buf16 = (u16*)buffer; + while (num_accesses < count) + { + u32 reg_list; + thisrun_accesses = ((count - num_accesses) >= 14) ? 14 : (count - num_accesses); + reg_list = (0xffff >> (15 - thisrun_accesses)) & 0xfffe; + + for (i = 1; i <= thisrun_accesses; i++) + { + if (i > last_reg) + last_reg = i; + reg[i] = *buf16++ & 0xffff; + } + + arm7_9->write_core_regs(target, reg_list, reg); + + for (i = 1; i <= thisrun_accesses; i++) + { + arm7_9->store_hword_reg(target, i); + + /* fast memory writes are only safe when the target is running + * from a sufficiently high clock (32 kHz is usually too slow) + */ + if (arm7_9->fast_memory_writes) + arm7_9_execute_fast_sys_speed(target); + else + arm7_9_execute_sys_speed(target); + } + + num_accesses += thisrun_accesses; + } + break; + case 1: + buf8 = buffer; + while (num_accesses < count) + { + u32 reg_list; + thisrun_accesses = ((count - num_accesses) >= 14) ? 14 : (count - num_accesses); + reg_list = (0xffff >> (15 - thisrun_accesses)) & 0xfffe; + + for (i = 1; i <= thisrun_accesses; i++) + { + if (i > last_reg) + last_reg = i; + reg[i] = *buf8++ & 0xff; + } + + arm7_9->write_core_regs(target, reg_list, reg); + + for (i = 1; i <= thisrun_accesses; i++) + { + arm7_9->store_byte_reg(target, i); + /* fast memory writes are only safe when the target is running + * from a sufficiently high clock (32 kHz is usually too slow) + */ + if (arm7_9->fast_memory_writes) + arm7_9_execute_fast_sys_speed(target); + else + arm7_9_execute_sys_speed(target); + } + + num_accesses += thisrun_accesses; + } + break; + default: + ERROR("BUG: we shouldn't get here"); + exit(-1); + break; + } + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while writing target memory"); + exit(-1); + } + + for (i=0; i<=last_reg; i++) + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).dirty = 1; + + arm7_9->read_xpsr(target, &cpsr, 0); + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while reading cpsr"); + exit(-1); + } + + if (((cpsr & 0x1f) == ARMV4_5_MODE_ABT) && (armv4_5->core_mode != ARMV4_5_MODE_ABT)) + { + ERROR("memory write caused data abort"); + + arm7_9->write_xpsr_im8(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8), 0, 0); + + return ERROR_TARGET_DATA_ABORT; + } + + return ERROR_OK; +} + +int arm7_9_bulk_write_memory(target_t *target, u32 address, u32 count, u8 *buffer) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + enum armv4_5_state core_state = armv4_5->core_state; + u32 r0 = buf_get_u32(armv4_5->core_cache->reg_list[0].value, 0, 32); + u32 r1 = buf_get_u32(armv4_5->core_cache->reg_list[1].value, 0, 32); + u32 pc = buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32); + int i; + + u32 dcc_code[] = + { + /* MRC TST BNE MRC STR B */ + 0xee101e10, 0xe3110001, 0x0afffffc, 0xee111e10, 0xe4801004, 0xeafffff9 + }; + + if (!arm7_9->dcc_downloads) + return target->type->write_memory(target, address, 4, count, buffer); + + /* regrab previously allocated working_area, or allocate a new one */ + if (!arm7_9->dcc_working_area) + { + /* make sure we have a working area */ + if (target_alloc_working_area(target, 24, &arm7_9->dcc_working_area) != ERROR_OK) + { + INFO("no working area available, falling back to memory writes"); + return target->type->write_memory(target, address, 4, count, buffer); + } + + /* write DCC code to working area */ + target->type->write_memory(target, arm7_9->dcc_working_area->address, 4, 6, (u8*)dcc_code); + } + + buf_set_u32(armv4_5->core_cache->reg_list[0].value, 0, 32, address); + armv4_5->core_cache->reg_list[0].valid = 1; + armv4_5->core_cache->reg_list[0].dirty = 1; + armv4_5->core_state = ARMV4_5_STATE_ARM; + + arm7_9_resume(target, 0, arm7_9->dcc_working_area->address, 1, 1); + + for (i = 0; i < count; i++) + { + embeddedice_write_reg(&arm7_9->eice_cache->reg_list[EICE_COMMS_DATA], buf_get_u32(buffer, 0, 32)); + buffer += 4; + } + + target->type->halt(target); + + while (target->state != TARGET_HALTED) + target->type->poll(target); + + /* restore target state */ + buf_set_u32(armv4_5->core_cache->reg_list[0].value, 0, 32, r0); + armv4_5->core_cache->reg_list[0].valid = 1; + armv4_5->core_cache->reg_list[0].dirty = 1; + buf_set_u32(armv4_5->core_cache->reg_list[1].value, 0, 32, r1); + armv4_5->core_cache->reg_list[1].valid = 1; + armv4_5->core_cache->reg_list[1].dirty = 1; + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, pc); + armv4_5->core_cache->reg_list[15].valid = 1; + armv4_5->core_cache->reg_list[15].dirty = 1; + armv4_5->core_state = core_state; + + return ERROR_OK; +} + +int arm7_9_register_commands(struct command_context_s *cmd_ctx) +{ + command_t *arm7_9_cmd; + + arm7_9_cmd = register_command(cmd_ctx, NULL, "arm7_9", NULL, COMMAND_ANY, NULL); + + register_command(cmd_ctx, arm7_9_cmd, "write_xpsr", handle_arm7_9_write_xpsr_command, COMMAND_EXEC, "write program status register <value> <not cpsr|spsr>"); + register_command(cmd_ctx, arm7_9_cmd, "write_xpsr_im8", handle_arm7_9_write_xpsr_im8_command, COMMAND_EXEC, "write program status register <8bit immediate> <rotate> <not cpsr|spsr>"); + + register_command(cmd_ctx, arm7_9_cmd, "write_core_reg", handle_arm7_9_write_core_reg_command, COMMAND_EXEC, "write core register <num> <mode> <value>"); + + register_command(cmd_ctx, arm7_9_cmd, "sw_bkpts", handle_arm7_9_sw_bkpts_command, COMMAND_EXEC, "support for software breakpoints <enable|disable>"); + register_command(cmd_ctx, arm7_9_cmd, "force_hw_bkpts", handle_arm7_9_force_hw_bkpts_command, COMMAND_EXEC, "use hardware breakpoints for all breakpoints (disables sw breakpoint support) <enable|disable>"); + register_command(cmd_ctx, arm7_9_cmd, "dbgrq", handle_arm7_9_dbgrq_command, + COMMAND_ANY, "use EmbeddedICE dbgrq instead of breakpoint for target halt requests <enable|disable>"); + register_command(cmd_ctx, arm7_9_cmd, "fast_writes", handle_arm7_9_fast_writes_command, + COMMAND_ANY, "use fast memory writes instead of slower but potentially unsafe slow writes <enable|disable>"); + register_command(cmd_ctx, arm7_9_cmd, "dcc_downloads", handle_arm7_9_dcc_downloads_command, + COMMAND_ANY, "use DCC downloads for larger memory writes <enable|disable>"); + + armv4_5_register_commands(cmd_ctx); + + return ERROR_OK; +} + +int handle_arm7_9_write_xpsr_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + u32 value; + int spsr; + int retval; + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "can't write registers while running"); + return ERROR_OK; + } + + if (argc < 2) + { + command_print(cmd_ctx, "usage: write_xpsr <value> <not cpsr|spsr>"); + return ERROR_OK; + } + + value = strtoul(args[0], NULL, 0); + spsr = strtol(args[1], NULL, 0); + + arm7_9->write_xpsr(target, value, spsr); + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while writing to xpsr"); + exit(-1); + } + + return ERROR_OK; +} + +int handle_arm7_9_write_xpsr_im8_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + u32 value; + int rotate; + int spsr; + int retval; + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "can't write registers while running"); + return ERROR_OK; + } + + if (argc < 3) + { + command_print(cmd_ctx, "usage: write_xpsr_im8 <im8> <rotate> <not cpsr|spsr>"); + return ERROR_OK; + } + + value = strtoul(args[0], NULL, 0); + rotate = strtol(args[1], NULL, 0); + spsr = strtol(args[2], NULL, 0); + + arm7_9->write_xpsr_im8(target, value, rotate, spsr); + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while writing 8-bit immediate to xpsr"); + exit(-1); + } + + return ERROR_OK; +} + +int handle_arm7_9_write_core_reg_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + u32 value; + u32 mode; + int num; + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "can't write registers while running"); + return ERROR_OK; + } + + if (argc < 3) + { + command_print(cmd_ctx, "usage: write_core_reg <num> <mode> <value>"); + return ERROR_OK; + } + + num = strtol(args[0], NULL, 0); + mode = strtoul(args[1], NULL, 0); + value = strtoul(args[2], NULL, 0); + + arm7_9_write_core_reg(target, num, mode, value); + + return ERROR_OK; +} + +int handle_arm7_9_sw_bkpts_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if (argc == 0) + { + command_print(cmd_ctx, "software breakpoints %s", (arm7_9->sw_bkpts_enabled) ? "enabled" : "disabled"); + return ERROR_OK; + } + + if (strcmp("enable", args[0]) == 0) + { + if (arm7_9->sw_bkpts_use_wp) + { + arm7_9_enable_sw_bkpts(target); + } + else + { + arm7_9->sw_bkpts_enabled = 1; + } + } + else if (strcmp("disable", args[0]) == 0) + { + if (arm7_9->sw_bkpts_use_wp) + { + arm7_9_disable_sw_bkpts(target); + } + else + { + arm7_9->sw_bkpts_enabled = 0; + } + } + else + { + command_print(cmd_ctx, "usage: arm7_9 sw_bkpts <enable|disable>"); + } + + command_print(cmd_ctx, "software breakpoints %s", (arm7_9->sw_bkpts_enabled) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +int handle_arm7_9_force_hw_bkpts_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if ((argc >= 1) && (strcmp("enable", args[0]) == 0)) + { + arm7_9->force_hw_bkpts = 1; + if (arm7_9->sw_bkpts_use_wp) + { + arm7_9_disable_sw_bkpts(target); + } + } + else if ((argc >= 1) && (strcmp("disable", args[0]) == 0)) + { + arm7_9->force_hw_bkpts = 0; + } + else + { + command_print(cmd_ctx, "usage: arm7_9 force_hw_bkpts <enable|disable>"); + } + + command_print(cmd_ctx, "force hardware breakpoints %s", (arm7_9->force_hw_bkpts) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +int handle_arm7_9_dbgrq_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if (argc > 0) + { + if (strcmp("enable", args[0]) == 0) + { + arm7_9->use_dbgrq = 1; + } + else if (strcmp("disable", args[0]) == 0) + { + arm7_9->use_dbgrq = 0; + } + else + { + command_print(cmd_ctx, "usage: arm7_9 dbgrq <enable|disable>"); + } + } + + command_print(cmd_ctx, "use of EmbeddedICE dbgrq instead of breakpoint for target halt %s", (arm7_9->use_dbgrq) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +int handle_arm7_9_fast_writes_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if (argc > 0) + { + if (strcmp("enable", args[0]) == 0) + { + arm7_9->fast_memory_writes = 1; + } + else if (strcmp("disable", args[0]) == 0) + { + arm7_9->fast_memory_writes = 0; + } + else + { + command_print(cmd_ctx, "usage: arm7_9 fast_writes <enable|disable>"); + } + } + + command_print(cmd_ctx, "fast memory writes are %s", (arm7_9->fast_memory_writes) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +int handle_arm7_9_dcc_downloads_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target"); + return ERROR_OK; + } + + if (argc > 0) + { + if (strcmp("enable", args[0]) == 0) + { + arm7_9->dcc_downloads = 1; + } + else if (strcmp("disable", args[0]) == 0) + { + arm7_9->dcc_downloads = 0; + } + else + { + command_print(cmd_ctx, "usage: arm7_9 dcc_downloads <enable|disable>"); + } + } + + command_print(cmd_ctx, "dcc downloads are %s", (arm7_9->dcc_downloads) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +int arm7_9_init_arch_info(target_t *target, arm7_9_common_t *arm7_9) +{ + armv4_5_common_t *armv4_5 = &arm7_9->armv4_5_common; + + arm7_9->common_magic = ARM7_9_COMMON_MAGIC; + + arm_jtag_setup_connection(&arm7_9->jtag_info); + arm7_9->wp_available = 2; + arm7_9->wp0_used = 0; + arm7_9->wp1_used = 0; + arm7_9->force_hw_bkpts = 0; + arm7_9->use_dbgrq = 0; + arm7_9->has_etm = 0; + + arm7_9->reinit_embeddedice = 0; + + arm7_9->dcc_working_area = NULL; + + arm7_9->fast_memory_writes = 0; + arm7_9->dcc_downloads = 0; + + jtag_register_event_callback(arm7_9_jtag_callback, target); + + armv4_5->arch_info = arm7_9; + armv4_5->read_core_reg = arm7_9_read_core_reg; + armv4_5->write_core_reg = arm7_9_write_core_reg; + armv4_5->full_context = arm7_9_full_context; + + armv4_5_init_arch_info(target, armv4_5); + + return ERROR_OK; +} diff --git a/src/target/arm7_9_common.h b/src/target/arm7_9_common.h new file mode 100644 index 00000000..f03ae496 --- /dev/null +++ b/src/target/arm7_9_common.h @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ARM7_9_COMMON_H +#define ARM7_9_COMMON_H + +#include "armv4_5.h" +#include "arm_jtag.h" +#include "breakpoints.h" +#include "target.h" + +#define ARM7_9_COMMON_MAGIC 0x0a790a79 + +typedef struct arm7_9_common_s +{ + int common_magic; + + arm_jtag_t jtag_info; + reg_cache_t *eice_cache; + reg_cache_t *etm_cache; + + u32 arm_bkpt; + u16 thumb_bkpt; + int sw_bkpts_use_wp; + int wp_available; + int wp0_used; + int wp1_used; + int sw_bkpts_enabled; + int force_hw_bkpts; + int dbgreq_adjust_pc; + int use_dbgrq; + int has_etm; + + int reinit_embeddedice; + + struct working_area_s *dcc_working_area; + + int fast_memory_writes; + int dcc_downloads; + + int (*examine_debug_reason)(target_t *target); + + void (*change_to_arm)(target_t *target, u32 *r0, u32 *pc); + + void (*read_core_regs)(target_t *target, u32 mask, u32* core_regs[16]); + void (*read_xpsr)(target_t *target, u32 *xpsr, int spsr); + + void (*write_xpsr)(target_t *target, u32 xpsr, int spsr); + void (*write_xpsr_im8)(target_t *target, u8 xpsr_im, int rot, int spsr); + void (*write_core_regs)(target_t *target, u32 mask, u32 core_regs[16]); + + void (*load_word_regs)(target_t *target, u32 mask); + void (*load_hword_reg)(target_t *target, int num); + void (*load_byte_reg)(target_t *target, int num); + + void (*store_word_regs)(target_t *target, u32 mask); + void (*store_hword_reg)(target_t *target, int num); + void (*store_byte_reg)(target_t *target, int num); + + void (*write_pc)(target_t *target, u32 pc); + void (*branch_resume)(target_t *target); + void (*branch_resume_thumb)(target_t *target); + + void (*enable_single_step)(target_t *target); + void (*disable_single_step)(target_t *target); + + void (*pre_debug_entry)(target_t *target); + void (*post_debug_entry)(target_t *target); + + void (*pre_restore_context)(target_t *target); + void (*post_restore_context)(target_t *target); + + armv4_5_common_t armv4_5_common; + void *arch_info; + +} arm7_9_common_t; + +int arm7_9_register_commands(struct command_context_s *cmd_ctx); + +enum target_state arm7_9_poll(target_t *target); + +int arm7_9_assert_reset(target_t *target); +int arm7_9_deassert_reset(target_t *target); +int arm7_9_reset_request_halt(target_t *target); +int arm7_9_early_halt(target_t *target); +int arm7_9_soft_reset_halt(struct target_s *target); + +int arm7_9_halt(target_t *target); +int arm7_9_debug_entry(target_t *target); +int arm7_9_full_context(target_t *target); +int arm7_9_resume(struct target_s *target, int current, u32 address, int handle_breakpoints, int debug_execution); +int arm7_9_step(struct target_s *target, int current, u32 address, int handle_breakpoints); +int arm7_9_read_core_reg(struct target_s *target, int num, enum armv4_5_mode mode); +int arm7_9_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); +int arm7_9_write_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); +int arm7_9_bulk_write_memory(target_t *target, u32 address, u32 count, u8 *buffer); + +int arm7_9_run_algorithm(struct target_s *target, int num_mem_params, mem_param_t *mem_params, int num_reg_prams, reg_param_t *reg_param, u32 entry_point, void *arch_info); + +int arm7_9_add_breakpoint(struct target_s *target, u32 address, u32 length, enum breakpoint_type type); +int arm7_9_remove_breakpoint(struct target_s *target, breakpoint_t *breakpoint); +int arm7_9_add_watchpoint(struct target_s *target, u32 address, u32 length, enum watchpoint_rw rw); +int arm7_9_remove_watchpoint(struct target_s *target, watchpoint_t *watchpoint); + +void arm7_9_enable_eice_step(target_t *target); +void arm7_9_disable_eice_step(target_t *target); + +int arm7_9_execute_sys_speed(struct target_s *target); + +int arm7_9_init_arch_info(target_t *target, arm7_9_common_t *arm7_9); + + +#endif /* ARM7_9_COMMON_H */ diff --git a/src/target/arm7tdmi.c b/src/target/arm7tdmi.c new file mode 100644 index 00000000..c2079477 --- /dev/null +++ b/src/target/arm7tdmi.c @@ -0,0 +1,780 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" + +#include "arm7tdmi.h" + +#include "arm7_9_common.h" +#include "register.h" +#include "target.h" +#include "armv4_5.h" +#include "embeddedice.h" +#include "etm.h" +#include "log.h" +#include "jtag.h" +#include "arm_jtag.h" + +#include <stdlib.h> +#include <string.h> + +#if 0 +#define _DEBUG_INSTRUCTION_EXECUTION_ +#endif + +/* cli handling */ +int arm7tdmi_register_commands(struct command_context_s *cmd_ctx); + +/* forward declarations */ +int arm7tdmi_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target); +int arm7tdmi_init_target(struct command_context_s *cmd_ctx, struct target_s *target); +int arm7tdmi_quit(); + +/* target function declarations */ +enum target_state arm7tdmi_poll(struct target_s *target); +int arm7tdmi_halt(target_t *target); +int arm7tdmi_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); + +target_type_t arm7tdmi_target = +{ + .name = "arm7tdmi", + + .poll = arm7_9_poll, + .arch_state = armv4_5_arch_state, + + .halt = arm7_9_halt, + .resume = arm7_9_resume, + .step = arm7_9_step, + + .assert_reset = arm7_9_assert_reset, + .deassert_reset = arm7_9_deassert_reset, + .soft_reset_halt = arm7_9_soft_reset_halt, + + .get_gdb_reg_list = armv4_5_get_gdb_reg_list, + + .read_memory = arm7_9_read_memory, + .write_memory = arm7_9_write_memory, + .bulk_write_memory = arm7_9_bulk_write_memory, + + .run_algorithm = armv4_5_run_algorithm, + + .add_breakpoint = arm7_9_add_breakpoint, + .remove_breakpoint = arm7_9_remove_breakpoint, + .add_watchpoint = arm7_9_add_watchpoint, + .remove_watchpoint = arm7_9_remove_watchpoint, + + .register_commands = arm7tdmi_register_commands, + .target_command = arm7tdmi_target_command, + .init_target = arm7tdmi_init_target, + .quit = arm7tdmi_quit +}; + +int arm7tdmi_examine_debug_reason(target_t *target) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + /* only check the debug reason if we don't know it already */ + if ((target->debug_reason != DBG_REASON_DBGRQ) + && (target->debug_reason != DBG_REASON_SINGLESTEP)) + { + scan_field_t fields[2]; + u8 databus[4]; + u8 breakpoint; + + jtag_add_end_state(TAP_PD); + + fields[0].device = arm7_9->jtag_info.chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = NULL; + fields[0].out_mask = NULL; + fields[0].in_value = &breakpoint; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = arm7_9->jtag_info.chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = NULL; + fields[1].out_mask = NULL; + fields[1].in_value = databus; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + arm_jtag_scann(&arm7_9->jtag_info, 0x1); + arm_jtag_set_instr(&arm7_9->jtag_info, arm7_9->jtag_info.intest_instr); + + jtag_add_dr_scan(2, fields, TAP_PD); + jtag_execute_queue(); + + fields[0].in_value = NULL; + fields[0].out_value = &breakpoint; + fields[1].in_value = NULL; + fields[1].out_value = databus; + + jtag_add_dr_scan(2, fields, TAP_PD); + + if (breakpoint & 1) + target->debug_reason = DBG_REASON_WATCHPOINT; + else + target->debug_reason = DBG_REASON_BREAKPOINT; + } + + return ERROR_OK; +} + +/* put an instruction in the ARM7TDMI pipeline or write the data bus, and optionally read data */ +int arm7tdmi_clock_out(arm_jtag_t *jtag_info, u32 out, u32 *in, int breakpoint) +{ + scan_field_t fields[2]; + u8 out_buf[4]; + u8 breakpoint_buf; + + out = flip_u32(out, 32); + buf_set_u32(out_buf, 0, 32, out); + buf_set_u32(&breakpoint_buf, 0, 1, breakpoint); + + jtag_add_end_state(TAP_PD); + arm_jtag_scann(jtag_info, 0x1); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = &breakpoint_buf; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = out_buf; + fields[1].out_mask = NULL; + if (in) + { + fields[1].in_value = (u8*)in; + fields[1].in_handler = arm_jtag_buf_to_u32_flip; + fields[1].in_handler_priv = in; + } else + { + fields[1].in_value = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + } + + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + + jtag_add_dr_scan(2, fields, -1); + + jtag_add_runtest(0, -1); + +#ifdef _DEBUG_INSTRUCTION_EXECUTION_ +{ + char* in_string; + jtag_execute_queue(); + + if (in) + { + in_string = buf_to_char((u8*)in, 32); + DEBUG("out: 0x%8.8x, in: %s", flip_u32(out, 32), in_string); + free(in_string); + } + else + DEBUG("out: 0x%8.8x", flip_u32(out, 32)); +} +#endif + + return ERROR_OK; +} + +/* put an instruction in the ARM7TDMI pipeline, and optionally read data */ +int arm7tdmi_clock_data_in(arm_jtag_t *jtag_info, u32 *in) +{ + scan_field_t fields[2]; + + jtag_add_end_state(TAP_PD); + arm_jtag_scann(jtag_info, 0x1); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = NULL; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = NULL; + fields[1].out_mask = NULL; + fields[1].in_value = (u8*)in; + fields[1].in_handler = arm_jtag_buf_to_u32_flip; + fields[1].in_handler_priv = in; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + + jtag_add_dr_scan(2, fields, -1); + + jtag_add_runtest(0, -1); + +#ifdef _DEBUG_INSTRUCTION_EXECUTION_ +{ + char* in_string; + jtag_execute_queue(); + + if (in) + { + in_string = buf_to_char((u8*)in, 32); + DEBUG("in: %s", in_string); + free(in_string); + } +} +#endif + + return ERROR_OK; +} + +void arm7tdmi_change_to_arm(target_t *target, u32 *r0, u32 *pc) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* save r0 before using it and put system in ARM state + * to allow common handling of ARM and THUMB debugging */ + + /* fetch STR r0, [r0] */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_STR(0, 0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + /* nothing fetched, STR r0, [r0] in Execute (2) */ + arm7tdmi_clock_data_in(jtag_info, r0); + + /* MOV r0, r15 fetched, STR in Decode */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_MOV(0, 15), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_STR(0, 0), NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + /* nothing fetched, STR r0, [r0] in Execute (2) */ + arm7tdmi_clock_data_in(jtag_info, pc); + + /* fetch MOV */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_MOV_IM(0, 0x0), NULL, 0); + + /* fetch BX */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_BX(0), NULL, 0); + /* NOP fetched, BX in Decode, MOV in Execute */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + /* NOP fetched, BX in Execute (1) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + + jtag_execute_queue(); + + /* fix program counter: + * MOV r0, r15 was the 4th instruction (+6) + * reading PC in Thumb state gives address of instruction + 4 + */ + *pc -= 0xa; + +} + +void arm7tdmi_read_core_regs(target_t *target, u32 mask, u32* core_regs[16]) +{ + int i; + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* STMIA r0-15, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_STMIA(0, mask & 0xffff, 0, 0), NULL, 0); + + /* fetch NOP, STM in DECODE stage */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* fetch NOP, STM in EXECUTE stage (1st cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + + for (i = 0; i <= 15; i++) + { + if (mask & (1 << i)) + /* nothing fetched, STM still in EXECUTE (1+i cycle) */ + arm7tdmi_clock_data_in(jtag_info, core_regs[i]); + } + +} + +void arm7tdmi_read_xpsr(target_t *target, u32 *xpsr, int spsr) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* MRS r0, cpsr */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_MRS(0, spsr & 1), NULL, 0); + + /* STR r0, [r15] */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_STR(0, 15), NULL, 0); + /* fetch NOP, STR in DECODE stage */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* fetch NOP, STR in EXECUTE stage (1st cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* nothing fetched, STR still in EXECUTE (2nd cycle) */ + arm7tdmi_clock_data_in(jtag_info, xpsr); + +} + +void arm7tdmi_write_xpsr(target_t *target, u32 xpsr, int spsr) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + DEBUG("xpsr: %8.8x, spsr: %i", xpsr, spsr); + + /* MSR1 fetched */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM(xpsr & 0xff, 0, 1, spsr), NULL, 0); + /* MSR2 fetched, MSR1 in DECODE */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff00) >> 8, 0xc, 2, spsr), NULL, 0); + /* MSR3 fetched, MSR1 in EXECUTE (1), MSR2 in DECODE */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff0000) >> 16, 0x8, 4, spsr), NULL, 0); + /* nothing fetched, MSR1 in EXECUTE (2) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* MSR4 fetched, MSR2 in EXECUTE (1), MSR3 in DECODE */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff000000) >> 24, 0x4, 8, spsr), NULL, 0); + /* nothing fetched, MSR2 in EXECUTE (2) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* NOP fetched, MSR3 in EXECUTE (1), MSR4 in DECODE */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* nothing fetched, MSR3 in EXECUTE (2) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* NOP fetched, MSR4 in EXECUTE (1) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* nothing fetched, MSR4 in EXECUTE (2) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); +} + +void arm7tdmi_write_xpsr_im8(target_t *target, u8 xpsr_im, int rot, int spsr) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + DEBUG("xpsr_im: %2.2x, rot: %i, spsr: %i", xpsr_im, rot, spsr); + + /* MSR fetched */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM(xpsr_im, rot, 1, spsr), NULL, 0); + /* NOP fetched, MSR in DECODE */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* NOP fetched, MSR in EXECUTE (1) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* nothing fetched, MSR in EXECUTE (2) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + +} + +void arm7tdmi_write_core_regs(target_t *target, u32 mask, u32 core_regs[16]) +{ + int i; + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* LDMIA r0-15, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, mask & 0xffff, 0, 0), NULL, 0); + + /* fetch NOP, LDM in DECODE stage */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + + for (i = 0; i <= 15; i++) + { + if (mask & (1 << i)) + /* nothing fetched, LDM still in EXECUTE (1+i cycle) */ + arm7tdmi_clock_out(jtag_info, core_regs[i], NULL, 0); + } + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + +} + +void arm7tdmi_load_word_regs(target_t *target, u32 mask) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed load-multiple into the pipeline */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, mask & 0xffff, 0, 1), NULL, 0); + +} + +void arm7tdmi_load_hword_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed load half-word into the pipeline */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDRH_IP(num, 0), NULL, 0); + +} + +void arm7tdmi_load_byte_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed load byte into the pipeline */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDRB_IP(num, 0), NULL, 0); + +} + +void arm7tdmi_store_word_regs(target_t *target, u32 mask) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed store-multiple into the pipeline */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_STMIA(0, mask, 0, 1), NULL, 0); + +} + +void arm7tdmi_store_hword_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed store half-word into the pipeline */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_STRH_IP(num, 0), NULL, 0); + +} + +void arm7tdmi_store_byte_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed store byte into the pipeline */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_STRB_IP(num, 0), NULL, 0); + +} + +void arm7tdmi_write_pc(target_t *target, u32 pc) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* LDMIA r0-15, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, 0x8000, 0, 0), NULL, 0); + /* fetch NOP, LDM in DECODE stage */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (1st cycle) load register */ + arm7tdmi_clock_out(jtag_info, pc, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (2nd cycle) load register */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (3rd cycle) load register */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (4th cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (5th cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); +} + +void arm7tdmi_branch_resume(target_t *target) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_B(0xfffffa, 0), NULL, 0); + +} + +void arm7tdmi_branch_resume_thumb(target_t *target) +{ + DEBUG(""); + + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + + /* LDMIA r0, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, 0x1, 0, 0), NULL, 0); + + /* fetch NOP, LDM in DECODE stage */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (2nd cycle) */ + arm7tdmi_clock_out(jtag_info, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32) | 1, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (3rd cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + + /* Branch and eXchange */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_BX(0), NULL, 0); + + embeddedice_read_reg(dbg_stat); + + /* fetch NOP, BX in DECODE stage */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + + /* target is now in Thumb state */ + embeddedice_read_reg(dbg_stat); + + /* fetch NOP, BX in EXECUTE stage (1st cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_NOP, NULL, 0); + + /* target is now in Thumb state */ + embeddedice_read_reg(dbg_stat); + + /* clean r0 bits to avoid alignment problems */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_MOV_IM(0, 0x0), NULL, 0); + /* load r0 value, MOV_IM in Decode*/ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_LDR(0, 0), NULL, 0); + /* fetch NOP, LDR in Decode, MOV_IM in Execute */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + /* fetch NOP, LDR in Execute */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + /* nothing fetched, LDR in EXECUTE stage (2nd cycle) */ + arm7tdmi_clock_out(jtag_info, buf_get_u32(armv4_5->core_cache->reg_list[0].value, 0, 32), NULL, 0); + /* nothing fetched, LDR in EXECUTE stage (3rd cycle) */ + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 0); + + embeddedice_read_reg(dbg_stat); + + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, NULL, 1); + arm7tdmi_clock_out(jtag_info, ARMV4_5_T_B(0x7f7), NULL, 0); + +} + +void arm7tdmi_build_reg_cache(target_t *target) +{ + reg_cache_t **cache_p = register_get_last_cache_p(&target->reg_cache); + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + arm7tdmi_common_t *arch_info = arm7_9->arch_info; + + + (*cache_p) = armv4_5_build_reg_cache(target, armv4_5); + armv4_5->core_cache = (*cache_p); + + (*cache_p)->next = embeddedice_build_reg_cache(target, jtag_info, 0); + arm7_9->eice_cache = (*cache_p)->next; + + if (arm7_9->has_etm) + { + (*cache_p)->next->next = etm_build_reg_cache(target, jtag_info, 0); + arm7_9->etm_cache = (*cache_p)->next->next; + } + + if (arch_info->has_monitor_mode) + (*cache_p)->next->reg_list[0].size = 6; + else + (*cache_p)->next->reg_list[0].size = 3; + + (*cache_p)->next->reg_list[1].size = 5; + +} + +int arm7tdmi_init_target(struct command_context_s *cmd_ctx, struct target_s *target) +{ + + arm7tdmi_build_reg_cache(target); + + return ERROR_OK; + +} + +int arm7tdmi_quit() +{ + + return ERROR_OK; +} + +int arm7tdmi_init_arch_info(target_t *target, arm7tdmi_common_t *arm7tdmi, int chain_pos, char *variant) +{ + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + int has_etm = 0; + + arm7_9 = &arm7tdmi->arm7_9_common; + armv4_5 = &arm7_9->armv4_5_common; + + /* prepare JTAG information for the new target */ + arm7_9->jtag_info.chain_pos = chain_pos; + arm7_9->jtag_info.scann_size = 4; + + /* register arch-specific functions */ + arm7_9->examine_debug_reason = arm7tdmi_examine_debug_reason; + arm7_9->change_to_arm = arm7tdmi_change_to_arm; + arm7_9->read_core_regs = arm7tdmi_read_core_regs; + arm7_9->read_xpsr = arm7tdmi_read_xpsr; + + arm7_9->write_xpsr = arm7tdmi_write_xpsr; + arm7_9->write_xpsr_im8 = arm7tdmi_write_xpsr_im8; + arm7_9->write_core_regs = arm7tdmi_write_core_regs; + + arm7_9->load_word_regs = arm7tdmi_load_word_regs; + arm7_9->load_hword_reg = arm7tdmi_load_hword_reg; + arm7_9->load_byte_reg = arm7tdmi_load_byte_reg; + + arm7_9->store_word_regs = arm7tdmi_store_word_regs; + arm7_9->store_hword_reg = arm7tdmi_store_hword_reg; + arm7_9->store_byte_reg = arm7tdmi_store_byte_reg; + + arm7_9->write_pc = arm7tdmi_write_pc; + arm7_9->branch_resume = arm7tdmi_branch_resume; + arm7_9->branch_resume_thumb = arm7tdmi_branch_resume_thumb; + + arm7_9->enable_single_step = arm7_9_enable_eice_step; + arm7_9->disable_single_step = arm7_9_disable_eice_step; + + arm7_9->pre_debug_entry = NULL; + arm7_9->post_debug_entry = NULL; + + arm7_9->pre_restore_context = NULL; + arm7_9->post_restore_context = NULL; + + /* initialize arch-specific breakpoint handling */ + buf_set_u32((u8*)(&arm7_9->arm_bkpt), 0, 32, 0xdeeedeee); + buf_set_u32((u8*)(&arm7_9->thumb_bkpt), 0, 16, 0xdeee); + + arm7_9->sw_bkpts_use_wp = 1; + arm7_9->sw_bkpts_enabled = 0; + arm7_9->dbgreq_adjust_pc = 2; + arm7_9->arch_info = arm7tdmi; + + arm7tdmi->has_monitor_mode = 0; + arm7tdmi->arch_info = NULL; + arm7tdmi->common_magic = ARM7TDMI_COMMON_MAGIC; + + if (variant) + { + if (strcmp(variant, "arm7tdmi-s_r4") == 0) + arm7tdmi->has_monitor_mode = 1; + else if (strcmp(variant, "arm7tdmi_r4") == 0) + arm7tdmi->has_monitor_mode = 1; + else if (strcmp(variant, "lpc2000") == 0) + { + arm7tdmi->has_monitor_mode = 1; + has_etm = 1; + } + arm7tdmi->variant = strdup(variant); + } + else + arm7tdmi->variant = strdup(""); + + arm7_9_init_arch_info(target, arm7_9); + + arm7_9->has_etm = has_etm; + + return ERROR_OK; +} + +/* target arm7tdmi <endianess> <startup_mode> <chain_pos> <variant> */ +int arm7tdmi_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target) +{ + int chain_pos; + char *variant = NULL; + arm7tdmi_common_t *arm7tdmi = malloc(sizeof(arm7tdmi_common_t)); + + if (argc < 4) + { + ERROR("'target arm7tdmi' requires at least one additional argument"); + exit(-1); + } + + chain_pos = strtoul(args[2], NULL, 0); + + if (argc >= 5) + variant = args[4]; + + arm7tdmi_init_arch_info(target, arm7tdmi, chain_pos, variant); + + return ERROR_OK; +} + +int arm7tdmi_register_commands(struct command_context_s *cmd_ctx) +{ + int retval; + + retval = arm7_9_register_commands(cmd_ctx); + + return ERROR_OK; + +} + diff --git a/src/target/arm7tdmi.h b/src/target/arm7tdmi.h new file mode 100644 index 00000000..ca2df8b6 --- /dev/null +++ b/src/target/arm7tdmi.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ARM7TDMI_H +#define ARM7TDMI_H + +#include "target.h" +#include "register.h" +#include "armv4_5.h" +#include "embeddedice.h" +#include "arm_jtag.h" +#include "arm7_9_common.h" + +#define ARM7TDMI_COMMON_MAGIC 0x00a700a7 + +typedef struct arm7tdmi_common_s +{ + int common_magic; + char *variant; + int has_monitor_mode; + void *arch_info; + arm7_9_common_t arm7_9_common; +} arm7tdmi_common_t; + +int arm7tdmi_register_commands(struct command_context_s *cmd_ctx); +int arm7tdmi_init_arch_info(target_t *target, arm7tdmi_common_t *arm7tdmi, int chain_pos, char *variant); +int arm7tdmi_init_target(struct command_context_s *cmd_ctx, struct target_s *target); + + +#endif /* ARM7TDMI_H */ diff --git a/src/target/arm920t.c b/src/target/arm920t.c new file mode 100644 index 00000000..eb0fa7df --- /dev/null +++ b/src/target/arm920t.c @@ -0,0 +1,967 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" + +#include "arm920t.h" +#include "jtag.h" +#include "log.h" + +#include <stdlib.h> +#include <string.h> + +#if 0 +#define _DEBUG_INSTRUCTION_EXECUTION_ +#endif + +/* cli handling */ +int arm920t_register_commands(struct command_context_s *cmd_ctx); + +int arm920t_handle_cp15_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm920t_handle_cp15i_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm920t_handle_virt2phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm920t_handle_cache_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm920t_handle_md_phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int arm920t_handle_mw_phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +/* forward declarations */ +int arm920t_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target); +int arm920t_init_target(struct command_context_s *cmd_ctx, struct target_s *target); +int arm920t_quit(); +int arm920t_arch_state(struct target_s *target, char *buf, int buf_size); +int arm920t_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); +int arm920t_write_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); +int arm920t_soft_reset_halt(struct target_s *target); + +target_type_t arm920t_target = +{ + .name = "arm920t", + + .poll = arm7_9_poll, + .arch_state = arm920t_arch_state, + + .halt = arm7_9_halt, + .resume = arm7_9_resume, + .step = arm7_9_step, + + .assert_reset = arm7_9_assert_reset, + .deassert_reset = arm7_9_deassert_reset, + .soft_reset_halt = arm920t_soft_reset_halt, + + .get_gdb_reg_list = armv4_5_get_gdb_reg_list, + + .read_memory = arm920t_read_memory, + .write_memory = arm920t_write_memory, + .bulk_write_memory = arm7_9_bulk_write_memory, + + .run_algorithm = armv4_5_run_algorithm, + + .add_breakpoint = arm7_9_add_breakpoint, + .remove_breakpoint = arm7_9_remove_breakpoint, + .add_watchpoint = arm7_9_add_watchpoint, + .remove_watchpoint = arm7_9_remove_watchpoint, + + .register_commands = arm920t_register_commands, + .target_command = arm920t_target_command, + .init_target = arm920t_init_target, + .quit = arm920t_quit +}; + +int arm920t_read_cp15_physical(target_t *target, int reg_addr, u32 *value) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + scan_field_t fields[4]; + u8 access_type_buf = 1; + u8 reg_addr_buf = reg_addr & 0x3f; + u8 nr_w_buf = 0; + + jtag_add_end_state(TAP_RTI); + arm_jtag_scann(jtag_info, 0xf); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = &access_type_buf; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = NULL; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = jtag_info->chain_pos; + fields[2].num_bits = 6; + fields[2].out_value = ®_addr_buf; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + fields[3].device = jtag_info->chain_pos; + fields[3].num_bits = 1; + fields[3].out_value = &nr_w_buf; + fields[3].out_mask = NULL; + fields[3].in_value = NULL; + fields[3].in_check_value = NULL; + fields[3].in_check_mask = NULL; + fields[3].in_handler = NULL; + fields[3].in_handler_priv = NULL; + + jtag_add_dr_scan(4, fields, -1); + + fields[1].in_value = (u8*)value; + + jtag_add_dr_scan(4, fields, -1); + + return ERROR_OK; +} + +int arm920t_write_cp15_physical(target_t *target, int reg_addr, u32 value) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + scan_field_t fields[4]; + u8 access_type_buf = 1; + u8 reg_addr_buf = reg_addr & 0x3f; + u8 nr_w_buf = 1; + + jtag_add_end_state(TAP_RTI); + arm_jtag_scann(jtag_info, 0xf); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = &access_type_buf; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = (u8*)&value; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = jtag_info->chain_pos; + fields[2].num_bits = 6; + fields[2].out_value = ®_addr_buf; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + fields[3].device = jtag_info->chain_pos; + fields[3].num_bits = 1; + fields[3].out_value = &nr_w_buf; + fields[3].out_mask = NULL; + fields[3].in_value = NULL; + fields[3].in_check_value = NULL; + fields[3].in_check_mask = NULL; + fields[3].in_handler = NULL; + fields[3].in_handler_priv = NULL; + + jtag_add_dr_scan(4, fields, -1); + + return ERROR_OK; +} + +int arm920t_read_cp15_interpreted(target_t *target, u32 opcode, u32 *value) +{ + u32 cp15c15 = 0x0; + scan_field_t fields[4]; + u8 access_type_buf = 0; /* interpreted access */ + u8 reg_addr_buf = 0x0; + u8 nr_w_buf = 0; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + u32* context_p[1]; + + /* read-modify-write CP15 test state register + * to enable interpreted access mode */ + arm920t_read_cp15_physical(target, 0x1e, &cp15c15); + jtag_execute_queue(); + cp15c15 |= 1; /* set interpret mode */ + arm920t_write_cp15_physical(target, 0x1e, cp15c15); + + jtag_add_end_state(TAP_RTI); + arm_jtag_scann(jtag_info, 0xf); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = &access_type_buf; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = (u8*)&opcode; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = jtag_info->chain_pos; + fields[2].num_bits = 6; + fields[2].out_value = ®_addr_buf; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + fields[3].device = jtag_info->chain_pos; + fields[3].num_bits = 1; + fields[3].out_value = &nr_w_buf; + fields[3].out_mask = NULL; + fields[3].in_value = NULL; + fields[3].in_check_value = NULL; + fields[3].in_check_mask = NULL; + fields[3].in_handler = NULL; + fields[3].in_handler_priv = NULL; + + jtag_add_dr_scan(4, fields, -1); + + arm9tdmi_clock_out(jtag_info, ARMV4_5_LDR(0, 15), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + arm7_9_execute_sys_speed(target); + jtag_execute_queue(); + + /* read-modify-write CP15 test state register + * to disable interpreted access mode */ + arm920t_read_cp15_physical(target, 0x1e, &cp15c15); + jtag_execute_queue(); + cp15c15 &= ~1U; /* clear interpret mode */ + arm920t_write_cp15_physical(target, 0x1e, cp15c15); + + context_p[0] = value; + arm9tdmi_read_core_regs(target, 0x1, context_p); + jtag_execute_queue(); + + DEBUG("opcode: %8.8x, value: %8.8x", opcode, *value); + + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 0).dirty = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 15).dirty = 1; + + return ERROR_OK; +} + +int arm920t_write_cp15_interpreted(target_t *target, u32 opcode, u32 value, u32 address) +{ + u32 cp15c15 = 0x0; + scan_field_t fields[4]; + u8 access_type_buf = 0; /* interpreted access */ + u8 reg_addr_buf = 0x0; + u8 nr_w_buf = 0; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + u32 regs[2]; + + regs[0] = value; + regs[1] = address; + + arm9tdmi_write_core_regs(target, 0x3, regs); + + /* read-modify-write CP15 test state register + * to enable interpreted access mode */ + arm920t_read_cp15_physical(target, 0x1e, &cp15c15); + jtag_execute_queue(); + cp15c15 |= 1; /* set interpret mode */ + arm920t_write_cp15_physical(target, 0x1e, cp15c15); + + jtag_add_end_state(TAP_RTI); + arm_jtag_scann(jtag_info, 0xf); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 1; + fields[0].out_value = &access_type_buf; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = (u8*)&opcode; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = jtag_info->chain_pos; + fields[2].num_bits = 6; + fields[2].out_value = ®_addr_buf; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + fields[3].device = jtag_info->chain_pos; + fields[3].num_bits = 1; + fields[3].out_value = &nr_w_buf; + fields[3].out_mask = NULL; + fields[3].in_value = NULL; + fields[3].in_check_value = NULL; + fields[3].in_check_mask = NULL; + fields[3].in_handler = NULL; + fields[3].in_handler_priv = NULL; + + jtag_add_dr_scan(4, fields, -1); + + arm9tdmi_clock_out(jtag_info, ARMV4_5_STR(0, 1), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + arm7_9_execute_sys_speed(target); + jtag_execute_queue(); + + /* read-modify-write CP15 test state register + * to disable interpreted access mode */ + arm920t_read_cp15_physical(target, 0x1e, &cp15c15); + jtag_execute_queue(); + cp15c15 &= ~1U; /* set interpret mode */ + arm920t_write_cp15_physical(target, 0x1e, cp15c15); + + DEBUG("opcode: %8.8x, value: %8.8x, address: %8.8x", opcode, value, address); + + return ERROR_OK; +} + +u32 arm920t_get_ttb(target_t *target) +{ + int retval; + u32 ttb = 0x0; + + if ((retval = arm920t_read_cp15_interpreted(target, 0xeebf0f51, &ttb)) != ERROR_OK) + return retval; + + return ttb; +} + +void arm920t_disable_mmu_caches(target_t *target, int mmu, int d_u_cache, int i_cache) +{ + u32 cp15_control; + + /* read cp15 control register */ + arm920t_read_cp15_physical(target, 0x2, &cp15_control); + jtag_execute_queue(); + + if (mmu) + cp15_control &= ~0x1U; + + if (d_u_cache) + cp15_control &= ~0x4U; + + if (i_cache) + cp15_control &= ~0x1000U; + + arm920t_write_cp15_physical(target, 0x2, cp15_control); +} + +void arm920t_enable_mmu_caches(target_t *target, int mmu, int d_u_cache, int i_cache) +{ + u32 cp15_control; + + /* read cp15 control register */ + arm920t_read_cp15_physical(target, 0x2, &cp15_control); + jtag_execute_queue(); + + if (mmu) + cp15_control |= 0x1U; + + if (d_u_cache) + cp15_control |= 0x4U; + + if (i_cache) + cp15_control |= 0x1000U; + + arm920t_write_cp15_physical(target, 0x2, cp15_control); +} + +void arm920t_post_debug_entry(target_t *target) +{ + u32 cp15c15; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm9tdmi_common_t *arm9tdmi = arm7_9->arch_info; + arm920t_common_t *arm920t = arm9tdmi->arch_info; + + /* examine cp15 control reg */ + arm920t_read_cp15_physical(target, 0x2, &arm920t->cp15_control_reg); + jtag_execute_queue(); + DEBUG("cp15_control_reg: %8.8x", arm920t->cp15_control_reg); + + if (arm920t->armv4_5_mmu.armv4_5_cache.ctype == -1) + { + u32 cache_type_reg; + /* identify caches */ + arm920t_read_cp15_physical(target, 0x1, &cache_type_reg); + jtag_execute_queue(); + armv4_5_identify_cache(cache_type_reg, &arm920t->armv4_5_mmu.armv4_5_cache); + } + + arm920t->armv4_5_mmu.mmu_enabled = (arm920t->cp15_control_reg & 0x1U) ? 1 : 0; + arm920t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled = (arm920t->cp15_control_reg & 0x4U) ? 1 : 0; + arm920t->armv4_5_mmu.armv4_5_cache.i_cache_enabled = (arm920t->cp15_control_reg & 0x1000U) ? 1 : 0; + + /* save i/d fault status and address register */ + arm920t_read_cp15_interpreted(target, 0xee150f10, &arm920t->d_fsr); + arm920t_read_cp15_interpreted(target, 0xee150f30, &arm920t->i_fsr); + arm920t_read_cp15_interpreted(target, 0xee160f10, &arm920t->d_far); + arm920t_read_cp15_interpreted(target, 0xee160f30, &arm920t->i_far); + + /* read-modify-write CP15 test state register + * to disable I/D-cache linefills */ + arm920t_read_cp15_physical(target, 0x1e, &cp15c15); + jtag_execute_queue(); + cp15c15 |= 0x600; + arm920t_write_cp15_physical(target, 0x1e, cp15c15); + +} + +void arm920t_pre_restore_context(target_t *target) +{ + u32 cp15c15; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm9tdmi_common_t *arm9tdmi = arm7_9->arch_info; + arm920t_common_t *arm920t = arm9tdmi->arch_info; + + /* restore i/d fault status and address register */ + arm920t_write_cp15_interpreted(target, 0xee050f10, arm920t->d_fsr, 0x0); + arm920t_write_cp15_interpreted(target, 0xee050f30, arm920t->i_fsr, 0x0); + arm920t_write_cp15_interpreted(target, 0xee060f10, arm920t->d_far, 0x0); + arm920t_write_cp15_interpreted(target, 0xee060f30, arm920t->i_far, 0x0); + + /* read-modify-write CP15 test state register + * to reenable I/D-cache linefills */ + arm920t_read_cp15_physical(target, 0x1e, &cp15c15); + jtag_execute_queue(); + cp15c15 &= ~0x600U; + arm920t_write_cp15_physical(target, 0x1e, cp15c15); + +} + +int arm920t_get_arch_pointers(target_t *target, armv4_5_common_t **armv4_5_p, arm7_9_common_t **arm7_9_p, arm9tdmi_common_t **arm9tdmi_p, arm920t_common_t **arm920t_p) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9; + arm9tdmi_common_t *arm9tdmi; + arm920t_common_t *arm920t; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + return -1; + } + + arm7_9 = armv4_5->arch_info; + if (arm7_9->common_magic != ARM7_9_COMMON_MAGIC) + { + return -1; + } + + arm9tdmi = arm7_9->arch_info; + if (arm9tdmi->common_magic != ARM9TDMI_COMMON_MAGIC) + { + return -1; + } + + arm920t = arm9tdmi->arch_info; + if (arm920t->common_magic != ARM920T_COMMON_MAGIC) + { + return -1; + } + + *armv4_5_p = armv4_5; + *arm7_9_p = arm7_9; + *arm9tdmi_p = arm9tdmi; + *arm920t_p = arm920t; + + return ERROR_OK; +} + +int arm920t_arch_state(struct target_s *target, char *buf, int buf_size) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm9tdmi_common_t *arm9tdmi = arm7_9->arch_info; + arm920t_common_t *arm920t = arm9tdmi->arch_info; + + char *state[] = + { + "disabled", "enabled" + }; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + ERROR("BUG: called for a non-ARMv4/5 target"); + exit(-1); + } + + snprintf(buf, buf_size, + "target halted in %s state due to %s, current mode: %s\n" + "cpsr: 0x%8.8x pc: 0x%8.8x\n" + "MMU: %s, D-Cache: %s, I-Cache: %s", + armv4_5_state_strings[armv4_5->core_state], + target_debug_reason_strings[target->debug_reason], + armv4_5_mode_strings[armv4_5_mode_to_number(armv4_5->core_mode)], + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32), + state[arm920t->armv4_5_mmu.mmu_enabled], + state[arm920t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled], + state[arm920t->armv4_5_mmu.armv4_5_cache.i_cache_enabled]); + + return ERROR_OK; +} + +int arm920t_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer) +{ + int retval; + + retval = arm7_9_read_memory(target, address, size, count, buffer); + + return retval; +} + +int arm920t_write_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer) +{ + int retval; + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm9tdmi_common_t *arm9tdmi = arm7_9->arch_info; + arm920t_common_t *arm920t = arm9tdmi->arch_info; + + if ((retval = arm7_9_write_memory(target, address, size, count, buffer)) != ERROR_OK) + return retval; + + if (((size == 4) || (size == 2)) && (count == 1)) + { + if (arm920t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled) + { + DEBUG("D-Cache enabled, writing through to main memory"); + u32 pa, cb, ap; + int type, domain; + + pa = armv4_5_mmu_translate_va(target, &arm920t->armv4_5_mmu, address, &type, &cb, &domain, &ap); + if (type == -1) + return ERROR_OK; + /* cacheable & bufferable means write-back region */ + if (cb == 3) + armv4_5_mmu_write_physical(target, &arm920t->armv4_5_mmu, pa, size, count, buffer); + } + + if (arm920t->armv4_5_mmu.armv4_5_cache.i_cache_enabled) + { + DEBUG("I-Cache enabled, invalidating affected I-Cache line"); + arm920t_write_cp15_interpreted(target, 0xee070f35, 0x0, address); + } + } + + return retval; +} + +int arm920t_soft_reset_halt(struct target_s *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm9tdmi_common_t *arm9tdmi = arm7_9->arch_info; + arm920t_common_t *arm920t = arm9tdmi->arch_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + + if (target->state == TARGET_RUNNING) + { + target->type->halt(target); + } + + while (buf_get_u32(dbg_stat->value, EICE_DBG_CONTROL_DBGACK, 1) == 0) + { + embeddedice_read_reg(dbg_stat); + jtag_execute_queue(); + } + + target->state = TARGET_HALTED; + + /* SVC, ARM state, IRQ and FIQ disabled */ + buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 8, 0xd3); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + + /* start fetching from 0x0 */ + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, 0x0); + armv4_5->core_cache->reg_list[15].dirty = 1; + armv4_5->core_cache->reg_list[15].valid = 1; + + armv4_5->core_mode = ARMV4_5_MODE_SVC; + armv4_5->core_state = ARMV4_5_STATE_ARM; + + arm920t_disable_mmu_caches(target, 1, 1, 1); + arm920t->armv4_5_mmu.mmu_enabled = 0; + arm920t->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled = 0; + arm920t->armv4_5_mmu.armv4_5_cache.i_cache_enabled = 0; + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + + return ERROR_OK; +} + +int arm920t_init_target(struct command_context_s *cmd_ctx, struct target_s *target) +{ + arm9tdmi_init_target(cmd_ctx, target); + + return ERROR_OK; + +} + +int arm920t_quit() +{ + + return ERROR_OK; +} + +int arm920t_init_arch_info(target_t *target, arm920t_common_t *arm920t, int chain_pos, char *variant) +{ + arm9tdmi_common_t *arm9tdmi = &arm920t->arm9tdmi_common; + arm7_9_common_t *arm7_9 = &arm9tdmi->arm7_9_common; + + arm9tdmi_init_arch_info(target, arm9tdmi, chain_pos, variant); + + arm9tdmi->arch_info = arm920t; + arm920t->common_magic = ARM920T_COMMON_MAGIC; + + arm7_9->post_debug_entry = arm920t_post_debug_entry; + arm7_9->pre_restore_context = arm920t_pre_restore_context; + + arm920t->armv4_5_mmu.armv4_5_cache.ctype = -1; + arm920t->armv4_5_mmu.get_ttb = arm920t_get_ttb; + arm920t->armv4_5_mmu.read_memory = arm7_9_read_memory; + arm920t->armv4_5_mmu.write_memory = arm7_9_write_memory; + arm920t->armv4_5_mmu.disable_mmu_caches = arm920t_disable_mmu_caches; + arm920t->armv4_5_mmu.enable_mmu_caches = arm920t_enable_mmu_caches; + arm920t->armv4_5_mmu.has_tiny_pages = 1; + arm920t->armv4_5_mmu.mmu_enabled = 0; + + arm9tdmi->has_single_step = 1; + + return ERROR_OK; +} + +int arm920t_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target) +{ + int chain_pos; + char *variant = NULL; + arm920t_common_t *arm920t = malloc(sizeof(arm920t_common_t)); + + if (argc < 4) + { + ERROR("'target arm920t' requires at least one additional argument"); + exit(-1); + } + + chain_pos = strtoul(args[3], NULL, 0); + + if (argc >= 5) + variant = strdup(args[4]); + + DEBUG("chain_pos: %i, variant: %s", chain_pos, variant); + + arm920t_init_arch_info(target, arm920t, chain_pos, variant); + + return ERROR_OK; +} + +int arm920t_register_commands(struct command_context_s *cmd_ctx) +{ + int retval; + command_t *arm920t_cmd; + + + retval = arm9tdmi_register_commands(cmd_ctx); + + arm920t_cmd = register_command(cmd_ctx, NULL, "arm920t", NULL, COMMAND_ANY, "arm920t specific commands"); + + register_command(cmd_ctx, arm920t_cmd, "cp15", arm920t_handle_cp15_command, COMMAND_EXEC, "display/modify cp15 register <num> [value]"); + register_command(cmd_ctx, arm920t_cmd, "cp15i", arm920t_handle_cp15i_command, COMMAND_EXEC, "display/modify cp15 (interpreted access) <opcode> [value] [address]"); + register_command(cmd_ctx, arm920t_cmd, "cache_info", arm920t_handle_cache_info_command, COMMAND_EXEC, "display information about target caches"); + register_command(cmd_ctx, arm920t_cmd, "virt2phys", arm920t_handle_virt2phys_command, COMMAND_EXEC, "translate va to pa <va>"); + + register_command(cmd_ctx, arm920t_cmd, "mdw_phys", arm920t_handle_md_phys_command, COMMAND_EXEC, "display memory words <physical addr> [count]"); + register_command(cmd_ctx, arm920t_cmd, "mdh_phys", arm920t_handle_md_phys_command, COMMAND_EXEC, "display memory half-words <physical addr> [count]"); + register_command(cmd_ctx, arm920t_cmd, "mdb_phys", arm920t_handle_md_phys_command, COMMAND_EXEC, "display memory bytes <physical addr> [count]"); + + register_command(cmd_ctx, arm920t_cmd, "mww_phys", arm920t_handle_mw_phys_command, COMMAND_EXEC, "write memory word <physical addr> <value>"); + register_command(cmd_ctx, arm920t_cmd, "mwh_phys", arm920t_handle_mw_phys_command, COMMAND_EXEC, "write memory half-word <physical addr> <value>"); + register_command(cmd_ctx, arm920t_cmd, "mwb_phys", arm920t_handle_mw_phys_command, COMMAND_EXEC, "write memory byte <physical addr> <value>"); + + return ERROR_OK; +} + +int arm920t_handle_cp15_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int retval; + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm9tdmi_common_t *arm9tdmi; + arm920t_common_t *arm920t; + arm_jtag_t *jtag_info; + + if (arm920t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm9tdmi, &arm920t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM920t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + /* one or more argument, access a single register (write if second argument is given */ + if (argc >= 1) + { + int address = strtoul(args[0], NULL, 0); + + if (argc == 1) + { + u32 value; + if ((retval = arm920t_read_cp15_physical(target, address, &value)) != ERROR_OK) + { + command_print(cmd_ctx, "couldn't access reg %i", address); + return ERROR_OK; + } + jtag_execute_queue(); + + command_print(cmd_ctx, "%i: %8.8x", address, value); + } + else if (argc == 2) + { + u32 value = strtoul(args[1], NULL, 0); + if ((retval = arm920t_write_cp15_physical(target, address, value)) != ERROR_OK) + { + command_print(cmd_ctx, "couldn't access reg %i", address); + return ERROR_OK; + } + command_print(cmd_ctx, "%i: %8.8x", address, value); + } + } + + return ERROR_OK; +} + +int arm920t_handle_cp15i_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int retval; + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm9tdmi_common_t *arm9tdmi; + arm920t_common_t *arm920t; + arm_jtag_t *jtag_info; + + if (arm920t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm9tdmi, &arm920t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM920t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + /* one or more argument, access a single register (write if second argument is given */ + if (argc >= 1) + { + u32 opcode = strtoul(args[0], NULL, 0); + + if (argc == 1) + { + u32 value; + if ((retval = arm920t_read_cp15_interpreted(target, opcode, &value)) != ERROR_OK) + { + command_print(cmd_ctx, "couldn't execute %8.8x", opcode); + return ERROR_OK; + } + + command_print(cmd_ctx, "%8.8x: %8.8x", opcode, value); + } + else if (argc == 2) + { + u32 value = strtoul(args[1], NULL, 0); + if ((retval = arm920t_write_cp15_interpreted(target, opcode, value, 0)) != ERROR_OK) + { + command_print(cmd_ctx, "couldn't execute %8.8x", opcode); + return ERROR_OK; + } + command_print(cmd_ctx, "%8.8x: %8.8x", opcode, value); + } + else if (argc == 3) + { + u32 value = strtoul(args[1], NULL, 0); + u32 address = strtoul(args[2], NULL, 0); + if ((retval = arm920t_write_cp15_interpreted(target, opcode, value, address)) != ERROR_OK) + { + command_print(cmd_ctx, "couldn't execute %8.8x", opcode); + return ERROR_OK; + } + command_print(cmd_ctx, "%8.8x: %8.8x %8.8x", opcode, value, address); + } + } + + return ERROR_OK; +} + +int arm920t_handle_cache_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm9tdmi_common_t *arm9tdmi; + arm920t_common_t *arm920t; + + if (arm920t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm9tdmi, &arm920t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM920t target"); + return ERROR_OK; + } + + return armv4_5_handle_cache_info_command(cmd_ctx, &arm920t->armv4_5_mmu.armv4_5_cache); +} + +int arm920t_handle_virt2phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm9tdmi_common_t *arm9tdmi; + arm920t_common_t *arm920t; + arm_jtag_t *jtag_info; + + if (arm920t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm9tdmi, &arm920t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM920t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + return armv4_5_mmu_handle_virt2phys_command(cmd_ctx, cmd, args, argc, target, &arm920t->armv4_5_mmu); +} + +int arm920t_handle_md_phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm9tdmi_common_t *arm9tdmi; + arm920t_common_t *arm920t; + arm_jtag_t *jtag_info; + + if (arm920t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm9tdmi, &arm920t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM920t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + return armv4_5_mmu_handle_md_phys_command(cmd_ctx, cmd, args, argc, target, &arm920t->armv4_5_mmu); +} + +int arm920t_handle_mw_phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + arm9tdmi_common_t *arm9tdmi; + arm920t_common_t *arm920t; + arm_jtag_t *jtag_info; + + if (arm920t_get_arch_pointers(target, &armv4_5, &arm7_9, &arm9tdmi, &arm920t) != ERROR_OK) + { + command_print(cmd_ctx, "current target isn't an ARM920t target"); + return ERROR_OK; + } + + jtag_info = &arm7_9->jtag_info; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + return armv4_5_mmu_handle_mw_phys_command(cmd_ctx, cmd, args, argc, target, &arm920t->armv4_5_mmu); +} diff --git a/src/target/arm920t.h b/src/target/arm920t.h new file mode 100644 index 00000000..bedf31f2 --- /dev/null +++ b/src/target/arm920t.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ARM920T_H +#define ARM920T_H + +#include "target.h" +#include "register.h" +#include "embeddedice.h" +#include "arm_jtag.h" +#include "arm9tdmi.h" +#include "armv4_5_mmu.h" +#include "armv4_5_cache.h" + +#define ARM920T_COMMON_MAGIC 0xa920a920 + +typedef struct arm920t_common_s +{ + int common_magic; + armv4_5_mmu_common_t armv4_5_mmu; + arm9tdmi_common_t arm9tdmi_common; + u32 cp15_control_reg; + u32 d_fsr; + u32 i_fsr; + u32 d_far; + u32 i_far; +} arm920t_common_t; + +#endif /* ARM920T_H */ diff --git a/src/target/arm9tdmi.c b/src/target/arm9tdmi.c new file mode 100644 index 00000000..48b201a1 --- /dev/null +++ b/src/target/arm9tdmi.c @@ -0,0 +1,848 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" + +#include "arm9tdmi.h" + +#include "arm7_9_common.h" +#include "register.h" +#include "target.h" +#include "armv4_5.h" +#include "embeddedice.h" +#include "log.h" +#include "jtag.h" +#include "arm_jtag.h" + +#include <stdlib.h> +#include <string.h> + +#if 0 +#define _DEBUG_INSTRUCTION_EXECUTION_ +#endif + +/* cli handling */ +int arm9tdmi_register_commands(struct command_context_s *cmd_ctx); + +/* forward declarations */ +int arm9tdmi_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target); +int arm9tdmi_init_target(struct command_context_s *cmd_ctx, struct target_s *target); +int arm9tdmi_quit(); + +/* target function declarations */ +enum target_state arm9tdmi_poll(struct target_s *target); +int arm9tdmi_halt(target_t *target); +int arm9tdmi_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); + +target_type_t arm9tdmi_target = +{ + .name = "arm9tdmi", + + .poll = arm7_9_poll, + .arch_state = armv4_5_arch_state, + + .halt = arm7_9_halt, + .resume = arm7_9_resume, + .step = arm7_9_step, + + .assert_reset = arm7_9_assert_reset, + .deassert_reset = arm7_9_deassert_reset, + .soft_reset_halt = arm7_9_soft_reset_halt, + + .get_gdb_reg_list = armv4_5_get_gdb_reg_list, + + .read_memory = arm7_9_read_memory, + .write_memory = arm7_9_write_memory, + .bulk_write_memory = arm7_9_bulk_write_memory, + + .add_breakpoint = arm7_9_add_breakpoint, + .remove_breakpoint = arm7_9_remove_breakpoint, + .add_watchpoint = arm7_9_add_watchpoint, + .remove_watchpoint = arm7_9_remove_watchpoint, + + .register_commands = arm9tdmi_register_commands, + .target_command = arm9tdmi_target_command, + .init_target = arm9tdmi_init_target, + .quit = arm9tdmi_quit +}; + +int arm9tdmi_examine_debug_reason(target_t *target) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + + /* only check the debug reason if we don't know it already */ + if ((target->debug_reason != DBG_REASON_DBGRQ) + && (target->debug_reason != DBG_REASON_SINGLESTEP)) + { + scan_field_t fields[3]; + u8 databus[4]; + u8 instructionbus[4]; + u8 debug_reason; + + jtag_add_end_state(TAP_PD); + + fields[0].device = arm7_9->jtag_info.chain_pos; + fields[0].num_bits = 32; + fields[0].out_value = NULL; + fields[0].out_mask = NULL; + fields[0].in_value = databus; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = arm7_9->jtag_info.chain_pos; + fields[1].num_bits = 3; + fields[1].out_value = NULL; + fields[1].out_mask = NULL; + fields[1].in_value = &debug_reason; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = arm7_9->jtag_info.chain_pos; + fields[2].num_bits = 32; + fields[2].out_value = NULL; + fields[2].out_mask = NULL; + fields[2].in_value = instructionbus; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + arm_jtag_scann(&arm7_9->jtag_info, 0x1); + arm_jtag_set_instr(&arm7_9->jtag_info, arm7_9->jtag_info.intest_instr); + + jtag_add_dr_scan(3, fields, TAP_PD); + jtag_execute_queue(); + + fields[0].in_value = NULL; + fields[0].out_value = databus; + fields[1].in_value = NULL; + fields[1].out_value = &debug_reason; + fields[2].in_value = NULL; + fields[2].out_value = instructionbus; + + jtag_add_dr_scan(3, fields, TAP_PD); + + if (debug_reason & 0x4) + if (debug_reason & 0x2) + target->debug_reason = DBG_REASON_WPTANDBKPT; + else + target->debug_reason = DBG_REASON_WATCHPOINT; + else + target->debug_reason = DBG_REASON_BREAKPOINT; + } + + return ERROR_OK; +} + +/* put an instruction in the ARM9TDMI pipeline or write the data bus, and optionally read data */ +int arm9tdmi_clock_out(arm_jtag_t *jtag_info, u32 instr, u32 out, u32 *in, int sysspeed) +{ + scan_field_t fields[3]; + u8 out_buf[4]; + u8 instr_buf[4]; + u8 sysspeed_buf = 0x0; + + /* prepare buffer */ + buf_set_u32(out_buf, 0, 32, out); + + instr = flip_u32(instr, 32); + buf_set_u32(instr_buf, 0, 32, instr); + + if (sysspeed) + buf_set_u32(&sysspeed_buf, 2, 1, 1); + + jtag_add_end_state(TAP_PD); + arm_jtag_scann(jtag_info, 0x1); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 32; + fields[0].out_value = out_buf; + fields[0].out_mask = NULL; + if (in) + { + fields[0].in_value = (u8*)in; + } else + { + fields[0].in_value = NULL; + } + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 3; + fields[1].out_value = &sysspeed_buf; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = jtag_info->chain_pos; + fields[2].num_bits = 32; + fields[2].out_value = instr_buf; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + jtag_add_dr_scan(3, fields, -1); + + jtag_add_runtest(0, -1); + +#ifdef _DEBUG_INSTRUCTION_EXECUTION_ + { + char* in_string; + jtag_execute_queue(); + + if (in) + { + in_string = buf_to_char((u8*)in, 32); + DEBUG("instr: 0x%8.8x, out: 0x%8.8x, in: %s", flip_u32(instr, 32), out, in_string); + free(in_string); + } + else + DEBUG("instr: 0x%8.8x, out: 0x%8.8x", flip_u32(instr, 32), out); + } +#endif + + return ERROR_OK; +} + +/* just read data (instruction and data-out = don't care) */ +int arm9tdmi_clock_data_in(arm_jtag_t *jtag_info, u32 *in) +{ + scan_field_t fields[3]; + + jtag_add_end_state(TAP_PD); + arm_jtag_scann(jtag_info, 0x1); + arm_jtag_set_instr(jtag_info, jtag_info->intest_instr); + + fields[0].device = jtag_info->chain_pos; + fields[0].num_bits = 32; + fields[0].out_value = NULL; + fields[0].out_mask = NULL; + fields[0].in_value = (u8*)in; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + + fields[1].device = jtag_info->chain_pos; + fields[1].num_bits = 3; + fields[1].out_value = NULL; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + + fields[2].device = jtag_info->chain_pos; + fields[2].num_bits = 32; + fields[2].out_value = NULL; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + jtag_add_dr_scan(3, fields, -1); + + jtag_add_runtest(0, -1); + +#ifdef _DEBUG_INSTRUCTION_EXECUTION_ + { + char* in_string; + jtag_execute_queue(); + + if (in) + { + in_string = buf_to_char((u8*)in, 32); + DEBUG("in: %s", in_string); + free(in_string); + } + } +#endif + + return ERROR_OK; +} + +void arm9tdmi_change_to_arm(target_t *target, u32 *r0, u32 *pc) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* save r0 before using it and put system in ARM state + * to allow common handling of ARM and THUMB debugging */ + + /* fetch STR r0, [r0] */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_STR(0, 0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + /* STR r0, [r0] in Memory */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, r0, 0); + + /* MOV r0, r15 fetched, STR in Decode */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_MOV(0, 15), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_STR(0, 0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + /* nothing fetched, STR r0, [r0] in Memory */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, pc, 0); + + /* fetch MOV */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_MOV_IM(0, 0x0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + + /* fetch BX */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_BX(0), 0, NULL, 0); + /* NOP fetched, BX in Decode, MOV in Execute */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + /* NOP fetched, BX in Execute (1) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + + jtag_execute_queue(); + + /* fix program counter: + * MOV r0, r15 was the 5th instruction (+8) + * reading PC in Thumb state gives address of instruction + 4 + */ + *pc -= 0xc; +} + +void arm9tdmi_read_core_regs(target_t *target, u32 mask, u32* core_regs[16]) +{ + int i; + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* STMIA r0-15, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_STMIA(0, mask & 0xffff, 0, 0), 0, NULL, 0); + + /* fetch NOP, STM in DECODE stage */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* fetch NOP, STM in EXECUTE stage (1st cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + + for (i = 0; i <= 15; i++) + { + if (mask & (1 << i)) + /* nothing fetched, STM in MEMORY (i'th cycle) */ + arm9tdmi_clock_data_in(jtag_info, core_regs[i]); + } + +} + +void arm9tdmi_read_xpsr(target_t *target, u32 *xpsr, int spsr) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* MRS r0, cpsr */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_MRS(0, spsr & 1), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + + /* STR r0, [r15] */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_STR(0, 15), 0, NULL, 0); + /* fetch NOP, STR in DECODE stage */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* fetch NOP, STR in EXECUTE stage (1st cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, STR in MEMORY */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, xpsr, 0); + +} + +void arm9tdmi_write_xpsr(target_t *target, u32 xpsr, int spsr) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + DEBUG("xpsr: %8.8x, spsr: %i", xpsr, spsr); + + /* MSR1 fetched */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM(xpsr & 0xff, 0, 1, spsr), 0, NULL, 0); + /* MSR2 fetched, MSR1 in DECODE */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff00) >> 8, 0xc, 2, spsr), 0, NULL, 0); + /* MSR3 fetched, MSR1 in EXECUTE (1), MSR2 in DECODE */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff0000) >> 16, 0x8, 4, spsr), 0, NULL, 0); + /* nothing fetched, MSR1 in EXECUTE (2) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, MSR1 in EXECUTE (3) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* MSR4 fetched, MSR2 in EXECUTE (1), MSR3 in DECODE */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM((xpsr & 0xff000000) >> 24, 0x4, 8, spsr), 0, NULL, 0); + /* nothing fetched, MSR2 in EXECUTE (2) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, MSR2 in EXECUTE (3) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* NOP fetched, MSR3 in EXECUTE (1), MSR4 in DECODE */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, MSR3 in EXECUTE (2) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, MSR3 in EXECUTE (3) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* NOP fetched, MSR4 in EXECUTE (1) */ + /* last MSR writes flags, which takes only one cycle */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); +} + +void arm9tdmi_write_xpsr_im8(target_t *target, u8 xpsr_im, int rot, int spsr) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + DEBUG("xpsr_im: %2.2x, rot: %i, spsr: %i", xpsr_im, rot, spsr); + + /* MSR fetched */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_MSR_IM(xpsr_im, rot, 1, spsr), 0, NULL, 0); + /* NOP fetched, MSR in DECODE */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* NOP fetched, MSR in EXECUTE (1) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + + /* rot == 4 writes flags, which takes only one cycle */ + if (rot != 4) + { + /* nothing fetched, MSR in EXECUTE (2) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, MSR in EXECUTE (3) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + } +} + +void arm9tdmi_write_core_regs(target_t *target, u32 mask, u32 core_regs[16]) +{ + int i; + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* LDMIA r0-15, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, mask & 0xffff, 0, 0), 0, NULL, 0); + + /* fetch NOP, LDM in DECODE stage */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + + for (i = 0; i <= 15; i++) + { + if (mask & (1 << i)) + /* nothing fetched, LDM still in EXECUTE (1+i cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, core_regs[i], NULL, 0); + } + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + +} + +void arm9tdmi_load_word_regs(target_t *target, u32 mask) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed load-multiple into the pipeline */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, mask & 0xffff, 0, 1), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + +} + +void arm9tdmi_load_hword_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed load half-word into the pipeline */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_LDRH_IP(num, 0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); +} + +void arm9tdmi_load_byte_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed load byte into the pipeline */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_LDRB_IP(num, 0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + +} + +void arm9tdmi_store_word_regs(target_t *target, u32 mask) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed store-multiple into the pipeline */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_STMIA(0, mask, 0, 1), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + +} + +void arm9tdmi_store_hword_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed store half-word into the pipeline */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_STRH_IP(num, 0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + +} + +void arm9tdmi_store_byte_reg(target_t *target, int num) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* put system-speed store byte into the pipeline */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_STRB_IP(num, 0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + +} + +void arm9tdmi_write_pc(target_t *target, u32 pc) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + /* LDMIA r0-15, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, 0x8000, 0, 0), 0, NULL, 0); + + /* fetch NOP, LDM in DECODE stage */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (2nd cycle) (output data) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, pc, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (3rd cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (4th cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (5th cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + +} + +void arm9tdmi_branch_resume(target_t *target) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + + arm9tdmi_clock_out(jtag_info, ARMV4_5_B(0xfffffc, 0), 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 1); + +} + +void arm9tdmi_branch_resume_thumb(target_t *target) +{ + DEBUG(""); + + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + reg_t *dbg_stat = &arm7_9->eice_cache->reg_list[EICE_DBG_STAT]; + + /* LDMIA r0-15, [r0] at debug speed + * register values will start to appear on 4th DCLK + */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_LDMIA(0, 0x1, 0, 0), 0, NULL, 0); + + /* fetch NOP, LDM in DECODE stage */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* fetch NOP, LDM in EXECUTE stage (1st cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (2nd cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32) | 1, NULL, 0); + /* nothing fetched, LDM in EXECUTE stage (3rd cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + + /* Branch and eXchange */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_BX(0), 0, NULL, 0); + + embeddedice_read_reg(dbg_stat); + + /* fetch NOP, BX in DECODE stage */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + + embeddedice_read_reg(dbg_stat); + + /* fetch NOP, BX in EXECUTE stage (1st cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_NOP, 0, NULL, 0); + + /* target is now in Thumb state */ + embeddedice_read_reg(dbg_stat); + + /* clean r0 bits to avoid alignment problems */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_MOV_IM(0, 0x0), 0, NULL, 0); + /* load r0 value, MOV_IM in Decode*/ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_LDR(0, 0), 0, NULL, 0); + /* fetch NOP, LDR in Decode, MOV_IM in Execute */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + /* fetch NOP, LDR in Execute */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + /* nothing fetched, LDR in EXECUTE stage (2nd cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, buf_get_u32(armv4_5->core_cache->reg_list[0].value, 0, 32), NULL, 0); + /* nothing fetched, LDR in EXECUTE stage (3rd cycle) */ + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + + embeddedice_read_reg(dbg_stat); + + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_B(0x7f6), 0, NULL, 1); + arm9tdmi_clock_out(jtag_info, ARMV4_5_T_NOP, 0, NULL, 0); + +} + +void arm9tdmi_enable_single_step(target_t *target) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm9tdmi_common_t *arm9 = arm7_9->arch_info; + + if (arm9->has_single_step) + { + buf_set_u32(arm7_9->eice_cache->reg_list[EICE_DBG_CTRL].value, 3, 1, 1); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_DBG_CTRL]); + } + else + { + arm7_9_enable_eice_step(target); + } +} + +void arm9tdmi_disable_single_step(target_t *target) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm9tdmi_common_t *arm9 = arm7_9->arch_info; + + if (arm9->has_single_step) + { + buf_set_u32(arm7_9->eice_cache->reg_list[EICE_DBG_CTRL].value, 3, 1, 0); + embeddedice_store_reg(&arm7_9->eice_cache->reg_list[EICE_DBG_CTRL]); + } + else + { + arm7_9_disable_eice_step(target); + } +} + +void arm9tdmi_build_reg_cache(target_t *target) +{ + reg_cache_t **cache_p = register_get_last_cache_p(&target->reg_cache); + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + arm7_9_common_t *arm7_9 = armv4_5->arch_info; + arm_jtag_t *jtag_info = &arm7_9->jtag_info; + arm9tdmi_common_t *arm9tdmi = arm7_9->arch_info; + + + (*cache_p) = armv4_5_build_reg_cache(target, armv4_5); + armv4_5->core_cache = (*cache_p); + + (*cache_p)->next = embeddedice_build_reg_cache(target, jtag_info, 0); + arm7_9->eice_cache = (*cache_p)->next; + + if (arm9tdmi->has_monitor_mode) + (*cache_p)->next->reg_list[0].size = 6; + else + (*cache_p)->next->reg_list[0].size = 4; + + (*cache_p)->next->reg_list[1].size = 5; + +} + +int arm9tdmi_init_target(struct command_context_s *cmd_ctx, struct target_s *target) +{ + + arm9tdmi_build_reg_cache(target); + + return ERROR_OK; + +} + +int arm9tdmi_quit() +{ + + return ERROR_OK; +} + +int arm9tdmi_init_arch_info(target_t *target, arm9tdmi_common_t *arm9tdmi, int chain_pos, char *variant) +{ + armv4_5_common_t *armv4_5; + arm7_9_common_t *arm7_9; + + arm7_9 = &arm9tdmi->arm7_9_common; + armv4_5 = &arm7_9->armv4_5_common; + + /* prepare JTAG information for the new target */ + arm7_9->jtag_info.chain_pos = chain_pos; + arm7_9->jtag_info.scann_size = 5; + + /* register arch-specific functions */ + arm7_9->examine_debug_reason = arm9tdmi_examine_debug_reason; + arm7_9->change_to_arm = arm9tdmi_change_to_arm; + arm7_9->read_core_regs = arm9tdmi_read_core_regs; + arm7_9->read_xpsr = arm9tdmi_read_xpsr; + + arm7_9->write_xpsr = arm9tdmi_write_xpsr; + arm7_9->write_xpsr_im8 = arm9tdmi_write_xpsr_im8; + arm7_9->write_core_regs = arm9tdmi_write_core_regs; + + arm7_9->load_word_regs = arm9tdmi_load_word_regs; + arm7_9->load_hword_reg = arm9tdmi_load_hword_reg; + arm7_9->load_byte_reg = arm9tdmi_load_byte_reg; + + arm7_9->store_word_regs = arm9tdmi_store_word_regs; + arm7_9->store_hword_reg = arm9tdmi_store_hword_reg; + arm7_9->store_byte_reg = arm9tdmi_store_byte_reg; + + arm7_9->write_pc = arm9tdmi_write_pc; + arm7_9->branch_resume = arm9tdmi_branch_resume; + arm7_9->branch_resume_thumb = arm9tdmi_branch_resume_thumb; + + arm7_9->enable_single_step = arm9tdmi_enable_single_step; + arm7_9->disable_single_step = arm9tdmi_disable_single_step; + + arm7_9->pre_debug_entry = NULL; + arm7_9->post_debug_entry = NULL; + + arm7_9->pre_restore_context = NULL; + arm7_9->post_restore_context = NULL; + + /* initialize arch-specific breakpoint handling */ + buf_set_u32((u8*)(&arm7_9->arm_bkpt), 0, 32, 0xdeeedeee); + buf_set_u32((u8*)(&arm7_9->thumb_bkpt), 0, 16, 0xdeee); + + arm7_9->sw_bkpts_use_wp = 1; + arm7_9->sw_bkpts_enabled = 0; + arm7_9->dbgreq_adjust_pc = 3; + arm7_9->arch_info = arm9tdmi; + arm7_9->use_dbgrq = 1; + + arm9tdmi->common_magic = ARM9TDMI_COMMON_MAGIC; + arm9tdmi->has_monitor_mode = 0; + arm9tdmi->has_single_step = 0; + arm9tdmi->arch_info = NULL; + + if (variant) + { + if (strcmp(variant, "arm920t") == 0) + arm9tdmi->has_single_step = 1; + else if (strcmp(variant, "arm922t") == 0) + arm9tdmi->has_single_step = 1; + else if (strcmp(variant, "arm940t") == 0) + arm9tdmi->has_single_step = 1; + } + + arm7_9_init_arch_info(target, arm7_9); + + return ERROR_OK; +} + +/* target arm9tdmi <endianess> <startup_mode> <chain_pos> <variant>*/ +int arm9tdmi_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target) +{ + int chain_pos; + char *variant = NULL; + arm9tdmi_common_t *arm9tdmi = malloc(sizeof(arm9tdmi_common_t)); + + if (argc < 4) + { + ERROR("'target arm9tdmi' requires at least one additional argument"); + exit(-1); + } + + chain_pos = strtoul(args[3], NULL, 0); + + if (argc >= 5) + variant = strdup(args[4]); + + arm9tdmi_init_arch_info(target, arm9tdmi, chain_pos, variant); + + return ERROR_OK; +} + +int arm9tdmi_register_commands(struct command_context_s *cmd_ctx) +{ + int retval; + + retval = arm7_9_register_commands(cmd_ctx); + + return ERROR_OK; + +} + diff --git a/src/target/arm9tdmi.h b/src/target/arm9tdmi.h new file mode 100644 index 00000000..7bbaac7c --- /dev/null +++ b/src/target/arm9tdmi.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ARM9TDMI_H +#define ARM9TDMI_H + +#include "target.h" +#include "register.h" +#include "armv4_5.h" +#include "embeddedice.h" +#include "arm_jtag.h" +#include "arm7_9_common.h" + +#define ARM9TDMI_COMMON_MAGIC 0x00a900a9 + +typedef struct arm9tdmi_common_s +{ + int common_magic; + char *variant; + int has_monitor_mode; + int has_single_step; + void *arch_info; + arm7_9_common_t arm7_9_common; +} arm9tdmi_common_t; + +extern int arm9tdmi_init_target(struct command_context_s *cmd_ctx, struct target_s *target); +extern int arm9tdmi_init_arch_info(target_t *target, arm9tdmi_common_t *arm9tdmi, int chain_pos, char *variant); +extern int arm9tdmi_register_commands(struct command_context_s *cmd_ctx); + +extern int arm9tdmi_clock_out(arm_jtag_t *jtag_info, u32 instr, u32 out, u32 *in, int sysspeed); +extern int arm9tdmi_clock_data_in(arm_jtag_t *jtag_info, u32 *in); +extern void arm9tdmi_read_core_regs(target_t *target, u32 mask, u32* core_regs[16]); +extern void arm9tdmi_write_core_regs(target_t *target, u32 mask, u32 core_regs[16]); + +#endif /* ARM9TDMI_H */ diff --git a/src/target/arm_jtag.c b/src/target/arm_jtag.c new file mode 100644 index 00000000..c401d8f3 --- /dev/null +++ b/src/target/arm_jtag.c @@ -0,0 +1,116 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" + +#include "arm_jtag.h" + +#include "binarybuffer.h" +#include "log.h" +#include "jtag.h" + +#include <stdlib.h> + +int arm_jtag_set_instr(arm_jtag_t *jtag_info, u32 new_instr) +{ + jtag_device_t *device = jtag_get_device(jtag_info->chain_pos); + + if (buf_get_u32(device->cur_instr, 0, device->ir_length) != new_instr) + { + scan_field_t field; + + field.device = jtag_info->chain_pos; + field.num_bits = device->ir_length; + field.out_value = calloc(CEIL(field.num_bits, 8), 1); + buf_set_u32(field.out_value, 0, field.num_bits, new_instr); + field.out_mask = NULL; + field.in_value = NULL; + field.in_check_value = NULL; + field.in_check_mask = NULL; + field.in_handler = NULL; + field.in_handler_priv = NULL; + + jtag_add_ir_scan(1, &field, -1); + + free(field.out_value); + } + + return ERROR_OK; +} + +int arm_jtag_scann(arm_jtag_t *jtag_info, u32 new_scan_chain) +{ + if(jtag_info->cur_scan_chain != new_scan_chain) + { + scan_field_t field; + + field.device = jtag_info->chain_pos; + field.num_bits = jtag_info->scann_size; + field.out_value = calloc(CEIL(field.num_bits, 8), 1); + buf_set_u32(field.out_value, 0, field.num_bits, new_scan_chain); + field.out_mask = NULL; + //field.in_value = &scan_n_capture; + field.in_value = NULL; + field.in_check_value = NULL; + field.in_check_mask = NULL; + field.in_handler = NULL; + field.in_handler_priv = NULL; + + arm_jtag_set_instr(jtag_info, jtag_info->scann_instr); + jtag_add_dr_scan(1, &field, -1); + + jtag_info->cur_scan_chain = new_scan_chain; + + free(field.out_value); + } + + return ERROR_OK; +} + +int arm_jtag_reset_callback(enum jtag_event event, void *priv) +{ + arm_jtag_t *jtag_info = priv; + + if (event == JTAG_TRST_ASSERTED) + { + jtag_info->cur_scan_chain = 0; + } + + return ERROR_OK; +} + +int arm_jtag_setup_connection(arm_jtag_t *jtag_info) +{ + jtag_info->scann_instr = 0x2; + jtag_info->cur_scan_chain = 0; + jtag_info->intest_instr = 0xc; + + jtag_register_event_callback(arm_jtag_reset_callback, jtag_info); + + return ERROR_OK; +} + +int arm_jtag_buf_to_u32_flip(u8 *in_buf, void *priv) +{ + u32 *dest = priv; + + *dest = flip_u32(buf_get_u32(in_buf, 0, 32), 32); + + return ERROR_OK; +} diff --git a/src/target/arm_jtag.h b/src/target/arm_jtag.h new file mode 100644 index 00000000..a9f90846 --- /dev/null +++ b/src/target/arm_jtag.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ARM_JTAG +#define ARM_JTAG + +#include "types.h" + +typedef struct arm_jtag_s +{ + int chain_pos; + + int scann_size; + u32 scann_instr; + int cur_scan_chain; + + u32 intest_instr; +} arm_jtag_t; + +extern int arm_jtag_set_instr(arm_jtag_t *jtag_info, u32 new_instr); +extern int arm_jtag_scann(arm_jtag_t *jtag_info, u32 new_scan_chain); +extern int arm_jtag_buf_to_u32_flip(u8 *in_buf, void *priv); +extern int arm_jtag_setup_connection(arm_jtag_t *jtag_info); + +#endif /* ARM_JTAG */ + diff --git a/src/target/armv4_5.c b/src/target/armv4_5.c new file mode 100644 index 00000000..51fd4b42 --- /dev/null +++ b/src/target/armv4_5.c @@ -0,0 +1,583 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" + +#include "armv4_5.h" + +#include "target.h" +#include "register.h" +#include "log.h" +#include "binarybuffer.h" +#include "command.h" + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +bitfield_desc_t armv4_5_psr_bitfield_desc[] = +{ + {"M[4:0]", 5}, + {"T", 1}, + {"F", 1}, + {"I", 1}, + {"reserved", 16}, + {"J", 1}, + {"reserved", 2}, + {"Q", 1}, + {"V", 1}, + {"C", 1}, + {"Z", 1}, + {"N", 1}, +}; + +char* armv4_5_core_reg_list[] = +{ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13_usr", "lr_usr", "pc", + + "r8_fiq", "r9_fiq", "r10_fiq", "r11_fiq", "r12_fiq", "r13_fiq", "lr_fiq", + + "r13_irq", "lr_irq", + + "r13_svc", "lr_svc", + + "r13_abt", "lr_abt", + + "r13_und", "lr_und", + + "cpsr", "spsr_fiq", "spsr_irq", "spsr_svc", "spsr_abt", "spsr_und" +}; + +char* armv4_5_mode_strings[] = +{ + "User", "FIQ", "IRQ", "Supervisor", "Abort", "Undefined", "System" +}; + +char* armv4_5_state_strings[] = +{ + "ARM", "Thumb", "Jazelle" +}; + +int armv4_5_core_reg_arch_type = -1; + +armv4_5_core_reg_t armv4_5_core_reg_list_arch_info[] = +{ + {0, ARMV4_5_MODE_ANY, NULL, NULL}, + {1, ARMV4_5_MODE_ANY, NULL, NULL}, + {2, ARMV4_5_MODE_ANY, NULL, NULL}, + {3, ARMV4_5_MODE_ANY, NULL, NULL}, + {4, ARMV4_5_MODE_ANY, NULL, NULL}, + {5, ARMV4_5_MODE_ANY, NULL, NULL}, + {6, ARMV4_5_MODE_ANY, NULL, NULL}, + {7, ARMV4_5_MODE_ANY, NULL, NULL}, + {8, ARMV4_5_MODE_ANY, NULL, NULL}, + {9, ARMV4_5_MODE_ANY, NULL, NULL}, + {10, ARMV4_5_MODE_ANY, NULL, NULL}, + {11, ARMV4_5_MODE_ANY, NULL, NULL}, + {12, ARMV4_5_MODE_ANY, NULL, NULL}, + {13, ARMV4_5_MODE_USR, NULL, NULL}, + {14, ARMV4_5_MODE_USR, NULL, NULL}, + {15, ARMV4_5_MODE_ANY, NULL, NULL}, + + {8, ARMV4_5_MODE_FIQ, NULL, NULL}, + {9, ARMV4_5_MODE_FIQ, NULL, NULL}, + {10, ARMV4_5_MODE_FIQ, NULL, NULL}, + {11, ARMV4_5_MODE_FIQ, NULL, NULL}, + {12, ARMV4_5_MODE_FIQ, NULL, NULL}, + {13, ARMV4_5_MODE_FIQ, NULL, NULL}, + {14, ARMV4_5_MODE_FIQ, NULL, NULL}, + + {13, ARMV4_5_MODE_IRQ, NULL, NULL}, + {14, ARMV4_5_MODE_IRQ, NULL, NULL}, + + {13, ARMV4_5_MODE_SVC, NULL, NULL}, + {14, ARMV4_5_MODE_SVC, NULL, NULL}, + + {13, ARMV4_5_MODE_ABT, NULL, NULL}, + {14, ARMV4_5_MODE_ABT, NULL, NULL}, + + {13, ARMV4_5_MODE_UND, NULL, NULL}, + {14, ARMV4_5_MODE_UND, NULL, NULL}, + + {16, ARMV4_5_MODE_ANY, NULL, NULL}, + {16, ARMV4_5_MODE_FIQ, NULL, NULL}, + {16, ARMV4_5_MODE_IRQ, NULL, NULL}, + {16, ARMV4_5_MODE_SVC, NULL, NULL}, + {16, ARMV4_5_MODE_ABT, NULL, NULL}, + {16, ARMV4_5_MODE_UND, NULL, NULL} +}; + +/* map core mode (USR, FIQ, ...) and register number to indizes into the register cache */ +int armv4_5_core_reg_map[7][17] = +{ + { /* USR */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 31 + }, + { /* FIQ */ + 0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 15, 32 + }, + { /* IRQ */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 23, 24, 15, 33 + }, + { /* SVC */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 25, 26, 15, 34 + }, + { /* ABT */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 27, 28, 15, 35 + }, + { /* UND */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 29, 30, 15, 36 + }, + { /* SYS */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 31 + } +}; + +u8 armv4_5_gdb_dummy_fp_value[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +reg_t armv4_5_gdb_dummy_fp_reg = +{ + "GDB dummy floating-point register", armv4_5_gdb_dummy_fp_value, 0, 1, 96, NULL, 0, NULL, 0 +}; + +u8 armv4_5_gdb_dummy_fps_value[] = {0, 0, 0, 0}; + +reg_t armv4_5_gdb_dummy_fps_reg = +{ + "GDB dummy floating-point status register", armv4_5_gdb_dummy_fps_value, 0, 1, 32, NULL, 0, NULL, 0 +}; + +/* map psr mode bits to linear number */ +int armv4_5_mode_to_number(enum armv4_5_mode mode) +{ + switch (mode) + { + case 16: return 0; break; + case 17: return 1; break; + case 18: return 2; break; + case 19: return 3; break; + case 23: return 4; break; + case 27: return 5; break; + case 31: return 6; break; + case -1: return 0; break; /* map MODE_ANY to user mode */ + default: + ERROR("invalid mode value encountered"); + return -1; + } +} + +/* map linear number to mode bits */ +enum armv4_5_mode armv4_5_number_to_mode(int number) +{ + switch(number) + { + case 0: return ARMV4_5_MODE_USR; break; + case 1: return ARMV4_5_MODE_FIQ; break; + case 2: return ARMV4_5_MODE_IRQ; break; + case 3: return ARMV4_5_MODE_SVC; break; + case 4: return ARMV4_5_MODE_ABT; break; + case 5: return ARMV4_5_MODE_UND; break; + case 6: return ARMV4_5_MODE_SYS; break; + default: + ERROR("mode index out of bounds"); + return -1; + } +}; + +int armv4_5_get_core_reg(reg_t *reg) +{ + int retval; + armv4_5_core_reg_t *armv4_5 = reg->arch_info; + target_t *target = armv4_5->target; + + if (target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + //retval = armv4_5->armv4_5_common->full_context(target); + retval = armv4_5->armv4_5_common->read_core_reg(target, armv4_5->num, armv4_5->mode); + + return retval; +} + +int armv4_5_set_core_reg(reg_t *reg, u32 value) +{ + armv4_5_core_reg_t *armv4_5 = reg->arch_info; + target_t *target = armv4_5->target; + + if (target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + buf_set_u32(reg->value, 0, 32, value); + reg->dirty = 1; + reg->valid = 1; + + return ERROR_OK; +} + +int armv4_5_invalidate_core_regs(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + int i; + + for (i = 0; i < 37; i++) + { + armv4_5->core_cache->reg_list[i].valid = 0; + armv4_5->core_cache->reg_list[i].dirty = 0; + } + + return ERROR_OK; +} + +reg_cache_t* armv4_5_build_reg_cache(target_t *target, armv4_5_common_t *armv4_5_common) +{ + int num_regs = 37; + reg_cache_t *cache = malloc(sizeof(reg_cache_t)); + reg_t *reg_list = malloc(sizeof(reg_t) * num_regs); + armv4_5_core_reg_t *arch_info = malloc(sizeof(reg_t) * num_regs); + int i; + + cache->name = "arm v4/5 registers"; + cache->next = NULL; + cache->reg_list = reg_list; + cache->num_regs = num_regs; + + if (armv4_5_core_reg_arch_type == -1) + armv4_5_core_reg_arch_type = register_reg_arch_type(armv4_5_get_core_reg, armv4_5_set_core_reg); + + for (i = 0; i < 37; i++) + { + arch_info[i] = armv4_5_core_reg_list_arch_info[i]; + arch_info[i].target = target; + arch_info[i].armv4_5_common = armv4_5_common; + reg_list[i].name = armv4_5_core_reg_list[i]; + reg_list[i].size = 32; + reg_list[i].value = calloc(1, 4); + reg_list[i].dirty = 0; + reg_list[i].valid = 0; + reg_list[i].bitfield_desc = NULL; + reg_list[i].num_bitfields = 0; + reg_list[i].arch_type = armv4_5_core_reg_arch_type; + reg_list[i].arch_info = &arch_info[i]; + } + + return cache; +} + +int armv4_5_arch_state(struct target_s *target, char *buf, int buf_size) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + ERROR("BUG: called for a non-ARMv4/5 target"); + exit(-1); + } + + snprintf(buf, buf_size, + "target halted in %s state due to %s, current mode: %s\ncpsr: 0x%8.8x pc: 0x%8.8x", + armv4_5_state_strings[armv4_5->core_state], + target_debug_reason_strings[target->debug_reason], + armv4_5_mode_strings[armv4_5_mode_to_number(armv4_5->core_mode)], + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + + return ERROR_OK; +} + +int handle_armv4_5_reg_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + char output[128]; + int output_len; + int mode, num; + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5 = target->arch_info; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + command_print(cmd_ctx, "current target isn't an ARMV4/5 target"); + return ERROR_OK; + } + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "error: target must be halted for register accesses"); + return ERROR_OK; + } + + for (num = 0; num <= 15; num++) + { + output_len = 0; + for (mode = 0; mode < 6; mode++) + { + if (!ARMV4_5_CORE_REG_MODENUM(armv4_5->core_cache, mode, num).valid) + { + armv4_5->full_context(target); + } + output_len += snprintf(output + output_len, 128 - output_len, "%8s: %8.8x ", ARMV4_5_CORE_REG_MODENUM(armv4_5->core_cache, mode, num).name, + buf_get_u32(ARMV4_5_CORE_REG_MODENUM(armv4_5->core_cache, mode, num).value, 0, 32)); + } + command_print(cmd_ctx, output); + } + command_print(cmd_ctx, " cpsr: %8.8x spsr_fiq: %8.8x spsr_irq: %8.8x spsr_svc: %8.8x spsr_abt: %8.8x spsr_und: %8.8x", + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_SPSR_FIQ].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_SPSR_IRQ].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_SPSR_SVC].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_SPSR_ABT].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_SPSR_UND].value, 0, 32)); + + return ERROR_OK; +} + +int handle_armv4_5_core_state_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5 = target->arch_info; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + command_print(cmd_ctx, "current target isn't an ARMV4/5 target"); + return ERROR_OK; + } + + if (argc > 0) + { + if (strcmp(args[0], "arm") == 0) + { + armv4_5->core_state = ARMV4_5_STATE_ARM; + } + if (strcmp(args[0], "thumb") == 0) + { + armv4_5->core_state = ARMV4_5_STATE_THUMB; + } + } + + command_print(cmd_ctx, "core state: %s", armv4_5_state_strings[armv4_5->core_state]); + + return ERROR_OK; +} + +int armv4_5_register_commands(struct command_context_s *cmd_ctx) +{ + command_t *armv4_5_cmd; + + armv4_5_cmd = register_command(cmd_ctx, NULL, "armv4_5", NULL, COMMAND_ANY, NULL); + + register_command(cmd_ctx, armv4_5_cmd, "reg", handle_armv4_5_reg_command, COMMAND_EXEC, "display ARM core registers"); + register_command(cmd_ctx, armv4_5_cmd, "core_state", handle_armv4_5_core_state_command, COMMAND_EXEC, "display/change ARM core state <arm|thumb>"); + + return ERROR_OK; +} + +int armv4_5_get_gdb_reg_list(target_t *target, reg_t **reg_list[], int *reg_list_size) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + int i; + + if (target->state != TARGET_HALTED) + { + return ERROR_TARGET_NOT_HALTED; + } + + *reg_list_size = 26; + *reg_list = malloc(sizeof(reg_t*) * (*reg_list_size)); + + for (i = 0; i < 16; i++) + { + (*reg_list)[i] = &ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i); + } + + for (i = 16; i < 24; i++) + { + (*reg_list)[i] = &armv4_5_gdb_dummy_fp_reg; + } + + (*reg_list)[24] = &armv4_5_gdb_dummy_fps_reg; + (*reg_list)[25] = &armv4_5->core_cache->reg_list[ARMV4_5_CPSR]; + + return ERROR_OK; +} + +int armv4_5_run_algorithm(struct target_s *target, int num_mem_params, mem_param_t *mem_params, int num_reg_params, reg_param_t *reg_params, u32 entry_point, u32 exit_point, int timeout_ms, void *arch_info) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + armv4_5_algorithm_t *armv4_5_algorithm_info = arch_info; + enum armv4_5_state core_state = armv4_5->core_state; + enum armv4_5_mode core_mode = armv4_5->core_mode; + u32 context[17]; + u32 cpsr; + int exit_breakpoint_size = 0; + int i; + int retval = ERROR_OK; + + if (armv4_5_algorithm_info->common_magic != ARMV4_5_COMMON_MAGIC) + { + ERROR("current target isn't an ARMV4/5 target"); + return ERROR_TARGET_INVALID; + } + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + for (i = 0; i <= 16; i++) + { + if (!ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).valid) + armv4_5->read_core_reg(target, i, armv4_5_algorithm_info->core_mode); + context[i] = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).value, 0, 32); + } + cpsr = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32); + + for (i = 0; i < num_mem_params; i++) + { + target_write_buffer(target, mem_params[i].address, mem_params[i].size, mem_params[i].value); + } + + for (i = 0; i < num_reg_params; i++) + { + reg_t *reg = register_get_by_name(armv4_5->core_cache, reg_params[i].reg_name, 0); + if (!reg) + { + ERROR("BUG: register '%s' not found", reg_params[i].reg_name); + exit(-1); + } + + if (reg->size != reg_params[i].size) + { + ERROR("BUG: register '%s' size doesn't match reg_params[i].size", reg_params[i].reg_name); + exit(-1); + } + + armv4_5_set_core_reg(reg, buf_get_u32(reg_params[i].value, 0, 32)); + } + + armv4_5->core_state = armv4_5_algorithm_info->core_state; + if (armv4_5->core_state == ARMV4_5_STATE_ARM) + exit_breakpoint_size = 4; + else if (armv4_5->core_state == ARMV4_5_STATE_THUMB) + exit_breakpoint_size = 2; + else + { + ERROR("BUG: can't execute algorithms when not in ARM or Thumb state"); + exit(-1); + } + + if (armv4_5_algorithm_info->core_mode != ARMV4_5_MODE_ANY) + { + DEBUG("setting core_mode: 0x%2.2x", armv4_5_algorithm_info->core_mode); + buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 5, armv4_5_algorithm_info->core_mode); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + } + + if ((retval = breakpoint_add(target, exit_point, exit_breakpoint_size, BKPT_HARD)) != ERROR_OK) + { + ERROR("can't add breakpoint to finish algorithm execution"); + return ERROR_TARGET_FAILURE; + } + + target->type->resume(target, 0, entry_point, 1, 1); + target->type->poll(target); + + while (target->state != TARGET_HALTED) + { + usleep(10000); + target->type->poll(target); + if ((timeout_ms -= 10) <= 0) + { + ERROR("timeout waiting for algorithm to complete, trying to halt target"); + target->type->halt(target); + timeout_ms = 1000; + while (target->state != TARGET_HALTED) + { + usleep(10000); + target->type->poll(target); + if ((timeout_ms -= 10) <= 0) + { + ERROR("target didn't reenter debug state, exiting"); + exit(-1); + } + } + retval = ERROR_TARGET_TIMEOUT; + } + } + + breakpoint_remove(target, exit_point); + + for (i = 0; i < num_mem_params; i++) + { + if (mem_params[i].direction != PARAM_OUT) + target_read_buffer(target, mem_params[i].address, mem_params[i].size, mem_params[i].value); + } + + for (i = 0; i < num_reg_params; i++) + { + if (reg_params[i].direction != PARAM_OUT) + { + + reg_t *reg = register_get_by_name(armv4_5->core_cache, reg_params[i].reg_name, 0); + if (!reg) + { + ERROR("BUG: register '%s' not found", reg_params[i].reg_name); + exit(-1); + } + + if (reg->size != reg_params[i].size) + { + ERROR("BUG: register '%s' size doesn't match reg_params[i].size", reg_params[i].reg_name); + exit(-1); + } + + buf_set_u32(reg_params[i].value, 0, 32, buf_get_u32(reg->value, 0, 32)); + } + } + + for (i = 0; i <= 16; i++) + { + DEBUG("restoring register %s with value 0x%8.8x", ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).name, buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).value, 0, 32)); + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).value, 0, 32, context[i]); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).valid = 1; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_algorithm_info->core_mode, i).dirty = 1; + } + buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32, cpsr); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1; + + armv4_5->core_state = core_state; + armv4_5->core_mode = core_mode; + + return retval; +} + +int armv4_5_init_arch_info(target_t *target, armv4_5_common_t *armv4_5) +{ + target->arch_info = armv4_5; + + armv4_5->common_magic = ARMV4_5_COMMON_MAGIC; + armv4_5->core_state = ARMV4_5_STATE_ARM; + armv4_5->core_mode = ARMV4_5_MODE_USR; + + return ERROR_OK; +} diff --git a/src/target/armv4_5.h b/src/target/armv4_5.h new file mode 100644 index 00000000..a28bfa12 --- /dev/null +++ b/src/target/armv4_5.h @@ -0,0 +1,237 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ARMV4_5_H +#define ARMV4_5_H + +#include "register.h" +#include "target.h" + +enum armv4_5_mode +{ + ARMV4_5_MODE_USR = 16, + ARMV4_5_MODE_FIQ = 17, + ARMV4_5_MODE_IRQ = 18, + ARMV4_5_MODE_SVC = 19, + ARMV4_5_MODE_ABT = 23, + ARMV4_5_MODE_UND = 27, + ARMV4_5_MODE_SYS = 31, + ARMV4_5_MODE_ANY = -1 +}; + +extern char* armv4_5_mode_strings[]; + +enum armv4_5_state +{ + ARMV4_5_STATE_ARM, + ARMV4_5_STATE_THUMB, + ARMV4_5_STATE_JAZELLE, +}; + +extern char* armv4_5_state_strings[]; + +extern int armv4_5_core_reg_map[7][17]; + +#define ARMV4_5_CORE_REG_MODE(cache, mode, num) \ + cache->reg_list[armv4_5_core_reg_map[armv4_5_mode_to_number(mode)][num]] +#define ARMV4_5_CORE_REG_MODENUM(cache, mode, num) \ + cache->reg_list[armv4_5_core_reg_map[mode][num]] + +/* offsets into armv4_5 core register cache */ +enum +{ + ARMV4_5_CPSR = 31, + ARMV4_5_SPSR_FIQ = 32, + ARMV4_5_SPSR_IRQ = 33, + ARMV4_5_SPSR_SVC = 34, + ARMV4_5_SPSR_ABT = 35, + ARMV4_5_SPSR_UND = 36 +}; + +#define ARMV4_5_COMMON_MAGIC 0x0A450A45 + +typedef struct armv4_5_common_s +{ + int common_magic; + reg_cache_t *core_cache; + enum armv4_5_mode core_mode; + enum armv4_5_state core_state; + int (*full_context)(struct target_s *target); + int (*read_core_reg)(struct target_s *target, int num, enum armv4_5_mode mode); + int (*write_core_reg)(struct target_s *target, int num, enum armv4_5_mode mode, u32 value); + void *arch_info; +} armv4_5_common_t; + +typedef struct armv4_5_algorithm_s +{ + int common_magic; + + enum armv4_5_mode core_mode; + enum armv4_5_state core_state; +} armv4_5_algorithm_t; + +typedef struct armv4_5_core_reg_s +{ + int num; + enum armv4_5_mode mode; + target_t *target; + armv4_5_common_t *armv4_5_common; +} armv4_5_core_reg_t; + +extern reg_cache_t* armv4_5_build_reg_cache(target_t *target, armv4_5_common_t *armv4_5_common); +extern enum armv4_5_mode armv4_5_number_to_mode(int number); +extern int armv4_5_mode_to_number(enum armv4_5_mode mode); + +extern int armv4_5_arch_state(struct target_s *target, char *buf, int buf_size); +extern int armv4_5_get_gdb_reg_list(target_t *target, reg_t **reg_list[], int *reg_list_size); +extern int armv4_5_invalidate_core_regs(target_t *target); + +extern int armv4_5_register_commands(struct command_context_s *cmd_ctx); +extern int armv4_5_init_arch_info(target_t *target, armv4_5_common_t *armv4_5); + +extern int armv4_5_run_algorithm(struct target_s *target, int num_mem_params, mem_param_t *mem_params, int num_reg_params, reg_param_t *reg_params, u32 entry_point, u32 exit_point, int timeout_ms, void *arch_info); + +extern int armv4_5_invalidate_core_regs(target_t *target); + +/* ARM mode instructions + */ + +/* Store multiple increment after + * Rn: base register + * List: for each bit in list: store register + * S: in priviledged mode: store user-mode registers + * W=1: update the base register. W=0: leave the base register untouched + */ +#define ARMV4_5_STMIA(Rn, List, S, W) (0xe8800000 | (S << 22) | (W << 21) | (Rn << 16) | (List)) + +/* Load multiple increment after + * Rn: base register + * List: for each bit in list: store register + * S: in priviledged mode: store user-mode registers + * W=1: update the base register. W=0: leave the base register untouched + */ +#define ARMV4_5_LDMIA(Rn, List, S, W) (0xe8900000 | (S << 22) | (W << 21) | (Rn << 16) | (List)) + +/* MOV r8, r8 */ +#define ARMV4_5_NOP (0xe1a08008) + +/* Move PSR to general purpose register + * R=1: SPSR R=0: CPSR + * Rn: target register + */ +#define ARMV4_5_MRS(Rn, R) (0xe10f0000 | (R << 22) | (Rn << 12)) + +/* Store register + * Rd: register to store + * Rn: base register + */ +#define ARMV4_5_STR(Rd, Rn) (0xe5800000 | (Rd << 12) | (Rn << 16)) + +/* Load register + * Rd: register to load + * Rn: base register + */ +#define ARMV4_5_LDR(Rd, Rn) (0xe5900000 | (Rd << 12) | (Rn << 16)) + +/* Move general purpose register to PSR + * R=1: SPSR R=0: CPSR + * Field: Field mask + * 1: control field 2: extension field 4: status field 8: flags field + * Rm: source register + */ +#define ARMV4_5_MSR_GP(Rm, Field, R) (0xe120f000 | Rm | (Field << 16) | (R << 22)) +#define ARMV4_5_MSR_IM(Im, Rotate, Field, R) (0xe320f000 | (Im) | (Rotate << 8) | (Field << 16) | (R << 22)) + +/* Load Register Halfword Immediate Post-Index + * Rd: register to load + * Rn: base register + */ +#define ARMV4_5_LDRH_IP(Rd, Rn) (0xe0d000b2 | (Rd << 12) | (Rn << 16)) + +/* Load Register Byte Immediate Post-Index + * Rd: register to load + * Rn: base register + */ +#define ARMV4_5_LDRB_IP(Rd, Rn) (0xe4d00001 | (Rd << 12) | (Rn << 16)) + +/* Store register Halfword Immediate Post-Index + * Rd: register to store + * Rn: base register + */ +#define ARMV4_5_STRH_IP(Rd, Rn) (0xe0c000b2 | (Rd << 12) | (Rn << 16)) + +/* Store register Byte Immediate Post-Index + * Rd: register to store + * Rn: base register + */ +#define ARMV4_5_STRB_IP(Rd, Rn) (0xe4c00001 | (Rd << 12) | (Rn << 16)) + +/* Branch (and Link) + * Im: Branch target (left-shifted by 2 bits, added to PC) + * L: 1: branch and link 0: branch only + */ +#define ARMV4_5_B(Im, L) (0xea000000 | Im | (L << 24)) + +/* Branch and exchange (ARM state) + * Rm: register holding branch target address + */ +#define ARMV4_5_BX(Rm) (0xe12fff10 | Rm) + +/* Thumb mode instructions + */ + +/* Store register (Thumb mode) + * Rd: source register + * Rn: base register + */ +#define ARMV4_5_T_STR(Rd, Rn) ((0x6000 | Rd | (Rn << 3)) | ((0x6000 | Rd | (Rn << 3)) << 16)) + +/* Load register (Thumb state) + * Rd: destination register + * Rn: base register + */ +#define ARMV4_5_T_LDR(Rd, Rn) ((0x6800 | (Rn << 3) | Rd) | ((0x6800 | (Rn << 3) | Rd) << 16)) + +/* Move hi register (Thumb mode) + * Rd: destination register + * Rm: source register + */ +#define ARMV4_5_T_MOV(Rd, Rm) ((0x4600 | (Rd & 0x7) | ((Rd & 0x8) << 4) | ((Rm & 0x7) << 3) | ((Rm & 0x8) << 3)) | ((0x4600 | (Rd & 0x7) | ((Rd & 0x8) << 4) | ((Rm & 0x7) << 3) | ((Rm & 0x8) << 3)) << 16)) + +/* No operation (Thumb mode) + */ +#define ARMV4_5_T_NOP (0x1c3f | (0x1c3f << 16)) + +/* Move immediate to register (Thumb state) + * Rd: destination register + * Im: 8-bit immediate value + */ +#define ARMV4_5_T_MOV_IM(Rd, Im) ((0x2000 | (Rd << 8) | Im) | ((0x2000 | (Rd << 8) | Im) << 16)) + +/* Branch and Exchange + * Rm: register containing branch target + */ +#define ARMV4_5_T_BX(Rm) ((0x4700 | (Rm << 3)) | ((0x4700 | (Rm << 3)) << 16)) + +/* Branch (Thumb state) + * Imm: Branch target + */ +#define ARMV4_5_T_B(Imm) ((0xe000 | Imm) | ((0xe000 | Imm) << 16)) + +#endif /* ARMV4_5_H */ diff --git a/src/target/armv4_5_cache.c b/src/target/armv4_5_cache.c new file mode 100644 index 00000000..326f10ee --- /dev/null +++ b/src/target/armv4_5_cache.c @@ -0,0 +1,112 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "armv4_5_cache.h" + +#include "log.h" +#include "command.h" + +int armv4_5_identify_cache(u32 cache_type_reg, armv4_5_cache_common_t *cache) +{ + int size, assoc, M, len, multiplier; + + cache->ctype = (cache_type_reg & 0x1e000000U) >> 25; + cache->separate = (cache_type_reg & 0x01000000U) >> 24; + + size = (cache_type_reg & 0x1c0000) >> 18; + assoc = (cache_type_reg & 0x38000) >> 15; + M = (cache_type_reg & 0x4000) >> 14; + len = (cache_type_reg & 0x3000) >> 12; + multiplier = 2 + M; + + if ((assoc != 0) || (M != 1)) /* assoc 0 and M 1 means cache absent */ + { + /* cache is present */ + cache->d_u_size.linelen = 1 << (len + 3); + cache->d_u_size.associativity = multiplier << (assoc - 1); + cache->d_u_size.nsets = 1 << (size + 6 - assoc - len); + cache->d_u_size.cachesize = multiplier << (size + 8); + } + else + { + /* cache is absent */ + cache->d_u_size.linelen = -1; + cache->d_u_size.associativity = -1; + cache->d_u_size.nsets = -1; + cache->d_u_size.cachesize = -1; + } + + if (cache->separate) + { + size = (cache_type_reg & 0x1c0) >> 6; + assoc = (cache_type_reg & 0x38) >> 3; + M = (cache_type_reg & 0x4) >> 2; + len = (cache_type_reg & 0x3); + multiplier = 2 + M; + + if ((assoc != 0) || (M != 1)) /* assoc 0 and M 1 means cache absent */ + { + /* cache is present */ + cache->i_size.linelen = 1 << (len + 3); + cache->i_size.associativity = multiplier << (assoc - 1); + cache->i_size.nsets = 1 << (size + 6 - assoc - len); + cache->i_size.cachesize = multiplier << (size + 8); + } + else + { + /* cache is absent */ + cache->i_size.linelen = -1; + cache->i_size.associativity = -1; + cache->i_size.nsets = -1; + cache->i_size.cachesize = -1; + } + } + else + { + cache->i_size = cache->d_u_size; + } + + return ERROR_OK; +} + +int armv4_5_handle_cache_info_command(struct command_context_s *cmd_ctx, armv4_5_cache_common_t *armv4_5_cache) +{ + if (armv4_5_cache->ctype == -1) + { + command_print(cmd_ctx, "cache not yet identified"); + return ERROR_OK; + } + + command_print(cmd_ctx, "cache type: 0x%1.1x, %s", armv4_5_cache->ctype, + (armv4_5_cache->separate) ? "separate caches" : "unified cache"); + + command_print(cmd_ctx, "D-Cache: linelen %i, associativity %i, nsets %i, cachesize 0x%x", + armv4_5_cache->d_u_size.linelen, + armv4_5_cache->d_u_size.associativity, + armv4_5_cache->d_u_size.nsets, + armv4_5_cache->d_u_size.cachesize); + + command_print(cmd_ctx, "I-Cache: linelen %i, associativity %i, nsets %i, cachesize 0x%x", + armv4_5_cache->i_size.linelen, + armv4_5_cache->i_size.associativity, + armv4_5_cache->i_size.nsets, + armv4_5_cache->i_size.cachesize); + + return ERROR_OK; +} diff --git a/src/target/armv4_5_cache.h b/src/target/armv4_5_cache.h new file mode 100644 index 00000000..766718b6 --- /dev/null +++ b/src/target/armv4_5_cache.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ARMV4_5_CACHE_H +#define ARMV4_5_CACHE_H + +#include "types.h" +#include "command.h" + +typedef struct armv4_5_cachesize_s +{ + int linelen; + int associativity; + int nsets; + int cachesize; +} armv4_5_cachesize_t; + +typedef struct armv4_5_cache_common_s +{ + int ctype; /* specify supported cache operations */ + int separate; /* separate caches or unified cache */ + armv4_5_cachesize_t d_u_size; /* data cache */ + armv4_5_cachesize_t i_size; /* instruction cache */ + int i_cache_enabled; + int d_u_cache_enabled; +} armv4_5_cache_common_t; + +extern int armv4_5_identify_cache(u32 cache_type_reg, armv4_5_cache_common_t *cache); +extern int armv4_5_cache_state(u32 cp15_control_reg, armv4_5_cache_common_t *cache); + +extern int armv4_5_handle_cache_info_command(struct command_context_s *cmd_ctx, armv4_5_cache_common_t *armv4_5_cache); + +#endif /* ARMV4_5_CACHE_H */ diff --git a/src/target/armv4_5_mmu.c b/src/target/armv4_5_mmu.c new file mode 100644 index 00000000..c7dad3e2 --- /dev/null +++ b/src/target/armv4_5_mmu.c @@ -0,0 +1,358 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "arm7_9_common.h" +#include "log.h" +#include "command.h" +#include "armv4_5_mmu.h" + +#include <stdlib.h> + +u32 armv4mmu_translate_va(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 va, int *type, u32 *cb, int *domain, u32 *ap); +int armv4_5_mmu_read_physical(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 address, u32 size, u32 count, u8 *buffer); +int armv4_5_mmu_write_physical(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 address, u32 size, u32 count, u8 *buffer); + +char* armv4_5_mmu_page_type_names[] = +{ + "section", "large page", "small page", "tiny page" +}; + +u32 armv4_5_mmu_translate_va(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 va, int *type, u32 *cb, int *domain, u32 *ap) +{ + u32 first_lvl_descriptor = 0x0; + u32 second_lvl_descriptor = 0x0; + u32 ttb = armv4_5_mmu->get_ttb(target); + + armv4_5_mmu_read_physical(target, armv4_5_mmu, + (ttb & 0xffffc000) | ((va & 0xfff00000) >> 18), + 4, 1, (u8*)&first_lvl_descriptor); + + DEBUG("1st lvl desc: %8.8x", first_lvl_descriptor); + + if ((first_lvl_descriptor & 0x3) == 0) + { + *type = -1; + return ERROR_TARGET_TRANSLATION_FAULT; + } + + if (!armv4_5_mmu->has_tiny_pages && ((first_lvl_descriptor & 0x3) == 3)) + { + *type = -1; + return ERROR_TARGET_TRANSLATION_FAULT; + } + + /* domain is always specified in bits 8-5 */ + *domain = (first_lvl_descriptor & 0x1e0) >> 5; + + if ((first_lvl_descriptor & 0x3) == 2) + { + /* section descriptor */ + *type = ARMV4_5_SECTION; + *cb = (first_lvl_descriptor & 0xc) >> 2; + *ap = (first_lvl_descriptor & 0xc00) >> 10; + return (first_lvl_descriptor & 0xfff00000) | (va & 0x000fffff); + } + + if ((first_lvl_descriptor & 0x3) == 1) + { + /* coarse page table */ + armv4_5_mmu_read_physical(target, armv4_5_mmu, + (first_lvl_descriptor & 0xfffffc00) | ((va & 0x000ff000) >> 10), + 4, 1, (u8*)&second_lvl_descriptor); + } + + if ((first_lvl_descriptor & 0x3) == 3) + { + /* fine page table */ + armv4_5_mmu_read_physical(target, armv4_5_mmu, + (first_lvl_descriptor & 0xfffff000) | ((va & 0x000ffc00) >> 8), + 4, 1, (u8*)&second_lvl_descriptor); + } + + DEBUG("2nd lvl desc: %8.8x", first_lvl_descriptor); + + if ((second_lvl_descriptor & 0x3) == 0) + { + *type = -1; + return ERROR_TARGET_TRANSLATION_FAULT; + } + + /* cacheable/bufferable is always specified in bits 3-2 */ + *cb = (second_lvl_descriptor & 0xc) >> 2; + + if ((second_lvl_descriptor & 0x3) == 1) + { + /* large page descriptor */ + *type = ARMV4_5_LARGE_PAGE; + *ap = (second_lvl_descriptor & 0xff0) >> 4; + return (second_lvl_descriptor & 0xffff0000) | (va & 0x0000ffff); + } + + if ((second_lvl_descriptor & 0x3) == 2) + { + /* small page descriptor */ + *type = ARMV4_5_SMALL_PAGE; + *ap = (second_lvl_descriptor & 0xff0) >> 4; + return (second_lvl_descriptor & 0xfffff000) | (va & 0x00000fff); + } + + if ((second_lvl_descriptor & 0x3) == 3) + { + /* tiny page descriptor */ + *type = ARMV4_5_TINY_PAGE; + *ap = (second_lvl_descriptor & 0x30) >> 4; + return (second_lvl_descriptor & 0xfffffc00) | (va & 0x000003ff); + } + + /* should not happen */ + *type = -1; + return ERROR_TARGET_TRANSLATION_FAULT; +} + +int armv4_5_mmu_read_physical(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 address, u32 size, u32 count, u8 *buffer) +{ + int retval; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + /* disable MMU and data (or unified) cache */ + armv4_5_mmu->disable_mmu_caches(target, 1, 1, 0); + + retval = armv4_5_mmu->read_memory(target, address, size, count, buffer); + + /* reenable MMU / cache */ + armv4_5_mmu->enable_mmu_caches(target, armv4_5_mmu->mmu_enabled, + armv4_5_mmu->armv4_5_cache.d_u_cache_enabled, + armv4_5_mmu->armv4_5_cache.i_cache_enabled); + + return retval; +} + +int armv4_5_mmu_write_physical(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 address, u32 size, u32 count, u8 *buffer) +{ + int retval; + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + /* disable MMU and data (or unified) cache */ + armv4_5_mmu->disable_mmu_caches(target, 1, 1, 0); + + retval = armv4_5_mmu->write_memory(target, address, size, count, buffer); + + /* reenable MMU / cache */ + armv4_5_mmu->enable_mmu_caches(target, armv4_5_mmu->mmu_enabled, + armv4_5_mmu->armv4_5_cache.d_u_cache_enabled, + armv4_5_mmu->armv4_5_cache.i_cache_enabled); + + return retval; +} + +int armv4_5_mmu_handle_virt2phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc, target_t *target, armv4_5_mmu_common_t *armv4_5_mmu) +{ + u32 va; + u32 pa; + int type; + u32 cb; + int domain; + u32 ap; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"virt2phys\" command"); + return ERROR_OK; + } + + if (argc == 0) + { + command_print(cmd_ctx, "usage: virt2phys <virtual address>"); + return ERROR_OK; + } + + if (argc == 1) + { + va = strtoul(args[0], NULL, 0); + pa = armv4_5_mmu_translate_va(target, armv4_5_mmu, va, &type, &cb, &domain, &ap); + if (type == -1) + { + switch (pa) + { + case ERROR_TARGET_TRANSLATION_FAULT: + command_print(cmd_ctx, "no valid translation for 0x%8.8x", va); + break; + default: + command_print(cmd_ctx, "unknown translation error"); + } + return ERROR_OK; + } + + command_print(cmd_ctx, "0x%8.8x -> 0x%8.8x, type: %s, cb: %i, domain: %i, ap: %2.2x", + va, pa, armv4_5_mmu_page_type_names[type], cb, domain, ap); + } + + return ERROR_OK; +} + +int armv4_5_mmu_handle_md_phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc, target_t *target, armv4_5_mmu_common_t *armv4_5_mmu) +{ + int count = 1; + int size = 4; + u32 address = 0; + int i; + + char output[128]; + int output_len; + + int retval; + + u8 *buffer; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + if (argc < 1) + return ERROR_OK; + + if (argc == 2) + count = strtoul(args[1], NULL, 0); + + address = strtoul(args[0], NULL, 0); + + switch (cmd[2]) + { + case 'w': + size = 4; + break; + case 'h': + size = 2; + break; + case 'b': + size = 1; + break; + default: + return ERROR_OK; + } + + buffer = calloc(count, size); + if ((retval = armv4_5_mmu_read_physical(target, armv4_5_mmu, address, size, count, buffer)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_UNALIGNED_ACCESS: + command_print(cmd_ctx, "error: address not aligned"); + break; + case ERROR_TARGET_NOT_HALTED: + command_print(cmd_ctx, "error: target must be halted for memory accesses"); + break; + case ERROR_TARGET_DATA_ABORT: + command_print(cmd_ctx, "error: access caused data abort, system possibly corrupted"); + break; + default: + command_print(cmd_ctx, "error: unknown error"); + } + } + + output_len = 0; + + for (i = 0; i < count; i++) + { + if (i%8 == 0) + output_len += snprintf(output + output_len, 128 - output_len, "0x%8.8x: ", address + (i*size)); + + switch (size) + { + case 4: + output_len += snprintf(output + output_len, 128 - output_len, "%8.8x ", ((u32*)buffer)[i]); + break; + case 2: + output_len += snprintf(output + output_len, 128 - output_len, "%4.4x ", ((u16*)buffer)[i]); + break; + case 1: + output_len += snprintf(output + output_len, 128 - output_len, "%2.2x ", ((u8*)buffer)[i]); + break; + } + + if ((i%8 == 7) || (i == count - 1)) + { + command_print(cmd_ctx, output); + output_len = 0; + } + } + + free(buffer); + + return ERROR_OK; +} + +int armv4_5_mmu_handle_mw_phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc, target_t *target, armv4_5_mmu_common_t *armv4_5_mmu) +{ + u32 address = 0; + u32 value = 0; + int retval; + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + if (argc < 2) + return ERROR_OK; + + address = strtoul(args[0], NULL, 0); + value = strtoul(args[1], NULL, 0); + + switch (cmd[2]) + { + case 'w': + retval = armv4_5_mmu_write_physical(target, armv4_5_mmu, address, 4, 1, (u8*)&value); + break; + case 'h': + retval = armv4_5_mmu_write_physical(target, armv4_5_mmu, address, 2, 1, (u8*)&value); + break; + case 'b': + retval = armv4_5_mmu_write_physical(target, armv4_5_mmu, address, 1, 1, (u8*)&value); + break; + default: + return ERROR_OK; + } + + switch (retval) + { + case ERROR_TARGET_UNALIGNED_ACCESS: + command_print(cmd_ctx, "error: address not aligned"); + break; + case ERROR_TARGET_DATA_ABORT: + command_print(cmd_ctx, "error: access caused data abort, system possibly corrupted"); + break; + case ERROR_TARGET_NOT_HALTED: + command_print(cmd_ctx, "error: target must be halted for memory accesses"); + break; + case ERROR_OK: + break; + default: + command_print(cmd_ctx, "error: unknown error"); + } + + return ERROR_OK; +} diff --git a/src/target/armv4_5_mmu.h b/src/target/armv4_5_mmu.h new file mode 100644 index 00000000..3adb30e2 --- /dev/null +++ b/src/target/armv4_5_mmu.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ARMV4_5_MMU_H +#define ARMV4_5_MMU_H + +#include "armv4_5_cache.h" + +typedef struct armv4_5_mmu_common_s +{ + u32 (*get_ttb)(target_t *target); + int (*read_memory)(target_t *target, u32 address, u32 size, u32 count, u8 *buffer); + int (*write_memory)(target_t *target, u32 address, u32 size, u32 count, u8 *buffer); + void (*disable_mmu_caches)(target_t *target, int mmu, int d_u_cache, int i_cache); + void (*enable_mmu_caches)(target_t *target, int mmu, int d_u_cache, int i_cache); + armv4_5_cache_common_t armv4_5_cache; + int has_tiny_pages; + int mmu_enabled; +} armv4_5_mmu_common_t; + +enum +{ + ARMV4_5_SECTION, ARMV4_5_LARGE_PAGE, ARMV4_5_SMALL_PAGE, ARMV4_5_TINY_PAGE +}; + +extern char* armv4_5_page_type_names[]; + +extern u32 armv4_5_mmu_translate_va(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 va, int *type, u32 *cb, int *domain, u32 *ap); +extern int armv4_5_mmu_read_physical(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 address, u32 size, u32 count, u8 *buffer); +extern int armv4_5_mmu_write_physical(target_t *target, armv4_5_mmu_common_t *armv4_5_mmu, u32 address, u32 size, u32 count, u8 *buffer); + +extern int armv4_5_mmu_handle_virt2phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, target_t *target, armv4_5_mmu_common_t *armv4_5_mmu); +extern int armv4_5_mmu_handle_md_phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, target_t *target, armv4_5_mmu_common_t *armv4_5_mmu); +extern int armv4_5_mmu_handle_mw_phys_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, target_t *target, armv4_5_mmu_common_t *armv4_5_mmu); + +#endif /* ARMV4_5_MMU_H */ diff --git a/src/target/breakpoints.c b/src/target/breakpoints.c new file mode 100644 index 00000000..efba25c9 --- /dev/null +++ b/src/target/breakpoints.c @@ -0,0 +1,219 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" + +#include <stdlib.h> + +#include "binarybuffer.h" +#include "target.h" +#include "log.h" +#include "types.h" + +#include "breakpoints.h" + +char *breakpoint_type_strings[] = +{ + "hardware", + "software" +}; + +char *watchpoint_rw_strings[] = +{ + "read", + "write", + "access" +}; + +int breakpoint_add(target_t *target, u32 address, u32 length, enum breakpoint_type type) +{ + breakpoint_t *breakpoint = target->breakpoints; + breakpoint_t **breakpoint_p = &target->breakpoints; + int retval; + + while (breakpoint) + { + if (breakpoint->address == address) + return ERROR_OK; + breakpoint_p = &breakpoint->next; + breakpoint = breakpoint->next; + } + + if ((retval = target->type->add_breakpoint(target, address, length, type)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_RESOURCE_NOT_AVAILABLE: + INFO("can't add %s breakpoint, resource not available", breakpoint_type_strings[type]); + return retval; + break; + case ERROR_TARGET_NOT_HALTED: + INFO("can't add breakpoint while target is running"); + return retval; + break; + default: + ERROR("unknown error"); + exit(-1); + break; + } + } + + (*breakpoint_p) = malloc(sizeof(breakpoint_t)); + (*breakpoint_p)->address = address; + (*breakpoint_p)->length = length; + (*breakpoint_p)->type = type; + (*breakpoint_p)->set = 0; + (*breakpoint_p)->orig_instr = malloc(CEIL(length, 8)); + (*breakpoint_p)->next = NULL; + + DEBUG("added %s breakpoint at 0x%8.8x of length 0x%8.8x", breakpoint_type_strings[type], address, length); + + return ERROR_OK; +} + +int breakpoint_remove(target_t *target, u32 address) +{ + breakpoint_t *breakpoint = target->breakpoints; + breakpoint_t **breakpoint_p = &target->breakpoints; + int retval; + + while (breakpoint) + { + if (breakpoint->address == address) + break; + breakpoint_p = &breakpoint->next; + breakpoint = breakpoint->next; + } + + if (breakpoint) + { + if ((retval = target->type->remove_breakpoint(target, breakpoint)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_NOT_HALTED: + INFO("can't remove breakpoint while target is running"); + return retval; + break; + default: + ERROR("unknown error"); + exit(-1); + break; + } + } + (*breakpoint_p) = breakpoint->next; + free(breakpoint->orig_instr); + free(breakpoint); + } + else + { + ERROR("no breakpoint at address 0x%8.8x found", address); + } + + return ERROR_OK; +} + +breakpoint_t* breakpoint_find(target_t *target, u32 address) +{ + breakpoint_t *breakpoint = target->breakpoints; + + while (breakpoint) + { + if (breakpoint->address == address) + return breakpoint; + breakpoint = breakpoint->next; + } + + return NULL; +} + +int watchpoint_add(target_t *target, u32 address, u32 length, enum watchpoint_rw rw, u32 value, u32 mask) +{ + watchpoint_t *watchpoint = target->watchpoints; + watchpoint_t **watchpoint_p = &target->watchpoints; + int retval; + + while (watchpoint) + { + if (watchpoint->address == address) + return ERROR_OK; + watchpoint_p = &watchpoint->next; + watchpoint = watchpoint->next; + } + + if ((retval = target->type->add_watchpoint(target, address, length, rw)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_RESOURCE_NOT_AVAILABLE: + INFO("can't add %s watchpoint, resource not available", watchpoint_rw_strings[rw]); + return retval; + break; + default: + ERROR("unknown error"); + exit(-1); + break; + } + } + + (*watchpoint_p) = malloc(sizeof(watchpoint_t)); + (*watchpoint_p)->address = address; + (*watchpoint_p)->length = length; + (*watchpoint_p)->value = value; + (*watchpoint_p)->mask = mask; + (*watchpoint_p)->rw = rw; + (*watchpoint_p)->set = 0; + (*watchpoint_p)->next = NULL; + + DEBUG("added %s watchpoint at 0x%8.8x of length 0x%8.8x", watchpoint_rw_strings[rw], address, length); + + return ERROR_OK; +} + +int watchpoint_remove(target_t *target, u32 address) +{ + watchpoint_t *watchpoint = target->watchpoints; + watchpoint_t **watchpoint_p = &target->watchpoints; + int retval; + + while (watchpoint) + { + if (watchpoint->address == address) + break; + watchpoint_p = &watchpoint->next; + watchpoint = watchpoint->next; + } + + if (watchpoint) + { + if ((retval = target->type->remove_watchpoint(target, watchpoint)) != ERROR_OK) + { + ERROR("BUG: can't remove watchpoint"); + exit(-1); + } + (*watchpoint_p) = watchpoint->next; + free(watchpoint); + } + else + { + ERROR("no watchpoint at address 0x%8.8x found", address); + } + + return ERROR_OK; +} diff --git a/src/target/breakpoints.h b/src/target/breakpoints.h new file mode 100644 index 00000000..7eba39aa --- /dev/null +++ b/src/target/breakpoints.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef BREAKPOINTS_H +#define BREAKPOINTS_H + +#include "target.h" + +struct target_s; + +enum breakpoint_type +{ + BKPT_HARD, + BKPT_SOFT, +}; + +extern char *breakpoint_type_strings[]; + +enum watchpoint_rw +{ + WPT_READ = 0, WPT_WRITE = 1, WPT_ACCESS = 2 +}; + +extern char *watchpoint_rw_strings[]; + +typedef struct breakpoint_s +{ + u32 address; + int length; + enum breakpoint_type type; + int set; + u8 *orig_instr; + struct breakpoint_s *next; +} breakpoint_t; + +typedef struct watchpoint_s +{ + u32 address; + int length; + u32 mask; + u32 value; + enum watchpoint_rw rw; + int set; + struct watchpoint_s *next; +} watchpoint_t; + +extern int breakpoint_add(struct target_s *target, u32 address, u32 length, enum breakpoint_type type); +extern int breakpoint_remove(struct target_s *target, u32 address); +extern breakpoint_t* breakpoint_find(struct target_s *target, u32 address); +extern int watchpoint_add(struct target_s *target, u32 address, u32 length, enum watchpoint_rw rw, u32 value, u32 mask); +extern int watchpoint_remove(struct target_s *target, u32 address); + +#endif /* BREAKPOINTS_H */ + diff --git a/src/target/embeddedice.c b/src/target/embeddedice.c new file mode 100644 index 00000000..e148b888 --- /dev/null +++ b/src/target/embeddedice.c @@ -0,0 +1,301 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" + +#include "embeddedice.h" + +#include "armv4_5.h" +#include "arm7_9_common.h" + +#include "log.h" +#include "arm_jtag.h" +#include "types.h" +#include "binarybuffer.h" +#include "target.h" +#include "register.h" +#include "jtag.h" + +#include <stdlib.h> + +bitfield_desc_t embeddedice_comms_ctrl_bitfield_desc[] = +{ + {"R", 1}, + {"W", 1}, + {"reserved", 26}, + {"version", 4} +}; + +int embeddedice_reg_arch_info[] = +{ + 0x0, 0x1, 0x4, 0x5, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 +}; + +char* embeddedice_reg_list[] = +{ + "debug_ctrl", + "debug_status", + + "comms_ctrl", + "comms_data", + + "watch 0 addr value", + "watch 0 addr mask", + "watch 0 data value", + "watch 0 data mask", + "watch 0 control value", + "watch 0 control mask", + + "watch 1 addr value", + "watch 1 addr mask", + "watch 1 data value", + "watch 1 data mask", + "watch 1 control value", + "watch 1 control mask" +}; + +int embeddedice_reg_arch_type = -1; + +int embeddedice_get_reg(reg_t *reg); +int embeddedice_set_reg(reg_t *reg, u32 value); + +int embeddedice_write_reg(reg_t *reg, u32 value); +int embeddedice_read_reg(reg_t *reg); + +reg_cache_t* embeddedice_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, int extra_reg) +{ + reg_cache_t *reg_cache = malloc(sizeof(reg_cache_t)); + reg_t *reg_list = NULL; + embeddedice_reg_t *arch_info = NULL; + int num_regs = 16 + extra_reg; + int i; + + /* register a register arch-type for EmbeddedICE registers only once */ + if (embeddedice_reg_arch_type == -1) + embeddedice_reg_arch_type = register_reg_arch_type(embeddedice_get_reg, embeddedice_set_reg_w_exec); + + /* the actual registers are kept in two arrays */ + reg_list = calloc(num_regs, sizeof(reg_t)); + arch_info = calloc(num_regs, sizeof(embeddedice_reg_t)); + + /* fill in values for the reg cache */ + reg_cache->name = "EmbeddedICE registers"; + reg_cache->next = NULL; + reg_cache->reg_list = reg_list; + reg_cache->num_regs = num_regs; + + /* set up registers */ + for (i = 0; i < num_regs - extra_reg; i++) + { + reg_list[i].name = embeddedice_reg_list[i]; + reg_list[i].size = 32; + reg_list[i].dirty = 0; + reg_list[i].valid = 0; + reg_list[i].bitfield_desc = NULL; + reg_list[i].num_bitfields = 0; + reg_list[i].value = calloc(1, 4); + reg_list[i].arch_info = &arch_info[i]; + reg_list[i].arch_type = embeddedice_reg_arch_type; + arch_info[i].addr = embeddedice_reg_arch_info[i]; + arch_info[i].jtag_info = jtag_info; + } + + /* there may be one extra reg (Abort status (ARM7 rev4) or Vector catch (ARM9)) */ + if (extra_reg) + { + reg_list[num_regs - 1].arch_info = &arch_info[num_regs - 1]; + arch_info[num_regs - 1].jtag_info = jtag_info; + } + + return reg_cache; +} + +int embeddedice_get_reg(reg_t *reg) +{ + if (embeddedice_read_reg(reg) != ERROR_OK) + { + ERROR("BUG: error scheduling EmbeddedICE register read"); + exit(-1); + } + + if (jtag_execute_queue() != ERROR_OK) + { + ERROR("register read failed"); + } + + return ERROR_OK; +} + +int embeddedice_read_reg_w_check(reg_t *reg, u8* check_value, u8* check_mask) +{ + embeddedice_reg_t *ice_reg = reg->arch_info; + u8 reg_addr = ice_reg->addr & 0x1f; + scan_field_t fields[3]; + + DEBUG("%i", ice_reg->addr); + + jtag_add_end_state(TAP_RTI); + arm_jtag_scann(ice_reg->jtag_info, 0x2); + arm_jtag_set_instr(ice_reg->jtag_info, ice_reg->jtag_info->intest_instr); + + fields[0].device = ice_reg->jtag_info->chain_pos; + fields[0].num_bits = 32; + fields[0].out_value = reg->value; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = ice_reg->jtag_info->chain_pos; + fields[1].num_bits = 5; + fields[1].out_value = malloc(1); + buf_set_u32(fields[1].out_value, 0, 5, reg_addr); + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = ice_reg->jtag_info->chain_pos; + fields[2].num_bits = 1; + fields[2].out_value = malloc(1); + buf_set_u32(fields[2].out_value, 0, 1, 0); + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + jtag_add_dr_scan(3, fields, -1); + + fields[0].in_value = reg->value; + fields[0].in_check_value = check_value; + fields[0].in_check_mask = check_mask; + + /* when reading the DCC data register, leaving the address field set to + * EICE_COMMS_DATA would read the register twice + * reading the control register is safe + */ + buf_set_u32(fields[1].out_value, 0, 5, embeddedice_reg_arch_info[EICE_COMMS_CTRL]); + + jtag_add_dr_scan(3, fields, -1); + + free(fields[1].out_value); + free(fields[2].out_value); + + return ERROR_OK; +} + +int embeddedice_read_reg(reg_t *reg) +{ + return embeddedice_read_reg_w_check(reg, NULL, NULL); +} + +int embeddedice_set_reg(reg_t *reg, u32 value) +{ + if (embeddedice_write_reg(reg, value) != ERROR_OK) + { + ERROR("BUG: error scheduling EmbeddedICE register write"); + exit(-1); + } + + buf_set_u32(reg->value, 0, reg->size, value); + reg->valid = 1; + reg->dirty = 0; + + return ERROR_OK; +} + +int embeddedice_set_reg_w_exec(reg_t *reg, u32 value) +{ + embeddedice_set_reg(reg, value); + + if (jtag_execute_queue() != ERROR_OK) + { + ERROR("register write failed"); + exit(-1); + } + return ERROR_OK; +} + +int embeddedice_write_reg(reg_t *reg, u32 value) +{ + embeddedice_reg_t *ice_reg = reg->arch_info; + u8 reg_addr = ice_reg->addr & 0x1f; + scan_field_t fields[3]; + + DEBUG("%i: 0x%8.8x", ice_reg->addr, value); + + jtag_add_end_state(TAP_RTI); + arm_jtag_scann(ice_reg->jtag_info, 0x2); + arm_jtag_set_instr(ice_reg->jtag_info, ice_reg->jtag_info->intest_instr); + + fields[0].device = ice_reg->jtag_info->chain_pos; + fields[0].num_bits = 32; + fields[0].out_value = malloc(4); + buf_set_u32(fields[0].out_value, 0, 32, value); + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = ice_reg->jtag_info->chain_pos; + fields[1].num_bits = 5; + fields[1].out_value = malloc(1); + buf_set_u32(fields[1].out_value, 0, 5, reg_addr); + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + fields[2].device = ice_reg->jtag_info->chain_pos; + fields[2].num_bits = 1; + fields[2].out_value = malloc(1); + buf_set_u32(fields[2].out_value, 0, 1, 1); + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = NULL; + fields[2].in_check_mask = NULL; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + jtag_add_dr_scan(3, fields, -1); + + free(fields[0].out_value); + free(fields[1].out_value); + free(fields[2].out_value); + + return ERROR_OK; +} + +int embeddedice_store_reg(reg_t *reg) +{ + return embeddedice_write_reg(reg, buf_get_u32(reg->value, 0, reg->size)); +} + diff --git a/src/target/embeddedice.h b/src/target/embeddedice.h new file mode 100644 index 00000000..0062153f --- /dev/null +++ b/src/target/embeddedice.h @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef EMBEDDED_ICE_H +#define EMBEDDED_ICE_H + +#include "target.h" +#include "register.h" +#include "arm_jtag.h" + +enum +{ + EICE_DBG_CTRL = 0, + EICE_DBG_STAT = 1, + EICE_COMMS_CTRL = 2, + EICE_COMMS_DATA = 3, + EICE_W0_ADDR_VALUE = 4, + EICE_W0_ADDR_MASK = 5, + EICE_W0_DATA_VALUE = 6, + EICE_W0_DATA_MASK = 7, + EICE_W0_CONTROL_VALUE = 8, + EICE_W0_CONTROL_MASK = 9, + EICE_W1_ADDR_VALUE = 10, + EICE_W1_ADDR_MASK = 11, + EICE_W1_DATA_VALUE = 12, + EICE_W1_DATA_MASK = 13, + EICE_W1_CONTROL_VALUE = 14, + EICE_W1_CONTROL_MASK = 15 +}; + +enum +{ + EICE_DBG_CONTROL_INTDIS = 2, + EICE_DBG_CONTROL_DBGRQ = 1, + EICE_DBG_CONTROL_DBGACK = 0, +}; + +enum +{ + EICE_DBG_STATUS_ITBIT = 4, + EICE_DBG_STATUS_SYSCOMP = 3, + EICE_DBG_STATUS_IFEN = 2, + EICE_DBG_STATUS_DBGRQ = 1, + EICE_DBG_STATUS_DBGACK = 0 +}; + +enum +{ + EICE_W_CTRL_ENABLE = 0x100, + EICE_W_CTRL_RANGE = 0x80, + EICE_W_CTRL_CHAIN = 0x40, + EICE_W_CTRL_EXTERN = 0x20, + EICE_W_CTRL_nTRANS = 0x10, + EICE_W_CTRL_nOPC = 0x8, + EICE_W_CTRL_MAS = 0x6, + EICE_W_CTRL_ITBIT = 0x2, + EICE_W_CTRL_nRW = 0x1 +}; + +typedef struct embeddedice_reg_s +{ + int addr; + arm_jtag_t *jtag_info; +} embeddedice_reg_t; + +extern reg_cache_t* embeddedice_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, int extra_reg); +extern int embeddedice_read_reg(reg_t *reg); +extern int embeddedice_write_reg(reg_t *reg, u32 value); +extern int embeddedice_read_reg_w_check(reg_t *reg, u8* check_value, u8* check_mask); +extern int embeddedice_store_reg(reg_t *reg); +extern int embeddedice_set_reg(reg_t *reg, u32 value); +extern int embeddedice_set_reg_w_exec(reg_t *reg, u32 value); + +#endif /* EMBEDDED_ICE_H */ diff --git a/src/target/etm.c b/src/target/etm.c new file mode 100644 index 00000000..a84e2d67 --- /dev/null +++ b/src/target/etm.c @@ -0,0 +1,409 @@ +/***************************************************************************
+ * Copyright (C) 2005 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * 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., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "config.h"
+
+#include "etm.h"
+
+#include "armv4_5.h"
+#include "arm7_9_common.h"
+
+#include "log.h"
+#include "arm_jtag.h"
+#include "types.h"
+#include "binarybuffer.h"
+#include "target.h"
+#include "register.h"
+#include "jtag.h"
+
+#include <stdlib.h>
+
+bitfield_desc_t etm_comms_ctrl_bitfield_desc[] =
+{
+ {"R", 1},
+ {"W", 1},
+ {"reserved", 26},
+ {"version", 4}
+};
+
+int etm_reg_arch_info[] =
+{
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+};
+
+int etm_reg_arch_size_info[] =
+{
+ 32, 32, 17, 8, 3, 9, 32, 17,
+ 26, 16, 25, 8, 17, 32, 32, 17,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 16, 16, 16, 16, 18, 18, 18, 18,
+ 17, 17, 17, 17, 16, 16, 16, 16,
+ 17, 17, 17, 17, 17, 17, 2,
+ 17, 17, 17, 17, 32, 32, 32, 32
+};
+
+char* etm_reg_list[] =
+{
+ "ETM_CTRL",
+ "ETM_CONFIG",
+ "ETM_TRIG_EVENT",
+ "ETM_MMD_CTRL",
+ "ETM_STATUS",
+ "ETM_SYS_CONFIG",
+ "ETM_TRACE_RESOURCE_CTRL",
+ "ETM_TRACE_EN_CTRL2",
+ "ETM_TRACE_EN_EVENT",
+ "ETM_TRACE_EN_CTRL1",
+ "ETM_FIFOFULL_REGION",
+ "ETM_FIFOFULL_LEVEL",
+ "ETM_VIEWDATA_EVENT",
+ "ETM_VIEWDATA_CTRL1",
+ "ETM_VIEWDATA_CTRL2",
+ "ETM_VIEWDATA_CTRL3",
+ "ETM_ADDR_COMPARATOR_VALUE1",
+ "ETM_ADDR_COMPARATOR_VALUE2",
+ "ETM_ADDR_COMPARATOR_VALUE3",
+ "ETM_ADDR_COMPARATOR_VALUE4",
+ "ETM_ADDR_COMPARATOR_VALUE5",
+ "ETM_ADDR_COMPARATOR_VALUE6",
+ "ETM_ADDR_COMPARATOR_VALUE7",
+ "ETM_ADDR_COMPARATOR_VALUE8",
+ "ETM_ADDR_COMPARATOR_VALUE9",
+ "ETM_ADDR_COMPARATOR_VALUE10",
+ "ETM_ADDR_COMPARATOR_VALUE11",
+ "ETM_ADDR_COMPARATOR_VALUE12",
+ "ETM_ADDR_COMPARATOR_VALUE13",
+ "ETM_ADDR_COMPARATOR_VALUE14",
+ "ETM_ADDR_COMPARATOR_VALUE15",
+ "ETM_ADDR_COMPARATOR_VALUE16",
+ "ETM_ADDR_ACCESS_TYPE1",
+ "ETM_ADDR_ACCESS_TYPE2",
+ "ETM_ADDR_ACCESS_TYPE3",
+ "ETM_ADDR_ACCESS_TYPE4",
+ "ETM_ADDR_ACCESS_TYPE5",
+ "ETM_ADDR_ACCESS_TYPE6",
+ "ETM_ADDR_ACCESS_TYPE7",
+ "ETM_ADDR_ACCESS_TYPE8",
+ "ETM_ADDR_ACCESS_TYPE9",
+ "ETM_ADDR_ACCESS_TYPE10",
+ "ETM_ADDR_ACCESS_TYPE11",
+ "ETM_ADDR_ACCESS_TYPE12",
+ "ETM_ADDR_ACCESS_TYPE13",
+ "ETM_ADDR_ACCESS_TYPE14",
+ "ETM_ADDR_ACCESS_TYPE15",
+ "ETM_ADDR_ACCESS_TYPE16",
+ "ETM_DATA_COMPARATOR_VALUE1",
+ "ETM_DATA_COMPARATOR_VALUE2",
+ "ETM_DATA_COMPARATOR_VALUE3",
+ "ETM_DATA_COMPARATOR_VALUE4",
+ "ETM_DATA_COMPARATOR_VALUE5",
+ "ETM_DATA_COMPARATOR_VALUE6",
+ "ETM_DATA_COMPARATOR_VALUE7",
+ "ETM_DATA_COMPARATOR_VALUE8",
+ "ETM_DATA_COMPARATOR_VALUE9",
+ "ETM_DATA_COMPARATOR_VALUE10",
+ "ETM_DATA_COMPARATOR_VALUE11",
+ "ETM_DATA_COMPARATOR_VALUE12",
+ "ETM_DATA_COMPARATOR_VALUE13",
+ "ETM_DATA_COMPARATOR_VALUE14",
+ "ETM_DATA_COMPARATOR_VALUE15",
+ "ETM_DATA_COMPARATOR_VALUE16",
+ "ETM_DATA_COMPARATOR_MASK1",
+ "ETM_DATA_COMPARATOR_MASK2",
+ "ETM_DATA_COMPARATOR_MASK3",
+ "ETM_DATA_COMPARATOR_MASK4",
+ "ETM_DATA_COMPARATOR_MASK5",
+ "ETM_DATA_COMPARATOR_MASK6",
+ "ETM_DATA_COMPARATOR_MASK7",
+ "ETM_DATA_COMPARATOR_MASK8",
+ "ETM_DATA_COMPARATOR_MASK9",
+ "ETM_DATA_COMPARATOR_MASK10",
+ "ETM_DATA_COMPARATOR_MASK11",
+ "ETM_DATA_COMPARATOR_MASK12",
+ "ETM_DATA_COMPARATOR_MASK13",
+ "ETM_DATA_COMPARATOR_MASK14",
+ "ETM_DATA_COMPARATOR_MASK15",
+ "ETM_DATA_COMPARATOR_MASK16",
+ "ETM_COUNTER_INITAL_VALUE1",
+ "ETM_COUNTER_INITAL_VALUE2",
+ "ETM_COUNTER_INITAL_VALUE3",
+ "ETM_COUNTER_INITAL_VALUE4",
+ "ETM_COUNTER_ENABLE1",
+ "ETM_COUNTER_ENABLE2",
+ "ETM_COUNTER_ENABLE3",
+ "ETM_COUNTER_ENABLE4",
+ "ETM_COUNTER_RELOAD_VALUE1",
+ "ETM_COUNTER_RELOAD_VALUE2",
+ "ETM_COUNTER_RELOAD_VALUE3",
+ "ETM_COUNTER_RELOAD_VALUE4",
+ "ETM_COUNTER_VALUE1",
+ "ETM_COUNTER_VALUE2",
+ "ETM_COUNTER_VALUE3",
+ "ETM_COUNTER_VALUE4",
+ "ETM_SEQUENCER_CTRL1",
+ "ETM_SEQUENCER_CTRL2",
+ "ETM_SEQUENCER_CTRL3",
+ "ETM_SEQUENCER_CTRL4",
+ "ETM_SEQUENCER_CTRL5",
+ "ETM_SEQUENCER_CTRL6",
+ "ETM_SEQUENCER_STATE",
+ "ETM_EXTERNAL_OUTPUT1",
+ "ETM_EXTERNAL_OUTPUT2",
+ "ETM_EXTERNAL_OUTPUT3",
+ "ETM_EXTERNAL_OUTPUT4",
+ "ETM_CONTEXTID_COMPARATOR_VALUE1",
+ "ETM_CONTEXTID_COMPARATOR_VALUE2",
+ "ETM_CONTEXTID_COMPARATOR_VALUE3",
+ "ETM_CONTEXTID_COMPARATOR_MASK"
+};
+
+int etm_reg_arch_type = -1;
+
+int etm_get_reg(reg_t *reg);
+int etm_set_reg(reg_t *reg, u32 value);
+
+int etm_write_reg(reg_t *reg, u32 value);
+int etm_read_reg(reg_t *reg);
+
+reg_cache_t* etm_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, int extra_reg)
+{
+ reg_cache_t *reg_cache = malloc(sizeof(reg_cache_t));
+ reg_t *reg_list = NULL;
+ etm_reg_t *arch_info = NULL;
+ int num_regs = sizeof(etm_reg_arch_info)/sizeof(int);
+ int i;
+
+ /* register a register arch-type for etm registers only once */
+ if (etm_reg_arch_type == -1)
+ etm_reg_arch_type = register_reg_arch_type(etm_get_reg, etm_set_reg_w_exec);
+
+ /* the actual registers are kept in two arrays */
+ reg_list = calloc(num_regs, sizeof(reg_t));
+ arch_info = calloc(num_regs, sizeof(etm_reg_t));
+
+ /* fill in values for the reg cache */
+ reg_cache->name = "etm registers";
+ reg_cache->next = NULL;
+ reg_cache->reg_list = reg_list;
+ reg_cache->num_regs = num_regs;
+
+ /* set up registers */
+ for (i = 0; i < num_regs; i++)
+ {
+ reg_list[i].name = etm_reg_list[i];
+ reg_list[i].size = 32;
+ reg_list[i].dirty = 0;
+ reg_list[i].valid = 0;
+ reg_list[i].bitfield_desc = NULL;
+ reg_list[i].num_bitfields = 0;
+ reg_list[i].value = calloc(1, 4);
+ reg_list[i].arch_info = &arch_info[i];
+ reg_list[i].arch_type = etm_reg_arch_type;
+ reg_list[i].size = etm_reg_arch_size_info[i];
+ arch_info[i].addr = etm_reg_arch_info[i];
+ arch_info[i].jtag_info = jtag_info;
+ }
+ return reg_cache;
+}
+
+int etm_get_reg(reg_t *reg)
+{
+ if (etm_read_reg(reg) != ERROR_OK)
+ {
+ ERROR("BUG: error scheduling etm register read");
+ exit(-1);
+ }
+
+ if (jtag_execute_queue() != ERROR_OK)
+ {
+ ERROR("register read failed");
+ }
+
+ return ERROR_OK;
+}
+
+int etm_read_reg_w_check(reg_t *reg, u8* check_value, u8* check_mask)
+{
+ etm_reg_t *etm_reg = reg->arch_info;
+ u8 reg_addr = etm_reg->addr & 0x7f;
+ scan_field_t fields[3];
+
+ DEBUG("%i", etm_reg->addr);
+
+ jtag_add_end_state(TAP_RTI);
+ arm_jtag_scann(etm_reg->jtag_info, 0x6);
+ arm_jtag_set_instr(etm_reg->jtag_info, etm_reg->jtag_info->intest_instr);
+
+ fields[0].device = etm_reg->jtag_info->chain_pos;
+ fields[0].num_bits = 32;
+ fields[0].out_value = reg->value;
+ fields[0].out_mask = NULL;
+ fields[0].in_value = NULL;
+ fields[0].in_check_value = NULL;
+ fields[0].in_check_mask = NULL;
+ fields[0].in_handler = NULL;
+ fields[0].in_handler_priv = NULL;
+
+ fields[1].device = etm_reg->jtag_info->chain_pos;
+ fields[1].num_bits = 7;
+ fields[1].out_value = malloc(1);
+ buf_set_u32(fields[1].out_value, 0, 7, reg_addr);
+ fields[1].out_mask = NULL;
+ fields[1].in_value = NULL;
+ fields[1].in_check_value = NULL;
+ fields[1].in_check_mask = NULL;
+ fields[1].in_handler = NULL;
+ fields[1].in_handler_priv = NULL;
+
+ fields[2].device = etm_reg->jtag_info->chain_pos;
+ fields[2].num_bits = 1;
+ fields[2].out_value = malloc(1);
+ buf_set_u32(fields[2].out_value, 0, 1, 0);
+ fields[2].out_mask = NULL;
+ fields[2].in_value = NULL;
+ fields[2].in_check_value = NULL;
+ fields[2].in_check_mask = NULL;
+ fields[2].in_handler = NULL;
+ fields[2].in_handler_priv = NULL;
+
+ jtag_add_dr_scan(3, fields, -1);
+
+ fields[0].in_value = reg->value;
+ fields[0].in_check_value = check_value;
+ fields[0].in_check_mask = check_mask;
+
+ jtag_add_dr_scan(3, fields, -1);
+
+ free(fields[1].out_value);
+ free(fields[2].out_value);
+
+ return ERROR_OK;
+}
+
+int etm_read_reg(reg_t *reg)
+{
+ return etm_read_reg_w_check(reg, NULL, NULL);
+}
+
+int etm_set_reg(reg_t *reg, u32 value)
+{
+ if (etm_write_reg(reg, value) != ERROR_OK)
+ {
+ ERROR("BUG: error scheduling etm register write");
+ exit(-1);
+ }
+
+ buf_set_u32(reg->value, 0, reg->size, value);
+ reg->valid = 1;
+ reg->dirty = 0;
+
+ return ERROR_OK;
+}
+
+int etm_set_reg_w_exec(reg_t *reg, u32 value)
+{
+ etm_set_reg(reg, value);
+
+ if (jtag_execute_queue() != ERROR_OK)
+ {
+ ERROR("register write failed");
+ exit(-1);
+ }
+ return ERROR_OK;
+}
+
+int etm_write_reg(reg_t *reg, u32 value)
+{
+ etm_reg_t *etm_reg = reg->arch_info;
+ u8 reg_addr = etm_reg->addr & 0x7f;
+ scan_field_t fields[3];
+
+ DEBUG("%i: 0x%8.8x", etm_reg->addr, value);
+
+ jtag_add_end_state(TAP_RTI);
+ arm_jtag_scann(etm_reg->jtag_info, 0x6);
+ arm_jtag_set_instr(etm_reg->jtag_info, etm_reg->jtag_info->intest_instr);
+
+ fields[0].device = etm_reg->jtag_info->chain_pos;
+ fields[0].num_bits = 32;
+ fields[0].out_value = malloc(4);
+ buf_set_u32(fields[0].out_value, 0, 32, value);
+ fields[0].out_mask = NULL;
+ fields[0].in_value = NULL;
+ fields[0].in_check_value = NULL;
+ fields[0].in_check_mask = NULL;
+ fields[0].in_handler = NULL;
+ fields[0].in_handler_priv = NULL;
+
+ fields[1].device = etm_reg->jtag_info->chain_pos;
+ fields[1].num_bits = 7;
+ fields[1].out_value = malloc(1);
+ buf_set_u32(fields[1].out_value, 0, 7, reg_addr);
+ fields[1].out_mask = NULL;
+ fields[1].in_value = NULL;
+ fields[1].in_check_value = NULL;
+ fields[1].in_check_mask = NULL;
+ fields[1].in_handler = NULL;
+ fields[1].in_handler_priv = NULL;
+
+ fields[2].device = etm_reg->jtag_info->chain_pos;
+ fields[2].num_bits = 1;
+ fields[2].out_value = malloc(1);
+ buf_set_u32(fields[2].out_value, 0, 1, 1);
+ fields[2].out_mask = NULL;
+ fields[2].in_value = NULL;
+ fields[2].in_check_value = NULL;
+ fields[2].in_check_mask = NULL;
+ fields[2].in_handler = NULL;
+ fields[2].in_handler_priv = NULL;
+
+ jtag_add_dr_scan(3, fields, -1);
+
+ free(fields[0].out_value);
+ free(fields[1].out_value);
+ free(fields[2].out_value);
+
+ return ERROR_OK;
+}
+
+int etm_store_reg(reg_t *reg)
+{
+ return etm_write_reg(reg, buf_get_u32(reg->value, 0, reg->size));
+}
+
diff --git a/src/target/etm.h b/src/target/etm.h new file mode 100644 index 00000000..dbe78f35 --- /dev/null +++ b/src/target/etm.h @@ -0,0 +1,76 @@ +/***************************************************************************
+ * Copyright (C) 2005 by Dominic Rath *
+ * Dominic.Rath@gmx.de *
+ * *
+ * 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., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef ETM_H
+#define ETM_H
+
+#include "target.h"
+#include "register.h"
+#include "arm_jtag.h"
+
+// ETM registers (V1.2 protocol)
+enum
+{
+ ETM_CTRL = 0x00,
+ ETM_CONFIG = 0x01,
+ ETM_TRIG_EVENT = 0x02,
+ ETM_MMD_CTRL = 0x03,
+ ETM_STATUS = 0x04,
+ ETM_SYS_CONFIG = 0x05,
+ ETM_TRACE_RESOURCE_CTRL = 0x06,
+ ETM_TRACE_EN_CTRL2 = 0x07,
+ ETM_TRACE_EN_EVENT = 0x08,
+ ETM_TRACE_EN_CTRL1 = 0x09,
+ ETM_FIFOFULL_REGION = 0x0a,
+ ETM_FIFOFULL_LEVEL = 0x0b,
+ ETM_VIEWDATA_EVENT = 0x0c,
+ ETM_VIEWDATA_CTRL1 = 0x0d,
+ ETM_VIEWDATA_CTRL2 = 0x0e,
+ ETM_VIEWDATA_CTRL3 = 0x0f,
+ ETM_ADDR_COMPARATOR_VALUE = 0x10,
+ ETM_ADDR_ACCESS_TYPE = 0x20,
+ ETM_DATA_COMPARATOR_VALUE = 0x30,
+ ETM_DATA_COMPARATOR_MASK = 0x40,
+ ETM_COUNTER_INITAL_VALUE = 0x50,
+ ETM_COUNTER_ENABLE = 0x54,
+ ETM_COUNTER_RELOAD_VALUE = 0x58,
+ ETM_COUNTER_VALUE = 0x5c,
+ ETM_SEQUENCER_CTRL = 0x60,
+ ETM_SEQUENCER_STATE = 0x67,
+ ETM_EXTERNAL_OUTPUT = 0x68,
+ ETM_CONTEXTID_COMPARATOR_VALUE = 0x6c,
+ ETM_CONTEXTID_COMPARATOR_MASK = 0x6f,
+};
+
+
+typedef struct etm_reg_s
+{
+ int addr;
+ arm_jtag_t *jtag_info;
+} etm_reg_t;
+
+extern reg_cache_t* etm_build_reg_cache(target_t *target, arm_jtag_t *jtag_info, int extra_reg);
+extern int etm_read_reg(reg_t *reg);
+extern int etm_write_reg(reg_t *reg, u32 value);
+extern int etm_read_reg_w_check(reg_t *reg, u8* check_value, u8* check_mask);
+extern int etm_store_reg(reg_t *reg);
+extern int etm_set_reg(reg_t *reg, u32 value);
+extern int etm_set_reg_w_exec(reg_t *reg, u32 value);
+
+#endif /* ETM_H */
diff --git a/src/target/register.c b/src/target/register.c new file mode 100644 index 00000000..7b98cfcf --- /dev/null +++ b/src/target/register.c @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "register.h" + +#include "log.h" +#include "command.h" + +#include <string.h> +#include <stdlib.h> + +reg_arch_type_t *reg_arch_types = NULL; + +reg_t* register_get_by_name(reg_cache_t *first, char *name, int search_all) +{ + int i; + reg_cache_t *cache = first; + + while (cache) + { + for (i = 0; i < cache->num_regs; i++) + { + if (strcmp(cache->reg_list[i].name, name) == 0) + return &(cache->reg_list[i]); + } + + if (search_all) + cache = cache->next; + else + break; + } + + return NULL; +} + +reg_cache_t** register_get_last_cache_p(reg_cache_t **first) +{ + reg_cache_t **cache_p = first; + + if (*cache_p) + while (*cache_p) + cache_p = &((*cache_p)->next); + else + return first; + + return cache_p; +} + +int register_reg_arch_type(int (*get)(reg_t *reg), int (*set)(reg_t *reg, u32 value)) +{ + reg_arch_type_t** arch_type_p = ®_arch_types; + int id = 0; + + if (*arch_type_p) + { + while (*arch_type_p) + { + id = (*arch_type_p)->id; + arch_type_p = &((*arch_type_p)->next); + } + } + + (*arch_type_p) = malloc(sizeof(reg_arch_type_t)); + (*arch_type_p)->id = id + 1; + (*arch_type_p)->set = set; + (*arch_type_p)->get = get; + (*arch_type_p)->next = NULL; + + return id + 1; +} + +reg_arch_type_t* register_get_arch_type(int id) +{ + reg_arch_type_t *arch_type = reg_arch_types; + + while (arch_type) + { + if (arch_type->id == id) + return arch_type; + arch_type = arch_type->next; + } + + return NULL; +} diff --git a/src/target/register.h b/src/target/register.h new file mode 100644 index 00000000..8a903edd --- /dev/null +++ b/src/target/register.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef REGISTER_H +#define REGISTER_H + +#include "types.h" +#include "target.h" + +struct target_s; + +typedef struct bitfield_desc_s +{ + char *name; + int num_bits; +} bitfield_desc_t; + +typedef struct reg_s +{ + char *name; + u8 *value; + int dirty; + int valid; + int size; + bitfield_desc_t *bitfield_desc; + int num_bitfields; + void *arch_info; + int arch_type; +} reg_t; + +typedef struct reg_cache_s +{ + char *name; + struct reg_cache_s *next; + reg_t *reg_list; + int num_regs; +} reg_cache_t; + +typedef struct reg_arch_type_s +{ + int id; + int (*get)(reg_t *reg); + int (*set)(reg_t *reg, u32 value); + struct reg_arch_type_s *next; +} reg_arch_type_t; + +extern reg_t* register_get_by_name(reg_cache_t *first, char *name, int search_all); +extern reg_cache_t** register_get_last_cache_p(reg_cache_t **first); +extern int register_reg_arch_type(int (*get)(reg_t *reg), int (*set)(reg_t *reg, u32 value)); +extern reg_arch_type_t* register_get_arch_type(int id); + +#endif /* REGISTER_H */ + diff --git a/src/target/target.c b/src/target/target.c new file mode 100644 index 00000000..98407afb --- /dev/null +++ b/src/target/target.c @@ -0,0 +1,1701 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" +#include "target.h" + +#include "log.h" +#include "configuration.h" +#include "binarybuffer.h" +#include "jtag.h" + +#include <string.h> +#include <stdlib.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> + +#include <sys/time.h> +#include <time.h> + +#include <time_support.h> + +int cli_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv); + +int handle_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_daemon_startup_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_targets_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +int handle_target_script_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_run_and_halt_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_working_area_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +int handle_reg_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_poll_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_halt_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_wait_halt_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_reset_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_soft_reset_halt_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_resume_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_step_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_md_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_mw_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_load_binary_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_dump_binary_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_bp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_rbp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_wp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_rwp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +/* targets + */ +extern target_type_t arm7tdmi_target; +extern target_type_t arm720t_target; +extern target_type_t arm9tdmi_target; +extern target_type_t arm920t_target; + +target_type_t *target_types[] = +{ + &arm7tdmi_target, + &arm9tdmi_target, + &arm920t_target, + &arm720t_target, + NULL, +}; + +target_t *targets = NULL; +target_event_callback_t *target_event_callbacks = NULL; +target_timer_callback_t *target_timer_callbacks = NULL; + +char *target_state_strings[] = +{ + "unknown", + "running", + "halted", + "reset", + "debug_running", +}; + +char *target_debug_reason_strings[] = +{ + "debug request", "breakpoint", "watchpoint", + "watchpoint and breakpoint", "single step", + "target not halted" +}; + +char *target_endianess_strings[] = +{ + "big endian", + "little endian", +}; + +enum daemon_startup_mode startup_mode = DAEMON_ATTACH; + +static int target_continous_poll = 1; + +/* returns a pointer to the n-th configured target */ +target_t* get_target_by_num(int num) +{ + target_t *target = targets; + int i = 0; + + while (target) + { + if (num == i) + return target; + target = target->next; + i++; + } + + return NULL; +} + +int get_num_by_target(target_t *query_target) +{ + target_t *target = targets; + int i = 0; + + while (target) + { + if (target == query_target) + return i; + target = target->next; + i++; + } + + return -1; +} + +target_t* get_current_target(command_context_t *cmd_ctx) +{ + target_t *target = get_target_by_num(cmd_ctx->current_target); + + if (target == NULL) + { + ERROR("BUG: current_target out of bounds"); + exit(-1); + } + + return target; +} + +/* Process target initialization, when target entered debug out of reset + * the handler is unregistered at the end of this function, so it's only called once + */ +int target_init_handler(struct target_s *target, enum target_event event, void *priv) +{ + FILE *script; + struct command_context_s *cmd_ctx = priv; + + if ((event == TARGET_EVENT_HALTED) && (target->reset_script)) + { + script = fopen(target->reset_script, "r"); + if (!script) + { + ERROR("couldn't open script file %s", target->reset_script); + return ERROR_OK; + } + + INFO("executing reset script '%s'", target->reset_script); + command_run_file(cmd_ctx, script, COMMAND_EXEC); + fclose(script); + + jtag_execute_queue(); + + target_unregister_event_callback(target_init_handler, priv); + } + + return ERROR_OK; +} + +int target_run_and_halt_handler(void *priv) +{ + target_t *target = priv; + + target->type->halt(target); + + return ERROR_OK; +} + +int target_process_reset(struct command_context_s *cmd_ctx) +{ + int retval = ERROR_OK; + target_t *target; + + target = targets; + while (target) + { + target->type->assert_reset(target); + target = target->next; + } + jtag_execute_queue(); + + /* request target halt if necessary, and schedule further action */ + target = targets; + while (target) + { + switch (target->reset_mode) + { + case RESET_RUN: + /* nothing to do if target just wants to be run */ + break; + case RESET_RUN_AND_HALT: + /* schedule halt */ + target_register_timer_callback(target_run_and_halt_handler, target->run_and_halt_time, 0, target); + break; + case RESET_RUN_AND_INIT: + /* schedule halt */ + target_register_timer_callback(target_run_and_halt_handler, target->run_and_halt_time, 0, target); + target_register_event_callback(target_init_handler, cmd_ctx); + break; + case RESET_HALT: + target->type->halt(target); + break; + case RESET_INIT: + target->type->halt(target); + target_register_event_callback(target_init_handler, cmd_ctx); + break; + default: + ERROR("BUG: unknown target->reset_mode"); + } + target = target->next; + } + + target = targets; + while (target) + { + target->type->deassert_reset(target); + target = target->next; + } + jtag_execute_queue(); + + return retval; +} + +int target_init(struct command_context_s *cmd_ctx) +{ + target_t *target = targets; + + while (target) + { + if (target->type->init_target(cmd_ctx, target) != ERROR_OK) + { + ERROR("target '%s' init failed", target->type->name); + exit(-1); + } + target = target->next; + } + + if (targets) + { + target_register_user_commands(cmd_ctx); + target_register_timer_callback(handle_target, 100, 1, NULL); + } + + if (startup_mode == DAEMON_RESET) + target_process_reset(cmd_ctx); + + return ERROR_OK; +} + +int target_register_event_callback(int (*callback)(struct target_s *target, enum target_event event, void *priv), void *priv) +{ + target_event_callback_t **callbacks_p = &target_event_callbacks; + + if (callback == NULL) + { + return ERROR_INVALID_ARGUMENTS; + } + + if (*callbacks_p) + { + while ((*callbacks_p)->next) + callbacks_p = &((*callbacks_p)->next); + callbacks_p = &((*callbacks_p)->next); + } + + (*callbacks_p) = malloc(sizeof(target_event_callback_t)); + (*callbacks_p)->callback = callback; + (*callbacks_p)->priv = priv; + (*callbacks_p)->next = NULL; + + return ERROR_OK; +} + +int target_register_timer_callback(int (*callback)(void *priv), int time_ms, int periodic, void *priv) +{ + target_timer_callback_t **callbacks_p = &target_timer_callbacks; + struct timeval now; + + if (callback == NULL) + { + return ERROR_INVALID_ARGUMENTS; + } + + if (*callbacks_p) + { + while ((*callbacks_p)->next) + callbacks_p = &((*callbacks_p)->next); + callbacks_p = &((*callbacks_p)->next); + } + + (*callbacks_p) = malloc(sizeof(target_timer_callback_t)); + (*callbacks_p)->callback = callback; + (*callbacks_p)->periodic = periodic; + (*callbacks_p)->time_ms = time_ms; + + gettimeofday(&now, NULL); + (*callbacks_p)->when.tv_usec = now.tv_usec + (time_ms % 1000) * 1000; + time_ms -= (time_ms % 1000); + (*callbacks_p)->when.tv_sec = now.tv_sec + (time_ms / 1000); + if ((*callbacks_p)->when.tv_usec > 1000000) + { + (*callbacks_p)->when.tv_usec = (*callbacks_p)->when.tv_usec - 1000000; + (*callbacks_p)->when.tv_sec += 1; + } + + (*callbacks_p)->priv = priv; + (*callbacks_p)->next = NULL; + + return ERROR_OK; +} + +int target_unregister_event_callback(int (*callback)(struct target_s *target, enum target_event event, void *priv), void *priv) +{ + target_event_callback_t **p = &target_event_callbacks; + target_event_callback_t *c = target_event_callbacks; + + if (callback == NULL) + { + return ERROR_INVALID_ARGUMENTS; + } + + while (c) + { + target_event_callback_t *next = c->next; + if ((c->callback == callback) && (c->priv == priv)) + { + *p = next; + free(c); + return ERROR_OK; + } + else + p = &(c->next); + c = next; + } + + return ERROR_OK; +} + +int target_unregister_timer_callback(int (*callback)(void *priv), void *priv) +{ + target_timer_callback_t **p = &target_timer_callbacks; + target_timer_callback_t *c = target_timer_callbacks; + + if (callback == NULL) + { + return ERROR_INVALID_ARGUMENTS; + } + + while (c) + { + target_timer_callback_t *next = c->next; + if ((c->callback == callback) && (c->priv == priv)) + { + *p = next; + free(c); + return ERROR_OK; + } + else + p = &(c->next); + c = next; + } + + return ERROR_OK; +} + +int target_call_event_callbacks(target_t *target, enum target_event event) +{ + target_event_callback_t *callback = target_event_callbacks; + target_event_callback_t *next_callback; + + DEBUG("target event %i", event); + + while (callback) + { + next_callback = callback->next; + callback->callback(target, event, callback->priv); + callback = next_callback; + } + + return ERROR_OK; +} + +int target_call_timer_callbacks() +{ + target_timer_callback_t *callback = target_timer_callbacks; + target_timer_callback_t *next_callback; + struct timeval now; + + gettimeofday(&now, NULL); + + while (callback) + { + next_callback = callback->next; + + if (((now.tv_sec >= callback->when.tv_sec) && (now.tv_usec >= callback->when.tv_usec)) + || (now.tv_sec > callback->when.tv_sec)) + { + callback->callback(callback->priv); + if (callback->periodic) + { + int time_ms = callback->time_ms; + callback->when.tv_usec = now.tv_usec + (time_ms % 1000) * 1000; + time_ms -= (time_ms % 1000); + callback->when.tv_sec = now.tv_sec + time_ms / 1000; + if (callback->when.tv_usec > 1000000) + { + callback->when.tv_usec = callback->when.tv_usec - 1000000; + callback->when.tv_sec += 1; + } + } + else + target_unregister_timer_callback(callback->callback, callback->priv); + } + + callback = next_callback; + } + + return ERROR_OK; +} + +int target_alloc_working_area(struct target_s *target, u32 size, working_area_t **area) +{ + working_area_t *c = target->working_areas; + working_area_t *new_wa = NULL; + + /* only allocate multiples of 4 byte */ + if (size % 4) + { + ERROR("BUG: code tried to allocate unaligned number of bytes, padding"); + size = CEIL(size, 4); + } + + /* see if there's already a matching working area */ + while (c) + { + if ((c->free) && (c->size == size)) + { + new_wa = c; + break; + } + c = c->next; + } + + /* if not, allocate a new one */ + if (!new_wa) + { + working_area_t **p = &target->working_areas; + u32 first_free = target->working_area; + u32 free_size = target->working_area_size; + + DEBUG("allocating new working area"); + + c = target->working_areas; + while (c) + { + first_free += c->size; + free_size -= c->size; + p = &c->next; + c = c->next; + } + + if (free_size < size) + { + WARNING("not enough working area available"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + new_wa = malloc(sizeof(working_area_t)); + new_wa->next = NULL; + new_wa->size = size; + new_wa->address = first_free; + + if (target->backup_working_area) + { + new_wa->backup = malloc(new_wa->size); + target->type->read_memory(target, new_wa->address, 4, new_wa->size / 4, new_wa->backup); + } + else + { + new_wa->backup = NULL; + } + + /* put new entry in list */ + *p = new_wa; + } + + /* mark as used, and return the new (reused) area */ + new_wa->free = 0; + *area = new_wa; + + /* user pointer */ + new_wa->user = area; + + return ERROR_OK; +} + +int target_free_working_area(struct target_s *target, working_area_t *area) +{ + if (area->free) + return ERROR_OK; + + if (target->backup_working_area) + target->type->write_memory(target, area->address, 4, area->size / 4, area->backup); + + area->free = 1; + + /* mark user pointer invalid */ + *area->user = NULL; + area->user = NULL; + + return ERROR_OK; +} + +int target_free_all_working_areas(struct target_s *target) +{ + working_area_t *c = target->working_areas; + + while (c) + { + working_area_t *next = c->next; + target_free_working_area(target, c); + + if (c->backup) + free(c->backup); + + free(c); + + c = next; + } + + target->working_areas = NULL; + + return ERROR_OK; +} + +int target_register_commands(struct command_context_s *cmd_ctx) +{ + register_command(cmd_ctx, NULL, "target", handle_target_command, COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "targets", handle_targets_command, COMMAND_EXEC, NULL); + register_command(cmd_ctx, NULL, "daemon_startup", handle_daemon_startup_command, COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "target_script", handle_target_script_command, COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "run_and_halt_time", handle_run_and_halt_time_command, COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "working_area", handle_working_area_command, COMMAND_CONFIG, NULL); + + return ERROR_OK; +} + +int target_write_buffer(struct target_s *target, u32 address, u32 size, u8 *buffer) +{ + int retval; + + DEBUG("writing buffer of %i byte at 0x%8.8x", size, address); + + /* handle writes of less than 4 byte */ + if (size < 4) + { + if ((retval = target->type->write_memory(target, address, 1, size, buffer)) != ERROR_OK) + return retval; + } + + /* handle unaligned head bytes */ + if (address % 4) + { + int unaligned = 4 - (address % 4); + + if ((retval = target->type->write_memory(target, address, 1, unaligned, buffer)) != ERROR_OK) + return retval; + + buffer += unaligned; + address += unaligned; + size -= unaligned; + } + + /* handle aligned words */ + if (size >= 4) + { + int aligned = size - (size % 4); + + /* use bulk writes above a certain limit. This may have to be changed */ + if (aligned > 128) + { + if ((retval = target->type->bulk_write_memory(target, address, aligned / 4, buffer)) != ERROR_OK) + return retval; + } + else + { + if ((retval = target->type->write_memory(target, address, 4, aligned / 4, buffer)) != ERROR_OK) + return retval; + } + + buffer += aligned; + address += aligned; + size -= aligned; + } + + /* handle tail writes of less than 4 bytes */ + if (size > 0) + { + if ((retval = target->type->write_memory(target, address, 1, size, buffer)) != ERROR_OK) + return retval; + } + + return ERROR_OK; +} + +int target_read_buffer(struct target_s *target, u32 address, u32 size, u8 *buffer) +{ + int retval; + + DEBUG("reading buffer of %i byte at 0x%8.8x", size, address); + + /* handle reads of less than 4 byte */ + if (size < 4) + { + if ((retval = target->type->read_memory(target, address, 1, size, buffer)) != ERROR_OK) + return retval; + } + + /* handle unaligned head bytes */ + if (address % 4) + { + int unaligned = 4 - (address % 4); + + if ((retval = target->type->read_memory(target, address, 1, unaligned, buffer)) != ERROR_OK) + return retval; + + address += unaligned; + size -= unaligned; + } + + /* handle aligned words */ + if (size >= 4) + { + int aligned = size - (size % 4); + + if ((retval = target->type->read_memory(target, address, 4, aligned / 4, buffer)) != ERROR_OK) + return retval; + + address += aligned; + size -= aligned; + } + + /* handle tail writes of less than 4 bytes */ + if (size > 0) + { + if ((retval = target->type->read_memory(target, address, 1, size, buffer)) != ERROR_OK) + return retval; + } + + return ERROR_OK; +} + +int target_register_user_commands(struct command_context_s *cmd_ctx) +{ + register_command(cmd_ctx, NULL, "reg", handle_reg_command, COMMAND_EXEC, NULL); + register_command(cmd_ctx, NULL, "poll", handle_poll_command, COMMAND_EXEC, "poll target state"); + register_command(cmd_ctx, NULL, "wait_halt", handle_wait_halt_command, COMMAND_EXEC, "wait for target halt"); + register_command(cmd_ctx, NULL, "halt", handle_halt_command, COMMAND_EXEC, "halt target"); + register_command(cmd_ctx, NULL, "resume", handle_resume_command, COMMAND_EXEC, "resume target [addr]"); + register_command(cmd_ctx, NULL, "step", handle_step_command, COMMAND_EXEC, "step one instruction"); + register_command(cmd_ctx, NULL, "reset", handle_reset_command, COMMAND_EXEC, "reset target [run|halt|init|run_and_halt|run_and_init]"); + register_command(cmd_ctx, NULL, "soft_reset_halt", handle_soft_reset_halt_command, COMMAND_EXEC, "halt the target and do a soft reset"); + + register_command(cmd_ctx, NULL, "mdw", handle_md_command, COMMAND_EXEC, "display memory words <addr> [count]"); + register_command(cmd_ctx, NULL, "mdh", handle_md_command, COMMAND_EXEC, "display memory half-words <addr> [count]"); + register_command(cmd_ctx, NULL, "mdb", handle_md_command, COMMAND_EXEC, "display memory bytes <addr> [count]"); + + register_command(cmd_ctx, NULL, "mww", handle_mw_command, COMMAND_EXEC, "write memory word <addr> <value>"); + register_command(cmd_ctx, NULL, "mwh", handle_mw_command, COMMAND_EXEC, "write memory half-word <addr> <value>"); + register_command(cmd_ctx, NULL, "mwb", handle_mw_command, COMMAND_EXEC, "write memory byte <addr> <value>"); + + register_command(cmd_ctx, NULL, "bp", handle_bp_command, COMMAND_EXEC, "set breakpoint <address> <length> [hw]"); + register_command(cmd_ctx, NULL, "rbp", handle_rbp_command, COMMAND_EXEC, "remove breakpoint <adress>"); + register_command(cmd_ctx, NULL, "wp", handle_wp_command, COMMAND_EXEC, "set watchpoint <address> <length> <r/w/a> [value] [mask]"); + register_command(cmd_ctx, NULL, "rwp", handle_rwp_command, COMMAND_EXEC, "remove watchpoint <adress>"); + + register_command(cmd_ctx, NULL, "load_binary", handle_load_binary_command, COMMAND_EXEC, "load binary <file> <address>"); + register_command(cmd_ctx, NULL, "dump_binary", handle_dump_binary_command, COMMAND_EXEC, "dump binary <file> <address> <size>"); + + return ERROR_OK; +} + +int handle_targets_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = targets; + int count = 0; + + if (argc == 1) + { + int num = strtoul(args[0], NULL, 0); + + while (target) + { + count++; + target = target->next; + } + + if (num < count) + cmd_ctx->current_target = num; + else + command_print(cmd_ctx, "%i is out of bounds, only %i targets are configured", num, count); + + return ERROR_OK; + } + + while (target) + { + command_print(cmd_ctx, "%i: %s (%s), state: %s", count++, target->type->name, target_endianess_strings[target->endianness], target_state_strings[target->state]); + target = target->next; + } + + return ERROR_OK; +} + +int handle_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int i; + int found = 0; + + if (argc < 3) + { + ERROR("target command requires at least three arguments: <type> <endianess> <reset_mode>"); + exit(-1); + } + + /* search for the specified target */ + if (args[0] && (args[0][0] != 0)) + { + for (i = 0; target_types[i]; i++) + { + if (strcmp(args[0], target_types[i]->name) == 0) + { + target_t **last_target_p = &targets; + + /* register target specific commands */ + if (target_types[i]->register_commands(cmd_ctx) != ERROR_OK) + { + ERROR("couldn't register '%s' commands", args[0]); + exit(-1); + } + + if (*last_target_p) + { + while ((*last_target_p)->next) + last_target_p = &((*last_target_p)->next); + last_target_p = &((*last_target_p)->next); + } + + *last_target_p = malloc(sizeof(target_t)); + + (*last_target_p)->type = target_types[i]; + + if (strcmp(args[1], "big") == 0) + (*last_target_p)->endianness = TARGET_BIG_ENDIAN; + else if (strcmp(args[1], "little") == 0) + (*last_target_p)->endianness = TARGET_LITTLE_ENDIAN; + else + { + ERROR("endianness must be either 'little' or 'big', not '%s'", args[1]); + exit(-1); + } + + /* what to do on a target reset */ + if (strcmp(args[2], "reset_halt") == 0) + (*last_target_p)->reset_mode = RESET_HALT; + else if (strcmp(args[2], "reset_run") == 0) + (*last_target_p)->reset_mode = RESET_RUN; + else if (strcmp(args[2], "reset_init") == 0) + (*last_target_p)->reset_mode = RESET_INIT; + else if (strcmp(args[2], "run_and_halt") == 0) + (*last_target_p)->reset_mode = RESET_RUN_AND_HALT; + else if (strcmp(args[2], "run_and_init") == 0) + (*last_target_p)->reset_mode = RESET_RUN_AND_INIT; + else + { + ERROR("unknown target startup mode %s", args[2]); + exit(-1); + } + (*last_target_p)->run_and_halt_time = 1000; /* default 1s */ + + (*last_target_p)->reset_script = NULL; + (*last_target_p)->post_halt_script = NULL; + (*last_target_p)->pre_resume_script = NULL; + + (*last_target_p)->working_area = 0x0; + (*last_target_p)->working_area_size = 0x0; + (*last_target_p)->working_areas = NULL; + (*last_target_p)->backup_working_area = 0; + + (*last_target_p)->endianness = TARGET_LITTLE_ENDIAN; + (*last_target_p)->state = TARGET_UNKNOWN; + (*last_target_p)->reg_cache = NULL; + (*last_target_p)->breakpoints = NULL; + (*last_target_p)->watchpoints = NULL; + (*last_target_p)->next = NULL; + (*last_target_p)->arch_info = NULL; + + (*last_target_p)->type->target_command(cmd_ctx, cmd, args, argc, *last_target_p); + + found = 1; + break; + } + } + } + + /* no matching target found */ + if (!found) + { + ERROR("target '%s' not found", args[0]); + exit(-1); + } + + return ERROR_OK; +} + +/* usage: target_script <target#> <event> <script_file> */ +int handle_target_script_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = NULL; + + if (argc < 3) + { + ERROR("incomplete target_script command"); + exit(-1); + } + + target = get_target_by_num(strtoul(args[0], NULL, 0)); + + if (!target) + { + ERROR("target number '%s' not defined", args[0]); + exit(-1); + } + + if (strcmp(args[1], "reset") == 0) + { + if (target->reset_script) + free(target->reset_script); + target->reset_script = strdup(args[2]); + } + else if (strcmp(args[1], "post_halt") == 0) + { + if (target->post_halt_script) + free(target->post_halt_script); + target->post_halt_script = strdup(args[2]); + } + else if (strcmp(args[1], "pre_resume") == 0) + { + if (target->pre_resume_script) + free(target->pre_resume_script); + target->pre_resume_script = strdup(args[2]); + } + else + { + ERROR("unknown event type: '%s", args[1]); + exit(-1); + } + + return ERROR_OK; +} + +int handle_run_and_halt_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = NULL; + + if (argc < 2) + { + ERROR("incomplete run_and_halt_time command"); + exit(-1); + } + + target = get_target_by_num(strtoul(args[0], NULL, 0)); + + if (!target) + { + ERROR("target number '%s' not defined", args[0]); + exit(-1); + } + + target->run_and_halt_time = strtoul(args[1], NULL, 0); + + return ERROR_OK; +} + +int handle_working_area_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = NULL; + + if (argc < 4) + { + ERROR("incomplete working_area command. usage: working_area <target#> <address> <size> <'backup'|'nobackup'>"); + exit(-1); + } + + target = get_target_by_num(strtoul(args[0], NULL, 0)); + + if (!target) + { + ERROR("target number '%s' not defined", args[0]); + exit(-1); + } + + target->working_area = strtoul(args[1], NULL, 0); + target->working_area_size = strtoul(args[2], NULL, 0); + + if (strcmp(args[3], "backup") == 0) + { + target->backup_working_area = 1; + } + else if (strcmp(args[3], "nobackup") == 0) + { + target->backup_working_area = 0; + } + else + { + ERROR("unrecognized <backup|nobackup> argument (%s)", args[3]); + exit(-1); + } + + return ERROR_OK; +} + + +/* process target state changes */ +int handle_target(void *priv) +{ + int retval; + target_t *target = targets; + + while (target) + { + /* only poll if target isn't already halted */ + if (target->state != TARGET_HALTED) + { + if (target_continous_poll) + if ((retval = target->type->poll(target)) < 0) + { + ERROR("couldn't poll target, exiting"); + exit(-1); + } + } + + target = target->next; + } + + return ERROR_OK; +} + +int handle_reg_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target; + reg_t *reg = NULL; + int count = 0; + char *value; + + DEBUG(""); + + target = get_current_target(cmd_ctx); + + /* list all available registers for the current target */ + if (argc == 0) + { + reg_cache_t *cache = target->reg_cache; + + count = 0; + while(cache) + { + int i; + for (i = 0; i < cache->num_regs; i++) + { + value = buf_to_char(cache->reg_list[i].value, cache->reg_list[i].size); + command_print(cmd_ctx, "(%i) %s (/%i): 0x%s (dirty: %i, valid: %i)", count++, cache->reg_list[i].name, cache->reg_list[i].size, value, cache->reg_list[i].dirty, cache->reg_list[i].valid); + free(value); + } + cache = cache->next; + } + + return ERROR_OK; + } + + /* access a single register by its ordinal number */ + if ((args[0][0] >= '0') && (args[0][0] <= '9')) + { + int num = strtoul(args[0], NULL, 0); + reg_cache_t *cache = target->reg_cache; + + count = 0; + while(cache) + { + int i; + for (i = 0; i < cache->num_regs; i++) + { + if (count++ == num) + { + reg = &cache->reg_list[i]; + break; + } + } + if (reg) + break; + cache = cache->next; + } + + if (!reg) + { + command_print(cmd_ctx, "%i is out of bounds, the current target has only %i registers (0 - %i)", num, count, count - 1); + return ERROR_OK; + } + } else /* access a single register by its name */ + { + reg = register_get_by_name(target->reg_cache, args[0], 1); + + if (!reg) + { + command_print(cmd_ctx, "register %s not found in current target", args[0]); + return ERROR_OK; + } + } + + /* display a register */ + if ((argc == 1) || ((argc == 2) && !((args[1][0] >= '0') && (args[1][0] <= '9')))) + { + if ((argc == 2) && (strcmp(args[1], "force") == 0)) + reg->valid = 0; + + if (reg->valid == 0) + { + reg_arch_type_t *arch_type = register_get_arch_type(reg->arch_type); + if (arch_type == NULL) + { + ERROR("BUG: encountered unregistered arch type"); + return ERROR_OK; + } + arch_type->get(reg); + } + value = buf_to_char(reg->value, reg->size); + command_print(cmd_ctx, "%s (/%i): 0x%s", reg->name, reg->size, value); + free(value); + return ERROR_OK; + } + + /* set register value */ + if (argc == 2) + { + u32 new_value = strtoul(args[1], NULL, 0); + reg_arch_type_t *arch_type = register_get_arch_type(reg->arch_type); + if (arch_type == NULL) + { + ERROR("BUG: encountered unregistered arch type"); + return ERROR_OK; + } + + arch_type->set(reg, new_value); + value = buf_to_char(reg->value, reg->size); + command_print(cmd_ctx, "%s (/%i): 0x%s", reg->name, reg->size, value); + free(value); + + return ERROR_OK; + } + + command_print(cmd_ctx, "usage: reg <#|name> [value]"); + + return ERROR_OK; +} + +int handle_poll_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + char buffer[512]; + + if (argc == 0) + { + command_print(cmd_ctx, "target state: %s", target_state_strings[target->type->poll(target)]); + if (target->state == TARGET_HALTED) + { + target->type->arch_state(target, buffer, 512); + buffer[511] = 0; + command_print(cmd_ctx, "%s", buffer); + } + } + else + { + if (strcmp(args[0], "on") == 0) + { + target_continous_poll = 1; + } + else if (strcmp(args[0], "off") == 0) + { + target_continous_poll = 0; + } + } + + + return ERROR_OK; +} + +int handle_wait_halt_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + struct timeval timeout, now; + + gettimeofday(&timeout, NULL); + timeval_add_time(&timeout, 5, 0); + + command_print(cmd_ctx, "waiting for target halted..."); + + while(target->type->poll(target)) + { + if (target->state == TARGET_HALTED) + { + command_print(cmd_ctx, "target halted"); + break; + } + target_call_timer_callbacks(); + + gettimeofday(&now, NULL); + if ((now.tv_sec >= timeout.tv_sec) && (now.tv_usec >= timeout.tv_usec)) + { + command_print(cmd_ctx, "timed out while waiting for target halt"); + ERROR("timed out while waiting for target halt"); + break; + } + } + + return ERROR_OK; +} + +int handle_halt_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int retval; + target_t *target = get_current_target(cmd_ctx); + + DEBUG(""); + + command_print(cmd_ctx, "requesting target halt..."); + + if ((retval = target->type->halt(target)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_ALREADY_HALTED: + command_print(cmd_ctx, "target already halted"); + break; + case ERROR_TARGET_TIMEOUT: + command_print(cmd_ctx, "target timed out... shutting down"); + exit(-1); + default: + command_print(cmd_ctx, "unknown error... shutting down"); + exit(-1); + } + } + + return ERROR_OK; + +} + +/* what to do on daemon startup */ +int handle_daemon_startup_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 1) + { + if (strcmp(args[0], "attach") == 0) + { + startup_mode = DAEMON_ATTACH; + return ERROR_OK; + } + else if (strcmp(args[0], "reset") == 0) + { + startup_mode = DAEMON_RESET; + return ERROR_OK; + } + } + + WARNING("invalid daemon_startup configuration directive: %s", args[0]); + return ERROR_OK; + +} + +int handle_soft_reset_halt_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + int retval; + + command_print(cmd_ctx, "requesting target halt and executing a soft reset"); + + if ((retval = target->type->soft_reset_halt(target)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_TIMEOUT: + command_print(cmd_ctx, "target timed out... shutting down"); + exit(-1); + default: + command_print(cmd_ctx, "unknown error... shutting down"); + exit(-1); + } + } + + return ERROR_OK; +} + +int handle_reset_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + enum target_reset_mode reset_mode = RESET_RUN; + + DEBUG(""); + + if (argc >= 1) + { + if (strcmp("run", args[0]) == 0) + reset_mode = RESET_RUN; + else if (strcmp("halt", args[0]) == 0) + reset_mode = RESET_HALT; + else if (strcmp("init", args[0]) == 0) + reset_mode = RESET_INIT; + else if (strcmp("run_and_halt", args[0]) == 0) + { + reset_mode = RESET_RUN_AND_HALT; + if (argc >= 2) + { + target->run_and_halt_time = strtoul(args[1], NULL, 0); + } + } + else if (strcmp("run_and_init", args[0]) == 0) + { + reset_mode = RESET_RUN_AND_INIT; + if (argc >= 2) + { + target->run_and_halt_time = strtoul(args[1], NULL, 0); + } + } + else + { + command_print(cmd_ctx, "usage: reset ['run', 'halt', 'init', 'run_and_halt', 'run_and_init]"); + return ERROR_OK; + } + target->reset_mode = reset_mode; + } + + target_process_reset(cmd_ctx); + + return ERROR_OK; +} + +int handle_resume_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int retval; + target_t *target = get_current_target(cmd_ctx); + + DEBUG(""); + + if (argc == 0) + retval = target->type->resume(target, 1, 0, 1, 0); /* current pc, addr = 0, handle breakpoints, not debugging */ + else if (argc == 1) + retval = target->type->resume(target, 0, strtoul(args[0], NULL, 0), 1, 0); /* addr = args[0], handle breakpoints, not debugging */ + else + { + command_print(cmd_ctx, "usage: resume [address]"); + return ERROR_OK; + } + + if (retval != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_NOT_HALTED: + command_print(cmd_ctx, "target not halted"); + break; + default: + command_print(cmd_ctx, "unknown error... shutting down"); + exit(-1); + } + } + + return ERROR_OK; +} + +int handle_step_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + + DEBUG(""); + + if (argc == 0) + target->type->step(target, 1, 0, 1); /* current pc, addr = 0, handle breakpoints */ + + if (argc == 1) + target->type->step(target, 0, strtoul(args[0], NULL, 0), 1); /* addr = args[0], handle breakpoints */ + + return ERROR_OK; +} + +int handle_md_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int count = 1; + int size = 4; + u32 address = 0; + int i; + + char output[128]; + int output_len; + + int retval; + + u8 *buffer; + target_t *target = get_current_target(cmd_ctx); + + if (argc < 1) + return ERROR_OK; + + if (argc == 2) + count = strtoul(args[1], NULL, 0); + + address = strtoul(args[0], NULL, 0); + + + switch (cmd[2]) + { + case 'w': + size = 4; + break; + case 'h': + size = 2; + break; + case 'b': + size = 1; + break; + default: + return ERROR_OK; + } + + buffer = calloc(count, size); + if ((retval = target->type->read_memory(target, address, size, count, buffer)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_UNALIGNED_ACCESS: + command_print(cmd_ctx, "error: address not aligned"); + break; + case ERROR_TARGET_NOT_HALTED: + command_print(cmd_ctx, "error: target must be halted for memory accesses"); + break; + case ERROR_TARGET_DATA_ABORT: + command_print(cmd_ctx, "error: access caused data abort, system possibly corrupted"); + break; + default: + command_print(cmd_ctx, "error: unknown error"); + break; + } + } + + output_len = 0; + + for (i = 0; i < count; i++) + { + if (i%8 == 0) + output_len += snprintf(output + output_len, 128 - output_len, "0x%8.8x: ", address + (i*size)); + + switch (size) + { + case 4: + output_len += snprintf(output + output_len, 128 - output_len, "%8.8x ", ((u32*)buffer)[i]); + break; + case 2: + output_len += snprintf(output + output_len, 128 - output_len, "%4.4x ", ((u16*)buffer)[i]); + break; + case 1: + output_len += snprintf(output + output_len, 128 - output_len, "%2.2x ", ((u8*)buffer)[i]); + break; + } + + if ((i%8 == 7) || (i == count - 1)) + { + command_print(cmd_ctx, output); + output_len = 0; + } + } + + free(buffer); + + return ERROR_OK; +} + +int handle_mw_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + u32 address = 0; + u32 value = 0; + int retval; + target_t *target = get_current_target(cmd_ctx); + + if (argc < 2) + return ERROR_OK; + + address = strtoul(args[0], NULL, 0); + value = strtoul(args[1], NULL, 0); + + switch (cmd[2]) + { + case 'w': + retval = target->type->write_memory(target, address, 4, 1, (u8*)&value); + break; + case 'h': + retval = target->type->write_memory(target, address, 2, 1, (u8*)&value); + break; + case 'b': + retval = target->type->write_memory(target, address, 1, 1, (u8*)&value); + break; + default: + return ERROR_OK; + } + + switch (retval) + { + case ERROR_TARGET_UNALIGNED_ACCESS: + command_print(cmd_ctx, "error: address not aligned"); + break; + case ERROR_TARGET_DATA_ABORT: + command_print(cmd_ctx, "error: access caused data abort, system possibly corrupted"); + break; + case ERROR_TARGET_NOT_HALTED: + command_print(cmd_ctx, "error: target must be halted for memory accesses"); + break; + case ERROR_OK: + break; + default: + command_print(cmd_ctx, "error: unknown error"); + break; + } + + return ERROR_OK; + +} + +int handle_load_binary_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + FILE *binary; + u32 address; + struct stat binary_stat; + u32 binary_size; + + u8 *buffer; + u32 buf_cnt; + + struct timeval start, end; + + target_t *target = get_current_target(cmd_ctx); + + if (argc != 2) + { + command_print(cmd_ctx, "usage: load_binary <filename> <address>"); + return ERROR_OK; + } + + address = strtoul(args[1], NULL, 0); + + if (stat(args[0], &binary_stat) == -1) + { + ERROR("couldn't stat() %s: %s", args[0], strerror(errno)); + command_print(cmd_ctx, "error accessing file %s", args[0]); + return ERROR_OK; + } + + if (!(binary = fopen(args[0], "r"))) + { + ERROR("couldn't open %s: %s", args[0], strerror(errno)); + command_print(cmd_ctx, "error accessing file %s", args[0]); + return ERROR_OK; + } + + buffer = malloc(128 * 1024); + + gettimeofday(&start, NULL); + + binary_size = binary_stat.st_size; + while (binary_size > 0) + { + buf_cnt = fread(buffer, 1, 128*1024, binary); + target_write_buffer(target, address, buf_cnt, buffer); + address += buf_cnt; + binary_size -= buf_cnt; + } + + gettimeofday(&end, NULL); + + free(buffer); + + command_print(cmd_ctx, "downloaded %lli byte in %is %ius", (long long) binary_stat.st_size, end.tv_sec - start.tv_sec, end.tv_usec - start.tv_usec); + + fclose(binary); + + return ERROR_OK; + +} + +int handle_dump_binary_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + FILE *binary; + u32 address; + u32 size; + u8 buffer[560]; + + target_t *target = get_current_target(cmd_ctx); + + if (argc != 3) + { + command_print(cmd_ctx, "usage: dump_binary <filename> <address> <size>"); + return ERROR_OK; + } + + address = strtoul(args[1], NULL, 0); + size = strtoul(args[2], NULL, 0); + + if (!(binary = fopen(args[0], "w"))) + { + ERROR("couldn't open %s for writing: %s", args[0], strerror(errno)); + command_print(cmd_ctx, "error accessing file %s", args[0]); + return ERROR_OK; + } + + if ((address & 3) || (size & 3)) + { + command_print(cmd_ctx, "only 32-bit aligned address and size are supported"); + return ERROR_OK; + } + + while (size > 0) + { + u32 this_run_size = (size > 560) ? 560 : size; + target->type->read_memory(target, address, 4, this_run_size / 4, buffer); + fwrite(buffer, 1, this_run_size, binary); + size -= this_run_size; + address += this_run_size; + } + + fclose(binary); + + return ERROR_OK; + +} + +int handle_bp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + int retval; + target_t *target = get_current_target(cmd_ctx); + + if (argc == 0) + { + breakpoint_t *breakpoint = target->breakpoints; + + while (breakpoint) + { + if (breakpoint->type == BKPT_SOFT) + { + char* buf = buf_to_char(breakpoint->orig_instr, breakpoint->length); + command_print(cmd_ctx, "0x%8.8x, 0x%x, %i, 0x%s", breakpoint->address, breakpoint->length, breakpoint->set, buf); + free(buf); + } + else + { + command_print(cmd_ctx, "0x%8.8x, 0x%x, %i", breakpoint->address, breakpoint->length, breakpoint->set); + } + breakpoint = breakpoint->next; + } + } + else if (argc >= 2) + { + int hw = BKPT_SOFT; + u32 length = 0; + + length = strtoul(args[1], NULL, 0); + + if (argc >= 3) + if (strcmp(args[2], "hw") == 0) + hw = BKPT_HARD; + + if ((retval = breakpoint_add(target, strtoul(args[0], NULL, 0), length, hw)) != ERROR_OK) + { + switch (retval) + { + case ERROR_TARGET_NOT_HALTED: + command_print(cmd_ctx, "target must be halted to set breakpoints"); + break; + case ERROR_TARGET_RESOURCE_NOT_AVAILABLE: + command_print(cmd_ctx, "no more breakpoints available"); + break; + default: + command_print(cmd_ctx, "unknown error, breakpoint not set"); + break; + } + } + } + + return ERROR_OK; +} + +int handle_rbp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + + if (argc > 0) + breakpoint_remove(target, strtoul(args[0], NULL, 0)); + + return ERROR_OK; +} + +int handle_wp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + + if (argc == 0) + { + watchpoint_t *watchpoint = target->watchpoints; + + while (watchpoint) + { + command_print(cmd_ctx, "address: 0x%8.8x, mask: 0x%8.8x, r/w/a: %i, value: 0x%8.8x, mask: 0x%8.8x", watchpoint->address, watchpoint->length, watchpoint->rw, watchpoint->value, watchpoint->mask); + watchpoint = watchpoint->next; + } + } + else if (argc >= 2) + { + enum watchpoint_rw type = WPT_ACCESS; + u32 data_value = 0x0; + u32 data_mask = 0xffffffff; + + if (argc >= 3) + { + switch(args[2][0]) + { + case 'r': + type = WPT_READ; + break; + case 'w': + type = WPT_WRITE; + break; + case 'a': + type = WPT_ACCESS; + break; + default: + command_print(cmd_ctx, "usage: wp <address> <length> [r/w/a] [value] [mask]"); + return ERROR_OK; + } + } + if (argc >= 4) + { + data_value = strtoul(args[3], NULL, 0); + } + if (argc >= 5) + { + data_mask = strtoul(args[4], NULL, 0); + } + watchpoint_add(target, strtoul(args[0], NULL, 0), strtoul(args[1], NULL, 0), type, data_value, data_mask); + } + else + { + command_print(cmd_ctx, "usage: wp <address> <length> [r/w/a] [value] [mask]"); + } + + return ERROR_OK; +} + +int handle_rwp_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + + if (argc > 0) + watchpoint_remove(target, strtoul(args[0], NULL, 0)); + + return ERROR_OK; +} + diff --git a/src/target/target.h b/src/target/target.h new file mode 100644 index 00000000..6d3b6d17 --- /dev/null +++ b/src/target/target.h @@ -0,0 +1,231 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef TARGET_H +#define TARGET_H + +#include "register.h" +#include "breakpoints.h" +#include "algorithm.h" + +#include "command.h" +#include "types.h" + +#include <sys/time.h> +#include <time.h> + +struct reg_s; +struct command_context_s; + +enum target_state +{ + TARGET_UNKNOWN = 0, + TARGET_RUNNING = 1, + TARGET_HALTED = 2, + TARGET_RESET = 3, + TARGET_DEBUG_RUNNING = 4, +}; + +extern char *target_state_strings[]; + +enum daemon_startup_mode +{ + DAEMON_ATTACH, /* simply attach to the target */ + DAEMON_RESET, /* reset target (behaviour defined by reset_mode */ +}; + +enum target_reset_mode +{ + RESET_RUN = 0, /* reset and let target run */ + RESET_HALT = 1, /* reset and halt target out of reset */ + RESET_INIT = 2, /* reset and halt target out of reset, then run init script */ + RESET_RUN_AND_HALT = 3, /* reset and let target run, halt after n milliseconds */ + RESET_RUN_AND_INIT = 4, /* reset and let target run, halt after n milliseconds, then run init script */ +}; + +enum target_debug_reason +{ + DBG_REASON_DBGRQ = 0, + DBG_REASON_BREAKPOINT = 1, + DBG_REASON_WATCHPOINT = 2, + DBG_REASON_WPTANDBKPT = 3, + DBG_REASON_SINGLESTEP = 4, + DBG_REASON_NOTHALTED = 5 +}; + +extern char *target_debug_reason_strings[]; + +enum target_endianess +{ + TARGET_BIG_ENDIAN = 0, TARGET_LITTLE_ENDIAN = 1 +}; + +extern char *target_endianess_strings[]; + +struct target_s; + +typedef struct working_area_s +{ + u32 address; + u32 size; + int free; + u8 *backup; + struct working_area_s **user; + struct working_area_s *next; +} working_area_t; + +typedef struct target_type_s +{ + char *name; + + /* poll current target status */ + enum target_state (*poll)(struct target_s *target); + /* architecture specific status reply */ + int (*arch_state)(struct target_s *target, char *buf, int buf_size); + + /* target execution control */ + int (*halt)(struct target_s *target); + int (*resume)(struct target_s *target, int current, u32 address, int handle_breakpoints, int debug_execution); + int (*step)(struct target_s *target, int current, u32 address, int handle_breakpoints); + + /* target reset control */ + int (*assert_reset)(struct target_s *target); + int (*deassert_reset)(struct target_s *target); + int (*soft_reset_halt)(struct target_s *target); + + /* target register access for gdb */ + int (*get_gdb_reg_list)(struct target_s *target, struct reg_s **reg_list[], int *reg_list_size); + + /* target memory access + * size: 1 = byte (8bit), 2 = half-word (16bit), 4 = word (32bit) + * count: number of items of <size> + */ + int (*read_memory)(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); + int (*write_memory)(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); + + /* write target memory in multiples of 4 byte, optimized for writing large quantities of data */ + int (*bulk_write_memory)(struct target_s *target, u32 address, u32 count, u8 *buffer); + + /* target break-/watchpoint control + * rw: 0 = write, 1 = read, 2 = access + */ + int (*add_breakpoint)(struct target_s *target, u32 address, u32 length, enum breakpoint_type type); + int (*remove_breakpoint)(struct target_s *target, breakpoint_t *breakpoint); + int (*add_watchpoint)(struct target_s *target, u32 address, u32 length, enum watchpoint_rw rw); + int (*remove_watchpoint)(struct target_s *target, watchpoint_t *watchpoint); + + /* target algorithm support */ + int (*run_algorithm)(struct target_s *target, int num_mem_params, mem_param_t *mem_params, int num_reg_params, reg_param_t *reg_param, u32 entry_point, u32 exit_point, int timeout_ms, void *arch_info); + + int (*register_commands)(struct command_context_s *cmd_ctx); + int (*target_command)(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target); + int (*init_target)(struct command_context_s *cmd_ctx, struct target_s *target); + int (*quit)(void); + +} target_type_t; + +typedef struct target_s +{ + target_type_t *type; /* target type definition (name, access functions) */ + enum target_reset_mode reset_mode; /* what to do after a reset */ + int run_and_halt_time; /* how long the target should run after a run_and_halt reset */ + char *reset_script; /* script file to initialize the target after a reset */ + char *post_halt_script; /* script file to execute after the target halted */ + char *pre_resume_script; /* script file to execute before the target resumed */ + u32 working_area; /* working area (initialized RAM) */ + u32 working_area_size; /* size in bytes */ + u32 backup_working_area; /* whether the content of the working area has to be preserved */ + struct working_area_s *working_areas;/* list of allocated working areas */ + enum target_debug_reason debug_reason; /* reason why the target entered debug state */ + enum target_endianess endianness; /* target endianess */ + enum target_state state; /* the current backend-state (running, halted, ...) */ + struct reg_cache_s *reg_cache; /* the first register cache of the target (core regs) */ + struct breakpoint_s *breakpoints; /* list of breakpoints */ + struct watchpoint_s *watchpoints; /* list of watchpoints */ + void *arch_info; /* architecture specific information */ + struct target_s *next; /* next target in list */ +} target_t; + +enum target_event +{ + TARGET_EVENT_HALTED, /* target entered debug state from normal execution or reset */ + TARGET_EVENT_RESUMED, /* target resumed to normal execution */ + TARGET_EVENT_RESET, /* target entered reset */ + TARGET_EVENT_DEBUG_HALTED, /* target entered debug state, but was executing on behalf of the debugger */ + TARGET_EVENT_DEBUG_RESUMED, /* target resumed to execute on behalf of the debugger */ +}; + +typedef struct target_event_callback_s +{ + int (*callback)(struct target_s *target, enum target_event event, void *priv); + void *priv; + struct target_event_callback_s *next; +} target_event_callback_t; + +typedef struct target_timer_callback_s +{ + int (*callback)(void *priv); + int time_ms; + int periodic; + struct timeval when; + void *priv; + struct target_timer_callback_s *next; +} target_timer_callback_t; + +extern int target_register_commands(struct command_context_s *cmd_ctx); +extern int target_register_user_commands(struct command_context_s *cmd_ctx); +extern int target_init(struct command_context_s *cmd_ctx); +extern int handle_target(void *priv); + +extern int target_register_event_callback(int (*callback)(struct target_s *target, enum target_event event, void *priv), void *priv); +extern int target_unregister_event_callback(int (*callback)(struct target_s *target, enum target_event event, void *priv), void *priv); +extern int target_call_event_callbacks(target_t *target, enum target_event event); + +extern int target_register_timer_callback(int (*callback)(void *priv), int time_ms, int periodic, void *priv); +extern int target_unregister_timer_callback(int (*callback)(void *priv), void *priv); +extern int target_call_timer_callbacks(); + +extern target_t* get_current_target(struct command_context_s *cmd_ctx); +extern int get_num_by_target(target_t *query_target); +extern target_t* get_target_by_num(int num); + +extern int target_write_buffer(struct target_s *target, u32 address, u32 size, u8 *buffer); +extern int target_read_buffer(struct target_s *target, u32 address, u32 size, u8 *buffer); + +extern int target_alloc_working_area(struct target_s *target, u32 size, working_area_t **area); +extern int target_free_working_area(struct target_s *target, working_area_t *area); +extern int target_free_all_working_areas(struct target_s *target); + +extern target_t *targets; + +extern target_event_callback_t *target_event_callbacks; +extern target_timer_callback_t *target_timer_callbacks; + +#define ERROR_TARGET_INVALID (-300) +#define ERROR_TARGET_INIT_FAILED (-301) +#define ERROR_TARGET_TIMEOUT (-302) +#define ERROR_TARGET_ALREADY_HALTED (-303) +#define ERROR_TARGET_NOT_HALTED (-304) +#define ERROR_TARGET_FAILURE (-305) +#define ERROR_TARGET_UNALIGNED_ACCESS (-306) +#define ERROR_TARGET_DATA_ABORT (-307) +#define ERROR_TARGET_RESOURCE_NOT_AVAILABLE (-308) +#define ERROR_TARGET_TRANSLATION_FAULT (-309) + +#endif /* TARGET_H */ diff --git a/src/xsvf/Makefile.am b/src/xsvf/Makefile.am new file mode 100644 index 00000000..d9e80875 --- /dev/null +++ b/src/xsvf/Makefile.am @@ -0,0 +1,5 @@ +INCLUDES = -I$(top_srcdir)/src/gdb -I$(top_srcdir)/src/helper -I$(top_srcdir)/src/jtag $(all_includes) +METASOURCES = AUTO +noinst_LIBRARIES = libxsvf.a +noinst_HEADERS = xsvf.h +libxsvf_a_SOURCES = xsvf.c diff --git a/src/xsvf/xsvf.c b/src/xsvf/xsvf.c new file mode 100644 index 00000000..013803f6 --- /dev/null +++ b/src/xsvf/xsvf.c @@ -0,0 +1,506 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "xsvf.h" + +#include "jtag.h" +#include "command.h" +#include "log.h" + +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <string.h> + +#include <sys/time.h> +#include <time.h> + +#define XSTATE_MAX_PATH (12) + +int handle_xsvf_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); + +int xsvf_fd = 0; + +u8 *dr_out_buf; /* from host to device (TDI) */ +u8 *dr_in_buf; /* from device to host (TDO) */ +u8 *dr_in_mask; + +int xsdrsize = 0; +int xruntest = 0; /* number of TCK cycles / microseconds */ +int xrepeat = 0x20; /* number of XC9500 retries */ + +int xendir = 0; +int xenddr = 0; + +enum tap_state xsvf_to_tap[] = +{ + TAP_TLR, TAP_RTI, + TAP_SDS, TAP_CD, TAP_SD, TAP_E1D, TAP_PD, TAP_E2D, TAP_UD, + TAP_SIS, TAP_CI, TAP_SI, TAP_E1I, TAP_PI, TAP_E2I, TAP_UI, +}; + +int tap_to_xsvf[] = +{ + 0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1, 0x9, 0xa, 0xb, 0xc, 0xe, 0xf +}; + +int xsvf_register_commands(struct command_context_s *cmd_ctx) +{ + register_command(cmd_ctx, NULL, "xsvf", handle_xsvf_command, + COMMAND_EXEC, "run xsvf <file>"); + + return ERROR_OK; +} + +int xsvf_read_buffer(int num_bits, int fd, u8* buf) +{ + int num_bytes; + + for (num_bytes = (num_bits + 7) / 8; num_bytes > 0; num_bytes--) + { + if (read(fd, buf + num_bytes - 1, 1) < 0) + return ERROR_XSVF_EOF; + } + + return ERROR_OK; +} + +int xsvf_read_xstates(int fd, enum tap_state *path, int max_path, int *path_len) +{ + char c; + unsigned char uc; + + while ((read(fd, &c, 1) > 0) && (c == 0x12)) + { + if (*path_len > max_path) + { + WARNING("XSTATE path longer than max_path"); + break; + } + if (read(fd, &uc, 1) < 0) + { + return ERROR_XSVF_EOF; + } + path[(*path_len)++] = xsvf_to_tap[uc]; + } + + lseek(fd, -1, SEEK_CUR); + + return ERROR_OK; +} + +int handle_xsvf_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + char c; + unsigned char uc, uc2; + unsigned int ui; + unsigned short us; + + int do_abort = 0; + int unsupported = 0; + int tdo_mismatch = 0; + + int runtest_requires_tck = 0; + + int device = -1; /* use -1 to indicate a "plain" xsvf file which accounts for additional devices in the scan chain, otherwise the device that should be affected */ + + if (argc < 2) + { + command_print(cmd_ctx, "usage: xsvf <device#|plain> <file> <variant>"); + return ERROR_OK; + } + + if (strcmp(args[0], "plain") != 0) + { + device = strtoul(args[0], NULL, 0); + } + + if ((xsvf_fd = open(args[1], O_RDONLY)) < 0) + { + command_print(cmd_ctx, "file %s not found", args[0]); + return ERROR_OK; + } + + if ((argc > 2) && (strcmp(args[2], "virt2") == 0)) + { + runtest_requires_tck = 1; + } + + while (read(xsvf_fd, &c, 1) > 0) + { + switch (c) + { + case 0x00: /* XCOMPLETE */ + DEBUG("XCOMPLETE"); + if (jtag_execute_queue() != ERROR_OK) + { + tdo_mismatch = 1; + break; + } + break; + case 0x01: /* XTDOMASK */ + DEBUG("XTDOMASK"); + if (dr_in_mask && (xsvf_read_buffer(xsdrsize, xsvf_fd, dr_in_mask) != ERROR_OK)) + do_abort = 1; + break; + case 0x02: /* XSIR */ + DEBUG("XSIR"); + if (read(xsvf_fd, &c, 1) < 0) + do_abort = 1; + else + { + u8 *ir_buf = malloc((c + 7) / 8); + if (xsvf_read_buffer(c, xsvf_fd, ir_buf) != ERROR_OK) + do_abort = 1; + else + { + scan_field_t field; + field.device = device; + field.num_bits = c; + field.out_value = ir_buf; + field.out_mask = NULL; + field.in_value = NULL; + field.in_check_value = NULL; + field.in_check_mask = NULL; + field.in_handler = NULL; + field.in_handler_priv = NULL; + if (device == -1) + jtag_add_plain_ir_scan(1, &field, TAP_PI); + else + jtag_add_ir_scan(1, &field, TAP_PI); + if (jtag_execute_queue() != ERROR_OK) + { + tdo_mismatch = 1; + free(ir_buf); + break; + } + if (xruntest) + { + if (runtest_requires_tck) + jtag_add_runtest(xruntest, xsvf_to_tap[xendir]); + else + { + jtag_add_statemove(TAP_RTI); + jtag_add_sleep(xruntest); + jtag_add_statemove(xsvf_to_tap[xendir]); + } + } + else if (xendir != 0xd) /* Pause-IR */ + jtag_add_statemove(xsvf_to_tap[xendir]); + } + free(ir_buf); + } + break; + case 0x03: /* XSDR */ + DEBUG("XSDR"); + if (xsvf_read_buffer(xsdrsize, xsvf_fd, dr_out_buf) != ERROR_OK) + do_abort = 1; + else + { + scan_field_t field; + field.device = device; + field.num_bits = xsdrsize; + field.out_value = dr_out_buf; + field.out_mask = NULL; + field.in_value = NULL; + field.in_check_value = dr_in_buf; + field.in_check_mask = dr_in_mask; + field.in_handler = NULL; + field.in_handler_priv = NULL; + if (device == -1) + jtag_add_plain_dr_scan(1, &field, TAP_PD); + else + jtag_add_dr_scan(1, &field, TAP_PD); + if (jtag_execute_queue() != ERROR_OK) + { + tdo_mismatch = 1; + break; + } + if (xruntest) + { + if (runtest_requires_tck) + jtag_add_runtest(xruntest, xsvf_to_tap[xenddr]); + else + { + jtag_add_statemove(TAP_RTI); + jtag_add_sleep(xruntest); + jtag_add_statemove(xsvf_to_tap[xenddr]); + } + } + else if (xendir != 0x6) /* Pause-DR */ + jtag_add_statemove(xsvf_to_tap[xenddr]); + } + break; + case 0x04: /* XRUNTEST */ + DEBUG("XRUNTEST"); + if (read(xsvf_fd, &ui, 4) < 0) + do_abort = 1; + else + { + xruntest = ntohl(ui); + } + break; + case 0x07: /* XREPEAT */ + DEBUG("XREPEAT"); + if (read(xsvf_fd, &c, 1) < 0) + do_abort = 1; + else + { + xrepeat = c; + } + break; + case 0x08: /* XSDRSIZE */ + DEBUG("XSDRSIZE"); + if (read(xsvf_fd, &ui, 4) < 0) + do_abort = 1; + else + { + xsdrsize = ntohl(ui); + free(dr_out_buf); + free(dr_in_buf); + free(dr_in_mask); + dr_out_buf = malloc((xsdrsize + 7) / 8); + dr_in_buf = malloc((xsdrsize + 7) / 8); + dr_in_mask = malloc((xsdrsize + 7) / 8); + } + break; + case 0x09: /* XSDRTDO */ + DEBUG("XSDRTDO"); + if (xsvf_read_buffer(xsdrsize, xsvf_fd, dr_out_buf) != ERROR_OK) + do_abort = 1; + else + { + if (xsvf_read_buffer(xsdrsize, xsvf_fd, dr_in_buf) != ERROR_OK) + do_abort = 1; + else + { + scan_field_t field; + field.device = device; + field.num_bits = xsdrsize; + field.out_value = dr_out_buf; + field.out_mask = NULL; + field.in_value = NULL; + field.in_check_value = dr_in_buf; + field.in_check_mask = dr_in_mask; + field.in_handler = NULL; + field.in_handler_priv = NULL; + if (device == -1) + jtag_add_plain_dr_scan(1, &field, TAP_PD); + else + jtag_add_dr_scan(1, &field, TAP_PD); + if (jtag_execute_queue() != ERROR_OK) + { + tdo_mismatch = 1; + break; + } + if (xruntest) + { + if (runtest_requires_tck) + jtag_add_runtest(xruntest, xsvf_to_tap[xenddr]); + else + { + jtag_add_statemove(TAP_RTI); + jtag_add_sleep(xruntest); + jtag_add_statemove(xsvf_to_tap[xenddr]); + } + } + else if (xendir != 0x6) /* Pause-DR */ + jtag_add_statemove(xsvf_to_tap[xenddr]); + } + } + break; + case 0x0a: /* XSETDRMASKS */ + printf("unsupported XSETSDRMASKS\n"); + unsupported = 1; + break; + case 0x0b: /* XSDRINC */ + printf("unsupported XSDRINC\n"); + unsupported = 1; + break; + case 0x0c: /* XSDRB */ + unsupported = 1; + break; + case 0x0d: /* XSDRC */ + unsupported = 1; + break; + case 0x0e: /* XSDRE */ + unsupported = 1; + break; + case 0x0f: /* XSDRTDOB */ + unsupported = 1; + break; + case 0x10: /* XSDRTDOB */ + unsupported = 1; + break; + case 0x11: /* XSDRTDOB */ + unsupported = 1; + break; + case 0x12: /* XSTATE */ + DEBUG("XSTATE"); + if (read(xsvf_fd, &uc, 1) < 0) + do_abort = 1; + else + { + enum tap_state *path = calloc(XSTATE_MAX_PATH, 4); + int path_len = 1; + path[0] = xsvf_to_tap[uc]; + if (xsvf_read_xstates(xsvf_fd, path, XSTATE_MAX_PATH, &path_len) != ERROR_OK) + do_abort = 1; + else + { + jtag_add_pathmove(path_len, path); + } + free(path); + } + break; + case 0x13: /* XENDIR */ + DEBUG("XENDIR"); + if (read(xsvf_fd, &c, 1) < 0) + do_abort = 1; + else + { + if (c == 0) + xendir = 1; + else if (c == 1) + xendir = 0xd; + else + { + ERROR("unknown XENDIR endstate"); + unsupported = 1; + } + } + break; + case 0x14: /* XENDDR */ + DEBUG("XENDDR"); + if (read(xsvf_fd, &c, 1) < 0) + do_abort = 1; + else + { + if (c == 0) + xenddr = 1; + else if (c == 1) + xenddr = 0x6; + else + { + ERROR("unknown XENDDR endstate"); + unsupported = 1; + } + } + break; + case 0x15: /* XSIR2 */ + DEBUG("XSIR2"); + if (read(xsvf_fd, &us, 2) < 0) + do_abort = 1; + else + { + u8 *ir_buf; + us = ntohs(us); + ir_buf = malloc((us + 7) / 8); + if (xsvf_read_buffer(us, xsvf_fd, ir_buf) != ERROR_OK) + do_abort = 1; + else + { + scan_field_t field; + field.device = device; + field.num_bits = us; + field.out_value = ir_buf; + field.out_mask = NULL; + field.in_value = NULL; + field.in_check_value = NULL; + field.in_check_mask = NULL; + field.in_handler = NULL; + field.in_handler_priv = NULL; + if (device == -1) + jtag_add_plain_ir_scan(1, &field, xsvf_to_tap[xendir]); + else + jtag_add_ir_scan(1, &field, xsvf_to_tap[xendir]); + } + free(ir_buf); + } + break; + case 0x16: /* XCOMMENT */ + do + { + if (read(xsvf_fd, &c, 1) < 0) + { + do_abort = 1; + break; + } + } while (c != 0); + break; + case 0x17: /* XWAIT */ + DEBUG("XWAIT"); + if ((read(xsvf_fd, &uc, 1) < 0) || (read(xsvf_fd, &uc2, 1) < 0) || (read(xsvf_fd, &ui, 4) < 0)) + do_abort = 1; + else + { + jtag_add_statemove(xsvf_to_tap[uc]); + ui = ntohl(ui); + jtag_add_sleep(ui); + jtag_add_statemove(xsvf_to_tap[uc2]); + } + break; + default: + printf("unknown xsvf command (0x%2.2x)\n", c); + unsupported = 1; + } + + if (do_abort || unsupported || tdo_mismatch) + break; + } + + if (tdo_mismatch) + { + command_print(cmd_ctx, "TDO mismatch, aborting"); + jtag_cancel_queue(); + return ERROR_OK; + } + + if (unsupported) + { + command_print(cmd_ctx, "unsupported xsvf command encountered, aborting"); + jtag_cancel_queue(); + return ERROR_OK; + } + + if (do_abort) + { + command_print(cmd_ctx, "premature end detected, aborting"); + jtag_cancel_queue(); + return ERROR_OK; + } + + if (dr_out_buf) + free(dr_out_buf); + + if (dr_in_buf) + free(dr_in_buf); + + if (dr_in_mask) + free(dr_in_mask); + + close(xsvf_fd); + + command_print(cmd_ctx, "XSVF file programmed successfully"); + + return ERROR_OK; +} diff --git a/src/xsvf/xsvf.h b/src/xsvf/xsvf.h new file mode 100644 index 00000000..017af882 --- /dev/null +++ b/src/xsvf/xsvf.h @@ -0,0 +1,30 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef XSVF_H +#define XSVF_H + +#include "command.h" + +extern int xsvf_register_commands(struct command_context_s *cmd_ctx); + +#define ERROR_XSVF_EOF (-200) +#define ERROR_XSVF_FAILED (-201) + +#endif /* XSVF_H */ |