This section describes the common routines used in the bootstrap implementation.
Example 3-6 is an example of binInstallByMask() and binInstallByType() source code, provided in kernel/snippet/nucleus/boot_tools/binInstall.c.
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 3-1.
Table 3-1 mask Bitsmask Bit | Meaning |
---|---|
BIN_STANDALONE | install bootstrap program and debug agent binaries |
BIN_KERNEL | install microkernel |
BIN_ACTOR | install built-in drivers and actors |
binInstallByMask() 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 zeros bss. binInstallOne() only processes segments that belong to the initial kernel address space, SEG_KSP.
binInstallOne() assumes that the memory banks containing the installing binaries have already been installed and that the destination addresses are already accessible.
The dbg_start() function starts the debug agent driver and the debug agent. Example 3-7 is dbg_start() source code provided in kernel/snippet/nucleus/boot_tools/dbg_start.c.
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); }
dbg_start() 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 bootConf 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.
dbg_start() assumes that the debug agent and its driver are already installed.
Finaly, dbg_start() makes console IO available for the bootstrap program by intializing its printf()/scanf() support library.
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. reboot_start() differentiates between a cold boot and a hot boot by testing the value of the rebootDesc field in the bootConf structure:
Initially, in the case of a cold boot, rebootDesc is NULL. The reboot program subsequently initializes rebootDesc with a non-zero value (see "Reboot Program Initialization") . During the subsequent hot boot, the value of rebootDesc is passed from the reboot program to the bootconf program (see "Hot Reboot").
For a hot boot, rebootDesc is also initially set to NULL. The bootconf program then re-initializes the field with the argument passed from the reboot program (see Example 3-5).
reboot_start() calls binInstallByType() (see "binInstallByMask and binInstallByType()") to install the reboot program. It then retrieves the descriptor pointing to the reboot program binary and jumps to its entry point, passing the address of the bootConf structure as an argument.
reboot_start() assumes that the memory banks containing the reboot program have already been installed and that the destination addresses are already accessible.
Example 3-10 is reboot_start() source code provided in kernel/snippet/nucleus/boot_tools/reboot_start.c.
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); } } }
The prstInstall() function is used by the bootstrap program to check whether:
there are RAM blocks already occupied by persistent memory devices that must be registred (tagged) as allocated
there are requests for additional RAM blocks to be allocated for new persistent memory devices
Example 3-10 is prstInstall() source code provided in kernel/snippet/nucleus/boot_tools/prstInstall.c.
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; } } } }
The kernel_start() function calls binInstallByMask() (see "binInstallByMask and binInstallByType()") to install the microkernel and all actors that belong to the initial kernel 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.
kernel_start() 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 break points, for example
kernel_start() assumes that the memory banks containing the microkernel and the installing actors have already been installed and that the destination addresses are already accessible.
Example 3-10 is kernel_start() source code provided in kernel/snippet/nucleus/boot_tools/kernel_start.c.
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); } } }
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:
RAM_ALLOCATED, indicating that the physical address belongs to an occupied portion of RAM
RAM_FREE, indicating that the physical address belongs to a portion of RAM available for allocation
RAM_NONEXISTENT, indicating that the physical address does not belong to RAM (or is not available for allocation as RAM)
Three routines are provided for managing RamDesc
. These
routines can be used by the bootstrap program to update RamDesc
according to
the target BKI.
ram_tag()
void ram_tag (RamDesc* ram, PhAddr paddr, Phsize size, int tag);
ram_tag() attempts to change the tag values of all addresses from the address range of size size starting from paddr to the value specified by tag. The ram_tag() function only changes a tag if the initial value is RAM_FREE or RAM_NONEXISTENT. It does not change a tag if the initial value is RAM_ALLOCATED.
ram_alloc()
int ram_alloc (RamDesc* ram, PhAddr*, paddr, PhSize size, int align);
ram_alloc() allocates a portion of available RAM, tags the corresponding address range as RAM_ALLOCATED, and returns the physical address of the allocated RAM in the variable that paddr points to. align specifies the RAM alignment constraints (in bytes), or is 0 if there are no constraints. ram_alloc() returns 1 if successful, and 0 if the allocation failed.
ram_free()
void ram_free (RamDesc* ram, PhAddr paddr, PhSize size);
ram_free() changes the tag values for the specified range of physical addresses to RAM_FREE.