Reading "Reset cause" from u-boot to Operating system

  • I wanted to know the reset cause from U-boot into my operating system, and was hacking to find a way to get this. Maybe this benefits others.
    This is the reset cause from u-boot:

    U-Boot 2014.07 (Jan 19 2017 - 13:16:57) for F&S

    CPU: Freescale i.MX6UL rev1.0 at 396 MHz
    Reset: WDOG <- This will change depending on the cause of reset (power off, warm reboot etc)
    Board: efusA7UL Rev 1.10 (LAN, WLAN, 1x DRAM)
    Since this is reset before the enviroment is initialized, I needed to introduce a new variable, and copy the reset cause there, and then tried to find a sane location to inject it into enviroment variable reset_cause.
    I do it during the countdown when booting, and there is probably better places to introduce it.

    Once fully booted to Operating system, I use fw_printenv | grep reset_cause to read it, and voila.

    Attached patch for u-boot sources from F&S fsimx6ul-V2.0, happy hacking guys!

  • This is not fully correct, because it may cause problems in the future. Here is why.

    U-Boot can be started in different ways, depending on the type of volatile memory where it is stored. When starting from NAND flash, it needs some kind of first stage bootloader that activates RAM and loads U-Boot from NAND to RAM. In our case this is NBoot. So yes, in this case RAM is already available when U-Boot starts. But U-Boot can also be started from non-volatile memory that allows execute-in-place, for example NOR flash memory or SPI NAND flash. In this case a first stage bootloader is not required and U-Boot is the only bootloader. Then of course RAM is not yet initialized when U-Boot starts and initializing RAM is one of the tasks of U-Boot itself. So there is a part of U-Boot right at the beginning that assumes to be run without any regular RAM at all. Code that is running in this early stage of U-Boot obviously must not use any global variables, because they would need to be located in RAM, which is not available yet.

    Later, when RAM is initialized and fully available, U-Boot relocates itself to the end of RAM. This is also true when U-Boot starts in RAM already. It relocates itself to the end of RAM in any case. And only when this is done, it can start using variables.

    The code for get_reset_cause() is actually executed in this early stage of U-Boot when no variables are allowed. So storing the pointer to the reset cause string in a global variable is basically wrong. First of all the pointer still points to the strings at the old U-Boot position before relocation, not to the strings at the new U-Boot position at the end of RAM after relocation. So if you download a large rootfs to RAM, the data at the old postion is overwritten and the strings are gone. And also the variable itself is located at a different position after relocation. Actually I'm astonished that it works at all. Because the code that stores the value is run at the old U-Boot position and will store the value at the old position before relocation, but the code that uses the value later (to store it in an environment variable) is run at the new U-Boot position and loads the value from a place near the end of RAM. So I'm actually surprised that the value is available there, i.e. that it survives the relocation at all. But you should not rely on this behavior, this may work by pure accident now. For example if we ever do a board where U-Boot is stored in some other flash media and where we do not need NBoot as first stage loader, this kind of code would not work anymore because either the variable would still be located in flash memory at this early time of execution before relocation or trying to access non-initialized RAM would cause a bus error or some similar exception. So the value could definitely not be stored in the variable.

    The correct way of doing this would be to store the reset cause in the "global data". U-Boot relies on the fact that there is at least a small kind of CPU-internal SRAM where a few values can be stored even during the first boot stage when no regular RAM is available. This space is called the global data. Of yourse this area should be kept as small as possible so that it also fits into the very small SRAM areas available on some CPUs. Luckily on i.MX6, we have enough SRAM to add a few more values to the global data. So the correct way would be to move the reset cause value (rval) to the global data (struct arch_global_data in arch/arm/include/asm/global_data.h). You can still refer to this data later when U-Boot is relocated.

    Finally storing the value to an environment variable can be done in board_late_init() in fsimx6.c. This function is used for similar tasks anyway. This would be better than the place in autostart that you have chosen.

    By the way isn't fw_printenv only showing variables from the saved environment in NAND? So do you really get the reset cause from the last boot process? Or just the one from the last time that you have saved your environment in U-Boot with saveenv?

    However making the reset cause available to Linux would be a good idea anyway. So I have noted down this idea and I think we will have an official way for this in the next release. The best place would be /sys/bdinfo. This location will show quite some more information in the future anyway, for example the board revision, architecture, platform name, some NAND information and similar things. The reset cause would fit in there quite nicely.

    Your F&S Support Team

    F&S Elektronik Systeme GmbH
    As this is an international forum, please try to post in English.
    Da dies ein internationales Forum ist, bitten wir darum, Beiträge möglichst in Englisch zu verfassen.

    Edited 12 times, last by fs-support_HK ().