Making the Boot Loader go Faster

First published on the PARSE Software Devices website September 19th, 2004 © Copyright 2004 by Robert Krten, all rights reserved.

Synopsis:

This article describes how to modify the QNX-supplied master boot record (MBR) bootloader (the "primary bootloader"), and the secondary boot loader, to minimize the boot up time for deeply embedded systems. It assumes that your x86 box has a BIOS which then transfers control to the primary bootloader. The speedup is accomplished by modifying ("patching") the bootloaders.

Background

When the PC boots, the x86 processor goes to the reset vector located at the last 16 bytes of memory. This reset vector is located in the BIOS that comes with your computer.

The BIOS is responsible for setting up the various chipsets for your board, performing memory tests, etc., and finally determining what kind of mass storage devices you have.

When all these activities are completed, the BIOS then transfers control to the first block of the first valid mass media storage device. (You select in the BIOS the particular search order that the BIOS uses to determine which device that is — for purposes of discussion, we'll assume it's the first EIDE device).

This loader is called the "Primary Boot Loader", and its job is to load the "next thing" (usually the operating system, or another loader).

Most primary boot loaders give you the option to load one of several different operating systems, depending on how many partitions you have on your hard disk.

The primary boot loader that comes with QNX 6 allows you to press a button to indicate which disk you wish to boot from, and/or which partition on the disk you wish to boot.

In a deeply embedded system, of course, there's no real requirement to choose an operating system — you simply want to transfer control to the one and only operating system and boot as quickly as possible. We will discuss the modification necessary to accomplish this.

Once the primary boot loader has selected an operating system to boot, and we'll assume it's QNX 6 for our discussion here, the primary boot loader loads the secondary boot loader.

The secondary boot loader is similar to the primary boot loader — its job is to load the "next thing" — in this case, the operating system startup code.

The secondary boot loader that comes with QNX 6 allows you to press a button to indicate if you wish to load the operating system image from the file "/.boot" or from the file "/.altboot" — again, not something you wish to waste time on in a deeply embedded system.

Modification of Primary Boot Loader

So the first thing we want to do is remove the delay from the primary boot loader, so that it goes "right away" to the secondary boot loader. I'm somewhat cautious by nature, so instead of entirely "removing" the ability for the user to specify which partition and disk they wish to boot from, I simply made the timeout much smaller than the default. This also minimizes the amount of code change, which is always a Good Thing.

If you disassemble the primary boot loader, located in the first 512 bytes of the hard disk ("head -c -n512 /dev/hd0 | hd -v" will get you a HEX dump), you'll see the following code:

009d  b9 48 00      mov cx, 0048     ; load timeout constant into CX
00a0  b4 01         mov ah, 01       ; INT 16 service AH=1 "Test if keyboard character available"
00a2  55            push bp          ; save
00a3  51            push cx          ; save
00a4  cd 16         int 16           ; do the BIOS call
00a6  59            pop cx           ; restore
00a7  5d            pop bp           ; restore
00a8  75 17         jnz 00c1         ; goto 00C1 if a key is available
00aa  06            push es          ; save ES, we're going to go look at the BIOS data segment
00ab  ba 40 00      mov dx, 0040
00ae  8e c2         mov es, dx
00b0  26            es:
00b1  8b 16 6c 00   mov dx, [006c]   ; get current value of BIOS tick counter (0040:006C)
00b5  26            es:
00b6  3b 16 6c 00   cmp dx, [006c]   ; is it the same?
00ba  74 f9         jz 00b5          ; yes, try again
00bc  07            pop es
00bd  e2 e1         loop 00a0        ; no, some time has elapsed, go try for another key again

The above is a disassembly of just one of the many boot loaders provided by QNX — here's another one:

0067  b9 48 00      mov cx, 0048     ; the same, just at a different offset
006a  b4 01         mov ah, 01
006c  55            push bp
006d  51            push cx
006e  cd 16         int 16
0070  59            pop cx
0071  5d            pop bp
0072  75 18         jnz 008c         ; note different relative offset value (0x18 vs 0x17)
0074  06            push es
0075  ba 40 00      mov dx, 0040
0078  8e c2         mov es, dx
007a  26            es:
007b  8b 16 6c 00   mov dx, [006c]
007f  26            es:
0080  3b 16 6c 00   cmp dx, [006c]
0084  74 f9         jz 007f
0086  07            pop es
0087  e2 e1         loop 006a

Almost the same, but at a different location, and with a different relative jump offset at address 0x0072.

The point of this is — BEWARE. Make sure you understand what you are looking for in the bootloader BEFORE patching your disk. A good thing to do is to either disassemble the code yourself and look for the characteristic loop based on CX and the int 0x16 BIOS call, or just be supremely confident that you can restore your disk :-)

Basically, the idea is that CX contains a timeout value (0x48, or 72 decimal) that's used in the loop between 0x00A0 and 0x00BD (or 0x006A and 0x0087 in the second example). When this loop count reaches zero, it indicates that no characters were detected and the primary boot loader should go off and do whatever its normal default action is — which is booting the partition identified in the partition table as the default boot partition.

Since this is exactly what we want, all we need to do is adjust the timeout value to be smaller.

Consulting the IBM PC/AT Technical Reference manual, we see that 0040:006C is identified as "TIMER_LOW", and commented as "low word of timer count". This value gets bumped 18.2 times per second, so the CX value of 0x48 is approximately 72 / 18.2 = 3.95 seconds.

If we simply patch location 0x009E (or 0x0068) from 0x48 to something much smaller, like say "1" or "2", this will meet the requirements of greatly decreasing the bootup time.

Modification of the Secondary Boot Loader

The secondary boot loader is the one that prompts us with "Hit Esc for .altboot", and then loads the operating system image.

Again, we want to minimize the delay here.

If you disassemble the secondary boot loader, located in the first 512 bytes of the partition ("head -c -n512 /dev/hd0t79 | hd -v" will get you a HEX dump), you'll see the following:

007f  b9 24 00      mov cx, 0024     ; load timeout constant into CX
0082  b8 00 01      mov ax, 0100     ; INT 16 service AH=1 "Test if keyboard character available"
0085  cd 16         int 16
0087  75 12         jnz 009b         ; if key present, go to 009B
0089  1e            push ds
008a  31 c0         xor ax, ax
008c  8e d8         mov ds, ax
008e  8b 16 6c 04   mov dx, [046c]   ; get current value of BIOS tick counter
                                     ; (0000:046C (same as 0040:006C))
0092  3b 16 6c 04   cmp dx, [046c]   ; changed?
0096  74 fa         jz 0092          ; nope, wait for change
0098  1f            pop ds
0099  e2 e7         loop 0082        ; yes changed, go back to CX loop at 0082

Again, be careful to ensure that your bootloader is identical to the sample shown here! Here's another common one — exact same, just at a slightly different location...

0071  b9 24 00      mov cx, 0024
0074  b8 00 01      mov ax, 0100
0077  cd 16         int 16
0079  75 12         jnz 008d
007b  1e            push ds
007c  31 c0         xor ax, ax
007e  8e d8         mov ds, ax
0080  8b 16 6c 04   mov dx, [046c]
0084  3b 16 6c 04   cmp dx, [046c]
0088  74 fa         jz 0084
008a  1f            pop ds
008b  e2 e7         loop 0074

Here, CX is loaded with 0x24 (36 decimal) which works out to 36/18.2 or 1.98 seconds.

Simply patching this number with a far smaller number accomplishes our goal.

I hope this tutorial has been helpful.

Remember, if you screw up your system, miss your deadline, and you get fired, it's your fault — if you make your system boot faster and you get a raise, it's my fault :-)

Enjoy -RK