1/* Copyright (c) 2013 The Chromium OS 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 * High-level firmware API for loading and verifying rewritable firmware.
6 * (Firmware portion)
7 */
8
9#include "sysincludes.h"
10
11#include "bmpblk_header.h"
12#include "region.h"
13#include "gbb_access.h"
14#include "gbb_header.h"
15#include "load_kernel_fw.h"
16#include "utility.h"
17#include "vboot_api.h"
18#include "vboot_struct.h"
19
20static VbError_t VbRegionReadGbb(VbCommonParams *cparams, uint32_t offset,
21				  uint32_t size, void *buf)
22{
23	return VbRegionReadData(cparams, VB_REGION_GBB, offset, size, buf);
24}
25
26VbError_t VbGbbReadBmpHeader(VbCommonParams *cparams, BmpBlockHeader *hdr_ret)
27{
28	BmpBlockHeader *hdr;
29	VbError_t ret;
30
31	if (!cparams)
32		return VBERROR_INVALID_GBB;
33	if (!cparams->bmp) {
34		GoogleBinaryBlockHeader *gbb = cparams->gbb;
35
36		if (0 == gbb->bmpfv_size)
37			return VBERROR_INVALID_GBB;
38
39		hdr = VbExMalloc(sizeof(*hdr));
40		ret = VbRegionReadGbb(cparams, gbb->bmpfv_offset,
41				      sizeof(BmpBlockHeader), hdr);
42		if (ret) {
43			VbExFree(hdr);
44			return ret;
45		}
46
47		/* Sanity-check the bitmap block header */
48		if ((0 != Memcmp(hdr->signature, BMPBLOCK_SIGNATURE,
49				BMPBLOCK_SIGNATURE_SIZE)) ||
50		(hdr->major_version > BMPBLOCK_MAJOR_VERSION) ||
51		((hdr->major_version == BMPBLOCK_MAJOR_VERSION) &&
52		(hdr->minor_version > BMPBLOCK_MINOR_VERSION))) {
53			VBDEBUG(("VbDisplayScreenFromGBB(): "
54				"invalid/too new bitmap header\n"));
55			VbExFree(hdr);
56			return VBERROR_INVALID_BMPFV;
57		}
58		cparams->bmp = hdr;
59	}
60
61	*hdr_ret = *cparams->bmp;
62	return VBERROR_SUCCESS;
63}
64
65VbError_t VbRegionReadHWID(VbCommonParams *cparams, char *hwid,
66			   uint32_t max_size)
67{
68	GoogleBinaryBlockHeader *gbb;
69	VbError_t ret;
70
71	if (!max_size)
72		return VBERROR_INVALID_PARAMETER;
73	*hwid = '\0';
74	StrnAppend(hwid, "{INVALID}", max_size);
75	if (!cparams)
76		return VBERROR_INVALID_GBB;
77
78	gbb = cparams->gbb;
79
80	if (0 == gbb->hwid_size) {
81		VBDEBUG(("VbHWID(): invalid hwid size\n"));
82		return VBERROR_SUCCESS; /* oddly enough! */
83	}
84
85	if (gbb->hwid_size > max_size) {
86		VBDEBUG(("VbDisplayDebugInfo(): invalid hwid offset/size\n"));
87		return VBERROR_INVALID_PARAMETER;
88	}
89	ret = VbRegionReadGbb(cparams, gbb->hwid_offset, gbb->hwid_size, hwid);
90	if (ret)
91		return ret;
92
93	return VBERROR_SUCCESS;
94}
95
96VbError_t VbGbbReadImage(VbCommonParams *cparams,
97			       uint32_t localization, uint32_t screen_index,
98			       uint32_t image_num, ScreenLayout *layout,
99			       ImageInfo *image_info, char **image_datap,
100			       uint32_t *image_data_sizep)
101{
102	uint32_t layout_offset, image_offset, data_offset, data_size;
103	GoogleBinaryBlockHeader *gbb;
104	BmpBlockHeader hdr;
105	void *data = NULL;
106	VbError_t ret;
107
108	if (!cparams)
109		return VBERROR_INVALID_GBB;
110
111	ret = VbGbbReadBmpHeader(cparams, &hdr);
112	if (ret)
113		return ret;
114
115	gbb = cparams->gbb;
116	layout_offset = gbb->bmpfv_offset + sizeof(BmpBlockHeader) +
117		localization * hdr.number_of_screenlayouts *
118			sizeof(ScreenLayout) +
119		screen_index * sizeof(ScreenLayout);
120	ret = VbRegionReadGbb(cparams, layout_offset, sizeof(*layout), layout);
121	if (ret)
122		return ret;
123
124	if (!layout->images[image_num].image_info_offset)
125		return VBERROR_NO_IMAGE_PRESENT;
126
127	image_offset = gbb->bmpfv_offset +
128			layout->images[image_num].image_info_offset;
129	ret = VbRegionReadGbb(cparams, image_offset, sizeof(*image_info),
130			      image_info);
131	if (ret)
132		return ret;
133
134	data_offset = image_offset + sizeof(*image_info);
135	data_size = image_info->compressed_size;
136	if (data_size) {
137		void *orig_data;
138
139		data = VbExMalloc(image_info->compressed_size);
140		ret = VbRegionReadGbb(cparams, data_offset,
141				      image_info->compressed_size, data);
142		if (ret) {
143			VbExFree(data);
144			return ret;
145		}
146		if (image_info->compression != COMPRESS_NONE) {
147			uint32_t inoutsize = image_info->original_size;
148
149			orig_data = VbExMalloc(image_info->original_size);
150			ret = VbExDecompress(data,
151					     image_info->compressed_size,
152					     image_info->compression,
153					     orig_data, &inoutsize);
154			data_size = inoutsize;
155			VbExFree(data);
156			data = orig_data;
157			if (ret) {
158				VbExFree(data);
159				return ret;
160			}
161		}
162	}
163
164	*image_datap = data;
165	*image_data_sizep = data_size;
166
167	return VBERROR_SUCCESS;
168}
169
170#define OUTBUF_LEN 128
171
172void VbRegionCheckVersion(VbCommonParams *cparams)
173{
174	GoogleBinaryBlockHeader *gbb;
175
176	if (!cparams)
177		return;
178
179	gbb = cparams->gbb;
180
181	/*
182	 * If GBB flags is nonzero, complain because that's something that the
183	 * factory MUST fix before shipping. We only have to do this here,
184	 * because it's obvious that something is wrong if we're not displaying
185	 * screens from the GBB.
186	 */
187	if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1 &&
188	    (gbb->flags != 0)) {
189		uint32_t used = 0;
190		char outbuf[OUTBUF_LEN];
191
192		*outbuf = '\0';
193		used += StrnAppend(outbuf + used, "gbb.flags is nonzero: 0x",
194				OUTBUF_LEN - used);
195		used += Uint64ToString(outbuf + used, OUTBUF_LEN - used,
196				       gbb->flags, 16, 8);
197		used += StrnAppend(outbuf + used, "\n", OUTBUF_LEN - used);
198		(void)VbExDisplayDebugInfo(outbuf);
199	}
200}
201