mirror of https://github.com/tongzx/nt5src
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.
2065 lines
50 KiB
2065 lines
50 KiB
/*++
|
|
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
arcemul.c
|
|
|
|
Abstract:
|
|
|
|
This module provides the x86 emulation for the Arc routines which are
|
|
built into the firmware on ARC machines.
|
|
|
|
N. B. This is where all the initialization of the SYSTEM_PARAMETER_BLOCK
|
|
takes place. If there is any non-standard hardware, some of the
|
|
vectors may have to be changed. This is where to do it.
|
|
|
|
|
|
Author:
|
|
|
|
Allen Kay (akay) 26-Jan-1996
|
|
|
|
Base on i386 version by John Vert (jvert) 13-Jun-1991
|
|
|
|
Environment:
|
|
|
|
EFI
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "arccodes.h"
|
|
#include "bootia64.h"
|
|
#include "ntdddisk.h"
|
|
#include "string.h"
|
|
#include "stdio.h"
|
|
#include "stdlib.h"
|
|
#include "scsi.h"
|
|
#include "scsiboot.h"
|
|
|
|
#define CMOS_CONTROL_PORT ((PUCHAR)0x70)
|
|
#define CMOS_DATA_PORT ((PUCHAR)0x71)
|
|
#define CMOS_STATUS_B 0x0B
|
|
#define CMOS_DAYLIGHT_BIT 1
|
|
|
|
extern PCHAR MnemonicTable[];
|
|
|
|
//
|
|
// Size definitions for HardDiskInitialize()
|
|
//
|
|
|
|
#define SUPPORTED_NUMBER_OF_DISKS 32
|
|
#define SIZE_FOR_SUPPORTED_DISK_STRUCTURE (SUPPORTED_NUMBER_OF_DISKS*sizeof(DRIVER_LOOKUP_ENTRY))
|
|
|
|
|
|
PDRIVER_UNLOAD AEDriverUnloadRoutine = NULL;
|
|
|
|
|
|
#define PORT_BUFFER_SIZE 10
|
|
UCHAR PortBuffer[PORT_BUFFER_SIZE];
|
|
ULONG PortBufferStart = 0;
|
|
ULONG PortBufferEnd = 0;
|
|
|
|
//
|
|
// Miniport DriverEntry typedef
|
|
//
|
|
|
|
typedef NTSTATUS
|
|
(*PDRIVER_ENTRY) (
|
|
IN PVOID DriverObject,
|
|
IN PVOID Parameter2
|
|
);
|
|
|
|
//
|
|
// Private function prototypes
|
|
//
|
|
|
|
ARC_STATUS
|
|
BlArcNotYetImplemented(
|
|
IN ULONG FileId
|
|
);
|
|
|
|
PCONFIGURATION_COMPONENT
|
|
AEComponentInfo(
|
|
IN PCONFIGURATION_COMPONENT Current
|
|
);
|
|
|
|
PCONFIGURATION_COMPONENT
|
|
FwGetChild(
|
|
IN PCONFIGURATION_COMPONENT Current
|
|
);
|
|
|
|
PCHAR
|
|
AEGetEnvironment(
|
|
IN PCHAR Variable
|
|
);
|
|
|
|
PCONFIGURATION_COMPONENT
|
|
FwGetPeer(
|
|
IN PCONFIGURATION_COMPONENT Current
|
|
);
|
|
|
|
PCONFIGURATION_COMPONENT
|
|
AEGetParent(
|
|
IN PCONFIGURATION_COMPONENT Current
|
|
);
|
|
|
|
ARC_STATUS
|
|
AEGetConfigurationData(
|
|
IN PVOID ConfigurationData,
|
|
IN PCONFIGURATION_COMPONENT Current
|
|
);
|
|
|
|
PMEMORY_DESCRIPTOR
|
|
AEGetMemoryDescriptor(
|
|
IN PMEMORY_DESCRIPTOR MemoryDescriptor OPTIONAL
|
|
);
|
|
|
|
ARC_STATUS
|
|
AEOpen(
|
|
IN PCHAR OpenPath,
|
|
IN OPEN_MODE OpenMode,
|
|
OUT PULONG FileId
|
|
);
|
|
|
|
ARC_STATUS
|
|
AEClose(
|
|
IN ULONG FileId
|
|
);
|
|
|
|
ARC_STATUS
|
|
AERead (
|
|
IN ULONG FileId,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
);
|
|
|
|
ARC_STATUS
|
|
AEReadStatus (
|
|
IN ULONG FileId
|
|
);
|
|
|
|
VOID
|
|
AEReboot(
|
|
VOID
|
|
);
|
|
|
|
ARC_STATUS
|
|
AESeek (
|
|
IN ULONG FileId,
|
|
IN PLARGE_INTEGER Offset,
|
|
IN SEEK_MODE SeekMode
|
|
);
|
|
|
|
ARC_STATUS
|
|
AEWrite (
|
|
IN ULONG FileId,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
);
|
|
|
|
ARC_STATUS
|
|
AEGetFileInformation(
|
|
IN ULONG FileId,
|
|
OUT PFILE_INFORMATION FileInformation
|
|
);
|
|
|
|
PTIME_FIELDS
|
|
AEGetTime(
|
|
VOID
|
|
);
|
|
|
|
ULONG
|
|
AEGetRelativeTime(
|
|
VOID
|
|
);
|
|
|
|
ARC_STATUS
|
|
ScsiDiskClose (
|
|
IN ULONG FileId
|
|
);
|
|
|
|
ARC_STATUS
|
|
ScsiDiskMount (
|
|
IN PCHAR MountPath,
|
|
IN MOUNT_OPERATION Operation
|
|
);
|
|
|
|
ARC_STATUS
|
|
ScsiDiskOpen (
|
|
IN PCHAR OpenPath,
|
|
IN OPEN_MODE OpenMode,
|
|
OUT PULONG FileId
|
|
);
|
|
|
|
ARC_STATUS
|
|
ScsiDiskRead (
|
|
IN ULONG FileId,
|
|
IN PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
);
|
|
|
|
ARC_STATUS
|
|
ScsiDiskSeek (
|
|
IN ULONG FileId,
|
|
IN PLARGE_INTEGER Offset,
|
|
IN SEEK_MODE SeekMode
|
|
);
|
|
|
|
ARC_STATUS
|
|
ScsiDiskWrite (
|
|
IN ULONG FileId,
|
|
IN PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
);
|
|
|
|
VOID
|
|
HardDiskInitialize(
|
|
IN OUT PVOID LookupTable,
|
|
IN ULONG Entries,
|
|
IN PVOID DeviceFoundCallback
|
|
);
|
|
|
|
//
|
|
// This is the x86 version of the system parameter block on the ARC machines.
|
|
// It lives here, and any module that uses an ArcXXXX routine must declare
|
|
// it external. Machines that have other than very plain-vanilla hardware
|
|
// may have to replace some of the hard-wired vectors with different
|
|
// procedures.
|
|
//
|
|
|
|
PVOID GlobalFirmwareVectors[MaximumRoutine];
|
|
|
|
SYSTEM_PARAMETER_BLOCK GlobalSystemBlock =
|
|
{
|
|
0, // Signature??
|
|
sizeof(SYSTEM_PARAMETER_BLOCK), // Length
|
|
0, // Version
|
|
0, // Revision
|
|
NULL, // RestartBlock
|
|
NULL, // DebugBlock
|
|
NULL, // GenerateExceptionVector
|
|
NULL, // TlbMissExceptionVector
|
|
MaximumRoutine, // FirmwareVectorLength
|
|
GlobalFirmwareVectors, // Pointer to vector block
|
|
0, // VendorVectorLength
|
|
NULL // Pointer to vendor vector block
|
|
};
|
|
|
|
|
|
extern BL_FILE_TABLE BlFileTable[BL_FILE_TABLE_SIZE];
|
|
|
|
//
|
|
// temptemp John Vert (jvert) 6-Sep-1991
|
|
// Just do this until we can make our device driver interface look
|
|
// like the ARC firmware one.
|
|
//
|
|
|
|
extern BL_DEVICE_ENTRY_TABLE ScsiDiskEntryTable;
|
|
|
|
ULONG FwStallCounter;
|
|
|
|
|
|
VOID
|
|
AEInitializeStall(
|
|
VOID
|
|
)
|
|
{
|
|
FwStallCounter = GET_STALL_COUNT();
|
|
return;
|
|
}
|
|
|
|
#if !defined(NO_LEGACY_DRIVERS)
|
|
|
|
|
|
ARC_STATUS
|
|
AEInitializeIo(
|
|
IN ULONG DriveId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes SCSI boot driver, if any. Loads ntbootdd.sys from the
|
|
boot partition, binds it to the osloader, and initializes it.
|
|
|
|
Arguments:
|
|
|
|
DriveId - file id of the opened boot partition
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - Drivers successfully initialized
|
|
|
|
--*/
|
|
|
|
{
|
|
extern ULONG ScsiPortCount;
|
|
extern ULONG MachineType;
|
|
ARC_STATUS Status;
|
|
PVOID Buffer;
|
|
PVOID ImageBase;
|
|
PLDR_DATA_TABLE_ENTRY DriverDataTableEntry;
|
|
PDRIVER_ENTRY Entry;
|
|
ULONG i;
|
|
ULONG ImageBasePage;
|
|
|
|
ScsiPortCount = 0;
|
|
|
|
FwStallCounter = GET_STALL_COUNT();
|
|
Status = BlLoadImage(DriveId,
|
|
MemoryFirmwarePermanent,
|
|
"\\NTBOOTDD.SYS",
|
|
TARGET_IMAGE,
|
|
&ImageBase);
|
|
if (Status != ESUCCESS) {
|
|
return(Status);
|
|
}
|
|
|
|
Status = BlAllocateDataTableEntry("NTBOOTDD.SYS",
|
|
"\\NTBOOTDD.SYS",
|
|
ImageBase,
|
|
&DriverDataTableEntry);
|
|
if (Status != ESUCCESS) {
|
|
return(Status);
|
|
}
|
|
//
|
|
// Scan the import table and bind to osloader
|
|
//
|
|
Status = BlScanOsloaderBoundImportTable(DriverDataTableEntry);
|
|
if (Status != ESUCCESS) {
|
|
return(Status);
|
|
}
|
|
|
|
Entry = (PDRIVER_ENTRY)DriverDataTableEntry->EntryPoint;
|
|
|
|
Status = (*Entry)(NULL,NULL);
|
|
if (Status == ESUCCESS) {
|
|
//
|
|
// Find the firmware's copy of the memory descriptor that
|
|
// contains the driver and change it from MemoryFree to
|
|
// MemoryFirmwareTemporary.
|
|
//
|
|
ImageBasePage = ((PtrToUlong(ImageBase) & 0x7fffffff) >> PAGE_SHIFT);
|
|
i=0;
|
|
while ((MDArray[i].BasePage >= ImageBasePage) ||
|
|
(MDArray[i].BasePage + MDArray[i].PageCount < ImageBasePage)) {
|
|
i++;
|
|
}
|
|
|
|
MDArray[i].MemoryType = MemoryFirmwareTemporary;
|
|
|
|
Buffer = BlAllocateHeap(SIZE_FOR_SUPPORTED_DISK_STRUCTURE);
|
|
|
|
if(Buffer == NULL) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
HardDiskInitialize(Buffer, SUPPORTED_NUMBER_OF_DISKS, NULL);
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
#endif // NO_LEGACY_DRIVERS
|
|
|
|
|
|
VOID
|
|
BlFillInSystemParameters(
|
|
IN PBOOT_CONTEXT BootContextRecord
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine fills in all the fields in the Global System Parameter Block
|
|
that it can. This includes all the firmware vectors, the vendor-specific
|
|
information, and anything else that may come up.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
int cnt;
|
|
|
|
//
|
|
// Fill in the pointers to the firmware functions which we emulate.
|
|
// Those which we don't emulate are stubbed by BlArcNotYetImplemented,
|
|
// which will print an error message if it is accidentally called.
|
|
//
|
|
|
|
for (cnt=0; cnt<MaximumRoutine; cnt++) {
|
|
GlobalFirmwareVectors[cnt]=(PVOID)BlArcNotYetImplemented;
|
|
}
|
|
GlobalFirmwareVectors[CloseRoutine] = (PVOID)AEClose;
|
|
GlobalFirmwareVectors[OpenRoutine] = (PVOID)AEOpen;
|
|
GlobalFirmwareVectors[MemoryRoutine]= (PVOID)AEGetMemoryDescriptor;
|
|
GlobalFirmwareVectors[SeekRoutine] = (PVOID)AESeek;
|
|
GlobalFirmwareVectors[ReadRoutine] = (PVOID)AERead;
|
|
GlobalFirmwareVectors[ReadStatusRoutine] = (PVOID)AEReadStatus;
|
|
GlobalFirmwareVectors[WriteRoutine] = (PVOID)AEWrite;
|
|
GlobalFirmwareVectors[GetFileInformationRoutine] = (PVOID)AEGetFileInformation;
|
|
GlobalFirmwareVectors[GetTimeRoutine] = (PVOID)AEGetTime;
|
|
GlobalFirmwareVectors[GetRelativeTimeRoutine] = (PVOID)AEGetRelativeTime;
|
|
GlobalFirmwareVectors[GetPeerRoutine] = (PVOID)FwGetPeer;
|
|
GlobalFirmwareVectors[GetChildRoutine] = (PVOID)FwGetChild;
|
|
GlobalFirmwareVectors[GetParentRoutine] = (PVOID)AEGetParent;
|
|
GlobalFirmwareVectors[GetComponentRoutine] = (PVOID)FwGetComponent;
|
|
GlobalFirmwareVectors[GetDataRoutine] = (PVOID)AEGetConfigurationData;
|
|
GlobalFirmwareVectors[GetEnvironmentRoutine] = (PVOID)AEGetEnvironment;
|
|
|
|
GlobalFirmwareVectors[RestartRoutine] = (PVOID)AEReboot;
|
|
GlobalFirmwareVectors[RebootRoutine] = (PVOID)AEReboot;
|
|
|
|
}
|
|
|
|
|
|
PMEMORY_DESCRIPTOR
|
|
AEGetMemoryDescriptor(
|
|
IN PMEMORY_DESCRIPTOR MemoryDescriptor OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Emulates the Arc GetMemoryDescriptor call. This must translate
|
|
between the memory description passed to us by the SU module and
|
|
the MEMORYDESCRIPTOR type defined by ARC.
|
|
|
|
Arguments:
|
|
|
|
MemoryDescriptor - Supplies current memory descriptor.
|
|
If MemoryDescriptor==NULL, return the first memory descriptor.
|
|
If MemoryDescriptor!=NULL, return the next memory descriptor.
|
|
|
|
Return Value:
|
|
|
|
Next memory descriptor in the list.
|
|
NULL if MemoryDescriptor is the last descriptor in the list.
|
|
|
|
--*/
|
|
|
|
{
|
|
extern ULONG NumberDescriptors;
|
|
PMEMORY_DESCRIPTOR Return;
|
|
if (MemoryDescriptor==NULL) {
|
|
Return=MDArray;
|
|
} else {
|
|
if((ULONG)(MemoryDescriptor-MDArray) >= (NumberDescriptors-1)) {
|
|
return NULL;
|
|
} else {
|
|
Return = ++MemoryDescriptor;
|
|
}
|
|
}
|
|
return(Return);
|
|
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
BlArcNotYetImplemented(
|
|
IN ULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a stub routine used to fill in the firmware vectors which haven't
|
|
been defined yet. It uses BlPrint to print a message on the screen.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
EINVAL
|
|
|
|
--*/
|
|
|
|
{
|
|
#if DBG
|
|
BlPrint(TEXT("ERROR - Unimplemented Firmware Vector called (FID %lx)\r\n"),
|
|
FileId );
|
|
#endif
|
|
return(EINVAL);
|
|
}
|
|
|
|
|
|
PCONFIGURATION_COMPONENT
|
|
FwGetChild(
|
|
IN PCONFIGURATION_COMPONENT Current
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the arc emulation routine for GetChild. Based on the current
|
|
component, it returns the component's child component.
|
|
|
|
Arguments:
|
|
|
|
Current - Supplies pointer to the current configuration component
|
|
|
|
Return Value:
|
|
|
|
A pointer to a CONFIGURATION_COMPONENT structure OR
|
|
NULL - No more configuration information
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONFIGURATION_COMPONENT_DATA CurrentEntry;
|
|
|
|
//
|
|
// if current component is NULL, return a pointer to first system
|
|
// component; otherwise return current component's child component.
|
|
//
|
|
|
|
if (Current) {
|
|
CurrentEntry = CONTAINING_RECORD(Current,
|
|
CONFIGURATION_COMPONENT_DATA,
|
|
ComponentEntry);
|
|
if (CurrentEntry->Child) {
|
|
return(&(CurrentEntry->Child->ComponentEntry));
|
|
} else {
|
|
return(NULL);
|
|
}
|
|
} else {
|
|
if (FwConfigurationTree) {
|
|
return(&(FwConfigurationTree->ComponentEntry));
|
|
} else {
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
PCONFIGURATION_COMPONENT
|
|
FwGetPeer(
|
|
IN PCONFIGURATION_COMPONENT Current
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the arc emulation routine for GetPeer. Based on the current
|
|
component, it returns the component's sibling.
|
|
|
|
Arguments:
|
|
|
|
Current - Supplies pointer to the current configuration component
|
|
|
|
Return Value:
|
|
|
|
A pointer to a CONFIGURATION_COMPONENT structure OR
|
|
NULL - No more configuration information
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONFIGURATION_COMPONENT_DATA CurrentEntry;
|
|
|
|
|
|
if (Current) {
|
|
CurrentEntry = CONTAINING_RECORD(Current,
|
|
CONFIGURATION_COMPONENT_DATA,
|
|
ComponentEntry);
|
|
if (CurrentEntry->Sibling) {
|
|
return(&(CurrentEntry->Sibling->ComponentEntry));
|
|
} else {
|
|
return(NULL);
|
|
}
|
|
} else {
|
|
return(NULL);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
PCONFIGURATION_COMPONENT
|
|
AEGetParent(
|
|
IN PCONFIGURATION_COMPONENT Current
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the arc emulation routine for GetParent. Based on the current
|
|
component, it returns the component's parent.
|
|
|
|
Arguments:
|
|
|
|
Current - Supplies pointer to the current configuration component
|
|
|
|
Return Value:
|
|
|
|
A pointer to a CONFIGURATION_COMPONENT structure OR
|
|
NULL - No more configuration information
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONFIGURATION_COMPONENT_DATA CurrentEntry;
|
|
|
|
|
|
if (Current) {
|
|
CurrentEntry = CONTAINING_RECORD(Current,
|
|
CONFIGURATION_COMPONENT_DATA,
|
|
ComponentEntry);
|
|
if (CurrentEntry->Parent) {
|
|
return(&(CurrentEntry->Parent->ComponentEntry));
|
|
} else {
|
|
return(NULL);
|
|
}
|
|
} else {
|
|
return(NULL);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
AEGetConfigurationData(
|
|
IN PVOID ConfigurationData,
|
|
IN PCONFIGURATION_COMPONENT Current
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the arc emulation routine for GetParent. Based on the current
|
|
component, it returns the component's parent.
|
|
|
|
Arguments:
|
|
|
|
Current - Supplies pointer to the current configuration component
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - Data successfully returned.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONFIGURATION_COMPONENT_DATA CurrentEntry;
|
|
|
|
|
|
if (Current) {
|
|
CurrentEntry = CONTAINING_RECORD(Current,
|
|
CONFIGURATION_COMPONENT_DATA,
|
|
ComponentEntry);
|
|
RtlMoveMemory(ConfigurationData,
|
|
CurrentEntry->ConfigurationData,
|
|
Current->ConfigurationDataLength);
|
|
return(ESUCCESS);
|
|
} else {
|
|
return(EINVAL);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
PCHAR
|
|
AEGetEnvironment(
|
|
IN PCHAR Variable
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the arc emulation routine for ArcGetEnvironment. It returns
|
|
the value of the specified NVRAM environment variable.
|
|
|
|
NOTE John Vert (jvert) 23-Apr-1992
|
|
This particular implementation uses the Daylight Savings Bit on
|
|
the Real Time Clock to reflect the state of the LastKnownGood
|
|
environment variable. This is the only variable we support.
|
|
|
|
Arguments:
|
|
|
|
Variable - Supplies the name of the environment variable to look up.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the specified environment variable's value, or
|
|
NULL if the variable does not exist.
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR StatusByte;
|
|
|
|
#if 1
|
|
|
|
//
|
|
// Until firmware implements LastKnownGood variable,
|
|
// return NULL for now.
|
|
//
|
|
|
|
return(NULL);
|
|
|
|
#else
|
|
|
|
if (_stricmp(Variable, "LastKnownGood") != 0) {
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Read the Daylight Savings Bit out of the RTC to determine whether
|
|
// the LastKnownGood environment variable is TRUE or FALSE.
|
|
//
|
|
|
|
WRITE_PORT_UCHAR(CMOS_CONTROL_PORT, CMOS_STATUS_B);
|
|
StatusByte = READ_PORT_UCHAR(CMOS_DATA_PORT);
|
|
if (StatusByte & CMOS_DAYLIGHT_BIT) {
|
|
return("TRUE");
|
|
} else {
|
|
return(NULL);
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
AEOpen(
|
|
IN PCHAR OpenPath,
|
|
IN OPEN_MODE OpenMode,
|
|
OUT PULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens the file or device specified by OpenPath.
|
|
|
|
Arguments:
|
|
|
|
OpenPath - Supplies a pointer to the fully-qualified path name.
|
|
|
|
OpenMode - Supplies the mode to open the file.
|
|
0 - Read Only
|
|
1 - Write Only
|
|
2 - Read/Write
|
|
|
|
FileId - Returns the file descriptor for use with the Close, Read, Write,
|
|
and Seek routines
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - File successfully opened.
|
|
|
|
--*/
|
|
|
|
{
|
|
ARC_STATUS Status;
|
|
CHAR Buffer[128];
|
|
|
|
Status = BiosConsoleOpen( OpenPath,
|
|
OpenMode,
|
|
FileId );
|
|
|
|
if (Status == ESUCCESS) {
|
|
return(ESUCCESS);
|
|
}
|
|
|
|
Status = BiosPartitionOpen( OpenPath,
|
|
OpenMode,
|
|
FileId );
|
|
|
|
if (Status == ESUCCESS) {
|
|
return(ESUCCESS);
|
|
}
|
|
|
|
#if !defined(NO_LEGACY_DRIVERS)
|
|
|
|
//
|
|
// It's not the console or a BIOS partition, so let's try the SCSI
|
|
// driver.
|
|
//
|
|
|
|
//
|
|
// Find a free FileId
|
|
//
|
|
|
|
*FileId = 2;
|
|
while (BlFileTable[*FileId].Flags.Open == 1) {
|
|
*FileId += 1;
|
|
if (*FileId == BL_FILE_TABLE_SIZE) {
|
|
return(ENOENT);
|
|
}
|
|
}
|
|
|
|
strcpy(Buffer,OpenPath);
|
|
|
|
Status = ScsiDiskOpen( Buffer,
|
|
OpenMode,
|
|
FileId );
|
|
|
|
if (Status == ESUCCESS) {
|
|
|
|
//
|
|
// SCSI successfully opened it. For now, we stick the appropriate
|
|
// SCSI DeviceEntryTable into the BlFileTable. This is temporary.
|
|
//
|
|
|
|
BlFileTable[*FileId].Flags.Open = 1;
|
|
BlFileTable[*FileId].DeviceEntryTable = &ScsiDiskEntryTable;
|
|
return(ESUCCESS);
|
|
}
|
|
|
|
#endif // NO_LEGACY_DRIVERS
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
AESeek (
|
|
IN ULONG FileId,
|
|
IN PLARGE_INTEGER Offset,
|
|
IN SEEK_MODE SeekMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Changes the current offset of the file specified by FileId
|
|
|
|
Arguments:
|
|
|
|
FileId - specifies the file on which the current offset is to
|
|
be changed.
|
|
|
|
Offset - New offset into file.
|
|
|
|
SeekMode - Either SeekAbsolute or SeekRelative
|
|
SeekEndRelative is not supported
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - Operation completed succesfully
|
|
|
|
EBADF - Operation did not complete successfully.
|
|
|
|
--*/
|
|
|
|
{
|
|
return(BlFileTable[FileId].DeviceEntryTable->Seek)( FileId,
|
|
Offset,
|
|
SeekMode );
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
AEClose (
|
|
IN ULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes the file specified by FileId
|
|
|
|
Arguments:
|
|
|
|
FileId - specifies the file to close
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - Operation completed succesfully
|
|
|
|
EBADF - Operation did not complete successfully.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
return(BlFileTable[FileId].DeviceEntryTable->Close)(FileId);
|
|
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
AEReadStatus(
|
|
IN ULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if data is available on the specified device
|
|
|
|
Arguments:
|
|
|
|
FileId - Specifies the device to check for data.
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - At least one byte is available.
|
|
|
|
EAGAIN - No data is available
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Special case for console input
|
|
//
|
|
|
|
if (FileId == 0) {
|
|
#if 0
|
|
//
|
|
// Give priority to dumb terminal
|
|
//
|
|
if (BlIsTerminalConnected() && (PortBufferStart != PortBufferEnd)) {
|
|
return(ESUCCESS);
|
|
}
|
|
|
|
if (BlIsTerminalConnected() && (BlPortPollOnly(BlTerminalDeviceId) == CP_GET_SUCCESS)) {
|
|
return(ESUCCESS);
|
|
}
|
|
#endif
|
|
return(BiosConsoleReadStatus(FileId));
|
|
} else {
|
|
return(BlArcNotYetImplemented(FileId));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
AERead (
|
|
IN ULONG FileId,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads from the specified file or device
|
|
|
|
Arguments:
|
|
|
|
FileId - specifies the file to read from
|
|
|
|
Buffer - Address of buffer to hold the data that is read
|
|
|
|
Length - Maximum number of bytes to read
|
|
|
|
Count - Address of location in which to store the actual bytes read.
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - Read completed successfully
|
|
|
|
!ESUCCESS - Read failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
ARC_STATUS Status;
|
|
ULONG Limit;
|
|
ULONG PartCount;
|
|
PCHAR TmpBuffer;
|
|
ULONG StartTime;
|
|
ULONG LastTime;
|
|
UCHAR Ch;
|
|
|
|
//
|
|
// Special case for console input
|
|
//
|
|
|
|
if (FileId == 0) {
|
|
|
|
RetryRead:
|
|
#if 0
|
|
if (BlIsTerminalConnected()) {
|
|
|
|
*Count = 0;
|
|
TmpBuffer = (PCHAR)Buffer;
|
|
|
|
while (*Count < Length) {
|
|
|
|
//
|
|
// First return any buffered input
|
|
//
|
|
if (PortBufferStart != PortBufferEnd) {
|
|
TmpBuffer[*Count] = PortBuffer[PortBufferStart];
|
|
PortBufferStart++;
|
|
PortBufferStart = PortBufferStart % PORT_BUFFER_SIZE;
|
|
*Count = *Count + 1;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now check for new input
|
|
//
|
|
if (BlPortPollByte(BlTerminalDeviceId, TmpBuffer + *Count) != CP_GET_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Convert ESC key to the local equivalent
|
|
//
|
|
if (TmpBuffer[*Count] == 0x1b) {
|
|
TmpBuffer[*Count] = (CHAR)ASCI_CSI_IN;
|
|
|
|
//
|
|
// Wait for the user to type a key.
|
|
//
|
|
StartTime = AEGetRelativeTime();
|
|
|
|
while (BlPortPollOnly(BlTerminalDeviceId) != CP_GET_SUCCESS) {
|
|
LastTime = AEGetRelativeTime();
|
|
|
|
//
|
|
// if the counter wraps back to zero, just restart the wait.
|
|
//
|
|
if (LastTime < StartTime) {
|
|
StartTime = LastTime;
|
|
}
|
|
|
|
//
|
|
// If one second has passed, the user must have just wanted a single
|
|
// escape key, so return with that.
|
|
//
|
|
if ((LastTime - StartTime) > 1) {
|
|
*Count = *Count + 1;
|
|
return (ESUCCESS);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// We have another key, get it and translate the escape sequence.
|
|
//
|
|
if (BlPortPollByte(BlTerminalDeviceId, &Ch) != CP_GET_SUCCESS) {
|
|
*Count = *Count + 1;
|
|
return (ESUCCESS);
|
|
}
|
|
|
|
|
|
switch (Ch) {
|
|
case '@': // F12 key
|
|
PortBuffer[PortBufferEnd] = 'O';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
PortBuffer[PortBufferEnd] = 'B';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case '!': // F11 key
|
|
PortBuffer[PortBufferEnd] = 'O';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
PortBuffer[PortBufferEnd] = 'A';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case '0': // F10 key
|
|
PortBuffer[PortBufferEnd] = 'O';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
PortBuffer[PortBufferEnd] = 'M';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case '8': // F8 key
|
|
PortBuffer[PortBufferEnd] = 'O';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
PortBuffer[PortBufferEnd] = 'r';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case '6': // F6 key
|
|
PortBuffer[PortBufferEnd] = 'O';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
PortBuffer[PortBufferEnd] = 'u';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case '5': // F5 key
|
|
PortBuffer[PortBufferEnd] = 'O';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
PortBuffer[PortBufferEnd] = 't';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case '3': // F3 key
|
|
PortBuffer[PortBufferEnd] = 'O';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
PortBuffer[PortBufferEnd] = 'w';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case '1': // F1 key
|
|
PortBuffer[PortBufferEnd] = 'O';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
PortBuffer[PortBufferEnd] = 'P';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case '+': // Home key
|
|
PortBuffer[PortBufferEnd] = 'H';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case '-': // End key
|
|
PortBuffer[PortBufferEnd] = 'K';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case '[': // Cursor movement key
|
|
|
|
//
|
|
// The local computer can run a lot faster than the serial port can give bytes,
|
|
// so spin, polling, for a second.
|
|
//
|
|
StartTime = AEGetRelativeTime();
|
|
while (BlPortPollOnly(BlTerminalDeviceId) != CP_GET_SUCCESS) {
|
|
LastTime = AEGetRelativeTime();
|
|
|
|
//
|
|
// if the counter wraps back to zero, just restart the wait.
|
|
//
|
|
if (LastTime < StartTime) {
|
|
StartTime = LastTime;
|
|
}
|
|
|
|
//
|
|
// If one second has passed, we must be done.
|
|
//
|
|
if ((LastTime - StartTime) > 1) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (BlPortPollByte(BlTerminalDeviceId, &Ch) != CP_GET_SUCCESS) {
|
|
PortBuffer[PortBufferEnd] = '[';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
if ((Ch == 'A') || (Ch == 'B') || (Ch == 'C') || (Ch == 'D')) { // Arrow key.
|
|
|
|
PortBuffer[PortBufferEnd] = Ch;
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Leave it as is
|
|
//
|
|
PortBuffer[PortBufferEnd] = '[';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
PortBuffer[PortBufferEnd] = Ch;
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
PortBuffer[PortBufferEnd] = Ch;
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
} else if (TmpBuffer[*Count] == 0x7F) { // DEL key
|
|
TmpBuffer[*Count] = (CHAR)ASCI_CSI_IN;
|
|
PortBuffer[PortBufferEnd] = 'P';
|
|
PortBufferEnd++;
|
|
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
|
}
|
|
|
|
*Count = *Count + 1;
|
|
}
|
|
|
|
if (*Count != 0) {
|
|
return(ESUCCESS);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
if (BiosConsoleReadStatus(FileId) == ESUCCESS) {
|
|
return(BiosConsoleRead(FileId,Buffer,Length,Count));
|
|
}
|
|
|
|
goto RetryRead;
|
|
|
|
} else {
|
|
|
|
*Count = 0;
|
|
|
|
do {
|
|
|
|
if ((PtrToUlong(Buffer) & 0xffff0000) !=
|
|
((PtrToUlong(Buffer) + Length) & 0xffff0000)) {
|
|
|
|
Limit = 0x10000 - (PtrToUlong(Buffer) & 0x0000ffff);
|
|
} else {
|
|
|
|
Limit = Length;
|
|
|
|
}
|
|
|
|
Status = (BlFileTable[FileId].DeviceEntryTable->Read)( FileId,
|
|
Buffer,
|
|
Limit,
|
|
&PartCount );
|
|
*Count += PartCount;
|
|
Length -= Limit;
|
|
(PCHAR) Buffer += Limit;
|
|
|
|
if (Status != ESUCCESS) {
|
|
#if DBG
|
|
BlPrint(TEXT("Disk I/O error: Status = %lx\r\n"),Status);
|
|
#endif
|
|
return(Status);
|
|
}
|
|
|
|
} while (Length > 0);
|
|
|
|
return(Status);
|
|
}
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
AEWrite (
|
|
IN ULONG FileId,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes to the specified file or device
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies the file or device to write to
|
|
|
|
Buffer - Supplies address of the data to be written
|
|
|
|
Length - Supplies number of bytes to write
|
|
|
|
Count - Address of location in which to store the actual bytes written.
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - Read completed successfully
|
|
|
|
!ESUCCESS - Read failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
ARC_STATUS Status;
|
|
ULONG Limit;
|
|
ULONG PartCount;
|
|
PCHAR TmpBuffer;
|
|
|
|
//
|
|
// Special case for console output
|
|
//
|
|
|
|
if (FileId == 1) {
|
|
|
|
#if 0
|
|
if (BlIsTerminalConnected()) {
|
|
for (PartCount = 0, TmpBuffer = (PCHAR)Buffer; PartCount < Length; PartCount++) {
|
|
BlPortPutByte(BlTerminalDeviceId, TmpBuffer[PartCount]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return(BiosConsoleWrite(FileId,(PWCHAR)Buffer,Length,Count));
|
|
} else {
|
|
*Count = 0;
|
|
|
|
do {
|
|
|
|
if ((PtrToUlong(Buffer) & 0xffff0000) !=
|
|
((PtrToUlong(Buffer) + Length) & 0xffff0000)) {
|
|
|
|
Limit = 0x10000 - (PtrToUlong(Buffer) & 0x0000ffff);
|
|
} else {
|
|
|
|
Limit = Length;
|
|
|
|
}
|
|
|
|
Status = (BlFileTable[FileId].DeviceEntryTable->Write)( FileId,
|
|
Buffer,
|
|
Limit,
|
|
&PartCount );
|
|
*Count += PartCount;
|
|
Length -= Limit;
|
|
(PCHAR) Buffer += Limit;
|
|
|
|
if (Status != ESUCCESS) {
|
|
#if DBG
|
|
BlPrint(TEXT("AERead: Status = %lx\r\n"),Status);
|
|
#endif
|
|
return(Status);
|
|
}
|
|
|
|
} while (Length > 0);
|
|
|
|
return(Status);
|
|
}
|
|
}
|
|
|
|
ARC_STATUS
|
|
AEGetFileInformation(
|
|
IN ULONG FileId,
|
|
OUT PFILE_INFORMATION FileInformation
|
|
)
|
|
{
|
|
return(BlFileTable[FileId].DeviceEntryTable->GetFileInformation)( FileId,
|
|
FileInformation);
|
|
}
|
|
|
|
|
|
TIME_FIELDS AETime;
|
|
|
|
PTIME_FIELDS
|
|
AEGetTime(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG Date,Time;
|
|
|
|
GET_DATETIME(&Date,&Time);
|
|
|
|
//
|
|
// Date and time are filled as as follows:
|
|
//
|
|
// Date:
|
|
//
|
|
// bits 0 - 4 : day
|
|
// bits 5 - 8 : month
|
|
// bits 9 - 31 : year
|
|
//
|
|
// Time:
|
|
//
|
|
// bits 0 - 5 : second
|
|
// bits 6 - 11 : minute
|
|
// bits 12 - 16 : hour
|
|
//
|
|
|
|
AETime.Second = (CSHORT)((Time & 0x0000003f) >> 0);
|
|
AETime.Minute = (CSHORT)((Time & 0x00000fc0) >> 6);
|
|
AETime.Hour = (CSHORT)((Time & 0x0001f000) >> 12);
|
|
|
|
AETime.Day = (CSHORT)((Date & 0x0000001f) >> 0);
|
|
AETime.Month = (CSHORT)((Date & 0x000001e0) >> 5);
|
|
AETime.Year = (CSHORT)((Date & 0xfffffe00) >> 9);
|
|
|
|
AETime.Milliseconds = 0; // info is not available
|
|
AETime.Weekday = 7; // info is not available - set out of range
|
|
|
|
return(&AETime);
|
|
}
|
|
|
|
|
|
ULONG
|
|
AEGetRelativeTime(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the time in seconds since some arbitrary starting point.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Time in seconds since some arbitrary starting point.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG TimerTicks;
|
|
|
|
TimerTicks = GET_COUNTER();
|
|
|
|
return((TimerTicks*10) / 182);
|
|
}
|
|
|
|
|
|
VOID
|
|
AEReboot(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reboots the machine.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Does not return
|
|
|
|
--*/
|
|
|
|
{
|
|
REBOOT_PROCESSOR();
|
|
}
|
|
|
|
|
|
|
|
ARC_STATUS
|
|
HardDiskPartitionOpen(
|
|
IN ULONG FileId,
|
|
IN ULONG DiskId,
|
|
IN UCHAR PartitionNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine opens the specified partition and sets the partition info
|
|
in the FileTable at the specified index. It does not fill in the
|
|
Device Entry table.
|
|
|
|
It reads the partition information until the requested partition
|
|
is found or no more partitions are defined.
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies the file id for the file table entry.
|
|
|
|
DiskId - Supplies the file id for the physical device.
|
|
|
|
PartitionNumber - Supplies the zero-based partition number
|
|
|
|
Return Value:
|
|
|
|
If a valid partition is found on the hard disk, then ESUCCESS is
|
|
returned. Otherwise, EIO is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
USHORT DataBuffer[SECTOR_SIZE / sizeof(USHORT)];
|
|
PPARTITION_DESCRIPTOR Partition;
|
|
ULONG PartitionLength;
|
|
ULONG StartingSector;
|
|
ULONG VolumeOffset;
|
|
ARC_STATUS Status;
|
|
BOOLEAN PrimaryPartitionTable;
|
|
ULONG PartitionOffset=0;
|
|
ULONG PartitionIndex,PartitionCount=0;
|
|
ULONG Count;
|
|
LARGE_INTEGER SeekPosition;
|
|
|
|
BlFileTable[FileId].u.PartitionContext.DiskId=(UCHAR)DiskId;
|
|
BlFileTable[FileId].Position.QuadPart=0;
|
|
|
|
VolumeOffset=0;
|
|
PrimaryPartitionTable=TRUE;
|
|
|
|
//
|
|
// Change to a 1-based partition number
|
|
//
|
|
PartitionNumber++;
|
|
|
|
do {
|
|
SeekPosition.QuadPart = (ULONGLONG)PartitionOffset * (ULONGLONG)SECTOR_SIZE;
|
|
Status = (BlFileTable[DiskId].DeviceEntryTable->Seek)(DiskId,
|
|
&SeekPosition,
|
|
SeekAbsolute );
|
|
if (Status != ESUCCESS) {
|
|
return(Status);
|
|
}
|
|
|
|
Status = (BlFileTable[DiskId].DeviceEntryTable->Read)(DiskId,
|
|
DataBuffer,
|
|
SECTOR_SIZE,
|
|
&Count );
|
|
|
|
if (Status != ESUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// If sector zero is not a master boot record, then return failure
|
|
// status. Otherwise return success.
|
|
//
|
|
|
|
if (DataBuffer[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE) {
|
|
#if DBG
|
|
BlPrint(TEXT("Boot record signature %x not found (%x found)\r\n"),
|
|
BOOT_RECORD_SIGNATURE,
|
|
DataBuffer[BOOT_SIGNATURE_OFFSET] );
|
|
#endif
|
|
Status = EIO;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Read the partition information until the four entries are
|
|
// checked or until we found the requested one.
|
|
//
|
|
Partition = (PPARTITION_DESCRIPTOR)&DataBuffer[PARTITION_TABLE_OFFSET];
|
|
|
|
for (PartitionIndex=0;
|
|
PartitionIndex < NUM_PARTITION_TABLE_ENTRIES;
|
|
PartitionIndex++,Partition++) {
|
|
|
|
//
|
|
// Count first the partitions in the MBR. The units
|
|
// inside the extended partition are counted later.
|
|
//
|
|
if ((Partition->PartitionType != PARTITION_ENTRY_UNUSED) &&
|
|
(Partition->PartitionType != STALE_GPT_PARTITION_ENTRY)
|
|
&& !IsContainerPartition(Partition->PartitionType))
|
|
{
|
|
PartitionCount++; // another partition found.
|
|
}
|
|
|
|
//
|
|
// Check if the requested partition has already been found.
|
|
// set the partition info in the file table and return.
|
|
//
|
|
if (PartitionCount == PartitionNumber) {
|
|
StartingSector = (ULONG)(Partition->StartingSectorLsb0) |
|
|
(ULONG)(Partition->StartingSectorLsb1 << 8) |
|
|
(ULONG)(Partition->StartingSectorMsb0 << 16) |
|
|
(ULONG)(Partition->StartingSectorMsb1 << 24);
|
|
PartitionLength = (ULONG)(Partition->PartitionLengthLsb0) |
|
|
(ULONG)(Partition->PartitionLengthLsb1 << 8) |
|
|
(ULONG)(Partition->PartitionLengthMsb0 << 16) |
|
|
(ULONG)(Partition->PartitionLengthMsb1 << 24);
|
|
BlFileTable[FileId].u.PartitionContext.PartitionLength.QuadPart =
|
|
(PartitionLength << SECTOR_SHIFT);
|
|
BlFileTable[FileId].u.PartitionContext.StartingSector=PartitionOffset + StartingSector;
|
|
|
|
return ESUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If requested partition was not yet found.
|
|
// Look for an extended partition.
|
|
//
|
|
Partition = (PPARTITION_DESCRIPTOR)&DataBuffer[PARTITION_TABLE_OFFSET];
|
|
PartitionOffset = 0;
|
|
|
|
for (PartitionIndex=0;
|
|
PartitionIndex < NUM_PARTITION_TABLE_ENTRIES;
|
|
PartitionIndex++,Partition++) {
|
|
|
|
if (IsContainerPartition(Partition->PartitionType)) {
|
|
StartingSector = (ULONG)(Partition->StartingSectorLsb0) |
|
|
(ULONG)(Partition->StartingSectorLsb1 << 8) |
|
|
(ULONG)(Partition->StartingSectorMsb0 << 16) |
|
|
(ULONG)(Partition->StartingSectorMsb1 << 24);
|
|
PartitionOffset = VolumeOffset+StartingSector;
|
|
|
|
if (PrimaryPartitionTable) {
|
|
VolumeOffset = StartingSector;
|
|
}
|
|
|
|
break; // only one partition can be extended.
|
|
}
|
|
}
|
|
|
|
PrimaryPartitionTable = FALSE;
|
|
} while (PartitionOffset != 0);
|
|
|
|
return EBADF;
|
|
}
|
|
|
|
#if 0
|
|
|
|
VOID
|
|
BlpTranslateDosToArc(
|
|
IN PCHAR DosName,
|
|
OUT PCHAR ArcName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a DOS drive name ("A:" "B:" "C:" etc.) and translates
|
|
it into an ARC name. ("multi(0)disk(0)rdisk(0)partition(1)")
|
|
|
|
N.B. This will always return some sort of name suitable for passing
|
|
to BiosPartitionOpen. The name it constructs may not be an
|
|
actual partition. BiosPartitionOpen is responsible for
|
|
determining whether the partition actually exists.
|
|
|
|
Since no other driver should ever use ARC names beginning with
|
|
"multi(0)disk(0)..." this will not be a problem. (there is no
|
|
way this routine will construct a name that BiosPartitionOpen
|
|
will not open, but some other random driver will grab and
|
|
successfully open)
|
|
|
|
Arguments:
|
|
|
|
DosName - Supplies the DOS name of the drive.
|
|
|
|
ArcName - Returns the ARC name of the drive.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
ARC_STATUS Status;
|
|
ULONG DriveId;
|
|
ULONG PartitionNumber;
|
|
ULONG PartitionCount;
|
|
ULONG Count;
|
|
USHORT DataBuffer[SECTOR_SIZE / sizeof(USHORT)];
|
|
PPARTITION_DESCRIPTOR Partition;
|
|
ULONG PartitionIndex;
|
|
BOOLEAN HasPrimary;
|
|
LARGE_INTEGER SeekPosition;
|
|
|
|
//
|
|
// Eliminate the easy ones first.
|
|
// A: is always "multi(0)disk(0)fdisk(0)partition(0)"
|
|
// B: is always "multi(0)disk(0)fdisk(1)partition(0)"
|
|
// C: is always "multi(0)disk(0)rdisk(0)partition(1)"
|
|
//
|
|
|
|
if (_stricmp(DosName,"A:")==0) {
|
|
strcpy(ArcName,"multi(0)disk(0)fdisk(0)partition(0)");
|
|
return;
|
|
}
|
|
if (_stricmp(DosName,"B:")==0) {
|
|
strcpy(ArcName,"multi(0)disk(0)fdisk(1)partition(0)");
|
|
return;
|
|
}
|
|
if (_stricmp(DosName,"C:")==0) {
|
|
strcpy(ArcName,"multi(0)disk(0)rdisk(0)partition(1)");
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Now things get more unpleasant. If there are two drives, then
|
|
// D: is the primary partition on the second drive. Successive letters
|
|
// are the secondary partitions on the first drive, then back to the
|
|
// second drive when that runs out.
|
|
//
|
|
// The exception to this is when there is no primary partition on the
|
|
// second drive. Then, we letter the partitions on the first driver
|
|
// consecutively, and when those partitions run out, we letter the
|
|
// partitions on the second drive.
|
|
//
|
|
// I have no idea who came up with this wonderful scheme, but we have
|
|
// to live with it.
|
|
//
|
|
|
|
//
|
|
// Try to open the second drive. If this doesn't work, we only have
|
|
// one drive and life is easy.
|
|
//
|
|
Status = ArcOpen("multi(0)disk(0)rdisk(1)partition(0)",
|
|
ArcOpenReadOnly,
|
|
&DriveId );
|
|
|
|
if (Status != ESUCCESS) {
|
|
|
|
//
|
|
// We only have one drive, so whatever drive letter he's requesting
|
|
// has got to be on it.
|
|
//
|
|
|
|
sprintf(ArcName,
|
|
"multi(0)disk(0)rdisk(0)partition(%d)",
|
|
toupper(DosName[0]) - 'C' + 1 );
|
|
|
|
return;
|
|
} else {
|
|
|
|
//
|
|
// Now we read the partition table off the second drive, so we can
|
|
// tell if there is a primary partition or not.
|
|
//
|
|
SeekPosition.QuadPart = 0;
|
|
|
|
Status = ArcSeek(DriveId,
|
|
&SeekPosition,
|
|
SeekAbsolute);
|
|
if (Status != ESUCCESS) {
|
|
ArcName[0]='\0';
|
|
return;
|
|
}
|
|
|
|
Status = ArcRead(DriveId, DataBuffer, SECTOR_SIZE, &Count);
|
|
ArcClose(DriveId);
|
|
|
|
if (Status != ESUCCESS) {
|
|
ArcName[0] = '\0';
|
|
return;
|
|
}
|
|
|
|
HasPrimary = FALSE;
|
|
|
|
Partition = (PPARTITION_DESCRIPTOR)&DataBuffer[PARTITION_TABLE_OFFSET];
|
|
for (PartitionIndex = 0;
|
|
PartitionIndex < NUM_PARTITION_TABLE_ENTRIES;
|
|
PartitionIndex++,Partition++) {
|
|
if (IsRecognizedPartition(Partition->PartitionType)) {
|
|
HasPrimary = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now we have to go through and count
|
|
// the partitions on the first drive. We do this by just constructing
|
|
// ARC names for each successive partition until one BiosPartitionOpen
|
|
// call fails.
|
|
//
|
|
|
|
PartitionCount = 0;
|
|
do {
|
|
++PartitionCount;
|
|
sprintf(ArcName,
|
|
"multi(0)disk(0)rdisk(0)partition(%d)",
|
|
PartitionCount+1);
|
|
|
|
Status = BiosPartitionOpen( ArcName,
|
|
ArcOpenReadOnly,
|
|
&DriveId );
|
|
|
|
if (Status==ESUCCESS) {
|
|
BiosPartitionClose(DriveId);
|
|
}
|
|
} while ( Status == ESUCCESS );
|
|
|
|
PartitionNumber = toupper(DosName[0])-'C' + 1;
|
|
|
|
if (HasPrimary) {
|
|
|
|
//
|
|
// There is Windows NT primary partition on the second drive.
|
|
//
|
|
// If the DosName is "D:" then we know
|
|
// this is the first partition on the second drive.
|
|
//
|
|
|
|
if (_stricmp(DosName,"D:")==0) {
|
|
strcpy(ArcName,"multi(0)disk(0)rdisk(1)partition(1)");
|
|
return;
|
|
}
|
|
|
|
if (PartitionNumber-1 > PartitionCount) {
|
|
PartitionNumber -= PartitionCount;
|
|
sprintf(ArcName,
|
|
"multi(0)disk(0)rdisk(1)partition(%d)",
|
|
PartitionNumber );
|
|
} else {
|
|
sprintf(ArcName,
|
|
"multi(0)disk(0)rdisk(0)partition(%d)",
|
|
PartitionNumber-1);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// There is no primary partition on the second drive, so we
|
|
// consecutively letter the partitions on the first drive,
|
|
// then the second drive.
|
|
//
|
|
|
|
if (PartitionNumber > PartitionCount) {
|
|
PartitionNumber -= PartitionCount;
|
|
sprintf(ArcName,
|
|
"multi(0)disk(0)rdisk(1)partition(%d)",
|
|
PartitionNumber );
|
|
} else {
|
|
sprintf(ArcName,
|
|
"multi(0)disk(0)rdisk(0)partition(%d)",
|
|
PartitionNumber);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
VOID
|
|
FwStallExecution(
|
|
IN ULONG Microseconds
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does a busy wait for a specified number of microseconds (very approximate!)
|
|
|
|
Arguments:
|
|
|
|
Microseconds - Supplies the number of microseconds to busy wait.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
extern EFI_SYSTEM_TABLE *EfiST;
|
|
EfiST->BootServices->Stall( Microseconds );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
FwGetPathMnemonicKey(
|
|
IN PCHAR OpenPath,
|
|
IN PCHAR Mnemonic,
|
|
IN PULONG Key
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine looks for the given Mnemonic in OpenPath.
|
|
If Mnemonic is a component of the path, then it converts the key
|
|
value to an integer wich is returned in Key.
|
|
|
|
Arguments:
|
|
|
|
OpenPath - Pointer to a string that contains an ARC pathname.
|
|
|
|
Mnemonic - Pointer to a string that contains a ARC Mnemonic
|
|
|
|
Key - Pointer to a ULONG where the Key value is stored.
|
|
|
|
|
|
Return Value:
|
|
|
|
FALSE if mnemonic is found in path and a valid key is converted.
|
|
TRUE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
return(BlGetPathMnemonicKey(OpenPath,Mnemonic,Key));
|
|
}
|
|
|
|
|
|
PCONFIGURATION_COMPONENT
|
|
FwAddChild (
|
|
IN PCONFIGURATION_COMPONENT Component,
|
|
IN PCONFIGURATION_COMPONENT NewComponent,
|
|
IN PVOID ConfigurationData OPTIONAL
|
|
)
|
|
{
|
|
ULONG Size;
|
|
PCONFIGURATION_COMPONENT_DATA NewEntry;
|
|
PCONFIGURATION_COMPONENT_DATA Parent;
|
|
|
|
if (Component==NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
Parent = CONTAINING_RECORD(Component,
|
|
CONFIGURATION_COMPONENT_DATA,
|
|
ComponentEntry);
|
|
|
|
Size = sizeof(CONFIGURATION_COMPONENT_DATA) +
|
|
NewComponent->IdentifierLength + 1;
|
|
|
|
NewEntry = BlAllocateHeap(Size);
|
|
if (NewEntry==NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
RtlCopyMemory(&NewEntry->ComponentEntry,
|
|
NewComponent,
|
|
sizeof(CONFIGURATION_COMPONENT));
|
|
NewEntry->ComponentEntry.Identifier = (PUCHAR)(NewEntry+1);
|
|
NewEntry->ComponentEntry.ConfigurationDataLength = 0;
|
|
strncpy(NewEntry->ComponentEntry.Identifier,
|
|
NewComponent->Identifier,
|
|
NewComponent->IdentifierLength);
|
|
|
|
//
|
|
// Add the new component as the first child of its parent.
|
|
//
|
|
NewEntry->Child = NULL;
|
|
NewEntry->Sibling = Parent->Child;
|
|
Parent->Child = NewEntry;
|
|
|
|
return(&NewEntry->ComponentEntry);
|
|
|
|
}
|
|
|
|
PCONFIGURATION_COMPONENT
|
|
FwGetComponent(
|
|
IN PCHAR Pathname
|
|
)
|
|
{
|
|
PCONFIGURATION_COMPONENT Component;
|
|
PCONFIGURATION_COMPONENT MatchComponent;
|
|
PCHAR PathString;
|
|
PCHAR MatchString;
|
|
PCHAR Token;
|
|
ULONG Key;
|
|
|
|
PathString = Pathname;
|
|
|
|
//
|
|
// Get the the root component.
|
|
//
|
|
|
|
MatchComponent = FwGetChild(NULL);
|
|
|
|
//
|
|
// Repeat search for each new match component.
|
|
//
|
|
|
|
do {
|
|
|
|
//
|
|
// Get the first child of the current match component.
|
|
//
|
|
|
|
Component = FwGetChild( MatchComponent );
|
|
|
|
//
|
|
// Search each child of the current match component for the next match.
|
|
//
|
|
|
|
while ( Component != NULL ) {
|
|
|
|
//
|
|
// Reset Token to be the current position on the pathname.
|
|
//
|
|
|
|
Token = PathString;
|
|
|
|
MatchString = MnemonicTable[Component->Type];
|
|
|
|
//
|
|
// Compare strings.
|
|
//
|
|
|
|
while (*MatchString == tolower(*Token)) {
|
|
MatchString++;
|
|
Token++;
|
|
}
|
|
|
|
//
|
|
// Strings compare if the first mismatch is the terminator for
|
|
// each.
|
|
//
|
|
|
|
if ((*MatchString == 0) && (*Token == '(')) {
|
|
|
|
//
|
|
// Form key.
|
|
//
|
|
|
|
Key = 0;
|
|
Token++;
|
|
while ((*Token != ')') && (*Token != 0)) {
|
|
Key = (Key * 10) + *Token++ - '0';
|
|
}
|
|
|
|
//
|
|
// If the key matches the component matches, so update
|
|
// pointers and break.
|
|
//
|
|
|
|
if (Component->Key == Key) {
|
|
PathString = Token + 1;
|
|
MatchComponent = Component;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Component = FwGetPeer( Component );
|
|
}
|
|
|
|
} while ((Component != NULL) && (*PathString != 0));
|
|
|
|
return MatchComponent;
|
|
}
|
|
|