Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1245 lines
31 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
simbad.c (formerly simbad.cxx)
Abstract:
This module contains the main function of SimBad, which
allows the user to to ask the device driver to simulate
bad sectors.
Author:
Bill McJohn (billmc) 16-Aug-1991
Lars Opstad (a-larso) 6-Feb-1993
Environment:
ULIB, User Mode
Revision History:
4.Apr.92 - BobRi - Minor fix to list function (device control interface)
9.Apr.92 - BobRi - Modified (with BillMc's help) to support the access
triggers on when block numbers are to fail.
29.Jan.93 - LarsOp - Major changes to change to allow redirected console
input. This required lots of changes because of
dependencies on cxx libraries -> convert to C.
Also added functionality on Sanjay's request for
supporting file input with multiple lists of bad
sectors.
5.Feb.93 - Larsop - Added new function - orphan device. Any accesses to
the indicated partition will fail with a status of
STATUS_IO_DEVICE_ERROR.
20.May.93 - mglass - Add support for VERIFY access type. This will allow
SIMBAD to test the format utiltiy.
21.Jul.93 - mglass - Define access bit to fail attempt to reassign blocks.
12.May.94 - Venkat - Added code to drop of writes to DISK (CHKDSK testing)
22.Nov.94 - kpeery - Added code to force hardware reset (for restarts)
--*/
#include "stdlib.h"
#include "stdio.h"
#include "nt.h"
#include "ntrtl.h"
#include "ntdddisk.h"
#include <simbad.h>
//
// Constants for command line arguments
//
#define HD_SPEC 'h'
#define PART_SPEC 'p'
#define LIST 'l'
#define ZERO_OFF 'z'
#define MAP 'm'
#define ADD_ALL 'a'
#define ADD_READ 'r'
#define ADD_WRITE 'w'
#define ADD_VERIFY 'v'
#define DO_NOT_MAP 'x'
#define ENABLE 'e'
#define DISABLE 'd'
#define ORPHAN 'o'
#define CLEAR 'c'
#define FROM_FILE 'f'
#define RANDOM 'n'
#define BUGCHECK 'b'
#define FIRMWARE_RESET 't'
#define MAX_DEV_NAME 128
char devname[MAX_DEV_NAME]="\\Device\\HardDisk0\\Partition1";
char *filename;
WCHAR whattodo=LIST;
ULONG Seed, accessType=0;
int hdnum=0, partnum=1;
ULONG sectors[MAXIMUM_SIMBAD_SECTORS];
VOID
InputSectorList (
OUT PULONG sectors,
OUT PULONG count
)
/*++
Routine Description:
This function reads the sector list from stdin. The list is
terminated by end of file (max bad sectors entered).
Arguments:
sectors -- receives array of sectors
count -- receives number of sectors read from stdin
Return value:
None.
--*/
{
//
// loop until end-of-file or count exceeds max sectors
//
while (!feof(stdin) && (*count) < MAXIMUM_SIMBAD_SECTORS) {
fscanf(stdin, "%lx", &(sectors[(*count)++]));
} // while
//
// if count is less than MAX_BAD_SECTORS,
// the last read was an EOF => decrement count.
//
if (*count < MAXIMUM_SIMBAD_SECTORS) {
(*count)--;
} // if
} // InputSectorList()
VOID
ShowUsage (
)
/*++
Routine Description:
This function displays command line options for Simbad.
Arguments:
None.
Return value:
None.
--*/
{
printf("\nUsage: Simbad DeviceSpecifier /command /options\n"
"or Simbad /f file /options\n"
"\nwhere DeviceSpecifier is in one of the following formats:\n"
"\t\\Device\\HardDisk#\\Partition#\n"
"\t\\DosDevices\\C:\n"
"\t/h <hard disk #> /p <partition #>\n"
"\nand command is one of the following:\n"
"\ta Fail all access (enter sector list)\n"
"\tr Fail read access (enter sector list)\n"
"\tw Fail write access (enter sector list)\n"
"\tv Fail verify access (enter sector list)\n"
"\to Orphan entire device (return DEVICE FAILURE)\n"
"\tn Dropping writes randomly\n"
"\tb BugChecks the system\n"
"\tt Reset the system\n"
"\te Enable (previously entered) bad sectors\n"
"\td Disable (previously enabled) bad sectors\n"
"\tl List bad sectors\n"
"\tc Clear bad sectors\n"
"\nand options includes any of the following:\n"
"\tm Pass block reassignments to lower driver\n"
"\tz Failures report zero offset\n"
"\tx Fail block reassignment\n"
"\nNote: /h and /p have been added. The following cmd lines are equivalent:\n\n"
"\tSimbad \\Device\\HardDisk0\\Partition1 /l\n"
"\tSimbad /h 0 /p 1 /l\n"
"\nThe format of the file used with the /f option is as follows:\n\n"
"\\Device\\HardDisk#\\Partition#\n"
"R:0 8 a ...\n"
"W:...\n"
"A:...\n\n"
"This file can contain lists for multiple devices.\n"
"The bad sector emulation is not enabled until the /e option is entered.\n"
);
exit(-1);
} // ShowUsage();
VOID
ParseCmdArgs (
IN int argc,
IN char *argv[]
)
/*++
Routine Description:
This function sets some global variables based on the command line
arguments given to main (and passed here).
The globals that are set are as follows:
devname -- name of device to simulate bad sectors on
accessType -- fail on READ or WRITE, reassign, zero offset
whattodo -- which action (add, list, enable/disable, readfromfile)
filename -- which filename to use for FROM_FILE action
Arguments:
argc -- supplies number of arguments
argv -- supplies list of arguments
Return value:
None.
--*/
{
int i=1;
//
// if no arguments, just show usage.
//
if (argc==1) {
ShowUsage();
} // if
//
// check each argument
//
while (i<argc) {
//
// check first character
//
switch (argv[i][0]) {
//
// if first character is - or /, it is a switch.
//
case '-':
case '/':
//
// switch value is the second character
//
switch (tolower(argv[i][1])) {
//
// specify harddisk, sprintf devname
//
case HD_SPEC:
hdnum=atoi(argv[++i]);
sprintf(devname,
"\\Device\\HardDisk%d\\Partition%d",
hdnum,
partnum);
i++;
break;
//
// specify partition, sprintf devname
//
case PART_SPEC:
partnum=atoi(argv[++i]);
sprintf(devname,
"\\Device\\HardDisk%d\\Partition%d",
hdnum,
partnum);
i++;
break;
//
// set action to FROM_FILE and set filename
//
case FROM_FILE:
whattodo=argv[i++][1];
filename=argv[i++];
break;
//
// set zero offset bit in accessMode
//
// This means a failed io will return the beginning
// of the io instead of the failing sector.
//
case ZERO_OFF:
accessType|=SIMBAD_ACCESS_ERROR_ZERO_OFFSET;
whattodo=ADD_ALL;
i++;
break;
//
// set reassign bit in accessMode
//
// This allows simbad to reassign sectors on this device.
// Basically, simbad just removes the sector from the list
// on a reassign request.
//
case MAP:
accessType|=SIMBAD_ACCESS_CAN_REASSIGN_SECTOR;
whattodo=ADD_ALL;
i++;
break;
//
// Fail device control requests to reassign a bad block.
//
case DO_NOT_MAP:
accessType|=SIMBAD_ACCESS_FAIL_REASSIGN_SECTOR;
whattodo=ADD_ALL;
i++;
break;
//
// set fail-on-read bit in access mode (action is ADD)
//
case ADD_READ:
accessType|=SIMBAD_ACCESS_READ;
whattodo=ADD_ALL;
i++;
break;
//
// set fail-on-write bit in access mode (action is ADD)
//
case ADD_WRITE:
accessType|=SIMBAD_ACCESS_WRITE;
whattodo=ADD_ALL;
i++;
break;
//
// set fail-on-verify bit in access mode (action is ADD)
//
case ADD_VERIFY:
accessType|=SIMBAD_ACCESS_VERIFY;
whattodo=ADD_ALL;
i++;
break;
//
// set fail-on-read and fail-on-write bit in access mode
//
// Since ADD_ALL is the intended action, fall through to
// the next case which just sets the action to whatever
// character the switch is.
//
case ADD_ALL:
accessType|=(SIMBAD_ACCESS_READ|SIMBAD_ACCESS_WRITE|SIMBAD_ACCESS_VERIFY);
//
// NOTE: break intentionally left out (fall through to next)
//
//
// for list, enable, disable and clear, the action is the
// switch value
//
case LIST:
case ORPHAN:
case BUGCHECK:
case FIRMWARE_RESET:
case ENABLE:
case DISABLE:
case CLEAR:
whattodo=argv[i++][1];
break;
case RANDOM:
whattodo=argv[i++][1];
if ( i< argc ){
sscanf( argv[i++],"%lu", &Seed);
}else{
Seed=100;
}
break;
//
// any other switch, show usage
//
default:
ShowUsage();
} // switch (second character)
break;
//
// if first char is \, it is a device name
//
case '\\':
strcpy(devname,argv[i++]);
break;
//
// anything else, show usage
//
default:
ShowUsage();
} // switch (first character)
} // while
} // ParseCmdArgs()
//
// Some of this code was taken directly from simbad.cxx (older version)
// and PLBN needed to be defined for some of the older routines.
//
#define PLBN PULONG
BOOLEAN
OpenDrive(
IN char *psz_string,
OUT PHANDLE _phandle
)
/*++
Routine Description:
This routine opens the specified drive and passes back the handle.
Arguments:
psz_string -- supplies the device name (format "\harddiskX\partitionY")
_phandle -- receives the handle to the opened device
Return value:
Returns whether the open was successful.
--*/
{
OBJECT_ATTRIBUTES oa;
IO_STATUS_BLOCK status_block;
NTSTATUS _last_status;
ANSI_STRING ans_string;
UNICODE_STRING uni_string;
//
// Build unicode string
//
RtlInitAnsiString(&ans_string, psz_string);
RtlAnsiStringToUnicodeString(&uni_string, &ans_string, TRUE);
//
// setup object attributes for openfile
//
InitializeObjectAttributes( &oa,
&uni_string,
OBJ_CASE_INSENSITIVE,
0,
0 );
_last_status = NtOpenFile(_phandle,
SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
&oa, &status_block,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_ALERT);
//
// Deallocate unicode string
//
RtlFreeUnicodeString(&uni_string);
//RtlFreeAnsiString(&ans_string); // BUG BUG This causes access viol, why?
return NT_SUCCESS(_last_status);
} // OpenDrive()
#ifndef SIMBAD_ORPHAN
#define SIMBAD_ORPHAN 0x00000005
#endif
//
// Bugchecks the system
//
BOOLEAN
BugCheckOrResetTheSystem(HANDLE _handle, ULONG simbadFunction)
/*++
Routine Description:
This routine will setup SIMBAD for bug checking or reseting the system.
Arguments:
_handle - a device handle
simbadFunction - the desired function to call in the driver
Return Value:
TRUE upon successful completion.
--*/
{
SIMBAD_DATA SimbadData;
IO_STATUS_BLOCK StatusBlock;
NTSTATUS Status;
SimbadData.Function = simbadFunction;
Status= NtDeviceIoControlFile(_handle,
0,
NULL,
NULL,
&StatusBlock,
IOCTL_DISK_SIMBAD,
&SimbadData,
sizeof(SIMBAD_DATA),
NULL,
0);
if ((BOOLEAN)NT_SUCCESS(Status)){
return TRUE;
}else{
fprintf(stderr,
"BugCheckOrResetTheSystem(0x%08x) function %s failed (%lX)\n",
simbadFunction, devname, Status);
return FALSE;
}
} //BugCheckOrRestTheSystem
//
// Drops of writes to disk. Purpose to corrupt the disk.
//
BOOLEAN
RandomWriteFailure(
HANDLE _handle
)
/*++
Routine Description:
This routine causes io to the specified device to fail writes randomly.
Arguments:
Return Value:
TRUE upon successful completion.
--*/
{
SIMBAD_DATA SimbadData;
IO_STATUS_BLOCK StatusBlock;
NTSTATUS Status;
SimbadData.Count = Seed;
SimbadData.Function = SIMBAD_RANDOM_WRITE_FAIL;
Status= NtDeviceIoControlFile(_handle,
0,
NULL,
NULL,
&StatusBlock,
IOCTL_DISK_SIMBAD,
&SimbadData,
sizeof(SIMBAD_DATA),
NULL,
0);
if ((BOOLEAN)NT_SUCCESS(Status)){
return TRUE;
}else{
fprintf(stderr,
"RandomWriteFailure disk on %s failed (%lX)\n",
devname, Status);
return FALSE;
}
} //RandomWriteFailure
BOOLEAN
OrphanDisk(
HANDLE _handle
)
/*++
Routine Description:
This routine causes io to the specified device to return DEVICE_FAILURE
Arguments:
Return Value:
TRUE upon successful completion.
--*/
{
SIMBAD_DATA SimbadData;
IO_STATUS_BLOCK StatusBlock;
SimbadData.Count = 0;
SimbadData.Function = SIMBAD_ORPHAN;
return (BOOLEAN)
NT_SUCCESS( NtDeviceIoControlFile(_handle,
0,
NULL,
NULL,
&StatusBlock,
IOCTL_DISK_SIMBAD,
&SimbadData,
sizeof(SIMBAD_DATA),
NULL,
0) );
} // OrphanDisk()
BOOLEAN
EnableBadSectorSimulation(
HANDLE _handle,
BOOLEAN Enable
)
/*++
Routine Description:
This routine turns bad sector simulation on or off.
Arguments:
Enable -- supplies whether to turn simulation on (TRUE) or
off (FALSE)
Return Value:
TRUE upon successful completion.
--*/
{
SIMBAD_DATA SimbadData;
IO_STATUS_BLOCK StatusBlock;
SimbadData.Count = 0;
SimbadData.Function = Enable ? SIMBAD_ENABLE : SIMBAD_DISABLE;
return (BOOLEAN)
NT_SUCCESS( NtDeviceIoControlFile(_handle,
0,
NULL,
NULL,
&StatusBlock,
IOCTL_DISK_SIMBAD,
&SimbadData,
sizeof(SIMBAD_DATA),
NULL,
0) );
} // EnableBadSectorSimulation()
BOOLEAN
ClearBadSectors(
HANDLE _handle
)
/*++
Routine Description:
This routine turns bad sector simulation on or off.
Arguments:
_handle - something with which to grip
Return Value:
TRUE upon successful completion.
--*/
{
SIMBAD_DATA SimbadData;
IO_STATUS_BLOCK StatusBlock;
SimbadData.Count = 0;
SimbadData.Function = SIMBAD_CLEAR;
return (BOOLEAN)
NT_SUCCESS( NtDeviceIoControlFile(_handle,
0,
NULL,
NULL,
&StatusBlock,
IOCTL_DISK_SIMBAD,
&SimbadData,
sizeof(SIMBAD_DATA),
NULL,
0) );
} // ClearBadSectors()
BOOLEAN
SimulateBadSectors(
HANDLE _handle,
BOOLEAN Add,
ULONG Count,
PLBN SectorArray,
NTSTATUS Status,
ULONG AccessType
)
/*++
Routine Description:
This function adds sectors to or removes sectors from the
bad sector simulation list.
Arguments:
Add -- supplies a flag indicating whether to add (TRUE)
or remove (FALSE) sectors
Count -- supplies the number of sectors in the sector array
SectorArray -- supplies the array of sectors to mark as bad.
(May be NULL if Count is zero.)
Status -- supplies the status to associate with these sectors.
AccessType -- supplies the type of access that will trigger the error
for all sectors in the SectorArray.
Return Value:
TRUE upon successful completion.
--*/
{
SIMBAD_DATA SimbadData;
IO_STATUS_BLOCK StatusBlock;
ULONG i;
if( Count > MAXIMUM_SIMBAD_SECTORS ) {
return FALSE;
} // if
SimbadData.Function = Add ? SIMBAD_ADD_SECTORS : SIMBAD_REMOVE_SECTORS;
SimbadData.Count = Count;
for( i = 0; i < Count; i++ ) {
SimbadData.Sector[i].BlockAddress = SectorArray[i];
SimbadData.Sector[i].AccessType = AccessType;
SimbadData.Sector[i].Status = Status;
} // for
for( i = Count; i < MAXIMUM_SIMBAD_SECTORS; i++ ) {
SimbadData.Sector[i].BlockAddress = 0;
SimbadData.Sector[i].AccessType = 0;
SimbadData.Sector[i].Status = 0;
} // for
return (BOOLEAN)
NT_SUCCESS( NtDeviceIoControlFile(_handle,
0,
NULL,
NULL,
&StatusBlock,
IOCTL_DISK_SIMBAD,
&SimbadData,
sizeof(SIMBAD_DATA),
NULL,
0) );
} // SimulateBadSectors()
BOOLEAN
QuerySimulatedBadSectors(
IN HANDLE _handle,
OUT PULONG Count,
IN ULONG MaximumLbnsInBuffer,
OUT PLBN SectorArray,
OUT PULONG AccessTypeArray
)
/*++
Routine Description:
This method retrieves the sectors in the bad sector simulation list.
Arguments:
Count -- receives the number of returned sectors
MaximumLbnsInBuffer -- supplies the length of the buffer
SectorArray -- receives the bad sector list
AccessTypeArray -- receives the list of access types that trigger
the error.
Return Value:
TRUE upon successful completion
--*/
{
SIMBAD_DATA SimbadData;
IO_STATUS_BLOCK StatusBlock;
ULONG ReturnedCount;
ULONG i;
SimbadData.Function = SIMBAD_LIST_BAD_SECTORS;
if( !NT_SUCCESS( NtDeviceIoControlFile(_handle,
0,
NULL,
NULL,
&StatusBlock,
IOCTL_DISK_SIMBAD,
&SimbadData,
sizeof(SIMBAD_DATA),
&SimbadData,
sizeof(SIMBAD_DATA)) ) ) {
return FALSE;
} // if
ReturnedCount = (SimbadData.Count < MaximumLbnsInBuffer) ?
SimbadData.Count :
MaximumLbnsInBuffer;
for( i = 0; i < ReturnedCount; i++ ) {
SectorArray[i] = SimbadData.Sector[i].BlockAddress;
AccessTypeArray[i] = SimbadData.Sector[i].AccessType;
} // for
*Count = ReturnedCount;
return TRUE;
} // QuerySimulatedBadSectors()
//
// HEX_DIGITS is a string used to search for hex values in the input string
// in ReadFromFile().
//
#define HEX_DIGITS "0123456789abcdefABCDEF"
VOID
ReadFromFile (
)
/*++
Routine Description:
This routine reads the list(s) of bad sectors for 1 or more devices
and adds the sectors from that file.
Arguments:
None; filename is set in ParseCmdArgs as a global.
Return value:
None.
--*/
{
FILE * thefile;
HANDLE _handle=0;
ULONG count;
ULONG access;
char Buffer[MAX_DEV_NAME*8],
*curptr; // curptr is current location in buffer
//
// if able to open the filename (specified on command line), parse it
//
if(thefile=fopen(filename,"r")) {
//
// read until file is done
//
while (!feof(thefile)) {
//
// if able to read a string, parse it.
//
if(fgets(Buffer, MAX_DEV_NAME*8, thefile)) {
curptr=Buffer;
//
// Always start with the global access type value in case
// user specified an option, e.g. -x.
//
access = accessType;
//
// switch based on the beginning of the buffer
//
switch (Buffer[0]) {
//
// if \ is first char, Buffer is a device name
//
case '\\':
//
// if there is an open handle, close it.
//
if(_handle) {
NtClose(_handle);
_handle=0;
} // if(_handle)
//
// trim Buffer and use as devname for OpenDrive()
//
sscanf(Buffer, "%s", devname);
OpenDrive(devname, &_handle);
break;
//
// r means Buffer is a list of read-failing sectors
//
case 'r':
case 'R':
access |= SIMBAD_ACCESS_READ;
goto commonRead;
//
// w means Buffer is a list of write-failing sectors
//
case 'w':
case 'W':
access |= SIMBAD_ACCESS_WRITE;
goto commonRead;
//
// v means Buffer is a list of verify-failing sectors
//
case 'v':
case 'V':
access |= SIMBAD_ACCESS_VERIFY;
goto commonRead;
//
// a means Buffer is a list of verify-failing sectors
//
case 'a':
case 'A':
access |= SIMBAD_ACCESS_VERIFY |
SIMBAD_ACCESS_READ |
SIMBAD_ACCESS_WRITE;
commonRead:
//
// if there is an open drive
//
if (_handle) {
count=0;
curptr+=2; // Move past token:
//
// loop until no more sectors > MAX
//
while (0 < sscanf(curptr,"%lx", &(sectors[count]))
&& count<MAXIMUM_SIMBAD_SECTORS) {
//
// scan to first hex digit
//
curptr=&(curptr[strcspn(curptr,HEX_DIGITS)]);
count++;
//
// scan to first non hex char
//
curptr=&(curptr[strspn(curptr,HEX_DIGITS)]);
} // while
if (!SimulateBadSectors(_handle,
TRUE,
count,
sectors,
STATUS_DEVICE_DATA_ERROR,
access)) {
printf("Add failed \n");
} // if (!SimulateBadSectors())
} // if (_handle)
break;
//
// any other first character, do nothing
//
default:
;
} // switch
} // if (fgets())
} // while (!feof())
//
// if there is an open handle at the end, close it
//
if(_handle) {
NtClose(_handle);
} // if (_handle)
} // if (fopen())
} // ReadFromFile()
VOID _CRTAPI1
main (
IN int argc,
IN char * argv[]
)
/*++
Routine Description:
This routine is the entry point for simbad. It parses the command
arguments and then performs the action specified on the command line.
(The action and accessmode are set in SetCmdArgs()).
Arguments:
argc -- supplies the number of cmd line arguments
argv -- supplies the array of cmd line arguments
Return value:
None.
--*/
{
ULONG accarr[MAXIMUM_SIMBAD_SECTORS];
HANDLE _handle;
ULONG count, i;
//
// ParseCmdArgs and set whattodo, devname and accessmode
//
ParseCmdArgs(argc, argv);
if (whattodo==FROM_FILE) {
ReadFromFile();
} else { // NOT from file, read from stdin
//
// if able to open drive, perform action
//
if (OpenDrive(devname, &_handle)) {
//
// perform an action based on whattodo
//
switch (tolower(whattodo)) {
//
// LIST calls QuerySimulatedBadSectors and displays on stdout
//
case LIST:
if (!QuerySimulatedBadSectors(_handle,
&count,
MAXIMUM_SIMBAD_SECTORS,
sectors,
accarr )) {
printf("List failed %lx\n", GetLastError());
} else {
for (i=0; i<count; i++) {
printf("Sector: %lx\tAccess: %lx\n",
sectors[i],
accarr[i] );
} // for
} // if
break;
//
// ADD gets a list of sectors from InputSectorList and adds them
//
case ADD_ALL:
count=0;
InputSectorList(sectors, &count);
if (!SimulateBadSectors(_handle,
TRUE,
count,
sectors,
STATUS_DEVICE_DATA_ERROR,
accessType )) {
printf("Unable to add sectors to %s (%lx)\n",
devname,
GetLastError());
} // if
break;
//
// ORPHAN causes the partition to return DEVICE_FAILUR
//
case ORPHAN:
if (!OrphanDisk(_handle)) {
printf ("Orphan disk on %s failed (%lx)\n",
devname,
GetLastError());
} // if
break;
//
// ENABLE turns on the bad sector simulation
//
case ENABLE:
if (!EnableBadSectorSimulation(_handle, TRUE)) {
printf("Enable bad sectors on %s failed (%lx)\n",
devname,
GetLastError());
} // if
break;
//
// DISABLE turns off the bad sector simulation
//
case DISABLE:
if (!EnableBadSectorSimulation(_handle, FALSE)) {
printf("Disable bad sectors on %s failed %lx\n",
devname,
GetLastError());
} // if
break;
//
// CLEAR clears bad sector list in driver
//
case CLEAR:
if (!ClearBadSectors(_handle)) {
printf("Clear bad sectors on %s failed %lx\n",
devname,
GetLastError());
} // if
break;
//
// RANDOM causes the partition to drop writes randomly
//
case RANDOM:
if (!RandomWriteFailure(_handle )) {
printf ("RandomWriteFailure disk on %s failed (%lx)\n",
devname,
GetLastError());
} // if
break;
//
// BUGCHECK sets up the system for bug check
//
case BUGCHECK:
if (!BugCheckOrResetTheSystem(_handle, SIMBAD_BUG_CHECK)){
printf ("BugCheckTheSystem function failed.\n");
} // if
break;
//
// FIRMWARE_RESET setup for firmware reset when enabled.
//
case FIRMWARE_RESET:
if (!BugCheckOrResetTheSystem(_handle, SIMBAD_FIRMWARE_RESET)){
printf ("ResetTheSystem function failed.\n");
} // if
break;
// any other action, just close the handle and show usage
//
default:
NtClose(_handle);
ShowUsage();
} // switch (whattodo)
NtClose(_handle);
} else {
printf("Open %s failed %lx\n", devname, GetLastError());
} // if (OpenDrive())
} // if (whattodo==FROM_FILE)
} // main()