This technote includes:
You can generate images as described in mkifs in the Utilities Reference and in Making an OS Image in Building Embedded Systems.
When you use mkifs to build an OS filesystem, you can specify that particular executables will either execute in place (normally in flash) or be copied to RAM and executed there (see “Notes on XIP versus copy,” in the mkifs entry in Utilities Reference). Executing in place saves a bit of RAM by avoiding the copying of the code and/or data segment of an object from one DRAM location to another.
But what if you want some executables to run from flash and others from RAM? That's what multiple images are used for.
Multiple image filesystems are typically used in execute-in-place (XIP) systems to separate executables that must run directly from flash (to conserve RAM) and those that should be copied and then executed from RAM (for performance).
Simply put, you create two separate images and then stitch them together. This is what the OS image will look like:
The boot image filesystem will be run from RAM to improve performance and the XIP image filesystem will be run from flash to conserve RAM.
Each of the three sections must begin at a memory page boundary (typically 4 KB). The IPL code will execute the OS in the first image.
|
There are some restrictions in XIP image filesystems:
Once the kernel is running, you can mount image filesystems. The syntax for this is:
mount -tifs -o offset=XXXXXXXX,size=YYYYYYYY /dev/mem /mountpoint
The variables are:
For example, suppose an image filesystem starts at physical address 0x600000 and is a size of 0x23A5C. You can use the mkifs -v command to determine the size when you build the image. The next highest 4-KB page boundary is 0x24000. Suppose we want to mount this image as /ifs2. The syntax would be:
mount -tifs -o offset=0x600000,size=0x24000 /dev/mem /ifs2
There are several options for using a second image filesystem:
You can't use this flash device for an image filesystem and a flash filesystem (devf-*) at the same time. |
In this technical note, we'll use the third option.
We'll use the MGT5200 Total board (revision 1) in this example. This board has a flash part that's 32 MB in size and sits in memory space from 0xFE000000 to 0xFFFFFFFF. The reset vector jumps to 0xFE000000.
You can design the flash any way you wish. Here's the layout that we'll use in this example:
From | To | Contains | Size |
---|---|---|---|
0xFE000000 | 0xFE03FFFF | IPL | 256 KB |
0xFE040000 | 0xFE2FFFFF | Boot image 1 (os1) | 3 MB − 256 KB |
0xFE300000 | 0xFE3FFFFF | Boot image 2 (os2) | 1 MB |
0xFE400000 | 0xFEFFFFFF | Flash filesystem mounted as / | 28 MB |
The IPL has been changed to start searching for os1 at 0xFE040000. By default, the IPL will search from 0xFE010000 to 0xFE030000.
First we need to make the os1 and os2 boot images. Let's look at the buildfiles.
The os1.build file looks like this:
[image=0x20000] [virtual=ppcbe,binary +compress] .bootstrap = { # reserve 4M of ram at the 6M physical address point. and zero it out (0 flag) startup-mgt5200-dual -vvv -r 0x600000,0x400000,0 ####################################################################### ## PATH set here is the *safe* path for executables. ## LD_LIBRARY_PATH set here is the *safe* path for libraries. ## i.e. These are the paths searched by setuid/setgid binaries. ## (confstr(_CS_PATH...) and confstr(_CS_LIBPATH...)) ####################################################################### PATH=:/proc/boot:/bin:/usr/bin LD_LIBRARY_PATH=:/proc/boot:/lib:/usr/lib:/lib/dll procnto-600 -v } [+script] .script = { procmgr_symlink ../../proc/boot/libc.so.2 /usr/lib/ldqnx.so.2 ####################################################################### ## TOTAL 5200 SDP ####################################################################### display_msg Welcome to QNX Neutrino 6.3 on the Motorola TOTAL5200 SDP.. OS1 devc-serpsc -c 132000000 -u 3 -e -F -S -b115200 0xf0002400,67 waitfor /dev/ser3 reopen /dev/ser3 SYSNAME=nto TERM=qansi HOME=/ PATH=:/proc/boot:/bin:/usr/bin:/opt/bin TERM=qansi LD_LIBRARY_PATH=:/proc/boot:/lib:/usr/lib:/lib/dll:/opt/lib [+session] ksh & } [type=link] /bin/sh=/proc/boot/ksh [type=link] /dev/console=/dev/ser1 [type=link] /tmp=/dev/shmem [perms=+r,+x] libc.so fpemu.so.2 [data=c] devc-serpsc ls ksh pipe pidin uname slogger sloginfo slay mount cp mv hd spatch #/usr/lib/terminfo=/usr/lib/terminfo dumpmem=./dumpmem/dumpmem umount devf-mgt5200=/root/workspace/bsp-mgt5200_devf-mgt5200/mgt5200/ppc/be/devf-mgt5200 flashctl
The os2.build buildfile is given below. Note that this image filesystem is always read-only.
# set search path for files [search=/usr/qnx630/target/qnx6/ppcbe/bin:/usr/qnx630/target/qnx6/ppcbe/sbin:/usr/qnx630/target/qnx6/ppcbe/usr/bin:/usr/qnx630/target/qnx6/ppcbe/usr/sbin] # code=uip means that the code will be run from the image filesystem # (Use In Place). # # +raw means to not strip the programs in any way [data=copy code=uip] [perms=+r,+x] # my files to include # these files will reside directly under the mountpoint as specified # with the mount command [+raw] /raw/hd=hd /unraw/hd=hd devc-ser8250 io-net # these files will be placed /bin/use=use
Unlike the build script for a bootable OS image, the XIP script doesn't have a boot section, nor does it have a boot script. If you run mkifs -v on this buildfile, you'll see that the first entry is the image filesystem header rather than the startup code normally found in a bootable image.
In os1.build, the startup code reserves 4 MB of RAM at a physical address of 0x600000. This will be where the os2 image filesystem will reside. The operating system won't use this memory range for other programs.
You'll need to program the srec files that will be created into flash. The best way to do this with the MGT5200 is to use the dBUG tool and the fp command.
To program os1.srec:
dBUG> set filename /xfer/os1.srec
You can store this setting using the store command.
dn
fp 0 fe040000 fe2fffff 20000
Both the os1.srec and os2.srec files are created with offsets of 0x20000. This allows them to be downloaded in to RAM using the dBUG tool and then programmed. Be sure to include the offset (20000) when you flash program (fp).
Use the same programming technique for os2.srec, but change the start and end address:
fp 0 fe300000 fe3fffff 20000
Note the following about the os1.build file:
In order to make this copy, edit the BSP's bsp-startup-mgt5200/mgt5200/main.c and modify the main() function:
... add_typed_string(_CS_MACHINE, "MGT5200"); /* Load the bootstrap executables in the image filesystem and initialize various syspage pointers. This must be the *last* initialization done before transferring to the next program. */ init_system_private(); /* At this point, copy the second image to RAM: - The source will be 0xFE300000 (physical flash address) - The destination will be 0x600000 (physical RAM location) Make sure the destination is the same physical address XXXXXX that is specified in the 'startup-mgt5200 -r XXXXXX,YYYYYY,Z' in the buildfile. - The size to copy is 0x50000. The size to copy must be at least as large as the os2.ifs file. It is recommended to erase the flash area for os2.ifs so that any extra bytes at the end are zero. This copy must be done after the init_system_private() call. */ copy_memory( 0xFE300000, 0x600000, 0x50000 ); /* Dump the system page if verbose level 3 (-vvv) */ if (debug_flag > 2) { print_syspage(); } return 0;
It's possible to mount multiple image filesystems at the same mountpoint. For example:
mount -tifs -o offset=0x600000,size=0x24000 /dev/mem / mount -tifs -o offset=0xa00000,size=0x31000 /dev/mem /
This mounts two additional images at the root (/) mountpoint. QNX Neutrino supports union filesystems. If there are duplicate paths to the same file, then the last image to mount will be the one that's used.
Here's a testing program called dumpmem.c that you can include in your first boot image, so that you can look at physical memory locations:
#include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <inttypes.h> #include <string.h> #include <sys/neutrino.h> int main( int argc, char **argv ) { char *ptr; size_t len; uint64_t addr; long int ltmp; char c; int i; if ( argc < 3 ) { fprintf(stderr,"enter addr and size\n"); exit(1); } addr = strtoull( argv[1], NULL, 0 ); ltmp = strtol( argv[2], NULL, 0 ); len = ltmp; fprintf(stderr,"Dumping %d (0x%x) bytes at addr 0x%llx\n", len, len, addr ); ThreadCtl( _NTO_TCTL_IO, 0); ptr = mmap_device_memory( 0, len, PROT_READ|PROT_WRITE|PROT_NOCACHE, 0, addr ); if ( ptr == MAP_FAILED ) { perror( "mmap_device_memory for physical address failed" ); exit( EXIT_FAILURE ); } for ( i=0; i < len; i++ ) { c =(*(ptr+i) & 0xff); if ( isprint(c) ) fprintf(stderr, "%c", c ); fprintf(stderr, "[%x] ", c ); if ( ( i % 20 ) == 0 ) fprintf(stderr,"\n"); } }
For example, to print the first 100 bytes at address 0x600000 (to look for the “imagefs” signature), type:
dumpmem 0x600000 100
mkifs, mkimage, and mkrec in the Utilities Reference
Making an OS Image in Building Embedded Systems