Have you ever wondered just what happens when you switch on your machine? I have spent most of the last 2 years of my working life trying to make sure that the right things happen when an Alpha AXP processor is powered on. The right thing is, of course, to boot Linux. I believe that, just like the Universe, the most interesting things happen in the first few moments. This article takes a brief tour through the sequence of events from power on to the login prompt on an Alpha AXP based Linux system.
When a microprocessor is turned on it must run some initialization software. Typically that software is held in non-volatile memory; either a flash or ROM device. Mostly these days, flash devices are used. The startup software for Intel PC systems is known as Basic Input Output Setup or BIOS code. This startup software has an aura of mystique about it as it does play such an important part of the system configuration. In reality, it is no more complex than an average Linux device driver. At power up all Intel chips start operating exactly the way that the Intel 8086 CPU used to; they start executing from address 0xE000 in memory and this is where the flash memory is mapped.
Alpha AXP PCs also uses flash ROM based code to set up the system. Alpha AXP based systems all put the flash or ROM device in a seperate address space from the system's main memory. Unfortunately, the Alpha AXP microprocessors have less addressing modes than Intel's X86 CPUs and they have no access to any external memory spaces without help from a support chipset. The Alpha AXP architecture does not have an instruction which means "read from I/O address foo and put the results in register bar". The support chips all provide I/O space access through a sparse memory mapping scheme. The sparse mapping schemes vary but they all involve mapping some part of Alpha's rather large address space into I/O space. For systems using the Apecs (2117x) chipset this means that the PCI I/O address space starts at 0xFFFFFC01C0000000 . All of this means that the Alpha CPU cannot access the flash until after the main memory has been set up and it most certainly cannot run instructions directly from flash or ROM. To solve this problem, Alpha AXP uses a synchronous ROM, or SROM, to set up the system's memory before it starts executing code that it copies from the flash ROM and into the system's main memory.
It is the SROM code which detects what size and speed of memory Simms have been fitted and sets up the memory refresh timers. Once these have been set up, the SROM code's next task is to load a flash ROM image into memory. As flash has become cheaper there is more of it, sometimes as much as a 1Mbyte. This means that you can fit more than one image into it. So that the SROM code can figure out which image to load, these images each have a header. The header includes such information as where in memory to load the image and a checksum that allows the SROM code to figure out if the image is damaged. Additionally there must be a way to select a particular image. On some systems a jumper might be used and on others a spare byte is used in the battery backed RAM of the TOY clock. This might sound a little bizarre but system software designers tend to use these little tricks. The settings of a bank of jumpers can be read from a register which is mapped somewhere in the system's memory.
Once the SROM has loaded the ROM image into memory there is a slight problem. The image was loaded and written to main memory as data from the SROM code that was running out of the internal instruction cache. This means that the data cache now contains parts of the image to run but that the instruction cache does not have any of the ROM image in it. This is a classic problem with Harvard architecture CPUs where the instruction and data cache are seperate. So, just before control is transferred to the image, the instruction cache is flushed clearing it out ready for the console code. Of course, flushing the cache whilst you are executing out of it is tricky. You have to branch someplace that is not in the cache and execute some pipelined NOPs whilst the cache is flushed.
The Linux kernel itself can be written directly into the boot block but it can also use LILO which is flexible enough to allow more than one OS to be loaded; most dual booted Intel X86 systems use this mechanism. LILO uses the BIOS callbacks and the underlying device drivers to load the Linux kernel into memory and transfer control of the system to it. The BIOS code remains resident in memory but it does not take up much memory, after all most of it is in flash with only its data structures left in system memory.
All in all, BIOS is a rather nice thing to have, unfortunately only Intel X86 PCs have it. So, what do other architectures do?
These non-Intel X86 systems have usually been built from non-standard components that, compared to the Intel PC market are expensive. Furthermore, they did not, by and large, use generic devices that could be used in a number of machines. You could not take the graphics device from a VAXStation and put it into a SPARCstation. Every manufacturer had their own boot code written for their own ends and they did not need it to be standard in any way. You could not walk up to one vendor's console and expect it to have the same interface as any other machine. Sometimes you could not even expect different machines made by the same vendor to have the same console interface. By contrast, you can pretty much guarantee to boot any Intel PC as they are very much alike even though their BIOS code may have been written by different companies.
Historically then, non-PC computers had their own boot code and these are known as consoles. Digital is no different from the other manufacturers and our console is known as the SRM Console (named after The System Reference Manual) and it is used to boot both OpenVMS and Digital Unix. The SRM is an operating system in its own right and it includes such features as multi-threading and loadable device drivers. It is also somewhat bigger than the Linux kernel and Digital charges rather a lot of money for it. As it is itself an operating system, it requires device drivers to be written for it before they can supported by the operating system itself. On the other hand it was designed to boot Digital Unix and when Linux was first ported to the Alpha, this is the console that was used. As well as loading the Linux kernel, the SRM console does provide it with Digital Unix PALcode. Later versions of the SRM console include our BIOS emulation library which allows us to run the Intel BIOS code from video cards in emulation thus giving our systems a wider choice of video cards. BIOS emulation is also starting to be used on the SCSI subsystem.
Over the last few years the higher end, workstation class of machines, have moved towards using standard PC components. This is because they are widely available, cheap and now often have very high performance. For example a 64 bit PCI graphics card is very powerful. I cannot speak for other computer manufacturers but Digital's move towards PC components allied with fast processors has been very marked. Five years ago Digital were shipping bespoke systems with almost no standard components. Today, there is very little difference between a workstation and a PC. Whilst the marketting people have seen Microsoft's Windows NT as their goal the Alpha PC is also a big opportunity for Linux and, hopefully for Digital.
Unfortunately it has a large disadvantage so far as running Linux is concerned. It is based on Windows NT PALcode which will not support any UNIX operating system and, worst of all, it runs in superpage addressing mode and not KSEG addressing mode so none of the Linux code would even run.
When the uncompressed Milo starts to run, pretty much all that Milo assumes has been configured is the memory. Milo first sets up the serial ports of the system. It then writes information about the boot sequence to COM port 1. If Milo is not booting on a particular system then often it is a good idea to hook up a serial line to that port to see what is happening. after that, Milo's boot sequence is very similar to the Linux kernel's except that Milo does not initialize quite as many things as the Linux kernel.
This similarity to the Linux kernel is no accident. Milo uses device drivers from the Linux kernel as is. This means that, theoretically at least, any device driver that works for Alpha Linux will work in Milo. So far, this is true for the floppy driver, the IDE driver, the NCR 810 SCSI driver, Adaptec 2940 and 3940 SCSI drivers as well as the Qlogic SCSI driver. It does not make sense, however, to include all of the Linux kernel inside Milo. Instead Milo uses pseudo-kernel code which maintains the interfaces that the drivers expect. For example, the timer code is Milo's but the PCI code is the kernel's.The drawback is that as the kernel revises the code, Milo must change to keep up. I have learnt a lot about the Linux kernel this way. Another example of Milo's pseudo-kernel code is the kernel memory allocation/deallocation code, the Milo buffer code is much simpler and less elegant than the kernel code but it gets the job done reasonably well if not particularly efficiently.
If it finds a VGA device then Milo will use Digital's BIOS emulation library to run (in X86 emulation) the on board BIOS code on the video device. This code is only shipped as a library and, unlike the rest of Milo, is not free license. The Milo source tree also includes a freeware alternative that is not bad with S3 based chips.
Milo's role in life is to find and load the kernel. Milo does include a keyboard driver and so it has a simple command interface. Unlike Lilo, which is table driven, Milo accepts commands and even stores configuration information in NVRAM. Milo's interface is simple, it does enough to find out which disk partition to load the Linux kernel from and what arguments to pass to the kernel. Milo supports filesystems (DOS, EXT2, ISO9660) and so the boot commands make human sense, for example:
MILO > boot sda2:vmlinuz root=/dev/sda2
Did I mention that it also figures out if the image is gzip'd and, if it is, automatically uncompresses it? When Milo has loaded the Linux kernel into the appropriate place in physical memory it sets the page tables to their initial state and builds the HWRPB (Hardware Restart Parameter Block). It is the HWRPB which tells Linux what sort of system this is and what physical memory is free. Almost immediately, the Alpha AXP setup code discards the HWRPB and Linux goes on to reinitialize the system itself. It does, however, take notice of what physical memory Milo told it was free and what was not.
Milo has been very successful, most of the Alpha Linux systems sold include it and it is included in the Alpha Linux distributions, Red Hat AXP Linux for example.