471 lines
11 KiB
C
471 lines
11 KiB
C
/*++
|
||
|
||
Copyright (c) 1990-1998 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
xipdisp.c
|
||
|
||
Abstract:
|
||
|
||
This file implements functions for communicating with the XIP Disk Driver.
|
||
|
||
Most importantly this routine is used by the kernel to communicate
|
||
information about the location of the memory set aside for XIP.
|
||
|
||
Author:
|
||
|
||
Dave Probert (davepr) 2000/10/10
|
||
|
||
Environment:
|
||
|
||
kernel mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "exp.h"
|
||
#pragma hdrstop
|
||
|
||
#include "cpyuchr.h"
|
||
#include "fat.h"
|
||
#include "xip.h"
|
||
|
||
|
||
#if defined(_X86_)
|
||
|
||
typedef struct _XIP_CONFIGURATION {
|
||
XIP_BOOT_PARAMETERS BootParameters;
|
||
BIOS_PARAMETER_BLOCK BiosParameterBlock;
|
||
ULONG ClusterZeroPage;
|
||
} XIP_CONFIGURATION, *PXIP_CONFIGURATION;
|
||
|
||
PXIP_CONFIGURATION XIPConfiguration;
|
||
BOOLEAN XIPConfigured;
|
||
|
||
VOID
|
||
XIPInit(
|
||
PLOADER_PARAMETER_BLOCK LoaderBlock
|
||
);
|
||
|
||
PMEMORY_ALLOCATION_DESCRIPTOR
|
||
XIPpFindMemoryDescriptor(
|
||
PLOADER_PARAMETER_BLOCK LoaderBlock
|
||
);
|
||
|
||
|
||
#if defined(ALLOC_PRAGMA)
|
||
#pragma alloc_text(INIT, XIPInit)
|
||
#pragma alloc_text(INIT, XIPpFindMemoryDescriptor)
|
||
#pragma alloc_text(PAGE, XIPLocatePages)
|
||
#endif
|
||
|
||
|
||
VOID
|
||
XIPInit(
|
||
PLOADER_PARAMETER_BLOCK LoaderBlock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets up the boot parameter information for XIP Rom.
|
||
|
||
Arguments:
|
||
|
||
|
||
Environment:
|
||
|
||
Called only at INIT.
|
||
|
||
--*/
|
||
{
|
||
PMEMORY_ALLOCATION_DESCRIPTOR XIPMemoryDescriptor;
|
||
PPACKED_BOOT_SECTOR pboot;
|
||
BIOS_PARAMETER_BLOCK bios;
|
||
PHYSICAL_ADDRESS physicalAddress;
|
||
|
||
PCHAR Options;
|
||
|
||
PCHAR XIPBoot, XIPRom, XIPRam, XIPSize, XIPVerbose;
|
||
|
||
PCHAR sizestr;
|
||
ULONG nmegs = 0;
|
||
|
||
//
|
||
// Process the boot options. Really only need to know whether or not we are the boot device, and RAM or ROM.
|
||
// But the other checking is done for diagnostic purposes (at least in checked builds).
|
||
//
|
||
|
||
Options = LoaderBlock->LoadOptions;
|
||
if (!Options) {
|
||
return;
|
||
}
|
||
|
||
XIPBoot = strstr(Options, "XIPBOOT");
|
||
XIPRom = strstr(Options, "XIPROM=");
|
||
XIPRam = strstr(Options, "XIPRAM=");
|
||
XIPSize = strstr(Options, "XIPMEGS=");
|
||
XIPVerbose = strstr(Options, "XIPVERBOSE");
|
||
|
||
if (XIPVerbose) {
|
||
DbgPrint("\n\nXIP: debug timestamp at line %d in %s: <<<%s %s>>>\n\n\n", __LINE__, __FILE__, __DATE__, __TIME__);
|
||
}
|
||
|
||
XIPMemoryDescriptor = XIPpFindMemoryDescriptor(LoaderBlock);
|
||
|
||
if (!XIPMemoryDescriptor) {
|
||
return;
|
||
}
|
||
|
||
if (XIPVerbose) {
|
||
DbgPrint("XIP: Base %x Count %x\n", XIPMemoryDescriptor->BasePage, XIPMemoryDescriptor->PageCount);
|
||
}
|
||
|
||
if (XIPRom && XIPRam) {
|
||
return;
|
||
}
|
||
|
||
if (!XIPRom && !XIPRam) {
|
||
return;
|
||
}
|
||
|
||
sizestr = XIPSize? strchr(XIPSize, '=') : NULL;
|
||
if (sizestr) {
|
||
nmegs = (ULONG) atol(sizestr+1);
|
||
}
|
||
|
||
if (nmegs == 0) {
|
||
return;
|
||
}
|
||
|
||
if (XIPVerbose && XIPMemoryDescriptor->PageCount != nmegs * 1024*1024 / PAGE_SIZE) {
|
||
DbgPrint("XIPMEGS=%d in boot options is %d pages, but only %d pages were allocated by NTLDR\n",
|
||
nmegs * 1024*1024 / PAGE_SIZE,
|
||
XIPMemoryDescriptor->PageCount * PAGE_SIZE);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Get info from FAT16 boot sector.
|
||
// We only need to map one page, so we've allocated an MDL on the stack.
|
||
//
|
||
|
||
//
|
||
// Temporarily map the page with the boot sector so we can unpack it.
|
||
//
|
||
|
||
physicalAddress.QuadPart = XIPMemoryDescriptor->BasePage * PAGE_SIZE;
|
||
|
||
pboot = (PPACKED_BOOT_SECTOR) MmMapIoSpace(physicalAddress, PAGE_SIZE, MmCached);
|
||
if (!pboot) {
|
||
return;
|
||
}
|
||
|
||
FatUnpackBios(&bios, &pboot->PackedBpb);
|
||
|
||
MmUnmapIoSpace (pboot, PAGE_SIZE);
|
||
|
||
//
|
||
// Check Bios parameters
|
||
//
|
||
if (bios.BytesPerSector != 512
|
||
|| FatBytesPerCluster(&bios) != PAGE_SIZE
|
||
|| FatFileAreaLbo(&bios) & (PAGE_SIZE-1)) {
|
||
|
||
if (XIPVerbose) {
|
||
DbgPrint("XIP: Malformed FAT Filesystem: BytesPerSector=%x BytesPerCluster=%x ClusterZeroOffset=%x\n",
|
||
bios.BytesPerSector, FatBytesPerCluster(&bios), FatFileAreaLbo(&bios));
|
||
}
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Boot.ini parameters and Bios parameters were ok, so initialize the XIP configuration.
|
||
//
|
||
|
||
XIPConfiguration = ExAllocatePoolWithTag (NonPagedPool, sizeof(*XIPConfiguration), XIP_POOLTAG);
|
||
if (!XIPConfiguration) {
|
||
return;
|
||
}
|
||
|
||
XIPConfigured = TRUE;
|
||
|
||
XIPConfiguration->BiosParameterBlock = bios;
|
||
XIPConfiguration->BootParameters.SystemDrive = XIPBoot? TRUE : FALSE;
|
||
XIPConfiguration->BootParameters.ReadOnly = XIPRom? TRUE : FALSE;
|
||
|
||
XIPConfiguration->BootParameters.BasePage = XIPMemoryDescriptor->BasePage;
|
||
XIPConfiguration->BootParameters.PageCount = XIPMemoryDescriptor->PageCount;
|
||
|
||
XIPConfiguration->ClusterZeroPage = FatFileAreaLbo(&bios) >> PAGE_SHIFT;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
XIPDispatch(
|
||
IN XIPCMD Command,
|
||
IN OUT PVOID ParameterBuffer OPTIONAL,
|
||
IN ULONG BufferSize
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets up the boot parameter information for XIP Rom.
|
||
|
||
Arguments:
|
||
|
||
|
||
Environment:
|
||
|
||
Only to be called at INIT time.
|
||
|
||
--*/
|
||
{
|
||
ULONG sz;
|
||
|
||
if (!XIPConfiguration) {
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
switch (Command) {
|
||
case XIPCMD_GETBOOTPARAMETERS:
|
||
sz = sizeof(XIPConfiguration->BootParameters);
|
||
if (sz != BufferSize) {
|
||
break;
|
||
}
|
||
RtlCopyMemory(ParameterBuffer, &XIPConfiguration->BootParameters, sz);
|
||
return STATUS_SUCCESS;
|
||
|
||
case XIPCMD_GETBIOSPARAMETERS:
|
||
sz = sizeof(XIPConfiguration->BiosParameterBlock);
|
||
if (sz != BufferSize) {
|
||
break;
|
||
}
|
||
RtlCopyMemory(ParameterBuffer, &XIPConfiguration->BiosParameterBlock, sz);
|
||
return STATUS_SUCCESS;
|
||
|
||
case XIPCMD_NOOP:
|
||
if (BufferSize) {
|
||
break;
|
||
}
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
////////////////////////////
|
||
// DEBUG
|
||
int XIPlocate_noisy = 0;
|
||
int XIPlocate_breakin = 0;
|
||
int XIPlocate_disable = 0;
|
||
struct {
|
||
int attempted;
|
||
int bounced;
|
||
int succeeded;
|
||
int no_irp;
|
||
int no_devobj;
|
||
int no_contig;
|
||
int no_endofdisk;
|
||
} XIPlocatecnt;
|
||
////////////////////////////
|
||
|
||
NTSTATUS
|
||
XIPLocatePages(
|
||
IN PFILE_OBJECT FileObject,
|
||
OUT PPHYSICAL_ADDRESS PhysicalAddress
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Return the requested XIP physical address. If the requested page range
|
||
is not contiguous in the file, or there is any other problem, the routine fails.
|
||
|
||
Arguments:
|
||
|
||
FileObject - the file of interest
|
||
|
||
PhysicalAddress - used to return the physical address of the start of the file in ROM.
|
||
|
||
Environment:
|
||
|
||
Kernel
|
||
|
||
--*/
|
||
{
|
||
STARTING_VCN_INPUT_BUFFER startingvcn;
|
||
RETRIEVAL_POINTERS_BUFFER retrbuf;
|
||
IO_STATUS_BLOCK iostatus;
|
||
|
||
PDEVICE_OBJECT deviceObject;
|
||
PIO_STACK_LOCATION irpSp;
|
||
NTSTATUS status;
|
||
KEVENT event;
|
||
PIRP irp;
|
||
|
||
PFN_NUMBER firstPage, numberOfPages;
|
||
PDEVICE_OBJECT xipDeviceObject;
|
||
|
||
xipDeviceObject = FileObject->DeviceObject;
|
||
|
||
if (!XIPConfiguration) {
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
if (!xipDeviceObject || !(xipDeviceObject->Flags & DO_XIP)) {
|
||
return STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
////////////////
|
||
XIPlocatecnt.attempted++;
|
||
////////////////
|
||
|
||
startingvcn.StartingVcn.QuadPart = 0;
|
||
deviceObject = IoGetRelatedDeviceObject(FileObject);
|
||
|
||
if (!deviceObject) {
|
||
////////////////
|
||
XIPlocatecnt.no_devobj++;
|
||
////////////////
|
||
return STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
//
|
||
// Ask fat for the retrieval pointers (relative to cluster 0).
|
||
//
|
||
irp = IoBuildDeviceIoControlRequest(
|
||
FSCTL_GET_RETRIEVAL_POINTERS,
|
||
deviceObject,
|
||
&startingvcn,
|
||
sizeof(startingvcn),
|
||
&retrbuf,
|
||
sizeof(retrbuf),
|
||
FALSE,
|
||
&event,
|
||
&iostatus);
|
||
if (!irp) {
|
||
////////////////
|
||
XIPlocatecnt.no_irp++;
|
||
////////////////
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
irp->Flags |= IRP_SYNCHRONOUS_API;
|
||
irp->Tail.Overlay.OriginalFileObject = FileObject;
|
||
|
||
irpSp = IoGetNextIrpStackLocation( irp );
|
||
irpSp->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
|
||
irpSp->MinorFunction = IRP_MN_USER_FS_REQUEST;
|
||
irpSp->FileObject = FileObject;
|
||
|
||
//
|
||
// Take out another reference to the file object to match I/O completion will deref.
|
||
//
|
||
|
||
ObReferenceObject( FileObject );
|
||
|
||
//
|
||
// Do the FSCTL
|
||
//
|
||
|
||
KeInitializeEvent( &event, NotificationEvent, FALSE );
|
||
status = IoCallDriver( deviceObject, irp );
|
||
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject( &event, Suspended, KernelMode, FALSE, NULL );
|
||
status = iostatus.Status;
|
||
}
|
||
|
||
if (!NT_SUCCESS(iostatus.Status)
|
||
|| retrbuf.ExtentCount != 1
|
||
|| retrbuf.Extents[0].Lcn.HighPart
|
||
|| retrbuf.Extents[0].NextVcn.HighPart
|
||
|| retrbuf.StartingVcn.QuadPart != 0L) {
|
||
|
||
////////////////
|
||
XIPlocatecnt.no_contig++;
|
||
////////////////
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
firstPage = XIPConfiguration->BootParameters.BasePage
|
||
+ XIPConfiguration->ClusterZeroPage
|
||
+ retrbuf.Extents[0].Lcn.LowPart;
|
||
|
||
numberOfPages = retrbuf.Extents[0].NextVcn.LowPart;
|
||
|
||
if (firstPage + numberOfPages > XIPConfiguration->BootParameters.BasePage
|
||
+ XIPConfiguration->BootParameters.PageCount) {
|
||
|
||
XIPlocatecnt.no_endofdisk++;
|
||
return STATUS_DISK_CORRUPT_ERROR;
|
||
}
|
||
|
||
////////////////
|
||
////////////////
|
||
if (XIPlocate_noisy || XIPlocate_breakin) {
|
||
DbgPrint("Break top of XIPLocatePages. bounced=%x attempted=%x succeeded=%x\n"
|
||
" %x nt!XIPlocate_disable %s\n"
|
||
" %x nt!XIPlocate_breakin %s\n"
|
||
" %x nt!XIPConfiguration\n"
|
||
" Would have returned address %x (npages was %x)\n",
|
||
XIPlocatecnt.bounced, XIPlocatecnt.attempted, XIPlocatecnt.succeeded,
|
||
&XIPlocate_disable, XIPlocate_disable? "DISABLED" : "enabled",
|
||
&XIPlocate_breakin, XIPlocate_breakin? "WILL BREAK" : "no break",
|
||
XIPConfiguration, firstPage, numberOfPages);
|
||
if (XIPlocate_breakin) {
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
if (XIPlocate_disable) {
|
||
XIPlocatecnt.bounced++;
|
||
return STATUS_DEVICE_OFF_LINE;
|
||
}
|
||
XIPlocatecnt.succeeded++;
|
||
////////////////
|
||
////////////////
|
||
PhysicalAddress->QuadPart = firstPage << PAGE_SHIFT;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
//
|
||
// Find the XIP memory descriptor
|
||
// Called only at INIT.
|
||
//
|
||
PMEMORY_ALLOCATION_DESCRIPTOR
|
||
XIPpFindMemoryDescriptor(
|
||
IN PLOADER_PARAMETER_BLOCK LoaderBlock
|
||
)
|
||
{
|
||
PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
|
||
PLIST_ENTRY NextMd;
|
||
|
||
for (NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
|
||
NextMd != &LoaderBlock->MemoryDescriptorListHead;
|
||
NextMd = MemoryDescriptor->ListEntry.Flink
|
||
)
|
||
{
|
||
MemoryDescriptor = CONTAINING_RECORD(NextMd,
|
||
MEMORY_ALLOCATION_DESCRIPTOR,
|
||
ListEntry);
|
||
|
||
if (MemoryDescriptor->MemoryType == LoaderXIPRom) {
|
||
|
||
return MemoryDescriptor;
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
#endif //__X86__
|