xbox-kernel/private/ntos/bootx/bldr32/i386/romcpy.c
2020-09-30 17:17:25 +02:00

409 lines
9.8 KiB
C

/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
romcpy.c
Abstract:
This module implements code to uncompress, copy, and check Xbox ROM
--*/
#include "bldr32.h"
#include <ldi.h>
#include <bldr.h>
#include <sha.h>
#include <rc4.h>
//
// Local support
//
BOOLEAN
BldrDecompress(
IN ULONG FromAddress,
IN ULONG ToAddress,
IN ULONG UncompressedSize
);
MI_MEMORY
DIAMONDAPI
BldrAlloc(
ULONG NumberOfBytes
);
VOID
DIAMONDAPI
BldrFree(
MI_MEMORY pointer
);
//
// Location of the topmost byte, not inclusive, of the ROM.
//
ULONG BldrTopOfROMAddress = 0;
//
// Number of bytes that have been allocated for LZX decompression. Used to mark
// the top of the heap.
//
ULONG BldrNumberOfBytesAllocated;
VOID
BldrCopyROMToRAM(
IN const UCHAR* DecryptionKey
)
/*++
Routine Description:
Decrypts and decompresses kernel image from ROM to the RAM.
Arguments:
DecryptionKey - Key used to decrypt the kernel image
Return Value:
None.
--*/
{
ULONG BldrAddress;
PBOOTLDRPARAM BootLdrParam;
PXBOOT_PARAM BootParam;
ULONG CompressedKernelAddress;
ULONG UncompressedKernelDataAddress;
#ifdef MCP_B02XM3
A_SHA_CTX SHAHash;
UCHAR SHADigest[A_SHA_DIGEST_LEN];
ULONG InitTableAddress;
#endif
PUCHAR KernelBuffer;
//
// Start address for the boot loader image
//
BldrAddress = 0x80000000 + BLDR_RELOCATED_ORIGIN;
//
// Calculate a pointer to the boot parameters stored at the begining of
// of the boot loader image eg command line. In case of ROM, entire image
// including the parameter structure is decrypted so its safe to use
// the command line which will normally be empty.
//
BootLdrParam = (PBOOTLDRPARAM)BldrAddress;
//
// Boot param structure is at the end of the boot loader block
//
BootParam = (PXBOOT_PARAM)(BldrAddress + BLDR_BLOCK_SIZE - sizeof(XBOOT_PARAM));
//
// Compute the address of the uncompressed kernel data block
//
UncompressedKernelDataAddress = 0 - ROM_DEC_SIZE -
BLDR_BLOCK_SIZE - BootParam->UncompressedKernelDataSize;
//
// Compute the address of the compressed kernel image
//
CompressedKernelAddress = UncompressedKernelDataAddress -
BootParam->CompressedKernelSize;
#ifdef MCP_B02XM3
//
// Calculate SHA1 digest for the compressed kernel image and uncompressed
// kernel data block.
//
// Compute the address of the init table. The ROM image is aliased
// through out the top 16MB of address space. For a 1MB image, it will
// be aliased 16 times. We use the top 1MB alias and the address mapped
// in startup.asm. This should work fine for 512K or 256K but will not
// work for parts larger than 1MB.
//
InitTableAddress = 0UL - 0x100000;
BldrPrint(("Bldr: Init Table=%08x, Version=%x", InitTableAddress,
*((PUCHAR)(InitTableAddress + 0x78))));
//
// Calculate a SHA1 digest with the following ROM components
// 1. Size and contents of compressed and encrypted kernel in ROM
// 2. Size and contents of uncompressed data
// 3. Size and contents of init table
//
// Note: We calculate the digests directly from ROM rather than
// depending on the contents of RAM at this point
//
A_SHAInit(&SHAHash);
A_SHAUpdate(&SHAHash, (PUCHAR)DecryptionKey, 16);
A_SHAUpdate(&SHAHash, (PUCHAR)&BootParam->CompressedKernelSize, sizeof(ULONG));
A_SHAUpdate(&SHAHash, (PVOID)CompressedKernelAddress, BootParam->CompressedKernelSize);
A_SHAUpdate(&SHAHash, (PUCHAR)&BootParam->UncompressedKernelDataSize, sizeof(ULONG));
A_SHAUpdate(&SHAHash, (PVOID)UncompressedKernelDataAddress, BootParam->UncompressedKernelDataSize);
#ifdef DEVKIT
//
// Hack to allow manual editing of the init table. For devkit systems, if
// the InitTableSize is 0, we skip calculating the digest for the init table
// This can be initiatiated by ROMBLD's /HACKINITTABLE option
//
if (BootParam->InitTableSize != 0) {
A_SHAUpdate(&SHAHash, (PUCHAR)&BootParam->InitTableSize, sizeof(ULONG));
A_SHAUpdate(&SHAHash, (PVOID)InitTableAddress, BootParam->InitTableSize);
}
#else
A_SHAUpdate(&SHAHash, (PUCHAR)&BootParam->InitTableSize, sizeof(ULONG));
A_SHAUpdate(&SHAHash, (PVOID)InitTableAddress, BootParam->InitTableSize);
#endif
A_SHAFinal(&SHAHash, SHADigest);
A_SHAInit(&SHAHash);
A_SHAUpdate(&SHAHash, (PUCHAR)DecryptionKey, 16);
A_SHAUpdate(&SHAHash, SHADigest, sizeof SHADigest);
A_SHAFinal(&SHAHash, SHADigest);
//
// Compare the calculated digest with the digest in the boot param
//
if (memcmp(SHADigest, BootParam->MainRomDigest, sizeof(SHADigest)) != 0) {
BldrPrint(("Bldr: ROM digests do not match"));
BldrShutdownSystem();
}
#endif // MCP_B02XM3
//
// If we got here, the ROM image is OK to decrypt. Allocate enough space
// so we can copy the compessed encrypted kernel image to RAM. We don't have to
// worry about freeing this memory.
//
KernelBuffer = (PUCHAR)BldrAlloc(BootParam->CompressedKernelSize);
//
// Copy the compress encrypted kernel image from ROM to RAM
//
BldrPrint(("Bldr: Copying from %08x to %08x", CompressedKernelAddress, KernelBuffer));
memcpy(KernelBuffer, (PUCHAR)CompressedKernelAddress, BootParam->CompressedKernelSize);
//
// Decrypt the RAM kernel buffer in place
//
BldrEncDec(KernelBuffer, BootParam->CompressedKernelSize, DecryptionKey, 16);
//
// Decompress the decrypted RAM kernel buffer to the location kernel image
// needs to run from
//
BldrDecompress((ULONG_PTR)KernelBuffer, (ULONG_PTR)PsNtosImageBase,
BootParam->CompressedKernelSize);
//
// Fixup the pointer to the uncompressed .data with the actual load address
// of the ROM. If the ROM is loaded at the top of memory, then this doesn't
// affect the pointer.
//
((PXDATA_SECTION_HEADER)(((PIMAGE_DOS_HEADER)PsNtosImageBase)->e_res2))->PointerToRawData +=
BldrTopOfROMAddress;
}
MI_MEMORY
DIAMONDAPI
BldrAlloc(
ULONG NumberOfBytes
)
/*++
Routine Description:
Simple memory allocation used for limited known allocation
Arguments:
Return Value:
A pointer to where the memory was allocated
--*/
{
PVOID BaseAddress;
BaseAddress = (PVOID)(0x80000000 + BLDR_RELOCATED_ORIGIN + BLDR_BLOCK_SIZE +
BldrNumberOfBytesAllocated);
BldrNumberOfBytesAllocated += NumberOfBytes;
BldrPrint(("Bldr: Allocate %d bytes at %x", NumberOfBytes, BaseAddress));
return BaseAddress;
}
VOID
DIAMONDAPI
BldrFree(
MI_MEMORY pointer
)
{
// we don't need to free the memory
}
BOOLEAN
BldrDecompress(
IN ULONG FromAddress,
IN ULONG ToAddress,
IN ULONG UncompressedSize
)
{
PLZXBOX_BLOCK Block;
ULONG Source;
ULONG Dest;
UINT SourceSize;
LZXDECOMPRESS Decomp;
UINT DestSize;
ULONG BytesDecompressed;
LDI_CONTEXT_HANDLE Handle;
BOOLEAN Success = FALSE;
BldrPrint(("Bldr: Decompressing from=0x%08X to=0x%08X bytes=%d",
FromAddress, ToAddress, UncompressedSize));
//
// Initialize decompression engine
//
Decomp.fCPUtype = LDI_CPU_80386;
Decomp.WindowSize = LZX_WINDOW_SIZE;
SourceSize = LZX_CHUNK_SIZE;
if (LDICreateDecompression(&SourceSize, &Decomp, BldrAlloc, BldrFree, &DestSize,
&Handle, NULL,NULL,NULL,NULL,NULL) != MDI_ERROR_NO_ERROR) {
BldrPrint(("Bldr: Failed to init decompression engine"));
return Success;
}
Source = FromAddress;
Dest = ToAddress;
for (;;) {
//
// Read the compression block
//
Block = (PLZXBOX_BLOCK)Source;
Source += sizeof(LZXBOX_BLOCK);
//
// Perform decompression
//
BytesDecompressed = Block->UncompressedSize;
if (LDIDecompress(Handle, (PVOID)Source, Block->CompressedSize, (PVOID)Dest,
&BytesDecompressed) != MDI_ERROR_NO_ERROR) {
goto CleanupAndExit;
}
//
// Advance the pointers
//
Source += Block->CompressedSize;
Dest += Block->UncompressedSize;
//
// Check to see if we are done
//
if ((Source - FromAddress) >= UncompressedSize) {
break;
}
}
Success = TRUE;
CleanupAndExit:
(VOID)LDIDestroyDecompression(Handle);
BldrPrint(("Bldr: Decompression %s", (Success ? "successful" : "failed")));
return Success;
}
VOID
BldrShutdownSystem(
VOID
)
/*++
Routine Description:
Shutdown the system.
Arguments:
None.
Return Value:
None.
--*/
{
BldrPrint(("Bldr: *** Shutdown occurred"));
#ifdef RETAILXM3
//
// Flat line the clocks
//
_outpd(PCI_TYPE1_ADDR_PORT, 0x8000036C);
_outpd(PCI_TYPE1_DATA_PORT, 0x01000000);
#endif
__asm {
hlt
}
}
VOID
BldrEncDec(
IN PUCHAR MsgData,
IN ULONG MsgDataLen,
IN const UCHAR* Key,
IN ULONG KeyBytes
)
/*++
Routine Description:
Encrypt or decrypt the given data buffer in-place using RC4 which is symmetric algorithm.
Arguments:
Return Value:
None
--*/
{
struct RC4_KEYSTRUCT rc4KeyCtl;
BldrPrint(("Bldr: Decrypting %d bytes at %x", MsgDataLen, MsgData));
rc4_key(&rc4KeyCtl, (UINT)KeyBytes, (PUCHAR)Key);
rc4(&rc4KeyCtl, (UINT)MsgDataLen, MsgData);
}