Friday, April 7, 2017

WCE7 detailed boot procedure

Here is a detailed description of how, theoretically, Windows CE boots on a device..


1. ROMIMAGE fixes up binaries in the OS image
An EXE or a DLL can be fixed up, which means that they are modified to adjust the variables and functions addresses depending on the binary location in virtual memory. At build time, the compiler doesn't know where the binaries will be located, so all of the pre-built addresses are wrong and need to be updated at load time. This is exactly what ROMIMAGE does when inserting the binaries in the final image that reflects the device's memory. To achieve this, ROMIMAGE relies on config.bib.
Note: All EXE and DLL files have a reserved section to be fixed up.
Note 2: All EXE and DLL files have an entry point that can be read in binary.

The image will contain 2 major binaries that will be required to boot:

Binary Description
NK.exe Contains all the functionality that is platform and architecture specific (typically, the OAL)
kernel.dll Contains architecture-neutral functionalities 

2. ROMIMAGE places tge TOC in the image file and fills pTOC variable with the location address
TOC stands for "Table Of Content". When all binaries have been placed in the image by ROMIMAGE, the start address of every binary is listed into a TOC structure. The TOC is then placed in the image as well.
The TOC will be needed at runtime and is accessible in the OAL with a pointer named pTOC. The value of this pointer is -1 at build time because the location of the TOC is unkown at this moment. The value is updated by ROMIMAGE once the TOC has been placed in the image.

Using the TOC, the program can find all other pieces of the operating system image.


3. Bootloader puts the image at the right place in memory
4. Bootloader looks for the TOC in the image
Bootloader does not have access to pTOC variable (it is not part of NK.exe). Instead, it will use a trick done by ROMIMAGE.
When the TOC is placed in the image, it is prepended with a marker : "CECE" (or 0x44424442). The bootloader will find this marker and read the TOC.
5. Bootloader looks for NK.exe in TOC
6. Bootloader jumps to the entry point of NK.exe
7. NK.exe calculates the physical address of OEMAddressTable
At this moment of boot, the memory can only be addressed physically.
OEMAddressTable is a symbol in NK.exe, which means that it has a virtual memory fixed by ROMIMAGE. NK.exe knows its own physical location in memory and its future location in virtual memory.
OEMAddressTable physical address = (Physical address of NK.exe) + ( (OEMAddressTable virtual address) - (Virtual base address of NK.exe) )
8. NK.exe sets up the virtual memory mapping for the MMU
Virtual memory is now enabled and NK.exe moves to virtual memory.
However, RAM has not been initialized yet which means that all read/write variables can have any content whatsoever.
9. NK.exe copies the "copy entries" to RAM (R/W portions or RAM)
The function that does this is KernelReloacte().
The TOC describes also the RAM and where the read/write portions of each module are to be located. Pieces of OS image that need to be copied to RAM are called "copy entries".
Note: Obviously, pTOC is not located in RAM otherwise none of this would work. It is in ROM (const variable).
10. NK.exe sets up the Kernel Data Page and its initial content
Windows CE reserves a few regions of virtual address range for its own private use inside the OS kernel. There are several 4k pages of virtual memory from 0xFFFE0000 upwards. At least one page is reserved to Kernel Data Page.
NK.exe sets up the location and inserts:
  • A copy of TOC
  • The address of OEMAddressTable
  • The address of the function OEMInitGlobals()
11. NK.exe jumps to kernel.dll entry point with the address of the kernel data page as parameter
12. kernel.dll calls OEMInitGlobals() with the address of NKGlobals as parameter and gets the dadress of OEMGlobals as a return value

Variable Description
NKGlobals Static table of functions (ex: SetLastError(), NKwvsprintfW() ) and data in kernel.dll
OEMGlobals Static table of functions pointers and data : PFN_InitDebugSerial(), PFN_Ioctl(),... The functions pointers points to legacy OEM_xxx() functions (like OEMInitDebugSerial())

Once the call to OEMInitGlobals() completes, the kernel.dll has everuthing it needs to architecture-generic and platform-specific processing. It knows how memory is laid out virtually and where are all modules in the image.
NK.exe has also access to a set of kernel functions that it can call.
13. kernel.dll runs architecture-specific setup (ARMSetup() or X86Setup())
During this step, Interlocked API will be copied to Kernel Data Page. This is a critical part of the system that manages the coordination between threads.
14. kernel.dll runs architecture-neutral setup
Initialization of kernel debug output, selection of kernel processor type, check of the presence of KITL.dll.
Note: KITL is the system debugger.
Note 2: KITL can be loaded as a dll or directly included in the OAL. If the dll exists, it is loaded here.
15. kernel.dll runs platform-specific setup (OEMInit())
Platform specific initialization. KITL is started (if included in the image).
16. kernel.dll runs its first thread (SystemStartupFunc())
17. SystemStartupFunc() initializes the system
System loader, paging pool, system logging, system debugger, message queue, watchdog.
Creates a thread for Power Management.
Creates a thread for File System.
18. SystemStartupFunc() creates another thead which calls RunAppAtStartup()
This new thread creates first user processes.


Post a Comment