ipxe / wimboot

WIM bootloader
https://ipxe.org/wimboot
GNU General Public License v2.0
238 stars 42 forks source link

Failure to start with error 0xc000000d (Win8/Win10 32-bit, >4GB RAM) #15

Closed mcb30 closed 3 years ago

mcb30 commented 3 years ago

Raised from #13

The following .yml file defines a test case that can reproduce the problem:

name: Windows 10 (High RAM, 32-bit)
version: win10
arch: x86
memory: 5120
logcheck:
  - 'Found initrd at \[0x[0-9a-f]{8},0x[8-9a-f][0-9a-f]{7}\)'
  - 'Placing initrd at \[0x[0-9a-f]{8},0x[0-7][0-9a-f]{7}\)'
  - 'Placing initrd at physical \[0x[1-9a-f][0-9a-f]{8,},'

This test fails for Windows 10 (or Windows 8.1) with a 0xc000000d error: c000000d

The test passes if using any 64-bit version of Windows, or if using 32-bit Windows 7.

mcb30 commented 3 years ago

Using the linear option to inhibit relocation above 4GB will prevent the 0xc000000d error (at the cost of consuming low memory).

Experimentation so far suggests that the error will occur if any of the bootapp regions describe pages at 4GB or higher.

mcb30 commented 3 years ago

The error gets reported in winload!MmMapPhysicalAddress.

As far as I can tell from the disassembly, this is attempting to identity-map any regions mentioned in the BOOTAPP table. There is a call to a subroutine winload!MmArchTranslateVirtualAddress which passes in the region start address as a parameter in register %edx (and hence truncated to 32 bits). When the initrd region is placed above 4GB, this causes winload!MmMapPhysicalAddress to call winload!MmArchTranslateVirtualAddress with an incorrect value.

The call to winload!MmArchTranslateVirtualAddress doesn't actually fail. However, there seems to be a subsequent sanity check in winload!MmMapPhysicalAddress that will verify that the full 64-bit address was mapped. This check fails and returns the 0xc000000d error.

My working hypothesis is that this full 64-bit check is present only in more recent Windows versions, very probably as a result of integer-size comparison warnings introduced by newer compilers. This would explain why Windows 7 does not exhibit the same problem.

To test this hypothesis, I hacked src/paging.c to force the initrd region down close to 0x100000000 (i.e. 4GB). This causes the truncated address to collide with an existing mapping for the region at 1MB. With this forced address, Windows 7 will also fail to boot with error 0xc000000d.