2964 lines
74 KiB
C
2964 lines
74 KiB
C
/*++
|
||
|
||
|
||
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:
|
||
|
||
John Vert (jvert) 13-Jun-1991
|
||
|
||
Environment:
|
||
|
||
x86 only
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "arccodes.h"
|
||
#include "bootx86.h"
|
||
#include "ntdddisk.h"
|
||
#include "string.h"
|
||
#include "stdio.h"
|
||
#include "stdlib.h"
|
||
#include "scsi.h"
|
||
#include "scsiboot.h"
|
||
#include "ramdisk.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))
|
||
|
||
BOOLEAN AEBiosDisabled = FALSE;
|
||
|
||
// spew UTF8 data over the headless port on FE builds.
|
||
#define UTF8_CLIENT_SUPPORT (1)
|
||
|
||
|
||
BOOLEAN AEArcDiskInformationInitialized = FALSE;
|
||
ARC_DISK_INFORMATION AEArcDiskInformation;
|
||
|
||
PDRIVER_UNLOAD AEDriverUnloadRoutine = NULL;
|
||
|
||
#define PORT_BUFFER_SIZE 10
|
||
UCHAR PortBuffer[PORT_BUFFER_SIZE];
|
||
ULONG PortBufferStart = 0;
|
||
ULONG PortBufferEnd = 0;
|
||
|
||
//
|
||
// Macro for aligning buffers. It returns the aligned pointer into the
|
||
// buffer. Buffer should be of size you want to use + alignment.
|
||
//
|
||
|
||
#define ALIGN_BUFFER_ON_BOUNDARY(Buffer,Alignment) ((PVOID) \
|
||
((((ULONG_PTR)(Buffer) + (Alignment) - 1)) & (~((ULONG_PTR)(Alignment) - 1))))
|
||
|
||
//
|
||
// Miniport DriverEntry typedef
|
||
//
|
||
|
||
typedef NTSTATUS
|
||
(*PDRIVER_ENTRY) (
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PVOID Parameter2
|
||
);
|
||
|
||
typedef
|
||
BOOLEAN
|
||
(*PFWNODE_CALLBACK)(
|
||
IN PCONFIGURATION_COMPONENT FoundComponent
|
||
);
|
||
|
||
//
|
||
// Private function prototypes
|
||
//
|
||
|
||
ARC_STATUS
|
||
BlArcNotYetImplemented(
|
||
IN ULONG FileId
|
||
);
|
||
|
||
PCONFIGURATION_COMPONENT
|
||
AEComponentInfo(
|
||
IN PCONFIGURATION_COMPONENT Current
|
||
);
|
||
|
||
PCONFIGURATION_COMPONENT
|
||
FwGetChild(
|
||
IN PCONFIGURATION_COMPONENT Current
|
||
);
|
||
|
||
BOOLEAN
|
||
FwSearchTree(
|
||
IN PCONFIGURATION_COMPONENT Node,
|
||
IN CONFIGURATION_CLASS Class,
|
||
IN CONFIGURATION_TYPE Type,
|
||
IN ULONG Key,
|
||
IN PFWNODE_CALLBACK CallbackRoutine
|
||
);
|
||
|
||
PCHAR
|
||
AEGetEnvironment(
|
||
IN PCHAR Variable
|
||
);
|
||
|
||
PCONFIGURATION_COMPONENT
|
||
FwGetPeer(
|
||
IN PCONFIGURATION_COMPONENT Current
|
||
);
|
||
|
||
BOOLEAN
|
||
AEEnumerateDisks(
|
||
PCONFIGURATION_COMPONENT Disk
|
||
);
|
||
|
||
VOID
|
||
AEGetArcDiskInformation(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
AEGetPathnameFromComponent(
|
||
IN PCONFIGURATION_COMPONENT Component,
|
||
OUT PCHAR ArcName
|
||
);
|
||
|
||
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
|
||
);
|
||
|
||
BOOLEAN
|
||
AEReadDiskSignature(
|
||
IN PCHAR DiskName,
|
||
IN BOOLEAN IsCdRom
|
||
);
|
||
|
||
//
|
||
// 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;
|
||
|
||
|
||
|
||
//
|
||
// This table provides a quick lookup conversion between ASCII values
|
||
// that fall between 128 and 255, and their UNICODE counterpart.
|
||
//
|
||
// Note that ASCII values between 0 and 127 are equvilent to their
|
||
// unicode counter parts, so no lookups would be required.
|
||
//
|
||
// Therefore when using this table, remove the high bit from the ASCII
|
||
// value and use the resulting value as an offset into this array. For
|
||
// example, 0x80 ->(remove the high bit) 00 -> 0x00C7.
|
||
//
|
||
USHORT PcAnsiToUnicode[0xFF] = {
|
||
0x00C7,
|
||
0x00FC,
|
||
0x00E9,
|
||
0x00E2,
|
||
0x00E4,
|
||
0x00E0,
|
||
0x00E5,
|
||
0x0087,
|
||
0x00EA,
|
||
0x00EB,
|
||
0x00E8,
|
||
0x00EF,
|
||
0x00EE,
|
||
0x00EC,
|
||
0x00C4,
|
||
0x00C5,
|
||
0x00C9,
|
||
0x00E6,
|
||
0x00C6,
|
||
0x00F4,
|
||
0x00F6,
|
||
0x00F2,
|
||
0x00FB,
|
||
0x00F9,
|
||
0x00FF,
|
||
0x00D6,
|
||
0x00DC,
|
||
0x00A2,
|
||
0x00A3,
|
||
0x00A5,
|
||
0x20A7,
|
||
0x0192,
|
||
0x00E1,
|
||
0x00ED,
|
||
0x00F3,
|
||
0x00FA,
|
||
0x00F1,
|
||
0x00D1,
|
||
0x00AA,
|
||
0x00BA,
|
||
0x00BF,
|
||
0x2310,
|
||
0x00AC,
|
||
0x00BD,
|
||
0x00BC,
|
||
0x00A1,
|
||
0x00AB,
|
||
0x00BB,
|
||
0x2591,
|
||
0x2592,
|
||
0x2593,
|
||
0x2502,
|
||
0x2524,
|
||
0x2561,
|
||
0x2562,
|
||
0x2556,
|
||
0x2555,
|
||
0x2563,
|
||
0x2551,
|
||
0x2557,
|
||
0x255D,
|
||
0x255C,
|
||
0x255B,
|
||
0x2510,
|
||
0x2514,
|
||
0x2534,
|
||
0x252C,
|
||
0x251C,
|
||
0x2500,
|
||
0x253C,
|
||
0x255E,
|
||
0x255F,
|
||
0x255A,
|
||
0x2554,
|
||
0x2569,
|
||
0x2566,
|
||
0x2560,
|
||
0x2550,
|
||
0x256C,
|
||
0x2567,
|
||
0x2568,
|
||
0x2564,
|
||
0x2565,
|
||
0x2559,
|
||
0x2558,
|
||
0x2552,
|
||
0x2553,
|
||
0x256B,
|
||
0x256A,
|
||
0x2518,
|
||
0x250C,
|
||
0x2588,
|
||
0x2584,
|
||
0x258C,
|
||
0x2590,
|
||
0x2580,
|
||
0x03B1,
|
||
0x00DF,
|
||
0x0393,
|
||
0x03C0,
|
||
0x03A3,
|
||
0x03C3,
|
||
0x00B5,
|
||
0x03C4,
|
||
0x03A6,
|
||
0x0398,
|
||
0x03A9,
|
||
0x03B4,
|
||
0x221E,
|
||
0x03C6,
|
||
0x03B5,
|
||
0x2229,
|
||
0x2261,
|
||
0x00B1,
|
||
0x2265,
|
||
0x2264,
|
||
0x2320,
|
||
0x2321,
|
||
0x00F7,
|
||
0x2248,
|
||
0x00B0,
|
||
0x2219,
|
||
0x00B7,
|
||
0x221A,
|
||
0x207F,
|
||
0x00B2,
|
||
0x25A0,
|
||
0x00A0
|
||
};
|
||
|
||
|
||
|
||
VOID
|
||
AEInitializeStall(
|
||
VOID
|
||
)
|
||
{
|
||
FwStallCounter = GET_STALL_COUNT();
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
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;
|
||
PKLDR_DATA_TABLE_ENTRY DriverDataTableEntry;
|
||
PDRIVER_ENTRY Entry;
|
||
extern MEMORY_DESCRIPTOR MDArray[];
|
||
|
||
ScsiPortCount = 0;
|
||
|
||
FwStallCounter = GET_STALL_COUNT();
|
||
Status = BlLoadImage(DriveId,
|
||
MemoryFirmwarePermanent,
|
||
"\\NTBOOTDD.SYS",
|
||
TARGET_IMAGE,
|
||
&ImageBase);
|
||
|
||
if (Status != ESUCCESS) {
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// Find the memory descriptor for this entry in the table in the loader
|
||
// block and then allocate it in the MD array.
|
||
//
|
||
|
||
{
|
||
ULONG imageBasePage;
|
||
ULONG imageEndPage = 0;
|
||
PLIST_ENTRY entry;
|
||
|
||
imageBasePage = (((ULONG)ImageBase) & 0x7fffffff) >> PAGE_SHIFT;
|
||
|
||
entry = BlLoaderBlock->MemoryDescriptorListHead.Flink;
|
||
|
||
while(entry != &(BlLoaderBlock->MemoryDescriptorListHead)) {
|
||
PMEMORY_ALLOCATION_DESCRIPTOR descriptor;
|
||
|
||
descriptor = CONTAINING_RECORD(entry,
|
||
MEMORY_ALLOCATION_DESCRIPTOR,
|
||
ListEntry);
|
||
|
||
if(descriptor->BasePage == imageBasePage) {
|
||
imageEndPage = imageBasePage + descriptor->PageCount;
|
||
break;
|
||
}
|
||
|
||
entry = entry->Flink;
|
||
}
|
||
|
||
if(imageEndPage == 0) {
|
||
return EINVAL;
|
||
}
|
||
|
||
Status = MempAllocDescriptor(imageBasePage,
|
||
imageEndPage,
|
||
MemoryFirmwareTemporary);
|
||
|
||
if(Status != ESUCCESS) {
|
||
return EINVAL;
|
||
}
|
||
}
|
||
|
||
Status = BlAllocateDataTableEntry("NTBOOTDD.SYS",
|
||
"\\NTBOOTDD.SYS",
|
||
ImageBase,
|
||
&DriverDataTableEntry);
|
||
if (Status != ESUCCESS) {
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// [ChuckL 2001-Dec-04]
|
||
// BlAllocateDataTableEntry inserts the data table entry for NTBOOTDD.SYS
|
||
// into BlLoaderBlock->LoadOrderListHead. We don't want this, for at least
|
||
// two reasons:
|
||
//
|
||
// 1) This entry is only temporarily loaded for use by the loader. We
|
||
// don't want the kernel to think that it's loaded.
|
||
//
|
||
// 2) There is code in the kernel (MM) that assumes that the first two
|
||
// entries in the list are the kernel and HAL. But we've just
|
||
// inserted ntbootdd.sys as the first entry. This really screws up
|
||
// MM, because it ends up moving the HAL as if it were a loaded
|
||
// driver.
|
||
//
|
||
// Prior to a change to boot\bldr\osloader.c, the routine BlMemoryInitialize()
|
||
// was called twice during loader init. The second call occurred after ntbootdd
|
||
// was loaded, and reinitialized the LoadOrderListHead, thereby eliminating (by
|
||
// accident) ntbootdd from the module list. Now we don't do the second memory
|
||
// initialization, so we have to explicitly remove ntbootdd from the list.
|
||
//
|
||
|
||
RemoveEntryList(&DriverDataTableEntry->InLoadOrderLinks);
|
||
|
||
//
|
||
// Scan the import table and bind to osloader
|
||
//
|
||
|
||
Status = BlScanOsloaderBoundImportTable(DriverDataTableEntry);
|
||
|
||
if (Status != ESUCCESS) {
|
||
return(Status);
|
||
}
|
||
|
||
Entry = (PDRIVER_ENTRY)DriverDataTableEntry->EntryPoint;
|
||
|
||
//
|
||
// Before calling into the driver we need to collect ARC info blocks
|
||
// for all the bios based devices.
|
||
//
|
||
|
||
AEGetArcDiskInformation();
|
||
|
||
//
|
||
// Zero out the driver object.
|
||
//
|
||
|
||
Status = (*Entry)(NULL, NULL);
|
||
|
||
if (Status == ESUCCESS) {
|
||
|
||
Buffer = FwAllocateHeap(SIZE_FOR_SUPPORTED_DISK_STRUCTURE);
|
||
|
||
if(Buffer == NULL) {
|
||
return ENOMEM;
|
||
}
|
||
|
||
HardDiskInitialize(Buffer, SUPPORTED_NUMBER_OF_DISKS, NULL);
|
||
}
|
||
|
||
if(Status == ESUCCESS) {
|
||
AEBiosDisabled = TRUE;
|
||
}
|
||
return(Status);
|
||
}
|
||
|
||
|
||
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 MEMORY_DESCRIPTOR MDArray[];
|
||
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
|
||
|
||
--*/
|
||
|
||
{
|
||
BlPrint("ERROR - Unimplemented Firmware Vector called (FID %lx)\n",
|
||
FileId );
|
||
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 (_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);
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
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 = RamdiskOpen( OpenPath,
|
||
OpenMode,
|
||
FileId );
|
||
|
||
if (Status == ESUCCESS) {
|
||
return(ESUCCESS);
|
||
}
|
||
|
||
Status = BiosConsoleOpen( OpenPath,
|
||
OpenMode,
|
||
FileId );
|
||
|
||
if (Status == ESUCCESS) {
|
||
return(ESUCCESS);
|
||
}
|
||
|
||
//
|
||
// Once a disk driver has been loaded we need to disable bios access to
|
||
// all drives to avoid mixing bios & driver i/o operations.
|
||
//
|
||
|
||
if(AEBiosDisabled == FALSE) {
|
||
Status = BiosPartitionOpen( OpenPath,
|
||
OpenMode,
|
||
FileId );
|
||
|
||
if (Status == ESUCCESS) {
|
||
return(ESUCCESS);
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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);
|
||
}
|
||
|
||
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) {
|
||
|
||
//
|
||
// Give priority to dumb terminal
|
||
//
|
||
if (BlIsTerminalConnected() && (PortBufferStart != PortBufferEnd)) {
|
||
return(ESUCCESS);
|
||
}
|
||
|
||
if (BlIsTerminalConnected() && (BlPortPollOnly(BlTerminalDeviceId) == CP_GET_SUCCESS)) {
|
||
return(ESUCCESS);
|
||
}
|
||
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 console input
|
||
//
|
||
if (FileId == 0) {
|
||
|
||
RetryRead:
|
||
|
||
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 '9': // F9 key
|
||
PortBuffer[PortBufferEnd] = 'O';
|
||
PortBufferEnd++;
|
||
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
||
PortBuffer[PortBufferEnd] = 'p';
|
||
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 '7': // F7 key
|
||
PortBuffer[PortBufferEnd] = 'O';
|
||
PortBufferEnd++;
|
||
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
||
PortBuffer[PortBufferEnd] = 'q';
|
||
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 '4': // F4 key
|
||
PortBuffer[PortBufferEnd] = 'O';
|
||
PortBufferEnd++;
|
||
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
||
PortBuffer[PortBufferEnd] = 'x';
|
||
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 '2': // F2 key
|
||
PortBuffer[PortBufferEnd] = 'O';
|
||
PortBufferEnd++;
|
||
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
||
PortBuffer[PortBufferEnd] = 'Q';
|
||
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 'H': // Home key
|
||
case 'h': // Home key
|
||
PortBuffer[PortBufferEnd] = 'H';
|
||
PortBufferEnd++;
|
||
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
||
break;
|
||
|
||
case 'K': // End key
|
||
case 'k': // End key
|
||
PortBuffer[PortBufferEnd] = 'K';
|
||
PortBufferEnd++;
|
||
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
||
break;
|
||
|
||
case '+': // Insert key
|
||
PortBuffer[PortBufferEnd] = '@';
|
||
PortBufferEnd++;
|
||
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
||
break;
|
||
|
||
case '-': // Del key
|
||
PortBuffer[PortBufferEnd] = 'P';
|
||
PortBufferEnd++;
|
||
PortBufferEnd = PortBufferEnd % PORT_BUFFER_SIZE;
|
||
break;
|
||
|
||
case (UCHAR)TAB_KEY: // Tab key
|
||
PortBuffer[PortBufferEnd] = (UCHAR)TAB_KEY;
|
||
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);
|
||
}
|
||
|
||
}
|
||
|
||
if (BiosConsoleReadStatus(FileId) == ESUCCESS) {
|
||
return(BiosConsoleRead(FileId,Buffer,Length,Count));
|
||
}
|
||
|
||
goto RetryRead;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Declare a local 64KB aligned buffer, so we don't have to
|
||
// break up I/Os of size less than 64KB, because the buffer
|
||
// crosses a 64KB boundary.
|
||
//
|
||
static PCHAR AlignedBuf = 0;
|
||
BOOLEAN fUseAlignedBuf;
|
||
|
||
//
|
||
// Initialize the AlignedBuf once from the pool.
|
||
//
|
||
|
||
if (!AlignedBuf) {
|
||
AlignedBuf = FwAllocatePool(128 * 1024);
|
||
AlignedBuf = ALIGN_BUFFER_ON_BOUNDARY(AlignedBuf, 64 * 1024);
|
||
}
|
||
|
||
*Count = 0;
|
||
|
||
do {
|
||
fUseAlignedBuf = FALSE;
|
||
|
||
if (((ULONG) Buffer & 0xffff0000) !=
|
||
(((ULONG) Buffer + Length - 1) & 0xffff0000)) {
|
||
|
||
//
|
||
// If the buffer crosses the 64KB boundary use our
|
||
// aligned buffer instead. If we don't have an aligned
|
||
// buffer, adjust the read size.
|
||
//
|
||
|
||
if (AlignedBuf) {
|
||
fUseAlignedBuf = TRUE;
|
||
|
||
//
|
||
// We can read max 64KB into our aligned
|
||
// buffer.
|
||
//
|
||
|
||
Limit = Length;
|
||
|
||
if (Limit > (ULONG) 0xFFFF) {
|
||
Limit = (ULONG) 0xFFFF;
|
||
}
|
||
|
||
} else {
|
||
Limit = (64 * 1024) - ((ULONG_PTR) Buffer & 0xFFFF);
|
||
}
|
||
|
||
} else {
|
||
|
||
Limit = Length;
|
||
}
|
||
|
||
Status = (BlFileTable[FileId].DeviceEntryTable->Read)( FileId,
|
||
(fUseAlignedBuf) ? AlignedBuf : Buffer,
|
||
Limit,
|
||
&PartCount );
|
||
|
||
//
|
||
// If we used our aligned buffer, copy the read data
|
||
// to the callers buffer.
|
||
//
|
||
|
||
if (fUseAlignedBuf) {
|
||
RtlCopyMemory(Buffer, AlignedBuf, PartCount);
|
||
}
|
||
|
||
*Count += PartCount;
|
||
Length -= Limit;
|
||
(PCHAR) Buffer += Limit;
|
||
|
||
if (Status != ESUCCESS) {
|
||
#if DBG
|
||
BlPrint("Disk I/O error: Status = %lx\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;
|
||
PUCHAR TmpBuffer, String;
|
||
UCHAR Char;
|
||
USHORT WChar;
|
||
|
||
//
|
||
// Special case for console output
|
||
//
|
||
if (FileId == 1) {
|
||
|
||
if (BlIsTerminalConnected()) {
|
||
BOOLEAN InTerminalEscape = FALSE;
|
||
|
||
//
|
||
// Translate ANSI codes to vt100 escape sequences
|
||
//
|
||
TmpBuffer = (PUCHAR)Buffer;
|
||
Limit = Length;
|
||
if (Length == 4) {
|
||
if (strncmp(TmpBuffer, "\033[2J", Length)==0) {
|
||
//
|
||
// <CSI>2J turns into <CSI>H<CSI>J
|
||
//
|
||
// (erase entire screen)
|
||
//
|
||
TmpBuffer = "\033[H\033[J";
|
||
Limit = 6;
|
||
} else if (strncmp(TmpBuffer, "\033[0J", Length)==0) {
|
||
//
|
||
// <CSI>0J turns into <CSI>J
|
||
//
|
||
// (erase to end of screen)
|
||
//
|
||
TmpBuffer = "\033[J";
|
||
Limit = 3;
|
||
} else if (strncmp(TmpBuffer, "\033[0K", Length)==0) {
|
||
//
|
||
// <CSI>0K turns into <CSI>K
|
||
//
|
||
// (erase to end of the line)
|
||
//
|
||
TmpBuffer = "\033[K";
|
||
Limit = 3;
|
||
} else if (strncmp(TmpBuffer, "\033[0m", Length)==0) {
|
||
//
|
||
// <CSI>0m turns into <CSI>m
|
||
//
|
||
// (turn attributes off)
|
||
//
|
||
TmpBuffer = "\033[m";
|
||
Limit = 3;
|
||
}
|
||
}
|
||
|
||
//
|
||
// loop through the string to be output, printing data to the
|
||
// headless terminal.
|
||
//
|
||
String = TmpBuffer;
|
||
for (PartCount = 0; PartCount < Limit; PartCount++, String++) {
|
||
|
||
#if UTF8_CLIENT_SUPPORT
|
||
|
||
//
|
||
// check if we're in a DBCS language. If we are, then we
|
||
// need to translate the characters into UTF8 codes by
|
||
// referencing a lookup table in bootfont.bin
|
||
//
|
||
if (DbcsLangId) {
|
||
UCHAR UTF8Encoding[3];
|
||
ULONG i;
|
||
|
||
if (GrIsDBCSLeadByte(*String)) {
|
||
|
||
//
|
||
// double byte characters have their own separate table
|
||
// from the SBCS characters.
|
||
//
|
||
// we need to advance the string forward 2 characters
|
||
// for double byte characters.
|
||
//
|
||
GetDBCSUtf8Translation(String,UTF8Encoding);
|
||
String += 1;
|
||
PartCount += 1;
|
||
|
||
} else {
|
||
ULONG Bytes;
|
||
LPSTR p;
|
||
//
|
||
// single byte characters have their own separate table
|
||
// from the DBCS characters.
|
||
//
|
||
GetSBCSUtf8Translation(String,UTF8Encoding);
|
||
}
|
||
|
||
|
||
for( i = 0; i < 3; i++ ) {
|
||
if( UTF8Encoding[i] != 0 ) {
|
||
BlPortPutByte( BlTerminalDeviceId, UTF8Encoding[i] );
|
||
FwStallExecution(BlTerminalDelay);
|
||
}
|
||
}
|
||
|
||
|
||
} else
|
||
#endif
|
||
{
|
||
//
|
||
// standard ASCII character
|
||
//
|
||
Char = *String;
|
||
#if 1
|
||
//
|
||
// filter some characters that aren't printable in VT100
|
||
// into substitute characters which are printable
|
||
//
|
||
if (Char & 0x80) {
|
||
|
||
switch (Char) {
|
||
case 0xB0: // Light shaded block
|
||
case 0xB3: // Light vertical
|
||
case 0xBA: // Double vertical line
|
||
Char = '|';
|
||
break;
|
||
case 0xB1: // Middle shaded block
|
||
case 0xDC: // Lower half block
|
||
case 0xDD: // Right half block
|
||
case 0xDE: // Left half block
|
||
case 0xDF: // Upper half block
|
||
Char = '%';
|
||
break;
|
||
case 0xB2: // Dark shaded block
|
||
case 0xDB: // Full block
|
||
Char = '#';
|
||
break;
|
||
case 0xA9: // Reversed NOT sign
|
||
case 0xAA: // NOT sign
|
||
case 0xBB: // '<27>'
|
||
case 0xBC: // '<27>'
|
||
case 0xBF: // '<27>'
|
||
case 0xC0: // '<27>'
|
||
case 0xC8: // '<27>'
|
||
case 0xC9: // '<27>'
|
||
case 0xD9: // '<27>'
|
||
case 0xDA: // '<27>'
|
||
Char = '+';
|
||
break;
|
||
case 0xC4: // '<27>'
|
||
Char = '-';
|
||
break;
|
||
case 0xCD: // '<27>'
|
||
Char = '=';
|
||
break;
|
||
}
|
||
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// If the high-bit is still set, and we're here, then we know we're
|
||
// not doing DBCS/SBCS characters. We need to convert this
|
||
// 8bit ANSI character into unicode, then UTF8 encode that, then send
|
||
// it over the wire.
|
||
//
|
||
if( Char & 0x80 ) {
|
||
|
||
UCHAR UTF8Encoding[3];
|
||
ULONG i;
|
||
|
||
//
|
||
// Lookup the Unicode equivilent of this 8-bit ANSI value.
|
||
//
|
||
UTF8Encode( PcAnsiToUnicode[(Char & 0x7F)],
|
||
UTF8Encoding );
|
||
|
||
for( i = 0; i < 3; i++ ) {
|
||
if( UTF8Encoding[i] != 0 ) {
|
||
BlPortPutByte( BlTerminalDeviceId, UTF8Encoding[i] );
|
||
FwStallExecution(BlTerminalDelay);
|
||
}
|
||
}
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// write the data to the port. Note that we write an 8 bit
|
||
// character to the terminal, and that the remote display
|
||
// must correctly interpret the code for it to display
|
||
// properly.
|
||
//
|
||
BlPortPutByte(BlTerminalDeviceId, Char);
|
||
FwStallExecution(BlTerminalDelay);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return (BiosConsoleWrite(FileId,Buffer,Length,Count));
|
||
|
||
} else {
|
||
|
||
*Count = 0;
|
||
|
||
do {
|
||
|
||
if (((ULONG) Buffer & 0xffff0000) !=
|
||
(((ULONG) Buffer + Length) & 0xffff0000)) {
|
||
|
||
Limit = 0x10000 - ((ULONG) 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("AERead: Status = %lx\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
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG DriveId;
|
||
ULONG Status;
|
||
|
||
TextGrTerminate();
|
||
|
||
//
|
||
// HACKHACK John Vert (jvert)
|
||
// Some SCSI drives get really confused and return zeroes when
|
||
// you use the BIOS to query their size after the AHA driver has
|
||
// initialized. This can completely tube OS/2 or DOS. So here
|
||
// we try and open both BIOS-accessible hard drives. Our open
|
||
// code is smart enough to retry if it gets back zeros, so hopefully
|
||
// this will give the SCSI drives a chance to get their act together.
|
||
//
|
||
Status = ArcOpen("multi(0)disk(0)rdisk(0)partition(0)",
|
||
ArcOpenReadOnly,
|
||
&DriveId);
|
||
if (Status == ESUCCESS) {
|
||
ArcClose(DriveId);
|
||
}
|
||
|
||
Status = ArcOpen("multi(0)disk(0)rdisk(1)partition(0)",
|
||
ArcOpenReadOnly,
|
||
&DriveId);
|
||
if (Status == ESUCCESS) {
|
||
ArcClose(DriveId);
|
||
}
|
||
|
||
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 = (LONGLONG)PartitionOffset * 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("Boot record signature %x not found (%x found)\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;
|
||
}
|
||
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG FinalCount;
|
||
|
||
FinalCount = Microseconds * FwStallCounter;
|
||
|
||
_asm {
|
||
mov eax,FinalCount
|
||
looptop:
|
||
sub eax,1
|
||
jnz short looptop
|
||
}
|
||
}
|
||
|
||
|
||
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 = FwAllocateHeap(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;
|
||
}
|
||
/**********************
|
||
*
|
||
* The following are just stubs for the MIPS firmware. They all return NULL
|
||
*
|
||
***********************/
|
||
|
||
|
||
|
||
ARC_STATUS
|
||
FwDeleteComponent (
|
||
IN PCONFIGURATION_COMPONENT Component
|
||
)
|
||
{
|
||
return(ESUCCESS);
|
||
}
|
||
|
||
|
||
VOID
|
||
AEGetArcDiskInformation(
|
||
VOID
|
||
)
|
||
{
|
||
InitializeListHead(&(AEArcDiskInformation.DiskSignatures));
|
||
AEArcDiskInformationInitialized = TRUE;
|
||
|
||
//
|
||
// Scan through each node of the hardware tree - look for disk type
|
||
// devices hanging off of multi-function controllers.
|
||
//
|
||
|
||
FwSearchTree(FwGetChild(NULL),
|
||
PeripheralClass,
|
||
DiskPeripheral,
|
||
-1,
|
||
AEEnumerateDisks);
|
||
return;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
FwSearchTree(
|
||
IN PCONFIGURATION_COMPONENT Node,
|
||
IN CONFIGURATION_CLASS Class,
|
||
IN CONFIGURATION_TYPE Type,
|
||
IN ULONG Key,
|
||
IN PFWNODE_CALLBACK CallbackRoutine
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Conduct a depth-first search of the firmware configuration tree starting
|
||
at a given node, looking for nodes that match a given class and type.
|
||
When a matching node is found, call a callback routine.
|
||
|
||
Arguments:
|
||
|
||
CurrentNode - node at which to begin the search.
|
||
|
||
Class - configuration class to match, or -1 to match any class
|
||
|
||
Type - configuration type to match, or -1 to match any class
|
||
|
||
Key - key to match, or -1 to match any key
|
||
|
||
FoundRoutine - pointer to a routine to be called when a node whose
|
||
class and type match the class and type passed in is located.
|
||
The routine takes a pointer to the configuration node and must
|
||
return a boolean indicating whether to continue with the traversal.
|
||
|
||
Return Value:
|
||
|
||
FALSE if the caller should abandon the search.
|
||
--*/
|
||
{
|
||
PCONFIGURATION_COMPONENT child;
|
||
|
||
do {
|
||
if (child = FwGetChild(Node)) {
|
||
if (!FwSearchTree(child,
|
||
Class,
|
||
Type,
|
||
Key,
|
||
CallbackRoutine)) {
|
||
return(FALSE);
|
||
}
|
||
}
|
||
|
||
if (((Class == -1) || (Node->Class == Class)) &&
|
||
((Type == -1) || (Node->Type == Type)) &&
|
||
((Key == (ULONG)-1) || (Node->Key == Key))) {
|
||
|
||
if (!CallbackRoutine(Node)) {
|
||
return(FALSE);
|
||
}
|
||
}
|
||
|
||
Node = FwGetPeer(Node);
|
||
|
||
} while ( Node != NULL );
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
VOID
|
||
AEGetPathnameFromComponent(
|
||
IN PCONFIGURATION_COMPONENT Component,
|
||
OUT PCHAR ArcName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function builds an ARC pathname for the specified component.
|
||
|
||
Arguments:
|
||
|
||
Component - Supplies a pointer to a configuration component.
|
||
|
||
ArcName - Returns the ARC name of the specified component. Caller must
|
||
provide a large enough buffer.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
if (AEGetParent(Component) != NULL) {
|
||
AEGetPathnameFromComponent(AEGetParent(Component),ArcName);
|
||
|
||
//
|
||
// append our segment to the arcname
|
||
//
|
||
|
||
sprintf(ArcName+strlen(ArcName),
|
||
"%s(%d)",
|
||
MnemonicTable[Component->Type],
|
||
Component->Key);
|
||
|
||
} else {
|
||
//
|
||
// We are the parent, initialize the string and return
|
||
//
|
||
ArcName[0] = '\0';
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
AEEnumerateDisks(
|
||
IN PCONFIGURATION_COMPONENT Disk
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Callback routine for enumerating the disks in the ARC firmware tree. It
|
||
reads all the necessary information from the disk to uniquely identify
|
||
it.
|
||
|
||
Arguments:
|
||
|
||
ConfigData - Supplies a pointer to the disk's ARC component data.
|
||
|
||
Return Value:
|
||
|
||
TRUE - continue searching
|
||
|
||
FALSE - stop searching tree.
|
||
|
||
--*/
|
||
|
||
{
|
||
UCHAR path[100] = "";
|
||
ULONG key;
|
||
|
||
AEGetPathnameFromComponent(Disk, path);
|
||
|
||
#if 0
|
||
if((BlGetPathMnemonicKey(path, "multi", &key) == FALSE) ||
|
||
(BlGetPathMnemonicKey(path, "eisa", &key) == FALSE)) {
|
||
DbgPrint("Found multi disk %s\n", path);
|
||
} else {
|
||
DbgPrint("Found disk %s\n", path);
|
||
}
|
||
#endif
|
||
|
||
AEReadDiskSignature(path, FALSE);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
AEReadDiskSignature(
|
||
IN PCHAR DiskName,
|
||
IN BOOLEAN IsCdRom
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given an ARC disk name, reads the MBR and adds its signature to the list of
|
||
disks.
|
||
|
||
Arguments:
|
||
|
||
Diskname - Supplies the name of the disk.
|
||
|
||
IsCdRom - Indicates whether the disk is a CD-ROM.
|
||
|
||
Return Value:
|
||
|
||
TRUE - Success
|
||
|
||
FALSE - Failure
|
||
|
||
--*/
|
||
|
||
{
|
||
PARC_DISK_SIGNATURE signature;
|
||
BOOLEAN status;
|
||
|
||
signature = FwAllocateHeap(sizeof(ARC_DISK_SIGNATURE));
|
||
if (signature==NULL) {
|
||
return(FALSE);
|
||
}
|
||
|
||
signature->ArcName = FwAllocateHeap(strlen(DiskName)+2);
|
||
if (signature->ArcName==NULL) {
|
||
return(FALSE);
|
||
}
|
||
|
||
status = BlGetDiskSignature(DiskName, IsCdRom, signature);
|
||
if (status) {
|
||
InsertHeadList(&(AEArcDiskInformation.DiskSignatures),
|
||
&(signature->ListEntry));
|
||
|
||
}
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
BlFindDiskSignature(
|
||
IN PCHAR DiskName,
|
||
IN PARC_DISK_SIGNATURE Signature
|
||
)
|
||
{
|
||
PARC_DISK_SIGNATURE match;
|
||
UCHAR buffer[] = "multi(xxx)disk(xxx)rdisk(xxx)";
|
||
|
||
if(AEArcDiskInformationInitialized == FALSE) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// If the disk name passed in contains an eisa component then convert
|
||
// the entire string into one with a multi component.
|
||
//
|
||
|
||
if(strncmp(DiskName, "eisa", strlen("eisa")) == 0) {
|
||
strcpy(&(buffer[1]), DiskName);
|
||
RtlCopyMemory(buffer, "multi", 5);
|
||
DiskName = buffer;
|
||
}
|
||
|
||
match = CONTAINING_RECORD(AEArcDiskInformation.DiskSignatures.Flink,
|
||
ARC_DISK_SIGNATURE,
|
||
ListEntry);
|
||
|
||
|
||
while(&(match->ListEntry) != &(AEArcDiskInformation.DiskSignatures)) {
|
||
|
||
if(strcmp(DiskName, match->ArcName) == 0) {
|
||
|
||
PCHAR c;
|
||
|
||
//
|
||
// We found a match. Copy all the information out of this node.
|
||
//
|
||
|
||
// DbgPrint("BlFindDiskSignature found a match for %s - %#08lx\n", DiskName, match);
|
||
|
||
Signature->CheckSum = match->CheckSum;
|
||
Signature->Signature = match->Signature;
|
||
Signature->ValidPartitionTable = match->ValidPartitionTable;
|
||
|
||
strcpy(Signature->ArcName, match->ArcName);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
match = CONTAINING_RECORD(match->ListEntry.Flink,
|
||
ARC_DISK_SIGNATURE,
|
||
ListEntry);
|
||
}
|
||
|
||
DbgPrint("BlFindDiskSignature found no match for %s\n", DiskName);
|
||
return FALSE;
|
||
}
|
||
|
||
VOID
|
||
AETerminateIo(
|
||
VOID
|
||
)
|
||
{
|
||
if(AEDriverUnloadRoutine != NULL) {
|
||
AEDriverUnloadRoutine(NULL);
|
||
}
|
||
return;
|
||
}
|