singrdk/base/boot/Singldr/usbdevice.cpp

429 lines
15 KiB
C++

//////////////////////////////////////////////////////////////////////////////
//
// usbdevice.cpp - Access Fat16 USB volumes from SINGLDR
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#include "usbdevice.h"
#include "fnames.h"
#pragma warning(disable: 4505) // Compiler generated constructor unreferenced
//////////////////////////////////////////////////////////////////////////////
//
// Private functions for reading the USB disk
// read the FAT to find the cluster that follows currentCluster
uint32 UsbDevice::CalcNextCluster(uint32 currentCluster) __far
{
uint32 SectorToRead, OffsetInSector;
uint32 result32;
uint16 result16;
OffsetInSector = (currentCluster*FatOffsetMultiplier) % (BytesPerSec);
SectorToRead = ((currentCluster*FatOffsetMultiplier) / (BytesPerSec)) + (RsvdSecs);
uint8 __far * buffer = FatBuffer;
BiosDiskReadCHS(buffer, SectorToRead, BootDrive, 1, SecsPerTrack, NumHeads);
result16 = *((uint16 __far *)(buffer+OffsetInSector));
result32 = result16; // implicit cast
return result32;
}
// read cluster # ClusterNum into a (pre-allocated) buffer
// due to CHS limitations, we have to read it one sector at a time...
void UsbDevice::ReadCluster(uint32 ClusterNum, uint8 __far * buffer) __far
{
uint8 __far * buff = buffer;
int sectorcounter;
// figure out the first sector:
uint32 startingSector = ((ClusterNum - 2) * (SecsPerClus)) + (FirstDataSec);
for (sectorcounter = 0; sectorcounter < SecsPerClus; sectorcounter++) {
// read one sector
BiosDiskReadCHS(buff, startingSector, BootDrive, 1, SecsPerTrack, NumHeads);
// advance to next sector
startingSector++;
// advance the buffer
buff += BytesPerSec;
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Private Method for matching filenames
// (uses public funcs from FatDevice code)
// Search from the first cluster of a directory entry to find the Fat
// encoding of a filename. We accomplish this through a FSM.
// Fat16RootDir is a special flag the root directory is not a cluster chain,
// but all other directories are.
int UsbDevice::DirLookup(LPCHAR filename,
uint8 /* len */,
FilePtr directory,
FilePtr file,
int Fat16RootDir) __far
{
// points into the data we've loaded from disk:
uint8 __far * buffer;
// we will use the generic term "block" to refer to a readable entity
// on the disk, since the Fat16 root dir is in sectors while the rest
// of Fat is in clusters
uint16 totalBlocks, blockCounter = 0;
uint16 nextBlock, entriesPerBlock, entryCounter = 0;
if (Fat16RootDir) {
totalBlocks = RootDirSecs;
entriesPerBlock = BytesPerSec / 32;
nextBlock = (uint16)RootStartSec;
}
else {
totalBlocks = 2; // value doesn't matter; make sure totalBlocks>blockCounter
entriesPerBlock = (uint16)DirEntriesPerClus;
nextBlock = (uint16)directory->FirstBlock;
}
// "segment" will refer to the portion of the LongFileName
// about which we care, since it will be in 13-char chunks
uint8 totalSegs, segCounter, segsMatched = 0;
totalSegs = FullFNameLength(filename);
totalSegs = (uint8) ((totalSegs + 12) / 13);
segCounter = totalSegs;
// long file name verification requires a checksum, as well as matching a
// special "entry signature" in byte[0] of the directory entry
uint8 checksum = 0, signature = 0;
// the primary loop fetches data to check
while (blockCounter < totalBlocks) {
// read some data and set a counter for when we need more data
if (Fat16RootDir) {
BiosDiskReadCHS(FatBuffer, nextBlock, BootDrive, 1, SecsPerTrack, NumHeads);
entryCounter = BytesPerSec / 32;
}
else {
ReadCluster(nextBlock, FatBuffer);
entryCounter = (uint16)DirEntriesPerClus;
}
buffer = FatBuffer;
// the inner loop checks entries
while (entryCounter>0) {
// failure condition #1: if the first bit is zero,
// there are no more directory entries.
if (buffer[0] == 0) {
return -1;
}
// success condition #1: do we match on 8.3?
// success condition #2: if segCounter == 0, does checksum match?
if ((FatShortNameMatch(buffer, filename) == 1) ||
(segCounter == 0 && FatChecksum(buffer) == checksum))
{
file->FirstBlock = (*((uint16 __far *)(buffer+20))<<16) +
*((uint16 __far *)(buffer+26));
file->Size = *((uint32 __far *)(buffer+28));
return 0;
}
// progress condition: do we match on the current segment of
// long file name?
// first calc checksum and the signature
if (segCounter == totalSegs) {
signature = (uint8) (segCounter | 0x40);
checksum = buffer[13];
}
else {
signature = segCounter;
}
// now do the check
if (signature == FatLongNamePartialMatch(buffer, filename, signature, checksum)) {
// advance to next directory entry and earlier segment in the filename
segCounter--;
buffer += 32;
entryCounter--;
}
else {
// if we were checking the first LFN entry then it's time
// to advance the counter
if (segCounter == totalSegs) {
buffer += 32;
entryCounter--;
}
else {
// otherwise this might just be the start of
// the correct entry, so don't advance the counter,
// just reset to the first segment of the filename
segCounter = totalSegs;
}
}
}
// calculate next data block
if (Fat16RootDir == 1) {
nextBlock++;
blockCounter++;
}
else {
nextBlock = (uint16) CalcNextCluster(nextBlock);
if (nextBlock >= (EndOfClusterMarker)) {
blockCounter = 2;
}
}
}
return -1;
}
//////////////////////////////////////////////////////////////////////////////
//
// Public Functions
int UsbDevice::OpenDevice() __far
{
int entrycounter = 4; // up to 4 entries in partition table
// allocate memory for reading a sector (assume 512-byte sector size)
BootSectorBuffer = (uint8 __far *) alloc(512, 0);
// Read the BootSector
// we are in a bind here: we don't know the SecsPerTrack or the NumHeads
// but we need them to do the read!
BiosDiskReadCHS(BootSectorBuffer, 0, BootDrive, 1, 1, 1);
// temp to make using far pointers easier
uint8 __far * buffer = BootSectorBuffer;
// read data that should always be in the same place, no matter what
BytesPerSec = *((uint16 __far *)(buffer+11));
SecsPerClus = buffer[13];
SecsPerTrack = *((uint16 __far *)(buffer+24));
NumHeads = *((uint16 __far *)(buffer+26));
RsvdSecs = *((uint16 __far *)(buffer+14));
NumFats = buffer[16];
RootDirEntries = *((uint16 __far *)(buffer+17));
HiddenSecs = *((uint32 __far *)(buffer+28));
// "compute" the NumFatSecs and TotalSecs fields
// (i.e. read one value, if it is zero, read another)
uint16 tmp16;
tmp16 = *((uint16 __far *)(buffer+19));
if (tmp16 == 0) {
TotalSecs = *((uint32 __far *)(buffer+32));
}
else {
TotalSecs = tmp16;
}
tmp16 = *((uint16 __far *)(buffer+22));
if (tmp16 == 0) {
NumFatSecs = *((uint32 __far *)(buffer+36));
}
else {
NumFatSecs = tmp16;
}
// now we may compute RootDirSectors =
// ((BPB_RootEntCnt*32) + (BPB_BytsPerSec-1))/BPB_BytesPerSec
RootDirSecs = ((RootDirEntries*32) + (BytesPerSec-1))/BytesPerSec;
// Next compute the total data sectors in the volume:
TotalDataSecs = TotalSecs - (RsvdSecs + (NumFats*NumFatSecs) + RootDirSecs);
// Finally, compute the # of clusters in the volume:
ClusterCount = TotalDataSecs / SecsPerClus;
// Do the verification:
if (!(ClusterCount >= 4085 && ClusterCount<65525)) {
printf("USB: File System Type is not supported\n");
return -1; // the partition table did not match the boot sector
// we can't trust this filesystem.
}
FatType = 16;
// now we can set fields that are FAT-type specific:
EndOfClusterMarker = 0xFFF8;
BadClusterMarker = 0xFFF7;
FatOffsetMultiplier = 2;
RootStartSec = (RsvdSecs + (NumFats*NumFatSecs));
// final computations:
FirstDataSec = (NumFats*NumFatSecs) + RsvdSecs + RootDirSecs;
BytesPerClus = SecsPerClus * BytesPerSec;
DirEntriesPerClus = BytesPerClus/32;
// allocate data for remaining fs buffers:
FatBuffer = (uint8 __far *) alloc((uint16)BytesPerClus, 0); // exactly the size of a cluster
FileBuffer = (uint8 __far *) alloc(0x7FFF, 0); // 32KB
// display results
#if 0
printf("\nUsb Configuration\n");
printf("------------------------------\n");
printf("Boot Drive = %2xh\n", BootDrive);
printf("Bytes/Sector = %4xh\n", BytesPerSec);
printf("\nFileSystem Configuration\n");
printf("------------------------------\n");
printf("Fat Type = FAT%d\n", FatType);
printf("Sectors/Cluster = %2xh\n", SecsPerClus);
printf("Reserved Sectors = %4xh\n", RsvdSecs);
printf("Number of Fats = %2xh\n", NumFats);
printf("Hidden Sectors = %8lxh\n", HiddenSecs);
printf("Sectors per Fat = %8lxh\n", NumFatSecs);
printf("First Data Sector in Partition = %8lxh\n", FirstDataSec);
printf("Bytes/Cluster = %8lxh\n", BytesPerClus);
printf("Dir Entries/Cluster = %8lxh\n", DirEntriesPerClus);
#endif
return 0;
}
int UsbDevice::CloseDevice() __far
{
return 0;
}
int UsbDevice::GetFileProperties(LPCHAR filename,
FilePtr file,
FilePtr directory) __far
{
LPCHAR fname = filename;
uint8 len;
int result;
char c;
int isRootDir = 1; // the Fat16 Root Directory is special.
// Flag the first Fat16 read (but not subsequent reads)
// we'll continually recycle the File and Directory structs
file->Size = 0;
file->FirstBlock = 0;
// traverse through the filename, identifying tokens and looking them
// up in the current context
bool done = 0;
while (!done) {
// consume the leading '/'
while (fname[0] == '/') {
fname++;
}
// failure condition: whitespace
if (IsEndToken(fname[0])) {
printf("USB: Invalid filename: ");
PutFName(filename);
printf("\n");
return -1;
}
// find the next separator in the filename ('/' or whitespace),
// store it, and replace it with 0
len = ShortFNameLength(fname);
c = fname[len];
fname[len] = 0;
// look it up and put the results into File
result = DirLookup(fname, len, directory, file, isRootDir);
// undo the change we made to the filename:
fname[len] = c;
// now shut off the isRootDir flag for subsequent directory scans
isRootDir = 0;
if (result == -1) {
printf("USB: File not found: ");
PutFName(filename);
printf("\n");
return -1;
}
// check loop termination condition
if (c != '/') {
done = 1;
}
else {
fname += len;
// transfer results from File into Directory
directory->FirstBlock = file->FirstBlock;
directory->Size = file->Size;
file->Size = 0;
file->FirstBlock = 0;
}
}
return 0;
}
INT16 UsbDevice::ReadFileLow(LPCHAR /* filename */,
FilePtr file,
uint8 __far * buffer) __far
{
uint32 bytesread = 0;
uint8 __far * destination = buffer;
uint32 currentcluster = file->FirstBlock;
while (bytesread < file->Size) {
// read the current cluster
if (currentcluster == BadClusterMarker) {
printf("USB: Bad Cluster encountered\n");
return -1;
}
ReadCluster(currentcluster, destination);
currentcluster = CalcNextCluster(currentcluster);
bytesread += BytesPerClus;
destination = (uint8 __far *) _MK_FP(_FP_SEG(destination), (_FP_OFF(destination)+BytesPerClus));
}
return 0;
}
uint32 UsbDevice::ReadFileHigh(
LPCHAR /* filename */,
FilePtr file,
uint32 destinationAddress,
uint32 /* cbDestinationAddress */
) __far
{
uint32 bytesread = 0;
uint32 nextdestination = destinationAddress;
uint32 currentcluster = file->FirstBlock;
uint32 sector;
uint32 bufferaddress = PointerToUint32(FileBuffer);
while (bytesread < file->Size) {
// ensure cluster is valid
if (currentcluster == BadClusterMarker) {
printf("USB: Bad Cluster encountered\n");
return bytesread;
}
// calc true sector for this cluster
sector = ((currentcluster-2) * SecsPerClus)+FirstDataSec;
// read one cluster
BiosDiskReadCHS(FileBuffer, sector, BootDrive, SecsPerClus, SecsPerTrack, NumHeads);
// move the data into extended memory
PModeTransfer(bufferaddress, nextdestination, BytesPerClus);
// get next cluster number
currentcluster = CalcNextCluster(currentcluster);
// update the count of bytes read
bytesread += BytesPerClus;
// and update the destination address
nextdestination += BytesPerClus;
}
// since we read full sectors at the bios level, we should
// trim our count back down a bit here
if (bytesread>file->Size) {
bytesread = file->Size;
}
return bytesread;
}