mdebug

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 05cbeb429420e7a4de9f5359a9fb23ba10a03a66
parent 7d3808108efefdbf258c89673bb1147111f3796d
Author: Brian Swetland <swetland@frotz.net>
Date:   Wed,  3 Feb 2021 14:48:11 -0800

tools: picoboot: tool to talk to rpi pico / rp2040 bootloader

- Quick first pass.
- It only knows "flash" and "reboot" so far.

Diffstat:
MMakefile | 2++
Atools/picoboot.c | 254+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 256 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -48,6 +48,8 @@ $(call agent, cc13xx, 0x20000400, M3) $(call agent, nrf528xx, 0x20000400, M3) $(call agent, pico, 0x20000400, M0) +$(call program,picoboot,tools/picoboot.c) + # tool to pack the agents into a source file SRCS := tools/mkbuiltins.c $(call program,mkbuiltins,$(SRCS)) diff --git a/tools/picoboot.c b/tools/picoboot.c @@ -0,0 +1,254 @@ +// Copyright 2020, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0. + +#include <fcntl.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <libusb-1.0/libusb.h> + +#define ERASE_MASK (4096 - 1) +#define BLOCK_MASK (256 - 1) + +void *load_file(const char *fn, size_t *_sz) { + int fd; + off_t sz; + void *data = NULL; + fd = open(fn, O_RDONLY); + if (fd < 0) goto fail; + sz = lseek(fd, 0, SEEK_END); + if (sz < 0) goto fail; + if (lseek(fd, 0, SEEK_SET)) goto fail; + if ((data = malloc(sz + BLOCK_MASK)) == NULL) goto fail; + if (read(fd, data, sz) != sz) goto fail; + memset(data + sz, 0, BLOCK_MASK); + *_sz = sz; + return data; +fail: + if (data) free(data); + if (fd >= 0) close(fd); + return NULL; +} + + +#define PB_VID 0x2e8a +#define PB_PID 0x0003 + +#define PB_IFC 1 +#define PB_EP_IN 0x84 +#define PB_EP_OUT 0x03 + +#define PB_MAGIC 0x431fd10b + +typedef struct { + uint32_t magic; + uint32_t token; + uint8_t cmdid; + uint8_t argslen; + uint16_t reserved; + uint32_t xferlen; + union { + uint8_t u8[16]; + uint32_t u32[4]; + }; +} pbcmd_t; + +#define CMD_EXCLUSIVE_ACCESS 0x01 // excl:u8 +#define CMD_REBOOT 0x02 // pc:u32 sp:u32 delay:u32 +#define CMD_FLASH_ERASE 0x03 // addr:u32 size:u32 +#define CMD_READ 0x04 // addr:u32 size:u32 +#define CMD_WRITE 0x05 // addr:u32 size:u32 +#define CMD_EXIT_XIP 0x06 +#define CMD_ENTER_XIP 0x07 +#define CMD_EXEC 0x08 // addr:u32 +#define CMD_VECTORIZE_FLASH 0x09 // addr:u32 + +#define EA_NOT 0 +#define EA_EXCLUSIVE 1 +#define EA_EXCLUSIVE_AND_EJECT 2 + +static libusb_context* usbctx = NULL; +static libusb_device_handle* usbdev = NULL; +static uint32_t token = 0; + + +typedef struct { + uint32_t token; + uint32_t status; + uint8_t cmd; + uint8_t busy; + uint8_t reserved[6]; +} pbstatus_t; + +int pb_status(void) { + pbstatus_t pbs; + int r = libusb_control_transfer(usbdev, + LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_RECIPIENT_INTERFACE | + LIBUSB_ENDPOINT_IN, + 0x42, 0, 1, (void*) &pbs, sizeof(pbs), 1000); + if (r != sizeof(pbs)) { + fprintf(stderr, "picoboot: cannot read status %d\n", r); + return -1; + } + fprintf(stderr, "picoboot: status: tok=%08x sts=%08x cmd=%02x busy=%02x\n", + pbs.token, pbs.status, pbs.cmd, pbs.busy); + return pbs.status; +} + +int TRACE = 0; + +int pb_txn(pbcmd_t* cmd, void* data, int tx) { + cmd->magic = PB_MAGIC; + cmd->token = token++; + int xfer; + int r; + uint8_t tmp[64]; + + if (TRACE) fprintf(stderr, "picoboot: txn cmd=%02x len=%u\n", cmd->cmdid, cmd->xferlen); + if ((r = libusb_bulk_transfer(usbdev, PB_EP_OUT, (void*) cmd, sizeof(pbcmd_t), &xfer, 0)) < 0) { + fprintf(stderr, "picoboot: usb error sending command %d\n", r); + pb_status(); + return r; + } + if (TRACE) pb_status(); + + if (tx && (cmd->xferlen > 0)) { + xfer = 0; + if ((r = libusb_bulk_transfer(usbdev, PB_EP_OUT, data, cmd->xferlen, &xfer, 5000)) < 0) { + fprintf(stderr, "xfer %u\n", xfer); + fprintf(stderr, "picoboot: usb error sending payload %d\n", r); + pb_status(); + return r; + } + } + + xfer = 0; + if ((r = libusb_bulk_transfer(usbdev, PB_EP_IN, tmp, 64, &xfer, 0)) < 0) { + fprintf(stderr, "xfer %u\n", xfer); + fprintf(stderr, "picoboot: usb error receiving ack %d\n", r); + pb_status(); + return -1; + } + + if (TRACE) pb_status(); + return 0; +} + +void usage(void) { + fprintf(stderr, + "usage: picoboot ( cmd )*\n" + "\n" + "commands: -flash <filename> ( @hex ) write file to flash\n" + " -reboot reboot device\n" + " -help show this\n" + ); +} + +int main(int argc, char** argv) { + if (argc == 1) { + usage(); + return 0; + } + + if (libusb_init(&usbctx) < 0) return -1; + if ((usbdev = libusb_open_device_with_vid_pid(usbctx, PB_VID, PB_PID)) == NULL) { + fprintf(stderr, "picoboot: cannot find compatible device\n"); + return -1; + } + if ((libusb_claim_interface(usbdev, PB_IFC)) < 0) { + fprintf(stderr, "picoboot: cannot claim interface\n"); + return -1; + } + libusb_clear_halt(usbdev, PB_EP_IN); + libusb_clear_halt(usbdev, PB_EP_OUT); + + size_t sz; + void* data; + pbcmd_t cmd; + for (argc--, argv++; argc > 0; argc--, argv++) { + if (!strcmp(argv[0], "-reboot")) { + memset(&cmd, 0, sizeof(cmd)); + cmd.cmdid = CMD_REBOOT; + cmd.argslen = 12; + cmd.u32[0] = 0; // pc + cmd.u32[1] = 0; // ignored + cmd.u32[2] = 100; // ms delay + if (pb_txn(&cmd, NULL, 0)) { + fprintf(stderr, "picoboot: EXIT XIP failed\n"); + return -1; + } + } else if (!strcmp(argv[0], "-flash")) { + const char *fn = argv[1]; + if (argc < 2) { + fprintf(stderr, "picoboot: missing argument\n"); + return -1; + } + if ((data = load_file(fn, &sz)) == NULL) { + fprintf(stderr, "picoboot: failed to load '%s'\n", fn); + return -1; + } + argc--; argv++; + uint32_t addr = 0x10000000; + if ((argc > 1) && (argv[1][0] == '@')) { + addr = strtoul(argv[1] + 1, NULL, 16); + argc--; argv++; + } + if ((addr < 0x10000000) || (addr >= 0x11000000) || (addr & ERASE_MASK)) { + fprintf(stderr, "picoboot: bad flash start address 0x%08x\n", addr); + return -1; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmdid = CMD_EXCLUSIVE_ACCESS; + cmd.argslen = 1; + cmd.u8[0] = EA_EXCLUSIVE; + if (pb_txn(&cmd, NULL, 0)) { + fprintf(stderr, "picoboot: EXCLUSIVE ACCESS failed\n"); + return -1; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmdid = CMD_EXIT_XIP; + if (pb_txn(&cmd, NULL, 0)) { + fprintf(stderr, "picoboot: EXIT XIP failed\n"); + return -1; + } + + fprintf(stderr, "picoboot: erase flash @ 0x%08x\n", addr); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmdid = CMD_FLASH_ERASE; + cmd.argslen = 8; + cmd.u32[0] = addr; + cmd.u32[1] = (sz + ERASE_MASK) & (~ERASE_MASK); + if (pb_txn(&cmd, NULL, 0)) { + fprintf(stderr, "picoboot: ERASE failed\n"); + return -1; + } + + fprintf(stderr, "picoboot: write '%s' to flash @ 0x%08x\n", fn, addr); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmdid = CMD_WRITE; + cmd.argslen = 8; + cmd.u32[0] = addr; + cmd.u32[1] = sz; + cmd.xferlen = sz; + if (pb_txn(&cmd, data, 1)) { + fprintf(stderr, "picoboot: WRITE failed\n"); + return -1; + } + } else if (!strcmp(argv[0], "-help")) { + usage(); + return 0; + } else { + fprintf(stderr, "picoboot: unknown command '%s'\n", argv[0]); + usage(); + return -1; + } + } + + return 0; +}