Difference between revisions of "Linux/MIPS Porting Guide"

From LinuxMIPS
Jump to: navigation, search
(Interrupts: 67.176.99.141: Typo fix)
(What if the board only has one serial port?)
(34 intermediate revisions by 5 users not shown)
Line 1: Line 1:
== Prefix ==
+
This document reflects what [http://junsun.net/ I] have learned through porting several MIPS machines and other related Linux work. Hopefully it will help beginners to get started and give the experienced a reference point.
 
+
This document reflects what I have learned through porting several MIPS machines and other related Linux work. Hopefully it will help beginners to get started and give the experienced a reference point.
+
  
 
This document goes through all the major steps to port Linux to a MIPS machine. The focus can perhaps be called "MIPS machine abstraction layer", i.e., the interface between machine-specific code and, mostly, MIPS common code. Another useful document focuses on "Linux hardware abstraction layer", i.e., the interface between Linux common code and architecture-specific code. [http://hanishkvc.tripod.com/work/docs/LinuxAJourney.html The document] is written by C Hanish Menon (www.hanishkvc.com).
 
This document goes through all the major steps to port Linux to a MIPS machine. The focus can perhaps be called "MIPS machine abstraction layer", i.e., the interface between machine-specific code and, mostly, MIPS common code. Another useful document focuses on "Linux hardware abstraction layer", i.e., the interface between Linux common code and architecture-specific code. [http://hanishkvc.tripod.com/work/docs/LinuxAJourney.html The document] is written by C Hanish Menon (www.hanishkvc.com).
  
There are some notations used in this document.
+
== Overview ==
 
+
; TODO : Reminder for incomplete part
+
; HELP : Really need your help on this
+
; DEBATE : Here is my opinion. What do you think?
+
; THANKS : Thanks to the person who pointed this out
+
; NOTE : Additional note added later, but the original comment may be still useful
+
 
+
== An Overview ==
+
  
 
=== Prerequisites ===
 
=== Prerequisites ===
 
* Know C programming.
 
* Know C programming.
 
* Have some knowledge of OS concepts, such as interrupt handling, system calls, memory management.
 
* Have some knowledge of OS concepts, such as interrupt handling, system calls, memory management.
* Know how to configure and make Linux kernel. You can find many helps on this if you are not very comfortable.
+
* Know how to configure and make Linux kernel. You can find much help on this if you are not very comfortable.
 
* Have some knowledge of MIPS CPU. More than likely you will need to deal with CP0 registers, enable or disable interrupts, etc..
 
* Have some knowledge of MIPS CPU. More than likely you will need to deal with CP0 registers, enable or disable interrupts, etc..
 
* You don't have to be an expert in MIPS assembly, but total ignorance of it might make you handicapped in some situations.
 
* You don't have to be an expert in MIPS assembly, but total ignorance of it might make you handicapped in some situations.
Line 24: Line 14:
 
* Finally but most importantly, you need a willing-to-learn heart and perhaps many restless debugging hours. :-)  
 
* Finally but most importantly, you need a willing-to-learn heart and perhaps many restless debugging hours. :-)  
  
It is also highly recommanded to read through the [[Linux MIPS HOWTO]] by Ralf Bächle, ralf@gnu.org. By the way, as part of the pre-requisite, you should also remember Ralf's name. :-)
+
It is also highly recommanded to read through the [http://oss.sgi.com/mips/mips-howto.html Linux MIPS HOWTO] by Ralf Bächle, ralf@gnu.org. By the way, as part of the pre-requisite, you should also remember Ralf's name. :-)
 
+
=== Kernel source trees ===
+
The common MIPS tree is the CVS tree at linux-mips.org. See the instructions in "Anonymous CVS servers" section in Linux/MIPS HOWTO. The current kernel version as of 2004/01/26 is 2.6.1. You can always check out earlier stable revisions by using "linux_2_4" or even "linux_2_2" branch tag.
+
 
+
=== Kernel patches ===
+
For various reasons, a kernel tree may leave bugs there for a quite long time before a suitable fix is checked in. There are various places to get patches. Here are some of the more common ones:
+
 
+
* [http://linux.junsun.net/patches/ Jun Sun patches]
+
* [ftp://ftp.linux-mips.org/pub/linux/mips/ Linux/MIPS FTP archive]
+
* [ftp://ftp.ds2.pg.gda.pl/pub/macro/ Maciej W. Rozyki patches]
+
* [ftp://ftp.laronde.org/pub/linux/patches Brad LaRonde's patches]
+
 
+
=== Cross-compilation and toolchains ===
+
 
+
More than likely your MIPS box does not run Linux yet (why would you bother otherwise?). Therefore you will need another machine to build the kernel image. Once the image is built, you need to download this image to your MIPS machine and let it run your MIPS kernel. This is called cross-development. Your MIPS box is often called the target machine and the machine used to build the kernel image is called the host machine.
+
 
+
Cross-development is common for developing on embedded targets, because usually embedded targets do not have enough power or the peripherals to develop natively.
+
 
+
The most common host machine is probably Linux on i386/PCs.
+
 
+
You need to have cross-development tools setup on your host before you can start. Several years of experience have shown that while you can find instructions to build cross-compilation tools, your best bet is probably to get some ready-made ones. Another reason is that a full [[Toolchains|toolchain]] build has become quite time consuming so you probably want to avoid it.
+
 
+
MontaVista used to offer for free Journeyman edition, which includes a full featured toolchain. Unfortunately, it does not offer that anymore. Instead you can download the preview kit, which includes a "slim" version of toolchain. You can get the kit from http://www.mvista.com/previewkit/index.html
+
 
+
Dan Kegel has a set of scripts that build cross-compiling tools. You can check it out [http://kegel.com/crosstool here].
+
 
+
The following are links to pre-build toolchains, instructions to build your own toolchain and finally pre-compiled distributions for MIPS boards:
+
 
+
* [http://laronde.org/~brad/mips/mips-cross-toolchain/ Brad LaRonde's cross toolchain for Linux]
+
* [ftp://ftp.realitydiluted.com/linux/MIPS/toolchains Steve Hill's toolchains for glibc and uClibc]
+
* [http://www.mips.com/content/Products/SoftwareTools/SDE_Lite/content_html Toolchains from MIPS technologies]
+
* [[Distributions]] for MIPS
+
  
 
=== Overall porting steps ===
 
=== Overall porting steps ===
Depending on your specific cases, some of the following steps can be skipped.
+
Depending on your specific case, some (but not all!) of the following may be skipped:
  
# Hello World! - Get board setup, serial porting working, and print out "Hello, world!" through the serial port.
+
# Setup a cross-development environment.
 +
# "Hello, World!" - Get board setup, serial porting working, and print out "Hello, world!" through the serial port.
 +
# Download the kernel -- this is where you'll be doing all your work.
 
# Add your own code.
 
# Add your own code.
 
# Get early printk working - Make the first MIPS image and see the printk output from kernel.
 
# Get early printk working - Make the first MIPS image and see the printk output from kernel.
Line 70: Line 30:
 
# PCI subsystems - If your machine has PCI, you need to get the PCI subsystem working before you can use PCI devices.
 
# PCI subsystems - If your machine has PCI, you need to get the PCI subsystem working before you can use PCI devices.
 
# Ethernet drivers - You should already have the serial port working before attempting this. Usually the next driver you want is the ethernet driver. With ethernet driver working, you can set up a NFS root file system which gives you a fully working Linux userland.
 
# Ethernet drivers - You should already have the serial port working before attempting this. Usually the next driver you want is the ethernet driver. With ethernet driver working, you can set up a NFS root file system which gives you a fully working Linux userland.
# ROMFS root file system - Alternatively you can create a userland file system as a ROMFS image stored in a ramdisk.
+
# ROMFS root file system - Alternatively you can create a userland file system as a ROMFS image stored in a [[ramdisk]].
 +
 
 +
== Setup a cross-development environment ==
 +
 
 +
You'll need a cross-development environment since your board is not yet self-hosting (ie. unable to ''natively'' build and run executable code). In this scenario, you usually have two machines -- a ''host'' and ''target''. It is on your ''host'' (eg. Linux on i386) that you build your code, and onto your ''target'' (MIPS board) that you download and run it. This is a preferred (and robust) methodology of doing embedded development, since most targets are resource-constrained (memory- and CPU-wise) for things like native builds of whole kernel trees.
 +
 
 +
A primary step towards setting up your environment is ''installing'' a cross-toolchain. While you may be able to roll your own, you're better off using a pre-built set (full toolchain builds have since become quite time consuming). An exception to this is when you require added support for your platform to the toolchain itself, which is out of scope for this guide.
 +
 
 +
See [[Toolchains]] for where to download pre-built tools, or instructions on how to roll your own.
  
 
== "Hello, world!" ==
 
== "Hello, world!" ==
 +
 
In cross development, the serial port is usually the most important interface: That is where you can see anything happening! It might be worthwhile to make sure you get serial porting work before you even start playing with Linux. You can find the [http://linux.junsun.net/porting-howto/src/barebone/ sample code] or [http://linux.junsun.net/porting-howto/src/barebone.tar.gz gzipped tar ball] of a stand-alone program that can do printf. Such a program can even be useful in later debugging staging, e.g., printing out hardware register values.
 
In cross development, the serial port is usually the most important interface: That is where you can see anything happening! It might be worthwhile to make sure you get serial porting work before you even start playing with Linux. You can find the [http://linux.junsun.net/porting-howto/src/barebone/ sample code] or [http://linux.junsun.net/porting-howto/src/barebone.tar.gz gzipped tar ball] of a stand-alone program that can do printf. Such a program can even be useful in later debugging staging, e.g., printing out hardware register values.
  
Line 84: Line 53:
 
Now, fire your "make" command.
 
Now, fire your "make" command.
  
Depending on your downloader on your MIPS box, you may need to generate ELF image, binary image or a SREC image.
+
Depending on your downloader on your MIPS box, you may need to generate [[ELF]] image, binary image or a [[SREC]] image.
  
 
Download the barebone image to your target and give it a run! Connect the serial port to your host machine. Start minicom and hopefully you can see the "Hello, world!" message.
 
Download the barebone image to your target and give it a run! Connect the serial port to your host machine. Start minicom and hopefully you can see the "Hello, world!" message.
Line 95: Line 64:
 
* Hopefully it is not the toolchain problem...
 
* Hopefully it is not the toolchain problem...
  
== Add your own code ==
+
== Getting the Kernel ==
Let us add some code to the tree and make a Linux image. For conveninence sake, let us say we are porting Linux to a MIPS board called Aloha.
+
  
=== Create the right directory for your board ===
+
Ideally, you should be working against the latest stable release. The older stable release, 2.4.x, is only open to bug fixes and won't be accepting any new features. Besides, countless bugs may have since been squished, APIs overhauled etc., all of which you'll greatly benefit from.
Your code for a new board can be classified into board-support code (or board-specific code) and drivers. Driver code should be placed under the 'drivers' directory and board specific code should be placed under 'arch/mips' directory.
+
  
The easiest choice is to create a directory called 'arch/mips/aloha'.
+
See [[Getting_the_kernel|Getting the Kernel]] for where to obtain the latest stable release.
 +
 
 +
== Initial board support ==
 +
 
 +
A first step toward adding support for your board is to hook into the kernel build system (''kbuild''). At this point you should be worrying about getting the tree to actually build. You'll also need to provide the board-specific hooks that the architecture back-end depends on.
 +
 
 +
In the following examples, we assume a board named ''Aloha'' supplied by a vendor ''Hawaii''.
 +
 
 +
=== Decide on a directory hierarchy ===
 +
 
 +
Your code for a new board can be classified into board-support code (or board-specific code) and drivers. Driver code should be placed under the ''drivers/'' directory and board specific code should be placed under the ''arch/mips/'' directory.
 +
 
 +
The easiest choice is to create a directory called ''arch/mips/aloha''.
  
 
However, a couple of other considerations might make it slightly complicated.
 
However, a couple of other considerations might make it slightly complicated.
  
* If Aloha uses a chipset or System on a Chip (SOC) that is already supported or belongs to a bigger family, such as NEC VR41xx and gt64120, it makes sense to put Aloha code under those sub-directories. You can re-use and share a lot of common code.
+
* If Aloha uses a chipset or [[Wikipedia:System_on_a_chip|System on a Chip]] (SOC) that is already supported or belongs to a bigger family, such as [[NEC_VR4100|NEC VR41xx]] or gt64120, it makes sense to put Aloha code under those sub-directories. You can re-use and share a lot of common code.
 
* Similarly, if Aloha is the first board that uses a chipset or SOC which is expected to be used in many other boards, you may want to create similar directory structure. However, if you are not sure, just create your own board specific directory.  
 
* Similarly, if Aloha is the first board that uses a chipset or SOC which is expected to be used in many other boards, you may want to create similar directory structure. However, if you are not sure, just create your own board specific directory.  
  
Line 112: Line 91:
 
To make things worse, sometimes boards made by different companies use the same chipset or SOC. Now what are you going to do? Are you going to duplicate the common code? Or are you going stick one company's board under another company's name?
 
To make things worse, sometimes boards made by different companies use the same chipset or SOC. Now what are you going to do? Are you going to duplicate the common code? Or are you going stick one company's board under another company's name?
  
For header files, you usually create similar directory or header files under include/asm-mips. [DEBATE] For board specific header files, I would encourage people to place them under the corresponding 'arch/mips' directory if possible.
+
For header files, you usually create similar directory or header files under ''include/asm-mips/''. [DEBATE] For board specific header files, I would encourage people to place them under the corresponding ''arch/mips/'' directory if possible.
  
In our exmaple, we will create 'arch/mips/aloha' directory.
+
In our example, we will create ''arch/mips/aloha/'' directory.
  
=== Write the minimum Aloha code ===
+
=== Hook into the build system ===
  
Let us write some code for the Aloha board which can generate a complete Linux image without complaining about missing symbols.
+
Add a Kconfig entry for your board so you are able to select it with any of <code>make *config</code>. For a description of this syntax, see ''Documentation/kbuild/kconfig-language.txt'': (TODO: Document CONFIG_SYS_SUPPORTS_* or better yet, just do it in arch/mips/Kconfig)
  
Go to [http://linux.junsun.net/porting-howto/src/aloha/ this directory] to browse ''arch/mips/aloha/'' directory. Or download the [http://linux.junsun.net/porting-howto/src/aloha.tar.gz gzipped file] of the directory.
+
--- a/arch/mips/Kconfig
 +
+++ b/arch/mips/Kconfig
 +
@@ -229,6 +229,18 @@ config MIPS_ITE8172
 +
          either a NEC Vr5432 or QED RM5231. Say Y here if you wish to build
 +
          a kernel for this platform.
 +
 
 +
+config HAWAII_ALOHA
 +
+      bool "Hawaii Aloha"
 +
+      select DMA_COHERENT
 +
+      select SYS_HAS_CPU_R5000
 +
+      select SYS_SUPPORTS_32BIT_KERNEL
 +
+      select SYS_SUPPORTS_64BIT_KERNEL if EXPERIMENTAL
 +
+      select SYS_SUPPORTS_LITTLE_ENDIAN
 +
+      help
 +
+        This is an evaluation board only available in Hawaii.
 +
+        Before importing one, please check with your local import
 +
+        laws on fictional hardware.
 +
+
 +
  config MACH_JAZZ
 +
        bool "Support for the Jazz family of machines"
 +
        select ARC
  
Obviously the code is not complete yet, but if you follow the following steps and everything is correct, you should be able to generate a Linux/MIPS kernel image of your very own!
+
You'll need a corresponding Makefile entry so <code>make</code> may recurse into your platform sub-directory.
  
=== Hook up your code with the Linux tree ===
+
<code>$(load-y)</code> is the starting address for your Linux image when it is loaded into RAM. Note that the first <code>0x200</code> bytes are used by the exception vectors on most CPUs. Some CPUs will require a larger space, so modify <code>$(load-y)</code> accordingly. Due to the linker's addressing limit, the start address is aligned on a 8KB boundary, so setting <code>$(load-y)</code> to <code>0xffffffff80002000</code> should be reasonable:
  
Most of the steps are fairly straightforward:
+
--- a/arch/mips/Makefile
 +
+++ b/arch/mips/Makefile
 +
@@ -401,6 +401,12 @@ core-$(CONFIG_MIPS_IVR)            += arch/mips/it
 +
  load-$(CONFIG_MIPS_IVR)                += 0xffffffff80100000
 +
 
 +
  #
 +
+# Hawaii Aloha board
 +
+#
 +
+core-$(CONFIG_HAWAII_ALOHA)    += arch/mips/aloha/
 +
+load-$(CONFIG_HAWAII_ALOHA)    += 0xffffffff80002000
 +
+
 +
+#
 +
  # ITE 8172 eval board with QED 5231 CPU
 +
  #
 +
  core-$(CONFIG_MIPS_ITE8172)    += arch/mips/ite-boards/qed-4n-s01b/
  
* ''include/asm-mips/bootinfo.h'' - Add your machine group ID, machine group name and Aloha machine ID.
+
=== Provide board-specific hooks ===
* ''arch/mips/kernel/setup.c'' - Add 'aloha_setup' function declaration and invocation code.
+
These are the hooks you need to get a tree that actually builds. Actual implementation depends on your hardware:
* ''arch/mips/Makefile'' - Add a section that links your Aloha code in.
+
  
#
+
* <code>get_system_type()</code>
  # Hawaii Aloha board
+
  /*
#
+
  * arch/mips/aloha/init.c
ifdef CONFIG_ALOHA
+
  */
SUBDIRS      += arch/mips/aloha
+
   
LIBS          += arch/mips/aloha/aloha.o
+
  const char *get_system_type(void)
  LOADADDR      += 0x80002000
+
{
  endif
+
        return "Hawaii Aloha";
 +
}
  
<code>LOADADDR</code> is the starting address for your Linux image when it is loaded into RAM. Note that the first <code>0x200</code> bytes are used by the exception vectors on most CPUs. Some CPUs will requries a larger space, so modify the <code>LOADADDR</code> accordingly. Due to the linker's addressing limit, the start address is aligned on a 8KB boundary, so setting your <code>LOADADDR</code> to <code>0x80002000</code> should be reasonable.
+
* <code>arch_init_irq()</code>
 +
/*
 +
  * arch/mips/aloha/irq.c
 +
  */
 +
 +
#include <linux/init.h>
 +
 +
void __init arch_init_irq(void)
 +
{
 +
        /* do board-specific irq initialization */
 +
}
  
* ''arch/mips/config-shared.in'' - Add necessary config information for Aloha board.
+
* <code>prom_init()</code>, <code>prom_free_prom_memory()</code>
# Add the following to 'Machine selection'.
+
  
  dep_bool 'Support for Hawaii Aloha board (EXPERIMENTAL)' CONFIG_ALOHA $CONFIG_EXPERIMENTAL
+
  /*
 +
  * arch/mips/aloha/prom.c
 +
  */
 +
   
 +
#include <linux/init.h>
 +
 +
void __init prom_init(void)
 +
{
 +
        /* get whatever info we need from the PROM */
 +
}
 +
 +
unsigned long prom_free_prom_memory(void)
 +
{
 +
        return 0; /* "Freeing unused memory: %ldk freed\n" */
 +
}
  
# Add a set of default configs for the board, which depends on the features and drivers that Linux port will supports. Here is a very simple example for our minimum Aloha board configurations.
+
* <code>plat_setup()</code>
  
  if [ "$CONFIG_ALOHA" = "y" ]; then
+
  /*
    define_bool CONFIG_CPU_R4X00 y
+
  * arch/mips/aloha/setup.c
    define_bool CONFIG_CPU_LITTLE_ENDIAN y
+
  */
    define_bool CONFIG_SERIAL y
+
    define_bool CONFIG_SERIAL_MANY_PORTS y
+
#include <linux/init.h>
    define_bool CONFIG_NEW_IRQ y
+
    define_bool CONFIG_NEW_TIME_C y
+
void __init plat_setup(void)
    define_bool CONFIG_SCSI n
+
{
  fi
+
        /* do board-specific setup.. */
 +
  }
  
There are two kinds of configuration options here. The first kind are those you cannot select interactively during <code>'make config'</code> or <code>'make menuconfig'</code> or <code>'make xconfig'</code>. Examples are <code>CONFIG_NEW_IRQ</code> and <code>CONFIG_NEW_TIME_C</code>. You must put them here, or else they will not get selected. The second kind of options are those you can select interactively, such as <code>CONFIG_CPU_R4X00</code> and <code>CONFIG_SERIAL</code>. However you may also put them here if you know which selection is right for the board. This way people will make fewer mistakes when they configure for the board.
+
Finally, add your machine group and type IDs to ''include/asm-mips/bootinfo.h''. See the comments at the top of this file for a description of what these are:
  
For instant gratification, you can find a [http://linux.junsun.net/porting-howto/patches/aloha-support.patch complete patch] for adding the Aloha board support to the Linux/MIPS CVS tree checked out on January 20, 2004.
+
--- a/include/asm-mips/bootinfo.h
 +
+++ b/include/asm-mips/bootinfo.h
 +
@@ -218,6 +218,13 @@
 +
  #define MACH_GROUP_TITAN      22      /* PMC-Sierra Titan            */
 +
  #define  MACH_TITAN_YOSEMITE  1      /* PMC-Sierra Yosemite          */
 +
 
 +
+/*
 +
+ * Valid machtype for group HAWAII
 +
+ */
 +
+#define MACH_GROUP_HAWAII      23
 +
+#define  MACH_HAWAII_ALOHA    0      /* Hawaii Aloha */
 +
+#define  MACH_HAWAII_HONOLULU  1      /* Hawaii Honolulu */
 +
+
 +
  #define CL_SIZE                        COMMAND_LINE_SIZE
 +
 
 +
  const char *get_system_type(void);
  
 
=== Configure and build a kernel image ===
 
=== Configure and build a kernel image ===
Line 186: Line 238:
 
The sign of a live Linux kernel comes from the output of printk, which is routed to the first console. Since we have configured a serial console, we should be able to see something on the serial wire if we have set it up correctly.
 
The sign of a live Linux kernel comes from the output of printk, which is routed to the first console. Since we have configured a serial console, we should be able to see something on the serial wire if we have set it up correctly.
  
Unfortunately, setup of the serial console happens much later during the kernel startup process. (See Appendix A for a chart of the kernel start-up sequence). Chances are your new kernel probably dies even before that. That is where the early printk patch comes in handy. It allows you to see printk as early as the first line of C code.
+
Unfortunately, setup of the serial console happens much later during the kernel startup process. (See [[#Appendix A: Linux startup sequence in 2.4.x|Appendix A: Linux startup sequence in 2.4.x]] for a chart of the kernel start-up sequence). Chances are your new kernel probably dies even before that. That is where the early printk patch comes in handy. It allows you to see printk as early as the first line of C code.
  
 
By the way the first line of C code for Linux MIPS is the first line of code of 'init_arch()' function in the 'arch/mips/setup.c' file.
 
By the way the first line of C code for Linux MIPS is the first line of code of 'init_arch()' function in the 'arch/mips/setup.c' file.
Line 194: Line 246:
 
== Serial driver and console ==
 
== Serial driver and console ==
  
While early printk is rather useful, you still need to get the real serial driver working.
+
While early printk is rather useful, you still need to get the real serial driver working. [[Serial_Driver_and_Console|Serial Driver and Console]] has all the details you'll need in order to implement a device driver for your serial port.
 
+
Assuming you have a standard serial port, there are two ways to add serial support: static defines and run-time setup.
+
 
+
With static defines, you modify the 'include/asm-mips/serial.h' file. Looking through the code, it is not difficult to figure out how to add support for your board's serial port(s).
+
 
+
As more boards are supported by Linux/MIPS, the 'serial.h' file gets crowded. One potential solution is to do run-time serial setup. Sometimes run-time serial setup is necessary if any of the parameters can only be detected at the run-time where settings are read from a non-volatile memory device or an option is passed on the kernel command line.
+
 
+
There are two elements to consider for doing run-time serial setup:
+
 
+
* Reserve the <code>rs_table[]</code> size, see the ''drivers/char/serial.c'' file. Unfortunately there is not a clean way to accomplish this yet. A temporary workaround is to define <code>CONFIG_SERIAL_MANY_PORTS</code> in ''arch/mips/config-shared.in'' for your board. This configuration reserves up to 64 serial port entries for your board!
+
* Call the <code>early_serial_setup()</code> routine in your board setup routine. Here is a piece of [http://linux.junsun.net/porting-howto/src/vr4181_serial_init.c sample run-time initialization code].
+
 
+
=== Serial parameters ===
+
 
+
Most of the parameter settings are rather obvious. Here is a list of some less obvious ones:
+
 
+
==== <code>line</code> ====
+
 
+
Only used for run-time serial configuration. It is the index into the 'rs_table[] array'.
+
 
+
==== <code>io_type</code> ====
+
 
+
<code>io_type</code> determines how your serial registers are accessed. Two common types are SERIAL_IO_PORT and SERIAL_IO_MEM. A SERIAL_IO_PORT type driver uses the inb/outb macros to access registers whose base address is at port. In other words, if you specify SERIAL_IO_PORT as the io_type, you should also specify the port parameter.
+
 
+
For SERIAL_IO_MEM, the driver uses readb/writeb macros to access regsiters whose address is at iomem_base plus a shifted offset. The number of digits shifted is specified by iomem_reg_shift. For example, all the serial registers are placed at 4-byte boundary, then you have an iomem_reg_shift of 2.
+
 
+
Generally SERIAL_IO_PORT is for serial ports on an ISA bus and SERIAL_IO_MEM is for memory-mapped serial ports.
+
 
+
There are also SERIAL_IO_HUB6 and SERIAL_IO_GSC. The HUB6 was a multi-port serial card produced by Bell Technologies. The GSC is a special bus found on PA-RISC systems. These options will not be used on any MIPS boards.
+
 
+
=== Non-standard serial ports ===
+
 
+
If you have a non-standard serial port, you will have to write your own serial driver.
+
 
+
Some people derive their code from the standard serial driver. Unfortunately this is a very daunting task. The 'drivers/char/serial.c' file has over 6000 lines of code!
+
 
+
Fortunately, there is an alternative [THANKS: Fillod Stephane]. There is a generic serial driver, 'drivers/char/generic_serial.c'. This file provides a set of serial routines that are hardware independent. Then your task is to provide only the routines that are hardware dependent such as the interrupt handler routine and low-level I/O functions. There are plenty of examples. Just look inside the 'drivers/char/Makefile' and look for drivers that link with 'generic_serial.o' file.
+
 
+
[HELP: Would appreciate if you can share your experience of writing proprietary serial driver code or using the 'generic_serial.c' file.]
+
  
 
== kgdb ==
 
== kgdb ==
Line 292: Line 305:
  
 
The second option uses a script called 'kgdb_demux' written by Brian Moyle. It creates two virtual ports, typically ttya0 and ttya1. It then listens to the real serial port (such as ttyS0). It will forward console traffic to ttya0 and KGDB traffic to ttya1. All you have to do then is to start minicom on /dev/ttya0 (port setting does not matter) and KGDB on <code>/dev/ttya1</code>.
 
The second option uses a script called 'kgdb_demux' written by Brian Moyle. It creates two virtual ports, typically ttya0 and ttya1. It then listens to the real serial port (such as ttyS0). It will forward console traffic to ttya0 and KGDB traffic to ttya1. All you have to do then is to start minicom on /dev/ttya0 (port setting does not matter) and KGDB on <code>/dev/ttya1</code>.
 +
With 2.6 kernel pseudo terminals can be <code>/dev/pts/1-7</code>
  
You can download [http://linux.junsun.net/porting-howto/src/kdmx-1.02.tar.gz the tarball here]. A couple of usage tips.
+
You can download [http://linux.junsun.net/porting-howto/src/kdmx-1.02.tar.gz the tarball here]. A couple of usage tips.  
  
 
* Untar the file to some place.
 
* Untar the file to some place.
Line 299: Line 313:
 
* Set the port parameters properly before you start kgdb_demux.
 
* Set the port parameters properly before you start kgdb_demux.
  
== CPU support ==
+
A newer C version of kdmx can be downloaded [http://osdir.com/ml/linux.kernel.debugging.kgdb.bugs/2006-01/msg00007.html from this post].
 +
 
 +
== Supporting a new CPU ==
 +
 
 +
In the MIPS world, there are different families of MIPS cores. For example Toshiba [[TX49XX|TX49]], [[SB1|SiByte SB1]], [[NEC_VR4100|NEC VR41XX]], [[MIPS32]], and [[MIPS64]] to name a few. These different families may have different cache architectures, may be 32-bit or 64-bit, and may or may not have a FP unit. A complete list of families is found in the ''arch/mips/Kconfig'' and ''include/asm-mips/cpu.h'' files. When you are configuring your kernel the complete list will appear under the ''CPU type'' menu selection. If you are adding support for an entirely new family of MIPS cores, you will need to change the following files:
 +
 
 +
* ''arch/mips/Kconfig'': You will need to add your processor family to the list under ''CPU type''.
 +
 
 +
* ''include/asm-mip/cpu.h'': You will need to add a <code>PRID_COMP</code> and <code>PRID_IMP</code> entries for your processor family if they do not already exist. You will also need to add the CPU types that you will support in that family. Adding CPU types is covered below.
  
In the MIPS world, there are different families of MIPS cores. For example Toshiba TX49, [[SB1|SiByte SB1]], NEC VR41XX, [[MIPS32]], and [[MIPS64]] to name a few. These different families may have different cache architectures, may be 32-bit or 64-bit, and may or may not have a FP unit. A complete list of families is found in the 'arch/mips/config-shared.in' and 'include/asm-mips/cpu.h' files. When you are configuring your kernel the complete list will appear under the 'CPU type' menu selection. If you are adding support for an entirely new family of MIPS cores, you will need to change the following files:
+
* ''arch/mips/kernel/cpu-probe.c'': For each processor family, there is typically a <code>cpu_probe_XXX()</code> function that probes and fills in the <code>struct cpuinfo_mips</code> which contains information about the CPU type, whether a FP unit exists, the number of TLBs available, etc. Your function is responsible for properly probing and filling in this information for the entire family of processors. Finally, if your family does something special for CPU idle, then you will have to define a <code>wait()</code> function. Most likely, you will be able to use one that already exists. If not, add your <code>wait()</code> function and enable detection of it in <code>check_wait()</code>.
  
* arch/mips/config-shared.in - You will need to add your processor family to the list under 'CPU type'.
+
* ''arch/mips/mm'': If your processor family has a unique cache architecture or anything out of the ordinary, you will need to either add or modify files in this directory.  
* include/asm-mip/cpu.h - You will need to add a PRID_COMP and PRID_IMP entries for your processor family if they do not already exist. You will also need to then add the CPU types that you will support in that family. Adding CPU types is covered below.
+
* arch/mips/kernel/cpu-probe.c - For each processor family, there is typically a cpu_probe_XXX function that probes and fills in the struct cpuinfo_mips which contains information about the CPU type, whether a FP unit exists, the number of TLBs available, etc. Your function is responsible for properly probing and filling in this information for the entire family of processors. Finally, if your family does something special for CPU idle, then you will have to define a 'wait' function. Most likely, you will be able to use one that already exists. If not, add your 'wait' function and enable detection of it in check_wait.
+
* arch/mips/mm - If your processor family has a unique cache architecture or anything out of the ordinary, you will need to either add or modify files in this directory.  
+
  
Within each family of MIPS cores, there are multiple processors. For example, Alchemy processors and Broadcom processors are in the MIPS32 family, the Sibyte 1250 is in the SB1 family, and so on. Once you have decided what family your processor is in, you need to verify that a unique CPU identification exists for it. The 'include/asm-mips/cpu.h' file contains all of the currently supported CPU types for the Linux/MIPS kernel.
+
Within each family of MIPS cores, there are multiple processors. For example, Alchemy processors and Broadcom processors are in the MIPS32 family, the Sibyte 1250 is in the SB1 family, and so on. Once you have decided what family your processor is in, you need to verify that a unique CPU identification exists for it. ''include/asm-mips/cpu.h'' contains all of the currently supported CPU types for the Linux/MIPS kernel.
  
If your processor is already listed in the 'include/asm-mips/cpu.h' file, the existing Linux/MIPS code should auto-detect it. If your processor is not listed, you will need to change three files to properly detect your processor:
+
If your processor is already listed in ''include/asm-mips/cpu.h'', the existing Linux/MIPS code should auto-detect it. Otherwise, you'll need to change three files to properly detect your processor:
  
* include/asm-mips/cpu.h - Add your processor at the end of the CPU_XXX definitions. Make sure you update the 'CPU_LAST' value to match that of the processor you added.
+
* ''include/asm-mips/cpu.h'': Add your processor at the end of the <code>CPU_XXX</code> definitions. Make sure you update the <code>CPU_LAST</code> value to match that of the processor you added.
* arch/mips/kernel/proc.c - Add an entry to the cpu_name array so that your CPU name can be properly displayed in /proc/cpuinfo.
+
* ''arch/mips/kernel/proc.c'': Add an entry to the <code>cpu_name</code> array so that your CPU name can be properly displayed in /proc/cpuinfo.
* arch/mips/kernel/cpu-probe.c - Add your processor into the case statement in the function check_wait if your CPU can do some sort of wait or idle operation. Add the necessary code in the cpu_probe_XX function to detect and set your CPU type.  
+
* ''arch/mips/kernel/cpu-probe.c'': Add your processor into the case statement in the function <code>check_wait()</code> if your CPU can do some sort of wait or idle operation. Add the necessary code in the <code>cpu_probe_XXX()</code> function to detect and set your CPU type.  
  
 
Once you have support for your processor family and specific CPU type, you should be able to take full advantage of its capabilities.
 
Once you have support for your processor family and specific CPU type, you should be able to take full advantage of its capabilities.
Line 351: Line 370:
 
=== CPU as an IRQ controller ===
 
=== CPU as an IRQ controller ===
  
Most R4K-compatible MIPS CPUs have the ability to support eight interrupt sources. The first two are software interrupts, and the last one is typically used for CPU counter interrupt. Therefore you can have up to five external interrupt sources.
+
MIPS processors include a simple interrupt controller. In it's simplest case as implemented in the [[R2000]] it implements two ''software interrupts''. This are interrupts that can only be raised by software setting the bit in the cause register and needs to be cleared by the interrupt handler. Otherwise they will behave just like hardware interrupts.
  
Luckily if the CPU is the only IRQ controller in the system, you are done! The <code>hw_irq_handler</code> code is written in ''arch/mips/kernel/irq_cpu.c'' file. All you have to do is to define <code>CONFIG_IRQ_CPU</code> for your machine. And in your irq setup routine make sure you call <code>mips_cpu_irq_init(0)</code>. This call will initialize the interrupt descriptor and set up the IRQ numbers to range from 0 to 7.
+
In case of the [[R4000]] and most newer processors newer than the R4000 the IP7 bit in the cp0 status and cause registers doubles to serve the timer interrupt also. That is the IP7 interrupt will be raised whenever the cop0 count and compare registers have the same value. The exact mode of operation of the count register and the timer interrupt are often configurable by the CPU's mode bits. Using IP7 as the timer interrupt typically also means this interrupt cannot sensibly used for any other purpose.
  
You can also find a matching interrupt dispatching code in [http://linux.junsun.net/porting-howto/src/int_handler.S this file].
+
So here's how to initialize the timer interrupt in a typical system. First define "CONFIG_IRQ_CPU" for your machine to enable compile the support code into the kernel. Then in your <code>arch_init_irq()</code> just call <code>mips_cpu_irq_init(x)</code> where for <code>x</code> you put the first of the 8 interrupt numbers you're going to assign to the CPU's interrupt controller.
  
 
=== Set up cascading interrupts ===
 
=== Set up cascading interrupts ===
Line 361: Line 380:
 
More than likely you will have more interrupt sources than those that can directly connect to the CPU interrupt pins. A second or even third-level interrupt controller may connect to one or more of those CPU pins. In that case, you have cascading interrupts.
 
More than likely you will have more interrupt sources than those that can directly connect to the CPU interrupt pins. A second or even third-level interrupt controller may connect to one or more of those CPU pins. In that case, you have cascading interrupts.
  
There are plenty of examples of how cascading interrupt works, such as the DDB5477 and the Osprey. Here is a short summary:
+
There are plenty of examples of how cascading interrupt works, such as the DDB5477. Here is a short summary:
  
* Assign blocks of IRQ numbers to various interrupt controllers in the whole system. For example, in the case of the Osprey, CPU interrupts occupy IRQ 0 to 7. [[NEC_VR4100|Vr4181]] system interrupts occupy IRQ 8 to 39, and GPIO interrupts occupy 40 to 56. In most cases, the actual IRQ numbers do not matter, as long as the driver knows which IRQ number it should use. However, if you have an i8259 interrupt controller and an ISA bus, you should try to assign IRQ number 0 to 16 for the i8259 interrupts because it will make the legacy PC drivers happy. (Please note before ~12/08/2001 in version 2.4.16 of the Linux kernel, the 'i8259.c' file set the base vector to be 0x20. If you use the IRQ acknowledgement cycle to obtain the interrupt vector, you will get an IRQ number from 0x20 to 0x2f. You will then need to substract 0x20 from the return value to get the correct IRQ number.)
+
* Assign blocks of IRQ numbers to various interrupt controllers in the whole system. For example, in the case of Vr4181 systems, CPU interrupts occupy IRQ 0 to 7. [[NEC_VR4100|Vr4181]] system interrupts occupy IRQ 8 to 39, and GPIO interrupts occupy 40 to 56. In most cases, the actual IRQ numbers do not matter, as long as the driver knows which IRQ number it should use. However, if you have an i8259 interrupt controller and an ISA bus, you should try to assign IRQ number 0 to 16 for the i8259 interrupts because it will make the legacy PC drivers happy. (Please note before ~12/08/2001 in version 2.4.16 of the Linux kernel, the 'i8259.c' file set the base vector to be 0x20. If you use the IRQ acknowledgement cycle to obtain the interrupt vector, you will get an IRQ number from 0x20 to 0x2f. You will then need to substract 0x20 from the return value to get the correct IRQ number.)
  
 
* Write the 'hw_irq_controller' member functinos for your specific controllers. Note that CPU and i8259 already have their code written. You just need to define appropriate CONFIG options for your board. See the next sub-section for more details about writing 'hw_irq_controller' member functions.
 
* Write the 'hw_irq_controller' member functinos for your specific controllers. Note that CPU and i8259 already have their code written. You just need to define appropriate CONFIG options for your board. See the next sub-section for more details about writing 'hw_irq_controller' member functions.
Line 501: Line 520:
 
== PCI support ==
 
== PCI support ==
  
The PCI subsystem is perhaps the most complex code you have to deal with during the porting process. Thanks to the similarity of [[Wikipedia:PCI|PCI]], [[Wikipedia:Hypertransport|Hypertransport]], [[Wikipedia:PCI-X|PCI-X]], [[Wikipedia:Cardbus|Cardbus]] and other bus systems the time for understanding it well invested - and the key to making the PCI subsystem work properly is a good understanding of the PCI bus itself, the code layout, and the execution flow in Linux. Like many other parts of porting, you will find in the end, the actual code writen is minimal.
+
The PCI subsystem is perhaps the most complex code you have to deal with during the porting process. Thanks to the similarity of [[Wikipedia:PCI|PCI]], [[HyperTransport]], [[Wikipedia:PCI-X|PCI-X]], [[Wikipedia:Cardbus|Cardbus]] and other bus systems the time for understanding it well invested - and the key to making the PCI subsystem work properly is a good understanding of the PCI bus itself, the code layout, and the execution flow in Linux. Like many other parts of porting, you will find in the end, the actual code writen is minimal.
  
See [[PCI_Subsystem|PCI Subsystem]] for a discussion on the architecture-specific implementation and the board-specific hooks you'll need to implement.
+
See [[PCI_Subsystem|PCI Subsystem]] for a discussion on the architecture-specific implementation and board-specific hooks you'll need to implement.
  
 
== Debugging ==
 
== Debugging ==
Line 615: Line 634:
 
* Henri Girard [mailto:khgirard@broadbandnetdevices.com <khgirard@broadbandnetdevices.com>]
 
* Henri Girard [mailto:khgirard@broadbandnetdevices.com <khgirard@broadbandnetdevices.com>]
 
* Neal Crook [mailto:ncrook@micron.com <ncrook@micron.com>]
 
* Neal Crook [mailto:ncrook@micron.com <ncrook@micron.com>]
* Steven J. Hill [mailto:James.Hill@timesys.com <ncrook@micron.com>]
+
* Steven J. Hill [mailto:James.Hill@timesys.com <James.Hill@timesys.com>]
* TAKANO Ryousei [mailto:takano@axe-inc.co.jp <ncrook@micron.com>]
+
* TAKANO Ryousei [mailto:takano@axe-inc.co.jp <takano@axe-inc.co.jp>]
 
* Motoya Kurotsu [mailto:kurotsu@allied-telesis.co.jp <kurotsu@allied-telesis.co.jp>]
 
* Motoya Kurotsu [mailto:kurotsu@allied-telesis.co.jp <kurotsu@allied-telesis.co.jp>]

Revision as of 00:14, 20 November 2007

This document reflects what I have learned through porting several MIPS machines and other related Linux work. Hopefully it will help beginners to get started and give the experienced a reference point.

This document goes through all the major steps to port Linux to a MIPS machine. The focus can perhaps be called "MIPS machine abstraction layer", i.e., the interface between machine-specific code and, mostly, MIPS common code. Another useful document focuses on "Linux hardware abstraction layer", i.e., the interface between Linux common code and architecture-specific code. The document is written by C Hanish Menon (www.hanishkvc.com).

Overview

Prerequisites

  • Know C programming.
  • Have some knowledge of OS concepts, such as interrupt handling, system calls, memory management.
  • Know how to configure and make Linux kernel. You can find much help on this if you are not very comfortable.
  • Have some knowledge of MIPS CPU. More than likely you will need to deal with CP0 registers, enable or disable interrupts, etc..
  • You don't have to be an expert in MIPS assembly, but total ignorance of it might make you handicapped in some situations.
  • Obviously, you need a MIPS hardware to play with.
  • Finally but most importantly, you need a willing-to-learn heart and perhaps many restless debugging hours. :-)

It is also highly recommanded to read through the Linux MIPS HOWTO by Ralf Bächle, ralf@gnu.org. By the way, as part of the pre-requisite, you should also remember Ralf's name. :-)

Overall porting steps

Depending on your specific case, some (but not all!) of the following may be skipped:

  1. Setup a cross-development environment.
  2. "Hello, World!" - Get board setup, serial porting working, and print out "Hello, world!" through the serial port.
  3. Download the kernel -- this is where you'll be doing all your work.
  4. Add your own code.
  5. Get early printk working - Make the first MIPS image and see the printk output from kernel.
  6. Serial driver and serial console - Get the real printk working with the serial console.
  7. KGDB - KGDB can be enormously helpful in your development. It is highly recommended and it is not that difficult to set up.
  8. CPU support - If your MIPS CPU is not currently supported, you need to add new code that supports it.
  9. Board specific support - Create your board-specific directory. Setup interrupt routing/handling and kernel timer services.
  10. PCI subsystems - If your machine has PCI, you need to get the PCI subsystem working before you can use PCI devices.
  11. Ethernet drivers - You should already have the serial port working before attempting this. Usually the next driver you want is the ethernet driver. With ethernet driver working, you can set up a NFS root file system which gives you a fully working Linux userland.
  12. ROMFS root file system - Alternatively you can create a userland file system as a ROMFS image stored in a ramdisk.

Setup a cross-development environment

You'll need a cross-development environment since your board is not yet self-hosting (ie. unable to natively build and run executable code). In this scenario, you usually have two machines -- a host and target. It is on your host (eg. Linux on i386) that you build your code, and onto your target (MIPS board) that you download and run it. This is a preferred (and robust) methodology of doing embedded development, since most targets are resource-constrained (memory- and CPU-wise) for things like native builds of whole kernel trees.

A primary step towards setting up your environment is installing a cross-toolchain. While you may be able to roll your own, you're better off using a pre-built set (full toolchain builds have since become quite time consuming). An exception to this is when you require added support for your platform to the toolchain itself, which is out of scope for this guide.

See Toolchains for where to download pre-built tools, or instructions on how to roll your own.

"Hello, world!"

In cross development, the serial port is usually the most important interface: That is where you can see anything happening! It might be worthwhile to make sure you get serial porting work before you even start playing with Linux. You can find the sample code or gzipped tar ball of a stand-alone program that can do printf. Such a program can even be useful in later debugging staging, e.g., printing out hardware register values.

Before you rush to type 'make', check and modify the following configurations:

  1. The sample code assumes R4K style CP0 structure. It should apply to most CPUs named above number 4000 and the recent MIPS32/MIPS64.
  2. Check if you have 1MB RAM size. (You really should have at 1MB to run Linux at all.) It is recommanded you have 8MB RAM or more.
  3. Is your serial port a standard UART type? If yes, modify the serial code and parameters. If not, you will have to supply your own functions to utilize the UART.
  4. What is your cross-tool name and path? Modify the Makefile accordingly.

Now, fire your "make" command.

Depending on your downloader on your MIPS box, you may need to generate ELF image, binary image or a SREC image.

Download the barebone image to your target and give it a run! Connect the serial port to your host machine. Start minicom and hopefully you can see the "Hello, world!" message.

Trouble shooting:

  • Make sure your bootloader downloads the image to uncached KSEG1 segment. If your bootloader downloads to the cached KSEG0 area, you will want to run the image from the KSEG0 area too.
  • If your bootloader has already initialized the serial port, you may want to skip your own initialization.
  • Did you set up minicom correctly? Test it with other machines.
  • Hopefully it is not the toolchain problem...

Getting the Kernel

Ideally, you should be working against the latest stable release. The older stable release, 2.4.x, is only open to bug fixes and won't be accepting any new features. Besides, countless bugs may have since been squished, APIs overhauled etc., all of which you'll greatly benefit from.

See Getting the Kernel for where to obtain the latest stable release.

Initial board support

A first step toward adding support for your board is to hook into the kernel build system (kbuild). At this point you should be worrying about getting the tree to actually build. You'll also need to provide the board-specific hooks that the architecture back-end depends on.

In the following examples, we assume a board named Aloha supplied by a vendor Hawaii.

Decide on a directory hierarchy

Your code for a new board can be classified into board-support code (or board-specific code) and drivers. Driver code should be placed under the drivers/ directory and board specific code should be placed under the arch/mips/ directory.

The easiest choice is to create a directory called arch/mips/aloha.

However, a couple of other considerations might make it slightly complicated.

  • If Aloha uses a chipset or System on a Chip (SOC) that is already supported or belongs to a bigger family, such as NEC VR41xx or gt64120, it makes sense to put Aloha code under those sub-directories. You can re-use and share a lot of common code.
  • Similarly, if Aloha is the first board that uses a chipset or SOC which is expected to be used in many other boards, you may want to create similar directory structure. However, if you are not sure, just create your own board specific directory.

In the past people have created directories based on the board manufacturer's name, such as "mips-boards". This generally is not a good idea. It is almost certain that some of these boards do not share anything common at all.

To make things worse, sometimes boards made by different companies use the same chipset or SOC. Now what are you going to do? Are you going to duplicate the common code? Or are you going stick one company's board under another company's name?

For header files, you usually create similar directory or header files under include/asm-mips/. [DEBATE] For board specific header files, I would encourage people to place them under the corresponding arch/mips/ directory if possible.

In our example, we will create arch/mips/aloha/ directory.

Hook into the build system

Add a Kconfig entry for your board so you are able to select it with any of make *config. For a description of this syntax, see Documentation/kbuild/kconfig-language.txt: (TODO: Document CONFIG_SYS_SUPPORTS_* or better yet, just do it in arch/mips/Kconfig)

--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -229,6 +229,18 @@ config MIPS_ITE8172
          either a NEC Vr5432 or QED RM5231. Say Y here if you wish to build
          a kernel for this platform.
 
+config HAWAII_ALOHA
+       bool "Hawaii Aloha"
+       select DMA_COHERENT
+       select SYS_HAS_CPU_R5000
+       select SYS_SUPPORTS_32BIT_KERNEL
+       select SYS_SUPPORTS_64BIT_KERNEL if EXPERIMENTAL
+       select SYS_SUPPORTS_LITTLE_ENDIAN
+       help
+         This is an evaluation board only available in Hawaii.
+         Before importing one, please check with your local import
+         laws on fictional hardware.
+
 config MACH_JAZZ
        bool "Support for the Jazz family of machines"
        select ARC

You'll need a corresponding Makefile entry so make may recurse into your platform sub-directory.

$(load-y) is the starting address for your Linux image when it is loaded into RAM. Note that the first 0x200 bytes are used by the exception vectors on most CPUs. Some CPUs will require a larger space, so modify $(load-y) accordingly. Due to the linker's addressing limit, the start address is aligned on a 8KB boundary, so setting $(load-y) to 0xffffffff80002000 should be reasonable:

--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -401,6 +401,12 @@ core-$(CONFIG_MIPS_IVR)            += arch/mips/it
 load-$(CONFIG_MIPS_IVR)                += 0xffffffff80100000
 
 #
+# Hawaii Aloha board
+#
+core-$(CONFIG_HAWAII_ALOHA)    += arch/mips/aloha/
+load-$(CONFIG_HAWAII_ALOHA)    += 0xffffffff80002000
+
+#
 # ITE 8172 eval board with QED 5231 CPU
 #
 core-$(CONFIG_MIPS_ITE8172)    += arch/mips/ite-boards/qed-4n-s01b/

Provide board-specific hooks

These are the hooks you need to get a tree that actually builds. Actual implementation depends on your hardware:

  • get_system_type()
/*
 * arch/mips/aloha/init.c
 */

const char *get_system_type(void)
{
        return "Hawaii Aloha";
}
  • arch_init_irq()
/*
 * arch/mips/aloha/irq.c
 */

#include <linux/init.h>

void __init arch_init_irq(void)
{
        /* do board-specific irq initialization */
}
  • prom_init(), prom_free_prom_memory()
/*
 * arch/mips/aloha/prom.c
 */

#include <linux/init.h>

void __init prom_init(void)
{
        /* get whatever info we need from the PROM */
}

unsigned long prom_free_prom_memory(void)
{
        return 0; /* "Freeing unused memory: %ldk freed\n" */
}
  • plat_setup()
/*
 * arch/mips/aloha/setup.c
 */

#include <linux/init.h>

void __init plat_setup(void)
{
        /* do board-specific setup.. */
}

Finally, add your machine group and type IDs to include/asm-mips/bootinfo.h. See the comments at the top of this file for a description of what these are:

--- a/include/asm-mips/bootinfo.h
+++ b/include/asm-mips/bootinfo.h
@@ -218,6 +218,13 @@
 #define MACH_GROUP_TITAN       22      /* PMC-Sierra Titan             */
 #define  MACH_TITAN_YOSEMITE   1       /* PMC-Sierra Yosemite          */
 
+/*
+ * Valid machtype for group HAWAII
+ */
+#define MACH_GROUP_HAWAII      23
+#define  MACH_HAWAII_ALOHA     0       /* Hawaii Aloha */
+#define  MACH_HAWAII_HONOLULU  1       /* Hawaii Honolulu */
+
 #define CL_SIZE                        COMMAND_LINE_SIZE
 
 const char *get_system_type(void);

Configure and build a kernel image

Now you are ready to run your favorite configuration tool. Since we do not have much code added yet, do not be too greedy with selecting options. Just pick a couple of simple options such as the serial and serial console.

  • If you denoted the Aloha board support to be EXPERIMENTAL, select 'Prompt for development and/or incomplete code/drivers' under 'Code maturity level options'.
  • Select 'Support for Hawaii Aloha board' and unselect all other machines under 'Machine selection'.
  • Select the right CPU. Under 'CPU selection' select your CPU. If there is no entry for the CPU on your board, you will need to add support for it. Most recent CPUs can generally run to some degree with CPU_R4X00.
  • Under 'Character devices', select 'Standard/generic (8250/16550 and compatible UARTs) serial support' and 'Support for console on serial port'. Unselect the 'Virtual terminal' option.
  • Under the 'Kernel hacking' option, select 'Are you using a crosscompiler'.
  • For other options either take the default or select 'no'.

Here is a sample minimum config for our Aloha board.

Before you type make, double-check the arch/mips/Makefile and make sure the cross-toolchain program names are correct and in your execution path i.e. your PATH environment variable.

Now type make dep and make. Then wait for a miracle to happen!

Early printk()

Assuming you are lucky and actually generate an image from the last chapter, don't bother running it because you won't see anything. This is not strange because all our board-specific code is empty and we have not told Linux kernel anything about our serial port or I/O devices yet.

The sign of a live Linux kernel comes from the output of printk, which is routed to the first console. Since we have configured a serial console, we should be able to see something on the serial wire if we have set it up correctly.

Unfortunately, setup of the serial console happens much later during the kernel startup process. (See Appendix A: Linux startup sequence in 2.4.x for a chart of the kernel start-up sequence). Chances are your new kernel probably dies even before that. That is where the early printk patch comes in handy. It allows you to see printk as early as the first line of C code.

By the way the first line of C code for Linux MIPS is the first line of code of 'init_arch()' function in the 'arch/mips/setup.c' file.

For kernel version earlier than 2.4.10, you can find the early printk patch here for boards with standard UART serial ports. Starting from 2.4.10 and beyond, a new printk patch is needed. If you have already got the stand-alone "Hello, world!" program running, the early printk should be easy to get going, and you should have printk output from the Linux kernel very soon.

Serial driver and console

While early printk is rather useful, you still need to get the real serial driver working. Serial Driver and Console has all the details you'll need in order to implement a device driver for your serial port.

kgdb

For many Linux kernel developers, KGDB is a life-saving tool. With KGDB, you can debug the kernel while it runs! You can set breakpoints or do single stepping at the source code level.

To do this, you will need a dedicated serial port on your target, and use a crossover-cable (also known as a null-modem) to connect it to your development host. If you are also using a serial console, this implies you will need two serial ports on your target. It is possible to do both kernel debug and serial console through a single serial port. This will be mentioned later in this chapter.

When you configure the kernel, select the Remote GDB kernel debugging which is listed under Kernel hacking. Do a make clean and recompile the kernel so that debugging symbols are compiled into your kernel image. Try to make a new image. You will soon discover two missing symbols in the final linking stage:

arch/mips/kernel/kernel.o: In function `getpacket':
arch/mips/kernel/kernel.o(.text+0x85ac): undefined reference to `getDebugChar'
arch/mips/kernel/kernel.o(.text+0x85cc): undefined reference to `getDebugChar'
arch/mips/kernel/kernel.o(.text+0x8634): undefined reference to `getDebugChar'
arch/mips/kernel/kernel.o(.text+0x864c): undefined reference to `getDebugChar'
arch/mips/kernel/kernel.o(.text+0x8670): undefined reference to `putDebugChar'
arch/mips/kernel/kernel.o(.text+0x8680): undefined reference to `putDebugChar'
arch/mips/kernel/kernel.o(.text+0x8698): undefined reference to `putDebugChar'
arch/mips/kernel/kernel.o(.text+0x86a0): undefined reference to `putDebugChar'

You need to supply these two functions for your own boards:

int putDebugChar(uint8 byte)
uint8 getDebugChar(void)

As an example, here is the dbg_io.c for DDB5476 board. DDB5476 uses a standard UART serial port to implement those two functions.

After supplying those two functions, you are ready to debug the kernel. Run the new kernel image. If you also use the early printk patch, you should be able to see something like this on your console:

Wait for gdb client connection ...

Assuming you have already connected a cross-over serial cable between the dedicated serial port on the target and a serial port on your host (say, COM0), you can then set the appropriate baud rate and start the cross gdb on your host:

stty -F /dev/ttyS0 38400
mipsel-linux-gdb vmlinux

At the gdb prompt, type

target remote /dev/ttyS0

And, voila! You should be talking to the kernel through KGDB - if you are lucky enough!

A couple of tips on using KGDB:

  • Any functions preceded with the __init label will not break very well with breakpoints. Sometimes it will screw up the line numbers of other functions in the same file. Try undefining __init to be an empty macro in your include/linux/init.h file. Refer to the patch. [NOTE: This problem is fixed in the latest gdb version, at least in gdb 5.2]
  • Sometimes if you break on a function, you cannot see the correct value in variables and cannot do back-tracing. This is probably because certain registers are still not initialized [HELP: because kernel is compiled with -O2 flag?]. Step into the function a couple of lines, and you should see the variable and back-tracing fine.

What if the board only has one serial port?

Some boards only have one serial port. If you use it as serial console, you cannot really use it for KGDB - unless you do some tricks to it.

There are two solutions. One is GDB console, and the other is to use a KGDB demuxing script.

It is easy to use the GDB console. When you select 'Remote GDB kernel debugging' under the 'Kernel hacking' sub-menu, you are also prompted for 'Console output to GDB'. Simply selecting that choice will work! In fact, this option is so easy to use you might want to use it even if you have a second serial port.

However, this option has a limit. When the kernel goes to userland, the console stops working. This is because the KGDB stub in the kernel and GDB are not designed to provide interactive output. [HELP: any volunteers?]

The second option uses a script called 'kgdb_demux' written by Brian Moyle. It creates two virtual ports, typically ttya0 and ttya1. It then listens to the real serial port (such as ttyS0). It will forward console traffic to ttya0 and KGDB traffic to ttya1. All you have to do then is to start minicom on /dev/ttya0 (port setting does not matter) and KGDB on /dev/ttya1. With 2.6 kernel pseudo terminals can be /dev/pts/1-7

You can download the tarball here. A couple of usage tips.

  • Untar the file to some place.
  • Copy kgdb_demux script to your execution path, and modify it properly.
  • Set the port parameters properly before you start kgdb_demux.

A newer C version of kdmx can be downloaded from this post.

Supporting a new CPU

In the MIPS world, there are different families of MIPS cores. For example Toshiba TX49, SiByte SB1, NEC VR41XX, MIPS32, and MIPS64 to name a few. These different families may have different cache architectures, may be 32-bit or 64-bit, and may or may not have a FP unit. A complete list of families is found in the arch/mips/Kconfig and include/asm-mips/cpu.h files. When you are configuring your kernel the complete list will appear under the CPU type menu selection. If you are adding support for an entirely new family of MIPS cores, you will need to change the following files:

  • arch/mips/Kconfig: You will need to add your processor family to the list under CPU type.
  • include/asm-mip/cpu.h: You will need to add a PRID_COMP and PRID_IMP entries for your processor family if they do not already exist. You will also need to add the CPU types that you will support in that family. Adding CPU types is covered below.
  • arch/mips/kernel/cpu-probe.c: For each processor family, there is typically a cpu_probe_XXX() function that probes and fills in the struct cpuinfo_mips which contains information about the CPU type, whether a FP unit exists, the number of TLBs available, etc. Your function is responsible for properly probing and filling in this information for the entire family of processors. Finally, if your family does something special for CPU idle, then you will have to define a wait() function. Most likely, you will be able to use one that already exists. If not, add your wait() function and enable detection of it in check_wait().
  • arch/mips/mm: If your processor family has a unique cache architecture or anything out of the ordinary, you will need to either add or modify files in this directory.

Within each family of MIPS cores, there are multiple processors. For example, Alchemy processors and Broadcom processors are in the MIPS32 family, the Sibyte 1250 is in the SB1 family, and so on. Once you have decided what family your processor is in, you need to verify that a unique CPU identification exists for it. include/asm-mips/cpu.h contains all of the currently supported CPU types for the Linux/MIPS kernel.

If your processor is already listed in include/asm-mips/cpu.h, the existing Linux/MIPS code should auto-detect it. Otherwise, you'll need to change three files to properly detect your processor:

  • include/asm-mips/cpu.h: Add your processor at the end of the CPU_XXX definitions. Make sure you update the CPU_LAST value to match that of the processor you added.
  • arch/mips/kernel/proc.c: Add an entry to the cpu_name array so that your CPU name can be properly displayed in /proc/cpuinfo.
  • arch/mips/kernel/cpu-probe.c: Add your processor into the case statement in the function check_wait() if your CPU can do some sort of wait or idle operation. Add the necessary code in the cpu_probe_XXX() function to detect and set your CPU type.

Once you have support for your processor family and specific CPU type, you should be able to take full advantage of its capabilities.

Interrupts

If you followed the previous steps, most likely you will see kernel hanging at the BogusMIPS calibration step. The reason is simple: The interrupt code is not there and jiffies are never updated. Therefore the calibration can never be done.

Before you start writing interrupt code, it really pays to study the hardware first. Pay particular attention to identify all interrupt sources, their corresponding controllers and how they are routed.

Then you need to come up with a strategy, which typically includes:

  • a static interrupt routing map
  • a list of interrupt sources
  • a list of their corresponding controllers
  • how interrupt controllers cascade from each other

Interrupt code overview

To completely service an interrupt, four different pieces of code work together:

  • IRQ detection/dispatching:

This is typically assembly code in a file called 'int_handler.S'. Sometimes there is also secondary-level dispatching code written in C for complicated IRQ detection. The end result is that we identify and select a single IRQ source, represented by an integer, and then pass it to function 'do_IRQ()'.

  • do_IRQ()

do_IRQ() is provided in the 'arch/mips/kernel/irq.c' file. It provides a common framework for IRQ handling. It invokes the individual IRQ controller code to enable/disable a particular interrupt. It calls the driver supplied interrupt handling routine that does the real processing.

  • hw_irq_handler

It is a structure associated with each IRQ source. The structure is a collection of function pointers, which tells do_IRQ() how it should deal with this particular IRQ.

  • driver interrupt handling code

The code that does the real job.

Obviously for our porting purposes we need to write IRQ detection/disptaching code and the hw_irq_handler code for any new IRQ controller used in the system. In addition, there is also IRQ setup and initialization code.

CPU as an IRQ controller

MIPS processors include a simple interrupt controller. In it's simplest case as implemented in the R2000 it implements two software interrupts. This are interrupts that can only be raised by software setting the bit in the cause register and needs to be cleared by the interrupt handler. Otherwise they will behave just like hardware interrupts.

In case of the R4000 and most newer processors newer than the R4000 the IP7 bit in the cp0 status and cause registers doubles to serve the timer interrupt also. That is the IP7 interrupt will be raised whenever the cop0 count and compare registers have the same value. The exact mode of operation of the count register and the timer interrupt are often configurable by the CPU's mode bits. Using IP7 as the timer interrupt typically also means this interrupt cannot sensibly used for any other purpose.

So here's how to initialize the timer interrupt in a typical system. First define "CONFIG_IRQ_CPU" for your machine to enable compile the support code into the kernel. Then in your arch_init_irq() just call mips_cpu_irq_init(x) where for x you put the first of the 8 interrupt numbers you're going to assign to the CPU's interrupt controller.

Set up cascading interrupts

More than likely you will have more interrupt sources than those that can directly connect to the CPU interrupt pins. A second or even third-level interrupt controller may connect to one or more of those CPU pins. In that case, you have cascading interrupts.

There are plenty of examples of how cascading interrupt works, such as the DDB5477. Here is a short summary:

  • Assign blocks of IRQ numbers to various interrupt controllers in the whole system. For example, in the case of Vr4181 systems, CPU interrupts occupy IRQ 0 to 7. Vr4181 system interrupts occupy IRQ 8 to 39, and GPIO interrupts occupy 40 to 56. In most cases, the actual IRQ numbers do not matter, as long as the driver knows which IRQ number it should use. However, if you have an i8259 interrupt controller and an ISA bus, you should try to assign IRQ number 0 to 16 for the i8259 interrupts because it will make the legacy PC drivers happy. (Please note before ~12/08/2001 in version 2.4.16 of the Linux kernel, the 'i8259.c' file set the base vector to be 0x20. If you use the IRQ acknowledgement cycle to obtain the interrupt vector, you will get an IRQ number from 0x20 to 0x2f. You will then need to substract 0x20 from the return value to get the correct IRQ number.)
  • Write the 'hw_irq_controller' member functinos for your specific controllers. Note that CPU and i8259 already have their code written. You just need to define appropriate CONFIG options for your board. See the next sub-section for more details about writing 'hw_irq_controller' member functions.
  • In your IRQ setup routine, initialize all the controllers, usually by calling 'interrupt_controller_XXX_init()' functions.
  • In your IRQ setup routine, setup the cascading IRQs. This setup will enable interrupts for the upper interrupt controller so that the lower-level interrupts can cascade through once they are enabled. A typical way of doing this is to have a dummy 'irqaction struct' and setup as follows:
static struct irqaction cascade =
        { no_action, SA_INTERRUPT, 0, "cascade", NULL, NULL };

extern int setup_irq(unsigned int irq, struct irqaction *irqaction);

void __init <my>_irq_init(void)
{
	....
	setup_irq(CPU_IP3, &cascade);
}
  • You need to expand your interrupt dispatching code properly to identify the added interrupt sources. If the code is simple enough, you can do it in the same int_handler.S file. If it is more complicated, you may do it in a separate C function (such as in the DDB5476 board).

The hw_irq_controller struct

The hw_irq_controller structure' is a defined in the 'include/linux/irq.h' file as an alias for the 'hw_interrupt_type' structure.

struct hw_interrupt_type {
        const char * typename;
        unsigned int (*startup)(unsigned int irq);
        void (*shutdown)(unsigned int irq);
        void (*enable)(unsigned int irq);
        void (*disable)(unsigned int irq);
        void (*ack)(unsigned int irq);
        void (*end)(unsigned int irq);
        void (*set_affinity)(unsigned int irq, unsigned long mask);
};

The 'arch/mips/kernel/irq_cpu.c' is a good sample code to write 'hw_irq_controller' member functions. Here are some more programming notes for each of these functions:

  • const char * typename;

Controller name. Will be displayed under /proc/interrupts.

  • unsigned int (*startup)(unsigned int irq);

Invoked when request_irq() or setup_irq are called. You need to enable this interrupt here. Other than that you may also want to do some IRQ-specific initialization (such as turning on power for this interrupt, perhaps).

  • void (*shutdown)(unsigned int irq);

Invoked when free_irq() is called. You need to disable this interrupt and perhaps some other IRQ-specific cleanup.

  • void (*enable)(unsigned int irq) and void (*disable)(unsigned int irq)

They are used to implement enable_irq(), disable_irq() and disable_irq_nosync(), which in turn are used by driver code.

  • void (*ack)(unsigned int irq)

ack() is invoked at the beginning of do_IRQ() when we want to acknoledge an interrupt. I think you need also to disable this interrupt here so that you don't get recursive interrupts on the same interrupt source. [HELP: can someone confirm?]

  • void (*end)(unsigned int irq)

This is called by do_IRQ() after it has handled this interrupt. If you disabled interrupt in ack() function, you should enable it here. [HELP: generally what else we should do here?]

  • void (*set_affinity)(unsigned int irq, unsigned long mask)

This is used in SMP machines to set up interrupt handling affinity with certain CPUs. [TODO] [HELP]

The IRQ initialization code

The IRQ initialization is done in 'init_IRQ()'. Currently it is supplied by each individual board. In the future, it will probably be a MIPS common routine, which will further invoke a board-specific function, board_irq_init(). board_irq_init will be a function pointer that <my_board>_setup() function needs to assign propoer value.

In any case, the following is a skeleton code for a normal init_IRQ() routine.

extern asmlinkage void vr4181_handle_irq(void);
extern void breakpoint(void);
extern int setup_irq(unsigned int irq, struct irqaction *irqaction);
extern void mips_cpu_irq_init(u32 irq_base);
extern void init_generic_irq(void);

static struct irqaction cascade =
        { no_action, SA_INTERRUPT, 0, "cascade", NULL, NULL };
static struct irqaction reserved =
        { no_action, SA_INTERRUPT, 0, "reserved", NULL, NULL };
static struct irqaction error_irq =
        { error_action, SA_INTERRUPT, 0, "error", NULL, NULL };

void __init init_IRQ(void)
{
        int i;
        extern irq_desc_t irq_desc[];

	/* hardware initialization code */
	....

	/* this is the routine defined in int_handler.S file */
       set_except_vector(0, my_irq_dispatcher);

	/* setup the default irq descriptor */
	init_generic_irq();

       /* init all interrupt controllers */
       mips_cpu_irq_init(CPU_IRQ_BASE);
	....

       /* set up cascading IRQ */
        setup_irq(CPU_IRQ_IP3, &cascade);

	/* set up reserved IRQ so that others can not mistakingly request
	 * it later.
 	 */
        setup_irq(CPU_IRQ_IP4, &reserved);

#ifdef CONFIG_DEBUG
	/* setup debug IRQ so that if that interrupt happens, we can 
	 * capture it.
	 */
        setup_irq(CPU_IRQ_IP4, &error_irq);
#endif

#ifdef CONFIG_REMOTE_DEBUG
        printk("Setting debug traps - please connect the remote debugger.\n");
        set_debug_traps();
        breakpoint();
#endif
}

Final notes

What is described in this chapter is what is so-called new style interrupt handling. We used to have three different ways to handle interrupts: new style (CONFIG_NEW_IRQ), the old style (CONFIG_ROTTEN_IRQ) and board-private ad hoc routines. New style is now the only valid method since October-2002.

System time and timer

Linux relies on an RTC (real-time clock) device to obtain the real calendar data and time when it boots up. It relies on a system timer to advance the tick count, jiffies. If you don't provide proper time and timer code, Linux won't run. In fact it will stick in 'calibrate_delay()' during the startup process because jiffies is never incremented. (See Appendix B for more details about Linux/MIPS startup sequence).

There is an excellent document (I becomes a little shameless. :-0) under 'Documentations/mips/time.README' that should be read to further understand timekeeping for Linux/MIPS. It is a must read.

Here are some comments on implementing time and timer services:

  • If your system has a CPU counter and another hardware timer, use the hardware timer over the CPU counter, even though CPU counter might be easier to setup and use. This is because CPU counter relies on the CPU frequency which is more likely to change in the future. In addition, performance-critical code may need to access the CPU counter for its own measurements. Some CPUs may have a variable CPU frequency which makes CPU counter not usable as a timer source. [DEBATE: 03/12/04, I changed my preference on this issue. Linux in the future will have richer and higher resolution time support. If all MIPS boards use CPU counter as the system timer, we can maximize the code sharing.]
  • Unless you have to use interrupts to calibrate the CPU frequency, you can generally avoid implementing the 'board_time_init()' function. Most of its work can be done in the board setup routine.
  • When you implement 'rtc_set_time()', more than likely you need to call the 'to_tm()' function which converts a single jiffy value to a full 'struct rtc_time'. This function is provided in 'arch/mips/kernel/time.c' and declared in 'include/asm-mips/time.h'.

PCI support

The PCI subsystem is perhaps the most complex code you have to deal with during the porting process. Thanks to the similarity of PCI, HyperTransport, PCI-X, Cardbus and other bus systems the time for understanding it well invested - and the key to making the PCI subsystem work properly is a good understanding of the PCI bus itself, the code layout, and the execution flow in Linux. Like many other parts of porting, you will find in the end, the actual code writen is minimal.

See PCI Subsystem for a discussion on the architecture-specific implementation and board-specific hooks you'll need to implement.

Debugging

Probably the most important chapter. In this chapter, I will list some commonly used debugging tips and tricks.

Kernel Oops

ksymoops is a program that deciphers all the secret numbers in a kernel core dump. Old versions of ksymoops may not work well for MIPS.

I sometimes use a script, call2sym, written by Phil Hollenback. You just cut and past the call trace part to feed the script and it will display a possible function call stack at the time crash. Note it is likely some symbols are bogus, but the real ones should be displayed. You will have to use your own judgement.

Linux 2.6 will automatically convert symbols if CONFIG_KALLSYMS is enabled. It may bloat kernels but it's a true life saver, so highly recommended.

Appendix A: Linux startup sequence in 2.4.x

kernel_entry() - arch/mips/kernel/head.S
   set stack;
   prepare argc/argp/envp;
   jal init_arch - arch/mips/kernel/setup.c
        cpu_probe() -
        prom_init(...) - arch/mips/ddb5074/prom.c
        loadmmu()
        start_kernel()  - init/main.c
                setup_arch(&commaind_line); - arch/mips/kernel/setup.c
                        ddb_setup()         - arch/mips/ddb5074/setup.c
                parse_options(command_line);
                trap_init();
                init_IRQ();                 - arch/mips/kernel/irq.c
                sched_init();
                softirq_init();
                time_init();
                        if (board_time_init) board_time_init();
                        set xtime by calling rtc_get_time();
			 pick appropriate do_gettimeoffset()
			 board_timer_setup(&timer_irqaction);
                console_init();
                init_modules();
                kmem_cache_init();
                sti();			/* interrupt first open */
                calibrate_delay();
                mem_init();
                kmem_cache_sizes_init();
		 pgtable_cache_init();
                fork_init();
                proc_caches_init();
                vfs_caches_init();
                buffer_init();
                page_cache_init();
                signals_init();
                smp_init();
                kernel_thread(init, ...)
                cpu_idle();


init() - init/main.c
  - lock_kernel();
    do_basic_setup();
      - [MTRR] mtrr_init();
        [SYSCTL] sysctl_init();
	 [S390] s390_init_machine_check();
        [ACPI] acpi_init();
        [PCI] pci_init();
        [SBUS] sbus_init();
        [PPC] ppc_init();
        [MCA] mca_init();
        [ARCH_ACORN] ecard_init();
        [ZORRO] zorro_init();
        [DIO] dio_init();
        [MAC] nubus_init();
        [ISAPNP] isapnp_init();
        [TC] tc_init();
        sock_init();
	 start_context_thread();
        do_initcalls();
        [IRDA] irda_proto_init();
        [IRDA] irda_device_init();
        [PCMCIA] init_pcmcia_ds();

    prepare_namespace();
    free_initmem();
    unlock_kernel();

    files = current->files;
    if(unshare_files())
            panic("unshare");
    put_files_struct(files);

    if (open("/dev/console", O_RDWR, 0) < 0)
                printk("Warning: unable to open an initial console.\n");
    (void) dup(0);
    (void) dup(0);

    if (execute_command)
                run_init_process(execute_command);
    run_init_process("/sbin/init");
    run_init_process("/etc/init");
    run_init_process("/bin/init");
    run_init_process("/bin/sh");

    panic("No init found.  Try passing init= option to kernel.");

Appendix B: Credits

People in the following list have generously given their feedback to me. In spite of my effort to keep the list as complete as possible, I am afraid many people are still missing here.