Document:Emulating Windows 8 build 8061 ARM32

This guide is also relevant for other early ARM32 Windows NT builds from build 8042 (fbl_core1_mobile_dev) onwards, which additionally includes those pertaining to early Windows Phone 8 builds. All builds up to 8141 must have the Hardware Abstraction Layer patched in order to boot under an emulator. NT build 8141 (fbl_core1_mobile_dev) will boot without registry modifications, but 8124 and lower will not.
The patches on this guide are documented at a high level, and many structures and variable/argument names originate from leaked private symbols that were sourced from later Windows NT builds. As build 8061 lacks the appropriate debugging symbols (let alone private symbols), the exact same code may not be presented if the binaries were simply loaded into a decompiler.

Boot issues[edit | edit source]

Hardware Abstraction Layer (HAL)[edit | edit source]

This build is not compatible with the current version of QEMU WoA, mainly due to the lack of support for the final ARM Generic Timer ACPI descriptor table. Later builds (after 8141) acquire information about timers through the GTDT descriptor table, however this build uses its predecessor - the EGIT table.

Attempting to boot this build in QEMU WoA results in the following bugcheck:

KeBugCheckEx(HAL_INITIALIZATION_FAILED, 0x110, 0x5250631, TimerProblemNoScalingSource, STATUS_UNSUCCESSFUL);

The EGIT table used by this build is defined as follows:

typedef struct _EGIT_TABLE
{
	DESCRIPTION_HEADER Header;
	ULONGLONG MemoryMappedPhysicalAddress;
	ULONG SecurePhysicalTimerGsiv;
	ULONG NonSecurePhysicalTimerGsiv;
	ULONG VirtualTimerEventGsiv;
	ULONG HypervisorTimerEventGsiv;
	BYTE SecurePhysicalTimerFlags;
	BYTE NonSecurePhysicalTimerFlags;
	BYTE VirtualTimerEventFlags;
	BYTE HypervisorTimerEventFlags;
	ULONG GlobalFlags;
} EGIT_TABLE;
typedef EGIT_TABLE *PEGIT_TABLE;

While the earliest version of GTDT supported by WoA builds is defined as:

typedef struct _GTDT_TABLE
{
	DESCRIPTION_HEADER Header;
	ULONGLONG MemoryMappedPhysicalAddress;
	ULONG GlobalFlags;
	ULONG SecurePhysicalTimerGsiv;
	ULONG SecurePhysicalTimerFlags;
	ULONG NonSecurePhysicalTimerGsiv;
	ULONG NonSecurePhysicalTimerFlags;
	ULONG VirtualTimerEventGsiv;
	ULONG VirtualTimerEventFlags;
	ULONG NonSecurePhysicalTimer2Gsiv;
	ULONG NonSecurePhysicalTimer2Flags;
} GTDT_TABLE;
typedef GTDT_TABLE *PGTDT_TABLE;

The HAL in build 8061 queries for the EGIT. As the existing QEMU WoA fork only includes support for the GTDT, it will not find the table and hence bugcheck during HAL initialization.

HAL queries for the EGIT table through the following function call in HalpGitDiscover:

HalpAcpiGetTable(HalpTimerLoaderBlock, 'TIGE', NULL, NULL);

In order to make it query for the GTDT supported by QEMU WoA, it must be patched to become:

HalpAcpiGetTable(HalpTimerLoaderBlock, 'TDTG', NULL, NULL);

This however does not solve all issues. It is apparent that the two tables are largely identical, except for EGIT's inclusion of the Hypervisor Timer Event interrupt and GTDT's inclusion of the Non-Secure Physical Timer 2 interrupt. The biggest difference between the two tables are the ordering of the fields, EGIT places all the global system interrupt vectors (GSIVs), and all of the flags next to each other, while GTDT groups the GSIV and flags of each interrupt together. The build expects the EL1 Virtual Timer GSIV at offset 52 of the structure and flags at offset 62, while they are actually located at offsets 64 and 68 respectively in the GTDT. Moreover, the flags in the EGIT and the GTDT have different widths and meaning. To allow the GSIV and flags to be retrieved correctly, the following code in HalpGitDiscover:

NewTimer.Interrupt.Gsi = *((ULONG *)(Table + 52));
NewTimer.Interrupt.Polarity = InterruptActiveLow;
BYTE Flags = *((BYTE *)(Table + 62));
if (Flags & 2)
{
    NewTimer.Interrupt.Polarity = InterruptActiveHigh;
}
NewTimer.Interrupt.Mode = (Flags & 1);

must be patched to become:

NewTimer.Interrupt.Gsi = *((ULONG *)(Table + 64));
NewTimer.Interrupt.Polarity = InterruptActiveHigh;
ULONG Flags = *((ULONG *)(Table + 68));
if (Flags & 2)
{
    NewTimer.Interrupt.Polarity = InterruptActiveLow;
}
NewTimer.Interrupt.Mode = (Flags & 1);

Note the changes to the width of the flags and the interrupt polarity. The patches may be carried out on this build's HAL by changing:

  • 02 bytes at offset 0x138 from 8E D3 to DC E3 (PE checksum correction).
  • 19 bytes at offset 0x15B96 from 63 6B 17 93 02 23 18 93 94 F8 3E 30 13 F0 02 0F 01 D0 01 to 23 6C 17 93 01 23 18 93 63 6C 00 BF 13 F0 02 0F 01 D0 02 (table offset fixes).
  • 04 bytes at offset 0x15C10 from 45 47 49 54 to 47 54 44 54 (EGIT → GTDT).

After these patches, the HAL will be able to detect the ARM generic timers that are emulated by QEMU.

Registry fixes[edit | edit source]

Attempting to boot this build in the QEMU WoA fork with the patched HAL results in the following bugcheck:

KeBugCheckEx(INACCESSIBLE_BOOT_DEVICE, L"\\ArcName\\multi(0)disk(0)rdisk(2)partition(3)", STATUS_OBJECT_NAME_NOT_FOUND, 0, 0);

This is caused by the lack of SD host controller drivers. The below values must be merged into the registry in order for Windows to load the built-in SD host controller driver:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\ACPI]

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\ACPI\PNP0D40]

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\ACPI\PNP0D40\0]
"Capabilities"=dword:00000030
"ContainerID"="{00000000-0000-0000-ffff-ffffffffffff}"
"HardwareID"=hex(7):41,00,43,00,50,00,49,00,5c,00,50,00,4e,00,50,00,30,00,44,\
  00,34,00,30,00,00,00,2a,00,50,00,4e,00,50,00,30,00,44,00,34,00,30,00,00,00,\
  00,00
"ClassGUID"="{a0a588a4-c46f-4b37-b7ea-c82fe89870c6}"
"Service"="sdbus"
"DeviceDesc"="@sdbus.inf,%acpi\\pnp0d40.devicedesc%;SDA Standard Compliant SD Host Controller (compatible)"
"Driver"="{a0a588a4-c46f-4b37-b7ea-c82fe89870c6}\\0000"
"Mfg"="@sdbus.inf,%generic%;SDA Standard Compliant SD Host Controller Vendor"
"UINumberDescFormat"="@sdbus.inf,%SDBUSSlot%;SD Host Slot %1!u!"
"ConfigFlags"=dword:00000000

DWM[edit | edit source]

The Desktop Window Manager does not work as its implementation at this stage in port development is not complete and will assume all existing GPU drivers (including the generic Microsoft display driver) to be incompatible with this build, preventing software-based composition from working properly unless a Terminal Services client is used on real hardware.

Software-based rendering can be forcefully enabled by patching the main dwm.exe binary, and involves altering the method CDwmAppHost::VerifyGraphicsCapabilities to respectively set the fields fValidCompositionMode and dwDisabledEvent of the GraphicsCapabilities structure pointer pCaps to 1 and 0 and return 1 afterwards.

The patched function should look like the following:

DWORD CDwmAppHost::VerifyGraphicsCapabilities(MIL_CHANNEL hChannel, BOOL fIsBitmapRemoting, GraphicsCapabilities *pCaps)
{
	pCaps->fValidCompositionMode = 1;
	pCaps->dwDisabledEvent = 0;
	return 1;
}

The patches may be carried out on 8061's dwm.exe by changing:

  • 02 bytes at 0x00148 from 80 78 to 69 62 (PE checksum correction).
  • 06 bytes at 0x0233A from 04 DB 02 9B 13 B9 to 00 BF 02 9B 00 BF (ignore incompatible hardware).

Tips[edit | edit source]

  • The original binaries for the ACPI, SD bus and partition management drivers, as well as binaries for the boot manager and boot loader can be used under emulation.
  • Reapply the registry values outlined above after setup completes the second phase of setup to prevent an INACCESSIBLE_BOOT_DEVICE bugcheck loop, as the respective driver enumerator is removed from the Windows registry during hardware detection. This issue only occurs once and will not recur in subsequent boots after reapplication.