1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _BOOT_PICOBOOT_H
8 #define _BOOT_PICOBOOT_H
9 
10 #include <stdint.h>
11 #include <stdbool.h>
12 #include <assert.h>
13 
14 #ifndef NO_PICO_PLATFORM
15 #include "pico/platform.h"
16 #endif
17 
18 /** \file picoboot.h
19 *  \defgroup boot_picoboot boot_picoboot
20 *
21 * Header file for the PICOBOOT USB interface exposed by an RP2040 in BOOTSEL mode.
22 */
23 
24 #define PICOBOOT_MAGIC 0x431fd10bu
25 
26 // --------------------------------------------
27 // CONTROL REQUESTS FOR THE PICOBOOT INTERFACE
28 // --------------------------------------------
29 
30 // size 0 OUT - unstall EPs and reset
31 #define PICOBOOT_IF_RESET 0x41
32 
33 // size 16 IN - return the status of the last command
34 #define PICOBOOT_IF_CMD_STATUS 0x42
35 
36 // --------------------------------------------------
37 // COMMAND REQUESTS SENT TO THE PICOBOOT OUT ENDPOINT
38 // --------------------------------------------------
39 //
40 // picoboot_cmd structure of size 32 is sent to OUT endpoint
41 // transfer_length bytes are transferred via IN/OUT
42 // device responds on success with 0 length ACK packet set via OUT/IN
43 // device may stall the transferring endpoint in case of error
44 
45 enum picoboot_cmd_id {
46     PC_EXCLUSIVE_ACCESS = 0x1,
47     PC_REBOOT = 0x2,
48     PC_FLASH_ERASE = 0x3,
49     PC_READ = 0x84, // either RAM or FLASH
50     PC_WRITE = 5, // either RAM or FLASH (does no erase)
51     PC_EXIT_XIP = 0x6,
52     PC_ENTER_CMD_XIP = 0x7,
53     PC_EXEC = 0x8,
54     PC_VECTORIZE_FLASH = 0x9
55 };
56 
57 enum picoboot_status {
58     PICOBOOT_OK = 0,
59     PICOBOOT_UNKNOWN_CMD = 1,
60     PICOBOOT_INVALID_CMD_LENGTH = 2,
61     PICOBOOT_INVALID_TRANSFER_LENGTH = 3,
62     PICOBOOT_INVALID_ADDRESS = 4,
63     PICOBOOT_BAD_ALIGNMENT = 5,
64     PICOBOOT_INTERLEAVED_WRITE = 6,
65     PICOBOOT_REBOOTING = 7,
66     PICOBOOT_UNKNOWN_ERROR = 8,
67 };
68 
69 struct __packed picoboot_reboot_cmd {
70     uint32_t dPC; // 0 means reset into bootrom
71     uint32_t dSP;
72     uint32_t dDelayMS;
73 };
74 
75 // used for EXEC, VECTORIZE_FLASH
76 struct __packed picoboot_address_only_cmd {
77     uint32_t dAddr;
78 };
79 
80 // used for READ, WRITE, FLASH_ERASE
81 struct __packed picoboot_range_cmd {
82     uint32_t dAddr;
83     uint32_t dSize;
84 };
85 
86 enum picoboot_exclusive_type {
87     NOT_EXCLUSIVE = 0,
88     EXCLUSIVE,
89     EXCLUSIVE_AND_EJECT
90 };
91 
92 struct __packed picoboot_exclusive_cmd {
93     uint8_t bExclusive;
94 };
95 
96 // little endian
97 struct __packed __aligned(4) picoboot_cmd {
98     uint32_t dMagic;
99     uint32_t dToken; // an identifier for this token to correlate with a status response
100     uint8_t bCmdId; // top bit set for IN
101     uint8_t bCmdSize; // bytes of actual data in the arg part of this structure
102     uint16_t _unused;
103     uint32_t dTransferLength; // length of IN/OUT transfer (or 0) if none
104     union {
105         uint8_t args[16];
106         struct picoboot_reboot_cmd reboot_cmd;
107         struct picoboot_range_cmd range_cmd;
108         struct picoboot_address_only_cmd address_only_cmd;
109         struct picoboot_exclusive_cmd exclusive_cmd;
110     };
111 };
112 
113 static_assert(32 == sizeof(struct picoboot_cmd), "picoboot_cmd must be 32 bytes big");
114 
115 struct __packed __aligned(4) picoboot_cmd_status {
116     uint32_t dToken;
117     uint32_t dStatusCode;
118     uint8_t bCmdId;
119     uint8_t bInProgress;
120     uint8_t _pad[6];
121 };
122 
123 static_assert(16 == sizeof(struct picoboot_cmd_status), "picoboot_cmd_status must be 16 bytes big");
124 #endif
125