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_headers boot_picoboot_headers
20 *
21 * \brief Header file for the PICOBOOT USB interface exposed by an RP2xxx chip in BOOTSEL mode
22 */
23 
24 #include "picoboot_constants.h"
25 
26 #define PICOBOOT_MAGIC 0x431fd10bu
27 
28 // --------------------------------------------
29 // CONTROL REQUESTS FOR THE PICOBOOT INTERFACE
30 // --------------------------------------------
31 
32 // size 0 OUT - un-stall EPs and reset
33 #define PICOBOOT_IF_RESET 0x41
34 
35 // size 16 IN - return the status of the last command
36 #define PICOBOOT_IF_CMD_STATUS 0x42
37 
38 // --------------------------------------------------
39 // COMMAND REQUESTS SENT TO THE PICOBOOT OUT ENDPOINT
40 // --------------------------------------------------
41 //
42 // picoboot_cmd structure of size 32 is sent to OUT endpoint
43 // transfer_length bytes are transferred via IN/OUT
44 // device responds on success with 0 length ACK packet set via OUT/IN
45 // device may stall the transferring endpoint in case of error
46 
47 enum picoboot_cmd_id {
48     PC_EXCLUSIVE_ACCESS = 0x1,
49     PC_REBOOT = 0x2,
50     PC_FLASH_ERASE = 0x3,
51     PC_READ = 0x84, // either RAM or FLASH
52     PC_WRITE = 0x5, // either RAM or FLASH (does no erase)
53     PC_EXIT_XIP = 0x6,
54     PC_ENTER_CMD_XIP = 0x7,
55     PC_EXEC = 0x8,
56     PC_VECTORIZE_FLASH = 0x9,
57     // RP2350 only below here
58     PC_REBOOT2 = 0xa,
59     PC_GET_INFO = 0x8b,
60     PC_OTP_READ = 0x8c,
61     PC_OTP_WRITE = 0xd,
62     //PC_EXEC2 = 0xe, // currently unused
63 };
64 
65 enum picoboot_status {
66     PICOBOOT_OK = 0,
67     PICOBOOT_UNKNOWN_CMD = 1,
68     PICOBOOT_INVALID_CMD_LENGTH = 2,
69     PICOBOOT_INVALID_TRANSFER_LENGTH = 3,
70     PICOBOOT_INVALID_ADDRESS = 4,
71     PICOBOOT_BAD_ALIGNMENT = 5,
72     PICOBOOT_INTERLEAVED_WRITE = 6,
73     PICOBOOT_REBOOTING = 7,
74     PICOBOOT_UNKNOWN_ERROR = 8,
75     PICOBOOT_INVALID_STATE = 9,
76     PICOBOOT_NOT_PERMITTED = 10,
77     PICOBOOT_INVALID_ARG = 11,
78     PICOBOOT_BUFFER_TOO_SMALL = 12,
79     PICOBOOT_PRECONDITION_NOT_MET = 13,
80     PICOBOOT_MODIFIED_DATA = 14,
81     PICOBOOT_INVALID_DATA = 15,
82     PICOBOOT_NOT_FOUND = 16,
83     PICOBOOT_UNSUPPORTED_MODIFICATION = 17,
84 };
85 
86 struct __packed picoboot_reboot_cmd {
87     uint32_t dPC; // 0 means reset into regular boot path
88     uint32_t dSP;
89     uint32_t dDelayMS;
90 };
91 
92 
93 // note this (with pc_sp) union member has the same layout as picoboot_reboot_cmd except with extra dFlags
94 struct __packed picoboot_reboot2_cmd {
95     uint32_t dFlags;
96     uint32_t dDelayMS;
97     uint32_t dParam0;
98     uint32_t dParam1;
99 };
100 
101 // used for EXEC, VECTORIZE_FLASH
102 struct __packed picoboot_address_only_cmd {
103     uint32_t dAddr;
104 };
105 
106 // used for READ, WRITE, FLASH_ERASE
107 struct __packed picoboot_range_cmd {
108     uint32_t dAddr;
109     uint32_t dSize;
110 };
111 
112 struct __packed picoboot_exec2_cmd {
113     uint32_t image_base;
114     uint32_t image_size;
115     uint32_t workarea_base;
116     uint32_t workarea_size;
117 };
118 
119 enum picoboot_exclusive_type {
120     NOT_EXCLUSIVE = 0,
121     EXCLUSIVE,
122     EXCLUSIVE_AND_EJECT
123 };
124 
125 struct __packed picoboot_exclusive_cmd {
126     uint8_t bExclusive;
127 };
128 
129 struct __packed picoboot_otp_cmd {
130     uint16_t wRow; // OTP row
131     uint16_t wRowCount; // number of rows to transfer
132     uint8_t bEcc; // use error correction (16 bit per register vs 24 (stored as 32) bit raw)
133 };
134 
135 
136 struct __packed picoboot_get_info_cmd {
137     uint8_t bType;
138     uint8_t bParam;
139     uint16_t wParam;
140     uint32_t dParams[3];
141 };
142 
143 // little endian
144 struct __packed __aligned(4) picoboot_cmd {
145     uint32_t dMagic;
146     uint32_t dToken; // an identifier for this token to correlate with a status response
147     uint8_t bCmdId; // top bit set for IN
148     uint8_t bCmdSize; // bytes of actual data in the arg part of this structure
149     uint16_t _unused;
150     uint32_t dTransferLength; // length of IN/OUT transfer (or 0) if none
151     union {
152         uint8_t args[16];
153         struct picoboot_reboot_cmd reboot_cmd;
154         struct picoboot_range_cmd range_cmd;
155         struct picoboot_address_only_cmd address_only_cmd;
156         struct picoboot_exclusive_cmd exclusive_cmd;
157         struct picoboot_reboot2_cmd reboot2_cmd;
158         struct picoboot_otp_cmd otp_cmd;
159         struct picoboot_get_info_cmd get_info_cmd;
160         struct picoboot_exec2_cmd exec2_cmd;
161     };
162 };
163 static_assert(32 == sizeof(struct picoboot_cmd), "picoboot_cmd must be 32 bytes big");
164 
165 struct __packed __aligned(4) picoboot_cmd_status {
166     uint32_t dToken;
167     uint32_t dStatusCode;
168     uint8_t bCmdId;
169     uint8_t bInProgress;
170     uint8_t _pad[6];
171 };
172 
173 static_assert(16 == sizeof(struct picoboot_cmd_status), "picoboot_cmd_status must be 16 bytes big");
174 
175 #endif
176