mdebug

cortex m series debugger
git clone http://frotz.net/git/mdebug.git
Log | Files | Refs | README | LICENSE

picoboot.c (6296B)


      1 // Copyright 2020, Brian Swetland <swetland@frotz.net>
      2 // Licensed under the Apache License, Version 2.0.
      3 
      4 #include <fcntl.h>
      5 #include <stdio.h>
      6 #include <stdint.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 #include <unistd.h>
     10 
     11 #include <libusb-1.0/libusb.h>
     12 
     13 #define ERASE_MASK (4096 - 1)
     14 #define BLOCK_MASK (256 - 1)
     15 
     16 void *load_file(const char *fn, size_t *_sz) {
     17         int fd;
     18         off_t sz;
     19         void *data = NULL;
     20         fd = open(fn, O_RDONLY);
     21         if (fd < 0) goto fail;
     22         sz = lseek(fd, 0, SEEK_END);
     23         if (sz < 0) goto fail;
     24         if (lseek(fd, 0, SEEK_SET)) goto fail;
     25         if ((data = malloc(sz)) == NULL) goto fail;
     26         if (read(fd, data, sz) != sz) goto fail;
     27         *_sz = sz;
     28         return data;
     29 fail:
     30         if (data) free(data);
     31         if (fd >= 0) close(fd);
     32         return NULL;
     33 }
     34 
     35 #define PB_VID 0x2e8a
     36 #define PB_PID 0x0003
     37 
     38 #define PB_IFC 1
     39 #define PB_EP_IN 0x84
     40 #define PB_EP_OUT 0x03
     41 
     42 #define PB_MAGIC 0x431fd10b
     43 
     44 typedef struct {
     45 	uint32_t magic;
     46 	uint32_t token;
     47 	uint8_t cmdid;
     48 	uint8_t argslen;
     49 	uint16_t reserved;
     50 	uint32_t xferlen;
     51 	union {
     52 		uint8_t u8[16];
     53 		uint32_t u32[4];
     54 	};
     55 } pbcmd_t;
     56 
     57 typedef struct {
     58 	uint32_t token;
     59 	uint32_t status;
     60 	uint8_t cmd;
     61 	uint8_t busy;
     62 	uint8_t reserved[6];
     63 } pbstatus_t;
     64 
     65 #define CMD_EXCLUSIVE_ACCESS 0x01 // excl:u8
     66 #define CMD_REBOOT           0x02 // pc:u32 sp:u32 delay:u32
     67 #define CMD_FLASH_ERASE      0x03 // addr:u32 size:u32
     68 #define CMD_READ             0x04 // addr:u32 size:u32
     69 #define CMD_WRITE            0x05 // addr:u32 size:u32
     70 #define CMD_EXIT_XIP         0x06
     71 #define CMD_ENTER_XIP        0x07
     72 #define CMD_EXEC             0x08 // addr:u32
     73 #define CMD_VECTORIZE_FLASH  0x09 // addr:u32
     74 
     75 #define EA_NOT 0
     76 #define EA_EXCLUSIVE 1
     77 #define EA_EXCLUSIVE_AND_EJECT 2
     78 
     79 static libusb_context* usbctx = NULL;
     80 static libusb_device_handle* usbdev = NULL;
     81 static uint32_t token = 0;
     82 
     83 int pb_status(void) {
     84 	pbstatus_t pbs;
     85 	int r = libusb_control_transfer(usbdev,
     86 			LIBUSB_REQUEST_TYPE_VENDOR |
     87 			LIBUSB_RECIPIENT_INTERFACE |
     88 			LIBUSB_ENDPOINT_IN,
     89 			0x42, 0, 1, (void*) &pbs, sizeof(pbs), 1000);
     90 	if (r != sizeof(pbs)) {
     91 		fprintf(stderr, "picoboot: cannot read status %d\n", r);
     92 		return -1;
     93 	}
     94 	fprintf(stderr, "picoboot: status: tok=%08x sts=%08x cmd=%02x busy=%02x\n",
     95 		pbs.token, pbs.status, pbs.cmd, pbs.busy);
     96 	return pbs.status;
     97 }
     98 
     99 int TRACE = 0;
    100 
    101 int pb_txn(pbcmd_t* cmd, void* data, int tx) {
    102 	cmd->magic = PB_MAGIC;
    103 	cmd->token = token++;
    104 	int xfer;
    105 	int r;
    106 	uint8_t tmp[64];
    107 
    108 	if (TRACE) fprintf(stderr, "picoboot: txn cmd=%02x len=%u\n", cmd->cmdid, cmd->xferlen);
    109 	if ((r = libusb_bulk_transfer(usbdev, PB_EP_OUT, (void*) cmd, sizeof(pbcmd_t), &xfer, 0)) < 0) {
    110 		fprintf(stderr, "picoboot: usb error sending command %d\n", r);
    111 		pb_status();
    112 		return r;
    113 	}
    114 	if (TRACE) pb_status();
    115 
    116 	if (tx && (cmd->xferlen > 0)) {
    117 		xfer = 0;
    118 		if ((r = libusb_bulk_transfer(usbdev, PB_EP_OUT, data, cmd->xferlen, &xfer, 5000)) < 0) {
    119 			fprintf(stderr, "xfer %u\n", xfer);
    120 			fprintf(stderr, "picoboot: usb error sending payload %d\n", r);
    121 			pb_status();
    122 			return r;
    123 		}
    124 	}
    125 
    126 	xfer = 0;
    127 	if ((r = libusb_bulk_transfer(usbdev, PB_EP_IN, tmp, 64, &xfer, 0)) < 0) {
    128 		fprintf(stderr, "xfer %u\n", xfer);
    129 		fprintf(stderr, "picoboot: usb error receiving ack %d\n", r);
    130 		pb_status();
    131 		return -1;
    132 	}
    133 
    134 	if (TRACE) pb_status();
    135 	return 0;
    136 }
    137 
    138 void usage(void) {
    139 	fprintf(stderr,
    140 		"usage: picoboot ( cmd )*\n"
    141 		"\n"
    142 		"commands: -flash <filename> ( @hex ) write file to flash\n"
    143 		"          -reboot                    reboot device\n"
    144 		"          -help                      show this\n"
    145 		);
    146 }
    147 
    148 int pb_io(uint8_t _cmd, uint32_t addr, uint32_t size, void* data) {
    149 	pbcmd_t cmd;
    150 	memset(&cmd, 0, sizeof(cmd));
    151 	cmd.cmdid = _cmd;
    152 	cmd.argslen = 8;
    153 	cmd.u32[0] = addr;
    154 	cmd.u32[1] = size;
    155 	if (data) cmd.xferlen = size;
    156 	return pb_txn(&cmd, data, 1);
    157 }
    158 
    159 int pb_cmd(uint8_t _cmd) {
    160 	pbcmd_t cmd;
    161 	memset(&cmd, 0, sizeof(cmd));
    162 	cmd.cmdid = _cmd;
    163 	switch (_cmd) {
    164 	case CMD_EXCLUSIVE_ACCESS:
    165 		cmd.argslen = 1;
    166 		cmd.u8[0] = EA_EXCLUSIVE;
    167 		break;
    168 	case CMD_REBOOT:
    169 		cmd.argslen = 12;
    170 		cmd.u32[2] = 100; // ms
    171 		break;
    172 	}
    173 	return pb_txn(&cmd, NULL, 0);
    174 }
    175 
    176 int main(int argc, char** argv) {
    177 	if (argc == 1) {
    178 		usage();
    179 		return 0;
    180 	}
    181 
    182 	if (libusb_init(&usbctx) < 0) return -1;
    183 	if ((usbdev = libusb_open_device_with_vid_pid(usbctx, PB_VID, PB_PID)) == NULL) {
    184 		fprintf(stderr, "picoboot: cannot find compatible device\n");
    185 		return -1;
    186 	}
    187 	if ((libusb_claim_interface(usbdev, PB_IFC)) < 0) {
    188 		fprintf(stderr, "picoboot: cannot claim interface\n");
    189 		return -1;
    190 	}
    191 	libusb_clear_halt(usbdev, PB_EP_IN);
    192 	libusb_clear_halt(usbdev, PB_EP_OUT);
    193 
    194 	size_t sz;
    195 	void* data;
    196 	for (argc--, argv++; argc > 0; argc--, argv++) {
    197 		if (!strcmp(argv[0], "-reboot")) {
    198 			if (pb_cmd(CMD_REBOOT)) {
    199 				fprintf(stderr, "picoboot: REBOOT failed\n");
    200 				return -1;
    201 			}
    202 		} else if (!strcmp(argv[0], "-flash")) {
    203 			const char *fn = argv[1];
    204 			if (argc < 2) {
    205 				fprintf(stderr, "picoboot: missing argument\n");
    206 				return -1;
    207 			}
    208 			if ((data = load_file(fn, &sz)) == NULL) {
    209 				fprintf(stderr, "picoboot: failed to load '%s'\n", fn);
    210 				return -1;
    211 			}
    212 			argc--; argv++;
    213 			uint32_t addr = 0x10000000;
    214 			if ((argc > 1) && (argv[1][0] == '@')) {
    215 				addr = strtoul(argv[1] + 1, NULL, 16);
    216 				argc--; argv++;
    217 			}
    218 			if ((addr < 0x10000000) || (addr >= 0x11000000) || (addr & ERASE_MASK)) {
    219 				fprintf(stderr, "picoboot: bad flash start address 0x%08x\n", addr);
    220 				return -1;
    221 			}
    222 			if (pb_cmd(CMD_EXCLUSIVE_ACCESS)) {
    223 				fprintf(stderr, "picoboot: EXCLUSIVE ACCESS failed\n");
    224 				return -1;
    225 			}
    226 			if (pb_cmd(CMD_EXIT_XIP)) {
    227 				fprintf(stderr, "picoboot: EXIT XIP failed\n");
    228 				return -1;
    229 			}
    230 			fprintf(stderr, "picoboot: erase flash @ 0x%08x\n", addr);
    231 			if (pb_io(CMD_FLASH_ERASE, addr, (sz + ERASE_MASK) & (~ERASE_MASK), NULL)) {
    232 				fprintf(stderr, "picoboot: ERASE failed\n");
    233 				return -1;
    234 			}
    235 			fprintf(stderr, "picoboot: write '%s' to flash @ 0x%08x\n", fn, addr);
    236 			if (pb_io(CMD_WRITE, addr, sz, data)) {
    237 				fprintf(stderr, "picoboot: WRITE failed\n");
    238 				return -1;
    239 			}
    240 		} else if (!strcmp(argv[0], "-help")) {
    241 			usage();
    242 			return 0;
    243 		} else {
    244 			fprintf(stderr, "picoboot: unknown command '%s'\n", argv[0]);
    245 			usage();
    246 			return -1;
    247 		}
    248 	}
    249 
    250 	return 0;
    251 }