]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * | |
3 | * Copyright (c) 2017-2018 Solarflare Communications Inc. | |
4 | * All rights reserved. | |
5 | */ | |
6 | ||
7 | #include "efx.h" | |
8 | #include "efx_impl.h" | |
9 | ||
10 | #if EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 | |
11 | ||
12 | #if EFSYS_OPT_IMAGE_LAYOUT | |
13 | ||
14 | /* | |
15 | * Utility routines to support limited parsing of ASN.1 tags. This is not a | |
16 | * general purpose ASN.1 parser, but is sufficient to locate the required | |
17 | * objects in a signed image with CMS headers. | |
18 | */ | |
19 | ||
20 | /* DER encodings for ASN.1 tags (see ITU-T X.690) */ | |
21 | #define ASN1_TAG_INTEGER (0x02) | |
22 | #define ASN1_TAG_OCTET_STRING (0x04) | |
23 | #define ASN1_TAG_OBJ_ID (0x06) | |
24 | #define ASN1_TAG_SEQUENCE (0x30) | |
25 | #define ASN1_TAG_SET (0x31) | |
26 | ||
27 | #define ASN1_TAG_IS_PRIM(tag) ((tag & 0x20) == 0) | |
28 | ||
29 | #define ASN1_TAG_PRIM_CONTEXT(n) (0x80 + (n)) | |
30 | #define ASN1_TAG_CONS_CONTEXT(n) (0xA0 + (n)) | |
31 | ||
32 | typedef struct efx_asn1_cursor_s { | |
33 | uint8_t *buffer; | |
34 | uint32_t length; | |
35 | ||
36 | uint8_t tag; | |
37 | uint32_t hdr_size; | |
38 | uint32_t val_size; | |
39 | } efx_asn1_cursor_t; | |
40 | ||
41 | ||
42 | /* Parse header of DER encoded ASN.1 TLV and match tag */ | |
43 | static __checkReturn efx_rc_t | |
44 | efx_asn1_parse_header_match_tag( | |
45 | __inout efx_asn1_cursor_t *cursor, | |
46 | __in uint8_t tag) | |
47 | { | |
48 | efx_rc_t rc; | |
49 | ||
50 | if (cursor == NULL || cursor->buffer == NULL || cursor->length < 2) { | |
51 | rc = EINVAL; | |
52 | goto fail1; | |
53 | } | |
54 | ||
55 | cursor->tag = cursor->buffer[0]; | |
56 | if (cursor->tag != tag) { | |
57 | /* Tag not matched */ | |
58 | rc = ENOENT; | |
59 | goto fail2; | |
60 | } | |
61 | ||
62 | if ((cursor->tag & 0x1F) == 0x1F) { | |
63 | /* Long tag format not used in CMS syntax */ | |
64 | rc = EINVAL; | |
65 | goto fail3; | |
66 | } | |
67 | ||
68 | if ((cursor->buffer[1] & 0x80) == 0) { | |
69 | /* Short form: length is 0..127 */ | |
70 | cursor->hdr_size = 2; | |
71 | cursor->val_size = cursor->buffer[1]; | |
72 | } else { | |
73 | /* Long form: length encoded as [0x80+nbytes][length bytes] */ | |
74 | uint32_t nbytes = cursor->buffer[1] & 0x7F; | |
75 | uint32_t offset; | |
76 | ||
77 | if (nbytes == 0) { | |
78 | /* Indefinite length not allowed in DER encoding */ | |
79 | rc = EINVAL; | |
80 | goto fail4; | |
81 | } | |
82 | if (2 + nbytes > cursor->length) { | |
83 | /* Header length overflows image buffer */ | |
84 | rc = EINVAL; | |
85 | goto fail6; | |
86 | } | |
87 | if (nbytes > sizeof (uint32_t)) { | |
88 | /* Length encoding too big */ | |
89 | rc = E2BIG; | |
90 | goto fail5; | |
91 | } | |
92 | cursor->hdr_size = 2 + nbytes; | |
93 | cursor->val_size = 0; | |
94 | for (offset = 2; offset < cursor->hdr_size; offset++) { | |
95 | cursor->val_size = | |
96 | (cursor->val_size << 8) | cursor->buffer[offset]; | |
97 | } | |
98 | } | |
99 | ||
100 | if ((cursor->hdr_size + cursor->val_size) > cursor->length) { | |
101 | /* Length overflows image buffer */ | |
102 | rc = E2BIG; | |
103 | goto fail7; | |
104 | } | |
105 | ||
106 | return (0); | |
107 | ||
108 | fail7: | |
109 | EFSYS_PROBE(fail7); | |
110 | fail6: | |
111 | EFSYS_PROBE(fail6); | |
112 | fail5: | |
113 | EFSYS_PROBE(fail5); | |
114 | fail4: | |
115 | EFSYS_PROBE(fail4); | |
116 | fail3: | |
117 | EFSYS_PROBE(fail3); | |
118 | fail2: | |
119 | EFSYS_PROBE(fail2); | |
120 | fail1: | |
121 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
122 | ||
123 | return (rc); | |
124 | } | |
125 | ||
126 | /* Enter nested ASN.1 TLV (contained in value of current TLV) */ | |
127 | static __checkReturn efx_rc_t | |
128 | efx_asn1_enter_tag( | |
129 | __inout efx_asn1_cursor_t *cursor, | |
130 | __in uint8_t tag) | |
131 | { | |
132 | efx_rc_t rc; | |
133 | ||
134 | if (cursor == NULL) { | |
135 | rc = EINVAL; | |
136 | goto fail1; | |
137 | } | |
138 | ||
139 | if (ASN1_TAG_IS_PRIM(tag)) { | |
140 | /* Cannot enter a primitive tag */ | |
141 | rc = ENOTSUP; | |
142 | goto fail2; | |
143 | } | |
144 | rc = efx_asn1_parse_header_match_tag(cursor, tag); | |
145 | if (rc != 0) { | |
146 | /* Invalid TLV or wrong tag */ | |
147 | goto fail3; | |
148 | } | |
149 | ||
150 | /* Limit cursor range to nested TLV */ | |
151 | cursor->buffer += cursor->hdr_size; | |
152 | cursor->length = cursor->val_size; | |
153 | ||
154 | return (0); | |
155 | ||
156 | fail3: | |
157 | EFSYS_PROBE(fail3); | |
158 | fail2: | |
159 | EFSYS_PROBE(fail2); | |
160 | fail1: | |
161 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
162 | ||
163 | return (rc); | |
164 | } | |
165 | ||
166 | /* | |
167 | * Check that the current ASN.1 TLV matches the given tag and value. | |
168 | * Advance cursor to next TLV on a successful match. | |
169 | */ | |
170 | static __checkReturn efx_rc_t | |
171 | efx_asn1_match_tag_value( | |
172 | __inout efx_asn1_cursor_t *cursor, | |
173 | __in uint8_t tag, | |
174 | __in const void *valp, | |
175 | __in uint32_t val_size) | |
176 | { | |
177 | efx_rc_t rc; | |
178 | ||
179 | if (cursor == NULL) { | |
180 | rc = EINVAL; | |
181 | goto fail1; | |
182 | } | |
183 | rc = efx_asn1_parse_header_match_tag(cursor, tag); | |
184 | if (rc != 0) { | |
185 | /* Invalid TLV or wrong tag */ | |
186 | goto fail2; | |
187 | } | |
188 | if (cursor->val_size != val_size) { | |
189 | /* Value size is different */ | |
190 | rc = EINVAL; | |
191 | goto fail3; | |
192 | } | |
193 | if (memcmp(cursor->buffer + cursor->hdr_size, valp, val_size) != 0) { | |
194 | /* Value content is different */ | |
195 | rc = EINVAL; | |
196 | goto fail4; | |
197 | } | |
198 | cursor->buffer += cursor->hdr_size + cursor->val_size; | |
199 | cursor->length -= cursor->hdr_size + cursor->val_size; | |
200 | ||
201 | return (0); | |
202 | ||
203 | fail4: | |
204 | EFSYS_PROBE(fail4); | |
205 | fail3: | |
206 | EFSYS_PROBE(fail3); | |
207 | fail2: | |
208 | EFSYS_PROBE(fail2); | |
209 | fail1: | |
210 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
211 | ||
212 | return (rc); | |
213 | } | |
214 | ||
215 | /* Advance cursor to next TLV */ | |
216 | static __checkReturn efx_rc_t | |
217 | efx_asn1_skip_tag( | |
218 | __inout efx_asn1_cursor_t *cursor, | |
219 | __in uint8_t tag) | |
220 | { | |
221 | efx_rc_t rc; | |
222 | ||
223 | if (cursor == NULL) { | |
224 | rc = EINVAL; | |
225 | goto fail1; | |
226 | } | |
227 | ||
228 | rc = efx_asn1_parse_header_match_tag(cursor, tag); | |
229 | if (rc != 0) { | |
230 | /* Invalid TLV or wrong tag */ | |
231 | goto fail2; | |
232 | } | |
233 | cursor->buffer += cursor->hdr_size + cursor->val_size; | |
234 | cursor->length -= cursor->hdr_size + cursor->val_size; | |
235 | ||
236 | return (0); | |
237 | ||
238 | fail2: | |
239 | EFSYS_PROBE(fail2); | |
240 | fail1: | |
241 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
242 | ||
243 | return (rc); | |
244 | } | |
245 | ||
246 | /* Return pointer to value octets and value size from current TLV */ | |
247 | static __checkReturn efx_rc_t | |
248 | efx_asn1_get_tag_value( | |
249 | __inout efx_asn1_cursor_t *cursor, | |
250 | __in uint8_t tag, | |
251 | __out uint8_t **valp, | |
252 | __out uint32_t *val_sizep) | |
253 | { | |
254 | efx_rc_t rc; | |
255 | ||
256 | if (cursor == NULL || valp == NULL || val_sizep == NULL) { | |
257 | rc = EINVAL; | |
258 | goto fail1; | |
259 | } | |
260 | ||
261 | rc = efx_asn1_parse_header_match_tag(cursor, tag); | |
262 | if (rc != 0) { | |
263 | /* Invalid TLV or wrong tag */ | |
264 | goto fail2; | |
265 | } | |
266 | *valp = cursor->buffer + cursor->hdr_size; | |
267 | *val_sizep = cursor->val_size; | |
268 | ||
269 | return (0); | |
270 | ||
271 | fail2: | |
272 | EFSYS_PROBE(fail2); | |
273 | fail1: | |
274 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
275 | ||
276 | return (rc); | |
277 | } | |
278 | ||
279 | ||
280 | /* | |
281 | * Utility routines for parsing CMS headers (see RFC2315, PKCS#7) | |
282 | */ | |
283 | ||
284 | /* OID 1.2.840.113549.1.7.2 */ | |
285 | static const uint8_t PKCS7_SignedData[] = | |
286 | { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 }; | |
287 | ||
288 | /* OID 1.2.840.113549.1.7.1 */ | |
289 | static const uint8_t PKCS7_Data[] = | |
290 | { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 }; | |
291 | ||
292 | /* SignedData structure version */ | |
293 | static const uint8_t SignedData_Version[] = | |
294 | { 0x03 }; | |
295 | ||
296 | /* | |
297 | * Check for a valid image in signed image format. This uses CMS syntax | |
298 | * (see RFC2315, PKCS#7) to provide signatures, and certificates required | |
299 | * to validate the signatures. The encapsulated content is in unsigned image | |
300 | * format (reflash header, image code, trailer checksum). | |
301 | */ | |
302 | static __checkReturn efx_rc_t | |
303 | efx_check_signed_image_header( | |
304 | __in void *bufferp, | |
305 | __in uint32_t buffer_size, | |
306 | __out uint32_t *content_offsetp, | |
307 | __out uint32_t *content_lengthp) | |
308 | { | |
309 | efx_asn1_cursor_t cursor; | |
310 | uint8_t *valp; | |
311 | uint32_t val_size; | |
312 | efx_rc_t rc; | |
313 | ||
314 | if (content_offsetp == NULL || content_lengthp == NULL) { | |
315 | rc = EINVAL; | |
316 | goto fail1; | |
317 | } | |
318 | cursor.buffer = (uint8_t *)bufferp; | |
319 | cursor.length = buffer_size; | |
320 | ||
321 | /* ContextInfo */ | |
322 | rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE); | |
323 | if (rc != 0) | |
324 | goto fail2; | |
325 | ||
326 | /* ContextInfo.contentType */ | |
327 | rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_OBJ_ID, | |
328 | PKCS7_SignedData, sizeof (PKCS7_SignedData)); | |
329 | if (rc != 0) | |
330 | goto fail3; | |
331 | ||
332 | /* ContextInfo.content */ | |
333 | rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_CONS_CONTEXT(0)); | |
334 | if (rc != 0) | |
335 | goto fail4; | |
336 | ||
337 | /* SignedData */ | |
338 | rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE); | |
339 | if (rc != 0) | |
340 | goto fail5; | |
341 | ||
342 | /* SignedData.version */ | |
343 | rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_INTEGER, | |
344 | SignedData_Version, sizeof (SignedData_Version)); | |
345 | if (rc != 0) | |
346 | goto fail6; | |
347 | ||
348 | /* SignedData.digestAlgorithms */ | |
349 | rc = efx_asn1_skip_tag(&cursor, ASN1_TAG_SET); | |
350 | if (rc != 0) | |
351 | goto fail7; | |
352 | ||
353 | /* SignedData.encapContentInfo */ | |
354 | rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE); | |
355 | if (rc != 0) | |
356 | goto fail8; | |
357 | ||
358 | /* SignedData.encapContentInfo.econtentType */ | |
359 | rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_OBJ_ID, | |
360 | PKCS7_Data, sizeof (PKCS7_Data)); | |
361 | if (rc != 0) | |
362 | goto fail9; | |
363 | ||
364 | /* SignedData.encapContentInfo.econtent */ | |
365 | rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_CONS_CONTEXT(0)); | |
366 | if (rc != 0) | |
367 | goto fail10; | |
368 | ||
369 | /* | |
370 | * The octet string contains the image header, image code bytes and | |
371 | * image trailer CRC (same as unsigned image layout). | |
372 | */ | |
373 | valp = NULL; | |
374 | val_size = 0; | |
375 | rc = efx_asn1_get_tag_value(&cursor, ASN1_TAG_OCTET_STRING, | |
376 | &valp, &val_size); | |
377 | if (rc != 0) | |
378 | goto fail11; | |
379 | ||
380 | if ((valp == NULL) || (val_size == 0)) { | |
381 | rc = EINVAL; | |
382 | goto fail12; | |
383 | } | |
384 | if (valp < (uint8_t *)bufferp) { | |
385 | rc = EINVAL; | |
386 | goto fail13; | |
387 | } | |
388 | if ((valp + val_size) > ((uint8_t *)bufferp + buffer_size)) { | |
389 | rc = EINVAL; | |
390 | goto fail14; | |
391 | } | |
392 | ||
393 | *content_offsetp = (uint32_t)(valp - (uint8_t *)bufferp); | |
394 | *content_lengthp = val_size; | |
395 | ||
396 | return (0); | |
397 | ||
398 | fail14: | |
399 | EFSYS_PROBE(fail14); | |
400 | fail13: | |
401 | EFSYS_PROBE(fail13); | |
402 | fail12: | |
403 | EFSYS_PROBE(fail12); | |
404 | fail11: | |
405 | EFSYS_PROBE(fail11); | |
406 | fail10: | |
407 | EFSYS_PROBE(fail10); | |
408 | fail9: | |
409 | EFSYS_PROBE(fail9); | |
410 | fail8: | |
411 | EFSYS_PROBE(fail8); | |
412 | fail7: | |
413 | EFSYS_PROBE(fail7); | |
414 | fail6: | |
415 | EFSYS_PROBE(fail6); | |
416 | fail5: | |
417 | EFSYS_PROBE(fail5); | |
418 | fail4: | |
419 | EFSYS_PROBE(fail4); | |
420 | fail3: | |
421 | EFSYS_PROBE(fail3); | |
422 | fail2: | |
423 | EFSYS_PROBE(fail2); | |
424 | fail1: | |
425 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
426 | ||
427 | return (rc); | |
428 | } | |
429 | ||
430 | static __checkReturn efx_rc_t | |
431 | efx_check_unsigned_image( | |
432 | __in void *bufferp, | |
433 | __in uint32_t buffer_size) | |
434 | { | |
435 | efx_image_header_t *header; | |
436 | efx_image_trailer_t *trailer; | |
437 | uint32_t crc; | |
438 | efx_rc_t rc; | |
439 | ||
440 | EFX_STATIC_ASSERT(sizeof (*header) == EFX_IMAGE_HEADER_SIZE); | |
441 | EFX_STATIC_ASSERT(sizeof (*trailer) == EFX_IMAGE_TRAILER_SIZE); | |
442 | ||
443 | /* Must have at least enough space for required image header fields */ | |
444 | if (buffer_size < (EFX_FIELD_OFFSET(efx_image_header_t, eih_size) + | |
445 | sizeof (header->eih_size))) { | |
446 | rc = ENOSPC; | |
447 | goto fail1; | |
448 | } | |
449 | header = (efx_image_header_t *)bufferp; | |
450 | ||
451 | if (header->eih_magic != EFX_IMAGE_HEADER_MAGIC) { | |
452 | rc = EINVAL; | |
453 | goto fail2; | |
454 | } | |
455 | ||
456 | /* | |
457 | * Check image header version is same or higher than lowest required | |
458 | * version. | |
459 | */ | |
460 | if (header->eih_version < EFX_IMAGE_HEADER_VERSION) { | |
461 | rc = EINVAL; | |
462 | goto fail3; | |
463 | } | |
464 | ||
465 | /* Buffer must have space for image header, code and image trailer. */ | |
466 | if (buffer_size < (header->eih_size + header->eih_code_size + | |
467 | EFX_IMAGE_TRAILER_SIZE)) { | |
468 | rc = ENOSPC; | |
469 | goto fail4; | |
470 | } | |
471 | ||
472 | /* Check CRC from image buffer matches computed CRC. */ | |
473 | trailer = (efx_image_trailer_t *)((uint8_t *)header + | |
474 | header->eih_size + header->eih_code_size); | |
475 | ||
476 | crc = efx_crc32_calculate(0, (uint8_t *)header, | |
477 | (header->eih_size + header->eih_code_size)); | |
478 | ||
479 | if (trailer->eit_crc != crc) { | |
480 | rc = EINVAL; | |
481 | goto fail5; | |
482 | } | |
483 | ||
484 | return (0); | |
485 | ||
486 | fail5: | |
487 | EFSYS_PROBE(fail5); | |
488 | fail4: | |
489 | EFSYS_PROBE(fail4); | |
490 | fail3: | |
491 | EFSYS_PROBE(fail3); | |
492 | fail2: | |
493 | EFSYS_PROBE(fail2); | |
494 | fail1: | |
495 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
496 | ||
497 | return (rc); | |
498 | } | |
499 | ||
500 | __checkReturn efx_rc_t | |
501 | efx_check_reflash_image( | |
502 | __in void *bufferp, | |
503 | __in uint32_t buffer_size, | |
504 | __out efx_image_info_t *infop) | |
505 | { | |
506 | efx_image_format_t format = EFX_IMAGE_FORMAT_NO_IMAGE; | |
507 | uint32_t image_offset; | |
508 | uint32_t image_size; | |
509 | void *imagep; | |
510 | efx_rc_t rc; | |
511 | ||
512 | ||
513 | EFSYS_ASSERT(infop != NULL); | |
514 | if (infop == NULL) { | |
515 | rc = EINVAL; | |
516 | goto fail1; | |
517 | } | |
518 | memset(infop, 0, sizeof (*infop)); | |
519 | ||
520 | if (bufferp == NULL || buffer_size == 0) { | |
521 | rc = EINVAL; | |
522 | goto fail2; | |
523 | } | |
524 | ||
525 | /* | |
526 | * Check if the buffer contains an image in signed format, and if so, | |
527 | * locate the image header. | |
528 | */ | |
529 | rc = efx_check_signed_image_header(bufferp, buffer_size, | |
530 | &image_offset, &image_size); | |
531 | if (rc == 0) { | |
532 | /* | |
533 | * Buffer holds signed image format. Check that the encapsulated | |
534 | * content is in unsigned image format. | |
535 | */ | |
536 | format = EFX_IMAGE_FORMAT_SIGNED; | |
537 | } else { | |
538 | /* Check if the buffer holds image in unsigned image format */ | |
539 | format = EFX_IMAGE_FORMAT_UNSIGNED; | |
540 | image_offset = 0; | |
541 | image_size = buffer_size; | |
542 | } | |
543 | if (image_offset + image_size > buffer_size) { | |
544 | rc = E2BIG; | |
545 | goto fail3; | |
546 | } | |
547 | imagep = (uint8_t *)bufferp + image_offset; | |
548 | ||
549 | /* Check unsigned image layout (image header, code, image trailer) */ | |
550 | rc = efx_check_unsigned_image(imagep, image_size); | |
551 | if (rc != 0) | |
552 | goto fail4; | |
553 | ||
554 | /* Return image details */ | |
555 | infop->eii_format = format; | |
556 | infop->eii_imagep = bufferp; | |
557 | infop->eii_image_size = buffer_size; | |
558 | infop->eii_headerp = (efx_image_header_t *)imagep; | |
559 | ||
560 | return (0); | |
561 | ||
562 | fail4: | |
563 | EFSYS_PROBE(fail4); | |
564 | fail3: | |
565 | EFSYS_PROBE(fail3); | |
566 | fail2: | |
567 | EFSYS_PROBE(fail2); | |
568 | infop->eii_format = EFX_IMAGE_FORMAT_INVALID; | |
569 | infop->eii_imagep = NULL; | |
570 | infop->eii_image_size = 0; | |
571 | ||
572 | fail1: | |
573 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
574 | ||
575 | return (rc); | |
576 | } | |
577 | ||
578 | __checkReturn efx_rc_t | |
579 | efx_build_signed_image_write_buffer( | |
9f95a23c TL |
580 | __out_bcount(buffer_size) |
581 | uint8_t *bufferp, | |
11fdf7f2 TL |
582 | __in uint32_t buffer_size, |
583 | __in efx_image_info_t *infop, | |
584 | __out efx_image_header_t **headerpp) | |
585 | { | |
586 | signed_image_chunk_hdr_t chunk_hdr; | |
587 | uint32_t hdr_offset; | |
588 | struct { | |
589 | uint32_t offset; | |
590 | uint32_t size; | |
591 | } cms_header, image_header, code, image_trailer, signature; | |
592 | efx_rc_t rc; | |
593 | ||
594 | EFSYS_ASSERT((infop != NULL) && (headerpp != NULL)); | |
595 | ||
596 | if ((bufferp == NULL) || (buffer_size == 0) || | |
597 | (infop == NULL) || (headerpp == NULL)) { | |
598 | /* Invalid arguments */ | |
599 | rc = EINVAL; | |
600 | goto fail1; | |
601 | } | |
602 | if ((infop->eii_format != EFX_IMAGE_FORMAT_SIGNED) || | |
603 | (infop->eii_imagep == NULL) || | |
604 | (infop->eii_headerp == NULL) || | |
605 | ((uint8_t *)infop->eii_headerp < (uint8_t *)infop->eii_imagep) || | |
606 | (infop->eii_image_size < EFX_IMAGE_HEADER_SIZE) || | |
607 | ((size_t)((uint8_t *)infop->eii_headerp - infop->eii_imagep) > | |
608 | (infop->eii_image_size - EFX_IMAGE_HEADER_SIZE))) { | |
609 | /* Invalid image info */ | |
610 | rc = EINVAL; | |
611 | goto fail2; | |
612 | } | |
613 | ||
614 | /* Locate image chunks in original signed image */ | |
615 | cms_header.offset = 0; | |
616 | cms_header.size = | |
617 | (uint32_t)((uint8_t *)infop->eii_headerp - infop->eii_imagep); | |
618 | if ((cms_header.size > buffer_size) || | |
619 | (cms_header.offset > (buffer_size - cms_header.size))) { | |
620 | rc = EINVAL; | |
621 | goto fail3; | |
622 | } | |
623 | ||
624 | image_header.offset = cms_header.offset + cms_header.size; | |
625 | image_header.size = infop->eii_headerp->eih_size; | |
626 | if ((image_header.size > buffer_size) || | |
627 | (image_header.offset > (buffer_size - image_header.size))) { | |
628 | rc = EINVAL; | |
629 | goto fail4; | |
630 | } | |
631 | ||
632 | code.offset = image_header.offset + image_header.size; | |
633 | code.size = infop->eii_headerp->eih_code_size; | |
634 | if ((code.size > buffer_size) || | |
635 | (code.offset > (buffer_size - code.size))) { | |
636 | rc = EINVAL; | |
637 | goto fail5; | |
638 | } | |
639 | ||
640 | image_trailer.offset = code.offset + code.size; | |
641 | image_trailer.size = EFX_IMAGE_TRAILER_SIZE; | |
642 | if ((image_trailer.size > buffer_size) || | |
643 | (image_trailer.offset > (buffer_size - image_trailer.size))) { | |
644 | rc = EINVAL; | |
645 | goto fail6; | |
646 | } | |
647 | ||
648 | signature.offset = image_trailer.offset + image_trailer.size; | |
649 | signature.size = (uint32_t)(infop->eii_image_size - signature.offset); | |
650 | if ((signature.size > buffer_size) || | |
651 | (signature.offset > (buffer_size - signature.size))) { | |
652 | rc = EINVAL; | |
653 | goto fail7; | |
654 | } | |
655 | ||
656 | EFSYS_ASSERT3U(infop->eii_image_size, ==, cms_header.size + | |
657 | image_header.size + code.size + image_trailer.size + | |
658 | signature.size); | |
659 | ||
660 | /* BEGIN CSTYLED */ | |
661 | /* | |
662 | * Build signed image partition, inserting chunk headers. | |
663 | * | |
664 | * Signed Image: Image in NVRAM partition: | |
665 | * | |
666 | * +-----------------+ +-----------------+ | |
667 | * | CMS header | | mcfw.update |<----+ | |
668 | * +-----------------+ | | | | |
669 | * | reflash header | +-----------------+ | | |
670 | * +-----------------+ | chunk header: |-->--|-+ | |
671 | * | mcfw.update | | REFLASH_TRAILER | | | | |
672 | * | | +-----------------+ | | | |
673 | * +-----------------+ +-->| CMS header | | | | |
674 | * | reflash trailer | | +-----------------+ | | | |
675 | * +-----------------+ | | chunk header: |->-+ | | | |
676 | * | signature | | | REFLASH_HEADER | | | | | |
677 | * +-----------------+ | +-----------------+ | | | | |
678 | * | | reflash header |<--+ | | | |
679 | * | +-----------------+ | | | |
680 | * | | chunk header: |-->--+ | | |
681 | * | | IMAGE | | | |
682 | * | +-----------------+ | | |
683 | * | | reflash trailer |<------+ | |
684 | * | +-----------------+ | |
685 | * | | chunk header: | | |
686 | * | | SIGNATURE |->-+ | |
687 | * | +-----------------+ | | |
688 | * | | signature |<--+ | |
689 | * | +-----------------+ | |
690 | * | | ...unused... | | |
691 | * | +-----------------+ | |
692 | * +-<-| chunk header: | | |
693 | * >-->| CMS_HEADER | | |
694 | * +-----------------+ | |
695 | * | |
696 | * Each chunk header gives the partition offset and length of the image | |
697 | * chunk's data. The image chunk data is immediately followed by the | |
698 | * chunk header for the next chunk. | |
699 | * | |
700 | * The data chunk for the firmware code must be at the start of the | |
701 | * partition (needed for the bootloader). The first chunk header in the | |
702 | * chain (for the CMS header) is stored at the end of the partition. The | |
703 | * chain of chunk headers maintains the same logical order of image | |
704 | * chunks as the original signed image file. This set of constraints | |
705 | * results in the layout used for the data chunks and chunk headers. | |
706 | */ | |
707 | /* END CSTYLED */ | |
9f95a23c | 708 | memset(bufferp, 0xFF, buffer_size); |
11fdf7f2 TL |
709 | |
710 | EFX_STATIC_ASSERT(sizeof (chunk_hdr) == SIGNED_IMAGE_CHUNK_HDR_LEN); | |
711 | memset(&chunk_hdr, 0, SIGNED_IMAGE_CHUNK_HDR_LEN); | |
712 | ||
713 | /* | |
714 | * CMS header | |
715 | */ | |
716 | if (buffer_size < SIGNED_IMAGE_CHUNK_HDR_LEN) { | |
717 | rc = ENOSPC; | |
718 | goto fail8; | |
719 | } | |
720 | hdr_offset = buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN; | |
721 | ||
722 | chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC; | |
723 | chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION; | |
724 | chunk_hdr.id = SIGNED_IMAGE_CHUNK_CMS_HEADER; | |
725 | chunk_hdr.offset = code.size + SIGNED_IMAGE_CHUNK_HDR_LEN; | |
726 | chunk_hdr.len = cms_header.size; | |
727 | ||
728 | memcpy(bufferp + hdr_offset, &chunk_hdr, sizeof (chunk_hdr)); | |
729 | ||
730 | if ((chunk_hdr.len > buffer_size) || | |
731 | (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) { | |
732 | rc = ENOSPC; | |
733 | goto fail9; | |
734 | } | |
735 | memcpy(bufferp + chunk_hdr.offset, | |
736 | infop->eii_imagep + cms_header.offset, | |
737 | cms_header.size); | |
738 | ||
739 | /* | |
740 | * Image header | |
741 | */ | |
742 | hdr_offset = chunk_hdr.offset + chunk_hdr.len; | |
743 | if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) { | |
744 | rc = ENOSPC; | |
745 | goto fail10; | |
746 | } | |
747 | chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC; | |
748 | chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION; | |
749 | chunk_hdr.id = SIGNED_IMAGE_CHUNK_REFLASH_HEADER; | |
750 | chunk_hdr.offset = hdr_offset + SIGNED_IMAGE_CHUNK_HDR_LEN; | |
751 | chunk_hdr.len = image_header.size; | |
752 | ||
753 | memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN); | |
754 | ||
755 | if ((chunk_hdr.len > buffer_size) || | |
756 | (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) { | |
757 | rc = ENOSPC; | |
758 | goto fail11; | |
759 | } | |
760 | memcpy(bufferp + chunk_hdr.offset, | |
761 | infop->eii_imagep + image_header.offset, | |
762 | image_header.size); | |
763 | ||
764 | *headerpp = (efx_image_header_t *)(bufferp + chunk_hdr.offset); | |
765 | ||
766 | /* | |
767 | * Firmware code | |
768 | */ | |
769 | hdr_offset = chunk_hdr.offset + chunk_hdr.len; | |
770 | if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) { | |
771 | rc = ENOSPC; | |
772 | goto fail12; | |
773 | } | |
774 | chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC; | |
775 | chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION; | |
776 | chunk_hdr.id = SIGNED_IMAGE_CHUNK_IMAGE; | |
777 | chunk_hdr.offset = 0; | |
778 | chunk_hdr.len = code.size; | |
779 | ||
780 | memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN); | |
781 | ||
782 | if ((chunk_hdr.len > buffer_size) || | |
783 | (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) { | |
784 | rc = ENOSPC; | |
785 | goto fail13; | |
786 | } | |
787 | memcpy(bufferp + chunk_hdr.offset, | |
788 | infop->eii_imagep + code.offset, | |
789 | code.size); | |
790 | ||
791 | /* | |
792 | * Image trailer (CRC) | |
793 | */ | |
794 | chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC; | |
795 | chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION; | |
796 | chunk_hdr.id = SIGNED_IMAGE_CHUNK_REFLASH_TRAILER; | |
797 | chunk_hdr.offset = hdr_offset + SIGNED_IMAGE_CHUNK_HDR_LEN; | |
798 | chunk_hdr.len = image_trailer.size; | |
799 | ||
800 | hdr_offset = code.size; | |
801 | if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) { | |
802 | rc = ENOSPC; | |
803 | goto fail14; | |
804 | } | |
805 | ||
806 | memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN); | |
807 | ||
808 | if ((chunk_hdr.len > buffer_size) || | |
809 | (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) { | |
810 | rc = ENOSPC; | |
811 | goto fail15; | |
812 | } | |
813 | memcpy((uint8_t *)bufferp + chunk_hdr.offset, | |
814 | infop->eii_imagep + image_trailer.offset, | |
815 | image_trailer.size); | |
816 | ||
817 | /* | |
818 | * Signature | |
819 | */ | |
820 | hdr_offset = chunk_hdr.offset + chunk_hdr.len; | |
821 | if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) { | |
822 | rc = ENOSPC; | |
823 | goto fail16; | |
824 | } | |
825 | chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC; | |
826 | chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION; | |
827 | chunk_hdr.id = SIGNED_IMAGE_CHUNK_SIGNATURE; | |
828 | chunk_hdr.offset = chunk_hdr.offset + SIGNED_IMAGE_CHUNK_HDR_LEN; | |
829 | chunk_hdr.len = signature.size; | |
830 | ||
831 | memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN); | |
832 | ||
833 | if ((chunk_hdr.len > buffer_size) || | |
834 | (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) { | |
835 | rc = ENOSPC; | |
836 | goto fail17; | |
837 | } | |
838 | memcpy(bufferp + chunk_hdr.offset, | |
839 | infop->eii_imagep + signature.offset, | |
840 | signature.size); | |
841 | ||
842 | return (0); | |
843 | ||
844 | fail17: | |
845 | EFSYS_PROBE(fail17); | |
846 | fail16: | |
847 | EFSYS_PROBE(fail16); | |
848 | fail15: | |
849 | EFSYS_PROBE(fail15); | |
850 | fail14: | |
851 | EFSYS_PROBE(fail14); | |
852 | fail13: | |
853 | EFSYS_PROBE(fail13); | |
854 | fail12: | |
855 | EFSYS_PROBE(fail12); | |
856 | fail11: | |
857 | EFSYS_PROBE(fail11); | |
858 | fail10: | |
859 | EFSYS_PROBE(fail10); | |
860 | fail9: | |
861 | EFSYS_PROBE(fail9); | |
862 | fail8: | |
863 | EFSYS_PROBE(fail8); | |
864 | fail7: | |
865 | EFSYS_PROBE(fail7); | |
866 | fail6: | |
867 | EFSYS_PROBE(fail6); | |
868 | fail5: | |
869 | EFSYS_PROBE(fail5); | |
870 | fail4: | |
871 | EFSYS_PROBE(fail4); | |
872 | fail3: | |
873 | EFSYS_PROBE(fail3); | |
874 | fail2: | |
875 | EFSYS_PROBE(fail2); | |
876 | fail1: | |
877 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
878 | ||
879 | return (rc); | |
880 | } | |
881 | ||
882 | ||
883 | ||
884 | #endif /* EFSYS_OPT_IMAGE_LAYOUT */ | |
885 | ||
886 | #endif /* EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */ |