-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathurloader.c
144 lines (121 loc) · 4.43 KB
/
urloader.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
//****************************************************************************
// Copyright 2022 Richard Hulme
//
// SPDX-License-Identifier: BSD-3-Clause
//
// Urloader for the RP2040
//
// Tries to start the flashloader if possible. If not, tries to start the
// application.
//
// If all else fails (application is invalid and no update image can be found)
// the flashloader will drop back to bootrom bootloader.
#include <stdint.h>
#include "hardware/regs/m0plus.h"
#include "hardware/clocks.h"
#include "hardware/watchdog.h"
#include "hardware/resets.h"
#include "hardware/dma.h"
#include "pico/bootrom.h"
#include "pico/binary_info.h"
#include "urloader.h"
// Start addresses defined in linker script
extern void* __FLASHLOADER_START;
extern void* __APPLICATION_START;
bi_decl(bi_program_version_string("1.01"));
#define bl2crc(x) (*((uint32_t*)(((uint32_t)(x) + 0xfc))))
//****************************************************************************
// This is normally provided as part of pico_stdlib so we have to provide it
// here if not we're not using it.
void exit(int ret)
{
(void)ret;
while(true)
tight_loop_contents();
}
//****************************************************************************
// Replace the standard 'atexit' with an empty version to avoid pulling in
// additional code that we don't need anyway.
int atexit(void *a, void (*f)(void*), void *d)
{
(void)a;
(void)f;
(void)d;
return 0;
}
//****************************************************************************
// Calculate the CRC32 (no reflection, no final XOR) of a block of data.
// This makes use of the DMA sniffer to calculate the CRC for us. Speed is
// not really a huge issue as most of the time we just need to check the
// boot2 image is valid (252 bytes) but using DMA ought to be faster than
// looping over the data without a lookup table and is certainly a lot smaller
// than the lookup table.
uint32_t crc32(const void *data, size_t len, uint32_t crc)
{
// Nothing else is running on the system, so it doesn't matter which
// DMA channel we use
static const uint8_t channel = 0;
uint8_t dummy;
dma_channel_config c = dma_channel_get_default_config(channel);
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
channel_config_set_read_increment(&c, true);
channel_config_set_write_increment(&c, false);
channel_config_set_sniff_enable(&c, true);
// Turn on CRC32 (non-bit-reversed data)
dma_sniffer_enable(channel, 0x00, true);
dma_hw->sniff_data = crc;
dma_channel_configure(
channel,
&c,
&dummy,
data,
len,
true // Start immediately
);
dma_channel_wait_for_finish_blocking(channel);
return(dma_hw->sniff_data);
}
//****************************************************************************
// Start the application at the given address if its boot2 image is valid.
// Will not return unless the image is invalid
int startApplication(uint32_t imgStart)
{
imgStart += XIP_BASE;
if(crc32((const void*)imgStart, 252, 0xffffffff) == bl2crc(imgStart))
{
// Hold DMA block in reset again
reset_block(RESETS_RESET_DMA_BITS);
// Code appears to be OK so we can map the code's
// vector table and jump to its start
asm volatile (
"mov r0, %[start]\n"
"ldr r1, =%[vtable]\n"
"str r0, [r1]\n"
"ldmia r0, {r0, r1}\n"
"msr msp, r0\n"
"bx r1\n"
:
: [start] "r" (imgStart + 0x100), [vtable] "X" (PPB_BASE + M0PLUS_VTOR_OFFSET)
:
);
}
// We will only return if the main application couldn't be started
return 0;
}
//****************************************************************************
int main(void)
{
// Take DMA block out of reset so we can use it to calculate CRCs
unreset_block_wait(RESETS_RESET_DMA_BITS);
// Try to start the flashloader
startApplication((uint32_t)&__FLASHLOADER_START);
// If that fails (and we return), try to start the application but let
// the application know the flashloader is invalid so it can try to
// re-flash the flashloader (if possible)
watchdog_hw->scratch[0] = URLOADER_BAD_FLASHLOADER;
startApplication((uint32_t)&__APPLICATION_START);
// Otherwise go to the bootrom bootloader as a last resort
// Disable resuscitation or the reset into the bootloader doesn't work
clocks_hw->resus.ctrl = 0;
reset_usb_boot(0, 0);
}