reactos/boot/freeldr/freeldr/arch/i386/hardware.c
2019-08-31 01:42:40 +02:00

477 lines
14 KiB
C

/*
* FreeLoader
*
* Copyright (C) 2003, 2004 Eric Kohl
* Copyright (C) 2009 Hervé Poussineau
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <freeldr.h>
#include <debug.h>
DBG_DEFAULT_CHANNEL(HWDETECT);
#define MILLISEC (10)
#define PRECISION (8)
#define HZ (100)
#define CLOCK_TICK_RATE (1193182)
#define LATCH (CLOCK_TICK_RATE / HZ)
static unsigned int delay_count = 1;
/* Used for BIOS disks pre-enumeration performed when detecting the boot devices in InitializeBootDevices() */
extern UCHAR PcBiosDiskCount;
/* This function is slightly different in its PC and XBOX versions */
GET_HARDDISK_CONFIG_DATA GetHarddiskConfigurationData = NULL;
PCHAR
GetHarddiskIdentifier(UCHAR DriveNumber);
/* FUNCTIONS *****************************************************************/
static
VOID
__StallExecutionProcessor(ULONG Loops)
{
register volatile unsigned int i;
for (i = 0; i < Loops; i++);
}
VOID StallExecutionProcessor(ULONG Microseconds)
{
ULONGLONG LoopCount = ((ULONGLONG)delay_count * (ULONGLONG)Microseconds) / 1000ULL;
__StallExecutionProcessor((ULONG)LoopCount);
}
static
ULONG
Read8254Timer(VOID)
{
ULONG Count;
WRITE_PORT_UCHAR((PUCHAR)0x43, 0x00);
Count = READ_PORT_UCHAR((PUCHAR)0x40);
Count |= READ_PORT_UCHAR((PUCHAR)0x40) << 8;
return Count;
}
static
VOID
WaitFor8254Wraparound(VOID)
{
ULONG CurCount;
ULONG PrevCount = ~0;
LONG Delta;
CurCount = Read8254Timer();
do
{
PrevCount = CurCount;
CurCount = Read8254Timer();
Delta = CurCount - PrevCount;
/*
* This limit for delta seems arbitrary, but it isn't, it's
* slightly above the level of error a buggy Mercury/Neptune
* chipset timer can cause.
*/
}
while (Delta < 300);
}
VOID
HalpCalibrateStallExecution(VOID)
{
ULONG i;
ULONG calib_bit;
ULONG CurCount;
/* Initialise timer interrupt with MILLISECOND ms interval */
WRITE_PORT_UCHAR((PUCHAR)0x43, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */
WRITE_PORT_UCHAR((PUCHAR)0x40, LATCH & 0xff); /* LSB */
WRITE_PORT_UCHAR((PUCHAR)0x40, LATCH >> 8); /* MSB */
/* Stage 1: Coarse calibration */
delay_count = 1;
do
{
/* Next delay count to try */
delay_count <<= 1;
WaitFor8254Wraparound();
/* Do the delay */
__StallExecutionProcessor(delay_count);
CurCount = Read8254Timer();
}
while (CurCount > LATCH / 2);
/* Get bottom value for delay */
delay_count >>= 1;
/* Stage 2: Fine calibration */
/* Which bit are we going to test */
calib_bit = delay_count;
for (i = 0; i < PRECISION; i++)
{
/* Next bit to calibrate */
calib_bit >>= 1;
/* If we have done all bits, stop */
if (!calib_bit) break;
/* Set the bit in delay_count */
delay_count |= calib_bit;
WaitFor8254Wraparound();
/* Do the delay */
__StallExecutionProcessor(delay_count);
CurCount = Read8254Timer();
/* If a tick has passed, turn the calibrated bit back off */
if (CurCount <= LATCH / 2)
delay_count &= ~calib_bit;
}
/* We're finished: Do the finishing touches */
/* Calculate delay_count for 1ms */
delay_count /= (MILLISEC / 2);
}
static
UCHAR
GetFloppyType(UCHAR DriveNumber)
{
UCHAR Data;
WRITE_PORT_UCHAR((PUCHAR)0x70, 0x10);
Data = READ_PORT_UCHAR((PUCHAR)0x71);
if (DriveNumber == 0)
return Data >> 4;
else if (DriveNumber == 1)
return Data & 0x0F;
return 0;
}
static
PVOID
GetInt1eTable(VOID)
{
PUSHORT SegPtr = (PUSHORT)0x7A;
PUSHORT OfsPtr = (PUSHORT)0x78;
return (PVOID)((ULONG_PTR)(((ULONG)(*SegPtr)) << 4) + (ULONG)(*OfsPtr));
}
static
VOID
DetectBiosFloppyPeripheral(PCONFIGURATION_COMPONENT_DATA ControllerKey)
{
PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
PCM_FLOPPY_DEVICE_DATA FloppyData;
CHAR Identifier[20];
PCONFIGURATION_COMPONENT_DATA PeripheralKey;
ULONG Size;
UCHAR FloppyNumber;
UCHAR FloppyType;
ULONG MaxDensity[6] = {0, 360, 1200, 720, 1440, 2880};
PUCHAR Ptr;
for (FloppyNumber = 0; FloppyNumber < 2; FloppyNumber++)
{
FloppyType = GetFloppyType(FloppyNumber);
if ((FloppyType > 5) || (FloppyType == 0))
continue;
if (!DiskResetController(FloppyNumber))
continue;
Ptr = GetInt1eTable();
/* Set 'Identifier' value */
sprintf(Identifier, "FLOPPY%d", FloppyNumber + 1);
Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
sizeof(CM_FLOPPY_DEVICE_DATA);
PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
if (PartialResourceList == NULL)
{
ERR("Failed to allocate resource descriptor\n");
return;
}
memset(PartialResourceList, 0, Size);
PartialResourceList->Version = 1;
PartialResourceList->Revision = 1;
PartialResourceList->Count = 1;
PartialDescriptor = &PartialResourceList->PartialDescriptors[0];
PartialDescriptor->Type = CmResourceTypeDeviceSpecific;
PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
PartialDescriptor->u.DeviceSpecificData.DataSize = sizeof(CM_FLOPPY_DEVICE_DATA);
FloppyData = (PVOID)(((ULONG_PTR)PartialResourceList) + sizeof(CM_PARTIAL_RESOURCE_LIST));
FloppyData->Version = 2;
FloppyData->Revision = 0;
FloppyData->MaxDensity = MaxDensity[FloppyType];
FloppyData->MountDensity = 0;
RtlCopyMemory(&FloppyData->StepRateHeadUnloadTime, Ptr, 11);
FloppyData->MaximumTrackValue = (FloppyType == 1) ? 39 : 79;
FloppyData->DataTransferRate = 0;
FldrCreateComponentKey(ControllerKey,
PeripheralClass,
FloppyDiskPeripheral,
Input | Output,
FloppyNumber,
0xFFFFFFFF,
Identifier,
PartialResourceList,
Size,
&PeripheralKey);
}
}
static
PCONFIGURATION_COMPONENT_DATA
DetectBiosFloppyController(PCONFIGURATION_COMPONENT_DATA BusKey)
{
PCONFIGURATION_COMPONENT_DATA ControllerKey;
PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
ULONG Size;
ULONG FloppyCount;
FloppyCount = MachGetFloppyCount();
TRACE("Floppy count: %u\n", FloppyCount);
/* Always create a BIOS disk controller, no matter if we have floppy drives or not */
Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
2 * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
if (PartialResourceList == NULL)
{
ERR("Failed to allocate resource descriptor\n");
return NULL;
}
memset(PartialResourceList, 0, Size);
/* Initialize resource descriptor */
PartialResourceList->Version = 1;
PartialResourceList->Revision = 1;
PartialResourceList->Count = 3;
/* Set IO Port */
PartialDescriptor = &PartialResourceList->PartialDescriptors[0];
PartialDescriptor->Type = CmResourceTypePort;
PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
PartialDescriptor->u.Port.Start.LowPart = 0x03F0;
PartialDescriptor->u.Port.Start.HighPart = 0x0;
PartialDescriptor->u.Port.Length = 8;
/* Set Interrupt */
PartialDescriptor = &PartialResourceList->PartialDescriptors[1];
PartialDescriptor->Type = CmResourceTypeInterrupt;
PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
PartialDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
PartialDescriptor->u.Interrupt.Level = 6;
PartialDescriptor->u.Interrupt.Vector = 6;
PartialDescriptor->u.Interrupt.Affinity = 0xFFFFFFFF;
/* Set DMA channel */
PartialDescriptor = &PartialResourceList->PartialDescriptors[2];
PartialDescriptor->Type = CmResourceTypeDma;
PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
PartialDescriptor->Flags = 0;
PartialDescriptor->u.Dma.Channel = 2;
PartialDescriptor->u.Dma.Port = 0;
/* Create floppy disk controller */
FldrCreateComponentKey(BusKey,
ControllerClass,
DiskController,
Output | Input,
0x0,
0xFFFFFFFF,
NULL,
PartialResourceList,
Size,
&ControllerKey);
TRACE("Created key: DiskController\\0\n");
if (FloppyCount)
DetectBiosFloppyPeripheral(ControllerKey);
return ControllerKey;
}
VOID
DetectBiosDisks(PCONFIGURATION_COMPONENT_DATA SystemKey,
PCONFIGURATION_COMPONENT_DATA BusKey)
{
PCONFIGURATION_COMPONENT_DATA ControllerKey, DiskKey;
PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
PCM_INT13_DRIVE_PARAMETER Int13Drives;
GEOMETRY Geometry;
UCHAR DiskCount, DriveNumber;
USHORT i;
ULONG Size;
/* The pre-enumeration of the BIOS disks was already done in InitializeBootDevices() */
DiskCount = PcBiosDiskCount;
/* Use the floppy disk controller as our controller */
ControllerKey = DetectBiosFloppyController(BusKey);
if (!ControllerKey)
{
ERR("Failed to detect BIOS disk controller\n");
return;
}
/* Allocate resource descriptor */
Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
sizeof(CM_INT13_DRIVE_PARAMETER) * DiskCount;
PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
if (PartialResourceList == NULL)
{
ERR("Failed to allocate resource descriptor\n");
return;
}
/* Initialize resource descriptor */
memset(PartialResourceList, 0, Size);
PartialResourceList->Version = 1;
PartialResourceList->Revision = 1;
PartialResourceList->Count = 1;
PartialResourceList->PartialDescriptors[0].Type = CmResourceTypeDeviceSpecific;
PartialResourceList->PartialDescriptors[0].ShareDisposition = 0;
PartialResourceList->PartialDescriptors[0].Flags = 0;
PartialResourceList->PartialDescriptors[0].u.DeviceSpecificData.DataSize =
sizeof(CM_INT13_DRIVE_PARAMETER) * DiskCount;
/* Get harddisk Int13 geometry data */
Int13Drives = (PVOID)(((ULONG_PTR)PartialResourceList) + sizeof(CM_PARTIAL_RESOURCE_LIST));
for (i = 0; i < DiskCount; i++)
{
DriveNumber = 0x80 + i;
if (MachDiskGetDriveGeometry(DriveNumber, &Geometry))
{
Int13Drives[i].DriveSelect = DriveNumber;
Int13Drives[i].MaxCylinders = Geometry.Cylinders - 1;
Int13Drives[i].SectorsPerTrack = (USHORT)Geometry.Sectors;
Int13Drives[i].MaxHeads = (USHORT)Geometry.Heads - 1;
Int13Drives[i].NumberDrives = DiskCount;
TRACE("Disk %x: %u Cylinders %u Heads %u Sectors %u Bytes\n",
DriveNumber,
Geometry.Cylinders - 1,
Geometry.Heads - 1,
Geometry.Sectors,
Geometry.BytesPerSector);
}
}
/* Update the 'System' key's configuration data with BIOS INT13h information */
FldrSetConfigurationData(SystemKey, PartialResourceList, Size);
/* Create and fill subkey for each harddisk */
for (i = 0; i < DiskCount; i++)
{
PCHAR Identifier;
DriveNumber = 0x80 + i;
/* Get disk values */
PartialResourceList = GetHarddiskConfigurationData(DriveNumber, &Size);
Identifier = GetHarddiskIdentifier(DriveNumber);
/* Create disk key */
FldrCreateComponentKey(ControllerKey,
PeripheralClass,
DiskPeripheral,
Output | Input,
0x0,
0xFFFFFFFF,
Identifier,
PartialResourceList,
Size,
&DiskKey);
}
}
VOID
FrLdrCheckCpuCompatibility(VOID)
{
INT CpuInformation[4] = {-1};
ULONG NumberOfIds;
/* Check if the processor first supports ID 1 */
__cpuid(CpuInformation, 0);
NumberOfIds = CpuInformation[0];
if (NumberOfIds == 0)
{
FrLdrBugCheckWithMessage(MISSING_HARDWARE_REQUIREMENTS,
__FILE__,
__LINE__,
"ReactOS requires the CPUID instruction to return "
"more than one supported ID.\n\n");
}
/* NumberOfIds will be greater than 1 if the processor is new enough */
if (NumberOfIds == 1)
{
INT ProcessorFamily;
/* Get information */
__cpuid(CpuInformation, 1);
ProcessorFamily = (CpuInformation[0] >> 8) & 0xF;
/* If it's Family 4 or lower, bugcheck */
if (ProcessorFamily < 5)
{
FrLdrBugCheckWithMessage(MISSING_HARDWARE_REQUIREMENTS,
__FILE__,
__LINE__,
"Processor is too old (family %u < 5)\n"
"ReactOS requires a Pentium-level processor or newer.",
ProcessorFamily);
}
}
}
/* EOF */