Tom is a senior engineer at Cisco Systems and Scott at Phoenix Technologies. They can be contacted at http://www.deaddog .com/users/tomkat/ and [email protected] phoenix.com, respectively.
One of the best things about the PC architecture is its extensibility, which lets you add more RAM, upgrade to faster CPUs, install adapter cards, or add new boot devices. An adapter card that controls a boot device has some boot firmware on it called an "Option ROM" (OPROM). The OPROM code is called by the system BIOS to connect the boot device to the system so that the device may participate in the boot process.
But what if you have multiple boot devices? How do you determine which one will boot? How do you configure the system to change boot devices, and how do you keep them from conflicting with one another? Until now, there were no clear answers to these questions. Users struggled to get systems to work, often compromising flexibility in favor of compatibility. With the advent of Plug-and-Play (PnP) technology, however, an industry standard called the BIOS Boot Specification (BBS) opened the door to solving these problems.
Phoenix Technologies, Compaq Computer, and Intel authored the BBS to address the concept of boot priority. The BBS uses an existing industry standard -- the PnP BIOS Specification -- as a starting point, then defines how all boot devices could be recognized by the system BIOS and prioritized by the user, instead of by the machine or (worse yet) by the devices themselves. BBS-compliant machines and boot devices are starting to hit the shelves. They have found a particular niche in servers, where it's not uncommon to have dozens of drives and many network cards installed. One example of a BBS-compliant BIOS is PhoenixBIOS 4.0 Release 5.1 with MultiBoot II.
In this article, we'll focus on a PnP OPROM data structure called the "$PnP Header," introduced in the PnP BIOS Specification and clarified in the BBS. $PnP Headers are found in PnP ISA OPROMs and the more recent PCI OPROMs (see Table 1). You can find them by searching for the characteristic "$PnP" string (its location is indicated by the WORD at offset 001Ah in the OPROM).
The $PnP Headers are used by the system BIOS or a utility program to determine which OPROMs have a booting capability, and to some extent, how the devices were configured. Each header contains strings to describe its device, relevant data that classifies the device, and code entry points to initialize it for booting. (Table 2 describes the structure of the $PnP Header.) The nice thing about the $PnP Header is the capability for the system BIOS to control the OPROM's Power-On-Self-Test (POST) behavior. The PnP BIOS Specification states that ROMs with a $PnP Header are called twice by the system BIOS. The first call is the initialization call made to offset 0003h during ROM scan and determines the device's booting capabilities. At some point later (and based on the boot capabilities reported during the first call), a vector within the $PnP Header may be called to select the device for booting. This is powerful stuff compared to the old method in which devices tried to control their own booting, but it has some limitations.
The greatest limitation of this method of booting is that it becomes the responsibility of the system BIOS to recognize all boot devices and provide a way for the user to select among them for booting. The details of how this should be done are curiously absent from the PnP BIOS Specification. Recognizing this deficiency and the general lack of a standard on booting behavior, the BBS defines the expected behavior of the system BIOS and boot devices during POST. This is welcome news for the system BIOS designers and vendors of bootable network and SCSI cards.
Another limitation of the new $PnP Header method of booting is that devices were interpreted to be controllers. Since the OPROM was bound to the controller, this seemed like the natural interpretation. However, the SCSI adapter people had worked through problems with that interpretation some time ago. PCI systems, for example, could often control multiple SCSI controllers with one OPROM and save some upper-memory space. For an effective boot, device-selection mechanisms had to be drives.
Expansion headers with signatures other than "$PnP" can be defined. (See Table 3 for the structure of the generic expansion header.) The $PnP Header itself is part of a linked list of headers that each correspond to a particular boot device. The linking and checksum behavior is specified in the PnP BIOS Specification, but the interpretation of the other fields in the $PnP Header is specific to the particular "$--" signature. At this writing, $PnP is the only header type that has been defined.
While the linked structure of expansion headers appears to be designed for multiple expansion header types, there is no restriction against multiple occurrences of the same header type. To implement a header per device, shadowed OPROMs (all PCI and PnP ISA with the shadowable attribute set) first load with one standard $PnP Header. When the system BIOS calls the OPROM to initialize and to determine its bootability, the OPROM enumerates its devices (such as drives) and spawns a $PnP Header for each device. Each $PnP header has a unique descriptive string and entry point, which are used by the BIOS Setup program to present a menu of drives and allow users to select the boot priority of all devices in the system. If this doesn't impress you, you've never had to get SCSI drives, IDE drives, and a bootable LAN card working together in the same system.
Of course, the BIOS has to change a little bit, too. It doesn't do much good to reorder all the SCSI drives but still have them mount after the IDE drives. Thus, IDE drives can also be individually reordered using the same user interface that allows $PnP drive reordering. None of the $PnP drives' entry points explicitly load an operating system -- they merely connect devices by linking into the PC interrupt 13h chain. These devices use the Boot Connection Vector (BCV) in the $PnP Header. Devices controlled by the system BIOS that support similar functionality (such as IDE drives) emulate the BCV mechanism internally. All BCV devices must be ordered and linked into the interrupt 13h chain since each one may be needed at run time.
A special case boot device is the LAN device. When the system BIOS attempts to load an operating system from a LAN device, it should only return to the system BIOS if an error occurred. In olden days, a LAN card configured to boot would simply hook interrupt 18h and wait for both the floppy and hard drive to fail booting before it got a chance. Nowadays, PnP LAN cards get a little more respect and participate in the boot process in a cooperative manner. Their booting entry point is the Bootstrap Entry Vector (BEV) in the $PnP Header.
With the increase in the different types of boot devices (PCMCIA, CD-ROM, 1394, and the like) comes the need to be able to set a boot order or priority. The boot priority is key in that it puts all boot devices on the same playing field. No longer will IDE drives install before SCSI drives. All you have to do is change your boot priority using the BIOS Setup program, and you can switch between booting from an IDE drive and a SCSI drive in a snap. But your BIOS must support the BBS to gain this capability.
The ROMSCAN.EXE Program
The program ROMSCAN.EXE (available electronically; see "Availability," page 3) traverses your ROM space, or the file given as an argument, scanning for ROM headers. It displays information about each properly checksummed 55h AAh ROM header block. If an Expansion Header is found within a ROM, it traverses the Expansion Header chain looking for additional Expansion Headers. If the first Expansion Header does not checksum properly, it will assume this ROM does not support Expansion Headers. If subsequent headers on the chain don't checksum properly, or if a link points outside the bounds of the OPROM, an error will be reported. The contents of each Expansion Header are displayed. For $PnP headers, the entire structure will be displayed. For demonstration purposes, $Tom headers have been defined and their contents will also be completely displayed. For other types of headers (please tell us if you come across one), the generic header information will be displayed, and that is all.
When executed without arguments, ROMSCAN.EXE tells you about the Expansion Headers in your Upper Memory Block (UMB) space. When run with a file name as an argument, ROMSCAN.EXE scans the file as if it were a range of UMB space. It could be one or multiple OPROMs with the appropriate 2-KB aligned padding. For developers of $PnP OPROMs, this is a useful tool to verify your header structures and checksums. Be sure to rechecksum your Expansion Headers and your ROM image after any change that may affect their checksums. We've also supplied debug scripts to synthesize OPROMs with a variety of header types. These are useful both for using the supplied program and for writing your own OPROMs.
Relevant specifications and white papers are available at http://www.phoenix.com/.
Copyright © 1997, Dr. Dobb's Journal