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         r = write(fd, x, len);
100         if (r < 0) {
101             if (errno == EINTR) {
102                 continue;
103             }
104             return -1;
105         }
106         len -= r;
107         x += r;
108     }
109     return 0;
110 }
111 
112 static uint8_t filler[4096] = { 0, };
113 
bootimage_write(bootimage * img,int fd)114 int bootimage_write(bootimage *img, int fd) {
115     unsigned off = 4096;
116     unsigned n, s;
117     if (writex(fd, img->entry, 4096)) {
118         return -1;
119     }
120     for (n = 1; n < 64; n++) {
121         if (img->offset[n] == 0) continue;
122         if (img->offset[n] < off) return -1;
123         s = img->offset[n] - off;
124         if (s > 4095) return -1;
125         if (writex(fd, filler, s)) {
126             return -1;
127         }
128         off += s;
129         if (writex(fd, img->data[n], img->length[n])) {
130             return -1;
131         }
132         off += img->length[n];
133     }
134     if (off & 4095) {
135         if (writex(fd, filler, 4096 - (off & 4095))) return -1;
136     }
137     return 0;
138 }
139 
load_file(const char * fn,size_t * len)140 static void *load_file(const char *fn, size_t *len) {
141     off_t sz;
142     void *data = NULL;
143     char *x;
144     int fd, r;
145 
146     if ((fd = open(fn, O_RDONLY)) < 0) {
147         return NULL;
148     }
149 
150     if ((sz = lseek(fd, 0, SEEK_END)) < 0) {
151         goto fail;
152     }
153     if (lseek(fd, 0, SEEK_SET) != 0) {
154         goto fail;
155     }
156 
157     if ((data = malloc(sz)) == NULL) {
158         goto fail;
159     }
160     x = data;
161     if (len) {
162         *len = sz;
163     }
164     while (sz > 0) {
165         r = read(fd, x, sz);
166         if (r < 0) {
167             if (errno == EINTR) {
168                 continue;
169             }
170             goto fail;
171         }
172         sz -= r;
173         x += r;
174     }
175     close(fd);
176     return data;
177 
178 fail:
179     if (data) {
180         free(data);
181     }
182     close(fd);
183     return NULL;
184 }
185 
bootimage_add_file(bootimage * img,unsigned type,const char * fn)186 bootentry_file *bootimage_add_file(bootimage *img, unsigned type, const char *fn) {
187     unsigned char *data;
188     size_t len;
189 
190     if ((data = load_file(fn, &len)) == NULL) {
191         fprintf(stderr, "error: cannot load '%s'\n", fn);
192         return NULL;
193     }
194 
195     /* if fpga image, trim everything before ffffffaa995566 and wordwise endian swap */
196     if (type == TYPE_FPGA_IMAGE) {
197         static const unsigned char pat[] = { 0xff, 0xff, 0xff, 0xff, 0xaa, 0x99, 0x55, 0x66 };
198 
199         size_t i;
200         if (len < sizeof(pat)) {
201             free(data);
202             fprintf(stderr, "error: fpga image too short\n");
203             return NULL;
204         }
205 
206         for (i = 0; i < len - sizeof(pat); i++) {
207             if (!memcmp(data + i, pat, sizeof(pat))) {
208                 /* we've found the pattern, trim everything before it */
209                 memmove(data, data + i, len - i);
210                 len -= i;
211             }
212         }
213 
214         /* wordwise endian swap */
215 #define SWAP_32(x) \
216     (((uint32_t)(x) << 24) | (((uint32_t)(x) & 0xff00) << 8) |(((uint32_t)(x) & 0x00ff0000) >> 8) | ((uint32_t)(x) >> 24))
217         uint32_t *w = (uint32_t *)data;
218         for (i = 0; i < len / 4; i++) {
219             *w = SWAP_32(*w);
220             w++;
221         }
222 #undef SWAP_32
223     }
224 
225     return bootimage_add_filedata(img, type, data, len);
226 }
227 
228