/* loader * Tom Trebisky 9-22-2016 * * Program to talk to the serial boot loader on an STM32 chip. * In particular, I am developing and testing on a STM32F103C8T6 * * **** This copy is wired up to do one and only one thing, * **** namely send the "disable readout protection" command * **** to the unit, this erases the flash and unlocks the chip. * **** It does NOT unlock the chip so this protocol can read it. * **** However, after this STLINK can read anyplace and * **** load code images to flash memory. * * The protocol is described in AN3155 * * I was dissatified with the existing programs. * They either gave confusing errors (including traceback) * and/or they lacked features to read memory areas. * * I tried initially to write this program in Ruby using * rubyserial, but that just was not working out. * * If this fails to start up and make a connection to the * boot loader, try doing a board reset, then launching * this program. This is common and typical. * * Here is a memory map of my chip from page 34 of the data sheet. * * 0x00000000 - 0x07ffffff - aliased to flash or sys memory depending on BOOT jumpers * 0x08000000 - 0x0800ffff - Flash (64K) * 0x1ffff000 - 0x1ffff7ff - Boot firmware in system memory * 0x1ffff800 - 0x1fffffff - option bytes * 0x20000000 - 0x20004fff - SRAM (20k) * 0x40000000 - 0x40023400 - peripherals * * The following area cannot be read or written by the boot rom * 0x20000000 - 0x20000200 - SRAM (512 bytes) */ #include #include #include #include #include char *port = "/dev/ttyUSB1"; int speed = B115200; int serial_fd; void error ( char * ); /* commands */ #define STM_INIT 0x7F #define STM_GET 0x00 /* get version and commands */ #define STM_GET2 0x01 /* get version and protect status */ #define STM_CHIP 0x02 /* get chip ID */ #define STM_READ 0x11 /* read memory */ #define STM_UNK1 0x12 /* unknown (listed in my devices list of commands) */ #define STM_GO 0x21 /* Jump to flash or sram */ #define STM_WRITE 0x31 /* write flash or sram */ #define STM_ERASE 0x43 /* erase one to all pages */ #define STM_ERASE_EXT 0x44 /* erase one to all, extended */ #define STM_WPRO 0x63 /* write protect specified sectors */ #define STM_UNPROTECT 0x73 /* disable write protect for all sectors */ /* Mentioned in AN3155, not supported by my device */ #define STM_RPRO 0x82 /* enable readout protection */ #define STM_RPRO_DIS 0x92 /* disable readout protection */ /* Extended erase is only available for v3.x bootloaders and above. * extended means a 2 byte address is allowed (for bigger devices?) */ /* responses */ #define STM_ACK 0x79 #define STM_NACK 0x1F void serial_setup () { struct termios termdata; serial_fd = open ( port, O_RDWR | O_NOCTTY | O_NDELAY ); if ( serial_fd < 0 ) error ( "Cannot open serial port" ); tcgetattr ( serial_fd, &termdata ); // Baud rate cfsetispeed ( &termdata, speed ); cfsetospeed ( &termdata, speed ); // input modes - strip and check parity termdata.c_iflag &= ~( IXON | IXOFF | IXANY ); termdata.c_iflag |= ( INPCK | ISTRIP ); // output modes - no hokey pokey processing termdata.c_oflag &= ~OPOST; // control modes. // Stop bits, we want one, not two. termdata.c_cflag &= ~CSTOPB; // We want 8 bits. termdata.c_cflag &= ~CSIZE; termdata.c_cflag |= CS8; // parity enabled, even - not odd termdata.c_cflag |= PARENB; termdata.c_cflag &= ~PARODD; // ignore modem lines, enable receiver termdata.c_cflag |= ( CLOCAL | CREAD ); // Disable HW and SW flow control (not in posix) termdata.c_cflag &= ~CRTSCTS; // local modes - this gives us raw input. termdata.c_lflag &= ~( ICANON | ECHO | ECHOE | ISIG ); // minimum characters for raw read // (so it will block until one character is ready). termdata.c_cc[VMIN] = 1; // timeout in deciseconds for raw read termdata.c_cc[VTIME] = 0; tcsetattr ( serial_fd, TCSANOW, &termdata ); tcflush ( serial_fd, TCIOFLUSH ); // Disable non-blocking stuff fcntl ( serial_fd, F_SETFL, 0 ); // fcntl ( serial_fd, F_SETFL, O_NONBLOCK ); } void write_one ( int val ) { char vbuf = val; write ( serial_fd, &vbuf, 1 ); } /* All commands are sent as the byte followed by its complement. * a logical extension of the checksum on all blocks. */ void write_cmd ( int cmd ) { char buf[2]; buf[0] = cmd; buf[1] = ~cmd; write ( serial_fd, buf, 2 ); } /* When you want to play fast and loose */ int read_one ( void ) { char val; read ( serial_fd, &val, 1 ); // return val & 0xff; return val; } /* Read with short timeout. * (10 milliseconds) */ int read_f ( char *buf ) { struct timeval tv; fd_set ios; int rv; tv.tv_sec = 0; tv.tv_usec = 1000 * 10; FD_ZERO ( &ios ); FD_SET ( serial_fd, &ios ); rv = select ( serial_fd + 1, &ios, NULL, NULL, &tv ); if ( rv < 1 ) return 0; return read ( serial_fd, buf, 1 ); } /* Read with timeout */ int read_t ( char *buf, int secs ) { struct timeval tv; fd_set ios; int rv; if ( secs > 0 ) { tv.tv_sec = secs; tv.tv_usec = 0; FD_ZERO ( &ios ); FD_SET ( serial_fd, &ios ); rv = select ( serial_fd + 1, &ios, NULL, NULL, &tv ); if ( rv < 1 ) return 0; } return read ( serial_fd, buf, 1 ); } int check_ack ( void ) { char ack; int n; n = read_t ( &ack, 0 ); if ( n == 1 && ack == STM_ACK ) return 1; return 0; } int stm_cmd ( int cmd ) { write_cmd ( cmd ); return check_ack (); } unsigned char checksum ( unsigned char *buf, int len ) { unsigned char rv = 0; int i; for ( i=0; i