xref: /openbmc/u-boot/common/bootstage.c (revision e8f80a5a)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
23a608ca0SSimon Glass /*
33a608ca0SSimon Glass  * Copyright (c) 2011, Google Inc. All rights reserved.
43a608ca0SSimon Glass  */
53a608ca0SSimon Glass 
63a608ca0SSimon Glass 
73a608ca0SSimon Glass /*
83a608ca0SSimon Glass  * This module records the progress of boot and arbitrary commands, and
93a608ca0SSimon Glass  * permits accurate timestamping of each.
103a608ca0SSimon Glass  */
113a608ca0SSimon Glass 
123a608ca0SSimon Glass #include <common.h>
13b08c8c48SMasahiro Yamada #include <linux/libfdt.h>
14fb7db41cSSimon Glass #include <malloc.h>
15fb7db41cSSimon Glass #include <linux/compiler.h>
163a608ca0SSimon Glass 
173a608ca0SSimon Glass DECLARE_GLOBAL_DATA_PTR;
183a608ca0SSimon Glass 
1903ecac31SSimon Glass enum {
20d69bb0ecSSimon Glass 	RECORD_COUNT = CONFIG_VAL(BOOTSTAGE_RECORD_COUNT),
2103ecac31SSimon Glass };
2203ecac31SSimon Glass 
233a608ca0SSimon Glass struct bootstage_record {
243a608ca0SSimon Glass 	ulong time_us;
25094e06a5SSimon Glass 	uint32_t start_us;
263a608ca0SSimon Glass 	const char *name;
273a608ca0SSimon Glass 	int flags;		/* see enum bootstage_flags */
283a608ca0SSimon Glass 	enum bootstage_id id;
293a608ca0SSimon Glass };
303a608ca0SSimon Glass 
31b383d6c0SSimon Glass struct bootstage_data {
3203ecac31SSimon Glass 	uint rec_count;
33b383d6c0SSimon Glass 	uint next_id;
3403ecac31SSimon Glass 	struct bootstage_record record[RECORD_COUNT];
35b383d6c0SSimon Glass };
363a608ca0SSimon Glass 
37fcf509b8SSimon Glass enum {
38fcf509b8SSimon Glass 	BOOTSTAGE_VERSION	= 0,
39fcf509b8SSimon Glass 	BOOTSTAGE_MAGIC		= 0xb00757a3,
40b8bcaa3aSSimon Glass 	BOOTSTAGE_DIGITS	= 9,
41fcf509b8SSimon Glass };
42fcf509b8SSimon Glass 
43fcf509b8SSimon Glass struct bootstage_hdr {
44fcf509b8SSimon Glass 	uint32_t version;	/* BOOTSTAGE_VERSION */
45fcf509b8SSimon Glass 	uint32_t count;		/* Number of records */
46fcf509b8SSimon Glass 	uint32_t size;		/* Total data size (non-zero if valid) */
47fcf509b8SSimon Glass 	uint32_t magic;		/* Unused */
48fcf509b8SSimon Glass };
49fcf509b8SSimon Glass 
bootstage_relocate(void)50150678a5SDoug Anderson int bootstage_relocate(void)
51150678a5SDoug Anderson {
52b383d6c0SSimon Glass 	struct bootstage_data *data = gd->bootstage;
53150678a5SDoug Anderson 	int i;
54150678a5SDoug Anderson 
55150678a5SDoug Anderson 	/*
56150678a5SDoug Anderson 	 * Duplicate all strings.  They may point to an old location in the
57150678a5SDoug Anderson 	 * program .text section that can eventually get trashed.
58150678a5SDoug Anderson 	 */
5903ecac31SSimon Glass 	debug("Relocating %d records\n", data->rec_count);
6003ecac31SSimon Glass 	for (i = 0; i < data->rec_count; i++)
61b383d6c0SSimon Glass 		data->record[i].name = strdup(data->record[i].name);
62150678a5SDoug Anderson 
63150678a5SDoug Anderson 	return 0;
64150678a5SDoug Anderson }
65150678a5SDoug Anderson 
find_id(struct bootstage_data * data,enum bootstage_id id)6603ecac31SSimon Glass struct bootstage_record *find_id(struct bootstage_data *data,
6703ecac31SSimon Glass 				 enum bootstage_id id)
6803ecac31SSimon Glass {
6903ecac31SSimon Glass 	struct bootstage_record *rec;
7003ecac31SSimon Glass 	struct bootstage_record *end;
7103ecac31SSimon Glass 
7203ecac31SSimon Glass 	for (rec = data->record, end = rec + data->rec_count; rec < end;
7303ecac31SSimon Glass 	     rec++) {
7403ecac31SSimon Glass 		if (rec->id == id)
7503ecac31SSimon Glass 			return rec;
7603ecac31SSimon Glass 	}
7703ecac31SSimon Glass 
7803ecac31SSimon Glass 	return NULL;
7903ecac31SSimon Glass }
8003ecac31SSimon Glass 
ensure_id(struct bootstage_data * data,enum bootstage_id id)8103ecac31SSimon Glass struct bootstage_record *ensure_id(struct bootstage_data *data,
8203ecac31SSimon Glass 				   enum bootstage_id id)
8303ecac31SSimon Glass {
8403ecac31SSimon Glass 	struct bootstage_record *rec;
8503ecac31SSimon Glass 
8603ecac31SSimon Glass 	rec = find_id(data, id);
8703ecac31SSimon Glass 	if (!rec && data->rec_count < RECORD_COUNT) {
8803ecac31SSimon Glass 		rec = &data->record[data->rec_count++];
8903ecac31SSimon Glass 		rec->id = id;
9003ecac31SSimon Glass 		return rec;
9103ecac31SSimon Glass 	}
9203ecac31SSimon Glass 
9303ecac31SSimon Glass 	return rec;
9403ecac31SSimon Glass }
9503ecac31SSimon Glass 
bootstage_add_record(enum bootstage_id id,const char * name,int flags,ulong mark)963a608ca0SSimon Glass ulong bootstage_add_record(enum bootstage_id id, const char *name,
97094e06a5SSimon Glass 			   int flags, ulong mark)
983a608ca0SSimon Glass {
99b383d6c0SSimon Glass 	struct bootstage_data *data = gd->bootstage;
1003a608ca0SSimon Glass 	struct bootstage_record *rec;
1013a608ca0SSimon Glass 
1023a608ca0SSimon Glass 	if (flags & BOOTSTAGEF_ALLOC)
103b383d6c0SSimon Glass 		id = data->next_id++;
1043a608ca0SSimon Glass 
1053a608ca0SSimon Glass 	/* Only record the first event for each */
10603ecac31SSimon Glass 	rec = find_id(data, id);
10703ecac31SSimon Glass 	if (!rec && data->rec_count < RECORD_COUNT) {
10803ecac31SSimon Glass 		rec = &data->record[data->rec_count++];
1093a608ca0SSimon Glass 		rec->time_us = mark;
1103a608ca0SSimon Glass 		rec->name = name;
1113a608ca0SSimon Glass 		rec->flags = flags;
1123a608ca0SSimon Glass 		rec->id = id;
1133a608ca0SSimon Glass 	}
1143a608ca0SSimon Glass 
1153a608ca0SSimon Glass 	/* Tell the board about this progress */
1163a608ca0SSimon Glass 	show_boot_progress(flags & BOOTSTAGEF_ERROR ? -id : id);
117cbcd6970SSimon Glass 
1183a608ca0SSimon Glass 	return mark;
1193a608ca0SSimon Glass }
1203a608ca0SSimon Glass 
1213a608ca0SSimon Glass 
bootstage_mark(enum bootstage_id id)1223a608ca0SSimon Glass ulong bootstage_mark(enum bootstage_id id)
1233a608ca0SSimon Glass {
124094e06a5SSimon Glass 	return bootstage_add_record(id, NULL, 0, timer_get_boot_us());
1253a608ca0SSimon Glass }
1263a608ca0SSimon Glass 
bootstage_error(enum bootstage_id id)1273a608ca0SSimon Glass ulong bootstage_error(enum bootstage_id id)
1283a608ca0SSimon Glass {
129094e06a5SSimon Glass 	return bootstage_add_record(id, NULL, BOOTSTAGEF_ERROR,
130094e06a5SSimon Glass 				    timer_get_boot_us());
1313a608ca0SSimon Glass }
1323a608ca0SSimon Glass 
bootstage_mark_name(enum bootstage_id id,const char * name)1333a608ca0SSimon Glass ulong bootstage_mark_name(enum bootstage_id id, const char *name)
1343a608ca0SSimon Glass {
1353a608ca0SSimon Glass 	int flags = 0;
1363a608ca0SSimon Glass 
1373a608ca0SSimon Glass 	if (id == BOOTSTAGE_ID_ALLOC)
1383a608ca0SSimon Glass 		flags = BOOTSTAGEF_ALLOC;
139cbcd6970SSimon Glass 
140094e06a5SSimon Glass 	return bootstage_add_record(id, name, flags, timer_get_boot_us());
1413a608ca0SSimon Glass }
1423a608ca0SSimon Glass 
bootstage_mark_code(const char * file,const char * func,int linenum)143fb7db41cSSimon Glass ulong bootstage_mark_code(const char *file, const char *func, int linenum)
144fb7db41cSSimon Glass {
145fb7db41cSSimon Glass 	char *str, *p;
146fb7db41cSSimon Glass 	__maybe_unused char *end;
147fb7db41cSSimon Glass 	int len = 0;
148fb7db41cSSimon Glass 
149fb7db41cSSimon Glass 	/* First work out the length we need to allocate */
150fb7db41cSSimon Glass 	if (linenum != -1)
151fb7db41cSSimon Glass 		len = 11;
152fb7db41cSSimon Glass 	if (func)
153fb7db41cSSimon Glass 		len += strlen(func);
154fb7db41cSSimon Glass 	if (file)
155fb7db41cSSimon Glass 		len += strlen(file);
156fb7db41cSSimon Glass 
157fb7db41cSSimon Glass 	str = malloc(len + 1);
158fb7db41cSSimon Glass 	p = str;
159fb7db41cSSimon Glass 	end = p + len;
160fb7db41cSSimon Glass 	if (file)
161fb7db41cSSimon Glass 		p += snprintf(p, end - p, "%s,", file);
162fb7db41cSSimon Glass 	if (linenum != -1)
163fb7db41cSSimon Glass 		p += snprintf(p, end - p, "%d", linenum);
164fb7db41cSSimon Glass 	if (func)
165fb7db41cSSimon Glass 		p += snprintf(p, end - p, ": %s", func);
166fb7db41cSSimon Glass 
167fb7db41cSSimon Glass 	return bootstage_mark_name(BOOTSTAGE_ID_ALLOC, str);
168fb7db41cSSimon Glass }
169fb7db41cSSimon Glass 
bootstage_start(enum bootstage_id id,const char * name)1700e996773SSimon Glass uint32_t bootstage_start(enum bootstage_id id, const char *name)
1710e996773SSimon Glass {
172b383d6c0SSimon Glass 	struct bootstage_data *data = gd->bootstage;
17303ecac31SSimon Glass 	struct bootstage_record *rec = ensure_id(data, id);
17403ecac31SSimon Glass 	ulong start_us = timer_get_boot_us();
1750e996773SSimon Glass 
17603ecac31SSimon Glass 	if (rec) {
17703ecac31SSimon Glass 		rec->start_us = start_us;
1780e996773SSimon Glass 		rec->name = name;
17903ecac31SSimon Glass 	}
180cbcd6970SSimon Glass 
18103ecac31SSimon Glass 	return start_us;
1820e996773SSimon Glass }
1830e996773SSimon Glass 
bootstage_accum(enum bootstage_id id)1840e996773SSimon Glass uint32_t bootstage_accum(enum bootstage_id id)
1850e996773SSimon Glass {
186b383d6c0SSimon Glass 	struct bootstage_data *data = gd->bootstage;
18703ecac31SSimon Glass 	struct bootstage_record *rec = ensure_id(data, id);
1880e996773SSimon Glass 	uint32_t duration;
1890e996773SSimon Glass 
19003ecac31SSimon Glass 	if (!rec)
19103ecac31SSimon Glass 		return 0;
1920e996773SSimon Glass 	duration = (uint32_t)timer_get_boot_us() - rec->start_us;
1930e996773SSimon Glass 	rec->time_us += duration;
194cbcd6970SSimon Glass 
1950e996773SSimon Glass 	return duration;
1960e996773SSimon Glass }
1970e996773SSimon Glass 
19894fd1316SSimon Glass /**
19994fd1316SSimon Glass  * Get a record name as a printable string
20094fd1316SSimon Glass  *
20194fd1316SSimon Glass  * @param buf	Buffer to put name if needed
20294fd1316SSimon Glass  * @param len	Length of buffer
20394fd1316SSimon Glass  * @param rec	Boot stage record to get the name from
20494fd1316SSimon Glass  * @return pointer to name, either from the record or pointing to buf.
20594fd1316SSimon Glass  */
get_record_name(char * buf,int len,const struct bootstage_record * rec)20694fd1316SSimon Glass static const char *get_record_name(char *buf, int len,
2079d2542d0SSimon Glass 				   const struct bootstage_record *rec)
20894fd1316SSimon Glass {
20994fd1316SSimon Glass 	if (rec->name)
21094fd1316SSimon Glass 		return rec->name;
21194fd1316SSimon Glass 	else if (rec->id >= BOOTSTAGE_ID_USER)
21294fd1316SSimon Glass 		snprintf(buf, len, "user_%d", rec->id - BOOTSTAGE_ID_USER);
21394fd1316SSimon Glass 	else
21494fd1316SSimon Glass 		snprintf(buf, len, "id=%d", rec->id);
21594fd1316SSimon Glass 
21694fd1316SSimon Glass 	return buf;
21794fd1316SSimon Glass }
21894fd1316SSimon Glass 
print_time_record(struct bootstage_record * rec,uint32_t prev)219b383d6c0SSimon Glass static uint32_t print_time_record(struct bootstage_record *rec, uint32_t prev)
2203a608ca0SSimon Glass {
22194fd1316SSimon Glass 	char buf[20];
22294fd1316SSimon Glass 
2230e996773SSimon Glass 	if (prev == -1U) {
2240e996773SSimon Glass 		printf("%11s", "");
225b8bcaa3aSSimon Glass 		print_grouped_ull(rec->time_us, BOOTSTAGE_DIGITS);
2260e996773SSimon Glass 	} else {
227b8bcaa3aSSimon Glass 		print_grouped_ull(rec->time_us, BOOTSTAGE_DIGITS);
228b8bcaa3aSSimon Glass 		print_grouped_ull(rec->time_us - prev, BOOTSTAGE_DIGITS);
2290e996773SSimon Glass 	}
23094fd1316SSimon Glass 	printf("  %s\n", get_record_name(buf, sizeof(buf), rec));
23194fd1316SSimon Glass 
2323a608ca0SSimon Glass 	return rec->time_us;
2333a608ca0SSimon Glass }
2343a608ca0SSimon Glass 
h_compare_record(const void * r1,const void * r2)2353a608ca0SSimon Glass static int h_compare_record(const void *r1, const void *r2)
2363a608ca0SSimon Glass {
2373a608ca0SSimon Glass 	const struct bootstage_record *rec1 = r1, *rec2 = r2;
2383a608ca0SSimon Glass 
2393a608ca0SSimon Glass 	return rec1->time_us > rec2->time_us ? 1 : -1;
2403a608ca0SSimon Glass }
2413a608ca0SSimon Glass 
24294fd1316SSimon Glass #ifdef CONFIG_OF_LIBFDT
24394fd1316SSimon Glass /**
24494fd1316SSimon Glass  * Add all bootstage timings to a device tree.
24594fd1316SSimon Glass  *
24694fd1316SSimon Glass  * @param blob	Device tree blob
24794fd1316SSimon Glass  * @return 0 on success, != 0 on failure.
24894fd1316SSimon Glass  */
add_bootstages_devicetree(struct fdt_header * blob)24994fd1316SSimon Glass static int add_bootstages_devicetree(struct fdt_header *blob)
25094fd1316SSimon Glass {
251b383d6c0SSimon Glass 	struct bootstage_data *data = gd->bootstage;
25294fd1316SSimon Glass 	int bootstage;
25394fd1316SSimon Glass 	char buf[20];
25403ecac31SSimon Glass 	int recnum;
25594fd1316SSimon Glass 	int i;
25694fd1316SSimon Glass 
25794fd1316SSimon Glass 	if (!blob)
25894fd1316SSimon Glass 		return 0;
25994fd1316SSimon Glass 
26094fd1316SSimon Glass 	/*
26194fd1316SSimon Glass 	 * Create the node for bootstage.
26294fd1316SSimon Glass 	 * The address of flat device tree is set up by the command bootm.
26394fd1316SSimon Glass 	 */
26494fd1316SSimon Glass 	bootstage = fdt_add_subnode(blob, 0, "bootstage");
26594fd1316SSimon Glass 	if (bootstage < 0)
266e003310aSSimon Glass 		return -EINVAL;
26794fd1316SSimon Glass 
26894fd1316SSimon Glass 	/*
26994fd1316SSimon Glass 	 * Insert the timings to the device tree in the reverse order so
27094fd1316SSimon Glass 	 * that they can be printed in the Linux kernel in the right order.
27194fd1316SSimon Glass 	 */
27203ecac31SSimon Glass 	for (recnum = data->rec_count - 1, i = 0; recnum >= 0; recnum--, i++) {
27303ecac31SSimon Glass 		struct bootstage_record *rec = &data->record[recnum];
27494fd1316SSimon Glass 		int node;
27594fd1316SSimon Glass 
27603ecac31SSimon Glass 		if (rec->id != BOOTSTAGE_ID_AWAKE && rec->time_us == 0)
27794fd1316SSimon Glass 			continue;
27894fd1316SSimon Glass 
27994fd1316SSimon Glass 		node = fdt_add_subnode(blob, bootstage, simple_itoa(i));
28094fd1316SSimon Glass 		if (node < 0)
28194fd1316SSimon Glass 			break;
28294fd1316SSimon Glass 
28394fd1316SSimon Glass 		/* add properties to the node. */
28494fd1316SSimon Glass 		if (fdt_setprop_string(blob, node, "name",
28594fd1316SSimon Glass 				       get_record_name(buf, sizeof(buf), rec)))
286e003310aSSimon Glass 			return -EINVAL;
28794fd1316SSimon Glass 
28894fd1316SSimon Glass 		/* Check if this is a 'mark' or 'accum' record */
28994fd1316SSimon Glass 		if (fdt_setprop_cell(blob, node,
29094fd1316SSimon Glass 				rec->start_us ? "accum" : "mark",
29194fd1316SSimon Glass 				rec->time_us))
292e003310aSSimon Glass 			return -EINVAL;
29394fd1316SSimon Glass 	}
29494fd1316SSimon Glass 
29594fd1316SSimon Glass 	return 0;
29694fd1316SSimon Glass }
29794fd1316SSimon Glass 
bootstage_fdt_add_report(void)29894fd1316SSimon Glass int bootstage_fdt_add_report(void)
29994fd1316SSimon Glass {
30094fd1316SSimon Glass 	if (add_bootstages_devicetree(working_fdt))
30194fd1316SSimon Glass 		puts("bootstage: Failed to add to device tree\n");
30294fd1316SSimon Glass 
30394fd1316SSimon Glass 	return 0;
30494fd1316SSimon Glass }
30594fd1316SSimon Glass #endif
30694fd1316SSimon Glass 
bootstage_report(void)3073a608ca0SSimon Glass void bootstage_report(void)
3083a608ca0SSimon Glass {
309b383d6c0SSimon Glass 	struct bootstage_data *data = gd->bootstage;
310b383d6c0SSimon Glass 	struct bootstage_record *rec = data->record;
3113a608ca0SSimon Glass 	uint32_t prev;
31203ecac31SSimon Glass 	int i;
3133a608ca0SSimon Glass 
31403ecac31SSimon Glass 	printf("Timer summary in microseconds (%d records):\n",
31503ecac31SSimon Glass 	       data->rec_count);
3163a608ca0SSimon Glass 	printf("%11s%11s  %s\n", "Mark", "Elapsed", "Stage");
3173a608ca0SSimon Glass 
318b383d6c0SSimon Glass 	prev = print_time_record(rec, 0);
3193a608ca0SSimon Glass 
3203a608ca0SSimon Glass 	/* Sort records by increasing time */
32103ecac31SSimon Glass 	qsort(data->record, data->rec_count, sizeof(*rec), h_compare_record);
3223a608ca0SSimon Glass 
32303ecac31SSimon Glass 	for (i = 1, rec++; i < data->rec_count; i++, rec++) {
32403ecac31SSimon Glass 		if (rec->id && !rec->start_us)
325b383d6c0SSimon Glass 			prev = print_time_record(rec, prev);
3263a608ca0SSimon Glass 	}
32703ecac31SSimon Glass 	if (data->rec_count > RECORD_COUNT)
32803ecac31SSimon Glass 		printf("Overflowed internal boot id table by %d entries\n"
329d69bb0ecSSimon Glass 		       "Please increase CONFIG_(SPL_)BOOTSTAGE_RECORD_COUNT\n",
33003ecac31SSimon Glass 		       data->rec_count - RECORD_COUNT);
3310e996773SSimon Glass 
3320e996773SSimon Glass 	puts("\nAccumulated time:\n");
33303ecac31SSimon Glass 	for (i = 0, rec = data->record; i < data->rec_count; i++, rec++) {
3340e996773SSimon Glass 		if (rec->start_us)
335b383d6c0SSimon Glass 			prev = print_time_record(rec, -1);
3360e996773SSimon Glass 	}
3373a608ca0SSimon Glass }
338fcf509b8SSimon Glass 
339fcf509b8SSimon Glass /**
340fcf509b8SSimon Glass  * Append data to a memory buffer
341fcf509b8SSimon Glass  *
342fcf509b8SSimon Glass  * Write data to the buffer if there is space. Whether there is space or not,
343fcf509b8SSimon Glass  * the buffer pointer is incremented.
344fcf509b8SSimon Glass  *
345fcf509b8SSimon Glass  * @param ptrp	Pointer to buffer, updated by this function
346fcf509b8SSimon Glass  * @param end	Pointer to end of buffer
347fcf509b8SSimon Glass  * @param data	Data to write to buffer
348fcf509b8SSimon Glass  * @param size	Size of data
349fcf509b8SSimon Glass  */
append_data(char ** ptrp,char * end,const void * data,int size)350fcf509b8SSimon Glass static void append_data(char **ptrp, char *end, const void *data, int size)
351fcf509b8SSimon Glass {
352fcf509b8SSimon Glass 	char *ptr = *ptrp;
353fcf509b8SSimon Glass 
354fcf509b8SSimon Glass 	*ptrp += size;
355fcf509b8SSimon Glass 	if (*ptrp > end)
356fcf509b8SSimon Glass 		return;
357fcf509b8SSimon Glass 
358fcf509b8SSimon Glass 	memcpy(ptr, data, size);
359fcf509b8SSimon Glass }
360fcf509b8SSimon Glass 
bootstage_stash(void * base,int size)361fcf509b8SSimon Glass int bootstage_stash(void *base, int size)
362fcf509b8SSimon Glass {
3639d2542d0SSimon Glass 	const struct bootstage_data *data = gd->bootstage;
364fcf509b8SSimon Glass 	struct bootstage_hdr *hdr = (struct bootstage_hdr *)base;
3659d2542d0SSimon Glass 	const struct bootstage_record *rec;
366fcf509b8SSimon Glass 	char buf[20];
367fcf509b8SSimon Glass 	char *ptr = base, *end = ptr + size;
368fcf509b8SSimon Glass 	uint32_t count;
36903ecac31SSimon Glass 	int i;
370fcf509b8SSimon Glass 
371fcf509b8SSimon Glass 	if (hdr + 1 > (struct bootstage_hdr *)end) {
372fcf509b8SSimon Glass 		debug("%s: Not enough space for bootstage hdr\n", __func__);
373e003310aSSimon Glass 		return -ENOSPC;
374fcf509b8SSimon Glass 	}
375fcf509b8SSimon Glass 
376fcf509b8SSimon Glass 	/* Write an arbitrary version number */
377fcf509b8SSimon Glass 	hdr->version = BOOTSTAGE_VERSION;
378fcf509b8SSimon Glass 
379fcf509b8SSimon Glass 	/* Count the number of records, and write that value first */
38003ecac31SSimon Glass 	for (rec = data->record, i = count = 0; i < data->rec_count;
38103ecac31SSimon Glass 	     i++, rec++) {
38203ecac31SSimon Glass 		if (rec->id != 0)
383fcf509b8SSimon Glass 			count++;
384fcf509b8SSimon Glass 	}
385fcf509b8SSimon Glass 	hdr->count = count;
386fcf509b8SSimon Glass 	hdr->size = 0;
387fcf509b8SSimon Glass 	hdr->magic = BOOTSTAGE_MAGIC;
388fcf509b8SSimon Glass 	ptr += sizeof(*hdr);
389fcf509b8SSimon Glass 
390fcf509b8SSimon Glass 	/* Write the records, silently stopping when we run out of space */
39103ecac31SSimon Glass 	for (rec = data->record, i = 0; i < data->rec_count; i++, rec++) {
392fcf509b8SSimon Glass 		append_data(&ptr, end, rec, sizeof(*rec));
393fcf509b8SSimon Glass 	}
394fcf509b8SSimon Glass 
395fcf509b8SSimon Glass 	/* Write the name strings */
39603ecac31SSimon Glass 	for (rec = data->record, i = 0; i < data->rec_count; i++, rec++) {
397fcf509b8SSimon Glass 		const char *name;
398fcf509b8SSimon Glass 
399fcf509b8SSimon Glass 		name = get_record_name(buf, sizeof(buf), rec);
400fcf509b8SSimon Glass 		append_data(&ptr, end, name, strlen(name) + 1);
401fcf509b8SSimon Glass 	}
402fcf509b8SSimon Glass 
403fcf509b8SSimon Glass 	/* Check for buffer overflow */
404fcf509b8SSimon Glass 	if (ptr > end) {
405fcf509b8SSimon Glass 		debug("%s: Not enough space for bootstage stash\n", __func__);
406e003310aSSimon Glass 		return -ENOSPC;
407fcf509b8SSimon Glass 	}
408fcf509b8SSimon Glass 
409fcf509b8SSimon Glass 	/* Update total data size */
410fcf509b8SSimon Glass 	hdr->size = ptr - (char *)base;
411ff00226eSSimon Glass 	debug("Stashed %d records\n", hdr->count);
412fcf509b8SSimon Glass 
413fcf509b8SSimon Glass 	return 0;
414fcf509b8SSimon Glass }
415fcf509b8SSimon Glass 
bootstage_unstash(const void * base,int size)4169d2542d0SSimon Glass int bootstage_unstash(const void *base, int size)
417fcf509b8SSimon Glass {
4189d2542d0SSimon Glass 	const struct bootstage_hdr *hdr = (struct bootstage_hdr *)base;
419b383d6c0SSimon Glass 	struct bootstage_data *data = gd->bootstage;
4209d2542d0SSimon Glass 	const char *ptr = base, *end = ptr + size;
421fcf509b8SSimon Glass 	struct bootstage_record *rec;
422fcf509b8SSimon Glass 	uint rec_size;
42303ecac31SSimon Glass 	int i;
424fcf509b8SSimon Glass 
425fcf509b8SSimon Glass 	if (size == -1)
426fcf509b8SSimon Glass 		end = (char *)(~(uintptr_t)0);
427fcf509b8SSimon Glass 
428fcf509b8SSimon Glass 	if (hdr + 1 > (struct bootstage_hdr *)end) {
429fcf509b8SSimon Glass 		debug("%s: Not enough space for bootstage hdr\n", __func__);
430e003310aSSimon Glass 		return -EPERM;
431fcf509b8SSimon Glass 	}
432fcf509b8SSimon Glass 
433fcf509b8SSimon Glass 	if (hdr->magic != BOOTSTAGE_MAGIC) {
434fcf509b8SSimon Glass 		debug("%s: Invalid bootstage magic\n", __func__);
435e003310aSSimon Glass 		return -ENOENT;
436fcf509b8SSimon Glass 	}
437fcf509b8SSimon Glass 
438fcf509b8SSimon Glass 	if (ptr + hdr->size > end) {
439fcf509b8SSimon Glass 		debug("%s: Bootstage data runs past buffer end\n", __func__);
440e003310aSSimon Glass 		return -ENOSPC;
441fcf509b8SSimon Glass 	}
442fcf509b8SSimon Glass 
443fcf509b8SSimon Glass 	if (hdr->count * sizeof(*rec) > hdr->size) {
4445d3bd345SSimon Glass 		debug("%s: Bootstage has %d records needing %lu bytes, but "
445fcf509b8SSimon Glass 			"only %d bytes is available\n", __func__, hdr->count,
4465d3bd345SSimon Glass 		      (ulong)hdr->count * sizeof(*rec), hdr->size);
447e003310aSSimon Glass 		return -ENOSPC;
448fcf509b8SSimon Glass 	}
449fcf509b8SSimon Glass 
450fcf509b8SSimon Glass 	if (hdr->version != BOOTSTAGE_VERSION) {
451fcf509b8SSimon Glass 		debug("%s: Bootstage data version %#0x unrecognised\n",
452fcf509b8SSimon Glass 		      __func__, hdr->version);
453e003310aSSimon Glass 		return -EINVAL;
454fcf509b8SSimon Glass 	}
455fcf509b8SSimon Glass 
45603ecac31SSimon Glass 	if (data->rec_count + hdr->count > RECORD_COUNT) {
457fcf509b8SSimon Glass 		debug("%s: Bootstage has %d records, we have space for %d\n"
458d69bb0ecSSimon Glass 			"Please increase CONFIG_(SPL_)BOOTSTAGE_RECORD_COUNT\n",
45903ecac31SSimon Glass 		      __func__, hdr->count, RECORD_COUNT - data->rec_count);
460e003310aSSimon Glass 		return -ENOSPC;
461fcf509b8SSimon Glass 	}
462fcf509b8SSimon Glass 
463fcf509b8SSimon Glass 	ptr += sizeof(*hdr);
464fcf509b8SSimon Glass 
465fcf509b8SSimon Glass 	/* Read the records */
466b383d6c0SSimon Glass 	rec_size = hdr->count * sizeof(*data->record);
46703ecac31SSimon Glass 	memcpy(data->record + data->rec_count, ptr, rec_size);
468fcf509b8SSimon Glass 
469fcf509b8SSimon Glass 	/* Read the name strings */
470fcf509b8SSimon Glass 	ptr += rec_size;
47103ecac31SSimon Glass 	for (rec = data->record + data->next_id, i = 0; i < hdr->count;
47203ecac31SSimon Glass 	     i++, rec++) {
473fcf509b8SSimon Glass 		rec->name = ptr;
474fcf509b8SSimon Glass 
475fcf509b8SSimon Glass 		/* Assume no data corruption here */
476fcf509b8SSimon Glass 		ptr += strlen(ptr) + 1;
477fcf509b8SSimon Glass 	}
478fcf509b8SSimon Glass 
479fcf509b8SSimon Glass 	/* Mark the records as read */
48003ecac31SSimon Glass 	data->rec_count += hdr->count;
481ff00226eSSimon Glass 	debug("Unstashed %d records\n", hdr->count);
482fcf509b8SSimon Glass 
483fcf509b8SSimon Glass 	return 0;
484fcf509b8SSimon Glass }
485b383d6c0SSimon Glass 
bootstage_get_size(void)48625e7dc6aSSimon Glass int bootstage_get_size(void)
48725e7dc6aSSimon Glass {
48825e7dc6aSSimon Glass 	return sizeof(struct bootstage_data);
48925e7dc6aSSimon Glass }
49025e7dc6aSSimon Glass 
bootstage_init(bool first)491b383d6c0SSimon Glass int bootstage_init(bool first)
492b383d6c0SSimon Glass {
493b383d6c0SSimon Glass 	struct bootstage_data *data;
494b383d6c0SSimon Glass 	int size = sizeof(struct bootstage_data);
495b383d6c0SSimon Glass 
496b383d6c0SSimon Glass 	gd->bootstage = (struct bootstage_data *)malloc(size);
497b383d6c0SSimon Glass 	if (!gd->bootstage)
498b383d6c0SSimon Glass 		return -ENOMEM;
499b383d6c0SSimon Glass 	data = gd->bootstage;
500b383d6c0SSimon Glass 	memset(data, '\0', size);
50103ecac31SSimon Glass 	if (first) {
50203ecac31SSimon Glass 		data->next_id = BOOTSTAGE_ID_USER;
503b383d6c0SSimon Glass 		bootstage_add_record(BOOTSTAGE_ID_AWAKE, "reset", 0, 0);
50403ecac31SSimon Glass 	}
505b383d6c0SSimon Glass 
506b383d6c0SSimon Glass 	return 0;
507b383d6c0SSimon Glass }
508