  <title>Booting ARM Linux</title>

  <articleinfo>
      <author>
	<firstname>Vincent</firstname>
	<surname>Sanders</surname>
      <affiliation>
	<address><email>vince@arm.linux.org.uk</email></address>
      </affiliation>
      </author>

    <editor>
      <firstname>Becky</firstname>
      <surname>Parry</surname>
    </editor>

    <othercredit>
      <firstname>Russell</firstname>
      <surname>King</surname>
      <contrib>Review and advice, large chunks of the ARM Linux kernel, all around good guy</contrib>
    </othercredit>

    <othercredit>
      <firstname>Nicolas</firstname>
      <surname>Pitre</surname>
      <contrib>Review, advice and numerous clarifications.</contrib>
    </othercredit>

    <othercredit>
      <firstname>Erik</firstname>
      <surname>Mouw</surname>
      <contrib>Review and advice</contrib>
    </othercredit>

    <othercredit>
      <firstname>Zwane</firstname>
      <surname>Mwaikambo</surname>
      <contrib>Review and advice</contrib>
    </othercredit>

    <othercredit>
      <firstname>Jeff</firstname>
      <surname>Sutherland</surname>
      <contrib>Review and advice</contrib>
    </othercredit>

    <othercredit>
      <firstname>Ralph</firstname>
      <surname>Siemsen</surname>
      <contrib>Review and advice</contrib>
    </othercredit>

    <othercredit>
      <surname>Wookey</surname>
      <contrib>Review and referenced information (see bibliography)</contrib>
    </othercredit>

    <othercredit>
      <firstname>Daniel</firstname>
      <surname>Silverstone</surname>
      <contrib>Review and advice</contrib>
    </othercredit>

    <othercredit>
      <firstname>Martin</firstname>
      <surname>Michlmayr</surname>
      <contrib>Review and advice</contrib>
    </othercredit>

    <othercredit>
      <firstname>Michael</firstname>
      <surname>Stevens</surname>
      <contrib>Review and advice</contrib>
    </othercredit>

    <othercredit>
      <firstname>Lesley</firstname>
      <surname>Mitchell</surname>
      <contrib>Review and advice</contrib>
    </othercredit>

    <othercredit>
      <firstname>Matthew</firstname>
      <surname>Richardson</surname>
      <contrib>Review and advice</contrib>
    </othercredit>

    <copyright>
      <year>2004</year>
      <holder>Vincent Sanders</holder>
    </copyright>

    <pubdate>2004-06-04</pubdate>

    <revhistory>
      <revision>
	<revnumber>1.00</revnumber>
	<date>10th May 2004</date>
	<authorinitials>VRS</authorinitials>
	<revdescription>
	  <para>Initial Release.</para>
	</revdescription>
      </revision>
      <revision>
	<revnumber>1.10</revnumber>
	<date>4th June 2004</date>
	<authorinitials>VRS</authorinitials>
	<revdescription>
	  <para><simplelist>
	      <member>Update example code to be more complete.</member>
	      <member>Improve wording in places, changes suggested by <personname><firstname>Nicolas</firstname><surname>Pitre</surname></personname>.</member>
	      <member>Update <xref linkend="section_other_bootloaders"/>.</member>
	      <member>Update acknowledgements.</member>
	    </simplelist>
	  </para>
	</revdescription>
      </revision>


    </revhistory>

    <legalnotice>
      <itemizedlist>
	<listitem><para>This document is released under a GPL licence.</para></listitem>
	<listitem><para>All trademarks are acknowledged.</para></listitem>
      </itemizedlist>
    </legalnotice>
    &articlelegal;

  </articleinfo>


  <abstract>
  <para>This document defines in clear concise terms, with
  implementation guidance and examples, the requirements and
  procedures for a bootloader to start an ARM Linux kernel. </para>

  </abstract>

  <section>
    <title>About this document</title>

    <para>This document describes the &quot;new&quot; booting
    procedure which all version 2.4.18 and later kernels use. The
    legacy &quot;struct&quot; method must <emphasis>not</emphasis> be
    used.</para>

    <para>This document contains information from a wide variety of
    sources (see the <xref linkend="article_bilbiography"/>) and
    authors, you are encouraged to consult these sources for more
    information before asking questions of the Maintainers, or on the
    ARM Linux mailing lists. Most of these areas have been covered
    repeatedly in the past and you are likely to be ignored if you
    haven't done at least basic research.</para>

    <para>Additionally it should be noted that provided the guidance
    in this document is followed, there should be no need for an
    implementor to understand every nuance of the assembler that
    starts the kernel. Experience has shown on numerous occasions that
    most booting problems are unlikely to be related to this code,
    said code is also quite tricky and unlikely to give any insight
    into the problem.</para>

  </section>

  <section id="section_other_bootloaders">
    <title>Other bootloaders</title>

    <para>Before embarking on writing a new bootloader a developer
    should consider if one of the existing loaders is appropriate. There
    are examples of loaders in most areas, from simple GPL loaders to
    full blown commercial offerings. A short list is provided here but
    the documents in the <xref linkend="article_bilbiography"/> offer
    more solutions.</para>

    <table frame="all" id="table_bootloaders">
      <title>Bootloaders</title>
      <tgroup cols="3">
	<colspec colname="bootloader" colwidth="1*"/>
	<colspec colname="url" colwidth="3*"/>
	<colspec colname="description" colwidth="4*"/>
	<thead>
	  <row>
	    <entry>Name</entry>
	    <entry>URL</entry>
	    <entry>Description</entry>
	  </row>
	</thead>
	<tbody>
	  <row>
	    <entry>Blob</entry>
	    <entry><ulink url="http://www.sf.net/projects/blob/">Blob bootloader</ulink></entry>
	    <entry>GPL bootloader for SA11x0 (StrongARM) platforms.</entry>
	  </row>
	  <row>
	    <entry>Bootldr</entry>
	    <entry><ulink url="http://www.handhelds.org/sources.html">Bootldr</ulink></entry>
	    <entry>Both GPL and non-GPL versions available, mainly used for handheld devices.</entry>
	  </row>
	  <row>
	    <entry>Redboot</entry>
	    <entry><ulink url="http://sources.redhat.com/redboot/">Redboot</ulink></entry>
	    <entry>Redhat loader released under their eCos licence.</entry>
	  </row>

	  <row>
	    <entry>U-Boot</entry>
	    <entry><ulink url="http://sourceforge.net/projects/u-boot/">U-Boot</ulink></entry>
	    <entry>GPL universal bootloader, provides support for several CPUs.</entry>
	  </row>

	  <row>
	    <entry>ABLE</entry>
	    <entry><ulink url="http://www.simtec.co.uk/products/SWABLE/">ABLE bootloader</ulink></entry>
	    <entry>Commercial bootloader with comprehensive feature set</entry>
	  </row>
	</tbody>
      </tgroup>
    </table>


  </section>

  <section>
    <title>Overview</title>

    <para>ARM Linux cannot be started on a machine without a small
          amount of machine specific code to initialise the system. ARM Linux
          <emphasis>requires</emphasis> the bootloader code to do very little,
          although several bootloaders do provide extensive additional
          functionality. The minimal requirements are:

    <simplelist>
	<member>Configure the memory system.</member>
	<member>Load the kernel image at the correct memory
address.</member>
	<member>Optionally load an initial RAM disk at the correct
memory address.</member>
	<member>Initialise the boot parameters to pass to the
kernel.</member>
	<member>Obtain the ARM Linux machine type</member>
	<member>Enter the kernel with the appropriate register values.</member>
    </simplelist>
    </para>

    <para>It is usually expected that the bootloader will initialise a
    serial or video console for the kernel in addition to these basic
    tasks. Indeed a serial port is almost considered mandatory in
    most system configurations.</para>

    <para>Each of these steps will be examined in the following sections.</para>
  </section>

  <section>
    <title>Configuring the system's memory</title>

    <para>The bootloader is expected to find and initialise all RAM
    that the kernel will use for volatile data storage in the system.
    It performs this in a machine dependent manner. It may use
    internal algorithms to automatically locate and size all RAM, or
    it may use knowledge of the RAM in the machine, or any other
    method the bootloader designer sees fit.</para>

    <para>In all cases it should be noted that all setup is performed
    by the bootloader. The kernel should have no knowledge of the
    setup or configuration of the RAM within a system other than that
    provided by the bootloader. The use of machine_fixup() within the
    kernel is most definitely not the correct place for this. There is
    a clear distinction between the bootloaders responsibility and the
    kernel in this area.</para>

    <para>The physical memory layout is passed to the kernel using the
    <xref linkend="ATAG_MEM"/> parameter. Memory does not necessarily
    have to be completely contiguous, although the minimum number of
    fragments is preferred. Multiple <xref linkend="ATAG_MEM"/> blocks
    allow for several memory regions. The kernel will coalesce blocks
    passed to it if they are contiguous physical regions.</para>

    <para>The bootloader may also manipulate the memory with the
    kernels command line, using the 'mem=' parameter, the options for
    this parameter are fully documented in
    <literal>linux/Documentation/kernel-parameters.txt</literal></para>

    <para>The kernel command line 'mem=' has the syntax
    <literal>mem=&lt;size&gt;[KM][,@&lt;phys_offset&gt;]</literal> which allows
    the size and physical memory location for a memory area to be
    defined. This allows for specifying multiple discontigous memory
    blocks at differing offsets by providing the mem= parameter multiple
    times.</para>

  </section>


  <section>
    <title>Loading the kernel image</title>

    <para>Kernel images generated by the kernel build process are
    either uncompressed &quot;Image&quot; files or compressed zImage
    files.</para>

    <para>The uncompressed Image files are generally not used, as they
    do not contain a readily identifiable magic number. The compressed
    zImage format is almost universally used in preference.</para>

    <para> The zImage has several benefits in addition to the magic
    number. Typically, the decompression of the image is
    <emphasis>faster</emphasis> than reading from some external media.
    The integrity of the image can be assured, as any errors will
    result in a failed decompress. The kernel has knowledge of its
    internal structure and state, which allows for better results than
    a generic external compression method.</para>

    <para>The zImage has a magic number and some useful information
    near its beginning.


    <table frame="all" id="table_zimage_head">
      <title>Useful fields in zImage head code</title>
      <tgroup cols="3">
	<colspec colname="offset" colwidth="1*"/>
	<colspec colname="value" colwidth="3*"/>
	<colspec colname="description" colwidth="4*"/>
	<thead>
	  <row>
	    <entry>Offset into zImage</entry>
	    <entry>Value</entry>
	    <entry>Description</entry>
	  </row>
	</thead>
	<tbody>
	  <row>
	    <entry>0x24</entry>
	    <entry>0x016F2818</entry>
	    <entry>Magic number used to identify this is an ARM Linux zImage</entry>
	  </row>
	  <row>
	    <entry>0x28</entry>
	    <entry>start address</entry>
	    <entry>The address the zImage starts at</entry>
	  </row>
	  <row>
	    <entry>0x2C</entry>
	    <entry>end address</entry>
	    <entry>The address the zImage ends at</entry>
	  </row>
	  </tbody>
	</tgroup>
      </table>


    </para>

    <para>The start and end offsets can be used to determine the
    length of the compressed image (size = end - start). This is used
    by several bootloaders to determine if any data is appended to the
    kernel image. This data is typically used for an initial RAM disk
    (initrd). The start address is usually 0 as the zImage code is
    position independent.</para>

    <para>The zImage code is Position Independent Code (PIC) so may be
    loaded anywhere within the available address space. The maximum kernel
    size after decompression is 4Megabytes. This is a hard limit and
    would include the initrd if a bootpImage target was used.</para>

    <note>

      <para>Although the zImage may be located anywhere, care should
      be taken.  Starting a compressed kernel requires additional
      memory for the image to be uncompressed into. This space has
      certain constraints.</para>

      <para>The zImage decompression code will ensure it is not going
      to overwrite the compressed data<!-- (if the compressed data is
      coming from from flash or some other area of memory this is
      obviously not be an issue)-->. If the kernel detects such a
      conflict it will uncompress the image immediately
      <emphasis>after</emphasis> the compressed zImage data and
      relocate the kernel after decompression. This obviously has the
      impact that the memory region the zImage is loaded into
      <emphasis>must</emphasis> have up to 4Megabytes of space after
      it (the maximum uncompressed kernel size), i.e. placing the
      zImage in the same 4Megabyte bank as its ZRELADDR would probably
      not work as expected.</para>

    </note>

    <para>Despite the ability to place zImage anywhere within memory,
    convention has it that it is loaded at the base of physical RAM
    plus an offset of 0x8000 (32K). This leaves space for the parameter
    block usually placed at offset 0x100, zero page exception vectors
    and page tables. This convention is <emphasis>very</emphasis>
    common.</para>

  </section>

  <section>
    <title>Loading an initial RAM disk</title>

    <para>An initial RAM disk is a common requirement on many systems.
    It provides a way to have a root filesystem available without
    access to other drivers or configurations. Full details can be
    obtained from
    <literal>linux/Documentation/initrd.txt</literal></para>


    <para>There are two methods available on ARM Linux to obtain an
    initial RAM disk. The first is a special build target bootpImage
    which takes an initial RAM disk at <emphasis>build</emphasis> time
    and appends it to a zImage. This method has the benefit that it needs
    no bootloader intervention, but requires the kernel build process to
    have knowledge of the physical address to place the ramdisk (using
    the INITRD_PHYS definition). The hard size limit for the
    uncompressed kernel and initrd of 4Megabytes applies. Because of these
    limitations this target is rarely used in practice.</para>

    <para>The second and much more widely used method is for the
    bootloader to place a given initial ramdisk image, obtained from
    whatever media, into memory at a set location. This location is
    passed to the kernel using <xref linkend="ATAG_INITRD2"/> and
    <xref linkend="ATAG_RAMDISK"/>.</para>

    <para>Conventionally the initrd is placed 8Megabytes from the base
    of physical memory. Wherever it is placed there must be
    sufficient memory after boot to decompress the initial ramdisk
    into a real ramdisk i.e. enough memory for zImage + decompressed
    zImage + initrd + uncompressed ramdisk. The compressed initial
    ramdisk memory will be freed after the decompression has
    happened. Limitations to the position of the ramdisk are:

<simplelist>
	<member>It must lie completely within a single memory region (must not cross between areas defined by different <xref linkend="ATAG_MEM"/> parameters)</member>
	<member>It must be aligned to a page boundary (typically 4k)</member>
	<member>It must not conflict with the memory the zImage head code uses to decompress the kernel or it <emphasis>will</emphasis> be overwritten as no checking is performed.</member>
      </simplelist>
</para>

  </section>

  <section>
    <title>Initialising a console</title>

    <para>A console is highly recommended as a method to see what
    actions the kernel is performing when initialising a system. This can
    be any input output device with a suitable driver, the most common
    cases are a video framebuffer driver or a serial driver. Systems
    that ARM Linux runs on tend to almost always provide a serial
    console port.</para>

    <para>The bootloader should initialise and enable one serial port
    on the target.This includes enabling any hardware power
    management etc., to use the port.  This allows the kernel serial
    driver to automatically detect which serial port it should use for
    the kernel console (generally used for debugging purposes, or
    communication with the target.)</para>

    <para>As an alternative, the bootloader can pass the relevant
    'console=' option to the kernel, via the command line parameter
    specifying the port, and serial format options as described in

        <literal>linux/Documentation/kernel-parameters.txt</literal>
</para>
  </section>

  <section>
    <title>Kernel parameters</title>
    <para>The bootloader must pass parameters to the kernel to describe the setup it has performed, the size and shape of memory in the system and, optionally, numerous other values.</para>

    <para>The tagged list should conform to the following constraints
      <simplelist>

        <member>The list must be stored in RAM and placed in a region
        of memory where neither the kernel decompresser nor initrd
        manipulation will overwrite it.  The recommended placement is
        in the first 16KiB of RAM, usually the start of physical RAM
        plus 0x100 (which avoids zero page exception
        vectors).</member>

	<member>The physical address of the tagged list must be placed
        in R2 on entry to the kernel, however historically this has not
        been mandatory and the kernel has used the fixed value of
        the start of physical RAM plus 0x100. This must
        <emphasis>not</emphasis> be relied upon in the
        future.</member>

	<member>The list must not extend past the 0x4000 boundary
	where the kernel's initial translation page table is
	created. The kernel performs no bounds checking and will
	overwrite the parameter list if it does so.</member>

	<member>The list must be aligned to a word (32 bit, 4byte)
	boundary (if not using the recommended location)</member>

	<member>The list must begin with an <xref linkend="ATAG_CORE"/> and end with <xref linkend="ATAG_NONE"/></member>

	<member>The list must contain at least one <xref linkend="ATAG_MEM"/></member>

      </simplelist>
    </para>

    <para>Each tag in the list consists of a header containing two
    unsigned 32 bit values, the size of the tag (in 32 bit, 4 byte
    words) and the tag value

      <programlisting>struct atag_header {
        u32 size; /* legth of tag in words including this header */
        u32 tag;  /* tag value */
};</programlisting>
    </para>

    <para>Each tag header is followed by data associated with that
    tag, excepting <xref linkend="ATAG_NONE"/> which has no data and
    <xref linkend="ATAG_CORE"/> where the data is optional. The size
    of the data is determined by the size field in header, the minimum
    size is 2 as the headers size is included in this value. The <xref
    linkend="ATAG_NONE"/> is unique in that its size field is set to
    zero.</para>

    <para>A tag may contain additional data after the mandated
    structures provided the size is adjusted to cover the extra
    information, this allows for future expansion and for a bootloader
    to extend the data provided to the kernel. For example a
    bootloader may provide additional serial number information in an
    <xref linkend="ATAG_SERIAL"/> which could them be interpreted by a
    modified kernel.</para>

    <para>The order of the tags in the parameter list is unimportant,
    they may appear as many times as required although interpretation
    of duplicate tags is tag dependant.</para>

    <para>The data for each individual tag is described in the <xref
    linkend="appendix_tag_reference"/> section.</para>

    <table frame="all" id="table_atags_list">
      <title>List of usable tags</title>
      <tgroup cols="4">
	<colspec colname="name" colwidth="1*"/>
	<colspec colname="value" colwidth="2*"/>
	<colspec colname="size" colwidth="1*"/>
	<colspec colname="description" colwidth="4*"/>
	<thead>
	  <row>
	    <entry>Tag name</entry>
	    <entry>Value</entry>
	    <entry>Size</entry>
	    <entry>Description</entry>
	  </row>
	</thead>
	<tbody>
	  <row>
	    <entry><xref linkend="ATAG_NONE"/></entry>
	    <entry>0x00000000</entry>
	    <entry>2</entry>
	    <entry>Empty tag used to end list</entry>
	  </row>
	  <row>
	    <entry><xref linkend="ATAG_CORE"/></entry>
	    <entry>0x54410001</entry>
	    <entry>5 (2 if empty)</entry>
	    <entry>First tag used to start list</entry>
	  </row>
	  <row><entry><xref linkend="ATAG_MEM"/></entry>
	    <entry>0x54410002</entry>
	    <entry>4</entry>
	    <entry>Describes a physical area of memory</entry>
	  </row>
	  <row>
	    <entry><xref linkend="ATAG_VIDEOTEXT"/></entry>
	    <entry>0x54410003</entry>
	    <entry>5</entry>
	    <entry>Describes a VGA text display</entry>
	  </row>
	  <row>
	    <entry><xref linkend="ATAG_RAMDISK"/></entry>
	    <entry>0x54410004</entry>
	    <entry>5</entry>
	    <entry>Describes how the ramdisk will be used in kernel
</entry>
	  </row>
	  <row>
	    <entry><xref linkend="ATAG_INITRD2"/></entry>
	    <entry>0x54420005</entry>
	    <entry>4</entry>
	    <entry>Describes where the compressed ramdisk image is
placed in memory</entry>
	  </row>
	  <row>
	    <entry><xref linkend="ATAG_SERIAL"/></entry>
	    <entry>0x54410006</entry>
	    <entry>4</entry>
	    <entry>64 bit board serial number</entry>
	  </row>
	  <row>
	    <entry><xref linkend="ATAG_REVISION"/></entry>
	    <entry>0x54410007</entry>
	    <entry>3</entry>
	    <entry>32 bit board revision number</entry>
	  </row>
	  <row>
	    <entry><xref linkend="ATAG_VIDEOLFB"/></entry>
	    <entry>0x54410008</entry>
	    <entry>8</entry>
	    <entry>Initial values for vesafb-type framebuffers</entry>
	  </row>
	  <row>
	    <entry><xref linkend="ATAG_CMDLINE"/></entry>
	    <entry>0x54410009</entry>
	    <entry>2 + ((length_of_cmdline + 3) / 4)</entry>
	    <entry>Command line to pass to kernel </entry>
	  </row>
	</tbody>
      </tgroup>
      </table>

    <para>For implementation purposes a structure can be defined for a tag
      <programlisting>struct atag {
        struct atag_header hdr;
        union {
                struct atag_core         core;
                struct atag_mem          mem;
                struct atag_videotext    videotext;
                struct atag_ramdisk      ramdisk;
                struct atag_initrd2      initrd2;
                struct atag_serialnr     serialnr;
                struct atag_revision     revision;
                struct atag_videolfb     videolfb;
                struct atag_cmdline      cmdline;
        } u;
};</programlisting></para>

    <para>Once these structures have been defined an implementation needs to create the list this can be implemented with code similar to
<programlisting>#define tag_next(t)     ((struct tag *)((u32 *)(t) + (t)->hdr.size))
#define tag_size(type)  ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
static struct atag *params; /* used to point at the current tag */

static void
setup_core_tag(void * address,long pagesize)
{
    params = (struct tag *)address;         /* Initialise parameters to start at given address */

    params->hdr.tag = ATAG_CORE;            /* start with the core tag */
    params->hdr.size = tag_size(atag_core); /* size the tag */

    params->u.core.flags = 1;               /* ensure read-only */
    params->u.core.pagesize = pagesize;     /* systems pagesize (4k) */
    params->u.core.rootdev = 0;             /* zero root device (typicaly overidden from commandline )*/

    params = tag_next(params);              /* move pointer to next tag */
}

static void
setup_mem_tag(u32_t start, u32_t len)
{
    params->hdr.tag = ATAG_MEM;             /* Memory tag */
    params->hdr.size = tag_size(atag_mem);  /* size tag */

    params->u.mem.start = start;            /* Start of memory area (physical address) */
    params->u.mem.size = len;               /* Length of area */

    params = tag_next(params);              /* move pointer to next tag */
}

static void
setup_end_tag(void)
{
    params->hdr.tag = ATAG_NONE;            /* Empty tag ends list */
    params->hdr.size = 0;                   /* zero length */
}


static void
setup_tags(void)
{
    setup_core_tag(0x100, 4096);            /* standard core tag 4k pagesize */
    setup_mem_tag(0x10000000, 0x400000);    /* 64Mb at 0x10000000 */
    setup_mem_tag(0x18000000, 0x400000);    /* 64Mb at 0x18000000 */
    setup_end_tag(void);                    /* end of tags */
}</programlisting>
    </para>

    <para>While this code fragment is complete it illustrates the
    absolute minimal requirements for a parameter set and is intended
    to demonstrate the concepts expressed earlier in this section.  A
    real bootloader would probably pass additional values and would
    probably probe for the memory actually in a system rather than
    using fixed values. A more complete example can be found in <xref
    linkend="appendix_complete_example"/></para>




  </section>


  <section>
    <title>Obtaining the ARM Linux machine type</title>

    <para>The only additional information the bootloader needs to
    provide is the machine type, this is a simple number
    unique for each ARM system often referred to as a MACH_TYPE.</para>

    <para>The machine type number is obtained via the ARM Linux
    website <ulink
    url="http://www.arm.linux.org.uk/developer/machines/">Machine
    Registry</ulink>. A machine type should be obtained as early in a
    projects life as possible, it has a number of ramifications for
    the kernel port itself (machine definitions etc.) and changing
    definitions afterwards may lead to a number of undesirable
    issues. These values are represented by a list of defines within
    the kernel source (linux/arch/arm/tools/mach-types)</para>

    <para>The boot loader must obtain the machine type value by some
    method.  Whether this is a hard coded value or an algorithm that
    looks at the connected hardware. Implementation is completely
    system specific and is beyond the scope of this document.</para>

  </section>


  <section>
    <title>Starting the kernel</title>

    <para>Once the bootloader has performed all the other steps it
    must start execution of the kernel with the correct values in the
    CPU registers.</para>

    <para>The entry requirements are:
    <simplelist>
      <member>The CPU must be in SVC (supervisor) mode with both IRQ and FIQ interrupts disabled.</member>
      <member>The MMU must be <emphasis>off</emphasis>, i.e. code running from physical RAM with no translated addressing.</member>
      <member>Data cache must be <emphasis>off</emphasis></member>
      <member>Instruction cache may be either on or off</member>
      <member>CPU register 0 must be 0</member>
      <member>CPU register 1 must be the ARM Linux machine type</member>
      <member>CPU register 2 must be the physical address of the parameter list</member>
    </simplelist>
    </para>

    <para>The bootloader is expected to call the kernel image by
    jumping directly to the first instruction of the kernel
    image.</para>

  </section>

  <appendix id="appendix_tag_reference">
    <title>Tag Reference</title>

    <refentry id="ATAG_CORE">
      <refnamediv>
	<refname>ATAG_CORE</refname>
	<refpurpose>Start tag used to begin list</refpurpose>
      </refnamediv>
      <refsection>
	<title>Value</title>
	<para>0x54410001</para>
      </refsection>
      <refsection>
	<title>Size</title>
	<para>5 (2 if no data)</para>
      </refsection>
      <refsection>
	<title>Structure members</title>
	<para>
<programlisting>struct atag_core {
        u32 flags;              /* bit 0 = read-only */
        u32 pagesize;           /* systems page size (usually 4k) */
        u32 rootdev;            /* root device number */
};</programlisting>
        </para>
      </refsection>
      <refsection>
	<title>Description</title>

	<para>This tag <emphasis>must</emphasis> be used to start the
	list, it contains the basic information any bootloader must
	pass, a tag length of 2 indicates the tag has no structure
	attached.</para>

      </refsection>
      </refentry>

      <refentry id="ATAG_NONE">
	<refnamediv>
	  <refname>ATAG_NONE</refname>
	  <refpurpose>Empty tag used to end list</refpurpose>
	</refnamediv>
	<refsection>
	  <title>Value</title>
	  <para>0x00000000</para>
	</refsection>
	<refsection>
	  <title>Size</title>
	  <para>2</para>
	</refsection>
	<refsection>
	  <title>Structure members</title>
	  <para>None</para>
	</refsection>
	<refsection>
	  <title>Description</title>
	  <para>This tag is used to indicate the list end. It is
	  unique in that its size field in the header should be set to
	  0 (not 2).</para>
	</refsection>
      </refentry>


      <refentry id="ATAG_MEM">
	<refnamediv>
	  <refname>ATAG_MEM</refname>
	  <refpurpose>Tag used to describe a physical area of memory.</refpurpose>
	</refnamediv>
	<refsection>
	  <title>Value</title>
	  <para>0x54410002</para>
	</refsection>
	<refsection>
	  <title>Size</title>
	  <para>4</para>
	</refsection>
	<refsection>
	  <title>Structure members</title>
	  <para><programlisting>struct atag_mem {
        u32     size;   /* size of the area */
        u32     start;  /* physical start address */
};</programlisting></para>
	</refsection>
	<refsection>
	  <title>Description</title>
	  <para>Describes an area of physical memory the kernel is to use.</para>
	</refsection>
      </refentry>

      <refentry id="ATAG_VIDEOTEXT">
	<refnamediv>
	  <refname>ATAG_VIDEOTEXT</refname>
	  <refpurpose>Tag used to describe VGA text type displays</refpurpose>
	</refnamediv>
	<refsection>
	  <title>Value</title>
	  <para>0x54410003</para>
	</refsection>
	<refsection>
	  <title>Size</title>
	  <para>5</para>
	</refsection>
	<refsection>
	  <title>Structure members</title>
	  <para><programlisting>struct atag_videotext {
        u8              x;           /* width of display */
        u8              y;           /* height of display */
        u16             video_page;
        u8              video_mode;
        u8              video_cols;
        u16             video_ega_bx;
        u8              video_lines;
        u8              video_isvga;
        u16             video_points;
};</programlisting></para>
	</refsection>
	<refsection>
	  <title>Description</title>
	  <para></para>
	</refsection>
      </refentry>

      <refentry id="ATAG_RAMDISK">
	<refnamediv>
	  <refname>ATAG_RAMDISK</refname>
	  <refpurpose>Tag describing how the ramdisk will be used by the kernel</refpurpose>
	</refnamediv>
	<refsection>
	  <title>Value</title>
	  <para>0x54410004</para>
	</refsection>
	<refsection>
	  <title>Size</title>
	  <para>5</para>
	</refsection>
	<refsection>
	  <title>Structure members</title>
	  <para><programlisting>struct atag_ramdisk {
        u32 flags;      /* bit 0 = load, bit 1 = prompt */
        u32 size;       /* decompressed ramdisk size in _kilo_ bytes */
        u32 start;      /* starting block of floppy-based RAM disk image */
};</programlisting></para>
	</refsection>
	<refsection>
	  <title>Description</title>
	  <para>Describes how the (initial) ramdisk will be configured by the kernel, specifically this allows for the bootloader to ensure the ramdisk will be large enough to take the <emphasis>decompressed</emphasis> initial ramdisk image the bootloader is passing using <xref linkend="ATAG_INITRD2"/>.</para>
	</refsection>
      </refentry>

      <refentry id="ATAG_INITRD2">
	<refnamediv>
	  <refname>ATAG_INITRD2</refname>
	  <refpurpose>Tag describing the physical location of the compressed ramdisk image</refpurpose>
	</refnamediv>
	<refsection>
	  <title>Value</title>
	  <para>0x54420005</para>
	</refsection>
	<refsection>
	  <title>Size</title>
	  <para>4</para>
	</refsection>
	<refsection>
	  <title>Structure members</title>
	  <para><programlisting>struct atag_initrd2 {
        u32 start;      /* physical start address */
        u32 size;       /* size of compressed ramdisk image in bytes */
};</programlisting></para>
	</refsection>
	<refsection>
	  <title>Description</title>
	<para>Location of a compressed ramdisk image, usually combined with an <xref linkend="ATAG_RAMDISK"/>. Can be used as an initial root file system with the addition of a command line parameter of 'root=/dev/ram'. This tag <emphasis>supersedes</emphasis> the original ATAG_INITRD which used virtual addressing, this was a mistake and produced issues on some systems. All new bootloaders should use this tag in preference.</para>
	</refsection>
      </refentry>

      <refentry id="ATAG_SERIAL">
	<refnamediv>
	  <refname>ATAG_SERIAL</refname>
	  <refpurpose>Tag with 64 bit serial number of the board</refpurpose>
	</refnamediv>
	<refsection>
	  <title>Value</title>
	  <para>0x54410006</para>
	</refsection>
	<refsection>
	  <title>Size</title>
	  <para>4</para>
	</refsection>
	<refsection>
	  <title>Structure members</title>
	  <para><programlisting>struct atag_serialnr {
        u32 low;
        u32 high;
};</programlisting></para>
	</refsection>
	<refsection>
	  <title>Description</title>
	  <para></para>
	</refsection>
      </refentry>

      <refentry id="ATAG_REVISION">
	<refnamediv>
	  <refname>ATAG_REVISION</refname>
	  <refpurpose>Tag for the board revision</refpurpose>
	</refnamediv>
	<refsection>
	  <title>Value</title>
	  <para>0x54410007</para>
	</refsection>
	<refsection>
	  <title>Size</title>
	  <para>3</para>
	</refsection>
	<refsection>
	  <title>Structure members</title>
	  <para><programlisting>struct atag_revision {
        u32 rev;
};</programlisting></para>
	</refsection>
	<refsection>
	  <title>Description</title>
	  <para></para>
	</refsection>
      </refentry>

      <refentry id="ATAG_VIDEOLFB">
	<refnamediv>
	  <refname>ATAG_VIDEOLFB</refname>
	  <refpurpose>Tag describing parameters for a framebuffer type display</refpurpose>
	</refnamediv>
	<refsection>
	  <title>Value</title>
	  <para>0x54410008</para>
	</refsection>
	<refsection>
	  <title>Size</title>
	  <para>8</para>
	</refsection>
	<refsection>
	  <title>Structure members</title>
	  <para><programlisting>struct atag_videolfb {
        u16             lfb_width;
        u16             lfb_height;
        u16             lfb_depth;
        u16             lfb_linelength;
        u32             lfb_base;
        u32             lfb_size;
        u8              red_size;
        u8              red_pos;
        u8              green_size;
        u8              green_pos;
        u8              blue_size;
        u8              blue_pos;
        u8              rsvd_size;
        u8              rsvd_pos;
};</programlisting></para>
	</refsection>
	<refsection>
	  <title>Description</title>
	  <para></para>
	</refsection>
      </refentry>

      <refentry id="ATAG_CMDLINE">
	<refnamediv>
	  <refname>ATAG_CMDLINE</refname>
	  <refpurpose>Tag used to pass the commandline to the kernel</refpurpose>
	</refnamediv>
	<refsection>
	  <title>Value</title>
	  <para>0x54410009</para>
	</refsection>
	<refsection>
	  <title>Size</title>
	  <para>2 + ((length_of_cmdline + 3) / 4)</para>
	</refsection>
	<refsection>
	  <title>Structure members</title>
	  <para><programlisting>struct atag_cmdline {
        char    cmdline[1];     /* this is the minimum size */
};</programlisting></para>
	</refsection>
	<refsection>
	  <title>Description</title>
	  <para>Used to pass command line parameters to the
	  kernel. The command line must be NULL terminated. The
	  length_of_cmdline variable should include the terminator.</para>
	</refsection>
      </refentry>

  </appendix>

  <appendix id="appendix_complete_example">
    <title>Complete example</title>

    <para>This is a worked example of a simple bootloader and shows
    all the information explained throughout this document. More code
    would be required for a real bootloader this example is purely
    illustrative.</para>

    <para>The code in this example is distributed under a BSD licence,
    it may be freely copied and used if necessary.
<programlisting>/* example.c
 * example ARM Linux bootloader code
 * this example is distributed under the BSD licence
 */

/* list of possible tags */
#define ATAG_NONE       0x00000000
#define ATAG_CORE       0x54410001
#define ATAG_MEM        0x54410002
#define ATAG_VIDEOTEXT  0x54410003
#define ATAG_RAMDISK    0x54410004
#define ATAG_INITRD2    0x54420005
#define ATAG_SERIAL     0x54410006
#define ATAG_REVISION   0x54410007
#define ATAG_VIDEOLFB   0x54410008
#define ATAG_CMDLINE    0x54410009

/* structures for each atag */
struct atag_header {
        u32 size; /* length of tag in words including this header */
        u32 tag;  /* tag type */
};

struct atag_core {
        u32 flags;
        u32 pagesize;
        u32 rootdev;
};

struct atag_mem {
        u32     size;
        u32     start;
};

struct atag_videotext {
        u8              x;
        u8              y;
        u16             video_page;
        u8              video_mode;
        u8              video_cols;
        u16             video_ega_bx;
        u8              video_lines;
        u8              video_isvga;
        u16             video_points;
};

struct atag_ramdisk {
        u32 flags;
        u32 size;
        u32 start;
};

struct atag_initrd2 {
        u32 start;
        u32 size;
};

struct atag_serialnr {
        u32 low;
        u32 high;
};

struct atag_revision {
        u32 rev;
};

struct atag_videolfb {
        u16             lfb_width;
        u16             lfb_height;
        u16             lfb_depth;
        u16             lfb_linelength;
        u32             lfb_base;
        u32             lfb_size;
        u8              red_size;
        u8              red_pos;
        u8              green_size;
        u8              green_pos;
        u8              blue_size;
        u8              blue_pos;
        u8              rsvd_size;
        u8              rsvd_pos;
};

struct atag_cmdline {
        char    cmdline[1];
};

struct atag {
        struct atag_header hdr;
        union {
                struct atag_core         core;
                struct atag_mem          mem;
                struct atag_videotext    videotext;
                struct atag_ramdisk      ramdisk;
                struct atag_initrd2      initrd2;
                struct atag_serialnr     serialnr;
                struct atag_revision     revision;
                struct atag_videolfb     videolfb;
                struct atag_cmdline      cmdline;
        } u;
};


#define tag_next(t)     ((struct tag *)((u32 *)(t) + (t)->hdr.size))
#define tag_size(type)  ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
static struct atag *params; /* used to point at the current tag */

static void
setup_core_tag(void * address,long pagesize)
{
    params = (struct tag *)address;         /* Initialise parameters to start at given address */

    params->hdr.tag = ATAG_CORE;            /* start with the core tag */
    params->hdr.size = tag_size(atag_core); /* size the tag */

    params->u.core.flags = 1;               /* ensure read-only */
    params->u.core.pagesize = pagesize;     /* systems pagesize (4k) */
    params->u.core.rootdev = 0;             /* zero root device (typicaly overidden from commandline )*/

    params = tag_next(params);              /* move pointer to next tag */
}

static void
setup_ramdisk_tag(u32_t size)
{
    params->hdr.tag = ATAG_RAMDISK;         /* Ramdisk tag */
    params->hdr.size = tag_size(atag_ramdisk);  /* size tag */

    params->u.ramdisk.flags = 0;            /* Load the ramdisk */
    params->u.ramdisk.size = size;          /* Decompressed ramdisk size */
    params->u.ramdisk.start = 0;            /* Unused */

    params = tag_next(params);              /* move pointer to next tag */
}

static void
setup_initrd2_tag(u32_t start, u32_t size)
{
    params->hdr.tag = ATAG_INITRD2;         /* Initrd2 tag */
    params->hdr.size = tag_size(atag_initrd2);  /* size tag */

    params->u.initrd2.start = start;        /* physical start */
    params->u.initrd2.size = size;          /* compressed ramdisk size */

    params = tag_next(params);              /* move pointer to next tag */
}

static void
setup_mem_tag(u32_t start, u32_t len)
{
    params->hdr.tag = ATAG_MEM;             /* Memory tag */
    params->hdr.size = tag_size(atag_mem);  /* size tag */

    params->u.mem.start = start;            /* Start of memory area (physical address) */
    params->u.mem.size = len;               /* Length of area */

    params = tag_next(params);              /* move pointer to next tag */
}

static void
setup_cmdline_tag(const char * line)
{
    int linelen = strlen(line);

    if(!linelen)
        return;                             /* do not insert a tag for an empty commandline */

    params->hdr.tag = ATAG_CMDLINE;         /* Commandline tag */
    params->hdr.size = (sizeof(struct atag_header) + linelen + 1 + 4) >> 2;

    strcpy(params->u.cmdline.cmdline,line); /* place commandline into tag */

    params = tag_next(params);              /* move pointer to next tag */
}

static void
setup_end_tag(void)
{
    params->hdr.tag = ATAG_NONE;            /* Empty tag ends list */
    params->hdr.size = 0;                   /* zero length */
}


#define DRAM_BASE 0x10000000
#define ZIMAGE_LOAD_ADDRESS DRAM_BASE + 0x8000
#define INITRD_LOAD_ADDRESS DRAM_BASE + 0x800000

static void
setup_tags(parameters)
{
    setup_core_tag(parameters, 4096);       /* standard core tag 4k pagesize */
    setup_mem_tag(DRAM_BASE, 0x4000000);    /* 64Mb at 0x10000000 */
    setup_mem_tag(DRAM_BASE + 0x8000000, 0x4000000); /* 64Mb at 0x18000000 */
    setup_ramdisk_tag(4096);                /* create 4Mb ramdisk */ 
    setup_initrd2_tag(INITRD_LOAD_ADDRESS, 0x100000); /* 1Mb of compressed data placed 8Mb into memory */
    setup_cmdline_tag("root=/dev/ram0");    /* commandline setting root device */
    setup_end_tag(void);                    /* end of tags */
}

int
start_linux(char *name,char *rdname)
{
    void (*theKernel)(int zero, int arch, u32 params);
    u32 exec_at = (u32)-1;
    u32 parm_at = (u32)-1;
    u32 machine_type;

    exec_at = ZIMAGE_LOAD_ADDRESS;
    parm_at = DRAM_BASE + 0x100

    load_image(name, exec_at);              /* copy image into RAM */

    load_image(rdname, INITRD_LOAD_ADDRESS);/* copy initial ramdisk image into RAM */

    setup_tags(parm_at);                    /* sets up parameters */

    machine_type = get_mach_type();         /* get machine type */

    irq_shutdown();                         /* stop irq */

    cpu_op(CPUOP_MMUCHANGE, NULL);          /* turn MMU off */

    theKernel = (void (*)(int, int, u32))exec_at; /* set the kernel address */

    theKernel(0, machine_type, parm_at);    /* jump to kernel with register set */

    return 0;
}</programlisting></para>
  </appendix>

  <bibliography id="article_bilbiography">
    <biblioentry>
      <citetitle><ulink url="http://www.arm.linux.org.uk/developer/">ARM Linux website Documentation</ulink></citetitle>
      <author>
	<firstname>Russell</firstname>
	<surname>King</surname>
	<othername>M</othername>
      </author>
    </biblioentry>
    <biblioentry>
      <citetitle><ulink url="http://www.arm.linux.org.uk/developer/booting.php">Linux Kernel Documentation/arm/booting.txt</ulink></citetitle>
      <author>
	<firstname>Russell</firstname>
	<surname>King</surname>
	<othername>M</othername>
      </author>
    </biblioentry>
    <biblioentry>
      <citetitle><ulink url="http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2003-January/013126.html">Setting R2 correctly for booting the kernel</ulink> (explanation of booting requirements)</citetitle>
      <author>
	<firstname>Russell</firstname>
	<surname>King</surname>
	<othername>M</othername>
      </author>
    </biblioentry>
    <biblioentry>
      <citetitle><ulink url="http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2002-April/008700.html">Wookey's post summarising booting</ulink></citetitle>
      <author>
	<surname>Wookey</surname>
      </author>
    </biblioentry>
    <biblioentry>
      <citetitle><ulink url="http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2001-July/004064.html">Makefile defines and symbols</ulink></citetitle>
      <author>
	<firstname>Russell</firstname>
	<surname>King</surname>
	<othername>M</othername>
      </author>
    </biblioentry>
    <biblioentry>
      <citetitle><ulink url="http://www.aleph1.co.uk/armlinux/docs/ARMbooting/t1.html">Bootloader guide</ulink></citetitle>
      <author>
	<surname>Wookey</surname>
      </author>
    </biblioentry>

    <biblioentry>
      <citetitle><ulink url="http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2001-October/005212.html">Kernel boot order</ulink></citetitle>
      <author>
	<firstname>Russell</firstname>
	<surname>King</surname>
	<othername>M</othername>
      </author>
    </biblioentry>

    <biblioentry>
      <citetitle><ulink url="http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2002-January/006730.html">Advice for head.S Debugging</ulink></citetitle>
      <author>
	<firstname>Russell</firstname>
	<surname>King</surname>
	<othername>M</othername>
      </author>
    </biblioentry>

    <biblioentry>
      <citetitle><ulink url="http://billgatliff.com/articles/emb-linux/startup.html/index.html">Linux kernel 2.4 startup</ulink></citetitle>
      <author>
	<firstname>Bill</firstname>
	<surname>Gatliff</surname>
      </author>
    </biblioentry>

    <biblioentry>
      <citetitle><ulink url="http://www.sf.net/projects/blob/">Blob bootloader</ulink></citetitle>
      <author>
	<firstname>Erik</firstname>
	<surname>Mouw</surname>
      </author>
    </biblioentry>

    <biblioentry>
      <citetitle><ulink url="http://www.lart.tudelft.nl/lartware/blob/">Blob bootloader on lart</ulink></citetitle>
      <author>
	<firstname>Erik</firstname>
	<surname>Mouw</surname>
      </author>
    </biblioentry>

  </bibliography>

<!--
Local Variables:
sgml-parent-document: ("../linux-set.xml" "book" "article")
End:
-->
