Understanding CEPC Boot Sequence in Windows Embedded Compact 7 – part 2

Posted By David Campbell
Program Manager

Welcome back! This is part 2 of Doug Boling’s great write-up on Understanding CEPC Boot Sequence in Windows Embedded Compact 7 . Last time we covered the PC boot sequence in detail. That provides the background for the Windows Embedded Compact specifics. Let’s jump right in...

In Part 1, I started this discussion with an overview of two of the three different CEPC boot loaders provided in the Windows Embedded Compact 7. I discussed the LoadCEPC bootloader as well as how to use the BIOSLoader. I also talked about how the FAT file system. An understanding of the layout of a FAT storage device is important when understanding how these bootloaders work. In this installment, I will cover how the BIOSLoader and the WCELDR work and how to modify the WCELDR to adapt it to your hardware.

How BIOSLoader Works

In Part 1, I discussed how the file system works, now let’s return back to the BIOSLoader to cover how it works. When the system starts, the BIOS will load the Master Boot Records (MBR) into RAM which will find and load the boot sector of the active partition. This boot sector will be one of the BIOSLoader boot sectors that will have to be installed on the disk. The source code for the BIOSLoader boot sectors is located in \WINCE700\platform\cepc\src\bootloader\biosloader\bootsector, There is a unique boot sector for each of the different File Allocation Table (FAT) formats including ExFAT.

The boot sector code finds the root directory and looks for the name BLDR with no extension. It expects to find this name in one of the first 32 entries in the root since the boot sector only reads the first sector of the root directory into memory.

When the BLDR entry is found, the boot loader finds the location of the file data by using the first cluster entries in the directory entry for the file. Instead of following the FAT chain to properly load the entire file, the boot sector assumes that the file will be stored in linear sectors on the disk and reads a fixed (68 sectors or 34816 bytes) into memory at address 0:1000. This hard coded size provides an absolute limit on the size of the BLDR code.

The boot sector then jumps to the first byte of the BLDR file. The BLDR then switches to protected mode and executes the remaining tasks from there. Those tasks include reading and parsing the BOOT.INI file and downloading or reading from the disk the NK.BIN file.

BIOS Interface

I have yet to mention how the boot sector and the BIOSLoader read the disk. It uses the standard BIOS interface that has been present on PCs for decades. The BIOS interface is a real mode set of functions accessed using the x86 software interrupt opcode. Functional groups are assigned the same interrupt. For example, floppy and hard disk access is accomplished using Interrupt 13 (opcode Int 13). Here is a short list of some of the BIOS interrupts:

Interrupt 10 Video display

Interrupt 13 Storage I/O

Interrupt 14 Serial port

Interrupt 15 System level control

Interrupt 16 Keyboard services

Interrupt 1A RTC and PCI services

The command and subcommand for interrupt are in the registers AH and AL, respectively. Other parameters are passed in the 16 bit versions of the x86 register set.

As the BIOSLoader generally operates in 32 bit protected mode, it must switch back to real mode to call the BIOS and then return to protected mode once the BIOS call returns. This code can be seen in the file: C:\WINCE700\platform\CEPC\src\bootloader\biosloader\loader\Fixed\Main\bios.asm.

Building BIOSLoader

Building BIOSLoader is a challenge. The boot sector code must be compiled with MASM, Microsoft’s assembler. MASM is available on MSDN for download. Unfortunately the MASM version available has a linker that throws errors when building the boot sector. I was able to get it to build using a linker from an old MASM 5.1 set of files. In addition, the build scripts for the boot loader use a DEBUG script to trim off the end of the binary file. This makes it compatible with the BIOSLoader version of CeSys. This also fixes to the byte, the size of the boot sector code. If you decide to change the boot sector, these scripts will need to be adjusted.

Building the BIOSLoader itself is fairly straightforward. A unique version is created for each FAT version available including ExFAT. However, this build process also includes a DEBUG script to insert an opcode size adjustment at the beginning of the image. As DEBUG won’t run on my Win64 development machine, I ended up writing a trivial patch utility to make that change.

Fortunately, there are binary versions of the boot sectors and BLDR files for each of the FAT types. The boot sector binaries are available in C:\WINCE700\platform\CEPC\src\bootloader\biosloader\bootsector\Images. The BLDR binaries for the FAT formats is available in C:\WINCE700\platform\CEPC\src\bootloader\biosloader\loader\Images.

Installing BIOSLoader

In Windows Embedded CE 6 Platform Builder provides a DOS utility called CeSys that would install both a BIOSLoader boot sector along with the BLDR file itself. This binary has been removed from PB 7 although the source for the code is provided in C:\WINCE700\platform\CEPC\src\bootloader\biosloader\utilities\cesys\dos\cesys.c. The file is a 16 bit DOS application so you will need to use VC 1.52 to build it.

There is another version of CeSys located in C:\WINCE700\platform\CEPC\src\boot\tools\bin. This tool will work with BIOSLoader. However, it does not support ExFAT volumes. I’ll discuss this Windows CeSys tool later in the post.

BIOSLoader Limitations

The BIOSLoader is a good solution for booting Windows Embedded Compact but it does have some limitations. First, the size of the code is limited by what the boot sector code loads into the real mode RAM. This, among other issues, limits the BIOSLoader to less than 32 Kbytes of code. Given the code already written to parse the INI file, find and load the bootable BIN file and other functions, there is little room for expansion. In fact, it is quite easy to grow the BIOSLoader beyond its functional size limit by simply enabling the full error message list in the code.

In addition, because of the way the BIOSLoader is compiled, there are debug scripts in the build sequence to patch jump addresses into the final image. For those of us who are running the Win64 version of Windows on our development machines, this won’t work as Win64 doesn’t support the old DOS Debug utility.

The BIOSLoader is also designed to use the serial port as its primary communication medium. For newer systems that may not have a 16550 compatible UART on the motherboard, this eliminates the ability to debug boot sequence the BIOSLoader as it starts. Because of these and other limitations, Microsoft wrote a new boot loader for CEPC for Windows Embedded Compact 7.

WCELDR

WCELDR was released in Windows Embedded Compact 7 with little fanfare but is actually a very important addition to the operating system. It cleans up the build problems endemic with the BIOSLoader while adding a set of new features to the loader.

The WCELDR differs in features, design and user interface from the BIOSLoader. Instead of a text based configuration file as in BIOSLoader, the WCELDR stores its configuration in a portion of its own loader file. This technique allows the user to store changes to the configuration while WCELDR is running.

In addition, WCELDR is actually two loaders, one version that uses a serial port to interact with the user and the other that uses the PC’s display and keyboard as the U/I. This is a fantastic feature for developers who are designing with the newer non-legacy PC motherboards that don’t have serial ports.

Finally, the code in WCELDR is written so that it can grow significantly beyond the 32 Kbyte limit of the BIOSLoader. This redesign also removes the need to patch the loader with DEBUG scripts and so WCELDR can easily be built in a standard Platform Builder install on both Win32 and Win64 systems.

WCELDR will either automatically boot into a kernel image on the disk or via download from Platform Builder. Alternatively, the user can interrupt the boot process and have WCELDR display (via the serial port or the display) the following menu:

[1] Show Current Settings

[2] Set Boot Device

[3] Select KITL Device

[4] Network Settings

[5] Display Settings

[6] Debug Port Settings

[7] Save Settings

[0] Exit and Continue

This menu allows the user to see and change the current configuration. There are submenus available off many of the main commands.

All in all, it is a fairly complete set of commands. The only obvious missing command would be to dump the list of PCI devices. This would help for debugging PC systems where all the hardware isn’t completely known.

WCELDR Limitations

Unfortunately WCELDR also removes a few features. The most significant may be the text based configuration file that makes BIOSLoader so easy to preconfigure. The WCELDR actually stores its configuration in a block of the WCELDR file. Since the format of the configuration data storage is known (since we have the source) and the data storage location is defined by an interesting build method, it would be possible to write an application that could pre-write the configuration data. This is an exercise that Microsoft has left to the developer.

Another issue is that the BIOSLoader boot sectors are not compatible with WCELDR because of the different names of the boot loader files; BLDR for BIOSLoader and WCELDR for WCELDR. Because of this, a system must never mix the boot sector code of the two boot loaders with the other’s main loader code. And no, you can’t simply rename WCELDR to BLDR because the code in WCELDR has code that looks for the WCELDR file on the disk.

A minor limitation is that WCELDR does not support ExFAT. If that FAT version is required, you will need to port the ExFAT boot sector and the ExFAT file access routines from the BIOSLoader to the WCELDR code.

WCELDR Construction

Before I discuss how WCELDR works, I need to present how WCELDR is constructed. The clever design of the loader is instrumental in allowing a larger amount of code and also in avoiding any special patch scripts.

WCELDR is actually two separate loaders, XLDR and BLDR and a data file BLDRCfg.nb0. The XLDR is a simple loader that can find and read its own file. The XLDR component of WCELDR is a binary image so that when the boots sector loads and jumps to the entry point of WCELDR it is the XLDR binary that executes. The BLDR component is the “real” boot loader that does all the work of displaying menus, loading files, accessing and configuring Ethernet controllers and downloading files. BLDRCfg.nb0 is the configuration data block. The file is 512 bytes in length and contains a header string with the remainder of the file all zeros. This is the file that would be changed to pre-configure the boot loader.

During the WCELDR is build process, the BLDR component is built first, resulting in BldrC.bin and BldrS.bin files (one for the serial and one for the console versions of the loader). The XLDR component is then built as a binary nb0 image, resulting in XldrC.nb0 and XldrS.nb0files. The final step of the build process is a simple Copy command to append the XLdr, BldrClg and Bldr components together into one file. Due to the order of the copy, XLDR is at the start of the file, BldrCfg is in the middle and BLdr.bin is at the end. The final result of the build process is two files in the Release directory, WCELdrC and WCELdrS both without an extension.

WCELDR Sequence

The WCELDR starts like the BIOSLoader with a custom boot sector that finds and loads the WCELDR file in the root directory of the boot disk. The boot sector code loads 68 linear sectors from the disk to the RAM at address 0x1000.

The XLDR code then reads the WCELDR file again to scan for the B00FF signature that marks the beginning of the BLDR.bin component. At that point, XLDR reads the packets of the .BIN component and expands them into RAM. Once the BLDR.BIN component of the file has been completely expanded, XLDR jumps to the entry point of the BLDR component.

The BLDR component then reads the configuration block to load its settings. This is done by re-reading the WCELDR file scanning for the BLDRCFG string that indicates the start of the configuration block. It then uses that configuration as a guide for the remainder of the boot.

If configured, WCELDR prints a prompt to the user (either via serial or console depending on the binary) which states to press the space bar to enter the configuration menu. If the user doesn’t press the space bar after a preconfigured time, the loader then attempts the boot sequence either downloading via KITL or reading a .BIN file from the disk.

If the user does press the space bar, the menu shown earlier is displayed. This menu interface lets the user configure the loader and optionally save the configuration permanently. If the user requests to save the configuration, the loader reads the WCELDR file, finds the configuration block, and writes the new date back into its own file.

Modifying WCELDR

WCELDR is a full featured boot loader but there is one place where many developers will need to modify it. As written, WCELDR is linked to libraries to manage the RTL8139, DEC21140 and the eternal NE2000 Ethernet controllers. If your design has a different Ethernet hardware, you will need to add your library to the loader. Fortunately, this is a simple process and the libraries that WCELDR uses are compatible with the older KITL libraries in Windows Embedded CE 6.

Adding a new Ethernet library involves modifying one C file and two Source files. The C file to modify is Init.C in the directory \wince700\platform\cepc\boot\bldr. At the top of the file, add the include file (.h) reference to your library as shown below.

#include "bldr.h"

#include <bootBios.h>

#include <bootBlockBios.h>

#include <bootDisplayBios.h>

#include <bootTransportEdbg.h>

#include <bootTransportEdbgRtl8139.h>

#include <bootTransportEdbgDec21140.h>

#include <bootTransportEdbgNe2000.h>

#include <bootTransportEdbgDp83815.h>

#include <bootTransportEdbgMyEthernetController.h>

#include <pehdr.h>

#include <romldr.h>

Just below the include file references, you will need to declare a structure that contains a jump table to your Ethernet library’s entry points as shown below. Note that not all the entry points are required.

static const BootEdbgDriver_t s_MyECtlr =

{

NULL, // InitDMABuffer

MyECtlrInit, // InitNICControler

NULL, // DeInitNICControler

MyECtlrSendFrame, // SendFrame

MyECtlrGetFrame, // GetFrame

NULL // Filter

};

Finally, you will need to add a reference to this new structure in the s_devices table as shown below

static const Device_t s_devices[] =

{

{ L"Boot Disk", DeviceTypeStore, (enum_t)IfcTypeUndefined, 0, NULL },

{ L"RTL8139", DeviceTypeEdbg, IfcTypePci, 0x12111113, &s_Rtl8139 },

{ L"RTL8139", DeviceTypeEdbg, IfcTypePci, 0x13001186, &s_Rtl8139 },

{ L"RTL8139", DeviceTypeEdbg, IfcTypePci, 0x813910EC, &s_Rtl8139 },

{ L"DEC21140", DeviceTypeEdbg, IfcTypePci, 0x00091011, &s_Dec21140 },

{ L"NE2000", DeviceTypeEdbg, IfcTypePci, 0x09401050, &s_Ne2000 },

{ L"NE2000", DeviceTypeEdbg, IfcTypePci, 0x802910EC, &s_Ne2000 }

{ L"MyECtlr", DeviceTypeEdbg, IfcTypePci, 0x604017f3, &s_MyECtlr },

};

Notice the format of the table. The first field is the name that the user will see when they select the boot device. The second field is type of the device. If the controller is a PCI device, the next field indicates this and the fourth field is the PCI VID and PID IDs for the hardware. Since a single library may work with multiple versions of the hardware, some of the Ethernet libraries in the table have multiple entries.

You will also need to modify the SOURCES file in the BLDR component. There are two SOURCES files, one for the console version of the loader and one for the serial version. Even if you only plan on using one, it is a good idea to modify both as you never knows who will later use your code as an example. The sources files are located in the Console and Serial subdirectories under \wince700\platform\cepc\boot\bldr. The following is a modified version of the SOURCES file with the comments removed and some editing for brevity:

TARGETNAME=bldrC

TARGETTYPE=PROGRAM

EXEENTRY=BootStart

POSTBUILDTARGETS=wceldr

#ensure we don't run romimage the same time another project is running #romimage

POSTBUILD_PRODUCES=bldr_serial

LDEFINES=/DEBUG /DEBUGTYPE:CV /FIXED:NO

SAFESEH=

NOLIBC=1

FILE_VIEW_ROOT_FOLDER=bldr.bib

INCLUDES=$(_PLATFORMROOT)\common\src\common\bldr\inc;$(INCLUDES)

INCLUDES=..;$(INCLUDES)

INCLUDES=$(_PLATFORMROOT)\common\src\SoC\x86_ms_v1\inc;$(INCLUDES)

SOURCES= \

..\init.c \

..\main.c \

..\powerOn.c \

..\config.c \

..\download.c \

..\preLoad.c \

..\loadUldr.c \

..\loadOs.c \

..\run.c \

log.c \

..\powerOff.c \

..\notify.c \

..\drivers.c \

..\pci.c \

..\args.c \

..\stub.c \

TARGETLIBS= \

$(_PLATCOMMONLIB)\$(_CPUDEPPATH)\boot_driver_display_bios.lib \

$(_PLATCOMMONLIB)\$(_CPUINDPATH)\boot_driver_block_bios.lib \

$(_PLATCOMMONLIB)\$(_CPUDEPPATH)\boot_driver_block_utils.lib \

$(_PLATCOMMONLIB)\$(_CPUDEPPATH)\boot_driver_terminal_debug.lib \

$(_PLATCOMMONLIB)\$(_CPUDEPPATH)\boot_driver_terminal_utils.lib \

$(_PLATCOMMONLIB)\$(_CPUDEPPATH)\boot_driver_filesystem_fat.lib \

$(_PLATCOMMONLIB)\$(_CPUDEPPATH)\boot_driver_filesystem_utils.lib \

$(_PLATCOMMONLIB)\$(_CPUDEPPATH)\boot_driver_download_bin.lib \

$(_PLATCOMMONLIB)\$(_CPUDEPPATH)\boot_driver_transport_edbg.lib \

$(_PLATCOMMONLIB)\$(_CPUDEPPATH)\boot_log.lib \

$(_PLATCOMMONLIB)\$(_CPUDEPPATH)\boot_core_x86_bios.lib \

$(_PLATCOMMONLIB)\$(_CPUDEPPATH)\oal_ethdrv_rtl8139.lib \

$(_PLATCOMMONLIB)\$(_CPUDEPPATH)\oal_ethdrv_dec21140.lib \

$(_PLATCOMMONLIB)\$(_CPUDEPPATH)\oal_ethdrv_ne2000.lib \

$(_PLATLIB)\$(_CPUINDPATH)\bsp_ethdrv_MyECtlr.lib \

$(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\gsnull.lib \

$(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\fulllibc.lib

Notice that the path to my new controller library references the BSP library directory and no the Platform Common library folder used by the generic libraries provided by Microsoft. Of course you will also need to compile your Ethernet library. That is done identically to the way it was done in Windows Embedded CE 6.

Building WCELDR

Since WCELDR is architected in separate components it is critical that the build command is run in a directory high enough to build both the BLDR and XLDR components. Even if modifications are only made to BLDR, the XLDR component must be built because it is the XLDR build that combines the newly changed BLDR and XLDR components into one file. So always build from the folder \wince700\platform\cepc\boot when modifying WCELDR.

Installing BLDR/WCELDR On A Disk

Installing WCELDR has gotten much easier in WEC 7. Microsoft has provided a version of the CeSys utility that runs as a standard Win32 application. The utility is located in C:\WINCE700\platform\MyIntel\SRC\boot\tools\bin\i386. This version takes one argument, the name of the WCELDR file you wish to install. It will rename it during the install so if you use the command line:

CeSys WCELdrC

The utility will rename WCELdrC to WCELDR on the target disk. The boot loader code is statically bound into this new version of CeSys. The utility will determine if a FAT16 or FAT32 boot sector is needed and install the appropriate one.

The target disk should be freshly formatted with FAT32 or FAT16.

A critical issue with CeSys is that it requires that any partition table on the target disk to have the primary partition be marked “Active”. This can be a problem for pre-formatted storage as it rarely come from the factory with an active partition. In fact, many MBR sectors on factory formatted storage don’t even have the boot code necessary even if a partition is marked active.

A good way to do this is to attach your target media to your development machine and have the Windows Disk Manager first remove any partitions and then repartition the disk. This will ensure that there is boot code in the MBR. You will also need to mark the main partition active, but here lies yet another problem.

If Windows 7 sees the target disk as a hard disk, the Disk Manager utility can mark a partition active but if Windows 7 deems the disk as removable, that option is grayed out. I have written my own little utility to mark the partition active. You may have to do the same.

The new WCELDR is a powerful addition to the Windows Embedded Compact platform. The new loader is much easier to build and has a new set of features that is bound to appeal to most developers. If you need to use the older BIOSLoader, be sure to keep the code size down and to use the appropriate binary for the version of the FAT file system on the disk. You can use the CeSys tool written for WCELDR to install the BIOSLoader but be warned that CeSys will rename the BLDR binary to WCELDR when it installs it on the disk. This isn’t a problem for the current BIOSLoader but make sure any modifications you make don’t depend on the name BLDR.

In September, I will continue the discussion of the CEPC BSP. Instead of focusing on the boot loaders, I’m going to talk about the BSP in general. CEPC is one of the more enigmatic BSPs in Platform Builder. It is powerful, but difficult to modify without knowledge of the build system. I hope to peel away some of the mystery and demonstrate the power of the CEPC BSP. See you then.

Doug’s Next Webcast

Doug’s next monthly Webcast is coming up. Here are the details:

When: Tuesday, September 18, 2012, 9 a.m.-10 a.m. PDT

Title: Optimizing your Platform Builder Development System

Overview: Platform Builder is an extraordinarily powerful tool - but its power makes it somewhat difficult to fully tune the installation. This webcast will discuss how to properly install Platform Builder and how to tune the development environment to improve build times. Come learn how to get the most out of Platform Builder in this very practical webcast.

Registration link (includes link to previous recordings): https://www.microsoft.com/windowsembedded/en-us/develop/windows-embedded-compact-7-developer-classroom.aspx

For more information, visit Doug’s website at www.bolingconsulting.com. Thanks for your time.