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