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.
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.
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.
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.
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):
The top level my_board directory that contains all the files related to BSP component definition.
The src directory which contains the boot, reboot and dbg directories as well as the project.tmpl file. These directories contain the boot, reboot and debug agent source files for your particular platform.
You may also need to add any drivers that are specific to your board. For more information on this see "Driver Source File Structure").
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.
DRV: the generic and family specific drivers and BSP source component. This must be set to the installed directory pathname source_dir/nucleus/bsp. If you have added other drivers that are not within the ChorusOS DRV source tree then you may need to add a MYDRV component which is set to the pathname of your driver source.
BSP: the platform-specific source component. This must be set as the pathname of the my_board directory which contains the boot, reboot and debug agent parts of the BSP.
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. )
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.
For this example, the new BSP directory will be called my_board. This top level directory must contain at least the following four files:
Makefile.bin
Makefile.src
target.xml
target_action.xml
This section contains a short description of these files, showing the template files.
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.
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.
<!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.
This src directory must contain the following two files:
Project.tmpl
Imakefile
It also contains the following directories:
boot (see "The boot Directory")
dbg (see "The dbg Directory")
reboot (see "The reboot Directory")
The Project.tmpl file is used to produce a makefile to build your new BSP component (see ChorusOSMkMf(1CC)).
#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.
#define IHaveSubdirs SUBDIRS = boot dbg reboot
This Imakefile lists all of the sub-directories for each BSP class and should not be edited.
This is the directory where the source files for the boot program reside:
Imakefile
boot.c
This section contains a short description of the boot files, showing examples from the template directory.
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.
void start() { /* Code for your BSP boot */ }
This file needs to be adapted to your board.
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:
Imakefile
dbgBsp.c
uart.c
wdt.c
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.
#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.
#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 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:
Imakefile
reboot.c
This chapter describes how to write a bootstrap for your system. The bootstrap is implemented as three binaries:
"Power-up Program Implementation" explains how to write a power-up initialization program
"bootconf Implementation" explains the implementation of the generic bootconf program
"Bootstrap Program Implementation" explains how to write a bootstrap program
"The Boot-Kernel Interface" describes the boot kernel interface
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.
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.
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.
.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".
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.
The source_dir/nucleus/bsp/src/family/powerpc/boot_tools/bconf_crt0.s file contains the bootconf start-up routine, 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:
The .chr.my.rw.vaddr section indicates the base link-editing address of the read-write segment.
The .chr.my.rw.kaddr section indicates the beginning of the read-write segment's image within the memory bank.
The .chr.my.rw.size section indicates the size of the read-write segment.
The .chr.my.bss.vaddr section indicates the base link-editing address of the bss segment.
The .chr.my.bss.size section indicates the size of the bss segment.
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").
The source_dir/nucleus/bsp/src/boot_tools/bconf_main.c file contains the bootconf main routine, 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:
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.
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:
Initializes CPU items such as interrupts and the memory cache. This depends on the state in which the initial loader (or power-up initialization) leaves the CPU.
Installs banks, as specified by the BootConf structure.
Discovers existing RAM and modifies the ramAllocator field in the BootConf structure.
If necessary, builds the initial microkernel virtual address space, as defined by the kernelSpace field in the BootConf structure.
Launches the microkernel.
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.
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").
This section describes the common routines used in the bootstrap implementation.
Example 4-6 is an example of binInstallByMask() and binInstallByType() source code, provided in source_dir/nucleus/bsp/src/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 4-1.
Table 4-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 |
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.
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.
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.
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:
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 4-5).
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.
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 registered (tagged) as allocated
there are requests for additional RAM blocks to be allocated for new persistent memory devices
Code Example 4-9 is prstInstall() source code provided in source_dir/nucleus/bsp/src/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 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.
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);
The ram_tag() function 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);
The ram_alloc() function 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);
The ram_free() function changes the tag values for the specified range of physical addresses to RAM_FREE.
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:
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])
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.
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.
#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
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:
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])
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.
The L1 data cache is assumed to be disabled.
_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 */
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.
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:
binDesc must describe the binaries
of the microkernel and of all drivers and actors that the microkernel will
launch. The microkernel assumes that all segments of type SEG_KSP
are already installed at their execution addresses and that the segment's
zero-initialized data is already set to zero. binDesc
can contain descriptors of other binaries. See "Binaries"
for more information about binDesc.
ramAllocator must tag as RAM_FREE the portions of RAM available for immediate allocation.
All other RAM must be tagged as RAM_ALLOCATED. When the
system initialization procedure no longer requires the BootConf structure, the microkernel can reuse the RAM occupied by binaries
of the types BIN_BOOTSTRAP
and BIN_BOOTCONF
.
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.
/* * 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:
The root node must be defined and have the following properties:
PROP_NODE must have the value NODE_ROOT.
PROP_FAMILY must be defined and identify the processor family. The value of the property is a family-dependent string, such as FAMILY_POWERPC or FAMILY_INTEL.
PROP_PLATFORM must be defined and identify the board. The value is a platform-dependent string, such as "Motorola MCP7xx" or "Intel x86 PC/AT".
The CPU node must be defined and have the following properties:
PROP_NODE must have the value NODE_CPU.
PROP_CLOCK_FREQ specifies the CPU clock frequency. Its value is of type PropClockFreq.
A timer device node with the PROP_SYSTEM_TICK property must be defined, to describe the system clock.
Optionally, a second timer device node with the PROP_PERF_TICK property can be used by the microkernel for benchmarks.
This section details the state that the CPU should be in for PowerPC BKI acceptance.
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.
Register r3 must contain a pointer to the BootConf structure.
External interrupts must be disabled.
Instruction and data translations must be disabled.
Internal (L1) memory cache must be disabled.
The MMU must be disabled; all MMU registers and Translation Lookaside Buffer (TLB) entries, if any, must be in a reset/disabled/invalidated state.
The processor must be in Supervisor privilege state.
Machine-check exceptions must be enabled.
The processor must be in a recoverable exception state.
Floating point instructions must be disabled.
The kernelSpace field in the BootConf structure must point to a description of the initial
space that must be 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:
KSP_PPC60x_BAT bit specifies
that the mapping must be established using two Block Address Translation (BAT)
registers. In this case, the mapping specified by PhChunk
(the
physical address, the corresponding virtual address and the size) must be
properly aligned as required by the PowerPC 60x specifications. The microkernel
will establish the required mapping in one data BAT (DBAT) and one instruction
BAT (IBAT). Therefore, only four kernelSpace entries
can have BAT mappings. If the KSP_PPC60x_BAT bit is not
specified, the microkernel uses page address translations to establish the
required mapping.
KSP_PPC60x_W bit specifies that the write-through memory access attribute must be set in the corresponding BAT or PTEs.
KSP_PPC60x_I bit specifies that the caching-inhibited memory access attribute must be set in the corresponding BAT or PTEs.
KSP_PPC60x_M bit specifies that the memory coherency memory access attribute must be set in the corresponding BAT or PTEs.
KSP_PPC60x_G bit specifies that the guarded memory access attribute must be set in the corresponding BAT or PTEs.
KSP_PPC60x_RW bits specify that read and write access are allowed.
KSP_PPC60x_RO bits specify that only read access is allowed.
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.
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:
PhChunk
KSP Region TypePhChunk Bit | Meaning |
---|---|
KSP_US_TYPE_RAM | one-to-one RAM mapping |
Table 4-3
PHChunk
Data AttributesPhChunk 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 AttributesPHChunk 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;
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" explains the implementation of a reboot program, using the SBC8260 board as an example.
"Reboot Program Implementation Framework" describes the library routines that can be used in a reboot program.
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.
Code Example 5-1 is the reboot program initialization source code provided in source_dir/nucleus/bsp/powerpc/sbc8260/src/reboot/reboot.c.
#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.
Code Example 5-2 is the HotRebootDesc structure definition provided in source_dir/nucleus/sys/common/src/kern/bki/reboot.h.
/* * 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:
The allocation request comes from the sysReboot() system call. The system call specifies the name and size of the device to be created during the subsequent hot boot.
The reboot program allocates a new PrstChunk descriptor and initializes the id and size fields with the values specified by the sysReboot() call. See "Hot Reboot" for details.
Then, after reboot, the bootstrap program dynamically allocates RAM blocks and initializes the paddr field. It also sets the PRST_CHUNK_ALLOCATED bit in the status field. See "kernel_start()" for details.
Finally, the microkernel allocates a virtual address range and sets the vaddr field. It also sets the PRST_CHUNK_MAPPED bit in the status field.
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
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.
Code Example 5-4 is the cold reboot service routine source code provided in source_dir/nucleus/bsp/powerpc/sbc8260/src/reboot/reboot.c.
void rebootCold(KnRebootReq* req) { printf ("Cold reboot ...\n"); resetOp(0); }
This cold reboot service calls the debug agent hardware reset service routine.
Code Example 5-5 is the hot reboot service routine source code provided in source_dir/nucleus/bsp/powerpc/sbc8260/src/reboot/reboot.c.
#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:
Allocates, if required, a new persistent memory device descriptor. See "prstExtend() Routine" for details.
Enters in the real mode, disabling the memory management unit.
Jumps to the bootconf program reboot entry point.
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.
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.
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.
This section describes the library routines that can be used by reboot programs.
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.
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.
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.
/* * 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.
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.
This chapter describes the architecture of the ChorusOS debug agent with details on how to adapt it to run on your new board.
"The Debug Agent" describes the ChorusOS debug agent.
"dbgAgent" describes the dbgAgent binary file and considerations when porting to a new target board.
"dbgBsp" describes the dbgBsp binary file and considerations when porting to a new target board.
"BSP Hot Swap Support" describes the BSP hot swap support and considerations when porting to a new target 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 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:
dbgAgent contains generic and processor specific debug agent code. It is platform independent.
dbgBsp contains serial line device driver, board reset functions, and other debug critical drivers.
The interface between dbgAgent and dbgBsp is described in source_dir/nucleus/sys/common/src/kern/bki/dbgBsp.h.
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.
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.
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.
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
dbgBsp.c:
Declares and initializes a dbgBsp structure (as defined in bki/dbgBsp.h)
Implements the board reset function
Calls the serial and watchdog driver initialization functions and initializes the dbgBsp field in the BootConf structure
Contains dbgBsp_init, which is an entry point for dbgBsp
ns16550Bsp.c:
The ns16550_inb(), ns16550_outb() functions provide access to device registers
The ns16550_divisor() function calculates the divisor corresponding to a given baud rate, which is then used to program the baud rate in the generic driver
The ns16550_ioremap() function is called each time the I/O space mapping is changed
epfpldBsp.c:
The epfpld_init() function assigns an initial physical address to the watchdog device
The epfpld_pat() function resets the watchdog counter if the system is stopped by the system debugger
The epfpld_ioremap() function assigns a new physical address to the watchdog device
Imakefile:
Builds the dbgBsp binary file from dbgBsp.o, ns16550.o and epfpld.o
Includes $(DRV_DIST_BIN)/dbg/ns16550.o, $(DRV_DIST_BIN)/dbg/dbg/epfpld.o (the generic drivers) and $(DRV_DIST_BIN)/dbg/dbgUartConf.o (the serial line configuration) in the binary file
Declares dbgBsp_init as the entry point
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:
stopping a device driver instance when a board is extracted
starting a new device driver when a board is inserted
The implementation of the BSP hot swap support in the ChorusOS operating system is across three levels of drivers:
Hot Swap Controller
pciswap generic driver
PCI bridge PciswapEvent handlers
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.
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
Hot Swap Controller
PciSwap driver
PciSwap client
MCP750
Hot Swap Controller
PciSwap driver
PciSwap client
CPN5360
Hot Swap Controller
PciSwap driver
PciSwap client
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.
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.
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.
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.
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.
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" describes a ChorusOS system image and explains how to build it.
"Imported Objects" lists the predefined objects that can be used within your configuration files to specify different conditions.
"Summary of Required Definitions" lists the objects that must be specified in your configuration.
"The target.xml File Structure" describes the content and structure of the target.xml file.
"The mkimage System Image" describes the content of the system image created by mkimage.
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 ChorusOS operating system configuration tools, ews and configurator, enable you to create, view, and modify a system image's configuration files, which are defined using EMCL (based on XML). You can also modify the configuration files directly. In fact when you create the system image for a new target board, you will need to create the target configuration files using a text editor before being able to use these tools.
The mkimage tool creates the system image as defined in the configuration files.
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:
target.xml specifies the main board-specific configuration
target_action.xml specifies the board-specific system image reshaping procedure
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:
CPU: EC603e
Physical memory map: The power-up initialization code programs the chipset memory map decoder registers in such a way that in the processor's physical address space, all available RAM is contiguously mapped to (physical) address 0x0, and at least 1M of NOR flash memory is contiguously mapped to the end of the physical address space (that is, to the physical address 0xfff00000).
Deployment: The ChorusOS configuration supports two deployment schemes:
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:
SYSTEM specifies the kind of ChorusOS operating system currently built and is an enumeration. The possible values of SYSTEM are:
kernonly: the system image will contain the microkernel, built-in drivers and possibly built-in application actors
chorus: the system image will contain the microkernel, built-in drivers, the POSIX personality and possibly built-in application actors
uartTest or flashTest: the system image will contain the corresponding device driver (UART or flash memory) test program
kts or ktu: the system image will contain the general purpose microkernel test suite to be run in the supervisor (kts) or the user (ktu) mode
benchs or benchu: the system image will contain the microkernel benchmark program to be run in the supervisor (benchs) or the user (benchu) mode
BOOT_MODE specifies how the generated system image will be deployed on the target board, and is an enumeration. The possible values for BOOT_MODE are:
ROM: the system image will be deployed (burned, flashed) in ROM or Flash
RAM: the system image will be downloaded in RAM from a local device or the network
BUILD_DIR is a string specifying the build directory prepared by the configure utility during the ChorusOS operating system installation.
BSP_DIR is a string specifying the installation directory of the BSP configuration and bootstrap package.
DRV_DIR is a string specifying the installation directory of the family-specific and generic drivers package.
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:
IMAGE_DIR which specifies the mkimage tool output repository (Global variables folder).
sys_bank which specifies the default system image memory bank (Banks folder).
banks which specifies the list of system image memory banks (Misc folder).
ram_area which specifies the default system image linking area--only used for bss. (Areas folder).
supervisor_context_area_ref which specifies a reference to the address range where global system control blocks must be allocated (Areas folder).
user_context_area_ref which specifies a reference to the address range where user software registers must be allocated (Areas folder).
BSP_files which specifies the list of BSP files that must be added to the system image. The full paths of these files are specified in the Files folder (System image folder).
env_file which specifies the path of the generated file containing the initial state of the system environment variables. It is defined in the Auto-generated files folder.
symb which specifies the path of the generated file containing the kernel debugger symbols (Auto-generated files folder).
bootconf which specifies the path of the bootconf application (Auto-generated files folder).
heap_size which specifies the bootconf heap size (Misc folder).
ksp which specifies the microkernel address space configuration (Kernel address space folder).
space_barrier which must be defined for x86 and MPC860 based platforms.
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.
A target.xml file contains a number of hierarchical folders. The top level of folders is the following:
Global variables
Banks
Areas
Kernel address space
Link-edit models
Files
System image, which contains the following folders:
Files
Auto-generated files
Miscellaneous
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.
<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.
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:
addr is an integer that specifies the address at which the memory bank must be installed (burned or downloaded) during the system deployment process. Usually the address is physical on PowerPC and x86 systems and virtual in UltraSPARC systems.
size is an integer specifying the maximum size of the memory bank.
ram is a boolean specifying whether the bank occupies RAM at installation.
If the value of ram is true, mkimage determines the portion of the system RAM that the memory bank image will occupy after installation, and tags that RAM as PH_ALLOCATED in the initial state of the RAM allocator.
If the value of ram is false, the bank has no impact on the initial state of the RAM. For example, the value of ram must be false if the memory bank is to be burned in ROM.
The default value of ram is true.
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.
<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".
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:
addr is an integer specifying the starting address of the linking area. The address can be physical or virtual on PowerPC and x86 based systems and is usually virtual on UltraSPARC based systems.
size is an integer specifying the size of the linking area.
virtual is a boolean specifying whether the linking area occupies virtual address space. If the value of virtual is false, mkimage determines the portion of the system RAM that the segments link-edited into this area will occupy and tags the RAM as PH_ALLOCATED in the initial state of the RAM allocator.
If the value of virtual is true, the area will not impact the initial state of the RAM allocator. For example, the value of virtual must be true when the area object corresponds to a portion of a user virtual address space.
private is a boolean specifying whether or not the linking area is shared. If the value of private is false, the linking area is shared by all binaries that use it. This means that any two segments link-edited into this area will not overlap. These types of linking areas are typically used to link-edit supervisor address space binaries.
If the value of private is true, one instance of the linking area is created for each binary that uses it. This means that two segments link-edited into this area overlap if, and only if, they belong to two different binaries. These types of linking areas are typically used to link user address space actors.
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.
<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:
ram_area has the same definition as described above but is used to link-edit BIN_STANDALONE binaries, such as bootconf and bootstrap programs, the debug agent and the microkernel only
supervisor_actor_area is used to link-edit all built-in drivers and supervisor actors
user_actor_area is used to link-edit all user actors
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.
<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>
A board-specific configuration must define two references to linking area objects where global system control blocks are allocated:
supervisor_context_area_ref specifies the area where supervisor address space global control blocks will be allocated
user_context_area_ref specifies the area where the memory for user thread private data will be 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.
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), user_context_area_ref is defined as a reference to ram_area:
<definition name='user_context_area_ref'> <description>reference to the address range where user software registers must be allocated</description> <condition><not><var name='VIRTUAL_ADDRESS_SPACE' /></not> </condition> <type name='Area' ref-only='yes' /> <ref name='ram_area' /> </definition>
mkimage allocates user thread private data in ram_area.
When the microkernel is configured with one of memory management modules implementing the virtual memory model (that is, when the value of the VIRTUAL_ADDRESS_SPACE configuration variable is true), user_context_area_ref defines a specific address range (linking area) in the user virtual address space:
<definition name='user_context_area'> <description>address range where user software registers must be allocated</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>0xeffff000</const>></value> <value field='size'><const>0x00001000</const></value> </definition> <definition name='user_context_area_ref'> <description>reference to the address range where user software registers must be allocated</description> <condition><var name='VIRTUAL_ADDRESS_SPACE' /></condition> <type name='Area' ref-only='yes' /> <ref name='user_context_area' /> </definition>
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:
<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.
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:
bootconf_model specifies the default link-edit model for bootconf programs
bootstrap_model specifies the default link-edit model for bootstrap programs
debug_driver_model specifies the default link-edit model for debug agent drivers
debug_agent_model specifies the default link-edit model for debug agents
microkernel_model specifies the default link-edit model for the microkernel
driver_model specifies the default link-edit model for built-in drivers
supervisor_actor_model specifies the default link-edit model for supervisor actors
user_actor_model specifies the default link-edit model for user actors
reboot_model specifies the default link-edit model for reboot programs
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:
type is an enumeration specifying how the resulting program will be activated by the ChorusOS operating system boot process.
If a type value is not specified, the program will be activated by a specific procedure such as processor hardware reset or ChorusOS operating system afexec.
ro is an optional field specifying link-edit instructions for read-only sections (such as text and initialized read-only data). If the field is not specified, read-only sections will be link-edited using instructions for read-write sections.
rw is an optional field specifying link-edit instructions for read-write data sections. If the field is not specified, the program must not contain read-write data sections.
bss is an optional field specifying link-edit instructions for zero initialized read-write data sections.If the field is not specified, zero-initialized read-write sections will be link-edited using instructions for read-write sections.
strip is an enumeration specifying whether or not ELF headers, symbols and debug information must be included in the resulting program image.
If the value of strip is NOTHING, the resulting image will contain all ELF headers, symbol tables and debug information.
If the value of strip is ALL, the resulting image will contain program segment images only.
If the value of strip is SYMBOLS, the resulting image will contain program segment images and ELF file and segment headers.
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:
The optional area field is a reference to an Area object. If the field is defined, each section must be link-edited into an address range dynamically allocated in the linking area, whereas if the field is not defined, each section must be link-edited at the address equal to the section's image start in the memory bank.
If the value of the boolean xip is true, the segment must be executed at its place in the memory bank. If the value of the boolean xip is false the segment image must be copied in RAM for execution.
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:
True: the linking area must be virtual
False: the linking area can be virtual or physical
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.
<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.
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:
path is a character string specifying the pathname of the file.
bank is a reference to the Bank object specifying the memory bank where mkimage will store the file image.
binary is an optional field specifying the link-edit model for the file. If binary is not specified, the file will be processed as a raw data file.
Code Example 7-7 is an extract of the SBC8260 board-specific configuration file, and contains the definitions of the File objects.
<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>
The system image folder contains three sub-folders:
Files
Auto-generated files
Misc
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.
<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>
The mkimage tool generates three files:
The bootconf program, ${IMAGE_DIR}/bconf/${SYSTEM}_bconf
A program that stores the kdb symbol tables, ${IMAGE_DIR}/symb/${SYSTEM}_symb
A data file that stores the initial state of the system environment, ${IMAGE_DIR}/environ
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.
<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>
The misc folder contains the following:
banks is a list of references to all Bank objects that the system image will contain.
heap_size is an integer specifying the size of the bootconf program heap.
space_barrier is an integer specifying, if required, the highest address of the supervisor address space and the lowest address of the user address space.
Code Example 7-10 is an extract of the SBC8260 board-specific configuration file, and contains the Misc definition.
<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>
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.
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.
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 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:
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;
Code Example 7-11 contains an example of a BootConf structure.
The kernelSpace field describes the initial address space. The exact interpretation of this descriptor is architecture dependent:
In some cases, kernelSpace describes the address space which must be established in the hardware when entering the microkernel.
In other cases, kernelSpace describes the address space which 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" 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.
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 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 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.
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-2binType
ValuesBinType Value | Type of Binary |
---|---|
BIN_BOOTCONF | bootconf 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 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 7-3.
Table 7-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.
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 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 7-4.
Table 7-4SegSpace
ValuesSegSpace 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 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.
The numSegs field specifies the number of
elements in the array pointed to by SegDesc
.
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:
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 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.
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.
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.
This section contains an example of a BootConf structure for the SBC8260 PPC8260 EC603e-based board.
#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 the 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.