aboutsummaryrefslogtreecommitdiff
path: root/include/bootstage.h
blob: f4e77b09d747cf733838a9b7df47c363f1c9fdcc (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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * This file implements recording of each stage of the boot process. It is
 * intended to implement timing of each stage, reporting this information
 * to the user and passing it to the OS for logging / further analysis.
 * Note that it requires timer_get_boot_us() to be defined by the board
 *
 * Copyright (c) 2011 The Chromium OS Authors.
 */

#ifndef _BOOTSTAGE_H
#define _BOOTSTAGE_H

#include <linux/types.h>
#ifdef USE_HOSTCC
#include <linux/kconfig.h>
#endif

/* Flags for each bootstage record */
enum bootstage_flags {
	BOOTSTAGEF_ERROR	= 1 << 0,	/* Error record */
	BOOTSTAGEF_ALLOC	= 1 << 1,	/* Allocate an id */
};

/* bootstate sub-IDs used for kernel and ramdisk ranges */
enum {
	BOOTSTAGE_SUB_FORMAT,
	BOOTSTAGE_SUB_FORMAT_OK,
	BOOTSTAGE_SUB_NO_UNIT_NAME,
	BOOTSTAGE_SUB_UNIT_NAME,
	BOOTSTAGE_SUB_SUBNODE,

	BOOTSTAGE_SUB_CHECK,
	BOOTSTAGE_SUB_HASH = 5,
	BOOTSTAGE_SUB_CHECK_ARCH = 5,
	BOOTSTAGE_SUB_CHECK_ALL,
	BOOTSTAGE_SUB_GET_DATA,
	BOOTSTAGE_SUB_CHECK_ALL_OK = 7,
	BOOTSTAGE_SUB_GET_DATA_OK,
	BOOTSTAGE_SUB_LOAD,
};

/*
 * A list of boot stages that we know about. Each of these indicates the
 * state that we are at, and the action that we are about to perform. For
 * errors, we issue an error for an item when it fails. Therefore the
 * normal sequence is:
 *
 * progress action1
 * progress action2
 * progress action3
 *
 * and an error condition where action 3 failed would be:
 *
 * progress action1
 * progress action2
 * progress action3
 * error on action3
 */
enum bootstage_id {
	BOOTSTAGE_ID_START = 0,
	BOOTSTAGE_ID_CHECK_MAGIC,	/* Checking image magic */
	BOOTSTAGE_ID_CHECK_HEADER,	/* Checking image header */
	BOOTSTAGE_ID_CHECK_CHECKSUM,	/* Checking image checksum */
	BOOTSTAGE_ID_CHECK_ARCH,	/* Checking architecture */

	BOOTSTAGE_ID_CHECK_IMAGETYPE = 5,/* Checking image type */
	BOOTSTAGE_ID_DECOMP_IMAGE,	/* Decompressing image */
	BOOTSTAGE_ID_KERNEL_LOADED,	/* Kernel has been loaded */
	BOOTSTAGE_ID_DECOMP_UNIMPL = 7,	/* Odd decompression algorithm */
	BOOTSTAGE_ID_CHECK_BOOT_OS,	/* Calling OS-specific boot function */
	BOOTSTAGE_ID_BOOT_OS_RETURNED,	/* Tried to boot OS, but it returned */
	BOOTSTAGE_ID_CHECK_RAMDISK = 9,	/* Checking ram disk */

	BOOTSTAGE_ID_RD_MAGIC,		/* Checking ram disk magic */
	BOOTSTAGE_ID_RD_HDR_CHECKSUM,	/* Checking ram disk heder checksum */
	BOOTSTAGE_ID_RD_CHECKSUM,	/* Checking ram disk checksum */
	BOOTSTAGE_ID_COPY_RAMDISK = 12,	/* Copying ram disk into place */
	BOOTSTAGE_ID_RAMDISK,		/* Checking for valid ramdisk */
	BOOTSTAGE_ID_NO_RAMDISK,	/* No ram disk found (not an error) */

	BOOTSTAGE_ID_RUN_OS	= 15,	/* Exiting U-Boot, entering OS */

	BOOTSTAGE_ID_NEED_RESET = 30,
	BOOTSTAGE_ID_POST_FAIL,		/* Post failure */
	BOOTSTAGE_ID_POST_FAIL_R,	/* Post failure reported after reloc */

	/*
	 * This set is reported only by x86, and the meaning is different. In
	 * this case we are reporting completion of a particular stage.
	 * This should probably change in the x86 code (which doesn't report
	 * errors in any case), but discussion this can perhaps wait until we
	 * have a generic board implementation.
	 */
	BOOTSTAGE_ID_BOARD_INIT_R,	/* We have relocated */
	BOOTSTAGE_ID_BOARD_GLOBAL_DATA,	/* Global data is set up */

	BOOTSTAGE_ID_BOARD_INIT_SEQ,	/* We completed the init sequence */
	BOOTSTAGE_ID_BOARD_FLASH,	/* We have configured flash banks */
	BOOTSTAGE_ID_BOARD_FLASH_37,	/* In case you didn't hear... */
	BOOTSTAGE_ID_BOARD_ENV,		/* Environment is relocated & ready */
	BOOTSTAGE_ID_BOARD_PCI,		/* PCI is up */

	BOOTSTAGE_ID_BOARD_INTERRUPTS,	/* Exceptions / interrupts ready */
	BOOTSTAGE_ID_BOARD_DONE,	/* Board init done, off to main loop */
	/* ^^^ here ends the x86 sequence */

	/* Boot stages related to loading a kernel from an IDE device */
	BOOTSTAGE_ID_IDE_START = 41,
	BOOTSTAGE_ID_IDE_ADDR,
	BOOTSTAGE_ID_IDE_BOOT_DEVICE,
	BOOTSTAGE_ID_IDE_TYPE,

	BOOTSTAGE_ID_IDE_PART,
	BOOTSTAGE_ID_IDE_PART_INFO,
	BOOTSTAGE_ID_IDE_PART_TYPE,
	BOOTSTAGE_ID_IDE_PART_READ,
	BOOTSTAGE_ID_IDE_FORMAT,

	BOOTSTAGE_ID_IDE_CHECKSUM,	/* 50 */
	BOOTSTAGE_ID_IDE_READ,

	/* Boot stages related to loading a kernel from an NAND device */
	BOOTSTAGE_ID_NAND_PART,
	BOOTSTAGE_ID_NAND_SUFFIX,
	BOOTSTAGE_ID_NAND_BOOT_DEVICE,
	BOOTSTAGE_ID_NAND_HDR_READ = 55,
	BOOTSTAGE_ID_NAND_AVAILABLE = 55,
	BOOTSTAGE_ID_NAND_TYPE = 57,
	BOOTSTAGE_ID_NAND_READ,

	/* Boot stages related to loading a kernel from an network device */
	BOOTSTAGE_ID_NET_CHECKSUM = 60,
	BOOTSTAGE_ID_NET_ETH_START = 64,
	BOOTSTAGE_ID_NET_ETH_INIT,

	BOOTSTAGE_ID_NET_START = 80,
	BOOTSTAGE_ID_NET_NETLOOP_OK,
	BOOTSTAGE_ID_NET_LOADED,
	BOOTSTAGE_ID_NET_DONE_ERR,
	BOOTSTAGE_ID_NET_DONE,

	BOOTSTAGE_ID_FIT_FDT_START = 90,
	/*
	 * Boot stages related to loading a FIT image. Some of these are a
	 * bit wonky.
	 */
	BOOTSTAGE_ID_FIT_KERNEL_START = 100,

	BOOTSTAGE_ID_FIT_CONFIG = 110,
	BOOTSTAGE_ID_FIT_TYPE,

	BOOTSTAGE_ID_FIT_COMPRESSION,
	BOOTSTAGE_ID_FIT_OS,
	BOOTSTAGE_ID_FIT_LOADADDR,
	BOOTSTAGE_ID_OVERWRITTEN,

	/* Next 10 IDs used by BOOTSTAGE_SUB_... */
	BOOTSTAGE_ID_FIT_RD_START = 120,	/* Ramdisk stages */

	/* Next 10 IDs used by BOOTSTAGE_SUB_... */
	BOOTSTAGE_ID_FIT_SETUP_START = 130,	/* x86 setup stages */

	BOOTSTAGE_ID_IDE_FIT_READ = 140,
	BOOTSTAGE_ID_IDE_FIT_READ_OK,

	BOOTSTAGE_ID_NAND_FIT_READ = 150,
	BOOTSTAGE_ID_NAND_FIT_READ_OK,

	BOOTSTAGE_ID_FIT_LOADABLE_START = 160,	/* for Loadable Images */

	BOOTSTAGE_ID_FIT_SPL_START = 170,	/* for SPL Images */
	/*
	 * These boot stages are new, higher level, and not directly related
	 * to the old boot progress numbers. They are useful for recording
	 * rough boot timing information.
	 */
	BOOTSTAGE_ID_AWAKE,
	BOOTSTAGE_ID_START_TPL,
	BOOTSTAGE_ID_END_TPL,
	BOOTSTAGE_ID_START_SPL,
	BOOTSTAGE_ID_END_SPL,
	BOOTSTAGE_ID_START_VPL,
	BOOTSTAGE_ID_END_VPL,
	BOOTSTAGE_ID_START_UBOOT_F,
	BOOTSTAGE_ID_START_UBOOT_R,
	BOOTSTAGE_ID_USB_START,
	BOOTSTAGE_ID_ETH_START,
	BOOTSTAGE_ID_BOOTP_START,
	BOOTSTAGE_ID_BOOTP_STOP,
	BOOTSTAGE_ID_BOOTM_START,
	BOOTSTAGE_ID_BOOTM_HANDOFF,
	BOOTSTAGE_ID_MAIN_LOOP,
	BOOTSTAGE_ID_ENTER_CLI_LOOP,
	BOOTSTAGE_KERNELREAD_START,
	BOOTSTAGE_KERNELREAD_STOP,
	BOOTSTAGE_ID_BOARD_INIT,
	BOOTSTAGE_ID_BOARD_INIT_DONE,

	BOOTSTAGE_ID_CPU_AWAKE,
	BOOTSTAGE_ID_MAIN_CPU_AWAKE,
	BOOTSTAGE_ID_MAIN_CPU_READY,

	BOOTSTAGE_ID_ACCUM_LCD,
	BOOTSTAGE_ID_ACCUM_SCSI,
	BOOTSTAGE_ID_ACCUM_SPI,
	BOOTSTAGE_ID_ACCUM_DECOMP,
	BOOTSTAGE_ID_ACCUM_OF_LIVE,
	BOOTSTAGE_ID_FPGA_INIT,
	BOOTSTAGE_ID_ACCUM_DM_SPL,
	BOOTSTAGE_ID_ACCUM_DM_F,
	BOOTSTAGE_ID_ACCUM_DM_R,
	BOOTSTAGE_ID_ACCUM_FSP_M,
	BOOTSTAGE_ID_ACCUM_FSP_S,
	BOOTSTAGE_ID_ACCUM_MMAP_SPI,

	/* a few spare for the user, from here */
	BOOTSTAGE_ID_USER,
	BOOTSTAGE_ID_ALLOC,
};

/*
 * Return the time since boot in microseconds, This is needed for bootstage
 * and should be defined in CPU- or board-specific code. If undefined then
 * you will get a link error.
 */
ulong timer_get_boot_us(void);

#if defined(USE_HOSTCC) || !CONFIG_IS_ENABLED(SHOW_BOOT_PROGRESS)
#define show_boot_progress(val) do {} while (0)
#else
/**
 * Board code can implement show_boot_progress() if needed.
 *
 * @param val	Progress state (enum bootstage_id), or -id if an error
 *		has occurred.
 */
void show_boot_progress(int val);
#endif

#if !defined(USE_HOSTCC)
#if CONFIG_IS_ENABLED(BOOTSTAGE)
#define ENABLE_BOOTSTAGE
#endif
#endif

#ifdef ENABLE_BOOTSTAGE

#include <mapmem.h>

/* This is the full bootstage implementation */

/**
 * Relocate existing bootstage records
 *
 * Call this after relocation has happened and after malloc has been initted.
 * We need to copy any pointers in bootstage records that were added pre-
 * relocation, since memory can be overwritten later.
 * Return: Always returns 0, to indicate success
 */
int bootstage_relocate(void);

/**
 * Add a new bootstage record
 *
 * @param id	Bootstage ID to use (ignored if flags & BOOTSTAGEF_ALLOC)
 * @param name	Name of record, or NULL for none
 * @param flags	Flags (BOOTSTAGEF_...)
 * @param mark	Time to record in this record, in microseconds
 */
ulong bootstage_add_record(enum bootstage_id id, const char *name,
			   int flags, ulong mark);

/**
 * Mark a time stamp for the current boot stage.
 */
#define bootstage_mark(id)	bootstage_mark_name(id, __func__)
#define bootstage_error(id)	bootstage_error_name(id, __func__)

/**
 * bootstage_mark_name - record bootstage with passing id and name
 * @id: Bootstage id to record this timestamp against
 * @name: Textual name to display for this id in the report
 *
 * Return: recorded time stamp
 */
ulong bootstage_mark_name(enum bootstage_id id, const char *name);

/**
 * bootstage_error_name - record bootstage error with passing id and name
 * @id: Bootstage id to record this timestamp against
 * @name: Textual name to display for this id in the report
 *
 * Return: recorded time stamp
 */
ulong bootstage_error_name(enum bootstage_id id, const char *name);

/**
 * Mark a time stamp in the given function and line number
 *
 * See BOOTSTAGE_MARKER() for a convenient macro.
 *
 * @param file		Filename to record (NULL if none)
 * @param func		Function name to record
 * @param linenum	Line number to record
 * Return: recorded time stamp
 */
ulong bootstage_mark_code(const char *file, const char *func,
			  int linenum);

/**
 * Mark the start of a bootstage activity. The end will be marked later with
 * bootstage_accum() and at that point we accumulate the time taken. Calling
 * this function turns the given id into a accumulator rather than and
 * absolute mark in time. Accumulators record the total amount of time spent
 * in an activty during boot.
 *
 * @param id	Bootstage id to record this timestamp against
 * @param name	Textual name to display for this id in the report (maybe NULL)
 * Return: start timestamp in microseconds
 */
uint32_t bootstage_start(enum bootstage_id id, const char *name);

/**
 * Mark the end of a bootstage activity
 *
 * After previously marking the start of an activity with bootstage_start(),
 * call this function to mark the end. You can call these functions in pairs
 * as many times as you like.
 *
 * @param id	Bootstage id to record this timestamp against
 * Return: time spent in this iteration of the activity (i.e. the time now
 *		less the start time recorded in the last bootstage_start() call
 *		with this id.
 */
uint32_t bootstage_accum(enum bootstage_id id);

/* Print a report about boot time */
void bootstage_report(void);

/**
 * Add bootstage information to the device tree
 *
 * Return: 0 if ok, -ve on error
 */
int bootstage_fdt_add_report(void);

/**
 * Stash bootstage data into memory
 *
 * @param base	Base address of memory buffer
 * @param size	Size of memory buffer
 * Return: 0 if stashed ok, -1 if out of space
 */
int bootstage_stash(void *base, int size);

/**
 * Read bootstage data from memory
 *
 * Bootstage data is read from memory and placed in the bootstage table
 * in the user records.
 *
 * @param base	Base address of memory buffer
 * @param size	Size of memory buffer (-1 if unknown)
 * Return: 0 if unstashed ok, -ENOENT if bootstage info not found, -ENOSPC if
 *	there is not space for read the stashed data, or other error if
 *	something else went wrong
 */
int bootstage_unstash(const void *base, int size);

/**
 * bootstage_get_size() - Get the size of the bootstage data
 *
 * Return: size of boostage data in bytes
 */
int bootstage_get_size(void);

/**
 * bootstage_init() - Prepare bootstage for use
 *
 * @first: true if this is the first time bootstage is set up. This causes it
 *	to add a 'reset' record with a time of 0.
 */
int bootstage_init(bool first);

#else
static inline ulong bootstage_add_record(enum bootstage_id id,
		const char *name, int flags, ulong mark)
{
	return 0;
}

/*
 * This is a dummy implementation which just calls show_boot_progress(),
 * and won't even do that unless CONFIG_SHOW_BOOT_PROGRESS is defined
 */

static inline int bootstage_relocate(void)
{
	return 0;
}

static inline ulong bootstage_mark(enum bootstage_id id)
{
	show_boot_progress(id);
	return 0;
}

static inline ulong bootstage_error(enum bootstage_id id)
{
	show_boot_progress(-id);
	return 0;
}

static inline ulong bootstage_mark_name(enum bootstage_id id, const char *name)
{
	show_boot_progress(id);
	return 0;
}

static inline ulong bootstage_mark_code(const char *file, const char *func,
					int linenum)
{
	return 0;
}

static inline uint32_t bootstage_start(enum bootstage_id id, const char *name)
{
	return 0;
}

static inline uint32_t bootstage_accum(enum bootstage_id id)
{
	return 0;
}

static inline int bootstage_stash(void *base, int size)
{
	return 0;	/* Pretend to succeed */
}

static inline int bootstage_unstash(const void *base, int size)
{
	return 0;	/* Pretend to succeed */
}

static inline int bootstage_get_size(void)
{
	return 0;
}

static inline int bootstage_init(bool first)
{
	return 0;
}

#endif /* ENABLE_BOOTSTAGE */

/* helpers for SPL */
int _bootstage_stash_default(void);
int _bootstage_unstash_default(void);

static inline int bootstage_stash_default(void)
{
	if (CONFIG_IS_ENABLED(BOOTSTAGE) && IS_ENABLED(CONFIG_BOOTSTAGE_STASH))
		return _bootstage_stash_default();

	return 0;
}

static inline int bootstage_unstash_default(void)
{
	if (CONFIG_IS_ENABLED(BOOTSTAGE) && IS_ENABLED(CONFIG_BOOTSTAGE_STASH))
		return _bootstage_unstash_default();

	return 0;
}

/* Helper macro for adding a bootstage to a line of code */
#define BOOTSTAGE_MARKER()	\
		bootstage_mark_code(__FILE__, __func__, __LINE__)

#endif