From 8b4e882a1630d63bbc9840fa3f968e36b6ac3702 Mon Sep 17 00:00:00 2001 From: drath Date: Fri, 2 Jun 2006 10:36:31 +0000 Subject: [PATCH] - prepare OpenOCD for branching, created ./trunk/ git-svn-id: svn://svn.berlios.de/openocd/trunk@64 b42882b7-edfa-0310-969c-e2dbd0fdcd60 --- AUTHORS | 1 + COPYING | 340 +++++ ChangeLog | 7 + INSTALL | 194 +++ Makefile.am | 5 + NEWS | 0 README | 49 + TODO | 7 + bootstrap | 4 + configure.in | 112 ++ doc/configs/arm7_ft2232.cfg | 26 + doc/configs/arm7_ftd2xx.cfg | 29 + doc/configs/arm7_wig.cfg | 28 + doc/configs/arm9_ftd2xx.cfg | 28 + doc/configs/chameleon.cfg | 12 + src/Makefile.am | 40 + src/flash/Makefile.am | 5 + src/flash/at91sam7.c | 632 ++++++++++ src/flash/at91sam7.h | 83 ++ src/flash/cfi.c | 1194 ++++++++++++++++++ src/flash/cfi.h | 86 ++ src/flash/flash.c | 556 +++++++++ src/flash/flash.h | 76 ++ src/flash/lpc2000.c | 685 ++++++++++ src/flash/lpc2000.h | 54 + src/flash/str7x.c | 469 +++++++ src/flash/str7x.h | 106 ++ src/helper/Makefile.am | 6 + src/helper/binarybuffer.c | 246 ++++ src/helper/binarybuffer.h | 48 + src/helper/command.c | 508 ++++++++ src/helper/command.h | 67 + src/helper/configuration.c | 131 ++ src/helper/configuration.h | 31 + src/helper/interpreter.c | 237 ++++ src/helper/interpreter.h | 48 + src/helper/log.c | 134 ++ src/helper/log.h | 96 ++ src/helper/time_support.c | 82 ++ src/helper/time_support.h | 30 + src/helper/types.h | 36 + src/jtag/Makefile.am | 50 + src/jtag/amt_jtagaccel.c | 510 ++++++++ src/jtag/bitbang.c | 219 ++++ src/jtag/bitbang.h | 36 + src/jtag/ep93xx.c | 236 ++++ src/jtag/ftd2xx.c | 1004 +++++++++++++++ src/jtag/ftdi2232.c | 630 ++++++++++ src/jtag/jtag.c | 1585 ++++++++++++++++++++++++ src/jtag/jtag.h | 270 ++++ src/jtag/parport.c | 351 ++++++ src/openocd.c | 113 ++ src/server/Makefile.am | 5 + src/server/gdb_server.c | 1108 +++++++++++++++++ src/server/gdb_server.h | 47 + src/server/server.c | 368 ++++++ src/server/server.h | 75 ++ src/server/telnet_server.c | 570 +++++++++ src/server/telnet_server.h | 68 + src/target/Makefile.am | 7 + src/target/algorithm.c | 54 + src/target/algorithm.h | 53 + src/target/arm720t.c | 625 ++++++++++ src/target/arm720t.h | 43 + src/target/arm7_9_common.c | 2339 +++++++++++++++++++++++++++++++++++ src/target/arm7_9_common.h | 129 ++ src/target/arm7tdmi.c | 780 ++++++++++++ src/target/arm7tdmi.h | 46 + src/target/arm920t.c | 967 +++++++++++++++ src/target/arm920t.h | 45 + src/target/arm9tdmi.c | 848 +++++++++++++ src/target/arm9tdmi.h | 51 + src/target/arm_jtag.c | 116 ++ src/target/arm_jtag.h | 42 + src/target/armv4_5.c | 583 +++++++++ src/target/armv4_5.h | 237 ++++ src/target/armv4_5_cache.c | 112 ++ src/target/armv4_5_cache.h | 49 + src/target/armv4_5_mmu.c | 358 ++++++ src/target/armv4_5_mmu.h | 52 + src/target/breakpoints.c | 219 ++++ src/target/breakpoints.h | 70 ++ src/target/embeddedice.c | 301 +++++ src/target/embeddedice.h | 90 ++ src/target/etm.c | 409 ++++++ src/target/etm.h | 76 ++ src/target/register.c | 100 ++ src/target/register.h | 69 ++ src/target/target.c | 1701 +++++++++++++++++++++++++ src/target/target.h | 231 ++++ src/xsvf/Makefile.am | 5 + src/xsvf/xsvf.c | 506 ++++++++ src/xsvf/xsvf.h | 30 + 93 files changed, 25116 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 TODO create mode 100755 bootstrap create mode 100644 configure.in create mode 100644 doc/configs/arm7_ft2232.cfg create mode 100644 doc/configs/arm7_ftd2xx.cfg create mode 100644 doc/configs/arm7_wig.cfg create mode 100644 doc/configs/arm9_ftd2xx.cfg create mode 100644 doc/configs/chameleon.cfg create mode 100644 src/Makefile.am create mode 100644 src/flash/Makefile.am create mode 100644 src/flash/at91sam7.c create mode 100644 src/flash/at91sam7.h create mode 100644 src/flash/cfi.c create mode 100644 src/flash/cfi.h create mode 100644 src/flash/flash.c create mode 100644 src/flash/flash.h create mode 100644 src/flash/lpc2000.c create mode 100644 src/flash/lpc2000.h create mode 100644 src/flash/str7x.c create mode 100644 src/flash/str7x.h create mode 100644 src/helper/Makefile.am create mode 100644 src/helper/binarybuffer.c create mode 100644 src/helper/binarybuffer.h create mode 100644 src/helper/command.c create mode 100644 src/helper/command.h create mode 100644 src/helper/configuration.c create mode 100644 src/helper/configuration.h create mode 100644 src/helper/interpreter.c create mode 100644 src/helper/interpreter.h create mode 100644 src/helper/log.c create mode 100644 src/helper/log.h create mode 100644 src/helper/time_support.c create mode 100644 src/helper/time_support.h create mode 100644 src/helper/types.h create mode 100644 src/jtag/Makefile.am create mode 100644 src/jtag/amt_jtagaccel.c create mode 100644 src/jtag/bitbang.c create mode 100644 src/jtag/bitbang.h create mode 100644 src/jtag/ep93xx.c create mode 100644 src/jtag/ftd2xx.c create mode 100644 src/jtag/ftdi2232.c create mode 100644 src/jtag/jtag.c create mode 100644 src/jtag/jtag.h create mode 100644 src/jtag/parport.c create mode 100644 src/openocd.c create mode 100644 src/server/Makefile.am create mode 100644 src/server/gdb_server.c create mode 100644 src/server/gdb_server.h create mode 100644 src/server/server.c create mode 100644 src/server/server.h create mode 100644 src/server/telnet_server.c create mode 100644 src/server/telnet_server.h create mode 100644 src/target/Makefile.am create mode 100644 src/target/algorithm.c create mode 100644 src/target/algorithm.h create mode 100644 src/target/arm720t.c create mode 100644 src/target/arm720t.h create mode 100644 src/target/arm7_9_common.c create mode 100644 src/target/arm7_9_common.h create mode 100644 src/target/arm7tdmi.c create mode 100644 src/target/arm7tdmi.h create mode 100644 src/target/arm920t.c create mode 100644 src/target/arm920t.h create mode 100644 src/target/arm9tdmi.c create mode 100644 src/target/arm9tdmi.h create mode 100644 src/target/arm_jtag.c create mode 100644 src/target/arm_jtag.h create mode 100644 src/target/armv4_5.c create mode 100644 src/target/armv4_5.h create mode 100644 src/target/armv4_5_cache.c create mode 100644 src/target/armv4_5_cache.h create mode 100644 src/target/armv4_5_mmu.c create mode 100644 src/target/armv4_5_mmu.h create mode 100644 src/target/breakpoints.c create mode 100644 src/target/breakpoints.h create mode 100644 src/target/embeddedice.c create mode 100644 src/target/embeddedice.h create mode 100644 src/target/etm.c create mode 100644 src/target/etm.h create mode 100644 src/target/register.c create mode 100644 src/target/register.h create mode 100644 src/target/target.c create mode 100644 src/target/target.h create mode 100644 src/xsvf/Makefile.am create mode 100644 src/xsvf/xsvf.c create mode 100644 src/xsvf/xsvf.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000000..79755bb88a --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Dominic Rath diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000..5b6e7c66c2 --- /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. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 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. + + , 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 0000000000..66c2887f30 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,7 @@ +2005-07-03 Dominic Rath + + * First public release + +2005-10-27 Dominic Rath + + * First release of new codebase diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000000..f2008737e9 --- /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 0000000000..02520f201e --- /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 diff --git a/NEWS b/NEWS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/README b/README new file mode 100644 index 0000000000..30445f1b90 --- /dev/null +++ b/README @@ -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. + diff --git a/TODO b/TODO new file mode 100644 index 0000000000..335b7c6106 --- /dev/null +++ b/TODO @@ -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 0000000000..f7c4c0facd --- /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 0000000000..db378068d5 --- /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 ])], + [], + 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 0000000000..369d09b112 --- /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 +#target arm7tdmi +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 0000000000..59dad02f27 --- /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 +#target arm7tdmi +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 0000000000..c1e6bf9e31 --- /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 +#target arm7tdmi +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 0000000000..84885dd294 --- /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 +target arm9tdmi little reset_halt 0 arm920t +working_area 0 0x200000 0x4000 backup +run_and_halt_time 0 5000 + +#flash configuration +#flash bank [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 0000000000..94d581c6f1 --- /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 0000000000..e197382731 --- /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 0000000000..61e363c922 --- /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 0000000000..8a602a3f67 --- /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 +#include +#include + +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; pagentype->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 0000000000..8f9e3db760 --- /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 0000000000..5e943676d1 --- /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 +#include +#include + +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 "); + */ + return ERROR_OK; +} + +/* flash_bank cfi + */ +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 0000000000..d9700be2d4 --- /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 0000000000..a5067cd225 --- /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 +#include +#include +#include +#include +#include + +/* 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 "); + register_command(cmd_ctx, flash_cmd, "probe", handle_flash_probe_command, COMMAND_EXEC, + "identify flash bank "); + register_command(cmd_ctx, flash_cmd, "erase_check", handle_flash_erase_check_command, COMMAND_EXEC, + "check erase state of sectors in flash bank "); + register_command(cmd_ctx, flash_cmd, "protect_check", handle_flash_protect_check_command, COMMAND_EXEC, + "check protection state of sectors in flash bank "); + register_command(cmd_ctx, flash_cmd, "erase", handle_flash_erase_command, COMMAND_EXEC, + "erase sectors at "); + register_command(cmd_ctx, flash_cmd, "write", handle_flash_write_command, COMMAND_EXEC, + "write binary "); + register_command(cmd_ctx, flash_cmd, "protect", handle_flash_protect_command, COMMAND_EXEC, + "set protection of sectors at "); + } + + 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_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 "); + 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 "); + 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 "); + 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 "); + 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 "); + 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 "); + } + + 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 "); + 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 "); + 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 "); + } + + 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 "); + 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 "); + 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 0000000000..a8cc18691b --- /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 0000000000..b6fcb30b6b --- /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 +#include + +/* 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 "); + + 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 0 0 [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 "); + 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 0000000000..dbbe4b6adc --- /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 0000000000..2e3a6c8ca2 --- /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 +#include +#include + +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 0 0 + */ +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 0000000000..fe63b5e5e6 --- /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 0000000000..5fb2241d13 --- /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 0000000000..357d05c377 --- /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 +#include + +#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> (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>(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 0000000000..0a68894500 --- /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 0000000000..26eada62e7 --- /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 +#include +#include +#include +#include +#include + +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 milliseconds"); + + return context; +} + +/* sleep command sleeps for 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 0000000000..262786a82a --- /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 + +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 0000000000..e59ca6d1fb --- /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 +#endif + +#include "types.h" +#include "command.h" +#include "configuration.h" +#include "log.h" + +#include +#include +#include + +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 \n"); + printf("--debug | -d\tset debug level <0-3>\n"); + printf("--log_output | -l\tredirect log output to file \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 0000000000..cd96e2f753 --- /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 0000000000..7e88263b7f --- /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 +#include + +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 [num_fields|'del'] [size1] ..."); + register_command(cmd_ctx, NULL, "field", handle_field_command, + COMMAND_ANY, "display/modify variable field [value|'flip']"); + register_command(cmd_ctx, NULL, "script", handle_script_command, + COMMAND_ANY, "execute commands from "); + + 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 [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 "); + + 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 0000000000..93e8d39da7 --- /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 0000000000..60ba80bdc5 --- /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 +#include +#include +#include + +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 (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 0000000000..c495524cb8 --- /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 + +/* 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 0000000000..5a7869d92e --- /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 +#include + +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 0000000000..d8b7fe5ca6 --- /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 +#include + +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 0000000000..6d49bbb0ea --- /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 0000000000..a3a0660624 --- /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 0000000000..42f8bc3616 --- /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 +#include +#include + +#include +#include + +#if PARPORT_USE_PPDEV == 1 +#include +#include +#include +#include +#include +#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 0000000000..d6ff289830 --- /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 +#include +#include + +#include +#include + +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 0000000000..7049f4353d --- /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 0000000000..9c24dba447 --- /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 +#include +#include +#include +#include +#include +#include + +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 0000000000..c73c8d58ef --- /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 +#include +#include +#include + +#include +#include + +/* 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 "); + } + + 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 0000000000..efd528c3d2 --- /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 +#include +#include +#include +#include + +#include +#include + +/* 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 0000000000..e306b0a27b --- /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 + +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; icur_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; inum_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) "); + 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 "); + register_command(cmd_ctx, NULL, "jtag_reset", handle_jtag_reset_command, + COMMAND_EXEC, "toggle reset lines "); + register_command(cmd_ctx, NULL, "runtest", handle_runtest_command, + COMMAND_EXEC, "move to Run-Test/Idle, and execute "); + 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 [dev2] [instr2] ..."); + register_command(cmd_ctx, NULL, "drscan", handle_drscan_command, + COMMAND_EXEC, "execute DR scan [dev2] [var2] ..."); + + register_command(cmd_ctx, NULL, "verify_ircapture", handle_verify_ircapture_command, + COMMAND_ANY, "verify value captured during Capture-IR "); + 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 "); + 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 "; + 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 "); + 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 [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 [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 0000000000..124150ce7f --- /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 0000000000..8265ada8ad --- /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 +#include +#include +#define ioperm(startport,length,enable)\ + i386_set_ioperm((startport), (length), (enable)) +#else +#include +#endif + +#include +#include +#include + +#if PARPORT_USE_PPDEV == 1 +#include +#include +#include +#include +#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 0000000000..71b111a030 --- /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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 0000000000..2397a7f07c --- /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 0000000000..125206dcb3 --- /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 +#include +#include +#include + +// -ino: 060521-1116 +#ifdef __FreeBSD__ +#include +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; ipriv; + 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; itype->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 0000000000..860b29ca4a --- /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 0000000000..a034da7928 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 0000000000..625c364eaa --- /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 +#include +#include + +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 0000000000..d7933293ae --- /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 +#include +#include +#include +#include + +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 0000000000..d6ca0e86cb --- /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 0000000000..7e26762b4b --- /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 0000000000..fdebfc58b6 --- /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 + + +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 0000000000..e248ba5a88 --- /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 0000000000..68ea3d1ce3 --- /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 +#include + +#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 [value]"); + register_command(cmd_ctx, arm720t_cmd, "virt2phys", arm720t_handle_virt2phys_command, COMMAND_EXEC, "translate va to pa "); + + register_command(cmd_ctx, arm720t_cmd, "mdw_phys", arm720t_handle_md_phys_command, COMMAND_EXEC, "display memory words [count]"); + register_command(cmd_ctx, arm720t_cmd, "mdh_phys", arm720t_handle_md_phys_command, COMMAND_EXEC, "display memory half-words [count]"); + register_command(cmd_ctx, arm720t_cmd, "mdb_phys", arm720t_handle_md_phys_command, COMMAND_EXEC, "display memory bytes [count]"); + + register_command(cmd_ctx, arm720t_cmd, "mww_phys", arm720t_handle_mw_phys_command, COMMAND_EXEC, "write memory word "); + register_command(cmd_ctx, arm720t_cmd, "mwh_phys", arm720t_handle_mw_phys_command, COMMAND_EXEC, "write memory half-word "); + register_command(cmd_ctx, arm720t_cmd, "mwb_phys", arm720t_handle_mw_phys_command, COMMAND_EXEC, "write memory byte "); + + 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 0000000000..2479b5485e --- /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 0000000000..d167041f82 --- /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 +#include +#include + +#include +#include +#include +#include + +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
*/ + 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
*/ + 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 "); + 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> "); + + register_command(cmd_ctx, arm7_9_cmd, "write_core_reg", handle_arm7_9_write_core_reg_command, COMMAND_EXEC, "write core register "); + + register_command(cmd_ctx, arm7_9_cmd, "sw_bkpts", handle_arm7_9_sw_bkpts_command, COMMAND_EXEC, "support for software breakpoints "); + 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) "); + 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 "); + 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 "); + register_command(cmd_ctx, arm7_9_cmd, "dcc_downloads", handle_arm7_9_dcc_downloads_command, + COMMAND_ANY, "use DCC downloads for larger memory writes "); + + 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 "); + 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 "); + 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 "); + 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 "); + } + + 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 "); + } + + 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 "); + } + } + + 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 "); + } + } + + 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 "); + } + } + + 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 0000000000..f03ae49638 --- /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 0000000000..c207947790 --- /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 +#include + +#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 */ +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 0000000000..ca2df8b69e --- /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 0000000000..eb0fa7df68 --- /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 +#include + +#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 [value]"); + register_command(cmd_ctx, arm920t_cmd, "cp15i", arm920t_handle_cp15i_command, COMMAND_EXEC, "display/modify cp15 (interpreted access) [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 "); + + register_command(cmd_ctx, arm920t_cmd, "mdw_phys", arm920t_handle_md_phys_command, COMMAND_EXEC, "display memory words [count]"); + register_command(cmd_ctx, arm920t_cmd, "mdh_phys", arm920t_handle_md_phys_command, COMMAND_EXEC, "display memory half-words [count]"); + register_command(cmd_ctx, arm920t_cmd, "mdb_phys", arm920t_handle_md_phys_command, COMMAND_EXEC, "display memory bytes [count]"); + + register_command(cmd_ctx, arm920t_cmd, "mww_phys", arm920t_handle_mw_phys_command, COMMAND_EXEC, "write memory word "); + register_command(cmd_ctx, arm920t_cmd, "mwh_phys", arm920t_handle_mw_phys_command, COMMAND_EXEC, "write memory half-word "); + register_command(cmd_ctx, arm920t_cmd, "mwb_phys", arm920t_handle_mw_phys_command, COMMAND_EXEC, "write memory byte "); + + 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 0000000000..bedf31f216 --- /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 0000000000..48b201a11c --- /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 +#include + +#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 */ +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 0000000000..7bbaac7c9b --- /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 0000000000..c401d8f358 --- /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 + +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 0000000000..a9f90846a7 --- /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 0000000000..51fd4b4273 --- /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 +#include +#include + +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 "); + + 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 0000000000..a28bfa1276 --- /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 0000000000..326f10ee82 --- /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 0000000000..766718b6a7 --- /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 0000000000..c7dad3e279 --- /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 + +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 "); + 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 0000000000..3adb30e22c --- /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 0000000000..efba25c9d0 --- /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 + +#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 0000000000..7eba39aa3c --- /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 0000000000..e148b88872 --- /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 + +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 0000000000..0062153f15 --- /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 0000000000..a84e2d67cd --- /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 + +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 0000000000..dbe78f35ed --- /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 0000000000..7b98cfcf83 --- /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 +#include + +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 0000000000..8a903eddf2 --- /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 0000000000..98407afb79 --- /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 +#include + +#include +#include +#include +#include + +#include +#include + +#include + +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 [count]"); + register_command(cmd_ctx, NULL, "mdh", handle_md_command, COMMAND_EXEC, "display memory half-words [count]"); + register_command(cmd_ctx, NULL, "mdb", handle_md_command, COMMAND_EXEC, "display memory bytes [count]"); + + register_command(cmd_ctx, NULL, "mww", handle_mw_command, COMMAND_EXEC, "write memory word "); + register_command(cmd_ctx, NULL, "mwh", handle_mw_command, COMMAND_EXEC, "write memory half-word "); + register_command(cmd_ctx, NULL, "mwb", handle_mw_command, COMMAND_EXEC, "write memory byte "); + + register_command(cmd_ctx, NULL, "bp", handle_bp_command, COMMAND_EXEC, "set breakpoint
[hw]"); + register_command(cmd_ctx, NULL, "rbp", handle_rbp_command, COMMAND_EXEC, "remove breakpoint "); + register_command(cmd_ctx, NULL, "wp", handle_wp_command, COMMAND_EXEC, "set watchpoint
[value] [mask]"); + register_command(cmd_ctx, NULL, "rwp", handle_rwp_command, COMMAND_EXEC, "remove watchpoint "); + + register_command(cmd_ctx, NULL, "load_binary", handle_load_binary_command, COMMAND_EXEC, "load binary
"); + register_command(cmd_ctx, NULL, "dump_binary", handle_dump_binary_command, COMMAND_EXEC, "dump binary
"); + + 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: "); + 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 */ +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
<'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 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
"); + 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
"); + 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
[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
[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 0000000000..6d3b6d172e --- /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 +#include + +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 + */ + 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 0000000000..d9e808755a --- /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 0000000000..013803f6a3 --- /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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 "); + + 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 "); + 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 0000000000..017af882ed --- /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 */ -- 2.30.2