1 /*
2  * Copyright (c) 2014 Brian Swetland
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 <stdio.h>
10 #include <stdlib.h>
11 #include <stdint.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <errno.h>
16 
17 #include <lib/mincrypt/sha256.h>
18 
19 #include "bootimage.h"
20 
21 struct bootimage {
22     bootentry entry[64];
23     void *data[64];
24     uint32_t offset[64];
25     uint32_t length[64];
26     unsigned count;
27     uint32_t next_offset;
28 };
29 
bootimage_init(void)30 bootimage *bootimage_init(void) {
31     bootimage *img;
32 
33     if ((img = malloc(sizeof(bootimage))) == NULL) {
34         return NULL;
35     }
36     memset(img, 0, sizeof(bootimage));
37     img->count = 2;
38     img->next_offset = 4096;
39     memset(img->entry, 0, 4096);
40     img->entry[0].file.kind = KIND_FILE;
41     img->entry[0].file.type = TYPE_BOOT_IMAGE;
42     img->entry[0].file.offset = 0;
43     img->entry[0].file.length = 4096;
44     img->entry[1].info.kind = KIND_BOOT_INFO;
45     img->entry[1].info.version = BOOT_VERSION;
46     memcpy(img->entry[0].file.name, BOOT_MAGIC, BOOT_MAGIC_LENGTH);
47     return img;
48 }
49 
bootimage_add_string(bootimage * img,unsigned kind,const char * s)50 bootentry_data *bootimage_add_string(bootimage *img, unsigned kind, const char *s) {
51     unsigned n = img->count;
52     int len = strlen(s);
53     if (img->count == 64) return NULL;
54     if (len > 59) return NULL;
55     img->count++;
56 
57     img->entry[n].data.kind = kind;
58     strcpy((char *) img->entry[n].data.u.b, s);
59     return &(img->entry[n].data);
60 }
61 
bootimage_add_filedata(bootimage * img,unsigned type,void * data,unsigned len)62 bootentry_file *bootimage_add_filedata(bootimage *img, unsigned type, void *data, unsigned len) {
63     unsigned n = img->count;
64     if (img->count == 64) return NULL;
65     img->count++;
66 
67     // align to page boundary
68     img->next_offset = (img->next_offset + 4095) & (~4095);
69 
70     img->entry[n].file.kind = KIND_FILE;
71     img->entry[n].file.type = type;
72     img->entry[n].file.offset = img->next_offset;
73     img->entry[n].file.length = len;
74     SHA256_hash(data, len, img->entry[n].file.sha256);
75 
76     img->data[n] = data;
77     img->offset[n] = img->next_offset;
78     img->length[n] = len;
79 
80     img->next_offset += len;
81 
82     return &(img->entry[n].file);
83 }
84 
bootimage_done(bootimage * img)85 void bootimage_done(bootimage *img) {
86     unsigned sz = img->next_offset;
87     if (sz & 4095) {
88         sz += (4096 - (sz & 4095));
89     }
90     img->entry[1].info.image_size = sz;
91     img->entry[1].info.entry_count = img->count;
92     SHA256_hash((void *) &(img->entry[1]), 4096 - 64, img->entry[0].file.sha256);
93 }
94 
writex(int fd,void * data,size_t len)95 static int writex(int fd, void *data, size_t len) {
96     int r;
97     char *x = data;
98     while (len > 0) {
99         errno = NO_ERROR;
100         r = write(fd, x, len);
101         if (r < 0) {
102             if (errno == EINTR) {
103                 continue;
104             }
105             return -1;
106         }
107         len -= r;
108         x += r;
109     }
110     return 0;
111 }
112 
113 static uint8_t filler[4096] = { 0, };
114 
bootimage_write(bootimage * img,int fd)115 int bootimage_write(bootimage *img, int fd) {
116     unsigned off = 4096;
117     unsigned n, s;
118     if (writex(fd, img->entry, 4096)) {
119         return -1;
120     }
121     for (n = 1; n < 64; n++) {
122         if (img->offset[n] == 0) continue;
123         if (img->offset[n] < off) return -1;
124         s = img->offset[n] - off;
125         if (s > 4095) return -1;
126         if (writex(fd, filler, s)) {
127             return -1;
128         }
129         off += s;
130         if (writex(fd, img->data[n], img->length[n])) {
131             return -1;
132         }
133         off += img->length[n];
134     }
135     if (off & 4095) {
136         if (writex(fd, filler, 4096 - (off & 4095))) return -1;
137     }
138     return 0;
139 }
140 
load_file(const char * fn,size_t * len)141 static void *load_file(const char *fn, size_t *len) {
142     off_t sz;
143     void *data = NULL;
144     char *x;
145     int fd, r;
146 
147     if ((fd = open(fn, O_RDONLY)) < 0) {
148         return NULL;
149     }
150 
151     if ((sz = lseek(fd, 0, SEEK_END)) < 0) {
152         goto fail;
153     }
154     if (lseek(fd, 0, SEEK_SET) != 0) {
155         goto fail;
156     }
157 
158     if ((data = malloc(sz)) == NULL) {
159         goto fail;
160     }
161     x = data;
162     if (len) {
163         *len = sz;
164     }
165     while (sz > 0) {
166         errno = NO_ERROR;
167         r = read(fd, x, sz);
168         if (r < 0) {
169             if (errno == EINTR) {
170                 continue;
171             }
172             goto fail;
173         }
174         sz -= r;
175         x += r;
176     }
177     close(fd);
178     return data;
179 
180 fail:
181     if (data) {
182         free(data);
183     }
184     close(fd);
185     return NULL;
186 }
187 
bootimage_add_file(bootimage * img,unsigned type,const char * fn)188 bootentry_file *bootimage_add_file(bootimage *img, unsigned type, const char *fn) {
189     unsigned char *data;
190     size_t len;
191 
192     if ((data = load_file(fn, &len)) == NULL) {
193         fprintf(stderr, "error: cannot load '%s'\n", fn);
194         return NULL;
195     }
196 
197     /* if fpga image, trim everything before ffffffaa995566 and wordwise endian swap */
198     if (type == TYPE_FPGA_IMAGE) {
199         static const unsigned char pat[] = { 0xff, 0xff, 0xff, 0xff, 0xaa, 0x99, 0x55, 0x66 };
200 
201         size_t i;
202         if (len < sizeof(pat)) {
203             free(data);
204             fprintf(stderr, "error: fpga image too short\n");
205             return NULL;
206         }
207 
208         for (i = 0; i < len - sizeof(pat); i++) {
209             if (!memcmp(data + i, pat, sizeof(pat))) {
210                 /* we've found the pattern, trim everything before it */
211                 memmove(data, data + i, len - i);
212                 len -= i;
213             }
214         }
215 
216         /* wordwise endian swap */
217 #define SWAP_32(x) \
218     (((uint32_t)(x) << 24) | (((uint32_t)(x) & 0xff00) << 8) |(((uint32_t)(x) & 0x00ff0000) >> 8) | ((uint32_t)(x) >> 24))
219         uint32_t *w = (uint32_t *)data;
220         for (i = 0; i < len / 4; i++) {
221             *w = SWAP_32(*w);
222             w++;
223         }
224 #undef SWAP_32
225     }
226 
227     return bootimage_add_filedata(img, type, data, len);
228 }
229 
230