Allow XBE Section preload to fail

Since all sections in all Xbes are usually marked as preload, the Xbox
kernel attempts to load them all regardless.

In some titles (for example, Lego Star Wars DVD demo) the whole XBE is
too large to fit in RAM, and it is *expected* for the preload to fail.

Previously, when hit with this scenario, Cxbx-R would hard crash.

Now it mirrors hardware behavior and gracefully continues, skipping the
failed Xbe section.

Q: Why is this the case?
A: It's possible (and proven) that with titles such as the Lego Star Wars
DVD Demo, *all* game data is contained within the XBE file, rather than
on-disc as raw files. Naturally, these are too big to fit into the Xbox
RAM, so certain large sections fail to allocate, and are skipped
entirely by the Xbox Kernel.

The game then reads data from these sections by *not* loading them to
memory, but parsing the section *headers* to do raw file-io and read the
Xbe file as a binary blob, parsing the virtual filesystem.

This change allows Lego Star Wars DVD Demo (and other large Xbe titles)
to boot.

Lego Star Wars DVD Demo doesn't work yet, however, as it attempts a
dashboard update and fails, but it no longer crashes the whole emulator.
This commit is contained in:
Luke Usher 2019-04-11 08:32:39 +01:00
parent 2e5b9562e0
commit 4dbeab62ac
3 changed files with 23 additions and 19 deletions

View File

@ -75,11 +75,6 @@ XBSYSAPI EXPORTNUM(327) xboxkrnl::NTSTATUS NTAPI xboxkrnl::XeLoadSection
if (sectionData != nullptr) {
// If the reference count was zero, load the section
if (Section->SectionReferenceCount == 0) {
// Clear the memory the section requires
memset(Section->VirtualAddress, 0, Section->VirtualSize);
// Copy the section data
memcpy(Section->VirtualAddress, sectionData, Section->FileSize);
// REMARK: Some titles have sections less than PAGE_SIZE, which will cause an overlap with the next section
// since both will have the same aligned starting address.
// Test case: Dead or Alive 3, section XGRPH has a size of 764 bytes
@ -89,7 +84,15 @@ XBSYSAPI EXPORTNUM(327) xboxkrnl::NTSTATUS NTAPI xboxkrnl::XeLoadSection
VAddr BaseAddress = (VAddr)Section->VirtualAddress;
size_t SectionSize = (VAddr)Section->VirtualSize;
ret = g_VMManager.XbAllocateVirtualMemory(&BaseAddress, 0, &SectionSize, XBOX_MEM_COMMIT, XBOX_PAGE_EXECUTE_READWRITE);
ret = g_VMManager.XbAllocateVirtualMemory(&BaseAddress, 0, &SectionSize, XBOX_MEM_COMMIT, XBOX_PAGE_EXECUTE_READWRITE);
if (ret != STATUS_SUCCESS) {
RETURN(ret);
}
// Clear the memory the section requires
memset(Section->VirtualAddress, 0, Section->VirtualSize);
// Copy the section data
memcpy(Section->VirtualAddress, sectionData, Section->FileSize);
// Increment the head/tail page reference counters
(*Section->HeadReferenceCount)++;

View File

@ -1231,7 +1231,7 @@ void CxbxKrnlMain(int argc, char* argv[])
if ((sectionHeaders[i].Flags & XBEIMAGE_SECTION_PRELOAD) != 0) {
NTSTATUS result = xboxkrnl::XeLoadSection(&sectionHeaders[i]);
if (FAILED(result)) {
CxbxKrnlCleanupEx(LOG_PREFIX_INIT, "Failed to preload XBE section: %s", CxbxKrnl_Xbe->m_szSectionName[i]);
EmuLogEx(LOG_PREFIX_INIT, LOG_LEVEL::WARNING, "Failed to preload XBE section: %s", CxbxKrnl_Xbe->m_szSectionName[i]);
}
}
}

View File

@ -1744,6 +1744,19 @@ xboxkrnl::NTSTATUS VMManager::XbAllocateVirtualMemory(VAddr* addr, ULONG ZeroBit
goto Exit;
}
// Attempt to commit the requested range with VirtualAlloc *before* setting up and reserving the PT
// This allows an early-out in a failure scenario (Test Case: Star Wars Battlefront DVD Demo: LA-018 v1.02)
if (AlignedCapturedBase >= XBE_MAX_VA)
{
if (!VirtualAlloc((void*)AlignedCapturedBase, AlignedCapturedSize, MEM_COMMIT,
(ConvertXboxToWinProtection(PatchXboxPermissions(Protect))) & ~(PAGE_WRITECOMBINE | PAGE_NOCACHE)))
{
DBG_PRINTF("%s: VirtualAlloc failed to commit the memory! The error was %d\n", __func__, GetLastError());
status = STATUS_NO_MEMORY;
goto Exit;
}
}
// Check if we have to construct the PT's for this allocation
if (!AllocatePT(AlignedCapturedSize, AlignedCapturedBase))
@ -1769,18 +1782,6 @@ xboxkrnl::NTSTATUS VMManager::XbAllocateVirtualMemory(VAddr* addr, ULONG ZeroBit
PointerPte++;
}
// Actually commit the requested range but don't if it's inside the placeholder or we are committing an xbe section so that
// XeLoadSection works as expected
if (AlignedCapturedBase >= XBE_MAX_VA)
{
if (!VirtualAlloc((void*)AlignedCapturedBase, AlignedCapturedSize, MEM_COMMIT,
(ConvertXboxToWinProtection(PatchXboxPermissions(Protect))) & ~(PAGE_WRITECOMBINE | PAGE_NOCACHE)))
{
DBG_PRINTF("%s: VirtualAlloc failed to commit the memory! The error was %d\n", __func__, GetLastError());
}
}
// Because VirtualAlloc always zeros the memory for us, XBOX_MEM_NOZERO is still unsupported
if (AllocationType & XBOX_MEM_NOZERO) { DBG_PRINTF("XBOX_MEM_NOZERO flag is not supported!\n"); }