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
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
|
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <libintl.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "bblk_einfo.h"
#include "boot_utils.h"
bblk_hash_t bblk_no_hash = {BBLK_NO_HASH, 0, "(no hash)", NULL};
bblk_hash_t bblk_md5_hash = {BBLK_HASH_MD5, 0x10, "MD5", md5_calc};
bblk_hash_t *bblk_hash_list[BBLK_HASH_TOT] = {
&bblk_no_hash,
&bblk_md5_hash
};
/*
* einfo_compare_dotted_version()
* Compares two strings with an arbitrary long number of dot-separated numbers.
* Returns: 0 - if the version numbers are equal
* 1 - if str1 version number is more recent than str2
* 2 - if str2 version number is more recent than str1
* -1 - if an error occurred
*
* Comparison is done field by field, by retrieving an unsigned integer value,
* (missing fields are assumed as 0, but explict zeroes take precedence) so:
* 4.1.2.11 > 4.1.2.2 > 4.1.2.0 > 4.1.2
*
* where ">" means "more recent than".
*/
static int
einfo_compare_dotted_version(const char *str1, const char *str2)
{
int retval = 0;
char *verstr1, *verstr2, *freeptr1, *freeptr2;
char *parsep1, *parsep2;
unsigned int val_str1, val_str2;
freeptr1 = verstr1 = strdup(str1);
freeptr2 = verstr2 = strdup(str2);
if (verstr1 == NULL || verstr2 == NULL) {
retval = -1;
goto out;
}
while (verstr1 != NULL && verstr2 != NULL) {
parsep1 = strsep(&verstr1, ".");
parsep2 = strsep(&verstr2, ".");
val_str1 = atoi(parsep1);
val_str2 = atoi(parsep2);
if (val_str1 > val_str2) {
retval = 1;
goto out;
}
if (val_str2 > val_str1) {
retval = 2;
goto out;
}
}
/* Common portion of the version string is equal. */
if (verstr1 == NULL && verstr2 != NULL)
retval = 2;
if (verstr2 == NULL && verstr1 != NULL)
retval = 1;
out:
free(freeptr1);
free(freeptr2);
return (retval);
}
/*
* einfo_compare_timestamps()
* Currently, timestamp is in %Y%m%dT%H%M%SZ format in UTC, which means that
* we can simply do a lexicographic comparison to know which one is the most
* recent.
*
* Returns: 0 - if timestamps coincide
* 1 - if the timestamp in str1 is more recent
* 2 - if the timestamp in str2 is more recent
*/
static int
einfo_compare_timestamps(const char *str1, const char *str2)
{
int retval;
retval = strcmp(str1, str2);
if (retval > 0)
retval = 1;
if (retval < 0)
retval = 2;
return (retval);
}
/*
* einfo_compare_version()
* Given two extended versions, compare the two and returns which one is more
* "recent". Comparison is based on dotted version number fields and a
* timestamp.
*
* Returns: -1 - on error
* 0 - if the two versions coincide
* 1 - if the version in str1 is more recent
* 2 - if the version in str2 is more recent
*
* The version string generally uses following form:
* self_release,build_release:timestamp
* The release numbers are compared as dotted versions.
*
* While comparing, if the self releases are identical but the build
* release is missing, this version string is considered older.
*
* If the release strings are identical, and one of the timestamps is missing,
* we return an error. Otherwise, return the result from comparing the
* timestamps.
*/
static int
einfo_compare_version(const char *str1, const char *str2)
{
int retval = 0;
char *verstr1, *verstr2, *freeptr1, *freeptr2;
char *parsep1, *parsep2;
char *timep1, *timep2;
freeptr1 = verstr1 = strdup(str1);
freeptr2 = verstr2 = strdup(str2);
if (verstr1 == NULL || verstr2 == NULL) {
retval = -1;
goto out;
}
/* Extract the time part from the version string. */
timep1 = verstr1;
timep2 = verstr2;
parsep1 = strsep(&timep1, ":");
parsep2 = strsep(&timep2, ":");
while (parsep1 != NULL && parsep2 != NULL) {
parsep1 = strsep(&verstr1, ",-");
parsep2 = strsep(&verstr2, ",-");
/* If both are NULL, compare timestamps */
if (parsep1 == NULL && parsep2 == NULL)
break;
if (parsep1 == NULL) {
retval = 2;
goto out;
}
if (parsep2 == NULL) {
retval = 1;
goto out;
}
retval = einfo_compare_dotted_version(parsep1, parsep2);
if (retval == 0)
continue;
else
goto out;
}
/* The dotted versions are identical, check timestamps. */
if (timep1 == NULL || timep2 == NULL) {
retval = -1;
goto out;
}
retval = einfo_compare_timestamps(timep1, timep2);
out:
free(freeptr1);
free(freeptr2);
return (retval);
}
/*
* print_einfo()
*
* Print the extended information contained into the pointed structure.
* 'bufsize' specifies the real size of the structure, since str_off and
* hash_off need to point somewhere past the header.
*/
void
print_einfo(uint8_t flags, bblk_einfo_t *einfo, unsigned long bufsize)
{
int i = 0;
char *version;
boolean_t has_hash = B_FALSE;
unsigned char *hash = NULL;
if (einfo->str_off + einfo->str_size > bufsize) {
(void) fprintf(stdout, gettext("String offset %d is beyond the "
"buffer size\n"), einfo->str_off);
return;
}
version = (char *)einfo + einfo->str_off;
if (einfo->hash_type != BBLK_NO_HASH &&
einfo->hash_type < BBLK_HASH_TOT) {
if (einfo->hash_off + einfo->hash_size > bufsize) {
(void) fprintf(stdout, gettext("Warning: hashing "
"present but hash offset %d is beyond the buffer "
"size\n"), einfo->hash_off);
has_hash = B_FALSE;
} else {
hash = (unsigned char *)einfo + einfo->hash_off;
has_hash = B_TRUE;
}
}
if (flags & EINFO_PRINT_HEADER) {
(void) fprintf(stdout, "Boot Block Extended Info Header:\n");
(void) fprintf(stdout, "\tmagic: ");
for (i = 0; i < EINFO_MAGIC_SIZE; i++)
(void) fprintf(stdout, "%c", einfo->magic[i]);
(void) fprintf(stdout, "\n");
(void) fprintf(stdout, "\tversion: %d\n", einfo->version);
(void) fprintf(stdout, "\tflags: %x\n", einfo->flags);
(void) fprintf(stdout, "\textended version string offset: %d\n",
einfo->str_off);
(void) fprintf(stdout, "\textended version string size: %d\n",
einfo->str_size);
(void) fprintf(stdout, "\thashing type: %d (%s)\n",
einfo->hash_type, has_hash ?
bblk_hash_list[einfo->hash_type]->name : "nil");
(void) fprintf(stdout, "\thash offset: %d\n", einfo->hash_off);
(void) fprintf(stdout, "\thash size: %d\n", einfo->hash_size);
}
if (flags & EINFO_EASY_PARSE) {
(void) fprintf(stdout, "%s\n", version);
} else {
(void) fprintf(stdout, "Extended version string: %s\n",
version);
if (has_hash) {
(void) fprintf(stdout, "%s hash: ",
bblk_hash_list[einfo->hash_type]->name);
} else {
(void) fprintf(stdout, "No hashing available\n");
}
}
if (has_hash) {
for (i = 0; i < einfo->hash_size; i++) {
(void) fprintf(stdout, "%02x", hash[i]);
}
(void) fprintf(stdout, "\n");
}
}
static int
compute_hash(bblk_hs_t *hs, unsigned char *dest, bblk_hash_t *hash)
{
if (hs == NULL || dest == NULL || hash == NULL)
return (-1);
hash->compute_hash(dest, hs->src_buf, hs->src_size);
return (0);
}
int
prepare_and_write_einfo(unsigned char *dest, char *infostr, bblk_hs_t *hs,
uint32_t maxsize, uint32_t *used_space)
{
uint16_t hash_size;
uint32_t hash_off;
unsigned char *data;
bblk_einfo_t *einfo = (bblk_einfo_t *)dest;
bblk_hash_t *hashinfo = bblk_hash_list[BBLK_DEFAULT_HASH];
/*
* 'dest' might be both containing the buffer we want to hash and
* containing our einfo structure: delay any update of it after the
* hashing has been calculated.
*/
hash_size = hashinfo->size;
hash_off = sizeof (bblk_einfo_t);
if (hash_off + hash_size > maxsize) {
(void) fprintf(stderr, gettext("Unable to add extended info, "
"not enough space\n"));
return (-1);
}
data = dest + hash_off;
if (compute_hash(hs, data, hashinfo) < 0) {
(void) fprintf(stderr, gettext("%s hash operation failed\n"),
hashinfo->name);
einfo->hash_type = bblk_no_hash.type;
einfo->hash_size = bblk_no_hash.size;
} else {
einfo->hash_type = hashinfo->type;
einfo->hash_size = hashinfo->size;
}
(void) memcpy(einfo->magic, EINFO_MAGIC, EINFO_MAGIC_SIZE);
einfo->version = BBLK_EINFO_VERSION;
einfo->flags = 0;
einfo->hash_off = hash_off;
einfo->hash_size = hash_size;
einfo->str_off = einfo->hash_off + einfo->hash_size + 1;
if (infostr == NULL) {
(void) fprintf(stderr, gettext("Unable to add extended info, "
"string is empty\n"));
return (-1);
}
einfo->str_size = strlen(infostr);
if (einfo->str_off + einfo->str_size > maxsize) {
(void) fprintf(stderr, gettext("Unable to add extended info, "
"not enough space\n"));
return (-1);
}
data = dest + einfo->str_off;
(void) memcpy(data, infostr, einfo->str_size);
*used_space = einfo->str_off + einfo->str_size;
return (0);
}
/*
* einfo_should_update()
* Given information on the boot block currently on disk (disk_einfo) and
* information on the supplied boot block (hs for hashing, verstr as the
* associated version string) decide if an update of the on-disk boot block
* is necessary or not.
*/
boolean_t
einfo_should_update(bblk_einfo_t *disk_einfo, bblk_hs_t *hs, char *verstr)
{
bblk_hash_t *hashing;
unsigned char *disk_hash;
unsigned char *local_hash;
char *disk_version;
int retval;
if (disk_einfo == NULL)
return (B_TRUE);
if (memcmp(disk_einfo->magic, EINFO_MAGIC, EINFO_MAGIC_SIZE) != 0)
return (B_TRUE);
if (disk_einfo->version < BBLK_EINFO_VERSION)
return (B_TRUE);
disk_version = einfo_get_string(disk_einfo);
retval = einfo_compare_version(verstr, disk_version);
/*
* If something goes wrong or if the on-disk version is more recent
* do not update the bootblock.
*/
if (retval == -1 || retval == 2)
return (B_FALSE);
/*
* If we got here it means that the two version strings are either
* equal or the new bootblk binary is more recent. In order to save
* some needless writes let's use the hash to determine if an update
* is really necessary.
*/
if (disk_einfo->hash_type == bblk_no_hash.type)
return (B_TRUE);
if (disk_einfo->hash_type >= BBLK_HASH_TOT)
return (B_TRUE);
hashing = bblk_hash_list[disk_einfo->hash_type];
local_hash = malloc(hashing->size);
if (local_hash == NULL)
return (B_TRUE);
/*
* Failure in computing the hash may mean something wrong
* with the boot block file. Better be conservative here.
*/
if (compute_hash(hs, local_hash, hashing) < 0) {
free(local_hash);
return (B_FALSE);
}
disk_hash = (unsigned char *)einfo_get_hash(disk_einfo);
if (memcmp(local_hash, disk_hash, disk_einfo->hash_size) == 0) {
free(local_hash);
return (B_FALSE);
}
free(local_hash);
return (B_TRUE);
}
char *
einfo_get_string(bblk_einfo_t *einfo)
{
if (einfo == NULL)
return (NULL);
return ((char *)einfo + einfo->str_off);
}
char *
einfo_get_hash(bblk_einfo_t *einfo)
{
if (einfo == NULL)
return (NULL);
return ((char *)einfo + einfo->hash_off);
}
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _BBLKEINFO_H
#define _BBLKEINFO_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <md5.h>
#define BBLK_EINFO_VERSION (1)
#define EINFO_MAGIC "EXTINFO"
#define EINFO_MAGIC_SIZE (7)
#pragma pack(1)
typedef struct _extended_info {
char magic[EINFO_MAGIC_SIZE];
uint8_t version;
uint8_t flags;
uint32_t str_off;
uint16_t str_size;
uint8_t hash_type;
uint32_t hash_off;
uint16_t hash_size;
char rsvd[32];
} bblk_einfo_t;
#pragma pack()
enum bblk_hash_types_t {
BBLK_NO_HASH = 0,
BBLK_HASH_MD5,
BBLK_HASH_TOT
};
#define EINFO_PRINT_HEADER 0x01
#define EINFO_EASY_PARSE 0x02
typedef struct _hashing_function {
unsigned int type;
unsigned int size;
char name[16];
void (*compute_hash)(void *, const void *, unsigned int);
} bblk_hash_t;
typedef struct _hashing_source {
unsigned char *src_buf;
unsigned int src_size;
} bblk_hs_t;
#define BBLK_DEFAULT_HASH BBLK_HASH_MD5
extern bblk_hash_t bblk_no_hash;
extern bblk_hash_t bblk_md5_hash;
extern bblk_hash_t *bblk_hash_list[BBLK_HASH_TOT];
void print_einfo(uint8_t, bblk_einfo_t *, unsigned long);
int prepare_and_write_einfo(unsigned char *, char *, bblk_hs_t *,
uint32_t, uint32_t *);
boolean_t einfo_should_update(bblk_einfo_t *, bblk_hs_t *, char *);
char *einfo_get_string(bblk_einfo_t *);
char *einfo_get_hash(bblk_einfo_t *);
#ifdef __cplusplus
}
#endif
#endif /* _BBLKEINFO_H */
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <stdio.h>
#include <libintl.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include "bblk_einfo.h"
#include "boot_utils.h"
boolean_t boot_debug = B_FALSE;
boolean_t nowrite = B_FALSE;
void
boot_gdebug(const char *funcname, char *format, ...)
{
va_list ap;
if (boot_debug == B_FALSE)
return;
(void) fprintf(stdout, "%s(): ", funcname);
va_start(ap, format);
/* LINTED: E_SEC_PRINTF_VAR_FMT */
(void) vfprintf(stdout, format, ap);
va_end(ap);
}
/*
* Common functions to write out and read in block-sized data to a file
* descriptor.
*/
int
write_out(int fd, void *buffer, size_t size, off_t off)
{
int ret;
char *buf = buffer;
if (size % SECTOR_SIZE != 0)
BOOT_DEBUG("Expected block-sized data, got: %d\n", size);
/* Dry run. */
if (nowrite)
return (BC_SUCCESS);
for (;;) {
again:
ret = pwrite(fd, buf, size, off);
if (ret == -1) {
if (errno == EAGAIN)
goto again;
else
return (BC_ERROR);
}
if (ret < size) {
size -= ret;
off += ret;
buf += ret;
} else {
break;
}
}
return (BC_SUCCESS);
}
int
read_in(int fd, void *buffer, size_t size, off_t off)
{
int ret;
char *buf = buffer;
if (size % SECTOR_SIZE != 0)
BOOT_DEBUG("Expected block-sized data, got: %d\n", size);
for (;;) {
again:
ret = pread(fd, buf, size, off);
if (ret == -1) {
if (errno == EAGAIN)
goto again;
else
return (BC_ERROR);
}
if (ret < size) {
size -= ret;
off += ret;
buf += ret;
} else {
break;
}
}
return (BC_SUCCESS);
}
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _BOOT_UTILS_H
#define _BOOT_UTILS_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdarg.h>
#include <sys/types.h>
#include "bblk_einfo.h"
/* Common return values for various operations. */
#define BC_SUCCESS (0)
#define BC_ERROR (1)
#define BC_NOUPDT (4)
#define BC_NOEXTRA (5)
#define BC_NOEINFO (6)
#define SECTOR_SIZE (512)
extern boolean_t boot_debug;
extern boolean_t nowrite;
#define BOOT_DEBUG(...) boot_gdebug(__func__, __VA_ARGS__)
void boot_gdebug(const char *, char *, ...);
int write_out(int, void *, size_t, off_t);
int read_in(int, void *, size_t, off_t);
#ifdef __cplusplus
}
#endif
#endif /* _BOOT_UTILS_H */
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include <libintl.h>
#include <sys/multiboot.h>
#include <sys/sysmacros.h>
#include "bblk_einfo.h"
#include "boot_utils.h"
#include "mboot_extra.h"
/*
* Common functions to deal with the fake-multiboot encapsulation of the
* bootblock and the location of the extra information area.
*/
/* mboot checksum routine. */
uint32_t
compute_checksum(char *data, uint32_t size)
{
uint32_t *ck_ptr;
uint32_t cksum = 0;
int i;
ck_ptr = (uint32_t *)data;
for (i = 0; i < size; i += sizeof (uint32_t))
cksum += *ck_ptr++;
return (-cksum);
}
/* Given a buffer, look for a multiboot header within it. */
int
find_multiboot(char *buffer, uint32_t buf_size, uint32_t *mboot_off)
{
multiboot_header_t *mboot;
uint32_t *iter;
uint32_t cksum;
uint32_t boundary;
int i = 0;
iter = (uint32_t *)buffer;
*mboot_off = 0;
/* multiboot header has to be within the first 32K. */
boundary = MBOOT_SCAN_SIZE;
if (boundary > buf_size)
boundary = buf_size;
boundary = boundary - sizeof (multiboot_header_t);
for (i = 0; i < boundary; i += 4, iter++) {
mboot = (multiboot_header_t *)iter;
if (mboot->magic != MB_HEADER_MAGIC)
continue;
/* Found magic signature -- check checksum. */
cksum = -(mboot->flags + mboot->magic);
if (mboot->checksum != cksum) {
BOOT_DEBUG("multiboot magic found at %p, but checksum "
"mismatches (is %x, should be %x)\n", mboot,
mboot->checksum, cksum);
continue;
} else {
if (!(mboot->flags & BB_MBOOT_AOUT_FLAG)) {
BOOT_DEBUG("multiboot structure found, but no "
"AOUT kludge specified, skipping.\n");
continue;
} else {
/* proper multiboot structure found. */
*mboot_off = i;
return (BC_SUCCESS);
}
}
}
return (BC_ERROR);
}
/*
* Given a pointer to the extra information area (a sequence of bb_header_ext_t
* + payload chunks), find the extended information structure.
*/
bblk_einfo_t *
find_einfo(char *extra, uint32_t size)
{
bb_header_ext_t *ext_header;
bblk_einfo_t *einfo;
uint32_t cksum;
assert(extra != NULL);
ext_header = (bb_header_ext_t *)extra;
if (ext_header->size > size) {
BOOT_DEBUG("Unable to find extended versioning information, "
"data size too big\n");
return (NULL);
}
cksum = compute_checksum(extra + sizeof (bb_header_ext_t),
ext_header->size);
BOOT_DEBUG("Extended information header checksum is %x\n", cksum);
if (cksum != ext_header->checksum) {
BOOT_DEBUG("Unable to find extended versioning information, "
"data looks corrupted\n");
return (NULL);
}
/*
* Currently we only have one extra header so it must be encapsulating
* the extended information structure.
*/
einfo = (bblk_einfo_t *)(extra + sizeof (bb_header_ext_t));
if (memcmp(einfo->magic, EINFO_MAGIC, EINFO_MAGIC_SIZE) != 0) {
BOOT_DEBUG("Unable to read stage2 extended versioning "
"information, wrong magic identifier\n");
BOOT_DEBUG("Found %s, expected %s\n", einfo->magic,
EINFO_MAGIC);
return (NULL);
}
return (einfo);
}
/*
* Given a pointer to the extra area, add the extended information structure
* encapsulated by a bb_header_ext_t structure.
*/
void
add_einfo(char *extra, char *updt_str, bblk_hs_t *hs, uint32_t avail_space)
{
bb_header_ext_t *ext_hdr;
uint32_t used_space;
unsigned char *dest;
int ret;
assert(extra != NULL);
if (updt_str == NULL) {
BOOT_DEBUG("WARNING: no update string passed to "
"add_stage2_einfo()\n");
return;
}
/* Reserve space for the extra header. */
ext_hdr = (bb_header_ext_t *)extra;
dest = (unsigned char *)extra + sizeof (*ext_hdr);
/* Place the extended information structure. */
ret = prepare_and_write_einfo(dest, updt_str, hs, avail_space,
&used_space);
if (ret != 0) {
(void) fprintf(stderr, gettext("Unable to write the extended "
"versioning information\n"));
return;
}
/* Fill the extended information associated header. */
ext_hdr->size = P2ROUNDUP(used_space, 8);
ext_hdr->checksum = compute_checksum((char *)dest, ext_hdr->size);
}
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _MBOOT_EXTRA_H
#define _MBOOT_EXTRA_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdarg.h>
#include <sys/types.h>
#include "bblk_einfo.h"
/* multiboot header needs to be located in the first 64KB. */
#define MBOOT_SCAN_SIZE (64 * 1024)
/* multiboot header AOUT_KLUDGE flag. */
#define BB_MBOOT_AOUT_FLAG (0x00010000)
/* Extra header preceeding the payloads at the end of the bootblock. */
typedef struct _bb_extra_header {
uint32_t size;
uint32_t checksum;
} bb_header_ext_t;
uint32_t compute_checksum(char *, uint32_t);
bblk_einfo_t *find_einfo(char *, uint32_t);
int find_multiboot(char *, uint32_t, uint32_t *);
void add_einfo(char *, char *, bblk_hs_t *, uint32_t);
int compare_bootblocks(char *, char *, char **);
#ifdef __cplusplus
}
#endif
#endif /* _MBOOT_EXTRA_H */
|