-
-
Notifications
You must be signed in to change notification settings - Fork 725
/
Copy pathSAMD_BootloaderUpdater.cpp
116 lines (91 loc) · 3.49 KB
/
SAMD_BootloaderUpdater.cpp
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
/*
This file is part of the SAMD Bootloader Updater library.
Copyright (c) 2018 Arduino SA. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "SAMD_BootloaderUpdater.h"
#define PAGE_SIZE (64)
#define PAGES (4096)
#define MAX_FLASH (PAGE_SIZE * PAGES)
#define ROW_SIZE (PAGE_SIZE * 4)
__attribute__((aligned (4)))
const uint8_t booloaderData[8192] = {
#if defined(ARDUINO_SAMD_MKRVIDOR4000)
#include "bootloaders/mkrvidor4000.h"
#else
#error "Unsupported board!"
#endif
};
#define BOOTLOADER_START 0x00000000
#define USER_ROW_START 0x00804000
extern "C" {
// these functions must be in RAM (.data) and NOT inlined
// as they erase and copy the sketch data in flash
__attribute__ ((long_call, noinline, section (".data#")))
static void eraseFlash(uint32_t address, int length)
{
for (int i = 0; i < length; i += ROW_SIZE) {
NVMCTRL->ADDR.reg = ((uint32_t)((uint32_t)address + i)) / 2;
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
while (!NVMCTRL->INTFLAG.bit.READY);
}
}
__attribute__ ((long_call, noinline, section (".data#")))
static void readFlash(uint32_t src, void* dest, int length)
{
memcpy(dest, (void*)src, length);
}
__attribute__ ((long_call, noinline, section (".data#")))
static void writeFlash(uint32_t dest, const void* src, int length)
{
volatile uint32_t* d = (uint32_t*)dest;
const volatile uint32_t* s = (const uint32_t*)src;
for (int i = 0; i < length; i += 4) {
*d++ = *s++;
while (!NVMCTRL->INTFLAG.bit.READY);
}
}
}
bool SAMD_BootloaderUpdaterClass::needsUpdate()
{
return (memcmp(BOOTLOADER_START, booloaderData, sizeof(booloaderData)) == 0);
}
int SAMD_BootloaderUpdaterClass::update(void(*progressCallback)(float))
{
// enable auto page writes
NVMCTRL->CTRLB.bit.MANW = 0;
// read the user row
uint32_t userRow[PAGE_SIZE / sizeof(uint32_t)];
readFlash(USER_ROW_START, userRow, sizeof(userRow));
if ((userRow[0] & 0x00000007) != 0x00000007) {
// bootloader is protected, unprotect it
userRow[0] |= 0x00000007;
// erase the user row and flash the updated value
eraseFlash(USER_ROW_START, sizeof(userRow));
writeFlash(USER_ROW_START, userRow, sizeof(userRow));
}
if (progressCallback) {
progressCallback(0.0);
}
#define CHUNK_SIZE (ROW_SIZE * 2)
// erase and copy the flash row by row
for (size_t i = 0; i < sizeof(booloaderData); i += CHUNK_SIZE) {
eraseFlash(BOOTLOADER_START + i, CHUNK_SIZE);
writeFlash(BOOTLOADER_START + i, &booloaderData[i], CHUNK_SIZE);
if (progressCallback) {
progressCallback((i + CHUNK_SIZE) * 100.0 / sizeof(booloaderData));
}
}
return needsUpdate() ? 1 : 0;
}
SAMD_BootloaderUpdaterClass SAMD_BootloaderUpdater;