forked from ARMmbed/nrf5x-dfu-bootloader
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bootloader.c
393 lines (316 loc) · 13.3 KB
/
bootloader.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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is property of Nordic Semiconductor ASA.
* Terms and conditions of usage are described in detail in NORDIC
* SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
*
* Licensees are granted free, non-transferable use of the information. NO
* WARRANTY of ANY KIND is provided. This heading must NOT be removed from
* the file.
*
*/
#include "bootloader.h"
#include <string.h>
#include "bootloader_types.h"
#include "bootloader_util.h"
#include "bootloader_settings.h"
#include "dfu.h"
#include "dfu_transport.h"
#include "nrf51.h"
#include "app_error.h"
#include "nrf_sdm.h"
#include "nordic_common.h"
#include "crc16.h"
#include "pstorage.h"
#include "app_scheduler.h"
#include "nrf_delay.h"
#define IRQ_ENABLED 0x01 /**< Field identifying if an interrupt is enabled. */
#define MAX_NUMBER_INTERRUPTS 32 /**< Maximum number of interrupts available. */
/**@brief Enumeration for specifying current bootloader status.
*/
typedef enum
{
BOOTLOADER_UPDATING, /**< Bootloader status for indicating that an update is in progress. */
BOOTLOADER_SETTINGS_SAVING, /**< Bootloader status for indicating that saving of bootloader settings is in progress. */
BOOTLOADER_COMPLETE, /**< Bootloader status for indicating that all operations for the update procedure has completed and it is safe to reset the system. */
BOOTLOADER_TIMEOUT, /**< Bootloader status field for indicating that a timeout has occured and current update process should be aborted. */
BOOTLOADER_RESET, /**< Bootloader status field for indicating that a reset has been requested and current update process should be aborted. */
} bootloader_status_t;
static pstorage_handle_t m_bootsettings_handle; /**< Pstorage handle to use for registration and identifying the bootloader module on subsequent calls to the pstorage module for load and store of bootloader setting in flash. */
static bootloader_status_t m_update_status; /**< Current update status for the bootloader module to ensure correct behaviour when updating settings and when update completes. */
/**@brief Function for handling callbacks from pstorage module.
*
* @details Handles pstorage results for clear and storage operation. For detailed description of
* the parameters provided with the callback, please refer to \ref pstorage_ntf_cb_t.
*/
static void pstorage_callback_handler(pstorage_handle_t * p_handle,
uint8_t op_code,
uint32_t result,
uint8_t * p_data,
uint32_t data_len)
{
// If we are in BOOTLOADER_SETTINGS_SAVING state and we receive an PSTORAGE_STORE_OP_CODE
// response then settings has been saved and update has completed.
if ((m_update_status == BOOTLOADER_SETTINGS_SAVING) && (op_code == PSTORAGE_STORE_OP_CODE))
{
m_update_status = BOOTLOADER_COMPLETE;
}
APP_ERROR_CHECK(result);
}
/**@brief Function for waiting for events.
*
* @details This function will place the chip in low power mode while waiting for events from
* the SoftDevice or other peripherals. When interrupted by an event, it will call the
* @ref app_sched_execute function to process the received event. This function will return
* when the final state of the firmware update is reached OR when a tear down is in
* progress.
*/
static void wait_for_events(void)
{
for (;;)
{
// Wait in low power state for any events.
uint32_t err_code = sd_app_evt_wait();
APP_ERROR_CHECK(err_code);
// Event received. Process it from the scheduler.
app_sched_execute();
if ((m_update_status == BOOTLOADER_COMPLETE) ||
(m_update_status == BOOTLOADER_TIMEOUT) ||
(m_update_status == BOOTLOADER_RESET))
{
// When update has completed or a timeout/reset occured we will return.
return;
}
}
}
bool bootloader_app_is_valid(uint32_t app_addr)
{
const bootloader_settings_t * p_bootloader_settings;
// There exists an application in CODE region 1.
if (*((uint32_t *)app_addr) == EMPTY_FLASH_MASK)
{
return false;
}
bool success = false;
bootloader_util_settings_get(&p_bootloader_settings);
// The application in CODE region 1 is flagged as valid during update.
if (p_bootloader_settings->bank_0 == BANK_VALID_APP)
{
uint16_t image_crc = 0;
// A stored crc value of 0 indicates that CRC checking is not used.
if (p_bootloader_settings->bank_0_crc != 0)
{
image_crc = crc16_compute((uint8_t *)DFU_BANK_0_REGION_START,
p_bootloader_settings->bank_0_size,
NULL);
}
success = (image_crc == p_bootloader_settings->bank_0_crc);
}
return success;
}
static void bootloader_settings_save(bootloader_settings_t * p_settings)
{
uint32_t err_code = pstorage_clear(&m_bootsettings_handle, sizeof(bootloader_settings_t));
APP_ERROR_CHECK(err_code);
err_code = pstorage_store(&m_bootsettings_handle,
(uint8_t *)p_settings,
sizeof(bootloader_settings_t),
0);
APP_ERROR_CHECK(err_code);
}
void bootloader_dfu_update_process(dfu_update_status_t update_status)
{
static bootloader_settings_t settings;
const bootloader_settings_t * p_bootloader_settings;
bootloader_util_settings_get(&p_bootloader_settings);
if (update_status.status_code == DFU_UPDATE_APP_COMPLETE)
{
settings.bank_0_crc = update_status.app_crc;
settings.bank_0_size = update_status.app_size;
settings.bank_0 = BANK_VALID_APP;
settings.bank_1 = BANK_INVALID_APP;
m_update_status = BOOTLOADER_SETTINGS_SAVING;
bootloader_settings_save(&settings);
}
else if (update_status.status_code == DFU_UPDATE_SD_COMPLETE)
{
settings.bank_0_crc = update_status.app_crc;
settings.bank_0_size = update_status.sd_size +
update_status.bl_size +
update_status.app_size;
settings.bank_0 = BANK_VALID_SD;
settings.bank_1 = BANK_INVALID_APP;
settings.sd_image_size = update_status.sd_size;
settings.bl_image_size = update_status.bl_size;
settings.app_image_size = update_status.app_size;
settings.sd_image_start = update_status.sd_image_start;
m_update_status = BOOTLOADER_SETTINGS_SAVING;
bootloader_settings_save(&settings);
}
else if (update_status.status_code == DFU_UPDATE_BOOT_COMPLETE)
{
settings.bank_0 = p_bootloader_settings->bank_0;
settings.bank_0_crc = p_bootloader_settings->bank_0_crc;
settings.bank_0_size = p_bootloader_settings->bank_0_size;
settings.bank_1 = BANK_VALID_BOOT;
settings.sd_image_size = update_status.sd_size;
settings.bl_image_size = update_status.bl_size;
settings.app_image_size = update_status.app_size;
m_update_status = BOOTLOADER_SETTINGS_SAVING;
bootloader_settings_save(&settings);
}
else if (update_status.status_code == DFU_UPDATE_SD_SWAPPED)
{
if (p_bootloader_settings->bank_0 == BANK_VALID_SD)
{
settings.bank_0_crc = 0;
settings.bank_0_size = 0;
settings.bank_0 = BANK_INVALID_APP;
}
// This handles cases where SoftDevice was not updated, hence bank0 keeps its settings.
else
{
settings.bank_0 = p_bootloader_settings->bank_0;
settings.bank_0_crc = p_bootloader_settings->bank_0_crc;
settings.bank_0_size = p_bootloader_settings->bank_0_size;
}
settings.bank_1 = BANK_INVALID_APP;
settings.sd_image_size = 0;
settings.bl_image_size = 0;
settings.app_image_size = 0;
m_update_status = BOOTLOADER_SETTINGS_SAVING;
bootloader_settings_save(&settings);
}
else if (update_status.status_code == DFU_TIMEOUT)
{
// Timeout has occurred. Close the connection with the DFU Controller.
uint32_t err_code = dfu_transport_close();
APP_ERROR_CHECK(err_code);
m_update_status = BOOTLOADER_TIMEOUT;
}
else if (update_status.status_code == DFU_BANK_0_ERASED)
{
settings.bank_0_crc = 0;
settings.bank_0_size = 0;
settings.bank_0 = BANK_INVALID_APP;
settings.bank_1 = p_bootloader_settings->bank_1;
bootloader_settings_save(&settings);
}
else if (update_status.status_code == DFU_RESET)
{
m_update_status = BOOTLOADER_RESET;
}
else
{
// No implementation needed.
}
}
uint32_t bootloader_init(void)
{
uint32_t err_code;
pstorage_module_param_t storage_params;
storage_params.cb = pstorage_callback_handler;
storage_params.block_size = sizeof(bootloader_settings_t);
storage_params.block_count = 1;
err_code = pstorage_init();
if (err_code != NRF_SUCCESS)
{
return err_code;
}
err_code = pstorage_register(&storage_params, &m_bootsettings_handle);
return err_code;
}
uint32_t bootloader_dfu_start(void)
{
uint32_t err_code;
// Clear swap if banked update is used.
err_code = dfu_init();
if (err_code != NRF_SUCCESS)
{
return err_code;
}
err_code = dfu_transport_update_start();
wait_for_events();
return err_code;
}
/**@brief Function for disabling all interrupts before jumping from bootloader to application.
*/
static void interrupts_disable(void)
{
uint32_t interrupt_setting_mask;
uint32_t irq = 0; // We start from first interrupt, i.e. interrupt 0.
// Fetch the current interrupt settings.
interrupt_setting_mask = NVIC->ISER[0];
for (; irq < MAX_NUMBER_INTERRUPTS; irq++)
{
if (interrupt_setting_mask & (IRQ_ENABLED << irq))
{
// The interrupt was enabled, and hence disable it.
NVIC_DisableIRQ((IRQn_Type)irq);
}
}
}
void bootloader_app_start(uint32_t app_addr)
{
// If the applications CRC has been checked and passed, the magic number will be written and we
// can start the application safely.
uint32_t err_code = sd_softdevice_disable();
APP_ERROR_CHECK(err_code);
interrupts_disable();
err_code = sd_softdevice_vector_table_base_set(CODE_REGION_1_START);
APP_ERROR_CHECK(err_code);
bootloader_util_app_start(CODE_REGION_1_START);
}
bool bootloader_dfu_sd_in_progress(void)
{
const bootloader_settings_t * p_bootloader_settings;
bootloader_util_settings_get(&p_bootloader_settings);
if (p_bootloader_settings->bank_0 == BANK_VALID_SD ||
p_bootloader_settings->bank_1 == BANK_VALID_BOOT)
{
return true;
}
return false;
}
uint32_t bootloader_dfu_sd_update_continue(void)
{
uint32_t err_code;
if ((dfu_sd_image_validate() == NRF_SUCCESS) &&
(dfu_bl_image_validate() == NRF_SUCCESS))
{
return NRF_SUCCESS;
}
// Ensure that flash operations are not executed within the first 100 ms seconds to allow
// a debugger to be attached.
nrf_delay_ms(100);
err_code = dfu_sd_image_swap();
APP_ERROR_CHECK(err_code);
err_code = dfu_sd_image_validate();
APP_ERROR_CHECK(err_code);
err_code = dfu_bl_image_swap();
APP_ERROR_CHECK(err_code);
return err_code;
}
uint32_t bootloader_dfu_sd_update_finalize(void)
{
dfu_update_status_t update_status = {DFU_UPDATE_SD_SWAPPED, };
bootloader_dfu_update_process(update_status);
wait_for_events();
return NRF_SUCCESS;
}
void bootloader_settings_get(bootloader_settings_t * const p_settings)
{
bootloader_settings_t bootloader_settings;
(void) pstorage_load((uint8_t *)&bootloader_settings,
&m_bootsettings_handle,
sizeof(bootloader_settings_t),
0);
p_settings->bank_0 = bootloader_settings.bank_0;
p_settings->bank_0_crc = bootloader_settings.bank_0_crc;
p_settings->bank_0_size = bootloader_settings.bank_0_size;
p_settings->bank_1 = bootloader_settings.bank_1;
p_settings->sd_image_size = bootloader_settings.sd_image_size;
p_settings->bl_image_size = bootloader_settings.bl_image_size;
p_settings->app_image_size = bootloader_settings.app_image_size;
p_settings->sd_image_start = bootloader_settings.sd_image_start;
}