ChorusOS 5.0 Board Support Package Developer's Guide

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.