ChorusOS 4.0 Porting Guide

Appendix A Writing New Drivers and New BSPs

This appendix explains how to create new BSPs and new drivers for the ChorusOS operating system.

Source Files

This section explains the overall source tree organization and details the files needed to build a ChorusOS kernonly system image.

In the product BSP source tree, a template for a new driver can be found in the nucleus/bsp/template/drv directory and a template for a new BSP can be found in the nucleus/bsp/template/bsp directory. All the files within these directories can be copied and used as a starting point for implementation.

The ChorusOS kernonly system image can be built using the following commands:

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

The components available for configuring the kernonly system image are:


Note -

Refer to the ChorusOS 4.0 Installation Guide for Solaris Hosts or the ChorusOS 4.0 Installation Guide for Windows NT Hosts for the full directory paths for the TOOLS, KERNEL, DRV, DRV_F and BSP components.


Creating Driver/BSP Components

To create a new driver or BSP component the following configure command can be used:

% configure -b $TOOLS $KERNEL -s $DRV $DRV_F $MYBSP $MYDRV 

The configure utility is a program used to prepare a build directory for ChorusOS. It selects a number of components, either from the source tree or binary code. The arguments after -b are the pathnames of the binary components and those after -s are the pathnames of the source components. Refer to the ChorusOS 4.0 Installation Guide for Solaris Hosts or the ChorusOS 4.0 Installation Guide for Windows NT Hosts for full directory paths for the TOOLS, KERNEL, DRV and DRV_F.

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 a file called target.xml.

To summarize, the MYBSP component can be a completely new source tree for a new BSP or a copy of a BSP source tree from product sources with some slight modifications.

Creating a Driver Component

The driver component source tree is organized as follows:

Example

This section shows how a new driver can be created through the example of a non-existent keyboard chip 'zl345y'. The example explains which files should be present in which directories in order to create a new driver.

The Top' Level Driver Directory

For this example, the new directory will be called MYDRV_SRC_DIR. This root-level directory must contain the following two files:


Example A-1 New driver: Makefile.bin

COMPONENT += MYDRV

MYDRV.all::

The COMPONENT macro defines a list of components that will be processed in the build phase. The MYDRV source component is added to this list.


Example A-2 New driver: Makefile.src

MYDRV_SRC = $(MYDRV)/src

all:: MYDRV.all 

MYDRV.all:: $(MYDRV_DIR)/DONE

$(MYDRV_DIR)/DONE : $(MYDRV_DIR)/Makefile
        cd $(MYDRV_DIR); $(MAKE)
        touch $(MYDRV_DIR)/DONE

$(MYDRV_DIR)/Makefile: $(MYDRV_SRC)/Imakefile
        sh $(DEVTOOLS_DIR)/ChorusOSMkMf $(BUILD_DIR)
                         -s $(MYDRV_SRC) -b $(MYDRV_DIR) -d $(MYDRV_DIR)
        cd $(MYDRV_DIR); $(MAKE) Makefiles

This makefile defines the MYDRV.all target used to compile and link the MYDRV source files. The ChorusOSMkMf tool is used to create Makefiles from Imakefiles (see the ChorusOSMkMf(1CC) manpage for more details). The MYDRV_DIR variable is automatically set to $(BUILD_DIR)/build-MYDRV. This directory will be used as the build directory, for example where sources are compiled and linked. It will be used also as the binary delivery directory, for example where includes and binaries are exported.


Note -

These two files, Makefile.bin and Makefile.src , are referenced in the $BUILD_DIR/Makefile, which resides at the root of the build directory. The contents of both files should not be changed.


The Src Level Driver Directory

This src directory must contain the following two files:


Example A-3 New driver: Project.tmpl

#include "Package.rules"

SRC_DIR         = SourceDir
BUILD_DIR       = BuildDir
DIST_DIR        = DistDir

VPATH           = $(SRC_DIR)$(REL_DIR)

WARN            = $(WARN_ON)
DEBUG           = $(DEBUG_ON)

DRV_DIST_INC    = $(DIST_DIR)/include/chorus/drv

DRV_DIST_BIN    = $(DIST_DIR)/bin/drv

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

CPU_LIB         = $(NUCLEUS_DIR)/lib/cpu/cpu.s.a
EBD_LIB         = $(NUCLEUS_DIR)/lib/embedded/libebd.s.a

DRV_LIBS        = $(CPU_LIB) $(EBD_LIB)

The DRV_DIST_INC and DRV_DIST_BIN variables define paths where includes and binaries will be exported. This Project.tmpl file can be modified to add new variables or modify existing ones, for example to add a new include path or add a new library (DRV_LIBS).


Example A-4 New driver: Imakefile

#define IHaveSubdirs

SUBDIRS = keybrd

This Imakefile lists all of the sub-directories for each driver class and needs updapting to cater for your own drivers.

The Driver Directory

In this example, for a keyboard driver, the driver class directory would be MYDRV_SRC_DIR/src/keybrd, along with all other keyboard drivers.

The keybrd.h include file defines the DDI interface of this new driver. This include file must be exported to a well-known place in order for it to be accessible to a client of this driver. For example:

#include <ddi/keybrd/keybrd.h>
For this reason, a DistFile macro appears in the Imakefile, as follows:

#define IHaveSubdirs  
SUBDIRS = zl345y  
DistFile(keybrd.h, $(DIST_DIR)/include/chorus/ddi/keybrd) 

The Imakefile also contains a list of keyboard drivers for different chips.

The Chip Directory

The actual driver for the zl345y keyboard device would be located in MYDRV_SRC_DIR/src/keybrd/zl345y and consists of four files:

The Imakefile contains the following:

CSRCS = zl345y.c

OBJS = $(CSRCS:.c=.o) 

BuiltinDriver(D_zl345y.r, $(OBJS), $(DRV_LIBS))

DistProgram(D_zl345y.r, $(DRV_DIST_BIN)$(REL_DIR))

Depend($(CSRCS))

DistFile(zl345yProp.h, $(DRV_DIST_INC)$(REL_DIR))

The BuiltinDriver macro is used to produce the D_zl345y.r relocatable binary. The final link will be done by the mkimage tool. The first argument is the name of the binary. r is the standard extension for relocatable files. The second argument is the list of objects and the third is the list of libraries used in the link process.

The DistProgram macro copies the file, given as its the first argument, to the directory specified by its second argument. In the above example, the D_zl345y.r binary will be copied to the $MYDRV_DIR/bin/drv/keybrd/zl345y directory.

The zl345yProp.h file is exported as code by the DistFile macro to allows other drivers to include it.

Adding the New Driver

Finally, the driver must be added to the ChorusOS operating system. This can be done by either:

To include the new driver in the ChorusOS operating system image you will need to add the new driver's definition to the BSP target.xml file, as follows:

<definition name='zl345y'>
  <type name='File' />
  <value field='path'>
    <vstring>${MYDRV_DIR}/bin/drv/keybrd/zl345y/D_zl345y.r</vstring>
  </value>
  <value field='bank'><ref name='sys_bank' /></value>
  <value field='binary'><ref name='driver_model' /></value>
</definition>

Also, a reference to the new driver will need be to be made in the BSP FileList:

<definition name='BSP_files'>
  <description>system image BSP files</description>
  <type name='FileList' />
   ....
  <value index='size'><ref name='zl345y' /></value>
   ....
</definition>

Alternatively, to add the driver dynamically, after the system image has been created, the arun command should be used to run the new driver.

Creating a BSP Component

The BSP component source tree is organized as follows:

Example

This section shows how to create a new BSP through an example. The example explains which files should be present in which directories in order to create the new BSP.

The Top Level BSP Directory

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


Example A-5 New BSP: Makefile.bin

COMPONENT += BSP

BSP.all::

BSP_XML = target.xml target_action.xml

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

XML3 += 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.


Caution - Caution -

Only the name BSP is required.


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


Example A-6 New BSP: Makefile.src

BSP_SRC = $(BSP)/src

all:: BSP.all 

BSP.all:: $(BSP_DIR)/DONE

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

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

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

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

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


Example A-7 New BSP: target.xml

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

<fold      </description>
      <type name='Area' ref-only='yes' />
      <ref name='ram_area' />
    </definition>

    <definition name='user_context_area_ref'>
      <de      <value field='bank'><ref name='sys_bank' /></value>
      <value field='binary'><ref name='microkernel_model' /></value>
    </definition>
  </folder>

  <folder name='System image'>
    <description>ChorusOS system image configuration</description>

    <folder name='Files'>
      <description>system image files</description>

      <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='bootstrap' /></value>
        <value index='size'><ref name='microkernel' /></value>
      </definition>

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

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

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

        <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>
      </folder>

    </folder>

    <folder name='Misc'>
      <description>Misc configuration variables</description>
        
      <definition name='banks'>
        <description>list of system image banks</description>
        <type name='BankList'/>
        <value index='size'><ref name='sys_bank' /></value>
      </definition>

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

    </folder>

  </folder>

</folder>

The example target.xml file, above, specifies the basic routines needed to produce a kernonly archive. It is important to note that this file has to be adapted for your particular board.

The pathname ${BSP_DIR}/bin/mybsp is used to reference a number of BSP binaries. The mybsp 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 below.


Example A-8 New BSP: target_action.xml

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

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

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

</folder>

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

The Src Level Directory

This src directory must contain the following two files:


Example A-9 New BSP: Project.tmpl

#include "Package.rules"

SRC_DIR         = SourceDir
BUILD_DIR       = BuildDir
DIST_DIR        = DistDir

VPATH           = $(SRC_DIR)$(REL_DIR)

WARN            = $(WARN_ON)

MYBSP_DIST_BIN  = $(DIST_DIR)/bin/mybsp

DRV_DIST_BIN    = $(DRV_DIR)/bin/drv

INCLUDES        = -I$(NUCLEUS_DIR)/include/chorus \
                  -I$(NUCLEUS_DIR)/include/stdc   \
                  -I$(DRV_DIR)/include/chorus \
                  -I$(DRV_F_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 to which programs will be exported. The name mybsp can be changed to the real board name. The DIST_DIR variable is defined by the path given with the -d option of ChorusOSMkMf. This path is the same as BSP_DIR: $BUILD_DIR/build-BSP. The DRV_DIST_BIN variable will be used to reference objects exported by the DRV component.


Example A-10 New BSP: Src-level Imakefile

#define IHaveSubdirs

SUBDIRS = boot dbg

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

The boot Directory

This is the directory where the source files for the boot program reside. There are at least two files in this directory:


Example A-11 New BSP: Imakefile

C__SRCS = boot.c

AS_SRCS =

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

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

Depend($(C__SRCS) $(AS_SRCS))

The BspProgTarget macro is used to produce the boot.r relocatable binary file. The final link will be done by the mkimage tool. The first argument is the name of the binary ( with the standard file extension .r for 'relocatable'). 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.r binary file will be copied in to the $BSP_DIR/bin/mybsp/boot directory.


Example A-12 New BSP: boot.c

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

This file needs to be adapted to cater for your BSP.

The dbg Directory

The dbg directory contains the platform dependant code for the debug agent driver. There are three files located at this top level:


Example A-13 New BSP: dbg Imakefile

C__SRCS = dbgBsp.c ns16550Bsp.c

AS_SRCS =

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

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

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

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

Depend($(C__SRCS) $(AS_SRCS))

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


Example A-14 New BSP: dbgBsp.c

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

#include <drv/dbg/dbgUart.h>

extern void dbg_board_reset(int);

DbgBsp dbgBsp = { dbgUart_init,
                  dbgUart_ioctl,
                  dbgUart_mayGet,
                  dbgUart_getChar,
                  dbgUart_mayPut,
                  dbgUart_putChar,
                  dbgUart_ioRemap,
                  dbg_board_reset };

    void
dbg_board_reset(int mode)
{
    /* Code for your board reset */
}

    void
dbgBsp_init(BootConf* conf)
{
    conf->dbgBsp = dbgBsp;
}

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


Example A-15 New BSP: ns16550Bsp.c

#include <drv/dbg/ns16550Bsp.h>

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

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

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

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

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

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