|
|
/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
spdriver.c
Abstract:
Device-driver interface routines for text setup.
Author:
Ted Miller (tedm) 11-August-1993
Revision History:
--*/
#include "spprecmp.h"
#pragma hdrstop
#include "spcmdcon.h"
PSETUP_COMMUNICATION CommunicationParams;
PVOID RequestReadyEventObjectBody; PVOID RequestReadyEventWaitObjectBody;
PVOID RequestServicedEventObjectBody; PVOID RequestServicedEventWaitObjectBody;
PEPROCESS UsetupProcess; PAUTOCHK_MSG_PROCESSING_ROUTINE pAutochkCallbackRoutine;
SYSTEM_BASIC_INFORMATION SystemBasicInfo;
BOOLEAN AutochkRunning = FALSE; BOOLEAN AutofrmtRunning = FALSE;
NTSTATUS SetupOpenCreate( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
NTSTATUS SetupClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
NTSTATUS SetupDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
VOID SetupUnload( IN PDRIVER_OBJECT DriverObject );
NTSTATUS SpInitialize0( IN PDRIVER_OBJECT DriverObject );
BOOLEAN pSpVerifyEventWaitable( IN HANDLE hEvent, OUT PVOID *EventObjectBody, OUT PVOID *EventWaitObjectBody );
ULONG DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )
/*++
Routine Description:
This routine initializes the setup driver.
Arguments:
DriverObject - Pointer to driver object created by system.
RegistryPath - Pointer to the Unicode name of the registry path for this driver.
Return Value:
The function value is the final status from the initialization operation.
--*/
{ NTSTATUS status; UNICODE_STRING unicodeString; PDEVICE_OBJECT deviceObject;
//
// Create exclusive device object.
//
RtlInitUnicodeString(&unicodeString,DD_SETUP_DEVICE_NAME_U);
status = IoCreateDevice( DriverObject, 0, &unicodeString, FILE_DEVICE_UNKNOWN, 0, FALSE, &deviceObject );
if(!NT_SUCCESS(status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to create device object (%lx)\n",status)); return(status); }
//
// Set up device driver entry points.
//
//DriverObject->DriverStartIo = NULL;
DriverObject->DriverUnload = SetupUnload; DriverObject->MajorFunction[IRP_MJ_CREATE] = SetupOpenCreate; DriverObject->MajorFunction[IRP_MJ_CLOSE] = SetupClose; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SetupDeviceControl; //DriverObject->MajorFunction[IRP_MJ_CLEANUP] = NULL;
return((ULONG)SpInitialize0(DriverObject)); }
VOID SetupUnload( IN PDRIVER_OBJECT DriverObject )
/*++
Routine Description:
This routine is the setup driver unload routine.
Arguments:
DriverObject - Pointer to driver object.
Return Value:
None.
--*/
{ //
// Delete the device object.
//
IoDeleteDevice(DriverObject->DeviceObject);
return; }
NTSTATUS SetupOpenCreate( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
/*++
Routine Description:
This routine is the dispatch routine for open/create. When the setup device is opened, text setup begins. The open/create does not complete until text setup is done.
Arguments:
DeviceObject - Pointer to class device object.
Irp - Pointer to the request packet.
Return Value:
Status is returned.
--*/
{
Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = FILE_OPENED; IoCompleteRequest(Irp, IO_NO_INCREMENT);
return(STATUS_SUCCESS); }
NTSTATUS SetupClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
/*++
Routine Description:
This routine is the dispatch routine for close. Close requests are completed here.
Arguments:
DeviceObject - Pointer to class device object.
Irp - Pointer to the request packet.
Return Value:
Status is returned.
--*/
{ //
// Complete the request and return status.
//
Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT);
return(STATUS_SUCCESS); }
NTSTATUS SetupDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PIO_STACK_LOCATION IrpSp; NTSTATUS Status; PSETUP_START_INFO SetupStartInfo; BOOLEAN b;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
switch(IrpSp->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_SETUP_START:
//
// Make sure we've been passed a suitable input buffer.
//
if(IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(SETUP_START_INFO)) {
Status = STATUS_INVALID_PARAMETER;
} else {
//
// Save away relevent fields in the setup information
// parameters.
//
SetupStartInfo = (PSETUP_START_INFO)Irp->AssociatedIrp.SystemBuffer;
ResourceImageBase = SetupStartInfo->UserModeImageBase;
CommunicationParams = SetupStartInfo->Communication;
UsetupProcess = PsGetCurrentProcess(); // KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: usetup process = %lx \n", UsetupProcess));
b = pSpVerifyEventWaitable( SetupStartInfo->RequestReadyEvent, &RequestReadyEventObjectBody, &RequestReadyEventWaitObjectBody );
if(!b) { Status = STATUS_INVALID_HANDLE; break; }
b = pSpVerifyEventWaitable( SetupStartInfo->RequestServicedEvent, &RequestServicedEventObjectBody, &RequestServicedEventWaitObjectBody );
if(!b) { Status = STATUS_INVALID_HANDLE; ObDereferenceObject(RequestReadyEventObjectBody); break; }
SystemBasicInfo = SetupStartInfo->SystemBasicInfo;
//
// Start Setup going.
//
SpStartSetup();
ObDereferenceObject(RequestReadyEventObjectBody); ObDereferenceObject(RequestServicedEventObjectBody);
Status = STATUS_SUCCESS; } break;
case IOCTL_SETUP_FMIFS_MESSAGE:
//
// Make sure that we were not called by usetup.exe.
// Make sure we've been passed a suitable input buffer.
//
if( (UsetupProcess == PsGetCurrentProcess()) || (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(SETUP_FMIFS_MESSAGE)) ) {
ASSERT( UsetupProcess != PsGetCurrentProcess() );
Status = STATUS_INVALID_PARAMETER;
} else { PSETUP_FMIFS_MESSAGE SetupFmifsMessage; SetupFmifsMessage = (PSETUP_FMIFS_MESSAGE)Irp->AssociatedIrp.SystemBuffer;
Status = STATUS_SUCCESS; //
// If there's a callback override specified, use it.
//
if(pAutochkCallbackRoutine) { Status = pAutochkCallbackRoutine(SetupFmifsMessage); break; }
//
// If there is a gauge defined, then process the message.
// Otherwise, don't bother processing it.
//
if( UserModeGauge != NULL ) { //
// Save away relevent fields in the setup information
// parameters.
//
// KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: caller process = %lx \n", PsGetCurrentProcess()));
// KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: FmIfsPacketType = %d \n", SetupFmifsMessage->FmifsPacketType));
//
// Find out if the FmIfs packet is one of those that we care about
//
if( SetupFmifsMessage->FmifsPacketType == FmIfsPercentCompleted ) { ULONG PercentCompleted;
// KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: PercentCompleted = %d \n", ((PFMIFS_PERCENT_COMPLETE_INFORMATION)SetupFmifsMessage->FmifsPacket)->PercentCompleted ));
//
// Save the percentage in a local variable, before we attach to
// usetup address space
//
PercentCompleted = ((PFMIFS_PERCENT_COMPLETE_INFORMATION)SetupFmifsMessage->FmifsPacket)->PercentCompleted;
//
// We need to adjust the percentage, depending on the partition
// (System or NT partition) that is currently being accessed.
// We use this because we want to use only one gauge to display
// the progress on both System and NT partitions.
// When autochk is running, 50% of the gauge will be used to
// display the progress on the system partition, and the remaining
// 50% will be used for the NT partition.
// Note that when there are two partitions, the range of the
// gauge is initialized as 200. When there is only one partition
// the range is initialized as 100.
// Note also that when autofmt is running, we always set CurrentDiskIndex
// to 0.
//
ASSERT( CurrentDiskIndex <= 1 ); PercentCompleted += 100*CurrentDiskIndex;
//
// Attach to usetup.exe address space
//
KeAttachProcess( (PKPROCESS)UsetupProcess );
//
// Call the function that processes FmIfsPackets
//
// KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Calling ProcessFmIfsPacket \n"));
// Status = ProcessFmIfsPacket( SetupFmifsMessage );
SpFillGauge( UserModeGauge, PercentCompleted );
if (AutochkRunning) { SendSetupProgressEvent(PartitioningEvent, ValidatePartitionEvent, &PercentCompleted); } else if (AutofrmtRunning) { SendSetupProgressEvent(PartitioningEvent, FormatPartitionEvent, &PercentCompleted); }
//
// Now that the message was processed, detach from usetup.exe
// address space
//
KeDetachProcess(); } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: FmIfsPacketType = %d \n", SetupFmifsMessage->FmifsPacketType)); } } } break;
default:
Status = STATUS_INVALID_PARAMETER; break; }
Irp->IoStatus.Status = Status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp,IO_NO_INCREMENT);
return(Status); }
VOID SpSetAutochkCallback( IN PAUTOCHK_MSG_PROCESSING_ROUTINE AutochkCallbackRoutine ) { pAutochkCallbackRoutine = AutochkCallbackRoutine; }
BOOLEAN pSpVerifyEventWaitable( IN HANDLE hEvent, OUT PVOID *EventObjectBody, OUT PVOID *EventWaitObjectBody ) { POBJECT_HEADER ObjectHeader; NTSTATUS Status;
//
// Reference the event and verify that it is waitable.
//
Status = ObReferenceObjectByHandle( hEvent, EVENT_ALL_ACCESS, NULL, KernelMode, EventObjectBody, NULL );
if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to reference event object (%lx)\n",Status)); return(FALSE); }
ObjectHeader = OBJECT_TO_OBJECT_HEADER(*EventObjectBody); if(!ObjectHeader->Type->TypeInfo.UseDefaultObject) {
*EventWaitObjectBody = (PVOID)((PCHAR)(*EventObjectBody) + (ULONG_PTR)ObjectHeader->Type->DefaultObject);
} else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: event object not waitable!\n")); ObDereferenceObject(*EventObjectBody); return(FALSE); }
return(TRUE); }
NTSTATUS SpInvokeUserModeService( VOID ) { NTSTATUS Status;
//
// Set the event indicating that the communication buffer is
// ready for the user-mode process. Because this is a synchronization
// event, it automatically resets after releasing the waiting
// user-mode thread. Note that we specify WaitNext to prevent the
// race condition between setting this synchronization event and
// waiting on the next one.
//
KeSetEvent(RequestReadyEventObjectBody,EVENT_INCREMENT,TRUE);
//
// Wait for the user-mode process to indicate that it is done
// processing the request. We wait in user mode so that we can be
// interrupted if necessary -- say, by an exit APC.
//
Status = KeWaitForSingleObject( RequestServicedEventWaitObjectBody, Executive, UserMode, FALSE, NULL );
if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: KeWaitForSingleObject returns %lx\n",Status)); return(Status); }
//
// Return the status returned by the user mode process.
//
return(CommunicationParams->u.Status); }
NTSTATUS SpExecuteImage( IN PWSTR ImagePath, OUT PULONG ReturnStatus, OPTIONAL IN ULONG ArgumentCount, ... ) { va_list arglist; ULONG i; PSERVICE_EXECUTE RequestBuffer; NTSTATUS Status;
//
// Locate the request buffer and set up the request number.
//
CommunicationParams->u.RequestNumber = SetupServiceExecute; RequestBuffer = (PSERVICE_EXECUTE)&CommunicationParams->Buffer;
//
// Determine the lcoations of the two strings that get copied
// into the request buffer for this service.
//
RequestBuffer->FullImagePath = RequestBuffer->Buffer; RequestBuffer->CommandLine = RequestBuffer->FullImagePath + wcslen(ImagePath) + 1;
//
// Copy the image path into the request buffer.
//
wcscpy(RequestBuffer->FullImagePath,ImagePath);
//
// Move the arguments into the request buffer one by one
// starting with the image path.
//
wcscpy(RequestBuffer->CommandLine,ImagePath); va_start(arglist,ArgumentCount); for(i=0; i<ArgumentCount; i++) {
wcscat(RequestBuffer->CommandLine,L" "); wcscat(RequestBuffer->CommandLine,va_arg(arglist,PWSTR)); } va_end(arglist);
//
// Invoke the service.
//
Status = SpInvokeUserModeService();
//
// Set process's return status (if required)
//
if(NT_SUCCESS(Status) && ReturnStatus) { *ReturnStatus = RequestBuffer->ReturnStatus; }
return Status; }
NTSTATUS SpLoadUnloadKey( IN HANDLE TargetKeyRootDirectory, OPTIONAL IN HANDLE SourceFileRootDirectory, OPTIONAL IN PWSTR TargetKeyName, IN PWSTR SourceFileName OPTIONAL ) { //
// This was once a user-mode service but now the relevent apis
// are exported from the kernel so don't bother.
//
UNICODE_STRING KeyName,FileName; OBJECT_ATTRIBUTES ObjaKey,ObjaFile; NTSTATUS Status; BOOLEAN Loading; BOOLEAN bFileExists = FALSE;
//
// Loading if we have a source filename, otherwise unloading.
//
Loading = (BOOLEAN)(SourceFileName != NULL);
INIT_OBJA(&ObjaKey,&KeyName,TargetKeyName); ObjaKey.RootDirectory = TargetKeyRootDirectory;
if(Loading) {
INIT_OBJA(&ObjaFile,&FileName,SourceFileName); ObjaFile.RootDirectory = SourceFileRootDirectory;
//
// NOTE:ZwLoadKey(...) creates the file if does not exist
// so we need to check for the existence of the file
//
if (SpFileExists(SourceFileName, FALSE)) Status = ZwLoadKey(&ObjaKey,&ObjaFile); else Status = STATUS_NO_SUCH_FILE; } else { Status = ZwUnloadKey(&ObjaKey); }
if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: %wskey of %ws failed (%lx)\n", Loading ? L"load" : L"unload", TargetKeyName, Status )); }
return(Status); }
NTSTATUS SpDeleteKey( IN HANDLE KeyRootDirectory, OPTIONAL IN PWSTR Key ) { PSERVICE_DELETE_KEY RequestBuffer;
//
// Locate the request buffer and set up the request number.
//
CommunicationParams->u.RequestNumber = SetupServiceDeleteKey;
RequestBuffer = (PSERVICE_DELETE_KEY)&CommunicationParams->Buffer;
//
// Determine the lcoation of the strings that get copied
// into the request buffer for this service.
//
RequestBuffer->Key = RequestBuffer->Buffer;
//
// Copy the string into the request buffer.
//
wcscpy(RequestBuffer->Buffer,Key);
//
// Initialize the root directory fields.
//
RequestBuffer->KeyRootDirectory = KeyRootDirectory;
//
// Invoke the service.
//
return(SpInvokeUserModeService()); }
NTSTATUS SpQueryDirectoryObject( IN HANDLE DirectoryHandle, IN BOOLEAN RestartScan, IN OUT PULONG Context ) { PSERVICE_QUERY_DIRECTORY_OBJECT RequestBuffer; NTSTATUS Status;
CommunicationParams->u.RequestNumber = SetupServiceQueryDirectoryObject;
RequestBuffer = (PSERVICE_QUERY_DIRECTORY_OBJECT)&CommunicationParams->Buffer;
RequestBuffer->DirectoryHandle = DirectoryHandle; RequestBuffer->Context = *Context; RequestBuffer->RestartScan = RestartScan;
Status = SpInvokeUserModeService();
if(NT_SUCCESS(Status)) { *Context = RequestBuffer->Context; }
return(Status); }
NTSTATUS SpFlushVirtualMemory( IN PVOID BaseAddress, IN ULONG RangeLength ) { PSERVICE_FLUSH_VIRTUAL_MEMORY RequestBuffer;
CommunicationParams->u.RequestNumber = SetupServiceFlushVirtualMemory;
RequestBuffer = (PSERVICE_FLUSH_VIRTUAL_MEMORY)&CommunicationParams->Buffer;
RequestBuffer->BaseAddress = BaseAddress; RequestBuffer->RangeLength = RangeLength;
return(SpInvokeUserModeService()); }
VOID SpShutdownSystem( VOID ) { SendSetupProgressEvent(SetupCompletedEvent, ShutdownEvent, NULL);
CommunicationParams->u.RequestNumber = SetupServiceShutdownSystem;
SpInvokeUserModeService();
//
// Shouldn't get here, but just in case...
//
HalReturnToFirmware(HalRebootRoutine);
}
NTSTATUS SpLoadKbdLayoutDll( IN PWSTR Directory, IN PWSTR DllName, OUT PVOID *TableAddress ) { PSERVICE_LOAD_KBD_LAYOUT_DLL RequestBuffer; NTSTATUS Status;
CommunicationParams->u.RequestNumber = SetupServiceLoadKbdLayoutDll;
RequestBuffer = (PSERVICE_LOAD_KBD_LAYOUT_DLL)&CommunicationParams->Buffer;
wcscpy(RequestBuffer->DllName,Directory); SpConcatenatePaths(RequestBuffer->DllName,DllName);
Status = SpInvokeUserModeService();
if(NT_SUCCESS(Status)) { *TableAddress = RequestBuffer->TableAddress; }
return(Status); }
NTSTATUS SpLockUnlockVolume( IN HANDLE Handle, IN BOOLEAN LockVolume ) { PSERVICE_LOCK_UNLOCK_VOLUME RequestBuffer;
CommunicationParams->u.RequestNumber = (LockVolume)? SetupServiceLockVolume : SetupServiceUnlockVolume;
RequestBuffer = (PSERVICE_LOCK_UNLOCK_VOLUME)&CommunicationParams->Buffer;
RequestBuffer->Handle = Handle;
return(SpInvokeUserModeService()); }
NTSTATUS SpDismountVolume( IN HANDLE Handle ) { PSERVICE_DISMOUNT_VOLUME RequestBuffer;
CommunicationParams->u.RequestNumber = SetupServiceDismountVolume;
RequestBuffer = (PSERVICE_DISMOUNT_VOLUME)&CommunicationParams->Buffer;
RequestBuffer->Handle = Handle;
return(SpInvokeUserModeService()); }
NTSTATUS SpSetDefaultFileSecurity( IN PWSTR FileName ) { PSERVICE_DEFAULT_FILE_SECURITY RequestBuffer;
CommunicationParams->u.RequestNumber = SetupServiceSetDefaultFileSecurity;
RequestBuffer = (PSERVICE_DEFAULT_FILE_SECURITY)&CommunicationParams->Buffer;
wcscpy( RequestBuffer->FileName, FileName );
return(SpInvokeUserModeService()); }
NTSTATUS SpVerifyFileAccess( IN PWSTR FileName, IN ACCESS_MASK DesiredAccess ) { PSERVICE_VERIFY_FILE_ACCESS RequestBuffer;
CommunicationParams->u.RequestNumber = SetupServiceVerifyFileAccess;
RequestBuffer = (PSERVICE_VERIFY_FILE_ACCESS)&CommunicationParams->Buffer;
wcscpy( RequestBuffer->FileName, FileName ); RequestBuffer->DesiredAccess = DesiredAccess; return(SpInvokeUserModeService()); }
NTSTATUS SpCreatePageFile( IN PWSTR FileName, IN ULONG MinSize, IN ULONG MaxSize ) { PSERVICE_CREATE_PAGEFILE RequestBuffer;
CommunicationParams->u.RequestNumber = SetupServiceCreatePageFile;
RequestBuffer = (PSERVICE_CREATE_PAGEFILE)&CommunicationParams->Buffer;
wcscpy(RequestBuffer->FileName,FileName); RequestBuffer->MinSize.HighPart = 0; RequestBuffer->MinSize.LowPart = MinSize; RequestBuffer->MaxSize.HighPart = 0; RequestBuffer->MaxSize.LowPart = MaxSize;
return(SpInvokeUserModeService()); }
NTSTATUS SpGetFullPathName( IN OUT PWSTR FileName ) { PSERVICE_GETFULLPATHNAME RequestBuffer; NTSTATUS Status;
CommunicationParams->u.RequestNumber = SetupServiceGetFullPathName;
RequestBuffer = (PSERVICE_GETFULLPATHNAME)&CommunicationParams->Buffer;
wcscpy(RequestBuffer->FileName,FileName);
Status = SpInvokeUserModeService();
if(NT_SUCCESS(Status)) { wcscpy(FileName,RequestBuffer->NameOut); }
return(Status); }
|