1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <libzbi/zbi.h>
6 #include <stdbool.h>
7 #include <string.h>
8
9 struct check_state {
10 zbi_header_t** err;
11 bool seen_bootfs;
12 };
13
zbi_init(void * buffer,const size_t length)14 zbi_result_t zbi_init(void* buffer, const size_t length) {
15 if (length < sizeof(zbi_header_t)) {
16 return ZBI_RESULT_TOO_BIG;
17 }
18
19 zbi_header_t* hdr = (zbi_header_t*)buffer;
20 hdr->type = ZBI_TYPE_CONTAINER;
21 hdr->length = 0;
22 hdr->extra = ZBI_CONTAINER_MAGIC;
23 hdr->flags = ZBI_FLAG_VERSION;
24 hdr->reserved0 = 0;
25 hdr->reserved1 = 0;
26 hdr->magic = ZBI_ITEM_MAGIC;
27 hdr->crc32 = ZBI_ITEM_NO_CRC32;
28
29 return ZBI_RESULT_OK;
30 }
31
for_each_check_entry(zbi_header_t * hdr,void * payload,void * cookie)32 static zbi_result_t for_each_check_entry(zbi_header_t* hdr, void* payload,
33 void* cookie) {
34 struct check_state* const state = cookie;
35
36 zbi_result_t result = ZBI_RESULT_OK;
37
38 if (hdr->magic != ZBI_ITEM_MAGIC) {
39 result = ZBI_RESULT_BAD_MAGIC;
40 } else if ((hdr->flags & ZBI_FLAG_VERSION) == 0) {
41 result = ZBI_RESULT_BAD_VERSION;
42 } else if ((hdr->flags & ZBI_FLAG_CRC32) == 0 &&
43 hdr->crc32 != ZBI_ITEM_NO_CRC32) {
44 result = ZBI_RESULT_BAD_CRC;
45 }
46
47 // If we found a problem, try to report it back up to the caller.
48 if (state->err != NULL && result != ZBI_RESULT_OK) {
49 *state->err = hdr;
50 }
51
52 if (hdr->type == ZBI_TYPE_STORAGE_BOOTFS) {
53 state->seen_bootfs = true;
54 }
55
56 return result;
57 }
58
zbi_check_internal(const void * base,uint32_t check_complete,zbi_header_t ** err)59 static zbi_result_t zbi_check_internal(const void* base,
60 uint32_t check_complete,
61 zbi_header_t** err) {
62 zbi_result_t res = ZBI_RESULT_OK;
63 const zbi_header_t* header = base;
64
65 if (header->type != ZBI_TYPE_CONTAINER) {
66 res = ZBI_RESULT_BAD_TYPE;
67 } else if (header->extra != ZBI_CONTAINER_MAGIC) {
68 res = ZBI_RESULT_BAD_MAGIC;
69 } else if ((header->flags & ZBI_FLAG_VERSION) == 0) {
70 res = ZBI_RESULT_BAD_VERSION;
71 } else if ((header->flags & ZBI_FLAG_CRC32) == 0 &&
72 header->crc32 != ZBI_ITEM_NO_CRC32) {
73 res = ZBI_RESULT_BAD_CRC;
74 }
75
76 // Something was wrong with the container. Don't even attempt to process
77 // the rest of the image. Return diagnostic information back to the caller
78 // if they requested it.
79 if (res != ZBI_RESULT_OK) {
80 if (err) {
81 *err = (zbi_header_t*)header;
82 }
83 return res;
84 }
85
86 struct check_state state = {.err = err};
87 res = zbi_for_each(base, for_each_check_entry, &state);
88
89 if (res == ZBI_RESULT_OK && check_complete != 0) {
90 if (header->length == 0) {
91 res = ZBI_RESULT_ERR_TRUNCATED;
92 } else if (header[1].type != check_complete) {
93 res = ZBI_RESULT_INCOMPLETE_KERNEL;
94 if (err) {
95 *err = (zbi_header_t*)(header + 1);
96 }
97 } else if (!state.seen_bootfs) {
98 res = ZBI_RESULT_INCOMPLETE_BOOTFS;
99 if (err) {
100 *err = (zbi_header_t*)header;
101 }
102 }
103 }
104
105 if (err && res == ZBI_RESULT_ERR_TRUNCATED) {
106 // A truncated image perhaps indicates a problem with the container?
107 *err = (zbi_header_t*)header;
108 }
109
110 return res;
111 }
112
zbi_check(const void * base,zbi_header_t ** err)113 zbi_result_t zbi_check(const void* base, zbi_header_t** err) {
114 return zbi_check_internal(base, 0, err);
115 }
116
zbi_check_complete(const void * base,zbi_header_t ** err)117 zbi_result_t zbi_check_complete(const void* base, zbi_header_t** err) {
118 return zbi_check_internal(base,
119 #ifdef __aarch64__
120 ZBI_TYPE_KERNEL_ARM64,
121 #elif defined(__x86_64__) || defined(__i386__)
122 ZBI_TYPE_KERNEL_X64,
123 #else
124 #error "what architecture?"
125 #endif
126 err);
127 }
128
zbi_for_each(const void * base,const zbi_foreach_cb_t cb,void * cookie)129 zbi_result_t zbi_for_each(const void* base, const zbi_foreach_cb_t cb,
130 void* cookie) {
131 zbi_header_t* header = (zbi_header_t*)(base);
132
133 // Skip container header.
134 const uint32_t totalSize = (uint32_t)sizeof(zbi_header_t) + header->length;
135 uint32_t offset = sizeof(zbi_header_t);
136 while (offset < totalSize) {
137 zbi_header_t* entryHeader =
138 (zbi_header_t*)(base + offset);
139
140 zbi_result_t result = cb(entryHeader, entryHeader + 1, cookie);
141
142 if (result != ZBI_RESULT_OK) {
143 return result;
144 }
145
146 if ((offset + entryHeader->length + sizeof(zbi_header_t)) > totalSize) {
147 return ZBI_RESULT_ERR_TRUNCATED;
148 }
149
150 offset = ZBI_ALIGN(offset + entryHeader->length + sizeof(zbi_header_t));
151 }
152
153 return ZBI_RESULT_OK;
154 }
155
zbi_append_section(void * base,const size_t capacity,uint32_t section_length,uint32_t type,uint32_t extra,uint32_t flags,const void * payload)156 zbi_result_t zbi_append_section(void* base, const size_t capacity,
157 uint32_t section_length, uint32_t type,
158 uint32_t extra, uint32_t flags,
159 const void* payload) {
160
161 uint8_t* new_section;
162 zbi_result_t result = zbi_create_section(base, capacity, section_length,
163 type, extra, flags,
164 (void**)&new_section);
165
166 if (result != ZBI_RESULT_OK) {
167 return result;
168 }
169
170 // Copy in the payload.
171 memcpy(new_section, payload, section_length);
172 return ZBI_RESULT_OK;
173 }
174
zbi_create_section(void * base,size_t capacity,uint32_t section_length,uint32_t type,uint32_t extra,uint32_t flags,void ** payload)175 zbi_result_t zbi_create_section(void* base, size_t capacity,
176 uint32_t section_length, uint32_t type,
177 uint32_t extra, uint32_t flags,
178 void** payload) {
179 // We don't support CRC computation (yet?)
180 if (flags & ZBI_FLAG_CRC32) {
181 return ZBI_RESULT_ERROR;
182 }
183
184 zbi_header_t* hdr = (zbi_header_t*)base;
185
186 // Make sure we were actually passed a bootdata container.
187 if ((hdr->type != ZBI_TYPE_CONTAINER) ||
188 (hdr->magic != ZBI_ITEM_MAGIC) ||
189 (hdr->extra != ZBI_CONTAINER_MAGIC)) {
190 return ZBI_RESULT_BAD_TYPE;
191 }
192
193 // Make sure we have enough space in the buffer to append the new section.
194 if (capacity - sizeof(*hdr) < hdr->length) {
195 return ZBI_RESULT_TOO_BIG;
196 }
197 const size_t available = capacity - sizeof(*hdr) - hdr->length;
198 if (available < sizeof(*hdr) ||
199 available - sizeof(*hdr) < ZBI_ALIGN(section_length)) {
200 return ZBI_RESULT_TOO_BIG;
201 }
202
203 // Fill in the new section header.
204 zbi_header_t* new_header = (void*)((uint8_t*)(hdr + 1) + hdr->length);
205 *new_header = (zbi_header_t){
206 .type = type,
207 .length = section_length,
208 .extra = extra,
209 .flags = flags | ZBI_FLAG_VERSION,
210 .magic = ZBI_ITEM_MAGIC,
211 .crc32 = ZBI_ITEM_NO_CRC32,
212 };
213
214 // Tell the caller where to fill in the payload.
215 *payload = new_header + 1;
216
217 // Update the container header, always keeping the length aligned.
218 hdr->length += sizeof(*new_header) + new_header->length;
219 if (hdr->length % ZBI_ALIGNMENT != 0) {
220 uint32_t aligned_length = ZBI_ALIGN(hdr->length);
221 if (capacity - sizeof(*hdr) < aligned_length) {
222 return ZBI_RESULT_TOO_BIG;
223 }
224 memset((uint8_t*)(hdr + 1) + hdr->length, 0,
225 aligned_length - hdr->length);
226 hdr->length = aligned_length;
227 }
228
229 return ZBI_RESULT_OK;
230 }
231