diff options
Diffstat (limited to 'common/bootstage.c')
| -rw-r--r-- | common/bootstage.c | 159 | 
1 files changed, 159 insertions, 0 deletions
| diff --git a/common/bootstage.c b/common/bootstage.c index 9ea0128aa..a1e09394c 100644 --- a/common/bootstage.c +++ b/common/bootstage.c @@ -44,6 +44,18 @@ struct bootstage_record {  static struct bootstage_record record[BOOTSTAGE_ID_COUNT] = { {1} };  static int next_id = BOOTSTAGE_ID_USER; +enum { +	BOOTSTAGE_VERSION	= 0, +	BOOTSTAGE_MAGIC		= 0xb00757a3, +}; + +struct bootstage_hdr { +	uint32_t version;	/* BOOTSTAGE_VERSION */ +	uint32_t count;		/* Number of records */ +	uint32_t size;		/* Total data size (non-zero if valid) */ +	uint32_t magic;		/* Unused */ +}; +  ulong bootstage_add_record(enum bootstage_id id, const char *name,  			   int flags, ulong mark)  { @@ -282,3 +294,150 @@ ulong __timer_get_boot_us(void)  ulong timer_get_boot_us(void)  	__attribute__((weak, alias("__timer_get_boot_us"))); + +/** + * Append data to a memory buffer + * + * Write data to the buffer if there is space. Whether there is space or not, + * the buffer pointer is incremented. + * + * @param ptrp	Pointer to buffer, updated by this function + * @param end	Pointer to end of buffer + * @param data	Data to write to buffer + * @param size	Size of data + */ +static void append_data(char **ptrp, char *end, const void *data, int size) +{ +	char *ptr = *ptrp; + +	*ptrp += size; +	if (*ptrp > end) +		return; + +	memcpy(ptr, data, size); +} + +int bootstage_stash(void *base, int size) +{ +	struct bootstage_hdr *hdr = (struct bootstage_hdr *)base; +	struct bootstage_record *rec; +	char buf[20]; +	char *ptr = base, *end = ptr + size; +	uint32_t count; +	int id; + +	if (hdr + 1 > (struct bootstage_hdr *)end) { +		debug("%s: Not enough space for bootstage hdr\n", __func__); +		return -1; +	} + +	/* Write an arbitrary version number */ +	hdr->version = BOOTSTAGE_VERSION; + +	/* Count the number of records, and write that value first */ +	for (rec = record, id = count = 0; id < BOOTSTAGE_ID_COUNT; +			id++, rec++) { +		if (rec->time_us != 0) +			count++; +	} +	hdr->count = count; +	hdr->size = 0; +	hdr->magic = BOOTSTAGE_MAGIC; +	ptr += sizeof(*hdr); + +	/* Write the records, silently stopping when we run out of space */ +	for (rec = record, id = 0; id < BOOTSTAGE_ID_COUNT; id++, rec++) { +		if (rec->time_us != 0) +			append_data(&ptr, end, rec, sizeof(*rec)); +	} + +	/* Write the name strings */ +	for (rec = record, id = 0; id < BOOTSTAGE_ID_COUNT; id++, rec++) { +		if (rec->time_us != 0) { +			const char *name; + +			name = get_record_name(buf, sizeof(buf), rec); +			append_data(&ptr, end, name, strlen(name) + 1); +		} +	} + +	/* Check for buffer overflow */ +	if (ptr > end) { +		debug("%s: Not enough space for bootstage stash\n", __func__); +		return -1; +	} + +	/* Update total data size */ +	hdr->size = ptr - (char *)base; +	printf("Stashed %d records\n", hdr->count); + +	return 0; +} + +int bootstage_unstash(void *base, int size) +{ +	struct bootstage_hdr *hdr = (struct bootstage_hdr *)base; +	struct bootstage_record *rec; +	char *ptr = base, *end = ptr + size; +	uint rec_size; +	int id; + +	if (size == -1) +		end = (char *)(~(uintptr_t)0); + +	if (hdr + 1 > (struct bootstage_hdr *)end) { +		debug("%s: Not enough space for bootstage hdr\n", __func__); +		return -1; +	} + +	if (hdr->magic != BOOTSTAGE_MAGIC) { +		debug("%s: Invalid bootstage magic\n", __func__); +		return -1; +	} + +	if (ptr + hdr->size > end) { +		debug("%s: Bootstage data runs past buffer end\n", __func__); +		return -1; +	} + +	if (hdr->count * sizeof(*rec) > hdr->size) { +		debug("%s: Bootstage has %d records needing %d bytes, but " +			"only %d bytes is available\n", __func__, hdr->count, +		      hdr->count * sizeof(*rec), hdr->size); +		return -1; +	} + +	if (hdr->version != BOOTSTAGE_VERSION) { +		debug("%s: Bootstage data version %#0x unrecognised\n", +		      __func__, hdr->version); +		return -1; +	} + +	if (next_id + hdr->count > BOOTSTAGE_ID_COUNT) { +		debug("%s: Bootstage has %d records, we have space for %d\n" +			"- please increase CONFIG_BOOTSTAGE_USER_COUNT\n", +		      __func__, hdr->count, BOOTSTAGE_ID_COUNT - next_id); +		return -1; +	} + +	ptr += sizeof(*hdr); + +	/* Read the records */ +	rec_size = hdr->count * sizeof(*record); +	memcpy(record + next_id, ptr, rec_size); + +	/* Read the name strings */ +	ptr += rec_size; +	for (rec = record + next_id, id = 0; id < hdr->count; id++, rec++) { +		rec->name = ptr; + +		/* Assume no data corruption here */ +		ptr += strlen(ptr) + 1; +	} + +	/* Mark the records as read */ +	next_id += hdr->count; +	printf("Unstashed %d records\n", hdr->count); + +	return 0; +} |