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