From: Jerry Jacobs Date: Thu, 5 May 2016 12:46:42 +0000 (+0200) Subject: Reorganize files: X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=e454fe98191095333258cfcfa813585086f669ed;p=fw%2Fstlink Reorganize files: * Header files moved into include/stlink * doc/tutorial.{pdf/tex} converted to markdown for rendering by github and easy editing * Remove `stlink-` suffix from some C source files and move into `src/tools` --- diff --git a/49-stlinkv1.rules b/49-stlinkv1.rules deleted file mode 100644 index d474d6a..0000000 --- a/49-stlinkv1.rules +++ /dev/null @@ -1,11 +0,0 @@ -# stm32 discovery boards, with onboard st/linkv1 -# ie, STM32VL - -SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3744", \ - MODE:="0666", \ - SYMLINK+="stlinkv1_%n" - -# If you share your linux system with other users, or just don't like the -# idea of write permission for everybody, you can replace MODE:="0666" with -# OWNER:="yourusername" to create the device owned by you, or with -# GROUP:="somegroupname" and mange access using standard unix groups. diff --git a/49-stlinkv2-1.rules b/49-stlinkv2-1.rules deleted file mode 100644 index a5a79b9..0000000 --- a/49-stlinkv2-1.rules +++ /dev/null @@ -1,12 +0,0 @@ -# stm32 nucleo boards, with onboard st/linkv2-1 -# ie, STM32F0, STM32F4. -# STM32VL has st/linkv1, which is quite different - -SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", \ - MODE:="0666", \ - SYMLINK+="stlinkv2-1_%n" - -# If you share your linux system with other users, or just don't like the -# idea of write permission for everybody, you can replace MODE:="0666" with -# OWNER:="yourusername" to create the device owned by you, or with -# GROUP:="somegroupname" and mange access using standard unix groups. diff --git a/49-stlinkv2.rules b/49-stlinkv2.rules deleted file mode 100644 index a11215c..0000000 --- a/49-stlinkv2.rules +++ /dev/null @@ -1,12 +0,0 @@ -# stm32 discovery boards, with onboard st/linkv2 -# ie, STM32L, STM32F4. -# STM32VL has st/linkv1, which is quite different - -SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", \ - MODE:="0666", \ - SYMLINK+="stlinkv2_%n" - -# If you share your linux system with other users, or just don't like the -# idea of write permission for everybody, you can replace MODE:="0666" with -# OWNER:="yourusername" to create the device owned by you, or with -# GROUP:="somegroupname" and mange access using standard unix groups. diff --git a/CMakeLists.txt b/CMakeLists.txt index d26f03c..f75ce61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,37 +47,38 @@ elseif() add_cflag_if_supported("-O2") endif() -set(HFILES src/stlink-common.h - src/stlink-usb.h - src/stlink-sg.h - src/uglylogging.h - src/mmap.h) - -set(CFILES src/stlink-common.c - src/stlink-usb.c - src/stlink-sg.c - src/uglylogging.c - ) +set(STLINK_HEADERS include/stlink.h + include/stlink/usb.h + include/stlink/sg.h + include/stlink/logging.h + include/stlink/mmap.h +) + +set(STLINK_SOURCE src/common.c + src/usb.c + src/sg.c + src/logging.c +) include_directories(${LIBUSB_INCLUDE_DIR}) -include_directories(src) -include_directories(mingw) +include_directories(include) +include_directories(src/mingw) add_library(${PROJECT_NAME} STATIC - ${HFILES} # header files for ide projects generated by cmake - ${CFILES}) + ${STLINK_HEADERS} # header files for ide projects generated by cmake + ${STLINK_SOURCE}) target_link_libraries(${PROJECT_NAME} ${LIBUSB_LIBRARIES}) -add_executable(st-flash flash/main.c) +add_executable(st-flash src/tools/flash.c) target_link_libraries(st-flash ${PROJECT_NAME}) -add_executable(st-info src/st-info.c) +add_executable(st-info src/tools/info.c) target_link_libraries(st-info ${PROJECT_NAME}) -add_executable(st-util gdbserver/gdb-remote.c - gdbserver/gdb-remote.h - gdbserver/gdb-server.c - gdbserver/gdb-server.h) +add_executable(st-util src/gdbserver/gdb-remote.c + src/gdbserver/gdb-remote.h + src/gdbserver/gdb-server.c + src/gdbserver/gdb-server.h) target_link_libraries(st-util ${PROJECT_NAME}) install(TARGETS ${PROJECT_NAME} st-flash st-util st-info @@ -86,8 +87,8 @@ install(TARGETS ${PROJECT_NAME} st-flash st-util st-info ) if(NOT MINGW) - list(APPEND CFILES src/st-term.c) - add_executable(st-term src/st-term.c) + list(APPEND STLINK_SOURCE src/tools/term.c) + add_executable(st-term src/tools/term.c) target_link_libraries(st-term ${PROJECT_NAME}) install(TARGETS st-term diff --git a/Makefile.am b/Makefile.am index 9afbc0f..fc95184 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,43 +12,43 @@ endif noinst_LIBRARIES = libstlink.a -st_flash_SOURCES = flash/main.c -st_term_SOURCES = src/st-term.c -st_info_SOURCES = src/st-info.c -st_util_SOURCES = gdbserver/gdb-remote.c gdbserver/gdb-remote.h gdbserver/gdb-server.c mingw/mingw.c mingw/mingw.h +st_flash_SOURCES = src/tools/flash.c +st_term_SOURCES = src/tools/term.c +st_info_SOURCES = src/tools/info.c +st_util_SOURCES = src/gdbserver/gdb-remote.c src/gdbserver/gdb-remote.h src/gdbserver/gdb-server.c src/mingw/mingw.c src/mingw/mingw.h CFILES = \ - src/stlink-common.c \ - src/stlink-usb.c \ - src/stlink-sg.c \ - src/uglylogging.c + src/common.c \ + src/usb.c \ + src/sg.c \ + src/logging.c if !MINGW -CFILES += src/st-term.c +CFILES += src/tools/term.c endif HFILES = \ - src/stlink-common.h \ - src/stlink-usb.h \ - src/stlink-sg.h \ - src/uglylogging.h \ - src/mmap.h + include/stlink.h \ + include/stlink/usb.h \ + include/stlink/sg.h \ + include/stlink/logging.h \ + include/stlink/mmap.h libstlink_a_SOURCES = $(CFILES) $(HFILES) -libstlink_a_CPPFLAGS = -std=gnu99 -Wall -Wextra -O2 libstlink_a_LIBADD = $(LIBOBJS) +libstlink_a_CPPFLAGS = -std=gnu99 -Wall -Wextra -O2 -I$(top_srcdir)/include st_flash_LDADD = libstlink.a -st_flash_CPPFLAGS = -std=gnu99 -Wall -Wextra -O2 -I$(top_srcdir)/src -I$(top_srcdir)/mingw +st_flash_CPPFLAGS = -std=gnu99 -Wall -Wextra -O2 -I$(top_srcdir)/include -I$(top_srcdir)/src/mingw st_util_LDADD = libstlink.a -st_util_CPPFLAGS = -std=gnu99 -Wall -Wextra -O2 -I$(top_srcdir)/src -I$(top_srcdir)/mingw +st_util_CPPFLAGS = -std=gnu99 -Wall -Wextra -O2 -I$(top_srcdir)/include -I$(top_srcdir)/src/mingw st_term_LDADD = libstlink.a -st_term_CPPFLAGS = -std=gnu99 -Wall -Wextra -O2 -I$(top_srcdir)/src -I$(top_srcdir)/mingw +st_term_CPPFLAGS = -std=gnu99 -Wall -Wextra -O2 -I$(top_srcdir)/include -I$(top_srcdir)/src/mingw st_info_LDADD = libstlink.a -st_info_CPPFLAGS = -std=gnu99 -Wall -Wextra -O2 -I$(top_srcdir)/src -I$(top_srcdir)/mingw +st_info_CPPFLAGS = -std=gnu99 -Wall -Wextra -O2 -I$(top_srcdir)/include -I$(top_srcdir)/src/mingw EXTRA_DIST = autogen.sh diff --git a/README.md b/README.md deleted file mode 120000 index 100b938..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -README \ No newline at end of file diff --git a/TODO b/TODO deleted file mode 100644 index 2b0baf6..0000000 --- a/TODO +++ /dev/null @@ -1,11 +0,0 @@ -. flash tool - . improve flash writing, still use word fast write... too slow - -. documentation - . make README points to doc/tutorial - -. compile and test a realtime kernel, for instance: -http://www.chibios.org/dokuwiki/doku.php?id=chibios:articles:stm32l_discovery -svn checkout https://chibios.svn.sourceforge.net/svnroot/chibios/trunk ; -cd chibios/trunk/demos/ARMCM3-STM32L152-DISCOVERY ; -make ; diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..9c5059a --- /dev/null +++ b/TODO.md @@ -0,0 +1,11 @@ +# flash tool + +- improve flash writing, still use word fast write... too slow + +# documentation + +- make README points to doc/tutorial + +# testing + +- compile and test a realtime kernel (e.g ChibiOS) diff --git a/configure.ac b/configure.ac index 203cc6b..f374717 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.61) AC_INIT([stlink],[0.5.6],[davem@devkitpro.org]) -AC_CONFIG_SRCDIR([src/stlink-common.c]) +AC_CONFIG_SRCDIR([src/common.c]) AC_CONFIG_LIBOBJ_DIR([src]) AM_INIT_AUTOMAKE([1.10]) diff --git a/doc/tutorial.md b/doc/tutorial.md new file mode 100644 index 0000000..937e2da --- /dev/null +++ b/doc/tutorial.md @@ -0,0 +1,190 @@ +Using STM32 discovery kits with open source tools +======== + +This guide details the use of STMicroelectronics STM32 discovery kits in an open source environment. + +Installing a GNU toolchain +========================== + +Any toolchain supporting the cortex m3 should do. You can find the +necessary to install such a toolchain here: + +``` +https://github.com/esden/summon-arm-toolchain +``` + +Details for the installation are provided in the topmost `README` file. +This documentation assumes the toolchains is installed in a +`$TOOLCHAIN_PATH`. + +Installing STLINK +================= + +STLINK is open source software to program and debug ST’s STM32 Discovery +kits. Those kits have an onboard chip that translates USB commands sent +by the host PC into JTAG/SWD commands. This chip is called STLINK, (yes, +isn’t that confusing? suggest a better name!) and comes in 2 versions +(STLINK v1 and v2). From a software point of view, those versions differ +only in the transport layer used to communicate (v1 uses SCSI passthru +commands, while v2 uses raw USB). From a user point of view, they are +identical. + + +Before continuing, the following dependencies must be met: + +- libusb-1.0 +- pkg-config +- autotools + +STLINK should run on any system meeting the above constraints. + +The STLINK software source code is retrieved using: + +``` +$> git clone https://github.com/texane/stlink stlink.git +``` + +Everything can be built from the top directory: + +``` +$> cd stlink.git +$> ./autogen.sh +$> ./configure +$> make +``` + +It includes: + +- a communication library (stlink.git/libstlink.a), +- a GDB server (stlink.git/st-util), +- a flash manipulation tool (stlink.git/st-flash). + +Using the GDB server +==================== + + +This assumes you have got the libopencm3 project downloaded in `ocm3`. +The libopencm3 project has some good, reliable examples for each of the +Discovery boards. + +Even if you don’t plan on using libopencm3, the examples they provide +will help you verify that: + +- Your installed toolchain is capable of compiling for cortex M3/M4 + targets +- stlink is functional +- Your arm-none-eabi-gdb is functional +- Your board is functional + +A GDB server must be started to interact with the STM32. Depending on +the discovery kit you are using, you must run one of the 2 commands: + +``` +# STM32VL discovery kit (onboard ST-link) +$> ./st-util --stlinkv1 + +# STM32L or STM32F4 discovery kit (onboard ST-link/V2) +$> ./st-util + +# Full help for other options (listen port, version) +$> ./st-util --help +``` + +Then, GDB can be used to interact with the kit: + +``` +$> $TOOLCHAIN_PATH/bin/arm-none-eabi-gdb example_file.elf +``` + +From GDB, connect to the server using: + +``` +(gdb) target extended localhost:4242 +``` + +GDB has memory maps for as many chips as it knows about, and will load +your project into either flash or SRAM based on how the project was +linked. Linking projects to boot from SRAM is beyond the scope of this +document. + +Because of these built in memory maps, after specifying the .elf at the +command line, now we can simply “load” the target: + +``` +(gdb) load +``` + +st-util will load all sections into their appropriate addresses, and +“correctly” set the PC register. So, to run your freshly loaded program, +simply “continue” + +``` +(gdb) continue +``` + +Your program should now be running, and, if you used one of the blinking +examples from libopencm3, the LEDs on the board should be blinking for +you. + +Building and flashing a program +=============================== + +If you want to simply flash binary files to arbitrary sections of +memory, or read arbitary addresses of memory out to a binary file, use +the st-flash tool, as shown below: + +``` + +# stlinkv1 command to read 4096 from flash into out.bin +$> ./st-flash read v1 out.bin 0x8000000 4096 + +# stlinkv2 command +$> ./st-flash read out.bin 0x8000000 4096 + +# stlinkv1 command to write the file in.bin into flash +$> ./st-flash write v1 in.bin 0x8000000 + +# stlinkv2 command +$> ./st-flash write in.bin 0x8000000 +``` + +#### + +Of course, you can use this instead of the gdb server, if you prefer. +Just remember to use the “.bin” image, rather than the .elf file. + +``` + +# write blink.bin into FLASH +$> [sudo] ./st-flash write fancy_blink.bin 0x08000000 +``` + +Upon reset, the board LEDs should be blinking. + +Notes +===== + +Disassembling THUMB code in GDB +------------------------------- + +By default, the disassemble command in GDB operates in ARM mode. The +programs running on CORTEX-M3 are compiled in THUMB mode. To correctly +disassemble them under GDB, uses an odd address. For instance, if you +want to disassemble the code at 0x20000000, use:\ + +``` +(gdb) disassemble 0x20000001 +``` + +References +========== + +- + documentation related to the STM32L mcu + +- + documentation related to the STM32L discovery kit + +- + libopencm3, a project providing a firmware library, with solid + examples for Cortex M3, M4 and M0 processors from any vendor. diff --git a/doc/tutorial/tutorial.pdf b/doc/tutorial/tutorial.pdf deleted file mode 100644 index b73396c..0000000 Binary files a/doc/tutorial/tutorial.pdf and /dev/null differ diff --git a/doc/tutorial/tutorial.tex b/doc/tutorial/tutorial.tex deleted file mode 100644 index 5860ca8..0000000 --- a/doc/tutorial/tutorial.tex +++ /dev/null @@ -1,234 +0,0 @@ -\documentclass[a4paper, 11pt]{article} - -\usepackage{graphicx} -\usepackage{graphics} -\usepackage{verbatim} -\usepackage{listings} -\usepackage{color} - -\begin{document} - -\title{Using STM32 discovery kits with open source tools} -\author{STLINK development team} -\date{} - -\maketitle - -\newpage -\tableofcontents -\addtocontents{toc}{\protect\setcounter{tocdepth}{1}} - - -\newpage - -\section{Overview} -\paragraph{} -This guide details the use of STMicroelectronics STM32 discovery kits in -an open source environment. - - -\newpage - -\section{Installing a GNU toolchain} -\paragraph{} -Any toolchain supporting the cortex m3 should do. You can find the necessary -to install such a toolchain here:\\ -\begin{small} -\begin{lstlisting}[frame=tb] -https://github.com/esden/summon-arm-toolchain -\end{lstlisting} -\end{small} - -\paragraph{} -Details for the installation are provided in the topmost README file. -This documentation assumes the toolchains is installed in a \$TOOLCHAIN\_PATH. - - -\newpage - -\section{Installing STLINK} -\paragraph{} -STLINK is open source software to program and debug ST's STM32 Discovery kits. Those -kits have an onboard chip that translates USB commands sent by the host PC into -JTAG/SWD commands. This chip is called STLINK, (yes, isn't that confusing? suggest a better -name!) and comes in 2 versions (STLINK v1 and v2). From a software -point of view, those versions differ only in the transport layer used to communicate -(v1 uses SCSI passthru commands, while v2 uses raw USB). From a user point of view, they -are identical. - -\paragraph{} -Before continuing, the following dependencies must be met: -\begin{itemize} -\item libusb-1.0 -\item pkg-config -\item autotools -\end{itemize} - -\paragraph{} -STLINK should run on any system meeting the above constraints. - -\paragraph{} -The STLINK software source code is retrieved using:\\ -\begin{small} -\begin{lstlisting}[frame=tb] -$> git clone https://github.com/texane/stlink stlink.git -\end{lstlisting} -\end{small} - -\paragraph{} -Everything can be built from the top directory:\\ -\begin{small} -\begin{lstlisting}[frame=tb] -$> cd stlink.git -$> ./autogen.sh -$> ./configure -$> make -\end{lstlisting} -\end{small} -It includes: -\begin{itemize} -\item a communication library (stlink.git/libstlink.a), -\item a GDB server (stlink.git/st-util), -\item a flash manipulation tool (stlink.git/st-flash). -\end{itemize} - - -\newpage -\section{Using the GDB server} -\paragraph{} -This assumes you have got the libopencm3 project downloaded in [ocm3]. The -libopencm3 project has some good, reliable examples for each of the Discovery boards. - -Even if you don't plan on using libopencm3, the examples they provide will help you -verify that: - -\begin{itemize} -\item Your installed toolchain is capable of compiling for cortex M3/M4 targets -\item stlink is functional -\item Your arm-none-eabi-gdb is functional -\item Your board is functional -\end{itemize} - -\paragraph{} -A GDB server must be started to interact with the STM32. Depending on the discovery kit you -are using, you must run one of the 2 commands:\\ -\begin{small} -\begin{lstlisting}[frame=tb] -# STM32VL discovery kit (onboard ST-link) -$> ./st-util --stlinkv1 - -# STM32L or STM32F4 discovery kit (onboard ST-link/V2) -$> ./st-util - -# Full help for other options (listen port, version) -$> ./st-util --help -\end{lstlisting} -\end{small} - -\paragraph{} -Then, GDB can be used to interact with the kit:\\ -\begin{small} -\begin{lstlisting}[frame=tb] -$> $TOOLCHAIN_PATH/bin/arm-none-eabi-gdb example_file.elf -\end{lstlisting} -\end{small} - -\paragraph{} -From GDB, connect to the server using:\\ -\begin{small} -\begin{lstlisting}[frame=tb] -(gdb) target extended localhost:4242 -\end{lstlisting} -\end{small} - -\paragraph{} -GDB has memory maps for as many chips as it knows about, and will load your project -into either flash or SRAM based on how the project was linked. Linking projects -to boot from SRAM is beyond the scope of this document. - -Because of these built in memory maps, after specifying the .elf at the command line, now -we can simply "load" the target:\\ -\begin{small} -\begin{lstlisting}[frame=tb] -(gdb) load -\end{lstlisting} -\end{small} - -\paragraph{} -st-util will load all sections into their appropriate addresses, and "correctly" set the PC -register. So, to run your freshly loaded program, simply "continue"\\ -\begin{small} -\begin{lstlisting}[frame=tb] -(gdb) continue -\end{lstlisting} -\end{small} - -\paragraph{} -Your program should now be running, and, if you used one of the blinking examples from -libopencm3, the LEDs on the board should be blinking for you. - -\newpage -\section{Building and flashing a program} -\paragraph{} -If you want to simply flash binary files to arbitrary sections of memory, or -read arbitary addresses of memory out to a binary file, use the st-flash tool, -as shown below:\\ -\begin{small} -\begin{lstlisting}[frame=tb] - -# stlinkv1 command to read 4096 from flash into out.bin -$> ./st-flash read v1 out.bin 0x8000000 4096 - -# stlinkv2 command -$> ./st-flash read out.bin 0x8000000 4096 - -# stlinkv1 command to write the file in.bin into flash -$> ./st-flash write v1 in.bin 0x8000000 - -# stlinkv2 command -$> ./st-flash write in.bin 0x8000000 -\end{lstlisting} -\end{small} - -\paragraph{} -Of course, you can use this instead of the gdb server, if you prefer. Just remember -to use the ".bin" image, rather than the .elf file.\\ -\begin{small} -\begin{lstlisting}[frame=tb] - -# write blink.bin into FLASH -$> [sudo] ./st-flash write fancy_blink.bin 0x08000000 -\end{lstlisting} -\end{small} - -\paragraph{} -Upon reset, the board LEDs should be blinking. - -\newpage -\section{Notes} - -\subsection{Disassembling THUMB code in GDB} -\paragraph{} -By default, the disassemble command in GDB operates in ARM mode. The programs running on CORTEX-M3 -are compiled in THUMB mode. To correctly disassemble them under GDB, uses an odd address. For instance, -if you want to disassemble the code at 0x20000000, use:\\ -\begin{small} -\begin{lstlisting}[frame=tb] -(gdb) disassemble 0x20000001 -\end{lstlisting} -\end{small} - - -\newpage -\section{References} -\begin{itemize} -\item http://www.st.com/internet/mcu/product/248823.jsp\\ - documentation related to the STM32L mcu -\item http://www.st.com/internet/evalboard/product/250990.jsp\\ - documentation related to the STM32L discovery kit -\item http://www.libopencm3.org\\ - libopencm3, a project providing a firmware library, with solid examples for Cortex - M3, M4 and M0 processors from any vendor. -\end{itemize} - -\end{document} diff --git a/etc/modprobe.d/stlink_v1.conf b/etc/modprobe.d/stlink_v1.conf new file mode 100644 index 0000000..94b3786 --- /dev/null +++ b/etc/modprobe.d/stlink_v1.conf @@ -0,0 +1 @@ +options usb-storage quirks=483:3744:i diff --git a/etc/udev/rules.d/49-stlinkv1.rules b/etc/udev/rules.d/49-stlinkv1.rules new file mode 100644 index 0000000..d474d6a --- /dev/null +++ b/etc/udev/rules.d/49-stlinkv1.rules @@ -0,0 +1,11 @@ +# stm32 discovery boards, with onboard st/linkv1 +# ie, STM32VL + +SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3744", \ + MODE:="0666", \ + SYMLINK+="stlinkv1_%n" + +# If you share your linux system with other users, or just don't like the +# idea of write permission for everybody, you can replace MODE:="0666" with +# OWNER:="yourusername" to create the device owned by you, or with +# GROUP:="somegroupname" and mange access using standard unix groups. diff --git a/etc/udev/rules.d/49-stlinkv2-1.rules b/etc/udev/rules.d/49-stlinkv2-1.rules new file mode 100644 index 0000000..a5a79b9 --- /dev/null +++ b/etc/udev/rules.d/49-stlinkv2-1.rules @@ -0,0 +1,12 @@ +# stm32 nucleo boards, with onboard st/linkv2-1 +# ie, STM32F0, STM32F4. +# STM32VL has st/linkv1, which is quite different + +SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", \ + MODE:="0666", \ + SYMLINK+="stlinkv2-1_%n" + +# If you share your linux system with other users, or just don't like the +# idea of write permission for everybody, you can replace MODE:="0666" with +# OWNER:="yourusername" to create the device owned by you, or with +# GROUP:="somegroupname" and mange access using standard unix groups. diff --git a/etc/udev/rules.d/49-stlinkv2.rules b/etc/udev/rules.d/49-stlinkv2.rules new file mode 100644 index 0000000..a11215c --- /dev/null +++ b/etc/udev/rules.d/49-stlinkv2.rules @@ -0,0 +1,12 @@ +# stm32 discovery boards, with onboard st/linkv2 +# ie, STM32L, STM32F4. +# STM32VL has st/linkv1, which is quite different + +SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", \ + MODE:="0666", \ + SYMLINK+="stlinkv2_%n" + +# If you share your linux system with other users, or just don't like the +# idea of write permission for everybody, you can replace MODE:="0666" with +# OWNER:="yourusername" to create the device owned by you, or with +# GROUP:="somegroupname" and mange access using standard unix groups. diff --git a/flash/Makefile b/flash/Makefile deleted file mode 100644 index 5345dcb..0000000 --- a/flash/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -CFLAGS+=-g -CFLAGS+=-DDEBUG -CFLAGS+=-std=gnu99 -CFLAGS+=-Wall -Wextra -CFLAGS+=-I../src - -LDFLAGS=-L.. -lstlink - -# libusb location -LDFLAGS+=`pkg-config --libs libusb-1.0` -CFLAGS+=`pkg-config --cflags libusb-1.0` - -SRCS=main.c -OBJS=$(SRCS:.c=.o) - -NAME=st-flash - -all: $(NAME) - -$(NAME): $(OBJS) ../libstlink.a - $(CC) $(CFLAGS) -o $(NAME) $(OBJS) $(LDFLAGS) - -%.o: %.c - $(CC) $(CFLAGS) -c $^ -o $@ - -clean: - rm -f $(OBJS) - rm -f $(NAME) - -.PHONY: clean all diff --git a/flash/main.c b/flash/main.c deleted file mode 100644 index 0ba429c..0000000 --- a/flash/main.c +++ /dev/null @@ -1,300 +0,0 @@ -/* simple wrapper around the stlink_flash_write function */ - -// TODO - this should be done as just a simple flag to the st-util command line... - - -#include -#include -#include -#include -#include -#include "stlink-common.h" - -#define DEBUG_LOG_LEVEL 100 -#define STND_LOG_LEVEL 50 - -stlink_t *connected_stlink = NULL; - -static void cleanup(int signal __attribute__((unused))) { - if (connected_stlink) { - /* Switch back to mass storage mode before closing. */ - stlink_run(connected_stlink); - stlink_exit_debug_mode(connected_stlink); - stlink_close(connected_stlink); - } - - exit(1); -} - -enum st_cmds {DO_WRITE = 0, DO_READ = 1, DO_ERASE = 2}; -struct opts -{ - enum st_cmds cmd; - const char* devname; - char *serial; - const char* filename; - stm32_addr_t addr; - size_t size; - int reset; - int log_level; -}; - -static void usage(void) -{ - puts("stlinkv1 command line: ./st-flash [--debug] [--reset] [--serial ] {read|write} /dev/sgX path addr "); - puts("stlinkv1 command line: ./st-flash [--debug] /dev/sgX erase"); - puts("stlinkv2 command line: ./st-flash [--debug] [--reset] [--serial ] {read|write} path addr "); - puts("stlinkv2 command line: ./st-flash [--debug] [--serial ] erase"); - puts(" use hex format for addr, and "); -} - -static int get_opts(struct opts* o, int ac, char** av) -{ - /* stlinkv1 command line: ./st-flash {read|write} /dev/sgX path addr */ - /* stlinkv2 command line: ./st-flash {read|write} path addr */ - - unsigned int i = 0; - - if (ac < 1) return -1; - - if (strcmp(av[0], "--debug") == 0) - { - o->log_level = DEBUG_LOG_LEVEL; - ac--; - av++; - } - else - { - o->log_level = STND_LOG_LEVEL; - } - - if (strcmp(av[0], "--reset") == 0) - { - o->reset = 1; - ac--; - av++; - } - else - { - o->reset = 0; - } - - if (strcmp(av[0], "--serial") == 0) - { - ac--; - av++; - int i=strlen(av[0]); - if(i%2 != 0){ - puts("no valid hex value, length must be multiple of 2\n"); - return -1; - } - int j=0; - while(i>=0 && j<=13){ - char buffer[3]={0}; - memcpy(buffer,&av[0][i],2); - o->serial[12-j] = (char)strtol((const char*)buffer,NULL, 16); - j++; - i-=2; - } - ac--; - av++; - } - else - { - o->serial = NULL; - } - - if (ac < 1) return -1; - - /* stlinkv2 */ - o->devname = NULL; - - if (strcmp(av[0], "erase") == 0) - { - o->cmd = DO_ERASE; - - /* stlinkv1 mode */ - if (ac == 2) - { - o->devname = av[1]; - i = 1; - } - } - else { - if (ac < 3) return -1; - if (strcmp(av[0], "read") == 0) - { - o->cmd = DO_READ; - - /* stlinkv1 mode */ - if (ac == 5) - { - o->devname = av[1]; - i = 1; - } - if (ac > 3) - o->size = strtoul(av[i + 3], NULL, 16); - } - else if (strcmp(av[0], "write") == 0) - { - o->cmd = DO_WRITE; - - /* stlinkv1 mode */ - if (ac == 4) - { - o->devname = av[1]; - i = 1; - } - } - else - { - return -1; - } - } - - o->filename = av[i + 1]; - o->addr = strtoul(av[i + 2], NULL, 16); - - return 0; -} - - -int main(int ac, char** av) -{ - stlink_t* sl = NULL; - struct opts o; - char serial_buffer[13] = {0}; - o.serial = serial_buffer; - int err = -1; - - o.size = 0; - if (get_opts(&o, ac - 1, av + 1) == -1) - { - printf("invalid command line\n"); - usage(); - return -1; - } - - if (o.devname != NULL) /* stlinkv1 */ - sl = stlink_v1_open(o.log_level, 1); - else /* stlinkv2 */ - sl = stlink_open_usb(o.log_level, 1, o.serial); - - if (sl == NULL) - return -1; - - sl->verbose = o.log_level; - - connected_stlink = sl; - signal(SIGINT, &cleanup); - signal(SIGTERM, &cleanup); - signal(SIGSEGV, &cleanup); - - if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) { - if (stlink_exit_dfu_mode(sl)) { - printf("Failed to exit DFU mode\n"); - goto on_error; - } - } - - if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) { - if (stlink_enter_swd_mode(sl)) { - printf("Failed to enter SWD mode\n"); - goto on_error; - } - } - - if (o.reset){ - if (stlink_jtag_reset(sl, 2)) { - printf("Failed to reset JTAG\n"); - goto on_error; - } - - if (stlink_reset(sl)) { - printf("Failed to reset device\n"); - goto on_error; - } - } - - // Disable DMA - Set All DMA CCR Registers to zero. - AKS 1/7/2013 - if (sl->chip_id == STM32_CHIPID_F4) - { - memset(sl->q_buf,0,4); - for (int i=0;i<8;i++) { - stlink_write_mem32(sl,0x40026000+0x10+0x18*i,4); - stlink_write_mem32(sl,0x40026400+0x10+0x18*i,4); - stlink_write_mem32(sl,0x40026000+0x24+0x18*i,4); - stlink_write_mem32(sl,0x40026400+0x24+0x18*i,4); - } - } - - // Core must be halted to use RAM based flashloaders - if (stlink_force_debug(sl)) { - printf("Failed to halt the core\n"); - goto on_error; - } - - if (stlink_status(sl)) { - printf("Failed to get Core's status\n"); - goto on_error; - } - - if (o.cmd == DO_WRITE) /* write */ - { - if ((o.addr >= sl->flash_base) && - (o.addr < sl->flash_base + sl->flash_size)) { - err = stlink_fwrite_flash(sl, o.filename, o.addr); - if (err == -1) - { - printf("stlink_fwrite_flash() == -1\n"); - goto on_error; - } - } - else if ((o.addr >= sl->sram_base) && - (o.addr < sl->sram_base + sl->sram_size)) { - err = stlink_fwrite_sram(sl, o.filename, o.addr); - if (err == -1) - { - printf("stlink_fwrite_sram() == -1\n"); - goto on_error; - } - } - } else if (o.cmd == DO_ERASE) - { - err = stlink_erase_flash_mass(sl); - if (err == -1) - { - printf("stlink_erase_flash_mass() == -1\n"); - goto on_error; - } - } - else /* read */ - { - if ((o.addr >= sl->flash_base) && (o.size == 0) && - (o.addr < sl->flash_base + sl->flash_size)) - o.size = sl->flash_size; - else if ((o.addr >= sl->sram_base) && (o.size == 0) && - (o.addr < sl->sram_base + sl->sram_size)) - o.size = sl->sram_size; - err = stlink_fread(sl, o.filename, o.addr, o.size); - if (err == -1) - { - printf("stlink_fread() == -1\n"); - goto on_error; - } - } - - if (o.reset){ - stlink_jtag_reset(sl,2); - stlink_reset(sl); - } - - /* success */ - err = 0; - -on_error: - stlink_exit_debug_mode(sl); - stlink_close(sl); - - return err; -} diff --git a/gdbserver/Makefile b/gdbserver/Makefile deleted file mode 100644 index bd5c73d..0000000 --- a/gdbserver/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -PRG := st-util -OBJS = gdb-remote.o gdb-server.o - -CFLAGS+=-g -Wall -Werror -std=gnu99 -I../src -LDFLAGS=-L.. -lstlink - -# libusb location -LDFLAGS+=`pkg-config --libs libusb-1.0` -CFLAGS+=`pkg-config --cflags libusb-1.0` - -all: $(PRG) - -$(PRG): $(OBJS) ../libstlink.a - $(CC) -o $@ $^ $(LDFLAGS) - -clean: - rm -rf $(OBJS) - rm -rf $(PRG) - -.PHONY: clean all diff --git a/gdbserver/gdb-remote.c b/gdbserver/gdb-remote.c deleted file mode 100644 index 2f61b93..0000000 --- a/gdbserver/gdb-remote.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2011 Peter Zotov - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -#include -#include -#include -#include -#include -#ifdef __MINGW32__ -#include "mingw.h" -#else -#include -#endif - -static const char hex[] = "0123456789abcdef"; - -int gdb_send_packet(int fd, char* data) { - unsigned int data_length = strlen(data); - int length = data_length + 4; - char* packet = malloc(length); /* '$' data (hex) '#' cksum (hex) */ - - memset(packet, 0, length); - - packet[0] = '$'; - - uint8_t cksum = 0; - for(unsigned int i = 0; i < data_length; i++) { - packet[i + 1] = data[i]; - cksum += data[i]; - } - - packet[length - 3] = '#'; - packet[length - 2] = hex[cksum >> 4]; - packet[length - 1] = hex[cksum & 0xf]; - - while(1) { - if(write(fd, packet, length) != length) { - free(packet); - return -2; - } - - char ack; - if(read(fd, &ack, 1) != 1) { - free(packet); - return -2; - } - - if(ack == '+') { - free(packet); - return 0; - } - } -} - -#define ALLOC_STEP 1024 - -int gdb_recv_packet(int fd, char** buffer) { - unsigned packet_size = ALLOC_STEP + 1, packet_idx = 0; - uint8_t cksum = 0; - char recv_cksum[3] = {0}; - char* packet_buffer = malloc(packet_size); - unsigned state; - -start: - state = 0; - /* - * 0: waiting $ - * 1: data, waiting # - * 2: cksum 1 - * 3: cksum 2 - * 4: fin - */ - - char c; - while(state != 4) { - if(read(fd, &c, 1) != 1) { - return -2; - } - - switch(state) { - case 0: - if(c != '$') { - // ignore - } else { - state = 1; - } - break; - - case 1: - if(c == '#') { - state = 2; - } else { - packet_buffer[packet_idx++] = c; - cksum += c; - - if(packet_idx == packet_size) { - packet_size += ALLOC_STEP; - packet_buffer = realloc(packet_buffer, packet_size); - } - } - break; - - case 2: - recv_cksum[0] = c; - state = 3; - break; - - case 3: - recv_cksum[1] = c; - state = 4; - break; - } - } - - uint8_t recv_cksum_int = strtoul(recv_cksum, NULL, 16); - if(recv_cksum_int != cksum) { - char nack = '-'; - if(write(fd, &nack, 1) != 1) { - return -2; - } - - goto start; - } else { - char ack = '+'; - if(write(fd, &ack, 1) != 1) { - return -2; - } - } - - packet_buffer[packet_idx] = 0; - *buffer = packet_buffer; - - return packet_idx; -} - -// Here we skip any characters which are not \x03, GDB interrupt. -// As we use the mode with ACK, in a (very unlikely) situation of a packet -// lost because of this skipping, it will be resent anyway. -int gdb_check_for_interrupt(int fd) { - struct pollfd pfd; - pfd.fd = fd; - pfd.events = POLLIN; - - if(poll(&pfd, 1, 0) != 0) { - char c; - - if(read(fd, &c, 1) != 1) - return -2; - - if(c == '\x03') // ^C - return 1; - } - - return 0; -} - diff --git a/gdbserver/gdb-remote.h b/gdbserver/gdb-remote.h deleted file mode 100644 index bfa0104..0000000 --- a/gdbserver/gdb-remote.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _GDB_REMOTE_H_ -#define _GDB_REMOTE_H_ - -int gdb_send_packet(int fd, char* data); -int gdb_recv_packet(int fd, char** buffer); -int gdb_check_for_interrupt(int fd); - -#endif diff --git a/gdbserver/gdb-server.c b/gdbserver/gdb-server.c deleted file mode 100644 index eca6597..0000000 --- a/gdbserver/gdb-server.c +++ /dev/null @@ -1,1555 +0,0 @@ -/* - * Copyright (C) 2011 Peter Zotov - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -#include -#include -#include -#include -#include -#include -#include -#ifdef __MINGW32__ -#include "mingw.h" -#else -#include -#include -#include -#endif - -#include -#include - -#include "gdb-remote.h" -#include "gdb-server.h" - -#define FLASH_BASE 0x08000000 - -//Allways update the FLASH_PAGE before each use, by calling stlink_calculate_pagesize -#define FLASH_PAGE (sl->flash_pgsz) - -stlink_t *connected_stlink = NULL; - -static const char hex[] = "0123456789abcdef"; - -static const char* current_memory_map = NULL; - -typedef struct _st_state_t { - // things from command line, bleh - int stlink_version; - int logging_level; - int listen_port; - int persistent; - int reset; -} st_state_t; - - -int serve(stlink_t *sl, st_state_t *st); -char* make_memory_map(stlink_t *sl); -static void init_cache (stlink_t *sl); - -static void cleanup(int signal __attribute__((unused))) { - if (connected_stlink) { - /* Switch back to mass storage mode before closing. */ - stlink_run(connected_stlink); - stlink_exit_debug_mode(connected_stlink); - stlink_close(connected_stlink); - } - - exit(1); -} - - - -int parse_options(int argc, char** argv, st_state_t *st) { - static struct option long_options[] = { - {"help", no_argument, NULL, 'h'}, - {"verbose", optional_argument, NULL, 'v'}, - {"stlink_version", required_argument, NULL, 's'}, - {"stlinkv1", no_argument, NULL, '1'}, - {"listen_port", required_argument, NULL, 'p'}, - {"multi", optional_argument, NULL, 'm'}, - {"no-reset", optional_argument, NULL, 'n'}, - {0, 0, 0, 0}, - }; - const char * help_str = "%s - usage:\n\n" - " -h, --help\t\tPrint this help\n" - " -vXX, --verbose=XX\tSpecify a specific verbosity level (0..99)\n" - " -v, --verbose\t\tSpecify generally verbose logging\n" - " -s X, --stlink_version=X\n" - "\t\t\tChoose what version of stlink to use, (defaults to 2)\n" - " -1, --stlinkv1\tForce stlink version 1\n" - " -p 4242, --listen_port=1234\n" - "\t\t\tSet the gdb server listen port. " - "(default port: " STRINGIFY(DEFAULT_GDB_LISTEN_PORT) ")\n" - " -m, --multi\n" - "\t\t\tSet gdb server to extended mode.\n" - "\t\t\tst-util will continue listening for connections after disconnect.\n" - " -n, --no-reset\n" - "\t\t\tDo not reset board on connection.\n" - "\n" - "The STLINKv2 device to use can be specified in the environment\n" - "variable STLINK_DEVICE on the format :.\n" - "\n" - ; - - - int option_index = 0; - int c; - int q; - while ((c = getopt_long(argc, argv, "hv::s:1p:mn", long_options, &option_index)) != -1) { - switch (c) { - case 0: - printf("XXXXX Shouldn't really normally come here, only if there's no corresponding option\n"); - printf("option %s", long_options[option_index].name); - if (optarg) { - printf(" with arg %s", optarg); - } - printf("\n"); - break; - case 'h': - printf(help_str, argv[0]); - exit(EXIT_SUCCESS); - break; - case 'v': - if (optarg) { - st->logging_level = atoi(optarg); - } else { - st->logging_level = DEFAULT_LOGGING_LEVEL; - } - break; - case '1': - st->stlink_version = 1; - break; - case 's': - sscanf(optarg, "%i", &q); - if (q < 0 || q > 2) { - fprintf(stderr, "stlink version %d unknown!\n", q); - exit(EXIT_FAILURE); - } - st->stlink_version = q; - break; - case 'p': - sscanf(optarg, "%i", &q); - if (q < 0) { - fprintf(stderr, "Can't use a negative port to listen on: %d\n", q); - exit(EXIT_FAILURE); - } - st->listen_port = q; - break; - case 'm': - st->persistent = 1; - break; - case 'n': - st->reset = 0; - break; - } - } - - if (optind < argc) { - printf("non-option ARGV-elements: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); - } - return 0; -} - - -int main(int argc, char** argv) { - int32_t voltage; - - stlink_t *sl = NULL; - - st_state_t state; - memset(&state, 0, sizeof(state)); - // set defaults... - state.stlink_version = 2; - state.logging_level = DEFAULT_LOGGING_LEVEL; - state.listen_port = DEFAULT_GDB_LISTEN_PORT; - state.reset = 1; /* By default, reset board */ - parse_options(argc, argv, &state); - switch (state.stlink_version) { - case 2: - sl = stlink_open_usb(state.logging_level, state.reset, NULL); - if(sl == NULL) return 1; - break; - case 1: - sl = stlink_v1_open(state.logging_level, state.reset); - if(sl == NULL) return 1; - break; - } - - connected_stlink = sl; - signal(SIGINT, &cleanup); - signal(SIGTERM, &cleanup); - signal(SIGSEGV, &cleanup); - - if (state.reset) { - stlink_reset(sl); - } - - ILOG("Chip ID is %08x, Core ID is %08x.\n", sl->chip_id, sl->core_id); - - voltage = stlink_target_voltage(sl); - if (voltage != -1) { - ILOG("Target voltage is %d mV.\n", voltage); - } - - sl->verbose=0; - - current_memory_map = make_memory_map(sl); - -#ifdef __MINGW32__ - WSADATA wsadata; - if (WSAStartup(MAKEWORD(2,2),&wsadata) !=0 ) { - goto winsock_error; - } -#endif - - init_cache(sl); - - do { - if (serve(sl, &state)) { - sleep (1); // don't go bezurk if serve returns with error - } - - /* Continue */ - stlink_run(sl); - } while (state.persistent); - -#ifdef __MINGW32__ -winsock_error: - WSACleanup(); -#endif - - /* Switch back to mass storage mode before closing. */ - stlink_exit_debug_mode(sl); - stlink_close(sl); - - return 0; -} - -static const char* const target_description_F4 = - "" - "" - "" - " arm" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; - -static const char* const memory_map_template_F4 = - "" - "" - "" - " " // code = sram, bootrom or flash; flash is bigger - " " // ccm ram - " " // sram - " " //Sectors 0..3 - " 0x4000" //16kB - " " - " " //Sector 4 - " 0x10000" //64kB - " " - " " //Sectors 5..11 - " 0x20000" //128kB - " " - " " // peripheral regs - " " // AHB3 Peripherals - " " // cortex regs - " " // bootrom - " " // option byte area - ""; - -static const char* const memory_map_template_F4_HD = - "" - "" - "" - " " // code = sram, bootrom or flash; flash is bigger - " " // ccm ram - " " // sram - " " // fmc bank 1 (nor/psram/sram) - " " // fmc bank 2 & 3 (nand flash) - " " // fmc bank 4 (pc card) - " " // fmc sdram bank 1 & 2 - " " //Sectors 0..3 - " 0x4000" //16kB - " " - " " //Sector 4 - " 0x10000" //64kB - " " - " " //Sectors 5..11 - " 0x20000" //128kB - " " - " " // peripheral regs - " " // cortex regs - " " // bootrom - " " // option byte area - ""; - -static const char* const memory_map_template_F2 = - "" - "" - "" - " " // code = sram, bootrom or flash; flash is bigger - " " // sram - " " //Sectors 0..3 - " 0x4000" //16kB - " " - " " //Sector 4 - " 0x10000" //64kB - " " - " " //Sectors 5.. - " 0x20000" //128kB - " " - " " // peripheral regs - " " // cortex regs - " " // bootrom - " " // option byte area - ""; - -static const char* const memory_map_template_L4 = - "" - "" - "" - " " // code = sram, bootrom or flash; flash is bigger - " " // SRAM2 (32 KB) - " " // SRAM1 (96 KB) - " " - " 0x800" - " " - " " // peripheral regs - " " // AHB3 Peripherals - " " // cortex regs - " " // bootrom - " " // option byte area - " " // option byte area - ""; - -static const char* const memory_map_template = - "" - "" - "" - " " // code = sram, bootrom or flash; flash is bigger - " " // sram 8k - " " - " 0x%zx" - " " - " " // peripheral regs - " " // cortex regs - " " // bootrom - " " // option byte area - ""; - -static const char* const memory_map_template_F7 = - "" - "" - "" - " " // ITCM ram 16kB - " " // ITCM flash - " " // sram - " " // Sectors 0..3 - " 0x8000" // 32kB - " " - " " // Sector 4 - " 0x20000" // 128kB - " " - " " // Sectors 5..7 - " 0x40000" // 128kB - " " - " " // peripheral regs - " " // AHB3 Peripherals - " " // cortex regs - " " // bootrom - " " // option byte area - ""; - -char* make_memory_map(stlink_t *sl) { - /* This will be freed in serve() */ - char* map = malloc(4096); - map[0] = '\0'; - - if(sl->chip_id==STM32_CHIPID_F4 || sl->chip_id==STM32_CHIPID_F446) { - strcpy(map, memory_map_template_F4); - } else if(sl->chip_id==STM32_CHIPID_F4 || sl->chip_id==STM32_CHIPID_F7) { - strcpy(map, memory_map_template_F7); - } else if(sl->chip_id==STM32_CHIPID_F4_HD) { - strcpy(map, memory_map_template_F4_HD); - } else if(sl->chip_id==STM32_CHIPID_F2) { - snprintf(map, 4096, memory_map_template_F2, - sl->flash_size, - sl->sram_size, - sl->flash_size - 0x20000, - sl->sys_base, sl->sys_size); - } else if(sl->chip_id==STM32_CHIPID_L4) { - snprintf(map, 4096, memory_map_template_L4, - sl->flash_size, sl->flash_size); - } else { - snprintf(map, 4096, memory_map_template, - sl->flash_size, - sl->sram_size, - sl->flash_size, sl->flash_pgsz, - sl->sys_base, sl->sys_size); - } - return map; -} - - -/* - * DWT_COMP0 0xE0001020 - * DWT_MASK0 0xE0001024 - * DWT_FUNCTION0 0xE0001028 - * DWT_COMP1 0xE0001030 - * DWT_MASK1 0xE0001034 - * DWT_FUNCTION1 0xE0001038 - * DWT_COMP2 0xE0001040 - * DWT_MASK2 0xE0001044 - * DWT_FUNCTION2 0xE0001048 - * DWT_COMP3 0xE0001050 - * DWT_MASK3 0xE0001054 - * DWT_FUNCTION3 0xE0001058 - */ - -#define DATA_WATCH_NUM 4 - -enum watchfun { WATCHDISABLED = 0, WATCHREAD = 5, WATCHWRITE = 6, WATCHACCESS = 7 }; - -struct code_hw_watchpoint { - stm32_addr_t addr; - uint8_t mask; - enum watchfun fun; -}; - -struct code_hw_watchpoint data_watches[DATA_WATCH_NUM]; - -static void init_data_watchpoints(stlink_t *sl) { - uint32_t data; - DLOG("init watchpoints\n"); - - stlink_read_debug32(sl, 0xE000EDFC, &data); - data |= 1<<24; - // set trcena in debug command to turn on dwt unit - stlink_write_debug32(sl, 0xE000EDFC, data); - - // make sure all watchpoints are cleared - for(int i = 0; i < DATA_WATCH_NUM; i++) { - data_watches[i].fun = WATCHDISABLED; - stlink_write_debug32(sl, 0xe0001028 + i * 16, 0); - } -} - -static int add_data_watchpoint(stlink_t *sl, enum watchfun wf, - stm32_addr_t addr, unsigned int len) { - int i = 0; - uint32_t mask, dummy; - - // computer mask - // find a free watchpoint - // configure - - mask = -1; - i = len; - while(i) { - i >>= 1; - mask++; - } - - if((mask != (uint32_t)-1) && (mask < 16)) { - for(i = 0; i < DATA_WATCH_NUM; i++) { - // is this an empty slot ? - if(data_watches[i].fun == WATCHDISABLED) { - DLOG("insert watchpoint %d addr %x wf %u mask %u len %d\n", i, addr, wf, mask, len); - - data_watches[i].fun = wf; - data_watches[i].addr = addr; - data_watches[i].mask = mask; - - // insert comparator address - stlink_write_debug32(sl, 0xE0001020 + i * 16, addr); - - // insert mask - stlink_write_debug32(sl, 0xE0001024 + i * 16, mask); - - // insert function - stlink_write_debug32(sl, 0xE0001028 + i * 16, wf); - - // just to make sure the matched bit is clear ! - stlink_read_debug32(sl, 0xE0001028 + i * 16, &dummy); - return 0; - } - } - } - - DLOG("failure: add watchpoints addr %x wf %u len %u\n", addr, wf, len); - return -1; -} - -static int delete_data_watchpoint(stlink_t *sl, stm32_addr_t addr) -{ - int i; - - for(i = 0 ; i < DATA_WATCH_NUM; i++) { - if((data_watches[i].addr == addr) && (data_watches[i].fun != WATCHDISABLED)) { - DLOG("delete watchpoint %d addr %x\n", i, addr); - - data_watches[i].fun = WATCHDISABLED; - stlink_write_debug32(sl, 0xe0001028 + i * 16, 0); - - return 0; - } - } - - DLOG("failure: delete watchpoint addr %x\n", addr); - - return -1; -} - -int code_break_num; -int code_lit_num; -#define CODE_BREAK_NUM_MAX 15 -#define CODE_BREAK_LOW 0x01 -#define CODE_BREAK_HIGH 0x02 - -struct code_hw_breakpoint { - stm32_addr_t addr; - int type; -}; - -struct code_hw_breakpoint code_breaks[CODE_BREAK_NUM_MAX]; - -static void init_code_breakpoints(stlink_t *sl) { - unsigned int val; - memset(sl->q_buf, 0, 4); - stlink_write_debug32(sl, CM3_REG_FP_CTRL, 0x03 /*KEY | ENABLE4*/); - stlink_read_debug32(sl, CM3_REG_FP_CTRL, &val); - code_break_num = ((val >> 4) & 0xf); - code_lit_num = ((val >> 8) & 0xf); - - ILOG("Found %i hw breakpoint registers\n", code_break_num); - - for(int i = 0; i < code_break_num; i++) { - code_breaks[i].type = 0; - stlink_write_debug32(sl, CM3_REG_FP_COMP0 + i * 4, 0); - } -} - -static int update_code_breakpoint(stlink_t *sl, stm32_addr_t addr, int set) { - stm32_addr_t fpb_addr; - uint32_t mask; - int type = (addr & 0x2) ? CODE_BREAK_HIGH : CODE_BREAK_LOW; - - if(addr & 1) { - ELOG("update_code_breakpoint: unaligned address %08x\n", addr); - return -1; - } - - if (sl->chip_id==STM32_CHIPID_F7) { - fpb_addr = addr; - } else { - fpb_addr = addr & ~0x3; - } - - int id = -1; - for(int i = 0; i < code_break_num; i++) { - if(fpb_addr == code_breaks[i].addr || - (set && code_breaks[i].type == 0)) { - id = i; - break; - } - } - - if(id == -1) { - if(set) return -1; // Free slot not found - else return 0; // Breakpoint is already removed - } - - struct code_hw_breakpoint* brk = &code_breaks[id]; - - brk->addr = fpb_addr; - - if (sl->chip_id==STM32_CHIPID_F7) { - if(set) brk->type = type; - else brk->type = 0; - - mask = (brk->addr) | 1; - } else { - if(set) brk->type |= type; - else brk->type &= ~type; - - mask = (brk->addr) | 1 | (brk->type << 30); - } - - if(brk->type == 0) { - DLOG("clearing hw break %d\n", id); - - stlink_write_debug32(sl, 0xe0002008 + id * 4, 0); - } else { - DLOG("setting hw break %d at %08x (%d)\n", - id, brk->addr, brk->type); - DLOG("reg %08x \n", - mask); - - stlink_write_debug32(sl, 0xe0002008 + id * 4, mask); - } - - return 0; -} - - -struct flash_block { - stm32_addr_t addr; - unsigned length; - uint8_t* data; - - struct flash_block* next; -}; - -static struct flash_block* flash_root; - -static int flash_add_block(stm32_addr_t addr, unsigned length, stlink_t *sl) { - - if(addr < FLASH_BASE || addr + length > FLASH_BASE + sl->flash_size) { - ELOG("flash_add_block: incorrect bounds\n"); - return -1; - } - - stlink_calculate_pagesize(sl, addr); - if(addr % FLASH_PAGE != 0 || length % FLASH_PAGE != 0) { - ELOG("flash_add_block: unaligned block\n"); - return -1; - } - - struct flash_block* new = malloc(sizeof(struct flash_block)); - new->next = flash_root; - - new->addr = addr; - new->length = length; - new->data = calloc(length, 1); - - flash_root = new; - - return 0; -} - -static int flash_populate(stm32_addr_t addr, uint8_t* data, unsigned length) { - unsigned int fit_blocks = 0, fit_length = 0; - - for(struct flash_block* fb = flash_root; fb; fb = fb->next) { - /* Block: ------X------Y-------- - * Data: a-----b - * a--b - * a-----------b - * Block intersects with data, if: - * a < Y && b > x - */ - - unsigned X = fb->addr, Y = fb->addr + fb->length; - unsigned a = addr, b = addr + length; - if(a < Y && b > X) { - // from start of the block - unsigned start = (a > X ? a : X) - X; - unsigned end = (b > Y ? Y : b) - X; - - memcpy(fb->data + start, data, end - start); - - fit_blocks++; - fit_length += end - start; - } - } - - if(fit_blocks == 0) { - ELOG("Unfit data block %08x -> %04x\n", addr, length); - return -1; - } - - if(fit_length != length) { - WLOG("data block %08x -> %04x truncated to %04x\n", - addr, length, fit_length); - WLOG("(this is not an error, just a GDB glitch)\n"); - } - - return 0; -} - -static int flash_go(stlink_t *sl) { - int error = -1; - - // Some kinds of clock settings do not allow writing to flash. - stlink_reset(sl); - stlink_force_debug(sl); - - for(struct flash_block* fb = flash_root; fb; fb = fb->next) { - DLOG("flash_do: block %08x -> %04x\n", fb->addr, fb->length); - - for(stm32_addr_t page = fb->addr; page < fb->addr + fb->length; page += FLASH_PAGE) { - unsigned length = fb->length - (page - fb->addr); - - //Update FLASH_PAGE - stlink_calculate_pagesize(sl, page); - - DLOG("flash_do: page %08x\n", page); - unsigned send = length > FLASH_PAGE ? FLASH_PAGE : length; - if(stlink_write_flash(sl, page, fb->data + (page - fb->addr), - send, 0) < 0) - goto error; - length -= send; - - } - } - - stlink_reset(sl); - - error = 0; - -error: - for(struct flash_block* fb = flash_root, *next; fb; fb = next) { - next = fb->next; - free(fb->data); - free(fb); - } - - flash_root = NULL; - - return error; -} - -#define CLIDR 0xE000ED78 -#define CTR 0xE000ED7C -#define CCSIDR 0xE000ED80 -#define CSSELR 0xE000ED84 -#define CCR 0xE000ED14 -#define CCR_DC (1 << 16) -#define CCR_IC (1 << 17) -#define DCCSW 0xE000EF6C -#define ICIALLU 0xE000EF50 - -struct cache_level_desc -{ - unsigned int nsets; - unsigned int nways; - unsigned int log2_nways; - unsigned int width; -}; - -struct cache_desc_t -{ - /* Minimal line size in bytes. */ - unsigned int dminline; - unsigned int iminline; - - /* Last level of unification (uniprocessor). */ - unsigned int louu; - - struct cache_level_desc icache[7]; - struct cache_level_desc dcache[7]; -}; - -static struct cache_desc_t cache_desc; - -/* Return the smallest R so that V <= (1 << R). Not performance critical. */ -static unsigned ceil_log2(unsigned v) -{ - unsigned res; - for (res = 0; (1U << res) < v; res++) - ; - return res; -} - -static void read_cache_level_desc(stlink_t *sl, struct cache_level_desc *desc) -{ - unsigned int ccsidr; - unsigned int log2_nsets; - - stlink_read_debug32(sl, CCSIDR, &ccsidr); - desc->nsets = ((ccsidr >> 13) & 0x3fff) + 1; - desc->nways = ((ccsidr >> 3) & 0x1ff) + 1; - desc->log2_nways = ceil_log2 (desc->nways); - log2_nsets = ceil_log2 (desc->nsets); - desc->width = 4 + (ccsidr & 7) + log2_nsets; - ILOG("%08x LineSize: %u, ways: %u, sets: %u (width: %u)\n", - ccsidr, 4 << (ccsidr & 7), desc->nways, desc->nsets, desc->width); -} - -static void init_cache (stlink_t *sl) { - unsigned int clidr; - unsigned int ccr; - unsigned int ctr; - int i; - - /* Assume only F7 has a cache. */ - if(sl->chip_id!=STM32_CHIPID_F7) - return; - - stlink_read_debug32(sl, CLIDR, &clidr); - stlink_read_debug32(sl, CCR, &ccr); - stlink_read_debug32(sl, CTR, &ctr); - cache_desc.dminline = 4 << ((ctr >> 16) & 0x0f); - cache_desc.iminline = 4 << (ctr & 0x0f); - cache_desc.louu = (clidr >> 27) & 7; - - ILOG("Chip clidr: %08x, I-Cache: %s, D-Cache: %s\n", - clidr, ccr & CCR_IC ? "on" : "off", ccr & CCR_DC ? "on" : "off"); - ILOG(" cache: LoUU: %u, LoC: %u, LoUIS: %u\n", - (clidr >> 27) & 7, (clidr >> 24) & 7, (clidr >> 21) & 7); - ILOG(" cache: ctr: %08x, DminLine: %u bytes, IminLine: %u bytes\n", ctr, - cache_desc.dminline, cache_desc.iminline); - for(i = 0; i < 7; i++) - { - unsigned int ct = (clidr >> (3 * i)) & 0x07; - - cache_desc.dcache[i].width = 0; - cache_desc.icache[i].width = 0; - - if(ct == 2 || ct == 3 || ct == 4) - { - /* Data. */ - stlink_write_debug32(sl, CSSELR, i << 1); - ILOG("D-Cache L%d: ", i); - read_cache_level_desc(sl, &cache_desc.dcache[i]); - } - - if(ct == 1 || ct == 3) - { - /* Instruction. */ - stlink_write_debug32(sl, CSSELR, (i << 1) | 1); - ILOG("I-Cache L%d: ", i); - read_cache_level_desc(sl, &cache_desc.icache[i]); - } - } -} - -static void cache_flush(stlink_t *sl, unsigned ccr) { - int level; - - if (ccr & CCR_DC) - for (level = cache_desc.louu - 1; level >= 0; level--) - { - struct cache_level_desc *desc = &cache_desc.dcache[level]; - unsigned addr; - unsigned max_addr = 1 << desc->width; - unsigned way_sh = 32 - desc->log2_nways; - - /* D-cache clean by set-ways. */ - for (addr = (level << 1); addr < max_addr; addr += cache_desc.dminline) - { - unsigned int way; - - for (way = 0; way < desc->nways; way++) - stlink_write_debug32(sl, DCCSW, addr | (way << way_sh)); - } - } - - /* Invalidate all I-cache to oPU. */ - if (ccr & CCR_IC) - stlink_write_debug32(sl, ICIALLU, 0); -} - -static int cache_modified; - -static void cache_change(stm32_addr_t start, unsigned count) -{ - if (count == 0) - return; - (void)start; - cache_modified = 1; -} - -static void cache_sync(stlink_t *sl) -{ - unsigned ccr; - - if(sl->chip_id!=STM32_CHIPID_F7) - return; - if (!cache_modified) - return; - cache_modified = 0; - - stlink_read_debug32(sl, CCR, &ccr); - if (ccr & (CCR_IC | CCR_DC)) - cache_flush(sl, ccr); -} - -int serve(stlink_t *sl, st_state_t *st) { - int sock = socket(AF_INET, SOCK_STREAM, 0); - if(sock < 0) { - perror("socket"); - return 1; - } - - unsigned int val = 1; - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); - - struct sockaddr_in serv_addr; - memset(&serv_addr,0,sizeof(struct sockaddr_in)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(st->listen_port); - - if(bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - perror("bind"); - return 1; - } - - if(listen(sock, 5) < 0) { - perror("listen"); - return 1; - } - - ILOG("Listening at *:%d...\n", st->listen_port); - - int client = accept(sock, NULL, NULL); - //signal (SIGINT, SIG_DFL); - if(client < 0) { - perror("accept"); - return 1; - } - - close(sock); - - stlink_force_debug(sl); - if (st->reset) { - stlink_reset(sl); - } - init_code_breakpoints(sl); - init_data_watchpoints(sl); - - ILOG("GDB connected.\n"); - - /* - * To allow resetting the chip from GDB it is required to - * emulate attaching and detaching to target. - */ - unsigned int attached = 1; - - while(1) { - char* packet; - - int status = gdb_recv_packet(client, &packet); - if(status < 0) { - ELOG("cannot recv: %d\n", status); -#ifdef __MINGW32__ - win32_close_socket(sock); -#endif - return 1; - } - - DLOG("recv: %s\n", packet); - - char* reply = NULL; - reg regp; - - switch(packet[0]) { - case 'q': { - if(packet[1] == 'P' || packet[1] == 'C' || packet[1] == 'L') { - reply = strdup(""); - break; - } - - char *separator = strstr(packet, ":"), *params = ""; - if(separator == NULL) { - separator = packet + strlen(packet); - } else { - params = separator + 1; - } - - unsigned queryNameLength = (separator - &packet[1]); - char* queryName = calloc(queryNameLength + 1, 1); - strncpy(queryName, &packet[1], queryNameLength); - - DLOG("query: %s;%s\n", queryName, params); - - if(!strcmp(queryName, "Supported")) { - if(sl->chip_id==STM32_CHIPID_F4 - || sl->chip_id==STM32_CHIPID_F4_HD - || sl->chip_id==STM32_CHIPID_F7) { - reply = strdup("PacketSize=3fff;qXfer:memory-map:read+;qXfer:features:read+"); - } - else { - reply = strdup("PacketSize=3fff;qXfer:memory-map:read+"); - } - } else if(!strcmp(queryName, "Xfer")) { - char *type, *op, *__s_addr, *s_length; - char *tok = params; - char *annex __attribute__((unused)); - - type = strsep(&tok, ":"); - op = strsep(&tok, ":"); - annex = strsep(&tok, ":"); - __s_addr = strsep(&tok, ","); - s_length = tok; - - unsigned addr = strtoul(__s_addr, NULL, 16), - length = strtoul(s_length, NULL, 16); - - DLOG("Xfer: type:%s;op:%s;annex:%s;addr:%d;length:%d\n", - type, op, annex, addr, length); - - const char* data = NULL; - - if(!strcmp(type, "memory-map") && !strcmp(op, "read")) - data = current_memory_map; - - if(!strcmp(type, "features") && !strcmp(op, "read")) - data = target_description_F4; - - if(data) { - unsigned data_length = strlen(data); - if(addr + length > data_length) - length = data_length - addr; - - if(length == 0) { - reply = strdup("l"); - } else { - reply = calloc(length + 2, 1); - reply[0] = 'm'; - strncpy(&reply[1], data, length); - } - } - } else if(!strncmp(queryName, "Rcmd,",4)) { - // Rcmd uses the wrong separator - char *separator = strstr(packet, ","), *params = ""; - if(separator == NULL) { - separator = packet + strlen(packet); - } else { - params = separator + 1; - } - - - if (!strncmp(params,"726573756d65",12)) {// resume - DLOG("Rcmd: resume\n"); - cache_sync(sl); - stlink_run(sl); - - reply = strdup("OK"); - } else if (!strncmp(params,"68616c74",8)) { //halt - reply = strdup("OK"); - - stlink_force_debug(sl); - - DLOG("Rcmd: halt\n"); - } else if (!strncmp(params,"6a7461675f7265736574",20)) { //jtag_reset - reply = strdup("OK"); - - stlink_jtag_reset(sl, 0); - stlink_jtag_reset(sl, 1); - stlink_force_debug(sl); - - DLOG("Rcmd: jtag_reset\n"); - } else if (!strncmp(params,"7265736574",10)) { //reset - reply = strdup("OK"); - - stlink_force_debug(sl); - stlink_reset(sl); - init_code_breakpoints(sl); - init_data_watchpoints(sl); - - DLOG("Rcmd: reset\n"); - } else { - DLOG("Rcmd: %s\n", params); - } - - } - - if(reply == NULL) - reply = strdup(""); - - free(queryName); - - break; - } - - case 'v': { - char *params = NULL; - char *cmdName = strtok_r(packet, ":;", ¶ms); - - cmdName++; // vCommand -> Command - - if(!strcmp(cmdName, "FlashErase")) { - char *__s_addr, *s_length; - char *tok = params; - - __s_addr = strsep(&tok, ","); - s_length = tok; - - unsigned addr = strtoul(__s_addr, NULL, 16), - length = strtoul(s_length, NULL, 16); - - DLOG("FlashErase: addr:%08x,len:%04x\n", - addr, length); - - if(flash_add_block(addr, length, sl) < 0) { - reply = strdup("E00"); - } else { - reply = strdup("OK"); - } - } else if(!strcmp(cmdName, "FlashWrite")) { - char *__s_addr, *data; - char *tok = params; - - __s_addr = strsep(&tok, ":"); - data = tok; - - unsigned addr = strtoul(__s_addr, NULL, 16); - unsigned data_length = status - (data - packet); - - // Length of decoded data cannot be more than - // encoded, as escapes are removed. - // Additional byte is reserved for alignment fix. - uint8_t *decoded = calloc(data_length + 1, 1); - unsigned dec_index = 0; - for(unsigned int i = 0; i < data_length; i++) { - if(data[i] == 0x7d) { - i++; - decoded[dec_index++] = data[i] ^ 0x20; - } else { - decoded[dec_index++] = data[i]; - } - } - - // Fix alignment - if(dec_index % 2 != 0) - dec_index++; - - DLOG("binary packet %d -> %d\n", data_length, dec_index); - - if(flash_populate(addr, decoded, dec_index) < 0) { - reply = strdup("E00"); - } else { - reply = strdup("OK"); - } - } else if(!strcmp(cmdName, "FlashDone")) { - if(flash_go(sl) < 0) { - reply = strdup("E00"); - } else { - reply = strdup("OK"); - } - } else if(!strcmp(cmdName, "Kill")) { - attached = 0; - - reply = strdup("OK"); - } - - if(reply == NULL) - reply = strdup(""); - - break; - } - - case 'c': - cache_sync(sl); - stlink_run(sl); - - while(1) { - int status = gdb_check_for_interrupt(client); - if(status < 0) { - ELOG("cannot check for int: %d\n", status); -#ifdef __MINGW32__ - win32_close_socket(sock); -#endif - return 1; - } - - if(status == 1) { - stlink_force_debug(sl); - break; - } - - stlink_status(sl); - if(sl->core_stat == STLINK_CORE_HALTED) { - break; - } - - usleep(100000); - } - - reply = strdup("S05"); // TRAP - break; - - case 's': - cache_sync(sl); - stlink_step(sl); - - reply = strdup("S05"); // TRAP - break; - - case '?': - if(attached) { - reply = strdup("S05"); // TRAP - } else { - /* Stub shall reply OK if not attached. */ - reply = strdup("OK"); - } - break; - - case 'g': - stlink_read_all_regs(sl, ®p); - - reply = calloc(8 * 16 + 1, 1); - for(int i = 0; i < 16; i++) - sprintf(&reply[i * 8], "%08x", htonl(regp.r[i])); - - break; - - case 'p': { - unsigned id = strtoul(&packet[1], NULL, 16); - unsigned myreg = 0xDEADDEAD; - - if(id < 16) { - stlink_read_reg(sl, id, ®p); - myreg = htonl(regp.r[id]); - } else if(id == 0x19) { - stlink_read_reg(sl, 16, ®p); - myreg = htonl(regp.xpsr); - } else if(id == 0x1A) { - stlink_read_reg(sl, 17, ®p); - myreg = htonl(regp.main_sp); - } else if(id == 0x1B) { - stlink_read_reg(sl, 18, ®p); - myreg = htonl(regp.process_sp); - } else if(id == 0x1C) { - stlink_read_unsupported_reg(sl, id, ®p); - myreg = htonl(regp.control); - } else if(id == 0x1D) { - stlink_read_unsupported_reg(sl, id, ®p); - myreg = htonl(regp.faultmask); - } else if(id == 0x1E) { - stlink_read_unsupported_reg(sl, id, ®p); - myreg = htonl(regp.basepri); - } else if(id == 0x1F) { - stlink_read_unsupported_reg(sl, id, ®p); - myreg = htonl(regp.primask); - } else if(id >= 0x20 && id < 0x40) { - stlink_read_unsupported_reg(sl, id, ®p); - myreg = htonl(regp.s[id-0x20]); - } else if(id == 0x40) { - stlink_read_unsupported_reg(sl, id, ®p); - myreg = htonl(regp.fpscr); - } else { - reply = strdup("E00"); - } - - reply = calloc(8 + 1, 1); - sprintf(reply, "%08x", myreg); - - break; - } - - case 'P': { - char* s_reg = &packet[1]; - char* s_value = strstr(&packet[1], "=") + 1; - - unsigned reg = strtoul(s_reg, NULL, 16); - unsigned value = strtoul(s_value, NULL, 16); - - if(reg < 16) { - stlink_write_reg(sl, ntohl(value), reg); - } else if(reg == 0x19) { - stlink_write_reg(sl, ntohl(value), 16); - } else if(reg == 0x1A) { - stlink_write_reg(sl, ntohl(value), 17); - } else if(reg == 0x1B) { - stlink_write_reg(sl, ntohl(value), 18); - } else if(reg == 0x1C) { - stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); - } else if(reg == 0x1D) { - stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); - } else if(reg == 0x1E) { - stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); - } else if(reg == 0x1F) { - stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); - } else if(reg >= 0x20 && reg < 0x40) { - stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); - } else if(reg == 0x40) { - stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); - } else { - reply = strdup("E00"); - } - - if(!reply) { - reply = strdup("OK"); - } - - break; - } - - case 'G': - for(int i = 0; i < 16; i++) { - char str[9] = {0}; - strncpy(str, &packet[1 + i * 8], 8); - uint32_t reg = strtoul(str, NULL, 16); - stlink_write_reg(sl, ntohl(reg), i); - } - - reply = strdup("OK"); - break; - - case 'm': { - char* s_start = &packet[1]; - char* s_count = strstr(&packet[1], ",") + 1; - - stm32_addr_t start = strtoul(s_start, NULL, 16); - unsigned count = strtoul(s_count, NULL, 16); - - unsigned adj_start = start % 4; - unsigned count_rnd = (count + adj_start + 4 - 1) / 4 * 4; - if (count_rnd > sl->flash_pgsz) - count_rnd = sl->flash_pgsz; - if (count_rnd > 0x1800) - count_rnd = 0x1800; - if (count_rnd < count) - count = count_rnd; - - stlink_read_mem32(sl, start - adj_start, count_rnd); - - reply = calloc(count * 2 + 1, 1); - for(unsigned int i = 0; i < count; i++) { - reply[i * 2 + 0] = hex[sl->q_buf[i + adj_start] >> 4]; - reply[i * 2 + 1] = hex[sl->q_buf[i + adj_start] & 0xf]; - } - - break; - } - - case 'M': { - char* s_start = &packet[1]; - char* s_count = strstr(&packet[1], ",") + 1; - char* hexdata = strstr(packet, ":") + 1; - - stm32_addr_t start = strtoul(s_start, NULL, 16); - unsigned count = strtoul(s_count, NULL, 16); - - if(start % 4) { - unsigned align_count = 4 - start % 4; - if (align_count > count) align_count = count; - for(unsigned int i = 0; i < align_count; i ++) { - char hex[3] = { hexdata[i*2], hexdata[i*2+1], 0 }; - uint8_t byte = strtoul(hex, NULL, 16); - sl->q_buf[i] = byte; - } - stlink_write_mem8(sl, start, align_count); - cache_change(start, align_count); - start += align_count; - count -= align_count; - hexdata += 2*align_count; - } - - if(count - count % 4) { - unsigned aligned_count = count - count % 4; - - for(unsigned int i = 0; i < aligned_count; i ++) { - char hex[3] = { hexdata[i*2], hexdata[i*2+1], 0 }; - uint8_t byte = strtoul(hex, NULL, 16); - sl->q_buf[i] = byte; - } - stlink_write_mem32(sl, start, aligned_count); - cache_change(start, aligned_count); - count -= aligned_count; - start += aligned_count; - hexdata += 2*aligned_count; - } - - if(count) { - for(unsigned int i = 0; i < count; i ++) { - char hex[3] = { hexdata[i*2], hexdata[i*2+1], 0 }; - uint8_t byte = strtoul(hex, NULL, 16); - sl->q_buf[i] = byte; - } - stlink_write_mem8(sl, start, count); - cache_change(start, count); - } - reply = strdup("OK"); - break; - } - - case 'Z': { - char *endptr; - stm32_addr_t addr = strtoul(&packet[3], &endptr, 16); - stm32_addr_t len = strtoul(&endptr[1], NULL, 16); - - switch (packet[1]) { - case '1': - if(update_code_breakpoint(sl, addr, 1) < 0) { - reply = strdup("E00"); - } else { - reply = strdup("OK"); - } - break; - - case '2': // insert write watchpoint - case '3': // insert read watchpoint - case '4': { // insert access watchpoint - enum watchfun wf; - if(packet[1] == '2') { - wf = WATCHWRITE; - } else if(packet[1] == '3') { - wf = WATCHREAD; - } else { - wf = WATCHACCESS; - } - - if(add_data_watchpoint(sl, wf, addr, len) < 0) { - reply = strdup("E00"); - } else { - reply = strdup("OK"); - break; - } - } - - default: - reply = strdup(""); - } - break; - } - case 'z': { - char *endptr; - stm32_addr_t addr = strtoul(&packet[3], &endptr, 16); - //stm32_addr_t len = strtoul(&endptr[1], NULL, 16); - - switch (packet[1]) { - case '1': // remove breakpoint - update_code_breakpoint(sl, addr, 0); - reply = strdup("OK"); - break; - - case '2' : // remove write watchpoint - case '3' : // remove read watchpoint - case '4' : // remove access watchpoint - if(delete_data_watchpoint(sl, addr) < 0) { - reply = strdup("E00"); - } else { - reply = strdup("OK"); - break; - } - - default: - reply = strdup(""); - } - break; - } - - case '!': { - /* - * Enter extended mode which allows restarting. - * We do support that always. - */ - - /* - * Also, set to persistent mode - * to allow GDB disconnect. - */ - st->persistent = 1; - - reply = strdup("OK"); - - break; - } - - case 'R': { - /* Reset the core. */ - - stlink_reset(sl); - init_code_breakpoints(sl); - init_data_watchpoints(sl); - - attached = 1; - - reply = strdup("OK"); - - break; - } - - default: - reply = strdup(""); - } - - if(reply) { - DLOG("send: %s\n", reply); - - int result = gdb_send_packet(client, reply); - if(result != 0) { - ELOG("cannot send: %d\n", result); - free(reply); - free(packet); -#ifdef __MINGW32__ - win32_close_socket(sock); -#endif - return 1; - } - - free(reply); - } - - free(packet); - } - -#ifdef __MINGW32__ - win32_close_socket(sock); -#endif - - return 0; -} diff --git a/gdbserver/gdb-server.h b/gdbserver/gdb-server.h deleted file mode 100644 index 8c8d471..0000000 --- a/gdbserver/gdb-server.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _GDB_SERVER_H -#define _GDB_SERVER_H - -#define STRINGIFY_inner(name) #name -#define STRINGIFY(name) STRINGIFY_inner(name) - -#define DEFAULT_LOGGING_LEVEL 50 -#define DEBUG_LOGGING_LEVEL 100 -#define DEFAULT_GDB_LISTEN_PORT 4242 - -#endif diff --git a/include/stlink.h b/include/stlink.h new file mode 100644 index 0000000..24b4dcf --- /dev/null +++ b/include/stlink.h @@ -0,0 +1,735 @@ +/* + * File: stlink.h + * + * This should contain all the common top level stlink interfaces, regardless + * of how the backend does the work.... + */ +#ifndef STLINK_H +#define STLINK_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + // Max data transfer size. + // 6kB = max mem32_read block, 8kB sram + //#define Q_BUF_LEN 96 +#define Q_BUF_LEN (1024 * 100) + + // st-link vendor cmd's +#define USB_ST_VID 0x0483 +#define USB_STLINK_PID 0x3744 +#define USB_STLINK_32L_PID 0x3748 +#define USB_STLINK_NUCLEO_PID 0x374b + + // STLINK_DEBUG_RESETSYS, etc: +#define STLINK_OK 0x80 +#define STLINK_FALSE 0x81 +#define STLINK_CORE_RUNNING 0x80 +#define STLINK_CORE_HALTED 0x81 +#define STLINK_CORE_STAT_UNKNOWN -1 + +#define STLINK_GET_VERSION 0xf1 +#define STLINK_GET_CURRENT_MODE 0xf5 +#define STLINK_GET_TARGET_VOLTAGE 0xF7 + +#define STLINK_DEBUG_COMMAND 0xF2 +#define STLINK_DFU_COMMAND 0xF3 +#define STLINK_DFU_EXIT 0x07 + // enter dfu could be 0x08? + + // STLINK_GET_CURRENT_MODE +#define STLINK_DEV_DFU_MODE 0x00 +#define STLINK_DEV_MASS_MODE 0x01 +#define STLINK_DEV_DEBUG_MODE 0x02 +#define STLINK_DEV_UNKNOWN_MODE -1 + + // jtag mode cmds +#define STLINK_DEBUG_ENTER 0x20 +#define STLINK_DEBUG_EXIT 0x21 +#define STLINK_DEBUG_READCOREID 0x22 +#define STLINK_DEBUG_GETSTATUS 0x01 +#define STLINK_DEBUG_FORCEDEBUG 0x02 +#define STLINK_DEBUG_RESETSYS 0x03 +#define STLINK_DEBUG_READALLREGS 0x04 +#define STLINK_DEBUG_READREG 0x05 +#define STLINK_DEBUG_WRITEREG 0x06 +#define STLINK_DEBUG_READMEM_32BIT 0x07 +#define STLINK_DEBUG_WRITEMEM_32BIT 0x08 +#define STLINK_DEBUG_RUNCORE 0x09 +#define STLINK_DEBUG_STEPCORE 0x0a +#define STLINK_DEBUG_SETFP 0x0b +#define STLINK_DEBUG_WRITEMEM_8BIT 0x0d +#define STLINK_DEBUG_CLEARFP 0x0e +#define STLINK_DEBUG_WRITEDEBUGREG 0x0f +#define STLINK_DEBUG_ENTER_SWD 0xa3 +#define STLINK_DEBUG_ENTER_JTAG 0x00 + + // TODO - possible poor names... +#define STLINK_SWD_ENTER 0x30 +#define STLINK_SWD_READCOREID 0x32 // TBD +#define STLINK_JTAG_WRITEDEBUG_32BIT 0x35 +#define STLINK_JTAG_READDEBUG_32BIT 0x36 +#define STLINK_JTAG_DRIVE_NRST 0x3c +#define STLINK_JTAG_DRIVE_NRST 0x3c + + // cortex m3 technical reference manual +#define CM3_REG_CPUID 0xE000ED00 +#define CM3_REG_FP_CTRL 0xE0002000 +#define CM3_REG_FP_COMP0 0xE0002008 + + /* cortex core ids */ + // TODO clean this up... +#define STM32VL_CORE_ID 0x1ba01477 +#define STM32L_CORE_ID 0x2ba01477 +#define STM32F3_CORE_ID 0x2ba01477 +#define STM32F4_CORE_ID 0x2ba01477 +#define STM32F0_CORE_ID 0xbb11477 +#define CORE_M3_R1 0x1BA00477 +#define CORE_M3_R2 0x4BA00477 +#define CORE_M4_R0 0x2BA01477 + + /* + * Chip IDs are explained in the appropriate programming manual for the + * DBGMCU_IDCODE register (0xE0042000) + */ + // stm32 chipids, only lower 12 bits.. +#define STM32_CHIPID_F1_MEDIUM 0x410 +#define STM32_CHIPID_F2 0x411 +#define STM32_CHIPID_F1_LOW 0x412 +#define STM32_CHIPID_F4 0x413 +#define STM32_CHIPID_F1_HIGH 0x414 +#define STM32_CHIPID_L4 0x415 /* Seen on L4x6 (RM0351) */ +#define STM32_CHIPID_L1_MEDIUM 0x416 +#define STM32_CHIPID_L0 0x417 +#define STM32_CHIPID_F1_CONN 0x418 +#define STM32_CHIPID_F4_HD 0x419 +#define STM32_CHIPID_F1_VL_MEDIUM_LOW 0x420 + +#define STM32_CHIPID_F446 0x421 +#define STM32_CHIPID_F3 0x422 +#define STM32_CHIPID_F4_LP 0x423 + +#define STM32_CHIPID_F411RE 0x431 + +#define STM32_CHIPID_L1_MEDIUM_PLUS 0x427 +#define STM32_CHIPID_F1_VL_HIGH 0x428 +#define STM32_CHIPID_L1_CAT2 0x429 + +#define STM32_CHIPID_F1_XL 0x430 + +#define STM32_CHIPID_F37x 0x432 +#define STM32_CHIPID_F4_DE 0x433 +#define STM32_CHIPID_F4_DE 0x433 + +#define STM32_CHIPID_F4_DSI 0x434 + +#define STM32_CHIPID_L1_HIGH 0x436 +#define STM32_CHIPID_L152_RE 0x437 +#define STM32_CHIPID_F334 0x438 + +#define STM32_CHIPID_F3_SMALL 0x439 +#define STM32_CHIPID_F0 0x440 +#define STM32_CHIPID_F09X 0x442 +#define STM32_CHIPID_F0_SMALL 0x444 + +#define STM32_CHIPID_F04 0x445 + +#define STM32_CHIPID_F303_HIGH 0x446 + +#define STM32_CHIPID_F0_CAN 0x448 + +#define STM32_CHIPID_F7 0x449 + + /* + * 0x436 is actually assigned to some L1 chips that are called "Medium-Plus" + * and some that are called "High". 0x427 is assigned to the other "Medium- + * plus" chips. To make it a bit simpler we just call 427 MEDIUM_PLUS and + * 0x436 HIGH. + */ + + // Constant STM32 memory map figures +#define STM32_FLASH_BASE 0x08000000 +#define STM32_SRAM_BASE 0x20000000 + + /* Cortex™-M3 Technical Reference Manual */ + /* Debug Halting Control and Status Register */ +#define DHCSR 0xe000edf0 +#define DCRSR 0xe000edf4 +#define DCRDR 0xe000edf8 +#define DBGKEY 0xa05f0000 + + /* Enough space to hold both a V2 command or a V1 command packaged as generic scsi*/ +#define C_BUF_LEN 32 + + enum flash_type { + FLASH_TYPE_UNKNOWN = 0, + FLASH_TYPE_F0, + FLASH_TYPE_L0, + FLASH_TYPE_F4, + FLASH_TYPE_L4, + }; + + typedef struct chip_params_ { + uint32_t chip_id; + char* description; + enum flash_type flash_type; + uint32_t flash_size_reg; + uint32_t flash_pagesize; + uint32_t sram_size; + uint32_t bootrom_base, bootrom_size; + } chip_params_t; + + + // These maps are from a combination of the Programming Manuals, and + // also the Reference manuals. (flash size reg is normally in ref man) + static const chip_params_t devices[] = { + { + //RM0385 and DS10916 document was used to find these paramaters + .chip_id = STM32_CHIPID_F7, + .description = "F7 device", + .flash_type = FLASH_TYPE_F4, + .flash_size_reg = 0x1ff0f442, // section 41.2 + .flash_pagesize = 0x800, // No flash pages + .sram_size = 0x50000, // "SRAM" byte size in hex from DS Fig 18 + .bootrom_base = 0x00100000, // "System memory" starting address from DS Fig 18 + .bootrom_size = 0xEDC0 // "System memory" byte size in hex from DS Fig 18 + }, + { // table 2, PM0063 + .chip_id = STM32_CHIPID_F1_MEDIUM, + .description = "F1 Medium-density device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7e0, + .flash_pagesize = 0x400, + .sram_size = 0x5000, + .bootrom_base = 0x1ffff000, + .bootrom_size = 0x800 + }, + { // table 1, PM0059 + .chip_id = STM32_CHIPID_F2, + .description = "F2 device", + .flash_type = FLASH_TYPE_F4, + .flash_size_reg = 0x1fff7a22, /* As in RM0033 Rev 5*/ + .flash_pagesize = 0x20000, + .sram_size = 0x20000, + .bootrom_base = 0x1fff0000, + .bootrom_size = 0x7800 + }, + { // PM0063 + .chip_id = STM32_CHIPID_F1_LOW, + .description = "F1 Low-density device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7e0, + .flash_pagesize = 0x400, + .sram_size = 0x2800, + .bootrom_base = 0x1ffff000, + .bootrom_size = 0x800 + }, + { + .chip_id = STM32_CHIPID_F4, + .description = "F4 device", + .flash_type = FLASH_TYPE_F4, + .flash_size_reg = 0x1FFF7A22, /* As in rm0090 since Rev 2*/ + .flash_pagesize = 0x4000, + .sram_size = 0x30000, + .bootrom_base = 0x1fff0000, + .bootrom_size = 0x7800 + }, + { + .chip_id = STM32_CHIPID_F4_DSI, + .description = "F46x and F47x device", + .flash_type = FLASH_TYPE_F4, + .flash_size_reg = 0x1FFF7A22, /* As in rm0090 since Rev 2*/ + .flash_pagesize = 0x4000, + .sram_size = 0x40000, + .bootrom_base = 0x1fff0000, + .bootrom_size = 0x7800 + }, + { + .chip_id = STM32_CHIPID_F4_HD, + .description = "F42x and F43x device", + .flash_type = FLASH_TYPE_F4, + .flash_size_reg = 0x1FFF7A22, /* As in rm0090 since Rev 2*/ + .flash_pagesize = 0x4000, + .sram_size = 0x40000, + .bootrom_base = 0x1fff0000, + .bootrom_size = 0x7800 + }, + { + .chip_id = STM32_CHIPID_F4_LP, + .description = "F4 device (low power)", + .flash_type = FLASH_TYPE_F4, + .flash_size_reg = 0x1FFF7A22, + .flash_pagesize = 0x4000, + .sram_size = 0x10000, + .bootrom_base = 0x1fff0000, + .bootrom_size = 0x7800 + }, + { + .chip_id = STM32_CHIPID_F411RE, + .description = "F4 device (low power) - stm32f411re", + .flash_type = FLASH_TYPE_F4, + .flash_size_reg = 0x1FFF7A22, + .flash_pagesize = 0x4000, + .sram_size = 0x20000, + .bootrom_base = 0x1fff0000, + .bootrom_size = 0x7800 + }, + { + .chip_id = STM32_CHIPID_F4_DE, + .description = "F4 device (Dynamic Efficency)", + .flash_type = FLASH_TYPE_F4, + .flash_size_reg = 0x1FFF7A22, + .flash_pagesize = 0x4000, + .sram_size = 0x18000, + .bootrom_base = 0x1fff0000, + .bootrom_size = 0x7800 + }, + { + .chip_id = STM32_CHIPID_F1_HIGH, + .description = "F1 High-density device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7e0, + .flash_pagesize = 0x800, + .sram_size = 0x10000, + .bootrom_base = 0x1ffff000, + .bootrom_size = 0x800 + }, + { + // This ignores the EEPROM! (and uses the page erase size, + // not the sector write protection...) + .chip_id = STM32_CHIPID_L1_MEDIUM, + .description = "L1 Med-density device", + .flash_type = FLASH_TYPE_L0, + .flash_size_reg = 0x1ff8004c, + .flash_pagesize = 0x100, + .sram_size = 0x4000, + .bootrom_base = 0x1ff00000, + .bootrom_size = 0x1000 + }, + { + .chip_id = STM32_CHIPID_L1_CAT2, + .description = "L1 Cat.2 device", + .flash_type = FLASH_TYPE_L0, + .flash_size_reg = 0x1ff8004c, + .flash_pagesize = 0x100, + .sram_size = 0x8000, + .bootrom_base = 0x1ff00000, + .bootrom_size = 0x1000 + }, + { + .chip_id = STM32_CHIPID_L1_MEDIUM_PLUS, + .description = "L1 Medium-Plus-density device", + .flash_type = FLASH_TYPE_L0, + .flash_size_reg = 0x1ff800cc, + .flash_pagesize = 0x100, + .sram_size = 0x8000,/*Not completely clear if there are some with 48K*/ + .bootrom_base = 0x1ff00000, + .bootrom_size = 0x1000 + }, + { + .chip_id = STM32_CHIPID_L1_HIGH, + .description = "L1 High-density device", + .flash_type = FLASH_TYPE_L0, + .flash_size_reg = 0x1ff800cc, + .flash_pagesize = 0x100, + .sram_size = 0xC000, /*Not completely clear if there are some with 32K*/ + .bootrom_base = 0x1ff00000, + .bootrom_size = 0x1000 + }, + { + .chip_id = STM32_CHIPID_L152_RE, + .description = "L152RE", + .flash_type = FLASH_TYPE_L0, + .flash_size_reg = 0x1ff800cc, + .flash_pagesize = 0x100, + .sram_size = 0x14000, /*Not completely clear if there are some with 32K*/ + .bootrom_base = 0x1ff00000, + .bootrom_size = 0x1000 + }, + { + .chip_id = STM32_CHIPID_F1_CONN, + .description = "F1 Connectivity line device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7e0, + .flash_pagesize = 0x800, + .sram_size = 0x10000, + .bootrom_base = 0x1fffb000, + .bootrom_size = 0x4800 + }, + {//Low and Medium density VL have same chipid. RM0041 25.6.1 + .chip_id = STM32_CHIPID_F1_VL_MEDIUM_LOW, + .description = "F1 Medium/Low-density Value Line device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7e0, + .flash_pagesize = 0x400, + .sram_size = 0x2000,//0x1000 for low density devices + .bootrom_base = 0x1ffff000, + .bootrom_size = 0x800 + }, + { + // STM32F446x family. Support based on DM00135183.pdf (RM0390) document. + .chip_id = STM32_CHIPID_F446, + .description = "F446 device", + .flash_type = FLASH_TYPE_F4, + .flash_size_reg = 0x1fff7a22, + .flash_pagesize = 0x20000, + .sram_size = 0x20000, + .bootrom_base = 0x1fff0000, + .bootrom_size = 0x7800 + }, + { + // This is STK32F303VCT6 device from STM32 F3 Discovery board. + // Support based on DM00043574.pdf (RM0316) document. + .chip_id = STM32_CHIPID_F3, + .description = "F3 device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7cc, + .flash_pagesize = 0x800, + .sram_size = 0xa000, + .bootrom_base = 0x1ffff000, + .bootrom_size = 0x800 + }, + { + // This is STK32F373VCT6 device from STM32 F373 eval board + // Support based on 303 above (37x and 30x have same memory map) + .chip_id = STM32_CHIPID_F37x, + .description = "F3 device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7cc, + .flash_pagesize = 0x800, + .sram_size = 0xa000, + .bootrom_base = 0x1ffff000, + .bootrom_size = 0x800 + }, + { + .chip_id = STM32_CHIPID_F1_VL_HIGH, + .description = "F1 High-density value line device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7e0, + .flash_pagesize = 0x800, + .sram_size = 0x8000, + .bootrom_base = 0x1ffff000, + .bootrom_size = 0x800 + }, + { + .chip_id = STM32_CHIPID_F1_XL, + .description = "F1 XL-density device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7e0, + .flash_pagesize = 0x800, + .sram_size = 0x18000, + .bootrom_base = 0x1fffe000, + .bootrom_size = 0x1800 + }, + { + //Use this as an example for mapping future chips: + //RM0091 document was used to find these paramaters + .chip_id = STM32_CHIPID_F0_CAN, + .description = "F07x device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7cc, // "Flash size data register" (pg735) + .flash_pagesize = 0x800, // Page sizes listed in Table 4 + .sram_size = 0x4000, // "SRAM" byte size in hex from Table 2 + .bootrom_base = 0x1fffC800, // "System memory" starting address from Table 2 + .bootrom_size = 0x3000 // "System memory" byte size in hex from Table 2 + }, + { + //Use this as an example for mapping future chips: + //RM0091 document was used to find these paramaters + .chip_id = STM32_CHIPID_F0, + .description = "F0 device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7cc, // "Flash size data register" (pg735) + .flash_pagesize = 0x400, // Page sizes listed in Table 4 + .sram_size = 0x2000, // "SRAM" byte size in hex from Table 2 + .bootrom_base = 0x1fffec00, // "System memory" starting address from Table 2 + .bootrom_size = 0xC00 // "System memory" byte size in hex from Table 2 + }, + { + .chip_id = STM32_CHIPID_F09X, + .description = "F09X device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7cc, // "Flash size data register" (pg735) + .flash_pagesize = 0x800, // Page sizes listed in Table 4 (pg 56) + .sram_size = 0x8000, // "SRAM" byte size in hex from Table 2 (pg 50) + .bootrom_base = 0x1fffd800, // "System memory" starting address from Table 2 + .bootrom_size = 0x2000 // "System memory" byte size in hex from Table 2 + }, + { + //Use this as an example for mapping future chips: + //RM0091 document was used to find these paramaters + .chip_id = STM32_CHIPID_F04, + .description = "F04x device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7cc, // "Flash size data register" (pg735) + .flash_pagesize = 0x400, // Page sizes listed in Table 4 + .sram_size = 0x1800, // "SRAM" byte size in hex from Table 2 + .bootrom_base = 0x1fffec00, // "System memory" starting address from Table 2 + .bootrom_size = 0xC00 // "System memory" byte size in hex from Table 2 + }, + { + //Use this as an example for mapping future chips: + //RM0091 document was used to find these paramaters + .chip_id = STM32_CHIPID_F0_SMALL, + .description = "F0 small device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7cc, // "Flash size data register" (pg735) + .flash_pagesize = 0x400, // Page sizes listed in Table 4 + .sram_size = 0x1000, // "SRAM" byte size in hex from Table 2 + .bootrom_base = 0x1fffec00, // "System memory" starting address from Table 2 + .bootrom_size = 0xC00 // "System memory" byte size in hex from Table 2 + }, + { + // STM32F30x + .chip_id = STM32_CHIPID_F3_SMALL, + .description = "F3 small device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7cc, + .flash_pagesize = 0x800, + .sram_size = 0xa000, + .bootrom_base = 0x1fffd800, + .bootrom_size = 0x2000 + }, + { + // STM32L0x + // RM0367,RM0377 documents was used to find these parameters + .chip_id = STM32_CHIPID_L0, + .description = "L0x3 device", + .flash_type = FLASH_TYPE_L0, + .flash_size_reg = 0x1ff8007c, + .flash_pagesize = 0x80, + .sram_size = 0x2000, + .bootrom_base = 0x1ff0000, + .bootrom_size = 0x1000 + }, + { + // STM32F334 + // RM0364 document was used to find these parameters + .chip_id = STM32_CHIPID_F334, + .description = "F334 device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7cc, + .flash_pagesize = 0x800, + .sram_size = 0x3000, + .bootrom_base = 0x1fffd800, + .bootrom_size = 0x2000 + }, + { + // This is STK32F303RET6 device from STM32 F3 Nucelo board. + // Support based on DM00043574.pdf (RM0316) document rev 5. + .chip_id = STM32_CHIPID_F303_HIGH, + .description = "F303 high density device", + .flash_type = FLASH_TYPE_F0, + .flash_size_reg = 0x1ffff7cc, // 34.2.1 Flash size data register + .flash_pagesize = 0x800, // 4.2.1 Flash memory organization + .sram_size = 0x10000, // 3.3 Embedded SRAM + .bootrom_base = 0x1fffd800, // 3.3.2 / Table 4 System Memory + .bootrom_size = 0x2000 + }, + { + // STM32L4x6 + // From RM0351. + .chip_id = STM32_CHIPID_L4, + .description = "L4 device", + .flash_type = FLASH_TYPE_L4, + .flash_size_reg = 0x1fff75e0, // "Flash size data register" (sec 45.2, page 1671) + .flash_pagesize = 0x800, // 2K (sec 3.2, page 78; also appears in sec 3.3.1 and tables 4-6 on pages 79-81) + // SRAM1 is "up to" 96k in the standard Cortex-M memory map; + // SRAM2 is 32k mapped at at 0x10000000 (sec 2.3, page 73 for + // sizes; table 2, page 74 for SRAM2 location) + .sram_size = 0x18000, + .bootrom_base = 0x1fff0000, // Tables 4-6, pages 80-81 (Bank 1 system memory) + .bootrom_size = 0x7000 // 28k (per bank), same source as base + }, + + }; + + + typedef struct { + uint32_t r[16]; + uint32_t s[32]; + uint32_t xpsr; + uint32_t main_sp; + uint32_t process_sp; + uint32_t rw; + uint32_t rw2; + uint8_t control; + uint8_t faultmask; + uint8_t basepri; + uint8_t primask; + uint32_t fpscr; + } reg; + + typedef uint32_t stm32_addr_t; + + typedef struct _cortex_m3_cpuid_ { + uint16_t implementer_id; + uint16_t variant; + uint16_t part; + uint8_t revision; + } cortex_m3_cpuid_t; + + typedef struct stlink_version_ { + uint32_t stlink_v; + uint32_t jtag_v; + uint32_t swim_v; + uint32_t st_vid; + uint32_t stlink_pid; + } stlink_version_t; + + typedef struct flash_loader { + stm32_addr_t loader_addr; /* loader sram adddr */ + stm32_addr_t buf_addr; /* buffer sram address */ + } flash_loader_t; + + enum transport_type { + TRANSPORT_TYPE_ZERO = 0, + TRANSPORT_TYPE_LIBSG, + TRANSPORT_TYPE_LIBUSB, + TRANSPORT_TYPE_INVALID + }; + + typedef struct _stlink stlink_t; + + typedef struct _stlink_backend { + void (*close) (stlink_t * sl); + int (*exit_debug_mode) (stlink_t * sl); + int (*enter_swd_mode) (stlink_t * sl); + int (*enter_jtag_mode) (stlink_t * stl); + int (*exit_dfu_mode) (stlink_t * stl); + int (*core_id) (stlink_t * stl); + int (*reset) (stlink_t * stl); + int (*jtag_reset) (stlink_t * stl, int value); + int (*run) (stlink_t * stl); + int (*status) (stlink_t * stl); + int (*version) (stlink_t *sl); + int (*read_debug32) (stlink_t *sl, uint32_t addr, uint32_t *data); + int (*read_mem32) (stlink_t *sl, uint32_t addr, uint16_t len); + int (*write_debug32) (stlink_t *sl, uint32_t addr, uint32_t data); + int (*write_mem32) (stlink_t *sl, uint32_t addr, uint16_t len); + int (*write_mem8) (stlink_t *sl, uint32_t addr, uint16_t len); + int (*read_all_regs) (stlink_t *sl, reg * regp); + int (*read_reg) (stlink_t *sl, int r_idx, reg * regp); + int (*read_all_unsupported_regs) (stlink_t *sl, reg *regp); + int (*read_unsupported_reg) (stlink_t *sl, int r_idx, reg *regp); + int (*write_unsupported_reg) (stlink_t *sl, uint32_t value, int idx, reg *regp); + int (*write_reg) (stlink_t *sl, uint32_t reg, int idx); + int (*step) (stlink_t * stl); + int (*current_mode) (stlink_t * stl); + int (*force_debug) (stlink_t *sl); + int32_t (*target_voltage) (stlink_t *sl); + } stlink_backend_t; + + struct _stlink { + struct _stlink_backend *backend; + void *backend_data; + + // Room for the command header + unsigned char c_buf[C_BUF_LEN]; + // Data transferred from or to device + unsigned char q_buf[Q_BUF_LEN]; + int q_len; + + // transport layer verboseness: 0 for no debug info, 10 for lots + int verbose; + uint32_t core_id; + uint32_t chip_id; + int core_stat; + + char serial[16]; + int serial_size; + +#define STM32_FLASH_PGSZ 1024 +#define STM32L_FLASH_PGSZ 256 + +#define STM32F4_FLASH_PGSZ 16384 +#define STM32F4_FLASH_SIZE (128 * 1024 * 8) + + enum flash_type flash_type; + stm32_addr_t flash_base; + size_t flash_size; + size_t flash_pgsz; + + /* sram settings */ +#define STM32_SRAM_SIZE (8 * 1024) +#define STM32L_SRAM_SIZE (16 * 1024) + stm32_addr_t sram_base; + size_t sram_size; + + // bootloader + stm32_addr_t sys_base; + size_t sys_size; + + struct stlink_version_ version; + }; + + //stlink_t* stlink_quirk_open(const char *dev_name, const int verbose); + + // delegated functions... + int stlink_enter_swd_mode(stlink_t *sl); + int stlink_enter_jtag_mode(stlink_t *sl); + int stlink_exit_debug_mode(stlink_t *sl); + int stlink_exit_dfu_mode(stlink_t *sl); + void stlink_close(stlink_t *sl); + int stlink_core_id(stlink_t *sl); + int stlink_reset(stlink_t *sl); + int stlink_jtag_reset(stlink_t *sl, int value); + int stlink_run(stlink_t *sl); + int stlink_status(stlink_t *sl); + int stlink_version(stlink_t *sl); + int stlink_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data); + int stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len); + int stlink_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data); + int stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len); + int stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len); + int stlink_read_all_regs(stlink_t *sl, reg *regp); + int stlink_read_all_unsupported_regs(stlink_t *sl, reg *regp); + int stlink_read_reg(stlink_t *sl, int r_idx, reg *regp); + int stlink_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp); + int stlink_write_unsupported_reg(stlink_t *sl, uint32_t value, int r_idx, reg *regp); + int stlink_write_reg(stlink_t *sl, uint32_t reg, int idx); + int stlink_step(stlink_t *sl); + int stlink_current_mode(stlink_t *sl); + int stlink_force_debug(stlink_t *sl); + int stlink_target_voltage(stlink_t *sl); + + + // unprocessed + int stlink_erase_flash_mass(stlink_t* sl); + int stlink_write_flash(stlink_t* sl, stm32_addr_t address, uint8_t* data, uint32_t length, uint8_t eraseonly); + int stlink_fwrite_flash(stlink_t *sl, const char* path, stm32_addr_t addr); + int stlink_fwrite_sram(stlink_t *sl, const char* path, stm32_addr_t addr); + int stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, uint32_t length); + + // PUBLIC + int stlink_chip_id(stlink_t *sl, uint32_t *chip_id); + int stlink_cpu_id(stlink_t *sl, cortex_m3_cpuid_t *cpuid); + + // privates, publics, the rest.... + // TODO sort what is private, and what is not + int stlink_erase_flash_page(stlink_t* sl, stm32_addr_t flashaddr); + uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr); + uint16_t read_uint16(const unsigned char *c, const int pt); + void stlink_core_stat(stlink_t *sl); + void stlink_print_data(stlink_t *sl); + unsigned int is_bigendian(void); + uint32_t read_uint32(const unsigned char *c, const int pt); + void write_uint32(unsigned char* buf, uint32_t ui); + void write_uint16(unsigned char* buf, uint16_t ui); + unsigned int is_core_halted(stlink_t *sl); + int write_buffer_to_sram(stlink_t *sl, flash_loader_t* fl, const uint8_t* buf, size_t size); + int write_loader_to_sram(stlink_t *sl, stm32_addr_t* addr, size_t* size); + int stlink_fread(stlink_t* sl, const char* path, stm32_addr_t addr, size_t size); + int run_flash_loader(stlink_t *sl, flash_loader_t* fl, stm32_addr_t target, const uint8_t* buf, size_t size); + int stlink_load_device_params(stlink_t *sl); + +#include "stlink/sg.h" +#include "stlink/usb.h" + +#ifdef __cplusplus +} +#endif + +#endif /* STLINK_H */ diff --git a/include/stlink/logging.h b/include/stlink/logging.h new file mode 100644 index 0000000..df816ca --- /dev/null +++ b/include/stlink/logging.h @@ -0,0 +1,34 @@ +/* + * Ugly, low performance, configurable level, logging "framework" + */ + +#ifndef UGLYLOGGING_H +#define UGLYLOGGING_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum ugly_loglevel { + UDEBUG = 90, + UINFO = 50, + UWARN = 30, + UERROR = 20, + UFATAL = 10 +}; + +int ugly_init(int maximum_threshold); +int ugly_log(int level, const char *tag, const char *format, ...); + +#define DLOG(format, args...) ugly_log(UDEBUG, __FILE__, format, ## args) +#define ILOG(format, args...) ugly_log(UINFO, __FILE__, format, ## args) +#define WLOG(format, args...) ugly_log(UWARN, __FILE__, format, ## args) +#define ELOG(format, args...) ugly_log(UERROR, __FILE__, format, ## args) +#define fatal(format, args...) ugly_log(UFATAL, __FILE__, format, ## args) + +#ifdef __cplusplus +} +#endif + +#endif /* UGLYLOGGING_H */ + diff --git a/include/stlink/mmap.h b/include/stlink/mmap.h new file mode 100644 index 0000000..71de819 --- /dev/null +++ b/include/stlink/mmap.h @@ -0,0 +1,31 @@ +#ifndef STLINK_MMAP_H +#define STLINK_MMAP_H + +#ifdef HAVE_SYS_MMAN_H +#include +#else + +#define PROT_READ (1<<0) +#define PROT_WRITE (1<<1) + +#define MAP_SHARED (1<<0) +#define MAP_PRIVATE (1<<1) + +#define MAP_ANONYMOUS (1<<5) + +#define MAP_FAILED ((void *)-1) + +#ifdef __cplusplus +extern "C" { +#endif + + void *mmap(void *addr, size_t len, int prot, int flags, int fd, long long offset); + int munmap(void *addr, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* HAVE_SYS_MMAN_H */ + +#endif /* STLINK_MMAP_H */ diff --git a/include/stlink/sg.h b/include/stlink/sg.h new file mode 100644 index 0000000..56780f4 --- /dev/null +++ b/include/stlink/sg.h @@ -0,0 +1,73 @@ +/* + * File: stlink/sg.h + * Author: karl + * + * Created on October 1, 2011, 11:29 PM + */ + +#ifndef STLINK_SG_H +#define STLINK_SG_H + +#include +#include "stlink.h" + +#ifdef __cplusplus +extern "C" { +#endif + + // device access +#define RDWR 0 +#define RO 1 +#define SG_TIMEOUT_SEC 1 // actually 1 is about 2 sec +#define SG_TIMEOUT_MSEC 3 * 1000 + // Each CDB can be a total of 6, 10, 12, or 16 bytes, later version + // of the SCSI standard also allow for variable-length CDBs (min. CDB is 6). + // the stlink needs max. 10 bytes. +#define CDB_6 6 +#define CDB_10 10 +#define CDB_12 12 +#define CDB_16 16 + +#define CDB_SL 10 + + // Query data flow direction. +#define Q_DATA_OUT 0 +#define Q_DATA_IN 1 + + // The SCSI Request Sense command is used to obtain sense data + // (error information) from a target device. + // http://en.wikipedia.org/wiki/SCSI_Request_Sense_Command +#define SENSE_BUF_LEN 32 + + + + struct stlink_libsg { + libusb_context* libusb_ctx; + libusb_device_handle *usb_handle; + unsigned ep_rep; + unsigned ep_req; + + int sg_fd; + int do_scsi_pt_err; + + unsigned char cdb_cmd_blk[CDB_SL]; + + int q_data_dir; // Q_DATA_IN, Q_DATA_OUT + // the start of the query data in the device memory space + uint32_t q_addr; + + // Sense (error information) data + // obsolete, this was fed to the scsi tools + unsigned char sense_buf[SENSE_BUF_LEN]; + + reg reg; + }; + + stlink_t* stlink_v1_open(const int verbose, int reset); + +#ifdef __cplusplus +} +#endif + +#endif /* STLINK_SG_H */ + diff --git a/include/stlink/usb.h b/include/stlink/usb.h new file mode 100644 index 0000000..76ac921 --- /dev/null +++ b/include/stlink/usb.h @@ -0,0 +1,51 @@ +/* + * File: stlink/usb.h + * Author: karl + * + * Created on October 1, 2011, 11:29 PM + */ + +#ifndef STLINK_USB_H +#define STLINK_USB_H + +#include +#include + +#include "stlink.h" +#include "stlink/logging.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define STLINK_SG_SIZE 31 +#define STLINK_CMD_SIZE 16 + + struct stlink_libusb { + libusb_context* libusb_ctx; + libusb_device_handle* usb_handle; + unsigned int ep_req; + unsigned int ep_rep; + int protocoll; + unsigned int sg_transfer_idx; + unsigned int cmd_len; + }; + + /** + * Open a stlink + * @param verbose Verbosity loglevel + * @param reset Reset stlink programmer + * @param serial Serial number to search for, when NULL the first stlink found is opened (binary format) + * @retval NULL Error while opening the stlink + * @retval !NULL Stlink found and ready to use + */ + stlink_t *stlink_open_usb(enum ugly_loglevel verbose, bool reset, char serial[16]); + size_t stlink_probe_usb(stlink_t **stdevs[]); + void stlink_probe_usb_free(stlink_t **stdevs[], size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* STLINK_USB_H */ + diff --git a/mingw/mingw.c b/mingw/mingw.c deleted file mode 100644 index 3c5d025..0000000 --- a/mingw/mingw.c +++ /dev/null @@ -1,270 +0,0 @@ -#ifdef __MINGW32__ - -#include "mingw.h" - -#undef socket -#undef connect -#undef accept -#undef shutdown - -#include -#include -#include - -int win32_poll(struct pollfd *fds, unsigned int nfds, int timo) -{ - struct timeval timeout, *toptr; - fd_set ifds, ofds, efds, *ip, *op; - unsigned int i, rc; - - /* Set up the file-descriptor sets in ifds, ofds and efds. */ - FD_ZERO(&ifds); - FD_ZERO(&ofds); - FD_ZERO(&efds); - for (i = 0, op = ip = 0; i < nfds; ++i) { - fds[i].revents = 0; - if(fds[i].events & (POLLIN|POLLPRI)) { - ip = &ifds; - FD_SET(fds[i].fd, ip); - } - if(fds[i].events & POLLOUT) { - op = &ofds; - FD_SET(fds[i].fd, op); - } - FD_SET(fds[i].fd, &efds); - } - - /* Set up the timeval structure for the timeout parameter */ - if(timo < 0) { - toptr = 0; - } else { - toptr = &timeout; - timeout.tv_sec = timo / 1000; - timeout.tv_usec = (timo - timeout.tv_sec * 1000) * 1000; - } - -#ifdef DEBUG_POLL - printf("Entering select() sec=%ld usec=%ld ip=%lx op=%lx\n", - (long)timeout.tv_sec, (long)timeout.tv_usec, (long)ip, (long)op); -#endif - rc = select(0, ip, op, &efds, toptr); -#ifdef DEBUG_POLL - printf("Exiting select rc=%d\n", rc); -#endif - - if(rc <= 0) - return rc; - - if(rc > 0) { - for ( i = 0; i < nfds; ++i) { - int fd = fds[i].fd; - if(fds[i].events & (POLLIN|POLLPRI) && FD_ISSET(fd, &ifds)) - fds[i].revents |= POLLIN; - if(fds[i].events & POLLOUT && FD_ISSET(fd, &ofds)) - fds[i].revents |= POLLOUT; - if(FD_ISSET(fd, &efds)) - /* Some error was detected ... should be some way to know. */ - fds[i].revents |= POLLHUP; -#ifdef DEBUG_POLL - printf("%d %d %d revent = %x\n", - FD_ISSET(fd, &ifds), FD_ISSET(fd, &ofds), FD_ISSET(fd, &efds), - fds[i].revents - ); -#endif - } - } - return rc; -} -static void -set_connect_errno(int winsock_err) -{ - switch(winsock_err) { - case WSAEINVAL: - case WSAEALREADY: - case WSAEWOULDBLOCK: - errno = EINPROGRESS; - break; - default: - errno = winsock_err; - break; - } -} - -static void -set_socket_errno(int winsock_err) -{ - switch(winsock_err) { - case WSAEWOULDBLOCK: - errno = EAGAIN; - break; - default: - errno = winsock_err; - break; - } -} -/* - * A wrapper around the socket() function. The purpose of this wrapper - * is to ensure that the global errno symbol is set if an error occurs, - * even if we are using winsock. - */ -SOCKET -win32_socket(int domain, int type, int protocol) -{ - SOCKET fd = socket(domain, type, protocol); - if(fd == INVALID_SOCKET) { - set_socket_errno(WSAGetLastError()); - } - return fd; -} -/* - * A wrapper around the connect() function. The purpose of this wrapper - * is to ensure that the global errno symbol is set if an error occurs, - * even if we are using winsock. - */ -int -win32_connect(SOCKET fd, struct sockaddr *addr, socklen_t addr_len) -{ - int rc = connect(fd, addr, addr_len); - assert(rc == 0 || rc == SOCKET_ERROR); - if(rc == SOCKET_ERROR) { - set_connect_errno(WSAGetLastError()); - } - return rc; -} - -/* - * A wrapper around the accept() function. The purpose of this wrapper - * is to ensure that the global errno symbol is set if an error occurs, - * even if we are using winsock. - */ -SOCKET -win32_accept(SOCKET fd, struct sockaddr *addr, socklen_t *addr_len) -{ - SOCKET newfd = accept(fd, addr, addr_len); - if(newfd == INVALID_SOCKET) { - set_socket_errno(WSAGetLastError()); - newfd = -1; - } - return newfd; -} - -/* - * A wrapper around the shutdown() function. The purpose of this wrapper - * is to ensure that the global errno symbol is set if an error occurs, - * even if we are using winsock. - */ -int -win32_shutdown(SOCKET fd, int mode) -{ - int rc = shutdown(fd, mode); - assert(rc == 0 || rc == SOCKET_ERROR); - if(rc == SOCKET_ERROR) { - set_socket_errno(WSAGetLastError()); - } - return rc; -} - -int win32_close_socket(SOCKET fd) -{ - int rc = closesocket(fd); - if(rc == SOCKET_ERROR) { - set_socket_errno(WSAGetLastError()); - } - return rc; -} - -ssize_t win32_write_socket(SOCKET fd, void *buf, int n) -{ - int rc = send(fd, buf, n, 0); - if(rc == SOCKET_ERROR) { - set_socket_errno(WSAGetLastError()); - } - return rc; -} - -ssize_t win32_read_socket(SOCKET fd, void *buf, int n) -{ - int rc = recv(fd, buf, n, 0); - if(rc == SOCKET_ERROR) { - set_socket_errno(WSAGetLastError()); - } - return rc; -} - - -char * win32_strtok_r(char *s, const char *delim, char **lasts) -{ - register char *spanp; - register int c, sc; - char *tok; - - - if (s == NULL && (s = *lasts) == NULL) - return (NULL); - - /* - * Skip (span) leading delimiters (s += strspn(s, delim), sort of). - */ -cont: - c = *s++; - for (spanp = (char *)delim; (sc = *spanp++) != 0;) { - if (c == sc) - goto cont; - } - - if (c == 0) { /* no non-delimiter characters */ - *lasts = NULL; - return (NULL); - } - tok = s - 1; - - /* - * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). - * Note that delim must have one NUL; we stop if we see that, too. - */ - for (;;) { - c = *s++; - spanp = (char *)delim; - do { - if ((sc = *spanp++) == c) { - if (c == 0) - s = NULL; - else - s[-1] = 0; - *lasts = s; - return (tok); - } - } while (sc != 0); - } - /* NOTREACHED */ -} - -char *win32_strsep (char **stringp, const char *delim) -{ - register char *s; - register const char *spanp; - register int c, sc; - char *tok; - - if ((s = *stringp) == NULL) - return (NULL); - for (tok = s;;) { - c = *s++; - spanp = delim; - do { - if ((sc = *spanp++) == c) { - if (c == 0) - s = NULL; - else - s[-1] = 0; - *stringp = s; - return (tok); - } - } while (sc != 0); - } - /* NOTREACHED */ -} - -#endif - - diff --git a/mingw/mingw.h b/mingw/mingw.h deleted file mode 100644 index cb33301..0000000 --- a/mingw/mingw.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifdef __MINGW32__ - -#include - -#define _USE_W32_SOCKETS 1 -#include - -#define ENOTCONN WSAENOTCONN -#define EWOULDBLOCK WSAEWOULDBLOCK -#define ENOBUFS WSAENOBUFS -#define ECONNRESET WSAECONNRESET -#define ESHUTDOWN WSAESHUTDOWN -#define EAFNOSUPPORT WSAEAFNOSUPPORT -#define EPROTONOSUPPORT WSAEPROTONOSUPPORT -#define EINPROGRESS WSAEINPROGRESS -#define EISCONN WSAEISCONN - -/* winsock doesn't feature poll(), so there is a version implemented - * in terms of select() in mingw.c. The following definitions - * are copied from linux man pages. A poll() macro is defined to - * call the version in mingw.c. - */ -#define POLLIN 0x0001 /* There is data to read */ -#define POLLPRI 0x0002 /* There is urgent data to read */ -#define POLLOUT 0x0004 /* Writing now will not block */ -#define POLLERR 0x0008 /* Error condition */ -#define POLLHUP 0x0010 /* Hung up */ -#define POLLNVAL 0x0020 /* Invalid request: fd not open */ -struct pollfd { - SOCKET fd; /* file descriptor */ - short events; /* requested events */ - short revents; /* returned events */ -}; -#define poll(x, y, z) win32_poll(x, y, z) - -/* These wrappers do nothing special except set the global errno variable if - * an error occurs (winsock doesn't do this by default). They set errno - * to unix-like values (i.e. WSAEWOULDBLOCK is mapped to EAGAIN), so code - * outside of this file "shouldn't" have to worry about winsock specific error - * handling. - */ -#define socket(x, y, z) win32_socket(x, y, z) -#define connect(x, y, z) win32_connect(x, y, z) -#define accept(x, y, z) win32_accept(x, y, z) -#define shutdown(x, y) win32_shutdown(x, y) -#define read(x, y, z) win32_read_socket(x, y, z) -#define write(x, y, z) win32_write_socket(x, y, z) - -/* Winsock uses int instead of the usual socklen_t */ -typedef int socklen_t; - -int win32_poll(struct pollfd *, unsigned int, int); -SOCKET win32_socket(int, int, int); -int win32_connect(SOCKET, struct sockaddr*, socklen_t); -SOCKET win32_accept(SOCKET, struct sockaddr*, socklen_t *); -int win32_shutdown(SOCKET, int); -int win32_close_socket(SOCKET fd); - -#define strtok_r(x, y, z) win32_strtok_r(x, y, z) -#define strsep(x,y) win32_strsep(x,y) - -char *win32_strtok_r(char *s, const char *delim, char **lasts); -char *win32_strsep(char **stringp, const char *delim); - -ssize_t win32_read_socket(SOCKET fd, void *buf, int n); -ssize_t win32_write_socket(SOCKET fd, void *buf, int n); - -static inline void sleep(unsigned ms) { Sleep(ms); } - -#endif diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..3e87824 --- /dev/null +++ b/src/common.c @@ -0,0 +1,2131 @@ +#define DEBUG_FLASH 0 + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "stlink.h" +#include "stlink/mmap.h" +#include "stlink/logging.h" + +#ifndef _WIN32 +#define O_BINARY 0 +#endif + +/* todo: stm32l15xxx flash memory, pm0062 manual */ + +/* stm32f FPEC flash controller interface, pm0063 manual */ +// TODO - all of this needs to be abstracted out.... +// STM32F05x is identical, based on RM0091 (DM00031936, Doc ID 018940 Rev 2, August 2012) +#define FLASH_REGS_ADDR 0x40022000 +#define FLASH_REGS_SIZE 0x28 + +#define FLASH_ACR (FLASH_REGS_ADDR + 0x00) +#define FLASH_KEYR (FLASH_REGS_ADDR + 0x04) +#define FLASH_SR (FLASH_REGS_ADDR + 0x0c) +#define FLASH_CR (FLASH_REGS_ADDR + 0x10) +#define FLASH_AR (FLASH_REGS_ADDR + 0x14) +#define FLASH_OBR (FLASH_REGS_ADDR + 0x1c) +#define FLASH_WRPR (FLASH_REGS_ADDR + 0x20) + +// For STM32F05x, the RDPTR_KEY may be wrong, but as it is not used anywhere... +#define FLASH_RDPTR_KEY 0x00a5 +#define FLASH_KEY1 0x45670123 +#define FLASH_KEY2 0xcdef89ab + +#define FLASH_SR_BSY 0 +#define FLASH_SR_EOP 5 + +#define FLASH_CR_PG 0 +#define FLASH_CR_PER 1 +#define FLASH_CR_MER 2 +#define FLASH_CR_STRT 6 +#define FLASH_CR_LOCK 7 + + +//32L = 32F1 same CoreID as 32F4! +#define STM32L_FLASH_REGS_ADDR ((uint32_t)0x40023c00) +#define STM32L_FLASH_ACR (STM32L_FLASH_REGS_ADDR + 0x00) +#define STM32L_FLASH_PECR (STM32L_FLASH_REGS_ADDR + 0x04) +#define STM32L_FLASH_PDKEYR (STM32L_FLASH_REGS_ADDR + 0x08) +#define STM32L_FLASH_PEKEYR (STM32L_FLASH_REGS_ADDR + 0x0c) +#define STM32L_FLASH_PRGKEYR (STM32L_FLASH_REGS_ADDR + 0x10) +#define STM32L_FLASH_OPTKEYR (STM32L_FLASH_REGS_ADDR + 0x14) +#define STM32L_FLASH_SR (STM32L_FLASH_REGS_ADDR + 0x18) +#define STM32L_FLASH_OBR (STM32L_FLASH_REGS_ADDR + 0x1c) +#define STM32L_FLASH_WRPR (STM32L_FLASH_REGS_ADDR + 0x20) +#define FLASH_L1_FPRG 10 +#define FLASH_L1_PROG 3 + +//32L4 register base is at FLASH_REGS_ADDR (0x40022000) +#define STM32L4_FLASH_KEYR (FLASH_REGS_ADDR + 0x08) +#define STM32L4_FLASH_SR (FLASH_REGS_ADDR + 0x10) +#define STM32L4_FLASH_CR (FLASH_REGS_ADDR + 0x14) +#define STM32L4_FLASH_OPTR (FLASH_REGS_ADDR + 0x20) + +#define STM32L4_FLASH_SR_BSY 16 +#define STM32L4_FLASH_SR_ERRMASK 0x3f8 /* SR [9:3] */ + +#define STM32L4_FLASH_CR_LOCK 31 /* Lock control register */ +#define STM32L4_FLASH_CR_PG 0 /* Program */ +#define STM32L4_FLASH_CR_PER 1 /* Page erase */ +#define STM32L4_FLASH_CR_MER1 2 /* Bank 1 erase */ +#define STM32L4_FLASH_CR_MER2 15 /* Bank 2 erase */ +#define STM32L4_FLASH_CR_STRT 16 /* Start command */ +#define STM32L4_FLASH_CR_BKER 11 /* Bank select for page erase */ +#define STM32L4_FLASH_CR_PNB 3 /* Page number (8 bits) */ +// Bits requesting flash operations (useful when we want to clear them) +#define STM32L4_FLASH_CR_OPBITS \ + ((1lu< le (don't swap) + buf[0] = ((unsigned char*) &ui)[0]; + buf[1] = ((unsigned char*) &ui)[1]; + buf[2] = ((unsigned char*) &ui)[2]; + buf[3] = ((unsigned char*) &ui)[3]; + } else { + buf[0] = ((unsigned char*) &ui)[3]; + buf[1] = ((unsigned char*) &ui)[2]; + buf[2] = ((unsigned char*) &ui)[1]; + buf[3] = ((unsigned char*) &ui)[0]; + } +} + +void write_uint16(unsigned char* buf, uint16_t ui) { + if (!is_bigendian()) { // le -> le (don't swap) + buf[0] = ((unsigned char*) &ui)[0]; + buf[1] = ((unsigned char*) &ui)[1]; + } else { + buf[0] = ((unsigned char*) &ui)[1]; + buf[1] = ((unsigned char*) &ui)[0]; + } +} + +uint32_t read_uint32(const unsigned char *c, const int pt) { + uint32_t ui; + char *p = (char *) &ui; + + if (!is_bigendian()) { // le -> le (don't swap) + p[0] = c[pt + 0]; + p[1] = c[pt + 1]; + p[2] = c[pt + 2]; + p[3] = c[pt + 3]; + } else { + p[0] = c[pt + 3]; + p[1] = c[pt + 2]; + p[2] = c[pt + 1]; + p[3] = c[pt + 0]; + } + return ui; +} + +static uint32_t __attribute__((unused)) read_flash_rdp(stlink_t *sl) { + uint32_t rdp; + stlink_read_debug32(sl, FLASH_WRPR, &rdp); + return rdp & 0xff; +} + +static inline uint32_t read_flash_wrpr(stlink_t *sl) { + uint32_t wrpr; + stlink_read_debug32(sl, FLASH_WRPR, &wrpr); + return wrpr; +} + +static inline uint32_t read_flash_obr(stlink_t *sl) { + uint32_t obr; + stlink_read_debug32(sl, FLASH_OBR, &obr); + return obr; +} + +static inline uint32_t read_flash_cr(stlink_t *sl) { + uint32_t reg, res; + + if (sl->flash_type == FLASH_TYPE_F4) + reg = FLASH_F4_CR; + else if (sl->flash_type == FLASH_TYPE_L4) + reg = STM32L4_FLASH_CR; + else + reg = FLASH_CR; + + stlink_read_debug32(sl, reg, &res); + +#if DEBUG_FLASH + fprintf(stdout, "CR:0x%x\n", res); +#endif + return res; +} + +static inline unsigned int is_flash_locked(stlink_t *sl) { + /* return non zero for true */ + uint32_t cr_lock_shift, cr = read_flash_cr(sl); + + if (sl->flash_type == FLASH_TYPE_F4) + cr_lock_shift = FLASH_F4_CR_LOCK; + else if (sl->flash_type == FLASH_TYPE_L4) + cr_lock_shift = STM32L4_FLASH_CR_LOCK; + else + cr_lock_shift = FLASH_CR_LOCK; + + return cr & (1 << cr_lock_shift); +} + +static void unlock_flash(stlink_t *sl) { + uint32_t key_reg; + /* the unlock sequence consists of 2 write cycles where + 2 key values are written to the FLASH_KEYR register. + an invalid sequence results in a definitive lock of + the FPEC block until next reset. + */ + if (sl->flash_type == FLASH_TYPE_F4) + key_reg = FLASH_F4_KEYR; + else if (sl->flash_type == FLASH_TYPE_L4) + key_reg = STM32L4_FLASH_KEYR; + else + key_reg = FLASH_KEYR; + + stlink_write_debug32(sl, key_reg, FLASH_KEY1); + stlink_write_debug32(sl, key_reg, FLASH_KEY2); +} + +static int unlock_flash_if(stlink_t *sl) { + /* unlock flash if already locked */ + + if (is_flash_locked(sl)) { + unlock_flash(sl); + if (is_flash_locked(sl)) { + WLOG("Failed to unlock flash!\n"); + return -1; + } + } + DLOG("Successfully unlocked flash\n"); + return 0; +} + +static void lock_flash(stlink_t *sl) { + uint32_t cr_lock_shift, cr_reg, n; + + if (sl->flash_type == FLASH_TYPE_F4) { + cr_reg = FLASH_F4_CR; + cr_lock_shift = FLASH_F4_CR_LOCK; + } else if (sl->flash_type == FLASH_TYPE_L4) { + cr_reg = STM32L4_FLASH_CR; + cr_lock_shift = STM32L4_FLASH_CR_LOCK; + } else { + cr_reg = FLASH_CR; + cr_lock_shift = FLASH_CR_LOCK; + } + + n = read_flash_cr(sl) | (1 << cr_lock_shift); + stlink_write_debug32(sl, cr_reg, n); +} + + +static void set_flash_cr_pg(stlink_t *sl) { + uint32_t cr_reg, x; + + x = read_flash_cr(sl); + + if (sl->flash_type == FLASH_TYPE_F4) { + cr_reg = FLASH_F4_CR; + x |= 1 << FLASH_CR_PG; + } else if (sl->flash_type == FLASH_TYPE_L4) { + cr_reg = STM32L4_FLASH_CR; + x &= ~STM32L4_FLASH_CR_OPBITS; + x |= 1 << STM32L4_FLASH_CR_PG; + } else { + cr_reg = FLASH_CR; + x = 1 << FLASH_CR_PG; + } + + stlink_write_debug32(sl, cr_reg, x); +} + +static void __attribute__((unused)) clear_flash_cr_pg(stlink_t *sl) { + uint32_t cr_reg, n; + + if (sl->flash_type == FLASH_TYPE_F4) + cr_reg = FLASH_F4_CR; + else if (sl->flash_type == FLASH_TYPE_L4) + cr_reg = STM32L4_FLASH_CR; + else + cr_reg = FLASH_CR; + + n = read_flash_cr(sl) & ~(1 << FLASH_CR_PG); + stlink_write_debug32(sl, cr_reg, n); +} + +static void set_flash_cr_per(stlink_t *sl) { + const uint32_t n = 1 << FLASH_CR_PER; + stlink_write_debug32(sl, FLASH_CR, n); +} + +static void __attribute__((unused)) clear_flash_cr_per(stlink_t *sl) { + const uint32_t n = read_flash_cr(sl) & ~(1 << FLASH_CR_PER); + stlink_write_debug32(sl, FLASH_CR, n); +} + +static void set_flash_cr_mer(stlink_t *sl) { + uint32_t val, cr_reg, cr_mer; + + if (sl->flash_type == FLASH_TYPE_F4) { + cr_reg = FLASH_F4_CR; + cr_mer = 1 << FLASH_CR_MER; + } else if (sl->flash_type == FLASH_TYPE_L4) { + cr_reg = STM32L4_FLASH_CR; + cr_mer = (1 << STM32L4_FLASH_CR_MER1) | (1 << STM32L4_FLASH_CR_MER2); + } else { + cr_reg = FLASH_CR; + cr_mer = 1 << FLASH_CR_MER; + } + + stlink_read_debug32(sl, cr_reg, &val); + val |= cr_mer; + stlink_write_debug32(sl, cr_reg, val); +} + +static void __attribute__((unused)) clear_flash_cr_mer(stlink_t *sl) { + uint32_t val, cr_reg, cr_mer; + + if (sl->flash_type == FLASH_TYPE_F4) { + cr_reg = FLASH_F4_CR; + cr_mer = 1 << FLASH_CR_MER; + } else if (sl->flash_type == FLASH_TYPE_L4) { + cr_reg = STM32L4_FLASH_CR; + cr_mer = (1 << STM32L4_FLASH_CR_MER1) | (1 << STM32L4_FLASH_CR_MER2); + } else { + cr_reg = FLASH_CR; + cr_mer = 1 << FLASH_CR_MER; + } + + stlink_read_debug32(sl, cr_reg, &val); + val &= ~cr_mer; + stlink_write_debug32(sl, cr_reg, val); +} + +static void set_flash_cr_strt(stlink_t *sl) { + uint32_t val, cr_reg, cr_strt; + + if (sl->flash_type == FLASH_TYPE_F4) { + cr_reg = FLASH_F4_CR; + cr_strt = 1 << FLASH_F4_CR_STRT; + } else if (sl->flash_type == FLASH_TYPE_L4) { + cr_reg = STM32L4_FLASH_CR; + cr_strt = 1 << STM32L4_FLASH_CR_STRT; + } else { + cr_reg = FLASH_CR; + cr_strt = 1 << FLASH_CR_STRT; + } + + stlink_read_debug32(sl, cr_reg, &val); + val |= cr_strt; + stlink_write_debug32(sl, cr_reg, val); +} + +static inline uint32_t read_flash_acr(stlink_t *sl) { + uint32_t acr; + stlink_read_debug32(sl, FLASH_ACR, &acr); + return acr; +} + +static inline uint32_t read_flash_sr(stlink_t *sl) { + uint32_t res, sr_reg; + + if (sl->flash_type == FLASH_TYPE_F4) + sr_reg = FLASH_F4_SR; + else if (sl->flash_type == FLASH_TYPE_L4) + sr_reg = STM32L4_FLASH_SR; + else + sr_reg = FLASH_SR; + + stlink_read_debug32(sl, sr_reg, &res); + + return res; +} + +static inline unsigned int is_flash_busy(stlink_t *sl) { + uint32_t sr_busy_shift; + + if (sl->flash_type == FLASH_TYPE_F4) + sr_busy_shift = FLASH_F4_SR_BSY; + else if (sl->flash_type == FLASH_TYPE_L4) + sr_busy_shift = STM32L4_FLASH_SR_BSY; + else + sr_busy_shift = FLASH_SR_BSY; + + return read_flash_sr(sl) & (1 << sr_busy_shift); +} + +static void wait_flash_busy(stlink_t *sl) { + /* todo: add some delays here */ + while (is_flash_busy(sl)) + ; +} + +static void wait_flash_busy_progress(stlink_t *sl) { + int i = 0; + fprintf(stdout, "Mass erasing"); + fflush(stdout); + while (is_flash_busy(sl)) { + usleep(10000); + i++; + if (i % 100 == 0) { + fprintf(stdout, "."); + fflush(stdout); + } + } + fprintf(stdout, "\n"); +} + +static inline unsigned int is_flash_eop(stlink_t *sl) { + return read_flash_sr(sl) & (1 << FLASH_SR_EOP); +} + +static void __attribute__((unused)) clear_flash_sr_eop(stlink_t *sl) { + const uint32_t n = read_flash_sr(sl) & ~(1 << FLASH_SR_EOP); + stlink_write_debug32(sl, FLASH_SR, n); +} + +static void __attribute__((unused)) wait_flash_eop(stlink_t *sl) { + /* todo: add some delays here */ + while (is_flash_eop(sl) == 0) + ; +} + +static inline void write_flash_ar(stlink_t *sl, uint32_t n) { + stlink_write_debug32(sl, FLASH_AR, n); +} + +static inline void write_flash_cr_psiz(stlink_t *sl, uint32_t n) { + uint32_t x = read_flash_cr(sl); + x &= ~(0x03 << 8); + x |= (n << 8); +#if DEBUG_FLASH + fprintf(stdout, "PSIZ:0x%x 0x%x\n", x, n); +#endif + stlink_write_debug32(sl, FLASH_F4_CR, x); +} + + +static inline void write_flash_cr_snb(stlink_t *sl, uint32_t n) { + uint32_t x = read_flash_cr(sl); + x &= ~FLASH_F4_CR_SNB_MASK; + x |= (n << FLASH_F4_CR_SNB); + x |= (1 << FLASH_F4_CR_SER); +#if DEBUG_FLASH + fprintf(stdout, "SNB:0x%x 0x%x\n", x, n); +#endif + stlink_write_debug32(sl, FLASH_F4_CR, x); +} + +static inline void write_flash_cr_bker_pnb(stlink_t *sl, uint32_t n) { + stlink_write_debug32(sl, STM32L4_FLASH_SR, 0xFFFFFFFF & ~(1<backend->close(sl); + free(sl); +} + +int stlink_exit_debug_mode(stlink_t *sl) { + int ret; + + DLOG("*** stlink_exit_debug_mode ***\n"); + ret = stlink_write_debug32(sl, DHCSR, DBGKEY); + if (ret == -1) + return ret; + + return sl->backend->exit_debug_mode(sl); +} + +int stlink_enter_swd_mode(stlink_t *sl) { + DLOG("*** stlink_enter_swd_mode ***\n"); + return sl->backend->enter_swd_mode(sl); +} + +// Force the core into the debug mode -> halted state. +int stlink_force_debug(stlink_t *sl) { + DLOG("*** stlink_force_debug_mode ***\n"); + return sl->backend->force_debug(sl); +} + +int stlink_exit_dfu_mode(stlink_t *sl) { + DLOG("*** stlink_exit_dfu_mode ***\n"); + return sl->backend->exit_dfu_mode(sl); +} + +int stlink_core_id(stlink_t *sl) { + int ret; + + DLOG("*** stlink_core_id ***\n"); + ret = sl->backend->core_id(sl); + if (ret == -1) { + ELOG("Failed to read core_id\n"); + return ret; + } + if (sl->verbose > 2) + stlink_print_data(sl); + DLOG("core_id = 0x%08x\n", sl->core_id); + return ret; +} + +int stlink_chip_id(stlink_t *sl, uint32_t *chip_id) { + int ret; + + ret = stlink_read_debug32(sl, 0xE0042000, chip_id); + if (ret == -1) + return ret; + + if (*chip_id == 0) + ret = stlink_read_debug32(sl, 0x40015800, chip_id); //Try Corex M0 DBGMCU_IDCODE register address + + return ret; +} + +/** + * Cortex m3 tech ref manual, CPUID register description + * @param sl stlink context + * @param cpuid pointer to the result object + */ +int stlink_cpu_id(stlink_t *sl, cortex_m3_cpuid_t *cpuid) { + uint32_t raw; + + if (stlink_read_debug32(sl, CM3_REG_CPUID, &raw)) + return -1; + + cpuid->implementer_id = (raw >> 24) & 0x7f; + cpuid->variant = (raw >> 20) & 0xf; + cpuid->part = (raw >> 4) & 0xfff; + cpuid->revision = raw & 0xf; + return 0; +} + +/** + * reads and decodes the flash parameters, as dynamically as possible + * @param sl + * @return 0 for success, or -1 for unsupported core type. + */ +int stlink_load_device_params(stlink_t *sl) { + ILOG("Loading device parameters....\n"); + const chip_params_t *params = NULL; + stlink_core_id(sl); + uint32_t chip_id; + uint32_t flash_size; + + stlink_chip_id(sl, &chip_id); + sl->chip_id = chip_id & 0xfff; + /* Fix chip_id for F4 rev A errata , Read CPU ID, as CoreID is the same for F2/F4*/ + if (sl->chip_id == 0x411) { + uint32_t cpuid; + stlink_read_debug32(sl, 0xE000ED00, &cpuid); + if ((cpuid & 0xfff0) == 0xc240) + sl->chip_id = 0x413; + } + + for (size_t i = 0; i < sizeof(devices) / sizeof(devices[0]); i++) { + if(devices[i].chip_id == sl->chip_id) { + params = &devices[i]; + break; + } + } + if (params == NULL) { + WLOG("unknown chip id! %#x\n", chip_id); + return -1; + } + + if (params->flash_type == FLASH_TYPE_UNKNOWN) { + WLOG("Invalid flash type, please check device declaration\n"); + return -1; + } + + + // These are fixed... + sl->flash_base = STM32_FLASH_BASE; + sl->sram_base = STM32_SRAM_BASE; + stlink_read_debug32(sl,(params->flash_size_reg) & ~3, &flash_size); + if (params->flash_size_reg & 2) + flash_size = flash_size >>16; + flash_size = flash_size & 0xffff; + + if ((sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS) && ( flash_size == 0 )) { + sl->flash_size = 128 * 1024; + } else if (sl->chip_id == STM32_CHIPID_L1_CAT2) { + sl->flash_size = (flash_size & 0xff) * 1024; + } else if ((sl->chip_id & 0xFFF) == STM32_CHIPID_L1_HIGH) { + // 0 is 384k and 1 is 256k + if ( flash_size == 0 ) { + sl->flash_size = 384 * 1024; + } else { + sl->flash_size = 256 * 1024; + } + } else { + sl->flash_size = flash_size * 1024; + } + sl->flash_type = params->flash_type; + sl->flash_pgsz = params->flash_pagesize; + sl->sram_size = params->sram_size; + sl->sys_base = params->bootrom_base; + sl->sys_size = params->bootrom_size; + + //medium and low devices have the same chipid. ram size depends on flash size. + //STM32F100xx datasheet Doc ID 16455 Table 2 + if(sl->chip_id == STM32_CHIPID_F1_VL_MEDIUM_LOW && sl->flash_size < 64 * 1024){ + sl->sram_size = 0x1000; + } + + ILOG("Device connected is: %s, id %#x\n", params->description, chip_id); + // TODO make note of variable page size here..... + ILOG("SRAM size: %#x bytes (%d KiB), Flash: %#x bytes (%d KiB) in pages of %zd bytes\n", + sl->sram_size, sl->sram_size / 1024, sl->flash_size, sl->flash_size / 1024, + sl->flash_pgsz); + return 0; +} + +int stlink_reset(stlink_t *sl) { + DLOG("*** stlink_reset ***\n"); + return sl->backend->reset(sl); +} + +int stlink_jtag_reset(stlink_t *sl, int value) { + DLOG("*** stlink_jtag_reset ***\n"); + return sl->backend->jtag_reset(sl, value); +} + +int stlink_run(stlink_t *sl) { + DLOG("*** stlink_run ***\n"); + return sl->backend->run(sl); +} + +int stlink_status(stlink_t *sl) { + int ret; + + DLOG("*** stlink_status ***\n"); + ret = sl->backend->status(sl); + stlink_core_stat(sl); + + return ret; +} + +/** + * Decode the version bits, originally from -sg, verified with usb + * @param sl stlink context, assumed to contain valid data in the buffer + * @param slv output parsed version object + */ +void _parse_version(stlink_t *sl, stlink_version_t *slv) { + uint32_t b0 = sl->q_buf[0]; //lsb + uint32_t b1 = sl->q_buf[1]; + uint32_t b2 = sl->q_buf[2]; + uint32_t b3 = sl->q_buf[3]; + uint32_t b4 = sl->q_buf[4]; + uint32_t b5 = sl->q_buf[5]; //msb + + // b0 b1 || b2 b3 | b4 b5 + // 4b | 6b | 6b || 2B | 2B + // stlink_v | jtag_v | swim_v || st_vid | stlink_pid + + slv->stlink_v = (b0 & 0xf0) >> 4; + slv->jtag_v = ((b0 & 0x0f) << 2) | ((b1 & 0xc0) >> 6); + slv->swim_v = b1 & 0x3f; + slv->st_vid = (b3 << 8) | b2; + slv->stlink_pid = (b5 << 8) | b4; + return; +} + +int stlink_version(stlink_t *sl) { + DLOG("*** looking up stlink version\n"); + if (sl->backend->version(sl)) + return -1; + + _parse_version(sl, &sl->version); + + DLOG("st vid = 0x%04x (expect 0x%04x)\n", sl->version.st_vid, USB_ST_VID); + DLOG("stlink pid = 0x%04x\n", sl->version.stlink_pid); + DLOG("stlink version = 0x%x\n", sl->version.stlink_v); + DLOG("jtag version = 0x%x\n", sl->version.jtag_v); + DLOG("swim version = 0x%x\n", sl->version.swim_v); + if (sl->version.jtag_v == 0) { + DLOG(" notice: the firmware doesn't support a jtag/swd interface\n"); + } + if (sl->version.swim_v == 0) { + DLOG(" notice: the firmware doesn't support a swim interface\n"); + } + + return 0; +} + +int stlink_target_voltage(stlink_t *sl) { + int voltage = -1; + DLOG("*** reading target voltage\n"); + if (sl->backend->target_voltage != NULL) { + voltage = sl->backend->target_voltage(sl); + if (voltage != -1) { + DLOG("target voltage = %ldmV\n", voltage); + } else { + DLOG("error reading target voltage\n"); + } + } else { + DLOG("reading voltage not supported by backend\n"); + } + return voltage; +} + +int stlink_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) { + int ret; + + ret = sl->backend->read_debug32(sl, addr, data); + if (!ret) + DLOG("*** stlink_read_debug32 %x is %#x\n", *data, addr); + + return ret; +} + +int stlink_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) { + DLOG("*** stlink_write_debug32 %x to %#x\n", data, addr); + return sl->backend->write_debug32(sl, addr, data); +} + +int stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + DLOG("*** stlink_write_mem32 %u bytes to %#x\n", len, addr); + if (len % 4 != 0) { + fprintf(stderr, "Error: Data length doesn't have a 32 bit alignment: +%d byte.\n", len % 4); + abort(); + } + return sl->backend->write_mem32(sl, addr, len); +} + +int stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + DLOG("*** stlink_read_mem32 ***\n"); + if (len % 4 != 0) { // !!! never ever: fw gives just wrong values + fprintf(stderr, "Error: Data length doesn't have a 32 bit alignment: +%d byte.\n", + len % 4); + abort(); + } + return sl->backend->read_mem32(sl, addr, len); +} + +int stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { + DLOG("*** stlink_write_mem8 ***\n"); + if (len > 0x40 ) { // !!! never ever: Writing more then 0x40 bytes gives unexpected behaviour + fprintf(stderr, "Error: Data length > 64: +%d byte.\n", + len); + abort(); + } + return sl->backend->write_mem8(sl, addr, len); +} + +int stlink_read_all_regs(stlink_t *sl, reg *regp) { + DLOG("*** stlink_read_all_regs ***\n"); + return sl->backend->read_all_regs(sl, regp); +} + +int stlink_read_all_unsupported_regs(stlink_t *sl, reg *regp) { + DLOG("*** stlink_read_all_unsupported_regs ***\n"); + return sl->backend->read_all_unsupported_regs(sl, regp); +} + +int stlink_write_reg(stlink_t *sl, uint32_t reg, int idx) { + DLOG("*** stlink_write_reg\n"); + return sl->backend->write_reg(sl, reg, idx); +} + +int stlink_read_reg(stlink_t *sl, int r_idx, reg *regp) { + DLOG("*** stlink_read_reg\n"); + DLOG(" (%d) ***\n", r_idx); + + if (r_idx > 20 || r_idx < 0) { + fprintf(stderr, "Error: register index must be in [0..20]\n"); + return -1; + } + + return sl->backend->read_reg(sl, r_idx, regp); +} + +int stlink_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) { + int r_convert; + + DLOG("*** stlink_read_unsupported_reg\n"); + DLOG(" (%d) ***\n", r_idx); + + /* Convert to values used by DCRSR */ + if (r_idx >= 0x1C && r_idx <= 0x1F) { /* primask, basepri, faultmask, or control */ + r_convert = 0x14; + } else if (r_idx == 0x40) { /* FPSCR */ + r_convert = 0x21; + } else if (r_idx >= 0x20 && r_idx < 0x40) { + r_convert = 0x40 + (r_idx - 0x20); + } else { + fprintf(stderr, "Error: register address must be in [0x1C..0x40]\n"); + return -1; + } + + return sl->backend->read_unsupported_reg(sl, r_convert, regp); +} + +int stlink_write_unsupported_reg(stlink_t *sl, uint32_t val, int r_idx, reg *regp) { + int r_convert; + + DLOG("*** stlink_write_unsupported_reg\n"); + DLOG(" (%d) ***\n", r_idx); + + /* Convert to values used by DCRSR */ + if (r_idx >= 0x1C && r_idx <= 0x1F) { /* primask, basepri, faultmask, or control */ + r_convert = r_idx; /* The backend function handles this */ + } else if (r_idx == 0x40) { /* FPSCR */ + r_convert = 0x21; + } else if (r_idx >= 0x20 && r_idx < 0x40) { + r_convert = 0x40 + (r_idx - 0x20); + } else { + fprintf(stderr, "Error: register address must be in [0x1C..0x40]\n"); + return -1; + } + + return sl->backend->write_unsupported_reg(sl, val, r_convert, regp); +} + +unsigned int is_core_halted(stlink_t *sl) { + /* return non zero if core is halted */ + stlink_status(sl); + return sl->q_buf[0] == STLINK_CORE_HALTED; +} + +int stlink_step(stlink_t *sl) { + DLOG("*** stlink_step ***\n"); + return sl->backend->step(sl); +} + +int stlink_current_mode(stlink_t *sl) { + int mode = sl->backend->current_mode(sl); + switch (mode) { + case STLINK_DEV_DFU_MODE: + DLOG("stlink current mode: dfu\n"); + return mode; + case STLINK_DEV_DEBUG_MODE: + DLOG("stlink current mode: debug (jtag or swd)\n"); + return mode; + case STLINK_DEV_MASS_MODE: + DLOG("stlink current mode: mass\n"); + return mode; + } + DLOG("stlink mode: unknown!\n"); + return STLINK_DEV_UNKNOWN_MODE; +} + + + + +// End of delegates.... Common code below here... + +// Endianness +// http://www.ibm.com/developerworks/aix/library/au-endianc/index.html +// const int i = 1; +// #define is_bigendian() ( (*(char*)&i) == 0 ) + +inline unsigned int is_bigendian(void) { + static volatile const unsigned int i = 1; + return *(volatile const char*) &i == 0; +} + +uint16_t read_uint16(const unsigned char *c, const int pt) { + uint32_t ui; + char *p = (char *) &ui; + + if (!is_bigendian()) { // le -> le (don't swap) + p[0] = c[pt + 0]; + p[1] = c[pt + 1]; + } else { + p[0] = c[pt + 1]; + p[1] = c[pt + 0]; + } + return ui; +} + +// same as above with entrypoint. + +void stlink_run_at(stlink_t *sl, stm32_addr_t addr) { + stlink_write_reg(sl, addr, 15); /* pc register */ + + stlink_run(sl); + + while (is_core_halted(sl) == 0) + usleep(3000000); +} + +void stlink_core_stat(stlink_t *sl) { + if (sl->q_len <= 0) + return; + + switch (sl->q_buf[0]) { + case STLINK_CORE_RUNNING: + sl->core_stat = STLINK_CORE_RUNNING; + DLOG(" core status: running\n"); + return; + case STLINK_CORE_HALTED: + sl->core_stat = STLINK_CORE_HALTED; + DLOG(" core status: halted\n"); + return; + default: + sl->core_stat = STLINK_CORE_STAT_UNKNOWN; + fprintf(stderr, " core status: unknown\n"); + } +} + +void stlink_print_data(stlink_t * sl) { + if (sl->q_len <= 0 || sl->verbose < UDEBUG) + return; + if (sl->verbose > 2) + fprintf(stdout, "data_len = %d 0x%x\n", sl->q_len, sl->q_len); + + for (int i = 0; i < sl->q_len; i++) { + if (i % 16 == 0) { + /* + if (sl->q_data_dir == Q_DATA_OUT) + fprintf(stdout, "\n<- 0x%08x ", sl->q_addr + i); + else + fprintf(stdout, "\n-> 0x%08x ", sl->q_addr + i); + */ + } + fprintf(stdout, " %02x", (unsigned int) sl->q_buf[i]); + } + fputs("\n\n", stdout); +} + +/* memory mapped file */ + +typedef struct mapped_file { + uint8_t* base; + size_t len; +} mapped_file_t; + +#define MAPPED_FILE_INITIALIZER { NULL, 0 } + +static int map_file(mapped_file_t* mf, const char* path) { + int error = -1; + struct stat st; + + const int fd = open(path, O_RDONLY | O_BINARY); + if (fd == -1) { + fprintf(stderr, "open(%s) == -1\n", path); + return -1; + } + + if (fstat(fd, &st) == -1) { + fprintf(stderr, "fstat() == -1\n"); + goto on_error; + } + + mf->base = (uint8_t*) mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (mf->base == MAP_FAILED) { + fprintf(stderr, "mmap() == MAP_FAILED\n"); + goto on_error; + } + + mf->len = st.st_size; + + /* success */ + error = 0; + +on_error: + close(fd); + + return error; +} + +static void unmap_file(mapped_file_t * mf) { + munmap((void*) mf->base, mf->len); + mf->base = (unsigned char*) MAP_FAILED; + mf->len = 0; +} + +/* Limit the block size to compare to 0x1800 + Anything larger will stall the STLINK2 + Maybe STLINK V1 needs smaller value!*/ +static int check_file(stlink_t* sl, mapped_file_t* mf, stm32_addr_t addr) { + size_t off; + size_t n_cmp = sl->flash_pgsz; + if ( n_cmp > 0x1800) + n_cmp = 0x1800; + + for (off = 0; off < mf->len; off += n_cmp) { + size_t aligned_size; + + /* adjust last page size */ + size_t cmp_size = n_cmp; + if ((off + n_cmp) > mf->len) + cmp_size = mf->len - off; + + aligned_size = cmp_size; + if (aligned_size & (4 - 1)) + aligned_size = (cmp_size + 4) & ~(4 - 1); + + stlink_read_mem32(sl, addr + off, aligned_size); + + if (memcmp(sl->q_buf, mf->base + off, cmp_size)) + return -1; + } + + return 0; +} + +int stlink_fwrite_sram +(stlink_t * sl, const char* path, stm32_addr_t addr) { + /* write the file in sram at addr */ + + int error = -1; + size_t off; + size_t len; + mapped_file_t mf = MAPPED_FILE_INITIALIZER; + uint32_t val; + + + if (map_file(&mf, path) == -1) { + fprintf(stderr, "map_file() == -1\n"); + return -1; + } + + /* check addr range is inside the sram */ + if (addr < sl->sram_base) { + fprintf(stderr, "addr too low\n"); + goto on_error; + } else if ((addr + mf.len) < addr) { + fprintf(stderr, "addr overruns\n"); + goto on_error; + } else if ((addr + mf.len) > (sl->sram_base + sl->sram_size)) { + fprintf(stderr, "addr too high\n"); + goto on_error; + } else if (addr & 3) { + /* todo */ + fprintf(stderr, "unaligned addr\n"); + goto on_error; + } + + len = mf.len; + + if(len & 3) { + len -= len & 3; + } + + /* do the copy by 1k blocks */ + for (off = 0; off < len; off += 1024) { + size_t size = 1024; + if ((off + size) > len) + size = len - off; + + memcpy(sl->q_buf, mf.base + off, size); + + /* round size if needed */ + if (size & 3) + size += 2; + + stlink_write_mem32(sl, addr + off, size); + } + + if(mf.len > len) { + memcpy(sl->q_buf, mf.base + len, mf.len - len); + stlink_write_mem8(sl, addr + len, mf.len - len); + } + + /* check the file ha been written */ + if (check_file(sl, &mf, addr) == -1) { + fprintf(stderr, "check_file() == -1\n"); + goto on_error; + } + + /* success */ + error = 0; + /* set stack*/ + stlink_read_debug32(sl, addr, &val); + stlink_write_reg(sl, val, 13); + /* Set PC to the reset routine*/ + stlink_read_debug32(sl, addr + 4, &val); + stlink_write_reg(sl, val, 15); + stlink_run(sl); + +on_error: + unmap_file(&mf); + return error; +} + +int stlink_fread(stlink_t* sl, const char* path, stm32_addr_t addr, size_t size) { + /* read size bytes from addr to file */ + + int error = -1; + size_t off; + + const int fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 00700); + if (fd == -1) { + fprintf(stderr, "open(%s) == -1\n", path); + return -1; + } + + if (size <1) + size = sl->flash_size; + + if (size > sl->flash_size) + size = sl->flash_size; + + size_t cmp_size = (sl->flash_pgsz > 0x1800)? 0x1800:sl->flash_pgsz; + for (off = 0; off < size; off += cmp_size) { + size_t aligned_size; + + /* adjust last page size */ + if ((off + cmp_size) > size) + cmp_size = size - off; + + aligned_size = cmp_size; + if (aligned_size & (4 - 1)) + aligned_size = (cmp_size + 4) & ~(4 - 1); + + stlink_read_mem32(sl, addr + off, aligned_size); + + if (write(fd, sl->q_buf, sl->q_len) != (ssize_t) aligned_size) { + fprintf(stderr, "write() != aligned_size\n"); + goto on_error; + } + } + + /* success */ + error = 0; + +on_error: + close(fd); + + return error; +} + +int write_buffer_to_sram(stlink_t *sl, flash_loader_t* fl, const uint8_t* buf, size_t size) { + /* write the buffer right after the loader */ + size_t chunk = size & ~0x3; + size_t rem = size & 0x3; + if (chunk) { + memcpy(sl->q_buf, buf, chunk); + stlink_write_mem32(sl, fl->buf_addr, chunk); + } + if (rem) { + memcpy(sl->q_buf, buf+chunk, rem); + stlink_write_mem8(sl, (fl->buf_addr)+chunk, rem); + } + return 0; +} + +uint32_t calculate_F4_sectornum(uint32_t flashaddr){ + uint32_t offset = 0; + flashaddr &= ~STM32_FLASH_BASE; //Page now holding the actual flash address + if (flashaddr >= 0x100000) { + offset = 12; + flashaddr -= 0x100000; + } + if (flashaddr<0x4000) return (offset + 0); + else if(flashaddr<0x8000) return(offset + 1); + else if(flashaddr<0xc000) return(offset + 2); + else if(flashaddr<0x10000) return(offset + 3); + else if(flashaddr<0x20000) return(offset + 4); + else return offset + (flashaddr/0x20000) +4; + +} + +uint32_t calculate_F7_sectornum(uint32_t flashaddr){ + flashaddr &= ~STM32_FLASH_BASE; //Page now holding the actual flash address + if(flashaddr<0x20000) return(flashaddr/0x8000); + else if(flashaddr<0x40000) return(4); + else return(flashaddr/0x40000) +4; + +} + +// Returns BKER:PNB for the given page address +uint32_t calculate_L4_page(stlink_t *sl, uint32_t flashaddr) { + uint32_t bker = 0; + uint32_t flashopt; + stlink_read_debug32(sl, STM32L4_FLASH_OPTR, &flashopt); + flashaddr -= STM32_FLASH_BASE; + if (flashopt & (1lu << STM32L4_FLASH_OPTR_DUALBANK)) { + uint32_t banksize = sl->flash_size / 2; + if (flashaddr >= banksize) { + flashaddr -= banksize; + bker = 0x100; + } + } + // For 1MB chips without the dual-bank option set, the page address will + // overflow into the BKER bit, which gives us the correct bank:page value. + return bker | flashaddr/sl->flash_pgsz; +} + +uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr){ + if ((sl->chip_id == STM32_CHIPID_F2) || (sl->chip_id == STM32_CHIPID_F4) || (sl->chip_id == STM32_CHIPID_F4_DE) || + (sl->chip_id == STM32_CHIPID_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) || + (sl->chip_id == STM32_CHIPID_F446) || (sl->chip_id == STM32_CHIPID_F4_DSI)) { + uint32_t sector=calculate_F4_sectornum(flashaddr); + if (sector>= 12) { + sector -= 12; + } + if (sector<4) sl->flash_pgsz=0x4000; + else if(sector<5) sl->flash_pgsz=0x10000; + else sl->flash_pgsz=0x20000; + } + else if (sl->chip_id == STM32_CHIPID_F7) { + uint32_t sector=calculate_F7_sectornum(flashaddr); + if (sector<4) sl->flash_pgsz=0x8000; + else if(sector<5) sl->flash_pgsz=0x20000; + else sl->flash_pgsz=0x40000; + } + return (sl->flash_pgsz); +} + +/** + * Erase a page of flash, assumes sl is fully populated with things like chip/core ids + * @param sl stlink context + * @param flashaddr an address in the flash page to erase + * @return 0 on success -ve on failure + */ +int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr) +{ + if (sl->flash_type == FLASH_TYPE_F4 || sl->flash_type == FLASH_TYPE_L4) { + /* wait for ongoing op to finish */ + wait_flash_busy(sl); + + /* unlock if locked */ + unlock_flash_if(sl); + + /* select the page to erase */ + if (sl->chip_id == STM32_CHIPID_L4) { + // calculate the actual bank+page from the address + uint32_t page = calculate_L4_page(sl, flashaddr); + + fprintf(stderr, "EraseFlash - Page:0x%x Size:0x%x ", page, stlink_calculate_pagesize(sl, flashaddr)); + + write_flash_cr_bker_pnb(sl, page); + } else if (sl->chip_id == STM32_CHIPID_F7) { + // calculate the actual page from the address + uint32_t sector=calculate_F7_sectornum(flashaddr); + + fprintf(stderr, "EraseFlash - Sector:0x%x Size:0x%x ", sector, stlink_calculate_pagesize(sl, flashaddr)); + + write_flash_cr_snb(sl, sector); + } else { + // calculate the actual page from the address + uint32_t sector=calculate_F4_sectornum(flashaddr); + + fprintf(stderr, "EraseFlash - Sector:0x%x Size:0x%x ", sector, stlink_calculate_pagesize(sl, flashaddr)); + + //the SNB values for flash sectors in the second bank do not directly follow the values for the first bank on 2mb devices... + if (sector >= 12) sector += 4; + + write_flash_cr_snb(sl, sector); + } + + /* start erase operation */ + set_flash_cr_strt(sl); + + /* wait for completion */ + wait_flash_busy(sl); + + /* relock the flash */ + //todo: fails to program if this is in + lock_flash(sl); +#if DEBUG_FLASH + fprintf(stdout, "Erase Final CR:0x%x\n", read_flash_cr(sl)); +#endif + } else if (sl->flash_type == FLASH_TYPE_L0) { + + uint32_t val; + uint32_t flash_regs_base; + if (sl->chip_id == STM32_CHIPID_L0) { + flash_regs_base = STM32L0_FLASH_REGS_ADDR; + } else { + flash_regs_base = STM32L_FLASH_REGS_ADDR; + } + + /* check if the locks are set */ + stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); + if((val & (1<<0))||(val & (1<<1))) { + /* disable pecr protection */ + stlink_write_debug32(sl, flash_regs_base + FLASH_PEKEYR_OFF, 0x89abcdef); + stlink_write_debug32(sl, flash_regs_base + FLASH_PEKEYR_OFF, 0x02030405); + + /* check pecr.pelock is cleared */ + stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); + if (val & (1 << 0)) { + WLOG("pecr.pelock not clear (%#x)\n", val); + return -1; + } + + /* unlock program memory */ + stlink_write_debug32(sl, flash_regs_base + FLASH_PRGKEYR_OFF, 0x8c9daebf); + stlink_write_debug32(sl, flash_regs_base + FLASH_PRGKEYR_OFF, 0x13141516); + + /* check pecr.prglock is cleared */ + stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); + if (val & (1 << 1)) { + WLOG("pecr.prglock not clear (%#x)\n", val); + return -1; + } + } + + /* set pecr.{erase,prog} */ + val |= (1 << 9) | (1 << 3); + stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); +#if 0 /* fix_to_be_confirmed */ + + /* wait for sr.busy to be cleared + * MP: Test shows that busy bit is not set here. Perhaps, PM0062 is + * wrong and we do not need to wait here for clearing the busy bit. + * TEXANE: ok, if experience says so and it works for you, we comment + * it. If someone has a problem, please drop an email. + */ + do { + stlink_read_debug32(sl, STM32L_FLASH_SR, &val) + } while((val & (1 << 0)) != 0); + +#endif /* fix_to_be_confirmed */ + + /* write 0 to the first word of the page to be erased */ + stlink_write_debug32(sl, flashaddr, 0); + + /* MP: It is better to wait for clearing the busy bit after issuing + page erase command, even though PM0062 recommends to wait before it. + Test shows that a few iterations is performed in the following loop + before busy bit is cleared.*/ + do { + stlink_read_debug32(sl, flash_regs_base + FLASH_SR_OFF, &val); + } while ((val & (1 << 0)) != 0); + + /* reset lock bits */ + stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); + val |= (1 << 0) | (1 << 1) | (1 << 2); + stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); + } else if (sl->flash_type == FLASH_TYPE_F0) { + /* wait for ongoing op to finish */ + wait_flash_busy(sl); + + /* unlock if locked */ + unlock_flash_if(sl); + + /* set the page erase bit */ + set_flash_cr_per(sl); + + /* select the page to erase */ + write_flash_ar(sl, flashaddr); + + /* start erase operation, reset by hw with bsy bit */ + set_flash_cr_strt(sl); + + /* wait for completion */ + wait_flash_busy(sl); + + /* relock the flash */ + lock_flash(sl); + } else { + WLOG("unknown coreid %x, page erase failed\n", sl->core_id); + return -1; + } + + /* todo: verify the erased page */ + + return 0; +} + +int stlink_erase_flash_mass(stlink_t *sl) { + if (sl->flash_type == FLASH_TYPE_L0) { + /* erase each page */ + int i = 0, num_pages = sl->flash_size/sl->flash_pgsz; + for (i = 0; i < num_pages; i++) { + /* addr must be an addr inside the page */ + stm32_addr_t addr = sl->flash_base + i * sl->flash_pgsz; + if (stlink_erase_flash_page(sl, addr) == -1) { + WLOG("Failed to erase_flash_page(%#zx) == -1\n", addr); + return -1; + } + fprintf(stdout,"-> Flash page at %5d/%5d erased\n", i, num_pages); + fflush(stdout); + } + fprintf(stdout, "\n"); + } else { + /* wait for ongoing op to finish */ + wait_flash_busy(sl); + + /* unlock if locked */ + unlock_flash_if(sl); + + /* set the mass erase bit */ + set_flash_cr_mer(sl); + + /* start erase operation, reset by hw with bsy bit */ + set_flash_cr_strt(sl); + + /* wait for completion */ + wait_flash_busy_progress(sl); + + /* relock the flash */ + lock_flash(sl); + + /* todo: verify the erased memory */ + } + return 0; +} + +int init_flash_loader(stlink_t *sl, flash_loader_t* fl) { + size_t size; + + /* allocate the loader in sram */ + if (write_loader_to_sram(sl, &fl->loader_addr, &size) == -1) { + WLOG("Failed to write flash loader to sram!\n"); + return -1; + } + + /* allocate a one page buffer in sram right after loader */ + fl->buf_addr = fl->loader_addr + size; + ILOG("Successfully loaded flash loader in sram\n"); + return 0; +} + +int write_loader_to_sram(stlink_t *sl, stm32_addr_t* addr, size_t* size) { + /* from openocd, contrib/loaders/flash/stm32.s */ + static const uint8_t loader_code_stm32vl[] = { + 0x08, 0x4c, /* ldr r4, STM32_FLASH_BASE */ + 0x1c, 0x44, /* add r4, r3 */ + /* write_half_word: */ + 0x01, 0x23, /* movs r3, #0x01 */ + 0x23, 0x61, /* str r3, [r4, #STM32_FLASH_CR_OFFSET] */ + 0x30, 0xf8, 0x02, 0x3b, /* ldrh r3, [r0], #0x02 */ + 0x21, 0xf8, 0x02, 0x3b, /* strh r3, [r1], #0x02 */ + /* busy: */ + 0xe3, 0x68, /* ldr r3, [r4, #STM32_FLASH_SR_OFFSET] */ + 0x13, 0xf0, 0x01, 0x0f, /* tst r3, #0x01 */ + 0xfb, 0xd0, /* beq busy */ + 0x13, 0xf0, 0x14, 0x0f, /* tst r3, #0x14 */ + 0x01, 0xd1, /* bne exit */ + 0x01, 0x3a, /* subs r2, r2, #0x01 */ + 0xf0, 0xd1, /* bne write_half_word */ + /* exit: */ + 0x00, 0xbe, /* bkpt #0x00 */ + 0x00, 0x20, 0x02, 0x40, /* STM32_FLASH_BASE: .word 0x40022000 */ + }; + + /* flashloaders/stm32f0.s -- thumb1 only, same sequence as for STM32VL, bank ignored */ + static const uint8_t loader_code_stm32f0[] = { +#if 1 + /* + * These two NOPs here are a safety precaution, added by Pekka Nikander + * while debugging the STM32F05x support. They may not be needed, but + * there were strange problems with simpler programs, like a program + * that had just a breakpoint or a program that first moved zero to register r2 + * and then had a breakpoint. So, it appears safest to have these two nops. + * + * Feel free to remove them, if you dare, but then please do test the result + * rigorously. Also, if you remove these, it may be a good idea first to + * #if 0 them out, with a comment when these were taken out, and to remove + * these only a few months later... But YMMV. + */ + 0x00, 0x30, // nop /* add r0,#0 */ + 0x00, 0x30, // nop /* add r0,#0 */ +#endif + 0x0A, 0x4C, // ldr r4, STM32_FLASH_BASE + 0x01, 0x25, // mov r5, #1 /* FLASH_CR_PG, FLASH_SR_BUSY */ + 0x04, 0x26, // mov r6, #4 /* PGERR */ + // write_half_word: + 0x23, 0x69, // ldr r3, [r4, #16] /* FLASH->CR */ + 0x2B, 0x43, // orr r3, r5 + 0x23, 0x61, // str r3, [r4, #16] /* FLASH->CR |= FLASH_CR_PG */ + 0x03, 0x88, // ldrh r3, [r0] /* r3 = *sram */ + 0x0B, 0x80, // strh r3, [r1] /* *flash = r3 */ + // busy: + 0xE3, 0x68, // ldr r3, [r4, #12] /* FLASH->SR */ + 0x2B, 0x42, // tst r3, r5 /* FLASH_SR_BUSY */ + 0xFC, 0xD0, // beq busy + + 0x33, 0x42, // tst r3, r6 /* PGERR */ + 0x04, 0xD1, // bne exit + + 0x02, 0x30, // add r0, r0, #2 /* sram += 2 */ + 0x02, 0x31, // add r1, r1, #2 /* flash += 2 */ + 0x01, 0x3A, // sub r2, r2, #0x01 /* count-- */ + 0x00, 0x2A, // cmp r2, #0 + 0xF0, 0xD1, // bne write_half_word + // exit: + 0x23, 0x69, // ldr r3, [r4, #16] /* FLASH->CR */ + 0xAB, 0x43, // bic r3, r5 + 0x23, 0x61, // str r3, [r4, #16] /* FLASH->CR &= ~FLASH_CR_PG */ + 0x00, 0xBE, // bkpt #0x00 + 0x00, 0x20, 0x02, 0x40, /* STM32_FLASH_BASE: .word 0x40022000 */ + }; + + static const uint8_t loader_code_stm32l[] = { + + /* openocd.git/contrib/loaders/flash/stm32lx.S + r0, input, source addr + r1, input, dest addr + r2, input, word count + r2, output, remaining word count + */ + + 0x04, 0xe0, + + 0x50, 0xf8, 0x04, 0xcb, + 0x41, 0xf8, 0x04, 0xcb, + 0x01, 0x3a, + + 0x00, 0x2a, + 0xf8, 0xd3, + 0x00, 0xbe + }; + + static const uint8_t loader_code_stm32l0[] = { + + /* + r0, input, source addr + r1, input, dest addr + r2, input, word count + r2, output, remaining word count + */ + + 0x04, 0xe0, + + 0x04, 0x68, + 0x0c, 0x60, + 0x01, 0x3a, + 0x04, 0x31, + 0x04, 0x30, + + 0x00, 0x2a, + 0xf8, 0xd3, + 0x00, 0xbe + }; + + static const uint8_t loader_code_stm32f4[] = { + // flashloaders/stm32f4.s + + 0x07, 0x4b, + + 0x62, 0xb1, + 0x04, 0x68, + 0x0c, 0x60, + + 0xdc, 0x89, + 0x14, 0xf0, 0x01, 0x0f, + 0xfb, 0xd1, + 0x00, 0xf1, 0x04, 0x00, + 0x01, 0xf1, 0x04, 0x01, + 0xa2, 0xf1, 0x01, 0x02, + 0xf1, 0xe7, + + 0x00, 0xbe, + + 0x00, 0x3c, 0x02, 0x40, + }; + + static const uint8_t loader_code_stm32f4_lv[] = { + // flashloaders/stm32f4lv.s + 0x92, 0x00, + + 0x08, 0x4b, + 0x62, 0xb1, + 0x04, 0x78, + 0x0c, 0x70, + + 0xdc, 0x89, + 0x14, 0xf0, 0x01, 0x0f, + 0xfb, 0xd1, + 0x00, 0xf1, 0x01, 0x00, + 0x01, 0xf1, 0x01, 0x01, + 0xa2, 0xf1, 0x01, 0x02, + 0xf1, 0xe7, + + 0x00, 0xbe, + 0x00, 0xbf, + + 0x00, 0x3c, 0x02, 0x40, + }; + + static const uint8_t loader_code_stm32l4[] = { + // flashloaders/stm32l4.s + 0x08, 0x4b, // start: ldr r3, [pc, #32] ; + 0x72, 0xb1, // next: cbz r2, + 0x04, 0x68, // ldr r4, [r0, #0] + 0x45, 0x68, // ldr r5, [r0, #4] + 0x0c, 0x60, // str r4, [r1, #0] + 0x4d, 0x60, // str r5, [r1, #4] + 0x5c, 0x8a, // wait: ldrh r4, [r3, #18] + 0x14, 0xf0, 0x01, 0x0f, // tst.w r4, #1 + 0xfb, 0xd1, // bne.n + 0x00, 0xf1, 0x08, 0x00, // add.w r0, r0, #8 + 0x01, 0xf1, 0x08, 0x01, // add.w r1, r1, #8 + 0xa2, 0xf1, 0x01, 0x02, // sub.w r2, r2, #1 + 0xef, 0xe7, // b.n + 0x00, 0xbe, // done: bkpt 0x0000 + 0x00, 0x20, 0x02, 0x40 // flash_base: .word 0x40022000 + }; + + static const uint8_t loader_code_stm32f7[] = { + 0x08, 0x4b, + 0x72, 0xb1, + 0x04, 0x68, + 0x0c, 0x60, + 0xbf, 0xf3, 0x4f, 0x8f, // DSB Memory barrier for in order flash write + 0xdc, 0x89, + 0x14, 0xf0, 0x01, 0x0f, + 0xfb, 0xd1, + 0x00, 0xf1, 0x04, 0x00, + 0x01, 0xf1, 0x04, 0x01, + 0xa2, 0xf1, 0x01, 0x02, + 0xef, 0xe7, + 0x00, 0xbe, // bkpt #0x00 + 0x00, 0x3c, 0x02, 0x40, + }; + + const uint8_t* loader_code; + size_t loader_size; + + if (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_CAT2 + || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS || sl->chip_id == STM32_CHIPID_L1_HIGH + || sl->chip_id == STM32_CHIPID_L152_RE) { /* stm32l */ + loader_code = loader_code_stm32l; + loader_size = sizeof(loader_code_stm32l); + } else if (sl->core_id == STM32VL_CORE_ID + || sl->chip_id == STM32_CHIPID_F3 + || sl->chip_id == STM32_CHIPID_F3_SMALL + || sl->chip_id == STM32_CHIPID_F303_HIGH + || sl->chip_id == STM32_CHIPID_F37x + || sl->chip_id == STM32_CHIPID_F334) { + loader_code = loader_code_stm32vl; + loader_size = sizeof(loader_code_stm32vl); + } else if (sl->chip_id == STM32_CHIPID_F2 || sl->chip_id == STM32_CHIPID_F4 || (sl->chip_id == STM32_CHIPID_F4_DE) || + sl->chip_id == STM32_CHIPID_F4_LP || sl->chip_id == STM32_CHIPID_F4_HD || (sl->chip_id == STM32_CHIPID_F411RE) || + (sl->chip_id == STM32_CHIPID_F446) || (sl->chip_id == STM32_CHIPID_F4_DSI)){ + int voltage = stlink_target_voltage(sl); + if (voltage == -1) { + printf("Failed to read Target voltage\n"); + return voltage; + } else if (voltage > 2700) { + loader_code = loader_code_stm32f4; + loader_size = sizeof(loader_code_stm32f4); + } else { + loader_code = loader_code_stm32f4_lv; + loader_size = sizeof(loader_code_stm32f4_lv); + } + } else if (sl->chip_id == STM32_CHIPID_F7){ + loader_code = loader_code_stm32f7; + loader_size = sizeof(loader_code_stm32f7); + } else if (sl->chip_id == STM32_CHIPID_F0 || sl->chip_id == STM32_CHIPID_F04 || sl->chip_id == STM32_CHIPID_F0_CAN || sl->chip_id == STM32_CHIPID_F0_SMALL || sl->chip_id == STM32_CHIPID_F09X) { + loader_code = loader_code_stm32f0; + loader_size = sizeof(loader_code_stm32f0); + } else if (sl->chip_id == STM32_CHIPID_L0) { + loader_code = loader_code_stm32l0; + loader_size = sizeof(loader_code_stm32l0); + } else if (sl->chip_id == STM32_CHIPID_L4) { + loader_code = loader_code_stm32l4; + loader_size = sizeof(loader_code_stm32l4); + } else { + ELOG("unknown coreid, not sure what flash loader to use, aborting!: %x\n", sl->core_id); + return -1; + } + + memcpy(sl->q_buf, loader_code, loader_size); + stlink_write_mem32(sl, sl->sram_base, loader_size); + + *addr = sl->sram_base; + *size = loader_size; + + /* success */ + return 0; +} + +int stlink_fcheck_flash(stlink_t *sl, const char* path, stm32_addr_t addr) { + /* check the contents of path are at addr */ + + int res; + mapped_file_t mf = MAPPED_FILE_INITIALIZER; + + if (map_file(&mf, path) == -1) + return -1; + + res = check_file(sl, &mf, addr); + + unmap_file(&mf); + + return res; +} + +/** + * Verify addr..addr+len is binary identical to base...base+len + * @param sl stlink context + * @param address stm device address + * @param data host side buffer to check against + * @param length how much + * @return 0 for success, -ve for failure + */ +int stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, unsigned length) { + size_t off; + size_t cmp_size = (sl->flash_pgsz > 0x1800)? 0x1800:sl->flash_pgsz; + ILOG("Starting verification of write complete\n"); + for (off = 0; off < length; off += cmp_size) { + size_t aligned_size; + + /* adjust last page size */ + if ((off + cmp_size) > length) + cmp_size = length - off; + + aligned_size = cmp_size; + if (aligned_size & (4 - 1)) + aligned_size = (cmp_size + 4) & ~(4 - 1); + + stlink_read_mem32(sl, address + off, aligned_size); + + if (memcmp(sl->q_buf, data + off, cmp_size)) { + ELOG("Verification of flash failed at offset: %zd\n", off); + return -1; + } + } + ILOG("Flash written and verified! jolly good!\n"); + return 0; + +} + +int stm32l1_write_half_pages(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uint32_t len, uint32_t pagesize) +{ + unsigned int count; + unsigned int num_half_pages = len / pagesize; + uint32_t val; + uint32_t flash_regs_base; + flash_loader_t fl; + + if (sl->chip_id == STM32_CHIPID_L0) { + flash_regs_base = STM32L0_FLASH_REGS_ADDR; + } else { + flash_regs_base = STM32L_FLASH_REGS_ADDR; + } + + ILOG("Starting Half page flash write for STM32L core id\n"); + /* flash loader initialization */ + if (init_flash_loader(sl, &fl) == -1) { + WLOG("init_flash_loader() == -1\n"); + return -1; + } + /* Unlock already done */ + stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); + val |= (1 << FLASH_L1_FPRG); + stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); + + val |= (1 << FLASH_L1_PROG); + stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); + do { + stlink_read_debug32(sl, flash_regs_base + FLASH_SR_OFF, &val); + } while ((val & (1 << 0)) != 0); + + for (count = 0; count < num_half_pages; count ++) { + if (run_flash_loader(sl, &fl, addr + count * pagesize, base + count * pagesize, pagesize) == -1) { + WLOG("l1_run_flash_loader(%#zx) failed! == -1\n", addr + count * pagesize); + stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); + val &= ~((1 << FLASH_L1_FPRG) |(1 << FLASH_L1_PROG)); + stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); + return -1; + } + /* wait for sr.busy to be cleared */ + if (sl->verbose >= 1) { + /* show progress. writing procedure is slow + and previous errors are misleading */ + fprintf(stdout, "\r%3u/%u halfpages written", count + 1, num_half_pages); + fflush(stdout); + } + do { + stlink_read_debug32(sl, flash_regs_base + FLASH_SR_OFF, &val); + } while ((val & (1 << 0)) != 0); + } + stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); + val &= ~(1 << FLASH_L1_PROG); + stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); + stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); + val &= ~(1 << FLASH_L1_FPRG); + stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); + + return 0; +} + +int stlink_write_flash(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uint32_t len, uint8_t eraseonly) { + size_t off; + flash_loader_t fl; + ILOG("Attempting to write %d (%#x) bytes to stm32 address: %u (%#x)\n", + len, len, addr, addr); + /* check addr range is inside the flash */ + stlink_calculate_pagesize(sl, addr); + if (addr < sl->flash_base) { + ELOG("addr too low %#x < %#x\n", addr, sl->flash_base); + return -1; + } else if ((addr + len) < addr) { + ELOG("addr overruns\n"); + return -1; + } else if ((addr + len) > (sl->flash_base + sl->flash_size)) { + ELOG("addr too high\n"); + return -1; + } else if (addr & 1) { + ELOG("unaligned addr 0x%x\n", addr); + return -1; + } else if (len & 1) { + WLOG("unaligned len 0x%x -- padding with zero\n", len); + len += 1; + } else if (addr & (sl->flash_pgsz - 1)) { + ELOG("addr not a multiple of pagesize, not supported\n"); + return -1; + } + + // Make sure we've loaded the context with the chip details + stlink_core_id(sl); + /* erase each page */ + int page_count = 0; + for (off = 0; off < len; off += stlink_calculate_pagesize(sl, addr + off)) { + /* addr must be an addr inside the page */ + if (stlink_erase_flash_page(sl, addr + off) == -1) { + ELOG("Failed to erase_flash_page(%#zx) == -1\n", addr + off); + return -1; + } + fprintf(stdout,"\rFlash page at addr: 0x%08lx erased", + (unsigned long)addr + off); + fflush(stdout); + page_count++; + } + fprintf(stdout,"\n"); + ILOG("Finished erasing %d pages of %d (%#x) bytes\n", + page_count, sl->flash_pgsz, sl->flash_pgsz); + + if (eraseonly) + return 0; + + if ((sl->flash_type == FLASH_TYPE_F4) || (sl->flash_type == FLASH_TYPE_L4)) { + /* todo: check write operation */ + + ILOG("Starting Flash write for F2/F4/L4\n"); + /* flash loader initialization */ + if (init_flash_loader(sl, &fl) == -1) { + ELOG("init_flash_loader() == -1\n"); + return -1; + } + + /* First unlock the cr */ + unlock_flash_if(sl); + + /* TODO: Check that Voltage range is 2.7 - 3.6 V */ + if (sl->chip_id != STM32_CHIPID_L4) { + /* set parallelisim to 32 bit*/ + int voltage = stlink_target_voltage(sl); + if (voltage == -1) { + printf("Failed to read Target voltage\n"); + return voltage; + } else if (voltage > 2700) { + printf("enabling 32-bit flash writes\n"); + write_flash_cr_psiz(sl, 2); + } else { + printf("Target voltage (%d mV) too low for 32-bit flash, using 8-bit flash writes\n", voltage); + write_flash_cr_psiz(sl, 0); + } + } else { + /* L4 does not have a byte-write mode */ + int voltage = stlink_target_voltage(sl); + if (voltage == -1) { + printf("Failed to read Target voltage\n"); + return voltage; + } else if (voltage < 1710) { + printf("Target voltage (%d mV) too low for flash writes!\n", voltage); + return -1; + } + } + + /* set programming mode */ + set_flash_cr_pg(sl); + + for(off = 0; off < len;) { + size_t size = len - off > 0x8000 ? 0x8000 : len - off; + + printf("size: %zu\n", size); + + if (run_flash_loader(sl, &fl, addr + off, base + off, size) == -1) { + ELOG("run_flash_loader(%#zx) failed! == -1\n", addr + off); + return -1; + } + + off += size; + } + + /* Relock flash */ + lock_flash(sl); + + } //STM32F4END + + else if (sl->flash_type == FLASH_TYPE_L0) { + /* use fast word write. todo: half page. */ + uint32_t val; + uint32_t flash_regs_base; + uint32_t pagesize; + + if (sl->chip_id == STM32_CHIPID_L0) { + flash_regs_base = STM32L0_FLASH_REGS_ADDR; + pagesize = L0_WRITE_BLOCK_SIZE; + } else { + flash_regs_base = STM32L_FLASH_REGS_ADDR; + pagesize = L1_WRITE_BLOCK_SIZE; + } + + /* todo: check write operation */ + + /* disable pecr protection */ + stlink_write_debug32(sl, flash_regs_base + FLASH_PEKEYR_OFF, 0x89abcdef); + stlink_write_debug32(sl, flash_regs_base + FLASH_PEKEYR_OFF, 0x02030405); + + /* check pecr.pelock is cleared */ + stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); + if (val & (1 << 0)) { + fprintf(stderr, "pecr.pelock not clear\n"); + return -1; + } + + /* unlock program memory */ + stlink_write_debug32(sl, flash_regs_base + FLASH_PRGKEYR_OFF, 0x8c9daebf); + stlink_write_debug32(sl, flash_regs_base + FLASH_PRGKEYR_OFF, 0x13141516); + + /* check pecr.prglock is cleared */ + stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); + if (val & (1 << 1)) { + fprintf(stderr, "pecr.prglock not clear\n"); + return -1; + } + off = 0; + if (len > pagesize) { + if (stm32l1_write_half_pages(sl, addr, base, len, pagesize) == -1) { + /* This may happen on a blank device! */ + WLOG("\nwrite_half_pages failed == -1\n"); + } else { + off = (len / pagesize)*pagesize; + } + } + + /* write remainingword in program memory */ + for ( ; off < len; off += sizeof(uint32_t)) { + uint32_t data; + if (off > 254) + fprintf(stdout, "\r"); + + if ((off % sl->flash_pgsz) > (sl->flash_pgsz -5)) { + fprintf(stdout, "\r%3zd/%3zd pages written", + off/sl->flash_pgsz, len/sl->flash_pgsz); + fflush(stdout); + } + + write_uint32((unsigned char*) &data, *(uint32_t*) (base + off)); + stlink_write_debug32(sl, addr + off, data); + + /* wait for sr.busy to be cleared */ + do { + stlink_read_debug32(sl, flash_regs_base + FLASH_SR_OFF, &val); + } while ((val & (1 << 0)) != 0); + + /* todo: check redo write operation */ + + } + fprintf(stdout, "\n"); + /* reset lock bits */ + stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); + val |= (1 << 0) | (1 << 1) | (1 << 2); + stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); + } else if (sl->flash_type == FLASH_TYPE_F0) { + ILOG("Starting Flash write for VL/F0/F3 core id\n"); + /* flash loader initialization */ + if (init_flash_loader(sl, &fl) == -1) { + ELOG("init_flash_loader() == -1\n"); + return -1; + } + + int write_block_count = 0; + for (off = 0; off < len; off += sl->flash_pgsz) { + /* adjust last write size */ + size_t size = sl->flash_pgsz; + if ((off + sl->flash_pgsz) > len) size = len - off; + + /* unlock and set programming mode */ + unlock_flash_if(sl); + set_flash_cr_pg(sl); + //DLOG("Finished setting flash cr pg, running loader!\n"); + if (run_flash_loader(sl, &fl, addr + off, base + off, size) == -1) { + ELOG("run_flash_loader(%#zx) failed! == -1\n", addr + off); + return -1; + } + lock_flash(sl); + if (sl->verbose >= 1) { + /* show progress. writing procedure is slow + and previous errors are misleading */ + fprintf(stdout, "\r%3u/%lu pages written", write_block_count++, (unsigned long)len/sl->flash_pgsz); + fflush(stdout); + } + } + fprintf(stdout, "\n"); + } else { + ELOG("unknown coreid, not sure how to write: %x\n", sl->core_id); + return -1; + } + + return stlink_verify_write_flash(sl, addr, base, len); +} + +/** + * Write the given binary file into flash at address "addr" + * @param sl + * @param path readable file path, should be binary image + * @param addr where to start writing + * @return 0 on success, -ve on failure. + */ +int stlink_fwrite_flash(stlink_t *sl, const char* path, stm32_addr_t addr) { + /* write the file in flash at addr */ + int err; + unsigned int num_empty, index, val; + unsigned char erased_pattern; + mapped_file_t mf = MAPPED_FILE_INITIALIZER; + + if (map_file(&mf, path) == -1) { + ELOG("map_file() == -1\n"); + return -1; + } + + if (sl->flash_type == FLASH_TYPE_L0) + erased_pattern = 0x00; + else + erased_pattern = 0xff; + + index = mf.len; + for(num_empty = 0; num_empty != mf.len; ++num_empty) { + if (mf.base[--index] != erased_pattern) { + break; + } + } + /* Round down to words */ + num_empty -= (num_empty & 3); + if(num_empty != 0) { + ILOG("Ignoring %d bytes of 0x%02x at end of file\n", num_empty, erased_pattern); + } + err = stlink_write_flash(sl, addr, mf.base, num_empty == mf.len? mf.len : mf.len - num_empty, num_empty == mf.len); + /* set stack*/ + stlink_read_debug32(sl, addr, &val); + stlink_write_reg(sl, val, 13); + /* Set PC to the reset routine*/ + stlink_read_debug32(sl, addr + 4, &val); + stlink_write_reg(sl, val, 15); + stlink_run(sl); + unmap_file(&mf); + return err; +} + +int run_flash_loader(stlink_t *sl, flash_loader_t* fl, stm32_addr_t target, const uint8_t* buf, size_t size) { + + reg rr; + int i = 0; + size_t count = 0; + + DLOG("Running flash loader, write address:%#x, size: %zd\n", target, size); + // FIXME This can never return -1 + if (write_buffer_to_sram(sl, fl, buf, size) == -1) { + // IMPOSSIBLE! + ELOG("write_buffer_to_sram() == -1\n"); + return -1; + } + + if (sl->flash_type == FLASH_TYPE_F0) { + count = size / sizeof(uint16_t); + if (size % sizeof(uint16_t)) + ++count; + } else if (sl->flash_type == FLASH_TYPE_F4 || sl->flash_type == FLASH_TYPE_L0) { + count = size / sizeof(uint32_t); + if (size % sizeof(uint32_t)) + ++count; + } else if (sl->flash_type == FLASH_TYPE_L4) { + count = size / sizeof(uint64_t); + if (size % sizeof(uint64_t)) + ++count; + } + + /* setup core */ + stlink_write_reg(sl, fl->buf_addr, 0); /* source */ + stlink_write_reg(sl, target, 1); /* target */ + stlink_write_reg(sl, count, 2); /* count */ + stlink_write_reg(sl, 0, 3); /* flash bank 0 (input), only used on F0, but armless fopr others */ + stlink_write_reg(sl, fl->loader_addr, 15); /* pc register */ + + /* run loader */ + stlink_run(sl); + +#define WAIT_ROUNDS 10000 + /* wait until done (reaches breakpoint) */ + for (i = 0; i < WAIT_ROUNDS; i++) { + usleep(10); + if (is_core_halted(sl)) + break; + } + + if (i >= WAIT_ROUNDS) { + ELOG("flash loader run error\n"); + return -1; + } + + /* check written byte count */ + stlink_read_reg(sl, 2, &rr); + if (rr.r[2] != 0) { + fprintf(stderr, "write error, count == %u\n", rr.r[2]); + return -1; + } + + return 0; +} diff --git a/src/gdbserver/Makefile b/src/gdbserver/Makefile new file mode 100644 index 0000000..bd5c73d --- /dev/null +++ b/src/gdbserver/Makefile @@ -0,0 +1,20 @@ +PRG := st-util +OBJS = gdb-remote.o gdb-server.o + +CFLAGS+=-g -Wall -Werror -std=gnu99 -I../src +LDFLAGS=-L.. -lstlink + +# libusb location +LDFLAGS+=`pkg-config --libs libusb-1.0` +CFLAGS+=`pkg-config --cflags libusb-1.0` + +all: $(PRG) + +$(PRG): $(OBJS) ../libstlink.a + $(CC) -o $@ $^ $(LDFLAGS) + +clean: + rm -rf $(OBJS) + rm -rf $(PRG) + +.PHONY: clean all diff --git a/src/gdbserver/gdb-remote.c b/src/gdbserver/gdb-remote.c new file mode 100644 index 0000000..2f61b93 --- /dev/null +++ b/src/gdbserver/gdb-remote.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2011 Peter Zotov + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#include +#include +#include +#include +#include +#ifdef __MINGW32__ +#include "mingw.h" +#else +#include +#endif + +static const char hex[] = "0123456789abcdef"; + +int gdb_send_packet(int fd, char* data) { + unsigned int data_length = strlen(data); + int length = data_length + 4; + char* packet = malloc(length); /* '$' data (hex) '#' cksum (hex) */ + + memset(packet, 0, length); + + packet[0] = '$'; + + uint8_t cksum = 0; + for(unsigned int i = 0; i < data_length; i++) { + packet[i + 1] = data[i]; + cksum += data[i]; + } + + packet[length - 3] = '#'; + packet[length - 2] = hex[cksum >> 4]; + packet[length - 1] = hex[cksum & 0xf]; + + while(1) { + if(write(fd, packet, length) != length) { + free(packet); + return -2; + } + + char ack; + if(read(fd, &ack, 1) != 1) { + free(packet); + return -2; + } + + if(ack == '+') { + free(packet); + return 0; + } + } +} + +#define ALLOC_STEP 1024 + +int gdb_recv_packet(int fd, char** buffer) { + unsigned packet_size = ALLOC_STEP + 1, packet_idx = 0; + uint8_t cksum = 0; + char recv_cksum[3] = {0}; + char* packet_buffer = malloc(packet_size); + unsigned state; + +start: + state = 0; + /* + * 0: waiting $ + * 1: data, waiting # + * 2: cksum 1 + * 3: cksum 2 + * 4: fin + */ + + char c; + while(state != 4) { + if(read(fd, &c, 1) != 1) { + return -2; + } + + switch(state) { + case 0: + if(c != '$') { + // ignore + } else { + state = 1; + } + break; + + case 1: + if(c == '#') { + state = 2; + } else { + packet_buffer[packet_idx++] = c; + cksum += c; + + if(packet_idx == packet_size) { + packet_size += ALLOC_STEP; + packet_buffer = realloc(packet_buffer, packet_size); + } + } + break; + + case 2: + recv_cksum[0] = c; + state = 3; + break; + + case 3: + recv_cksum[1] = c; + state = 4; + break; + } + } + + uint8_t recv_cksum_int = strtoul(recv_cksum, NULL, 16); + if(recv_cksum_int != cksum) { + char nack = '-'; + if(write(fd, &nack, 1) != 1) { + return -2; + } + + goto start; + } else { + char ack = '+'; + if(write(fd, &ack, 1) != 1) { + return -2; + } + } + + packet_buffer[packet_idx] = 0; + *buffer = packet_buffer; + + return packet_idx; +} + +// Here we skip any characters which are not \x03, GDB interrupt. +// As we use the mode with ACK, in a (very unlikely) situation of a packet +// lost because of this skipping, it will be resent anyway. +int gdb_check_for_interrupt(int fd) { + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLIN; + + if(poll(&pfd, 1, 0) != 0) { + char c; + + if(read(fd, &c, 1) != 1) + return -2; + + if(c == '\x03') // ^C + return 1; + } + + return 0; +} + diff --git a/src/gdbserver/gdb-remote.h b/src/gdbserver/gdb-remote.h new file mode 100644 index 0000000..bfa0104 --- /dev/null +++ b/src/gdbserver/gdb-remote.h @@ -0,0 +1,8 @@ +#ifndef _GDB_REMOTE_H_ +#define _GDB_REMOTE_H_ + +int gdb_send_packet(int fd, char* data); +int gdb_recv_packet(int fd, char** buffer); +int gdb_check_for_interrupt(int fd); + +#endif diff --git a/src/gdbserver/gdb-server.c b/src/gdbserver/gdb-server.c new file mode 100644 index 0000000..666251c --- /dev/null +++ b/src/gdbserver/gdb-server.c @@ -0,0 +1,1555 @@ +/* + * Copyright (C) 2011 Peter Zotov + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef __MINGW32__ +#include "mingw.h" +#else +#include +#include +#include +#endif + +#include +#include + +#include "gdb-remote.h" +#include "gdb-server.h" + +#define FLASH_BASE 0x08000000 + +//Allways update the FLASH_PAGE before each use, by calling stlink_calculate_pagesize +#define FLASH_PAGE (sl->flash_pgsz) + +stlink_t *connected_stlink = NULL; + +static const char hex[] = "0123456789abcdef"; + +static const char* current_memory_map = NULL; + +typedef struct _st_state_t { + // things from command line, bleh + int stlink_version; + int logging_level; + int listen_port; + int persistent; + int reset; +} st_state_t; + + +int serve(stlink_t *sl, st_state_t *st); +char* make_memory_map(stlink_t *sl); +static void init_cache (stlink_t *sl); + +static void cleanup(int signal __attribute__((unused))) { + if (connected_stlink) { + /* Switch back to mass storage mode before closing. */ + stlink_run(connected_stlink); + stlink_exit_debug_mode(connected_stlink); + stlink_close(connected_stlink); + } + + exit(1); +} + + + +int parse_options(int argc, char** argv, st_state_t *st) { + static struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"verbose", optional_argument, NULL, 'v'}, + {"stlink_version", required_argument, NULL, 's'}, + {"stlinkv1", no_argument, NULL, '1'}, + {"listen_port", required_argument, NULL, 'p'}, + {"multi", optional_argument, NULL, 'm'}, + {"no-reset", optional_argument, NULL, 'n'}, + {0, 0, 0, 0}, + }; + const char * help_str = "%s - usage:\n\n" + " -h, --help\t\tPrint this help\n" + " -vXX, --verbose=XX\tSpecify a specific verbosity level (0..99)\n" + " -v, --verbose\t\tSpecify generally verbose logging\n" + " -s X, --stlink_version=X\n" + "\t\t\tChoose what version of stlink to use, (defaults to 2)\n" + " -1, --stlinkv1\tForce stlink version 1\n" + " -p 4242, --listen_port=1234\n" + "\t\t\tSet the gdb server listen port. " + "(default port: " STRINGIFY(DEFAULT_GDB_LISTEN_PORT) ")\n" + " -m, --multi\n" + "\t\t\tSet gdb server to extended mode.\n" + "\t\t\tst-util will continue listening for connections after disconnect.\n" + " -n, --no-reset\n" + "\t\t\tDo not reset board on connection.\n" + "\n" + "The STLINKv2 device to use can be specified in the environment\n" + "variable STLINK_DEVICE on the format :.\n" + "\n" + ; + + + int option_index = 0; + int c; + int q; + while ((c = getopt_long(argc, argv, "hv::s:1p:mn", long_options, &option_index)) != -1) { + switch (c) { + case 0: + printf("XXXXX Shouldn't really normally come here, only if there's no corresponding option\n"); + printf("option %s", long_options[option_index].name); + if (optarg) { + printf(" with arg %s", optarg); + } + printf("\n"); + break; + case 'h': + printf(help_str, argv[0]); + exit(EXIT_SUCCESS); + break; + case 'v': + if (optarg) { + st->logging_level = atoi(optarg); + } else { + st->logging_level = DEFAULT_LOGGING_LEVEL; + } + break; + case '1': + st->stlink_version = 1; + break; + case 's': + sscanf(optarg, "%i", &q); + if (q < 0 || q > 2) { + fprintf(stderr, "stlink version %d unknown!\n", q); + exit(EXIT_FAILURE); + } + st->stlink_version = q; + break; + case 'p': + sscanf(optarg, "%i", &q); + if (q < 0) { + fprintf(stderr, "Can't use a negative port to listen on: %d\n", q); + exit(EXIT_FAILURE); + } + st->listen_port = q; + break; + case 'm': + st->persistent = 1; + break; + case 'n': + st->reset = 0; + break; + } + } + + if (optind < argc) { + printf("non-option ARGV-elements: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + } + return 0; +} + + +int main(int argc, char** argv) { + int32_t voltage; + + stlink_t *sl = NULL; + + st_state_t state; + memset(&state, 0, sizeof(state)); + // set defaults... + state.stlink_version = 2; + state.logging_level = DEFAULT_LOGGING_LEVEL; + state.listen_port = DEFAULT_GDB_LISTEN_PORT; + state.reset = 1; /* By default, reset board */ + parse_options(argc, argv, &state); + switch (state.stlink_version) { + case 2: + sl = stlink_open_usb(state.logging_level, state.reset, NULL); + if(sl == NULL) return 1; + break; + case 1: + sl = stlink_v1_open(state.logging_level, state.reset); + if(sl == NULL) return 1; + break; + } + + connected_stlink = sl; + signal(SIGINT, &cleanup); + signal(SIGTERM, &cleanup); + signal(SIGSEGV, &cleanup); + + if (state.reset) { + stlink_reset(sl); + } + + ILOG("Chip ID is %08x, Core ID is %08x.\n", sl->chip_id, sl->core_id); + + voltage = stlink_target_voltage(sl); + if (voltage != -1) { + ILOG("Target voltage is %d mV.\n", voltage); + } + + sl->verbose=0; + + current_memory_map = make_memory_map(sl); + +#ifdef __MINGW32__ + WSADATA wsadata; + if (WSAStartup(MAKEWORD(2,2),&wsadata) !=0 ) { + goto winsock_error; + } +#endif + + init_cache(sl); + + do { + if (serve(sl, &state)) { + sleep (1); // don't go bezurk if serve returns with error + } + + /* Continue */ + stlink_run(sl); + } while (state.persistent); + +#ifdef __MINGW32__ +winsock_error: + WSACleanup(); +#endif + + /* Switch back to mass storage mode before closing. */ + stlink_exit_debug_mode(sl); + stlink_close(sl); + + return 0; +} + +static const char* const target_description_F4 = + "" + "" + "" + " arm" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +static const char* const memory_map_template_F4 = + "" + "" + "" + " " // code = sram, bootrom or flash; flash is bigger + " " // ccm ram + " " // sram + " " //Sectors 0..3 + " 0x4000" //16kB + " " + " " //Sector 4 + " 0x10000" //64kB + " " + " " //Sectors 5..11 + " 0x20000" //128kB + " " + " " // peripheral regs + " " // AHB3 Peripherals + " " // cortex regs + " " // bootrom + " " // option byte area + ""; + +static const char* const memory_map_template_F4_HD = + "" + "" + "" + " " // code = sram, bootrom or flash; flash is bigger + " " // ccm ram + " " // sram + " " // fmc bank 1 (nor/psram/sram) + " " // fmc bank 2 & 3 (nand flash) + " " // fmc bank 4 (pc card) + " " // fmc sdram bank 1 & 2 + " " //Sectors 0..3 + " 0x4000" //16kB + " " + " " //Sector 4 + " 0x10000" //64kB + " " + " " //Sectors 5..11 + " 0x20000" //128kB + " " + " " // peripheral regs + " " // cortex regs + " " // bootrom + " " // option byte area + ""; + +static const char* const memory_map_template_F2 = + "" + "" + "" + " " // code = sram, bootrom or flash; flash is bigger + " " // sram + " " //Sectors 0..3 + " 0x4000" //16kB + " " + " " //Sector 4 + " 0x10000" //64kB + " " + " " //Sectors 5.. + " 0x20000" //128kB + " " + " " // peripheral regs + " " // cortex regs + " " // bootrom + " " // option byte area + ""; + +static const char* const memory_map_template_L4 = + "" + "" + "" + " " // code = sram, bootrom or flash; flash is bigger + " " // SRAM2 (32 KB) + " " // SRAM1 (96 KB) + " " + " 0x800" + " " + " " // peripheral regs + " " // AHB3 Peripherals + " " // cortex regs + " " // bootrom + " " // option byte area + " " // option byte area + ""; + +static const char* const memory_map_template = + "" + "" + "" + " " // code = sram, bootrom or flash; flash is bigger + " " // sram 8k + " " + " 0x%zx" + " " + " " // peripheral regs + " " // cortex regs + " " // bootrom + " " // option byte area + ""; + +static const char* const memory_map_template_F7 = + "" + "" + "" + " " // ITCM ram 16kB + " " // ITCM flash + " " // sram + " " // Sectors 0..3 + " 0x8000" // 32kB + " " + " " // Sector 4 + " 0x20000" // 128kB + " " + " " // Sectors 5..7 + " 0x40000" // 128kB + " " + " " // peripheral regs + " " // AHB3 Peripherals + " " // cortex regs + " " // bootrom + " " // option byte area + ""; + +char* make_memory_map(stlink_t *sl) { + /* This will be freed in serve() */ + char* map = malloc(4096); + map[0] = '\0'; + + if(sl->chip_id==STM32_CHIPID_F4 || sl->chip_id==STM32_CHIPID_F446) { + strcpy(map, memory_map_template_F4); + } else if(sl->chip_id==STM32_CHIPID_F4 || sl->chip_id==STM32_CHIPID_F7) { + strcpy(map, memory_map_template_F7); + } else if(sl->chip_id==STM32_CHIPID_F4_HD) { + strcpy(map, memory_map_template_F4_HD); + } else if(sl->chip_id==STM32_CHIPID_F2) { + snprintf(map, 4096, memory_map_template_F2, + sl->flash_size, + sl->sram_size, + sl->flash_size - 0x20000, + sl->sys_base, sl->sys_size); + } else if(sl->chip_id==STM32_CHIPID_L4) { + snprintf(map, 4096, memory_map_template_L4, + sl->flash_size, sl->flash_size); + } else { + snprintf(map, 4096, memory_map_template, + sl->flash_size, + sl->sram_size, + sl->flash_size, sl->flash_pgsz, + sl->sys_base, sl->sys_size); + } + return map; +} + + +/* + * DWT_COMP0 0xE0001020 + * DWT_MASK0 0xE0001024 + * DWT_FUNCTION0 0xE0001028 + * DWT_COMP1 0xE0001030 + * DWT_MASK1 0xE0001034 + * DWT_FUNCTION1 0xE0001038 + * DWT_COMP2 0xE0001040 + * DWT_MASK2 0xE0001044 + * DWT_FUNCTION2 0xE0001048 + * DWT_COMP3 0xE0001050 + * DWT_MASK3 0xE0001054 + * DWT_FUNCTION3 0xE0001058 + */ + +#define DATA_WATCH_NUM 4 + +enum watchfun { WATCHDISABLED = 0, WATCHREAD = 5, WATCHWRITE = 6, WATCHACCESS = 7 }; + +struct code_hw_watchpoint { + stm32_addr_t addr; + uint8_t mask; + enum watchfun fun; +}; + +struct code_hw_watchpoint data_watches[DATA_WATCH_NUM]; + +static void init_data_watchpoints(stlink_t *sl) { + uint32_t data; + DLOG("init watchpoints\n"); + + stlink_read_debug32(sl, 0xE000EDFC, &data); + data |= 1<<24; + // set trcena in debug command to turn on dwt unit + stlink_write_debug32(sl, 0xE000EDFC, data); + + // make sure all watchpoints are cleared + for(int i = 0; i < DATA_WATCH_NUM; i++) { + data_watches[i].fun = WATCHDISABLED; + stlink_write_debug32(sl, 0xe0001028 + i * 16, 0); + } +} + +static int add_data_watchpoint(stlink_t *sl, enum watchfun wf, + stm32_addr_t addr, unsigned int len) { + int i = 0; + uint32_t mask, dummy; + + // computer mask + // find a free watchpoint + // configure + + mask = -1; + i = len; + while(i) { + i >>= 1; + mask++; + } + + if((mask != (uint32_t)-1) && (mask < 16)) { + for(i = 0; i < DATA_WATCH_NUM; i++) { + // is this an empty slot ? + if(data_watches[i].fun == WATCHDISABLED) { + DLOG("insert watchpoint %d addr %x wf %u mask %u len %d\n", i, addr, wf, mask, len); + + data_watches[i].fun = wf; + data_watches[i].addr = addr; + data_watches[i].mask = mask; + + // insert comparator address + stlink_write_debug32(sl, 0xE0001020 + i * 16, addr); + + // insert mask + stlink_write_debug32(sl, 0xE0001024 + i * 16, mask); + + // insert function + stlink_write_debug32(sl, 0xE0001028 + i * 16, wf); + + // just to make sure the matched bit is clear ! + stlink_read_debug32(sl, 0xE0001028 + i * 16, &dummy); + return 0; + } + } + } + + DLOG("failure: add watchpoints addr %x wf %u len %u\n", addr, wf, len); + return -1; +} + +static int delete_data_watchpoint(stlink_t *sl, stm32_addr_t addr) +{ + int i; + + for(i = 0 ; i < DATA_WATCH_NUM; i++) { + if((data_watches[i].addr == addr) && (data_watches[i].fun != WATCHDISABLED)) { + DLOG("delete watchpoint %d addr %x\n", i, addr); + + data_watches[i].fun = WATCHDISABLED; + stlink_write_debug32(sl, 0xe0001028 + i * 16, 0); + + return 0; + } + } + + DLOG("failure: delete watchpoint addr %x\n", addr); + + return -1; +} + +int code_break_num; +int code_lit_num; +#define CODE_BREAK_NUM_MAX 15 +#define CODE_BREAK_LOW 0x01 +#define CODE_BREAK_HIGH 0x02 + +struct code_hw_breakpoint { + stm32_addr_t addr; + int type; +}; + +struct code_hw_breakpoint code_breaks[CODE_BREAK_NUM_MAX]; + +static void init_code_breakpoints(stlink_t *sl) { + unsigned int val; + memset(sl->q_buf, 0, 4); + stlink_write_debug32(sl, CM3_REG_FP_CTRL, 0x03 /*KEY | ENABLE4*/); + stlink_read_debug32(sl, CM3_REG_FP_CTRL, &val); + code_break_num = ((val >> 4) & 0xf); + code_lit_num = ((val >> 8) & 0xf); + + ILOG("Found %i hw breakpoint registers\n", code_break_num); + + for(int i = 0; i < code_break_num; i++) { + code_breaks[i].type = 0; + stlink_write_debug32(sl, CM3_REG_FP_COMP0 + i * 4, 0); + } +} + +static int update_code_breakpoint(stlink_t *sl, stm32_addr_t addr, int set) { + stm32_addr_t fpb_addr; + uint32_t mask; + int type = (addr & 0x2) ? CODE_BREAK_HIGH : CODE_BREAK_LOW; + + if(addr & 1) { + ELOG("update_code_breakpoint: unaligned address %08x\n", addr); + return -1; + } + + if (sl->chip_id==STM32_CHIPID_F7) { + fpb_addr = addr; + } else { + fpb_addr = addr & ~0x3; + } + + int id = -1; + for(int i = 0; i < code_break_num; i++) { + if(fpb_addr == code_breaks[i].addr || + (set && code_breaks[i].type == 0)) { + id = i; + break; + } + } + + if(id == -1) { + if(set) return -1; // Free slot not found + else return 0; // Breakpoint is already removed + } + + struct code_hw_breakpoint* brk = &code_breaks[id]; + + brk->addr = fpb_addr; + + if (sl->chip_id==STM32_CHIPID_F7) { + if(set) brk->type = type; + else brk->type = 0; + + mask = (brk->addr) | 1; + } else { + if(set) brk->type |= type; + else brk->type &= ~type; + + mask = (brk->addr) | 1 | (brk->type << 30); + } + + if(brk->type == 0) { + DLOG("clearing hw break %d\n", id); + + stlink_write_debug32(sl, 0xe0002008 + id * 4, 0); + } else { + DLOG("setting hw break %d at %08x (%d)\n", + id, brk->addr, brk->type); + DLOG("reg %08x \n", + mask); + + stlink_write_debug32(sl, 0xe0002008 + id * 4, mask); + } + + return 0; +} + + +struct flash_block { + stm32_addr_t addr; + unsigned length; + uint8_t* data; + + struct flash_block* next; +}; + +static struct flash_block* flash_root; + +static int flash_add_block(stm32_addr_t addr, unsigned length, stlink_t *sl) { + + if(addr < FLASH_BASE || addr + length > FLASH_BASE + sl->flash_size) { + ELOG("flash_add_block: incorrect bounds\n"); + return -1; + } + + stlink_calculate_pagesize(sl, addr); + if(addr % FLASH_PAGE != 0 || length % FLASH_PAGE != 0) { + ELOG("flash_add_block: unaligned block\n"); + return -1; + } + + struct flash_block* new = malloc(sizeof(struct flash_block)); + new->next = flash_root; + + new->addr = addr; + new->length = length; + new->data = calloc(length, 1); + + flash_root = new; + + return 0; +} + +static int flash_populate(stm32_addr_t addr, uint8_t* data, unsigned length) { + unsigned int fit_blocks = 0, fit_length = 0; + + for(struct flash_block* fb = flash_root; fb; fb = fb->next) { + /* Block: ------X------Y-------- + * Data: a-----b + * a--b + * a-----------b + * Block intersects with data, if: + * a < Y && b > x + */ + + unsigned X = fb->addr, Y = fb->addr + fb->length; + unsigned a = addr, b = addr + length; + if(a < Y && b > X) { + // from start of the block + unsigned start = (a > X ? a : X) - X; + unsigned end = (b > Y ? Y : b) - X; + + memcpy(fb->data + start, data, end - start); + + fit_blocks++; + fit_length += end - start; + } + } + + if(fit_blocks == 0) { + ELOG("Unfit data block %08x -> %04x\n", addr, length); + return -1; + } + + if(fit_length != length) { + WLOG("data block %08x -> %04x truncated to %04x\n", + addr, length, fit_length); + WLOG("(this is not an error, just a GDB glitch)\n"); + } + + return 0; +} + +static int flash_go(stlink_t *sl) { + int error = -1; + + // Some kinds of clock settings do not allow writing to flash. + stlink_reset(sl); + stlink_force_debug(sl); + + for(struct flash_block* fb = flash_root; fb; fb = fb->next) { + DLOG("flash_do: block %08x -> %04x\n", fb->addr, fb->length); + + for(stm32_addr_t page = fb->addr; page < fb->addr + fb->length; page += FLASH_PAGE) { + unsigned length = fb->length - (page - fb->addr); + + //Update FLASH_PAGE + stlink_calculate_pagesize(sl, page); + + DLOG("flash_do: page %08x\n", page); + unsigned send = length > FLASH_PAGE ? FLASH_PAGE : length; + if(stlink_write_flash(sl, page, fb->data + (page - fb->addr), + send, 0) < 0) + goto error; + length -= send; + + } + } + + stlink_reset(sl); + + error = 0; + +error: + for(struct flash_block* fb = flash_root, *next; fb; fb = next) { + next = fb->next; + free(fb->data); + free(fb); + } + + flash_root = NULL; + + return error; +} + +#define CLIDR 0xE000ED78 +#define CTR 0xE000ED7C +#define CCSIDR 0xE000ED80 +#define CSSELR 0xE000ED84 +#define CCR 0xE000ED14 +#define CCR_DC (1 << 16) +#define CCR_IC (1 << 17) +#define DCCSW 0xE000EF6C +#define ICIALLU 0xE000EF50 + +struct cache_level_desc +{ + unsigned int nsets; + unsigned int nways; + unsigned int log2_nways; + unsigned int width; +}; + +struct cache_desc_t +{ + /* Minimal line size in bytes. */ + unsigned int dminline; + unsigned int iminline; + + /* Last level of unification (uniprocessor). */ + unsigned int louu; + + struct cache_level_desc icache[7]; + struct cache_level_desc dcache[7]; +}; + +static struct cache_desc_t cache_desc; + +/* Return the smallest R so that V <= (1 << R). Not performance critical. */ +static unsigned ceil_log2(unsigned v) +{ + unsigned res; + for (res = 0; (1U << res) < v; res++) + ; + return res; +} + +static void read_cache_level_desc(stlink_t *sl, struct cache_level_desc *desc) +{ + unsigned int ccsidr; + unsigned int log2_nsets; + + stlink_read_debug32(sl, CCSIDR, &ccsidr); + desc->nsets = ((ccsidr >> 13) & 0x3fff) + 1; + desc->nways = ((ccsidr >> 3) & 0x1ff) + 1; + desc->log2_nways = ceil_log2 (desc->nways); + log2_nsets = ceil_log2 (desc->nsets); + desc->width = 4 + (ccsidr & 7) + log2_nsets; + ILOG("%08x LineSize: %u, ways: %u, sets: %u (width: %u)\n", + ccsidr, 4 << (ccsidr & 7), desc->nways, desc->nsets, desc->width); +} + +static void init_cache (stlink_t *sl) { + unsigned int clidr; + unsigned int ccr; + unsigned int ctr; + int i; + + /* Assume only F7 has a cache. */ + if(sl->chip_id!=STM32_CHIPID_F7) + return; + + stlink_read_debug32(sl, CLIDR, &clidr); + stlink_read_debug32(sl, CCR, &ccr); + stlink_read_debug32(sl, CTR, &ctr); + cache_desc.dminline = 4 << ((ctr >> 16) & 0x0f); + cache_desc.iminline = 4 << (ctr & 0x0f); + cache_desc.louu = (clidr >> 27) & 7; + + ILOG("Chip clidr: %08x, I-Cache: %s, D-Cache: %s\n", + clidr, ccr & CCR_IC ? "on" : "off", ccr & CCR_DC ? "on" : "off"); + ILOG(" cache: LoUU: %u, LoC: %u, LoUIS: %u\n", + (clidr >> 27) & 7, (clidr >> 24) & 7, (clidr >> 21) & 7); + ILOG(" cache: ctr: %08x, DminLine: %u bytes, IminLine: %u bytes\n", ctr, + cache_desc.dminline, cache_desc.iminline); + for(i = 0; i < 7; i++) + { + unsigned int ct = (clidr >> (3 * i)) & 0x07; + + cache_desc.dcache[i].width = 0; + cache_desc.icache[i].width = 0; + + if(ct == 2 || ct == 3 || ct == 4) + { + /* Data. */ + stlink_write_debug32(sl, CSSELR, i << 1); + ILOG("D-Cache L%d: ", i); + read_cache_level_desc(sl, &cache_desc.dcache[i]); + } + + if(ct == 1 || ct == 3) + { + /* Instruction. */ + stlink_write_debug32(sl, CSSELR, (i << 1) | 1); + ILOG("I-Cache L%d: ", i); + read_cache_level_desc(sl, &cache_desc.icache[i]); + } + } +} + +static void cache_flush(stlink_t *sl, unsigned ccr) { + int level; + + if (ccr & CCR_DC) + for (level = cache_desc.louu - 1; level >= 0; level--) + { + struct cache_level_desc *desc = &cache_desc.dcache[level]; + unsigned addr; + unsigned max_addr = 1 << desc->width; + unsigned way_sh = 32 - desc->log2_nways; + + /* D-cache clean by set-ways. */ + for (addr = (level << 1); addr < max_addr; addr += cache_desc.dminline) + { + unsigned int way; + + for (way = 0; way < desc->nways; way++) + stlink_write_debug32(sl, DCCSW, addr | (way << way_sh)); + } + } + + /* Invalidate all I-cache to oPU. */ + if (ccr & CCR_IC) + stlink_write_debug32(sl, ICIALLU, 0); +} + +static int cache_modified; + +static void cache_change(stm32_addr_t start, unsigned count) +{ + if (count == 0) + return; + (void)start; + cache_modified = 1; +} + +static void cache_sync(stlink_t *sl) +{ + unsigned ccr; + + if(sl->chip_id!=STM32_CHIPID_F7) + return; + if (!cache_modified) + return; + cache_modified = 0; + + stlink_read_debug32(sl, CCR, &ccr); + if (ccr & (CCR_IC | CCR_DC)) + cache_flush(sl, ccr); +} + +int serve(stlink_t *sl, st_state_t *st) { + int sock = socket(AF_INET, SOCK_STREAM, 0); + if(sock < 0) { + perror("socket"); + return 1; + } + + unsigned int val = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); + + struct sockaddr_in serv_addr; + memset(&serv_addr,0,sizeof(struct sockaddr_in)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(st->listen_port); + + if(bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + perror("bind"); + return 1; + } + + if(listen(sock, 5) < 0) { + perror("listen"); + return 1; + } + + ILOG("Listening at *:%d...\n", st->listen_port); + + int client = accept(sock, NULL, NULL); + //signal (SIGINT, SIG_DFL); + if(client < 0) { + perror("accept"); + return 1; + } + + close(sock); + + stlink_force_debug(sl); + if (st->reset) { + stlink_reset(sl); + } + init_code_breakpoints(sl); + init_data_watchpoints(sl); + + ILOG("GDB connected.\n"); + + /* + * To allow resetting the chip from GDB it is required to + * emulate attaching and detaching to target. + */ + unsigned int attached = 1; + + while(1) { + char* packet; + + int status = gdb_recv_packet(client, &packet); + if(status < 0) { + ELOG("cannot recv: %d\n", status); +#ifdef __MINGW32__ + win32_close_socket(sock); +#endif + return 1; + } + + DLOG("recv: %s\n", packet); + + char* reply = NULL; + reg regp; + + switch(packet[0]) { + case 'q': { + if(packet[1] == 'P' || packet[1] == 'C' || packet[1] == 'L') { + reply = strdup(""); + break; + } + + char *separator = strstr(packet, ":"), *params = ""; + if(separator == NULL) { + separator = packet + strlen(packet); + } else { + params = separator + 1; + } + + unsigned queryNameLength = (separator - &packet[1]); + char* queryName = calloc(queryNameLength + 1, 1); + strncpy(queryName, &packet[1], queryNameLength); + + DLOG("query: %s;%s\n", queryName, params); + + if(!strcmp(queryName, "Supported")) { + if(sl->chip_id==STM32_CHIPID_F4 + || sl->chip_id==STM32_CHIPID_F4_HD + || sl->chip_id==STM32_CHIPID_F7) { + reply = strdup("PacketSize=3fff;qXfer:memory-map:read+;qXfer:features:read+"); + } + else { + reply = strdup("PacketSize=3fff;qXfer:memory-map:read+"); + } + } else if(!strcmp(queryName, "Xfer")) { + char *type, *op, *__s_addr, *s_length; + char *tok = params; + char *annex __attribute__((unused)); + + type = strsep(&tok, ":"); + op = strsep(&tok, ":"); + annex = strsep(&tok, ":"); + __s_addr = strsep(&tok, ","); + s_length = tok; + + unsigned addr = strtoul(__s_addr, NULL, 16), + length = strtoul(s_length, NULL, 16); + + DLOG("Xfer: type:%s;op:%s;annex:%s;addr:%d;length:%d\n", + type, op, annex, addr, length); + + const char* data = NULL; + + if(!strcmp(type, "memory-map") && !strcmp(op, "read")) + data = current_memory_map; + + if(!strcmp(type, "features") && !strcmp(op, "read")) + data = target_description_F4; + + if(data) { + unsigned data_length = strlen(data); + if(addr + length > data_length) + length = data_length - addr; + + if(length == 0) { + reply = strdup("l"); + } else { + reply = calloc(length + 2, 1); + reply[0] = 'm'; + strncpy(&reply[1], data, length); + } + } + } else if(!strncmp(queryName, "Rcmd,",4)) { + // Rcmd uses the wrong separator + char *separator = strstr(packet, ","), *params = ""; + if(separator == NULL) { + separator = packet + strlen(packet); + } else { + params = separator + 1; + } + + + if (!strncmp(params,"726573756d65",12)) {// resume + DLOG("Rcmd: resume\n"); + cache_sync(sl); + stlink_run(sl); + + reply = strdup("OK"); + } else if (!strncmp(params,"68616c74",8)) { //halt + reply = strdup("OK"); + + stlink_force_debug(sl); + + DLOG("Rcmd: halt\n"); + } else if (!strncmp(params,"6a7461675f7265736574",20)) { //jtag_reset + reply = strdup("OK"); + + stlink_jtag_reset(sl, 0); + stlink_jtag_reset(sl, 1); + stlink_force_debug(sl); + + DLOG("Rcmd: jtag_reset\n"); + } else if (!strncmp(params,"7265736574",10)) { //reset + reply = strdup("OK"); + + stlink_force_debug(sl); + stlink_reset(sl); + init_code_breakpoints(sl); + init_data_watchpoints(sl); + + DLOG("Rcmd: reset\n"); + } else { + DLOG("Rcmd: %s\n", params); + } + + } + + if(reply == NULL) + reply = strdup(""); + + free(queryName); + + break; + } + + case 'v': { + char *params = NULL; + char *cmdName = strtok_r(packet, ":;", ¶ms); + + cmdName++; // vCommand -> Command + + if(!strcmp(cmdName, "FlashErase")) { + char *__s_addr, *s_length; + char *tok = params; + + __s_addr = strsep(&tok, ","); + s_length = tok; + + unsigned addr = strtoul(__s_addr, NULL, 16), + length = strtoul(s_length, NULL, 16); + + DLOG("FlashErase: addr:%08x,len:%04x\n", + addr, length); + + if(flash_add_block(addr, length, sl) < 0) { + reply = strdup("E00"); + } else { + reply = strdup("OK"); + } + } else if(!strcmp(cmdName, "FlashWrite")) { + char *__s_addr, *data; + char *tok = params; + + __s_addr = strsep(&tok, ":"); + data = tok; + + unsigned addr = strtoul(__s_addr, NULL, 16); + unsigned data_length = status - (data - packet); + + // Length of decoded data cannot be more than + // encoded, as escapes are removed. + // Additional byte is reserved for alignment fix. + uint8_t *decoded = calloc(data_length + 1, 1); + unsigned dec_index = 0; + for(unsigned int i = 0; i < data_length; i++) { + if(data[i] == 0x7d) { + i++; + decoded[dec_index++] = data[i] ^ 0x20; + } else { + decoded[dec_index++] = data[i]; + } + } + + // Fix alignment + if(dec_index % 2 != 0) + dec_index++; + + DLOG("binary packet %d -> %d\n", data_length, dec_index); + + if(flash_populate(addr, decoded, dec_index) < 0) { + reply = strdup("E00"); + } else { + reply = strdup("OK"); + } + } else if(!strcmp(cmdName, "FlashDone")) { + if(flash_go(sl) < 0) { + reply = strdup("E00"); + } else { + reply = strdup("OK"); + } + } else if(!strcmp(cmdName, "Kill")) { + attached = 0; + + reply = strdup("OK"); + } + + if(reply == NULL) + reply = strdup(""); + + break; + } + + case 'c': + cache_sync(sl); + stlink_run(sl); + + while(1) { + int status = gdb_check_for_interrupt(client); + if(status < 0) { + ELOG("cannot check for int: %d\n", status); +#ifdef __MINGW32__ + win32_close_socket(sock); +#endif + return 1; + } + + if(status == 1) { + stlink_force_debug(sl); + break; + } + + stlink_status(sl); + if(sl->core_stat == STLINK_CORE_HALTED) { + break; + } + + usleep(100000); + } + + reply = strdup("S05"); // TRAP + break; + + case 's': + cache_sync(sl); + stlink_step(sl); + + reply = strdup("S05"); // TRAP + break; + + case '?': + if(attached) { + reply = strdup("S05"); // TRAP + } else { + /* Stub shall reply OK if not attached. */ + reply = strdup("OK"); + } + break; + + case 'g': + stlink_read_all_regs(sl, ®p); + + reply = calloc(8 * 16 + 1, 1); + for(int i = 0; i < 16; i++) + sprintf(&reply[i * 8], "%08x", htonl(regp.r[i])); + + break; + + case 'p': { + unsigned id = strtoul(&packet[1], NULL, 16); + unsigned myreg = 0xDEADDEAD; + + if(id < 16) { + stlink_read_reg(sl, id, ®p); + myreg = htonl(regp.r[id]); + } else if(id == 0x19) { + stlink_read_reg(sl, 16, ®p); + myreg = htonl(regp.xpsr); + } else if(id == 0x1A) { + stlink_read_reg(sl, 17, ®p); + myreg = htonl(regp.main_sp); + } else if(id == 0x1B) { + stlink_read_reg(sl, 18, ®p); + myreg = htonl(regp.process_sp); + } else if(id == 0x1C) { + stlink_read_unsupported_reg(sl, id, ®p); + myreg = htonl(regp.control); + } else if(id == 0x1D) { + stlink_read_unsupported_reg(sl, id, ®p); + myreg = htonl(regp.faultmask); + } else if(id == 0x1E) { + stlink_read_unsupported_reg(sl, id, ®p); + myreg = htonl(regp.basepri); + } else if(id == 0x1F) { + stlink_read_unsupported_reg(sl, id, ®p); + myreg = htonl(regp.primask); + } else if(id >= 0x20 && id < 0x40) { + stlink_read_unsupported_reg(sl, id, ®p); + myreg = htonl(regp.s[id-0x20]); + } else if(id == 0x40) { + stlink_read_unsupported_reg(sl, id, ®p); + myreg = htonl(regp.fpscr); + } else { + reply = strdup("E00"); + } + + reply = calloc(8 + 1, 1); + sprintf(reply, "%08x", myreg); + + break; + } + + case 'P': { + char* s_reg = &packet[1]; + char* s_value = strstr(&packet[1], "=") + 1; + + unsigned reg = strtoul(s_reg, NULL, 16); + unsigned value = strtoul(s_value, NULL, 16); + + if(reg < 16) { + stlink_write_reg(sl, ntohl(value), reg); + } else if(reg == 0x19) { + stlink_write_reg(sl, ntohl(value), 16); + } else if(reg == 0x1A) { + stlink_write_reg(sl, ntohl(value), 17); + } else if(reg == 0x1B) { + stlink_write_reg(sl, ntohl(value), 18); + } else if(reg == 0x1C) { + stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); + } else if(reg == 0x1D) { + stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); + } else if(reg == 0x1E) { + stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); + } else if(reg == 0x1F) { + stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); + } else if(reg >= 0x20 && reg < 0x40) { + stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); + } else if(reg == 0x40) { + stlink_write_unsupported_reg(sl, ntohl(value), reg, ®p); + } else { + reply = strdup("E00"); + } + + if(!reply) { + reply = strdup("OK"); + } + + break; + } + + case 'G': + for(int i = 0; i < 16; i++) { + char str[9] = {0}; + strncpy(str, &packet[1 + i * 8], 8); + uint32_t reg = strtoul(str, NULL, 16); + stlink_write_reg(sl, ntohl(reg), i); + } + + reply = strdup("OK"); + break; + + case 'm': { + char* s_start = &packet[1]; + char* s_count = strstr(&packet[1], ",") + 1; + + stm32_addr_t start = strtoul(s_start, NULL, 16); + unsigned count = strtoul(s_count, NULL, 16); + + unsigned adj_start = start % 4; + unsigned count_rnd = (count + adj_start + 4 - 1) / 4 * 4; + if (count_rnd > sl->flash_pgsz) + count_rnd = sl->flash_pgsz; + if (count_rnd > 0x1800) + count_rnd = 0x1800; + if (count_rnd < count) + count = count_rnd; + + stlink_read_mem32(sl, start - adj_start, count_rnd); + + reply = calloc(count * 2 + 1, 1); + for(unsigned int i = 0; i < count; i++) { + reply[i * 2 + 0] = hex[sl->q_buf[i + adj_start] >> 4]; + reply[i * 2 + 1] = hex[sl->q_buf[i + adj_start] & 0xf]; + } + + break; + } + + case 'M': { + char* s_start = &packet[1]; + char* s_count = strstr(&packet[1], ",") + 1; + char* hexdata = strstr(packet, ":") + 1; + + stm32_addr_t start = strtoul(s_start, NULL, 16); + unsigned count = strtoul(s_count, NULL, 16); + + if(start % 4) { + unsigned align_count = 4 - start % 4; + if (align_count > count) align_count = count; + for(unsigned int i = 0; i < align_count; i ++) { + char hex[3] = { hexdata[i*2], hexdata[i*2+1], 0 }; + uint8_t byte = strtoul(hex, NULL, 16); + sl->q_buf[i] = byte; + } + stlink_write_mem8(sl, start, align_count); + cache_change(start, align_count); + start += align_count; + count -= align_count; + hexdata += 2*align_count; + } + + if(count - count % 4) { + unsigned aligned_count = count - count % 4; + + for(unsigned int i = 0; i < aligned_count; i ++) { + char hex[3] = { hexdata[i*2], hexdata[i*2+1], 0 }; + uint8_t byte = strtoul(hex, NULL, 16); + sl->q_buf[i] = byte; + } + stlink_write_mem32(sl, start, aligned_count); + cache_change(start, aligned_count); + count -= aligned_count; + start += aligned_count; + hexdata += 2*aligned_count; + } + + if(count) { + for(unsigned int i = 0; i < count; i ++) { + char hex[3] = { hexdata[i*2], hexdata[i*2+1], 0 }; + uint8_t byte = strtoul(hex, NULL, 16); + sl->q_buf[i] = byte; + } + stlink_write_mem8(sl, start, count); + cache_change(start, count); + } + reply = strdup("OK"); + break; + } + + case 'Z': { + char *endptr; + stm32_addr_t addr = strtoul(&packet[3], &endptr, 16); + stm32_addr_t len = strtoul(&endptr[1], NULL, 16); + + switch (packet[1]) { + case '1': + if(update_code_breakpoint(sl, addr, 1) < 0) { + reply = strdup("E00"); + } else { + reply = strdup("OK"); + } + break; + + case '2': // insert write watchpoint + case '3': // insert read watchpoint + case '4': { // insert access watchpoint + enum watchfun wf; + if(packet[1] == '2') { + wf = WATCHWRITE; + } else if(packet[1] == '3') { + wf = WATCHREAD; + } else { + wf = WATCHACCESS; + } + + if(add_data_watchpoint(sl, wf, addr, len) < 0) { + reply = strdup("E00"); + } else { + reply = strdup("OK"); + break; + } + } + + default: + reply = strdup(""); + } + break; + } + case 'z': { + char *endptr; + stm32_addr_t addr = strtoul(&packet[3], &endptr, 16); + //stm32_addr_t len = strtoul(&endptr[1], NULL, 16); + + switch (packet[1]) { + case '1': // remove breakpoint + update_code_breakpoint(sl, addr, 0); + reply = strdup("OK"); + break; + + case '2' : // remove write watchpoint + case '3' : // remove read watchpoint + case '4' : // remove access watchpoint + if(delete_data_watchpoint(sl, addr) < 0) { + reply = strdup("E00"); + } else { + reply = strdup("OK"); + break; + } + + default: + reply = strdup(""); + } + break; + } + + case '!': { + /* + * Enter extended mode which allows restarting. + * We do support that always. + */ + + /* + * Also, set to persistent mode + * to allow GDB disconnect. + */ + st->persistent = 1; + + reply = strdup("OK"); + + break; + } + + case 'R': { + /* Reset the core. */ + + stlink_reset(sl); + init_code_breakpoints(sl); + init_data_watchpoints(sl); + + attached = 1; + + reply = strdup("OK"); + + break; + } + + default: + reply = strdup(""); + } + + if(reply) { + DLOG("send: %s\n", reply); + + int result = gdb_send_packet(client, reply); + if(result != 0) { + ELOG("cannot send: %d\n", result); + free(reply); + free(packet); +#ifdef __MINGW32__ + win32_close_socket(sock); +#endif + return 1; + } + + free(reply); + } + + free(packet); + } + +#ifdef __MINGW32__ + win32_close_socket(sock); +#endif + + return 0; +} diff --git a/src/gdbserver/gdb-server.h b/src/gdbserver/gdb-server.h new file mode 100644 index 0000000..8c8d471 --- /dev/null +++ b/src/gdbserver/gdb-server.h @@ -0,0 +1,11 @@ +#ifndef _GDB_SERVER_H +#define _GDB_SERVER_H + +#define STRINGIFY_inner(name) #name +#define STRINGIFY(name) STRINGIFY_inner(name) + +#define DEFAULT_LOGGING_LEVEL 50 +#define DEBUG_LOGGING_LEVEL 100 +#define DEFAULT_GDB_LISTEN_PORT 4242 + +#endif diff --git a/src/logging.c b/src/logging.c new file mode 100644 index 0000000..4969c44 --- /dev/null +++ b/src/logging.c @@ -0,0 +1,57 @@ +/* + * UglyLogging. Slow, yet another wheel reinvented, but enough to make the + * rest of our code pretty enough. + * + */ +#include +#include +#include +#include +#include + +#include "stlink/logging.h" + +static int max_level; + +int ugly_init(int maximum_threshold) { + max_level = maximum_threshold; + return 0; +} + +int ugly_log(int level, const char *tag, const char *format, ...) { + if (level > max_level) { + return 0; + } + va_list args; + va_start(args, format); + time_t mytt = time(NULL); + struct tm *tt; + tt = localtime(&mytt); + fprintf(stderr, "%d-%02d-%02dT%02d:%02d:%02d ", tt->tm_year + 1900, tt->tm_mon + 1, tt->tm_mday, tt->tm_hour, tt->tm_min, tt->tm_sec); + switch (level) { + case UDEBUG: + fprintf(stderr, "DEBUG %s: ", tag); + break; + case UINFO: + fprintf(stderr, "INFO %s: ", tag); + break; + case UWARN: + fprintf(stderr, "WARN %s: ", tag); + break; + case UERROR: + fprintf(stderr, "ERROR %s: ", tag); + break; + case UFATAL: + fprintf(stderr, "FATAL %s: ", tag); + vfprintf(stderr, format, args); + exit(EXIT_FAILURE); + // NEVER GETS HERE!!! + break; + default: + fprintf(stderr, "%d %s: ", level, tag); + break; + } + vfprintf(stderr, format, args); + va_end(args); + return 1; +} diff --git a/src/mingw/mingw.c b/src/mingw/mingw.c new file mode 100644 index 0000000..3c5d025 --- /dev/null +++ b/src/mingw/mingw.c @@ -0,0 +1,270 @@ +#ifdef __MINGW32__ + +#include "mingw.h" + +#undef socket +#undef connect +#undef accept +#undef shutdown + +#include +#include +#include + +int win32_poll(struct pollfd *fds, unsigned int nfds, int timo) +{ + struct timeval timeout, *toptr; + fd_set ifds, ofds, efds, *ip, *op; + unsigned int i, rc; + + /* Set up the file-descriptor sets in ifds, ofds and efds. */ + FD_ZERO(&ifds); + FD_ZERO(&ofds); + FD_ZERO(&efds); + for (i = 0, op = ip = 0; i < nfds; ++i) { + fds[i].revents = 0; + if(fds[i].events & (POLLIN|POLLPRI)) { + ip = &ifds; + FD_SET(fds[i].fd, ip); + } + if(fds[i].events & POLLOUT) { + op = &ofds; + FD_SET(fds[i].fd, op); + } + FD_SET(fds[i].fd, &efds); + } + + /* Set up the timeval structure for the timeout parameter */ + if(timo < 0) { + toptr = 0; + } else { + toptr = &timeout; + timeout.tv_sec = timo / 1000; + timeout.tv_usec = (timo - timeout.tv_sec * 1000) * 1000; + } + +#ifdef DEBUG_POLL + printf("Entering select() sec=%ld usec=%ld ip=%lx op=%lx\n", + (long)timeout.tv_sec, (long)timeout.tv_usec, (long)ip, (long)op); +#endif + rc = select(0, ip, op, &efds, toptr); +#ifdef DEBUG_POLL + printf("Exiting select rc=%d\n", rc); +#endif + + if(rc <= 0) + return rc; + + if(rc > 0) { + for ( i = 0; i < nfds; ++i) { + int fd = fds[i].fd; + if(fds[i].events & (POLLIN|POLLPRI) && FD_ISSET(fd, &ifds)) + fds[i].revents |= POLLIN; + if(fds[i].events & POLLOUT && FD_ISSET(fd, &ofds)) + fds[i].revents |= POLLOUT; + if(FD_ISSET(fd, &efds)) + /* Some error was detected ... should be some way to know. */ + fds[i].revents |= POLLHUP; +#ifdef DEBUG_POLL + printf("%d %d %d revent = %x\n", + FD_ISSET(fd, &ifds), FD_ISSET(fd, &ofds), FD_ISSET(fd, &efds), + fds[i].revents + ); +#endif + } + } + return rc; +} +static void +set_connect_errno(int winsock_err) +{ + switch(winsock_err) { + case WSAEINVAL: + case WSAEALREADY: + case WSAEWOULDBLOCK: + errno = EINPROGRESS; + break; + default: + errno = winsock_err; + break; + } +} + +static void +set_socket_errno(int winsock_err) +{ + switch(winsock_err) { + case WSAEWOULDBLOCK: + errno = EAGAIN; + break; + default: + errno = winsock_err; + break; + } +} +/* + * A wrapper around the socket() function. The purpose of this wrapper + * is to ensure that the global errno symbol is set if an error occurs, + * even if we are using winsock. + */ +SOCKET +win32_socket(int domain, int type, int protocol) +{ + SOCKET fd = socket(domain, type, protocol); + if(fd == INVALID_SOCKET) { + set_socket_errno(WSAGetLastError()); + } + return fd; +} +/* + * A wrapper around the connect() function. The purpose of this wrapper + * is to ensure that the global errno symbol is set if an error occurs, + * even if we are using winsock. + */ +int +win32_connect(SOCKET fd, struct sockaddr *addr, socklen_t addr_len) +{ + int rc = connect(fd, addr, addr_len); + assert(rc == 0 || rc == SOCKET_ERROR); + if(rc == SOCKET_ERROR) { + set_connect_errno(WSAGetLastError()); + } + return rc; +} + +/* + * A wrapper around the accept() function. The purpose of this wrapper + * is to ensure that the global errno symbol is set if an error occurs, + * even if we are using winsock. + */ +SOCKET +win32_accept(SOCKET fd, struct sockaddr *addr, socklen_t *addr_len) +{ + SOCKET newfd = accept(fd, addr, addr_len); + if(newfd == INVALID_SOCKET) { + set_socket_errno(WSAGetLastError()); + newfd = -1; + } + return newfd; +} + +/* + * A wrapper around the shutdown() function. The purpose of this wrapper + * is to ensure that the global errno symbol is set if an error occurs, + * even if we are using winsock. + */ +int +win32_shutdown(SOCKET fd, int mode) +{ + int rc = shutdown(fd, mode); + assert(rc == 0 || rc == SOCKET_ERROR); + if(rc == SOCKET_ERROR) { + set_socket_errno(WSAGetLastError()); + } + return rc; +} + +int win32_close_socket(SOCKET fd) +{ + int rc = closesocket(fd); + if(rc == SOCKET_ERROR) { + set_socket_errno(WSAGetLastError()); + } + return rc; +} + +ssize_t win32_write_socket(SOCKET fd, void *buf, int n) +{ + int rc = send(fd, buf, n, 0); + if(rc == SOCKET_ERROR) { + set_socket_errno(WSAGetLastError()); + } + return rc; +} + +ssize_t win32_read_socket(SOCKET fd, void *buf, int n) +{ + int rc = recv(fd, buf, n, 0); + if(rc == SOCKET_ERROR) { + set_socket_errno(WSAGetLastError()); + } + return rc; +} + + +char * win32_strtok_r(char *s, const char *delim, char **lasts) +{ + register char *spanp; + register int c, sc; + char *tok; + + + if (s == NULL && (s = *lasts) == NULL) + return (NULL); + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + *lasts = NULL; + return (NULL); + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = (char *)delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *lasts = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +char *win32_strsep (char **stringp, const char *delim) +{ + register char *s; + register const char *spanp; + register int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +#endif + + diff --git a/src/mingw/mingw.h b/src/mingw/mingw.h new file mode 100644 index 0000000..cb33301 --- /dev/null +++ b/src/mingw/mingw.h @@ -0,0 +1,70 @@ +#ifdef __MINGW32__ + +#include + +#define _USE_W32_SOCKETS 1 +#include + +#define ENOTCONN WSAENOTCONN +#define EWOULDBLOCK WSAEWOULDBLOCK +#define ENOBUFS WSAENOBUFS +#define ECONNRESET WSAECONNRESET +#define ESHUTDOWN WSAESHUTDOWN +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#define EINPROGRESS WSAEINPROGRESS +#define EISCONN WSAEISCONN + +/* winsock doesn't feature poll(), so there is a version implemented + * in terms of select() in mingw.c. The following definitions + * are copied from linux man pages. A poll() macro is defined to + * call the version in mingw.c. + */ +#define POLLIN 0x0001 /* There is data to read */ +#define POLLPRI 0x0002 /* There is urgent data to read */ +#define POLLOUT 0x0004 /* Writing now will not block */ +#define POLLERR 0x0008 /* Error condition */ +#define POLLHUP 0x0010 /* Hung up */ +#define POLLNVAL 0x0020 /* Invalid request: fd not open */ +struct pollfd { + SOCKET fd; /* file descriptor */ + short events; /* requested events */ + short revents; /* returned events */ +}; +#define poll(x, y, z) win32_poll(x, y, z) + +/* These wrappers do nothing special except set the global errno variable if + * an error occurs (winsock doesn't do this by default). They set errno + * to unix-like values (i.e. WSAEWOULDBLOCK is mapped to EAGAIN), so code + * outside of this file "shouldn't" have to worry about winsock specific error + * handling. + */ +#define socket(x, y, z) win32_socket(x, y, z) +#define connect(x, y, z) win32_connect(x, y, z) +#define accept(x, y, z) win32_accept(x, y, z) +#define shutdown(x, y) win32_shutdown(x, y) +#define read(x, y, z) win32_read_socket(x, y, z) +#define write(x, y, z) win32_write_socket(x, y, z) + +/* Winsock uses int instead of the usual socklen_t */ +typedef int socklen_t; + +int win32_poll(struct pollfd *, unsigned int, int); +SOCKET win32_socket(int, int, int); +int win32_connect(SOCKET, struct sockaddr*, socklen_t); +SOCKET win32_accept(SOCKET, struct sockaddr*, socklen_t *); +int win32_shutdown(SOCKET, int); +int win32_close_socket(SOCKET fd); + +#define strtok_r(x, y, z) win32_strtok_r(x, y, z) +#define strsep(x,y) win32_strsep(x,y) + +char *win32_strtok_r(char *s, const char *delim, char **lasts); +char *win32_strsep(char **stringp, const char *delim); + +ssize_t win32_read_socket(SOCKET fd, void *buf, int n); +ssize_t win32_write_socket(SOCKET fd, void *buf, int n); + +static inline void sleep(unsigned ms) { Sleep(ms); } + +#endif diff --git a/src/mmap.c b/src/mmap.c index c5794da..bee0dce 100644 --- a/src/mmap.c +++ b/src/mmap.c @@ -3,7 +3,7 @@ #include #include -#include "mmap.h" +#include "stlink/mmap.h" void *mmap (void *addr, size_t len, int prot, int flags, int fd, long long offset) { diff --git a/src/mmap.h b/src/mmap.h deleted file mode 100644 index 71de819..0000000 --- a/src/mmap.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef STLINK_MMAP_H -#define STLINK_MMAP_H - -#ifdef HAVE_SYS_MMAN_H -#include -#else - -#define PROT_READ (1<<0) -#define PROT_WRITE (1<<1) - -#define MAP_SHARED (1<<0) -#define MAP_PRIVATE (1<<1) - -#define MAP_ANONYMOUS (1<<5) - -#define MAP_FAILED ((void *)-1) - -#ifdef __cplusplus -extern "C" { -#endif - - void *mmap(void *addr, size_t len, int prot, int flags, int fd, long long offset); - int munmap(void *addr, size_t len); - -#ifdef __cplusplus -} -#endif - -#endif /* HAVE_SYS_MMAN_H */ - -#endif /* STLINK_MMAP_H */ diff --git a/src/sg.c b/src/sg.c new file mode 100644 index 0000000..4057098 --- /dev/null +++ b/src/sg.c @@ -0,0 +1,1088 @@ +/* + * Copyright (c) 2010 "Capt'ns Missing Link" Authors. All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + * + * A linux stlink access demo. The purpose of this file is to mitigate the usual + * "reinventing the wheel" force by incompatible licenses and give you an idea, + * how to access the stlink device. That doesn't mean you should be a free-loader + * and not contribute your improvements to this code. + * + * Author: Martin Capitanio + * The stlink related constants kindly provided by Oliver Spencer (OpenOCD) + * for use in a GPL compatible license. + * + * Tested compatibility: linux, gcc >= 4.3.3 + * + * The communication is based on standard USB mass storage device + * BOT (Bulk Only Transfer) + * - Endpoint 1: BULK_IN, 64 bytes max + * - Endpoint 2: BULK_OUT, 64 bytes max + * + * All CBW transfers are ordered with the LSB (byte 0) first (little endian). + * Any command must be answered before sending the next command. + * Each USB transfer must complete in less than 1s. + * + * SB Device Class Definition for Mass Storage Devices: + * www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf + * + * dt - Data Transfer (IN/OUT) + * CBW - Command Block Wrapper + * CSW - Command Status Wrapper + * RFU - Reserved for Future Use + * + * Originally, this driver used scsi pass through commands, which required the + * usb-storage module to be loaded, providing the /dev/sgX links. The USB mass + * storage implementation on the STLinkv1 is however terribly broken, and it can + * take many minutes for the kernel to give up. + * + * However, in Nov 2011, the scsi pass through was replaced by raw libusb, so + * instead of having to let usb-storage struggle with the device, and also greatly + * limiting the portability of the driver, you can now tell usb-storage to simply + * ignore this device completely. + * + * usb-storage.quirks + * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob_plain;f=Documentation/kernel-parameters.txt + * Each entry has the form VID:PID:Flags where VID and PID are Vendor and Product + * ID values (4-digit hex numbers) and Flags is a set of characters, each corresponding + * to a common usb-storage quirk flag as follows: + * + * a = SANE_SENSE (collect more than 18 bytes of sense data); + * b = BAD_SENSE (don't collect more than 18 bytes of sense data); + * c = FIX_CAPACITY (decrease the reported device capacity by one sector); + * h = CAPACITY_HEURISTICS (decrease the reported device capacity by one sector if the number is odd); + * i = IGNORE_DEVICE (don't bind to this device); + * l = NOT_LOCKABLE (don't try to lock and unlock ejectable media); + * m = MAX_SECTORS_64 (don't transfer more than 64 sectors = 32 KB at a time); + * o = CAPACITY_OK (accept the capacity reported by the device); + * r = IGNORE_RESIDUE (the device reports bogus residue values); + * s = SINGLE_LUN (the device has only one Logical Unit); + * w = NO_WP_DETECT (don't test whether the medium is write-protected). + * + * Example: quirks=0419:aaf5:rl,0421:0433:rc + * http://permalink.gmane.org/gmane.linux.usb.general/35053 + * + * For the stlinkv1, you just want the following + * + * modprobe -r usb-storage && modprobe usb-storage quirks=483:3744:i + * + * Equivalently, you can add a line saying + * + * options usb-storage quirks=483:3744:i + * + * to your /etc/modprobe.conf or /etc/modprobe.d/local.conf (or add the "quirks=..." + * part to an existing options line for usb-storage). + */ + + +#define __USE_GNU +#include +#include +#include +#include +#include + +#include "stlink.h" +#include "stlink/logging.h" + +static void clear_cdb(struct stlink_libsg *sl) { + for (size_t i = 0; i < sizeof (sl->cdb_cmd_blk); i++) + sl->cdb_cmd_blk[i] = 0; + // set default + sl->cdb_cmd_blk[0] = STLINK_DEBUG_COMMAND; + sl->q_data_dir = Q_DATA_IN; +} + +/** + * Close and free any _backend_ related information... + * @param sl + */ +void _stlink_sg_close(stlink_t *sl) { + if (sl) { + struct stlink_libsg *slsg = sl->backend_data; + libusb_close(slsg->usb_handle); + libusb_exit(slsg->libusb_ctx); + free(slsg); + } +} + +static int get_usb_mass_storage_status(libusb_device_handle *handle, uint8_t endpoint, uint32_t *tag) +{ + unsigned char csw[13]; + memset(csw, 0, sizeof(csw)); + int transferred; + int ret; + int try = 0; + do { + ret = libusb_bulk_transfer(handle, endpoint, (unsigned char *)&csw, sizeof(csw), + &transferred, SG_TIMEOUT_MSEC); + if (ret == LIBUSB_ERROR_PIPE) { + libusb_clear_halt(handle, endpoint); + } + try++; + } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3)); + if (ret != LIBUSB_SUCCESS) { + WLOG("%s: receiving failed: %d\n", __func__, ret); + return -1; + } + if (transferred != sizeof(csw)) { + WLOG("%s: received unexpected amount: %d\n", __func__, transferred); + return -1; + } + + uint32_t rsig = read_uint32(csw, 0); + uint32_t rtag = read_uint32(csw, 4); + /* uint32_t residue = read_uint32(csw, 8); */ +#define USB_CSW_SIGNATURE 0x53425355 // 'U' 'S' 'B' 'S' (reversed) + if (rsig != USB_CSW_SIGNATURE) { + WLOG("status signature was invalid: %#x\n", rsig); + return -1; + } + *tag = rtag; + uint8_t rstatus = csw[12]; + return rstatus; +} + +static int dump_CDB_command(uint8_t *cdb, uint8_t cdb_len) { + char dbugblah[100]; + char *dbugp = dbugblah; + dbugp += sprintf(dbugp, "Sending CDB ["); + for (uint8_t i = 0; i < cdb_len; i++) { + dbugp += sprintf(dbugp, " %#02x", (unsigned int) cdb[i]); + } + sprintf(dbugp, "]\n"); + DLOG(dbugblah); + return 0; +} + +/** + * Wraps a CDB mass storage command in the appropriate gunk to get it down + * @param handle + * @param endpoint + * @param cdb + * @param cdb_length + * @param lun + * @param flags + * @param expected_rx_size + * @return + */ +int send_usb_mass_storage_command(libusb_device_handle *handle, uint8_t endpoint_out, + uint8_t *cdb, uint8_t cdb_length, + uint8_t lun, uint8_t flags, uint32_t expected_rx_size) { + DLOG("Sending usb m-s cmd: cdblen:%d, rxsize=%d\n", cdb_length, expected_rx_size); + dump_CDB_command(cdb, cdb_length); + + static uint32_t tag; + if (tag == 0) { + tag = 1; + } + + int try = 0; + int ret = 0; + int real_transferred; + int i = 0; + + uint8_t c_buf[STLINK_SG_SIZE]; + // tag is allegedly ignored... TODO - verify + c_buf[i++] = 'U'; + c_buf[i++] = 'S'; + c_buf[i++] = 'B'; + c_buf[i++] = 'C'; + write_uint32(&c_buf[i], tag); + uint32_t this_tag = tag++; + write_uint32(&c_buf[i+4], expected_rx_size); + i+= 8; + c_buf[i++] = flags; + c_buf[i++] = lun; + + c_buf[i++] = cdb_length; + + // Now the actual CDB request + assert(cdb_length <= CDB_SL); + memcpy(&(c_buf[i]), cdb, cdb_length); + + int sending_length = STLINK_SG_SIZE; + + // send.... + do { + ret = libusb_bulk_transfer(handle, endpoint_out, c_buf, sending_length, + &real_transferred, SG_TIMEOUT_MSEC); + if (ret == LIBUSB_ERROR_PIPE) { + libusb_clear_halt(handle, endpoint_out); + } + try++; + } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3)); + if (ret != LIBUSB_SUCCESS) { + WLOG("sending failed: %d\n", ret); + return -1; + } + return this_tag; +} + + +/** + * Straight from stm8 stlink code... + * @param handle + * @param endpoint_in + * @param endpoint_out + */ + static void +get_sense(libusb_device_handle *handle, uint8_t endpoint_in, uint8_t endpoint_out) +{ + DLOG("Fetching sense...\n"); + uint8_t cdb[16]; + memset(cdb, 0, sizeof(cdb)); +#define REQUEST_SENSE 0x03 +#define REQUEST_SENSE_LENGTH 18 + cdb[0] = REQUEST_SENSE; + cdb[4] = REQUEST_SENSE_LENGTH; + uint32_t tag = send_usb_mass_storage_command(handle, endpoint_out, cdb, sizeof(cdb), 0, + LIBUSB_ENDPOINT_IN, REQUEST_SENSE_LENGTH); + if (tag == 0) { + WLOG("refusing to send request sense with tag 0\n"); + return; + } + unsigned char sense[REQUEST_SENSE_LENGTH]; + int transferred; + int ret; + int try = 0; + do { + ret = libusb_bulk_transfer(handle, endpoint_in, sense, sizeof(sense), + &transferred, SG_TIMEOUT_MSEC); + if (ret == LIBUSB_ERROR_PIPE) { + libusb_clear_halt(handle, endpoint_in); + } + try++; + } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3)); + if (ret != LIBUSB_SUCCESS) { + WLOG("receiving sense failed: %d\n", ret); + return; + } + if (transferred != sizeof(sense)) { + WLOG("received unexpected amount of sense: %d != %d\n", transferred, sizeof(sense)); + } + uint32_t received_tag; + int status = get_usb_mass_storage_status(handle, endpoint_in, &received_tag); + if (status != 0) { + WLOG("receiving sense failed with status: %02x\n", status); + return; + } + if (sense[0] != 0x70 && sense[0] != 0x71) { + WLOG("No sense data\n"); + } else { + WLOG("Sense KCQ: %02X %02X %02X\n", sense[2] & 0x0f, sense[12], sense[13]); + } +} + +/** + * Just send a buffer on an endpoint, no questions asked. + * Handles repeats, and time outs. Also handles reading status reports and sense + * @param handle libusb device * + * @param endpoint_out sends + * @param endpoint_in used to read status reports back in + * @param cbuf what to send + * @param length how much to send + * @return number of bytes actually sent, or -1 for failures. + */ +int send_usb_data_only(libusb_device_handle *handle, unsigned char endpoint_out, + unsigned char endpoint_in, unsigned char *cbuf, unsigned int length) { + int ret; + int real_transferred; + int try = 0; + do { + ret = libusb_bulk_transfer(handle, endpoint_out, cbuf, length, + &real_transferred, SG_TIMEOUT_MSEC); + if (ret == LIBUSB_ERROR_PIPE) { + libusb_clear_halt(handle, endpoint_out); + } + try++; + } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3)); + if (ret != LIBUSB_SUCCESS) { + WLOG("sending failed: %d\n", ret); + return -1; + } + + // now, swallow up the status, so that things behave nicely... + uint32_t received_tag; + // -ve is for my errors, 0 is good, +ve is libusb sense status bytes + int status = get_usb_mass_storage_status(handle, endpoint_in, &received_tag); + if (status < 0) { + WLOG("receiving status failed: %d\n", status); + return -1; + } + if (status != 0) { + WLOG("receiving status not passed :(: %02x\n", status); + } + if (status == 1) { + get_sense(handle, endpoint_in, endpoint_out); + return -1; + } + + return real_transferred; +} + + +int stlink_q(stlink_t *sl) { + struct stlink_libsg* sg = sl->backend_data; + //uint8_t cdb_len = 6; // FIXME varies!!! + uint8_t cdb_len = 10; // FIXME varies!!! + uint8_t lun = 0; // always zero... + uint32_t tag = send_usb_mass_storage_command(sg->usb_handle, sg->ep_req, + sg->cdb_cmd_blk, cdb_len, lun, LIBUSB_ENDPOINT_IN, sl->q_len); + + + // now wait for our response... + // length copied from stlink-usb... + int rx_length = sl->q_len; + int try = 0; + int real_transferred; + int ret; + if (rx_length > 0) { + do { + ret = libusb_bulk_transfer(sg->usb_handle, sg->ep_rep, sl->q_buf, rx_length, + &real_transferred, SG_TIMEOUT_MSEC); + if (ret == LIBUSB_ERROR_PIPE) { + libusb_clear_halt(sg->usb_handle, sg->ep_req); + } + try++; + } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3)); + + if (ret != LIBUSB_SUCCESS) { + WLOG("Receiving failed: %d\n", ret); + return -1; + } + + if (real_transferred != rx_length) { + WLOG("received unexpected amount: %d != %d\n", real_transferred, rx_length); + } + } + + uint32_t received_tag; + // -ve is for my errors, 0 is good, +ve is libusb sense status bytes + int status = get_usb_mass_storage_status(sg->usb_handle, sg->ep_rep, &received_tag); + if (status < 0) { + WLOG("receiving status failed: %d\n", status); + return -1; + } + if (status != 0) { + WLOG("receiving status not passed :(: %02x\n", status); + } + if (status == 1) { + get_sense(sg->usb_handle, sg->ep_rep, sg->ep_req); + return -1; + } + if (received_tag != tag) { + WLOG("received tag %d but expected %d\n", received_tag, tag); + //return -1; + } + if (rx_length > 0 && real_transferred != rx_length) { + return -1; + } + return 0; +} + +// TODO thinking, cleanup + +void stlink_stat(stlink_t *stl, char *txt) { + if (stl->q_len <= 0) + return; + + stlink_print_data(stl); + + switch (stl->q_buf[0]) { + case STLINK_OK: + DLOG(" %s: ok\n", txt); + return; + case STLINK_FALSE: + DLOG(" %s: false\n", txt); + return; + default: + DLOG(" %s: unknown\n", txt); + } +} + + +int _stlink_sg_version(stlink_t *stl) { + struct stlink_libsg *sl = stl->backend_data; + clear_cdb(sl); + sl->cdb_cmd_blk[0] = STLINK_GET_VERSION; + stl->q_len = 6; + sl->q_addr = 0; + return stlink_q(stl); +} + +// Get stlink mode: +// STLINK_DEV_DFU_MODE || STLINK_DEV_MASS_MODE || STLINK_DEV_DEBUG_MODE +// usb dfu || usb mass || jtag or swd + +int _stlink_sg_current_mode(stlink_t *stl) { + struct stlink_libsg *sl = stl->backend_data; + clear_cdb(sl); + sl->cdb_cmd_blk[0] = STLINK_GET_CURRENT_MODE; + stl->q_len = 2; + sl->q_addr = 0; + if (stlink_q(stl)) + return -1; + + return stl->q_buf[0]; +} + +// Exit the mass mode and enter the swd debug mode. + +int _stlink_sg_enter_swd_mode(stlink_t *sl) { + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_ENTER; + sg->cdb_cmd_blk[2] = STLINK_DEBUG_ENTER_SWD; + sl->q_len = 0; // >0 -> aboard + return stlink_q(sl); +} + +// Exit the mass mode and enter the jtag debug mode. +// (jtag is disabled in the discovery's stlink firmware) + +int _stlink_sg_enter_jtag_mode(stlink_t *sl) { + struct stlink_libsg *sg = sl->backend_data; + DLOG("\n*** stlink_enter_jtag_mode ***\n"); + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_ENTER; + sg->cdb_cmd_blk[2] = STLINK_DEBUG_ENTER_JTAG; + sl->q_len = 0; + return stlink_q(sl); +} + +// XXX kernel driver performs reset, the device temporally disappears +// Suspect this is no longer the case when we have ignore on? RECHECK +int _stlink_sg_exit_dfu_mode(stlink_t *sl) { + struct stlink_libsg *sg = sl->backend_data; + DLOG("\n*** stlink_exit_dfu_mode ***\n"); + clear_cdb(sg); + sg->cdb_cmd_blk[0] = STLINK_DFU_COMMAND; + sg->cdb_cmd_blk[1] = STLINK_DFU_EXIT; + sl->q_len = 0; // ?? + return stlink_q(sl); + /* + [135121.844564] sd 19:0:0:0: [sdb] Unhandled error code + [135121.844569] sd 19:0:0:0: [sdb] Result: hostbyte=DID_ERROR driverbyte=DRIVER_OK + [135121.844574] sd 19:0:0:0: [sdb] CDB: Read(10): 28 00 00 00 10 00 00 00 08 00 + [135121.844584] end_request: I/O error, dev sdb, sector 4096 + [135121.844590] Buffer I/O error on device sdb, logical block 512 + [135130.122567] usb 6-1: reset full speed USB device using uhci_hcd and address 7 + [135130.274551] usb 6-1: device firmware changed + [135130.274618] usb 6-1: USB disconnect, address 7 + [135130.275186] VFS: busy inodes on changed media or resized disk sdb + [135130.275424] VFS: busy inodes on changed media or resized disk sdb + [135130.286758] VFS: busy inodes on changed media or resized disk sdb + [135130.292796] VFS: busy inodes on changed media or resized disk sdb + [135130.301481] VFS: busy inodes on changed media or resized disk sdb + [135130.304316] VFS: busy inodes on changed media or resized disk sdb + [135130.431113] usb 6-1: new full speed USB device using uhci_hcd and address 8 + [135130.629444] usb-storage 6-1:1.0: Quirks match for vid 0483 pid 3744: 102a1 + [135130.629492] scsi20 : usb-storage 6-1:1.0 + [135131.625600] scsi 20:0:0:0: Direct-Access STM32 PQ: 0 ANSI: 0 + [135131.627010] sd 20:0:0:0: Attached scsi generic sg2 type 0 + [135131.633603] sd 20:0:0:0: [sdb] 64000 512-byte logical blocks: (32.7 MB/31.2 MiB) + [135131.633613] sd 20:0:0:0: [sdb] Assuming Write Enabled + [135131.633620] sd 20:0:0:0: [sdb] Assuming drive cache: write through + [135131.640584] sd 20:0:0:0: [sdb] Assuming Write Enabled + [135131.640592] sd 20:0:0:0: [sdb] Assuming drive cache: write through + [135131.640609] sdb: + [135131.652634] sd 20:0:0:0: [sdb] Assuming Write Enabled + [135131.652639] sd 20:0:0:0: [sdb] Assuming drive cache: write through + [135131.652645] sd 20:0:0:0: [sdb] Attached SCSI removable disk + [135131.671536] sd 20:0:0:0: [sdb] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE + [135131.671548] sd 20:0:0:0: [sdb] Sense Key : Illegal Request [current] + [135131.671553] sd 20:0:0:0: [sdb] Add. Sense: Logical block address out of range + [135131.671560] sd 20:0:0:0: [sdb] CDB: Read(10): 28 00 00 00 f9 80 00 00 08 00 + [135131.671570] end_request: I/O error, dev sdb, sector 63872 + [135131.671575] Buffer I/O error on device sdb, logical block 7984 + [135131.678527] sd 20:0:0:0: [sdb] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE + [135131.678532] sd 20:0:0:0: [sdb] Sense Key : Illegal Request [current] + [135131.678537] sd 20:0:0:0: [sdb] Add. Sense: Logical block address out of range + [135131.678542] sd 20:0:0:0: [sdb] CDB: Read(10): 28 00 00 00 f9 80 00 00 08 00 + [135131.678551] end_request: I/O error, dev sdb, sector 63872 + ... + [135131.853565] end_request: I/O error, dev sdb, sector 4096 + */ +} + +int _stlink_sg_core_id(stlink_t *sl) { + struct stlink_libsg *sg = sl->backend_data; + int ret; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_READCOREID; + sl->q_len = 4; + sg->q_addr = 0; + ret = stlink_q(sl); + if (ret) + return ret; + + sl->core_id = read_uint32(sl->q_buf, 0); + return 0; +} + +// Arm-core reset -> halted state. + +int _stlink_sg_reset(stlink_t *sl) { + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_RESETSYS; + sl->q_len = 2; + sg->q_addr = 0; + if (stlink_q(sl)) + return -1; + + stlink_stat(sl, "core reset"); + return 0; +} + +// Arm-core reset -> halted state. + +int _stlink_sg_jtag_reset(stlink_t *sl, int value) { + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_JTAG_DRIVE_NRST; + sg->cdb_cmd_blk[2] = (value)?0:1; + sl->q_len = 3; + sg->q_addr = 2; + if (stlink_q(sl)) + return -1; + + stlink_stat(sl, "core reset"); + + return 0; +} + +// Arm-core status: halted or running. + +int _stlink_sg_status(stlink_t *sl) { + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_GETSTATUS; + sl->q_len = 2; + sg->q_addr = 0; + return stlink_q(sl); +} + +// Force the core into the debug mode -> halted state. + +int _stlink_sg_force_debug(stlink_t *sl) { + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_FORCEDEBUG; + sl->q_len = 2; + sg->q_addr = 0; + if (stlink_q(sl)) + return -1; + + stlink_stat(sl, "force debug"); + return 0; +} + +// Read all arm-core registers. + +int _stlink_sg_read_all_regs(stlink_t *sl, reg *regp) { + struct stlink_libsg *sg = sl->backend_data; + + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_READALLREGS; + sl->q_len = 84; + sg->q_addr = 0; + if (stlink_q(sl)) + return -1; + + stlink_print_data(sl); + + // TODO - most of this should be re-extracted up.... + + // 0-3 | 4-7 | ... | 60-63 | 64-67 | 68-71 | 72-75 | 76-79 | 80-83 + // r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 + for (int i = 0; i < 16; i++) { + regp->r[i] = read_uint32(sl->q_buf, 4 * i); + if (sl->verbose > 1) + DLOG("r%2d = 0x%08x\n", i, regp->r[i]); + } + regp->xpsr = read_uint32(sl->q_buf, 64); + regp->main_sp = read_uint32(sl->q_buf, 68); + regp->process_sp = read_uint32(sl->q_buf, 72); + regp->rw = read_uint32(sl->q_buf, 76); + regp->rw2 = read_uint32(sl->q_buf, 80); + if (sl->verbose < 2) + return 0; + + DLOG("xpsr = 0x%08x\n", regp->xpsr); + DLOG("main_sp = 0x%08x\n", regp->main_sp); + DLOG("process_sp = 0x%08x\n", regp->process_sp); + DLOG("rw = 0x%08x\n", regp->rw); + DLOG("rw2 = 0x%08x\n", regp->rw2); + + return 0; +} + +// Read an arm-core register, the index must be in the range 0..20. +// 0 | 1 | ... | 15 | 16 | 17 | 18 | 19 | 20 +// r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 + +int _stlink_sg_read_reg(stlink_t *sl, int r_idx, reg *regp) { + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_READREG; + sg->cdb_cmd_blk[2] = r_idx; + sl->q_len = 4; + sg->q_addr = 0; + if (stlink_q(sl)) + return -1; + + // 0 | 1 | ... | 15 | 16 | 17 | 18 | 19 | 20 + // 0-3 | 4-7 | ... | 60-63 | 64-67 | 68-71 | 72-75 | 76-79 | 80-83 + // r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 + stlink_print_data(sl); + + uint32_t r = read_uint32(sl->q_buf, 0); + DLOG("r_idx (%2d) = 0x%08x\n", r_idx, r); + + switch (r_idx) { + case 16: + regp->xpsr = r; + break; + case 17: + regp->main_sp = r; + break; + case 18: + regp->process_sp = r; + break; + case 19: + regp->rw = r; //XXX ?(primask, basemask etc.) + break; + case 20: + regp->rw2 = r; //XXX ?(primask, basemask etc.) + break; + default: + regp->r[r_idx] = r; + } + + return 0; +} + +// Write an arm-core register. Index: +// 0 | 1 | ... | 15 | 16 | 17 | 18 | 19 | 20 +// r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 + +int _stlink_sg_write_reg(stlink_t *sl, uint32_t reg, int idx) { + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEREG; + // 2: reg index + // 3-6: reg content + sg->cdb_cmd_blk[2] = idx; + write_uint32(sg->cdb_cmd_blk + 3, reg); + sl->q_len = 2; + sg->q_addr = 0; + if (stlink_q(sl)) + return -1; + + stlink_stat(sl, "write reg"); + return 0; +} + +// Write a register of the debug module of the core. +// XXX ?(atomic writes) +// TODO test + +void stlink_write_dreg(stlink_t *sl, uint32_t reg, uint32_t addr) { + struct stlink_libsg *sg = sl->backend_data; + DLOG("\n*** stlink_write_dreg ***\n"); + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEDEBUGREG; + // 2-5: address of reg of the debug module + // 6-9: reg content + write_uint32(sg->cdb_cmd_blk + 2, addr); + write_uint32(sg->cdb_cmd_blk + 6, reg); + sl->q_len = 2; + sg->q_addr = addr; + stlink_q(sl); + stlink_stat(sl, "write debug reg"); +} + +// Force the core exit the debug mode. + +int _stlink_sg_run(stlink_t *sl) { + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_RUNCORE; + sl->q_len = 2; + sg->q_addr = 0; + if (stlink_q(sl)) + return -1; + + stlink_stat(sl, "run core"); + + return 0; +} + +// Step the arm-core. + +int _stlink_sg_step(stlink_t *sl) { + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_STEPCORE; + sl->q_len = 2; + sg->q_addr = 0; + if (stlink_q(sl)) + return -1; + + stlink_stat(sl, "step core"); + return 0; +} + +// TODO test +// see Cortex-M3 Technical Reference Manual +// TODO make delegate! +void stlink_set_hw_bp(stlink_t *sl, int fp_nr, uint32_t addr, int fp) { + DLOG("\n*** stlink_set_hw_bp ***\n"); + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_SETFP; + // 2:The number of the flash patch used to set the breakpoint + // 3-6: Address of the breakpoint (LSB) + // 7: FP_ALL (0x02) / FP_UPPER (0x01) / FP_LOWER (0x00) + sl->q_buf[2] = fp_nr; + write_uint32(sl->q_buf, addr); + sl->q_buf[7] = fp; + + sl->q_len = 2; + stlink_q(sl); + stlink_stat(sl, "set flash breakpoint"); +} + +// TODO test + +// TODO make delegate! +void stlink_clr_hw_bp(stlink_t *sl, int fp_nr) { + struct stlink_libsg *sg = sl->backend_data; + DLOG("\n*** stlink_clr_hw_bp ***\n"); + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_CLEARFP; + sg->cdb_cmd_blk[2] = fp_nr; + + sl->q_len = 2; + stlink_q(sl); + stlink_stat(sl, "clear flash breakpoint"); +} + +// Read a "len" bytes to the sl->q_buf from the memory, max 6kB (6144 bytes) + +int _stlink_sg_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_READMEM_32BIT; + // 2-5: addr + // 6-7: len + write_uint32(sg->cdb_cmd_blk + 2, addr); + write_uint16(sg->cdb_cmd_blk + 6, len); + + // data_in 0-0x40-len + // !!! len _and_ q_len must be max 6k, + // i.e. >1024 * 6 = 6144 -> aboard) + // !!! if len < q_len: 64*k, 1024*n, n=1..5 -> aboard + // (broken residue issue) + sl->q_len = len; + sg->q_addr = addr; + if (stlink_q(sl)) + return -1; + + stlink_print_data(sl); + return 0; +} + +// Write a "len" bytes from the sl->q_buf to the memory, max 64 Bytes. + +int _stlink_sg_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { + struct stlink_libsg *sg = sl->backend_data; + int ret; + + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEMEM_8BIT; + // 2-5: addr + // 6-7: len (>0x40 (64) -> aboard) + write_uint32(sg->cdb_cmd_blk + 2, addr); + write_uint16(sg->cdb_cmd_blk + 6, len); + + // this sends the command... + ret = send_usb_mass_storage_command(sg->usb_handle, + sg->ep_req, sg->cdb_cmd_blk, CDB_SL, 0, 0, 0); + if (ret == -1) + return ret; + + // This sends the data... + ret = send_usb_data_only(sg->usb_handle, + sg->ep_req, sg->ep_rep, sl->q_buf, len); + if (ret == -1) + return ret; + + stlink_print_data(sl); + return 0; +} + +// Write a "len" bytes from the sl->q_buf to the memory, max Q_BUF_LEN bytes. + +int _stlink_sg_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + struct stlink_libsg *sg = sl->backend_data; + int ret; + + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEMEM_32BIT; + // 2-5: addr + // 6-7: len "unlimited" + write_uint32(sg->cdb_cmd_blk + 2, addr); + write_uint16(sg->cdb_cmd_blk + 6, len); + + // this sends the command... + ret = send_usb_mass_storage_command(sg->usb_handle, + sg->ep_req, sg->cdb_cmd_blk, CDB_SL, 0, 0, 0); + if (ret == -1) + return ret; + + // This sends the data... + ret = send_usb_data_only(sg->usb_handle, + sg->ep_req, sg->ep_rep, sl->q_buf, len); + if (ret == -1) + return ret; + + stlink_print_data(sl); + return 0; +} + +// Write one DWORD data to memory + +int _stlink_sg_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) { + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_JTAG_WRITEDEBUG_32BIT; + // 2-5: addr + write_uint32(sg->cdb_cmd_blk + 2, addr); + write_uint32(sg->cdb_cmd_blk + 6, data); + sl->q_len = 2; + return stlink_q(sl); +} + +// Read one DWORD data from memory + +int _stlink_sg_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) { + struct stlink_libsg *sg = sl->backend_data; + clear_cdb(sg); + sg->cdb_cmd_blk[1] = STLINK_JTAG_READDEBUG_32BIT; + // 2-5: addr + write_uint32(sg->cdb_cmd_blk + 2, addr); + sl->q_len = 8; + if (stlink_q(sl)) + return -1; + + *data = read_uint32(sl->q_buf, 4); + return 0; +} + +// Exit the jtag or swd mode and enter the mass mode. + +int _stlink_sg_exit_debug_mode(stlink_t *stl) +{ + if (stl) { + struct stlink_libsg* sl = stl->backend_data; + clear_cdb(sl); + sl->cdb_cmd_blk[1] = STLINK_DEBUG_EXIT; + stl->q_len = 0; // >0 -> aboard + return stlink_q(stl); + } + + return 0; +} + + +// 1) open a sg device, switch the stlink from dfu to mass mode +// 2) wait 5s until the kernel driver stops reseting the broken device +// 3) reopen the device +// 4) the device driver is now ready for a switch to jtag/swd mode +// TODO thinking, better error handling, wait until the kernel driver stops reseting the plugged-in device + +stlink_backend_t _stlink_sg_backend = { + _stlink_sg_close, + _stlink_sg_exit_debug_mode, + _stlink_sg_enter_swd_mode, + _stlink_sg_enter_jtag_mode, + _stlink_sg_exit_dfu_mode, + _stlink_sg_core_id, + _stlink_sg_reset, + _stlink_sg_jtag_reset, + _stlink_sg_run, + _stlink_sg_status, + _stlink_sg_version, + _stlink_sg_read_debug32, + _stlink_sg_read_mem32, + _stlink_sg_write_debug32, + _stlink_sg_write_mem32, + _stlink_sg_write_mem8, + _stlink_sg_read_all_regs, + _stlink_sg_read_reg, + NULL, /* read_all_unsupported_regs */ + NULL, /* read_unsupported_regs */ + NULL, /* write_unsupported_regs */ + _stlink_sg_write_reg, + _stlink_sg_step, + _stlink_sg_current_mode, + _stlink_sg_force_debug, + NULL +}; + +static stlink_t* stlink_open(const int verbose) { + + stlink_t *sl = malloc(sizeof (stlink_t)); + memset(sl, 0, sizeof(stlink_t)); + struct stlink_libsg *slsg = malloc(sizeof (struct stlink_libsg)); + if (sl == NULL || slsg == NULL) { + WLOG("Couldn't malloc stlink and stlink_sg structures out of memory!\n"); + return NULL; + } + + if (libusb_init(&(slsg->libusb_ctx))) { + WLOG("failed to init libusb context, wrong version of libraries?\n"); + free(sl); + free(slsg); + return NULL; + } + + libusb_set_debug(slsg->libusb_ctx, 3); + + slsg->usb_handle = libusb_open_device_with_vid_pid(slsg->libusb_ctx, USB_ST_VID, USB_STLINK_PID); + if (slsg->usb_handle == NULL) { + WLOG("Failed to find an stlink v1 by VID:PID\n"); + libusb_close(slsg->usb_handle); + libusb_exit(slsg->libusb_ctx); + free(sl); + free(slsg); + return NULL; + } + + // TODO + // Could read the interface config descriptor, and assert lots of the assumptions + + // assumption: numInterfaces is always 1... + if (libusb_kernel_driver_active(slsg->usb_handle, 0) == 1) { + int r = libusb_detach_kernel_driver(slsg->usb_handle, 0); + if (r < 0) { + WLOG("libusb_detach_kernel_driver(() error %s\n", strerror(-r)); + libusb_close(slsg->usb_handle); + libusb_exit(slsg->libusb_ctx); + free(sl); + free(slsg); + return NULL; + } + DLOG("Kernel driver was successfully detached\n"); + } + + int config; + if (libusb_get_configuration(slsg->usb_handle, &config)) { + /* this may fail for a previous configured device */ + WLOG("libusb_get_configuration()\n"); + libusb_close(slsg->usb_handle); + libusb_exit(slsg->libusb_ctx); + free(sl); + free(slsg); + return NULL; + + } + + // assumption: bConfigurationValue is always 1 + if (config != 1) { + WLOG("Your stlink got into a real weird configuration, trying to fix it!\n"); + DLOG("setting new configuration (%d -> 1)\n", config); + if (libusb_set_configuration(slsg->usb_handle, 1)) { + /* this may fail for a previous configured device */ + WLOG("libusb_set_configuration() failed\n"); + libusb_close(slsg->usb_handle); + libusb_exit(slsg->libusb_ctx); + free(sl); + free(slsg); + return NULL; + } + } + + if (libusb_claim_interface(slsg->usb_handle, 0)) { + WLOG("libusb_claim_interface() failed\n"); + libusb_close(slsg->usb_handle); + libusb_exit(slsg->libusb_ctx); + free(sl); + free(slsg); + return NULL; + } + + // assumption: endpoint config is fixed mang. really. + slsg->ep_rep = 1 /* ep rep */ | LIBUSB_ENDPOINT_IN; + slsg->ep_req = 2 /* ep req */ | LIBUSB_ENDPOINT_OUT; + + DLOG("Successfully opened stlinkv1 by libusb :)\n"); + + sl->verbose = verbose; + sl->backend_data = slsg; + sl->backend = &_stlink_sg_backend; + + sl->core_stat = STLINK_CORE_STAT_UNKNOWN; + slsg->q_addr = 0; + + return sl; +} + + +stlink_t* stlink_v1_open_inner(const int verbose) { + ugly_init(verbose); + stlink_t *sl = stlink_open(verbose); + if (sl == NULL) { + ELOG("Could not open stlink device\n"); + return NULL; + } + + stlink_version(sl); + if ((sl->version.st_vid != USB_ST_VID) || (sl->version.stlink_pid != USB_STLINK_PID)) { + ELOG("WTF? successfully opened, but unable to read version details. BROKEN!\n"); + return NULL; + } + + DLOG("Reading current mode...\n"); + switch (stlink_current_mode(sl)) { + case STLINK_DEV_MASS_MODE: + return sl; + case STLINK_DEV_DEBUG_MODE: + // TODO go to mass? + return sl; + default: + ILOG("Current mode unusable, trying to get back to a useful state...\n"); + break; + } + + DLOG("Attempting to exit DFU mode\n"); + _stlink_sg_exit_dfu_mode(sl); + + // re-query device info (and retest) + stlink_version(sl); + if ((sl->version.st_vid != USB_ST_VID) || (sl->version.stlink_pid != USB_STLINK_PID)) { + ELOG("WTF? successfully opened, but unable to read version details. BROKEN!\n"); + return NULL; + } + + return sl; +} + +stlink_t* stlink_v1_open(const int verbose, int reset) { + stlink_t *sl = stlink_v1_open_inner(verbose); + if (sl == NULL) + return NULL; + + // by now, it _must_ be fully open and in a useful mode.... + stlink_enter_swd_mode(sl); + /* Now we are ready to read the parameters */ + if (reset) { + stlink_reset(sl); + } + stlink_load_device_params(sl); + ILOG("Successfully opened a stlink v1 debugger\n"); + return sl; +} diff --git a/src/st-info.c b/src/st-info.c deleted file mode 100644 index 1671468..0000000 --- a/src/st-info.c +++ /dev/null @@ -1,162 +0,0 @@ -#include -#include -#include -#include - -#include - -static void usage(void) -{ - puts("st-info --flash"); - puts("st-info --sram"); - puts("st-info --descr"); - puts("st-info --pagesize"); - puts("st-info --chipid"); - puts("st-info --serial"); - puts("st-info --hla-serial"); - puts("st-info --probe"); -} - -/* Print normal or OpenOCD hla_serial with newline */ -static void stlink_print_serial(stlink_t *sl, bool openocd) -{ - const char *fmt; - - if (openocd) { - printf("\""); - fmt = "\\x%02x"; - } else { - fmt = "%02x"; - } - - for (int n = 0; n < sl->serial_size; n++) - printf(fmt, sl->serial[n]); - - if (openocd) - printf("\""); - printf("\n"); -} - -static void stlink_print_info(stlink_t *sl) -{ - const chip_params_t *params = NULL; - - if (!sl) - return; - - printf(" serial: "); - stlink_print_serial(sl, false); - printf("openocd: "); - stlink_print_serial(sl, true); - - printf(" flash: %zu (pagesize: %zu)\n", sl->flash_size, sl->flash_pgsz); - printf(" sram: %zu\n", sl->sram_size); - printf(" chipid: 0x%.4x\n", sl->chip_id); - - for (size_t i = 0; i < sizeof(devices) / sizeof(devices[0]); i++) { - if (devices[i].chip_id == sl->chip_id) { - params = &devices[i]; - break; - } - } - - if (params) - printf(" descr: %s\n", params->description); -} - -static void stlink_probe(void) -{ - stlink_t **stdevs; - size_t size; - - size = stlink_probe_usb(&stdevs); - - printf("Found %zu stlink programmers\n", size); - - for (size_t n = 0; n < size; n++) - stlink_print_info(stdevs[n]); - - stlink_probe_usb_free(&stdevs, size); -} - -static stlink_t *stlink_open_first(void) -{ - stlink_t* sl = NULL; - sl = stlink_v1_open(0, 1); - if (sl == NULL) - sl = stlink_open_usb(0, 1, NULL); - - return sl; -} - -static int print_data(char **av) -{ - stlink_t* sl = NULL; - - // Probe needs all devices unclaimed - if (strcmp(av[1], "--probe") == 0) { - stlink_probe(); - return 0; - } - - sl = stlink_open_first(); - - if (sl == NULL) { - return -1; - } - - sl->verbose = 0; - - if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) - stlink_exit_dfu_mode(sl); - - if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) - stlink_enter_swd_mode(sl); - - if (strcmp(av[1], "--flash") == 0) - printf("0x%zx\n", sl->flash_size); - else if (strcmp(av[1], "--sram") == 0) - printf("0x%zx\n", sl->sram_size); - else if (strcmp(av[1], "--pagesize") == 0) - printf("0x%zx\n", sl->flash_pgsz); - else if (strcmp(av[1], "--chipid") == 0) - printf("0x%.4x\n", sl->chip_id); - else if (strcmp(av[1], "--serial") == 0) - stlink_print_serial(sl, false); - else if (strcmp(av[1], "--hla-serial") == 0) - stlink_print_serial(sl, true); - else if (strcmp(av[1], "--descr") == 0) { - const chip_params_t *params = NULL; - for (size_t i = 0; i < sizeof(devices) / sizeof(devices[0]); i++) { - if(devices[i].chip_id == sl->chip_id) { - params = &devices[i]; - break; - } - } - if (params == NULL) { - return -1; - } - printf("%s\n", params->description); - } - - if (sl) - { - stlink_exit_debug_mode(sl); - stlink_close(sl); - } - - return 0; -} - -int main(int ac, char** av) -{ - int err = -1; - if (ac < 2) { - usage(); - return -1; - } - - err = print_data(av); - - return err; -} diff --git a/src/st-term.c b/src/st-term.c deleted file mode 100644 index 245ed59..0000000 --- a/src/st-term.c +++ /dev/null @@ -1,321 +0,0 @@ -#include -#include -/* According to POSIX.1-2001 */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "stlink-common.h" - -/* STLinky structure on STM chip - -struct stlinky { - uint32_t magic; - uint32_t bufsize; - uint32_t up_tail; - uint32_t up_head; - uint32_t dw_tail; - uint32_t dw_head; - char upbuf[CONFIG_LIB_STLINKY_BSIZE]; - char dwbuf[CONFIG_LIB_STLINKY_BSIZE]; -} __attribute__ ((packed)); -*/ - - -#define STLINKY_MAGIC 0xDEADF00D - -#define ST_TERM_MAX_BUFF_SIZE (1024*1024) //1Mb - -#define RX_Q_OFFSET 8 -#define RX_BUFF_OFFSET 24 -#define TX_Q_OFFSET 16 -#define TX_BUFF_OFFSET(bufsize) (24 + bufsize) - -#define READ_UINT32_LE(buf) ((uint32_t) ( buf[0] \ - | buf[1] << 8 \ - | buf[2] << 16 \ - | buf[3] << 24)) - -static stlink_t* sl; -sigset_t sig_mask; - -struct stlinky { - stlink_t *sl; - uint32_t off; - size_t bufsize; -}; - -void nonblock(int state); - -static void cleanup(int signal __attribute__((unused))) { - if (sl) { - /* Switch back to mass storage mode before closing. */ - stlink_run(sl); - stlink_exit_debug_mode(sl); - stlink_close(sl); - } - - printf("\n"); - nonblock(0); - exit(1); -} - -void sig_init() { - sigemptyset(&sig_mask); - sigaddset(&sig_mask, SIGINT); - sigaddset(&sig_mask, SIGTERM); - signal(SIGINT, &cleanup); - signal(SIGTERM, &cleanup); - sigprocmask(SIG_BLOCK, &sig_mask, NULL); -} - -void sig_process() { - sigset_t pending; - sigpending(&pending); - if (sigismember(&pending, SIGINT) || sigismember(&pending, SIGTERM)) { - sigprocmask(SIG_UNBLOCK, &sig_mask, NULL); - sigsuspend(&pending); - sigprocmask(SIG_BLOCK, &sig_mask, NULL); - } -} - -/* Detects stlinky in RAM, returns handler */ -struct stlinky* stlinky_detect(stlink_t* sl) -{ - static const uint32_t sram_base = 0x20000000; - struct stlinky* st = malloc(sizeof(struct stlinky)); - int multiple=0; - st->sl = sl; - printf("sram: 0x%x bytes @ 0x%zx\n", sl->sram_base, sl->sram_size); - uint32_t off; - for (off = 0; off < sl->sram_size; off += 4) { - if (off % 1024 == 0) sig_process(); - stlink_read_mem32(sl, sram_base + off, 4); - if (STLINKY_MAGIC == READ_UINT32_LE(sl->q_buf)) - { - if (multiple > 0) printf("WARNING: another "); - printf("stlinky detected at 0x%x\n", sram_base + off); - st->off = sram_base + off; - stlink_read_mem32(sl, st->off + 4, 4); - st->bufsize = READ_UINT32_LE(sl->q_buf); - printf("stlinky buffer size 0x%zu \n", st->bufsize); - multiple++; - } - } - if (multiple > 0) { - if (multiple > 1) { - printf("Using last stlinky structure detected\n"); - } - return st; - } - return NULL; -} - -static void stlinky_read_buff(struct stlinky *st, uint32_t off, uint32_t size, char *buffer) -{ - int aligned_size; - - if (size == 0) - return; - - //Read from device with 4-byte alignment - aligned_size = (size & 0xFFFFFFFC) + 8; - stlink_read_mem32(st->sl, off & 0xFFFFFFFC, aligned_size); - - //copy to local buffer - memcpy(buffer, st->sl->q_buf + (off & 0x3), size); - - return; -} - -static void stlinky_write_buf(struct stlinky *st, uint32_t off, uint32_t size, char *buffer) -{ - memcpy(st->sl->q_buf, buffer, size); - stlink_write_mem8(st->sl, off, size); - return; -} - -size_t stlinky_rx(struct stlinky *st, char* buffer) -{ - //read head and tail values - uint32_t tail, head; - stlink_read_mem32(st->sl, st->off + RX_Q_OFFSET, sizeof(tail) + sizeof(head)); - memcpy(&tail, &st->sl->q_buf[0], sizeof(tail)); - memcpy(&head, &st->sl->q_buf[sizeof(tail)], sizeof(head)); - - //return if empty - if(head == tail) - return 0; - - //read data - int size_read = 0; - if(head > tail){ - stlinky_read_buff(st, st->off + RX_BUFF_OFFSET + tail, head - tail, buffer); - size_read += head - tail; - } else if(head < tail){ - stlinky_read_buff(st, st->off + RX_BUFF_OFFSET + tail, st->bufsize - tail, buffer); - size_read += st->bufsize - tail; - - stlinky_read_buff(st, st->off + RX_BUFF_OFFSET, head, buffer + size_read); - size_read += head; - } - - //move tail - tail = (tail + size_read) % st->bufsize; - - //write tail - memcpy(st->sl->q_buf, &tail, sizeof(tail)); - stlink_write_mem32(st->sl, st->off + RX_Q_OFFSET, sizeof(tail)); - - return size_read; -} - -size_t stlinky_tx(struct stlinky *st, char* buffer, size_t siz) -{ - //read head and tail values - uint32_t tail, head; - stlink_read_mem32(st->sl, st->off + TX_Q_OFFSET, sizeof(tail) + sizeof(head)); - memcpy(&tail, &st->sl->q_buf[0], sizeof(tail)); - memcpy(&head, &st->sl->q_buf[sizeof(tail)], sizeof(head)); - - //Figure out buffer usage - int usage = head - tail; - if (usage < 0) - usage += st->bufsize; - - //check if new data will fit - if (usage + siz >= st->bufsize) - return 0; - - //copy in data (take care of possible split) - int first_chunk = head + siz >= st->bufsize ? st->bufsize - head : siz; - int second_chunk = siz - first_chunk; - - //copy data - stlinky_write_buf(st, st->off + TX_BUFF_OFFSET(st->bufsize) + head, first_chunk, buffer); - if (second_chunk > 0) - stlinky_write_buf(st, st->off + TX_BUFF_OFFSET(st->bufsize), - second_chunk, buffer + first_chunk); - - //increment head pointer - head = (head + siz) % st->bufsize; - memcpy(st->sl->q_buf, &head, sizeof(head)); - stlink_write_mem32(st->sl, st->off + TX_Q_OFFSET + sizeof(tail), sizeof(head)); - - return siz; -} - -int kbhit() -{ - struct timeval tv; - fd_set fds; - tv.tv_sec = 0; - tv.tv_usec = 0; - FD_ZERO(&fds); - FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0 - select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); - return FD_ISSET(STDIN_FILENO, &fds); -} - -void nonblock(int state) -{ - struct termios ttystate; - - //get the terminal state - tcgetattr(STDIN_FILENO, &ttystate); - - if (state==1) - { - //turn off canonical mode - ttystate.c_lflag &= ~ICANON; - ttystate.c_lflag &= ~ECHO; - //minimum of number input read. - ttystate.c_cc[VMIN] = 1; - } - else if (state==0) - { - //turn on canonical mode - ttystate.c_lflag |= ICANON | ECHO; - } - //set the terminal attributes. - tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); - -} - -int main(int ac, char** av) { - struct stlinky *st=NULL; - - sig_init(); - - sl = stlink_open_usb(10, 1, NULL); - if (sl != NULL) { - printf("ST-Linky proof-of-concept terminal :: Created by Necromant for lulz\n"); - stlink_version(sl); - stlink_enter_swd_mode(sl); - printf("chip id: %#x\n", sl->chip_id); - printf("core_id: %#x\n", sl->core_id); - - cortex_m3_cpuid_t cpuid; - stlink_cpu_id(sl, &cpuid); - printf("cpuid:impl_id = %0#x, variant = %#x\n", cpuid.implementer_id, cpuid.variant); - printf("cpuid:part = %#x, rev = %#x\n", cpuid.part, cpuid.revision); - - stlink_reset(sl); - stlink_force_debug(sl); - stlink_run(sl); - stlink_status(sl); - - /* wait for device to boot */ - /* TODO: Make timeout adjustable via command line */ - sleep(1); - - if(ac == 1){ - st = stlinky_detect(sl); - }else if(ac == 2){ - st = malloc(sizeof(struct stlinky)); - st->sl = sl; - st->off = (int)strtol(av[1], NULL, 16); - printf("using stlinky at 0x%x\n", st->off); - stlink_read_mem32(sl, st->off + 4, 4); - st->bufsize = READ_UINT32_LE(sl->q_buf); - printf("stlinky buffer size 0x%zu \n", st->bufsize); - }else{ - cleanup(0); - } - if (st == NULL) - { - printf("stlinky magic not found in sram :(\n"); - cleanup(0); - } - if (st->bufsize > ST_TERM_MAX_BUFF_SIZE){ - printf("stlinky buffer size too big\n"); - cleanup(0); - } - char* rxbuf = malloc(st->bufsize); - char* txbuf = malloc(st->bufsize); - size_t tmp; - nonblock(1); - int fd = fileno(stdin); - int saved_flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, saved_flags & ~O_NONBLOCK); - printf("Entering interactive terminal. CTRL+C to exit\n\n\n"); - while(1) { - sig_process(); - tmp = stlinky_rx(st, rxbuf); - if(tmp > 0) - { - fwrite(rxbuf,tmp,1,stdout); - fflush(stdout); - } - if (kbhit()) { - tmp = read(fd, txbuf, st->bufsize); - stlinky_tx(st,txbuf,tmp); - } - } - } - return 0; -} diff --git a/src/stlink-common.c b/src/stlink-common.c deleted file mode 100644 index 18c76fb..0000000 --- a/src/stlink-common.c +++ /dev/null @@ -1,2131 +0,0 @@ -#define DEBUG_FLASH 0 - -#include -#include -#include -#include - -#include -#include -#include -#include -#include "mmap.h" - -#include "stlink-common.h" -#include "uglylogging.h" - -#ifndef _WIN32 -#define O_BINARY 0 -#endif - -/* todo: stm32l15xxx flash memory, pm0062 manual */ - -/* stm32f FPEC flash controller interface, pm0063 manual */ -// TODO - all of this needs to be abstracted out.... -// STM32F05x is identical, based on RM0091 (DM00031936, Doc ID 018940 Rev 2, August 2012) -#define FLASH_REGS_ADDR 0x40022000 -#define FLASH_REGS_SIZE 0x28 - -#define FLASH_ACR (FLASH_REGS_ADDR + 0x00) -#define FLASH_KEYR (FLASH_REGS_ADDR + 0x04) -#define FLASH_SR (FLASH_REGS_ADDR + 0x0c) -#define FLASH_CR (FLASH_REGS_ADDR + 0x10) -#define FLASH_AR (FLASH_REGS_ADDR + 0x14) -#define FLASH_OBR (FLASH_REGS_ADDR + 0x1c) -#define FLASH_WRPR (FLASH_REGS_ADDR + 0x20) - -// For STM32F05x, the RDPTR_KEY may be wrong, but as it is not used anywhere... -#define FLASH_RDPTR_KEY 0x00a5 -#define FLASH_KEY1 0x45670123 -#define FLASH_KEY2 0xcdef89ab - -#define FLASH_SR_BSY 0 -#define FLASH_SR_EOP 5 - -#define FLASH_CR_PG 0 -#define FLASH_CR_PER 1 -#define FLASH_CR_MER 2 -#define FLASH_CR_STRT 6 -#define FLASH_CR_LOCK 7 - - -//32L = 32F1 same CoreID as 32F4! -#define STM32L_FLASH_REGS_ADDR ((uint32_t)0x40023c00) -#define STM32L_FLASH_ACR (STM32L_FLASH_REGS_ADDR + 0x00) -#define STM32L_FLASH_PECR (STM32L_FLASH_REGS_ADDR + 0x04) -#define STM32L_FLASH_PDKEYR (STM32L_FLASH_REGS_ADDR + 0x08) -#define STM32L_FLASH_PEKEYR (STM32L_FLASH_REGS_ADDR + 0x0c) -#define STM32L_FLASH_PRGKEYR (STM32L_FLASH_REGS_ADDR + 0x10) -#define STM32L_FLASH_OPTKEYR (STM32L_FLASH_REGS_ADDR + 0x14) -#define STM32L_FLASH_SR (STM32L_FLASH_REGS_ADDR + 0x18) -#define STM32L_FLASH_OBR (STM32L_FLASH_REGS_ADDR + 0x1c) -#define STM32L_FLASH_WRPR (STM32L_FLASH_REGS_ADDR + 0x20) -#define FLASH_L1_FPRG 10 -#define FLASH_L1_PROG 3 - -//32L4 register base is at FLASH_REGS_ADDR (0x40022000) -#define STM32L4_FLASH_KEYR (FLASH_REGS_ADDR + 0x08) -#define STM32L4_FLASH_SR (FLASH_REGS_ADDR + 0x10) -#define STM32L4_FLASH_CR (FLASH_REGS_ADDR + 0x14) -#define STM32L4_FLASH_OPTR (FLASH_REGS_ADDR + 0x20) - -#define STM32L4_FLASH_SR_BSY 16 -#define STM32L4_FLASH_SR_ERRMASK 0x3f8 /* SR [9:3] */ - -#define STM32L4_FLASH_CR_LOCK 31 /* Lock control register */ -#define STM32L4_FLASH_CR_PG 0 /* Program */ -#define STM32L4_FLASH_CR_PER 1 /* Page erase */ -#define STM32L4_FLASH_CR_MER1 2 /* Bank 1 erase */ -#define STM32L4_FLASH_CR_MER2 15 /* Bank 2 erase */ -#define STM32L4_FLASH_CR_STRT 16 /* Start command */ -#define STM32L4_FLASH_CR_BKER 11 /* Bank select for page erase */ -#define STM32L4_FLASH_CR_PNB 3 /* Page number (8 bits) */ -// Bits requesting flash operations (useful when we want to clear them) -#define STM32L4_FLASH_CR_OPBITS \ - ((1lu< le (don't swap) - buf[0] = ((unsigned char*) &ui)[0]; - buf[1] = ((unsigned char*) &ui)[1]; - buf[2] = ((unsigned char*) &ui)[2]; - buf[3] = ((unsigned char*) &ui)[3]; - } else { - buf[0] = ((unsigned char*) &ui)[3]; - buf[1] = ((unsigned char*) &ui)[2]; - buf[2] = ((unsigned char*) &ui)[1]; - buf[3] = ((unsigned char*) &ui)[0]; - } -} - -void write_uint16(unsigned char* buf, uint16_t ui) { - if (!is_bigendian()) { // le -> le (don't swap) - buf[0] = ((unsigned char*) &ui)[0]; - buf[1] = ((unsigned char*) &ui)[1]; - } else { - buf[0] = ((unsigned char*) &ui)[1]; - buf[1] = ((unsigned char*) &ui)[0]; - } -} - -uint32_t read_uint32(const unsigned char *c, const int pt) { - uint32_t ui; - char *p = (char *) &ui; - - if (!is_bigendian()) { // le -> le (don't swap) - p[0] = c[pt + 0]; - p[1] = c[pt + 1]; - p[2] = c[pt + 2]; - p[3] = c[pt + 3]; - } else { - p[0] = c[pt + 3]; - p[1] = c[pt + 2]; - p[2] = c[pt + 1]; - p[3] = c[pt + 0]; - } - return ui; -} - -static uint32_t __attribute__((unused)) read_flash_rdp(stlink_t *sl) { - uint32_t rdp; - stlink_read_debug32(sl, FLASH_WRPR, &rdp); - return rdp & 0xff; -} - -static inline uint32_t read_flash_wrpr(stlink_t *sl) { - uint32_t wrpr; - stlink_read_debug32(sl, FLASH_WRPR, &wrpr); - return wrpr; -} - -static inline uint32_t read_flash_obr(stlink_t *sl) { - uint32_t obr; - stlink_read_debug32(sl, FLASH_OBR, &obr); - return obr; -} - -static inline uint32_t read_flash_cr(stlink_t *sl) { - uint32_t reg, res; - - if (sl->flash_type == FLASH_TYPE_F4) - reg = FLASH_F4_CR; - else if (sl->flash_type == FLASH_TYPE_L4) - reg = STM32L4_FLASH_CR; - else - reg = FLASH_CR; - - stlink_read_debug32(sl, reg, &res); - -#if DEBUG_FLASH - fprintf(stdout, "CR:0x%x\n", res); -#endif - return res; -} - -static inline unsigned int is_flash_locked(stlink_t *sl) { - /* return non zero for true */ - uint32_t cr_lock_shift, cr = read_flash_cr(sl); - - if (sl->flash_type == FLASH_TYPE_F4) - cr_lock_shift = FLASH_F4_CR_LOCK; - else if (sl->flash_type == FLASH_TYPE_L4) - cr_lock_shift = STM32L4_FLASH_CR_LOCK; - else - cr_lock_shift = FLASH_CR_LOCK; - - return cr & (1 << cr_lock_shift); -} - -static void unlock_flash(stlink_t *sl) { - uint32_t key_reg; - /* the unlock sequence consists of 2 write cycles where - 2 key values are written to the FLASH_KEYR register. - an invalid sequence results in a definitive lock of - the FPEC block until next reset. - */ - if (sl->flash_type == FLASH_TYPE_F4) - key_reg = FLASH_F4_KEYR; - else if (sl->flash_type == FLASH_TYPE_L4) - key_reg = STM32L4_FLASH_KEYR; - else - key_reg = FLASH_KEYR; - - stlink_write_debug32(sl, key_reg, FLASH_KEY1); - stlink_write_debug32(sl, key_reg, FLASH_KEY2); -} - -static int unlock_flash_if(stlink_t *sl) { - /* unlock flash if already locked */ - - if (is_flash_locked(sl)) { - unlock_flash(sl); - if (is_flash_locked(sl)) { - WLOG("Failed to unlock flash!\n"); - return -1; - } - } - DLOG("Successfully unlocked flash\n"); - return 0; -} - -static void lock_flash(stlink_t *sl) { - uint32_t cr_lock_shift, cr_reg, n; - - if (sl->flash_type == FLASH_TYPE_F4) { - cr_reg = FLASH_F4_CR; - cr_lock_shift = FLASH_F4_CR_LOCK; - } else if (sl->flash_type == FLASH_TYPE_L4) { - cr_reg = STM32L4_FLASH_CR; - cr_lock_shift = STM32L4_FLASH_CR_LOCK; - } else { - cr_reg = FLASH_CR; - cr_lock_shift = FLASH_CR_LOCK; - } - - n = read_flash_cr(sl) | (1 << cr_lock_shift); - stlink_write_debug32(sl, cr_reg, n); -} - - -static void set_flash_cr_pg(stlink_t *sl) { - uint32_t cr_reg, x; - - x = read_flash_cr(sl); - - if (sl->flash_type == FLASH_TYPE_F4) { - cr_reg = FLASH_F4_CR; - x |= 1 << FLASH_CR_PG; - } else if (sl->flash_type == FLASH_TYPE_L4) { - cr_reg = STM32L4_FLASH_CR; - x &= ~STM32L4_FLASH_CR_OPBITS; - x |= 1 << STM32L4_FLASH_CR_PG; - } else { - cr_reg = FLASH_CR; - x = 1 << FLASH_CR_PG; - } - - stlink_write_debug32(sl, cr_reg, x); -} - -static void __attribute__((unused)) clear_flash_cr_pg(stlink_t *sl) { - uint32_t cr_reg, n; - - if (sl->flash_type == FLASH_TYPE_F4) - cr_reg = FLASH_F4_CR; - else if (sl->flash_type == FLASH_TYPE_L4) - cr_reg = STM32L4_FLASH_CR; - else - cr_reg = FLASH_CR; - - n = read_flash_cr(sl) & ~(1 << FLASH_CR_PG); - stlink_write_debug32(sl, cr_reg, n); -} - -static void set_flash_cr_per(stlink_t *sl) { - const uint32_t n = 1 << FLASH_CR_PER; - stlink_write_debug32(sl, FLASH_CR, n); -} - -static void __attribute__((unused)) clear_flash_cr_per(stlink_t *sl) { - const uint32_t n = read_flash_cr(sl) & ~(1 << FLASH_CR_PER); - stlink_write_debug32(sl, FLASH_CR, n); -} - -static void set_flash_cr_mer(stlink_t *sl) { - uint32_t val, cr_reg, cr_mer; - - if (sl->flash_type == FLASH_TYPE_F4) { - cr_reg = FLASH_F4_CR; - cr_mer = 1 << FLASH_CR_MER; - } else if (sl->flash_type == FLASH_TYPE_L4) { - cr_reg = STM32L4_FLASH_CR; - cr_mer = (1 << STM32L4_FLASH_CR_MER1) | (1 << STM32L4_FLASH_CR_MER2); - } else { - cr_reg = FLASH_CR; - cr_mer = 1 << FLASH_CR_MER; - } - - stlink_read_debug32(sl, cr_reg, &val); - val |= cr_mer; - stlink_write_debug32(sl, cr_reg, val); -} - -static void __attribute__((unused)) clear_flash_cr_mer(stlink_t *sl) { - uint32_t val, cr_reg, cr_mer; - - if (sl->flash_type == FLASH_TYPE_F4) { - cr_reg = FLASH_F4_CR; - cr_mer = 1 << FLASH_CR_MER; - } else if (sl->flash_type == FLASH_TYPE_L4) { - cr_reg = STM32L4_FLASH_CR; - cr_mer = (1 << STM32L4_FLASH_CR_MER1) | (1 << STM32L4_FLASH_CR_MER2); - } else { - cr_reg = FLASH_CR; - cr_mer = 1 << FLASH_CR_MER; - } - - stlink_read_debug32(sl, cr_reg, &val); - val &= ~cr_mer; - stlink_write_debug32(sl, cr_reg, val); -} - -static void set_flash_cr_strt(stlink_t *sl) { - uint32_t val, cr_reg, cr_strt; - - if (sl->flash_type == FLASH_TYPE_F4) { - cr_reg = FLASH_F4_CR; - cr_strt = 1 << FLASH_F4_CR_STRT; - } else if (sl->flash_type == FLASH_TYPE_L4) { - cr_reg = STM32L4_FLASH_CR; - cr_strt = 1 << STM32L4_FLASH_CR_STRT; - } else { - cr_reg = FLASH_CR; - cr_strt = 1 << FLASH_CR_STRT; - } - - stlink_read_debug32(sl, cr_reg, &val); - val |= cr_strt; - stlink_write_debug32(sl, cr_reg, val); -} - -static inline uint32_t read_flash_acr(stlink_t *sl) { - uint32_t acr; - stlink_read_debug32(sl, FLASH_ACR, &acr); - return acr; -} - -static inline uint32_t read_flash_sr(stlink_t *sl) { - uint32_t res, sr_reg; - - if (sl->flash_type == FLASH_TYPE_F4) - sr_reg = FLASH_F4_SR; - else if (sl->flash_type == FLASH_TYPE_L4) - sr_reg = STM32L4_FLASH_SR; - else - sr_reg = FLASH_SR; - - stlink_read_debug32(sl, sr_reg, &res); - - return res; -} - -static inline unsigned int is_flash_busy(stlink_t *sl) { - uint32_t sr_busy_shift; - - if (sl->flash_type == FLASH_TYPE_F4) - sr_busy_shift = FLASH_F4_SR_BSY; - else if (sl->flash_type == FLASH_TYPE_L4) - sr_busy_shift = STM32L4_FLASH_SR_BSY; - else - sr_busy_shift = FLASH_SR_BSY; - - return read_flash_sr(sl) & (1 << sr_busy_shift); -} - -static void wait_flash_busy(stlink_t *sl) { - /* todo: add some delays here */ - while (is_flash_busy(sl)) - ; -} - -static void wait_flash_busy_progress(stlink_t *sl) { - int i = 0; - fprintf(stdout, "Mass erasing"); - fflush(stdout); - while (is_flash_busy(sl)) { - usleep(10000); - i++; - if (i % 100 == 0) { - fprintf(stdout, "."); - fflush(stdout); - } - } - fprintf(stdout, "\n"); -} - -static inline unsigned int is_flash_eop(stlink_t *sl) { - return read_flash_sr(sl) & (1 << FLASH_SR_EOP); -} - -static void __attribute__((unused)) clear_flash_sr_eop(stlink_t *sl) { - const uint32_t n = read_flash_sr(sl) & ~(1 << FLASH_SR_EOP); - stlink_write_debug32(sl, FLASH_SR, n); -} - -static void __attribute__((unused)) wait_flash_eop(stlink_t *sl) { - /* todo: add some delays here */ - while (is_flash_eop(sl) == 0) - ; -} - -static inline void write_flash_ar(stlink_t *sl, uint32_t n) { - stlink_write_debug32(sl, FLASH_AR, n); -} - -static inline void write_flash_cr_psiz(stlink_t *sl, uint32_t n) { - uint32_t x = read_flash_cr(sl); - x &= ~(0x03 << 8); - x |= (n << 8); -#if DEBUG_FLASH - fprintf(stdout, "PSIZ:0x%x 0x%x\n", x, n); -#endif - stlink_write_debug32(sl, FLASH_F4_CR, x); -} - - -static inline void write_flash_cr_snb(stlink_t *sl, uint32_t n) { - uint32_t x = read_flash_cr(sl); - x &= ~FLASH_F4_CR_SNB_MASK; - x |= (n << FLASH_F4_CR_SNB); - x |= (1 << FLASH_F4_CR_SER); -#if DEBUG_FLASH - fprintf(stdout, "SNB:0x%x 0x%x\n", x, n); -#endif - stlink_write_debug32(sl, FLASH_F4_CR, x); -} - -static inline void write_flash_cr_bker_pnb(stlink_t *sl, uint32_t n) { - stlink_write_debug32(sl, STM32L4_FLASH_SR, 0xFFFFFFFF & ~(1<backend->close(sl); - free(sl); -} - -int stlink_exit_debug_mode(stlink_t *sl) { - int ret; - - DLOG("*** stlink_exit_debug_mode ***\n"); - ret = stlink_write_debug32(sl, DHCSR, DBGKEY); - if (ret == -1) - return ret; - - return sl->backend->exit_debug_mode(sl); -} - -int stlink_enter_swd_mode(stlink_t *sl) { - DLOG("*** stlink_enter_swd_mode ***\n"); - return sl->backend->enter_swd_mode(sl); -} - -// Force the core into the debug mode -> halted state. -int stlink_force_debug(stlink_t *sl) { - DLOG("*** stlink_force_debug_mode ***\n"); - return sl->backend->force_debug(sl); -} - -int stlink_exit_dfu_mode(stlink_t *sl) { - DLOG("*** stlink_exit_dfu_mode ***\n"); - return sl->backend->exit_dfu_mode(sl); -} - -int stlink_core_id(stlink_t *sl) { - int ret; - - DLOG("*** stlink_core_id ***\n"); - ret = sl->backend->core_id(sl); - if (ret == -1) { - ELOG("Failed to read core_id\n"); - return ret; - } - if (sl->verbose > 2) - stlink_print_data(sl); - DLOG("core_id = 0x%08x\n", sl->core_id); - return ret; -} - -int stlink_chip_id(stlink_t *sl, uint32_t *chip_id) { - int ret; - - ret = stlink_read_debug32(sl, 0xE0042000, chip_id); - if (ret == -1) - return ret; - - if (*chip_id == 0) - ret = stlink_read_debug32(sl, 0x40015800, chip_id); //Try Corex M0 DBGMCU_IDCODE register address - - return ret; -} - -/** - * Cortex m3 tech ref manual, CPUID register description - * @param sl stlink context - * @param cpuid pointer to the result object - */ -int stlink_cpu_id(stlink_t *sl, cortex_m3_cpuid_t *cpuid) { - uint32_t raw; - - if (stlink_read_debug32(sl, CM3_REG_CPUID, &raw)) - return -1; - - cpuid->implementer_id = (raw >> 24) & 0x7f; - cpuid->variant = (raw >> 20) & 0xf; - cpuid->part = (raw >> 4) & 0xfff; - cpuid->revision = raw & 0xf; - return 0; -} - -/** - * reads and decodes the flash parameters, as dynamically as possible - * @param sl - * @return 0 for success, or -1 for unsupported core type. - */ -int stlink_load_device_params(stlink_t *sl) { - ILOG("Loading device parameters....\n"); - const chip_params_t *params = NULL; - stlink_core_id(sl); - uint32_t chip_id; - uint32_t flash_size; - - stlink_chip_id(sl, &chip_id); - sl->chip_id = chip_id & 0xfff; - /* Fix chip_id for F4 rev A errata , Read CPU ID, as CoreID is the same for F2/F4*/ - if (sl->chip_id == 0x411) { - uint32_t cpuid; - stlink_read_debug32(sl, 0xE000ED00, &cpuid); - if ((cpuid & 0xfff0) == 0xc240) - sl->chip_id = 0x413; - } - - for (size_t i = 0; i < sizeof(devices) / sizeof(devices[0]); i++) { - if(devices[i].chip_id == sl->chip_id) { - params = &devices[i]; - break; - } - } - if (params == NULL) { - WLOG("unknown chip id! %#x\n", chip_id); - return -1; - } - - if (params->flash_type == FLASH_TYPE_UNKNOWN) { - WLOG("Invalid flash type, please check device declaration\n"); - return -1; - } - - - // These are fixed... - sl->flash_base = STM32_FLASH_BASE; - sl->sram_base = STM32_SRAM_BASE; - stlink_read_debug32(sl,(params->flash_size_reg) & ~3, &flash_size); - if (params->flash_size_reg & 2) - flash_size = flash_size >>16; - flash_size = flash_size & 0xffff; - - if ((sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS) && ( flash_size == 0 )) { - sl->flash_size = 128 * 1024; - } else if (sl->chip_id == STM32_CHIPID_L1_CAT2) { - sl->flash_size = (flash_size & 0xff) * 1024; - } else if ((sl->chip_id & 0xFFF) == STM32_CHIPID_L1_HIGH) { - // 0 is 384k and 1 is 256k - if ( flash_size == 0 ) { - sl->flash_size = 384 * 1024; - } else { - sl->flash_size = 256 * 1024; - } - } else { - sl->flash_size = flash_size * 1024; - } - sl->flash_type = params->flash_type; - sl->flash_pgsz = params->flash_pagesize; - sl->sram_size = params->sram_size; - sl->sys_base = params->bootrom_base; - sl->sys_size = params->bootrom_size; - - //medium and low devices have the same chipid. ram size depends on flash size. - //STM32F100xx datasheet Doc ID 16455 Table 2 - if(sl->chip_id == STM32_CHIPID_F1_VL_MEDIUM_LOW && sl->flash_size < 64 * 1024){ - sl->sram_size = 0x1000; - } - - ILOG("Device connected is: %s, id %#x\n", params->description, chip_id); - // TODO make note of variable page size here..... - ILOG("SRAM size: %#x bytes (%d KiB), Flash: %#x bytes (%d KiB) in pages of %zd bytes\n", - sl->sram_size, sl->sram_size / 1024, sl->flash_size, sl->flash_size / 1024, - sl->flash_pgsz); - return 0; -} - -int stlink_reset(stlink_t *sl) { - DLOG("*** stlink_reset ***\n"); - return sl->backend->reset(sl); -} - -int stlink_jtag_reset(stlink_t *sl, int value) { - DLOG("*** stlink_jtag_reset ***\n"); - return sl->backend->jtag_reset(sl, value); -} - -int stlink_run(stlink_t *sl) { - DLOG("*** stlink_run ***\n"); - return sl->backend->run(sl); -} - -int stlink_status(stlink_t *sl) { - int ret; - - DLOG("*** stlink_status ***\n"); - ret = sl->backend->status(sl); - stlink_core_stat(sl); - - return ret; -} - -/** - * Decode the version bits, originally from -sg, verified with usb - * @param sl stlink context, assumed to contain valid data in the buffer - * @param slv output parsed version object - */ -void _parse_version(stlink_t *sl, stlink_version_t *slv) { - uint32_t b0 = sl->q_buf[0]; //lsb - uint32_t b1 = sl->q_buf[1]; - uint32_t b2 = sl->q_buf[2]; - uint32_t b3 = sl->q_buf[3]; - uint32_t b4 = sl->q_buf[4]; - uint32_t b5 = sl->q_buf[5]; //msb - - // b0 b1 || b2 b3 | b4 b5 - // 4b | 6b | 6b || 2B | 2B - // stlink_v | jtag_v | swim_v || st_vid | stlink_pid - - slv->stlink_v = (b0 & 0xf0) >> 4; - slv->jtag_v = ((b0 & 0x0f) << 2) | ((b1 & 0xc0) >> 6); - slv->swim_v = b1 & 0x3f; - slv->st_vid = (b3 << 8) | b2; - slv->stlink_pid = (b5 << 8) | b4; - return; -} - -int stlink_version(stlink_t *sl) { - DLOG("*** looking up stlink version\n"); - if (sl->backend->version(sl)) - return -1; - - _parse_version(sl, &sl->version); - - DLOG("st vid = 0x%04x (expect 0x%04x)\n", sl->version.st_vid, USB_ST_VID); - DLOG("stlink pid = 0x%04x\n", sl->version.stlink_pid); - DLOG("stlink version = 0x%x\n", sl->version.stlink_v); - DLOG("jtag version = 0x%x\n", sl->version.jtag_v); - DLOG("swim version = 0x%x\n", sl->version.swim_v); - if (sl->version.jtag_v == 0) { - DLOG(" notice: the firmware doesn't support a jtag/swd interface\n"); - } - if (sl->version.swim_v == 0) { - DLOG(" notice: the firmware doesn't support a swim interface\n"); - } - - return 0; -} - -int stlink_target_voltage(stlink_t *sl) { - int voltage = -1; - DLOG("*** reading target voltage\n"); - if (sl->backend->target_voltage != NULL) { - voltage = sl->backend->target_voltage(sl); - if (voltage != -1) { - DLOG("target voltage = %ldmV\n", voltage); - } else { - DLOG("error reading target voltage\n"); - } - } else { - DLOG("reading voltage not supported by backend\n"); - } - return voltage; -} - -int stlink_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) { - int ret; - - ret = sl->backend->read_debug32(sl, addr, data); - if (!ret) - DLOG("*** stlink_read_debug32 %x is %#x\n", *data, addr); - - return ret; -} - -int stlink_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) { - DLOG("*** stlink_write_debug32 %x to %#x\n", data, addr); - return sl->backend->write_debug32(sl, addr, data); -} - -int stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { - DLOG("*** stlink_write_mem32 %u bytes to %#x\n", len, addr); - if (len % 4 != 0) { - fprintf(stderr, "Error: Data length doesn't have a 32 bit alignment: +%d byte.\n", len % 4); - abort(); - } - return sl->backend->write_mem32(sl, addr, len); -} - -int stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { - DLOG("*** stlink_read_mem32 ***\n"); - if (len % 4 != 0) { // !!! never ever: fw gives just wrong values - fprintf(stderr, "Error: Data length doesn't have a 32 bit alignment: +%d byte.\n", - len % 4); - abort(); - } - return sl->backend->read_mem32(sl, addr, len); -} - -int stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { - DLOG("*** stlink_write_mem8 ***\n"); - if (len > 0x40 ) { // !!! never ever: Writing more then 0x40 bytes gives unexpected behaviour - fprintf(stderr, "Error: Data length > 64: +%d byte.\n", - len); - abort(); - } - return sl->backend->write_mem8(sl, addr, len); -} - -int stlink_read_all_regs(stlink_t *sl, reg *regp) { - DLOG("*** stlink_read_all_regs ***\n"); - return sl->backend->read_all_regs(sl, regp); -} - -int stlink_read_all_unsupported_regs(stlink_t *sl, reg *regp) { - DLOG("*** stlink_read_all_unsupported_regs ***\n"); - return sl->backend->read_all_unsupported_regs(sl, regp); -} - -int stlink_write_reg(stlink_t *sl, uint32_t reg, int idx) { - DLOG("*** stlink_write_reg\n"); - return sl->backend->write_reg(sl, reg, idx); -} - -int stlink_read_reg(stlink_t *sl, int r_idx, reg *regp) { - DLOG("*** stlink_read_reg\n"); - DLOG(" (%d) ***\n", r_idx); - - if (r_idx > 20 || r_idx < 0) { - fprintf(stderr, "Error: register index must be in [0..20]\n"); - return -1; - } - - return sl->backend->read_reg(sl, r_idx, regp); -} - -int stlink_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) { - int r_convert; - - DLOG("*** stlink_read_unsupported_reg\n"); - DLOG(" (%d) ***\n", r_idx); - - /* Convert to values used by DCRSR */ - if (r_idx >= 0x1C && r_idx <= 0x1F) { /* primask, basepri, faultmask, or control */ - r_convert = 0x14; - } else if (r_idx == 0x40) { /* FPSCR */ - r_convert = 0x21; - } else if (r_idx >= 0x20 && r_idx < 0x40) { - r_convert = 0x40 + (r_idx - 0x20); - } else { - fprintf(stderr, "Error: register address must be in [0x1C..0x40]\n"); - return -1; - } - - return sl->backend->read_unsupported_reg(sl, r_convert, regp); -} - -int stlink_write_unsupported_reg(stlink_t *sl, uint32_t val, int r_idx, reg *regp) { - int r_convert; - - DLOG("*** stlink_write_unsupported_reg\n"); - DLOG(" (%d) ***\n", r_idx); - - /* Convert to values used by DCRSR */ - if (r_idx >= 0x1C && r_idx <= 0x1F) { /* primask, basepri, faultmask, or control */ - r_convert = r_idx; /* The backend function handles this */ - } else if (r_idx == 0x40) { /* FPSCR */ - r_convert = 0x21; - } else if (r_idx >= 0x20 && r_idx < 0x40) { - r_convert = 0x40 + (r_idx - 0x20); - } else { - fprintf(stderr, "Error: register address must be in [0x1C..0x40]\n"); - return -1; - } - - return sl->backend->write_unsupported_reg(sl, val, r_convert, regp); -} - -unsigned int is_core_halted(stlink_t *sl) { - /* return non zero if core is halted */ - stlink_status(sl); - return sl->q_buf[0] == STLINK_CORE_HALTED; -} - -int stlink_step(stlink_t *sl) { - DLOG("*** stlink_step ***\n"); - return sl->backend->step(sl); -} - -int stlink_current_mode(stlink_t *sl) { - int mode = sl->backend->current_mode(sl); - switch (mode) { - case STLINK_DEV_DFU_MODE: - DLOG("stlink current mode: dfu\n"); - return mode; - case STLINK_DEV_DEBUG_MODE: - DLOG("stlink current mode: debug (jtag or swd)\n"); - return mode; - case STLINK_DEV_MASS_MODE: - DLOG("stlink current mode: mass\n"); - return mode; - } - DLOG("stlink mode: unknown!\n"); - return STLINK_DEV_UNKNOWN_MODE; -} - - - - -// End of delegates.... Common code below here... - -// Endianness -// http://www.ibm.com/developerworks/aix/library/au-endianc/index.html -// const int i = 1; -// #define is_bigendian() ( (*(char*)&i) == 0 ) - -inline unsigned int is_bigendian(void) { - static volatile const unsigned int i = 1; - return *(volatile const char*) &i == 0; -} - -uint16_t read_uint16(const unsigned char *c, const int pt) { - uint32_t ui; - char *p = (char *) &ui; - - if (!is_bigendian()) { // le -> le (don't swap) - p[0] = c[pt + 0]; - p[1] = c[pt + 1]; - } else { - p[0] = c[pt + 1]; - p[1] = c[pt + 0]; - } - return ui; -} - -// same as above with entrypoint. - -void stlink_run_at(stlink_t *sl, stm32_addr_t addr) { - stlink_write_reg(sl, addr, 15); /* pc register */ - - stlink_run(sl); - - while (is_core_halted(sl) == 0) - usleep(3000000); -} - -void stlink_core_stat(stlink_t *sl) { - if (sl->q_len <= 0) - return; - - switch (sl->q_buf[0]) { - case STLINK_CORE_RUNNING: - sl->core_stat = STLINK_CORE_RUNNING; - DLOG(" core status: running\n"); - return; - case STLINK_CORE_HALTED: - sl->core_stat = STLINK_CORE_HALTED; - DLOG(" core status: halted\n"); - return; - default: - sl->core_stat = STLINK_CORE_STAT_UNKNOWN; - fprintf(stderr, " core status: unknown\n"); - } -} - -void stlink_print_data(stlink_t * sl) { - if (sl->q_len <= 0 || sl->verbose < UDEBUG) - return; - if (sl->verbose > 2) - fprintf(stdout, "data_len = %d 0x%x\n", sl->q_len, sl->q_len); - - for (int i = 0; i < sl->q_len; i++) { - if (i % 16 == 0) { - /* - if (sl->q_data_dir == Q_DATA_OUT) - fprintf(stdout, "\n<- 0x%08x ", sl->q_addr + i); - else - fprintf(stdout, "\n-> 0x%08x ", sl->q_addr + i); - */ - } - fprintf(stdout, " %02x", (unsigned int) sl->q_buf[i]); - } - fputs("\n\n", stdout); -} - -/* memory mapped file */ - -typedef struct mapped_file { - uint8_t* base; - size_t len; -} mapped_file_t; - -#define MAPPED_FILE_INITIALIZER { NULL, 0 } - -static int map_file(mapped_file_t* mf, const char* path) { - int error = -1; - struct stat st; - - const int fd = open(path, O_RDONLY | O_BINARY); - if (fd == -1) { - fprintf(stderr, "open(%s) == -1\n", path); - return -1; - } - - if (fstat(fd, &st) == -1) { - fprintf(stderr, "fstat() == -1\n"); - goto on_error; - } - - mf->base = (uint8_t*) mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - if (mf->base == MAP_FAILED) { - fprintf(stderr, "mmap() == MAP_FAILED\n"); - goto on_error; - } - - mf->len = st.st_size; - - /* success */ - error = 0; - -on_error: - close(fd); - - return error; -} - -static void unmap_file(mapped_file_t * mf) { - munmap((void*) mf->base, mf->len); - mf->base = (unsigned char*) MAP_FAILED; - mf->len = 0; -} - -/* Limit the block size to compare to 0x1800 - Anything larger will stall the STLINK2 - Maybe STLINK V1 needs smaller value!*/ -static int check_file(stlink_t* sl, mapped_file_t* mf, stm32_addr_t addr) { - size_t off; - size_t n_cmp = sl->flash_pgsz; - if ( n_cmp > 0x1800) - n_cmp = 0x1800; - - for (off = 0; off < mf->len; off += n_cmp) { - size_t aligned_size; - - /* adjust last page size */ - size_t cmp_size = n_cmp; - if ((off + n_cmp) > mf->len) - cmp_size = mf->len - off; - - aligned_size = cmp_size; - if (aligned_size & (4 - 1)) - aligned_size = (cmp_size + 4) & ~(4 - 1); - - stlink_read_mem32(sl, addr + off, aligned_size); - - if (memcmp(sl->q_buf, mf->base + off, cmp_size)) - return -1; - } - - return 0; -} - -int stlink_fwrite_sram -(stlink_t * sl, const char* path, stm32_addr_t addr) { - /* write the file in sram at addr */ - - int error = -1; - size_t off; - size_t len; - mapped_file_t mf = MAPPED_FILE_INITIALIZER; - uint32_t val; - - - if (map_file(&mf, path) == -1) { - fprintf(stderr, "map_file() == -1\n"); - return -1; - } - - /* check addr range is inside the sram */ - if (addr < sl->sram_base) { - fprintf(stderr, "addr too low\n"); - goto on_error; - } else if ((addr + mf.len) < addr) { - fprintf(stderr, "addr overruns\n"); - goto on_error; - } else if ((addr + mf.len) > (sl->sram_base + sl->sram_size)) { - fprintf(stderr, "addr too high\n"); - goto on_error; - } else if (addr & 3) { - /* todo */ - fprintf(stderr, "unaligned addr\n"); - goto on_error; - } - - len = mf.len; - - if(len & 3) { - len -= len & 3; - } - - /* do the copy by 1k blocks */ - for (off = 0; off < len; off += 1024) { - size_t size = 1024; - if ((off + size) > len) - size = len - off; - - memcpy(sl->q_buf, mf.base + off, size); - - /* round size if needed */ - if (size & 3) - size += 2; - - stlink_write_mem32(sl, addr + off, size); - } - - if(mf.len > len) { - memcpy(sl->q_buf, mf.base + len, mf.len - len); - stlink_write_mem8(sl, addr + len, mf.len - len); - } - - /* check the file ha been written */ - if (check_file(sl, &mf, addr) == -1) { - fprintf(stderr, "check_file() == -1\n"); - goto on_error; - } - - /* success */ - error = 0; - /* set stack*/ - stlink_read_debug32(sl, addr, &val); - stlink_write_reg(sl, val, 13); - /* Set PC to the reset routine*/ - stlink_read_debug32(sl, addr + 4, &val); - stlink_write_reg(sl, val, 15); - stlink_run(sl); - -on_error: - unmap_file(&mf); - return error; -} - -int stlink_fread(stlink_t* sl, const char* path, stm32_addr_t addr, size_t size) { - /* read size bytes from addr to file */ - - int error = -1; - size_t off; - - const int fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 00700); - if (fd == -1) { - fprintf(stderr, "open(%s) == -1\n", path); - return -1; - } - - if (size <1) - size = sl->flash_size; - - if (size > sl->flash_size) - size = sl->flash_size; - - size_t cmp_size = (sl->flash_pgsz > 0x1800)? 0x1800:sl->flash_pgsz; - for (off = 0; off < size; off += cmp_size) { - size_t aligned_size; - - /* adjust last page size */ - if ((off + cmp_size) > size) - cmp_size = size - off; - - aligned_size = cmp_size; - if (aligned_size & (4 - 1)) - aligned_size = (cmp_size + 4) & ~(4 - 1); - - stlink_read_mem32(sl, addr + off, aligned_size); - - if (write(fd, sl->q_buf, sl->q_len) != (ssize_t) aligned_size) { - fprintf(stderr, "write() != aligned_size\n"); - goto on_error; - } - } - - /* success */ - error = 0; - -on_error: - close(fd); - - return error; -} - -int write_buffer_to_sram(stlink_t *sl, flash_loader_t* fl, const uint8_t* buf, size_t size) { - /* write the buffer right after the loader */ - size_t chunk = size & ~0x3; - size_t rem = size & 0x3; - if (chunk) { - memcpy(sl->q_buf, buf, chunk); - stlink_write_mem32(sl, fl->buf_addr, chunk); - } - if (rem) { - memcpy(sl->q_buf, buf+chunk, rem); - stlink_write_mem8(sl, (fl->buf_addr)+chunk, rem); - } - return 0; -} - -uint32_t calculate_F4_sectornum(uint32_t flashaddr){ - uint32_t offset = 0; - flashaddr &= ~STM32_FLASH_BASE; //Page now holding the actual flash address - if (flashaddr >= 0x100000) { - offset = 12; - flashaddr -= 0x100000; - } - if (flashaddr<0x4000) return (offset + 0); - else if(flashaddr<0x8000) return(offset + 1); - else if(flashaddr<0xc000) return(offset + 2); - else if(flashaddr<0x10000) return(offset + 3); - else if(flashaddr<0x20000) return(offset + 4); - else return offset + (flashaddr/0x20000) +4; - -} - -uint32_t calculate_F7_sectornum(uint32_t flashaddr){ - flashaddr &= ~STM32_FLASH_BASE; //Page now holding the actual flash address - if(flashaddr<0x20000) return(flashaddr/0x8000); - else if(flashaddr<0x40000) return(4); - else return(flashaddr/0x40000) +4; - -} - -// Returns BKER:PNB for the given page address -uint32_t calculate_L4_page(stlink_t *sl, uint32_t flashaddr) { - uint32_t bker = 0; - uint32_t flashopt; - stlink_read_debug32(sl, STM32L4_FLASH_OPTR, &flashopt); - flashaddr -= STM32_FLASH_BASE; - if (flashopt & (1lu << STM32L4_FLASH_OPTR_DUALBANK)) { - uint32_t banksize = sl->flash_size / 2; - if (flashaddr >= banksize) { - flashaddr -= banksize; - bker = 0x100; - } - } - // For 1MB chips without the dual-bank option set, the page address will - // overflow into the BKER bit, which gives us the correct bank:page value. - return bker | flashaddr/sl->flash_pgsz; -} - -uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr){ - if ((sl->chip_id == STM32_CHIPID_F2) || (sl->chip_id == STM32_CHIPID_F4) || (sl->chip_id == STM32_CHIPID_F4_DE) || - (sl->chip_id == STM32_CHIPID_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) || - (sl->chip_id == STM32_CHIPID_F446) || (sl->chip_id == STM32_CHIPID_F4_DSI)) { - uint32_t sector=calculate_F4_sectornum(flashaddr); - if (sector>= 12) { - sector -= 12; - } - if (sector<4) sl->flash_pgsz=0x4000; - else if(sector<5) sl->flash_pgsz=0x10000; - else sl->flash_pgsz=0x20000; - } - else if (sl->chip_id == STM32_CHIPID_F7) { - uint32_t sector=calculate_F7_sectornum(flashaddr); - if (sector<4) sl->flash_pgsz=0x8000; - else if(sector<5) sl->flash_pgsz=0x20000; - else sl->flash_pgsz=0x40000; - } - return (sl->flash_pgsz); -} - -/** - * Erase a page of flash, assumes sl is fully populated with things like chip/core ids - * @param sl stlink context - * @param flashaddr an address in the flash page to erase - * @return 0 on success -ve on failure - */ -int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr) -{ - if (sl->flash_type == FLASH_TYPE_F4 || sl->flash_type == FLASH_TYPE_L4) { - /* wait for ongoing op to finish */ - wait_flash_busy(sl); - - /* unlock if locked */ - unlock_flash_if(sl); - - /* select the page to erase */ - if (sl->chip_id == STM32_CHIPID_L4) { - // calculate the actual bank+page from the address - uint32_t page = calculate_L4_page(sl, flashaddr); - - fprintf(stderr, "EraseFlash - Page:0x%x Size:0x%x ", page, stlink_calculate_pagesize(sl, flashaddr)); - - write_flash_cr_bker_pnb(sl, page); - } else if (sl->chip_id == STM32_CHIPID_F7) { - // calculate the actual page from the address - uint32_t sector=calculate_F7_sectornum(flashaddr); - - fprintf(stderr, "EraseFlash - Sector:0x%x Size:0x%x ", sector, stlink_calculate_pagesize(sl, flashaddr)); - - write_flash_cr_snb(sl, sector); - } else { - // calculate the actual page from the address - uint32_t sector=calculate_F4_sectornum(flashaddr); - - fprintf(stderr, "EraseFlash - Sector:0x%x Size:0x%x ", sector, stlink_calculate_pagesize(sl, flashaddr)); - - //the SNB values for flash sectors in the second bank do not directly follow the values for the first bank on 2mb devices... - if (sector >= 12) sector += 4; - - write_flash_cr_snb(sl, sector); - } - - /* start erase operation */ - set_flash_cr_strt(sl); - - /* wait for completion */ - wait_flash_busy(sl); - - /* relock the flash */ - //todo: fails to program if this is in - lock_flash(sl); -#if DEBUG_FLASH - fprintf(stdout, "Erase Final CR:0x%x\n", read_flash_cr(sl)); -#endif - } else if (sl->flash_type == FLASH_TYPE_L0) { - - uint32_t val; - uint32_t flash_regs_base; - if (sl->chip_id == STM32_CHIPID_L0) { - flash_regs_base = STM32L0_FLASH_REGS_ADDR; - } else { - flash_regs_base = STM32L_FLASH_REGS_ADDR; - } - - /* check if the locks are set */ - stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); - if((val & (1<<0))||(val & (1<<1))) { - /* disable pecr protection */ - stlink_write_debug32(sl, flash_regs_base + FLASH_PEKEYR_OFF, 0x89abcdef); - stlink_write_debug32(sl, flash_regs_base + FLASH_PEKEYR_OFF, 0x02030405); - - /* check pecr.pelock is cleared */ - stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); - if (val & (1 << 0)) { - WLOG("pecr.pelock not clear (%#x)\n", val); - return -1; - } - - /* unlock program memory */ - stlink_write_debug32(sl, flash_regs_base + FLASH_PRGKEYR_OFF, 0x8c9daebf); - stlink_write_debug32(sl, flash_regs_base + FLASH_PRGKEYR_OFF, 0x13141516); - - /* check pecr.prglock is cleared */ - stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); - if (val & (1 << 1)) { - WLOG("pecr.prglock not clear (%#x)\n", val); - return -1; - } - } - - /* set pecr.{erase,prog} */ - val |= (1 << 9) | (1 << 3); - stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); -#if 0 /* fix_to_be_confirmed */ - - /* wait for sr.busy to be cleared - * MP: Test shows that busy bit is not set here. Perhaps, PM0062 is - * wrong and we do not need to wait here for clearing the busy bit. - * TEXANE: ok, if experience says so and it works for you, we comment - * it. If someone has a problem, please drop an email. - */ - do { - stlink_read_debug32(sl, STM32L_FLASH_SR, &val) - } while((val & (1 << 0)) != 0); - -#endif /* fix_to_be_confirmed */ - - /* write 0 to the first word of the page to be erased */ - stlink_write_debug32(sl, flashaddr, 0); - - /* MP: It is better to wait for clearing the busy bit after issuing - page erase command, even though PM0062 recommends to wait before it. - Test shows that a few iterations is performed in the following loop - before busy bit is cleared.*/ - do { - stlink_read_debug32(sl, flash_regs_base + FLASH_SR_OFF, &val); - } while ((val & (1 << 0)) != 0); - - /* reset lock bits */ - stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); - val |= (1 << 0) | (1 << 1) | (1 << 2); - stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); - } else if (sl->flash_type == FLASH_TYPE_F0) { - /* wait for ongoing op to finish */ - wait_flash_busy(sl); - - /* unlock if locked */ - unlock_flash_if(sl); - - /* set the page erase bit */ - set_flash_cr_per(sl); - - /* select the page to erase */ - write_flash_ar(sl, flashaddr); - - /* start erase operation, reset by hw with bsy bit */ - set_flash_cr_strt(sl); - - /* wait for completion */ - wait_flash_busy(sl); - - /* relock the flash */ - lock_flash(sl); - } else { - WLOG("unknown coreid %x, page erase failed\n", sl->core_id); - return -1; - } - - /* todo: verify the erased page */ - - return 0; -} - -int stlink_erase_flash_mass(stlink_t *sl) { - if (sl->flash_type == FLASH_TYPE_L0) { - /* erase each page */ - int i = 0, num_pages = sl->flash_size/sl->flash_pgsz; - for (i = 0; i < num_pages; i++) { - /* addr must be an addr inside the page */ - stm32_addr_t addr = sl->flash_base + i * sl->flash_pgsz; - if (stlink_erase_flash_page(sl, addr) == -1) { - WLOG("Failed to erase_flash_page(%#zx) == -1\n", addr); - return -1; - } - fprintf(stdout,"-> Flash page at %5d/%5d erased\n", i, num_pages); - fflush(stdout); - } - fprintf(stdout, "\n"); - } else { - /* wait for ongoing op to finish */ - wait_flash_busy(sl); - - /* unlock if locked */ - unlock_flash_if(sl); - - /* set the mass erase bit */ - set_flash_cr_mer(sl); - - /* start erase operation, reset by hw with bsy bit */ - set_flash_cr_strt(sl); - - /* wait for completion */ - wait_flash_busy_progress(sl); - - /* relock the flash */ - lock_flash(sl); - - /* todo: verify the erased memory */ - } - return 0; -} - -int init_flash_loader(stlink_t *sl, flash_loader_t* fl) { - size_t size; - - /* allocate the loader in sram */ - if (write_loader_to_sram(sl, &fl->loader_addr, &size) == -1) { - WLOG("Failed to write flash loader to sram!\n"); - return -1; - } - - /* allocate a one page buffer in sram right after loader */ - fl->buf_addr = fl->loader_addr + size; - ILOG("Successfully loaded flash loader in sram\n"); - return 0; -} - -int write_loader_to_sram(stlink_t *sl, stm32_addr_t* addr, size_t* size) { - /* from openocd, contrib/loaders/flash/stm32.s */ - static const uint8_t loader_code_stm32vl[] = { - 0x08, 0x4c, /* ldr r4, STM32_FLASH_BASE */ - 0x1c, 0x44, /* add r4, r3 */ - /* write_half_word: */ - 0x01, 0x23, /* movs r3, #0x01 */ - 0x23, 0x61, /* str r3, [r4, #STM32_FLASH_CR_OFFSET] */ - 0x30, 0xf8, 0x02, 0x3b, /* ldrh r3, [r0], #0x02 */ - 0x21, 0xf8, 0x02, 0x3b, /* strh r3, [r1], #0x02 */ - /* busy: */ - 0xe3, 0x68, /* ldr r3, [r4, #STM32_FLASH_SR_OFFSET] */ - 0x13, 0xf0, 0x01, 0x0f, /* tst r3, #0x01 */ - 0xfb, 0xd0, /* beq busy */ - 0x13, 0xf0, 0x14, 0x0f, /* tst r3, #0x14 */ - 0x01, 0xd1, /* bne exit */ - 0x01, 0x3a, /* subs r2, r2, #0x01 */ - 0xf0, 0xd1, /* bne write_half_word */ - /* exit: */ - 0x00, 0xbe, /* bkpt #0x00 */ - 0x00, 0x20, 0x02, 0x40, /* STM32_FLASH_BASE: .word 0x40022000 */ - }; - - /* flashloaders/stm32f0.s -- thumb1 only, same sequence as for STM32VL, bank ignored */ - static const uint8_t loader_code_stm32f0[] = { -#if 1 - /* - * These two NOPs here are a safety precaution, added by Pekka Nikander - * while debugging the STM32F05x support. They may not be needed, but - * there were strange problems with simpler programs, like a program - * that had just a breakpoint or a program that first moved zero to register r2 - * and then had a breakpoint. So, it appears safest to have these two nops. - * - * Feel free to remove them, if you dare, but then please do test the result - * rigorously. Also, if you remove these, it may be a good idea first to - * #if 0 them out, with a comment when these were taken out, and to remove - * these only a few months later... But YMMV. - */ - 0x00, 0x30, // nop /* add r0,#0 */ - 0x00, 0x30, // nop /* add r0,#0 */ -#endif - 0x0A, 0x4C, // ldr r4, STM32_FLASH_BASE - 0x01, 0x25, // mov r5, #1 /* FLASH_CR_PG, FLASH_SR_BUSY */ - 0x04, 0x26, // mov r6, #4 /* PGERR */ - // write_half_word: - 0x23, 0x69, // ldr r3, [r4, #16] /* FLASH->CR */ - 0x2B, 0x43, // orr r3, r5 - 0x23, 0x61, // str r3, [r4, #16] /* FLASH->CR |= FLASH_CR_PG */ - 0x03, 0x88, // ldrh r3, [r0] /* r3 = *sram */ - 0x0B, 0x80, // strh r3, [r1] /* *flash = r3 */ - // busy: - 0xE3, 0x68, // ldr r3, [r4, #12] /* FLASH->SR */ - 0x2B, 0x42, // tst r3, r5 /* FLASH_SR_BUSY */ - 0xFC, 0xD0, // beq busy - - 0x33, 0x42, // tst r3, r6 /* PGERR */ - 0x04, 0xD1, // bne exit - - 0x02, 0x30, // add r0, r0, #2 /* sram += 2 */ - 0x02, 0x31, // add r1, r1, #2 /* flash += 2 */ - 0x01, 0x3A, // sub r2, r2, #0x01 /* count-- */ - 0x00, 0x2A, // cmp r2, #0 - 0xF0, 0xD1, // bne write_half_word - // exit: - 0x23, 0x69, // ldr r3, [r4, #16] /* FLASH->CR */ - 0xAB, 0x43, // bic r3, r5 - 0x23, 0x61, // str r3, [r4, #16] /* FLASH->CR &= ~FLASH_CR_PG */ - 0x00, 0xBE, // bkpt #0x00 - 0x00, 0x20, 0x02, 0x40, /* STM32_FLASH_BASE: .word 0x40022000 */ - }; - - static const uint8_t loader_code_stm32l[] = { - - /* openocd.git/contrib/loaders/flash/stm32lx.S - r0, input, source addr - r1, input, dest addr - r2, input, word count - r2, output, remaining word count - */ - - 0x04, 0xe0, - - 0x50, 0xf8, 0x04, 0xcb, - 0x41, 0xf8, 0x04, 0xcb, - 0x01, 0x3a, - - 0x00, 0x2a, - 0xf8, 0xd3, - 0x00, 0xbe - }; - - static const uint8_t loader_code_stm32l0[] = { - - /* - r0, input, source addr - r1, input, dest addr - r2, input, word count - r2, output, remaining word count - */ - - 0x04, 0xe0, - - 0x04, 0x68, - 0x0c, 0x60, - 0x01, 0x3a, - 0x04, 0x31, - 0x04, 0x30, - - 0x00, 0x2a, - 0xf8, 0xd3, - 0x00, 0xbe - }; - - static const uint8_t loader_code_stm32f4[] = { - // flashloaders/stm32f4.s - - 0x07, 0x4b, - - 0x62, 0xb1, - 0x04, 0x68, - 0x0c, 0x60, - - 0xdc, 0x89, - 0x14, 0xf0, 0x01, 0x0f, - 0xfb, 0xd1, - 0x00, 0xf1, 0x04, 0x00, - 0x01, 0xf1, 0x04, 0x01, - 0xa2, 0xf1, 0x01, 0x02, - 0xf1, 0xe7, - - 0x00, 0xbe, - - 0x00, 0x3c, 0x02, 0x40, - }; - - static const uint8_t loader_code_stm32f4_lv[] = { - // flashloaders/stm32f4lv.s - 0x92, 0x00, - - 0x08, 0x4b, - 0x62, 0xb1, - 0x04, 0x78, - 0x0c, 0x70, - - 0xdc, 0x89, - 0x14, 0xf0, 0x01, 0x0f, - 0xfb, 0xd1, - 0x00, 0xf1, 0x01, 0x00, - 0x01, 0xf1, 0x01, 0x01, - 0xa2, 0xf1, 0x01, 0x02, - 0xf1, 0xe7, - - 0x00, 0xbe, - 0x00, 0xbf, - - 0x00, 0x3c, 0x02, 0x40, - }; - - static const uint8_t loader_code_stm32l4[] = { - // flashloaders/stm32l4.s - 0x08, 0x4b, // start: ldr r3, [pc, #32] ; - 0x72, 0xb1, // next: cbz r2, - 0x04, 0x68, // ldr r4, [r0, #0] - 0x45, 0x68, // ldr r5, [r0, #4] - 0x0c, 0x60, // str r4, [r1, #0] - 0x4d, 0x60, // str r5, [r1, #4] - 0x5c, 0x8a, // wait: ldrh r4, [r3, #18] - 0x14, 0xf0, 0x01, 0x0f, // tst.w r4, #1 - 0xfb, 0xd1, // bne.n - 0x00, 0xf1, 0x08, 0x00, // add.w r0, r0, #8 - 0x01, 0xf1, 0x08, 0x01, // add.w r1, r1, #8 - 0xa2, 0xf1, 0x01, 0x02, // sub.w r2, r2, #1 - 0xef, 0xe7, // b.n - 0x00, 0xbe, // done: bkpt 0x0000 - 0x00, 0x20, 0x02, 0x40 // flash_base: .word 0x40022000 - }; - - static const uint8_t loader_code_stm32f7[] = { - 0x08, 0x4b, - 0x72, 0xb1, - 0x04, 0x68, - 0x0c, 0x60, - 0xbf, 0xf3, 0x4f, 0x8f, // DSB Memory barrier for in order flash write - 0xdc, 0x89, - 0x14, 0xf0, 0x01, 0x0f, - 0xfb, 0xd1, - 0x00, 0xf1, 0x04, 0x00, - 0x01, 0xf1, 0x04, 0x01, - 0xa2, 0xf1, 0x01, 0x02, - 0xef, 0xe7, - 0x00, 0xbe, // bkpt #0x00 - 0x00, 0x3c, 0x02, 0x40, - }; - - const uint8_t* loader_code; - size_t loader_size; - - if (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_CAT2 - || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS || sl->chip_id == STM32_CHIPID_L1_HIGH - || sl->chip_id == STM32_CHIPID_L152_RE) { /* stm32l */ - loader_code = loader_code_stm32l; - loader_size = sizeof(loader_code_stm32l); - } else if (sl->core_id == STM32VL_CORE_ID - || sl->chip_id == STM32_CHIPID_F3 - || sl->chip_id == STM32_CHIPID_F3_SMALL - || sl->chip_id == STM32_CHIPID_F303_HIGH - || sl->chip_id == STM32_CHIPID_F37x - || sl->chip_id == STM32_CHIPID_F334) { - loader_code = loader_code_stm32vl; - loader_size = sizeof(loader_code_stm32vl); - } else if (sl->chip_id == STM32_CHIPID_F2 || sl->chip_id == STM32_CHIPID_F4 || (sl->chip_id == STM32_CHIPID_F4_DE) || - sl->chip_id == STM32_CHIPID_F4_LP || sl->chip_id == STM32_CHIPID_F4_HD || (sl->chip_id == STM32_CHIPID_F411RE) || - (sl->chip_id == STM32_CHIPID_F446) || (sl->chip_id == STM32_CHIPID_F4_DSI)){ - int voltage = stlink_target_voltage(sl); - if (voltage == -1) { - printf("Failed to read Target voltage\n"); - return voltage; - } else if (voltage > 2700) { - loader_code = loader_code_stm32f4; - loader_size = sizeof(loader_code_stm32f4); - } else { - loader_code = loader_code_stm32f4_lv; - loader_size = sizeof(loader_code_stm32f4_lv); - } - } else if (sl->chip_id == STM32_CHIPID_F7){ - loader_code = loader_code_stm32f7; - loader_size = sizeof(loader_code_stm32f7); - } else if (sl->chip_id == STM32_CHIPID_F0 || sl->chip_id == STM32_CHIPID_F04 || sl->chip_id == STM32_CHIPID_F0_CAN || sl->chip_id == STM32_CHIPID_F0_SMALL || sl->chip_id == STM32_CHIPID_F09X) { - loader_code = loader_code_stm32f0; - loader_size = sizeof(loader_code_stm32f0); - } else if (sl->chip_id == STM32_CHIPID_L0) { - loader_code = loader_code_stm32l0; - loader_size = sizeof(loader_code_stm32l0); - } else if (sl->chip_id == STM32_CHIPID_L4) { - loader_code = loader_code_stm32l4; - loader_size = sizeof(loader_code_stm32l4); - } else { - ELOG("unknown coreid, not sure what flash loader to use, aborting!: %x\n", sl->core_id); - return -1; - } - - memcpy(sl->q_buf, loader_code, loader_size); - stlink_write_mem32(sl, sl->sram_base, loader_size); - - *addr = sl->sram_base; - *size = loader_size; - - /* success */ - return 0; -} - -int stlink_fcheck_flash(stlink_t *sl, const char* path, stm32_addr_t addr) { - /* check the contents of path are at addr */ - - int res; - mapped_file_t mf = MAPPED_FILE_INITIALIZER; - - if (map_file(&mf, path) == -1) - return -1; - - res = check_file(sl, &mf, addr); - - unmap_file(&mf); - - return res; -} - -/** - * Verify addr..addr+len is binary identical to base...base+len - * @param sl stlink context - * @param address stm device address - * @param data host side buffer to check against - * @param length how much - * @return 0 for success, -ve for failure - */ -int stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, unsigned length) { - size_t off; - size_t cmp_size = (sl->flash_pgsz > 0x1800)? 0x1800:sl->flash_pgsz; - ILOG("Starting verification of write complete\n"); - for (off = 0; off < length; off += cmp_size) { - size_t aligned_size; - - /* adjust last page size */ - if ((off + cmp_size) > length) - cmp_size = length - off; - - aligned_size = cmp_size; - if (aligned_size & (4 - 1)) - aligned_size = (cmp_size + 4) & ~(4 - 1); - - stlink_read_mem32(sl, address + off, aligned_size); - - if (memcmp(sl->q_buf, data + off, cmp_size)) { - ELOG("Verification of flash failed at offset: %zd\n", off); - return -1; - } - } - ILOG("Flash written and verified! jolly good!\n"); - return 0; - -} - -int stm32l1_write_half_pages(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uint32_t len, uint32_t pagesize) -{ - unsigned int count; - unsigned int num_half_pages = len / pagesize; - uint32_t val; - uint32_t flash_regs_base; - flash_loader_t fl; - - if (sl->chip_id == STM32_CHIPID_L0) { - flash_regs_base = STM32L0_FLASH_REGS_ADDR; - } else { - flash_regs_base = STM32L_FLASH_REGS_ADDR; - } - - ILOG("Starting Half page flash write for STM32L core id\n"); - /* flash loader initialization */ - if (init_flash_loader(sl, &fl) == -1) { - WLOG("init_flash_loader() == -1\n"); - return -1; - } - /* Unlock already done */ - stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); - val |= (1 << FLASH_L1_FPRG); - stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); - - val |= (1 << FLASH_L1_PROG); - stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); - do { - stlink_read_debug32(sl, flash_regs_base + FLASH_SR_OFF, &val); - } while ((val & (1 << 0)) != 0); - - for (count = 0; count < num_half_pages; count ++) { - if (run_flash_loader(sl, &fl, addr + count * pagesize, base + count * pagesize, pagesize) == -1) { - WLOG("l1_run_flash_loader(%#zx) failed! == -1\n", addr + count * pagesize); - stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); - val &= ~((1 << FLASH_L1_FPRG) |(1 << FLASH_L1_PROG)); - stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); - return -1; - } - /* wait for sr.busy to be cleared */ - if (sl->verbose >= 1) { - /* show progress. writing procedure is slow - and previous errors are misleading */ - fprintf(stdout, "\r%3u/%u halfpages written", count + 1, num_half_pages); - fflush(stdout); - } - do { - stlink_read_debug32(sl, flash_regs_base + FLASH_SR_OFF, &val); - } while ((val & (1 << 0)) != 0); - } - stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); - val &= ~(1 << FLASH_L1_PROG); - stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); - stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); - val &= ~(1 << FLASH_L1_FPRG); - stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); - - return 0; -} - -int stlink_write_flash(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uint32_t len, uint8_t eraseonly) { - size_t off; - flash_loader_t fl; - ILOG("Attempting to write %d (%#x) bytes to stm32 address: %u (%#x)\n", - len, len, addr, addr); - /* check addr range is inside the flash */ - stlink_calculate_pagesize(sl, addr); - if (addr < sl->flash_base) { - ELOG("addr too low %#x < %#x\n", addr, sl->flash_base); - return -1; - } else if ((addr + len) < addr) { - ELOG("addr overruns\n"); - return -1; - } else if ((addr + len) > (sl->flash_base + sl->flash_size)) { - ELOG("addr too high\n"); - return -1; - } else if (addr & 1) { - ELOG("unaligned addr 0x%x\n", addr); - return -1; - } else if (len & 1) { - WLOG("unaligned len 0x%x -- padding with zero\n", len); - len += 1; - } else if (addr & (sl->flash_pgsz - 1)) { - ELOG("addr not a multiple of pagesize, not supported\n"); - return -1; - } - - // Make sure we've loaded the context with the chip details - stlink_core_id(sl); - /* erase each page */ - int page_count = 0; - for (off = 0; off < len; off += stlink_calculate_pagesize(sl, addr + off)) { - /* addr must be an addr inside the page */ - if (stlink_erase_flash_page(sl, addr + off) == -1) { - ELOG("Failed to erase_flash_page(%#zx) == -1\n", addr + off); - return -1; - } - fprintf(stdout,"\rFlash page at addr: 0x%08lx erased", - (unsigned long)addr + off); - fflush(stdout); - page_count++; - } - fprintf(stdout,"\n"); - ILOG("Finished erasing %d pages of %d (%#x) bytes\n", - page_count, sl->flash_pgsz, sl->flash_pgsz); - - if (eraseonly) - return 0; - - if ((sl->flash_type == FLASH_TYPE_F4) || (sl->flash_type == FLASH_TYPE_L4)) { - /* todo: check write operation */ - - ILOG("Starting Flash write for F2/F4/L4\n"); - /* flash loader initialization */ - if (init_flash_loader(sl, &fl) == -1) { - ELOG("init_flash_loader() == -1\n"); - return -1; - } - - /* First unlock the cr */ - unlock_flash_if(sl); - - /* TODO: Check that Voltage range is 2.7 - 3.6 V */ - if (sl->chip_id != STM32_CHIPID_L4) { - /* set parallelisim to 32 bit*/ - int voltage = stlink_target_voltage(sl); - if (voltage == -1) { - printf("Failed to read Target voltage\n"); - return voltage; - } else if (voltage > 2700) { - printf("enabling 32-bit flash writes\n"); - write_flash_cr_psiz(sl, 2); - } else { - printf("Target voltage (%d mV) too low for 32-bit flash, using 8-bit flash writes\n", voltage); - write_flash_cr_psiz(sl, 0); - } - } else { - /* L4 does not have a byte-write mode */ - int voltage = stlink_target_voltage(sl); - if (voltage == -1) { - printf("Failed to read Target voltage\n"); - return voltage; - } else if (voltage < 1710) { - printf("Target voltage (%d mV) too low for flash writes!\n", voltage); - return -1; - } - } - - /* set programming mode */ - set_flash_cr_pg(sl); - - for(off = 0; off < len;) { - size_t size = len - off > 0x8000 ? 0x8000 : len - off; - - printf("size: %zu\n", size); - - if (run_flash_loader(sl, &fl, addr + off, base + off, size) == -1) { - ELOG("run_flash_loader(%#zx) failed! == -1\n", addr + off); - return -1; - } - - off += size; - } - - /* Relock flash */ - lock_flash(sl); - - } //STM32F4END - - else if (sl->flash_type == FLASH_TYPE_L0) { - /* use fast word write. todo: half page. */ - uint32_t val; - uint32_t flash_regs_base; - uint32_t pagesize; - - if (sl->chip_id == STM32_CHIPID_L0) { - flash_regs_base = STM32L0_FLASH_REGS_ADDR; - pagesize = L0_WRITE_BLOCK_SIZE; - } else { - flash_regs_base = STM32L_FLASH_REGS_ADDR; - pagesize = L1_WRITE_BLOCK_SIZE; - } - - /* todo: check write operation */ - - /* disable pecr protection */ - stlink_write_debug32(sl, flash_regs_base + FLASH_PEKEYR_OFF, 0x89abcdef); - stlink_write_debug32(sl, flash_regs_base + FLASH_PEKEYR_OFF, 0x02030405); - - /* check pecr.pelock is cleared */ - stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); - if (val & (1 << 0)) { - fprintf(stderr, "pecr.pelock not clear\n"); - return -1; - } - - /* unlock program memory */ - stlink_write_debug32(sl, flash_regs_base + FLASH_PRGKEYR_OFF, 0x8c9daebf); - stlink_write_debug32(sl, flash_regs_base + FLASH_PRGKEYR_OFF, 0x13141516); - - /* check pecr.prglock is cleared */ - stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); - if (val & (1 << 1)) { - fprintf(stderr, "pecr.prglock not clear\n"); - return -1; - } - off = 0; - if (len > pagesize) { - if (stm32l1_write_half_pages(sl, addr, base, len, pagesize) == -1) { - /* This may happen on a blank device! */ - WLOG("\nwrite_half_pages failed == -1\n"); - } else { - off = (len / pagesize)*pagesize; - } - } - - /* write remainingword in program memory */ - for ( ; off < len; off += sizeof(uint32_t)) { - uint32_t data; - if (off > 254) - fprintf(stdout, "\r"); - - if ((off % sl->flash_pgsz) > (sl->flash_pgsz -5)) { - fprintf(stdout, "\r%3zd/%3zd pages written", - off/sl->flash_pgsz, len/sl->flash_pgsz); - fflush(stdout); - } - - write_uint32((unsigned char*) &data, *(uint32_t*) (base + off)); - stlink_write_debug32(sl, addr + off, data); - - /* wait for sr.busy to be cleared */ - do { - stlink_read_debug32(sl, flash_regs_base + FLASH_SR_OFF, &val); - } while ((val & (1 << 0)) != 0); - - /* todo: check redo write operation */ - - } - fprintf(stdout, "\n"); - /* reset lock bits */ - stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF, &val); - val |= (1 << 0) | (1 << 1) | (1 << 2); - stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val); - } else if (sl->flash_type == FLASH_TYPE_F0) { - ILOG("Starting Flash write for VL/F0/F3 core id\n"); - /* flash loader initialization */ - if (init_flash_loader(sl, &fl) == -1) { - ELOG("init_flash_loader() == -1\n"); - return -1; - } - - int write_block_count = 0; - for (off = 0; off < len; off += sl->flash_pgsz) { - /* adjust last write size */ - size_t size = sl->flash_pgsz; - if ((off + sl->flash_pgsz) > len) size = len - off; - - /* unlock and set programming mode */ - unlock_flash_if(sl); - set_flash_cr_pg(sl); - //DLOG("Finished setting flash cr pg, running loader!\n"); - if (run_flash_loader(sl, &fl, addr + off, base + off, size) == -1) { - ELOG("run_flash_loader(%#zx) failed! == -1\n", addr + off); - return -1; - } - lock_flash(sl); - if (sl->verbose >= 1) { - /* show progress. writing procedure is slow - and previous errors are misleading */ - fprintf(stdout, "\r%3u/%lu pages written", write_block_count++, (unsigned long)len/sl->flash_pgsz); - fflush(stdout); - } - } - fprintf(stdout, "\n"); - } else { - ELOG("unknown coreid, not sure how to write: %x\n", sl->core_id); - return -1; - } - - return stlink_verify_write_flash(sl, addr, base, len); -} - -/** - * Write the given binary file into flash at address "addr" - * @param sl - * @param path readable file path, should be binary image - * @param addr where to start writing - * @return 0 on success, -ve on failure. - */ -int stlink_fwrite_flash(stlink_t *sl, const char* path, stm32_addr_t addr) { - /* write the file in flash at addr */ - int err; - unsigned int num_empty, index, val; - unsigned char erased_pattern; - mapped_file_t mf = MAPPED_FILE_INITIALIZER; - - if (map_file(&mf, path) == -1) { - ELOG("map_file() == -1\n"); - return -1; - } - - if (sl->flash_type == FLASH_TYPE_L0) - erased_pattern = 0x00; - else - erased_pattern = 0xff; - - index = mf.len; - for(num_empty = 0; num_empty != mf.len; ++num_empty) { - if (mf.base[--index] != erased_pattern) { - break; - } - } - /* Round down to words */ - num_empty -= (num_empty & 3); - if(num_empty != 0) { - ILOG("Ignoring %d bytes of 0x%02x at end of file\n", num_empty, erased_pattern); - } - err = stlink_write_flash(sl, addr, mf.base, num_empty == mf.len? mf.len : mf.len - num_empty, num_empty == mf.len); - /* set stack*/ - stlink_read_debug32(sl, addr, &val); - stlink_write_reg(sl, val, 13); - /* Set PC to the reset routine*/ - stlink_read_debug32(sl, addr + 4, &val); - stlink_write_reg(sl, val, 15); - stlink_run(sl); - unmap_file(&mf); - return err; -} - -int run_flash_loader(stlink_t *sl, flash_loader_t* fl, stm32_addr_t target, const uint8_t* buf, size_t size) { - - reg rr; - int i = 0; - size_t count = 0; - - DLOG("Running flash loader, write address:%#x, size: %zd\n", target, size); - // FIXME This can never return -1 - if (write_buffer_to_sram(sl, fl, buf, size) == -1) { - // IMPOSSIBLE! - ELOG("write_buffer_to_sram() == -1\n"); - return -1; - } - - if (sl->flash_type == FLASH_TYPE_F0) { - count = size / sizeof(uint16_t); - if (size % sizeof(uint16_t)) - ++count; - } else if (sl->flash_type == FLASH_TYPE_F4 || sl->flash_type == FLASH_TYPE_L0) { - count = size / sizeof(uint32_t); - if (size % sizeof(uint32_t)) - ++count; - } else if (sl->flash_type == FLASH_TYPE_L4) { - count = size / sizeof(uint64_t); - if (size % sizeof(uint64_t)) - ++count; - } - - /* setup core */ - stlink_write_reg(sl, fl->buf_addr, 0); /* source */ - stlink_write_reg(sl, target, 1); /* target */ - stlink_write_reg(sl, count, 2); /* count */ - stlink_write_reg(sl, 0, 3); /* flash bank 0 (input), only used on F0, but armless fopr others */ - stlink_write_reg(sl, fl->loader_addr, 15); /* pc register */ - - /* run loader */ - stlink_run(sl); - -#define WAIT_ROUNDS 10000 - /* wait until done (reaches breakpoint) */ - for (i = 0; i < WAIT_ROUNDS; i++) { - usleep(10); - if (is_core_halted(sl)) - break; - } - - if (i >= WAIT_ROUNDS) { - ELOG("flash loader run error\n"); - return -1; - } - - /* check written byte count */ - stlink_read_reg(sl, 2, &rr); - if (rr.r[2] != 0) { - fprintf(stderr, "write error, count == %u\n", rr.r[2]); - return -1; - } - - return 0; -} diff --git a/src/stlink-common.h b/src/stlink-common.h deleted file mode 100644 index e2f7b75..0000000 --- a/src/stlink-common.h +++ /dev/null @@ -1,742 +0,0 @@ -/* - * File: stlink-common.h - * Bulk import from stlink-hw.h - * - * This should contain all the common top level stlink interfaces, regardless - * of how the backend does the work.... - */ - -#ifndef STLINK_COMMON_H -#define STLINK_COMMON_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - - // Max data transfer size. - // 6kB = max mem32_read block, 8kB sram - //#define Q_BUF_LEN 96 -#define Q_BUF_LEN (1024 * 100) - - // st-link vendor cmd's -#define USB_ST_VID 0x0483 -#define USB_STLINK_PID 0x3744 -#define USB_STLINK_32L_PID 0x3748 -#define USB_STLINK_NUCLEO_PID 0x374b - - // STLINK_DEBUG_RESETSYS, etc: -#define STLINK_OK 0x80 -#define STLINK_FALSE 0x81 -#define STLINK_CORE_RUNNING 0x80 -#define STLINK_CORE_HALTED 0x81 -#define STLINK_CORE_STAT_UNKNOWN -1 - -#define STLINK_GET_VERSION 0xf1 -#define STLINK_GET_CURRENT_MODE 0xf5 -#define STLINK_GET_TARGET_VOLTAGE 0xF7 - -#define STLINK_DEBUG_COMMAND 0xF2 -#define STLINK_DFU_COMMAND 0xF3 -#define STLINK_DFU_EXIT 0x07 - // enter dfu could be 0x08? - - // STLINK_GET_CURRENT_MODE -#define STLINK_DEV_DFU_MODE 0x00 -#define STLINK_DEV_MASS_MODE 0x01 -#define STLINK_DEV_DEBUG_MODE 0x02 -#define STLINK_DEV_UNKNOWN_MODE -1 - - // jtag mode cmds -#define STLINK_DEBUG_ENTER 0x20 -#define STLINK_DEBUG_EXIT 0x21 -#define STLINK_DEBUG_READCOREID 0x22 -#define STLINK_DEBUG_GETSTATUS 0x01 -#define STLINK_DEBUG_FORCEDEBUG 0x02 -#define STLINK_DEBUG_RESETSYS 0x03 -#define STLINK_DEBUG_READALLREGS 0x04 -#define STLINK_DEBUG_READREG 0x05 -#define STLINK_DEBUG_WRITEREG 0x06 -#define STLINK_DEBUG_READMEM_32BIT 0x07 -#define STLINK_DEBUG_WRITEMEM_32BIT 0x08 -#define STLINK_DEBUG_RUNCORE 0x09 -#define STLINK_DEBUG_STEPCORE 0x0a -#define STLINK_DEBUG_SETFP 0x0b -#define STLINK_DEBUG_WRITEMEM_8BIT 0x0d -#define STLINK_DEBUG_CLEARFP 0x0e -#define STLINK_DEBUG_WRITEDEBUGREG 0x0f -#define STLINK_DEBUG_ENTER_SWD 0xa3 -#define STLINK_DEBUG_ENTER_JTAG 0x00 - - // TODO - possible poor names... -#define STLINK_SWD_ENTER 0x30 -#define STLINK_SWD_READCOREID 0x32 // TBD -#define STLINK_JTAG_WRITEDEBUG_32BIT 0x35 -#define STLINK_JTAG_READDEBUG_32BIT 0x36 -#define STLINK_JTAG_DRIVE_NRST 0x3c -#define STLINK_JTAG_DRIVE_NRST 0x3c - - // cortex m3 technical reference manual -#define CM3_REG_CPUID 0xE000ED00 -#define CM3_REG_FP_CTRL 0xE0002000 -#define CM3_REG_FP_COMP0 0xE0002008 - - /* cortex core ids */ - // TODO clean this up... -#define STM32VL_CORE_ID 0x1ba01477 -#define STM32L_CORE_ID 0x2ba01477 -#define STM32F3_CORE_ID 0x2ba01477 -#define STM32F4_CORE_ID 0x2ba01477 -#define STM32F0_CORE_ID 0xbb11477 -#define CORE_M3_R1 0x1BA00477 -#define CORE_M3_R2 0x4BA00477 -#define CORE_M4_R0 0x2BA01477 - - /* - * Chip IDs are explained in the appropriate programming manual for the - * DBGMCU_IDCODE register (0xE0042000) - */ - // stm32 chipids, only lower 12 bits.. -#define STM32_CHIPID_F1_MEDIUM 0x410 -#define STM32_CHIPID_F2 0x411 -#define STM32_CHIPID_F1_LOW 0x412 -#define STM32_CHIPID_F4 0x413 -#define STM32_CHIPID_F1_HIGH 0x414 -#define STM32_CHIPID_L4 0x415 /* Seen on L4x6 (RM0351) */ -#define STM32_CHIPID_L1_MEDIUM 0x416 -#define STM32_CHIPID_L0 0x417 -#define STM32_CHIPID_F1_CONN 0x418 -#define STM32_CHIPID_F4_HD 0x419 -#define STM32_CHIPID_F1_VL_MEDIUM_LOW 0x420 - -#define STM32_CHIPID_F446 0x421 -#define STM32_CHIPID_F3 0x422 -#define STM32_CHIPID_F4_LP 0x423 - -#define STM32_CHIPID_F411RE 0x431 - -#define STM32_CHIPID_L1_MEDIUM_PLUS 0x427 -#define STM32_CHIPID_F1_VL_HIGH 0x428 -#define STM32_CHIPID_L1_CAT2 0x429 - -#define STM32_CHIPID_F1_XL 0x430 - -#define STM32_CHIPID_F37x 0x432 -#define STM32_CHIPID_F4_DE 0x433 -#define STM32_CHIPID_F4_DE 0x433 - -#define STM32_CHIPID_F4_DSI 0x434 - -#define STM32_CHIPID_L1_HIGH 0x436 -#define STM32_CHIPID_L152_RE 0x437 -#define STM32_CHIPID_F334 0x438 - -#define STM32_CHIPID_F3_SMALL 0x439 -#define STM32_CHIPID_F0 0x440 -#define STM32_CHIPID_F09X 0x442 -#define STM32_CHIPID_F0_SMALL 0x444 - -#define STM32_CHIPID_F04 0x445 - -#define STM32_CHIPID_F303_HIGH 0x446 - -#define STM32_CHIPID_F0_CAN 0x448 - -#define STM32_CHIPID_F7 0x449 - - /* - * 0x436 is actually assigned to some L1 chips that are called "Medium-Plus" - * and some that are called "High". 0x427 is assigned to the other "Medium- - * plus" chips. To make it a bit simpler we just call 427 MEDIUM_PLUS and - * 0x436 HIGH. - */ - - // Constant STM32 memory map figures -#define STM32_FLASH_BASE 0x08000000 -#define STM32_SRAM_BASE 0x20000000 - - /* Cortex™-M3 Technical Reference Manual */ - /* Debug Halting Control and Status Register */ -#define DHCSR 0xe000edf0 -#define DCRSR 0xe000edf4 -#define DCRDR 0xe000edf8 -#define DBGKEY 0xa05f0000 - - /* Enough space to hold both a V2 command or a V1 command packaged as generic scsi*/ -#define C_BUF_LEN 32 - - enum flash_type { - FLASH_TYPE_UNKNOWN = 0, - FLASH_TYPE_F0, - FLASH_TYPE_L0, - FLASH_TYPE_F4, - FLASH_TYPE_L4, - }; - - typedef struct chip_params_ { - uint32_t chip_id; - char* description; - enum flash_type flash_type; - uint32_t flash_size_reg; - uint32_t flash_pagesize; - uint32_t sram_size; - uint32_t bootrom_base, bootrom_size; - } chip_params_t; - - - // These maps are from a combination of the Programming Manuals, and - // also the Reference manuals. (flash size reg is normally in ref man) - static const chip_params_t devices[] = { - { - //RM0385 and DS10916 document was used to find these paramaters - .chip_id = STM32_CHIPID_F7, - .description = "F7 device", - .flash_type = FLASH_TYPE_F4, - .flash_size_reg = 0x1ff0f442, // section 41.2 - .flash_pagesize = 0x800, // No flash pages - .sram_size = 0x50000, // "SRAM" byte size in hex from DS Fig 18 - .bootrom_base = 0x00100000, // "System memory" starting address from DS Fig 18 - .bootrom_size = 0xEDC0 // "System memory" byte size in hex from DS Fig 18 - }, - { // table 2, PM0063 - .chip_id = STM32_CHIPID_F1_MEDIUM, - .description = "F1 Medium-density device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7e0, - .flash_pagesize = 0x400, - .sram_size = 0x5000, - .bootrom_base = 0x1ffff000, - .bootrom_size = 0x800 - }, - { // table 1, PM0059 - .chip_id = STM32_CHIPID_F2, - .description = "F2 device", - .flash_type = FLASH_TYPE_F4, - .flash_size_reg = 0x1fff7a22, /* As in RM0033 Rev 5*/ - .flash_pagesize = 0x20000, - .sram_size = 0x20000, - .bootrom_base = 0x1fff0000, - .bootrom_size = 0x7800 - }, - { // PM0063 - .chip_id = STM32_CHIPID_F1_LOW, - .description = "F1 Low-density device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7e0, - .flash_pagesize = 0x400, - .sram_size = 0x2800, - .bootrom_base = 0x1ffff000, - .bootrom_size = 0x800 - }, - { - .chip_id = STM32_CHIPID_F4, - .description = "F4 device", - .flash_type = FLASH_TYPE_F4, - .flash_size_reg = 0x1FFF7A22, /* As in rm0090 since Rev 2*/ - .flash_pagesize = 0x4000, - .sram_size = 0x30000, - .bootrom_base = 0x1fff0000, - .bootrom_size = 0x7800 - }, - { - .chip_id = STM32_CHIPID_F4_DSI, - .description = "F46x and F47x device", - .flash_type = FLASH_TYPE_F4, - .flash_size_reg = 0x1FFF7A22, /* As in rm0090 since Rev 2*/ - .flash_pagesize = 0x4000, - .sram_size = 0x40000, - .bootrom_base = 0x1fff0000, - .bootrom_size = 0x7800 - }, - { - .chip_id = STM32_CHIPID_F4_HD, - .description = "F42x and F43x device", - .flash_type = FLASH_TYPE_F4, - .flash_size_reg = 0x1FFF7A22, /* As in rm0090 since Rev 2*/ - .flash_pagesize = 0x4000, - .sram_size = 0x40000, - .bootrom_base = 0x1fff0000, - .bootrom_size = 0x7800 - }, - { - .chip_id = STM32_CHIPID_F4_LP, - .description = "F4 device (low power)", - .flash_type = FLASH_TYPE_F4, - .flash_size_reg = 0x1FFF7A22, - .flash_pagesize = 0x4000, - .sram_size = 0x10000, - .bootrom_base = 0x1fff0000, - .bootrom_size = 0x7800 - }, - { - .chip_id = STM32_CHIPID_F411RE, - .description = "F4 device (low power) - stm32f411re", - .flash_type = FLASH_TYPE_F4, - .flash_size_reg = 0x1FFF7A22, - .flash_pagesize = 0x4000, - .sram_size = 0x20000, - .bootrom_base = 0x1fff0000, - .bootrom_size = 0x7800 - }, - { - .chip_id = STM32_CHIPID_F4_DE, - .description = "F4 device (Dynamic Efficency)", - .flash_type = FLASH_TYPE_F4, - .flash_size_reg = 0x1FFF7A22, - .flash_pagesize = 0x4000, - .sram_size = 0x18000, - .bootrom_base = 0x1fff0000, - .bootrom_size = 0x7800 - }, - { - .chip_id = STM32_CHIPID_F1_HIGH, - .description = "F1 High-density device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7e0, - .flash_pagesize = 0x800, - .sram_size = 0x10000, - .bootrom_base = 0x1ffff000, - .bootrom_size = 0x800 - }, - { - // This ignores the EEPROM! (and uses the page erase size, - // not the sector write protection...) - .chip_id = STM32_CHIPID_L1_MEDIUM, - .description = "L1 Med-density device", - .flash_type = FLASH_TYPE_L0, - .flash_size_reg = 0x1ff8004c, - .flash_pagesize = 0x100, - .sram_size = 0x4000, - .bootrom_base = 0x1ff00000, - .bootrom_size = 0x1000 - }, - { - .chip_id = STM32_CHIPID_L1_CAT2, - .description = "L1 Cat.2 device", - .flash_type = FLASH_TYPE_L0, - .flash_size_reg = 0x1ff8004c, - .flash_pagesize = 0x100, - .sram_size = 0x8000, - .bootrom_base = 0x1ff00000, - .bootrom_size = 0x1000 - }, - { - .chip_id = STM32_CHIPID_L1_MEDIUM_PLUS, - .description = "L1 Medium-Plus-density device", - .flash_type = FLASH_TYPE_L0, - .flash_size_reg = 0x1ff800cc, - .flash_pagesize = 0x100, - .sram_size = 0x8000,/*Not completely clear if there are some with 48K*/ - .bootrom_base = 0x1ff00000, - .bootrom_size = 0x1000 - }, - { - .chip_id = STM32_CHIPID_L1_HIGH, - .description = "L1 High-density device", - .flash_type = FLASH_TYPE_L0, - .flash_size_reg = 0x1ff800cc, - .flash_pagesize = 0x100, - .sram_size = 0xC000, /*Not completely clear if there are some with 32K*/ - .bootrom_base = 0x1ff00000, - .bootrom_size = 0x1000 - }, - { - .chip_id = STM32_CHIPID_L152_RE, - .description = "L152RE", - .flash_type = FLASH_TYPE_L0, - .flash_size_reg = 0x1ff800cc, - .flash_pagesize = 0x100, - .sram_size = 0x14000, /*Not completely clear if there are some with 32K*/ - .bootrom_base = 0x1ff00000, - .bootrom_size = 0x1000 - }, - { - .chip_id = STM32_CHIPID_F1_CONN, - .description = "F1 Connectivity line device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7e0, - .flash_pagesize = 0x800, - .sram_size = 0x10000, - .bootrom_base = 0x1fffb000, - .bootrom_size = 0x4800 - }, - {//Low and Medium density VL have same chipid. RM0041 25.6.1 - .chip_id = STM32_CHIPID_F1_VL_MEDIUM_LOW, - .description = "F1 Medium/Low-density Value Line device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7e0, - .flash_pagesize = 0x400, - .sram_size = 0x2000,//0x1000 for low density devices - .bootrom_base = 0x1ffff000, - .bootrom_size = 0x800 - }, - { - // STM32F446x family. Support based on DM00135183.pdf (RM0390) document. - .chip_id = STM32_CHIPID_F446, - .description = "F446 device", - .flash_type = FLASH_TYPE_F4, - .flash_size_reg = 0x1fff7a22, - .flash_pagesize = 0x20000, - .sram_size = 0x20000, - .bootrom_base = 0x1fff0000, - .bootrom_size = 0x7800 - }, - { - // This is STK32F303VCT6 device from STM32 F3 Discovery board. - // Support based on DM00043574.pdf (RM0316) document. - .chip_id = STM32_CHIPID_F3, - .description = "F3 device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7cc, - .flash_pagesize = 0x800, - .sram_size = 0xa000, - .bootrom_base = 0x1ffff000, - .bootrom_size = 0x800 - }, - { - // This is STK32F373VCT6 device from STM32 F373 eval board - // Support based on 303 above (37x and 30x have same memory map) - .chip_id = STM32_CHIPID_F37x, - .description = "F3 device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7cc, - .flash_pagesize = 0x800, - .sram_size = 0xa000, - .bootrom_base = 0x1ffff000, - .bootrom_size = 0x800 - }, - { - .chip_id = STM32_CHIPID_F1_VL_HIGH, - .description = "F1 High-density value line device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7e0, - .flash_pagesize = 0x800, - .sram_size = 0x8000, - .bootrom_base = 0x1ffff000, - .bootrom_size = 0x800 - }, - { - .chip_id = STM32_CHIPID_F1_XL, - .description = "F1 XL-density device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7e0, - .flash_pagesize = 0x800, - .sram_size = 0x18000, - .bootrom_base = 0x1fffe000, - .bootrom_size = 0x1800 - }, - { - //Use this as an example for mapping future chips: - //RM0091 document was used to find these paramaters - .chip_id = STM32_CHIPID_F0_CAN, - .description = "F07x device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7cc, // "Flash size data register" (pg735) - .flash_pagesize = 0x800, // Page sizes listed in Table 4 - .sram_size = 0x4000, // "SRAM" byte size in hex from Table 2 - .bootrom_base = 0x1fffC800, // "System memory" starting address from Table 2 - .bootrom_size = 0x3000 // "System memory" byte size in hex from Table 2 - }, - { - //Use this as an example for mapping future chips: - //RM0091 document was used to find these paramaters - .chip_id = STM32_CHIPID_F0, - .description = "F0 device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7cc, // "Flash size data register" (pg735) - .flash_pagesize = 0x400, // Page sizes listed in Table 4 - .sram_size = 0x2000, // "SRAM" byte size in hex from Table 2 - .bootrom_base = 0x1fffec00, // "System memory" starting address from Table 2 - .bootrom_size = 0xC00 // "System memory" byte size in hex from Table 2 - }, - { - .chip_id = STM32_CHIPID_F09X, - .description = "F09X device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7cc, // "Flash size data register" (pg735) - .flash_pagesize = 0x800, // Page sizes listed in Table 4 (pg 56) - .sram_size = 0x8000, // "SRAM" byte size in hex from Table 2 (pg 50) - .bootrom_base = 0x1fffd800, // "System memory" starting address from Table 2 - .bootrom_size = 0x2000 // "System memory" byte size in hex from Table 2 - }, - { - //Use this as an example for mapping future chips: - //RM0091 document was used to find these paramaters - .chip_id = STM32_CHIPID_F04, - .description = "F04x device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7cc, // "Flash size data register" (pg735) - .flash_pagesize = 0x400, // Page sizes listed in Table 4 - .sram_size = 0x1800, // "SRAM" byte size in hex from Table 2 - .bootrom_base = 0x1fffec00, // "System memory" starting address from Table 2 - .bootrom_size = 0xC00 // "System memory" byte size in hex from Table 2 - }, - { - //Use this as an example for mapping future chips: - //RM0091 document was used to find these paramaters - .chip_id = STM32_CHIPID_F0_SMALL, - .description = "F0 small device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7cc, // "Flash size data register" (pg735) - .flash_pagesize = 0x400, // Page sizes listed in Table 4 - .sram_size = 0x1000, // "SRAM" byte size in hex from Table 2 - .bootrom_base = 0x1fffec00, // "System memory" starting address from Table 2 - .bootrom_size = 0xC00 // "System memory" byte size in hex from Table 2 - }, - { - // STM32F30x - .chip_id = STM32_CHIPID_F3_SMALL, - .description = "F3 small device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7cc, - .flash_pagesize = 0x800, - .sram_size = 0xa000, - .bootrom_base = 0x1fffd800, - .bootrom_size = 0x2000 - }, - { - // STM32L0x - // RM0367,RM0377 documents was used to find these parameters - .chip_id = STM32_CHIPID_L0, - .description = "L0x3 device", - .flash_type = FLASH_TYPE_L0, - .flash_size_reg = 0x1ff8007c, - .flash_pagesize = 0x80, - .sram_size = 0x2000, - .bootrom_base = 0x1ff0000, - .bootrom_size = 0x1000 - }, - { - // STM32F334 - // RM0364 document was used to find these parameters - .chip_id = STM32_CHIPID_F334, - .description = "F334 device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7cc, - .flash_pagesize = 0x800, - .sram_size = 0x3000, - .bootrom_base = 0x1fffd800, - .bootrom_size = 0x2000 - }, - { - // This is STK32F303RET6 device from STM32 F3 Nucelo board. - // Support based on DM00043574.pdf (RM0316) document rev 5. - .chip_id = STM32_CHIPID_F303_HIGH, - .description = "F303 high density device", - .flash_type = FLASH_TYPE_F0, - .flash_size_reg = 0x1ffff7cc, // 34.2.1 Flash size data register - .flash_pagesize = 0x800, // 4.2.1 Flash memory organization - .sram_size = 0x10000, // 3.3 Embedded SRAM - .bootrom_base = 0x1fffd800, // 3.3.2 / Table 4 System Memory - .bootrom_size = 0x2000 - }, - { - // STM32L4x6 - // From RM0351. - .chip_id = STM32_CHIPID_L4, - .description = "L4 device", - .flash_type = FLASH_TYPE_L4, - .flash_size_reg = 0x1fff75e0, // "Flash size data register" (sec 45.2, page 1671) - .flash_pagesize = 0x800, // 2K (sec 3.2, page 78; also appears in sec 3.3.1 and tables 4-6 on pages 79-81) - // SRAM1 is "up to" 96k in the standard Cortex-M memory map; - // SRAM2 is 32k mapped at at 0x10000000 (sec 2.3, page 73 for - // sizes; table 2, page 74 for SRAM2 location) - .sram_size = 0x18000, - .bootrom_base = 0x1fff0000, // Tables 4-6, pages 80-81 (Bank 1 system memory) - .bootrom_size = 0x7000 // 28k (per bank), same source as base - }, - - }; - - - typedef struct { - uint32_t r[16]; - uint32_t s[32]; - uint32_t xpsr; - uint32_t main_sp; - uint32_t process_sp; - uint32_t rw; - uint32_t rw2; - uint8_t control; - uint8_t faultmask; - uint8_t basepri; - uint8_t primask; - uint32_t fpscr; - } reg; - - typedef uint32_t stm32_addr_t; - - typedef struct _cortex_m3_cpuid_ { - uint16_t implementer_id; - uint16_t variant; - uint16_t part; - uint8_t revision; - } cortex_m3_cpuid_t; - - typedef struct stlink_version_ { - uint32_t stlink_v; - uint32_t jtag_v; - uint32_t swim_v; - uint32_t st_vid; - uint32_t stlink_pid; - } stlink_version_t; - - typedef struct flash_loader { - stm32_addr_t loader_addr; /* loader sram adddr */ - stm32_addr_t buf_addr; /* buffer sram address */ - } flash_loader_t; - - enum transport_type { - TRANSPORT_TYPE_ZERO = 0, - TRANSPORT_TYPE_LIBSG, - TRANSPORT_TYPE_LIBUSB, - TRANSPORT_TYPE_INVALID - }; - - typedef struct _stlink stlink_t; - - typedef struct _stlink_backend { - void (*close) (stlink_t * sl); - int (*exit_debug_mode) (stlink_t * sl); - int (*enter_swd_mode) (stlink_t * sl); - int (*enter_jtag_mode) (stlink_t * stl); - int (*exit_dfu_mode) (stlink_t * stl); - int (*core_id) (stlink_t * stl); - int (*reset) (stlink_t * stl); - int (*jtag_reset) (stlink_t * stl, int value); - int (*run) (stlink_t * stl); - int (*status) (stlink_t * stl); - int (*version) (stlink_t *sl); - int (*read_debug32) (stlink_t *sl, uint32_t addr, uint32_t *data); - int (*read_mem32) (stlink_t *sl, uint32_t addr, uint16_t len); - int (*write_debug32) (stlink_t *sl, uint32_t addr, uint32_t data); - int (*write_mem32) (stlink_t *sl, uint32_t addr, uint16_t len); - int (*write_mem8) (stlink_t *sl, uint32_t addr, uint16_t len); - int (*read_all_regs) (stlink_t *sl, reg * regp); - int (*read_reg) (stlink_t *sl, int r_idx, reg * regp); - int (*read_all_unsupported_regs) (stlink_t *sl, reg *regp); - int (*read_unsupported_reg) (stlink_t *sl, int r_idx, reg *regp); - int (*write_unsupported_reg) (stlink_t *sl, uint32_t value, int idx, reg *regp); - int (*write_reg) (stlink_t *sl, uint32_t reg, int idx); - int (*step) (stlink_t * stl); - int (*current_mode) (stlink_t * stl); - int (*force_debug) (stlink_t *sl); - int32_t (*target_voltage) (stlink_t *sl); - } stlink_backend_t; - - struct _stlink { - struct _stlink_backend *backend; - void *backend_data; - - // Room for the command header - unsigned char c_buf[C_BUF_LEN]; - // Data transferred from or to device - unsigned char q_buf[Q_BUF_LEN]; - int q_len; - - // transport layer verboseness: 0 for no debug info, 10 for lots - int verbose; - uint32_t core_id; - uint32_t chip_id; - int core_stat; - - char serial[16]; - int serial_size; - -#define STM32_FLASH_PGSZ 1024 -#define STM32L_FLASH_PGSZ 256 - -#define STM32F4_FLASH_PGSZ 16384 -#define STM32F4_FLASH_SIZE (128 * 1024 * 8) - - enum flash_type flash_type; - stm32_addr_t flash_base; - size_t flash_size; - size_t flash_pgsz; - - /* sram settings */ -#define STM32_SRAM_SIZE (8 * 1024) -#define STM32L_SRAM_SIZE (16 * 1024) - stm32_addr_t sram_base; - size_t sram_size; - - // bootloader - stm32_addr_t sys_base; - size_t sys_size; - - struct stlink_version_ version; - }; - - //stlink_t* stlink_quirk_open(const char *dev_name, const int verbose); - - // delegated functions... - int stlink_enter_swd_mode(stlink_t *sl); - int stlink_enter_jtag_mode(stlink_t *sl); - int stlink_exit_debug_mode(stlink_t *sl); - int stlink_exit_dfu_mode(stlink_t *sl); - void stlink_close(stlink_t *sl); - int stlink_core_id(stlink_t *sl); - int stlink_reset(stlink_t *sl); - int stlink_jtag_reset(stlink_t *sl, int value); - int stlink_run(stlink_t *sl); - int stlink_status(stlink_t *sl); - int stlink_version(stlink_t *sl); - int stlink_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data); - int stlink_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len); - int stlink_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data); - int stlink_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len); - int stlink_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len); - int stlink_read_all_regs(stlink_t *sl, reg *regp); - int stlink_read_all_unsupported_regs(stlink_t *sl, reg *regp); - int stlink_read_reg(stlink_t *sl, int r_idx, reg *regp); - int stlink_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp); - int stlink_write_unsupported_reg(stlink_t *sl, uint32_t value, int r_idx, reg *regp); - int stlink_write_reg(stlink_t *sl, uint32_t reg, int idx); - int stlink_step(stlink_t *sl); - int stlink_current_mode(stlink_t *sl); - int stlink_force_debug(stlink_t *sl); - int stlink_target_voltage(stlink_t *sl); - - - // unprocessed - int stlink_erase_flash_mass(stlink_t* sl); - int stlink_write_flash(stlink_t* sl, stm32_addr_t address, uint8_t* data, uint32_t length, uint8_t eraseonly); - int stlink_fwrite_flash(stlink_t *sl, const char* path, stm32_addr_t addr); - int stlink_fwrite_sram(stlink_t *sl, const char* path, stm32_addr_t addr); - int stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, uint32_t length); - - // PUBLIC - int stlink_chip_id(stlink_t *sl, uint32_t *chip_id); - int stlink_cpu_id(stlink_t *sl, cortex_m3_cpuid_t *cpuid); - - // privates, publics, the rest.... - // TODO sort what is private, and what is not - int stlink_erase_flash_page(stlink_t* sl, stm32_addr_t flashaddr); - uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr); - uint16_t read_uint16(const unsigned char *c, const int pt); - void stlink_core_stat(stlink_t *sl); - void stlink_print_data(stlink_t *sl); - unsigned int is_bigendian(void); - uint32_t read_uint32(const unsigned char *c, const int pt); - void write_uint32(unsigned char* buf, uint32_t ui); - void write_uint16(unsigned char* buf, uint16_t ui); - unsigned int is_core_halted(stlink_t *sl); - int write_buffer_to_sram(stlink_t *sl, flash_loader_t* fl, const uint8_t* buf, size_t size); - int write_loader_to_sram(stlink_t *sl, stm32_addr_t* addr, size_t* size); - int stlink_fread(stlink_t* sl, const char* path, stm32_addr_t addr, size_t size); - int run_flash_loader(stlink_t *sl, flash_loader_t* fl, stm32_addr_t target, const uint8_t* buf, size_t size); - int stlink_load_device_params(stlink_t *sl); - - - -#include "stlink-sg.h" -#include "stlink-usb.h" - - - -#ifdef __cplusplus -} -#endif - -#endif /* STLINK_COMMON_H */ diff --git a/src/stlink-sg.c b/src/stlink-sg.c deleted file mode 100644 index 03df7d8..0000000 --- a/src/stlink-sg.c +++ /dev/null @@ -1,1089 +0,0 @@ -/* - * Copyright (c) 2010 "Capt'ns Missing Link" Authors. All rights reserved. - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - * - * A linux stlink access demo. The purpose of this file is to mitigate the usual - * "reinventing the wheel" force by incompatible licenses and give you an idea, - * how to access the stlink device. That doesn't mean you should be a free-loader - * and not contribute your improvements to this code. - * - * Author: Martin Capitanio - * The stlink related constants kindly provided by Oliver Spencer (OpenOCD) - * for use in a GPL compatible license. - * - * Tested compatibility: linux, gcc >= 4.3.3 - * - * The communication is based on standard USB mass storage device - * BOT (Bulk Only Transfer) - * - Endpoint 1: BULK_IN, 64 bytes max - * - Endpoint 2: BULK_OUT, 64 bytes max - * - * All CBW transfers are ordered with the LSB (byte 0) first (little endian). - * Any command must be answered before sending the next command. - * Each USB transfer must complete in less than 1s. - * - * SB Device Class Definition for Mass Storage Devices: - * www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf - * - * dt - Data Transfer (IN/OUT) - * CBW - Command Block Wrapper - * CSW - Command Status Wrapper - * RFU - Reserved for Future Use - * - * Originally, this driver used scsi pass through commands, which required the - * usb-storage module to be loaded, providing the /dev/sgX links. The USB mass - * storage implementation on the STLinkv1 is however terribly broken, and it can - * take many minutes for the kernel to give up. - * - * However, in Nov 2011, the scsi pass through was replaced by raw libusb, so - * instead of having to let usb-storage struggle with the device, and also greatly - * limiting the portability of the driver, you can now tell usb-storage to simply - * ignore this device completely. - * - * usb-storage.quirks - * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob_plain;f=Documentation/kernel-parameters.txt - * Each entry has the form VID:PID:Flags where VID and PID are Vendor and Product - * ID values (4-digit hex numbers) and Flags is a set of characters, each corresponding - * to a common usb-storage quirk flag as follows: - * - * a = SANE_SENSE (collect more than 18 bytes of sense data); - * b = BAD_SENSE (don't collect more than 18 bytes of sense data); - * c = FIX_CAPACITY (decrease the reported device capacity by one sector); - * h = CAPACITY_HEURISTICS (decrease the reported device capacity by one sector if the number is odd); - * i = IGNORE_DEVICE (don't bind to this device); - * l = NOT_LOCKABLE (don't try to lock and unlock ejectable media); - * m = MAX_SECTORS_64 (don't transfer more than 64 sectors = 32 KB at a time); - * o = CAPACITY_OK (accept the capacity reported by the device); - * r = IGNORE_RESIDUE (the device reports bogus residue values); - * s = SINGLE_LUN (the device has only one Logical Unit); - * w = NO_WP_DETECT (don't test whether the medium is write-protected). - * - * Example: quirks=0419:aaf5:rl,0421:0433:rc - * http://permalink.gmane.org/gmane.linux.usb.general/35053 - * - * For the stlinkv1, you just want the following - * - * modprobe -r usb-storage && modprobe usb-storage quirks=483:3744:i - * - * Equivalently, you can add a line saying - * - * options usb-storage quirks=483:3744:i - * - * to your /etc/modprobe.conf or /etc/modprobe.d/local.conf (or add the "quirks=..." - * part to an existing options line for usb-storage). - */ - - -#define __USE_GNU -#include -#include -#include -#include -#include - -#include "stlink-common.h" -#include "stlink-sg.h" -#include "uglylogging.h" - -static void clear_cdb(struct stlink_libsg *sl) { - for (size_t i = 0; i < sizeof (sl->cdb_cmd_blk); i++) - sl->cdb_cmd_blk[i] = 0; - // set default - sl->cdb_cmd_blk[0] = STLINK_DEBUG_COMMAND; - sl->q_data_dir = Q_DATA_IN; -} - -/** - * Close and free any _backend_ related information... - * @param sl - */ -void _stlink_sg_close(stlink_t *sl) { - if (sl) { - struct stlink_libsg *slsg = sl->backend_data; - libusb_close(slsg->usb_handle); - libusb_exit(slsg->libusb_ctx); - free(slsg); - } -} - -static int get_usb_mass_storage_status(libusb_device_handle *handle, uint8_t endpoint, uint32_t *tag) -{ - unsigned char csw[13]; - memset(csw, 0, sizeof(csw)); - int transferred; - int ret; - int try = 0; - do { - ret = libusb_bulk_transfer(handle, endpoint, (unsigned char *)&csw, sizeof(csw), - &transferred, SG_TIMEOUT_MSEC); - if (ret == LIBUSB_ERROR_PIPE) { - libusb_clear_halt(handle, endpoint); - } - try++; - } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3)); - if (ret != LIBUSB_SUCCESS) { - WLOG("%s: receiving failed: %d\n", __func__, ret); - return -1; - } - if (transferred != sizeof(csw)) { - WLOG("%s: received unexpected amount: %d\n", __func__, transferred); - return -1; - } - - uint32_t rsig = read_uint32(csw, 0); - uint32_t rtag = read_uint32(csw, 4); - /* uint32_t residue = read_uint32(csw, 8); */ -#define USB_CSW_SIGNATURE 0x53425355 // 'U' 'S' 'B' 'S' (reversed) - if (rsig != USB_CSW_SIGNATURE) { - WLOG("status signature was invalid: %#x\n", rsig); - return -1; - } - *tag = rtag; - uint8_t rstatus = csw[12]; - return rstatus; -} - -static int dump_CDB_command(uint8_t *cdb, uint8_t cdb_len) { - char dbugblah[100]; - char *dbugp = dbugblah; - dbugp += sprintf(dbugp, "Sending CDB ["); - for (uint8_t i = 0; i < cdb_len; i++) { - dbugp += sprintf(dbugp, " %#02x", (unsigned int) cdb[i]); - } - sprintf(dbugp, "]\n"); - DLOG(dbugblah); - return 0; -} - -/** - * Wraps a CDB mass storage command in the appropriate gunk to get it down - * @param handle - * @param endpoint - * @param cdb - * @param cdb_length - * @param lun - * @param flags - * @param expected_rx_size - * @return - */ -int send_usb_mass_storage_command(libusb_device_handle *handle, uint8_t endpoint_out, - uint8_t *cdb, uint8_t cdb_length, - uint8_t lun, uint8_t flags, uint32_t expected_rx_size) { - DLOG("Sending usb m-s cmd: cdblen:%d, rxsize=%d\n", cdb_length, expected_rx_size); - dump_CDB_command(cdb, cdb_length); - - static uint32_t tag; - if (tag == 0) { - tag = 1; - } - - int try = 0; - int ret = 0; - int real_transferred; - int i = 0; - - uint8_t c_buf[STLINK_SG_SIZE]; - // tag is allegedly ignored... TODO - verify - c_buf[i++] = 'U'; - c_buf[i++] = 'S'; - c_buf[i++] = 'B'; - c_buf[i++] = 'C'; - write_uint32(&c_buf[i], tag); - uint32_t this_tag = tag++; - write_uint32(&c_buf[i+4], expected_rx_size); - i+= 8; - c_buf[i++] = flags; - c_buf[i++] = lun; - - c_buf[i++] = cdb_length; - - // Now the actual CDB request - assert(cdb_length <= CDB_SL); - memcpy(&(c_buf[i]), cdb, cdb_length); - - int sending_length = STLINK_SG_SIZE; - - // send.... - do { - ret = libusb_bulk_transfer(handle, endpoint_out, c_buf, sending_length, - &real_transferred, SG_TIMEOUT_MSEC); - if (ret == LIBUSB_ERROR_PIPE) { - libusb_clear_halt(handle, endpoint_out); - } - try++; - } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3)); - if (ret != LIBUSB_SUCCESS) { - WLOG("sending failed: %d\n", ret); - return -1; - } - return this_tag; -} - - -/** - * Straight from stm8 stlink code... - * @param handle - * @param endpoint_in - * @param endpoint_out - */ - static void -get_sense(libusb_device_handle *handle, uint8_t endpoint_in, uint8_t endpoint_out) -{ - DLOG("Fetching sense...\n"); - uint8_t cdb[16]; - memset(cdb, 0, sizeof(cdb)); -#define REQUEST_SENSE 0x03 -#define REQUEST_SENSE_LENGTH 18 - cdb[0] = REQUEST_SENSE; - cdb[4] = REQUEST_SENSE_LENGTH; - uint32_t tag = send_usb_mass_storage_command(handle, endpoint_out, cdb, sizeof(cdb), 0, - LIBUSB_ENDPOINT_IN, REQUEST_SENSE_LENGTH); - if (tag == 0) { - WLOG("refusing to send request sense with tag 0\n"); - return; - } - unsigned char sense[REQUEST_SENSE_LENGTH]; - int transferred; - int ret; - int try = 0; - do { - ret = libusb_bulk_transfer(handle, endpoint_in, sense, sizeof(sense), - &transferred, SG_TIMEOUT_MSEC); - if (ret == LIBUSB_ERROR_PIPE) { - libusb_clear_halt(handle, endpoint_in); - } - try++; - } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3)); - if (ret != LIBUSB_SUCCESS) { - WLOG("receiving sense failed: %d\n", ret); - return; - } - if (transferred != sizeof(sense)) { - WLOG("received unexpected amount of sense: %d != %d\n", transferred, sizeof(sense)); - } - uint32_t received_tag; - int status = get_usb_mass_storage_status(handle, endpoint_in, &received_tag); - if (status != 0) { - WLOG("receiving sense failed with status: %02x\n", status); - return; - } - if (sense[0] != 0x70 && sense[0] != 0x71) { - WLOG("No sense data\n"); - } else { - WLOG("Sense KCQ: %02X %02X %02X\n", sense[2] & 0x0f, sense[12], sense[13]); - } -} - -/** - * Just send a buffer on an endpoint, no questions asked. - * Handles repeats, and time outs. Also handles reading status reports and sense - * @param handle libusb device * - * @param endpoint_out sends - * @param endpoint_in used to read status reports back in - * @param cbuf what to send - * @param length how much to send - * @return number of bytes actually sent, or -1 for failures. - */ -int send_usb_data_only(libusb_device_handle *handle, unsigned char endpoint_out, - unsigned char endpoint_in, unsigned char *cbuf, unsigned int length) { - int ret; - int real_transferred; - int try = 0; - do { - ret = libusb_bulk_transfer(handle, endpoint_out, cbuf, length, - &real_transferred, SG_TIMEOUT_MSEC); - if (ret == LIBUSB_ERROR_PIPE) { - libusb_clear_halt(handle, endpoint_out); - } - try++; - } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3)); - if (ret != LIBUSB_SUCCESS) { - WLOG("sending failed: %d\n", ret); - return -1; - } - - // now, swallow up the status, so that things behave nicely... - uint32_t received_tag; - // -ve is for my errors, 0 is good, +ve is libusb sense status bytes - int status = get_usb_mass_storage_status(handle, endpoint_in, &received_tag); - if (status < 0) { - WLOG("receiving status failed: %d\n", status); - return -1; - } - if (status != 0) { - WLOG("receiving status not passed :(: %02x\n", status); - } - if (status == 1) { - get_sense(handle, endpoint_in, endpoint_out); - return -1; - } - - return real_transferred; -} - - -int stlink_q(stlink_t *sl) { - struct stlink_libsg* sg = sl->backend_data; - //uint8_t cdb_len = 6; // FIXME varies!!! - uint8_t cdb_len = 10; // FIXME varies!!! - uint8_t lun = 0; // always zero... - uint32_t tag = send_usb_mass_storage_command(sg->usb_handle, sg->ep_req, - sg->cdb_cmd_blk, cdb_len, lun, LIBUSB_ENDPOINT_IN, sl->q_len); - - - // now wait for our response... - // length copied from stlink-usb... - int rx_length = sl->q_len; - int try = 0; - int real_transferred; - int ret; - if (rx_length > 0) { - do { - ret = libusb_bulk_transfer(sg->usb_handle, sg->ep_rep, sl->q_buf, rx_length, - &real_transferred, SG_TIMEOUT_MSEC); - if (ret == LIBUSB_ERROR_PIPE) { - libusb_clear_halt(sg->usb_handle, sg->ep_req); - } - try++; - } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3)); - - if (ret != LIBUSB_SUCCESS) { - WLOG("Receiving failed: %d\n", ret); - return -1; - } - - if (real_transferred != rx_length) { - WLOG("received unexpected amount: %d != %d\n", real_transferred, rx_length); - } - } - - uint32_t received_tag; - // -ve is for my errors, 0 is good, +ve is libusb sense status bytes - int status = get_usb_mass_storage_status(sg->usb_handle, sg->ep_rep, &received_tag); - if (status < 0) { - WLOG("receiving status failed: %d\n", status); - return -1; - } - if (status != 0) { - WLOG("receiving status not passed :(: %02x\n", status); - } - if (status == 1) { - get_sense(sg->usb_handle, sg->ep_rep, sg->ep_req); - return -1; - } - if (received_tag != tag) { - WLOG("received tag %d but expected %d\n", received_tag, tag); - //return -1; - } - if (rx_length > 0 && real_transferred != rx_length) { - return -1; - } - return 0; -} - -// TODO thinking, cleanup - -void stlink_stat(stlink_t *stl, char *txt) { - if (stl->q_len <= 0) - return; - - stlink_print_data(stl); - - switch (stl->q_buf[0]) { - case STLINK_OK: - DLOG(" %s: ok\n", txt); - return; - case STLINK_FALSE: - DLOG(" %s: false\n", txt); - return; - default: - DLOG(" %s: unknown\n", txt); - } -} - - -int _stlink_sg_version(stlink_t *stl) { - struct stlink_libsg *sl = stl->backend_data; - clear_cdb(sl); - sl->cdb_cmd_blk[0] = STLINK_GET_VERSION; - stl->q_len = 6; - sl->q_addr = 0; - return stlink_q(stl); -} - -// Get stlink mode: -// STLINK_DEV_DFU_MODE || STLINK_DEV_MASS_MODE || STLINK_DEV_DEBUG_MODE -// usb dfu || usb mass || jtag or swd - -int _stlink_sg_current_mode(stlink_t *stl) { - struct stlink_libsg *sl = stl->backend_data; - clear_cdb(sl); - sl->cdb_cmd_blk[0] = STLINK_GET_CURRENT_MODE; - stl->q_len = 2; - sl->q_addr = 0; - if (stlink_q(stl)) - return -1; - - return stl->q_buf[0]; -} - -// Exit the mass mode and enter the swd debug mode. - -int _stlink_sg_enter_swd_mode(stlink_t *sl) { - struct stlink_libsg *sg = sl->backend_data; - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_ENTER; - sg->cdb_cmd_blk[2] = STLINK_DEBUG_ENTER_SWD; - sl->q_len = 0; // >0 -> aboard - return stlink_q(sl); -} - -// Exit the mass mode and enter the jtag debug mode. -// (jtag is disabled in the discovery's stlink firmware) - -int _stlink_sg_enter_jtag_mode(stlink_t *sl) { - struct stlink_libsg *sg = sl->backend_data; - DLOG("\n*** stlink_enter_jtag_mode ***\n"); - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_ENTER; - sg->cdb_cmd_blk[2] = STLINK_DEBUG_ENTER_JTAG; - sl->q_len = 0; - return stlink_q(sl); -} - -// XXX kernel driver performs reset, the device temporally disappears -// Suspect this is no longer the case when we have ignore on? RECHECK -int _stlink_sg_exit_dfu_mode(stlink_t *sl) { - struct stlink_libsg *sg = sl->backend_data; - DLOG("\n*** stlink_exit_dfu_mode ***\n"); - clear_cdb(sg); - sg->cdb_cmd_blk[0] = STLINK_DFU_COMMAND; - sg->cdb_cmd_blk[1] = STLINK_DFU_EXIT; - sl->q_len = 0; // ?? - return stlink_q(sl); - /* - [135121.844564] sd 19:0:0:0: [sdb] Unhandled error code - [135121.844569] sd 19:0:0:0: [sdb] Result: hostbyte=DID_ERROR driverbyte=DRIVER_OK - [135121.844574] sd 19:0:0:0: [sdb] CDB: Read(10): 28 00 00 00 10 00 00 00 08 00 - [135121.844584] end_request: I/O error, dev sdb, sector 4096 - [135121.844590] Buffer I/O error on device sdb, logical block 512 - [135130.122567] usb 6-1: reset full speed USB device using uhci_hcd and address 7 - [135130.274551] usb 6-1: device firmware changed - [135130.274618] usb 6-1: USB disconnect, address 7 - [135130.275186] VFS: busy inodes on changed media or resized disk sdb - [135130.275424] VFS: busy inodes on changed media or resized disk sdb - [135130.286758] VFS: busy inodes on changed media or resized disk sdb - [135130.292796] VFS: busy inodes on changed media or resized disk sdb - [135130.301481] VFS: busy inodes on changed media or resized disk sdb - [135130.304316] VFS: busy inodes on changed media or resized disk sdb - [135130.431113] usb 6-1: new full speed USB device using uhci_hcd and address 8 - [135130.629444] usb-storage 6-1:1.0: Quirks match for vid 0483 pid 3744: 102a1 - [135130.629492] scsi20 : usb-storage 6-1:1.0 - [135131.625600] scsi 20:0:0:0: Direct-Access STM32 PQ: 0 ANSI: 0 - [135131.627010] sd 20:0:0:0: Attached scsi generic sg2 type 0 - [135131.633603] sd 20:0:0:0: [sdb] 64000 512-byte logical blocks: (32.7 MB/31.2 MiB) - [135131.633613] sd 20:0:0:0: [sdb] Assuming Write Enabled - [135131.633620] sd 20:0:0:0: [sdb] Assuming drive cache: write through - [135131.640584] sd 20:0:0:0: [sdb] Assuming Write Enabled - [135131.640592] sd 20:0:0:0: [sdb] Assuming drive cache: write through - [135131.640609] sdb: - [135131.652634] sd 20:0:0:0: [sdb] Assuming Write Enabled - [135131.652639] sd 20:0:0:0: [sdb] Assuming drive cache: write through - [135131.652645] sd 20:0:0:0: [sdb] Attached SCSI removable disk - [135131.671536] sd 20:0:0:0: [sdb] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE - [135131.671548] sd 20:0:0:0: [sdb] Sense Key : Illegal Request [current] - [135131.671553] sd 20:0:0:0: [sdb] Add. Sense: Logical block address out of range - [135131.671560] sd 20:0:0:0: [sdb] CDB: Read(10): 28 00 00 00 f9 80 00 00 08 00 - [135131.671570] end_request: I/O error, dev sdb, sector 63872 - [135131.671575] Buffer I/O error on device sdb, logical block 7984 - [135131.678527] sd 20:0:0:0: [sdb] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE - [135131.678532] sd 20:0:0:0: [sdb] Sense Key : Illegal Request [current] - [135131.678537] sd 20:0:0:0: [sdb] Add. Sense: Logical block address out of range - [135131.678542] sd 20:0:0:0: [sdb] CDB: Read(10): 28 00 00 00 f9 80 00 00 08 00 - [135131.678551] end_request: I/O error, dev sdb, sector 63872 - ... - [135131.853565] end_request: I/O error, dev sdb, sector 4096 - */ -} - -int _stlink_sg_core_id(stlink_t *sl) { - struct stlink_libsg *sg = sl->backend_data; - int ret; - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_READCOREID; - sl->q_len = 4; - sg->q_addr = 0; - ret = stlink_q(sl); - if (ret) - return ret; - - sl->core_id = read_uint32(sl->q_buf, 0); - return 0; -} - -// Arm-core reset -> halted state. - -int _stlink_sg_reset(stlink_t *sl) { - struct stlink_libsg *sg = sl->backend_data; - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_RESETSYS; - sl->q_len = 2; - sg->q_addr = 0; - if (stlink_q(sl)) - return -1; - - stlink_stat(sl, "core reset"); - return 0; -} - -// Arm-core reset -> halted state. - -int _stlink_sg_jtag_reset(stlink_t *sl, int value) { - struct stlink_libsg *sg = sl->backend_data; - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_JTAG_DRIVE_NRST; - sg->cdb_cmd_blk[2] = (value)?0:1; - sl->q_len = 3; - sg->q_addr = 2; - if (stlink_q(sl)) - return -1; - - stlink_stat(sl, "core reset"); - - return 0; -} - -// Arm-core status: halted or running. - -int _stlink_sg_status(stlink_t *sl) { - struct stlink_libsg *sg = sl->backend_data; - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_GETSTATUS; - sl->q_len = 2; - sg->q_addr = 0; - return stlink_q(sl); -} - -// Force the core into the debug mode -> halted state. - -int _stlink_sg_force_debug(stlink_t *sl) { - struct stlink_libsg *sg = sl->backend_data; - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_FORCEDEBUG; - sl->q_len = 2; - sg->q_addr = 0; - if (stlink_q(sl)) - return -1; - - stlink_stat(sl, "force debug"); - return 0; -} - -// Read all arm-core registers. - -int _stlink_sg_read_all_regs(stlink_t *sl, reg *regp) { - struct stlink_libsg *sg = sl->backend_data; - - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_READALLREGS; - sl->q_len = 84; - sg->q_addr = 0; - if (stlink_q(sl)) - return -1; - - stlink_print_data(sl); - - // TODO - most of this should be re-extracted up.... - - // 0-3 | 4-7 | ... | 60-63 | 64-67 | 68-71 | 72-75 | 76-79 | 80-83 - // r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 - for (int i = 0; i < 16; i++) { - regp->r[i] = read_uint32(sl->q_buf, 4 * i); - if (sl->verbose > 1) - DLOG("r%2d = 0x%08x\n", i, regp->r[i]); - } - regp->xpsr = read_uint32(sl->q_buf, 64); - regp->main_sp = read_uint32(sl->q_buf, 68); - regp->process_sp = read_uint32(sl->q_buf, 72); - regp->rw = read_uint32(sl->q_buf, 76); - regp->rw2 = read_uint32(sl->q_buf, 80); - if (sl->verbose < 2) - return 0; - - DLOG("xpsr = 0x%08x\n", regp->xpsr); - DLOG("main_sp = 0x%08x\n", regp->main_sp); - DLOG("process_sp = 0x%08x\n", regp->process_sp); - DLOG("rw = 0x%08x\n", regp->rw); - DLOG("rw2 = 0x%08x\n", regp->rw2); - - return 0; -} - -// Read an arm-core register, the index must be in the range 0..20. -// 0 | 1 | ... | 15 | 16 | 17 | 18 | 19 | 20 -// r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 - -int _stlink_sg_read_reg(stlink_t *sl, int r_idx, reg *regp) { - struct stlink_libsg *sg = sl->backend_data; - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_READREG; - sg->cdb_cmd_blk[2] = r_idx; - sl->q_len = 4; - sg->q_addr = 0; - if (stlink_q(sl)) - return -1; - - // 0 | 1 | ... | 15 | 16 | 17 | 18 | 19 | 20 - // 0-3 | 4-7 | ... | 60-63 | 64-67 | 68-71 | 72-75 | 76-79 | 80-83 - // r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 - stlink_print_data(sl); - - uint32_t r = read_uint32(sl->q_buf, 0); - DLOG("r_idx (%2d) = 0x%08x\n", r_idx, r); - - switch (r_idx) { - case 16: - regp->xpsr = r; - break; - case 17: - regp->main_sp = r; - break; - case 18: - regp->process_sp = r; - break; - case 19: - regp->rw = r; //XXX ?(primask, basemask etc.) - break; - case 20: - regp->rw2 = r; //XXX ?(primask, basemask etc.) - break; - default: - regp->r[r_idx] = r; - } - - return 0; -} - -// Write an arm-core register. Index: -// 0 | 1 | ... | 15 | 16 | 17 | 18 | 19 | 20 -// r0 | r1 | ... | r15 | xpsr | main_sp | process_sp | rw | rw2 - -int _stlink_sg_write_reg(stlink_t *sl, uint32_t reg, int idx) { - struct stlink_libsg *sg = sl->backend_data; - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEREG; - // 2: reg index - // 3-6: reg content - sg->cdb_cmd_blk[2] = idx; - write_uint32(sg->cdb_cmd_blk + 3, reg); - sl->q_len = 2; - sg->q_addr = 0; - if (stlink_q(sl)) - return -1; - - stlink_stat(sl, "write reg"); - return 0; -} - -// Write a register of the debug module of the core. -// XXX ?(atomic writes) -// TODO test - -void stlink_write_dreg(stlink_t *sl, uint32_t reg, uint32_t addr) { - struct stlink_libsg *sg = sl->backend_data; - DLOG("\n*** stlink_write_dreg ***\n"); - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEDEBUGREG; - // 2-5: address of reg of the debug module - // 6-9: reg content - write_uint32(sg->cdb_cmd_blk + 2, addr); - write_uint32(sg->cdb_cmd_blk + 6, reg); - sl->q_len = 2; - sg->q_addr = addr; - stlink_q(sl); - stlink_stat(sl, "write debug reg"); -} - -// Force the core exit the debug mode. - -int _stlink_sg_run(stlink_t *sl) { - struct stlink_libsg *sg = sl->backend_data; - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_RUNCORE; - sl->q_len = 2; - sg->q_addr = 0; - if (stlink_q(sl)) - return -1; - - stlink_stat(sl, "run core"); - - return 0; -} - -// Step the arm-core. - -int _stlink_sg_step(stlink_t *sl) { - struct stlink_libsg *sg = sl->backend_data; - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_STEPCORE; - sl->q_len = 2; - sg->q_addr = 0; - if (stlink_q(sl)) - return -1; - - stlink_stat(sl, "step core"); - return 0; -} - -// TODO test -// see Cortex-M3 Technical Reference Manual -// TODO make delegate! -void stlink_set_hw_bp(stlink_t *sl, int fp_nr, uint32_t addr, int fp) { - DLOG("\n*** stlink_set_hw_bp ***\n"); - struct stlink_libsg *sg = sl->backend_data; - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_SETFP; - // 2:The number of the flash patch used to set the breakpoint - // 3-6: Address of the breakpoint (LSB) - // 7: FP_ALL (0x02) / FP_UPPER (0x01) / FP_LOWER (0x00) - sl->q_buf[2] = fp_nr; - write_uint32(sl->q_buf, addr); - sl->q_buf[7] = fp; - - sl->q_len = 2; - stlink_q(sl); - stlink_stat(sl, "set flash breakpoint"); -} - -// TODO test - -// TODO make delegate! -void stlink_clr_hw_bp(stlink_t *sl, int fp_nr) { - struct stlink_libsg *sg = sl->backend_data; - DLOG("\n*** stlink_clr_hw_bp ***\n"); - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_CLEARFP; - sg->cdb_cmd_blk[2] = fp_nr; - - sl->q_len = 2; - stlink_q(sl); - stlink_stat(sl, "clear flash breakpoint"); -} - -// Read a "len" bytes to the sl->q_buf from the memory, max 6kB (6144 bytes) - -int _stlink_sg_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { - struct stlink_libsg *sg = sl->backend_data; - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_READMEM_32BIT; - // 2-5: addr - // 6-7: len - write_uint32(sg->cdb_cmd_blk + 2, addr); - write_uint16(sg->cdb_cmd_blk + 6, len); - - // data_in 0-0x40-len - // !!! len _and_ q_len must be max 6k, - // i.e. >1024 * 6 = 6144 -> aboard) - // !!! if len < q_len: 64*k, 1024*n, n=1..5 -> aboard - // (broken residue issue) - sl->q_len = len; - sg->q_addr = addr; - if (stlink_q(sl)) - return -1; - - stlink_print_data(sl); - return 0; -} - -// Write a "len" bytes from the sl->q_buf to the memory, max 64 Bytes. - -int _stlink_sg_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { - struct stlink_libsg *sg = sl->backend_data; - int ret; - - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEMEM_8BIT; - // 2-5: addr - // 6-7: len (>0x40 (64) -> aboard) - write_uint32(sg->cdb_cmd_blk + 2, addr); - write_uint16(sg->cdb_cmd_blk + 6, len); - - // this sends the command... - ret = send_usb_mass_storage_command(sg->usb_handle, - sg->ep_req, sg->cdb_cmd_blk, CDB_SL, 0, 0, 0); - if (ret == -1) - return ret; - - // This sends the data... - ret = send_usb_data_only(sg->usb_handle, - sg->ep_req, sg->ep_rep, sl->q_buf, len); - if (ret == -1) - return ret; - - stlink_print_data(sl); - return 0; -} - -// Write a "len" bytes from the sl->q_buf to the memory, max Q_BUF_LEN bytes. - -int _stlink_sg_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { - struct stlink_libsg *sg = sl->backend_data; - int ret; - - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_DEBUG_WRITEMEM_32BIT; - // 2-5: addr - // 6-7: len "unlimited" - write_uint32(sg->cdb_cmd_blk + 2, addr); - write_uint16(sg->cdb_cmd_blk + 6, len); - - // this sends the command... - ret = send_usb_mass_storage_command(sg->usb_handle, - sg->ep_req, sg->cdb_cmd_blk, CDB_SL, 0, 0, 0); - if (ret == -1) - return ret; - - // This sends the data... - ret = send_usb_data_only(sg->usb_handle, - sg->ep_req, sg->ep_rep, sl->q_buf, len); - if (ret == -1) - return ret; - - stlink_print_data(sl); - return 0; -} - -// Write one DWORD data to memory - -int _stlink_sg_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) { - struct stlink_libsg *sg = sl->backend_data; - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_JTAG_WRITEDEBUG_32BIT; - // 2-5: addr - write_uint32(sg->cdb_cmd_blk + 2, addr); - write_uint32(sg->cdb_cmd_blk + 6, data); - sl->q_len = 2; - return stlink_q(sl); -} - -// Read one DWORD data from memory - -int _stlink_sg_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) { - struct stlink_libsg *sg = sl->backend_data; - clear_cdb(sg); - sg->cdb_cmd_blk[1] = STLINK_JTAG_READDEBUG_32BIT; - // 2-5: addr - write_uint32(sg->cdb_cmd_blk + 2, addr); - sl->q_len = 8; - if (stlink_q(sl)) - return -1; - - *data = read_uint32(sl->q_buf, 4); - return 0; -} - -// Exit the jtag or swd mode and enter the mass mode. - -int _stlink_sg_exit_debug_mode(stlink_t *stl) -{ - if (stl) { - struct stlink_libsg* sl = stl->backend_data; - clear_cdb(sl); - sl->cdb_cmd_blk[1] = STLINK_DEBUG_EXIT; - stl->q_len = 0; // >0 -> aboard - return stlink_q(stl); - } - - return 0; -} - - -// 1) open a sg device, switch the stlink from dfu to mass mode -// 2) wait 5s until the kernel driver stops reseting the broken device -// 3) reopen the device -// 4) the device driver is now ready for a switch to jtag/swd mode -// TODO thinking, better error handling, wait until the kernel driver stops reseting the plugged-in device - -stlink_backend_t _stlink_sg_backend = { - _stlink_sg_close, - _stlink_sg_exit_debug_mode, - _stlink_sg_enter_swd_mode, - _stlink_sg_enter_jtag_mode, - _stlink_sg_exit_dfu_mode, - _stlink_sg_core_id, - _stlink_sg_reset, - _stlink_sg_jtag_reset, - _stlink_sg_run, - _stlink_sg_status, - _stlink_sg_version, - _stlink_sg_read_debug32, - _stlink_sg_read_mem32, - _stlink_sg_write_debug32, - _stlink_sg_write_mem32, - _stlink_sg_write_mem8, - _stlink_sg_read_all_regs, - _stlink_sg_read_reg, - NULL, /* read_all_unsupported_regs */ - NULL, /* read_unsupported_regs */ - NULL, /* write_unsupported_regs */ - _stlink_sg_write_reg, - _stlink_sg_step, - _stlink_sg_current_mode, - _stlink_sg_force_debug, - NULL -}; - -static stlink_t* stlink_open(const int verbose) { - - stlink_t *sl = malloc(sizeof (stlink_t)); - memset(sl, 0, sizeof(stlink_t)); - struct stlink_libsg *slsg = malloc(sizeof (struct stlink_libsg)); - if (sl == NULL || slsg == NULL) { - WLOG("Couldn't malloc stlink and stlink_sg structures out of memory!\n"); - return NULL; - } - - if (libusb_init(&(slsg->libusb_ctx))) { - WLOG("failed to init libusb context, wrong version of libraries?\n"); - free(sl); - free(slsg); - return NULL; - } - - libusb_set_debug(slsg->libusb_ctx, 3); - - slsg->usb_handle = libusb_open_device_with_vid_pid(slsg->libusb_ctx, USB_ST_VID, USB_STLINK_PID); - if (slsg->usb_handle == NULL) { - WLOG("Failed to find an stlink v1 by VID:PID\n"); - libusb_close(slsg->usb_handle); - libusb_exit(slsg->libusb_ctx); - free(sl); - free(slsg); - return NULL; - } - - // TODO - // Could read the interface config descriptor, and assert lots of the assumptions - - // assumption: numInterfaces is always 1... - if (libusb_kernel_driver_active(slsg->usb_handle, 0) == 1) { - int r = libusb_detach_kernel_driver(slsg->usb_handle, 0); - if (r < 0) { - WLOG("libusb_detach_kernel_driver(() error %s\n", strerror(-r)); - libusb_close(slsg->usb_handle); - libusb_exit(slsg->libusb_ctx); - free(sl); - free(slsg); - return NULL; - } - DLOG("Kernel driver was successfully detached\n"); - } - - int config; - if (libusb_get_configuration(slsg->usb_handle, &config)) { - /* this may fail for a previous configured device */ - WLOG("libusb_get_configuration()\n"); - libusb_close(slsg->usb_handle); - libusb_exit(slsg->libusb_ctx); - free(sl); - free(slsg); - return NULL; - - } - - // assumption: bConfigurationValue is always 1 - if (config != 1) { - WLOG("Your stlink got into a real weird configuration, trying to fix it!\n"); - DLOG("setting new configuration (%d -> 1)\n", config); - if (libusb_set_configuration(slsg->usb_handle, 1)) { - /* this may fail for a previous configured device */ - WLOG("libusb_set_configuration() failed\n"); - libusb_close(slsg->usb_handle); - libusb_exit(slsg->libusb_ctx); - free(sl); - free(slsg); - return NULL; - } - } - - if (libusb_claim_interface(slsg->usb_handle, 0)) { - WLOG("libusb_claim_interface() failed\n"); - libusb_close(slsg->usb_handle); - libusb_exit(slsg->libusb_ctx); - free(sl); - free(slsg); - return NULL; - } - - // assumption: endpoint config is fixed mang. really. - slsg->ep_rep = 1 /* ep rep */ | LIBUSB_ENDPOINT_IN; - slsg->ep_req = 2 /* ep req */ | LIBUSB_ENDPOINT_OUT; - - DLOG("Successfully opened stlinkv1 by libusb :)\n"); - - sl->verbose = verbose; - sl->backend_data = slsg; - sl->backend = &_stlink_sg_backend; - - sl->core_stat = STLINK_CORE_STAT_UNKNOWN; - slsg->q_addr = 0; - - return sl; -} - - -stlink_t* stlink_v1_open_inner(const int verbose) { - ugly_init(verbose); - stlink_t *sl = stlink_open(verbose); - if (sl == NULL) { - ELOG("Could not open stlink device\n"); - return NULL; - } - - stlink_version(sl); - if ((sl->version.st_vid != USB_ST_VID) || (sl->version.stlink_pid != USB_STLINK_PID)) { - ELOG("WTF? successfully opened, but unable to read version details. BROKEN!\n"); - return NULL; - } - - DLOG("Reading current mode...\n"); - switch (stlink_current_mode(sl)) { - case STLINK_DEV_MASS_MODE: - return sl; - case STLINK_DEV_DEBUG_MODE: - // TODO go to mass? - return sl; - default: - ILOG("Current mode unusable, trying to get back to a useful state...\n"); - break; - } - - DLOG("Attempting to exit DFU mode\n"); - _stlink_sg_exit_dfu_mode(sl); - - // re-query device info (and retest) - stlink_version(sl); - if ((sl->version.st_vid != USB_ST_VID) || (sl->version.stlink_pid != USB_STLINK_PID)) { - ELOG("WTF? successfully opened, but unable to read version details. BROKEN!\n"); - return NULL; - } - - return sl; -} - -stlink_t* stlink_v1_open(const int verbose, int reset) { - stlink_t *sl = stlink_v1_open_inner(verbose); - if (sl == NULL) - return NULL; - - // by now, it _must_ be fully open and in a useful mode.... - stlink_enter_swd_mode(sl); - /* Now we are ready to read the parameters */ - if (reset) { - stlink_reset(sl); - } - stlink_load_device_params(sl); - ILOG("Successfully opened a stlink v1 debugger\n"); - return sl; -} diff --git a/src/stlink-sg.h b/src/stlink-sg.h deleted file mode 100644 index 7c02028..0000000 --- a/src/stlink-sg.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * File: stlink-sg.h - * Author: karl - * - * Created on October 1, 2011, 11:29 PM - */ - -#ifndef STLINK_SG_H -#define STLINK_SG_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include "stlink-common.h" - - // device access -#define RDWR 0 -#define RO 1 -#define SG_TIMEOUT_SEC 1 // actually 1 is about 2 sec -#define SG_TIMEOUT_MSEC 3 * 1000 - // Each CDB can be a total of 6, 10, 12, or 16 bytes, later version - // of the SCSI standard also allow for variable-length CDBs (min. CDB is 6). - // the stlink needs max. 10 bytes. -#define CDB_6 6 -#define CDB_10 10 -#define CDB_12 12 -#define CDB_16 16 - -#define CDB_SL 10 - - // Query data flow direction. -#define Q_DATA_OUT 0 -#define Q_DATA_IN 1 - - // The SCSI Request Sense command is used to obtain sense data - // (error information) from a target device. - // http://en.wikipedia.org/wiki/SCSI_Request_Sense_Command -#define SENSE_BUF_LEN 32 - - - - struct stlink_libsg { - libusb_context* libusb_ctx; - libusb_device_handle *usb_handle; - unsigned ep_rep; - unsigned ep_req; - - int sg_fd; - int do_scsi_pt_err; - - unsigned char cdb_cmd_blk[CDB_SL]; - - int q_data_dir; // Q_DATA_IN, Q_DATA_OUT - // the start of the query data in the device memory space - uint32_t q_addr; - - // Sense (error information) data - // obsolete, this was fed to the scsi tools - unsigned char sense_buf[SENSE_BUF_LEN]; - - reg reg; - }; - - stlink_t* stlink_v1_open(const int verbose, int reset); - -#ifdef __cplusplus -} -#endif - -#endif /* STLINK_SG_H */ - diff --git a/src/stlink-usb.c b/src/stlink-usb.c deleted file mode 100644 index 9865412..0000000 --- a/src/stlink-usb.c +++ /dev/null @@ -1,1007 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "stlink-common.h" -#include "stlink-usb.h" - -enum SCSI_Generic_Direction {SG_DXFER_TO_DEV=0, SG_DXFER_FROM_DEV=0x80}; - -void _stlink_usb_close(stlink_t* sl) { - if (!sl) - return; - - struct stlink_libusb * const handle = sl->backend_data; - // maybe we couldn't even get the usb device? - if (handle != NULL) { - if (handle->usb_handle != NULL) { - libusb_close(handle->usb_handle); - } - - libusb_exit(handle->libusb_ctx); - free(handle); - } -} - -ssize_t send_recv(struct stlink_libusb* handle, int terminate, - unsigned char* txbuf, size_t txsize, - unsigned char* rxbuf, size_t rxsize) { - /* note: txbuf and rxbuf can point to the same area */ - int res = 0; - - if (libusb_bulk_transfer(handle->usb_handle, handle->ep_req, - txbuf, - txsize, - &res, - 3000)) - return -1; - - if (rxsize != 0) { - if (libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, - rxbuf, - rxsize, - &res, - 3000)) - return -1; - } - - if ((handle->protocoll == 1) && terminate) { - /* Read the SG reply */ - unsigned char sg_buf[13]; - if (libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, - sg_buf, - 13, - &res, - 3000)) - return -1; - /* The STLink doesn't seem to evaluate the sequence number */ - handle->sg_transfer_idx++; - } - - return res; -} - -static inline int send_only -(struct stlink_libusb* handle, int terminate, - unsigned char* txbuf, size_t txsize) { - return send_recv(handle, terminate, txbuf, txsize, NULL, 0); -} - - -static int fill_command -(stlink_t * sl, enum SCSI_Generic_Direction dir, uint32_t len) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const cmd = sl->c_buf; - int i = 0; - memset(cmd, 0, sizeof (sl->c_buf)); - if(slu->protocoll == 1) { - cmd[i++] = 'U'; - cmd[i++] = 'S'; - cmd[i++] = 'B'; - cmd[i++] = 'C'; - write_uint32(&cmd[i], slu->sg_transfer_idx); - write_uint32(&cmd[i + 4], len); - i += 8; - cmd[i++] = (dir == SG_DXFER_FROM_DEV)?0x80:0; - cmd[i++] = 0; /* Logical unit */ - cmd[i++] = 0xa; /* Command length */ - } - return i; -} - -int _stlink_usb_version(stlink_t *sl) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const data = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - uint32_t rep_len = 6; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_GET_VERSION; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - - return 0; -} - -int32_t _stlink_usb_target_voltage(stlink_t *sl) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const rdata = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - uint32_t rep_len = 8; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - uint32_t factor, reading; - int voltage; - - cmd[i++] = STLINK_GET_TARGET_VOLTAGE; - - size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return -1; - } else if (size != 8) { - printf("[!] wrong length\n"); - return -1; - } - - factor = (rdata[3] << 24) | (rdata[2] << 16) | (rdata[1] << 8) | (rdata[0] << 0); - reading = (rdata[7] << 24) | (rdata[6] << 16) | (rdata[5] << 8) | (rdata[4] << 0); - voltage = 2400 * reading / factor; - - return voltage; -} - -int _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const rdata = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - const int rep_len = 8; - - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_JTAG_READDEBUG_32BIT; - write_uint32(&cmd[i], addr); - size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - *data = read_uint32(rdata, 4); - return 0; -} - -int _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const rdata = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - const int rep_len = 2; - - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_JTAG_WRITEDEBUG_32BIT; - write_uint32(&cmd[i], addr); - write_uint32(&cmd[i + 4], data); - size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - - return 0; -} - -int _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const data = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - int i, ret; - - i = fill_command(sl, SG_DXFER_TO_DEV, len); - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_WRITEMEM_32BIT; - write_uint32(&cmd[i], addr); - write_uint16(&cmd[i + 4], len); - ret = send_only(slu, 0, cmd, slu->cmd_len); - if (ret == -1) - return ret; - - ret = send_only(slu, 1, data, len); - if (ret == -1) - return ret; - - return 0; -} - -int _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const data = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - int i, ret; - - i = fill_command(sl, SG_DXFER_TO_DEV, 0); - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_WRITEMEM_8BIT; - write_uint32(&cmd[i], addr); - write_uint16(&cmd[i + 4], len); - ret = send_only(slu, 0, cmd, slu->cmd_len); - if (ret == -1) - return ret; - - ret = send_only(slu, 1, data, len); - if (ret == -1) - return ret; - - return 0; -} - - -int _stlink_usb_current_mode(stlink_t * sl) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const cmd = sl->c_buf; - unsigned char* const data = sl->q_buf; - ssize_t size; - int rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_GET_CURRENT_MODE; - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return -1; - } - return sl->q_buf[0]; -} - -int _stlink_usb_core_id(stlink_t * sl) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const cmd = sl->c_buf; - unsigned char* const data = sl->q_buf; - ssize_t size; - int rep_len = 4; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_READCOREID; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return -1; - } - - sl->core_id = read_uint32(data, 0); - return 0; -} - -int _stlink_usb_status(stlink_t * sl) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const data = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - int rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_GETSTATUS; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - sl->q_len = (size_t) size; - - return 0; -} - -int _stlink_usb_force_debug(stlink_t *sl) { - struct stlink_libusb *slu = sl->backend_data; - unsigned char* const data = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - int rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_FORCEDEBUG; - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - - return 0; -} - -int _stlink_usb_enter_swd_mode(stlink_t * sl) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - const int rep_len = 0; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_ENTER; - cmd[i++] = STLINK_DEBUG_ENTER_SWD; - - size = send_only(slu, 1, cmd, slu->cmd_len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - - return 0; -} - -int _stlink_usb_exit_dfu_mode(stlink_t* sl) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - int i = fill_command(sl, SG_DXFER_FROM_DEV, 0); - - cmd[i++] = STLINK_DFU_COMMAND; - cmd[i++] = STLINK_DFU_EXIT; - - size = send_only(slu, 1, cmd, slu->cmd_len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - - return 0; -} - -/** - * TODO - not convinced this does anything... - * @param sl - */ -int _stlink_usb_reset(stlink_t * sl) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const data = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - int rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_RESETSYS; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - - return 0; -} - - -int _stlink_usb_jtag_reset(stlink_t * sl, int value) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const data = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - int rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_JTAG_DRIVE_NRST; - cmd[i++] = value; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - - return 0; -} - - -int _stlink_usb_step(stlink_t* sl) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const data = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - int rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_STEPCORE; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - - return 0; -} - -/** - * This seems to do a good job of restarting things from the beginning? - * @param sl - */ -int _stlink_usb_run(stlink_t* sl) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const data = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - int rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_RUNCORE; - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - - return 0; -} - -int _stlink_usb_exit_debug_mode(stlink_t *sl) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - int i = fill_command(sl, SG_DXFER_FROM_DEV, 0); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_EXIT; - - size = send_only(slu, 1, cmd, slu->cmd_len); - if (size == -1) { - printf("[!] send_only\n"); - return size; - } - - return 0; -} - -int _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const data = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - int i = fill_command(sl, SG_DXFER_FROM_DEV, len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_READMEM_32BIT; - write_uint32(&cmd[i], addr); - write_uint16(&cmd[i + 4], len); - - size = send_recv(slu, 1, cmd, slu->cmd_len, data, len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - - sl->q_len = (size_t) size; - - stlink_print_data(sl); - return 0; -} - -int _stlink_usb_read_all_regs(stlink_t *sl, reg *regp) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const cmd = sl->c_buf; - unsigned char* const data = sl->q_buf; - ssize_t size; - uint32_t rep_len = 84; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_READALLREGS; - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - sl->q_len = (size_t) size; - stlink_print_data(sl); - for(i=0; i<16; i++) - regp->r[i]= read_uint32(sl->q_buf, i*4); - regp->xpsr = read_uint32(sl->q_buf, 64); - regp->main_sp = read_uint32(sl->q_buf, 68); - regp->process_sp = read_uint32(sl->q_buf, 72); - regp->rw = read_uint32(sl->q_buf, 76); - regp->rw2 = read_uint32(sl->q_buf, 80); - if (sl->verbose < 2) - return 0; - - DLOG("xpsr = 0x%08x\n", read_uint32(sl->q_buf, 64)); - DLOG("main_sp = 0x%08x\n", read_uint32(sl->q_buf, 68)); - DLOG("process_sp = 0x%08x\n", read_uint32(sl->q_buf, 72)); - DLOG("rw = 0x%08x\n", read_uint32(sl->q_buf, 76)); - DLOG("rw2 = 0x%08x\n", read_uint32(sl->q_buf, 80)); - - return 0; -} - -int _stlink_usb_read_reg(stlink_t *sl, int r_idx, reg *regp) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const data = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - uint32_t r; - uint32_t rep_len = 4; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_READREG; - cmd[i++] = (uint8_t) r_idx; - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - sl->q_len = (size_t) size; - stlink_print_data(sl); - r = read_uint32(sl->q_buf, 0); - DLOG("r_idx (%2d) = 0x%08x\n", r_idx, r); - - switch (r_idx) { - case 16: - regp->xpsr = r; - break; - case 17: - regp->main_sp = r; - break; - case 18: - regp->process_sp = r; - break; - case 19: - regp->rw = r; /* XXX ?(primask, basemask etc.) */ - break; - case 20: - regp->rw2 = r; /* XXX ?(primask, basemask etc.) */ - break; - default: - regp->r[r_idx] = r; - } - - return 0; -} - -/* See section C1.6 of the ARMv7-M Architecture Reference Manual */ -int _stlink_usb_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) { - uint32_t r; - int ret; - - sl->q_buf[0] = (unsigned char) r_idx; - for (int i = 1; i < 4; i++) { - sl->q_buf[i] = 0; - } - - ret = _stlink_usb_write_mem32(sl, DCRSR, 4); - if (ret == -1) - return ret; - - _stlink_usb_read_mem32(sl, DCRDR, 4); - if (ret == -1) - return ret; - - r = read_uint32(sl->q_buf, 0); - DLOG("r_idx (%2d) = 0x%08x\n", r_idx, r); - - switch (r_idx) { - case 0x14: - regp->primask = (uint8_t) (r & 0xFF); - regp->basepri = (uint8_t) ((r>>8) & 0xFF); - regp->faultmask = (uint8_t) ((r>>16) & 0xFF); - regp->control = (uint8_t) ((r>>24) & 0xFF); - break; - case 0x21: - regp->fpscr = r; - break; - default: - regp->s[r_idx - 0x40] = r; - break; - } - - return 0; -} - -int _stlink_usb_read_all_unsupported_regs(stlink_t *sl, reg *regp) { - int ret; - - ret = _stlink_usb_read_unsupported_reg(sl, 0x14, regp); - if (ret == -1) - return ret; - - ret = _stlink_usb_read_unsupported_reg(sl, 0x21, regp); - if (ret == -1) - return ret; - - for (int i = 0; i < 32; i++) { - ret = _stlink_usb_read_unsupported_reg(sl, 0x40+i, regp); - if (ret == -1) - return ret; - } - - return 0; -} - -/* See section C1.6 of the ARMv7-M Architecture Reference Manual */ -int _stlink_usb_write_unsupported_reg(stlink_t *sl, uint32_t val, int r_idx, reg *regp) { - int ret; - - if (r_idx >= 0x1C && r_idx <= 0x1F) { /* primask, basepri, faultmask, or control */ - /* These are held in the same register */ - ret = _stlink_usb_read_unsupported_reg(sl, 0x14, regp); - if (ret == -1) - return ret; - - val = (uint8_t) (val>>24); - - switch (r_idx) { - case 0x1C: /* control */ - val = (((uint32_t) val) << 24) | (((uint32_t) regp->faultmask) << 16) | (((uint32_t) regp->basepri) << 8) | ((uint32_t) regp->primask); - break; - case 0x1D: /* faultmask */ - val = (((uint32_t) regp->control) << 24) | (((uint32_t) val) << 16) | (((uint32_t) regp->basepri) << 8) | ((uint32_t) regp->primask); - break; - case 0x1E: /* basepri */ - val = (((uint32_t) regp->control) << 24) | (((uint32_t) regp->faultmask) << 16) | (((uint32_t) val) << 8) | ((uint32_t) regp->primask); - break; - case 0x1F: /* primask */ - val = (((uint32_t) regp->control) << 24) | (((uint32_t) regp->faultmask) << 16) | (((uint32_t) regp->basepri) << 8) | ((uint32_t) val); - break; - } - - r_idx = 0x14; - } - - write_uint32(sl->q_buf, val); - - ret = _stlink_usb_write_mem32(sl, DCRDR, 4); - if (ret == -1) - return ret; - - sl->q_buf[0] = (unsigned char) r_idx; - sl->q_buf[1] = 0; - sl->q_buf[2] = 0x01; - sl->q_buf[3] = 0; - - return _stlink_usb_write_mem32(sl, DCRSR, 4); -} - -int _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx) { - struct stlink_libusb * const slu = sl->backend_data; - unsigned char* const data = sl->q_buf; - unsigned char* const cmd = sl->c_buf; - ssize_t size; - uint32_t rep_len = 2; - int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); - - cmd[i++] = STLINK_DEBUG_COMMAND; - cmd[i++] = STLINK_DEBUG_WRITEREG; - cmd[i++] = idx; - write_uint32(&cmd[i], reg); - size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); - if (size == -1) { - printf("[!] send_recv\n"); - return size; - } - sl->q_len = (size_t) size; - stlink_print_data(sl); - - return 0; -} - -stlink_backend_t _stlink_usb_backend = { - _stlink_usb_close, - _stlink_usb_exit_debug_mode, - _stlink_usb_enter_swd_mode, - NULL, // no enter_jtag_mode here... - _stlink_usb_exit_dfu_mode, - _stlink_usb_core_id, - _stlink_usb_reset, - _stlink_usb_jtag_reset, - _stlink_usb_run, - _stlink_usb_status, - _stlink_usb_version, - _stlink_usb_read_debug32, - _stlink_usb_read_mem32, - _stlink_usb_write_debug32, - _stlink_usb_write_mem32, - _stlink_usb_write_mem8, - _stlink_usb_read_all_regs, - _stlink_usb_read_reg, - _stlink_usb_read_all_unsupported_regs, - _stlink_usb_read_unsupported_reg, - _stlink_usb_write_unsupported_reg, - _stlink_usb_write_reg, - _stlink_usb_step, - _stlink_usb_current_mode, - _stlink_usb_force_debug, - _stlink_usb_target_voltage -}; - -stlink_t *stlink_open_usb(enum ugly_loglevel verbose, bool reset, char serial[16]) -{ - stlink_t* sl = NULL; - struct stlink_libusb* slu = NULL; - int ret = -1; - int config; - - sl = calloc(1, sizeof (stlink_t)); - slu = calloc(1, sizeof (struct stlink_libusb)); - if (sl == NULL) - goto on_malloc_error; - if (slu == NULL) - goto on_malloc_error; - - ugly_init(verbose); - sl->backend = &_stlink_usb_backend; - sl->backend_data = slu; - - sl->core_stat = STLINK_CORE_STAT_UNKNOWN; - if (libusb_init(&(slu->libusb_ctx))) { - WLOG("failed to init libusb context, wrong version of libraries?\n"); - goto on_error; - } - - libusb_device **list; - int cnt = libusb_get_device_list(slu->libusb_ctx, &list); - struct libusb_device_descriptor desc; - int devBus =0; - int devAddr=0; - - /* @TODO: Reading a environment variable in a usb open function is not very nice, this - should be refactored and moved into the CLI tools, and instead of giving USB_BUS:USB_ADDR a real stlink - serial string should be passed to this function. Probably people are using this but this is very odd because - as programmer can change to multiple busses and it is better to detect them based on serial. */ - char *device = getenv("STLINK_DEVICE"); - if (device) { - char *c = strchr(device,':'); - if (c==NULL) { - WLOG("STLINK_DEVICE must be : format\n"); - goto on_error; - } - devBus=atoi(device); - *c++=0; - devAddr=atoi(c); - ILOG("bus %03d dev %03d\n",devBus, devAddr); - } - - while (cnt--) { - libusb_get_device_descriptor( list[cnt], &desc ); - if (desc.idVendor != USB_ST_VID) - continue; - - if (devBus && devAddr) { - if ((libusb_get_bus_number(list[cnt]) != devBus) - || (libusb_get_device_address(list[cnt]) != devAddr)) { - continue; - } - } - - if ((desc.idProduct == USB_STLINK_32L_PID) || (desc.idProduct == USB_STLINK_NUCLEO_PID)) { - struct libusb_device_handle *handle; - - libusb_open(list[cnt], &handle); - sl->serial_size = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, - (unsigned char *)sl->serial, sizeof(sl->serial)); - libusb_close(handle); - - if ((serial == NULL) || (*serial == 0)) - break; - - if (sl->serial_size < 0) - continue; - - if (memcmp(serial, &sl->serial, sl->serial_size) == 0) - break; - - continue; - } - - if (desc.idProduct == USB_STLINK_PID) { - slu->protocoll = 1; - break; - } - } - - if (cnt < 0) { - WLOG ("Couldn't find %s ST-Link/V2 devices\n",(devBus && devAddr)?"matched":"any"); - goto on_error; - } else { - ret = libusb_open(list[cnt], &slu->usb_handle); - if (ret != 0) { - WLOG("Error %d (%s) opening ST-Link/V2 device %03d:%03d\n", - ret, strerror (errno), libusb_get_bus_number(list[cnt]), libusb_get_device_address(list[cnt])); - goto on_error; - } - } - - libusb_free_device_list(list, 1); - - if (libusb_kernel_driver_active(slu->usb_handle, 0) == 1) { - ret = libusb_detach_kernel_driver(slu->usb_handle, 0); - if (ret < 0) { - WLOG("libusb_detach_kernel_driver(() error %s\n", strerror(-ret)); - goto on_libusb_error; - } - } - - if (libusb_get_configuration(slu->usb_handle, &config)) { - /* this may fail for a previous configured device */ - WLOG("libusb_get_configuration()\n"); - goto on_libusb_error; - } - - if (config != 1) { - printf("setting new configuration (%d -> 1)\n", config); - if (libusb_set_configuration(slu->usb_handle, 1)) { - /* this may fail for a previous configured device */ - WLOG("libusb_set_configuration() failed\n"); - goto on_libusb_error; - } - } - - if (libusb_claim_interface(slu->usb_handle, 0)) { - WLOG("Stlink usb device found, but unable to claim (probably already in use?)\n"); - goto on_libusb_error; - } - - // TODO - could use the scanning techniq from stm8 code here... - slu->ep_rep = 1 /* ep rep */ | LIBUSB_ENDPOINT_IN; - if (desc.idProduct == USB_STLINK_NUCLEO_PID) { - slu->ep_req = 1 /* ep req */ | LIBUSB_ENDPOINT_OUT; - } else { - slu->ep_req = 2 /* ep req */ | LIBUSB_ENDPOINT_OUT; - } - - slu->sg_transfer_idx = 0; - // TODO - never used at the moment, always CMD_SIZE - slu->cmd_len = (slu->protocoll == 1)? STLINK_SG_SIZE: STLINK_CMD_SIZE; - - if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) { - ILOG("-- exit_dfu_mode\n"); - stlink_exit_dfu_mode(sl); - } - - if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) { - stlink_enter_swd_mode(sl); - } - - if (reset) { - stlink_reset(sl); - usleep(10000); - } - - stlink_version(sl); - ret = stlink_load_device_params(sl); - -on_libusb_error: - if (ret == -1) { - stlink_close(sl); - return NULL; - } - - return sl; - -on_error: - if (slu->libusb_ctx) - libusb_exit(slu->libusb_ctx); - -on_malloc_error: - if (sl != NULL) - free(sl); - if (slu != NULL) - free(slu); - - return NULL; -} - -static size_t stlink_probe_usb_devs(libusb_device **devs, stlink_t **sldevs[]) { - stlink_t **_sldevs; - libusb_device *dev; - int i = 0; - int ret = 0; - size_t slcnt = 0; - size_t slcur = 0; - - /* Count stlink */ - while ((dev = devs[i++]) != NULL) { - struct libusb_device_descriptor desc; - int r = libusb_get_device_descriptor(dev, &desc); - if (r < 0) { - WLOG("failed to get libusb device descriptor\n"); - break; - } - - if (desc.idProduct != USB_STLINK_32L_PID && - desc.idProduct != USB_STLINK_NUCLEO_PID) - continue; - - slcnt++; - } - - /* Allocate list of pointers */ - _sldevs = calloc(slcnt, sizeof(stlink_t *)); - if (!_sldevs) { - *sldevs = NULL; - return 0; - } - - /* Open stlinks and attach to list */ - i = 0; - while ((dev = devs[i++]) != NULL) { - struct libusb_device_descriptor desc; - ret = libusb_get_device_descriptor(dev, &desc); - if (ret < 0) { - WLOG("failed to get libusb device descriptor\n"); - break; - } - - if (desc.idProduct != USB_STLINK_32L_PID && - desc.idProduct != USB_STLINK_NUCLEO_PID) - continue; - - struct libusb_device_handle* handle; - char serial[13]; - memset(serial, 0, sizeof(serial)); - - ret = libusb_open(dev, &handle); - if (ret < 0) { - WLOG("failed to get libusb device descriptor\n"); - break; - } - - ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, (unsigned char *)&serial, sizeof(serial)); - if (ret < 0) - *serial = NULL; - - libusb_close(handle); - - stlink_t *sl = NULL; - sl = stlink_open_usb(0, 1, serial); - if (!sl) - continue; - - _sldevs[slcur] = sl; - slcur++; - } - - /* Something went wrong */ - if (ret < 0) { - free(_sldevs); - *sldevs = NULL; - return 0; - } - - *sldevs = _sldevs; - return slcnt; -} - -size_t stlink_probe_usb(stlink_t **stdevs[]) { - libusb_device **devs; - stlink_t **sldevs; - - size_t slcnt = 0; - int r; - ssize_t cnt; - - r = libusb_init(NULL); - if (r < 0) - return 0; - - cnt = libusb_get_device_list(NULL, &devs); - if (cnt < 0) - return 0; - - slcnt = stlink_probe_usb_devs(devs, &sldevs); - libusb_free_device_list(devs, 1); - - libusb_exit(NULL); - - *stdevs = sldevs; - return slcnt; -} - -void stlink_probe_usb_free(stlink_t ***stdevs, size_t size) { - if (stdevs == NULL || *stdevs == NULL || size == 0) - return; - - for (size_t n = 0; n < size; n++) - stlink_close((*stdevs)[n]); - free(*stdevs); - *stdevs = NULL; -} diff --git a/src/stlink-usb.h b/src/stlink-usb.h deleted file mode 100644 index 747d54c..0000000 --- a/src/stlink-usb.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * File: stlink-usb.h - * Author: karl - * - * Created on October 1, 2011, 11:29 PM - */ - -#ifndef STLINK_USB_H -#define STLINK_USB_H - -#include -#include - -#include "stlink-common.h" -#include "uglylogging.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define STLINK_SG_SIZE 31 -#define STLINK_CMD_SIZE 16 - - struct stlink_libusb { - libusb_context* libusb_ctx; - libusb_device_handle* usb_handle; - unsigned int ep_req; - unsigned int ep_rep; - int protocoll; - unsigned int sg_transfer_idx; - unsigned int cmd_len; - }; - - /** - * Open a stlink - * @param verbose Verbosity loglevel - * @param reset Reset stlink programmer - * @param serial Serial number to search for, when NULL the first stlink found is opened (binary format) - * @retval NULL Error while opening the stlink - * @retval !NULL Stlink found and ready to use - */ - stlink_t *stlink_open_usb(enum ugly_loglevel verbose, bool reset, char serial[16]); - size_t stlink_probe_usb(stlink_t **stdevs[]); - void stlink_probe_usb_free(stlink_t **stdevs[], size_t size); - -#ifdef __cplusplus -} -#endif - -#endif /* STLINK_USB_H */ - diff --git a/src/test_sg.c b/src/test_sg.c deleted file mode 100644 index db7eed3..0000000 --- a/src/test_sg.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * File: test_main.c - * - * main() ripped out of old stlink-hw.c - */ - -#include -#include -#include -#include "stlink-common.h" -#include "uglylogging.h" - -static void __attribute__((unused)) mark_buf(stlink_t *sl) { - memset(sl->q_buf, 0, sizeof(sl->q_buf)); - sl->q_buf[0] = 0xaa; - sl->q_buf[1] = 0xbb; - sl->q_buf[2] = 0xcc; - sl->q_buf[3] = 0xdd; - sl->q_buf[4] = 0x11; - sl->q_buf[15] = 0x22; - sl->q_buf[16] = 0x33; - sl->q_buf[63] = 0x44; - sl->q_buf[64] = 0x69; - sl->q_buf[1024 * 6 - 1] = 0x42; //6kB - sl->q_buf[1024 * 8 - 1] = 0x42; //8kB -} - - -int main(int argc, char *argv[]) { - /* Avoid unused parameter warning */ - (void)argv; - // set scpi lib debug level: 0 for no debug info, 10 for lots - - switch (argc) { - case 1: - fputs( - "\nUsage: stlink-access-test [anything at all] ...\n" - "\n*** Notice: The stlink firmware violates the USB standard.\n" - "*** Because we just use libusb, we can just tell the kernel's\n" - "*** driver to simply ignore the device...\n" - "*** Unplug the stlink and execute once as root:\n" - "modprobe -r usb-storage && modprobe usb-storage quirks=483:3744:i\n\n", - stderr); - return EXIT_FAILURE; - default: - break; - } - - stlink_t *sl = stlink_v1_open(99, 1); - if (sl == NULL) - return EXIT_FAILURE; - - // we are in mass mode, go to swd - stlink_enter_swd_mode(sl); - stlink_current_mode(sl); - stlink_core_id(sl); - //---------------------------------------------------------------------- - - stlink_status(sl); - //stlink_force_debug(sl); - stlink_reset(sl); - stlink_status(sl); - // core system control block - stlink_read_mem32(sl, 0xe000ed00, 4); - DLOG("cpu id base register: SCB_CPUID = got 0x%08x expect 0x411fc231\n", read_uint32(sl->q_buf, 0)); - // no MPU - stlink_read_mem32(sl, 0xe000ed90, 4); - DLOG("mpu type register: MPU_TYPER = got 0x%08x expect 0x0\n", read_uint32(sl->q_buf, 0)); - -#if 0 - stlink_read_mem32(sl, 0xe000edf0, 4); - DD(sl, "DHCSR = 0x%08x", read_uint32(sl->q_buf, 0)); - - stlink_read_mem32(sl, 0x4001100c, 4); - DD(sl, "GPIOC_ODR = 0x%08x", read_uint32(sl->q_buf, 0)); -#endif -#if 0 - // happy new year 2011: let blink all the leds - // see "RM0041 Reference manual - STM32F100xx advanced ARM-based 32-bit MCUs" - -#define GPIOC 0x40011000 // port C -#define GPIOC_CRH (GPIOC + 0x04) // port configuration register high -#define GPIOC_ODR (GPIOC + 0x0c) // port output data register -#define LED_BLUE (1<<8) // pin 8 -#define LED_GREEN (1<<9) // pin 9 - stlink_read_mem32(sl, GPIOC_CRH, 4); - uint32_t io_conf = read_uint32(sl->q_buf, 0); - DLOG("GPIOC_CRH = 0x%08x\n", io_conf); - - // set: general purpose output push-pull, output mode, max speed 10 MHz. - write_uint32(sl->q_buf, 0x44444411); - stlink_write_mem32(sl, GPIOC_CRH, 4); - - memset(sl->q_buf, 0, sizeof(sl->q_buf)); - for (int i = 0; i < 100; i++) { - write_uint32(sl->q_buf, LED_BLUE | LED_GREEN); - stlink_write_mem32(sl, GPIOC_ODR, 4); - /* stlink_read_mem32(sl, 0x4001100c, 4); */ - /* DD(sl, "GPIOC_ODR = 0x%08x", read_uint32(sl->q_buf, 0)); */ - usleep(100 * 1000); - - memset(sl->q_buf, 0, sizeof(sl->q_buf)); - stlink_write_mem32(sl, GPIOC_ODR, 4); // PC lo - usleep(100 * 1000); - } - write_uint32(sl->q_buf, io_conf); // set old state - -#endif -#if 0 - // TODO rtfm: stlink doesn't have flash write routines - // writing to the flash area confuses the fw for the next read access - - //stlink_read_mem32(sl, 0, 1024*6); - // flash 0x08000000 128kB - fputs("++++++++++ read a flash at 0x0800 0000\n", stderr); - stlink_read_mem32(sl, 0x08000000, 1024 * 6); //max 6kB - clear_buf(sl); - stlink_read_mem32(sl, 0x08000c00, 5); - stlink_read_mem32(sl, 0x08000c00, 4); - mark_buf(sl); - stlink_write_mem32(sl, 0x08000c00, 4); - stlink_read_mem32(sl, 0x08000c00, 256); - stlink_read_mem32(sl, 0x08000c00, 256); -#endif -#if 0 - // sram 0x20000000 8kB - fputs("\n++++++++++ read/write 8bit, sram at 0x2000 0000 ++++++++++++++++\n\n", stderr); - memset(sl->q_buf, 0, sizeof(sl->q_buf)); - mark_buf(sl); - //stlink_write_mem8(sl, 0x20000000, 16); - - //stlink_write_mem8(sl, 0x20000000, 1); - //stlink_write_mem8(sl, 0x20000001, 1); - stlink_write_mem8(sl, 0x2000000b, 3); - stlink_read_mem32(sl, 0x20000000, 16); -#endif -#if 0 - // a not aligned mem32 access doesn't work indeed - fputs("\n++++++++++ read/write 32bit, sram at 0x2000 0000 ++++++++++++++++\n\n", stderr); - memset(sl->q_buf, 0, sizeof(sl->q_buf)); - mark_buf(sl); - stlink_write_mem32(sl, 0x20000000, 1); - stlink_read_mem32(sl, 0x20000000, 16); - mark_buf(sl); - stlink_write_mem32(sl, 0x20000001, 1); - stlink_read_mem32(sl, 0x20000000, 16); - mark_buf(sl); - stlink_write_mem32(sl, 0x2000000b, 3); - stlink_read_mem32(sl, 0x20000000, 16); - - mark_buf(sl); - stlink_write_mem32(sl, 0x20000000, 17); - stlink_read_mem32(sl, 0x20000000, 32); -#endif -#if 0 - // sram 0x20000000 8kB - fputs("++++++++++ read/write 32bit, sram at 0x2000 0000 ++++++++++++\n", stderr); - memset(sl->q_buf, 0, sizeof(sl->q_buf)); - mark_buf(sl); - stlink_write_mem8(sl, 0x20000000, 64); - stlink_read_mem32(sl, 0x20000000, 64); - - mark_buf(sl); - stlink_write_mem32(sl, 0x20000000, 1024 * 8); //8kB - stlink_read_mem32(sl, 0x20000000, 1024 * 6); - stlink_read_mem32(sl, 0x20000000 + 1024 * 6, 1024 * 2); -#endif -#if 1 - reg regs; - stlink_read_all_regs(sl, ®s); - stlink_step(sl); - fputs("++++++++++ write r0 = 0x12345678\n", stderr); - stlink_write_reg(sl, 0x12345678, 0); - stlink_read_reg(sl, 0, ®s); - stlink_read_all_regs(sl, ®s); -#endif -#if 0 - stlink_run(sl); - stlink_status(sl); - - stlink_force_debug(sl); - stlink_status(sl); -#endif -#if 0 /* read the system bootloader */ - fputs("\n++++++++++ reading bootloader ++++++++++++++++\n\n", stderr); - stlink_fread(sl, "/tmp/barfoo", sl->sys_base, sl->sys_size); -#endif -#if 0 /* read the flash memory */ - fputs("\n+++++++ read flash memory\n\n", stderr); - /* mark_buf(sl); */ - stlink_read_mem32(sl, 0x08000000, 4); -#endif -#if 0 /* flash programming */ - fputs("\n+++++++ program flash memory\n\n", stderr); - stlink_fwrite_flash(sl, "/tmp/foobar", 0x08000000); -#endif -#if 0 /* check file contents */ - fputs("\n+++++++ check flash memory\n\n", stderr); - { - const int res = stlink_fcheck_flash(sl, "/tmp/foobar", 0x08000000); - printf("_____ stlink_fcheck_flash() == %d\n", res); - } -#endif -#if 0 - fputs("\n+++++++ sram write and execute\n\n", stderr); - stlink_fwrite_sram(sl, "/tmp/foobar", sl->sram_base); - stlink_run_at(sl, sl->sram_base); -#endif - -#if 0 - stlink_run(sl); - stlink_status(sl); - //---------------------------------------------------------------------- - // back to mass mode, just in case ... - stlink_exit_debug_mode(sl); - stlink_current_mode(sl); - stlink_close(sl); -#endif - - //fflush(stderr); fflush(stdout); - return EXIT_SUCCESS; -} diff --git a/src/test_usb.c b/src/test_usb.c deleted file mode 100644 index 787ac12..0000000 --- a/src/test_usb.c +++ /dev/null @@ -1,102 +0,0 @@ -#include -#include "stlink-common.h" - - -int main(int ac, char** av) { - stlink_t* sl; - reg regs; - - /* unused */ - ac = ac; - av = av; - - sl = stlink_open_usb(10, 1, NULL); - if (sl != NULL) { - printf("-- version\n"); - stlink_version(sl); - - printf("mode before doing anything: %d\n", stlink_current_mode(sl)); - - if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) { - printf("-- exit_dfu_mode\n"); - stlink_exit_dfu_mode(sl); - } - - printf("-- enter_swd_mode\n"); - stlink_enter_swd_mode(sl); - - printf("-- mode after entering swd mode: %d\n", stlink_current_mode(sl)); - - printf("-- chip id: %#x\n", sl->chip_id); - printf("-- core_id: %#x\n", sl->core_id); - - cortex_m3_cpuid_t cpuid; - stlink_cpu_id(sl, &cpuid); - printf("cpuid:impl_id = %0#x, variant = %#x\n", cpuid.implementer_id, cpuid.variant); - printf("cpuid:part = %#x, rev = %#x\n", cpuid.part, cpuid.revision); - - printf("-- read_sram\n"); - static const uint32_t sram_base = 0x8000000; - uint32_t off; - for (off = 0; off < 16; off += 4) - stlink_read_mem32(sl, sram_base + off, 4); - - printf("FP_CTRL\n"); - stlink_read_mem32(sl, CM3_REG_FP_CTRL, 4); - - // no idea what reg this is.. */ - // stlink_read_mem32(sl, 0xe000ed90, 4); - // no idea what register this is... - // stlink_read_mem32(sl, 0xe000edf0, 4); - // offset 0xC into TIM11 register? TIMx_DIER? - // stlink_read_mem32(sl, 0x4001100c, 4); */ - - /* Test 32 bit Write */ - write_uint32(sl->q_buf,0x01234567); - stlink_write_mem32(sl,0x200000a8,4); - write_uint32(sl->q_buf,0x89abcdef); - stlink_write_mem32(sl,0x200000ac, 4); - stlink_read_mem32(sl, 0x200000a8, 4); - stlink_read_mem32(sl, 0x200000ac, 4); - - /* Test 8 bit write */ - write_uint32(sl->q_buf,0x01234567); - stlink_write_mem8(sl,0x200001a8,3); - write_uint32(sl->q_buf,0x89abcdef); - stlink_write_mem8(sl, 0x200001ac, 3); - stlink_read_mem32(sl, 0x200001a8, 4); - stlink_read_mem32(sl, 0x200001ac, 4); - - printf("-- status\n"); - stlink_status(sl); - - printf("-- reset\n"); - stlink_reset(sl); - stlink_force_debug(sl); - /* Test reg write*/ - stlink_write_reg(sl, 0x01234567, 3); - stlink_write_reg(sl, 0x89abcdef, 4); - stlink_write_reg(sl, 0x12345678, 15); - for (off = 0; off < 21; off += 1) - stlink_read_reg(sl, off, ®s); - - - stlink_read_all_regs(sl, ®s); - - printf("-- status\n"); - stlink_status(sl); - - printf("-- step\n"); - stlink_step(sl); - - printf("-- run\n"); - stlink_run(sl); - - printf("-- exit_debug_mode\n"); - stlink_exit_debug_mode(sl); - - stlink_close(sl); - } - - return 0; -} diff --git a/src/tools/flash.c b/src/tools/flash.c new file mode 100644 index 0000000..770289b --- /dev/null +++ b/src/tools/flash.c @@ -0,0 +1,301 @@ +/* simple wrapper around the stlink_flash_write function */ + +// TODO - this should be done as just a simple flag to the st-util command line... + + +#include +#include +#include +#include +#include + +#include + +#define DEBUG_LOG_LEVEL 100 +#define STND_LOG_LEVEL 50 + +stlink_t *connected_stlink = NULL; + +static void cleanup(int signal __attribute__((unused))) { + if (connected_stlink) { + /* Switch back to mass storage mode before closing. */ + stlink_run(connected_stlink); + stlink_exit_debug_mode(connected_stlink); + stlink_close(connected_stlink); + } + + exit(1); +} + +enum st_cmds {DO_WRITE = 0, DO_READ = 1, DO_ERASE = 2}; +struct opts +{ + enum st_cmds cmd; + const char* devname; + char *serial; + const char* filename; + stm32_addr_t addr; + size_t size; + int reset; + int log_level; +}; + +static void usage(void) +{ + puts("stlinkv1 command line: ./st-flash [--debug] [--reset] [--serial ] {read|write} /dev/sgX path addr "); + puts("stlinkv1 command line: ./st-flash [--debug] /dev/sgX erase"); + puts("stlinkv2 command line: ./st-flash [--debug] [--reset] [--serial ] {read|write} path addr "); + puts("stlinkv2 command line: ./st-flash [--debug] [--serial ] erase"); + puts(" use hex format for addr, and "); +} + +static int get_opts(struct opts* o, int ac, char** av) +{ + /* stlinkv1 command line: ./st-flash {read|write} /dev/sgX path addr */ + /* stlinkv2 command line: ./st-flash {read|write} path addr */ + + unsigned int i = 0; + + if (ac < 1) return -1; + + if (strcmp(av[0], "--debug") == 0) + { + o->log_level = DEBUG_LOG_LEVEL; + ac--; + av++; + } + else + { + o->log_level = STND_LOG_LEVEL; + } + + if (strcmp(av[0], "--reset") == 0) + { + o->reset = 1; + ac--; + av++; + } + else + { + o->reset = 0; + } + + if (strcmp(av[0], "--serial") == 0) + { + ac--; + av++; + int i=strlen(av[0]); + if(i%2 != 0){ + puts("no valid hex value, length must be multiple of 2\n"); + return -1; + } + int j=0; + while(i>=0 && j<=13){ + char buffer[3]={0}; + memcpy(buffer,&av[0][i],2); + o->serial[12-j] = (char)strtol((const char*)buffer,NULL, 16); + j++; + i-=2; + } + ac--; + av++; + } + else + { + o->serial = NULL; + } + + if (ac < 1) return -1; + + /* stlinkv2 */ + o->devname = NULL; + + if (strcmp(av[0], "erase") == 0) + { + o->cmd = DO_ERASE; + + /* stlinkv1 mode */ + if (ac == 2) + { + o->devname = av[1]; + i = 1; + } + } + else { + if (ac < 3) return -1; + if (strcmp(av[0], "read") == 0) + { + o->cmd = DO_READ; + + /* stlinkv1 mode */ + if (ac == 5) + { + o->devname = av[1]; + i = 1; + } + if (ac > 3) + o->size = strtoul(av[i + 3], NULL, 16); + } + else if (strcmp(av[0], "write") == 0) + { + o->cmd = DO_WRITE; + + /* stlinkv1 mode */ + if (ac == 4) + { + o->devname = av[1]; + i = 1; + } + } + else + { + return -1; + } + } + + o->filename = av[i + 1]; + o->addr = strtoul(av[i + 2], NULL, 16); + + return 0; +} + + +int main(int ac, char** av) +{ + stlink_t* sl = NULL; + struct opts o; + char serial_buffer[13] = {0}; + o.serial = serial_buffer; + int err = -1; + + o.size = 0; + if (get_opts(&o, ac - 1, av + 1) == -1) + { + printf("invalid command line\n"); + usage(); + return -1; + } + + if (o.devname != NULL) /* stlinkv1 */ + sl = stlink_v1_open(o.log_level, 1); + else /* stlinkv2 */ + sl = stlink_open_usb(o.log_level, 1, o.serial); + + if (sl == NULL) + return -1; + + sl->verbose = o.log_level; + + connected_stlink = sl; + signal(SIGINT, &cleanup); + signal(SIGTERM, &cleanup); + signal(SIGSEGV, &cleanup); + + if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) { + if (stlink_exit_dfu_mode(sl)) { + printf("Failed to exit DFU mode\n"); + goto on_error; + } + } + + if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) { + if (stlink_enter_swd_mode(sl)) { + printf("Failed to enter SWD mode\n"); + goto on_error; + } + } + + if (o.reset){ + if (stlink_jtag_reset(sl, 2)) { + printf("Failed to reset JTAG\n"); + goto on_error; + } + + if (stlink_reset(sl)) { + printf("Failed to reset device\n"); + goto on_error; + } + } + + // Disable DMA - Set All DMA CCR Registers to zero. - AKS 1/7/2013 + if (sl->chip_id == STM32_CHIPID_F4) + { + memset(sl->q_buf,0,4); + for (int i=0;i<8;i++) { + stlink_write_mem32(sl,0x40026000+0x10+0x18*i,4); + stlink_write_mem32(sl,0x40026400+0x10+0x18*i,4); + stlink_write_mem32(sl,0x40026000+0x24+0x18*i,4); + stlink_write_mem32(sl,0x40026400+0x24+0x18*i,4); + } + } + + // Core must be halted to use RAM based flashloaders + if (stlink_force_debug(sl)) { + printf("Failed to halt the core\n"); + goto on_error; + } + + if (stlink_status(sl)) { + printf("Failed to get Core's status\n"); + goto on_error; + } + + if (o.cmd == DO_WRITE) /* write */ + { + if ((o.addr >= sl->flash_base) && + (o.addr < sl->flash_base + sl->flash_size)) { + err = stlink_fwrite_flash(sl, o.filename, o.addr); + if (err == -1) + { + printf("stlink_fwrite_flash() == -1\n"); + goto on_error; + } + } + else if ((o.addr >= sl->sram_base) && + (o.addr < sl->sram_base + sl->sram_size)) { + err = stlink_fwrite_sram(sl, o.filename, o.addr); + if (err == -1) + { + printf("stlink_fwrite_sram() == -1\n"); + goto on_error; + } + } + } else if (o.cmd == DO_ERASE) + { + err = stlink_erase_flash_mass(sl); + if (err == -1) + { + printf("stlink_erase_flash_mass() == -1\n"); + goto on_error; + } + } + else /* read */ + { + if ((o.addr >= sl->flash_base) && (o.size == 0) && + (o.addr < sl->flash_base + sl->flash_size)) + o.size = sl->flash_size; + else if ((o.addr >= sl->sram_base) && (o.size == 0) && + (o.addr < sl->sram_base + sl->sram_size)) + o.size = sl->sram_size; + err = stlink_fread(sl, o.filename, o.addr, o.size); + if (err == -1) + { + printf("stlink_fread() == -1\n"); + goto on_error; + } + } + + if (o.reset){ + stlink_jtag_reset(sl,2); + stlink_reset(sl); + } + + /* success */ + err = 0; + +on_error: + stlink_exit_debug_mode(sl); + stlink_close(sl); + + return err; +} diff --git a/src/tools/info.c b/src/tools/info.c new file mode 100644 index 0000000..e29184e --- /dev/null +++ b/src/tools/info.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include + +#include + +static void usage(void) +{ + puts("st-info --flash"); + puts("st-info --sram"); + puts("st-info --descr"); + puts("st-info --pagesize"); + puts("st-info --chipid"); + puts("st-info --serial"); + puts("st-info --hla-serial"); + puts("st-info --probe"); +} + +/* Print normal or OpenOCD hla_serial with newline */ +static void stlink_print_serial(stlink_t *sl, bool openocd) +{ + const char *fmt; + + if (openocd) { + printf("\""); + fmt = "\\x%02x"; + } else { + fmt = "%02x"; + } + + for (int n = 0; n < sl->serial_size; n++) + printf(fmt, sl->serial[n]); + + if (openocd) + printf("\""); + printf("\n"); +} + +static void stlink_print_info(stlink_t *sl) +{ + const chip_params_t *params = NULL; + + if (!sl) + return; + + printf(" serial: "); + stlink_print_serial(sl, false); + printf("openocd: "); + stlink_print_serial(sl, true); + + printf(" flash: %zu (pagesize: %zu)\n", sl->flash_size, sl->flash_pgsz); + printf(" sram: %zu\n", sl->sram_size); + printf(" chipid: 0x%.4x\n", sl->chip_id); + + for (size_t i = 0; i < sizeof(devices) / sizeof(devices[0]); i++) { + if (devices[i].chip_id == sl->chip_id) { + params = &devices[i]; + break; + } + } + + if (params) + printf(" descr: %s\n", params->description); +} + +static void stlink_probe(void) +{ + stlink_t **stdevs; + size_t size; + + size = stlink_probe_usb(&stdevs); + + printf("Found %zu stlink programmers\n", size); + + for (size_t n = 0; n < size; n++) + stlink_print_info(stdevs[n]); + + stlink_probe_usb_free(&stdevs, size); +} + +static stlink_t *stlink_open_first(void) +{ + stlink_t* sl = NULL; + sl = stlink_v1_open(0, 1); + if (sl == NULL) + sl = stlink_open_usb(0, 1, NULL); + + return sl; +} + +static int print_data(char **av) +{ + stlink_t* sl = NULL; + + // Probe needs all devices unclaimed + if (strcmp(av[1], "--probe") == 0) { + stlink_probe(); + return 0; + } + + sl = stlink_open_first(); + + if (sl == NULL) { + return -1; + } + + sl->verbose = 0; + + if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) + stlink_exit_dfu_mode(sl); + + if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) + stlink_enter_swd_mode(sl); + + if (strcmp(av[1], "--flash") == 0) + printf("0x%zx\n", sl->flash_size); + else if (strcmp(av[1], "--sram") == 0) + printf("0x%zx\n", sl->sram_size); + else if (strcmp(av[1], "--pagesize") == 0) + printf("0x%zx\n", sl->flash_pgsz); + else if (strcmp(av[1], "--chipid") == 0) + printf("0x%.4x\n", sl->chip_id); + else if (strcmp(av[1], "--serial") == 0) + stlink_print_serial(sl, false); + else if (strcmp(av[1], "--hla-serial") == 0) + stlink_print_serial(sl, true); + else if (strcmp(av[1], "--descr") == 0) { + const chip_params_t *params = NULL; + for (size_t i = 0; i < sizeof(devices) / sizeof(devices[0]); i++) { + if(devices[i].chip_id == sl->chip_id) { + params = &devices[i]; + break; + } + } + if (params == NULL) { + return -1; + } + printf("%s\n", params->description); + } + + if (sl) + { + stlink_exit_debug_mode(sl); + stlink_close(sl); + } + + return 0; +} + +int main(int ac, char** av) +{ + int err = -1; + if (ac < 2) { + usage(); + return -1; + } + + err = print_data(av); + + return err; +} diff --git a/src/tools/term.c b/src/tools/term.c new file mode 100644 index 0000000..9b46e2a --- /dev/null +++ b/src/tools/term.c @@ -0,0 +1,322 @@ +#include +#include +/* According to POSIX.1-2001 */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* STLinky structure on STM chip + +struct stlinky { + uint32_t magic; + uint32_t bufsize; + uint32_t up_tail; + uint32_t up_head; + uint32_t dw_tail; + uint32_t dw_head; + char upbuf[CONFIG_LIB_STLINKY_BSIZE]; + char dwbuf[CONFIG_LIB_STLINKY_BSIZE]; +} __attribute__ ((packed)); +*/ + + +#define STLINKY_MAGIC 0xDEADF00D + +#define ST_TERM_MAX_BUFF_SIZE (1024*1024) //1Mb + +#define RX_Q_OFFSET 8 +#define RX_BUFF_OFFSET 24 +#define TX_Q_OFFSET 16 +#define TX_BUFF_OFFSET(bufsize) (24 + bufsize) + +#define READ_UINT32_LE(buf) ((uint32_t) ( buf[0] \ + | buf[1] << 8 \ + | buf[2] << 16 \ + | buf[3] << 24)) + +static stlink_t* sl; +sigset_t sig_mask; + +struct stlinky { + stlink_t *sl; + uint32_t off; + size_t bufsize; +}; + +void nonblock(int state); + +static void cleanup(int signal __attribute__((unused))) { + if (sl) { + /* Switch back to mass storage mode before closing. */ + stlink_run(sl); + stlink_exit_debug_mode(sl); + stlink_close(sl); + } + + printf("\n"); + nonblock(0); + exit(1); +} + +void sig_init() { + sigemptyset(&sig_mask); + sigaddset(&sig_mask, SIGINT); + sigaddset(&sig_mask, SIGTERM); + signal(SIGINT, &cleanup); + signal(SIGTERM, &cleanup); + sigprocmask(SIG_BLOCK, &sig_mask, NULL); +} + +void sig_process() { + sigset_t pending; + sigpending(&pending); + if (sigismember(&pending, SIGINT) || sigismember(&pending, SIGTERM)) { + sigprocmask(SIG_UNBLOCK, &sig_mask, NULL); + sigsuspend(&pending); + sigprocmask(SIG_BLOCK, &sig_mask, NULL); + } +} + +/* Detects stlinky in RAM, returns handler */ +struct stlinky* stlinky_detect(stlink_t* sl) +{ + static const uint32_t sram_base = 0x20000000; + struct stlinky* st = malloc(sizeof(struct stlinky)); + int multiple=0; + st->sl = sl; + printf("sram: 0x%x bytes @ 0x%zx\n", sl->sram_base, sl->sram_size); + uint32_t off; + for (off = 0; off < sl->sram_size; off += 4) { + if (off % 1024 == 0) sig_process(); + stlink_read_mem32(sl, sram_base + off, 4); + if (STLINKY_MAGIC == READ_UINT32_LE(sl->q_buf)) + { + if (multiple > 0) printf("WARNING: another "); + printf("stlinky detected at 0x%x\n", sram_base + off); + st->off = sram_base + off; + stlink_read_mem32(sl, st->off + 4, 4); + st->bufsize = READ_UINT32_LE(sl->q_buf); + printf("stlinky buffer size 0x%zu \n", st->bufsize); + multiple++; + } + } + if (multiple > 0) { + if (multiple > 1) { + printf("Using last stlinky structure detected\n"); + } + return st; + } + return NULL; +} + +static void stlinky_read_buff(struct stlinky *st, uint32_t off, uint32_t size, char *buffer) +{ + int aligned_size; + + if (size == 0) + return; + + //Read from device with 4-byte alignment + aligned_size = (size & 0xFFFFFFFC) + 8; + stlink_read_mem32(st->sl, off & 0xFFFFFFFC, aligned_size); + + //copy to local buffer + memcpy(buffer, st->sl->q_buf + (off & 0x3), size); + + return; +} + +static void stlinky_write_buf(struct stlinky *st, uint32_t off, uint32_t size, char *buffer) +{ + memcpy(st->sl->q_buf, buffer, size); + stlink_write_mem8(st->sl, off, size); + return; +} + +size_t stlinky_rx(struct stlinky *st, char* buffer) +{ + //read head and tail values + uint32_t tail, head; + stlink_read_mem32(st->sl, st->off + RX_Q_OFFSET, sizeof(tail) + sizeof(head)); + memcpy(&tail, &st->sl->q_buf[0], sizeof(tail)); + memcpy(&head, &st->sl->q_buf[sizeof(tail)], sizeof(head)); + + //return if empty + if(head == tail) + return 0; + + //read data + int size_read = 0; + if(head > tail){ + stlinky_read_buff(st, st->off + RX_BUFF_OFFSET + tail, head - tail, buffer); + size_read += head - tail; + } else if(head < tail){ + stlinky_read_buff(st, st->off + RX_BUFF_OFFSET + tail, st->bufsize - tail, buffer); + size_read += st->bufsize - tail; + + stlinky_read_buff(st, st->off + RX_BUFF_OFFSET, head, buffer + size_read); + size_read += head; + } + + //move tail + tail = (tail + size_read) % st->bufsize; + + //write tail + memcpy(st->sl->q_buf, &tail, sizeof(tail)); + stlink_write_mem32(st->sl, st->off + RX_Q_OFFSET, sizeof(tail)); + + return size_read; +} + +size_t stlinky_tx(struct stlinky *st, char* buffer, size_t siz) +{ + //read head and tail values + uint32_t tail, head; + stlink_read_mem32(st->sl, st->off + TX_Q_OFFSET, sizeof(tail) + sizeof(head)); + memcpy(&tail, &st->sl->q_buf[0], sizeof(tail)); + memcpy(&head, &st->sl->q_buf[sizeof(tail)], sizeof(head)); + + //Figure out buffer usage + int usage = head - tail; + if (usage < 0) + usage += st->bufsize; + + //check if new data will fit + if (usage + siz >= st->bufsize) + return 0; + + //copy in data (take care of possible split) + int first_chunk = head + siz >= st->bufsize ? st->bufsize - head : siz; + int second_chunk = siz - first_chunk; + + //copy data + stlinky_write_buf(st, st->off + TX_BUFF_OFFSET(st->bufsize) + head, first_chunk, buffer); + if (second_chunk > 0) + stlinky_write_buf(st, st->off + TX_BUFF_OFFSET(st->bufsize), + second_chunk, buffer + first_chunk); + + //increment head pointer + head = (head + siz) % st->bufsize; + memcpy(st->sl->q_buf, &head, sizeof(head)); + stlink_write_mem32(st->sl, st->off + TX_Q_OFFSET + sizeof(tail), sizeof(head)); + + return siz; +} + +int kbhit() +{ + struct timeval tv; + fd_set fds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0 + select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); + return FD_ISSET(STDIN_FILENO, &fds); +} + +void nonblock(int state) +{ + struct termios ttystate; + + //get the terminal state + tcgetattr(STDIN_FILENO, &ttystate); + + if (state==1) + { + //turn off canonical mode + ttystate.c_lflag &= ~ICANON; + ttystate.c_lflag &= ~ECHO; + //minimum of number input read. + ttystate.c_cc[VMIN] = 1; + } + else if (state==0) + { + //turn on canonical mode + ttystate.c_lflag |= ICANON | ECHO; + } + //set the terminal attributes. + tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); + +} + +int main(int ac, char** av) { + struct stlinky *st=NULL; + + sig_init(); + + sl = stlink_open_usb(10, 1, NULL); + if (sl != NULL) { + printf("ST-Linky proof-of-concept terminal :: Created by Necromant for lulz\n"); + stlink_version(sl); + stlink_enter_swd_mode(sl); + printf("chip id: %#x\n", sl->chip_id); + printf("core_id: %#x\n", sl->core_id); + + cortex_m3_cpuid_t cpuid; + stlink_cpu_id(sl, &cpuid); + printf("cpuid:impl_id = %0#x, variant = %#x\n", cpuid.implementer_id, cpuid.variant); + printf("cpuid:part = %#x, rev = %#x\n", cpuid.part, cpuid.revision); + + stlink_reset(sl); + stlink_force_debug(sl); + stlink_run(sl); + stlink_status(sl); + + /* wait for device to boot */ + /* TODO: Make timeout adjustable via command line */ + sleep(1); + + if(ac == 1){ + st = stlinky_detect(sl); + }else if(ac == 2){ + st = malloc(sizeof(struct stlinky)); + st->sl = sl; + st->off = (int)strtol(av[1], NULL, 16); + printf("using stlinky at 0x%x\n", st->off); + stlink_read_mem32(sl, st->off + 4, 4); + st->bufsize = READ_UINT32_LE(sl->q_buf); + printf("stlinky buffer size 0x%zu \n", st->bufsize); + }else{ + cleanup(0); + } + if (st == NULL) + { + printf("stlinky magic not found in sram :(\n"); + cleanup(0); + } + if (st->bufsize > ST_TERM_MAX_BUFF_SIZE){ + printf("stlinky buffer size too big\n"); + cleanup(0); + } + char* rxbuf = malloc(st->bufsize); + char* txbuf = malloc(st->bufsize); + size_t tmp; + nonblock(1); + int fd = fileno(stdin); + int saved_flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, saved_flags & ~O_NONBLOCK); + printf("Entering interactive terminal. CTRL+C to exit\n\n\n"); + while(1) { + sig_process(); + tmp = stlinky_rx(st, rxbuf); + if(tmp > 0) + { + fwrite(rxbuf,tmp,1,stdout); + fflush(stdout); + } + if (kbhit()) { + tmp = read(fd, txbuf, st->bufsize); + stlinky_tx(st,txbuf,tmp); + } + } + } + return 0; +} diff --git a/src/uglylogging.c b/src/uglylogging.c deleted file mode 100644 index a04ab33..0000000 --- a/src/uglylogging.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * UglyLogging. Slow, yet another wheel reinvented, but enough to make the - * rest of our code pretty enough. - * - */ - -#include -#include -#include -#include -#include - -#include "uglylogging.h" - -static int max_level; - -int ugly_init(int maximum_threshold) { - max_level = maximum_threshold; - return 0; -} - -int ugly_log(int level, const char *tag, const char *format, ...) { - if (level > max_level) { - return 0; - } - va_list args; - va_start(args, format); - time_t mytt = time(NULL); - struct tm *tt; - tt = localtime(&mytt); - fprintf(stderr, "%d-%02d-%02dT%02d:%02d:%02d ", tt->tm_year + 1900, tt->tm_mon + 1, tt->tm_mday, tt->tm_hour, tt->tm_min, tt->tm_sec); - switch (level) { - case UDEBUG: - fprintf(stderr, "DEBUG %s: ", tag); - break; - case UINFO: - fprintf(stderr, "INFO %s: ", tag); - break; - case UWARN: - fprintf(stderr, "WARN %s: ", tag); - break; - case UERROR: - fprintf(stderr, "ERROR %s: ", tag); - break; - case UFATAL: - fprintf(stderr, "FATAL %s: ", tag); - vfprintf(stderr, format, args); - exit(EXIT_FAILURE); - // NEVER GETS HERE!!! - break; - default: - fprintf(stderr, "%d %s: ", level, tag); - break; - } - vfprintf(stderr, format, args); - va_end(args); - return 1; -} diff --git a/src/uglylogging.h b/src/uglylogging.h deleted file mode 100644 index df816ca..0000000 --- a/src/uglylogging.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Ugly, low performance, configurable level, logging "framework" - */ - -#ifndef UGLYLOGGING_H -#define UGLYLOGGING_H - -#ifdef __cplusplus -extern "C" { -#endif - -enum ugly_loglevel { - UDEBUG = 90, - UINFO = 50, - UWARN = 30, - UERROR = 20, - UFATAL = 10 -}; - -int ugly_init(int maximum_threshold); -int ugly_log(int level, const char *tag, const char *format, ...); - -#define DLOG(format, args...) ugly_log(UDEBUG, __FILE__, format, ## args) -#define ILOG(format, args...) ugly_log(UINFO, __FILE__, format, ## args) -#define WLOG(format, args...) ugly_log(UWARN, __FILE__, format, ## args) -#define ELOG(format, args...) ugly_log(UERROR, __FILE__, format, ## args) -#define fatal(format, args...) ugly_log(UFATAL, __FILE__, format, ## args) - -#ifdef __cplusplus -} -#endif - -#endif /* UGLYLOGGING_H */ - diff --git a/src/usb.c b/src/usb.c new file mode 100644 index 0000000..e8d8ec3 --- /dev/null +++ b/src/usb.c @@ -0,0 +1,1006 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stlink.h" + +enum SCSI_Generic_Direction {SG_DXFER_TO_DEV=0, SG_DXFER_FROM_DEV=0x80}; + +void _stlink_usb_close(stlink_t* sl) { + if (!sl) + return; + + struct stlink_libusb * const handle = sl->backend_data; + // maybe we couldn't even get the usb device? + if (handle != NULL) { + if (handle->usb_handle != NULL) { + libusb_close(handle->usb_handle); + } + + libusb_exit(handle->libusb_ctx); + free(handle); + } +} + +ssize_t send_recv(struct stlink_libusb* handle, int terminate, + unsigned char* txbuf, size_t txsize, + unsigned char* rxbuf, size_t rxsize) { + /* note: txbuf and rxbuf can point to the same area */ + int res = 0; + + if (libusb_bulk_transfer(handle->usb_handle, handle->ep_req, + txbuf, + txsize, + &res, + 3000)) + return -1; + + if (rxsize != 0) { + if (libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, + rxbuf, + rxsize, + &res, + 3000)) + return -1; + } + + if ((handle->protocoll == 1) && terminate) { + /* Read the SG reply */ + unsigned char sg_buf[13]; + if (libusb_bulk_transfer(handle->usb_handle, handle->ep_rep, + sg_buf, + 13, + &res, + 3000)) + return -1; + /* The STLink doesn't seem to evaluate the sequence number */ + handle->sg_transfer_idx++; + } + + return res; +} + +static inline int send_only +(struct stlink_libusb* handle, int terminate, + unsigned char* txbuf, size_t txsize) { + return send_recv(handle, terminate, txbuf, txsize, NULL, 0); +} + + +static int fill_command +(stlink_t * sl, enum SCSI_Generic_Direction dir, uint32_t len) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const cmd = sl->c_buf; + int i = 0; + memset(cmd, 0, sizeof (sl->c_buf)); + if(slu->protocoll == 1) { + cmd[i++] = 'U'; + cmd[i++] = 'S'; + cmd[i++] = 'B'; + cmd[i++] = 'C'; + write_uint32(&cmd[i], slu->sg_transfer_idx); + write_uint32(&cmd[i + 4], len); + i += 8; + cmd[i++] = (dir == SG_DXFER_FROM_DEV)?0x80:0; + cmd[i++] = 0; /* Logical unit */ + cmd[i++] = 0xa; /* Command length */ + } + return i; +} + +int _stlink_usb_version(stlink_t *sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const data = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + uint32_t rep_len = 6; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_GET_VERSION; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + + return 0; +} + +int32_t _stlink_usb_target_voltage(stlink_t *sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const rdata = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + uint32_t rep_len = 8; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + uint32_t factor, reading; + int voltage; + + cmd[i++] = STLINK_GET_TARGET_VOLTAGE; + + size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return -1; + } else if (size != 8) { + printf("[!] wrong length\n"); + return -1; + } + + factor = (rdata[3] << 24) | (rdata[2] << 16) | (rdata[1] << 8) | (rdata[0] << 0); + reading = (rdata[7] << 24) | (rdata[6] << 16) | (rdata[5] << 8) | (rdata[4] << 0); + voltage = 2400 * reading / factor; + + return voltage; +} + +int _stlink_usb_read_debug32(stlink_t *sl, uint32_t addr, uint32_t *data) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const rdata = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + const int rep_len = 8; + + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_JTAG_READDEBUG_32BIT; + write_uint32(&cmd[i], addr); + size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + *data = read_uint32(rdata, 4); + return 0; +} + +int _stlink_usb_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const rdata = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + const int rep_len = 2; + + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_JTAG_WRITEDEBUG_32BIT; + write_uint32(&cmd[i], addr); + write_uint32(&cmd[i + 4], data); + size = send_recv(slu, 1, cmd, slu->cmd_len, rdata, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + + return 0; +} + +int _stlink_usb_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const data = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + int i, ret; + + i = fill_command(sl, SG_DXFER_TO_DEV, len); + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_WRITEMEM_32BIT; + write_uint32(&cmd[i], addr); + write_uint16(&cmd[i + 4], len); + ret = send_only(slu, 0, cmd, slu->cmd_len); + if (ret == -1) + return ret; + + ret = send_only(slu, 1, data, len); + if (ret == -1) + return ret; + + return 0; +} + +int _stlink_usb_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const data = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + int i, ret; + + i = fill_command(sl, SG_DXFER_TO_DEV, 0); + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_WRITEMEM_8BIT; + write_uint32(&cmd[i], addr); + write_uint16(&cmd[i + 4], len); + ret = send_only(slu, 0, cmd, slu->cmd_len); + if (ret == -1) + return ret; + + ret = send_only(slu, 1, data, len); + if (ret == -1) + return ret; + + return 0; +} + + +int _stlink_usb_current_mode(stlink_t * sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const cmd = sl->c_buf; + unsigned char* const data = sl->q_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_GET_CURRENT_MODE; + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return -1; + } + return sl->q_buf[0]; +} + +int _stlink_usb_core_id(stlink_t * sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const cmd = sl->c_buf; + unsigned char* const data = sl->q_buf; + ssize_t size; + int rep_len = 4; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_READCOREID; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return -1; + } + + sl->core_id = read_uint32(data, 0); + return 0; +} + +int _stlink_usb_status(stlink_t * sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const data = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_GETSTATUS; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + sl->q_len = (size_t) size; + + return 0; +} + +int _stlink_usb_force_debug(stlink_t *sl) { + struct stlink_libusb *slu = sl->backend_data; + unsigned char* const data = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_FORCEDEBUG; + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + + return 0; +} + +int _stlink_usb_enter_swd_mode(stlink_t * sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + const int rep_len = 0; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_ENTER; + cmd[i++] = STLINK_DEBUG_ENTER_SWD; + + size = send_only(slu, 1, cmd, slu->cmd_len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + + return 0; +} + +int _stlink_usb_exit_dfu_mode(stlink_t* sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + int i = fill_command(sl, SG_DXFER_FROM_DEV, 0); + + cmd[i++] = STLINK_DFU_COMMAND; + cmd[i++] = STLINK_DFU_EXIT; + + size = send_only(slu, 1, cmd, slu->cmd_len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + + return 0; +} + +/** + * TODO - not convinced this does anything... + * @param sl + */ +int _stlink_usb_reset(stlink_t * sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const data = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_RESETSYS; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + + return 0; +} + + +int _stlink_usb_jtag_reset(stlink_t * sl, int value) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const data = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_JTAG_DRIVE_NRST; + cmd[i++] = value; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + + return 0; +} + + +int _stlink_usb_step(stlink_t* sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const data = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_STEPCORE; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + + return 0; +} + +/** + * This seems to do a good job of restarting things from the beginning? + * @param sl + */ +int _stlink_usb_run(stlink_t* sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const data = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + int rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_RUNCORE; + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + + return 0; +} + +int _stlink_usb_exit_debug_mode(stlink_t *sl) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + int i = fill_command(sl, SG_DXFER_FROM_DEV, 0); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_EXIT; + + size = send_only(slu, 1, cmd, slu->cmd_len); + if (size == -1) { + printf("[!] send_only\n"); + return size; + } + + return 0; +} + +int _stlink_usb_read_mem32(stlink_t *sl, uint32_t addr, uint16_t len) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const data = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + int i = fill_command(sl, SG_DXFER_FROM_DEV, len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_READMEM_32BIT; + write_uint32(&cmd[i], addr); + write_uint16(&cmd[i + 4], len); + + size = send_recv(slu, 1, cmd, slu->cmd_len, data, len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + + sl->q_len = (size_t) size; + + stlink_print_data(sl); + return 0; +} + +int _stlink_usb_read_all_regs(stlink_t *sl, reg *regp) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const cmd = sl->c_buf; + unsigned char* const data = sl->q_buf; + ssize_t size; + uint32_t rep_len = 84; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_READALLREGS; + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + sl->q_len = (size_t) size; + stlink_print_data(sl); + for(i=0; i<16; i++) + regp->r[i]= read_uint32(sl->q_buf, i*4); + regp->xpsr = read_uint32(sl->q_buf, 64); + regp->main_sp = read_uint32(sl->q_buf, 68); + regp->process_sp = read_uint32(sl->q_buf, 72); + regp->rw = read_uint32(sl->q_buf, 76); + regp->rw2 = read_uint32(sl->q_buf, 80); + if (sl->verbose < 2) + return 0; + + DLOG("xpsr = 0x%08x\n", read_uint32(sl->q_buf, 64)); + DLOG("main_sp = 0x%08x\n", read_uint32(sl->q_buf, 68)); + DLOG("process_sp = 0x%08x\n", read_uint32(sl->q_buf, 72)); + DLOG("rw = 0x%08x\n", read_uint32(sl->q_buf, 76)); + DLOG("rw2 = 0x%08x\n", read_uint32(sl->q_buf, 80)); + + return 0; +} + +int _stlink_usb_read_reg(stlink_t *sl, int r_idx, reg *regp) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const data = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + uint32_t r; + uint32_t rep_len = 4; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_READREG; + cmd[i++] = (uint8_t) r_idx; + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + sl->q_len = (size_t) size; + stlink_print_data(sl); + r = read_uint32(sl->q_buf, 0); + DLOG("r_idx (%2d) = 0x%08x\n", r_idx, r); + + switch (r_idx) { + case 16: + regp->xpsr = r; + break; + case 17: + regp->main_sp = r; + break; + case 18: + regp->process_sp = r; + break; + case 19: + regp->rw = r; /* XXX ?(primask, basemask etc.) */ + break; + case 20: + regp->rw2 = r; /* XXX ?(primask, basemask etc.) */ + break; + default: + regp->r[r_idx] = r; + } + + return 0; +} + +/* See section C1.6 of the ARMv7-M Architecture Reference Manual */ +int _stlink_usb_read_unsupported_reg(stlink_t *sl, int r_idx, reg *regp) { + uint32_t r; + int ret; + + sl->q_buf[0] = (unsigned char) r_idx; + for (int i = 1; i < 4; i++) { + sl->q_buf[i] = 0; + } + + ret = _stlink_usb_write_mem32(sl, DCRSR, 4); + if (ret == -1) + return ret; + + _stlink_usb_read_mem32(sl, DCRDR, 4); + if (ret == -1) + return ret; + + r = read_uint32(sl->q_buf, 0); + DLOG("r_idx (%2d) = 0x%08x\n", r_idx, r); + + switch (r_idx) { + case 0x14: + regp->primask = (uint8_t) (r & 0xFF); + regp->basepri = (uint8_t) ((r>>8) & 0xFF); + regp->faultmask = (uint8_t) ((r>>16) & 0xFF); + regp->control = (uint8_t) ((r>>24) & 0xFF); + break; + case 0x21: + regp->fpscr = r; + break; + default: + regp->s[r_idx - 0x40] = r; + break; + } + + return 0; +} + +int _stlink_usb_read_all_unsupported_regs(stlink_t *sl, reg *regp) { + int ret; + + ret = _stlink_usb_read_unsupported_reg(sl, 0x14, regp); + if (ret == -1) + return ret; + + ret = _stlink_usb_read_unsupported_reg(sl, 0x21, regp); + if (ret == -1) + return ret; + + for (int i = 0; i < 32; i++) { + ret = _stlink_usb_read_unsupported_reg(sl, 0x40+i, regp); + if (ret == -1) + return ret; + } + + return 0; +} + +/* See section C1.6 of the ARMv7-M Architecture Reference Manual */ +int _stlink_usb_write_unsupported_reg(stlink_t *sl, uint32_t val, int r_idx, reg *regp) { + int ret; + + if (r_idx >= 0x1C && r_idx <= 0x1F) { /* primask, basepri, faultmask, or control */ + /* These are held in the same register */ + ret = _stlink_usb_read_unsupported_reg(sl, 0x14, regp); + if (ret == -1) + return ret; + + val = (uint8_t) (val>>24); + + switch (r_idx) { + case 0x1C: /* control */ + val = (((uint32_t) val) << 24) | (((uint32_t) regp->faultmask) << 16) | (((uint32_t) regp->basepri) << 8) | ((uint32_t) regp->primask); + break; + case 0x1D: /* faultmask */ + val = (((uint32_t) regp->control) << 24) | (((uint32_t) val) << 16) | (((uint32_t) regp->basepri) << 8) | ((uint32_t) regp->primask); + break; + case 0x1E: /* basepri */ + val = (((uint32_t) regp->control) << 24) | (((uint32_t) regp->faultmask) << 16) | (((uint32_t) val) << 8) | ((uint32_t) regp->primask); + break; + case 0x1F: /* primask */ + val = (((uint32_t) regp->control) << 24) | (((uint32_t) regp->faultmask) << 16) | (((uint32_t) regp->basepri) << 8) | ((uint32_t) val); + break; + } + + r_idx = 0x14; + } + + write_uint32(sl->q_buf, val); + + ret = _stlink_usb_write_mem32(sl, DCRDR, 4); + if (ret == -1) + return ret; + + sl->q_buf[0] = (unsigned char) r_idx; + sl->q_buf[1] = 0; + sl->q_buf[2] = 0x01; + sl->q_buf[3] = 0; + + return _stlink_usb_write_mem32(sl, DCRSR, 4); +} + +int _stlink_usb_write_reg(stlink_t *sl, uint32_t reg, int idx) { + struct stlink_libusb * const slu = sl->backend_data; + unsigned char* const data = sl->q_buf; + unsigned char* const cmd = sl->c_buf; + ssize_t size; + uint32_t rep_len = 2; + int i = fill_command(sl, SG_DXFER_FROM_DEV, rep_len); + + cmd[i++] = STLINK_DEBUG_COMMAND; + cmd[i++] = STLINK_DEBUG_WRITEREG; + cmd[i++] = idx; + write_uint32(&cmd[i], reg); + size = send_recv(slu, 1, cmd, slu->cmd_len, data, rep_len); + if (size == -1) { + printf("[!] send_recv\n"); + return size; + } + sl->q_len = (size_t) size; + stlink_print_data(sl); + + return 0; +} + +stlink_backend_t _stlink_usb_backend = { + _stlink_usb_close, + _stlink_usb_exit_debug_mode, + _stlink_usb_enter_swd_mode, + NULL, // no enter_jtag_mode here... + _stlink_usb_exit_dfu_mode, + _stlink_usb_core_id, + _stlink_usb_reset, + _stlink_usb_jtag_reset, + _stlink_usb_run, + _stlink_usb_status, + _stlink_usb_version, + _stlink_usb_read_debug32, + _stlink_usb_read_mem32, + _stlink_usb_write_debug32, + _stlink_usb_write_mem32, + _stlink_usb_write_mem8, + _stlink_usb_read_all_regs, + _stlink_usb_read_reg, + _stlink_usb_read_all_unsupported_regs, + _stlink_usb_read_unsupported_reg, + _stlink_usb_write_unsupported_reg, + _stlink_usb_write_reg, + _stlink_usb_step, + _stlink_usb_current_mode, + _stlink_usb_force_debug, + _stlink_usb_target_voltage +}; + +stlink_t *stlink_open_usb(enum ugly_loglevel verbose, bool reset, char serial[16]) +{ + stlink_t* sl = NULL; + struct stlink_libusb* slu = NULL; + int ret = -1; + int config; + + sl = calloc(1, sizeof (stlink_t)); + slu = calloc(1, sizeof (struct stlink_libusb)); + if (sl == NULL) + goto on_malloc_error; + if (slu == NULL) + goto on_malloc_error; + + ugly_init(verbose); + sl->backend = &_stlink_usb_backend; + sl->backend_data = slu; + + sl->core_stat = STLINK_CORE_STAT_UNKNOWN; + if (libusb_init(&(slu->libusb_ctx))) { + WLOG("failed to init libusb context, wrong version of libraries?\n"); + goto on_error; + } + + libusb_device **list; + int cnt = libusb_get_device_list(slu->libusb_ctx, &list); + struct libusb_device_descriptor desc; + int devBus =0; + int devAddr=0; + + /* @TODO: Reading a environment variable in a usb open function is not very nice, this + should be refactored and moved into the CLI tools, and instead of giving USB_BUS:USB_ADDR a real stlink + serial string should be passed to this function. Probably people are using this but this is very odd because + as programmer can change to multiple busses and it is better to detect them based on serial. */ + char *device = getenv("STLINK_DEVICE"); + if (device) { + char *c = strchr(device,':'); + if (c==NULL) { + WLOG("STLINK_DEVICE must be : format\n"); + goto on_error; + } + devBus=atoi(device); + *c++=0; + devAddr=atoi(c); + ILOG("bus %03d dev %03d\n",devBus, devAddr); + } + + while (cnt--) { + libusb_get_device_descriptor( list[cnt], &desc ); + if (desc.idVendor != USB_ST_VID) + continue; + + if (devBus && devAddr) { + if ((libusb_get_bus_number(list[cnt]) != devBus) + || (libusb_get_device_address(list[cnt]) != devAddr)) { + continue; + } + } + + if ((desc.idProduct == USB_STLINK_32L_PID) || (desc.idProduct == USB_STLINK_NUCLEO_PID)) { + struct libusb_device_handle *handle; + + libusb_open(list[cnt], &handle); + sl->serial_size = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, + (unsigned char *)sl->serial, sizeof(sl->serial)); + libusb_close(handle); + + if ((serial == NULL) || (*serial == 0)) + break; + + if (sl->serial_size < 0) + continue; + + if (memcmp(serial, &sl->serial, sl->serial_size) == 0) + break; + + continue; + } + + if (desc.idProduct == USB_STLINK_PID) { + slu->protocoll = 1; + break; + } + } + + if (cnt < 0) { + WLOG ("Couldn't find %s ST-Link/V2 devices\n",(devBus && devAddr)?"matched":"any"); + goto on_error; + } else { + ret = libusb_open(list[cnt], &slu->usb_handle); + if (ret != 0) { + WLOG("Error %d (%s) opening ST-Link/V2 device %03d:%03d\n", + ret, strerror (errno), libusb_get_bus_number(list[cnt]), libusb_get_device_address(list[cnt])); + goto on_error; + } + } + + libusb_free_device_list(list, 1); + + if (libusb_kernel_driver_active(slu->usb_handle, 0) == 1) { + ret = libusb_detach_kernel_driver(slu->usb_handle, 0); + if (ret < 0) { + WLOG("libusb_detach_kernel_driver(() error %s\n", strerror(-ret)); + goto on_libusb_error; + } + } + + if (libusb_get_configuration(slu->usb_handle, &config)) { + /* this may fail for a previous configured device */ + WLOG("libusb_get_configuration()\n"); + goto on_libusb_error; + } + + if (config != 1) { + printf("setting new configuration (%d -> 1)\n", config); + if (libusb_set_configuration(slu->usb_handle, 1)) { + /* this may fail for a previous configured device */ + WLOG("libusb_set_configuration() failed\n"); + goto on_libusb_error; + } + } + + if (libusb_claim_interface(slu->usb_handle, 0)) { + WLOG("Stlink usb device found, but unable to claim (probably already in use?)\n"); + goto on_libusb_error; + } + + // TODO - could use the scanning techniq from stm8 code here... + slu->ep_rep = 1 /* ep rep */ | LIBUSB_ENDPOINT_IN; + if (desc.idProduct == USB_STLINK_NUCLEO_PID) { + slu->ep_req = 1 /* ep req */ | LIBUSB_ENDPOINT_OUT; + } else { + slu->ep_req = 2 /* ep req */ | LIBUSB_ENDPOINT_OUT; + } + + slu->sg_transfer_idx = 0; + // TODO - never used at the moment, always CMD_SIZE + slu->cmd_len = (slu->protocoll == 1)? STLINK_SG_SIZE: STLINK_CMD_SIZE; + + if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) { + ILOG("-- exit_dfu_mode\n"); + stlink_exit_dfu_mode(sl); + } + + if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE) { + stlink_enter_swd_mode(sl); + } + + if (reset) { + stlink_reset(sl); + usleep(10000); + } + + stlink_version(sl); + ret = stlink_load_device_params(sl); + +on_libusb_error: + if (ret == -1) { + stlink_close(sl); + return NULL; + } + + return sl; + +on_error: + if (slu->libusb_ctx) + libusb_exit(slu->libusb_ctx); + +on_malloc_error: + if (sl != NULL) + free(sl); + if (slu != NULL) + free(slu); + + return NULL; +} + +static size_t stlink_probe_usb_devs(libusb_device **devs, stlink_t **sldevs[]) { + stlink_t **_sldevs; + libusb_device *dev; + int i = 0; + int ret = 0; + size_t slcnt = 0; + size_t slcur = 0; + + /* Count stlink */ + while ((dev = devs[i++]) != NULL) { + struct libusb_device_descriptor desc; + int r = libusb_get_device_descriptor(dev, &desc); + if (r < 0) { + WLOG("failed to get libusb device descriptor\n"); + break; + } + + if (desc.idProduct != USB_STLINK_32L_PID && + desc.idProduct != USB_STLINK_NUCLEO_PID) + continue; + + slcnt++; + } + + /* Allocate list of pointers */ + _sldevs = calloc(slcnt, sizeof(stlink_t *)); + if (!_sldevs) { + *sldevs = NULL; + return 0; + } + + /* Open stlinks and attach to list */ + i = 0; + while ((dev = devs[i++]) != NULL) { + struct libusb_device_descriptor desc; + ret = libusb_get_device_descriptor(dev, &desc); + if (ret < 0) { + WLOG("failed to get libusb device descriptor\n"); + break; + } + + if (desc.idProduct != USB_STLINK_32L_PID && + desc.idProduct != USB_STLINK_NUCLEO_PID) + continue; + + struct libusb_device_handle* handle; + char serial[13]; + memset(serial, 0, sizeof(serial)); + + ret = libusb_open(dev, &handle); + if (ret < 0) { + WLOG("failed to get libusb device descriptor\n"); + break; + } + + ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, (unsigned char *)&serial, sizeof(serial)); + if (ret < 0) + *serial = NULL; + + libusb_close(handle); + + stlink_t *sl = NULL; + sl = stlink_open_usb(0, 1, serial); + if (!sl) + continue; + + _sldevs[slcur] = sl; + slcur++; + } + + /* Something went wrong */ + if (ret < 0) { + free(_sldevs); + *sldevs = NULL; + return 0; + } + + *sldevs = _sldevs; + return slcnt; +} + +size_t stlink_probe_usb(stlink_t **stdevs[]) { + libusb_device **devs; + stlink_t **sldevs; + + size_t slcnt = 0; + int r; + ssize_t cnt; + + r = libusb_init(NULL); + if (r < 0) + return 0; + + cnt = libusb_get_device_list(NULL, &devs); + if (cnt < 0) + return 0; + + slcnt = stlink_probe_usb_devs(devs, &sldevs); + libusb_free_device_list(devs, 1); + + libusb_exit(NULL); + + *stdevs = sldevs; + return slcnt; +} + +void stlink_probe_usb_free(stlink_t ***stdevs, size_t size) { + if (stdevs == NULL || *stdevs == NULL || size == 0) + return; + + for (size_t n = 0; n < size; n++) + stlink_close((*stdevs)[n]); + free(*stdevs); + *stdevs = NULL; +} diff --git a/stlink_v1.modprobe.conf b/stlink_v1.modprobe.conf deleted file mode 100644 index 94b3786..0000000 --- a/stlink_v1.modprobe.conf +++ /dev/null @@ -1 +0,0 @@ -options usb-storage quirks=483:3744:i diff --git a/tests/stlink_sg.c b/tests/stlink_sg.c new file mode 100644 index 0000000..db7eed3 --- /dev/null +++ b/tests/stlink_sg.c @@ -0,0 +1,222 @@ +/* + * File: test_main.c + * + * main() ripped out of old stlink-hw.c + */ + +#include +#include +#include +#include "stlink-common.h" +#include "uglylogging.h" + +static void __attribute__((unused)) mark_buf(stlink_t *sl) { + memset(sl->q_buf, 0, sizeof(sl->q_buf)); + sl->q_buf[0] = 0xaa; + sl->q_buf[1] = 0xbb; + sl->q_buf[2] = 0xcc; + sl->q_buf[3] = 0xdd; + sl->q_buf[4] = 0x11; + sl->q_buf[15] = 0x22; + sl->q_buf[16] = 0x33; + sl->q_buf[63] = 0x44; + sl->q_buf[64] = 0x69; + sl->q_buf[1024 * 6 - 1] = 0x42; //6kB + sl->q_buf[1024 * 8 - 1] = 0x42; //8kB +} + + +int main(int argc, char *argv[]) { + /* Avoid unused parameter warning */ + (void)argv; + // set scpi lib debug level: 0 for no debug info, 10 for lots + + switch (argc) { + case 1: + fputs( + "\nUsage: stlink-access-test [anything at all] ...\n" + "\n*** Notice: The stlink firmware violates the USB standard.\n" + "*** Because we just use libusb, we can just tell the kernel's\n" + "*** driver to simply ignore the device...\n" + "*** Unplug the stlink and execute once as root:\n" + "modprobe -r usb-storage && modprobe usb-storage quirks=483:3744:i\n\n", + stderr); + return EXIT_FAILURE; + default: + break; + } + + stlink_t *sl = stlink_v1_open(99, 1); + if (sl == NULL) + return EXIT_FAILURE; + + // we are in mass mode, go to swd + stlink_enter_swd_mode(sl); + stlink_current_mode(sl); + stlink_core_id(sl); + //---------------------------------------------------------------------- + + stlink_status(sl); + //stlink_force_debug(sl); + stlink_reset(sl); + stlink_status(sl); + // core system control block + stlink_read_mem32(sl, 0xe000ed00, 4); + DLOG("cpu id base register: SCB_CPUID = got 0x%08x expect 0x411fc231\n", read_uint32(sl->q_buf, 0)); + // no MPU + stlink_read_mem32(sl, 0xe000ed90, 4); + DLOG("mpu type register: MPU_TYPER = got 0x%08x expect 0x0\n", read_uint32(sl->q_buf, 0)); + +#if 0 + stlink_read_mem32(sl, 0xe000edf0, 4); + DD(sl, "DHCSR = 0x%08x", read_uint32(sl->q_buf, 0)); + + stlink_read_mem32(sl, 0x4001100c, 4); + DD(sl, "GPIOC_ODR = 0x%08x", read_uint32(sl->q_buf, 0)); +#endif +#if 0 + // happy new year 2011: let blink all the leds + // see "RM0041 Reference manual - STM32F100xx advanced ARM-based 32-bit MCUs" + +#define GPIOC 0x40011000 // port C +#define GPIOC_CRH (GPIOC + 0x04) // port configuration register high +#define GPIOC_ODR (GPIOC + 0x0c) // port output data register +#define LED_BLUE (1<<8) // pin 8 +#define LED_GREEN (1<<9) // pin 9 + stlink_read_mem32(sl, GPIOC_CRH, 4); + uint32_t io_conf = read_uint32(sl->q_buf, 0); + DLOG("GPIOC_CRH = 0x%08x\n", io_conf); + + // set: general purpose output push-pull, output mode, max speed 10 MHz. + write_uint32(sl->q_buf, 0x44444411); + stlink_write_mem32(sl, GPIOC_CRH, 4); + + memset(sl->q_buf, 0, sizeof(sl->q_buf)); + for (int i = 0; i < 100; i++) { + write_uint32(sl->q_buf, LED_BLUE | LED_GREEN); + stlink_write_mem32(sl, GPIOC_ODR, 4); + /* stlink_read_mem32(sl, 0x4001100c, 4); */ + /* DD(sl, "GPIOC_ODR = 0x%08x", read_uint32(sl->q_buf, 0)); */ + usleep(100 * 1000); + + memset(sl->q_buf, 0, sizeof(sl->q_buf)); + stlink_write_mem32(sl, GPIOC_ODR, 4); // PC lo + usleep(100 * 1000); + } + write_uint32(sl->q_buf, io_conf); // set old state + +#endif +#if 0 + // TODO rtfm: stlink doesn't have flash write routines + // writing to the flash area confuses the fw for the next read access + + //stlink_read_mem32(sl, 0, 1024*6); + // flash 0x08000000 128kB + fputs("++++++++++ read a flash at 0x0800 0000\n", stderr); + stlink_read_mem32(sl, 0x08000000, 1024 * 6); //max 6kB + clear_buf(sl); + stlink_read_mem32(sl, 0x08000c00, 5); + stlink_read_mem32(sl, 0x08000c00, 4); + mark_buf(sl); + stlink_write_mem32(sl, 0x08000c00, 4); + stlink_read_mem32(sl, 0x08000c00, 256); + stlink_read_mem32(sl, 0x08000c00, 256); +#endif +#if 0 + // sram 0x20000000 8kB + fputs("\n++++++++++ read/write 8bit, sram at 0x2000 0000 ++++++++++++++++\n\n", stderr); + memset(sl->q_buf, 0, sizeof(sl->q_buf)); + mark_buf(sl); + //stlink_write_mem8(sl, 0x20000000, 16); + + //stlink_write_mem8(sl, 0x20000000, 1); + //stlink_write_mem8(sl, 0x20000001, 1); + stlink_write_mem8(sl, 0x2000000b, 3); + stlink_read_mem32(sl, 0x20000000, 16); +#endif +#if 0 + // a not aligned mem32 access doesn't work indeed + fputs("\n++++++++++ read/write 32bit, sram at 0x2000 0000 ++++++++++++++++\n\n", stderr); + memset(sl->q_buf, 0, sizeof(sl->q_buf)); + mark_buf(sl); + stlink_write_mem32(sl, 0x20000000, 1); + stlink_read_mem32(sl, 0x20000000, 16); + mark_buf(sl); + stlink_write_mem32(sl, 0x20000001, 1); + stlink_read_mem32(sl, 0x20000000, 16); + mark_buf(sl); + stlink_write_mem32(sl, 0x2000000b, 3); + stlink_read_mem32(sl, 0x20000000, 16); + + mark_buf(sl); + stlink_write_mem32(sl, 0x20000000, 17); + stlink_read_mem32(sl, 0x20000000, 32); +#endif +#if 0 + // sram 0x20000000 8kB + fputs("++++++++++ read/write 32bit, sram at 0x2000 0000 ++++++++++++\n", stderr); + memset(sl->q_buf, 0, sizeof(sl->q_buf)); + mark_buf(sl); + stlink_write_mem8(sl, 0x20000000, 64); + stlink_read_mem32(sl, 0x20000000, 64); + + mark_buf(sl); + stlink_write_mem32(sl, 0x20000000, 1024 * 8); //8kB + stlink_read_mem32(sl, 0x20000000, 1024 * 6); + stlink_read_mem32(sl, 0x20000000 + 1024 * 6, 1024 * 2); +#endif +#if 1 + reg regs; + stlink_read_all_regs(sl, ®s); + stlink_step(sl); + fputs("++++++++++ write r0 = 0x12345678\n", stderr); + stlink_write_reg(sl, 0x12345678, 0); + stlink_read_reg(sl, 0, ®s); + stlink_read_all_regs(sl, ®s); +#endif +#if 0 + stlink_run(sl); + stlink_status(sl); + + stlink_force_debug(sl); + stlink_status(sl); +#endif +#if 0 /* read the system bootloader */ + fputs("\n++++++++++ reading bootloader ++++++++++++++++\n\n", stderr); + stlink_fread(sl, "/tmp/barfoo", sl->sys_base, sl->sys_size); +#endif +#if 0 /* read the flash memory */ + fputs("\n+++++++ read flash memory\n\n", stderr); + /* mark_buf(sl); */ + stlink_read_mem32(sl, 0x08000000, 4); +#endif +#if 0 /* flash programming */ + fputs("\n+++++++ program flash memory\n\n", stderr); + stlink_fwrite_flash(sl, "/tmp/foobar", 0x08000000); +#endif +#if 0 /* check file contents */ + fputs("\n+++++++ check flash memory\n\n", stderr); + { + const int res = stlink_fcheck_flash(sl, "/tmp/foobar", 0x08000000); + printf("_____ stlink_fcheck_flash() == %d\n", res); + } +#endif +#if 0 + fputs("\n+++++++ sram write and execute\n\n", stderr); + stlink_fwrite_sram(sl, "/tmp/foobar", sl->sram_base); + stlink_run_at(sl, sl->sram_base); +#endif + +#if 0 + stlink_run(sl); + stlink_status(sl); + //---------------------------------------------------------------------- + // back to mass mode, just in case ... + stlink_exit_debug_mode(sl); + stlink_current_mode(sl); + stlink_close(sl); +#endif + + //fflush(stderr); fflush(stdout); + return EXIT_SUCCESS; +} diff --git a/tests/stlink_usb.c b/tests/stlink_usb.c new file mode 100644 index 0000000..787ac12 --- /dev/null +++ b/tests/stlink_usb.c @@ -0,0 +1,102 @@ +#include +#include "stlink-common.h" + + +int main(int ac, char** av) { + stlink_t* sl; + reg regs; + + /* unused */ + ac = ac; + av = av; + + sl = stlink_open_usb(10, 1, NULL); + if (sl != NULL) { + printf("-- version\n"); + stlink_version(sl); + + printf("mode before doing anything: %d\n", stlink_current_mode(sl)); + + if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE) { + printf("-- exit_dfu_mode\n"); + stlink_exit_dfu_mode(sl); + } + + printf("-- enter_swd_mode\n"); + stlink_enter_swd_mode(sl); + + printf("-- mode after entering swd mode: %d\n", stlink_current_mode(sl)); + + printf("-- chip id: %#x\n", sl->chip_id); + printf("-- core_id: %#x\n", sl->core_id); + + cortex_m3_cpuid_t cpuid; + stlink_cpu_id(sl, &cpuid); + printf("cpuid:impl_id = %0#x, variant = %#x\n", cpuid.implementer_id, cpuid.variant); + printf("cpuid:part = %#x, rev = %#x\n", cpuid.part, cpuid.revision); + + printf("-- read_sram\n"); + static const uint32_t sram_base = 0x8000000; + uint32_t off; + for (off = 0; off < 16; off += 4) + stlink_read_mem32(sl, sram_base + off, 4); + + printf("FP_CTRL\n"); + stlink_read_mem32(sl, CM3_REG_FP_CTRL, 4); + + // no idea what reg this is.. */ + // stlink_read_mem32(sl, 0xe000ed90, 4); + // no idea what register this is... + // stlink_read_mem32(sl, 0xe000edf0, 4); + // offset 0xC into TIM11 register? TIMx_DIER? + // stlink_read_mem32(sl, 0x4001100c, 4); */ + + /* Test 32 bit Write */ + write_uint32(sl->q_buf,0x01234567); + stlink_write_mem32(sl,0x200000a8,4); + write_uint32(sl->q_buf,0x89abcdef); + stlink_write_mem32(sl,0x200000ac, 4); + stlink_read_mem32(sl, 0x200000a8, 4); + stlink_read_mem32(sl, 0x200000ac, 4); + + /* Test 8 bit write */ + write_uint32(sl->q_buf,0x01234567); + stlink_write_mem8(sl,0x200001a8,3); + write_uint32(sl->q_buf,0x89abcdef); + stlink_write_mem8(sl, 0x200001ac, 3); + stlink_read_mem32(sl, 0x200001a8, 4); + stlink_read_mem32(sl, 0x200001ac, 4); + + printf("-- status\n"); + stlink_status(sl); + + printf("-- reset\n"); + stlink_reset(sl); + stlink_force_debug(sl); + /* Test reg write*/ + stlink_write_reg(sl, 0x01234567, 3); + stlink_write_reg(sl, 0x89abcdef, 4); + stlink_write_reg(sl, 0x12345678, 15); + for (off = 0; off < 21; off += 1) + stlink_read_reg(sl, off, ®s); + + + stlink_read_all_regs(sl, ®s); + + printf("-- status\n"); + stlink_status(sl); + + printf("-- step\n"); + stlink_step(sl); + + printf("-- run\n"); + stlink_run(sl); + + printf("-- exit_debug_mode\n"); + stlink_exit_debug_mode(sl); + + stlink_close(sl); + } + + return 0; +}