bootstage: Add a number of new features
cover bootstage: Add a number of new features
It is useful to be able to communicate the bootstage information to another program, such as the kernel, either by putting it in memory or the device tree. This makes the information accessible after boot so that it can easily be logged somewhere or perhaps reported back for diagnostics purposes. This series includes a few patches to implement this.
Sometimes a peripheral is touched multiple times during boot, or a single task is called repeatedly. Examples are reading from a device, which might happen in several parts, or setting up an LCD display, which may involve multiple steps to comply with backlight power sequences. This series adds a new bootstage time accumulation feature to add up all the time spent in a particular task.
This series also adds a new command 'bootstage' to provide access to the above features.
Simon Glass (6):
bootstage: Export bootstage_add_record() function
bootstage: Add time accumulation feature
bootstage: Store boot timings in device tree
bootstage: Add feature to stash/unstash bootstage info
bootstage: Add bootstage command
bootstage: Add new bootstage IDs for board, LCD
README | 25 ++++ arch/arm/lib/bootm.c | 3 + common/Makefile | 1 + common/bootstage.c | 306 +++++++++++++++++++++++++++++++++++++++++++++--- common/cmd_bootstage.c | 116 ++++++++++++++++++ include/bootstage.h | 85 +++++++++++++ 6 files changed, 517 insertions(+), 19 deletions(-) create mode 100644 common/cmd_bootstage.c
Signature · 1 line
1.7.7.3
PATCH 1/6 bootstage: Export bootstage_add_record() function
This function is not static, but not exported either. Add a prototype in the header file and move the required enum to the header also.
--- common/bootstage.c | 16 ++++++---------- include/bootstage.h | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 10 deletions(-)
common/bootstage.c +6 -10
@@ -33,13 +33,9 @@DECLARE_GLOBAL_DATA_PTR ; -enum bootstage_flags { -BOOTSTAGEF_ERROR =1 <<0 ,/* Error record */ -BOOTSTAGEF_ALLOC =1 <<1 ,/* Allocate an id */ -}; -struct bootstage_record {ulong time_us; +uint32_t start_us;const char *name;int flags;/* see enum bootstage_flags */ enum bootstage_id id;
@@ -49,10 +45,9 @@ static struct bootstage_record record[BOOTSTAGE_ID_COUNT] = { {1} };static int next_id =BOOTSTAGE_ID_USER ;ulong bootstage_add_record (enum bootstage_id id,const char *name, -int flags) +int flags,ulong mark) {struct bootstage_record *rec; -ulong mark =timer_get_boot_us ();if (flags &BOOTSTAGEF_ALLOC ) id = next_id++;
@@ -77,12 +72,13 @@ ulong bootstage_add_record(enum bootstage_id id, const char *name,ulong bootstage_mark (enum bootstage_id id) { -return bootstage_add_record (id,NULL ,0 ); +return bootstage_add_record (id,NULL ,0 ,timer_get_boot_us ()); }ulong bootstage_error (enum bootstage_id id) { -return bootstage_add_record (id,NULL ,BOOTSTAGEF_ERROR ); +return bootstage_add_record (id,NULL ,BOOTSTAGEF_ERROR , +timer_get_boot_us ()); }ulong bootstage_mark_name (enum bootstage_id id,const char *name)
@@ -91,7 +87,7 @@ ulong bootstage_mark_name(enum bootstage_id id, const char *name)if (id ==BOOTSTAGE_ID_ALLOC ) flags =BOOTSTAGEF_ALLOC ; -return bootstage_add_record (id, name, flags); +return bootstage_add_record (id, name, flags,timer_get_boot_us ()); }static void print_time (unsigned long us_time)
include/bootstage.h +17 -0
@@ -31,6 +31,12 @@#define CONFIG_BOOTSTAGE_USER_COUNT 20#endif +/* Flags for each bootstage record */ +enum bootstage_flags { +BOOTSTAGEF_ERROR =1 <<0 ,/* Error record */ +BOOTSTAGEF_ALLOC =1 <<1 ,/* Allocate an id */ +}; +/* * 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
@@ -221,6 +227,17 @@ void show_boot_progress(int val);#ifdef CONFIG_BOOTSTAGE /* This is the full bootstage implementation */ +/** +* 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. */
Signature · 1 line
1.7.7.3
PATCH 2/6 bootstage: Add time accumulation feature
In reply to Simon GlassSometimes we want to add up the amount of time spent in a particular activity when it is happening in a number of discrete chunks.
Add bootstage_start() to mark the start of an acitivity and bootstage_accum() to accumulate the time since the last start. Calling these function in pairs results in the accumulated time being collected.
--- common/bootstage.c | 36 +++++++++++++++++++++++++++++++++--- include/bootstage.h | 27 +++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-)
common/bootstage.c +33 -3
@@ -90,6 +90,25 @@ ulong bootstage_mark_name(enum bootstage_id id, const char *name)return bootstage_add_record (id, name, flags,timer_get_boot_us ()); } +uint32_t bootstage_start (enum bootstage_id id,const char *name) +{ +struct bootstage_record *rec = &record[id]; + + rec->start_us =timer_get_boot_us (); + rec->name = name; +return rec->start_us; +} + +uint32_t bootstage_accum (enum bootstage_id id) +{ +struct bootstage_record *rec = &record[id]; +uint32_t duration; + + duration = (uint32_t )timer_get_boot_us () - rec->start_us; + rec->time_us += duration; +return duration; +} +static void print_time (unsigned long us_time) {char str[15 ], *s;
@@ -108,8 +127,13 @@ static void print_time(unsigned long us_time)static uint32_t print_time_record (enum bootstage_id id,struct bootstage_record *rec,uint32_t prev) { -print_time (rec->time_us); -print_time (rec->time_us - prev); +if (prev ==-1U ) { +printf ("%11s" ,"" ); +print_time (rec->time_us); + }else { +print_time (rec->time_us); +print_time (rec->time_us - prev); + }if (rec->name)printf (" %s\n" , rec->name);else if (id >=BOOTSTAGE_ID_USER )
@@ -144,13 +168,19 @@ void bootstage_report(void)qsort (record,ARRAY_SIZE (record),sizeof (*rec), h_compare_record);for (id =0 ; id <BOOTSTAGE_ID_COUNT ; id++, rec++) { -if (rec->time_us !=0 ) +if (rec->time_us !=0 && !rec->start_us) prev =print_time_record (rec->id, rec, prev); }if (next_id >BOOTSTAGE_ID_COUNT )printf ("(Overflowed internal boot id table by %d entries\n" "- please increase CONFIG_BOOTSTAGE_USER_COUNT\n" , next_id -BOOTSTAGE_ID_COUNT ); + +puts ("\nAccumulated time:\n" ); +for (id =0 , rec = record; id <BOOTSTAGE_ID_COUNT ; id++, rec++) { +if (rec->start_us) + prev =print_time_record (id, rec,-1 ); + } }ulong __timer_get_boot_us (void )
include/bootstage.h +27 -0
@@ -247,6 +247,33 @@ ulong bootstage_error(enum bootstage_id id);ulong bootstage_mark_name (enum bootstage_id id,const char *name); +/** +* 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 );
Signature · 1 line
1.7.7.3
PATCH 3/6 bootstage: Store boot timings in device tree
In reply to Simon GlassAdd an option, CONFIG_BOOTSTAGE_FDT to pass boot timings to the kernel in the device tree, if available. To use this, you must have CONFIG_OF_LIBFDT defined.
--- README | 21 +++++++++++ arch/arm/lib/bootm.c | 3 ++ common/bootstage.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++--- include/bootstage.h | 7 ++++ 4 files changed, 120 insertions(+), 6 deletions(-)
README +21 -0
@@ -2378,6 +2378,27 @@ The following options need to be configured: 29,916,167 26,005,792 bootm_start 30,361,327 445,160 start_kernel + CONFIG_BOOTSTAGE_FDT + Stash the bootstage information in the FDT. A root 'bootstage' + node is created with each bootstage id as a child. Each child + has a 'name' property and either 'mark' containing the + mark time in microsecond, or 'accum' containing the + accumulated time for that bootstage id in microseconds. + For example: + + bootstage { + 154 { + name = "board_init_f"; + mark = <3575678>; + }; + 170 { + name = "lcd"; + accum = <33482>; + }; + }; + + Code in the Linux kernel can find this in /proc/devicetree. + Legacy uImage format: Arg Where When
arch/arm/lib/bootm.c +3 -0
@@ -96,6 +96,9 @@ static void announce_and_cleanup(void) {printf ("\nStarting kernel ...\n\n" );bootstage_mark_name (BOOTSTAGE_ID_BOOTM_HANDOFF ,"start_kernel" ); +#ifdef CONFIG_BOOTSTAGE_FDT +bootstage_fdt_add_report (); +#endif #ifdef CONFIG_BOOTSTAGE_REPORT bootstage_report ();#endif
common/bootstage.c +89 -6
116 lines not on this page · raw message
include/bootstage.h +7 -0
@@ -277,6 +277,13 @@ 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 ); +#else /* * This is a dummy implementation which just calls show_boot_progress(),
Signature · 1 line
1.7.7.3
PATCH 4/6 bootstage: Add feature to stash/unstash bootstage info
In reply to Simon GlassIt is useful to be able to write the bootstage information to memory for use by a later utility, or the Linux kernel. Provide a function to do this as well as a function to read bootstage information back and incorporate it into the current table.
This also makes it possible for U-Boot to chain to another U-Boot and pass on its bootstage information.
BRANCH=snow,link
--- common/bootstage.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/bootstage.h | 29 +++++++++ 2 files changed, 188 insertions(+), 0 deletions(-)
common/bootstage.c +159 -0
@@ -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 ; +}
include/bootstage.h +29 -0
@@ -284,6 +284,27 @@ void bootstage_report(void);*/ 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, -1 if bootstage info not found, or out of space +*/ +int bootstage_unstash (void *base,int size); +#else /* * This is a dummy implementation which just calls show_boot_progress(),
@@ -307,7 +328,15 @@ static inline ulong bootstage_mark_name(enum bootstage_id id, const char *name)return 0 ; } +static inline int bootstage_stash (void *base,int size) +{ +return 0 ;/* Pretend to succeed */ +} +static inline int bootstage_unstash (void *base,int size) +{ +return 0 ;/* Pretend to succeed */ +}#endif /* CONFIG_BOOTSTAGE */ #endif
Signature · 1 line
1.7.7.3
PATCH 5/6 bootstage: Add bootstage command
In reply to Simon GlassNow that there are a few features, add a bootstage command to access them.
bootstage report - prints a report
bootstage stash/unstash - stashes bootstage records in memory, reads them back
BRANCH=snow,link
--- README | 4 ++ common/Makefile | 1 + common/cmd_bootstage.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 0 deletions(-) create mode 100644 common/cmd_bootstage.c
README +4 -0
@@ -2378,6 +2378,10 @@ The following options need to be configured: 29,916,167 26,005,792 bootm_start 30,361,327 445,160 start_kernel + CONFIG_CMD_BOOTSTAGE + Add a 'bootstage' command which supports printing a report + and un/stashing of bootstage data. + CONFIG_BOOTSTAGE_FDT Stash the bootstage information in the FDT. A root 'bootstage' node is created with each bootstage id as a child. Each child
common/Makefile +1 -0
@@ -69,6 +69,7 @@ COBJS-$(CONFIG_CMD_BDI) += cmd_bdinfo.o COBJS-$(CONFIG_CMD_BEDBUG) += bedbug.o cmd_bedbug.o COBJS-$(CONFIG_CMD_BMP) += cmd_bmp.o COBJS-$(CONFIG_CMD_BOOTLDR) += cmd_bootldr.o +COBJS-$(CONFIG_CMD_BOOTSTAGE) += cmd_bootstage.o COBJS-$(CONFIG_CMD_CACHE) += cmd_cache.o COBJS-$(CONFIG_CMD_CONSOLE) += cmd_console.o COBJS-$(CONFIG_CMD_CPLBINFO) += cmd_cplbinfo.o
common/cmd_bootstage.c new file +116 -0
116 lines not on this page · raw message
Signature · 1 line
1.7.7.3
PATCH 6/6 bootstage: Add new bootstage IDs for board, LCD
In reply to Simon GlassAdd bootstage IDs for board init and LCD.
--- include/bootstage.h | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-)
include/bootstage.h +5 -0
@@ -187,6 +187,7 @@ enum bootstage_id {* rough boot timing information. */ BOOTSTAGE_ID_AWAKE , +BOOTSTAGE_ID_START_SPL ,BOOTSTAGE_ID_START_UBOOT_F ,BOOTSTAGE_ID_START_UBOOT_R , BOOTSTAGE_ID_USB_START,
@@ -198,11 +199,15 @@ enum bootstage_id {BOOTSTAGE_ID_MAIN_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 , +/* a few spare for the user, from here */ BOOTSTAGE_ID_USER , BOOTSTAGE_ID_COUNT = BOOTSTAGE_ID_USER + CONFIG_BOOTSTAGE_USER_COUNT,
Signature · 1 line
1.7.7.3
cover bootstage: Add a number of new features
In reply to Simon GlassHi Simon,
Quoted 25 lines · On Fri, 28 Sep 2012 11:56:34 -0700 Simon Glass <[email protected]> wrote:
It is useful to be able to communicate the bootstage information to another program, such as the kernel, either by putting it in memory or the device tree. This makes the information accessible after boot so that it can easily be logged somewhere or perhaps reported back for diagnostics purposes. This series includes a few patches to implement this.
Sometimes a peripheral is touched multiple times during boot, or a single task is called repeatedly. Examples are reading from a device, which might happen in several parts, or setting up an LCD display, which may involve multiple steps to comply with backlight power sequences. This series adds a new bootstage time accumulation feature to add up all the time spent in a particular task.
This series also adds a new command 'bootstage' to provide access to the above features.
Simon Glass (6):
bootstage: Exportbootstage_add_record()function
bootstage: Add time accumulation feature
bootstage: Store boot timings in device tree
bootstage: Add feature to stash/unstash bootstage info
bootstage: Add bootstage command
bootstage: Add new bootstage IDs for board, LCD
Quoted 8 lines
README | 25 ++++ arch/arm/lib/bootm.c | 3 + common/Makefile | 1 + common/bootstage.c | 306 +++++++++++++++++++++++++++++++++++++++++++++--- common/cmd_bootstage.c | 116 ++++++++++++++++++ include/bootstage.h | 85 +++++++++++++ 6 files changed, 517 insertions(+), 19 deletions(-) create mode 100644 common/cmd_bootstage.c
Applied this series to u-boot-staging/agust at denx.de-next, thanks!
I've dropped "BRANCH=snow,link" in commit log of the patches 4 and 5, hope this is okay?
Thanks,
Anatolij
cover bootstage: Add a number of new features
Hi Anatolij,
Quoted 39 lines · Anatolij Gustschin
Hi Simon,
Quoted 25 lines · On Fri, 28 Sep 2012 11:56:34 -0700 Simon Glass <[email protected]> wrote:
It is useful to be able to communicate the bootstage information to another program, such as the kernel, either by putting it in memory or the device tree. This makes the information accessible after boot so that it can easily be logged somewhere or perhaps reported back for diagnostics purposes. This series includes a few patches to implement this.
Sometimes a peripheral is touched multiple times during boot, or a single task is called repeatedly. Examples are reading from a device, which might happen in several parts, or setting up an LCD display, which may involve multiple steps to comply with backlight power sequences. This series adds a new bootstage time accumulation feature to add up all the time spent in a particular task.
This series also adds a new command 'bootstage' to provide access to the above features.
Simon Glass (6):
bootstage: Exportbootstage_add_record()function
bootstage: Add time accumulation feature
bootstage: Store boot timings in device tree
bootstage: Add feature to stash/unstash bootstage info
bootstage: Add bootstage command
bootstage: Add new bootstage IDs for board, LCDQuoted 8 lines
README | 25 ++++ arch/arm/lib/bootm.c | 3 + common/Makefile | 1 + common/bootstage.c | 306 +++++++++++++++++++++++++++++++++++++++++++++--- common/cmd_bootstage.c | 116 ++++++++++++++++++ include/bootstage.h | 85 +++++++++++++ 6 files changed, 517 insertions(+), 19 deletions(-) create mode 100644 common/cmd_bootstage.c
Quoted 4 lines
Applied this series to u-boot-staging/agust at denx.de-next, thanks!
I've dropped "BRANCH=snow,link" in commit log of the patches 4 and 5, hope this is okay?
Thanks - yes, sorry about that, will adjust my process a bit.
Quoted 3 lines
Thanks,
Anatolij
Regards
Simon