Porting LinuxBIOS to the AMD SC520

Building a Linux system that will boot in seconds, not minutes, requires a custom BIOS. But thanks to a new compiler and development process, we can build a BIOS for a new motherboard with only C code—no assembly.
What the Build Process Builds

The build process builds a binary image that is loaded to a Flash part. LinuxBIOS provides a utility, flash_rom, for this purpose. Alternatively, you can use the MTD drivers in the Linux kernel.

The layout of a typical ROM image is shown in Figure 2. The top 16 bytes contain two jump vectors, a jump to the fallback and a jump to the normal. LinuxBIOS always jumps to the fallback first. If all is well, it jumps back to the jump to normal vector at the top of memory, and from there to the normal image. If the fallback code detects problems or if the CMOS settings indicate that fallback BIOS should be run, the fallback BIOS runs.

Figure 2. A typical ROM image includes a fallback BIOS to allow booting in case of trouble with the main BIOS.

Building a Tree for the SC520 Board

Enough overview, let's get to work. To build support for a new board, we start with the mainboard first, and the easiest way to do this is to pick a similar mainboard. Because the Digital Logic ADL855 is much like the SC520, we start with that. We can clone much of the directory structure of the ADL855 for the SC520 board.

Mainboard Tree and Files

The basic naming process for directories in LinuxBIOS is to name the type of resource, in this case, mainboard; the vendor, here digitallogic; and the part name, in this case, msm586seg. Before we start the mainboard configuration file, we need to know what's on this mainboard. We don't have to get everything at first; in fact, we can leave a lot out simply to get something to work. Typically, the best approach is to make sure you know what drives the serial port and make sure you get that. To get DRAM up, you need to make sure you set up whatever device drives the SMBUS. None of these chips are in the right state when the board is turned on; you need to set a few bits to get things going.

For figuring this all out, you have a few choices. Almost always, the easiest thing to do is boot Linux and type lspci. For work with this type of board, it's easiest to have a CompactFlash part with a small Linux distribution installed so you can boot long enough to run the lspci command. You can use lspci to dump configuration space registers too, which sometimes is invaluable for discovering how to set control bits the vendor might have forgotten to tell you about. The setpci command also is handy for probing bits and learning the effects of setting and clearing them. On several boards, we've used setpci to probe the chipsets to find undocumented enable lines for onboard devices.


Although lspci shows discrete devices, on the SC520 they are integrated into the part. In the old days, we would create a new resource even if the part was integrated into the CPU. We have decided, based on previous experience, that if a part is integrated into the CPU, we do not consider it a separate resource. Therefore, there are no separate directories for the north and south bridge. The code for these devices is supported in the CPU device. The LinuxBIOS code base is flexible in this way. A given BIOS can be implemented with different types of parts, but in fact none of them are required.

Our first step in getting the resources set up for the mainboard is to name the CPU and set up the directory for it. The code for a given CPU is contained in the src/cpu directory. Luckily, the CPU in this case is an x86 system, so there is no need to add an architecture directory.

This article traces development from our point of view—a LinuxBIOS developer. If you want to develop a new tree, however, you can clone the LinuxBIOS arch repository, do development and submit patches to a developer. We will check your patches and help get them into the repository. In most cases with new developers, if their code is good, we allow them to become developers for our team.


We create a directory, src/cpu/amd/sc520, and populate it with files to support the CPU. We are not going to show all the commands for everything we do in this port, but for this first change, we show the commands to give you flavor of how it works. Even this simple part explains a lot of the important aspects of how LinuxBIOS is constructed:

cd src/cpu/amd
mkdir sc520
tla commit

This sets up the directory; now we need to populate it. The src/amd/socket_754 directory is a good candidate for providing model files, so we use them:

cd sc520
cp ../socket_754/* .

This gives us an initial set of files:

rminnich@q:~/src/freebios2/src/cpu/amd/sc520> ls
chip.h Config.lb socket_754.c

The chip.h file defines a simple data structure that is linked into the BIOS image by the Makefile, which is generated by the config tool. For this part, it's basically empty:

rminnich@q:~/src/freebios2/src/cpu/amd/sc520> catchip.h

extern struct chip_operations cpu_amd_socket_754_ops;

struct cpu_amd_socket_754_config {


What does this mean? First, we create an instance of a struct called chip_operations for this part, called cpu_amd_socket_754_ops. This is a generic structure, used by all chips. This generic structure looks like this:

/* Chip operations */

struct chip_operations {

        void (*enable_dev)(struct device *dev);


        char *name;



The chip_operations structure, in src/include/device/device.h, defines a generic method of accessing chips. It currently has two structure members: a function pointer to enable the device, enable_dev; and an optional name, used for debug prints, called name. Notice that in the style of the Linux kernel, C preprocessor-enabled code is controlled by testing the value of a preprocessor symbol, not by testing whether it is defined. As you can see, the enable_dev function takes a pointer to a device struct.

Why do we do this? Although there is one chip_operations structure for a type of chip, there is a device structure for each possible instance of a chip. We say possible because a device structure is defined for each chip that may exist in a system. Consider an SMP motherboard, which has from one to four or even eight CPUs; not all the CPUs may be there. Part of the job of the enable function is to determine whether the chip is even there.

The device struct looks like this:

struct device {
   struct bus * bus; /* bus this device is on, for
                      * bridge devices, it is the
                      * upstream bus */

   device_t sibling; /* next device on this bus */
   device_t next;    /* chain of all devices */
   struct device_path path;
   unsigned vendor;
   unsigned device;
   unsigned int class; /* 3 bytes:
                        * (base,sub,prog-if) */
   unsigned int hdr_type; /* PCI header type */
   unsigned int enabled : 1; /* set if we should
                              * enable the device */
   unsigned int initialized : 1;
    /* set if we have initialized the device */
   unsigned int have_resources : 1;
    /* Set if we have read the device's resources */
   unsigned int on_mainboard : 1;
   unsigned long rom_address;
   uint8_t command;

   /* Base registers for this device. I/O, MEM and
      Expansion ROM */

   struct resource resource[MAX_RESOURCES];
   unsigned int resources;

   /* links are (downstream) buses attached to the
    * device, usually a leaf device with no child
    * has 0 busses attached and a bridge has 1 bus */

   struct bus link[MAX_LINKS];

   /* number of buses attached to the device */
unsigned int links;

   struct device_operations *ops;
   struct chip_operations *chip_ops;
   void *chip_info;


This is a pretty complicated structure, and we don't go into all the issues here. During the configuration step, the LinuxBIOS configuration tool instantiates a struct device for each chip by writing C code to a file in the build directory. The C code that the config tool generates has initial values so that the array of device structures forms a tree, with sibling and child nodes. The LinuxBIOS hardwaremain() function walks this tree, starting at the root, and performs device probing and initialization.

The last structure member is a void *—that is, a pointer that can point to anything. The next-to-last element is a chip_operations pointer. As part of the creation of the initialized C structures, the config tool fills in the chip_info and chip_operations pointer with a pointer to the per-chip configuration structure and per-chip-type structure. Thus, each device in the tree has pointers to structures for the type of chip and the individual instance of the chip. The enable structure member, which is a function pointer, for the type of chip is called with a pointer to the structure for the device for each instance of the chip. The device structure has a lot of generic structure members, as you can see, and it has a pointer to a structure for nongeneric chip components.

For each chip, we optionally can provide declarations of both structures, but it is not required. The chip_operations structure, or the type-of-chip structure, has a type fixed by LinuxBIOS itself; the chip_info structure has a structure fixed by the chip. The enable function in the chip_operations structure can be un-initialized, in which case there is no enable function to call for the chip—the chip is always enabled. That is the case for the SC520 CPU—there is only one, and it is always there.

Now we need to change these files to match the SC520. We show them before and after to give you an idea how it looks.

chip.h changes to look like this:

extern struct chip_operations cpu_amd_sc520_ops;

struct cpu_amd_sc520_config {


The enable_dev pointer is empty and is not called. We leave it empty for now but may fill it in later as needed. Similarly, there are no special structure members for the chip_info structure.

The C code looks like this:

#include <device/device.h>
#include "chip.h"

struct chip_operations cpu_amd_socket_754_ops =
  { CHIP_NAME("socket 754") };

The changes are simple; we rename the file to sc520.c and then change it to this:

#include <device/device.h>
#include "chip.h"

struct chip_operations cpu_amd_sc520_ops =
  { CHIP_NAME("AMD SC520") };

The final file is the Config.lb file. Here we get our first glance at what a configuration file looks like. The original file looks like this:



        config chip.h


object socket_754.o

dir /cpu/amd/model_fxx

The first line declares that we are using the CONFIG_CHIP_NAME option. The language requires that we declare the variables we are going to use before we use them. In the case of this file that seems trivial, but in longer files this requirement is really useful. Second, if we are using the CONFIG_CHIP_NAME option, we use the chip.h file. Notice that nothing is set in chip.h unless we were using the CHIP_NAME macro, which is why this test is there. We declare any object files produced in this directory, in this case, socket_754. Finally, we include another directory using the dir keyword. The naming scheme in the config language for other directories is that the pathname is relative if it does not start with a /. Otherwise, it is rooted at the source of the LinuxBIOS source tree. In this case, the dir directive points to src/cpu/amd/model_fxx. As it happens, this is code for Opteron and is of no use to the SC520. After modifying this file for the SC520, it looks like this:



        config chip.h


object sc520.o

That's about it. We've now set up support for the SC520.



Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

just want to try this feature

MR Test's picture

Please remove this just want to see what it did and how?