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.
2441 lines
66 KiB
2441 lines
66 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
biosdrv.c
|
|
|
|
Abstract:
|
|
|
|
Provides the ARC emulation routines for I/O to a device supported by
|
|
real-mode INT 13h BIOS calls.
|
|
|
|
Author:
|
|
|
|
John Vert (jvert) 7-Aug-1991
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "arccodes.h"
|
|
#include "bootx86.h"
|
|
|
|
#include "stdlib.h"
|
|
#include "string.h"
|
|
|
|
#include "flop.h"
|
|
|
|
|
|
#if 0
|
|
#define DBGOUT(x) BlPrint x
|
|
#define DBGPAUSE while(!GET_KEY());
|
|
#else
|
|
#define DBGOUT(x)
|
|
#define DBGPAUSE
|
|
#endif
|
|
|
|
//
|
|
// Defines for buffer alignment and boundary checks in BiosDisk*
|
|
// functions.
|
|
//
|
|
|
|
#define BIOSDISK_1MB (1 * 1024 * 1024)
|
|
#define BIOSDISK_64KB (64 * 1024)
|
|
#define BIOSDISK_64KB_MASK (~(BIOSDISK_64KB - 1))
|
|
|
|
//
|
|
// Definitions for caching the last read sector.
|
|
//
|
|
|
|
#define BL_LAST_SECTOR_CACHE_MAX_SIZE 4096
|
|
|
|
typedef struct _BL_LAST_SECTOR_CACHE
|
|
{
|
|
BOOLEAN Initialized;
|
|
BOOLEAN Valid;
|
|
ULONG DeviceId;
|
|
ULONGLONG SectorNumber;
|
|
PUCHAR Data;
|
|
} BL_LAST_SECTOR_CACHE, *PBL_LAST_SECTOR_CACHE;
|
|
|
|
//
|
|
// This is the global variable used for caching the last sector read
|
|
// from the last disk. Callers who access files sequentially but do
|
|
// not make sure their offsets are sector aligned end up reading the
|
|
// last sector they read again with their next request. This solves
|
|
// the problem. Its Data buffer is allocated from the pool at the
|
|
// first disk read. It is setup and used in BiosDiskRead, invalidated
|
|
// in BiosDiskWrite.
|
|
//
|
|
|
|
BL_LAST_SECTOR_CACHE FwLastSectorCache = {0};
|
|
|
|
//
|
|
// defines for doing console I/O
|
|
//
|
|
#define CSI 0x95
|
|
#define SGR_INVERSE 7
|
|
#define SGR_NORMAL 0
|
|
|
|
//
|
|
// static data for console I/O
|
|
//
|
|
BOOLEAN ControlSequence=FALSE;
|
|
BOOLEAN EscapeSequence=FALSE;
|
|
BOOLEAN FontSelection=FALSE;
|
|
BOOLEAN HighIntensity=FALSE;
|
|
BOOLEAN Blink=FALSE;
|
|
ULONG PCount=0;
|
|
|
|
#define CONTROL_SEQUENCE_MAX_PARAMETER 10
|
|
ULONG Parameter[CONTROL_SEQUENCE_MAX_PARAMETER];
|
|
|
|
#define KEY_INPUT_BUFFER_SIZE 16
|
|
UCHAR KeyBuffer[KEY_INPUT_BUFFER_SIZE];
|
|
ULONG KeyBufferEnd=0;
|
|
ULONG KeyBufferStart=0;
|
|
|
|
//
|
|
// array for translating between ANSI colors and the VGA standard
|
|
//
|
|
UCHAR TranslateColor[] = {0,4,2,6,1,5,3,7};
|
|
|
|
ARC_STATUS
|
|
BiosDiskClose(
|
|
IN ULONG FileId
|
|
);
|
|
|
|
VOID
|
|
BiosConsoleFillBuffer(
|
|
IN ULONG Key
|
|
);
|
|
|
|
|
|
//
|
|
// There are two sorts of things we can open in this module, disk partitions,
|
|
// and raw disk devices. The following device entry tables are
|
|
// used for these things.
|
|
//
|
|
|
|
BL_DEVICE_ENTRY_TABLE BiosPartitionEntryTable =
|
|
{
|
|
(PARC_CLOSE_ROUTINE)BiosPartitionClose,
|
|
(PARC_MOUNT_ROUTINE)BlArcNotYetImplemented,
|
|
(PARC_OPEN_ROUTINE)BiosPartitionOpen,
|
|
(PARC_READ_ROUTINE)BiosPartitionRead,
|
|
(PARC_READ_STATUS_ROUTINE)BlArcNotYetImplemented,
|
|
(PARC_SEEK_ROUTINE)BiosPartitionSeek,
|
|
(PARC_WRITE_ROUTINE)BiosPartitionWrite,
|
|
(PARC_GET_FILE_INFO_ROUTINE)BiosPartitionGetFileInfo,
|
|
(PARC_SET_FILE_INFO_ROUTINE)BlArcNotYetImplemented,
|
|
(PRENAME_ROUTINE)BlArcNotYetImplemented,
|
|
(PARC_GET_DIRECTORY_ENTRY_ROUTINE)BlArcNotYetImplemented,
|
|
(PBOOTFS_INFO)NULL
|
|
};
|
|
|
|
BL_DEVICE_ENTRY_TABLE BiosDiskEntryTable =
|
|
{
|
|
(PARC_CLOSE_ROUTINE)BiosDiskClose,
|
|
(PARC_MOUNT_ROUTINE)BlArcNotYetImplemented,
|
|
(PARC_OPEN_ROUTINE)BiosDiskOpen,
|
|
(PARC_READ_ROUTINE)BiosDiskRead,
|
|
(PARC_READ_STATUS_ROUTINE)BlArcNotYetImplemented,
|
|
(PARC_SEEK_ROUTINE)BiosPartitionSeek,
|
|
(PARC_WRITE_ROUTINE)BiosDiskWrite,
|
|
(PARC_GET_FILE_INFO_ROUTINE)BiosDiskGetFileInfo,
|
|
(PARC_SET_FILE_INFO_ROUTINE)BlArcNotYetImplemented,
|
|
(PRENAME_ROUTINE)BlArcNotYetImplemented,
|
|
(PARC_GET_DIRECTORY_ENTRY_ROUTINE)BlArcNotYetImplemented,
|
|
(PBOOTFS_INFO)NULL
|
|
};
|
|
|
|
BL_DEVICE_ENTRY_TABLE BiosEDDSEntryTable =
|
|
{
|
|
(PARC_CLOSE_ROUTINE)BiosDiskClose,
|
|
(PARC_MOUNT_ROUTINE)BlArcNotYetImplemented,
|
|
(PARC_OPEN_ROUTINE)BiosDiskOpen,
|
|
(PARC_READ_ROUTINE)BiosElToritoDiskRead,
|
|
(PARC_READ_STATUS_ROUTINE)BlArcNotYetImplemented,
|
|
(PARC_SEEK_ROUTINE)BiosPartitionSeek,
|
|
(PARC_WRITE_ROUTINE)BlArcNotYetImplemented,
|
|
(PARC_GET_FILE_INFO_ROUTINE)BiosDiskGetFileInfo,
|
|
(PARC_SET_FILE_INFO_ROUTINE)BlArcNotYetImplemented,
|
|
(PRENAME_ROUTINE)BlArcNotYetImplemented,
|
|
(PARC_GET_DIRECTORY_ENTRY_ROUTINE)BlArcNotYetImplemented,
|
|
(PBOOTFS_INFO)NULL
|
|
};
|
|
|
|
|
|
ARC_STATUS
|
|
BiosDiskClose(
|
|
IN ULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes the specified device
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies file id of the device to be closed
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - Device closed successfully
|
|
|
|
!ESUCCESS - Device was not closed.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (BlFileTable[FileId].Flags.Open == 0) {
|
|
#if DBG
|
|
BlPrint("ERROR - Unopened fileid %lx closed\n",FileId);
|
|
#endif
|
|
}
|
|
BlFileTable[FileId].Flags.Open = 0;
|
|
|
|
//
|
|
// Invalidate the last read sector cache if it was for this disk.
|
|
//
|
|
if (FwLastSectorCache.DeviceId == FileId) {
|
|
FwLastSectorCache.Valid = FALSE;
|
|
}
|
|
|
|
return(ESUCCESS);
|
|
}
|
|
|
|
ARC_STATUS
|
|
BiosPartitionClose(
|
|
IN ULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes the specified device
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies file id of the device to be closed
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - Device closed successfully
|
|
|
|
!ESUCCESS - Device was not closed.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (BlFileTable[FileId].Flags.Open == 0) {
|
|
#if DBG
|
|
BlPrint("ERROR - Unopened fileid %lx closed\n",FileId);
|
|
#endif
|
|
}
|
|
BlFileTable[FileId].Flags.Open = 0;
|
|
|
|
return(BiosDiskClose((ULONG)BlFileTable[FileId].u.PartitionContext.DiskId));
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
BiosPartitionOpen(
|
|
IN PCHAR OpenPath,
|
|
IN OPEN_MODE OpenMode,
|
|
OUT PULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens the disk partition specified by OpenPath. This routine will open
|
|
floppy drives 0 and 1, and any partition on hard drive 0 or 1.
|
|
|
|
Arguments:
|
|
|
|
OpenPath - Supplies a pointer to the name of the partition. If OpenPath
|
|
is "A:" or "B:" the corresponding floppy drive will be opened.
|
|
If it is "C:" or above, this routine will find the corresponding
|
|
partition on hard drive 0 or 1 and open it.
|
|
|
|
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;
|
|
ULONG DiskFileId;
|
|
UCHAR PartitionNumber;
|
|
ULONG Controller;
|
|
ULONG Key;
|
|
BOOLEAN IsEisa = FALSE;
|
|
|
|
UNREFERENCED_PARAMETER( OpenMode );
|
|
|
|
//
|
|
// BIOS devices are always "multi(0)" (except for EISA flakiness
|
|
// where we treat "eisa(0)..." like "multi(0)..." in floppy cases.
|
|
//
|
|
if(FwGetPathMnemonicKey(OpenPath,"multi",&Key)) {
|
|
|
|
if(FwGetPathMnemonicKey(OpenPath,"eisa", &Key)) {
|
|
return(EBADF);
|
|
} else {
|
|
IsEisa = TRUE;
|
|
}
|
|
}
|
|
|
|
if (Key!=0) {
|
|
return(EBADF);
|
|
}
|
|
|
|
//
|
|
// If we're opening a floppy drive, there are no partitions
|
|
// so we can just return the physical device.
|
|
//
|
|
|
|
if((_stricmp(OpenPath,"multi(0)disk(0)fdisk(0)partition(0)") == 0) ||
|
|
(_stricmp(OpenPath,"eisa(0)disk(0)fdisk(0)partition(0)" ) == 0))
|
|
{
|
|
return(BiosDiskOpen( 0, 0, FileId));
|
|
}
|
|
if((_stricmp(OpenPath,"multi(0)disk(0)fdisk(1)partition(0)") == 0) ||
|
|
(_stricmp(OpenPath,"eisa(0)disk(0)fdisk(1)partition(0)" ) == 0))
|
|
{
|
|
return(BiosDiskOpen( 1, 0, FileId));
|
|
}
|
|
|
|
if((_stricmp(OpenPath,"multi(0)disk(0)fdisk(0)") == 0) ||
|
|
(_stricmp(OpenPath,"eisa(0)disk(0)fdisk(0)" ) == 0))
|
|
{
|
|
return(BiosDiskOpen( 0, 0, FileId));
|
|
}
|
|
if((_stricmp(OpenPath,"multi(0)disk(0)fdisk(1)") == 0) ||
|
|
(_stricmp(OpenPath,"eisa(0)disk(0)fdisk(1)" ) == 0))
|
|
{
|
|
return(BiosDiskOpen( 1, 0, FileId));
|
|
}
|
|
|
|
//
|
|
// We can't handle eisa(0) cases for hard disks.
|
|
//
|
|
if(IsEisa) {
|
|
return(EBADF);
|
|
}
|
|
|
|
//
|
|
// We can only deal with disk controller 0
|
|
//
|
|
|
|
if (FwGetPathMnemonicKey(OpenPath,"disk",&Controller)) {
|
|
return(EBADF);
|
|
}
|
|
if ( Controller!=0 ) {
|
|
return(EBADF);
|
|
}
|
|
|
|
if (!FwGetPathMnemonicKey(OpenPath,"cdrom",&Key)) {
|
|
//
|
|
// Now we have a CD-ROM disk number, so we open that for raw access.
|
|
// Use a special bit to indicate CD-ROM, because otherwise
|
|
// the BiosDiskOpen routine thinks a third or greater disk is
|
|
// a CD-ROM.
|
|
//
|
|
return(BiosDiskOpen( Key | 0x80000000, 0, FileId ) );
|
|
}
|
|
|
|
if (FwGetPathMnemonicKey(OpenPath,"rdisk",&Key)) {
|
|
return(EBADF);
|
|
}
|
|
|
|
//
|
|
// Now we have a disk number, so we open that for raw access.
|
|
// We need to add 0x80 to translate it to a BIOS number.
|
|
//
|
|
|
|
Status = BiosDiskOpen( 0x80 + Key,
|
|
0,
|
|
&DiskFileId );
|
|
|
|
if (Status != ESUCCESS) {
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// Find the partition number to open
|
|
//
|
|
|
|
if (FwGetPathMnemonicKey(OpenPath,"partition",&Key)) {
|
|
BiosPartitionClose(DiskFileId);
|
|
return(EBADF);
|
|
}
|
|
|
|
//
|
|
// If the partition number was 0, then we are opening the device
|
|
// for raw access, so we are already done.
|
|
//
|
|
if (Key == 0) {
|
|
*FileId = DiskFileId;
|
|
return(ESUCCESS);
|
|
}
|
|
|
|
//
|
|
// Before we open the partition, we need to find an available
|
|
// file descriptor.
|
|
//
|
|
|
|
*FileId=2;
|
|
|
|
while (BlFileTable[*FileId].Flags.Open != 0) {
|
|
*FileId += 1;
|
|
if (*FileId == BL_FILE_TABLE_SIZE) {
|
|
return(ENOENT);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We found an entry we can use, so mark it as open.
|
|
//
|
|
BlFileTable[*FileId].Flags.Open = 1;
|
|
|
|
BlFileTable[*FileId].DeviceEntryTable=&BiosPartitionEntryTable;
|
|
|
|
|
|
//
|
|
// Convert to zero-based partition number
|
|
//
|
|
PartitionNumber = (UCHAR)(Key - 1);
|
|
|
|
//
|
|
// Try to open the MBR partition
|
|
//
|
|
Status = HardDiskPartitionOpen( *FileId,
|
|
DiskFileId,
|
|
PartitionNumber);
|
|
|
|
#ifdef EFI_PARTITION_SUPPORT
|
|
|
|
if (Status != ESUCCESS) {
|
|
//
|
|
// Try to open the GPT partition
|
|
//
|
|
Status = BlOpenGPTDiskPartition( *FileId,
|
|
DiskFileId,
|
|
PartitionNumber);
|
|
}
|
|
|
|
#endif
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
BiosPartitionRead (
|
|
IN ULONG FileId,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads from the specified file
|
|
|
|
NOTE John Vert (jvert) 18-Jun-1991
|
|
This only supports block sector reads. Thus, everything
|
|
is assumed to start on a sector boundary, and every offset
|
|
is considered an offset from the logical beginning of the disk
|
|
partition.
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies the file to read from
|
|
|
|
Buffer - Supplies buffer to hold the data that is read
|
|
|
|
Length - Supplies maximum number of bytes to read
|
|
|
|
Count - Returns actual bytes read.
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - Read completed successfully
|
|
|
|
!ESUCCESS - Read failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
ARC_STATUS Status;
|
|
LARGE_INTEGER PhysicalOffset;
|
|
ULONG DiskId;
|
|
PhysicalOffset.QuadPart = BlFileTable[FileId].Position.QuadPart +
|
|
SECTOR_SIZE * (LONGLONG)BlFileTable[FileId].u.PartitionContext.StartingSector;
|
|
|
|
DiskId = BlFileTable[FileId].u.PartitionContext.DiskId;
|
|
|
|
Status = (BlFileTable[DiskId].DeviceEntryTable->Seek)(DiskId,
|
|
&PhysicalOffset,
|
|
SeekAbsolute );
|
|
|
|
if (Status != ESUCCESS) {
|
|
return(Status);
|
|
}
|
|
|
|
Status = (BlFileTable[DiskId].DeviceEntryTable->Read)(DiskId,
|
|
Buffer,
|
|
Length,
|
|
Count );
|
|
|
|
BlFileTable[FileId].Position.QuadPart += *Count;
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
ARC_STATUS
|
|
BiosPartitionSeek (
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
switch (SeekMode) {
|
|
case SeekAbsolute:
|
|
BlFileTable[FileId].Position = *Offset;
|
|
break;
|
|
case SeekRelative:
|
|
BlFileTable[FileId].Position.QuadPart += Offset->QuadPart;
|
|
break;
|
|
default:
|
|
#if DBG
|
|
BlPrint("SeekMode %lx not supported\n",SeekMode);
|
|
#endif
|
|
return(EACCES);
|
|
|
|
}
|
|
return(ESUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
ARC_STATUS
|
|
BiosPartitionWrite(
|
|
IN ULONG FileId,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes to the specified file
|
|
|
|
NOTE John Vert (jvert) 18-Jun-1991
|
|
This only supports block sector reads. Thus, everything
|
|
is assumed to start on a sector boundary, and every offset
|
|
is considered an offset from the logical beginning of the disk
|
|
partition.
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies the file to write to
|
|
|
|
Buffer - Supplies buffer with data to write
|
|
|
|
Length - Supplies number of bytes to write
|
|
|
|
Count - Returns actual bytes written.
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - write completed successfully
|
|
|
|
!ESUCCESS - write failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
ARC_STATUS Status;
|
|
LARGE_INTEGER PhysicalOffset;
|
|
ULONG DiskId;
|
|
PhysicalOffset.QuadPart = BlFileTable[FileId].Position.QuadPart +
|
|
SECTOR_SIZE * (LONGLONG)BlFileTable[FileId].u.PartitionContext.StartingSector;
|
|
|
|
DiskId = BlFileTable[FileId].u.PartitionContext.DiskId;
|
|
|
|
Status = (BlFileTable[DiskId].DeviceEntryTable->Seek)(DiskId,
|
|
&PhysicalOffset,
|
|
SeekAbsolute );
|
|
|
|
if (Status != ESUCCESS) {
|
|
return(Status);
|
|
}
|
|
|
|
Status = (BlFileTable[DiskId].DeviceEntryTable->Write)(DiskId,
|
|
Buffer,
|
|
Length,
|
|
Count );
|
|
|
|
if(Status == ESUCCESS) {
|
|
BlFileTable[FileId].Position.QuadPart += *Count;
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
ARC_STATUS
|
|
BiosConsoleOpen(
|
|
IN PCHAR OpenPath,
|
|
IN OPEN_MODE OpenMode,
|
|
OUT PULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempts to open either the console input or output
|
|
|
|
Arguments:
|
|
|
|
OpenPath - Supplies a pointer to the name of the device to open. If
|
|
this is either CONSOLE_INPUT_NAME or CONSOLE_OUTPUT_NAME,
|
|
a file descriptor is allocated and filled in.
|
|
|
|
OpenMode - Supplies the mode to open the file.
|
|
0 - Read Only (CONSOLE_INPUT_NAME)
|
|
1 - Write Only (CONSOLE_OUTPUT_NAME)
|
|
|
|
FileId - Returns the file descriptor for use with the Close, Read and
|
|
Write routines
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - Console successfully opened.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (_stricmp(OpenPath, CONSOLE_INPUT_NAME)==0) {
|
|
|
|
//
|
|
// Open the keyboard for input
|
|
//
|
|
|
|
if (OpenMode != ArcOpenReadOnly) {
|
|
return(EACCES);
|
|
}
|
|
|
|
*FileId = 0;
|
|
|
|
return(ESUCCESS);
|
|
}
|
|
|
|
if (_stricmp(OpenPath, CONSOLE_OUTPUT_NAME)==0) {
|
|
|
|
//
|
|
// Open the display for output
|
|
//
|
|
|
|
if (OpenMode != ArcOpenWriteOnly) {
|
|
return(EACCES);
|
|
}
|
|
*FileId = 1;
|
|
|
|
return(ESUCCESS);
|
|
}
|
|
|
|
return(ENOENT);
|
|
|
|
}
|
|
|
|
ARC_STATUS
|
|
BiosConsoleReadStatus(
|
|
IN ULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if there is a keypress pending
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies the FileId to be read. (should always be 0 for this
|
|
function)
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - There is a key pending
|
|
|
|
EAGAIN - There is not a key pending
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Key;
|
|
|
|
//
|
|
// enforce file id to be 0 by no reading console otherwise
|
|
//
|
|
if (FileId != 0) {
|
|
return EINVAL;
|
|
}
|
|
|
|
//
|
|
// If we have buffered input...
|
|
//
|
|
if (KeyBufferEnd != KeyBufferStart) {
|
|
return(ESUCCESS);
|
|
}
|
|
|
|
//
|
|
// Check for a key
|
|
//
|
|
Key = GET_KEY();
|
|
if (Key != 0) {
|
|
//
|
|
// We got a key, so we have to stick it back into our buffer
|
|
// and return ESUCCESS.
|
|
//
|
|
BiosConsoleFillBuffer(Key);
|
|
return(ESUCCESS);
|
|
|
|
} else {
|
|
//
|
|
// no key pending
|
|
//
|
|
return(EAGAIN);
|
|
}
|
|
|
|
}
|
|
|
|
ARC_STATUS
|
|
BiosConsoleRead(
|
|
IN ULONG FileId,
|
|
OUT PUCHAR Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gets input from the keyboard.
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies the FileId to be read (should always be 0 for this
|
|
function)
|
|
|
|
Buffer - Returns the keyboard input.
|
|
|
|
Length - Supplies the length of the buffer (in bytes)
|
|
|
|
Count - Returns the actual number of bytes read
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - Keyboard read completed succesfully.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Key;
|
|
|
|
//
|
|
// enforce file id to be 0 by no reading console otherwise
|
|
//
|
|
if (FileId != 0) {
|
|
return EINVAL;
|
|
}
|
|
|
|
*Count = 0;
|
|
|
|
while (*Count < Length) {
|
|
if (KeyBufferEnd == KeyBufferStart) { // then buffer is presently empty
|
|
do {
|
|
|
|
//
|
|
// Poll the keyboard until input is available
|
|
//
|
|
|
|
Key = GET_KEY();
|
|
} while ( Key==0 );
|
|
|
|
BiosConsoleFillBuffer(Key);
|
|
}
|
|
|
|
Buffer[*Count] = KeyBuffer[KeyBufferStart];
|
|
KeyBufferStart = (KeyBufferStart+1) % KEY_INPUT_BUFFER_SIZE;
|
|
|
|
*Count = *Count + 1;
|
|
}
|
|
return(ESUCCESS);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
BiosConsoleFillBuffer(
|
|
IN ULONG Key
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Places input from the keyboard into the keyboard buffer, expanding the
|
|
special keys as appropriate.
|
|
|
|
All keys translated here use the ARC translation table, as defined in the
|
|
ARC specification, with one exception -- the BACKTAB_KEY, for which the
|
|
ARC spec is lacking. I have decided that BACKTAB_KEY is ESC+TAB.
|
|
|
|
Arguments:
|
|
|
|
Key - Raw keypress value as returned by GET_KEY().
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
switch(Key) {
|
|
case UP_ARROW:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'A';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case DOWN_ARROW:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'B';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case RIGHT_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'C';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case LEFT_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'D';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case INS_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = '@';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case DEL_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'P';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case F1_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'O';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'P';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case F2_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'O';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'Q';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case F3_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'O';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'w';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case F4_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'O';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'x';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case F5_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'O';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 't';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case F6_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'O';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'u';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case F7_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'O';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'q';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case F8_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'O';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'r';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case F10_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'O';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'M';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case F11_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'O';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'A';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case F12_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'O';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'B';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case HOME_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'H';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case END_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = 'K';
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case ESCAPE_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
case BACKTAB_KEY:
|
|
KeyBuffer[KeyBufferEnd] = ASCI_CSI_IN;
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
KeyBuffer[KeyBufferEnd] = (UCHAR)(TAB_KEY & 0xFF);
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// The ASCII code is the low byte of Key
|
|
//
|
|
KeyBuffer[KeyBufferEnd] = (UCHAR)(Key & 0xff);
|
|
KeyBufferEnd = (KeyBufferEnd+1) % KEY_INPUT_BUFFER_SIZE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ARC_STATUS
|
|
BiosConsoleWrite(
|
|
IN ULONG FileId,
|
|
OUT PUCHAR Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Outputs to the console. (In this case, the VGA display)
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies the FileId to be written (should always be 1 for this
|
|
function)
|
|
|
|
Buffer - Supplies characters to be output
|
|
|
|
Length - Supplies the length of the buffer (in bytes)
|
|
|
|
Count - Returns the actual number of bytes written
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - Console write completed succesfully.
|
|
|
|
--*/
|
|
{
|
|
ARC_STATUS Status;
|
|
PUCHAR String;
|
|
ULONG Index;
|
|
UCHAR a;
|
|
PUCHAR p;
|
|
|
|
//
|
|
// enforce file id to be 0 by no reading console otherwise
|
|
//
|
|
if (FileId != 1) {
|
|
return EINVAL;
|
|
}
|
|
|
|
//
|
|
// Process each character in turn.
|
|
//
|
|
|
|
Status = ESUCCESS;
|
|
String = (PUCHAR)Buffer;
|
|
|
|
for ( *Count = 0 ;
|
|
*Count < Length ;
|
|
(*Count)++, String++ ) {
|
|
|
|
//
|
|
// If we're in the middle of a control sequence, continue scanning,
|
|
// otherwise process character.
|
|
//
|
|
|
|
if (ControlSequence) {
|
|
|
|
//
|
|
// If the character is a digit, update parameter value.
|
|
//
|
|
|
|
if ((*String >= '0') && (*String <= '9')) {
|
|
Parameter[PCount] = Parameter[PCount] * 10 + *String - '0';
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If we are in the middle of a font selection sequence, this
|
|
// character must be a 'D', otherwise reset control sequence.
|
|
//
|
|
|
|
if (FontSelection) {
|
|
|
|
//if (*String == 'D') {
|
|
//
|
|
// //
|
|
// // Other fonts not implemented yet.
|
|
// //
|
|
//
|
|
//} else {
|
|
//}
|
|
|
|
ControlSequence = FALSE;
|
|
FontSelection = FALSE;
|
|
continue;
|
|
}
|
|
|
|
switch (*String) {
|
|
|
|
//
|
|
// If a semicolon, move to the next parameter.
|
|
//
|
|
|
|
case ';':
|
|
|
|
PCount++;
|
|
if (PCount > CONTROL_SEQUENCE_MAX_PARAMETER) {
|
|
PCount = CONTROL_SEQUENCE_MAX_PARAMETER;
|
|
}
|
|
Parameter[PCount] = 0;
|
|
break;
|
|
|
|
//
|
|
// If a 'J', erase part or all of the screen.
|
|
//
|
|
|
|
case 'J':
|
|
|
|
switch (Parameter[0]) {
|
|
case 0:
|
|
//
|
|
// Erase to end of the screen
|
|
//
|
|
TextClearToEndOfDisplay();
|
|
break;
|
|
|
|
case 1:
|
|
//
|
|
// Erase from the beginning of the screen
|
|
//
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Erase entire screen
|
|
//
|
|
TextClearDisplay();
|
|
break;
|
|
}
|
|
|
|
ControlSequence = FALSE;
|
|
break;
|
|
|
|
//
|
|
// If a 'K', erase part or all of the line.
|
|
//
|
|
|
|
case 'K':
|
|
|
|
switch (Parameter[0]) {
|
|
|
|
//
|
|
// Erase to end of the line.
|
|
//
|
|
|
|
case 0:
|
|
TextClearToEndOfLine();
|
|
break;
|
|
|
|
//
|
|
// Erase from the beginning of the line.
|
|
//
|
|
|
|
case 1:
|
|
TextClearFromStartOfLine();
|
|
break;
|
|
|
|
//
|
|
// Erase entire line.
|
|
//
|
|
|
|
default :
|
|
TextClearFromStartOfLine();
|
|
TextClearToEndOfLine();
|
|
break;
|
|
}
|
|
|
|
ControlSequence = FALSE;
|
|
break;
|
|
|
|
//
|
|
// If a 'H', move cursor to position.
|
|
//
|
|
|
|
case 'H':
|
|
TextSetCursorPosition(Parameter[1]-1, Parameter[0]-1);
|
|
ControlSequence = FALSE;
|
|
break;
|
|
|
|
//
|
|
// If a ' ', could be a FNT selection command.
|
|
//
|
|
|
|
case ' ':
|
|
FontSelection = TRUE;
|
|
break;
|
|
|
|
case 'm':
|
|
//
|
|
// Select action based on each parameter.
|
|
//
|
|
// Blink and HighIntensity are by default disabled
|
|
// each time a new SGR is specified, unless they are
|
|
// explicitly specified again, in which case these
|
|
// will be set to TRUE at that time.
|
|
//
|
|
|
|
HighIntensity = FALSE;
|
|
Blink = FALSE;
|
|
|
|
for ( Index = 0 ; Index <= PCount ; Index++ ) {
|
|
switch (Parameter[Index]) {
|
|
|
|
//
|
|
// Attributes off.
|
|
//
|
|
|
|
case 0:
|
|
TextSetCurrentAttribute(7);
|
|
HighIntensity = FALSE;
|
|
Blink = FALSE;
|
|
break;
|
|
|
|
//
|
|
// High Intensity.
|
|
//
|
|
|
|
case 1:
|
|
TextSetCurrentAttribute(0xf);
|
|
HighIntensity = TRUE;
|
|
break;
|
|
|
|
//
|
|
// Underscored.
|
|
//
|
|
|
|
case 4:
|
|
break;
|
|
|
|
//
|
|
// Blink.
|
|
//
|
|
|
|
case 5:
|
|
TextSetCurrentAttribute(0x87);
|
|
Blink = TRUE;
|
|
break;
|
|
|
|
//
|
|
// Reverse Video.
|
|
//
|
|
|
|
case 7:
|
|
TextSetCurrentAttribute(0x70);
|
|
HighIntensity = FALSE;
|
|
Blink = FALSE;
|
|
break;
|
|
|
|
//
|
|
// Font selection, not implemented yet.
|
|
//
|
|
|
|
case 10:
|
|
case 11:
|
|
case 12:
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
case 16:
|
|
case 17:
|
|
case 18:
|
|
case 19:
|
|
break;
|
|
|
|
//
|
|
// Foreground Color
|
|
//
|
|
|
|
case 30:
|
|
case 31:
|
|
case 32:
|
|
case 33:
|
|
case 34:
|
|
case 35:
|
|
case 36:
|
|
case 37:
|
|
a = TextGetCurrentAttribute();
|
|
a &= 0x70;
|
|
a |= TranslateColor[Parameter[Index]-30];
|
|
if (HighIntensity) {
|
|
a |= 0x08;
|
|
}
|
|
if (Blink) {
|
|
a |= 0x80;
|
|
}
|
|
TextSetCurrentAttribute(a);
|
|
break;
|
|
|
|
//
|
|
// Background Color
|
|
//
|
|
|
|
case 40:
|
|
case 41:
|
|
case 42:
|
|
case 43:
|
|
case 44:
|
|
case 45:
|
|
case 46:
|
|
case 47:
|
|
a = TextGetCurrentAttribute();
|
|
a &= 0x8f;
|
|
a |= TranslateColor[Parameter[Index]-40] << 4;
|
|
TextSetCurrentAttribute(a);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
default:
|
|
ControlSequence = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// This is not a control sequence, check for escape sequence.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// If escape sequence, check for control sequence, otherwise
|
|
// process single character.
|
|
//
|
|
|
|
if (EscapeSequence) {
|
|
|
|
//
|
|
// Check for '[', means control sequence, any other following
|
|
// character is ignored.
|
|
//
|
|
|
|
if (*String == '[') {
|
|
|
|
ControlSequence = TRUE;
|
|
|
|
//
|
|
// Initialize first parameter.
|
|
//
|
|
|
|
PCount = 0;
|
|
Parameter[0] = 0;
|
|
}
|
|
EscapeSequence = FALSE;
|
|
|
|
//
|
|
// This is not a control or escape sequence, process single character.
|
|
//
|
|
|
|
} else {
|
|
|
|
switch (*String) {
|
|
//
|
|
// Check for escape sequence.
|
|
//
|
|
|
|
case ASCI_ESC:
|
|
EscapeSequence = TRUE;
|
|
break;
|
|
|
|
default:
|
|
p = TextCharOut(String);
|
|
//
|
|
// Each pass through the loop increments String by 1.
|
|
// If we output a dbcs char we need to increment by
|
|
// one more.
|
|
//
|
|
(*Count) += (p - String) - 1;
|
|
String += (p - String) - 1;
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
BiosDiskOpen(
|
|
IN ULONG DriveId,
|
|
IN OPEN_MODE OpenMode,
|
|
OUT PULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens a BIOS-accessible disk for raw sector access.
|
|
|
|
Arguments:
|
|
|
|
DriveId - Supplies the BIOS DriveId of the drive to open
|
|
0 - Floppy 0
|
|
1 - Floppy 1
|
|
0x80 - Hard Drive 0
|
|
0x81 - Hard Drive 1
|
|
0x82 - Hard Drive 2
|
|
etc
|
|
|
|
High bit set and ID > 0x81 means the device is expected to be
|
|
a CD-ROM drive.
|
|
|
|
OpenMode - Supplies the mode of the open
|
|
|
|
FileId - Supplies a pointer to a variable that specifies the file
|
|
table entry that is filled in if the open is successful.
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS is returned if the open operation is successful. Otherwise,
|
|
an unsuccessful status is returned that describes the reason for failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
USHORT NumberHeads;
|
|
UCHAR NumberSectors;
|
|
USHORT NumberCylinders;
|
|
UCHAR NumberDrives;
|
|
ULONG Result;
|
|
PDRIVE_CONTEXT Context;
|
|
BOOLEAN IsCd;
|
|
UCHAR *Buffer = FwDiskCache;
|
|
ULONG BufferSize = 512; // sector size
|
|
BOOLEAN xInt13;
|
|
|
|
UNREFERENCED_PARAMETER( OpenMode );
|
|
|
|
DBGOUT(("BiosDiskOpen: enter, id = 0x%lx\r\n",DriveId));
|
|
|
|
//
|
|
// Check special drive number encoding for CD-ROM case
|
|
//
|
|
if(DriveId > 0x80000081) {
|
|
IsCd = TRUE;
|
|
DriveId &= 0x7fffffff;
|
|
} else {
|
|
IsCd = FALSE;
|
|
}
|
|
|
|
xInt13 = FALSE;
|
|
|
|
//
|
|
// If we are opening Floppy 0 or Floppy 1, we want to read the BPB off
|
|
// the disk so we can deal with all the odd disk formats.
|
|
//
|
|
// If we are opening a hard drive, we can just call the BIOS to find out
|
|
// its characteristics
|
|
//
|
|
if(DriveId < 128) {
|
|
PPACKED_BOOT_SECTOR BootSector;
|
|
BIOS_PARAMETER_BLOCK Bpb;
|
|
|
|
//
|
|
// Read the boot sector off the floppy and extract the cylinder,
|
|
// sector, and head information. We fake the CHS values here
|
|
// to allow sector 0 to be read before we actually know the
|
|
// geometry we want to use.
|
|
//
|
|
if(ReadPhysicalSectors((UCHAR)DriveId,0,1,Buffer,1,1,1,FALSE)) {
|
|
DBGOUT(("BiosDiskOpen: error reading from floppy drive\r\n"));
|
|
DBGPAUSE
|
|
return(EIO);
|
|
}
|
|
BootSector = (PPACKED_BOOT_SECTOR)Buffer;
|
|
|
|
FatUnpackBios(&Bpb, &(BootSector->PackedBpb));
|
|
|
|
NumberHeads = Bpb.Heads;
|
|
NumberSectors = (UCHAR)Bpb.SectorsPerTrack;
|
|
|
|
if (Bpb.Sectors != 0) {
|
|
NumberCylinders = Bpb.Sectors / (NumberSectors * NumberHeads);
|
|
}
|
|
else {
|
|
ULONG Cylinders = Bpb.LargeSectors / (NumberSectors * NumberHeads);
|
|
|
|
//
|
|
// LargeSectors has size of ULONG.
|
|
// so truncate size at MAX(USHORT) and check for xint13
|
|
// so it can be used for the higher sectors
|
|
//
|
|
NumberCylinders = ( Cylinders > (USHORT)-1 ) ? (USHORT) -1 :
|
|
(USHORT) Cylinders;
|
|
|
|
//
|
|
// Attempt to get extended int13 parameters.
|
|
// Note that we use a buffer that's on the stack, so it's guaranteed
|
|
// to be under the 1 MB line (required when passing buffers to real-mode
|
|
// services).
|
|
//
|
|
// Note that we don't actually care about the parameters, just whether
|
|
// extended int13 services are available.
|
|
//
|
|
RtlZeroMemory(Buffer,BufferSize);
|
|
xInt13 = GET_XINT13_PARAMS(Buffer,(UCHAR)DriveId);
|
|
|
|
}
|
|
} else if(IsCd) {
|
|
//
|
|
// This is an El Torito drive
|
|
// Just use bogus values since CHS values are meaningless for no-emulation El Torito boot
|
|
//
|
|
NumberCylinders = 1;
|
|
NumberHeads = 1;
|
|
NumberSectors = 1;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Get Drive Parameters via int13 function 8
|
|
// Return of 0 means success; otherwise we get back what the BIOS
|
|
// returned in ax.
|
|
//
|
|
|
|
ULONG Retries = 0;
|
|
|
|
do {
|
|
if(BIOS_IO(0x08,(UCHAR)DriveId,0,0,0,0,0)) {
|
|
DBGOUT(("BiosDiskOpen: error getting params for drive\r\n"));
|
|
DBGPAUSE
|
|
return(EIO);
|
|
}
|
|
|
|
//
|
|
// At this point, ECX looks like this:
|
|
//
|
|
// bits 31..22 - Maximum cylinder
|
|
// bits 21..16 - Maximum sector
|
|
// bits 15..8 - Maximum head
|
|
// bits 7..0 - Number of drives
|
|
//
|
|
// Unpack the information from ecx.
|
|
//
|
|
_asm {
|
|
mov Result, ecx
|
|
}
|
|
|
|
NumberDrives = (UCHAR)Result;
|
|
NumberHeads = (((USHORT)Result >> 8) & 0xff) + 1;
|
|
NumberSectors = (UCHAR)((Result >> 16) & 0x3f);
|
|
NumberCylinders = (USHORT)(((Result >> 24) + ((Result >> 14) & 0x300)) + 1);
|
|
++Retries;
|
|
} while ( ((NumberHeads==0) || (NumberSectors==0) || (NumberCylinders==0))
|
|
&& (Retries < 5) );
|
|
|
|
DBGOUT((
|
|
"BiosDiskOpen: cyl=%u, heads=%u, sect=%u, drives=%u\r\n",
|
|
NumberCylinders,
|
|
NumberHeads,
|
|
NumberSectors,
|
|
NumberDrives
|
|
));
|
|
|
|
if(((UCHAR)DriveId & 0x7f) >= NumberDrives) {
|
|
//
|
|
// The requested drive does not exist
|
|
//
|
|
DBGOUT(("BiosDiskOpen: invalid drive\r\n"));
|
|
DBGPAUSE
|
|
return(EIO);
|
|
}
|
|
|
|
if (Retries == 5) {
|
|
DBGOUT(("Couldn't get BIOS configuration info\n"));
|
|
DBGPAUSE
|
|
return(EIO);
|
|
}
|
|
|
|
//
|
|
// Attempt to get extended int13 parameters.
|
|
// Note that we use a buffer that's on the stack, so it's guaranteed
|
|
// to be under the 1 MB line (required when passing buffers to real-mode
|
|
// services).
|
|
//
|
|
// Note that we don't actually care about the parameters, just whether
|
|
// extended int13 services are available.
|
|
//
|
|
RtlZeroMemory(Buffer,BufferSize);
|
|
xInt13 = GET_XINT13_PARAMS(Buffer,(UCHAR)DriveId);
|
|
|
|
DBGOUT(("BiosDiskOpen: xint13 for drive: %s\r\n",xInt13 ? "yes" : "no"));
|
|
}
|
|
|
|
//
|
|
// Find an available FileId descriptor to open the device with
|
|
//
|
|
*FileId=2;
|
|
|
|
while (BlFileTable[*FileId].Flags.Open != 0) {
|
|
*FileId += 1;
|
|
if(*FileId == BL_FILE_TABLE_SIZE) {
|
|
DBGOUT(("BiosDiskOpen: no file table entry available\r\n"));
|
|
DBGPAUSE
|
|
return(ENOENT);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We found an entry we can use, so mark it as open.
|
|
//
|
|
BlFileTable[*FileId].Flags.Open = 1;
|
|
|
|
BlFileTable[*FileId].DeviceEntryTable = IsCd
|
|
? &BiosEDDSEntryTable
|
|
: &BiosDiskEntryTable;
|
|
|
|
Context = &(BlFileTable[*FileId].u.DriveContext);
|
|
|
|
Context->IsCd = IsCd;
|
|
Context->Drive = (UCHAR)DriveId;
|
|
Context->Cylinders = NumberCylinders;
|
|
Context->Heads = NumberHeads;
|
|
Context->Sectors = NumberSectors;
|
|
Context->xInt13 = xInt13;
|
|
|
|
DBGOUT(("BiosDiskOpen: exit success\r\n"));
|
|
|
|
return(ESUCCESS);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
BiospWritePartialSector(
|
|
IN UCHAR Int13Unit,
|
|
IN ULONGLONG Sector,
|
|
IN PUCHAR Buffer,
|
|
IN BOOLEAN IsHead,
|
|
IN ULONG Bytes,
|
|
IN UCHAR SectorsPerTrack,
|
|
IN USHORT Heads,
|
|
IN USHORT Cylinders,
|
|
IN BOOLEAN AllowXInt13
|
|
)
|
|
{
|
|
ARC_STATUS Status;
|
|
|
|
//
|
|
// Read sector into the write buffer
|
|
//
|
|
Status = ReadPhysicalSectors(
|
|
Int13Unit,
|
|
Sector,
|
|
1,
|
|
FwDiskCache,
|
|
SectorsPerTrack,
|
|
Heads,
|
|
Cylinders,
|
|
AllowXInt13
|
|
);
|
|
|
|
if(Status != ESUCCESS) {
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// Transfer the appropriate bytes from the user buffer to the write buffer
|
|
//
|
|
RtlMoveMemory(
|
|
IsHead ? (FwDiskCache + Bytes) : FwDiskCache,
|
|
Buffer,
|
|
IsHead ? (SECTOR_SIZE - Bytes) : Bytes
|
|
);
|
|
|
|
//
|
|
// Write the sector out
|
|
//
|
|
Status = WritePhysicalSectors(
|
|
Int13Unit,
|
|
Sector,
|
|
1,
|
|
FwDiskCache,
|
|
SectorsPerTrack,
|
|
Heads,
|
|
Cylinders,
|
|
AllowXInt13
|
|
);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
BiosDiskWrite(
|
|
IN ULONG FileId,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes sectors directly to an open physical disk.
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies the file to write to
|
|
|
|
Buffer - Supplies buffer with data to write
|
|
|
|
Length - Supplies number of bytes to write
|
|
|
|
Count - Returns actual bytes written
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - write completed successfully
|
|
|
|
!ESUCCESS - write failed
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONGLONG HeadSector,TailSector,CurrentSector;
|
|
UCHAR Int13Unit;
|
|
ULONG HeadOffset,TailByteCount;
|
|
UCHAR SectorsPerTrack;
|
|
USHORT Heads,Cylinders;
|
|
BOOLEAN AllowXInt13;
|
|
ARC_STATUS Status;
|
|
ULONG BytesLeftToTransfer;
|
|
UCHAR SectorsToTransfer;
|
|
BOOLEAN Under1MegLine;
|
|
PVOID TransferBuffer;
|
|
PUCHAR UserBuffer;
|
|
ULONG PhysicalSectors;
|
|
|
|
BytesLeftToTransfer = Length;
|
|
PhysicalSectors = SECTOR_SIZE;
|
|
|
|
HeadSector = BlFileTable[FileId].Position.QuadPart / PhysicalSectors;
|
|
HeadOffset = (ULONG)(BlFileTable[FileId].Position.QuadPart % PhysicalSectors);
|
|
|
|
TailSector = (BlFileTable[FileId].Position.QuadPart + Length) / PhysicalSectors;
|
|
TailByteCount = (ULONG)((BlFileTable[FileId].Position.QuadPart + Length) % PhysicalSectors);
|
|
|
|
Int13Unit = BlFileTable[FileId].u.DriveContext.Drive;
|
|
SectorsPerTrack = BlFileTable[FileId].u.DriveContext.Sectors;
|
|
Heads = BlFileTable[FileId].u.DriveContext.Heads;
|
|
Cylinders = BlFileTable[FileId].u.DriveContext.Cylinders;
|
|
AllowXInt13 = BlFileTable[FileId].u.DriveContext.xInt13;
|
|
|
|
UserBuffer = Buffer;
|
|
|
|
//
|
|
// If this write will even partially write over the sector cached
|
|
// in the last read sector cache, invalidate the cache.
|
|
//
|
|
|
|
if (FwLastSectorCache.Initialized &&
|
|
FwLastSectorCache.Valid &&
|
|
(FwLastSectorCache.SectorNumber >= HeadSector) &&
|
|
(FwLastSectorCache.SectorNumber <= TailSector)) {
|
|
|
|
FwLastSectorCache.Valid = FALSE;
|
|
}
|
|
|
|
//
|
|
// Special case of transfer occuring entirely within one sector
|
|
//
|
|
CurrentSector = HeadSector;
|
|
if(HeadOffset && TailByteCount && (HeadSector == TailSector)) {
|
|
|
|
Status = ReadPhysicalSectors(
|
|
Int13Unit,
|
|
CurrentSector,
|
|
1,
|
|
FwDiskCache,
|
|
SectorsPerTrack,
|
|
Heads,
|
|
Cylinders,
|
|
AllowXInt13
|
|
);
|
|
|
|
if(Status != ESUCCESS) {
|
|
goto BiosDiskWriteDone;
|
|
}
|
|
|
|
RtlMoveMemory(FwDiskCache+HeadOffset,Buffer,Length);
|
|
|
|
Status = WritePhysicalSectors(
|
|
Int13Unit,
|
|
CurrentSector,
|
|
1,
|
|
FwDiskCache,
|
|
SectorsPerTrack,
|
|
Heads,
|
|
Cylinders,
|
|
AllowXInt13
|
|
);
|
|
|
|
if(Status != ESUCCESS) {
|
|
goto BiosDiskWriteDone;
|
|
}
|
|
|
|
BytesLeftToTransfer = 0;
|
|
goto BiosDiskWriteDone;
|
|
}
|
|
|
|
if(HeadOffset) {
|
|
|
|
Status = BiospWritePartialSector(
|
|
Int13Unit,
|
|
HeadSector,
|
|
Buffer,
|
|
TRUE,
|
|
HeadOffset,
|
|
SectorsPerTrack,
|
|
Heads,
|
|
Cylinders,
|
|
AllowXInt13
|
|
);
|
|
|
|
if(Status != ESUCCESS) {
|
|
return(Status);
|
|
}
|
|
|
|
BytesLeftToTransfer -= PhysicalSectors - HeadOffset;
|
|
UserBuffer += PhysicalSectors - HeadOffset;
|
|
CurrentSector += 1;
|
|
}
|
|
|
|
if(TailByteCount) {
|
|
|
|
Status = BiospWritePartialSector(
|
|
Int13Unit,
|
|
TailSector,
|
|
(PUCHAR)Buffer + Length - TailByteCount,
|
|
FALSE,
|
|
TailByteCount,
|
|
SectorsPerTrack,
|
|
Heads,
|
|
Cylinders,
|
|
AllowXInt13
|
|
);
|
|
|
|
if(Status != ESUCCESS) {
|
|
return(Status);
|
|
}
|
|
|
|
BytesLeftToTransfer -= TailByteCount;
|
|
}
|
|
|
|
//
|
|
// The following calculation is not inside the transfer loop because
|
|
// it is unlikely that a caller's buffer will *cross* the 1 meg line
|
|
// due to the PC memory map.
|
|
//
|
|
if((ULONG)UserBuffer + BytesLeftToTransfer <= 0x100000) {
|
|
Under1MegLine = TRUE;
|
|
} else {
|
|
Under1MegLine = FALSE;
|
|
}
|
|
|
|
//
|
|
// Now handle the middle part. This is some number of whole sectors.
|
|
//
|
|
while(BytesLeftToTransfer) {
|
|
|
|
//
|
|
// The number of sectors to transfer is the minimum of:
|
|
// - the number of sectors left in the current track
|
|
// - BytesLeftToTransfer / SECTOR_SIZE
|
|
//
|
|
// Because sectors per track is 1-63 we know this will fit in a UCHAR
|
|
//
|
|
SectorsToTransfer = (UCHAR)min(
|
|
SectorsPerTrack - (CurrentSector % SectorsPerTrack),
|
|
BytesLeftToTransfer / PhysicalSectors
|
|
);
|
|
|
|
//
|
|
// Now we'll figure out where to transfer the data from. If the
|
|
// caller's buffer is under the 1 meg line, we can transfer the
|
|
// data directly from the caller's buffer. Otherwise we'll copy the
|
|
// user's buffer to our local buffer and transfer from there.
|
|
// In the latter case we can only transfer in chunks of
|
|
// SCRATCH_BUFFER_SIZE because that's the size of the local buffer.
|
|
//
|
|
// Also make sure the transfer won't cross a 64k boundary.
|
|
//
|
|
if(Under1MegLine) {
|
|
//
|
|
// Check if the transfer would cross a 64k boundary. If so,
|
|
// use the local buffer. Otherwise use the user's buffer.
|
|
//
|
|
if(((ULONG)UserBuffer & 0xffff0000) !=
|
|
(((ULONG)UserBuffer + (SectorsToTransfer * PhysicalSectors) - 1) & 0xffff0000))
|
|
{
|
|
TransferBuffer = FwDiskCache;
|
|
SectorsToTransfer = (UCHAR)min(SectorsToTransfer, SCRATCH_BUFFER_SIZE / (USHORT)PhysicalSectors);
|
|
|
|
} else {
|
|
|
|
TransferBuffer = UserBuffer;
|
|
}
|
|
} else {
|
|
TransferBuffer = FwDiskCache;
|
|
SectorsToTransfer = (UCHAR)min(SectorsToTransfer, SCRATCH_BUFFER_SIZE / (USHORT)PhysicalSectors);
|
|
}
|
|
|
|
if(TransferBuffer == FwDiskCache) {
|
|
RtlMoveMemory(FwDiskCache,UserBuffer,SectorsToTransfer*PhysicalSectors);
|
|
}
|
|
|
|
Status = WritePhysicalSectors(
|
|
Int13Unit,
|
|
CurrentSector,
|
|
SectorsToTransfer,
|
|
TransferBuffer,
|
|
SectorsPerTrack,
|
|
Heads,
|
|
Cylinders,
|
|
AllowXInt13
|
|
);
|
|
|
|
if(Status != ESUCCESS) {
|
|
//
|
|
// Tail part isn't contiguous with middle part
|
|
//
|
|
BytesLeftToTransfer += TailByteCount;
|
|
return(Status);
|
|
}
|
|
|
|
CurrentSector += SectorsToTransfer;
|
|
BytesLeftToTransfer -= SectorsToTransfer * PhysicalSectors;
|
|
UserBuffer += SectorsToTransfer * PhysicalSectors;
|
|
}
|
|
|
|
Status = ESUCCESS;
|
|
|
|
BiosDiskWriteDone:
|
|
|
|
*Count = Length - BytesLeftToTransfer;
|
|
BlFileTable[FileId].Position.QuadPart += *Count;
|
|
return(Status);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
pBiosDiskReadWorker(
|
|
IN ULONG FileId,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count,
|
|
IN USHORT SectorSize,
|
|
IN BOOLEAN xInt13
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads sectors directly from an open physical disk.
|
|
|
|
Arguments:
|
|
|
|
FileId - Supplies the file to read from
|
|
|
|
Buffer - Supplies buffer to hold the data that is read
|
|
|
|
Length - Supplies maximum number of bytes to read
|
|
|
|
Count - Returns actual bytes read
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS - Read completed successfully
|
|
|
|
!ESUCCESS - Read failed
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONGLONG HeadSector,TailSector,CurrentSector;
|
|
ULONG HeadOffset,TailByteCount;
|
|
USHORT Heads,Cylinders;
|
|
UCHAR SectorsPerTrack;
|
|
UCHAR Int13Unit;
|
|
ARC_STATUS Status;
|
|
UCHAR SectorsToTransfer;
|
|
ULONG NumBytesToTransfer;
|
|
BOOLEAN AllowXInt13;
|
|
PUCHAR pDestInUserBuffer;
|
|
PUCHAR pEndOfUserBuffer;
|
|
PUCHAR pTransferDest;
|
|
PUCHAR pSrc;
|
|
ULONG CopyLength;
|
|
ULONG ReadLength;
|
|
PUCHAR pLastReadSector = NULL;
|
|
ULONGLONG LastReadSectorNumber = 0;
|
|
PUCHAR TargetBuffer;
|
|
|
|
DBGOUT(("BiosDiskRead: enter; length=0x%lx, sector size=%u, xint13=%u\r\n",Length,SectorSize,xInt13));
|
|
|
|
//
|
|
// Reset number of bytes transfered.
|
|
//
|
|
|
|
*Count = 0;
|
|
|
|
//
|
|
// Complete 0 length requests immediately.
|
|
//
|
|
|
|
if (Length == 0) {
|
|
return ESUCCESS;
|
|
}
|
|
|
|
//
|
|
// Initialize the last sector cache if it has not been
|
|
// initialized.
|
|
//
|
|
|
|
if (!FwLastSectorCache.Initialized) {
|
|
FwLastSectorCache.Data =
|
|
FwAllocatePool(BL_LAST_SECTOR_CACHE_MAX_SIZE);
|
|
|
|
if (FwLastSectorCache.Data) {
|
|
FwLastSectorCache.Initialized = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Gather disk stats.
|
|
//
|
|
|
|
SectorsPerTrack = BlFileTable[FileId].u.DriveContext.Sectors;
|
|
Heads = BlFileTable[FileId].u.DriveContext.Heads;
|
|
Cylinders = BlFileTable[FileId].u.DriveContext.Cylinders;
|
|
AllowXInt13 = BlFileTable[FileId].u.DriveContext.xInt13;
|
|
Int13Unit = BlFileTable[FileId].u.DriveContext.Drive;
|
|
|
|
DBGOUT(("BiosDiskRead: unit 0x%x CHS=%lu %lu %lu\r\n",
|
|
Int13Unit,
|
|
Cylinders,
|
|
Heads,
|
|
SectorsPerTrack));
|
|
|
|
//
|
|
// Initialize locals that denote where we are in satisfying the
|
|
// request.
|
|
//
|
|
|
|
//
|
|
// If the buffer is in the first 1MB of KSEG0, we want to use the
|
|
// identity-mapped address
|
|
//
|
|
if (((ULONG_PTR)((PUCHAR)Buffer+Length) & ~KSEG0_BASE) < BIOSDISK_1MB) {
|
|
pDestInUserBuffer = (PUCHAR)((ULONG_PTR)Buffer & ~KSEG0_BASE);
|
|
} else {
|
|
pDestInUserBuffer = Buffer;
|
|
}
|
|
|
|
pEndOfUserBuffer = (PUCHAR) pDestInUserBuffer + Length;
|
|
TargetBuffer = pDestInUserBuffer;
|
|
|
|
//
|
|
// Calculating these before hand makes it easier to hand the
|
|
// special cases. Note that tail sector is the last sector this
|
|
// read wants bytes from. That is why we subtract one. We handle
|
|
// the Length == 0 case above.
|
|
//
|
|
|
|
HeadSector = BlFileTable[FileId].Position.QuadPart / SectorSize;
|
|
HeadOffset = (ULONG)(BlFileTable[FileId].Position.QuadPart % SectorSize);
|
|
|
|
TailSector = (BlFileTable[FileId].Position.QuadPart + Length - 1) / SectorSize;
|
|
TailByteCount = (ULONG)((BlFileTable[FileId].Position.QuadPart + Length - 1) % SectorSize);
|
|
TailByteCount ++;
|
|
|
|
//
|
|
// While there is data we should read, read.
|
|
//
|
|
|
|
CurrentSector = HeadSector;
|
|
|
|
while (pDestInUserBuffer != pEndOfUserBuffer) {
|
|
//
|
|
// Look to see if we can find the current sector we have to
|
|
// read in the last sector cache.
|
|
//
|
|
|
|
if (FwLastSectorCache.Valid &&
|
|
FwLastSectorCache.DeviceId == FileId &&
|
|
FwLastSectorCache.SectorNumber == CurrentSector) {
|
|
|
|
pSrc = FwLastSectorCache.Data;
|
|
CopyLength = SectorSize;
|
|
|
|
//
|
|
// Adjust copy parameters depending on whether
|
|
// this sector is the Head and/or Tail sector.
|
|
//
|
|
|
|
if (HeadSector == CurrentSector) {
|
|
pSrc += HeadOffset;
|
|
CopyLength -= HeadOffset;
|
|
}
|
|
|
|
if (TailSector == CurrentSector) {
|
|
CopyLength -= (SectorSize - TailByteCount);
|
|
}
|
|
|
|
//
|
|
// Copy the cached data to users buffer.
|
|
//
|
|
|
|
RtlCopyMemory(pDestInUserBuffer, pSrc, CopyLength);
|
|
|
|
//
|
|
// Update our status.
|
|
//
|
|
|
|
CurrentSector += 1;
|
|
pDestInUserBuffer += CopyLength;
|
|
*Count += CopyLength;
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Calculate number of sectors we have to read. Read a maximum
|
|
// of SCRATCH_BUFFER_SIZE so we can use our local buffer if the
|
|
// user's buffer crosses 64KB boundary or is not under 1MB.
|
|
//
|
|
|
|
SectorsToTransfer = (UCHAR)min ((LONG) (TailSector - CurrentSector + 1),
|
|
SCRATCH_BUFFER_SIZE / SectorSize);
|
|
if (!xInt13) {
|
|
//
|
|
// Make sure the number of sectors to transfer does not exceed
|
|
// the number of sectors left in the track.
|
|
//
|
|
SectorsToTransfer = (UCHAR)min(SectorsPerTrack - (CurrentSector % SectorsPerTrack),
|
|
SectorsToTransfer);
|
|
}
|
|
NumBytesToTransfer = SectorsToTransfer * SectorSize;
|
|
|
|
//
|
|
// Determine where we want to read into. We can use the
|
|
// current chunk of user buffer only if it is under 1MB and
|
|
// does not cross a 64KB boundary, and it can take all we want
|
|
// to read into it.
|
|
//
|
|
|
|
if (((ULONG_PTR) pDestInUserBuffer + NumBytesToTransfer < BIOSDISK_1MB) &&
|
|
(((ULONG_PTR) pDestInUserBuffer & BIOSDISK_64KB_MASK) ==
|
|
(((ULONG_PTR) pDestInUserBuffer + NumBytesToTransfer) & BIOSDISK_64KB_MASK)) &&
|
|
((pEndOfUserBuffer - pDestInUserBuffer) >= (LONG) NumBytesToTransfer)) {
|
|
|
|
pTransferDest = pDestInUserBuffer;
|
|
|
|
} else {
|
|
|
|
pTransferDest = FwDiskCache;
|
|
}
|
|
|
|
//
|
|
// Perform the read.
|
|
//
|
|
|
|
if(xInt13) {
|
|
Status = ReadExtendedPhysicalSectors(Int13Unit,
|
|
CurrentSector,
|
|
SectorsToTransfer,
|
|
pTransferDest);
|
|
} else {
|
|
Status = ReadPhysicalSectors(Int13Unit,
|
|
CurrentSector,
|
|
SectorsToTransfer,
|
|
pTransferDest,
|
|
SectorsPerTrack,
|
|
Heads,
|
|
Cylinders,
|
|
AllowXInt13);
|
|
}
|
|
|
|
if(Status != ESUCCESS) {
|
|
DBGOUT(("BiosDiskRead: read failed with %u\r\n",Status));
|
|
goto BiosDiskReadDone;
|
|
}
|
|
|
|
//
|
|
// Note the last sector that was read from the disk.
|
|
//
|
|
|
|
pLastReadSector = pTransferDest + (SectorsToTransfer - 1) * SectorSize;
|
|
LastReadSectorNumber = CurrentSector + SectorsToTransfer - 1;
|
|
|
|
//
|
|
// Note the amount read.
|
|
//
|
|
|
|
ReadLength = NumBytesToTransfer;
|
|
|
|
//
|
|
// Copy transfered data into user's buffer if we did not
|
|
// directly read into that.
|
|
//
|
|
|
|
if (pTransferDest != pDestInUserBuffer) {
|
|
pSrc = pTransferDest;
|
|
CopyLength = NumBytesToTransfer;
|
|
|
|
//
|
|
// Adjust copy parameters depending on whether
|
|
// we have read the Head and/or Tail sectors.
|
|
//
|
|
|
|
if (HeadSector == CurrentSector) {
|
|
pSrc += HeadOffset;
|
|
CopyLength -= HeadOffset;
|
|
}
|
|
|
|
if (TailSector == CurrentSector + SectorsToTransfer - 1) {
|
|
CopyLength -= (SectorSize - TailByteCount);
|
|
}
|
|
|
|
//
|
|
// Copy the read data to users buffer.
|
|
//
|
|
ASSERT(pDestInUserBuffer >= TargetBuffer);
|
|
ASSERT(pEndOfUserBuffer >= pDestInUserBuffer + CopyLength);
|
|
ASSERT(CopyLength <= SCRATCH_BUFFER_SIZE);
|
|
ASSERT(pSrc >= (PUCHAR) FwDiskCache);
|
|
ASSERT(pSrc < (PUCHAR) FwDiskCache + SCRATCH_BUFFER_SIZE);
|
|
|
|
RtlCopyMemory(pDestInUserBuffer, pSrc, CopyLength);
|
|
|
|
//
|
|
// Adjust the amount read into user's buffer.
|
|
//
|
|
|
|
ReadLength = CopyLength;
|
|
}
|
|
|
|
//
|
|
// Update our status.
|
|
//
|
|
|
|
CurrentSector += SectorsToTransfer;
|
|
pDestInUserBuffer += ReadLength;
|
|
*Count += ReadLength;
|
|
}
|
|
|
|
//
|
|
// Update the last read sector cache.
|
|
//
|
|
|
|
if (pLastReadSector &&
|
|
FwLastSectorCache.Initialized &&
|
|
BL_LAST_SECTOR_CACHE_MAX_SIZE >= SectorSize) {
|
|
|
|
FwLastSectorCache.DeviceId = FileId;
|
|
FwLastSectorCache.SectorNumber = LastReadSectorNumber;
|
|
|
|
RtlCopyMemory(FwLastSectorCache.Data,
|
|
pLastReadSector,
|
|
SectorSize);
|
|
|
|
FwLastSectorCache.Valid = TRUE;
|
|
}
|
|
|
|
DBGOUT(("BiosDiskRead: exit success\r\n"));
|
|
Status = ESUCCESS;
|
|
|
|
BiosDiskReadDone:
|
|
|
|
BlFileTable[FileId].Position.QuadPart += *Count;
|
|
return(Status);
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
BiosDiskRead(
|
|
IN ULONG FileId,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
)
|
|
{
|
|
USHORT PhysicalSectors;
|
|
|
|
PhysicalSectors = SECTOR_SIZE;
|
|
return(pBiosDiskReadWorker(FileId,Buffer,Length,Count,PhysicalSectors,FALSE));
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
BiosElToritoDiskRead(
|
|
IN ULONG FileId,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG Count
|
|
)
|
|
{
|
|
return(pBiosDiskReadWorker(FileId,Buffer,Length,Count,2048,TRUE));
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
BiosPartitionGetFileInfo(
|
|
IN ULONG FileId,
|
|
OUT PFILE_INFORMATION Finfo
|
|
)
|
|
{
|
|
//
|
|
// THIS ROUTINE DOES NOT WORK FOR PARTITION 0.
|
|
//
|
|
|
|
PPARTITION_CONTEXT Context;
|
|
|
|
RtlZeroMemory(Finfo, sizeof(FILE_INFORMATION));
|
|
|
|
Context = &BlFileTable[FileId].u.PartitionContext;
|
|
|
|
Finfo->StartingAddress.QuadPart = Context->StartingSector;
|
|
Finfo->StartingAddress.QuadPart = Finfo->StartingAddress.QuadPart << (CCHAR)Context->SectorShift;
|
|
Finfo->EndingAddress.QuadPart = Finfo->StartingAddress.QuadPart + Context->PartitionLength.QuadPart;
|
|
Finfo->Type = DiskPeripheral;
|
|
|
|
return ESUCCESS;
|
|
}
|
|
|
|
ARC_STATUS
|
|
BiosDiskGetFileInfo(
|
|
IN ULONG FileId,
|
|
OUT PFILE_INFORMATION FileInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gets the information about the isk.
|
|
|
|
Arguments:
|
|
|
|
FileId - The file id to the disk for which information is needed
|
|
|
|
FileInfo - Place holder for returning information about the disk
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS if successful, otherwise appropriate ARC error code.
|
|
|
|
--*/
|
|
{
|
|
ARC_STATUS Result = EINVAL;
|
|
|
|
if (FileInfo) {
|
|
PDRIVE_CONTEXT DriveContext;
|
|
LONGLONG DriveSize = 0;
|
|
ULONG SectorSize = SECTOR_SIZE;
|
|
|
|
DriveContext = &(BlFileTable[FileId].u.DriveContext);
|
|
Result = EIO;
|
|
|
|
//
|
|
// NOTE : SectorSize == 512 bytes for everything except
|
|
// Eltorito disks for which sector size is 2048.
|
|
//
|
|
if (DriveContext->IsCd) {
|
|
SectorSize = 2048;
|
|
}
|
|
|
|
DriveSize = (DriveContext->Heads * DriveContext->Cylinders *
|
|
DriveContext->Sectors * SectorSize);
|
|
|
|
if (DriveSize) {
|
|
RtlZeroMemory(FileInfo, sizeof(FILE_INFORMATION));
|
|
|
|
FileInfo->StartingAddress.QuadPart = 0;
|
|
FileInfo->EndingAddress.QuadPart = DriveSize;
|
|
FileInfo->CurrentPosition = BlFileTable[FileId].Position;
|
|
|
|
//
|
|
// Any thing less than 3MB is floppy drive
|
|
//
|
|
if (DriveSize <= 0x300000) {
|
|
FileInfo->Type = FloppyDiskPeripheral;
|
|
} else {
|
|
FileInfo->Type = DiskPeripheral;
|
|
}
|
|
|
|
Result = ESUCCESS;
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|