ChorusOS 5.0 Board Support Package Developer's Guide

Part II Porting the ChorusOS Operating System to a New Target Board

This part describes the parts of the board support package that must be adapted to your new target architecture. It also describes how to configure and build a ChorusOS system image with a new BSP.

Chapter 3 Writing a New Board Support Package

This chapter describes the source file tree that you need to create for your new BSP. It also shows some examples of the content of the files in this tree, using the template files that are provided in the BSP source add-on package.

Creating a New BSP Source Tree

In order to create a new BSP component for your board you will need to create a new directory tree that contains all the required files. A good starting point for this is to duplicate an existing BSP source tree for a reference board that most closely resembles your board.


Note -

An optional BSP source package can be installed which contains example BSP and driver source files for both reference targets and other boards. There is also a template/ directory that contains the files that can be copied and used as a starting point for implementing a new BSP or device driver (contained in bsp/ and drv/ directories respectively.


Figure 3-1 New BSP Source Tree

Graphic

All reference BSP source trees for reference target boards are contained in the source_dir/nucleus/bsp/family/board directory, as shown in the above diagram.

You can either create your new my_board/ directory within the ChorusOS installed source tree under the appropriate family directory, or elsewhere on your file system. The path to your new BSP component is set up in your build_dir (see configure(1CC)) when you build your ChorusOS system image.

Your new BSP source tree will contain the following (see Figure 3-1):

You may also need to add any drivers that are specific to your board. For more information on this see "Driver Source File Structure").

Building a ChorusOS System Image with a New BSP/DRV Component

A kernonly system image is a minimal system image that contains no POSIX file system or networking layers. It is recommended that when creating a system image for a new board you initially create a kernonly system image, to limit the testing and debugging to the essential microkernel components only. Once this minimal configuration works, you can enable the support of other devices used by the POSIX OS layer and configure and build a standard ChorusOS system image that includes this layer.

To create a new ChorusOS kernonly system image you need to create a build directory and use the configure(1CC) command to prepare the build directory for ChorusOS, as shown below.

% cd build_dir
% configure -b $TOOLS $KERNEL -s $DRV $BSP
% make
% make kernonly

The two components DRV and BSP indicate the paths of your new BSP and you will notice that they are source components (indicated by the -s option). For more detailed information on building a system image with a new BSP component and booting the ChorusOS system image on a new target board see the ChorusOS 5.0 Source Delivery Guide and Part II of the ChorusOS 5.0 Installation Guide.


Note -

It is important to note that when adding new drivers to an existing BSP, the BSP should be considered as a new one. This is because the addition of new drivers to an existing BSP modifies some source files, in particular the initial device tree and the target.xml. (On some platforms the target.xml file may be split into multiple files: drivers.xml, drvlist.xml and target.xml. In this situation the drivers.xml and drvlist.xml files are included in the target.xml file. )


An Example of Some Typical BSP Source Files

This section gives you some more detailed information on the files that you must have in a BSP source tree using template files as examples. The templates/ directory can be found by installing the BSP source package.

The Top Level BSP Directory

For this example, the new BSP directory will be called my_board. This top level directory must contain at least the following four files:

This section contains a short description of these files, showing the template files.


Example 3-1 New BSP: Makefile.bin

COMPONENT += BSP

BSP.all::

BSP_XML = target.xml target_action.xml

xml:: DEVTOOLS.all
   @sh $(DEVTOOLS_DIR)/cpxml $(BUILD_DIR)/conf/mkimage $(BSP) $(BSP_XML)

XML8 += mkimage/mkimage.xml

The COMPONENT macro defines a list of components that will be processed in the build phase. The BSP source component is added to this list. Note that only the name "BSP" is required.

The xml makefile target is used to copy the target.xml and target_action.xml files to the $(BUILD_DIR)/conf/mkimage directory. The mkimage/mkimage.xml file is added to the XML macro. This XML file will be used at the end of the build to generate the image.


Example 3-2 New BSP: Makefile.src

BSP_SRC = $(BSP)/src

all:: BSP.all 

BSP.all:: $(BSP_DIR)/DONE

$(BSP_DIR)/DONE: $(BSP_DIR)/Makefile 
        sh $(DEVTOOLS_DIR)/resync BSP -f $(BSP) -s $(BSP_DIR)
        cd $(BSP_DIR); $(MAKE)
        touch $(BSP_DIR)/DONE

$(BSP_DIR)/Makefile: $(BSP_SRC)/Imakefile
        sh $(DEVTOOLS_DIR)/ChorusOSMkMf $(BUILD_DIR) -s \
           $(BSP_SRC) -b $(BSP_DIR) -d $(BSP_DIR)
        cd $(BSP_DIR); $(MAKE) Makefiles

This makefile defines the BSP.all target used to compile and link the BSP source files.

The ChorusOSMkMf tool is used to create Makefiles from Imakefiles (see ChorusOSMkMf(1CC)for more details).

The BSP_DIR variable is defined as $(BUILD_DIR)/build-BSP. This directory will be used as the build directory and the binary delivery directory.

The example target.xml file shown in section "The target.xml File Structure", specifies the target specific elements needed to produce a system image. This file contains the Embedded Component Markup Language (ECML) which is used to describe the configuration of the BSP as well as other target specific configuration in ChorusOS. For more information on ECML see "ECML Syntax" in ChorusOS 5.0 Source Delivery Guide.

This file must be adapted for your particular board.

The pathname ${BSP_DIR}/bin/my_board is used to reference a number of BSP binaries. The my_board name can be replaced by an explicit reference to your board. In this case, remember to change this path in the Project.tmpl file, detailed in Example 3-4.


Example 3-3 New BSP: target_action.xml

<!DOCTYPE folder PUBLIC "-//Sun Microsystems//DTD \
ChorusOS//EN" "ChorusOS.dtd">

<folder name='MYBOARD specific actions' visible='no'>
  <description>MYBOARD system image action</description>

  <action name='Archive renaming'>
    <application>shellCommand</application>
    <definition name='shellCommand_cp'>
      <type name='ShellCommand' />
      <value field='command'>
        <vstring>cp</vstring>
      </value>
      <value field='argument'>
        <value index='size'>
          <vstring>${IMAGE_DIR}/bank/sys_bank</vstring>
        </value>
        <value index='size'>
          <vstring>${RESULT}</vstring>
        </value>
      </value>
    </definition>
  </action>

</folder>

The target_action.xml file, detailed above, copies the sys_bank file into a file defined by the RESULT variable. This variable is defined as ${BUILD_DIR}/${SYSTEM}, where ${SYSTEM} is the name of the image to be produced. Note that multiple banks can be combined, and specific binary format headers can be included, to produce a more complex system image.

The src Directory

This src directory must contain the following two files:

It also contains the following directories:

The Project.tmpl file is used to produce a makefile to build your new BSP component (see ChorusOSMkMf(1CC)).


Example 3-4 New BSP: Project.tmpl

#include "Package.rules"

SRC_DIR         = SourceDir
BUILD_DIR       = BuildDir
DIST_DIR        = DistDir

VPATH           = $(SRC_DIR)$(REL_DIR)

WARN            = $(WARN_ON)

MYBSP_DIST_BIN  = $(DIST_DIR)/bin/mybsp

DRV_DIST_BIN    = $(DRV_DIR)/bin/drv

INCLUDES        = -I$(NUCLEUS_DIR)/include/chorus \
                  -I$(NUCLEUS_DIR)/include/stdc   \
                  -I$(DRV_DIR)/include/chorus \
                  -I$(DIST_DIR)/include/chorus \
                  -I$(SRC_DIR)$(REL_DIR)

BSP_LIBS        = $(NUCLEUS_DIR)/lib/boot_tools/boot_tools.s.a \
                  $(NUCLEUS_DIR)/lib/util/util.s.a \
                  $(NUCLEUS_DIR)/lib/bki/bki.s.a \
                  $(NUCLEUS_DIR)/lib/cpu/cpu.s.a \
                  $(NUCLEUS_DIR)/lib/stdc/stdc.s.a \
                  $(NUCLEUS_DIR)/lib/boot_tools/sys/sys.s.a 

The MYBSP_DIST_BIN variable defines the directory which programs will be exported to. The name mybsp can be changed to the real board name. The DIST_DIR variable is defined by the path given with the -d option of ChorusOSMkMf. This path is the same as BSP_DIR: $BUILD_DIR/build-BSP. The DRV_DIST_BIN variable will be used to reference objects exported by the DRV component.


Example 3-5 New BSP: src-level Imakefile

#define IHaveSubdirs

SUBDIRS = boot dbg reboot

This Imakefile lists all of the sub-directories for each BSP class and should not be edited.

The boot Directory

This is the directory where the source files for the boot program reside:

This section contains a short description of the boot files, showing examples from the template directory.


Example 3-6 New BSP: Imakefile

C__SRCS = boot.c

AS_SRCS =

OBJS = $(C__SRCS:.c=.o) $(AS_SRCS:.s=.o)
BspProgTarget(boot, start, $(OBJS), $(BSP_LIBS))

DistProgram(boot, $(MYBSP_DIST_BIN)$(REL_DIR))

Depend($(C__SRCS) $(AS_SRCS))

The BspProgTarget macro is used to produce the boot relocatable binary file. The final link will be done by the mkimage tool. The first argument is the name of the binary. The second argument is the entry point, the third is the list of objects and the fourth is the list of libraries used in the link process.

The DistProgram macro copies the file given by the first argument to the directory specified by the second argument. Therefore, in the example above, the boot binary file will be copied in to the $BSP_DIR/bin/my_board/boot directory.


Example 3-7 New BSP: boot.c

void
start()
{
    /* Code for your BSP boot */
}

This file needs to be adapted to your board.

The dbg Directory

The dbg directory contains the platform-dependant code for the debug agent, and some driver code that is necessary for debugging your BSP (that is the console and watchdog drivers). There are four files located at this top level:


Example 3-8 New BSP: dbg Imakefile

C__SRCS = dbgBsp.c ns16550Bsp.c

AS_SRCS =

OBJS = $(C__SRCS:.c=.o) $(AS_SRCS:.s=.o)

DRIVERS = \
        $(DRV_DIST_BIN)/dbg/ns16550.o

BspProgTarget(dbgBsp, dbgBsp_init, $(OBJS), $(DRIVERS) $(BSP_LIBS))

DistProgram(dbgBsp, $(MYBSP_DIST_BIN)$(REL_DIR))

Depend($(C__SRCS) $(AS_SRCS))

Note that dbgBsp relocatable binary file is linked with the ns16550.o object, which is located in the DRV component.


Example 3-9 New BSP: dbgBsp.c

#include <bki/bki.h>
#include <bki/dbgBsp.h>

#include <drv/dbg/dbgUart.h>
#include <drv/dbg/dbgWdt.h>

DbgBsp dbgBsp = {
    dbgBsp_initDrivers,
    dbgUart_ioctl,
    dbgUart_mayGet,
    dbgUart_getChar,
    dbgUart_mayPut,
    dbgUart_putChar,
    dbgBsp_ioRemap,
    dbgBsp_boardReset,
    dbgWdt_setStoppedFlag
};

unsigned int hostStopped = 0;

    void
dbgBsp_init (BootConf* conf)
{
        /* debug BSP specific initialization code */

    conf->dbgBsp = dbgBsp;
}

    static void
dbgBsp_initDrivers (int port, struct DbgBsp_sgtty* stty)
{
        /* code for UART and WDT intialization */
}

    static void
dbg_boardReset (int mode)
{
        /* code for your board reset */
}

    static void
dbgBsp_ioRemap (int flags, void* base)
{
    if (flags & DKI_IOREMAP_WDT) {
        /* WDT device remap */
    }
    if (flags & DKI_IOREMAP_UART) {
        /* UART device remap */
    }
}

    static void
dbgWdt_setStoppedFlag (unsigned char v)
{
    hostStopped += (v ? 1 : (hostStopped ? -1 : 0));
}

Note that dbgBsp.c needs to be adapted for your board.


Example 3-10 New BSP: ns16550Bsp.c

#include <drv/dbg/ns16550Bsp.h>

    void
ns16550_init(int port)
{
    /* Code for your board */
}

    void
ns16550_outb(unsigned int reg, unsigned char val)
{
    /* Code for your board */
}

    unsigned char
ns16550_inb(unsigned int reg)
{
    unsigned char c;
    /* Code for your board */
    return c;
}

    unsigned short
ns16550_divisor(unsigned int baud)
{
    unsigned short divisor;
    /* Code for your board */
    return divisor;
}

    void
ns16550_ioremap(void* newbase)
{
    /* Code for your board */
}

Note that the example above is for an ns16550 UART. The file will be need to be adapted for your board.

The reboot Directory

The reboot directory contains the platform dependant code for the reboot part of the board support package. This directory is optional, and in fact it is possible to include the reboot related code in the boot directory. If you implement reboot in a separate reboot directory the following two files are located at the top level:

Chapter 4 The ChorusOS Boot Program

This chapter describes how to write a bootstrap for your system. The bootstrap is implemented as three binaries:

Overview

In Figure 4-1 you will see a description of the boot process showing the system image and the binary files that it consists of and the flow of control between them.

Figure 4-1 Boot Process

Graphic

The power-up program is required when the operating system boots from the ROM. Otherwise, power-up inititializations are performed by the initial loader (firmware or ChorusOS bootMonitor).

The bootconf binary gets control from the power-up initialization program (or from the initial loader) and launches the bootstrap program. The bootstrap program launches the debug agent, the reboot program and the microkernel, according to the boot-kernel interface (BKI) specification (see "The Boot-Kernel Interface" for details).

The right part of the figure shows the BootConf data structure that is initialized by the bootconf part of the boot program and used to pass information to other binary programs in the system image.

The only jump between programs in the boot process is between the boot program and the microkernel.

Power-up Program Implementation

This section describes the power-up initialization program, including practical examples.

Example 4-1 is an example of a power-up initialization program for the EST SBC8260 board. The source is provided in source_dir/nucleus/bsp/powerpc/sbc8260/src/boot/trampoline.s.


Example 4-1 Power-up Program

        .text

           .globl  reset

reset:
        /*
         * Put power up initialization code here
         */   
        
        /*
         * Jump to bootconf entry point
         */
        xor        r3, r3, r3
        addis      r5, r0, entryPoint@ha        /*                      */
        addi       r5, r5, entryPoint@l         /*                      */
        lwz        r5, 0(r5)                    /* r5 = entryPoint      */
        mtlr       r5                           /*                      */
        blr                                     /* (*entryPoint)();     */

        .globl  __mkimage__label__REF_start
__mkimage__label__REF_start:
entryPoint:
        .long    0                              /* bootconf entry point */

At image generation the mkimage utility binds the bootconf program entry point to the power-up initialization program, patching the contents of the location labelled __mkimage__label__REF_start with the address of the entry point location labelled by __mkimage__label__start, as described in "bconf_main.c Source File".

This power-up program forces the board-specific (second) argument of the bootconf program to 0, using the argument passing convention described in "bootconf Binary".

bootconf Implementation

This section describes the bootconf program implementation provided in ChorusOS.

The main role of bootconf is to hold the BootConf structure described in "BootConf Structure". The structure is generated by the mkimage tool using the ChorusOS configuration file, as described in Chapter 7, Configuring the ChorusOS System Image. mkimage then links the generated BootConf structure with the program described in this section.

The role of the bootconf code is to install the standalone binaries (such as the bootstrap program, debug agents and debug drivers) and to transfer control to the bootstrap program, passing a pointer to the BootConf structure as an argument.

The bootconf implementation assumes that the memory bank containing the bootconf binary has already been installed at the bank's starting address by the initial loader.

The bootconf code is board-independent; it can be used on any board based on a given processor family. Examples in this section are for the PowerPC family of boards. The implementations for other target families have similar logic and structure.

The bootconf implementation is contained in two source files, bconf_crt0.s and bconf_main.c.

bconf_crt0.s Source File

The source_dir/nucleus/bsp/src/family/powerpc/boot_tools/bconf_crt0.s file contains the bootconf start-up routine, bconf_start().


Example 4-2 bconf_start()

        .section        .chr.my.rw.vaddr
rw_vaddr:       
        .section        .chr.my.rw.kaddr
rw_kaddr:       
        .section        .chr.my.rw.size
rw_size:        
        
        .section        .chr.my.bss.vaddr
bss_vaddr:      
        .section        .chr.my.bss.size
bss_size:       

        .section        .text
                
        GLOBAL(bconf_start)
bconf_start:
        GLOBAL(__mkimage__label__start)
__mkimage__label__start:
        LoadAddr(r5,bconf_main)         /* bconf_main(r3) */
        b       .L1


        GLOBAL(bconf_reboot_start)
bconf_reboot_start:
        GLOBAL(__mkimage__label__reboot)
__mkimage__label__reboot:
        LoadAddr(r5,bconf_reboot_main)  /* bconf_reboot_main(r3, r4) */
        b       .L1


.L1:    
        /*
         * Copy read/write segment if necessary
         *
         * (note that the segment can include the code that
         * we are executing)
         */

        LoadAddr(r8,  rw_kaddr)         /* r8 = src_addr */
        LoadAddr(r9,  rw_vaddr)         /* r9 = dst_addr */ 
        LoadAddr(r10, rw_size)          /* r10 = size    */
                
        cmpwi   r10,0                   /* if (size)                  */
        beq     .L3                     /*                            */
        cmpw    r8, r9                  /*   if (dst_addr != src_addr)  */
        beq     .L3                     /*                            */
.L2:                                    /*     do {                   */
        lbz     r0,0(r8)                /*       r0 = *src_addr       */
        stb     r0,0(r9)                /*       *dst_addr = r0       */
        addi    r8,r8,1                 /*       ++src_addrq          */
        addi    r9,r9,1                 /*       ++dst_addr           */
        subic.  r10,r10,1               /*       --size               */
        bne     .L2                     /*     } while (size)         */
.L3:                                    /*                            */
        /*
         * Zero bss segment if necessary
         */

        LoadAddr(r9,  bss_vaddr)        /* r9 = dst_addr              */
        LoadAddr(r10, bss_size)         /* r10 = size                 */

        cmpwi   r10,0                   /* if (size)                  */
        beq     .L5                     /*                            */
        li      r0,0                    /*   r0 = 0                   */
.L4:                                    /*   do {                     */
        stb     r0,0(r9)                /*     *dst_addr = 0          */
        addi    r9,r9,1                 /*     ++dst_addr             */
        subic.  r10,r10,1               /*     --size                 */
        bne     .L4                     /*   } while (size)           */
.L5:
        /*
         * Setup the stack
         */
stack_setup:

        LoadAddr(r1, heap)              /* sp = heap[]        */
        LoadAddr(r9, heapSize)          /*                    */
        lwz     r9, 0(r9)               /*                    */
        add     r1, r1, r9              /* sp = sp + heapSize */

        /*
         * Jump to the main
         *
         * (note that if the code was also copied we must jump into 
         * the copy)
         */
jump_to_main:

        mtlr    r5
        blr

The bootconf program defines two entry points bconf_start() and bconf_reboot_start(). The power-up initialization program enters using bconf_start() for a cold boot, whereas the reboot program enters using bconf_reboot_start() in the hot reboot path.

The install_me() routine tests whether the read-write segment and bss segment are link-edited in place (that is, at their places within the memory bank). To obtain the segment's layout within its memory bank and link-editing address, bconf_start() refers to predefined sections that mkimage relocated as follows:

If necessary, install_me() copies the read-write segment and initializes the bss segment to zero. Note that the bootconf code can be part of the read-write segment.

The stack_setup() routine initializes the stack pointer with the limit of the heap. The heap location and size are defined by heapStart and heapLimit (see "Heap and Stack").

The jump_to_main() routine jumps to the bconf_main() routine. If the bootconf code is part of the previously copied read-write segment, this jump transfers control from the original to the copy. The bconf_start program passes the content of register r3 as an opaque argument to bconf_main(). Note that this jump works without calling sync/isync instructions as caching is disabled at this time.

The __mkimage_label_start label enables the power-up program to transfer control to the bootconf binary, as described in Example 4-1.

Note also that bconf_crt0.s preserves the value of r3 and r4 to allow callers to pass, at most, two arguments to the subsequent programs. For example, the initial loader can pass one board-dependent argument to the bootstrap program in r3, whereas the reboot program can pass two arguments (see "Hot Reboot").

bconf_main.c Source File

The source_dir/nucleus/bsp/src/boot_tools/bconf_main.c file contains the bootconf main routine, bconf_main().


Example 4-3 bconf_main()

BootConfRebootEntry __mkimage__label__REF_reboot = 0;

    void
bconf_main(void* cookie)
{
    int i;

    /*
     * Register hot reboot entry
     */
    bootConf->rebootEntry = __mkimage__label__REF_reboot;

    /*
     * Install all standalone sections
     */
    binInstallByMask(BIN_STANDALONE);

    /*
     * Launch bootstrap program
     */
    for (i = 0; i < bootConf->numBins; ++i) {
        BinDesc* bin = &bootConf->binDesc[i];
        if (bin->type == BIN_BOOTSTRAP) {
            (* (BootstrapEntry) bin->entry)(bootConf, cookie);
        }
    }
}

First of all, the bconf_main() routine registers the bootconf hot reboot entry point in the BootConf structure. Note that the registered address must be a reference to the bootconf image. This address can be different from the entry point's link-edit address if the bootconf text segment is not XIP. To provide the reference, the mkimage tool stores the system image address of the location __mkimage__label__reboot in the data location __mkimage__label__REF_reboot.

The bconf_main() routine calls binInstallByMask() to copy (if necessary), the read-write segments of all standalone binaries and initializes their bss segments to zero. Calling binInstallByMask() at this point, bconf_main() assumes that any memory banks containing standalone binaries have already been installed at the required address. See "binInstallByMask() and binInstallByType()" for more information.

The binInstallByMask() function is implemented by the bootstrap framework library (see "Common Bootstrap Implementation Framework").

Finally, the bconf_main() function retrieves the descriptor that points to the bootstrap program binary in the BootConf structure, and jumps to its entry point. It passes as arguments the address of the BootConf structure and an opaque value from the initial loader.

The source_dir/nucleus/bsp/src/boot_tools/bconf_main.c file also contains the second main routine, bconf_reboot_main(), which is used for performing a hot reboot:


Example 4-4 bconf_reboot_main()

     
    void 
bconf_reboot_main(RebootDesc* rd, void* cookie)
{
		if (rd) {
			/*
			 * Restore the pointer to the persistent memory description
			 */
      bootConf->rebootDesc = rd;
		}

			/*
		 	 * Join the cold bootstrap path
		 	*/
		bconf_main(cookie);
}

bconf_reboot_main() registers a reference to the system state, preserved by the hot reboot (see "HotRebootDesc Structure" for details) in the BootConf structure, and then joins the cold reboot pass.

Bootstrap Program Implementation

This section includes information on the common bootstrap implementation framework and information on target specific bootstrap implementation framework.

A typical bootstrap program does the following:

Example 4-5 is the bootstrap program provided for the SBC8260 board. The source code is provided in source_dir/nucleus/bsp/powerpc/sbc8260/src/boot/boot.c.


Example 4-5 Bootstrap Program

BootCBootConf* bootConf;

    void
start(BootConf* conf, BootParams* bootp)
{
    bootConf = conf;

    /*
     * PowerPC 60x specific initializations
     */
    ppc60x_init();
        
    /*
     * Start debug agent
     */
    dbg_start(bootp);

    /*
     * Console IO is now available 
     */
    printf ("Booting ChorusOS ...");

    if (bootp) {
        /*
         * Check that BootParams doesn't overlap with already installed 
         *  system segments
         */
        int seg_nb;
        BinDesc* bin = 
            binCheckOverlap((VmAddr) bootp, sizeof(*bootp), 
                                              BIN_STANDALONE, &seg_nb);
            if (bin) {
            printf("Address range from 0x%x to 0x%x was used by the 
                                                             loader\n",
                   bootp, bootp + 1);
            printf("It overlaps with the segment #%d of '%s'\n", 
                   seg_nb - bin->firstSeg, bin->name);
            printf("Change the corresponding linging area dimensions "
                   "  to avoid the conflict\n");
            ASSERT(0);

        }
        bootConf->siteNumber = bootp->ipcSiteNb;
    }

    /*
     * Tag as free all available RAM
     */
    ram_tag(&bootConf->ramAllocator, 0, ramSize(), PH_RAM_FREE);

    /*
     * Start reboot program
     */
    reboot_start(bootp);

    /*
     * Allocate persistent memory
     */
    prstInstall();

    /*
     *  Build device tree
     */
    bootConf->rootDevice = buildDeviceTree(conf);

    /*
     * Jump to the u-kernel
     */
    kernel_start();

    /*
     * Never returns
     */
}

The start() function is the bootstrap program entry point. It gets control from the bootconf program. The bootp argument is a pointer to a board-dependent structure. It is passed to boot.c, as a cookie, through the bootconf program, from either the power-up initialization program or the reboot program.

For the SBC8260 power-up initialization program, the value of bootp is NULL. However, for the reboot program (see Example 5-2) , bootp points to a data structure containing the ChorusOS IPC site number value:

typedef struct BootParams {
		uint32_f		ipcSiteNb;
} BootParams;

As the BootParam structure can be allocated by another instance of the ChorusOS operating system (for example, bootMonitor) the bootstrap program checks that the structure had not already been corrupted by the system currently booting.

The ppc60x_init() function is the PowerPC 60x initialization program, and is described in "PowerPC 60x Bootstrap Implementation Framework".

The binCheckOverlap(), dbg_start(), ram_tag(), reboot_start(), prstInstall()and kernel_start() functions are target-independent routines, and are described in "Common Bootstrap Implementation Framework".

The buildDeviceTree() function is board-specific and builds the initial state of the SBC8260 device tree (see "Initial Device Tree").


Common Bootstrap Implementation Framework

This section describes the common routines used in the bootstrap implementation.

binInstallByMask() and binInstallByType()

Example 4-6 is an example of binInstallByMask() and binInstallByType() source code, provided in source_dir/nucleus/bsp/src/boot_tools/binInstall.c.


Example 4-6 binInstallByMask() and binInstallByType()

    void
binInstallOne(BinDesc* bin)
{
    int j;
    for (j = bin->firstSeg; j <= bin->lastSeg; j++) {
        BinSegDesc* seg = &bootConf->segDesc[j];               
        if (seg->space == SEG_KSP) {
            if ((VmAddr) seg->kaddr != seg->vaddr) {
                /*
                 * Move segment to its base address 
                 */
                bcopy((void*) seg->kaddr, (void*) seg->vaddr, seg->ksize);
            }
            if (seg->ksize < seg->vsize) {
                /*
                 *  Zero segments's bss
                 */
                bzero((void*) (seg->vaddr + seg->ksize), 
                      seg->vsize - seg->ksize);
            }
        }
    }
}

    void
binInstallByMask(BinType mask)
{
    int i;
    for (i = 0; i < bootConf->numBins; ++i) {
        BinDesc* bin =  &bootConf->binDesc[i];
        if (bin->type & mask) {
            binInstallOne(bin);
        }
    }
}

    void
binInstallByType(BinType type)
{
    int i;
    for (i = 0; i < bootConf->numBins; ++i) {
        BinDesc* bin =  &bootConf->binDesc[i];
        if (bin->type == type) {
            binInstallOne(bin);
        }
    }
}

The binInstallByMask() function installs binaries specified by the mask argument. The mask can be any combination of the bits listed in Table 4-1.

Table 4-1 mask Bits
mask Bit Meaning
 BIN_STANDALONE install bootstrap program and debug agent binaries
 BIN_KERNEL install microkernel
 BIN_ACTOR install built-in drivers and actors

The binInstallByMask() function installs all binaries with type values that match at least one bit set in the mask argument. See "Binaries" for more information.

The binInstallByType() function installs all binaries of one particular type.

To install a binary, the binInstallOne() internal function is used. It copies, if necessary, the binary segments from the memory bank to RAM and initializes the bss segment. binInstallOne() only processes segments that belong to the initial microkernel address space, SEG_KSP.

The binInstallOne() function assumes that the memory banks containing the installing binaries have already been installed and that the destination addresses are already accessible.

dbg_start()

The dbg_start() function starts the debug agent drivers and the debug agent (see "The Debug Agent"). Example 4-7 is dbg_start() source code provided in source_dir/nucleus/bsp/src/boot_tools/dbg_start.c.


Example 4-7 dbg_start()

    void
dbg_start(void* cookie)
{
    int i;

    /*
     * Launch debuging console driver
     */
    for (i = 0; i < bootConf->numBins; ++i) {
        BinDesc* bin = &bootConf->binDesc[i];
        if (bin->type == BIN_DBG_DRIVER) {
            (* (DbgDriverEntry) bin->entry)(bootConf, cookie);
        }
    }

    /*
     * Launch debuging agent
     */
    for (i = 0; i < bootConf->numBins; ++i) {
        BinDesc* bin = &bootConf->binDesc[i];
        if (bin->type == BIN_DBG_AGENT) {
            (* (DbgAgentEntry) bin->entry)(bootConf);
        }
    }

    _stdc_consInit(bootConf->dbgOps.consRead,
                   bootConf->dbgOps.consWrite);
}

The dbg_start() function retrieves the descriptor pointing to the debug agent driver binary from the BootConf structure. It then calls the debug agent driver's entry point, passing it the address of the BootConf structure and the opaque value that came from the initial loader. Once the driver has been started, dbg_start() retrieves the descriptor pointing to the debug agent itself. It calls the debug agent entry point, passing it the address of BootConf.

The dbg_start() function assumes that the debug agent and its driver are already installed.

Finally, dbg_start() makes console I/O available for the bootstrap program by initializing its printf()/scanf() support library.

reboot_start()

The reboot_start() function installs and launches the reboot program. The reboot program is installed only once during the first cold boot. It maintains a section of the system state that must be kept over subsequent hot reboots. The reboot_start() function differentiates between a cold boot and a hot boot by testing the value of the rebootDesc field in the BootConf structure:

The reboot_start() function calls binInstallByType() (see "binInstallByMask() and binInstallByType()") to install the reboot program. It then retrieves the descriptor that points to the reboot program binary and jumps to its entry point, passing the address of the BootConf structure as an argument.

The reboot_start() function assumes that the memory banks containing the reboot program have already been installed and that the destination addresses are already accessible.

Code Example 4-8 is reboot_start() source code provided in source_dir/nucleus/bsp/src/boot_tools/reboot_start.c.


Example 4-8 reboot_start()

    void
reboot_start(void* cookie)
{
    int i;

    if (bootConf->rebootDesc == 0) {
        /*
         * This is a cold reboot. Install reboot program
         */
        binInstallByType(BIN_REBOOT);
    }

    for (i = 0; i < bootConf->numBins; ++i) {
        BinDesc* bin = &bootConf->binDesc[i];
        if (bin->type == BIN_REBOOT) {
            (* (RebootEntry) bin->entry)(bootConf, cookie);
        }
    }
}

prstInstall()

The prstInstall() function is used by the bootstrap program to check whether:

Typically, during a cold boot, prstInstall() does nothing because there are no persistent memory devices occupying or requesting RAM blocks. During the first hot boot, creation of the first persistent memory device can be required. Subsequently, during the second hot boot, the first memory device would already be registered and another memory device can request RAM block allocation.

Code Example 4-9 is prstInstall() source code provided in source_dir/nucleus/bsp/src/boot_tools/prstInstall.c.


Example 4-9 prstInstall()

    void
prstInstall()
{
    int i;

    RamDesc* ram = &bootConf->ramAllocator;
    RebootDesc* rd = bootConf->rebootDesc;

    /*
     * Tag existing persistent memory as allocated
     */
    for (i = 0; i < rd->hot.prstMem.numChunks; ++i) {
        PrstChunk* chunk = &rd->hot.prstMem.chunks[i];
        ASSERT(CEILING2(chunk->size, PAGE_SIZE_f) == chunk->size);
        if (chunk->status & PRST_CHUNK_ALLOCATED) {
            ram_tag(ram, chunk->paddr, (PhSize) chunk->size,
              PH_RAM_ALLOCATED);
        }
    }

    /*
     * Extend persistent memory
     */
    for (i = 0; i < rd->hot.prstMem.numChunks; ++i) {
        PrstChunk* chunk = &rd->hot.prstMem.chunks[i];
        ASSERT(CEILING2(chunk->size, PAGE_SIZE_f) == chunk->size);
        if (!(chunk->status & PRST_CHUNK_ALLOCATED)) {
            if (!ram_alloc(ram, &chunk->paddr, (PhSize) chunk->size, 
                           PAGE_SIZE_f)) {
                printf ("can't allocate 0x%x bytes of persistent memory\n", 
                        chunk->size);
                ASSERT(0);
            } else {
                chunk->status |= PRST_CHUNK_ALLOCATED;
            }
        }
    }
}

kernel_start()

The kernel_start() function calls binInstallByMask() (see "binInstallByMask() and binInstallByType()") to install the microkernel and all actors that belong to the initial microkernel address space. It then retrieves the descriptor pointing to the microkernel binary and jumps to the microkernel entry point, passing the address of the BootConf structure to the microkernel as an argument.

The kernel_start() function also informs the system debugger that the microkernel was installed in the dedicated memory. From this moment, the debugger has access to the microkernel memory, in order to set breakpoints, for example.

The kernel_start() function assumes that the memory banks containing the microkernel and the installing actors have already been installed and that the destination addresses are already accessible.

Code Example 4-10 is kernel_start() source code provided in source_dir/nucleus/bsp/src/boot_tools/kernel_start.c.


Example 4-10 kernel_start()

void
kernel_start()
{
    int i;

    /*
     * Install all u-kernel's and actor's KSP sections
     */
    binInstallByMask(BIN_KERNEL | BIN_ACTOR);

    /*
     * Report to the debug agent that the kernel was installed in the memory
     * dedicated for it.
     */
    bootConf->dbgOps.kernelInitLevel(KERNEL_INSTALLED);

    /*
     * Jump to the u-kernel
     */
    for (i = 0; i < bootConf->numBins; ++i) {
        BinDesc* bin = &bootConf->binDesc[i];
        if (bin->type == BIN_KERNEL) {
            (* (KernelEntry) bin->entry)(bootConf);
        }
    }
}

RAM Allocator interface

The RAM Allocator interface is used to allocate and free RAM. During ChorusOS boot and initialization, the RAM occupation is described by a RamDesc object. The ramAllocator field in the BootConf structure points to the RamDesc object. The ramAllocator field is initialized by the mkimage tool as described in "RAM Occupation".

RAM occupation is described by a particular tag value associated with each address of the physical address space. The tag can have one of the following values:

Three routines are provided for managing RamDesc. These routines can be used by the bootstrap program to update RamDesc according to the target BKI.

PowerPC 60x Bootstrap Implementation Framework

ppc60x_init()

The ppc60x_init() function initializes the CPU for the PowerPC BKI (see "PowerPC BKI"). This routine applies to PowerPC 60x, 750, and MPC8260 processors. To put the CPU in an appropriate state for the PowerPC 60x BKI, the routine goes through the following steps:

  1. Clears all bits in the MSR register, except the ME and RI bits which are set:

    • disables external interrupts (MSR[EE])

    • disables instruction and data translations (MSR[IR], MSR[DR])

    • disables FPU instructions (MSR[FP])

    • puts the processor into a Supervisor privilege state (MSR[PR])

    • enables machine-check exceptions (MSR[ME])

    • puts the processor into a recoverable exception state (MSR[RI])

  2. Clears and invalidates all Segment Registers (SR) , including all Translation Lookaside Buffer (TLB) entries and Block Address Translation (BAT) registers. Also, resets, disables and/or invalidates all MMU registers.

  3. Invalidates all L1 data cache, for both L1 instruction and data caches and disables internal (L1) memory caches.

In addition, if the CPU is a MCP750 processor, then the L2 cache is also disabled as it is directly interfaced with the CPU.


Example 4-11 ppc60x_init()

#define MPC_750 8

        .text
        
        GLOBAL(ppc60x_init)
ppc60x_init:

        /*
         * Reset MSR register
         */
        li      r9, 0x0
        ori     r9, r9, MSR_ME | MSR_RI
        mtmsr   r9      
        isync
                
        /* 
         * Invalidate all segment registers 
         */
        lis     r9,   0x0
        mtsr    sr0,  r9
        mtsr    sr1,  r9
        mtsr    sr2,  r9
        mtsr    sr3,  r9
        mtsr    sr4,  r9
        mtsr    sr5,  r9
        mtsr    sr6,  r9
        mtsr    sr7,  r9
        mtsr    sr8,  r9
        mtsr    sr9,  r9
        mtsr    sr10, r9
        mtsr    sr11, r9
        mtsr    sr12, r9
        mtsr    sr13, r9
        mtsr    sr14, r9
        mtsr    sr15, r9

        /* 
         * Invalidate all TLB entries
         *
         * Implementation note:
         *
         *   as tlbia in not implemented on 604 nor on MPC750 processors,
         *   we use tlbie in a loop for the 64*4 entries (on 604e)
         *   followed by tlbsync
         */
        mfctr   r9
        li      r7, 64
        mtctr   r7
        li      r7, 0
loop:
        tlbie   r7
        addi    r7, r7, 0x1000
        bdnz    loop
        tlbsync
        mtctr   r9

        /* 
         * Reset BAT registers.
         *
         * Implementation note:
         *
         *   The 604 BAT registers are not initialized by the hardware
         *   after the power-up or reset sequence. Consequently, all valid
         *   bits in both instruction and data BAT areas must be cleared
         *   before setting any BAT area for the first time.
         *   This is true regardless of wether address translation is
         *   enabled. Also, software must avoid overlapping blocks while
         *   updating a BAT area or areas. Even if translation is
         *                               ^^^^^^^^^^^^^^^^^^^^^^
         *   disabled, multiple BAT area hits are treated as programming
         *   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         *   errors and can corrupt the BAT registers and produce
         *              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         *   unpredictable results.
         *
         *   from: "PowerPC 604 User's manual" 
         *         (Chapter 5: Memory management, p 5-13)
         *
         *   Note that this true for MPC750 too.
         */
        li      r9,     0
        mtspr   dbat0u, r9
        mtspr   dbat0l, r9
        mtspr   dbat1u, r9
        mtspr   dbat1l, r9
        mtspr   dbat2u, r9
        mtspr   dbat2l, r9
        mtspr   dbat3u, r9
        mtspr   dbat3l, r9
        mtspr   ibat0u, r9
        mtspr   ibat0l, r9
        mtspr   ibat1u, r9
        mtspr   ibat1l, r9
        mtspr   ibat2u, r9
        mtspr   ibat2l, r9
        mtspr   ibat3u, r9
        mtspr   ibat3l, r9
        isync

        /*
         * Disable L1 instruction and data caches
         */
        mfspr   r9, hid0
        andi.   r10, r9, (HID0_ICACHE_ENABLE | HID0_DCACHE_ENABLE)
        beq     L1Disabled            /* if disabled, nothing to do */
                /*
                 * flush L1 cache
                 */
        li      r3, 1024              /* 1024 blocks of 32 bytes*/
loop_load:
        lwz     r6, 0(r3)             /* load block       */
        addi    r3, r3, 32            /* go to next block */
        bdnz    loop_load
        li      r3, 1024              /* 1024 blocks of 32 bytes*/
        mtctr   r3
        li      r3, 0        
        mtctr   r3,
        li      r3, 0
loop_flush:
        dcbf    r0, r3
        addi    r3, r3, 32             /* cache line size */
        bdnz    loop_flush
                /*
                 * disable L1 cache
                 */
        rlwinm  r9, r9, 0, HID0_dce + 1, HID0_ice - 1
                                        /* Clear HID0[ICE,DCE] bits */
        isync
        mtspr   hid0, r9
        isync
L1Disabled:

        /*
         * Disable MPC750 L2 cache
         */
        mfspr   r9, pvr
        srwi    r9, r9, 16              /* only interested in version */
        cmpwi   r9, MPC_750             /* if not 750, nothing to do  */
        bne     L2Disabled              

        mfspr   r9, l2cr                
        andis.  r10, r9, HIWORDA(L2CR_ENABLE)
        beq     L2Disabled              /* if disabled, nothing to do */
                                
        rlwinm  r10, r9, 0, L2CR_l2e + 1, 31
                                        /* clear L2CR[L2E] bit  */
        sync                    
        mtspr   l2cr, r10
        sync                    
        isync
L2Disabled:

        blr

MPC8xx PowerPC Bootstrap Implementation Framework

The _start() routine initializes the CPU to the state required by the PowerPC BKI (see "PowerPC BKI"). Code Example 4-12 applies to the PowerPC MPC8xx microcontroller family. To put the CPU in the state required by the MPC8xx PowerPC BKI, the routine goes through the following steps:

  1. Clears all bits in the MSR register except the ME and RI bits, which are set, and:

    • Disables all external interrupts (MSR[EE])

    • Disables all instruction and data translations (MSR[IR], MSR[DR])

    • Disables FPU instructions (MSR[FP])

    • Puts the processor into Supervisor privilege state (MSR[PR])

    • Enables machine-check exceptions (MSR[ME])

    • Puts the processor in a recoverable exception state (MSR[RI])

  2. Resets instruction and data MMU control registers to clear any Translation Lookaside Buffer (TLB) reservation. Invalidates the TLB entries and resets, disables and/or invalidates all MMU registers.

  3. The L1 data cache is assumed to be disabled.


Example 4-12 start()

_start:
                               /* Setup init value for MSR */
                addi    r9, r0, (MSR_ME+MSR_RI)
                mtmsr   r9
                               /* Clear reservation of TLB entries */
                lis     r9, 0x0
                mtspr   mi_ctr, r9
                mtspr   md_ctr, r9
                mtspr   m_casid, r9
                mtspr   md_ap, r9
                tlbia          /* invalidate all TLB entries */

                               /* setup stack for boot */
                LoadAddr(r1, stackPtr)  
                bl      start
        loop:
                b       loop   /* Never here */

The Boot-Kernel Interface

This section describes the Boot-Kernel interface, including both common and target specific BKI.

The Boot-Kernel interface (BKI) is a set of rules used when the kernel is launched. Part of the BKI is common to all target families, and part of it is specific to a given family. A family-specific BKI can define a number of dialects. The microkernel binary must be configured to understand the dialect required by the bootstrap program.

Common BKI

The bootstrap program must transfer control to the microkernel entry point, passing, in a register, a pointer to an object of type BootConf (see "BootConf Structure"). The register name or number is defined by the family-specific BKI. The BootConf structure and all objects that it points to must be accessible for both reading and writing.

The microkernel code assumes that all text and data are already at the correct execution addresses, that the data is writable, and that non-initialized data is already set to zero.

All interrupts must be disabled at the CPU level before the bootstrap program transfers control to the microkernel. The microkernel assumes that all available memory caches that can be disabled are already disabled and have been properly flushed.

The microkernel does not expect a valid program stack from the bootstrap program.

The BootConf structure must be in the following state:

Initial Device Tree

The device tree is a data structure that describes hardware topology and device properties. The hardware topology is specified in terms of parent/child relationships. Each device node has some generic and some specific properties associated with it. A device property is a name/value pair. The property name is a null-terminated ASCII string. The property value is a sequence of bytes specified by a length/address pair.

The initial device tree is built by the bootstrap program using the DKI tree browsing API. See Chapter 9, Driver Kernel Interface Overview for information about the DKI interface and the device tree.

Generic device node properties used by the microkernel are defined in ddi/prop.h, as shown in code Example 4-13.


Example 4-13 Properties Defined in ddi/prop.h

/*
 * Generic properties:
 *
 *    The "byte-order" property specifies the byte ordering on cpu/bus.
 *      name:      PROP_BYTE_ORDER
 *      value:     PropByteOrder
 *      constants: BYTE_ORDER_BIG    - big-endian byte order
 *                 BYTE_ORDER_LITTLE - little-endian byte order
 *
 *    The "clock-freq" property specifies the CPU/bus clock frequency in Hz.
 *      name:      PROP_CLOCK_FREQ
 *      value:     PropClockFreq
 *
 *    The "active" property flags active device nodes, i.e. devices
 *    which are already serviced by drivers.
 *      name:      PROP_ACTIVE
 *      value:     -
 *
 *    The "driver" property specifies the name of driver which should be
 *    binded to the node:
 *      name:      PROP_DRIVER
 *      value:     char[] (NULL terminated ASCII string)
 *    
 *    The "system-tick" property flags a timer device node which
 *    is assigned to the system tick.
 *      name:      PROP_SYSTEM_TICK
 *      value:     -
 *
 *    The "perf-tick" property flags a timer device node which is
 *    assigned to the PERF module.
 *      name:      PROP_PERF_TICK
 *      value:     -
 *
 *    The "prof-tick" property flags a timer device node which is
 *    assigned to the PROF module.
 *      name:      PROP_PROF_TICK
 *      value:     -
 *
 *    The "ram-size" property specifies the amount of available RAM memory
 *      name:      PROP_RAM_SIZE
 *      value:     PhSize
 *
 *    The "family" property specifies the name of the product family
 *      name:      PROP_FAMILY
 *      value:     char[]
 *
 *    The "platform" property specifies the name of the target platform
 *      name:      PROP_PLATFORM
 *      value:     char[]
 *
 *    The "cache-size" property specifies the size of the L2 cache
 *      name:      PROP_CACHE_SIZE
 *      value:     PropCacheSize
 *
 *    The "banks" property specifies the physical memory layout
 *      name:      PROP_BANKS
 *      value:     PhChunk[]
 *
 *    The "timer-freq" property specifies the frequency of a
 *    given timer.
 *      name:      PROP_TIMER_FREQ
 *      value:     PropTimerFreq
 *
 *    The "dbg-link" property flags a device node which is assigned to
 *    the debugging agent.
 *      name:      PROP_DBG_LINK
 *      value:     -
 */

#define PROP_BYTE_ORDER         "byte-order"
#define PROP_CLOCK_FREQ         "clock-freq"
#define PROP_ACTIVE             "active"
#define PROP_DRIVER             "driver"
#define PROP_SYSTEM_TICK        "system-tick"
#define PROP_PERF_TICK          "perf-tick"
#define PROP_PROF_TICK          "prof-tick"
#define PROP_SYSTEM_PIC         "system-pic"
#define PROP_RAM_SIZE           "ram-size"
#define PROP_FAMILY             "family"
#define PROP_PLATFORM           "platform"
#define PROP_CACHE_SIZE         "cache-size"
#define PROP_BANKS              "banks"
#define PROP_TIMER_FREQ         "timer-freq"
#define PROP_DBG_LINK           "dbg-link"
#define PROP_SYSTEM_SPEAKER     "system-speaker"

typedef uint32_f PropByteOrder;

#define BYTE_ORDER_BIG          0x00010203      /* big-endian */
#define BYTE_ORDER_LITTLE       0x03020100      /* little-endian */

typedef uint32_f PropClockFreq;
typedef uint32_f PropTimerFreq;

typedef PhSize   PropRamSize;

typedef uint32_f PropCacheSize;

    /* Values for PROP_NODE property, common to all architectures */
#define NODE_ROOT               "local-bus"
#define NODE_CPU                "cpu"
#define NODE_MEM                "ram"
#define NODE_L2_CACHE           "l2-cache"

Family specific properties are defined in ddi/ddi_f.h.

The microkernel requires that the device tree contains the following device nodes and properties:

PowerPC BKI

This section details the state that the CPU should be in for PowerPC BKI acceptance.


Note -

For historical reasons, the naming convention for PowerPC BKI constants uses PPC60x, instead of PowerPC. The original PowerPC BKI was written for the PPC60x PowerPC BKI, however, all PowerPC processor families (8xx, 8260, 60x) now share the same BKI.


x86 BKI

This section details the state that the CPU should be in for the x86 BKI.

The BootConf structure pointer is pushed onto the stack before calling the microkernel entry point.

Interrupts must be disabled.

The processor must be in protected mode, and all the segments must cover the entire address space.

Translation must be disabled.

UltraSPARC IIi BKI

This section details the state that the CPU should be in for the UltraSPARC IIi BKI.

Register %o0 must contain a pointer to the BootConf structure.

Interrupts must be disabled.

Instruction and data caches must be disabled.

At address 0, a minimal trap table must be installed with spill/fill trap handlers.

MMU translation must be enabled.

An initial one-to-one mapping must be built by the boot program. This one-to-one mapping must start at 0 and be large enough to cover all virtual addresses for the memory bank's binary segments. Note that instruction and data Translation Lookaside Buffer (TLB) mapped in this way will initially be locked and all TLB entries not included in the initial mapping will be invalid.

The kernelSpace field in the BootConf structure must point to a description of the initial space established by the microkernel's memory management unit, as described in "The Initial Address Space". The family-dependent part of the tag field of the PhChunk entry is defined as a combination of the following bits:

Table 4-2 PhChunk KSP Region Type
PhChunk Bit Meaning
KSP_US_TYPE_RAM one-to-one RAM mapping

Table 4-3 PHChunk Data Attributes
PhChunk Bit Meaning
KSP_US_ATTR_DG   global
KSP_US_ATTR_DW writable
KSP_US_ATTR_DP privileged
KSP_US_ATTR_DE side effect
KSP_US_ATTR_DCV  cached virtually
KSP_US_ATTR_DCP cached physically (L2-cache)
KSP_US_ATTR_DIE inverted endiannes
KSP_US_ATTR_DNFO non faulted only
KSP_US_ATTR_DV valid

Table 4-4 PHChunk Instruction Attributes
PHChunk Bit Meaning
KSP_US_ATTR_IG   global
KSP_US_ATTR_IV valid
KSP_US_ATTR_IP privileged
KSP_US_ATTR_ICP  cached physically (L2-cache)

The initial kernel space must at least contain the one-to-one mapping of the available RAM.

The f_bootConf field of the BootConf structure must point to a processor specific decriptor:

typedef struct usparc_BootConf {

    VmAddr    tlb_lock_start; /* start, size and attributes of the memory */
    VmSize    tlb_lock_size;  /* region locked in I and D TLB by          */
    int       tlb_lock_attr;  /* the bootstrap program                    */

    VmAddr    trap_table_start; /* address of the trap table */

  } usparc_BootConf;

The first three fields decribe the initial one-to-one mapping. The last one gives the address of the trap table. The SPARC_PROP_I_CACHE and SPARC_PROP_D_CACHE properties must be present in the initial device tree. Values for these properties are of the type SparcPropCache:

typedef struct {
    uint32_f  csize;          /* cache size */
    uint32_f  bsize;          /* cache bank size */
    uint32_f  lsize;          /* cache line size */
    uint32_f  nbanks;         /* number of bunks (csize = bsize * nbanks) */
    uint32_f  type;           /* cache type */
  } SparcPropCache;

Chapter 5 The ChorusOS Reboot Program

This chapter explains how to write a reboot program for ChorusOS. The reboot program is implemented as a separate binary executable file. It is installed by the bootstrap program during the cold boot procedure. The reboot program implements board-specific support for the sysReboot() system call. This chapter covers:

Writing a Reboot Program for the ChorusOS Operating System

This section describes how to write a reboot program for the ChorusOS operating system, by taking a practical example of the implementation of the SBC8260 reboot program. It describes how the reboot program is initialized during the system boot, contains information on the global state of the reboot program and describes how the reboot program proceeds to reboot the system.

Reboot Program Initialization

Code Example 5-1 is the reboot program initialization source code provided in source_dir/nucleus/bsp/powerpc/sbc8260/src/reboot/reboot.c.


Example 5-1 reboot_main

#define MAX_CHUNKS 16
#define STR_LENGTH 256

PrstChunk chunks[MAX_CHUNKS];
char chunk_id_buf[STR_LENGTH];

RebootDesc rebootDesc;
BootConfRebootEntry rebootEntry;

DbgOps_reset	resetOp;

BootParams bootParams;

    void
reboot_main(BootConf* conf, BootParams* bootp)
{
		if (conf->rebootDesc == 0) {
       /*
        * This is a cold reboot.
        */

       /*
        * Initialize printf()/scanf() support library
        */
       _stdc_consInit(conf->dbgOps.consRead, conf->dbgOps.consWrite);

       /*
        * Register hot reboot descriptor
        */
       rebootDesc.rebootOp = do_reboot;
       rebootDesc.hot.prstMem.numChunks = 0;
       rebootDesc.hot.prstMem.chunks = chunks;
       rebootDesc.hot.prstMem.maxChunks = MAX_CHUNKS;
       rebootDesc.hot.prstMem.curStrLen = 0;
       rebootDesc.hot.prstMem.maxStrLen = STR_LENGTH;
       rebootDesc.hot.prstMem.str = chunk_id_buf;

       conf->rebootDesc = &rebootdesc;

       /*
        * Save hot reboot entry point
        */
       rebootEntry = conf->rebootEntry;

       /*
        * Save reset entry point
        */
       resetOp = conf->dbgOps.reset;

       /*
        * Save bootparams
        */
       if (bootp) {
           bootParams = *bootp;
       }
		} else {
       /*
        * This is a hot reboot. 
        *
        * Nothing to do as the reboot program was initialized at the
        * last cold reboot.
        */
		}
}

The reboot program is installed only once during a cold boot. For subsequent hot reboots, the reboot program maintains the section of the system state that must be kept. The reboot_main() function is the reboot program entry point and is called by the bootstrap program each time the system boots.

For a cold boot, when the rebootDesc field of the conf argument is NULL, the reboot program initializes its static data. Whereas for a hot boot the reboot program re-initialization is empty.

One purpose of the program is to maintain a stable system state during hot reboots. This state is represented by a generic HotRebootDesc structure (see "HotRebootDesc Structure") and a board specific BootParams structure (see "Bootstrap Program Implementation").

The bootstrap program exports the address of the reboot service routine, do_reboot(), in the rebootOp field of the rebootDesc structure. The microkernel jumps to this routine to proceed with any kind of reboot. See "HotRebootDesc Structure" for details.

HotRebootDesc Structure

Code Example 5-2 is the HotRebootDesc structure definition provided in source_dir/nucleus/sys/common/src/kern/bki/reboot.h.


Example 5-2 HotRebootDesc structure

/*
 * PrstChunk::status bit values
 */

#define PRST_CHUNK_ALLOCATED 0x1
#define PRST_CHUNK_MAPPED    0x2

/*
 * Descriptor of a chunk of persistent memory
 */

typedef struct PrstChunk {
    char*   id;              /* name string */
    int     status;          /* status bit string */
    VmSize  size;            /* size in bytes */

    PhAddr  paddr;           /* starting physical address 
                              * valid if PRST_CHUNK_ALLOCATED is set */
    VmAddr  vaddr;           /* starting virtual address */
                              * valid if PRST_CHUNK_MAPPED is set */
} PrstChunk;


/*
 * Persistent memory descriptor
 */

typedef struct PrstMem {
    int        maxChunks;    /* max number of persistent memory chunks */
    int        numChunks;    /* number of persistent memory chunks */
    PrstChunk* chunks;       /* array of persistent memory chunks */
    int        maxStrLen;    /* max cumulated size of chunk ids */
    int        curStrLen;    /* current cumulated size of chunk ids */
    char*      str;          /* chunk ids buffer*/
} PrstMem;

/*
 * The state kept over a hot reboot
 */

typedef struct HotRebootDesc {
    PrstMem    prstMem;      /* persistent memory descriptor */
} HotRebootDesc;

The prstMem field of HotRebootDesc describes the persistent memory (the portion of system RAM that must remain the same during subsequent hot reboots). The description is an array of PrstChunk structures, each one describing a persistent memory device (a contiguous named portion of persistent memory). The array is statically allocated in the reboot program data segment.

A persistent memory device is described by its symbolic name, id, the starting physical address, paddr, the starting virtual addresses, vaddr, and the size.

Different operating system components are involved in persistent memory device allocations:

Rebooting the Board

This section describes how the reboot program reboots the board. It describes the implementations of the cold and hot reboot, as well as providing some information on how the reboot program boots a new system image.

Code Example 5-3 is the reboot service routine source code provided in source_dir/nucleus/bsp/powerpc/sbc8260/src/reboot/reboot.c


Example 5-3 do_reboot() routine

    void
do_reboot(RebootDesc* rd, KnRebootReq* req)
{
    ASSERT(rd == &rebootDesc);

    if (req->mode == K_REBOOT_COLD) {
        rebootCold(req);
    }

    if (req->mode == K_REBOOT_HOT) {
        rebootHot(req);
    }

    if (req->mode == K_REBOOT_NEW) {
        rebootNew(req);
    }

    ASSERT(0);
}

This service routine dispatches execution to a particular reboot procedure.

Cold Reboot

Code Example 5-4 is the cold reboot service routine source code provided in source_dir/nucleus/bsp/powerpc/sbc8260/src/reboot/reboot.c.


Example 5-4 coldReboot() routine

    void
rebootCold(KnRebootReq* req)
{
    printf ("Cold reboot ...\n");
    resetOp(0);
}

This cold reboot service calls the debug agent hardware reset service routine.

Hot Reboot

Code Example 5-5 is the hot reboot service routine source code provided in source_dir/nucleus/bsp/powerpc/sbc8260/src/reboot/reboot.c.


Example 5-5 hotReboot() routine

#define STACK_SIZE 0x400
char stack[STACK_SIZE];

    void
rebootHot(KnRebootReq* req)
{
    printf ("Hot reboot ...\n");

    /*
     * Extend the persistent memory w.r.t
 the reboot request
     */
    prstExtend(&rebootDesc.hot.prstMem, &req->u.hot);

    /*
     * switch to a private stack
     */
    call_and_switch_stack(req, rebootHot_cont, stack + STACK_SIZE);
}


    void
rebootHot_cont(KnRebootReq* req)
{
    /*
     * Disable I/D-Caches.
     */
    cachesDisable();

    /*
     * Disable pagination.
     */
    setMSR(getMSR() & ~(MSR_IR | MSR_DR));

    /*
     * Reboot
     */
    rebootEntry(&rebootDesc, &bootParams);
}

This hot reboot service routine:

Note that the implementation switches from the original stack to a small stack area which is statically allocated in the reboot program data segment. This occurs prior to the MMU disabling to ensure that the current stack is safely accessible in the real mode. See "call_and_switch_stack() routine" for details.

Rebooting the New System Image

The ChorusOS boot monitor is implemented as an application on top of ChorusOS. The boot monitor reads the new system image from an external medium (for example, a local device, or the network) and stores that image in a buffer. It then performs a sysReboot() request that eventually invokes the rebootNew() service routine. To simplify the reboot implementation, the ChorusOS boot monitor has to use a ChorusOS system, configured with flat memory management, model. Code Example 5-6 is the service routine source code provided in source_dir/nucleus/bsp/powerpc/sbc8260/src/reboot/reboot.c.


Example 5-6 rebootNew() Routine

    void
rebootNew(KnRebootReq* req)
{
    KnNewRebootReq* nreq = >u.nw;
    BootParams* bootp;

    printf ("Boot new image ...\n");

    if (nreq->workSize < (launch_end - launch_start) + sizeof(BootParams)) {
        printf ("Not enough working memory to reboot\n");
        printf ("Try cold reboot\n");
        resetOp(0);
    }

    /*
     * Disable I/D-Caches.
     */
    cachesDisable();

    if (nreq->ipcSiteNb) {
        /*
         * Get Chours IPC site number from the loader
         */
        bootParams.ipcSiteNb = nreq->ipcSiteNb;
    }

    /*
     * Copy bootParams at the end of the working area
     */
    bootp = (BootParams*) (nreq->workAddr + nreq->workSize - 
                           sizeof(BootParams));
    bcopy(&bootParams, bootp, sizeof(BootParams));

    /*
     * Copy the launch routine to the working memory
     */
    bcopy(launch_start, (void*) nreq->workAddr, launch_end - launch_start);

    /*
     * Jump to the copied launch code
     */
    ((launch) nreq->workAddr)(nreq->dstAddr, nreq->srcAddr, 
                          nreq->size, nreq->entry, bootp);
}

The caller (that is, sysReboot()) provides a working area that is separate to that from the current system image, including the currently executing reboot program. The buffer, holding the new system image, and the system image destination area are also outside the working area. In the working area, the rebootNew() routine installs the bootParams structure and a small positional independent program, called launch(), and then runs launch(). The launch() program copies the new system image from the buffer to the destination address and jumps to its entry point, passing the address of the bootParams structure as the first argument. See "launch() Routine" for details.

Reboot Program Implementation Framework

This section describes the library routines that can be used by reboot programs.

prstExtend() Routine

The prstExtend() routine can be used to allocate a new persistent memory device descriptor. Code Example 5-7 is the prstExtend() routine source code provided in source_dir/nucleus/bsp/src/boot_tools/prstExtend.c.


Example 5-7 prstExtend() Routine

    void
prstExtend(PrstMem* pm, KnHotRebootReq* hreq)
{
    if (hreq->prstMemExtension) {
        if (pm->numChunks == pm->maxChunks) {
            printf ("Hot reboot can't allocate more than %d presistent "
                    "memory devices\n", pm->maxChunks);
        } else {
            int id_len = strlen(hreq->prstMemId) + 1;
            if (pm->curStrLen + id_len > pm->maxStrLen) {
                printf ("Hot reboot can't allocate more than %d bytes "
                        "for persistent memory device ids\n", 
                        pm->maxStrLen);
            } else {
                PrstChunk* ch_cur = pm->chunks+ pm->numChunks;
                ch_cur->status = 0;
                ch_cur->size = (PhSize) hreq->prstMemExtension;
                ch_cur->id = pm->str + pm->curStrLen;
                strcpy(ch_cur->id, hreq->prstMemId);
                pm->curStrLen += id_len;
                pm->numChunks += 1;
            }
        }
    }
}

The prstExtend() routine allocates the next available entry in the chunk array (the array is held within the persistent memory descriptor and is pointed to by the pm argument). The routine also copies the symbolic name of the new memory device from the caller address space into the str buffer (also held within the persistent memory descriptor). Note that the persistent memory descriptor is typically allocated within the reboot program data segment.

call_and_switch_stack() routine

Code Example 5-8 is the call_and_switch_stack() routine source code provided in source_dir/nucleus/bsp/src/boot_tools/call_and_switch_stack.s.


Example 5-8 call_and_switch_stack() Routine

			/*
			 *     void
			 * call_and_switch_stack(void* cookie, void* new_pc, 
       *       void* new_sp)
			 */

			GLOBAL(call_and_switch_stack)
call_and_switch_stack:
    mr    r5, r1    /* sp = new_sp */
    mtlr  r4, lr    /* (*new_pc) (cookie) */
    blr

The call_and_switch_stack() routine switches to the stack provided as argument three, new_pc, jumps to the function specified by the second argument, new_pc, passing to that function the first argument, cookie.

launch() Routine

The launch() routine source code provided in source_dir/nucleus/bsp/src/family/powerpc/boot_tools/launch.s.

The launch() function copies size bytes from the source address, src, to the destination address, dst, and then jumps to the location entry, passing cookie as the argument.

The launch() routine is position-independent. The caller can copy the instructions, starting from the address launch_start() up to the address launch_end(), to any destination location and execute them there.

The launch() routine does not require a stack and is not required to preserve any general register state.

Chapter 6 Porting the Debug Agent and Hot Swap Support

This chapter describes the architecture of the ChorusOS debug agent with details on how to adapt it to run on your new board.

For more information about debugging your ported system using debug agent, see "Debugging ChorusOS Systems" in ChorusOS 5.0 Debugging Guide.

The Debug Agent

The debug agent provides debug services as soon as ChorusOS has succesfully booted, enabling you to debug your ported ChorusOS system. Figure 1-1 shows the component architecture of ChorusOS and the parts of the BSP that must be adapted to your target architecture. The debug agent is not board specific but it relies on some debug critical drivers that are board specific. Therefore some work must be done to ensure that the debug agent works on your new board.

There are two binary files in the ChorusOS operating system bootable image that you must consider when porting the debug agent:

The interface between dbgAgent and dbgBsp is described in source_dir/nucleus/sys/common/src/kern/bki/dbgBsp.h.

dbgAgent

The debug agent source code can be found in the source_dir/nucleus/sys/common/src/dbg and source_dir/nucleus/sys/family/src/dbg directory trees. As this code is platform independent, you should not need to modify it and in fact you only have access to this code if you have a source delivery of the ChorusOS operating system.

dbgBsp

The dbgBsp consists of two main parts (see Figure 6-1) that interface with some critical devices that enable boot and debugging facilities. The first part is a device independent (but driver dependent) interface adaptation layer that links the debug agent and the debug criticial devices.

Figure 6-1 Debug Agent Showing Debug Critical Drivers

Graphic

The second part is made up of the device drivers that are used to manage the chips that the dbgBsp requires to be able to provide its services. This part is device (chip) dependent and implements the device independent interfaces that are defined in the upper layer.

For all boards that the ChorusOS operating system is supported on, the devices that must be implemented are the same: UART and WDT. However, the chips required by these devices are different.

The device-dependent (but chip-independent) definition files can be found in the source_dir/nucleus/bsp/src/dbg directory. There is a header file per device that defines the interfaces: dbgWdt.h and dbgUart.h.

The instanciation of this interface (or the stubs) can be found in the same directory. The naming convention for these files is: device.c and deviceBsp.h. The header file defines the functions from the device driver that can be used to implement the dbgBsp services. The C source file implements these services using the device driver calls that are defined in the header file. For example epfpld.c is a simple dbgBsp service that uses the epfpld device driver.

The Imakefile of this directory only lists the object files for independent compilation, and the headers that will be exported.

The second part of the dbgBsp is platform dependent. In this part the dbgBsp is first described in the Imakefile in terms of "what device drivers are used for this platform". This is where is it stated that for the SPARC CP1500, the dbgBsp relies on ns16550 UART driver and an epfpld WDT driver. This Imakefile can be found in the source_dir/nucleus/bsp/family/my_board/src/dbg directory. In this directory, you will also find the source for the implementation of the device driver interface defined in the deviceBsp.h file.

When porting to a new board you will need to write both the serial device drivers and the WDT device driver.

Example

This section describes a dbgBsp for the UltraSPARC CP1500 board. The source files for this example are in the source_dir/nucleus/bsp/usparc/cpxxxx/src/dbg directory. There are three files in this directory:

dbgBsp.c:

ns16550Bsp.c:

epfpldBsp.c:

Imakefile:

BSP Hot Swap Support

The Compact PCI bus is an extension of the PCI bus which enables a board to be inserted or removed when the bus is powered up. The ENUM# signal is a specific bus signal which notifies of such an event. The support of hot swap in the BSP consists of the following:

The implementation of the BSP hot swap support in the ChorusOS operating system is across three levels of drivers:

The Hot Swap Controller is responsible for receiving and handling the ENUM# signal and providing a generic interface to manage the corresponding event/interrupt. Usually this driver is board specific.

The pciswap driver is generic and provides an interface for forwarding the PciSwap events to its client. Its role is to probe the PCI configuration space and to read the HS_CSR register (defined by the PICMG Hot Swap specification). Therefore, it detects which device has been inserted or is going to be removed, and notifies its client.

The PciswapEvents handlers are implemented in the CompactPCI bridge device driver. This driver is the client of the pciswap driver and uses its interface to manage the PciSwap events. The handler of the corresponding events are responsible for starting or stopping the child drivers.

Reference Implementation

This section contains a list of the reference boards for hot swap support and the corresponding drivers used in the implementation. More information on these can be found the corresponding man page in the 9DRV section:

CP1500

usparchsc(9DRV)

Hot Swap Controller

pciswap(9DRV)

PciSwap driver

dec2115x(9DRV)

PciSwap client

MCP750

gpiohsc(9DRV)

Hot Swap Controller

pciswap(9DRV)

PciSwap driver

dec2115x(9DRV)

PciSwap client

CPN5360

fpga(9DRV)

Hot Swap Controller

pciswap(9DRV)

PciSwap driver

dec2155x(9DRV)

PciSwap client

Porting Hot Swap

The pciswap driver is generic and may be used on any board. It provides a drv_probe() routine so there is no need to add a node in the device tree for this driver.

The following sections give more information on the drivers that may need to be written when porting to a new board.

Hot Swap Controller

In the ChorusOS operating system the following Hot Swap Controller drivers are provided (one reference board per target family):

As the Hot Swap Controller is supposed to attach a handler to the ENUM# interrupt, this driver is very board specific.

PCI Bridge PciSwapEvents Handlers

The following are implemented as PciSwap clients: dec2115x(9DRV), dec2155x(9DRV). If the compact PCI bridge of the new board is not one of these, the PciswapEvents handlers need to be implemented.

The Device Tree

A node for the Hot Swap Controller must be added in the device tree. The HSC_PROP_BUS_PATH property and its corresponding value must be added to this node. This property describes the bus from which the devices may be hot swapped.

Chapter 7 Configuring the ChorusOS System Image

This chapter describes how to configure your system image to specify the new BSP component. The configuration of the ChorusOS system image depends on a number of configuration files written in ECML but for the purpose of adding a BSP you will mainly need to create and edit the target.xml and target_action.xml files.


Note -

On some platforms, target.xml is split into multiple file: drvlist.xml, drivers.xml which are included in target.xml.


Overview of Creating a System Image

This section explains what the system image is and how to create it using the tools and configuration files provided.

The system image is the initial state of the target board's physical memory. It contains a set of files that are organized into one or more memory banks. For more information on the complete set of ChorusOS configuration files see "Configuration Files" in ChorusOS 5.0 System Administrator's Guide.

When the target system is deployed, some of the system image memory banks are burnt into non-volatile physical memory. The rest can be downloaded from a local device or from the network by the initial loader (boot monitor or firmware).

To build the system image, you use an XML-based system. This system has two components:

The board-specific configuration must be stored in the board BSP directory: source_dir/nucleus/bsp/family/board. For example, the board-specific configuration files for the SBC8260 board are stored in source_dir/nucleus/bsp/powerpc/sbc8260, and those for the PC/AT are stored in source_dir/nucleus/bsp/x86/i386at.

There are two configuration files that define the BSP and driver configuration of the system image:

The configuration must specify the list of memory banks and, for each particular bank, a symbolic name, a base address, and a size limit. Also, the configuration can request mkimage to organize any particular memory bank as a file system volume. The current ChorusOS operating system release supports FAT MS-DOS format only.

The configuration must also specify a list of files to be included in the system image and, for each particular file, the bank where the file must be stored. In addition, for relocatable ELF files, the configuration can specify how the file must be relocated when stored in the memory bank.

Most of the examples in this chapter use the SBC8260 board. To understand the examples, the following information about the SBC8260 board is useful:

Imported Objects

The following objects are predefined and can be used within the board-specific configuration files. They can be used to specify conditions that enable different configurations to be set up for different situations, for example you can set up two different configurations for the two deployment schemes, that would depend on the object BOOT_MODE:

Summary of Required Definitions

A board-specific configuration must define the following objects. They are defined in the ECML target.xml file using variables and folders. More information on how to define these objects is given in the following sections:

The target.xml File Structure

This section describes the content and structure of the target.xml file. The main folders and the variables that they contain are shown in examples.

target.xml Definition

A target.xml file contains a number of hierarchical folders. The top level of folders is the following:

Global Variables

The Global variables folder contains definitions of global system image configuration variables.

Some of them are board-specific and used within the board-specific configuration files only. For example, a configuration for a PC/AT target defines the LOADER configuration variable, with a value specifying which of the two initial loaders (SVR4 boot and Linux LILO boot) will be used to load the system image. The format of the system image is tailored to match the requirements of the initial loader specified by LOADER.

There is one mandatory global variable, IMAGE_DIR. This variable specifies the working directory to be used by the mkimage tool to store the files created during the generation of the ChorusOS system image.

Example 7-1 is an extract of the SBC8260 board-specific configuration file, and defines two global configuration variables, IMAGE_DIR and RESULT.


Example 7-1 SBC8260 Global Variable Configuration

<definition name='IMAGE_DIR'>
		<description>'mkimage' tool output repository</description>
		<string/>
		<vstring>${BUILD_DIR}/image/${BOOT_MODE}/${SYSTEM}</vstring>
</definition>

<definition name='RESULT'>
		<description>the resulting system image file pathname</description>
		<string/>
		<vstring>${BUILD_DIR}/${SYSTEM}.${BOOT_MODE}</vstring>
</definition>

The value of IMAGE_DIR specifies the mkimage working directory as a function of the BOOT_MODE and SYSTEM configuration variables. For example, the working directory for RAM-based chorus system image generation is ${BUILD_DIR}/image/RAM/chorus.

The value of RESULT specifies the filename of the resulting (bootable) system image file. The filename also depends on the BOOT_MODE and SYSTEM configuration variables. For example, the bootable RAM-based chorus system image file will be stored in ${BUILD_DIR}/chorus.RAM.

Banks

The Banks folder contains definitions of the system image memory banks.

A system image memory bank is specified by an object of the type Bank. The following fields of a Bank object must be defined in the target.xml file in order to define the properties of each bank:

The board-specific configuration can define any number of memory banks. A board-specific configuration must define a Bank object named sys_bank.

For example, the SBC8260 board-specific configuration defines two memory banks trampoline_bank and sys_bank.

The trampoline_bank memory bank will contain the power-up initialization (trampoline) program (see "Power-up Program Implementation"). The sys_bank memory bank will contain the remains of the system image.

Code Example 7-2 is an extract of the SBC8260 board-specific configuration file, and defines the trampoline_bank and sys_bank objects.


Example 7-2 SBC8260 Bank Configuration

	
<folder name='Banks'>

    <description>system image memory banks</description>

    <definition name='sys_bank'>
      <description>default system image memory bank</description>
      <condition>
        <equal><var name='BOOT_MODE' /><const>RAM</const></equal>
      </condition>

      <type name='Bank' />
      <value field='ram'><true /></value>
      <value field='addr'><const>0x00100000</const></value>
      <value field='size'><const>0x00f00000</const></value>
    </definition>

    <definition name='trampoline_bank'>
      <description>memory bank for a 'trampoline' 
              (eg. power-up) binary</description>
      <condition>
        <equal><var name='BOOT_MODE' /><const>ROM</const></equal>
      </condition>
      <type name='Bank' />
      <value field='ram'><false /></value>
      <value field='addr'><const>0xfff00000</const></value>
      <value field='size'><const>0x00001000</const></value>
    </definition>

    <definition name='sys_bank'>
      <description>default system image memory bank</description>
      <condition>
        <equal><var name='BOOT_MODE' /><const>ROM</const></equal>
      </condition>
      <type name='Bank' />
      <value field='ram'><false /></value>
      <value field='addr'><const>0xfff01000</const></value>
      <value field='size'><const>0x000ff000</const></value>
    </definition>

  </folder>

Two bank configurations are provided. The first configuration is used when the system is configured to boot from ROM. This configuration is selected when the value of the BOOT_MODE configuration variable is set to ROM. In this case, trampoline_bank will start at the address 0xfff00000 and can contain up to 4K bytes, sufficient for the trampoline program to run. sys_bank starts immediately after trampoline_bank at the address 0xfff01000 and it can contain up to 0xff000 bytes.

The second configuration is used when the system is configured to boot from RAM. In this case, trampoline_bank is not defined. sys_bank starts at the address 0x00100000 and it can contain up to 0xf00000 bytes. The RAM occupied by the bank will be tagged as allocated when the initial state of the RAM allocator is generated, because the value of the ram field is true.

For more information on the resulting system image bank description structure see "Banks".

Areas

This folder contains definitions of the linking areas used during system image link-edit. A linking area is specified by an object of the type Area which defines the following fields:

The board-specific configuration can define any number of linking areas. A board-specific configuration must define an Area object named ram_area.

For example, the SBC8260 board-specific configuration defines three linking areas: ram_area, supervisor_actor_area, and user_actor_area.

When the microkernel is configured with the memory management module implementing the flat memory model (that is, when the value of the VIRTUAL_ADDRESS_SPACE configuration variable is false), only ram_area is defined. It is used to link-edit all non-XIP data segments.

Code Example 7-3 is an extract of the SBC8260 board-specific configuration file, and defines the ram_area object.


Example 7-3 SBC8260 ram_area Configuration

    <definition name='ram_area'>
      <description>default system image linking area  
          (used for bss only)</description>
      <condition>
        <equal><var name='BOOT_MODE' /><const>RAM</const></equal>
      </condition>
      <type name='Area' />
      <value field='addr'><const>0x00004000</const></value>
      <value field='size'><const>0x000fc000</const></value>
      <value field='private'><false/></value>
      <value field='virtual'><false/></value>
    </definition>

    <definition name='ram_area'>
      <description>default system image linking area
          (used for data segments)</description>
      <condition>
        <equal><var name='BOOT_MODE' /><const>ROM</const></equal>
      </condition>
      <type name='Area' />
      <value field='addr'><const>0x00004000</const></value>
      <value field='size'><const>0x00ffc000</const></value>
      <value field='private'><false/></value>
      <value field='virtual'><false/></value>
    </definition>

When the microkernel is configured with one of the memory management modules implementing the virtual memory model (that is, when the value of the VIRTUAL_ADDRESS_SPACE configuration variable is true), three linking areas are defined:

Code Example 7-4 is an extract of the SBC2860 board-specific configuration file, and defines the supervisor_actor_area and user_actor_area objects.


Example 7-4 SBC8260 supervisor_actor_area and user_actor_area Objects

   <definition name='supervisor_actor_area'>
      <description>linking area for supervisor actors</description>
      <condition><var name='VIRTUAL_ADDRESS_SPACE' /></condition>
      <type name='Area' />
      <value field='virtual'><true /></value>
      <value field='private'><false /></value>
      <value field='addr'><const>0xa0000000</const></value>
      <value field='size'><const>0x40000000</const></value>
    </definition>

    <definition name='user_actor_area'>
      <description>linking area for user actors</description>
      <condition><var name='VIRTUAL_ADDRESS_SPACE' /></condition>
      <type name='Area' />
      <value field='virtual'><true /></value>
      <value field='private'><true /></value>
      <value field='addr'><const>0x00000000</const></value>
      <value field='size'><const>0x80000000</const></value>
    </definition>

System Control Blocks

A board-specific configuration must define two references to linking area objects where global system control blocks are allocated:

For example, the SBC8260 board-specific configuration defines supervisor_context_area_ref as a reference to ram_area:

<definition name='supervisor_context_area_ref'>
			<description>reference to the address range where global system 
				control blocks must be allocated</description>
			<type name='Area' ref-only='yes' />
			<ref name='ram_area' />
</definition>

The mkimage tool will allocate an address range for supervisor address space control blocks in ram_area (as well as the address ranges for all segments link-edited into it).

Two configurations for user_context_area_ref are defined for the SBC8260 board.

Microkernel Address Space

A board-specific configuration can specify the initial microkernel address space layout. If specified, the object that defines it must be named ksp and is usually contained in the folder named KSP. The exact semantics and requirements for the initial microkernel address space specifications are family dependent. For more information on the resulting system image data see "The Initial Address Space"

An MPC60x family-dependent configuration must specify the initial microkernel address space layout.

The SBC8260 board-dependent configuration specifies the initial microkernel address space as follows:


Example 7-5 SBC8260 Board-Dependent Configuration

  <folder name='Kernel address SPace'>
    <description>kernel address space configurations</description>

    <definition name='ksp'>
      <description>flat memory management: system address space layout
               virtual memory management: see PPC 60x BKI</description>

      <type name='AddrSpaceMap' />
<!--
        PowerPC 60x bus SDRAM: 0x00000000 .. 0x00ffffff
-->
      <value index='size'>
        <value field='vaddr'><const>0x00000000</const></value>
        <value field='valid'><true/></value>
        <value field='paddr'><const>0x00000000</const></value>
        <value field='attr'>
          <const>KSP_PPC60x_BAT|KSP_PPC60x_M|KSP_PPC60x_RW</const>
        </value>
      </value>
<!--
        Local bus 32-bit SDRAM: 0x01000000 .. 0x0effffff
-->
      <value index='size'>
        <value field='vaddr'><const>0x01000000</const></value>
        <value field='valid'><false/></value>
      </value>
<!--
        8260 internal memory map (IMMR): 0x0f000000 .. 0x0f01ffff
-->
      <value index='size'>
        <value field='vaddr'><const>0x0f000000</const></value>
        <value field='valid'><true/></value>
        <value field='paddr'><const>0x0f000000</const></value>
        <value field='attr'>
          <const>KSP_PPC60x_BAT|KSP_PPC60x_G|KSP_PPC60x_I|KSP_PPC60x_RW
          </const>
        </value>
      </value>

      <value index='size'>
        <value field='vaddr'><const>0x0f020000</const></value>
        <value field='valid'><false/></value>
      </value>
<!--
        8-bits general purpose FLASH memory: 0xe0000000 .. 0xe0ffffff
-->
<!--
        32-bits general purpose FLASH memory.
                expandable : 0xfc000000 .. 0xfcffffff
                boot       : 0xfe000000 .. 0xffffffff
-->
      <value index='size'>
        <value field='vaddr'><const>0xfc000000</const></value>
        <value field='valid'><true/></value>
        <value field='paddr'><const>0xfc000000</const></value>
        <value field='attr'>
          <const>KSP_PPC60x_BAT|KSP_PPC60x_RO</const></value>
      </value>
    </definition>

  </folder>

According to this definition, the microkernel will initially map the RAM space from physical address 0 to 0x01000000, to the virtual address 0, using one pair of BATs. It will also map the flash memory space from physical address 0xfc000000 to 0xffffffff, to the virtual address 0xfc000000 using another pair of BATs. Finally, the microkernel will map 0x00020000 bytes of 8260 Internal Memory Mapped Registers based at the physical address 0x0f000000 to the virtual address 0x0f000000. The rest of the virtual address space will be invalid.

Link-Edit Models

The link-edit models folder contains a set of link-edit models, each one describing how to link-edit a particular type of binary file.

The models.xml file contains the standard set of link-edit models:

Usually, the board-specific configuration includes the models defined in model.xml and defines a small number of board-specific models.

A link-edit model is specified by an object of type Binary which defines the following fields:

The values of the ro, rw, and bss fields are objects of the type Segment. A Segment object specifies link-edit instructions for the set of sections held by the segment and instructions for the segment loading for execution. In the Segment object:

Therefore, if for a given segment a linking area is not defined (for instance, the segment is to be link-edited at its place in the memory bank) , the xip attribute must be true.

If, for a segment, a linking area is defined, the xip attribute can be either:

Code Example 7-6 is an extract of the SBC8260 board-specific configuration file, and contains the standard link-edit models and the link-edit model definition which contains the trampoline program.


Example 7-6 SBC8260 Link-Edit Model Configuration

<folder name='Link-edit models'>
			<description>layouts of executable binary files</description>

		<folderRef href='model.xml' />  

		<folder name='Genesis2-specific link-edit models'>
			<description>layouts of executable binary files</description>

			<definition name='trampoline_model'>
				<description>trampoline (eg. power-up) program 
                                link-edit model</description>
				<type name='Binary' />
				<value field='ro'>
					<value field='xip'><true/></value>
				</value>
				<value field='strip'><const>ALL</const></value>
			</definition>
		</folder>
</folder>

The trampoline program does not contain any data and its text section must be link-edited at its place in its memory bank, trampoline_bank.

Files

The Files folder contains the specification of the set of BSP files that can be included in the system image. Each file is specified by an object of the type File which contains the following fields:

Code Example 7-7 is an extract of the SBC8260 board-specific configuration file, and contains the definitions of the File objects.


Example 7-7 SBC8260 File Configuration

  <folder name='Files'>
    <description>files that system image can potentially 
                     contain</description>

    <definition name='powerup'>
      <description>powerup initialization program</description>
      <condition>
        <equal><var name='BOOT_MODE' /><const>ROM</const></equal>
      </condition>

      <type name='File' />
      <value field='path'>
        <vstring>${BSP_DIR}/bin/sbc8260/boot/trampoline</vstring>
      </value>
      <value field='bank'><ref name='trampoline_bank' /></value>
      <value field='binary'><ref name='trampoline_model' /></value>
    </definition>

    <definition name='bootstrap'>
      <description>bootstrap program</description>
      <type name='File' />
      <value field='path'>
        <vstring>${BSP_DIR}/bin/sbc8260/boot/boot</vstring>
      </value>
      <value field='bank'><ref name='sys_bank' /></value>
      <value field='binary'><ref name='bootstrap_model' /></value>
    </definition>

    <definition name='debug_driver'>
      <description>system debug agent driver</description>
      <type name='File' />
      <value field='path'>
        <vstring>${BSP_DIR}/bin/sbc8260/dbg/dbgBsp</vstring>
      </value>
      <value field='bank'><ref name='sys_bank' /></value>
      <value field='binary'><ref name='debug_driver_model' /></value>
    </definition>

    <definition name='debug_agent'>
      <description>system debug agent</description>
      <type name='File' />
      <value field='path'>
        <vstring>${BUILD_DIR}/obj/dbg/dbgAgent</vstring>
      </value>
      <value field='bank'><ref name='sys_bank' /></value>
      <value field='binary'><ref name='debug_agent_model' /></value>
    </definition>

    <definition name='microkernel'>
      <description>ChorusOS microkernel</description>
        <type name='File' />
      <value field='path'>
        <vstring>${BUILD_DIR}/obj/kern/kern</vstring>
      </value>
      <value field='bank'><ref name='sys_bank' /></value>
      <value field='binary'><ref name='microkernel_model' /></value>
    </definition>

    <definition name='tbdec'>
      <description>built-in driver</description>
      <type name='File' />
      <value field='path'>
        <vstring>${DRV_F_DIR}/bin/drv/timer/tbDec/D_tbDec</vstring>
      </value>
      <value field='bank'><ref name='sys_bank' /></value>
      <value field='binary'><ref name='driver_model' /></value>
    </definition>

    <definition name='quicc8260'>
      <description>built-in driver</description>
      <type name='File' />
      <value field='path'>
        <vstring>${DRV_F_DIR}/bin/drv/quicc/8260/D_quicc8260</vstring>
      </value>
      <value field='bank'><ref name='sys_bank' /></value>
      <value field='binary'><ref name='driver_model' /></value>
    </definition>

    <definition name='fccEther'>
      <description>built-in driver</description>
      <type name='File' />
      <value field='path'>
        <vstring>${DRV_DIR}/bin/drv/net/ether/fcc/D_fccEther</vstring>
      </value>
      <value field='bank'><ref name='sys_bank' /></value>
      <value field='binary'><ref name='driver_model' /></value>
    </definition>

    <definition name='sccEther'>
      <description>built-in driver</description>
      <type name='File' />
      <value field='path'>
        <vstring>${DRV_DIR}/bin/drv/net/ether/scc/D_sccEther</vstring>
      </value>
      <value field='bank'><ref name='sys_bank' /></value>
      <value field='binary'><ref name='driver_model' /></value>
    </definition>

    <definition name='quiccMii'>
      <description>built-in driver</description>
      <type name='File' />
      <value field='path'>
        <vstring>${DRV_DIR}/bin/drv/mii/quiccMii/D_quiccMii</vstring>
      </value>
      <value field='bank'><ref name='sys_bank' /></value>
      <value field='binary'><ref name='driver_model' /></value>
    </definition>

    <definition name='lxt970'>
      <description>built-in driver</description>
      <type name='File' />
      <value field='path'>
        <vstring>${DRV_DIR}/bin/drv/phy/lxt970/D_lxt970</vstring>
      </value>
      <value field='bank'><ref name='sys_bank' /></value>
      <value field='binary'><ref name='driver_model' /></value>
    </definition>

    <definition name='sccUart'>
      <description>built-in driver</description>
      <type name='File' />
      <value field='path'>
        <vstring>${DRV_DIR}/bin/drv/uart/scc/D_sccUart</vstring>
      </value>
      <value field='bank'><ref name='sys_bank' /></value>
      <value field='binary'><ref name='driver_model' /></value>
    </definition>

    <definition name='reboot'>
      <description>reboot program</description>
      <type name='File' />
      <value field='path'>
        <vstring>${BSP_DIR}/bin/sbc8260/reboot/reboot</vstring>
      </value>
      <value field='bank'><ref name='sys_bank' /></value>
      <value field='binary'><ref name='reboot_model' /></value>
    </definition>
  </folder>

System Image

The system image folder contains three sub-folders:

Files

The files folder contains the definition of a BSP_files object, which is a list of references to File objects specifying the files that must be included in the system image.

A board-specific configuration must define a BSP_files object.

Code Example 7-8 is an extract of the SBC8260 board-specific configuration file, and contains the BSP_files definition.


Example 7-8 SBC8260 BSP_files Configuration

      <definition name='BSP_files'>
        <description>system image BSP files</description>
        <type name='FileList' />
        <value index='size'><ref name='debug_driver' /></value>
        <value index='size'><ref name='debug_agent' /></value>
        <value index='size'><ref name='bootstrap' /></value>
        <value index='size'><ref name='reboot' /></value>
        <value index='size'><ref name='microkernel' /></value>
        <value index='size'><ref name='tbdec' /></value>
        <value index='size'><ref name='quicc8260' /></value>
        <value index='size'><ref name='fccEther' /></value>
      </definition>

      <setting name='BSP_files'>
        <condition>
          <equal><var name='BOOT_MODE' /><const>ROM</const></equal>
        </condition>
        <value index='size'><ref name='powerup' /></value>
      </setting>

Files Generated Automatically

The mkimage tool generates three files:

The Auto-generated files folder contains definitions of File objects corresponding to these three files. The File objects must be named bootconf, symb, and env_file, respectively.

Code Example 7-9 is an extract of the SBC8260 board-specific configuration file, and contains the Auto-generated files definition.


Example 7-9 SBC8260 Auto-generated Files Configuration

      <folder name='Auto-generated files'>
        <description>system image files automatically \
         generated by 'mkimage' tool</description>

        <definition name='env_file'>
          <description>initial state of system environment \
           variables</description>
          <type name='File' />
          <value field='path'>
            <vstring>${IMAGE_DIR}/environ</vstring>
          </value>
          <value field='bank'><ref name='sys_bank' /></value>
        </definition>

        <definition name='symb'>
          <description>kernel debuger (kdb) symbols</description>
          <type name='File' />
          <value field='path'>
            <vstring>${IMAGE_DIR}/symb/${SYSTEM}_symb</vstring>
          </value>
          <value field='bank'><ref name='sys_bank' /></value>
          <value field='binary'><ref name='debug_agent_model' /></value>
        </definition>

        <definition name='bootconf'>
          <description>bootconf program</description>
          <type name='File' />
          <value field='path'>
            <vstring>${IMAGE_DIR}/bconf/${SYSTEM}_bconf</vstring>
          </value>
          <value field='bank'><ref name='sys_bank' /></value>
          <value field='binary'><ref name='bootconf_model' /></value>
        </definition>

      </folder>

Miscellaneous Information

The misc folder contains the following:

Code Example 7-10 is an extract of the SBC8260 board-specific configuration file, and contains the Misc definition.


Example 7-10 SBC8260 Misc Configuration

    <folder name='Misc'>
      <description>Misc configuration variables</description>
        
      <definition name='banks'>
        <description>list of system image banks</description>
        <type name='BankList'/>
      </definition>

      <definition name='heap_size'>
        <description>bootconf heap size</description>
        <int/>
        <const>0x00002000</const>
      </definition>

      <setting name='banks'>
        <condition>
          <equal><var name='BOOT_MODE' /><const>ROM</const></equal>
        </condition>
        <value index='size'><ref name='trampoline_bank' /></value>
      </setting>

      <setting name='banks'>
        <value index='size'><ref name='sys_bank' /></value>
      </setting>

    </folder>

The mkimage System Image

This section contains more information on the output from the mkimage tool and the target-specific configuration files that are described in the previous section.

Relocating ELF Files

If relocation is specified for a given file, mkimage relocates it and stores the resulting absolute binary file in the memory bank. You can specify how many program segments must contain the resulting binary file and, for each individual segment, how it must be relocated.

A segment can be relocated to a linking area or to the segment image's base address within the memory bank.

A linking area is a named range of addresses specified by an id, a base address, and a size. You can specify a different linking area for each program segment. mkimage relocates a segment to the lowest available range of addresses of a suitable size within the linking area. mkimage guarantees that different segments relocated to the same linking area will never overlap. However, segments relocated to private linking areas can overlap.

Execution in Place (XIP)

For any particular segment you can specify whether the segment must be executed at its place within the memory bank (in this document this segment is refered to as the "XIP segment") or whether it must be copied into the RAM before it is executed.

bootconf Binary

The mkimage command builds a binary file called bootconf. The bootconf file contains a data structure called BootConf, which describes the system image contents and layout. mkimage includes the bootconf binary file in one of the system image memory banks.

The initial loader boots the ChorusOS operating system, jumping to the entry point of the bootconf binary (see "Overview" for more details).

bootconf is a board-independent program that gets control from the board-specific initial loader and transfers control to the board-specific bootstrap program. In some cases, the initial loader needs to pass a board-dependent parameter to the bootstrap. This parameter can be loaded into a specific CPU register, and bootconf passes the entry value of the register to the bootstrap program as an opaque parameter. The register is:

When the bootconf binary is entered, it sets up a program stack in a stack area within its data segment, finds the descriptor of the bootstrap binary in the BootConf structure, and jumps to this entry point, passing the following parameters:

To jump to the bootstrap entry point, bootconf uses the standard C function invocation conventions. The bootstrap entry point is:

typedef void (*BootEntry) (struct BootConf*, void* cookie);

BootConf Structure

The bootconf file contains an object of type BootConf and all the objects it points to. It describes the structure of the system image in terms of its components.

typedef struct BootConf { 
    unsigned int        stamp;          /* identification stamp */
    PhMap*              kernelSpace;    /* initial address space 
															   descriptor */
    VmAddr              spaceBarrier;   /* frontier between user and 
                                           supervisor address space */
    int                 numBanks;       /* number of system image memory 
                                           banks */
    BankDesc*           bankDesc;       /* system image memory bank 
                                           descriptors */
    int                 numBins;        /* number of binaries */     
    BinDesc*            binDesc;        /* array of binary descriptors */
    int                 numSegs;        /* number of segments */ 
    BinSegDesc*         segDesc;        /* array of segment descriptors */ 
    RamDesc             ramAllocator;   /* ram occupation */ 
    KnSymbolTable*      symbols;        /* symbols for kdb */  
    EnvDesc*            env;            /* environment descriptor */ 
    void*               heapStart;      /* memory heap: */
    void*               heapCur;        /* occupied from heapStart to 
                                           heapCur */
    void*               heapLimit;      /* available from heapCur to 
                                           heapLimit */
    unsigned int        siteNumber;     /* site number used for remote 
                                           IPC */
    DevNode             rootDevice;     /* device tree */ 
    IdleFunc            idleFunc;       /* function to be executed in the 
                                           idle loop */
    DbgBsp              dbgBsp;         /* bsp for Debug Agent */     
    DbgOps              dbgOps;         /* debug Agent interface */
    BootConfRebootEntry rebootEntry;    /* reboot entry */
    RebootDesc*         rebootDesc;     /* reboot descriptor */
    void*               f_bootConf;     /* a processor-specific 
                                           descriptor */
} BootConf;

Code Example 7-11 contains an example of a BootConf structure.

The Initial Address Space

The kernelSpace field describes the initial address space. The exact interpretation of this descriptor is architecture dependent:

Other architecture-dependent requirements for the initial address space layout may be specified. See "The Boot-Kernel Interface" for details.

If required, the initial virtual address space is specified by the PhMap structure:

typedef struct PhChunk {     
                PhAddr       start;  /* chunk physical start address */     
                PhSize       size;   /* chunk size in bytes */     
                unsigned int tag;    /* chunk attributes */ 
} PhChunk;  

typedef struct PhMap {     
                int      numChunks;  /* number of items in the array */     
                PhChunk* chunk;      /* array of chunk descriptors */ 
} PhMap; 

PhMap and PhChunk are declared in source_dir/nucleus/sys/common/src/kern/bki/phMap.h and source_dir/nucleus/sys/common/src/kern/bki/phChunk.h respectively. The PhMap structure is a series of PhChunk entries, each entry describing the virtual-to-physical translation of a contiguous range of virtual addresses. The size field of a PhChunk descriptor indicates the size of the range. The starting address of the range is given by the sum of the size values of the previous ranges. Therefore, the PhMap structure must describe the entire virtual address space.

If the tag value in PhChunk is equal to KSP_INVALID the translation is invalid and access to any address in the range described by PhChunk is prohibited.

Otherwise, the start field of a PhChunk structure gives the starting address of the physical address range that the virtual address is mapped to. The tag field of a PhChunk structure specifies the attributes associated with the mapping. The highest 24 bits of tag are used to store architecture-dependent attribute sets, such as memory access rights and cache control. See "The Boot-Kernel Interface" for details of architecture-specific BKI. The lowest 8 bits are reserved for use by the microkernel, and are set to 0 by mkimage.

The value of the spaceBarrier field may influence the dimensions of the supervisor and the user virtual address spaces. The exact meaning of this field is architecture dependent.

Banks

The bankDesc structure points to an array of BankDesc system image memory bank descriptors.

typedef struct BankDesc {     
        char*    id;      /* bank's symbolic name */ 
        BankType type;    /* bank's type */ 
        char*    fs;      /* files system type symbolic name */
        VmAddr   vaddr;   /* address required for the bank's image */
        VmSize   size;    /* bank's size */ 
} BankDesc;

The bankDesc structure is declared in source_dir/nucleus/sys/common/src/kern/bki/bki.h. A memory bank is specified by vaddr, indicating the starting address within the initial address space, and size.

The type field value is a combination of the bits listed in Table 7-1.

Table 7-1 type Bits
type Bit Meaning
BANK_KSP always "1" in this version
BANK_DEVICE bank is a memory device
BANK_DEVICE_READ data from the memory device can be read
BANK_DEVICE_WRITE data from the memory device can be modified
BANK_DEVICE_EXEC instructions from the memory device can be executed

If any of the last three bits is set, the BANK_DEVICE bit must also be set.

Files in a memory bank can be organized as a volume of a file system. In this case, the fs field points to a string containing the name of the file system type (for example, "FAT" stands for MS-DOS FAT file system). Otherwise, fs is 0.

When the initial loader jumps to the bootconf entry point, the memory banks containing the BIN_BOOTCONF and BIN_BOOTSTRAP program binaries must already be installed at the address indicated by vaddr.

When the bootstrap program transfers control to the microkernel, all memory banks must be installed at the address indicated by vaddr.

Binaries

The binDesc array points to an array of BinDesc structures. Each structure describes an absolute executable binary involved in the system deployment procedure, such as a bootconf, bootstrap, microkernel, or a driver. These binaries are part of the system image.

typedef struct BinDesc {     
                char*        name;      /* binary symbolic name */     
                int          firstSeg;  /* first segment index */     
                int          lastSeg;   /* last segment index */     
                KnPc         entry;     /* binary entry point */     
                BinType      type;      /* binary type */     
                BinMode      mode;      /* binary loading mode */     
                void*        actor;     /* private for the u-kernel */ 
} BinDesc;

Each binary is composed of a set of segments. The firstSeg and lastSeg fields specify the range of segments within the array of segment descriptors, pointed to by segDesc, that store the binary described by BinDesc.

The type field specifies the binary's type and is set to one of the binType values listed in Table 7-2.

Table 7-2 binType Values
BinType Value Type of Binary
BIN_BOOTCONFbootconf program
BIN_BOOTSTRAP bootstrap program
BIN_REBOOT reboot program
BIN_DBG_AGENT system debug agent
BIN_DBG_DRIVER debug agent communication driver
BIN_KERNEL microkernel
BIN_DRIVER a ChorusOS device driver
BIN_SUPERVISOR a supervisor actor
BIN_USER a user actor

BinType values are defined in such a way that:

The mode specifies the binary activation mode and can be any combination of the BinMode flags listed in Table 7-3.

Table 7-3 BinMode Flags
BinMode Flag Meaning
BIN_DEBUGGED The binary must be launched in debug mode.
BIN_STOPPED The binary must be loaded but not started.

These flags only have meaning for BIN_SUPERVISOR and BIN_USER binaries. The value of mode is ignored for all other binary types.

The numBins field specifies the number of elements in the array pointed to by binDesc.

segDesc points to an array of segment descriptors. Each item in the array is a SegDesc data structure and describes a segment of the system image. SegDesc is declared in source_dir/nucleus/sys/common/src/kern/bki/bki.h.

typedef struct SegDesc {     
              VmAddr    kaddr;  /* image address */     
              VmAddr    vaddr;  /* execution address (from link editor) */
              VmSize    ksize;  /* image size */     
              VmSize    vsize;  /* execution size */     
              SegType   type;   /* code or data */     
              SegSpace  space;  /* address space type */ 
} SegDesc;

The descriptor specifies two things:

The segment image is stored in the memory bank containing the segment's binary. When the memory bank is installed at the required address, as specified in the BankDesc structure, the segment image can be found at kaddr. The ksize field specifies the size of the segment image contained in the memory bank.

When executed, the segment must be installed at the address specified by the vaddr field and its size must be equal to vsize. If vsize is greater than ksize, the memory from vaddr+ksize to vaddr+vsize must be initialized to zero.

The space field indicates whether a segment belongs to the initial address space. It can be set to one of the values listed in Table 7-4.

Table 7-4 SegSpace Values
SegSpace Value Meaning
SEG_KSP segment belongs to the initial microkernel address space
SEG_VIRTUAL segment does not belong to the initial microkernel address space

Only binaries of the BIN_ACTOR type can hold SEG_VIRTUAL segments.

The value of the type field can be any combination of the bits listed in Table 7-5.

Table 7-5 SegType Bit Values
SegType Bit Meaning
SEG_XIP Segment must be executed at its place in the memory bank
SEG_EXEC Segment contains instructions
SEG_READ Segment contains read-only data
SEG_WRITE Segment contains write data

If a segment is executable in place (XIP), and belongs to the initial kernel address space (for example, the SEG_XIP bit is set in the type field and the space field is equal to SEG_KSP), the segment's execution address will be the same as the segment's image address and the segment's execution size will be equal the segment's image size. Therefore, when a memory bank is installed at the address specified by its descriptor (BankDesc::vaddr), all XIP KSP segments that belong to the memory bank will have no particular installation procedure and will be ready for execution.

The numSegs field specifies the number of elements in the array pointed to by SegDesc.

RAM Occupation

The ramAllocator field describes RAM occupation by the system image. The mkbootconf tool initializes the ramAllocator state. It tags as allocated the memory dedicated to:

The ChorusOS operating system provides a library of functions to manipulate the RamDesc objects (see "RAM Allocator interface"). RamDesc is declared in source_dir/nucleus/sys/common/src/kern/bki/ram.h. These functions allow the bootstrap program to tag any range of physical addresses with an attribute.

Environment Variables

The env field points to an EnvDesc structure. The EnvDesc structure is declared in source_dir/nucleus/sys/common/src/lib/util/env.h.

typedef struct EnvDesc {     
                int     envSize;    /* current size of environment */
                int     envMaxSize; /* memory available for environment */
                char*   envPtr;     /* pointer to the environment */
} EnvDesc; 

The system image contains a data file with the initial state of the system environment variables. The env field of the BootConf structure references the contents of this file.

The envPtr field points to a sequence of environment variable definitions. Each definition is a concatenation of two strings, envVariable and envValue, where envVariable is the null-terminated string name of an environment variable, and envValue is the null-terminated string value of the environment variable.

The envSize field is the size in bytes of the environment variable definition sequence.

The envMaxSize field is the environment file size. It must not exceed envMaxSize.

Heap and Stack

The heapStart field points to the beginning of a block of statically allocated memory. The block can be used by the bootstrap program for dynamic memory allocation. For instance, the heap is used by the device tree library memory allocator (see "Initial Device Tree") to build the initial state of the board's device tree.

The heapCur field points to the first byte available for future allocation. The mkbootconf tool sets heapCur to heapStart.

The heapLimit field points to the heap memory limit. The size of the heap memory is equal to heapLimit-heapStart.

The heap is also used for the initial stack. The bootconf start-up procedure initializes the current stack pointer with the heapLimit value.

All other fields of the BootConf structure are initialized dynamically during the system bootstrap.

Example BootConf Structure

This section contains an example of a BootConf structure for the SBC8260 PPC8260 EC603e-based board.


Example 7-11 BootConf Structure

#include bki/bki.h
#include bki/f_bki.h
PhChunk __mkimage__label__kernelSpace[] = {
        { 0x0, 0x1000000, KSP_PPC60x_BAT|KSP_PPC60x_M|KSP_PPC60x_RW },
        { 0x0, 0xe000000, KSP_INVALID },
        { 0xf000000, 0x20000, KSP_PPC60x_BAT|KSP_PPC60x_G|KSP_PPC60x_I|
            KSP_PPC60x_RW },
        { 0x0, 0xecfe0000, KSP_INVALID },
        { 0xfc000000, 0x4000000, KSP_PPC60x_BAT|KSP_PPC60x_RO }
};
BankDesc __mkimage__label__banks[] = {
        { "sys_bank", BANK_KSP, 0, 0x100000, 0xb3000}
};
BinDesc __mkimage__label__binDesc[] = {
        {
                "dbgBsp",
                0,
                0,
                0x10042c,
                BIN_DBG_DRIVER,
                0 | 0
        },
        {
                "dbgAgent",
                1,
                1,
                0x104494,
                BIN_DBG_AGENT,
                0 | 0
        },
        {
                "boot",
                2,
                2,
                0x108000,
                BIN_BOOTSTRAP,
                0 | 0
        },
        {
                "reboot",
                3,
                3,
                0x10d27c,
                BIN_REBOOT,
                0 | 0
        },
        {
                "kern",
                4,
                5,
                0x125418,
                BIN_KERNEL,
                0 | 0
        },
        {
                "D_tbDec",
                6,
                6,
                0x186000,
                BIN_DRIVER,
                0 | 0
        },
        {
                "D_quicc8260",
                7,
                8,
                0x188000,
                BIN_DRIVER,
                0 | 0
        },
        {
                "D_fccEther",
                9,
                9,
                0x18e000,
                BIN_DRIVER,
                0 | 0
        },
        {
                "PD",
                10,
                11,
                0x195000,
                BIN_SUPERVISOR,
                0 | 0
        },
        {
                "kernonly_symb",
                12,
                12,
                0x198000,
                BIN_DBG_AGENT,
                0 | 0
        },
        {
                "kernonly_bconf",
                13,
                13,
                0x1b0000,
                BIN_BOOTCONF,
                0 | 0
        }
};
BinSegDesc __mkimage__label__segDesc[] = {
        {
                0x100000,
                0x100000,
                0x608,
                0x608,
                SEG_XIP | SEG_EXEC | SEG_READ | SEG_WRITE,
                SEG_KSP
        },
        {
                0x101000,
                0x101000,
                0x6234,
                0x6234,
                SEG_XIP | SEG_EXEC | SEG_READ | SEG_WRITE,
                SEG_KSP
        },
        {
                0x108000,
                0x108000,
                0x4358,
                0x4358,
                SEG_XIP | SEG_EXEC | SEG_READ | SEG_WRITE,
                SEG_KSP
        },
        {
                0x10d000,
                0x10d000,
                0x2200,
                0x2200,
                SEG_XIP | SEG_EXEC | SEG_READ | SEG_WRITE,
                SEG_KSP
        },
        {
                0x110000,
                0x110000,
                0x75920,
                0x75920,
                SEG_XIP | SEG_EXEC | SEG_READ | SEG_WRITE,
                SEG_KSP
        },
        {
                0x186000,
                0x8000,
                0x0,
                0x1a000,
                SEG_READ | SEG_WRITE,
                SEG_KSP
        },
        {
                0x186000,
                0x186000,
                0x1be4,
                0x1be4,
                SEG_XIP | SEG_EXEC | SEG_READ | SEG_WRITE,
                SEG_KSP
        },
        {
                0x188000,
                0x188000,
                0x50dc,
                0x50dc,
                SEG_XIP | SEG_EXEC | SEG_READ | SEG_WRITE,
                SEG_KSP
        },
        {
                0x18e000,
                0x22000,
                0x0,
                0x10,
                SEG_READ | SEG_WRITE,
                SEG_KSP
        },
        {
                0x18e000,
                0x18e000,
                0x6654,
                0x6654,
                SEG_XIP | SEG_EXEC | SEG_READ | SEG_WRITE,
                SEG_KSP
        },
        {
                0x195000,
                0x195000,
                0x22c4,
                0x22c4,
                SEG_XIP | SEG_EXEC | SEG_READ | SEG_WRITE,
                SEG_KSP
        },
        {
                0x198000,
                0x23000,
                0x0,
                0xc04,
                SEG_READ | SEG_WRITE,
                SEG_KSP
        },
        {
                0x198000,
                0x198000,
                0x170c8,
                0x170c8,
                SEG_XIP | SEG_EXEC | SEG_READ | SEG_WRITE,
                SEG_KSP
        },
        {
                0x1b0000,
                0x1b0000,
                0x2c10,
                0x2c10,
                SEG_XIP | SEG_EXEC | SEG_READ | SEG_WRITE,
                SEG_KSP
        },
        {
                0,
                0,
                0,
                0,
                0,
                0
        },
        {
                0,
                0,
                0,
                0,
                0,
                0
        }
};
PhChunk __mkimage__label__ramChunks[64] = {
        { 0x0, 0x4000, PH_RAM_NONEXISTENT },
        { 0x4000, 0x20000, PH_RAM_ALLOCATED },
        { 0x24000, 0xdc000, PH_RAM_NONEXISTENT },
        { 0x100000, 0xb3000, PH_RAM_ALLOCATED },
        { 0x1b3000, 0xffe4d000, PH_RAM_NONEXISTENT }
};
EnvDesc __mkimage__label__environ = {   0x0, 0x0, (char*) 0x100000};
int heapSize = 0x2000;
char heap[0x2000];
BootConf __mkimage__label__bootConf = {
        0x38216b39,
        0,
        {
                5,
                __mkimage__label__kernelSpace
        },
        0x0,
        1,
        __mkimage__label__banks,
        11,
        __mkimage__label__binDesc,
        14,
        __mkimage__label__segDesc,
        {
                64,
                5,
                __mkimage__label__ramChunks
        },
        0,
        &__mkimage__label__environ,
        heap,
        heap,
        heap + 8192,
};
BootConf* bootConf= &__mkimage__label__bootConf;

The kernelSpace field of the BootConf structure contains the description of the kernel address space that must be established by the microkernel's memory management module.

The descriptor specifies that the first 16M of the physical address space, which corresponds to RAM potentially available on the board, must be mapped to the first 16M of the virtual address space using one Block Address Translation register pairs (IBAT and DBAT). The space must be mapped read/write enforcing memory coherency (that is, the memory control bit M must be set in the BATs).

Another pair of BATs must be used to map to the virtual address range from 0x0f000000 to 0x0f020000. These are the PPC8260 internal memory mapped registers which occupy the physical addresses from 0xf000000 to 0x0f020000. The space must be mapped read-only, bypassing the memory cache.

The system image is stored in one memory bank that must be installed at the address 0x00100000. The size of the bank is 0x00b3000 bytes.

A third pair of BATs must be used to map the last 64M of the physical address space, which corresponds to ROM (FLASH) potentially available on the board. This space must be mapped read-only.

The system image contains 11 binaries:

Some binaries (for example, boot) have just one XIP segment which includes all text and data. Others (for instance, kern) have zero-initialized data (bss), link-edited in a separate segment.

The RAM allocator specifies that the RAM from address 0x00004000 to address 0x00024000 and from address 0x00100000 to address 0x001b3000 is allocated. The memory from 0x00100000 to 0x001b3000 is occupied by the system image memory bank, and the memory from 0x00004000 to 0x00024000 by the system control blocks and bss.