-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathflasher.c
345 lines (297 loc) · 9.26 KB
/
flasher.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
#include <ch32v003fun.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "rv003usb/rv003usb/rv003usb.h"
#include "swio.h"
#include "v003_flash.h"
#define BACKUP_ADDR 0x08003800
#define BOOTLOAD_ADDR 0x1FFFF000
uint8_t scratch[80];
volatile uint8_t print_buf[8] = {0x8A, 'd', 'm', 'l', 'o', 'c', 'k'};
volatile uint8_t transmission_done;
bool dm_unlocked = false;
bool backup_present = false;
// "bootloader.bin" will be inserted into this structure at build time
typedef struct {
uint8_t *binary_addr;
uint32_t binary_size;
} bootloader_binary;
extern const bootloader_binary bootloader_image;
volatile int last = 0;
// Process incomming strings from the terminal
void handle_debug_input(int numbytes, uint8_t * data) {
last = data[0];
}
// DM unlock sequence taken from minichlik's code
void attempt_unlock(uint8_t t1coeff) {
if (*DMDATA0 == *DMDATA1 && *DMDATA0) {
AFIO->PCFR1 &= ~(AFIO_PCFR1_SWJ_CFG);
funPinMode(PD1, GPIO_Speed_50MHz | GPIO_CNF_OUT_PP);
MCFWriteReg32(DMSHDWCFGR, 0x5aa50000 | (1<<10), t1coeff); // Shadow Config Reg
MCFWriteReg32(DMCFGR, 0x5aa50000 | (1<<10), t1coeff); // CFGR (1<<10 == Allow output from slave)
MCFWriteReg32(DMCFGR, 0x5aa50000 | (1<<10), t1coeff);
MCFWriteReg32(DMABSTRACTAUTO, 0x00000000, t1coeff);
MCFWriteReg32(DMCONTROL, 0x80000001 | (1<<10), t1coeff);
MCFWriteReg32(DMCONTROL, 0x40000001 | (1<<10), t1coeff);
}
}
void usb_handle_user_in_request(struct usb_endpoint * e, uint8_t * scratchpad, int endp, uint32_t sendtok, struct rv003usb_internal * ist) {
// Make sure we only deal with control messages. Like get/set feature reports.
if(endp) {
usb_send_empty(sendtok);
}
}
void usb_handle_user_data(struct usb_endpoint * e, int current_endpoint, uint8_t * data, int len, struct rv003usb_internal * ist) {
// Differentiate between ID for the terminal and rest of incomming data
if (e->custom == 0xAB) {
// We reading only one character at a time, the same as minichlink
last = data[1];
} else {
int offset = e->count<<3;
int torx = e->max_len - offset;
if (torx > len) torx = len;
if (torx > 0) {
memcpy(scratch + offset, data, torx);
e->count++;
if ((e->count << 3) >= e->max_len) {
transmission_done = e->max_len;
}
}
}
}
void usb_handle_hid_get_report_start(struct usb_endpoint * e, int reqLen, uint32_t lValueLSBIndexMSB) {
// You can check the lValueLSBIndexMSB word to decide what you want to do here
// But, whatever you point this at will be returned back to the host PC where
// it calls hid_get_feature_report.
//
// Please note, that on some systems, for this to work, your return length must
// match the length defined in HID_REPORT_COUNT, in your HID report, in usb_config.h
// Check feature ID of a request. 0xAB is ID for the terminal
if ((lValueLSBIndexMSB & 0xFF) == 0xAB) {
// Checking if DM is unlocked, without it we can't use DMDATA0/1
if ((*DMDATA0 != *DMDATA1) && *DMDATA0) dm_unlocked = true;
if (dm_unlocked) {
// Copy data from DMDATA0/1 and clear them to signal to printf that it has been read
memcpy(print_buf, (uint8_t*)DMDATA0, 8);
// memcpy(print_buf+4, (uint8_t*)DMDATA1, 4);
*DMDATA0 = 0x0;
*DMDATA1 = 0x0;
}
// Set outgoing buffer to one we just copied to
e->opaque = print_buf;
e->max_len = 8;
} else {
if (reqLen > sizeof(scratch)) reqLen = sizeof(scratch);
e->opaque = scratch;
e->max_len = reqLen;
}
}
void usb_handle_hid_set_report_start(struct usb_endpoint * e, int reqLen, uint32_t lValueLSBIndexMSB) {
// Here is where you get an alert when the host PC calls hid_send_feature_report.
//
// You can handle the appropriate message here. Please note that in this
// example, the data is chunked into groups-of-8-bytes.
//
// Note that you may need to make this match HID_REPORT_COUNT, in your HID
// report, in usb_config.h
if ((lValueLSBIndexMSB & 0xFF) == 0xAB) {
reqLen = 8;
} else {
if (reqLen > sizeof(scratch)) reqLen = sizeof(scratch);
}
// Using e->caustom as a container to store feature ID of incomming data
e->custom = (lValueLSBIndexMSB & 0xFF);
e->max_len = reqLen;
}
void usb_handle_other_control_message(struct usb_endpoint * e, struct usb_urb * s, struct rv003usb_internal * ist) {
LogUEvent(SysTick->CNT, s->wRequestTypeLSBRequestMSB, s->lValueLSBIndexMSB, s->wLength);
}
void reboot_to_bootloader() {
__disable_irq();
// Unlock the ability to write to the MODE flag of STATR register.
FLASH->BOOT_MODEKEYR = 0x45670123; // KEY1
FLASH->BOOT_MODEKEYR = 0xCDEF89AB; // KEY2
// Set to run BOOT flash at next reset (MODE = 1).
FLASH->STATR = 0x4000;
// Clear all reset status flags (RMVF = 1).
RCC->RSTSCKR |= 0x1000000;
// Perform software reset (KEYCODE = 0xBEEF, RESETSYS = 1).
PFIC->CFGR = 0xBEEF0080;
}
int check_backup() {
uint32_t* addr = (uint32_t*)BACKUP_ADDR;
uint32_t* b_addr = (uint32_t*)BOOTLOAD_ADDR;
uint32_t len = bootloader_image.binary_size / 4;
int r = 0;
for (int i = 0; i < 16; i++) {
if (addr[i] != 0xFFFFFFFF) {
backup_present = true;
r = 1;
break;
}
}
if (backup_present) {
for (int i = 0; i < len; i++) {
if (addr[i] != b_addr[i]) {
r = -1;
break;
}
}
}
return r;
}
bool check_bootloader() {
uint32_t* addr = (uint32_t*)bootloader_image.binary_addr;
uint32_t* b_addr = (uint32_t*)BOOTLOAD_ADDR;
uint32_t len = bootloader_image.binary_size / 4;
for (int i = 0; i < len; i++) {
if (addr[i] != b_addr[i]) return false;
}
return true;
}
int do_backup() {
int ret = 0;
Delay_Ms(2);
flash_unlock(false, false);
printf("Flash unlocked\n");
Delay_Ms(10);
ret = flash_write_bulk((uint8_t*)BACKUP_ADDR, (uint8_t*)BOOTLOAD_ADDR, 1920, true);
printf("\nBackup is done\n");
return ret;
}
int write_bootloader(bool from_backup) {
int ret = 0;
Delay_Ms(2);
ret = flash_unlock(false, true);
if (!ret) {
printf("Flash unlocked\n");
} else {
printf("\nFailed to unlock flash: %d\n", ret);
return ret;
}
Delay_Ms(10);
if (from_backup) {
ret = flash_write_bulk((uint8_t*)BOOTLOAD_ADDR, (uint8_t*)BACKUP_ADDR, 1920, true);
if (!ret) printf("\nBackup restored\n");
else printf("\nRestoring backup failed: %d\n", ret);
} else {
ret = flash_write_bulk((uint8_t*)BOOTLOAD_ADDR, bootloader_image.binary_addr, bootloader_image.binary_size, true);
if (!ret) printf("\nBootloader written\n");
else printf("\nWriting bootloader failed: %d\n", ret);
}
return ret;
}
int main() {
SystemInit();
SysTick->CNT = 0;
Delay_Ms(1); // Ensures USB re-enumeration after bootloader or reset; Spec demand >2.5µs ( TDDIS )
usb_setup();
int ui_state = 0;
// Check if SWIO terminal was connected
if ((*DMDATA0 != *DMDATA1) && *DMDATA0) dm_unlocked = true;
while(1) {
for(int i = 0; i < 10000; i++) {
poll_input();
}
// Process incomming HID command
if(transmission_done) {
switch (scratch[1]) {
case 0xA1:
// reboot into bootloader
reboot_to_bootloader();
break;
case 0xA5:
attempt_unlock(scratch[2]);
break;
}
transmission_done = 0;
}
switch (ui_state) {
case 0:
if (dm_unlocked) { // Only print UI if there is someone to read it
printf("\n\n");
if (bootloader_image.binary_size > 1920) {
printf("Bootloader image is bigger than BOOT area.\nExiting\n\n");
ui_state = 11;
break;
}
int backup = check_backup();
// printf("foo 0x%x + 0x%x\n", bootloader_image.binary_addr, bootloader_image.binary_size);
if (check_bootloader()) {
printf("\nCurrent bootloader is the same as we have here.\n");
if (backup == -1) {
printf("But there is a backup.\nType 'r' to restore the backup\n");
ui_state = 4;
} else {
printf("Nothing to update\n");
ui_state = 10;
}
break;
}
if (!backup) {
printf("\nFirst we need to make a backup.\nType 'b' to proceed\n");
ui_state = 1;
} else if (backup == 1) {
printf("\nBootloader already backed up.\nType 'u' to update the bootloader\n");
ui_state = 2;
} else if (backup == -1) {
printf("\nBackup found.\nType 'r' to restore the backup\nOr type 'u' to update the bootloader\n");
ui_state = 3;
}
}
last = 0; // Reset incomming terminal string
break;
case 1:
if (last == 'b') {
printf("Backing up\n");
if (!do_backup()) {
printf("Type 'u' to update the bootloader\n");
ui_state = 2;
} else {
printf("Backup failed.\n Type 'b' to retry\n");
}
last = 0;
}
break;
case 2:
if (last == 'u') {
printf("Writing bootloader\n");
write_bootloader(false);
ui_state = 10;
last = 0;
}
break;
case 3:
if (last == 'r') {
printf("Restoring from backup\n");
write_bootloader(true);
ui_state = 10;
last = 0;
} else if (last == 'u') {
printf("Writing bootloader\n");
write_bootloader(false);
ui_state = 10;
last = 0;
}
break;
case 4:
if (last == 'r') {
printf("Restoring from backup\n");
write_bootloader(true);
ui_state = 10;
last = 0;
}
break;
case 10:
printf("You can now disconnect the device\n\n");
ui_state++;
break;
default:
break;
}
last = 0;
// Ack the terminal (minichlink needs this)
printf("%c", last);
}
}