Blame view

common/bootstage.c 12.2 KB
83d290c56   Tom Rini   SPDX: Convert all...
1
  // SPDX-License-Identifier: GPL-2.0+
3a608ca01   Simon Glass   bootstage: Implem...
2
3
  /*
   * Copyright (c) 2011, Google Inc. All rights reserved.
3a608ca01   Simon Glass   bootstage: Implem...
4
5
6
7
8
9
   */
  
  
  /*
   * This module records the progress of boot and arbitrary commands, and
   * permits accurate timestamping of each.
3a608ca01   Simon Glass   bootstage: Implem...
10
11
12
   */
  
  #include <common.h>
db41d65a9   Simon Glass   common: Move hang...
13
  #include <hang.h>
fb7db41cd   Simon Glass   bootstage: Allow ...
14
  #include <malloc.h>
8bef79bf3   Simon Glass   common: Move sort...
15
  #include <sort.h>
65b2d96f4   Simon Glass   bootstage: Avoid ...
16
  #include <spl.h>
fb7db41cd   Simon Glass   bootstage: Allow ...
17
  #include <linux/compiler.h>
65b2d96f4   Simon Glass   bootstage: Avoid ...
18
  #include <linux/libfdt.h>
3a608ca01   Simon Glass   bootstage: Implem...
19
20
  
  DECLARE_GLOBAL_DATA_PTR;
03ecac314   Simon Glass   bootstage: Use re...
21
  enum {
d69bb0ecb   Simon Glass   bootstage: Provid...
22
  	RECORD_COUNT = CONFIG_VAL(BOOTSTAGE_RECORD_COUNT),
03ecac314   Simon Glass   bootstage: Use re...
23
  };
3a608ca01   Simon Glass   bootstage: Implem...
24
25
  struct bootstage_record {
  	ulong time_us;
094e06a52   Simon Glass   bootstage: Export...
26
  	uint32_t start_us;
3a608ca01   Simon Glass   bootstage: Implem...
27
28
29
30
  	const char *name;
  	int flags;		/* see enum bootstage_flags */
  	enum bootstage_id id;
  };
b383d6c05   Simon Glass   bootstage: Conver...
31
  struct bootstage_data {
03ecac314   Simon Glass   bootstage: Use re...
32
  	uint rec_count;
b383d6c05   Simon Glass   bootstage: Conver...
33
  	uint next_id;
03ecac314   Simon Glass   bootstage: Use re...
34
  	struct bootstage_record record[RECORD_COUNT];
b383d6c05   Simon Glass   bootstage: Conver...
35
  };
3a608ca01   Simon Glass   bootstage: Implem...
36

fcf509b80   Simon Glass   bootstage: Add fe...
37
38
39
  enum {
  	BOOTSTAGE_VERSION	= 0,
  	BOOTSTAGE_MAGIC		= 0xb00757a3,
b8bcaa3ad   Simon Glass   Add function to p...
40
  	BOOTSTAGE_DIGITS	= 9,
fcf509b80   Simon Glass   bootstage: Add fe...
41
42
43
  };
  
  struct bootstage_hdr {
53a4f253f   Simon Glass   bootstage: Store ...
44
45
46
47
48
  	u32 version;		/* BOOTSTAGE_VERSION */
  	u32 count;		/* Number of records */
  	u32 size;		/* Total data size (non-zero if valid) */
  	u32 magic;		/* Magic number */
  	u32 next_id;		/* Next ID to use for bootstage */
fcf509b80   Simon Glass   bootstage: Add fe...
49
  };
150678a58   Doug Anderson   bootstage: Copy b...
50
51
  int bootstage_relocate(void)
  {
b383d6c05   Simon Glass   bootstage: Conver...
52
  	struct bootstage_data *data = gd->bootstage;
150678a58   Doug Anderson   bootstage: Copy b...
53
  	int i;
ac9cd4805   Simon Glass   bootstage: Correc...
54
55
56
57
  	char *ptr;
  
  	/* Figure out where to relocate the strings to */
  	ptr = (char *)(data + 1);
150678a58   Doug Anderson   bootstage: Copy b...
58
59
60
61
62
  
  	/*
  	 * Duplicate all strings.  They may point to an old location in the
  	 * program .text section that can eventually get trashed.
  	 */
03ecac314   Simon Glass   bootstage: Use re...
63
64
  	debug("Relocating %d records
  ", data->rec_count);
ac9cd4805   Simon Glass   bootstage: Correc...
65
66
67
68
69
70
71
  	for (i = 0; i < data->rec_count; i++) {
  		const char *from = data->record[i].name;
  
  		strcpy(ptr, from);
  		data->record[i].name = ptr;
  		ptr += strlen(ptr) + 1;
  	}
150678a58   Doug Anderson   bootstage: Copy b...
72
73
74
  
  	return 0;
  }
03ecac314   Simon Glass   bootstage: Use re...
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
  struct bootstage_record *find_id(struct bootstage_data *data,
  				 enum bootstage_id id)
  {
  	struct bootstage_record *rec;
  	struct bootstage_record *end;
  
  	for (rec = data->record, end = rec + data->rec_count; rec < end;
  	     rec++) {
  		if (rec->id == id)
  			return rec;
  	}
  
  	return NULL;
  }
  
  struct bootstage_record *ensure_id(struct bootstage_data *data,
  				   enum bootstage_id id)
  {
  	struct bootstage_record *rec;
  
  	rec = find_id(data, id);
  	if (!rec && data->rec_count < RECORD_COUNT) {
  		rec = &data->record[data->rec_count++];
  		rec->id = id;
  		return rec;
  	}
  
  	return rec;
  }
3a608ca01   Simon Glass   bootstage: Implem...
104
  ulong bootstage_add_record(enum bootstage_id id, const char *name,
094e06a52   Simon Glass   bootstage: Export...
105
  			   int flags, ulong mark)
3a608ca01   Simon Glass   bootstage: Implem...
106
  {
b383d6c05   Simon Glass   bootstage: Conver...
107
  	struct bootstage_data *data = gd->bootstage;
3a608ca01   Simon Glass   bootstage: Implem...
108
  	struct bootstage_record *rec;
3a608ca01   Simon Glass   bootstage: Implem...
109

173577252   Simon Glass   bootstage: Allow ...
110
111
112
113
114
115
116
  	/*
  	 * initf_bootstage() is called very early during boot but since hang()
  	 * calls bootstage_error() we can be called before bootstage is set up.
  	 * Add a check to avoid this.
  	 */
  	if (!data)
  		return mark;
3a608ca01   Simon Glass   bootstage: Implem...
117
  	if (flags & BOOTSTAGEF_ALLOC)
b383d6c05   Simon Glass   bootstage: Conver...
118
  		id = data->next_id++;
3a608ca01   Simon Glass   bootstage: Implem...
119

03ecac314   Simon Glass   bootstage: Use re...
120
121
122
123
124
125
126
127
  	/* Only record the first event for each */
  	rec = find_id(data, id);
  	if (!rec && data->rec_count < RECORD_COUNT) {
  		rec = &data->record[data->rec_count++];
  		rec->time_us = mark;
  		rec->name = name;
  		rec->flags = flags;
  		rec->id = id;
3a608ca01   Simon Glass   bootstage: Implem...
128
129
130
131
  	}
  
  	/* Tell the board about this progress */
  	show_boot_progress(flags & BOOTSTAGEF_ERROR ? -id : id);
cbcd6970a   Simon Glass   bootstage: Fix up...
132

3a608ca01   Simon Glass   bootstage: Implem...
133
134
135
136
137
138
  	return mark;
  }
  
  
  ulong bootstage_mark(enum bootstage_id id)
  {
094e06a52   Simon Glass   bootstage: Export...
139
  	return bootstage_add_record(id, NULL, 0, timer_get_boot_us());
3a608ca01   Simon Glass   bootstage: Implem...
140
141
142
143
  }
  
  ulong bootstage_error(enum bootstage_id id)
  {
094e06a52   Simon Glass   bootstage: Export...
144
145
  	return bootstage_add_record(id, NULL, BOOTSTAGEF_ERROR,
  				    timer_get_boot_us());
3a608ca01   Simon Glass   bootstage: Implem...
146
147
148
149
150
151
152
153
  }
  
  ulong bootstage_mark_name(enum bootstage_id id, const char *name)
  {
  	int flags = 0;
  
  	if (id == BOOTSTAGE_ID_ALLOC)
  		flags = BOOTSTAGEF_ALLOC;
cbcd6970a   Simon Glass   bootstage: Fix up...
154

094e06a52   Simon Glass   bootstage: Export...
155
  	return bootstage_add_record(id, name, flags, timer_get_boot_us());
3a608ca01   Simon Glass   bootstage: Implem...
156
  }
fb7db41cd   Simon Glass   bootstage: Allow ...
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
  ulong bootstage_mark_code(const char *file, const char *func, int linenum)
  {
  	char *str, *p;
  	__maybe_unused char *end;
  	int len = 0;
  
  	/* First work out the length we need to allocate */
  	if (linenum != -1)
  		len = 11;
  	if (func)
  		len += strlen(func);
  	if (file)
  		len += strlen(file);
  
  	str = malloc(len + 1);
  	p = str;
  	end = p + len;
  	if (file)
  		p += snprintf(p, end - p, "%s,", file);
  	if (linenum != -1)
  		p += snprintf(p, end - p, "%d", linenum);
  	if (func)
  		p += snprintf(p, end - p, ": %s", func);
  
  	return bootstage_mark_name(BOOTSTAGE_ID_ALLOC, str);
  }
0e9967735   Simon Glass   bootstage: Add ti...
183
184
  uint32_t bootstage_start(enum bootstage_id id, const char *name)
  {
b383d6c05   Simon Glass   bootstage: Conver...
185
  	struct bootstage_data *data = gd->bootstage;
03ecac314   Simon Glass   bootstage: Use re...
186
187
  	struct bootstage_record *rec = ensure_id(data, id);
  	ulong start_us = timer_get_boot_us();
0e9967735   Simon Glass   bootstage: Add ti...
188

03ecac314   Simon Glass   bootstage: Use re...
189
190
191
192
  	if (rec) {
  		rec->start_us = start_us;
  		rec->name = name;
  	}
cbcd6970a   Simon Glass   bootstage: Fix up...
193

03ecac314   Simon Glass   bootstage: Use re...
194
  	return start_us;
0e9967735   Simon Glass   bootstage: Add ti...
195
196
197
198
  }
  
  uint32_t bootstage_accum(enum bootstage_id id)
  {
b383d6c05   Simon Glass   bootstage: Conver...
199
  	struct bootstage_data *data = gd->bootstage;
03ecac314   Simon Glass   bootstage: Use re...
200
  	struct bootstage_record *rec = ensure_id(data, id);
0e9967735   Simon Glass   bootstage: Add ti...
201
  	uint32_t duration;
03ecac314   Simon Glass   bootstage: Use re...
202
203
  	if (!rec)
  		return 0;
0e9967735   Simon Glass   bootstage: Add ti...
204
205
  	duration = (uint32_t)timer_get_boot_us() - rec->start_us;
  	rec->time_us += duration;
cbcd6970a   Simon Glass   bootstage: Fix up...
206

0e9967735   Simon Glass   bootstage: Add ti...
207
208
  	return duration;
  }
94fd1316b   Simon Glass   bootstage: Store ...
209
210
211
212
213
214
215
216
217
  /**
   * Get a record name as a printable string
   *
   * @param buf	Buffer to put name if needed
   * @param len	Length of buffer
   * @param rec	Boot stage record to get the name from
   * @return pointer to name, either from the record or pointing to buf.
   */
  static const char *get_record_name(char *buf, int len,
9d2542d06   Simon Glass   bootstage: Adjust...
218
  				   const struct bootstage_record *rec)
94fd1316b   Simon Glass   bootstage: Store ...
219
220
221
222
223
224
225
226
227
228
  {
  	if (rec->name)
  		return rec->name;
  	else if (rec->id >= BOOTSTAGE_ID_USER)
  		snprintf(buf, len, "user_%d", rec->id - BOOTSTAGE_ID_USER);
  	else
  		snprintf(buf, len, "id=%d", rec->id);
  
  	return buf;
  }
b383d6c05   Simon Glass   bootstage: Conver...
229
  static uint32_t print_time_record(struct bootstage_record *rec, uint32_t prev)
3a608ca01   Simon Glass   bootstage: Implem...
230
  {
94fd1316b   Simon Glass   bootstage: Store ...
231
  	char buf[20];
0e9967735   Simon Glass   bootstage: Add ti...
232
233
  	if (prev == -1U) {
  		printf("%11s", "");
b8bcaa3ad   Simon Glass   Add function to p...
234
  		print_grouped_ull(rec->time_us, BOOTSTAGE_DIGITS);
0e9967735   Simon Glass   bootstage: Add ti...
235
  	} else {
b8bcaa3ad   Simon Glass   Add function to p...
236
237
  		print_grouped_ull(rec->time_us, BOOTSTAGE_DIGITS);
  		print_grouped_ull(rec->time_us - prev, BOOTSTAGE_DIGITS);
0e9967735   Simon Glass   bootstage: Add ti...
238
  	}
94fd1316b   Simon Glass   bootstage: Store ...
239
240
  	printf("  %s
  ", get_record_name(buf, sizeof(buf), rec));
3a608ca01   Simon Glass   bootstage: Implem...
241
242
243
244
245
246
247
248
249
  	return rec->time_us;
  }
  
  static int h_compare_record(const void *r1, const void *r2)
  {
  	const struct bootstage_record *rec1 = r1, *rec2 = r2;
  
  	return rec1->time_us > rec2->time_us ? 1 : -1;
  }
94fd1316b   Simon Glass   bootstage: Store ...
250
251
252
253
254
255
256
257
258
  #ifdef CONFIG_OF_LIBFDT
  /**
   * Add all bootstage timings to a device tree.
   *
   * @param blob	Device tree blob
   * @return 0 on success, != 0 on failure.
   */
  static int add_bootstages_devicetree(struct fdt_header *blob)
  {
b383d6c05   Simon Glass   bootstage: Conver...
259
  	struct bootstage_data *data = gd->bootstage;
94fd1316b   Simon Glass   bootstage: Store ...
260
261
  	int bootstage;
  	char buf[20];
03ecac314   Simon Glass   bootstage: Use re...
262
  	int recnum;
94fd1316b   Simon Glass   bootstage: Store ...
263
264
265
266
267
268
269
270
271
272
273
  	int i;
  
  	if (!blob)
  		return 0;
  
  	/*
  	 * Create the node for bootstage.
  	 * The address of flat device tree is set up by the command bootm.
  	 */
  	bootstage = fdt_add_subnode(blob, 0, "bootstage");
  	if (bootstage < 0)
e003310a7   Simon Glass   bootstage: Tidy u...
274
  		return -EINVAL;
94fd1316b   Simon Glass   bootstage: Store ...
275
276
277
278
279
  
  	/*
  	 * Insert the timings to the device tree in the reverse order so
  	 * that they can be printed in the Linux kernel in the right order.
  	 */
03ecac314   Simon Glass   bootstage: Use re...
280
281
  	for (recnum = data->rec_count - 1, i = 0; recnum >= 0; recnum--, i++) {
  		struct bootstage_record *rec = &data->record[recnum];
94fd1316b   Simon Glass   bootstage: Store ...
282
  		int node;
03ecac314   Simon Glass   bootstage: Use re...
283
  		if (rec->id != BOOTSTAGE_ID_AWAKE && rec->time_us == 0)
94fd1316b   Simon Glass   bootstage: Store ...
284
285
286
287
288
289
290
291
  			continue;
  
  		node = fdt_add_subnode(blob, bootstage, simple_itoa(i));
  		if (node < 0)
  			break;
  
  		/* add properties to the node. */
  		if (fdt_setprop_string(blob, node, "name",
03ecac314   Simon Glass   bootstage: Use re...
292
  				       get_record_name(buf, sizeof(buf), rec)))
e003310a7   Simon Glass   bootstage: Tidy u...
293
  			return -EINVAL;
94fd1316b   Simon Glass   bootstage: Store ...
294
295
296
297
298
  
  		/* Check if this is a 'mark' or 'accum' record */
  		if (fdt_setprop_cell(blob, node,
  				rec->start_us ? "accum" : "mark",
  				rec->time_us))
e003310a7   Simon Glass   bootstage: Tidy u...
299
  			return -EINVAL;
94fd1316b   Simon Glass   bootstage: Store ...
300
301
302
303
304
305
306
307
308
309
310
311
312
313
  	}
  
  	return 0;
  }
  
  int bootstage_fdt_add_report(void)
  {
  	if (add_bootstages_devicetree(working_fdt))
  		puts("bootstage: Failed to add to device tree
  ");
  
  	return 0;
  }
  #endif
3a608ca01   Simon Glass   bootstage: Implem...
314
315
  void bootstage_report(void)
  {
b383d6c05   Simon Glass   bootstage: Conver...
316
317
  	struct bootstage_data *data = gd->bootstage;
  	struct bootstage_record *rec = data->record;
3a608ca01   Simon Glass   bootstage: Implem...
318
  	uint32_t prev;
03ecac314   Simon Glass   bootstage: Use re...
319
  	int i;
3a608ca01   Simon Glass   bootstage: Implem...
320

03ecac314   Simon Glass   bootstage: Use re...
321
322
323
  	printf("Timer summary in microseconds (%d records):
  ",
  	       data->rec_count);
3a608ca01   Simon Glass   bootstage: Implem...
324
325
  	printf("%11s%11s  %s
  ", "Mark", "Elapsed", "Stage");
b383d6c05   Simon Glass   bootstage: Conver...
326
  	prev = print_time_record(rec, 0);
3a608ca01   Simon Glass   bootstage: Implem...
327
328
  
  	/* Sort records by increasing time */
03ecac314   Simon Glass   bootstage: Use re...
329
  	qsort(data->record, data->rec_count, sizeof(*rec), h_compare_record);
3a608ca01   Simon Glass   bootstage: Implem...
330

03ecac314   Simon Glass   bootstage: Use re...
331
332
  	for (i = 1, rec++; i < data->rec_count; i++, rec++) {
  		if (rec->id && !rec->start_us)
b383d6c05   Simon Glass   bootstage: Conver...
333
  			prev = print_time_record(rec, prev);
3a608ca01   Simon Glass   bootstage: Implem...
334
  	}
03ecac314   Simon Glass   bootstage: Use re...
335
336
337
  	if (data->rec_count > RECORD_COUNT)
  		printf("Overflowed internal boot id table by %d entries
  "
d69bb0ecb   Simon Glass   bootstage: Provid...
338
339
  		       "Please increase CONFIG_(SPL_)BOOTSTAGE_RECORD_COUNT
  ",
03ecac314   Simon Glass   bootstage: Use re...
340
  		       data->rec_count - RECORD_COUNT);
0e9967735   Simon Glass   bootstage: Add ti...
341
342
343
344
  
  	puts("
  Accumulated time:
  ");
03ecac314   Simon Glass   bootstage: Use re...
345
  	for (i = 0, rec = data->record; i < data->rec_count; i++, rec++) {
0e9967735   Simon Glass   bootstage: Add ti...
346
  		if (rec->start_us)
b383d6c05   Simon Glass   bootstage: Conver...
347
  			prev = print_time_record(rec, -1);
0e9967735   Simon Glass   bootstage: Add ti...
348
  	}
3a608ca01   Simon Glass   bootstage: Implem...
349
  }
3786980dd   Simon Glass   Move bootstage ti...
350

fcf509b80   Simon Glass   bootstage: Add fe...
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
  /**
   * 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)
  {
9d2542d06   Simon Glass   bootstage: Adjust...
375
  	const struct bootstage_data *data = gd->bootstage;
fcf509b80   Simon Glass   bootstage: Add fe...
376
  	struct bootstage_hdr *hdr = (struct bootstage_hdr *)base;
9d2542d06   Simon Glass   bootstage: Adjust...
377
  	const struct bootstage_record *rec;
fcf509b80   Simon Glass   bootstage: Add fe...
378
379
  	char buf[20];
  	char *ptr = base, *end = ptr + size;
03ecac314   Simon Glass   bootstage: Use re...
380
  	int i;
fcf509b80   Simon Glass   bootstage: Add fe...
381
382
383
384
  
  	if (hdr + 1 > (struct bootstage_hdr *)end) {
  		debug("%s: Not enough space for bootstage hdr
  ", __func__);
e003310a7   Simon Glass   bootstage: Tidy u...
385
  		return -ENOSPC;
fcf509b80   Simon Glass   bootstage: Add fe...
386
387
388
389
  	}
  
  	/* Write an arbitrary version number */
  	hdr->version = BOOTSTAGE_VERSION;
ed54bdaf8   Simon Glass   bootstage: Fix co...
390
  	hdr->count = data->rec_count;
fcf509b80   Simon Glass   bootstage: Add fe...
391
392
  	hdr->size = 0;
  	hdr->magic = BOOTSTAGE_MAGIC;
53a4f253f   Simon Glass   bootstage: Store ...
393
  	hdr->next_id = data->next_id;
fcf509b80   Simon Glass   bootstage: Add fe...
394
395
396
  	ptr += sizeof(*hdr);
  
  	/* Write the records, silently stopping when we run out of space */
ed54bdaf8   Simon Glass   bootstage: Fix co...
397
  	for (rec = data->record, i = 0; i < data->rec_count; i++, rec++)
c91001f60   Simon Glass   bootstage: Show r...
398
  		append_data(&ptr, end, rec, sizeof(*rec));
fcf509b80   Simon Glass   bootstage: Add fe...
399
400
  
  	/* Write the name strings */
03ecac314   Simon Glass   bootstage: Use re...
401
  	for (rec = data->record, i = 0; i < data->rec_count; i++, rec++) {
c91001f60   Simon Glass   bootstage: Show r...
402
  		const char *name;
fcf509b80   Simon Glass   bootstage: Add fe...
403

c91001f60   Simon Glass   bootstage: Show r...
404
405
  		name = get_record_name(buf, sizeof(buf), rec);
  		append_data(&ptr, end, name, strlen(name) + 1);
fcf509b80   Simon Glass   bootstage: Add fe...
406
407
408
409
410
411
  	}
  
  	/* Check for buffer overflow */
  	if (ptr > end) {
  		debug("%s: Not enough space for bootstage stash
  ", __func__);
e003310a7   Simon Glass   bootstage: Tidy u...
412
  		return -ENOSPC;
fcf509b80   Simon Glass   bootstage: Add fe...
413
414
415
416
  	}
  
  	/* Update total data size */
  	hdr->size = ptr - (char *)base;
ff00226e0   Simon Glass   bootstage: Use de...
417
418
  	debug("Stashed %d records
  ", hdr->count);
fcf509b80   Simon Glass   bootstage: Add fe...
419
420
421
  
  	return 0;
  }
9d2542d06   Simon Glass   bootstage: Adjust...
422
  int bootstage_unstash(const void *base, int size)
fcf509b80   Simon Glass   bootstage: Add fe...
423
  {
9d2542d06   Simon Glass   bootstage: Adjust...
424
  	const struct bootstage_hdr *hdr = (struct bootstage_hdr *)base;
b383d6c05   Simon Glass   bootstage: Conver...
425
  	struct bootstage_data *data = gd->bootstage;
9d2542d06   Simon Glass   bootstage: Adjust...
426
  	const char *ptr = base, *end = ptr + size;
fcf509b80   Simon Glass   bootstage: Add fe...
427
  	struct bootstage_record *rec;
fcf509b80   Simon Glass   bootstage: Add fe...
428
  	uint rec_size;
03ecac314   Simon Glass   bootstage: Use re...
429
  	int i;
fcf509b80   Simon Glass   bootstage: Add fe...
430
431
432
433
434
435
436
  
  	if (size == -1)
  		end = (char *)(~(uintptr_t)0);
  
  	if (hdr + 1 > (struct bootstage_hdr *)end) {
  		debug("%s: Not enough space for bootstage hdr
  ", __func__);
e003310a7   Simon Glass   bootstage: Tidy u...
437
  		return -EPERM;
fcf509b80   Simon Glass   bootstage: Add fe...
438
439
440
441
442
  	}
  
  	if (hdr->magic != BOOTSTAGE_MAGIC) {
  		debug("%s: Invalid bootstage magic
  ", __func__);
e003310a7   Simon Glass   bootstage: Tidy u...
443
  		return -ENOENT;
fcf509b80   Simon Glass   bootstage: Add fe...
444
445
446
447
448
  	}
  
  	if (ptr + hdr->size > end) {
  		debug("%s: Bootstage data runs past buffer end
  ", __func__);
e003310a7   Simon Glass   bootstage: Tidy u...
449
  		return -ENOSPC;
fcf509b80   Simon Glass   bootstage: Add fe...
450
451
452
  	}
  
  	if (hdr->count * sizeof(*rec) > hdr->size) {
5d3bd3454   Simon Glass   bootstage: Correc...
453
  		debug("%s: Bootstage has %d records needing %lu bytes, but "
fcf509b80   Simon Glass   bootstage: Add fe...
454
455
  			"only %d bytes is available
  ", __func__, hdr->count,
5d3bd3454   Simon Glass   bootstage: Correc...
456
  		      (ulong)hdr->count * sizeof(*rec), hdr->size);
e003310a7   Simon Glass   bootstage: Tidy u...
457
  		return -ENOSPC;
fcf509b80   Simon Glass   bootstage: Add fe...
458
459
460
461
462
463
  	}
  
  	if (hdr->version != BOOTSTAGE_VERSION) {
  		debug("%s: Bootstage data version %#0x unrecognised
  ",
  		      __func__, hdr->version);
e003310a7   Simon Glass   bootstage: Tidy u...
464
  		return -EINVAL;
fcf509b80   Simon Glass   bootstage: Add fe...
465
  	}
03ecac314   Simon Glass   bootstage: Use re...
466
  	if (data->rec_count + hdr->count > RECORD_COUNT) {
fcf509b80   Simon Glass   bootstage: Add fe...
467
468
  		debug("%s: Bootstage has %d records, we have space for %d
  "
d69bb0ecb   Simon Glass   bootstage: Provid...
469
470
  			"Please increase CONFIG_(SPL_)BOOTSTAGE_RECORD_COUNT
  ",
03ecac314   Simon Glass   bootstage: Use re...
471
  		      __func__, hdr->count, RECORD_COUNT - data->rec_count);
e003310a7   Simon Glass   bootstage: Tidy u...
472
  		return -ENOSPC;
fcf509b80   Simon Glass   bootstage: Add fe...
473
474
475
476
477
  	}
  
  	ptr += sizeof(*hdr);
  
  	/* Read the records */
b383d6c05   Simon Glass   bootstage: Conver...
478
  	rec_size = hdr->count * sizeof(*data->record);
03ecac314   Simon Glass   bootstage: Use re...
479
  	memcpy(data->record + data->rec_count, ptr, rec_size);
fcf509b80   Simon Glass   bootstage: Add fe...
480
481
482
  
  	/* Read the name strings */
  	ptr += rec_size;
03ecac314   Simon Glass   bootstage: Use re...
483
484
  	for (rec = data->record + data->next_id, i = 0; i < hdr->count;
  	     i++, rec++) {
fcf509b80   Simon Glass   bootstage: Add fe...
485
  		rec->name = ptr;
65b2d96f4   Simon Glass   bootstage: Avoid ...
486
487
  		if (spl_phase() == PHASE_SPL)
  			rec->name = strdup(ptr);
fcf509b80   Simon Glass   bootstage: Add fe...
488
489
490
491
492
493
  
  		/* Assume no data corruption here */
  		ptr += strlen(ptr) + 1;
  	}
  
  	/* Mark the records as read */
03ecac314   Simon Glass   bootstage: Use re...
494
  	data->rec_count += hdr->count;
53a4f253f   Simon Glass   bootstage: Store ...
495
  	data->next_id = hdr->next_id;
ff00226e0   Simon Glass   bootstage: Use de...
496
497
  	debug("Unstashed %d records
  ", hdr->count);
fcf509b80   Simon Glass   bootstage: Add fe...
498
499
500
  
  	return 0;
  }
b383d6c05   Simon Glass   bootstage: Conver...
501

25e7dc6a6   Simon Glass   bootstage: Suppor...
502
503
  int bootstage_get_size(void)
  {
ac9cd4805   Simon Glass   bootstage: Correc...
504
505
506
507
508
509
510
511
512
513
514
  	struct bootstage_data *data = gd->bootstage;
  	struct bootstage_record *rec;
  	int size;
  	int i;
  
  	size = sizeof(struct bootstage_data);
  	for (rec = data->record, i = 0; i < data->rec_count;
  	     i++, rec++)
  		size += strlen(rec->name) + 1;
  
  	return size;
25e7dc6a6   Simon Glass   bootstage: Suppor...
515
  }
b383d6c05   Simon Glass   bootstage: Conver...
516
517
518
519
520
521
522
523
524
525
  int bootstage_init(bool first)
  {
  	struct bootstage_data *data;
  	int size = sizeof(struct bootstage_data);
  
  	gd->bootstage = (struct bootstage_data *)malloc(size);
  	if (!gd->bootstage)
  		return -ENOMEM;
  	data = gd->bootstage;
  	memset(data, '\0', size);
03ecac314   Simon Glass   bootstage: Use re...
526
527
  	if (first) {
  		data->next_id = BOOTSTAGE_ID_USER;
b383d6c05   Simon Glass   bootstage: Conver...
528
  		bootstage_add_record(BOOTSTAGE_ID_AWAKE, "reset", 0, 0);
03ecac314   Simon Glass   bootstage: Use re...
529
  	}
b383d6c05   Simon Glass   bootstage: Conver...
530
531
532
  
  	return 0;
  }