MCCI Trusted Bootloader
Simple trusted bootloader and tools for small embedded systems
mccibootloader_main.c
Go to the documentation of this file.
1/* mccibootloader_main.c Tue Jul 14 2020 11:31:05 tmm */
2
3/*
4
5Module: mccibootloader_main.c
6
7Function:
8 Entry point for the MCCI bootloader.
9
10Version:
11 V0.1.0 Tue Jul 14 2020 11:31:05 tmm Edit level 1
12
13Copyright notice:
14 This file copyright (C) 2020 by
15
16 MCCI Corporation
17 3520 Krums Corners Road
18 Ithaca, NY 14850
19
20 An unpublished work. All rights reserved.
21
22 This file is proprietary information, and may not be disclosed or
23 copied without the prior permission of MCCI Corporation.
24
25Author:
26 Terry Moore, MCCI Corporation July 2020
27
28Revision history:
29 0.1.0 Tue Jul 14 2020 11:31:05 tmm
30 Module created.
31
32*/
33
34#include "mcci_bootloader.h"
35
38
39/****************************************************************************\
40|
41| Manifest constants & typedefs.
42|
43\****************************************************************************/
44
45
46/****************************************************************************\
47|
48| Read-only data.
49|
50\****************************************************************************/
51
52
53/****************************************************************************\
54|
55| Variables.
56|
57\****************************************************************************/
58
59McciBootloader_AppInfo_t g_McciBootloader_incomingAppInfo;
60
61/*
62
63Name: McciBootloader_main()
64
65Function:
66 Entry point for MCCI STM32 bootloader.
67
68Definition:
69 void McciBootloader_main(void);
70
71Description:
72 This function is called by the ROM boot loader, and implements
73 the MCCI SPI flash bootloading functionality for MCCI
74 Catena baords based on the STM32L0.
75
76 Input parameters come from the linker via symbols defined in
77 the ld script for the linker.
78
79Returns:
80 No explicit result. Doesn't return.
81
82Implementation notes:
83 Our reuse model is to have non-portable code at the
84 outer edges, and (potentially) portable code as library
85 functions. So the logic for parsing the flash structure,
86 checking the signature on the flash, etc., is in pure
87 functions that shoudl have no hardware dependency. Those
88 modules should not #include anything that is STM32L0-
89 specific. The outer drivers are allowed to do STM32L0-
90 specific things. As usual at MCCI, we avoid #if.
91
92 Functions named McciBootloaderPlatform_* map (via static
93 inline) to code that is specific to the targeted platform.
94 The MCCI implementation uses our usual style of function
95 pointers and drivers; but the table is static.
96
97 It takes a long time to verify a ed25519 signature on the STM32L0;
98 so we only check signatures when deciding whether to update
99 the flash. Otherwise we check the SHA512. The program format
100 still includes the signature, but we only check this when
101 deciding whether to apply it to the flash.
102
103 The sequence is as follows:
104
105 1. If the boardloader hash is not valid, we stop with a failure code.
106 2. Otherwise, there is a saved boot loader state. The state can be
107 either "update" or "go". If state is "go", we compute the
108 hash on the application image; if it's valid, we launch it.
109 3. The flag is set to "update", or the application is invalid, so we ask
110 the platform to enable the flash
111 driver. (This may bring up power to the flash chip, bring up the
112 SPI bus, etc.) We also ask the driver to enable notifications
113 as this is going to take a while.
114 4. We read through the flash application image.
115 (Including hash and signature.) This involves reading 4k at a
116 time and doing the signature check.
117 5. If the flash app image is not valid, and the application image
118 is valid, we reset the update flag and launch the application.
119 6. If the flash image is valid, we erase the flash, clear the update
120 flag, then program the new image into the app flash. When done,
121 we check the hash. If not valid, we continue to the next step.
122 7. If the flash app image is not valid, and the application image is
123 not valid, we repeat step 4 using the "safety app" image. If the
124 "safety app" is valid, we repeat step 6 with the safety app. If
125 that fails, we fail with an indication (as at step 1).
126
127 States we're trying to establish:
128
129 Case Boot App Flag Flash Safe State
130 (1) NG - - - - Halt with indication
131 (2) OK OK N - - Launch app
132 (3) OK OK Y NG - Launch app, clear flag
133 (4) OK OK Y OK - Load flash, clear flag & reevaluate
134 (Power failure during flash will
135 bring us up in some App NG state)
136 (5) OK NG - OK - Load flash, clear flag & reevaluate
137 (6) OK NG - NG OK Load fallback flash, clear flag & and reevaluate
138 (7) OK NG - NG NG Halt with indication
139
140*/
141
142void
144 {
146
147 /* run the platform entry code. This must be minimal, if it exists at all */
149
150 /* our first job is to check the hash of the boot loader */
153 bootloaderSize
154 ))
155 {
156 /* Case (1): boot loader isn't valid */
158 }
159
160 const McciBootloader_AppInfo_t * const pBootloaderAppInfo =
163 bootloaderSize
164 );
165
166 if (pBootloaderAppInfo == NULL)
167 {
168 /* Case (1): boot loader isn't valid */
170 }
171
172 /* locate the public key */
173 const McciBootloader_SignatureBlock_t * const pBootloaderSigBlock =
175 pBootloaderAppInfo
176 );
177
178 if (pBootloaderSigBlock == NULL)
179 {
180 /* Case (1): boot loader isn't valid */
182 }
183
184 /* fetch the public key pointer */
185 const mcci_tweetnacl_sign_publickey_t * const pPublicKey =
186 &pBootloaderSigBlock->publicKey;
187
188 /* next, we check the hash of the application */
189 bool const appOk = McciBootloader_checkCodeValid(
192 );
193
194 /* check the update flag */
195 bool fFirmwareUpdatePending;
196
197 fFirmwareUpdatePending = McciBootloaderPlatform_getUpdateFlag();
198 if (appOk && ! fFirmwareUpdatePending)
199 {
200 /* Case (2): looks like we're good to launch the application */
202 }
203
204 /* initialize the storage and annunciator drivers */
207
208 /* start with the primary image */
210
211 /* check the app image */
212 do {
213 /* because of power failures, don't clear the update-image flag just yet */
214
215 /* indicate that we're checking the storage */
218 );
219
220 const bool fImageOk = McciBootloader_checkStorageImage(
221 hPrimary,
223 pPublicKey
224 );
225
226 /* check for Case (3) */
227 if (appOk && !fImageOk)
228 {
229 /* case (3): "update" but update image failed tests */
230 /* consume the storage flag; don't check again until asked */
232 /* launch existing app */
234 }
235
236 /* check for case (4) */
237 if (appOk && fImageOk)
238 /* go on */;
239 /* check for case (5) */
240 else if (! appOk && fImageOk)
241 /* go on */;
242 else /* ! appOk && ! fImageOk */
243 break;
244
245 /* case (4) or (5) */
246 McciBootloaderError_t programResult;
247
248 /* as soon as we've erased the app, we'll reset the storage flag inside the routine below */
250 hPrimary,
252 );
253 if (programResult == McciBootloaderError_OK)
254 {
255 /* definitely (4) or (5): launch the application */
258 }
259 else
260 {
261 McciBootloaderPlatform_fail(programResult);
262 }
263
264 /* otherwise app is invalid so try the fallback image */
265 } while (0);
266
267 /* cases (6) through (9) */
268 do {
271
272 McciBootloaderError_t fImageOk;
273
274 fImageOk = McciBootloaderError_OK;
276 hFallback,
278 pPublicKey
279 )
280 )
281 {
282 hStorage = hFallback;
283 }
285 hPrimary,
287 pPublicKey
288 )
289 )
290 {
291 hStorage = hPrimary;
292 }
293 else
294 {
296 }
297
298 if (fImageOk == McciBootloaderError_OK)
299 {
300 /* cases (6), (7), (8) */
302 hStorage,
304 );
305
306 if (fImageOk == McciBootloaderError_OK)
307 {
308 /* cases (6), (7), (8) */
309 /* consume the storage flag; don't check again until asked */
312 }
313 }
314
315 /* case (9) */
316 /* consume the storage flag; don't check again until asked */
319 } while (0);
320 }
const void * gk_McciBootloader_BootBase
const void * gk_McciBootloader_BootTop
@ McciBootloaderState_CheckingPrimaryStorageHash
static size_t McciBootloader_codeSize(const void *base, const void *top)
const void * gk_McciBootloader_AppTop
const void * gk_McciBootloader_AppBase
@ McciBootloaderError_NoAppImage
app image not valid, no fallback available.
@ McciBootloaderError_OK
successful
@ McciBootloaderError_BootloaderNotValid
bootloader image isn't valid
static void McciBootloaderPlatform_storageInit(void)
static McciBootloaderStorageAddress_t McciBootloaderPlatform_getPrimaryStorageAddress(void)
static McciBootloaderStorageAddress_t McciBootloaderPlatform_getFallbackStorageAddress(void)
void MCCI_BOOTLOADER_NORETURN_PFX McciBootloaderPlatform_fail(McciBootloaderError_t errorCode) MCCI_BOOTLOADER_NORETURN_SFX
static bool McciBootloaderPlatform_getUpdateFlag(void)
static void McciBootloaderPlatform_annunciatorIndicateState(McciBootloaderState_t state)
static void McciBootloaderPlatform_setUpdateFlag(bool fUpdate)
static void McciBootloaderPlatform_annunciatorInit(void)
void McciBootloaderPlatform_entry(void)
uint32_t McciBootloaderError_t
error codes for the bootloader
uint32_t McciBootloaderStorageAddress_t
Abstract type for storage byte addresses.
bool McciBootloader_checkCodeValid(const void *pBase, size_t nBytes)
bool McciBootloader_checkStorageImage(McciBootloaderStorageAddress_t address, McciBootloader_AppInfo_t *pIncomingAppInfo, const mcci_tweetnacl_sign_publickey_t *pPublicKey)
McciBootloader_AppInfo_t g_McciBootloader_incomingAppInfo
void McciBootloader_main(void)
McciBootloaderError_t McciBootloader_programAndCheckFlash(McciBootloaderStorageAddress_t storageAddress, const McciBootloader_AppInfo_t *pAppInfo)
const McciBootloader_AppInfo_t * McciBootloaderPlatform_getAppInfo(const void *pHeader, size_t nHeader)
const McciBootloader_SignatureBlock_t * McciBootloaderPlatform_getSignatureBlock(const McciBootloader_AppInfo_t *pAppInfo)
void McciBootloaderPlatform_startApp(const void *pAppBase)