singrdk/base/boot/SingLdrPc/blcdrom.cpp

588 lines
16 KiB
C++

//++
//
// Copyright (c) Microsoft Corporation
//
// Module Name:
//
// blcdrom.cpp
//
// Abstract:
//
// This module implements CDROM support for the boot loader.
//
//--
#include "bl.h"
#define ISO9660_LOGICAL_BLOCK_SIZE 2048
#define ISO9660_ROUND_UP_TO_LOGICAL_BLOCKS(X) (((X) + ISO9660_LOGICAL_BLOCK_SIZE - 1) & (~(ISO9660_LOGICAL_BLOCK_SIZE - 1)))
#define ISO9660_VOLUME_SPACE_DATA_AREA_LBN 16
#define ISO9660_VOLUME_DESCRIPTOR_TYPE_BOOT 0x00
#define ISO9660_VOLUME_DESCRIPTOR_TYPE_PRIMARY 0x01
#define ISO9660_VOLUME_DESCRIPTOR_TYPE_SUPPLEMENTARY 0x02
#define ISO9660_VOLUME_DESCRIPTOR_TYPE_TERMINATOR 0xFF
#define ISO9660_MAX_PATH 255
#pragma pack(1)
typedef struct _ISO9660_LOGICAL_BLOCK {
UINT8 Data[ISO9660_LOGICAL_BLOCK_SIZE];
} ISO9660_LOGICAL_BLOCK, *PISO9660_LOGICAL_BLOCK;
C_ASSERT(sizeof(ISO9660_LOGICAL_BLOCK) == ISO9660_LOGICAL_BLOCK_SIZE);
typedef struct _ISO9660_DIRECTORY_RECORD {
UINT8 DirectoryRecordLength;
UINT8 ExtendedAttributeRecordLength;
UINT32 ExtentLocation;
UINT32 ExtentLocation_BigEndian;
UINT32 DataLength;
UINT32 DataLength_BigEndian;
UINT8 RecordingDateTime[7];
union {
struct {
UINT8 Present:1;
UINT8 Directory:1;
UINT8 AssociatedFile:1;
UINT8 Record:1;
UINT8 Protection:1;
UINT8 Reserved:2;
UINT8 MultiExtent:1;
} s1;
UINT8 FileFlags;
} u1;
UINT8 FileUnitSize;
UINT8 InterleaveGapSize;
UINT16 VolumeSequenceNumber;
UINT16 VolumeSequenceNumber_BigEndian;
UINT8 FileIdentifierLength;
UINT8 FileIdentifier[1];
} ISO9660_DIRECTORY_RECORD, *PISO9660_DIRECTORY_RECORD;
C_ASSERT(sizeof(ISO9660_DIRECTORY_RECORD) == 34);
typedef struct _ISO9660_PRIMARY_VOLUME_DESCRIPTOR {
UINT8 VolumeDescriptorType;
UINT8 StandardIdentifier[5];
UINT8 VolumeDescriptorVersion;
UINT8 Reserved1;
UINT8 SystemIdentifier[32];
UINT8 VolumeIdentifier[32];
UINT8 Reserved2[8];
UINT32 VolumeSpaceSize;
UINT32 VolumeSpaceSize_BigEndian;
UINT8 Reserved3[32];
UINT16 VolumeSetSize;
UINT16 VolumeSetSize_BigEndian;
UINT16 VolumeSequenceNumber;
UINT16 VolumeSequenceNumber_BigEndian;
UINT16 LogicalBlockSize;
UINT16 LogicalBlockSize_BigEndian;
UINT32 PathTableSize;
UINT32 PathTableSize_BigEndian;
UINT32 PathTableLocation[2];
UINT32 PathTableLocation_BigEndian[2];
ISO9660_DIRECTORY_RECORD RootDirectory;
UINT8 VolumeSetIdentifier[128];
UINT8 PublisherIdentifier[128];
UINT8 DataPreparerIdentifier[128];
UINT8 ApplicationIdentifier[128];
UINT8 CopyrightFileIdentifier[37];
UINT8 AbstractFileIdentifier[37];
UINT8 BibliographicFileIdentifier[37];
UINT8 VolumeCreationDateTime[17];
UINT8 VolumeModificationDateTime[17];
UINT8 VolumeExpirationDateTime[17];
UINT8 VolumeEffectiveDateTime[17];
UINT8 FileStructureVersion;
UINT8 Reserved4;
UINT8 ReservedForApplication[512];
UINT8 Reserved5[653];
} ISO9660_PRIMARY_VOLUME_DESCRIPTOR, *PISO9660_PRIMARY_VOLUME_DESCRIPTOR;
C_ASSERT(sizeof(ISO9660_PRIMARY_VOLUME_DESCRIPTOR) == ISO9660_LOGICAL_BLOCK_SIZE);
typedef struct _ISO9660_SUPPLEMENTARY_VOLUME_DESCRIPTOR {
UINT8 VolumeDescriptorType;
UINT8 StandardIdentifier[5];
UINT8 VolumeDescriptorVersion;
UINT8 VolumeFlags;
UINT8 SystemIdentifier[32];
UINT8 VolumeIdentifier[32];
UINT8 Reserved2[8];
UINT32 VolumeSpaceSize;
UINT32 VolumeSpaceSize_BigEndian;
UINT8 EscapeSequences[32];
UINT16 VolumeSetSize;
UINT16 VolumeSetSize_BigEndian;
UINT16 VolumeSequenceNumber;
UINT16 VolumeSequenceNumber_BigEndian;
UINT16 LogicalBlockSize;
UINT16 LogicalBlockSize_BigEndian;
UINT32 PathTableSize;
UINT32 PathTableSize_BigEndian;
UINT32 PathTableLocation[2];
UINT32 PathTableLocation_BigEndian[2];
ISO9660_DIRECTORY_RECORD RootDirectory;
UINT8 VolumeSetIdentifier[128];
UINT8 PublisherIdentifier[128];
UINT8 DataPreparerIdentifier[128];
UINT8 ApplicationIdentifier[128];
UINT8 CopyrightFileIdentifier[37];
UINT8 AbstractFileIdentifier[37];
UINT8 BibliographicFileIdentifier[37];
UINT8 VolumeCreationDateTime[17];
UINT8 VolumeModificationDateTime[17];
UINT8 VolumeExpirationDateTime[17];
UINT8 VolumeEffectiveDateTime[17];
UINT8 FileStructureVersion;
UINT8 Reserved4;
UINT8 ReservedForApplication[512];
UINT8 Reserved5[653];
} ISO9660_SUPPLEMENTARY_VOLUME_DESCRIPTOR, *PISO9660_SUPPLEMENTARY_VOLUME_DESCRIPTOR;
C_ASSERT(sizeof(ISO9660_SUPPLEMENTARY_VOLUME_DESCRIPTOR) == ISO9660_LOGICAL_BLOCK_SIZE);
typedef struct _ISO9660_VOLUME_DESCRIPTOR {
union {
UINT8 VolumeDescriptorType;
ISO9660_PRIMARY_VOLUME_DESCRIPTOR Primary;
ISO9660_SUPPLEMENTARY_VOLUME_DESCRIPTOR Supplementary;
} u1;
} ISO9660_VOLUME_DESCRIPTOR, *PISO9660_VOLUME_DESCRIPTOR;
C_ASSERT(sizeof(ISO9660_VOLUME_DESCRIPTOR) == ISO9660_LOGICAL_BLOCK_SIZE);
#pragma pack()
UINT8 BlCdDriveId;
INT13_DRIVE_PARAMETERS BlCdDriveParameters;
ISO9660_VOLUME_DESCRIPTOR BlCdVolumeDescriptor;
ISO9660_LOGICAL_BLOCK BlCdTemporaryBlock[32];
UINT16 BlCdTemporaryBlockCount = sizeof(BlCdTemporaryBlock) / sizeof(BlCdTemporaryBlock[0]);
VOID
BlCdReadLogicalBlock(
UINT32 LogicalBlockNumber,
UINT32 NumberOfBlocks,
PISO9660_LOGICAL_BLOCK LogicalBlock
)
//++
//
// Routine Description:
//
// This function reads the specified logical block.
//
// Arguments:
//
// LogicalBlockNumber - Supplies the number of the logical block to read.
//
// NumberOfBlocks - Supplies the number of blocks to read.
//
// LogicalBlock - Receives logical block data.
//
//--
{
UINT16 ChunkSize;
BOOLEAN Result;
while (NumberOfBlocks > 0) {
if (NumberOfBlocks < BlCdTemporaryBlockCount) {
ChunkSize = (UINT16) NumberOfBlocks;
} else {
ChunkSize = BlCdTemporaryBlockCount;
}
Result = BlRtlReadDrive(BlCdDriveId,
LogicalBlockNumber,
ChunkSize,
BlCdTemporaryBlock);
if (Result == FALSE) {
BlRtlPrintf("CDROM: I/O Error: DriveID=0x%02x LBN=%u Count=%u\n",
BlCdDriveId,
LogicalBlockNumber,
ChunkSize);
BlRtlHalt();
}
BlRtlCopyMemory(LogicalBlock,
BlCdTemporaryBlock,
ChunkSize * sizeof(ISO9660_LOGICAL_BLOCK));
LogicalBlockNumber += ChunkSize;
LogicalBlock += ChunkSize;
NumberOfBlocks -= ChunkSize;
}
return;
}
BOOLEAN
BlCdFindDirectoryRecord(
PCSTR Path,
PISO9660_DIRECTORY_RECORD DirectoryRecord
)
//++
//
// Routine Description:
//
// This function finds the directory record for the specified path.
//
// Arguments:
//
// Path - Supplies the path to look up.
//
// DirectoryRecord - Receives directory record.
//
// Return Value:
//
// TRUE, if directory record was found.
// FALSE, otherwise.
//
//--
{
PISO9660_LOGICAL_BLOCK DirectoryData;
UINT32 DirectoryDataIndex;
ULONG_PTR DirectoryDataLimit;
UINT32 DirectoryDataExtentStart;
UINT32 DirectoryDataExtentSize;
PISO9660_DIRECTORY_RECORD Entry;
UINT32 Index;
PCSTR NextToken;
PCSTR Separator;
CHAR Temp[ISO9660_MAX_PATH];
CHAR Token[ISO9660_MAX_PATH];
UINT32 TokenSize;
DirectoryDataExtentStart = BlCdVolumeDescriptor.u1.Supplementary.RootDirectory.ExtentLocation;
DirectoryDataExtentSize = ROUND_UP_TO_POWER2(BlCdVolumeDescriptor.u1.Supplementary.RootDirectory.DataLength, ISO9660_LOGICAL_BLOCK_SIZE) / ISO9660_LOGICAL_BLOCK_SIZE;
NextToken = Path;
for (;;) {
BLASSERT(DirectoryDataExtentStart > ISO9660_VOLUME_SPACE_DATA_AREA_LBN);
BLASSERT(DirectoryDataExtentSize > 0);
BLASSERT((*NextToken != 0) && (*NextToken != '/'));
Separator = NextToken;
while ((*Separator != '/') && (*Separator != 0)) {
Separator += 1;
}
TokenSize = (UINT32) (Separator - NextToken);
for (Index = 0; Index < TokenSize; Index += 1) {
Token[Index] = BlRtlConvertCharacterToUpperCase(NextToken[Index]);
}
Token[TokenSize] = 0;
DirectoryData = (PISO9660_LOGICAL_BLOCK) BlPoolAllocateBlock(DirectoryDataExtentSize * ISO9660_LOGICAL_BLOCK_SIZE);
DirectoryDataLimit = (ULONG_PTR) DirectoryData + (DirectoryDataExtentSize * ISO9660_LOGICAL_BLOCK_SIZE);
BlCdReadLogicalBlock(DirectoryDataExtentStart,
DirectoryDataExtentSize,
DirectoryData);
Entry = NULL;
for (DirectoryDataIndex = 0; (Entry == NULL) && (DirectoryDataIndex < DirectoryDataExtentSize); DirectoryDataIndex += 1) {
Entry = (PISO9660_DIRECTORY_RECORD) &DirectoryData[DirectoryDataIndex];
for (;;) {
if (Entry->DirectoryRecordLength == 0) {
Entry = NULL;
break;
}
if (Entry->FileIdentifierLength == (TokenSize * 2)) {
for (Index = 0; Index < TokenSize; Index += 1) {
Temp[Index] = BlRtlConvertCharacterToUpperCase(Entry->FileIdentifier[(Index * 2) + 1]);
}
if (BlRtlCompareMemory(Temp, Token, TokenSize) != FALSE) {
break;
}
}
Entry = (PISO9660_DIRECTORY_RECORD) ROUND_UP_TO_POWER2((((ULONG_PTR) Entry) + Entry->DirectoryRecordLength), 2);
if ((ULONG_PTR) Entry >= DirectoryDataLimit) {
Entry = NULL;
break;
}
}
}
if (Entry == NULL) {
BlPoolFreeBlock(DirectoryData);
return FALSE;
}
if (*Separator == 0) {
BlRtlCopyMemory(DirectoryRecord,
Entry,
sizeof(ISO9660_DIRECTORY_RECORD));
BlPoolFreeBlock(DirectoryData);
return TRUE;
}
if (Entry->u1.s1.Directory == FALSE) {
BlPoolFreeBlock(DirectoryData);
return FALSE;
}
DirectoryDataExtentStart = Entry->ExtentLocation;
DirectoryDataExtentSize = ROUND_UP_TO_POWER2(Entry->DataLength, ISO9660_LOGICAL_BLOCK_SIZE) / ISO9660_LOGICAL_BLOCK_SIZE;
NextToken = Separator + 1;
BlPoolFreeBlock(DirectoryData);
}
}
BOOLEAN
BlCdGetFileSize(
PCSTR Path,
PUINT32 FileSize
)
//++
//
// Routine Description:
//
// This function queries the size of the specified file.
//
// Arguments:
//
// Path - Supplies the path to the file to query.
//
// FileSize - Receives the size of the file.
//
// Return Value:
//
// TRUE, if the query operation was successful.
// FALSE, otherwise.
//
//--
{
ISO9660_DIRECTORY_RECORD DirectoryRecord;
if (BlCdFindDirectoryRecord(Path, &DirectoryRecord) == FALSE) {
BlRtlPrintf("CdGetFileSize: [%s] FAILED 1\n", Path);
return FALSE;
}
if (DirectoryRecord.u1.s1.Directory != FALSE) {
BlRtlPrintf("CdGetFileSize: [%s] FAILED 2\n", Path);
return FALSE;
}
*FileSize = DirectoryRecord.DataLength;
return TRUE;
}
BOOLEAN
BlCdReadFile(
PCSTR Path,
PVOID Buffer,
UINT32 NumberOfBytes
)
//++
//
// Routine Description:
//
// This function reads from the specified file.
//
// Arguments:
//
// Path - Supplies the path to the file to read.
//
// Buffer - Receives data.
//
// NumberOfBytes - Supplies the number of bytes to read.
//
// Return Value:
//
// TRUE, if the read operation was successful.
// FALSE, otherwise.
//
//--
{
ISO9660_LOGICAL_BLOCK Block;
UINT32 ChunkOffset;
UINT32 ChunkSize;
ISO9660_DIRECTORY_RECORD DirectoryRecord;
PUINT8 Next;
UINT32 Offset = 0;
if (BlCdFindDirectoryRecord(Path, &DirectoryRecord) == FALSE) {
return FALSE;
}
if (DirectoryRecord.u1.s1.Directory != FALSE) {
return FALSE;
}
if (NumberOfBytes > DirectoryRecord.DataLength) {
return FALSE;
}
if (NumberOfBytes == 0) {
return TRUE;
}
Next = (PUINT8) Buffer;
//
// Handle full-read blocks in a single step.
//
if (NumberOfBytes >= ISO9660_LOGICAL_BLOCK_SIZE) {
ChunkSize = NumberOfBytes - (NumberOfBytes % ISO9660_LOGICAL_BLOCK_SIZE);
BlCdReadLogicalBlock(DirectoryRecord.ExtentLocation,
ChunkSize / ISO9660_LOGICAL_BLOCK_SIZE,
(PISO9660_LOGICAL_BLOCK) Next);
Next += ChunkSize;
Offset += ChunkSize;
NumberOfBytes -= ChunkSize;
}
//
// Check if the ending block is a partial read.
//
BLASSERT(NumberOfBytes < ISO9660_LOGICAL_BLOCK_SIZE);
if (NumberOfBytes > 0) {
BlCdReadLogicalBlock(DirectoryRecord.ExtentLocation + (Offset / ISO9660_LOGICAL_BLOCK_SIZE),
1,
&Block);
BlRtlCopyMemory(Next,
Block.Data,
NumberOfBytes);
}
return TRUE;
}
VOID
BlCdInitialize(
UINT8 DriveId
)
//++
//
// Routine Description:
//
// This function initializes CDROM support.
//
// Arguments:
//
// DriveId - Supplies CDROM drive ID.
//
// --
{
UINT32 LogicalBlockNumber;
BlCdDriveId = DriveId;
if (BlRtlGetDriveParameters(BlCdDriveId, &BlCdDriveParameters) == FALSE) {
BlRtlPrintf("CDROM: Unable to get drive parameters!\n");
BlRtlHalt();
}
if (BlCdDriveParameters.BytesPerSector != ISO9660_LOGICAL_BLOCK_SIZE) {
BlRtlPrintf("CDROM: Unexpected sector size!\n");
BlRtlHalt();
}
//
// Locate Joliet volume descriptor.
//
LogicalBlockNumber = ISO9660_VOLUME_SPACE_DATA_AREA_LBN;
for (;;) {
BlCdReadLogicalBlock(LogicalBlockNumber,
1,
(PISO9660_LOGICAL_BLOCK) &BlCdVolumeDescriptor);
if ((BlCdVolumeDescriptor.u1.VolumeDescriptorType == ISO9660_VOLUME_DESCRIPTOR_TYPE_SUPPLEMENTARY) &&
(BlCdVolumeDescriptor.u1.Supplementary.EscapeSequences[0] == 0x25) &&
(BlCdVolumeDescriptor.u1.Supplementary.EscapeSequences[1] == 0x2F) &&
(BlCdVolumeDescriptor.u1.Supplementary.EscapeSequences[2] == 0x45)){
break;
}
if (BlCdVolumeDescriptor.u1.VolumeDescriptorType == ISO9660_VOLUME_DESCRIPTOR_TYPE_TERMINATOR) {
BlRtlPrintf("CDROM: Unable to find Joliet volume descriptor.\n");
BlRtlHalt();
}
LogicalBlockNumber += 1;
}
BlFsGetFileSize = BlCdGetFileSize;
BlFsReadFile = BlCdReadFile;
return;
}