1 /*
2 * Copyright (c) 2014 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8
9 #include <lib/bootimage.h>
10 #include <lk/trace.h>
11 #include <lk/err.h>
12 #include <lk/debug.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include <lib/bootimage_struct.h>
17 #include <lib/mincrypt/sha256.h>
18
19 #define LOCAL_TRACE 1
20
21 struct bootimage {
22 const uint8_t *ptr;
23 size_t len;
24 };
25
validate_bootimage(bootimage_t * bi)26 static status_t validate_bootimage(bootimage_t *bi) {
27 if (!bi)
28 return ERR_INVALID_ARGS;
29
30 /* is it large enough to hold the first entry */
31 if (bi->len < 4096) {
32 LTRACEF("bootentry too short\n");
33 return ERR_BAD_LEN;
34 }
35
36 bootentry *be = (bootentry *)bi->ptr;
37
38 /* check that the first entry is a file, type boot info, and is 4096 bytes at offset 0 */
39 if (be->kind != KIND_FILE ||
40 be->file.type != TYPE_BOOT_IMAGE ||
41 be->file.offset != 0 ||
42 be->file.length != 4096 ||
43 memcmp(be->file.name, BOOT_MAGIC, sizeof(be->file.name))) {
44 LTRACEF("invalid first entry\n");
45 return ERR_INVALID_ARGS;
46 }
47
48 /* check the sha256 of the rest of the first page */
49 SHA256_CTX ctx;
50 SHA256_init(&ctx);
51
52 SHA256_update(&ctx, be + 1, 4096 - sizeof(bootentry));
53 const uint8_t *hash = SHA256_final(&ctx);
54
55 if (memcmp(hash, be->file.sha256, sizeof(be->file.sha256)) != 0) {
56 LTRACEF("bad hash of first section\n");
57
58 return ERR_CHECKSUM_FAIL;
59 }
60
61 /* look at the second entry, which should be a boot info structure */
62 if (be[1].kind != KIND_BOOT_INFO) {
63 LTRACEF("second entry not boot info\n");
64 return ERR_INVALID_ARGS;
65 }
66
67 bootentry_info *info = &be[1].info;
68
69 /* is the image a handled version */
70 if (info->version > BOOT_VERSION) {
71 LTRACEF("unhandled version 0x%x\n", info->version);
72 return ERR_INVALID_ARGS;
73 }
74
75 /* is the image the right size? */
76 if (info->image_size > bi->len) {
77 LTRACEF("boot image block says image is too big (0x%x bytes)\n", info->image_size);
78 return ERR_INVALID_ARGS;
79 }
80
81 /* trim the len to what the info block says */
82 bi->len = info->image_size;
83
84 /* iterate over the remaining entries in the list */
85 for (size_t i = 2; i < info->entry_count; i++) {
86 if (be[i].kind == 0)
87 break;
88
89 LTRACEF("%u: kind 0x%x\n", i, be[i].kind);
90
91 switch (be[i].kind) {
92 case KIND_BOOT_INFO:
93 break;
94 case KIND_BOARD:
95 break;
96 case KIND_BUILD:
97 break;
98 case KIND_FILE: {
99 LTRACEF("\ttype %c%c%c%c offset 0x%x, length 0x%x\n",
100 (be[i].file.type >> 0) & 0xff, (be[i].file.type >> 8) & 0xff,
101 (be[i].file.type >> 16) & 0xff, (be[i].file.type >> 24) & 0xff,
102 be[i].file.offset, be[i].file.length);
103
104 /* check that the file section is inside the overall image */
105 uint32_t end = be[i].file.offset + be[i].file.length;
106 if (end < be[i].file.offset || end > info->image_size) {
107 LTRACEF("bad file section, size too large\n");
108 return ERR_INVALID_ARGS;
109 }
110
111 /* check the sha256 hash */
112 SHA256_init(&ctx);
113
114 LTRACEF("\tvalidating SHA256 hash\n");
115 SHA256_update(&ctx, (const uint8_t *)bi->ptr + be[i].file.offset, be[i].file.length);
116 hash = SHA256_final(&ctx);
117
118 if (memcmp(hash, be[i].file.sha256, sizeof(be[i].file.sha256)) != 0) {
119 LTRACEF("bad hash of file section\n");
120
121 return ERR_CHECKSUM_FAIL;
122 }
123
124 break;
125 }
126 default:
127 LTRACEF("unknown kind 0x%x\n", be[i].kind);
128 return ERR_INVALID_ARGS;
129 }
130 }
131
132 LTRACEF("image good\n");
133 return NO_ERROR;
134 }
135
bootimage_open(const void * ptr,size_t len,bootimage_t ** bi)136 status_t bootimage_open(const void *ptr, size_t len, bootimage_t **bi) {
137 LTRACEF("ptr %p, len %zu\n", ptr, len);
138
139 if (!bi)
140 return ERR_INVALID_ARGS;
141
142 *bi = calloc(1, sizeof(bootimage_t));
143 if (!*bi)
144 return ERR_NO_MEMORY;
145
146 (*bi)->ptr = ptr;
147 (*bi)->len = len;
148
149 /* try to validate it */
150 status_t err = validate_bootimage(*bi);
151 if (err < 0) {
152 bootimage_close(*bi);
153 return err;
154 }
155
156 return NO_ERROR;
157 }
158
bootimage_close(bootimage_t * bi)159 status_t bootimage_close(bootimage_t *bi) {
160 if (bi)
161 free(bi);
162
163 return NO_ERROR;
164 }
165
bootimage_get_range(bootimage_t * bi,const void ** ptr,size_t * len)166 status_t bootimage_get_range(bootimage_t *bi, const void **ptr, size_t *len) {
167 if (!bi)
168 return ERR_INVALID_ARGS;
169
170 if (ptr)
171 *ptr = bi->ptr;
172 if (len)
173 *len = bi->len;
174
175 return NO_ERROR;
176 }
177
bootimage_get_file_section(bootimage_t * bi,uint32_t type,const void ** ptr,size_t * len)178 status_t bootimage_get_file_section(bootimage_t *bi, uint32_t type, const void **ptr, size_t *len) {
179 if (!bi)
180 return ERR_INVALID_ARGS;
181
182 bootentry *be = (bootentry *)bi->ptr;
183 bootentry_info *info = &be[1].info;
184
185 for (size_t i = 2; i < info->entry_count; i++) {
186 if (be[i].kind == 0)
187 break;
188
189 if (be[i].kind != KIND_FILE)
190 continue;
191
192 if (type == be[i].file.type) {
193 if (ptr)
194 *ptr = bi->ptr + be[i].file.offset;
195 if (len)
196 *len = be[i].file.length;
197 return NO_ERROR;
198 }
199 }
200
201 return ERR_NOT_FOUND;
202 }
203
204