summaryrefslogtreecommitdiffstats
path: root/board/broadcom/bcmbca/boot_blob.c
blob: 80ceb318dc2c958b7b6772cdad76ceaa786d966e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * Copyright 2019 Broadcom Ltd.
 */

/* define DEBUG before common.h to enable debug macro */
#define DEBUG
#include <common.h>
#include <environment.h>
#include <nand.h>
#include <u-boot/sha256.h>
#include "boot_blob.h"
#include "boot_flash.h"
#include "spl_env.h"

/* overlay entry table */
#include "generated/hashtable.h"

static int search_blob_in_hash(uint32_t magic, uint32_t sel, int *len,
			       uint8_t * sha256)
{
	struct overlays entry;
	int i = 0, offset = -1;

	do {
		entry = ovl[i];
		if (entry.ovltype == 0)
			break;

		if (entry.ovltype == magic && entry.selector == sel) {
			offset = entry.offset;
			memcpy(sha256, entry.sha, 32);
			*len = entry.size;
			break;
		}
		i++;

	} while (1);

	if (offset == -1)
		printf("blob not found for magic 0x%x sel 0x%x!\n", magic, sel);

	return offset;
}

static int validate_blob_sha256_digest(void *buf, int size, void *pdigest)
{
	uint8_t out[32];
	sha256_context ctx;
	sha256_starts(&ctx);
	sha256_update(&ctx, buf, size);
	sha256_finish(&ctx, out);
	return memcmp(pdigest, out, 32);
}

static struct magic_search_s {
	uint32_t index;
	uint32_t last;
	uint32_t length;
	uint32_t dpfe_fs; /* dpfe blob fast search */  
	uint32_t magics[BOOT_BLOB_MAX_MAGIC_NUMS][BOOT_BLOB_MAX_MAGIC_SEARCH + 1];
} magic_search;

static void init_find_magic(void)
{
	int col, i;
	uint32_t e;
	printf("FFinit ");
	if (magic_search.last != 0) {
		return;
	}
	i = 0;
	while ((e = ovl[i].ovltype) != 0) {
		for (col = 0; col < BOOT_BLOB_MAX_MAGIC_NUMS; col++) {
			if (magic_search.magics[col][0] == e) {
				break;
			} else if (magic_search.magics[col][0] == 0) {
				magic_search.magics[col][0] = e;
				break;
			}
		}
		i++;
	}
	printf("done\n");

}

static int is_magic_match(uint32_t magic)
{
	/* DPFE blob maigc last nibble is the segment id. Ignore it */
	if ( IS_DPFE_MAGIC(magic_search.last))
		return ((magic_search.last&DPFE_MAGIC_MASK)
			== (magic&DPFE_MAGIC_MASK));
	else
		return (magic_search.last == magic);
}

static uint32_t find_magic(uint32_t magic)
{
	boot_blob_hdr hdr;
	uint32_t found;
	uint32_t end_addr = BOOT_BLOB_SEARCH_END_ADDR;

	if (!is_magic_match(magic)) {
		init_find_magic();
		magic_search.index = BOOT_BLOB_SEARCH_START_ADDR;
	}

	/* TODO start with cached offsets if already located ... */
	/* debug("look for magic number 0x%x starting at address 0x%x\n", */
	/*			   magic, magic_search.index); */
	while (magic_search.index < end_addr) {
		/* For DPFE segment blobs, all the segments are concontinous 
		 * in the flash. So the subsequent segment will be at least 
		 * at current addr + segment size. so we skip the search for 
		 * current segment to redcue read on boot blocks
		 */ 
		if (IS_DPFE_MAGIC(magic_search.last) && magic == (magic_search.last+1)
		    && magic_search.dpfe_fs) {
			magic_search.index += ROUND(magic_search.length + sizeof(hdr),
			    BOOT_BLOB_SEARCH_BOUNDARY);
			/* printf("magic 0x%x skip current dpfe blob, quick search at 0x%x\n", 
			      magic, magic_search.index);*/
		}
		
		hdr.length = hdr.magic = 0;
		/* if read fail, we hit bad block and revert back to 4k search */
		if (read_boot_device(magic_search.index, &hdr, sizeof(hdr)) < 0)
			magic_search.dpfe_fs = 0;

		/* TODO cache other magic numbers location while searching ... */
		found = magic_search.index;

		/* Return magic if we found it */
		if (hdr.magic == magic) {
			debug("find magic number 0x%x at address 0x%x\n",
				   magic, found);
			magic_search.last = hdr.magic;
			magic_search.length = hdr.length;
			if (IS_DPFE_MAGIC(magic_search.last))
				magic_search.dpfe_fs = 1;
			return found;
		} else {
			magic_search.dpfe_fs = 0;
		}

		/* Update search index to point to next entry */
		magic_search.index += BOOT_BLOB_SEARCH_BOUNDARY;
	}
	return ~0;
}

static int read_blob_from_flash(uint32_t magic, uint32_t offset, void *data,
				int len, uint8_t * digest)
{
	uint32_t entry;
	int ret = -1;

	while ((entry = find_magic(magic)) < BOOT_BLOB_SEARCH_END_ADDR) {
		printf("reading blob from 0x%x offset 0x%x len %d\n", entry, offset, len);
		read_boot_device(entry + offset, data, len);
		ret = validate_blob_sha256_digest(data, len, digest);
		if (ret == 0) {
			debug("digest sha256 OK\n");
			return BOOT_BLOB_SUCCESS;
		} else {
			debug("digest sha256 mismatch\n");
			magic_search.index += BOOT_BLOB_SEARCH_BOUNDARY;
			magic_search.dpfe_fs = 0;
		}
	}

	return BOOT_BLOB_MAGIC_NOT_FOUND;
}

int load_boot_blob(uint32_t magic, uint32_t sel, void *data, int* len)
{
	int offset;
	int size;
	unsigned char digest[32];

	/* find out the offset of the blob from the hash table first */
	if ((offset = search_blob_in_hash(magic, sel, &size, digest)) < 0)
		return BOOT_BLOB_NOT_IN_HASTTBL;

	if (size > *len) {
		printf("buffer too small\n");
		return BOOT_BLOB_INVALID_PARAM;
	}

	*len = size;
	/* find out the first the blob with the magic number from flash */
	return read_blob_from_flash(magic, offset, data, size, digest);
}

__weak void * load_spl_env(void *buffer)
{
	uint32_t entry;
	uint32_t len;
	uint32_t crc;
	uint32_t got;
	env_t *ep;
	while ((entry =
		find_magic(UBOOT_ENV_MAGIC)) < BOOT_BLOB_SEARCH_END_ADDR) {
		uint32_t *d = (uint32_t *) buffer;
		read_boot_device(entry, d, 8);
		len = d[1];
		if (len > BOOT_BLOB_MAX_ENV_SIZE) {
			continue;
		}
		read_boot_device(entry, d, len + 12);
		ep = (env_t *)(d+2);
		memcpy(&crc, &ep->crc, sizeof(crc));

		got = crc32(0, ep->data, len-4);

		if (got != crc) {
			debug("CRC mismatch len = %d\n",len);
			debug("computed %x \n",got);
			debug("expected %x \n",crc);
			magic_search.index += BOOT_BLOB_SEARCH_BOUNDARY;
		} else {
			return(buffer);
		}
	}
	return(NULL);
}


struct overlays* get_boot_blob_hash_entry(int i)
{

	if (i >= sizeof(ovl)/sizeof(struct overlays))
		return NULL;
	else
		return &ovl[i];
}