(c)  Copyright 1989-1999 Amiga, Inc.   All rights reserved.
The information contained herein is subject to change without notice, and 
is provided "as is" without warranty of any kind, either expressed or implied.  
The entire risk as to the use of this information is assumed by the user.



                    V1.3 KICKSTART AND AUTOBOOT

                       by Bart Whitbrook


       The V1.3 Kickstart allows devices other than DF0: to serve as the
   Amiga system boot device.  The purpose of this article is to show how
   you  may implement your own autoboot device in a system-compatible
   fashion.


   Features

       Kickstart V1.3 is an incremental kickstart release.  It is designed
   to maintain V1.2 compatability throughout, while adding autoboot from
   ROM-based expansion devices to the operating system.

       Automatic boot from any ROM-equipped expansion board is accomplished
   at cold-start time, before the DOS exists.  This facility makes it
   possible to automatically boot from a hard disk without any floppy
   disks inserted.  Likewise, it is possible to automatically boot from any
   device which supports the ROM protocol, such as nfs: over ethernet.

       Autoboot is implemented with the addition of the "romboot.library"
   to the ROM resident list, as well as conservative changes to the
   expansion.library, the dos.library and the strap.library.  These
   changes have been carefully considered to be compatible with V1.2
   (release 33.180) so that developers should not have to recompile
   their code for software compatability.  Without autoboot hardware,
   the V1.3 system will behave the same as the V1.2 release.

       Romboot.library functions are private at this time and are not
   needed by those developing autoboot expansion devices or drivers for
   such devices.
       
       



Changes Since V1.2 Release (33.180)
-----------------------------------

   Seven kickstart modules have changed since the release of KS_33.180:

     1)  expansion34.1   --  functional change (internal only)
     2)  graphics34.1    --  size change (no functional changes)
     3)  strap34.1       --  functional change (internal only)
     4)  romboot34.1     --  new module added to support autoboot
     5)  wb34.1          --  size change (no functional changes)
     6)  dos34.1         --  functional change (internal only)
     7)  intuition34.1   --  functional change (extended preferences only)

   Six include files have changed since the release of KS_33.180:

     8)  exec/nodes.h               -- a new define added
     9)  exec/node.i                -- a new define added
     10) libraries/expansionbase.h  -- now public
     11) libraries/expansionbase.i  -- now public
     12) libraries/romboot_base.h   -- a newly created include file
     13) libraries/romboot_base.i   -- a newly created include file
     14) intuition/intuition.h      -- split off preferences.h and screens.h
     15) intuition/intuition.i      -- split off preferences.i and screens.i
     16) intuition/screen.h         -- new include (was part of intuition.h)
     17) intuition/screen.i         -- new include (was part of intuition.i)
     18) intuition/preferences.h    -- new include (was part of intuition.h)
     19) intuition/preferences.i    -- new include (was part of intuition.i)

   Two library files have changed since the release of KS_33.180

     20) amiga.lib   -- addition of romboot.library
     21) rom.lib     -- addition of romboot.library



How Auto Boot Works
-------------------

       At system reboot time, and after exec is initialized, the expansion
   initialization procedure is called.  As each expansion board in the
   system is configured, the expansion initialization routine checks to see
   whether this board has a valid ROM area associated with it.  If such a
   ROM exists, its image is then copied into RAM memory.  This ROM "image"
   contains rom/diagnostic routines, a driver associated with this expansion
   board, and the board's bootstrap routine to actually boot the appropriate
   operating system (normally, AmigaDOS).

       Once the ROM image is copied into RAM, the config routine calls
   the rom/diagnostic vector of the board to perform any patching and/or
   relocation of the ROM image to reflect its new location in RAM.  This
   whole process is repeated for each auto-config board in the system.

       After the config routine, the other Resident Modules, except strap,
   are initialized (graphics, intuition, etc.).  Now, a new V1.3 module,
   romboot, is called upon to create the romboot.library.

       During its initialization phase, the romboot.library searches
   through the ROM "images" for valid romtags.  When a valid romtag is
   found, its rt_Init() function is called, to start up the driver
   associated with this board.  If the driver is able to startup
   successfully, the driver will enqueue a node on the expansion.library's
   eb_MountList (see libraries/expansionbase.h).

       Finally, the Resident Module strap is initialized.  Strap first
   checks to see if there is a valid boot disk in the internal drive, DF0:.
   If there is, strap overrides the autoboot and boots from DF0: instead.

       If there is no valid boot disk in DF0:, strap will try to autoboot
   by calling the romboot.library routine RomBoot().  For each appropriate
   node on the eb_Mountlist, RomBoot() will locate and execute the
   bootstrap routine for the board located in the associated ROM "image".
   If the board's bootstrap routine is successful, the system will come up
   using the board as the default boot device.

       If the autoboot fails, or if there are no appropriate nodes on the
   eb_MountList, strap will ask for the user to insert a valid Workbench
   disk, and will loop until its request is satisfied.



The Details of Autoboot
-----------------------

   AT EXPANSION CONFIGURATION TIME:

       As part of its initialization, the expansion.library auto-configs
   all the appropriate hardware boards in the system.

       When your auto-config hardware board is configured by the expansion
   initialization routine, its ExpansionRom structure is copied into the
   ExpansionRom subfield of a ConfigDev structure (see figure 1).  This
   ConfigDev structure will be linked to the expansion.library as part
   of the eb_BoardList.

        AFTER the board is configured, the er_Type field of its ExpansionRom
   structure is checked (see figure 2).  The DIAGVALID bit set declares
   that there is a valid DiagArea (you know, a rom/diagnostic area) on this
   board.

       If there is a valid DiagArea, expansion next tests the er_InitDiagVec
   vector in its copy of the ExpansionRom structure.  This offset is added
   to the base address of the configured board; the resulting address points
   to the start of this board's DiagArea.  Now expansion knows that there is
   a DiagArea and where it is.

       Next, expansion tests the first byte (see figure 3) of the DiagArea
   structure to determine if the CONFIGTIME bit is set.  If this bit
   is set, it checks the da_BootPoint offset vector to make sure that a
   valid bootstrap routine exists.  If so, expansion copies da_Size bytes
   into RAM memory, beginning at DiagArea and continuing until the entire
   DiagArea is copied into memory.

       The copy will start with the DiagArea structure itself, and typically
   include a rom/diagnostic routine, a device driver, and the board's
   bootstrap routine.  The copy will be made either nibblewise, bytewise,
   or wordwise, according to the BUSWIDTH subfield of da_Config (see
   figure 4).  Note that the da_BootPoint offset MUST BE NON-NULL, OR ELSE
   NO COPY WILL OCCUR.

       Now the ROM "image" exists in RAM memory.  Expansion stores the
   ULONG address of that "image" in the UBYTES er_Reserved0c, 0d, 0e and 0f.
   The address is stored with the most significant byte in er_Reserved0c,
   the next to most significant byte in er_Reserved0d, the next to least 
   significant byte in er_Reserved0e, and the least significant byte
   in er_Reserved0f (see figure 5).

       Expansion finally checks the da_DiagPoint offset vector, and if valid
   executes the rom/diagnostic routine contained as part of the ROM "image".
   This diagnostic routine is responsible for "patching" the ROM image so
   that required absolute addresses are relocated to reflect the actual
   location of the code, as well as performing any diagnostic functions 
   essential to the operation of its associated auto-config board (see
   figure 6).

       Your rom/diagnostic routine should return a non-zero value to
   indicate success;  otherwise the ROM "image" will be unloaded from
   memory, and its address will be replaced with NULL bytes in locations
   er_Reserved0c, 0d, 0e and 0f.
       
       Now that the ROM "image" has been copied into RAM, validated, and
   linked to board's ConfigDev structure, the expansion module is free to
   configure all other boards on the eb_BoardList.


   NEXT, OTHER RESIDENT MODULES ARE INITIALIZED:

       Currently, all Resident Modules (excepting strap) have a priority of
   zero or above.  The new romboot Resident Module has a priority level of
   -40.  The strap module has priority -60.  This means that Romboot will
   be initialized after all other modules but before the (boot)strap module.


   NOW, THE ROMBOOT.LIBRARY IS INITIALIZED:

       As part of the initialization procedure for the romboot.library,
   a search is made of the expansion eb_BoardList (which contains a
   ConfigDev structure for each of the auto-config hardware boards). 
   If the cd_Flags specify CONFIGME and the er_Type specifies DIAGVALID,
   the romboot device will do three things:

       First, it will bind the address of the current ConfigDev to the
   eb_CurrentBinding structure (see expansion.library/SetCurrentBinding).

       Second, it will check the DiagArea's da_Config flag to make sure that
   the CONFIGTIME bit is set.

       Third, it will search the ROM "image" associated with this hardware
   board for a valid Resident structure (exec/resident.h); and, if one is
   located, will call InitResident() on it, passing a NULL segment list
   pointer as part of the call.


   NOW, THIS BOARD'S DEVICE DRIVER IS INITIALIZED:

       The Resident structure associated with this board's device driver
   (which has now been "patched" by the rom/diagnostic routine) should
   follow standard system conventions in initializing the device driver
   provided in the bootroms.  This driver should obtain the address of its
   associated ConfigDev structure via the expansion eb_CurrentBinding
   structure (see figure 7).

       Once the driver is initialized, it is responsible for some further
   steps.  One, it must clear the CONFIGME bit in the cd_Flags of its
   ConfigDev structure , so that the system knows not to configure this
   device again if binddrivers is run after bootstrap.  Two, for this
   device to be bootable, the driver must create a BootNode structure,
   and link this BootNode onto the expansion eb_MountList.

   The BootNode structure (see libraries/romboot_base.h) contains a Node of
   the "new" type NT_BOOTNODE (see exec/nodes.h). The driver MUST initialize
   the bn_DeviceNode field to point to the ConfigDev structure which it has
   obtained via the GetCurrentBinding() call.  The bn_Flags subfield
   is currently unused and should be initialized to NULL.

       When the DOS is initialized later, it will attempt to boot from
   the first BootNode on the eb_MountList.  The eb_MountList is a priority
   sorted List, with nodes of the highest priority a the head of the List.
   For this reason, the device driver must Enqueue() a BootNode onto the
   List using the exec.library/Enqueue.

       In the case of an autoboot of AmigaDOS, the BootNode must be linked
   to a DeviceNode of the AmigaDOS type (see libraries/filehandler.h), which
   the driver can create via the expansion.library/MakeDosNode call.
   When the DOS "wakes up",it will attempt to boot from this DeviceNode. 

   Note:   DOS, in this context, means "device operating system".
           Typically this means AmigaDOS, although there is nothing
           to prevent a system boot from another OS (UNIX, MS-DOS.
           <boot via nfs:>, ETC.).

       When the romboot.library is finished setting up all the rom "images"
   of all the boards in the system, the Resident Module strap takes over.


  EXECUTION PASSES TO STRAP:

       This is where all your hard work pays off.

       If there is NO boot disk in the internal floppy drive, strap will
   call the function romboot.library/RomBoot().  This routine performs the
   autoboot. It will examine the eb_MountList; find the highest priority
   BootNode structure at the head of the List; validate the BootNode; 
   determine which ConfigDev on the eb_BootList is associated with this
   BootNode; find its DiagArea; and call its da_BootPoint function in the
   ROM "image" to bootstrap the appropriate DOS. This call, if successful,
   should not return.
   
       Otherwise, if a boot disk IS in the internal floppy drive, the strap
   will Enqueue() a BootNode on the eb_MountList for DF0: at the "suggested"
   priority (see autodoc for expansion.library/AddDosNode).  Strap will then
   open AmigaDOS, overriding the autoboot.  AmigaDOS will boot from the
   highest priority node on the eb_MountList which should, in this case, be
   DF0:.  Thus, games and other "bootable" floppy disks will still be able
   to obtain the system for their own use.

       In the event that there is NO boot disk in the internal floppy drive
   AND there are no ROM bootable devices on the auto-configuration chain,
   the system does the normal thing, asking the user to insert a WorkBench
   disk, and waiting until its request is satisfied before proceeding.


       


------------------------------------------------------------------------
                Figure 1: the ConfigDev structure
------------------------------------------------------------------------

        struct ConfigDev 
        {
            struct Node         cd_Node;
            UBYTE               cd_Flags;
            UBYTE               cd_Pad;
            struct ExpansionRom cd_Rom;         /* image of ExpansionRom  */
            APTR                cd_BoardAddr;   /* for this ConfigDevice */
            APTR                cd_BoardSize;   
            UWORD               cd_SlotAddr;
            UWORD               cd_SlotSize;
            APTR                cd_Driver;
            struct ConfigDev   *cd_NextCD;  
            ULONG               cd_Unused[4];
        };

------------------------------------------------------------------------
                Figure 2: the ExpansionRom structure
------------------------------------------------------------------------

        struct ExpansionRom 
        {
            UBYTE   er_Type;            /* <-- if ERTB_DIAGVALID set */
            UBYTE   er_Product;
            UBYTE   er_Flags;
            UBYTE   er_Reserved03;
            UWORD   er_Manufacturer;
            ULONG   er_SerialNumber;
            UWORD   er_InitDiagVec;     /* <-- then er_InitDiagVec      */
            UBYTE   er_Reserved0c;      /*     is added to cd_BoardAddr */
            UBYTE   er_Reserved0d;      /*     and points to DiagArea   */
            UBYTE   er_Reserved0e;      /*     ( see figure 3 )         */
            UBYTE   er_Reserved0f;
        };


------------------------------------------------------------------------
                Figure 3: the DiagArea structure
------------------------------------------------------------------------

        struct DiagArea 
        {
            UBYTE   da_Config;          /* <-- if DAC_CONFIGTIME is set */
            UBYTE   da_Flags;
            UWORD   da_Size;            /* <-- then da_Size bytes will  */
            UWORD   da_DiagPoint;       /*     be copied into RAM       */
            UWORD   da_BootPoint;       /*     ( see figure 4 )         */
            UWORD   da_Name;    
            UWORD   da_Reserved01;  
            UWORD   da_Reserved02;
        };


------------------------------------------------------------------------
                Figure 4: the RAM copy of the DiagArea structure
------------------------------------------------------------------------

                        DiagArea:  copy in RAM
                           ( see figure 5 )

        +-> CCFF    ; da_Config, da_Flags <-----+ <-----+ <-----+
        |   SIZE    ; da_Size                   |       |       |
        S   DIAG    ; da_DiagPoint              N       B       D
        I   BOOT    ; da_BootPoint              A       O       I
        Z   NAME    ; da_Name                   M       O       A
        E   0000    ; da_Reserved01             E       T       G
        |   0000    ; da_Reserved02             |       |       |
        |                                       |       |       |
        |   nnnn    ; DiagArea + da_Name <+-----+       |       |
        |   nnnn    ; name                              |       |
        |   nnnn    ; name                              |       |
        |   nnnn    ; name                              |       |
        |   nnnn    ; name                              |       |
        |                                               |       |
        |   bbbb    ; DiagArea + da_bootPoint <---------+       |
        |   bbbb    ; boot                                      |
        |   bbbb    ; boot                                      |
        |   bbbb    ; boot                                      |
        |   bbbb    ; boot                                      |
        |   bbbb    ; boot                                      |
        |   bbbb    ; boot                                      |
        |   bbbb    ; boot                                      |
        |                                                       |
        |   dddd    ; DiagArea + da_DiagPoint <-----------------+
        |   dddd    ; diag
        |   dddd    ; diag
        |   dddd    ; diag
        |   dddd    ; diag
        |   dddd    ; diag
        |   dddd    ; diag
        |   dddd    ; diag 
        |                   
        +-> last    ; DiagArea + da_Size


------------------------------------------------------------------------
                Figure 5: where the pointer to the RAM copy is kept
------------------------------------------------------------------------

        struct ConfigDev 
        {
            struct Node cd_Node;
            UBYTE       cd_Flags;
            UBYTE       cd_Pad;
            struct ExpansionRom {
                UBYTE   er_Type;
                UBYTE   er_Product;
                UBYTE   er_Flags;
                UBYTE   er_Reserved03;
                UWORD   er_Manufacturer;
                ULONG   er_SerialNumber;
                UWORD   er_InitDiagVec;
                UBYTE   er_Reserved0c;      /*  <-- address of RAM copy of  */
                UBYTE   er_Reserved0d;      /*      DiagArea is stored by   */
                UBYTE   er_Reserved0e;      /*      expansion.library in    */
                UBYTE   er_Reserved0f;      /*      these four bytes in the */
            } cd_Rom;                       /*      image of ExpansionRom   */
            APTR    cd_BoardAddr;           /*      for this ConfigDevice   */
            APTR    cd_BoardSize;   
            UWORD   cd_SlotAddr;
            UWORD   cd_SlotSize;
            APTR    cd_Driver;
            struct ConfigDev   *cd_NextCD;  
            ULONG   cd_Unused[4];
        }; 


------------------------------------------------------------------------
                Figure 6: DiagArea Initialization and Relocation
------------------------------------------------------------------------

                ( this figure omits several important steps )
                ( involved in error checking and relocation )
                ( of variables in the resident structure!!! )
                ( in this example the Init routine will be  )
                ( executed directly from the wordwide ROMS  )
            
    DiagArea:   dc.b    (DAC_WORDWIDE+DAC_CONFIGTIME)
                dc.b    (NULL)
                dc.w    (End-DiagArea)
                dc.w    (DiagPoint-DiagArea)
                dc.w    (BootPoint-DiagArea)
                dc.w    (Name-DiagArea)
                dc.w    (NULL)
                dc.w    (NULL)
                                
    Resident:   dc.w    RTC_MATCHWORD   ; UWORD RT_MATCHWORD
    rt_Match:   dc.l    Resident        ; APTR  RT_MATCHTAG
    rt_End:     dc.l    End             ; APTR  RT_ENDSKIP
                dc.b    RTW_COLDSTART   ; UBYTE RT_FLAGS
                dc.b    VERSION         ; UBYTE RT_VERSION
                dc.b    NT_DEVICE       ; UBYTE RT_TYPE
                dc.b    20              ; BYTE  RT_PRI
    rt_Name:    dc.l    Name            ; APTR  RT_NAME
    rt_ID:      dc.l    Name            ; APTR  RT_IDSTRING
    rt_Init:    dc.l    Init            ; APTR  RT_INIT == offset from ROM base
    
    Name:       dc.b    'AutoBoot',0    ; our own Name string

    DosName:    DOSNAME                 ; macro definition of DOS library name
                ds.w        0           ; word align code
    
    BootPoint:  lea DosName(PC),A1  
                jsr     _LVOFindResident(A6)    ; find the DOS resident tag
                move.l  d0,a0                   ; in order to bootstrap
                move.l  RT_INIT(A0),A0          ; set vector to DOS INIT
                jsr     (a0)                    ; and initialize DOS
                rts
    
    DiagPoint:  movem.l a1,-(sp)        ; a0 == pointer to boardbase
                lea     rt_Init(pc),a1  ; relocate address of Init routine
                move.l  (a1),d0         ; get long offset from ROM origin
                add.l   a0,d0           ; add the boardbase to the offset
                move.l  d0,(a1)         ; and replace offset with absolute
                movem.l (sp)+,a1        ; restore scratch register
                moveq.l #1,d0           ; indicate "success"
                rts

    End:        end

 
------------------------------------------------------------------------
                Figure 7: Adding a DOS BootNode to the eb_MountList 
------------------------------------------------------------------------

    char execName[] =   "trackdisk.device";  char dosName[] =   "DF1";
    ULONG parmPkt[] =   /* a 3.5" amiga format floppy drive */ 
    {   (ULONG) dosName,    
        (ULONG) execName,
        1,                  /* unit number */ 
        0,                  /* OpenDevice flags */
        12,                 /* table upper bound */
        512>>2,             /* # longwords in a block */
        0,                  /* sector origin */
        2,                  /* number of surfaces */
        1,                  /* secs per logical block */
        11,                 /* secs per track */
        2,                  /* reserved blocks -- 2 boot blocks */
        0,                  /* unused */
        0,                  /* interleave */
        0,                  /* lower cylinder */
        79,                 /* upper cylinder */
        5,                  /* number of buffers */
        3                   /* type of memory */    };

    Init() 
    {
        struct ExpansionBase *eb = OpenLibrary("expansion.library",V1_POINT_3);

        if(eb) 
        { 
            struct BootNode *bn = 
            AllocMem(sizeof(struct BootNode),MEMF_PUBLIC|MEMF_CLEAR);

            if(bn) 
            { 
                struct DeviceNode *dosnode = MakeDosNode( parmPkt );

                if(dosnode) 
                { 
                    struct CurrentBinding curbind;

                    GetCurrentBinding(&curbind, sizeof(struct CurrentBinding));
                    bn->bn_Node.ln_Type = NT_BOOTNODE;          
                    bn->bn_Node.ln_Pri  = 5;            
                    bn->bn_Node.ln_Name = curbind.cb_ConfigDev;         
                    bn->bn_Flags        = NULL;
                    bn->bn_DeviceNode   = dosnode;

                    Enqueue(&eb->MountList,bn);
                    
                } else { FreeMem(bn,sizeof(struct BootNode)); }
            }
        }
    }


