This chapter describes how to build your ChorusOS operating system for your chosen target.
"Creating the System Image" explains how to build a system image.
"Reshaping the System Image" explains how to transform the system image into a form suitable for deployment.
"Testing Your Ported System" explains how to test your system.
This section explains what the system image is and how to create it using the tools provided.
The system image is the initial state of the target board's physical memory. It contains a set of files organized in one or more memory banks.
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.
To build the system image, you use an XML-based system. This system has two components:
The ChorusOS operating system configuration tool, ews, enables you to create, view, and modify a system image's configuration files, which are defined using XML syntax. You can also modify the configuration files directly, but this is not recommended.
The mkimage tool creates the system image as defined in the configuration file.
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.
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 into 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 overlapping linking areas can overlap.
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.
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 bootconf binary.
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:
The address of the BootConf structure
An opaque value from the initial loader
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);
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;
Example 2-1 contains an example of a BootConf structure.
kernelSpace describes the initial address space. The exact interpretation of this descriptor is architecture dependent:
In some cases, it describes the address space which must be established in the hardware when entering the microkernel.
In some cases it describes the address space that must be established by the microkernel's memory management module.
Some architectures do not require kernelSpace at all (for example when there is no MMU).
Other architecture-dependent requirements for the initial address space layout may be specified. See "The Boot-Kernel Interface (BKI)" 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 ~nucleus/src/lib/bki/phMap.h and ~nucleus/src/lib/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 (BKI)" 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 (see "The Boot-Kernel Interface (BKI)" for details).
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 ~nucleus/sys/common/src/lib/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 2-1.
Table 2-1 type Bitstype 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 by organized as a volume of a file system. In this case, the fs field points to a stiring containing the name of the file system type (for example, "FAT" stands for MS-DOS FAT files 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.
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 2-2.
Table 2-2binType
ValuesBinType Value | Type of Binary |
---|---|
BIN_BOOTCONF | bootconf |
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 BIN_STANDALONE bit is 1 in, and only in, BIN_BOOTSTRAP, BIN_DBG_AGENT, and BIN_DBG_DRIVER.
The BIN_ACTOR bit is 1 in, and only in, BIN_DRIVER, BIN_SUPERVISOR, and BIN_USER.
The BIN_KERNEL bit is 1 in, and only in, BIN_KERNEL.
The mode specifies the binary activation mode and can be any combination of the BinMode flags listed in Table 2-3.
Table 2-3BinMode
FlagsBinMode 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.
numBins 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 ~nucleus/sys/common/src/lib/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 location of the segment's image
Where the segment must be installed for execution
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 2-4.
Table 2-4SegSpace
ValuesSegSpace Value | Meaning |
SEG_KSP | segment belongs to the initial kernel address space |
SEG_VIRTUAL | segment does not belongto the initial kernel 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 2-5.
Table 2-5 SegType Bit ValuesSegType 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.
numSegs specifies the number of elements
in the array pointed to by SegDesc
.
ramAllocator describes RAM occupation by the system image. The mkbootconf tool initializes the ramAllocator state. It tags as allocated the memory dedicated to:
All RAM based system image memory banks (see "Banks").
Global system control blocks (for example CpuContext
and ChorusContext
) that are mapped to non-virtual
linking areas (see "Areas").
All non-XIP segments (XIP segments are contained in memory banks) that are mapped to non-virtual linking areas (see "Areas").
The ChorusOS operating system provides a library of functions to manipulate
the RamDesc
objects (see "RAM Allocator interface"). RamDesc is declared in ~nucleus/sys/common/src/lib/bki/ram.h. These functions allow the bootstrap program to tag any range
of physical addresses with an attribute.
env points to an EnvDesc structure. EnvDesc is declared in ~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.
envPtr 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.
envSize is the size in bytes of the environment variable definition sequence.
envMaxSize is the environment file size. envSize must not exceed envMaxSize.
heapStart 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.
heapCur points to the first byte available for future allocation. mkbootconf sets heapCur to heapStart.
heapLimit 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 startup procedure initializes the current stack pointer with the heapLimit value.
All other fields of the BootConf structure are initialized dynamically during the system bootstrap.
This section contains an example of a BootConf
structure for the SBC8260 PPC8260 EC603e-based board.
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:
dbgAgent and dbgBsp are the system level debug agent and its BSP
boot is the bootstrap program
reboot is the reboot program
kern is the microkernel
D_* are three driver binaries
PD is the per-actor/thread data manager system actor
kernonly_symb is the KDB symbol table
kernonly_bconf is the bootconf binary
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.
By default, mkimage puts the contents of each system image memory bank into a file on the development system. That is, it creates one file for each memory bank. You may then need to transform these files into a format suitable for deployment on the target board.
Optionally, mkimage can generate a single executable ELF file containing all the memory banks that hold the system image. mkimage generates one ELF section and one ELF segment. The base address of both is the base address of the first memory bank. The size of both segments is the sum of the sizes of all the memory banks.
In order to test your system, you need to include the debug agent in the system image. Figure 2-1 shows the component architecture including the debug agent.
For more information about how to debug your system, see the Debug API specification, which is included in printable format with the documentation set for this release, and the ChorusOS 4.0 Introduction. See also Appendix B, Porting the Debug Agent, for information about porting the debug agent.