summaryrefslogtreecommitdiff
path: root/util/stm32mon.c
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2012-02-04 02:08:04 +0000
committerVincent Palatin <vpalatin@chromium.org>2012-02-06 20:05:33 +0000
commitc74bd90fb0b362250c0db935649944fa2d5868f8 (patch)
tree5f56e3a5493f6dea5d1140885c69909552dc68cc /util/stm32mon.c
parent6409913523b047ca7b1dd39bb51edf11f3eefdaa (diff)
downloadchrome-ec-c74bd90fb0b362250c0db935649944fa2d5868f8.tar.gz
stm32l: add a tool to flash the SoC using the serial monitor
When run with BOOT0=1 and BOOT1=0, the STM32L enters a system monitor which allows flashing over the serial port (USART1 pins PA9 and PA10). Implement commands to flash and run a program from a linux Host. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BUG=None TEST=on a serial port connected to Discovery board pins PA9 and PA10, run manually the various tools commands. Change-Id: I42f95ed50a56d82d728989149b3e47210af9dc96
Diffstat (limited to 'util/stm32mon.c')
-rw-r--r--util/stm32mon.c659
1 files changed, 659 insertions, 0 deletions
diff --git a/util/stm32mon.c b/util/stm32mon.c
new file mode 100644
index 0000000000..73850f2aab
--- /dev/null
+++ b/util/stm32mon.c
@@ -0,0 +1,659 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * STM32L SoC system monitor interface tool
+ */
+
+/* use cfmakeraw() */
+#define _BSD_SOURCE
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+/* Monitor command set */
+#define CMD_INIT 0x7f /* Starts the monitor */
+
+#define CMD_GETCMD 0x00 /* Gets the allowed commands */
+#define CMD_GETVER 0x01 /* Gets the bootloader version */
+#define CMD_GETID 0x02 /* Gets the Chip ID */
+#define CMD_READMEM 0x11 /* Reads memory */
+#define CMD_GO 0x21 /* Jumps to user code */
+#define CMD_WRITEMEM 0x31 /* Writes memory (SRAM or Flash) */
+#define CMD_ERASE 0x43 /* Erases n pages of Flash memory */
+#define CMD_EXTERASE 0x44 /* Erases n pages of Flash memory */
+#define CMD_WP 0x63 /* Enables write protect */
+#define CMD_WU 0x73 /* Disables write protect */
+#define CMD_RP 0x82 /* Enables the read protection */
+#define CMD_RU 0x92 /* Disables the read protection */
+
+#define RESP_NACK 0x1f
+#define RESP_ACK 0x79
+
+/* Extended erase special parameters */
+#define ERASE_ALL 0xffff
+#define ERASE_BANK1 0xfffe
+#define ERASE_BANK2 0xfffd
+
+/* known STM32 SoC parameters */
+struct stm32_def {
+ uint16_t id;
+ const char *name;
+ uint32_t flash_start;
+ uint32_t flash_size;
+} chip_defs[] = {
+ {0x416, "STM32L15xx", 0x08000000, 0x20000},
+ {0x420, "STM32F100xx", 0x08000000, 0x20000},
+ { 0 }
+};
+
+#define DEFAULT_TIMEOUT 2 /* seconds */
+#define DEFAULT_BAUDRATE B38400
+#define PAGE_SIZE 256
+
+/* store custom parameters */
+speed_t baudrate = DEFAULT_BAUDRATE;
+const char *serial_port = "/dev/ttyUSB1";
+const char *input_filename;
+const char *output_filename;
+
+/* optional command flags */
+enum {
+ FLAG_UNPROTECT = 0x01,
+ FLAG_ERASE = 0x02,
+ FLAG_GO = 0x04,
+};
+
+typedef struct {
+ int size;
+ uint8_t *data;
+} payload_t;
+
+int open_serial(const char *port)
+{
+ int fd, res;
+ struct termios cfg;
+
+ fd = open(port, O_RDWR | O_NOCTTY);
+ if (fd == -1) {
+ perror("Unable to open serial port");
+ return -1;
+ }
+
+ /* put the tty in "raw" mode at the defined baudrate */
+ res = tcgetattr(fd, &cfg);
+ if (res == -1) {
+ perror("Cannot read tty attributes");
+ close(fd);
+ return -1;
+ }
+ cfmakeraw(&cfg);
+ cfsetspeed(&cfg, baudrate);
+ /* serial mode is 8e1 */
+ cfg.c_cflag |= PARENB;
+ /* 200 ms timeout */
+ cfg.c_cc[VTIME] = 2;
+ cfg.c_cc[VMIN] = 0;
+ res = tcsetattr(fd, TCSANOW, &cfg);
+ if (res == -1) {
+ perror("Cannot set tty attributes");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int wait_for_ack(int fd)
+{
+ uint8_t resp;
+ int res;
+ time_t deadline = time(NULL) + DEFAULT_TIMEOUT;
+
+ while (time(NULL) < deadline) {
+ res = read(fd, &resp, 1);
+ if ((res < 0) && (errno != EAGAIN)) {
+ perror("Failed to read answer");
+ return -EIO;
+ }
+ if (res == 1) {
+ if (resp == RESP_ACK)
+ return 0;
+ else if (resp == RESP_NACK) {
+ fprintf(stderr, "NACK\n");
+ return -EINVAL;
+ } else {
+ fprintf(stderr, "Receive junk: %02x\n", resp);
+ }
+ }
+ }
+ return -ETIMEDOUT;
+}
+
+int send_command(int fd, uint8_t cmd, payload_t *loads, int cnt,
+ uint8_t *resp, int resp_size)
+{
+ int res, i, c;
+ payload_t *p;
+ int readcnt = 0;
+ int size;
+ uint8_t *data;
+ uint8_t crc = 0xff ^ cmd; /* XOR checksum */
+
+ /* Send the command index */
+ res = write(fd, &cmd, 1);
+ if (res <= 0) {
+ perror("Failed to write command");
+ return -1;
+ }
+ /* Send the checksum */
+ res = write(fd, &crc, 1);
+ if (res <= 0) {
+ perror("Failed to write checksum");
+ return -1;
+ }
+ /* Wait for the ACK */
+ if (wait_for_ack(fd) < 0) {
+ fprintf(stderr, "Failed to get command %02x ACK\n", cmd);
+ return -1;
+ }
+
+ /* Send the command payloads */
+ for (p = loads, c = 0; c < cnt ; c++, p++) {
+ crc = 0;
+ size = p->size;
+ data = p->data;
+ for (i = 0; i < size ; i++)
+ crc ^= data[i];
+ if (size == 1)
+ crc = 0xff ^ crc;
+
+ while (size) {
+ res = write(fd, data, size);
+ if (res < 0) {
+ perror("Failed to write command payload");
+ return -1;
+ }
+ size -= res;
+ data += res;
+ }
+
+ /* Send the checksum */
+ res = write(fd, &crc, 1);
+ if (res <= 0) {
+ perror("Failed to write checksum");
+ return -1;
+ }
+
+ /* Wait for the ACK */
+ if (wait_for_ack(fd) < 0) {
+ fprintf(stderr, "Failed to get payload %d ACK\n", c);
+ return -1;
+ }
+
+ }
+
+ /* Read the answer payload */
+ if (resp) {
+ while ((res = read(fd, resp, resp_size))) {
+ if (res < 0) {
+ perror("Failed to read payload");
+ return -1;
+ }
+ readcnt += res;
+ resp += res;
+ resp_size -= res;
+ }
+ }
+
+ return readcnt;
+}
+
+struct stm32_def *command_get_id(int fd)
+{
+ int res;
+ uint8_t id[4];
+ uint16_t chipid;
+ struct stm32_def *def;
+
+ res = send_command(fd, CMD_GETID, NULL, 0, id, sizeof(id));
+ if (res > 0) {
+ if (id[0] != 1 || id[3] != RESP_ACK) {
+ fprintf(stderr, "unknown ID : %02x %02x %02x %02x\n",
+ id[0], id[1], id[2], id[3]);
+ return NULL;
+ }
+ chipid = (id[1] << 8) | id[2];
+ for (def = chip_defs ; def->id ; def++)
+ if (def->id == chipid)
+ break;
+ if (def->id == 0)
+ def = NULL;
+ printf("ChipID 0x%03x : %s\n", chipid, def ? def->name : "???");
+ return def;
+ }
+
+ return NULL;
+}
+
+int init_monitor(int fd)
+{
+ int res, i;
+ uint8_t init = CMD_INIT;
+ uint8_t buffer[64];
+
+ printf("Waiting for the monitor startup ...");
+ fflush(stdout);
+
+ while (1) {
+ /* Send the command index */
+ res = write(fd, &init, 1);
+ if (res <= 0) {
+ perror("Failed to write command");
+ return -1;
+ }
+ /* Wait for the ACK */
+ res = wait_for_ack(fd);
+ if (res == 0)
+ break;
+ if (res == -EINVAL) {
+ /* we got NACK'ed, the loader might be already started
+ * let's ping it to check
+ */
+ if (command_get_id(fd)) {
+ printf("Monitor already started.\n");
+ return 0;
+ }
+ }
+ if (res < 0 && res != -ETIMEDOUT)
+ return -1;
+ printf(".");
+ fflush(stdout);
+ }
+ printf("Done.\n");
+
+ /* read trailing chars */
+ res = read(fd, buffer, sizeof(buffer));
+ if (res > 0) {
+ printf("Recv[%d]:", res);
+ for (i = 0; i < res; i++)
+ printf("%02x ", buffer[i]);
+ printf("\n");
+ }
+
+ return 0;
+}
+
+int command_get_commands(int fd)
+{
+ int res, i;
+ uint8_t cmds[64];
+
+ res = send_command(fd, CMD_GETCMD, NULL, 0, cmds, sizeof(cmds));
+ if (res > 0) {
+ if ((cmds[0] > sizeof(cmds) - 2) ||
+ (cmds[cmds[0] + 2] != RESP_ACK)) {
+ fprintf(stderr, "invalid GET answer (%02x...)\n",
+ cmds[0]);
+ return -1;
+ }
+ printf("Bootloader v%d.%d, commands : ",
+ cmds[1] >> 4, cmds[1] & 0xf);
+ for (i = 2; i < 2 + cmds[0]; i++)
+ printf("%02x ", cmds[i]);
+ printf("\n");
+ return 0;
+ }
+
+ return -1;
+}
+
+int command_read_mem(int fd, uint32_t address, uint32_t size, uint8_t *buffer)
+{
+ int res;
+ uint32_t remaining = size;
+ uint32_t addr_be;
+ uint8_t cnt;
+ payload_t loads[2] = {
+ {4, (uint8_t *)&addr_be},
+ {1, &cnt}
+ };
+
+ while (remaining) {
+ cnt = (remaining > PAGE_SIZE) ? PAGE_SIZE - 1 : remaining - 1;
+ addr_be = htonl(address);
+
+ printf(".");
+ fflush(stdout);
+ res = send_command(fd, CMD_READMEM, loads, 2, buffer, cnt + 1);
+ if (res < 0)
+ return -EIO;
+
+ buffer += cnt + 1;
+ address += cnt + 1;
+ remaining -= cnt + 1;
+ }
+
+ return size;
+}
+
+int command_write_mem(int fd, uint32_t address, uint32_t size, uint8_t *buffer)
+{
+ int res;
+ uint32_t remaining = size;
+ uint32_t addr_be;
+ uint32_t cnt;
+ uint8_t outbuf[257];
+ payload_t loads[2] = {
+ {4, (uint8_t *)&addr_be},
+ {sizeof(outbuf), outbuf}
+ };
+
+ while (remaining) {
+ cnt = (remaining > PAGE_SIZE) ? PAGE_SIZE : remaining;
+ addr_be = htonl(address);
+ outbuf[0] = cnt - 1;
+ loads[1].size = cnt + 1;
+ memcpy(outbuf + 1, buffer, cnt);
+
+ printf(".");
+ fflush(stdout);
+ res = send_command(fd, CMD_WRITEMEM, loads, 2, NULL, 0);
+ if (res < 0)
+ return -EIO;
+
+ buffer += cnt;
+ address += cnt;
+ remaining -= cnt;
+ }
+
+ return size;
+}
+
+int command_ext_erase(int fd, uint16_t count, uint16_t *pages)
+{
+ int res;
+ uint16_t count_be = htons(count);
+ payload_t load = { 2, (uint8_t *)&count_be };
+
+ if (count < 0xfff0) {
+ /* not a special value */
+ /* TODO implement page-list erase */
+ if (!pages)
+ return -EINVAL;
+ }
+
+ res = send_command(fd, CMD_EXTERASE, &load, 1, NULL, 0);
+ if (res >= 0)
+ printf("Flash erased.\n");
+ printf("ERASE res = %d\n", res);
+
+ return res;
+}
+
+int command_write_unprotect(int fd)
+{
+ int res;
+
+ res = send_command(fd, CMD_WU, NULL, 0, NULL, 0);
+ if (res < 0)
+ return -EIO;
+
+ /* Wait for the ACK */
+ if (wait_for_ack(fd) < 0) {
+ fprintf(stderr, "Failed to get write-protect ACK\n");
+ return -EINVAL;
+ }
+ printf("Flash write unprotected.\n");
+
+ /* This commands triggers a reset */
+ if (init_monitor(fd) < 0) {
+ fprintf(stderr, "Cannot recover after WP reset\n");
+ return -EIO;
+ }
+
+
+ return 0;
+}
+
+int command_go(int fd, uint32_t address)
+{
+ int res;
+ uint32_t addr_be = htonl(address);
+ payload_t load = { 4, (uint8_t *)&addr_be };
+
+ res = send_command(fd, CMD_GO, &load, 1, NULL, 0);
+ if (res < 0)
+ return -EIO;
+
+#if 0 /* this ACK should exist according to the documentation ... */
+ /* Wait for the ACK */
+ if (wait_for_ack(fd) < 0) {
+ fprintf(stderr, "Failed to get GO ACK\n");
+ return -EINVAL;
+ }
+#endif
+
+ printf("Program started at 0x%08x.\n", address);
+ return 0;
+}
+
+int read_flash(int fd, struct stm32_def *chip, const char *filename,
+ uint32_t offset, uint32_t size)
+{
+ int res;
+ FILE *hnd;
+ uint8_t *buffer = malloc(size);
+
+ if (!buffer) {
+ fprintf(stderr, "Cannot allocate %d bytes\n", size);
+ return -ENOMEM;
+ }
+
+ hnd = fopen(filename, "w");
+ if (!hnd) {
+ fprintf(stderr, "Cannot open file %s for writing\n", filename);
+ free(buffer);
+ return -EIO;
+ }
+
+ if (!size)
+ size = chip->flash_size;
+ offset += chip->flash_start;
+ printf("Reading %d bytes at 0x%08x ", size, offset);
+ res = command_read_mem(fd, offset, size, buffer);
+ if (res > 0) {
+ if (fwrite(buffer, res, 1, hnd) != 1)
+ fprintf(stderr, "Cannot write %s\n", filename);
+ }
+ printf(" %d bytes read.\n", res);
+
+ fclose(hnd);
+ free(buffer);
+ return res;
+}
+
+int write_flash(int fd, struct stm32_def *chip, const char *filename,
+ uint32_t offset)
+{
+ int res, written;
+ FILE *hnd;
+ int size = chip->flash_size;
+ uint8_t *buffer = malloc(size);
+
+ if (!buffer) {
+ fprintf(stderr, "Cannot allocate %d bytes\n", size);
+ return -ENOMEM;
+ }
+
+ hnd = fopen(filename, "r");
+ if (!hnd) {
+ fprintf(stderr, "Cannot open file %s for reading\n", filename);
+ free(buffer);
+ return -EIO;
+ }
+ if ((res = fread(buffer, 1, size, hnd)) <= 0) {
+ fprintf(stderr, "Cannot read %s\n", filename);
+ return -EIO;
+ }
+ fclose(hnd);
+
+ offset += chip->flash_start;
+ printf("Writing %d bytes at 0x%08x ", res, offset);
+ written = command_write_mem(fd, offset, res, buffer);
+ if (written != res)
+ fprintf(stderr, "Error writing to flash\n");
+ printf("Done.\n");
+
+ free(buffer);
+ return written;
+}
+
+static const struct option longopts[] = {
+ {"device", 1, 0, 'd'},
+ {"read", 1, 0, 'r'},
+ {"write", 1, 0, 'w'},
+ {"erase", 0, 0, 'e'},
+ {"go", 0, 0, 'g'},
+ {"help", 0, 0, 'h'},
+ {"unprotect", 0, 0, 'u'},
+ {"baudrate", 1, 0, 'b'},
+ {NULL, 0, 0, 0}
+};
+
+void display_usage(char *program)
+{
+ fprintf(stderr, "Usage: %s [-d <tty>] [-b <baudrate>] [-u] [-e]"
+ " [-r <file>] [-w <file>] [-g]\n", program);
+ fprintf(stderr, "--d[evice] <tty> : use <tty> as the serial port\n");
+ fprintf(stderr, "--b[audrate] <baudrate> : set serial port speed "
+ "to <baudrate> bauds\n");
+ fprintf(stderr, "--u[nprotect] : remove flash write protect\n");
+ fprintf(stderr, "--e[rase] : erase all the flash content\n");
+ fprintf(stderr, "--r[ead] <file> : read the flash content and "
+ "write it into <file>\n");
+ fprintf(stderr, "--w[rite] <file> : read <file> and "
+ "write it to flash\n");
+ fprintf(stderr, "--g[o] : jump to execute flash entrypoint\n");
+
+ exit(2);
+}
+
+speed_t parse_baudrate(const char *value)
+{
+ int rate = atoi(value);
+
+ switch (rate) {
+ case 9600:
+ return B9600;
+ case 19200:
+ return B19200;
+ case 38400:
+ return B38400;
+ case 57600:
+ return B57600;
+ case 115200:
+ return B115200;
+ default:
+ fprintf(stderr, "Invalid baudrate %s, using %d\n",
+ value, DEFAULT_BAUDRATE);
+ return DEFAULT_BAUDRATE;
+ }
+}
+
+int parse_parameters(int argc, char **argv)
+{
+ int opt, idx;
+ int flags = 0;
+
+ while ((opt = getopt_long(argc, argv, "b:d:eghr:w:u?",
+ longopts, &idx)) != -1) {
+ switch (opt) {
+ case 'b':
+ baudrate = parse_baudrate(optarg);
+ break;
+ case 'd':
+ serial_port = optarg;
+ break;
+ case 'e':
+ flags |= FLAG_ERASE;
+ break;
+ case 'g':
+ flags |= FLAG_GO;
+ break;
+ case 'h':
+ case '?':
+ display_usage(argv[0]);
+ break;
+ case 'r':
+ input_filename = optarg;
+ break;
+ case 'w':
+ output_filename = optarg;
+ break;
+ case 'u':
+ flags |= FLAG_UNPROTECT;
+ break;
+ }
+ }
+ return flags;
+}
+
+int main(int argc, char **argv)
+{
+ int ser;
+ struct stm32_def *chip;
+ int ret = 1;
+ int flags;
+
+ /* Parse command line options */
+ flags = parse_parameters(argc, argv);
+
+ /* Open the serial port tty */
+ ser = open_serial(serial_port);
+ if (ser < 0)
+ return 1;
+
+ /* Trigger embedded monitor detection */
+ if (init_monitor(ser) < 0)
+ goto terminate;
+
+ chip = command_get_id(ser);
+ if (!chip)
+ goto terminate;
+
+ command_get_commands(ser);
+
+ if (flags & FLAG_UNPROTECT)
+ command_write_unprotect(ser);
+
+ if (flags & FLAG_ERASE)
+ command_ext_erase(ser, ERASE_ALL, NULL);
+
+ if (input_filename)
+ read_flash(ser, chip, input_filename, 0, 0);
+
+ if (output_filename)
+ write_flash(ser, chip, output_filename, 0);
+
+ /* Run the program from flash */
+ if (flags & FLAG_GO)
+ command_go(ser, chip->flash_start);
+
+ /* Normal exit */
+ ret = 0;
+terminate:
+ /* Close serial port */
+ close(ser);
+ return ret;
+}