13a608ca0SSimon Glass /* 23a608ca0SSimon Glass * Copyright (c) 2011, Google Inc. All rights reserved. 33a608ca0SSimon Glass * 43a608ca0SSimon Glass * See file CREDITS for list of people who contributed to this 53a608ca0SSimon Glass * project. 63a608ca0SSimon Glass * 73a608ca0SSimon Glass * This program is free software; you can redistribute it and/or 83a608ca0SSimon Glass * modify it under the terms of the GNU General Public License as 93a608ca0SSimon Glass * published by the Free Software Foundation; either version 2 of 103a608ca0SSimon Glass * the License, or (at your option) any later version. 113a608ca0SSimon Glass * 123a608ca0SSimon Glass * This program is distributed in the hope that it will be useful, 133a608ca0SSimon Glass * but WITHOUT ANY WARRANTY; without even the implied warranty of 143a608ca0SSimon Glass * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 153a608ca0SSimon Glass * GNU General Public License for more details. 163a608ca0SSimon Glass * 173a608ca0SSimon Glass * You should have received a copy of the GNU General Public License 183a608ca0SSimon Glass * along with this program; if not, write to the Free Software 193a608ca0SSimon Glass * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 203a608ca0SSimon Glass * MA 02111-1307 USA 213a608ca0SSimon Glass */ 223a608ca0SSimon Glass 233a608ca0SSimon Glass 243a608ca0SSimon Glass /* 253a608ca0SSimon Glass * This module records the progress of boot and arbitrary commands, and 263a608ca0SSimon Glass * permits accurate timestamping of each. 273a608ca0SSimon Glass * 283a608ca0SSimon Glass * TBD: Pass timings to kernel in the FDT 293a608ca0SSimon Glass */ 303a608ca0SSimon Glass 313a608ca0SSimon Glass #include <common.h> 323a608ca0SSimon Glass #include <libfdt.h> 333a608ca0SSimon Glass 343a608ca0SSimon Glass DECLARE_GLOBAL_DATA_PTR; 353a608ca0SSimon Glass 363a608ca0SSimon Glass struct bootstage_record { 373a608ca0SSimon Glass ulong time_us; 38094e06a5SSimon Glass uint32_t start_us; 393a608ca0SSimon Glass const char *name; 403a608ca0SSimon Glass int flags; /* see enum bootstage_flags */ 413a608ca0SSimon Glass enum bootstage_id id; 423a608ca0SSimon Glass }; 433a608ca0SSimon Glass 443a608ca0SSimon Glass static struct bootstage_record record[BOOTSTAGE_ID_COUNT] = { {1} }; 453a608ca0SSimon Glass static int next_id = BOOTSTAGE_ID_USER; 463a608ca0SSimon Glass 47fcf509b8SSimon Glass enum { 48fcf509b8SSimon Glass BOOTSTAGE_VERSION = 0, 49fcf509b8SSimon Glass BOOTSTAGE_MAGIC = 0xb00757a3, 50fcf509b8SSimon Glass }; 51fcf509b8SSimon Glass 52fcf509b8SSimon Glass struct bootstage_hdr { 53fcf509b8SSimon Glass uint32_t version; /* BOOTSTAGE_VERSION */ 54fcf509b8SSimon Glass uint32_t count; /* Number of records */ 55fcf509b8SSimon Glass uint32_t size; /* Total data size (non-zero if valid) */ 56fcf509b8SSimon Glass uint32_t magic; /* Unused */ 57fcf509b8SSimon Glass }; 58fcf509b8SSimon Glass 59*150678a5SDoug Anderson int bootstage_relocate(void) 60*150678a5SDoug Anderson { 61*150678a5SDoug Anderson int i; 62*150678a5SDoug Anderson 63*150678a5SDoug Anderson /* 64*150678a5SDoug Anderson * Duplicate all strings. They may point to an old location in the 65*150678a5SDoug Anderson * program .text section that can eventually get trashed. 66*150678a5SDoug Anderson */ 67*150678a5SDoug Anderson for (i = 0; i < BOOTSTAGE_ID_COUNT; i++) 68*150678a5SDoug Anderson if (record[i].name) 69*150678a5SDoug Anderson record[i].name = strdup(record[i].name); 70*150678a5SDoug Anderson 71*150678a5SDoug Anderson return 0; 72*150678a5SDoug Anderson } 73*150678a5SDoug Anderson 743a608ca0SSimon Glass ulong bootstage_add_record(enum bootstage_id id, const char *name, 75094e06a5SSimon Glass int flags, ulong mark) 763a608ca0SSimon Glass { 773a608ca0SSimon Glass struct bootstage_record *rec; 783a608ca0SSimon Glass 793a608ca0SSimon Glass if (flags & BOOTSTAGEF_ALLOC) 803a608ca0SSimon Glass id = next_id++; 813a608ca0SSimon Glass 823a608ca0SSimon Glass if (id < BOOTSTAGE_ID_COUNT) { 833a608ca0SSimon Glass rec = &record[id]; 843a608ca0SSimon Glass 853a608ca0SSimon Glass /* Only record the first event for each */ 863a608ca0SSimon Glass if (!rec->time_us) { 873a608ca0SSimon Glass rec->time_us = mark; 883a608ca0SSimon Glass rec->name = name; 893a608ca0SSimon Glass rec->flags = flags; 903a608ca0SSimon Glass rec->id = id; 913a608ca0SSimon Glass } 923a608ca0SSimon Glass } 933a608ca0SSimon Glass 943a608ca0SSimon Glass /* Tell the board about this progress */ 953a608ca0SSimon Glass show_boot_progress(flags & BOOTSTAGEF_ERROR ? -id : id); 963a608ca0SSimon Glass return mark; 973a608ca0SSimon Glass } 983a608ca0SSimon Glass 993a608ca0SSimon Glass 1003a608ca0SSimon Glass ulong bootstage_mark(enum bootstage_id id) 1013a608ca0SSimon Glass { 102094e06a5SSimon Glass return bootstage_add_record(id, NULL, 0, timer_get_boot_us()); 1033a608ca0SSimon Glass } 1043a608ca0SSimon Glass 1053a608ca0SSimon Glass ulong bootstage_error(enum bootstage_id id) 1063a608ca0SSimon Glass { 107094e06a5SSimon Glass return bootstage_add_record(id, NULL, BOOTSTAGEF_ERROR, 108094e06a5SSimon Glass timer_get_boot_us()); 1093a608ca0SSimon Glass } 1103a608ca0SSimon Glass 1113a608ca0SSimon Glass ulong bootstage_mark_name(enum bootstage_id id, const char *name) 1123a608ca0SSimon Glass { 1133a608ca0SSimon Glass int flags = 0; 1143a608ca0SSimon Glass 1153a608ca0SSimon Glass if (id == BOOTSTAGE_ID_ALLOC) 1163a608ca0SSimon Glass flags = BOOTSTAGEF_ALLOC; 117094e06a5SSimon Glass return bootstage_add_record(id, name, flags, timer_get_boot_us()); 1183a608ca0SSimon Glass } 1193a608ca0SSimon Glass 1200e996773SSimon Glass uint32_t bootstage_start(enum bootstage_id id, const char *name) 1210e996773SSimon Glass { 1220e996773SSimon Glass struct bootstage_record *rec = &record[id]; 1230e996773SSimon Glass 1240e996773SSimon Glass rec->start_us = timer_get_boot_us(); 1250e996773SSimon Glass rec->name = name; 1260e996773SSimon Glass return rec->start_us; 1270e996773SSimon Glass } 1280e996773SSimon Glass 1290e996773SSimon Glass uint32_t bootstage_accum(enum bootstage_id id) 1300e996773SSimon Glass { 1310e996773SSimon Glass struct bootstage_record *rec = &record[id]; 1320e996773SSimon Glass uint32_t duration; 1330e996773SSimon Glass 1340e996773SSimon Glass duration = (uint32_t)timer_get_boot_us() - rec->start_us; 1350e996773SSimon Glass rec->time_us += duration; 1360e996773SSimon Glass return duration; 1370e996773SSimon Glass } 1380e996773SSimon Glass 1393a608ca0SSimon Glass static void print_time(unsigned long us_time) 1403a608ca0SSimon Glass { 1413a608ca0SSimon Glass char str[15], *s; 1423a608ca0SSimon Glass int grab = 3; 1433a608ca0SSimon Glass 1443a608ca0SSimon Glass /* We don't seem to have %'d in U-Boot */ 1453a608ca0SSimon Glass sprintf(str, "%12lu", us_time); 1463a608ca0SSimon Glass for (s = str + 3; *s; s += grab) { 1473a608ca0SSimon Glass if (s != str + 3) 1483a608ca0SSimon Glass putc(s[-1] != ' ' ? ',' : ' '); 1493a608ca0SSimon Glass printf("%.*s", grab, s); 1503a608ca0SSimon Glass grab = 3; 1513a608ca0SSimon Glass } 1523a608ca0SSimon Glass } 1533a608ca0SSimon Glass 15494fd1316SSimon Glass /** 15594fd1316SSimon Glass * Get a record name as a printable string 15694fd1316SSimon Glass * 15794fd1316SSimon Glass * @param buf Buffer to put name if needed 15894fd1316SSimon Glass * @param len Length of buffer 15994fd1316SSimon Glass * @param rec Boot stage record to get the name from 16094fd1316SSimon Glass * @return pointer to name, either from the record or pointing to buf. 16194fd1316SSimon Glass */ 16294fd1316SSimon Glass static const char *get_record_name(char *buf, int len, 16394fd1316SSimon Glass struct bootstage_record *rec) 16494fd1316SSimon Glass { 16594fd1316SSimon Glass if (rec->name) 16694fd1316SSimon Glass return rec->name; 16794fd1316SSimon Glass else if (rec->id >= BOOTSTAGE_ID_USER) 16894fd1316SSimon Glass snprintf(buf, len, "user_%d", rec->id - BOOTSTAGE_ID_USER); 16994fd1316SSimon Glass else 17094fd1316SSimon Glass snprintf(buf, len, "id=%d", rec->id); 17194fd1316SSimon Glass 17294fd1316SSimon Glass return buf; 17394fd1316SSimon Glass } 17494fd1316SSimon Glass 1753a608ca0SSimon Glass static uint32_t print_time_record(enum bootstage_id id, 1763a608ca0SSimon Glass struct bootstage_record *rec, uint32_t prev) 1773a608ca0SSimon Glass { 17894fd1316SSimon Glass char buf[20]; 17994fd1316SSimon Glass 1800e996773SSimon Glass if (prev == -1U) { 1810e996773SSimon Glass printf("%11s", ""); 1820e996773SSimon Glass print_time(rec->time_us); 1830e996773SSimon Glass } else { 1843a608ca0SSimon Glass print_time(rec->time_us); 1853a608ca0SSimon Glass print_time(rec->time_us - prev); 1860e996773SSimon Glass } 18794fd1316SSimon Glass printf(" %s\n", get_record_name(buf, sizeof(buf), rec)); 18894fd1316SSimon Glass 1893a608ca0SSimon Glass return rec->time_us; 1903a608ca0SSimon Glass } 1913a608ca0SSimon Glass 1923a608ca0SSimon Glass static int h_compare_record(const void *r1, const void *r2) 1933a608ca0SSimon Glass { 1943a608ca0SSimon Glass const struct bootstage_record *rec1 = r1, *rec2 = r2; 1953a608ca0SSimon Glass 1963a608ca0SSimon Glass return rec1->time_us > rec2->time_us ? 1 : -1; 1973a608ca0SSimon Glass } 1983a608ca0SSimon Glass 19994fd1316SSimon Glass #ifdef CONFIG_OF_LIBFDT 20094fd1316SSimon Glass /** 20194fd1316SSimon Glass * Add all bootstage timings to a device tree. 20294fd1316SSimon Glass * 20394fd1316SSimon Glass * @param blob Device tree blob 20494fd1316SSimon Glass * @return 0 on success, != 0 on failure. 20594fd1316SSimon Glass */ 20694fd1316SSimon Glass static int add_bootstages_devicetree(struct fdt_header *blob) 20794fd1316SSimon Glass { 20894fd1316SSimon Glass int bootstage; 20994fd1316SSimon Glass char buf[20]; 21094fd1316SSimon Glass int id; 21194fd1316SSimon Glass int i; 21294fd1316SSimon Glass 21394fd1316SSimon Glass if (!blob) 21494fd1316SSimon Glass return 0; 21594fd1316SSimon Glass 21694fd1316SSimon Glass /* 21794fd1316SSimon Glass * Create the node for bootstage. 21894fd1316SSimon Glass * The address of flat device tree is set up by the command bootm. 21994fd1316SSimon Glass */ 22094fd1316SSimon Glass bootstage = fdt_add_subnode(blob, 0, "bootstage"); 22194fd1316SSimon Glass if (bootstage < 0) 22294fd1316SSimon Glass return -1; 22394fd1316SSimon Glass 22494fd1316SSimon Glass /* 22594fd1316SSimon Glass * Insert the timings to the device tree in the reverse order so 22694fd1316SSimon Glass * that they can be printed in the Linux kernel in the right order. 22794fd1316SSimon Glass */ 22894fd1316SSimon Glass for (id = BOOTSTAGE_ID_COUNT - 1, i = 0; id >= 0; id--, i++) { 22994fd1316SSimon Glass struct bootstage_record *rec = &record[id]; 23094fd1316SSimon Glass int node; 23194fd1316SSimon Glass 23294fd1316SSimon Glass if (id != BOOTSTAGE_ID_AWAKE && rec->time_us == 0) 23394fd1316SSimon Glass continue; 23494fd1316SSimon Glass 23594fd1316SSimon Glass node = fdt_add_subnode(blob, bootstage, simple_itoa(i)); 23694fd1316SSimon Glass if (node < 0) 23794fd1316SSimon Glass break; 23894fd1316SSimon Glass 23994fd1316SSimon Glass /* add properties to the node. */ 24094fd1316SSimon Glass if (fdt_setprop_string(blob, node, "name", 24194fd1316SSimon Glass get_record_name(buf, sizeof(buf), rec))) 24294fd1316SSimon Glass return -1; 24394fd1316SSimon Glass 24494fd1316SSimon Glass /* Check if this is a 'mark' or 'accum' record */ 24594fd1316SSimon Glass if (fdt_setprop_cell(blob, node, 24694fd1316SSimon Glass rec->start_us ? "accum" : "mark", 24794fd1316SSimon Glass rec->time_us)) 24894fd1316SSimon Glass return -1; 24994fd1316SSimon Glass } 25094fd1316SSimon Glass 25194fd1316SSimon Glass return 0; 25294fd1316SSimon Glass } 25394fd1316SSimon Glass 25494fd1316SSimon Glass int bootstage_fdt_add_report(void) 25594fd1316SSimon Glass { 25694fd1316SSimon Glass if (add_bootstages_devicetree(working_fdt)) 25794fd1316SSimon Glass puts("bootstage: Failed to add to device tree\n"); 25894fd1316SSimon Glass 25994fd1316SSimon Glass return 0; 26094fd1316SSimon Glass } 26194fd1316SSimon Glass #endif 26294fd1316SSimon Glass 2633a608ca0SSimon Glass void bootstage_report(void) 2643a608ca0SSimon Glass { 2653a608ca0SSimon Glass struct bootstage_record *rec = record; 2663a608ca0SSimon Glass int id; 2673a608ca0SSimon Glass uint32_t prev; 2683a608ca0SSimon Glass 2693a608ca0SSimon Glass puts("Timer summary in microseconds:\n"); 2703a608ca0SSimon Glass printf("%11s%11s %s\n", "Mark", "Elapsed", "Stage"); 2713a608ca0SSimon Glass 2723a608ca0SSimon Glass /* Fake the first record - we could get it from early boot */ 2733a608ca0SSimon Glass rec->name = "reset"; 2743a608ca0SSimon Glass rec->time_us = 0; 2753a608ca0SSimon Glass prev = print_time_record(BOOTSTAGE_ID_AWAKE, rec, 0); 2763a608ca0SSimon Glass 2773a608ca0SSimon Glass /* Sort records by increasing time */ 2783a608ca0SSimon Glass qsort(record, ARRAY_SIZE(record), sizeof(*rec), h_compare_record); 2793a608ca0SSimon Glass 2803a608ca0SSimon Glass for (id = 0; id < BOOTSTAGE_ID_COUNT; id++, rec++) { 2810e996773SSimon Glass if (rec->time_us != 0 && !rec->start_us) 2823a608ca0SSimon Glass prev = print_time_record(rec->id, rec, prev); 2833a608ca0SSimon Glass } 2843a608ca0SSimon Glass if (next_id > BOOTSTAGE_ID_COUNT) 2853a608ca0SSimon Glass printf("(Overflowed internal boot id table by %d entries\n" 2863a608ca0SSimon Glass "- please increase CONFIG_BOOTSTAGE_USER_COUNT\n", 2873a608ca0SSimon Glass next_id - BOOTSTAGE_ID_COUNT); 2880e996773SSimon Glass 2890e996773SSimon Glass puts("\nAccumulated time:\n"); 2900e996773SSimon Glass for (id = 0, rec = record; id < BOOTSTAGE_ID_COUNT; id++, rec++) { 2910e996773SSimon Glass if (rec->start_us) 2920e996773SSimon Glass prev = print_time_record(id, rec, -1); 2930e996773SSimon Glass } 2943a608ca0SSimon Glass } 2953786980dSSimon Glass 2963786980dSSimon Glass ulong __timer_get_boot_us(void) 2973786980dSSimon Glass { 2983786980dSSimon Glass static ulong base_time; 2993786980dSSimon Glass 3003786980dSSimon Glass /* 3013786980dSSimon Glass * We can't implement this properly. Return 0 on the first call and 3023786980dSSimon Glass * larger values after that. 3033786980dSSimon Glass */ 3043786980dSSimon Glass if (base_time) 3053786980dSSimon Glass return get_timer(base_time) * 1000; 3063786980dSSimon Glass base_time = get_timer(0); 3073786980dSSimon Glass return 0; 3083786980dSSimon Glass } 3093786980dSSimon Glass 3103786980dSSimon Glass ulong timer_get_boot_us(void) 3113786980dSSimon Glass __attribute__((weak, alias("__timer_get_boot_us"))); 312fcf509b8SSimon Glass 313fcf509b8SSimon Glass /** 314fcf509b8SSimon Glass * Append data to a memory buffer 315fcf509b8SSimon Glass * 316fcf509b8SSimon Glass * Write data to the buffer if there is space. Whether there is space or not, 317fcf509b8SSimon Glass * the buffer pointer is incremented. 318fcf509b8SSimon Glass * 319fcf509b8SSimon Glass * @param ptrp Pointer to buffer, updated by this function 320fcf509b8SSimon Glass * @param end Pointer to end of buffer 321fcf509b8SSimon Glass * @param data Data to write to buffer 322fcf509b8SSimon Glass * @param size Size of data 323fcf509b8SSimon Glass */ 324fcf509b8SSimon Glass static void append_data(char **ptrp, char *end, const void *data, int size) 325fcf509b8SSimon Glass { 326fcf509b8SSimon Glass char *ptr = *ptrp; 327fcf509b8SSimon Glass 328fcf509b8SSimon Glass *ptrp += size; 329fcf509b8SSimon Glass if (*ptrp > end) 330fcf509b8SSimon Glass return; 331fcf509b8SSimon Glass 332fcf509b8SSimon Glass memcpy(ptr, data, size); 333fcf509b8SSimon Glass } 334fcf509b8SSimon Glass 335fcf509b8SSimon Glass int bootstage_stash(void *base, int size) 336fcf509b8SSimon Glass { 337fcf509b8SSimon Glass struct bootstage_hdr *hdr = (struct bootstage_hdr *)base; 338fcf509b8SSimon Glass struct bootstage_record *rec; 339fcf509b8SSimon Glass char buf[20]; 340fcf509b8SSimon Glass char *ptr = base, *end = ptr + size; 341fcf509b8SSimon Glass uint32_t count; 342fcf509b8SSimon Glass int id; 343fcf509b8SSimon Glass 344fcf509b8SSimon Glass if (hdr + 1 > (struct bootstage_hdr *)end) { 345fcf509b8SSimon Glass debug("%s: Not enough space for bootstage hdr\n", __func__); 346fcf509b8SSimon Glass return -1; 347fcf509b8SSimon Glass } 348fcf509b8SSimon Glass 349fcf509b8SSimon Glass /* Write an arbitrary version number */ 350fcf509b8SSimon Glass hdr->version = BOOTSTAGE_VERSION; 351fcf509b8SSimon Glass 352fcf509b8SSimon Glass /* Count the number of records, and write that value first */ 353fcf509b8SSimon Glass for (rec = record, id = count = 0; id < BOOTSTAGE_ID_COUNT; 354fcf509b8SSimon Glass id++, rec++) { 355fcf509b8SSimon Glass if (rec->time_us != 0) 356fcf509b8SSimon Glass count++; 357fcf509b8SSimon Glass } 358fcf509b8SSimon Glass hdr->count = count; 359fcf509b8SSimon Glass hdr->size = 0; 360fcf509b8SSimon Glass hdr->magic = BOOTSTAGE_MAGIC; 361fcf509b8SSimon Glass ptr += sizeof(*hdr); 362fcf509b8SSimon Glass 363fcf509b8SSimon Glass /* Write the records, silently stopping when we run out of space */ 364fcf509b8SSimon Glass for (rec = record, id = 0; id < BOOTSTAGE_ID_COUNT; id++, rec++) { 365fcf509b8SSimon Glass if (rec->time_us != 0) 366fcf509b8SSimon Glass append_data(&ptr, end, rec, sizeof(*rec)); 367fcf509b8SSimon Glass } 368fcf509b8SSimon Glass 369fcf509b8SSimon Glass /* Write the name strings */ 370fcf509b8SSimon Glass for (rec = record, id = 0; id < BOOTSTAGE_ID_COUNT; id++, rec++) { 371fcf509b8SSimon Glass if (rec->time_us != 0) { 372fcf509b8SSimon Glass const char *name; 373fcf509b8SSimon Glass 374fcf509b8SSimon Glass name = get_record_name(buf, sizeof(buf), rec); 375fcf509b8SSimon Glass append_data(&ptr, end, name, strlen(name) + 1); 376fcf509b8SSimon Glass } 377fcf509b8SSimon Glass } 378fcf509b8SSimon Glass 379fcf509b8SSimon Glass /* Check for buffer overflow */ 380fcf509b8SSimon Glass if (ptr > end) { 381fcf509b8SSimon Glass debug("%s: Not enough space for bootstage stash\n", __func__); 382fcf509b8SSimon Glass return -1; 383fcf509b8SSimon Glass } 384fcf509b8SSimon Glass 385fcf509b8SSimon Glass /* Update total data size */ 386fcf509b8SSimon Glass hdr->size = ptr - (char *)base; 387fcf509b8SSimon Glass printf("Stashed %d records\n", hdr->count); 388fcf509b8SSimon Glass 389fcf509b8SSimon Glass return 0; 390fcf509b8SSimon Glass } 391fcf509b8SSimon Glass 392fcf509b8SSimon Glass int bootstage_unstash(void *base, int size) 393fcf509b8SSimon Glass { 394fcf509b8SSimon Glass struct bootstage_hdr *hdr = (struct bootstage_hdr *)base; 395fcf509b8SSimon Glass struct bootstage_record *rec; 396fcf509b8SSimon Glass char *ptr = base, *end = ptr + size; 397fcf509b8SSimon Glass uint rec_size; 398fcf509b8SSimon Glass int id; 399fcf509b8SSimon Glass 400fcf509b8SSimon Glass if (size == -1) 401fcf509b8SSimon Glass end = (char *)(~(uintptr_t)0); 402fcf509b8SSimon Glass 403fcf509b8SSimon Glass if (hdr + 1 > (struct bootstage_hdr *)end) { 404fcf509b8SSimon Glass debug("%s: Not enough space for bootstage hdr\n", __func__); 405fcf509b8SSimon Glass return -1; 406fcf509b8SSimon Glass } 407fcf509b8SSimon Glass 408fcf509b8SSimon Glass if (hdr->magic != BOOTSTAGE_MAGIC) { 409fcf509b8SSimon Glass debug("%s: Invalid bootstage magic\n", __func__); 410fcf509b8SSimon Glass return -1; 411fcf509b8SSimon Glass } 412fcf509b8SSimon Glass 413fcf509b8SSimon Glass if (ptr + hdr->size > end) { 414fcf509b8SSimon Glass debug("%s: Bootstage data runs past buffer end\n", __func__); 415fcf509b8SSimon Glass return -1; 416fcf509b8SSimon Glass } 417fcf509b8SSimon Glass 418fcf509b8SSimon Glass if (hdr->count * sizeof(*rec) > hdr->size) { 419fcf509b8SSimon Glass debug("%s: Bootstage has %d records needing %d bytes, but " 420fcf509b8SSimon Glass "only %d bytes is available\n", __func__, hdr->count, 421fcf509b8SSimon Glass hdr->count * sizeof(*rec), hdr->size); 422fcf509b8SSimon Glass return -1; 423fcf509b8SSimon Glass } 424fcf509b8SSimon Glass 425fcf509b8SSimon Glass if (hdr->version != BOOTSTAGE_VERSION) { 426fcf509b8SSimon Glass debug("%s: Bootstage data version %#0x unrecognised\n", 427fcf509b8SSimon Glass __func__, hdr->version); 428fcf509b8SSimon Glass return -1; 429fcf509b8SSimon Glass } 430fcf509b8SSimon Glass 431fcf509b8SSimon Glass if (next_id + hdr->count > BOOTSTAGE_ID_COUNT) { 432fcf509b8SSimon Glass debug("%s: Bootstage has %d records, we have space for %d\n" 433fcf509b8SSimon Glass "- please increase CONFIG_BOOTSTAGE_USER_COUNT\n", 434fcf509b8SSimon Glass __func__, hdr->count, BOOTSTAGE_ID_COUNT - next_id); 435fcf509b8SSimon Glass return -1; 436fcf509b8SSimon Glass } 437fcf509b8SSimon Glass 438fcf509b8SSimon Glass ptr += sizeof(*hdr); 439fcf509b8SSimon Glass 440fcf509b8SSimon Glass /* Read the records */ 441fcf509b8SSimon Glass rec_size = hdr->count * sizeof(*record); 442fcf509b8SSimon Glass memcpy(record + next_id, ptr, rec_size); 443fcf509b8SSimon Glass 444fcf509b8SSimon Glass /* Read the name strings */ 445fcf509b8SSimon Glass ptr += rec_size; 446fcf509b8SSimon Glass for (rec = record + next_id, id = 0; id < hdr->count; id++, rec++) { 447fcf509b8SSimon Glass rec->name = ptr; 448fcf509b8SSimon Glass 449fcf509b8SSimon Glass /* Assume no data corruption here */ 450fcf509b8SSimon Glass ptr += strlen(ptr) + 1; 451fcf509b8SSimon Glass } 452fcf509b8SSimon Glass 453fcf509b8SSimon Glass /* Mark the records as read */ 454fcf509b8SSimon Glass next_id += hdr->count; 455fcf509b8SSimon Glass printf("Unstashed %d records\n", hdr->count); 456fcf509b8SSimon Glass 457fcf509b8SSimon Glass return 0; 458fcf509b8SSimon Glass } 459