This technote includes:
The IFS Restoration feature applies to systems that require an ultra-low power mode in which the main CPU is completely turned off while the SDRAM is left in a self-refresh state. This provides the ability to create a low-power mode with power consumption ranging from 1-3 mA (depending on the power draw of the SDRAM in self-refresh) with an accelerated wake-up/reboot.
IFS Restoration makes use of the fact that the image filesystem (IFS) is already in RAM on wake-up from this state and avoids the slow process of copying the image from FLASH to RAM. Note that when booting for the first time (SDRAM turned off), there's be no time savings — savings are only on subsequent boots:
The following diagram shows the difference between a normal boot and an accelerated boot using IFS Restoration:
There are two mechanisms to achieve the IFS restoration:
On wake-up, the entire primary IFS is copied, while the secondary IFS can be reused without having to copy. Note that additional binaries can be added to the primary IFS, but these will be copied along with the rest of the primary IFS.
The advantage of kernel restoration is that it can be enabled and used with very little intervention by the user. Secondary IFS restoration may be desired for projects that wish to use multiple image filesystems, but requires more management by the user, such as maintaining multiple buildfiles and specifying the secondary IFS size and location.
It's possible to enable both kernel and secondary IFS restoration at the same time (i.e the startup and kernel from the primary IFS are restored, and the secondary IFS is reused and automatically mounted).
IFS Restoration was designed to support the following features:
Compressed secondary image filesystems aren't currently supported. |
The features are enabled via command-line options to the board-specific startup. Since all of the support is built into the startup library, the features are generic across all platforms.
To enable kernel restoration, use the -I option to the startup in the buildfile:
startup-boardname -I flag
where flag is 0 to disable checksum verification, or 1 to enable it.
For debugging output, specify at least two levels of debugging via the -v option:
startup-boardname -I flag -vv
If checksum verification is enabled and fails, the entire image is reloaded.
Even if the IFS checksum verification is disabled, a checksum is still performed on the IFS Restoration internal data structure (approximately 32 bytes) to ensure at least some data integrity. |
To enable secondary IFS restoration, use the -i option to the startup in the buildfile:
startup-boardname -i ifs2_size[,flags][,paddr_src][,paddr_dst]
The arguments are:
For debugging output, specify at least two levels of debugging via the -v option:
startup-boardname -i ifs2_size[,flags][,paddr_src][,paddr_dst] -vv
If the checksum is enabled and fails, the entire secondary IFS is reloaded.
Even if the secondary IFS checksum is disabled, a checksum is still performed on the IFS Restoration internal data structure (approximately 16 bytes) to ensure at least some data integrity. |
Kernel and secondary IFS restoration aren't guaranteed to work if the
image is downloaded serially.
This is because the IPL may copy the serially downloaded image to a
location in RAM that overwrites the secondary IFS or data structures
used by the restore features.
In practice, this isn't an issue since serial downloading won't be used other than for testing. If serial download is required, try manually setting the destination location of the secondary IFS to be somewhere away from where the IPL downloads the image. |
The following examples are for the EDOSK7780 reference platform, based on an SH4A 400 MHz CPU.
The sample buildfile for kernel restoration on the EDOSK7780 reference platform is as follows:
################################################################# ## START OF BUILD SCRIPT for Renesas EDOSK7780 Board ################################################################# [image=0x88010000] [virtual=shle/binary +compress] .bootstrap = { startup-edosk7780 -Dscif..115200.1843200.16 -f400000000 -vv -I1 PATH=/proc/boot LD_LIBRARY_PATH=/proc/boot procnto -vvvv } [+script] .script = { procmgr_symlink ../../proc/boot/libc.so.2 /usr/lib/ldqnx.so.2 display_msg Welcome to QNX Neutrino on the Renesas EDOSK-7780 devc-sersci -e -F -x -b115200 -c1843200/16 scif0 scif1 & reopen /dev/ser1 SYSNAME=nto TERM=qansi [+session] PATH=:/proc/boot LD_LIBRARY_PATH=/proc/boot ksh & } [type=link] /dev/console=/dev/ser1 [type=link] /tmp=/dev/shmem libc.so [data=c] devc-sersci ls ksh pidin ################################################################### ## END OF BUILD SCRIPT ###################################################################
To build the image:
# mkifs -v edosk7780.build edosk7780.ifs
The sample output is as follows:
QNX Neutrino IPL for the EDOSK-7780 Press 'd' to download an OS image serially Press any other key to boot from flash
Press a key other than d; the output continues:
Downloading from Flash Restore IFS searching for valid IFS in RAM... rifs_info PADDR = 0x08008000 rifs_info ADDR = 0x88008000 INVALID IFS signature Restore IFS failed - Reload entire IFS. PT_LOAD RW: 0004b000 size is 000030b0 Found procnto Elf header bootable exec data: offset 0004b000, size 000030b0 Compressed image, store data Calculate restore info checksum System page at phys:081d1000 user:081d1000 kern:881d1000 Starting next program at v8803c258 Welcome to QNX Neutrino on the Renesas EDOSK-7780
The board has booted; run an application:
# # ls dev proc tmp usr
Press the reset button to simulate wake-up:
# QNX Neutrino IPL for the EDOSK-7780 Press 'd' to download an OS image serially Press any other key to boot from flash
Press a key other than d. The output continues:
Downloading from Flash Restore IFS searching for valid IFS in RAM... rifs_info PADDR = 0x08008000 rifs_info ADDR = 0x88008000 FOUND valid IFS signature FOUND valid RIFS signature FOUND valid RIFS info FOUND valid IFS signature and RIFS info in RAM. IFS pre checksum = 0x7bbb7898 (shouldn't be 0x0) bootable exec 0 offset: 0x0004bef8 bootable exec 0 size: 0x000030b0 Compressed image src = 0x0800a000 IFS post checksum = 0x00000000 (should be 0x0) PT_LOAD RW: 0004b000 size is 000030b0 Found procnto Elf header bootable exec data: offset 0004b000, size 000030b0 Compressed image, store data Calculate restore info checksum System page at phys:081d1000 user:081d1000 kern:881d1000 Starting next program at v8803c258 Welcome to QNX Neutrino on the Renesas EDOSK-7780 # ls dev proc tmp usr
To disable the checksum verification on the IFS after it has been restored, modify the buildfile to:
startup-edosk7780 -Dscif..115200.1843200.16 -f400000000 -vv —I0
On subsequent boots, you will see the additional output (with debug enabled):
WARNING: Skipped image checksum verification
With debugging enabled, the checksum value is still calculated so that it can be displayed. However, the checksum value isn't used to determine if the IFS has been restored properly (it's assumed that it was). |
If you're working with uncompressed images, the buildfile looks like:
[virtual=shle/binary] .bootstrap = { startup-edosk7780 -Dscif..115200.1843200.16 -f400000000 -vv -I1 PATH=/proc/boot LD_LIBRARY_PATH=/proc/boot procnto -vvvv }
The sample output looks something like:
Restore IFS searching for valid IFS in RAM... rifs_info PADDR = 0x08008000 rifs_info ADDR = 0x88008000 INVALID IFS signature Restore IFS failed - Reload entire IFS. PT_LOAD RW: 0004b000 size is 000030b0 Found procnto Elf header bootable exec data: offset 0004b000, size 000030b0 Calculate restore info checksum
Press the reset button to simulate wake-up. The output continues:
Restore IFS searching for valid IFS in RAM... rifs_info PADDR = 0x08008000 rifs_info ADDR = 0x88008000 FOUND valid IFS signature FOUND valid RIFS signature FOUND valid RIFS info FOUND valid IFS signature and RIFS info in RAM. IFS pre checksum = 0x7a82a8f2 (shouldn't be 0x0) bootable exec 0 offset: 0x0004bef8 bootable exec 0 size: 0x000030b0 Uncompressed image paddr_src = 0x00059000 IFS post checksum = 0x00000000 (should be 0x0) PT_LOAD RW: 0004b000 size is 000030b0 Found procnto Elf header bootable exec data: offset 0004b000, size 000030b0 Calculate restore info checksum
To build a multiple IFS image, you must create a primary and a secondary IFS. The primary IFS includes only the startup and procnto (kernel). The secondary IFS includes all other libraries and binaries including libc, drivers, and applications.
Here's a sample primary buildfile:
################################################################# ## START OF PRIMARY IFS BUILD SCRIPT for Renesas EDOSK7780 Board ################################################################# [image=0x88010000] [virtual=shle/binary +compress] .bootstrap = { startup-edosk7780 -Dscif..115200.1843200.16 -f400000000 -i0x700000 -vv PATH=/proc/boot:/ifs2 LD_LIBRARY_PATH=/proc/boot:/ifs2 procnto -vvvv } [+script] .script = { # Default libc symbolic link #procmgr_symlink ../../proc/boot/libc.so.2 /usr/lib/ldqnx.so.2 # Symbolic link to libc in secondary IFS procmgr_symlink ../../ifs2/libc.so.2 /usr/lib/ldqnx.so.2 display_msg Welcome to QNX Neutrino on the Renesas EDOSK-7780 devc-sersci -e -F -x -b115200 -c1843200/16 scif0 scif1 & reopen /dev/ser1 SYSNAME=nto TERM=qansi [+session] PATH=:/proc/boot:/ifs2 LD_LIBRARY_PATH=/proc/boot:/ifs2 ksh & } [type=link] /dev/console=/dev/ser1 [type=link] /tmp=/dev/shmem ################################################################### ## END OF PRIMARY IFS BUILD SCRIPT ###################################################################
The size 0x700000 passed to the -i option was chosen to be much larger than the size of the secondary IFS. Don't make it too large, since memory will be reserved according to this size, and a copy will be made from flash. |
The sample secondary buildfile is as follows:
################################################################# ## START OF SECONDARY IFS BUILD SCRIPT for Renesas EDOSK7780 Board ################################################################# # Specify the search path, otherwise defaults to x86 [search=${QNX_TARGET}/shle/bin:${QNX_TARGET}/shle/usr/bin:${QNX_TARGET}/shle/sbin:${QNX_TARGET}/shle/usr/sbin:${QNX_TARGET}/shle/lib:${QNX_TARGET}/shle/lib/dll:${QNX_TARGET}/shle/usr/lib] # Windows mkifs needs to be reminded of permissions [perms=+x] # Where the files will be mounted at boot time [prefix=/ifs2] # Libraries to include [data=copy] libc.so # Binaries to include [+raw data=copy] cat devc-sersci ls pidin ksh echo ################################################################### ## END OF SECONDARY IFS BUILD SCRIPT ###################################################################
Unless the physical address for the source location for the secondary image is specified, IFS Restoration will automatically look for the secondary IFS in flash directly following the primary IFS.
To create an image consisting of both primary and secondary IFS:
# mkifs -v primary.build primary.ifs # mkifs -v secondary.build secondary.ifs # cat primary.ifs secondary.ifs > edosk7780.ifs # mkflashimage
The above example loads a primary IFS and a secondary IFS mounted at /ifs2. The output will look something like:
Press 'd' to download an OS image serially Press any other key to boot from flash
Press a key other than d.
Downloading from Flash ifs2_paddr_dst: 0x0806d000 ifs2_paddr_src: 0x0003a9b0 ifs2_paddr_src (auto): 0x0003a9b0 System page at phys:0800a000 user:0800a000 kern:8800a000 Starting next program at v8803c258 Welcome to QNX Neutrino on the Renesas EDOSK-7780 # ls /proc/boot .script procnto # ls /ifs2 cat ksh ls echo libc.so pidin devc-sersci libc.so.2
To restore the secondary IFS, modify the options to startup in the primary buildfile (enable secondary IFS restoration, enable checksum):
startup-edosk7780 -Dscif..115200.1843200.16 -f400000000 -i0x700000,K -vv
After rebuilding and burning the updated multiple IFS image to flash, the output will look like:
QNX Neutrino IPL for the EDOSK-7780 Press 'd' to download an OS image serially Press any other key to boot from flash
Press a key other than d.
Downloading from Flash ifs2_paddr_dst: 0x0806d000 Restore IFS2 searching for valid IFS in RAM... rifs2_info PADDR = 0x08008000 rifs2_info ADDR = 0x88008000 INVALID IFS signature Restore IFS2 failed - Reload entire IFS2. ifs2_paddr_src: 0x0003a9b0 ifs2_paddr_src (auto): 0x0003a9b0 System page at phys:0800b000 user:0800b000 kern:8800b000 Starting next program at v8803c258 Welcome to QNX Neutrino on the Renesas EDOSK-7780 #
Press the reset button to simulate wake-up:
# QNX Neutrino IPL for the EDOSK-7780 Press 'd' to download an OS image serially Press any other key to boot from flash
Press a key other than d:
Downloading from Flash ifs2_paddr_dst: 0x0806d000 Restore IFS2 searching for valid IFS in RAM... rifs2_info PADDR = 0x08008000 rifs2_info ADDR = 0x88008000 FOUND valid IFS signature FOUND valid IFS2 signature and RIFS2 info in RAM. System page at phys:0800b000 user:0800b000 kern:8800b000 Starting next program at v8803c258 Welcome to QNX Neutrino on the Renesas EDOSK-7780 #
To disable the checksum verification on the secondary IFS after it has been restored, modify the buildfile to:
startup-edosk7780 -Dscif..115200.1843200.16 -f400000000 -i0x700000,R -vv
On subsequent boots, you will see the additional output (with debug enabled):
WARNING: Skipped IFS2 checksum verification
If you don't want to put the primary and secondary IFS together in flash (by using the cat utility), you can put the secondary IFS anywhere in the flash you wish and manually specify its location. Additionally, you can manually specify the location to copy the secondary IFS to in RAM if you don't wish to use the default location. Use the optional arguments to specify the physical address for the secondary IFS source and destination:
To calculate the destination in RAM:
paddr_dst = paddr_start_of_RAM + paddr_location_in_RAM
For example, on the EDOSK7780, there is 128 MB of RAM starting at the address 0x08000000. To put the secondary IFS at the 120 MB location in RAM:
paddr_dst = 0x08000000 + 120 * 1024 * 1024 = 0x0F800000
The destination location in RAM must lie on a 4 KB page boundary.
The address passed in is automatically adjusted to ensure this:
paddr_dst = paddr_dst & 0xFFFFF000. |
To manually specify the source of the secondary IFS located in flash at 0x00040000:
startup-edosk7780 -Dscif..115200.1843200.16 -f400000000 -i0x700000,0x00040000
To manually specify the source of the secondary IFS located in flash at 0x00040000, and the destination of the secondary IFS in RAM at 0x0F800000:
startup-edosk7780 -Dscif..115200.1843200.16 -f400000000 -i0x700000,0x00040000, 0x0F800000
To manually specify the source of the secondary IFS located in flash at 0x00040000, the destination of the secondary IFS in RAM at 0xF8000000, and enable restoring with checksum:
startup-edosk7780 -Dscif..115200.1843200.16 -f400000000 -i0x700000,K,0x00040000, 0x0F800000
The power callout is a piece of code that resides as part of the startup and is used to put the CPU and SDRAM into low-power states. The Neutrino kernel doesn't automatically call the power callout to put the system into a low-power mode. A user application (typically a Power Manager application) will make the decision to power down the system and call the power callout via the sysmgr_cpumode() kernel call (see the Appendix for a sample program).
The advantage of putting the power-down code into the power callout is that the kernel will lock the system to prevent applications from running. The Power Manager application is responsible for notifying any other applications and device drivers of any power-state changes before calling the power callout, so that they can prepare for the upcoming power-mode change (i.e. save their state). The function prototype to invoke the power callout is:
int sysmgr_cpumode(int mode);
A power callout may support multiple modes to put the CPU into different power modes. The mode parameter is used by the Power Manager to select the power mode the power callout will put the system into.
In the case of IFS Restoration, the mode parameter may not be used, since there may be only one power mode: turning the CPU off. |
The power callout code is custom for each CPU/board. It must be position-independent code (PIC) and written in assembly. For more information on writing a power callout, see “Callout information” and “Writing your own kernel callout” in the Customizing Image Startup Programs chapter of Building Embedded Systems.
As an overview, the easiest way to start writing a power callout is to copy the reboot callout. For example, in the EDOSK7780 startup, make a copy of callout_reboot_edosk7780.s and rename it to callout_power_edosk7780.s. In the CALLOUT_START and CALLOUT_END, change the reboot_sh_edosk7780 references to power_sh_edosk7780. In between CALLOUT_START and CALLOUT_END, insert the assembly instructions required to put the SDRAM into self-refresh and power off the CPU.
# Power Callout Example .include "callout.ah" .include "asmoff.def" CALLOUT_START power_sh_edosk7780, 0, 0 # Assembly code to power down CPU # Globally disable interrupts # Pre-fetch final instructions into icache, or jump to code in flash # Put SDRAM into self-refresh # Sequence to power off CPU (i.e. set GPIO pin connected to external # power controller chip) CALLOUT_END power_sh_edosk7780
Because the power callout runs out of RAM that will be put into a self-refresh state (meaning that instructions in RAM can't be accessed), you may need to prefetch the instructions that follow the RAM going into self-refresh into the instruction cache. This will ensure that the instructions can be run when the SDRAM is in self-refresh. Alternatively, you can place a portion of your power callout in static RAM or flash, and then jump to this location to execute the final assembly instructions. |
You also need to modify the main.c of startup to tell the kernel the about the power callout:
/* Callout prototypes */ extern struct callout_rtn reboot_sh_edosk7780; extern struct callout_rtn power_sh_edosk7780; /* Kernel callout array */ const struct callout_slot callouts[] = { { CALLOUT_SLOT( reboot, _sh_edosk7780 ) }, { CALLOUT_SLOT( power, _sh_edosk7780 ) }, };
The IFS Restoration was designed to be compatible with minidrivers. Any copies of data made during IFS restoration will include periodic polls to the minidriver handler (if enabled) based on the value specified by the mdriver_max variable:
unsigned mdriver_max = KILO(16);
This value specifies how many bytes will be copied from flash to RAM before the minidriver handler will be polled. For more information, see the Instant Device Activation User's Guide.
Similarly, IFS Restoration will perform the potentially lengthy operation of performing a checksum on the entire IFS (if enabled). To ensure that the minidriver handler is periodically polled, a polling value is defined:
unsigned mdriver_cksum_max = KILO(500);
This value specifies how many bytes of the IFS checksum will be performed before the minidriver handler will be polled. The default value is set to be much larger than the mdriver_max variable since it's assumed that the checksum (adding bytes in RAM) will be much faster than copying bytes from flash to RAM.
The following table shows the boot time savings using IFS Restoration, as measured on an EDOSK7780 at 400 MHz. Note that debug output is disabled.
Restoration type | Image size | IFS compression | IFS checksum | Initial boot time | Subsequent boot time | Savings |
---|---|---|---|---|---|---|
Kernel | 32.8 MB | UCL | No | 8 s | < 0.5 s | 7.5 s |
Kernel | 32.8 MB | UCL | Yes | 8 s | < 1.0 s | 7.0 s |
Kernel | 32.8 MB | None | No | 7 s | < 0.5 s | 6.5 s |
Kernel | 32.8 MB | None | Yes | 7 s | < 1.0 s | 6.0 s |
Secondary IFS | 32.0 MB (0.2 MB, 31.8 MB) | UCL (primary)
None (secondary) |
No | 7 s | < 0.5 s | 6.5 s |
Secondary IFS | 32.0 MB (0.2 MB, 31.8 MB) | UCL (primary)
None (secondary) |
Yes | 7 s | < 1.0 s | 6.0 s |
Note the following details regarding these performance numbers:
This appendix includes:
#include <stdio.h> #include <stdlib.h> #include <inttypes.h> #include <sys/sysmgr.h> #include <sys/neutrino.h> #include <errno.h> int main(int argc, char *argv[]) { int mode = 0; int ret = 0; int verbose = 0; int opt; while ((opt = getopt(argc, argv, "m:v")) != -1) { switch(opt) { case 'v': verbose++; break; case 'm': // Obtain the desired CPU power mode mode = atoi(optarg); break; } } // Obtain I/O privileges ThreadCtl( _NTO_TCTL_IO, 0 ); // NOTE: Any printf's called before the call to sysmgr_cpumode() may // cause the CPU to wake up due to a serial TX interrupt. errno = 0; // Call the power callout in startup ret = sysmgr_cpumode(mode); if(ret == -1) { if(verbose) perror("sysmgr_cpumode()"); } else { if(verbose) printf("\nCPU powered UP!\n\n"); } return 0; }
To manually mount a 10 MB IFS located in memory at 0x0F800000:
mount -tifs -ooffset=0x0F800000,size=0xA00000 /dev/mem /ifs2
#!/bin/sh # script to build a binary IPL and boot image for the EDOSK7780 board set -v #convert IPL into an S-Record ${QNX_HOST}/usr/bin/ntosh-objcopy -Osrec ipl-edosk7780 ipl-tmp.srec #convert S-Record IPL to binary ${QNX_HOST}/usr/bin/ntosh-objcopy -Isrec -Obinary ipl-tmp.srec ipl.tmp mkrec -r -ffull -s0x2000 ipl.tmp > ipl-tmp.bin #combine ipl with boot image cat ipl-tmp.bin edosk7780.ifs > edosk7780.bin echo "done!!!!!!!"
io-pkt -dsmc9000 ioport=0x15800000,irq=6 -pttcpip if=en0:x.x.x.x qconn (use to transfer new image to /dev/shmem) devf-edosk7780 -s0x0,64M (Overwrite the boot image in current bank) flashctl -p/dev/fs0 -l2M -ev cp -V fb.ifs /dev/fs0
To burn the image in the other bank (i.e. not the bank used to boot), use the following instead: devf-edosk7780 -s0x04000000,64M |