| /* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| * |
| * High-level firmware API for loading and verifying rewritable firmware. |
| * (Firmware portion) |
| */ |
| |
| #include "2sysincludes.h" |
| #include "2common.h" |
| |
| #include "sysincludes.h" |
| #include "bmpblk_header.h" |
| #include "region.h" |
| #include "gbb_access.h" |
| #include "gbb_header.h" |
| #include "load_kernel_fw.h" |
| #include "utility.h" |
| #include "vboot_api.h" |
| #include "vboot_struct.h" |
| |
| static VbError_t VbRegionReadGbb(VbCommonParams *cparams, uint32_t offset, |
| uint32_t size, void *buf) |
| { |
| return VbRegionReadData(cparams, VB_REGION_GBB, offset, size, buf); |
| } |
| |
| VbError_t VbGbbReadBmpHeader(VbCommonParams *cparams, BmpBlockHeader *hdr_ret) |
| { |
| BmpBlockHeader *hdr; |
| VbError_t ret; |
| |
| if (!cparams) |
| return VBERROR_INVALID_GBB; |
| if (!cparams->bmp) { |
| GoogleBinaryBlockHeader *gbb = cparams->gbb; |
| |
| if (0 == gbb->bmpfv_size) |
| return VBERROR_INVALID_GBB; |
| |
| hdr = malloc(sizeof(*hdr)); |
| ret = VbRegionReadGbb(cparams, gbb->bmpfv_offset, |
| sizeof(BmpBlockHeader), hdr); |
| if (ret) { |
| free(hdr); |
| return ret; |
| } |
| |
| /* Sanity-check the bitmap block header */ |
| if ((0 != memcmp(hdr->signature, BMPBLOCK_SIGNATURE, |
| BMPBLOCK_SIGNATURE_SIZE)) || |
| (hdr->major_version > BMPBLOCK_MAJOR_VERSION) || |
| ((hdr->major_version == BMPBLOCK_MAJOR_VERSION) && |
| (hdr->minor_version > BMPBLOCK_MINOR_VERSION))) { |
| VB2_DEBUG("VbGbbReadBmpHeader(): " |
| "invalid/too new bitmap header\n"); |
| free(hdr); |
| return VBERROR_INVALID_BMPFV; |
| } |
| cparams->bmp = hdr; |
| } |
| |
| *hdr_ret = *cparams->bmp; |
| return VBERROR_SUCCESS; |
| } |
| |
| VbError_t VbRegionReadHWID(VbCommonParams *cparams, char *hwid, |
| uint32_t max_size) |
| { |
| GoogleBinaryBlockHeader *gbb; |
| VbError_t ret; |
| |
| if (!max_size) |
| return VBERROR_INVALID_PARAMETER; |
| *hwid = '\0'; |
| StrnAppend(hwid, "{INVALID}", max_size); |
| if (!cparams) |
| return VBERROR_INVALID_GBB; |
| |
| gbb = cparams->gbb; |
| |
| if (0 == gbb->hwid_size) { |
| VB2_DEBUG("VbHWID(): invalid hwid size\n"); |
| return VBERROR_SUCCESS; /* oddly enough! */ |
| } |
| |
| if (gbb->hwid_size > max_size) { |
| VB2_DEBUG("VbDisplayDebugInfo(): invalid hwid offset/size\n"); |
| return VBERROR_INVALID_PARAMETER; |
| } |
| ret = VbRegionReadGbb(cparams, gbb->hwid_offset, gbb->hwid_size, hwid); |
| if (ret) |
| return ret; |
| |
| return VBERROR_SUCCESS; |
| } |
| |
| VbError_t VbGbbReadImage(VbCommonParams *cparams, |
| uint32_t localization, uint32_t screen_index, |
| uint32_t image_num, ScreenLayout *layout, |
| ImageInfo *image_info, char **image_datap, |
| uint32_t *image_data_sizep) |
| { |
| uint32_t layout_offset, image_offset, data_offset, data_size; |
| GoogleBinaryBlockHeader *gbb; |
| BmpBlockHeader hdr; |
| void *data = NULL; |
| VbError_t ret; |
| |
| if (!cparams) |
| return VBERROR_INVALID_GBB; |
| |
| ret = VbGbbReadBmpHeader(cparams, &hdr); |
| if (ret) |
| return ret; |
| |
| gbb = cparams->gbb; |
| layout_offset = gbb->bmpfv_offset + sizeof(BmpBlockHeader) + |
| localization * hdr.number_of_screenlayouts * |
| sizeof(ScreenLayout) + |
| screen_index * sizeof(ScreenLayout); |
| ret = VbRegionReadGbb(cparams, layout_offset, sizeof(*layout), layout); |
| if (ret) |
| return ret; |
| |
| if (!layout->images[image_num].image_info_offset) |
| return VBERROR_NO_IMAGE_PRESENT; |
| |
| image_offset = gbb->bmpfv_offset + |
| layout->images[image_num].image_info_offset; |
| ret = VbRegionReadGbb(cparams, image_offset, sizeof(*image_info), |
| image_info); |
| if (ret) |
| return ret; |
| |
| data_offset = image_offset + sizeof(*image_info); |
| data_size = image_info->compressed_size; |
| if (data_size) { |
| void *orig_data; |
| |
| data = malloc(image_info->compressed_size); |
| ret = VbRegionReadGbb(cparams, data_offset, |
| image_info->compressed_size, data); |
| if (ret) { |
| free(data); |
| return ret; |
| } |
| if (image_info->compression != COMPRESS_NONE) { |
| uint32_t inoutsize = image_info->original_size; |
| |
| orig_data = malloc(image_info->original_size); |
| ret = VbExDecompress(data, |
| image_info->compressed_size, |
| image_info->compression, |
| orig_data, &inoutsize); |
| data_size = inoutsize; |
| free(data); |
| data = orig_data; |
| if (ret) { |
| free(data); |
| return ret; |
| } |
| } |
| } |
| |
| *image_datap = data; |
| *image_data_sizep = data_size; |
| |
| return VBERROR_SUCCESS; |
| } |
| |
| #define OUTBUF_LEN 128 |
| |
| void VbRegionCheckVersion(VbCommonParams *cparams) |
| { |
| GoogleBinaryBlockHeader *gbb; |
| |
| if (!cparams) |
| return; |
| |
| gbb = cparams->gbb; |
| |
| /* |
| * If GBB flags is nonzero, complain because that's something that the |
| * factory MUST fix before shipping. We only have to do this here, |
| * because it's obvious that something is wrong if we're not displaying |
| * screens from the GBB. |
| */ |
| if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1 && |
| (gbb->flags != 0)) { |
| uint32_t used = 0; |
| char outbuf[OUTBUF_LEN]; |
| |
| *outbuf = '\0'; |
| used += StrnAppend(outbuf + used, "gbb.flags is nonzero: 0x", |
| OUTBUF_LEN - used); |
| used += Uint64ToString(outbuf + used, OUTBUF_LEN - used, |
| gbb->flags, 16, 8); |
| used += StrnAppend(outbuf + used, "\n", OUTBUF_LEN - used); |
| (void)VbExDisplayDebugInfo(outbuf); |
| } |
| } |