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