4 * Copyright (C) 2003, 2004 Eric Kohl
5 * Copyright (C) 2009 Hervé Poussineau
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 DBG_DEFAULT_CHANNEL(HWDETECT
);
33 #define CLOCK_TICK_RATE (1193182)
34 #define LATCH (CLOCK_TICK_RATE / HZ)
36 static unsigned int delay_count
= 1;
38 /* Used for BIOS disks pre-enumeration performed when detecting the boot devices in InitializeBootDevices() */
39 extern UCHAR PcBiosDiskCount
;
41 /* This function is slightly different in its PC and XBOX versions */
42 GET_HARDDISK_CONFIG_DATA GetHarddiskConfigurationData
= NULL
;
45 GetHarddiskIdentifier(UCHAR DriveNumber
);
47 /* FUNCTIONS *****************************************************************/
51 __StallExecutionProcessor(ULONG Loops
)
53 register volatile unsigned int i
;
54 for (i
= 0; i
< Loops
; i
++);
57 VOID
StallExecutionProcessor(ULONG Microseconds
)
59 ULONGLONG LoopCount
= ((ULONGLONG
)delay_count
* (ULONGLONG
)Microseconds
) / 1000ULL;
60 __StallExecutionProcessor((ULONG
)LoopCount
);
69 WRITE_PORT_UCHAR((PUCHAR
)0x43, 0x00);
70 Count
= READ_PORT_UCHAR((PUCHAR
)0x40);
71 Count
|= READ_PORT_UCHAR((PUCHAR
)0x40) << 8;
78 WaitFor8254Wraparound(VOID
)
84 CurCount
= Read8254Timer();
89 CurCount
= Read8254Timer();
90 Delta
= CurCount
- PrevCount
;
93 * This limit for delta seems arbitrary, but it isn't, it's
94 * slightly above the level of error a buggy Mercury/Neptune
95 * chipset timer can cause.
102 HalpCalibrateStallExecution(VOID
)
108 /* Initialise timer interrupt with MILLISECOND ms interval */
109 WRITE_PORT_UCHAR((PUCHAR
)0x43, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */
110 WRITE_PORT_UCHAR((PUCHAR
)0x40, LATCH
& 0xff); /* LSB */
111 WRITE_PORT_UCHAR((PUCHAR
)0x40, LATCH
>> 8); /* MSB */
113 /* Stage 1: Coarse calibration */
115 WaitFor8254Wraparound();
121 /* Next delay count to try */
124 WaitFor8254Wraparound();
127 __StallExecutionProcessor(delay_count
);
129 CurCount
= Read8254Timer();
131 while (CurCount
> LATCH
/ 2);
133 /* Get bottom value for delay */
136 /* Stage 2: Fine calibration */
138 /* Which bit are we going to test */
139 calib_bit
= delay_count
;
141 for (i
= 0; i
< PRECISION
; i
++)
143 /* Next bit to calibrate */
146 /* If we have done all bits, stop */
147 if (!calib_bit
) break;
149 /* Set the bit in delay_count */
150 delay_count
|= calib_bit
;
152 WaitFor8254Wraparound();
155 __StallExecutionProcessor(delay_count
);
157 CurCount
= Read8254Timer();
158 /* If a tick has passed, turn the calibrated bit back off */
159 if (CurCount
<= LATCH
/ 2)
160 delay_count
&= ~calib_bit
;
163 /* We're finished: Do the finishing touches */
165 /* Calculate delay_count for 1ms */
166 delay_count
/= (MILLISEC
/ 2);
176 WRITE_PORT_UCHAR((PUCHAR
)0x70, 0x10);
177 Data
= READ_PORT_UCHAR((PUCHAR
)0x71);
179 return ((Data
& 0xF0) ? 1 : 0) + ((Data
& 0x0F) ? 1 : 0);
184 GetFloppyType(UCHAR DriveNumber
)
188 WRITE_PORT_UCHAR((PUCHAR
)0x70, 0x10);
189 Data
= READ_PORT_UCHAR((PUCHAR
)0x71);
191 if (DriveNumber
== 0)
193 else if (DriveNumber
== 1)
203 PUSHORT SegPtr
= (PUSHORT
)0x7A;
204 PUSHORT OfsPtr
= (PUSHORT
)0x78;
206 return (PVOID
)((ULONG_PTR
)(((ULONG
)(*SegPtr
)) << 4) + (ULONG
)(*OfsPtr
));
211 DetectBiosFloppyPeripheral(PCONFIGURATION_COMPONENT_DATA ControllerKey
)
213 PCM_PARTIAL_RESOURCE_LIST PartialResourceList
;
214 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor
;
215 PCM_FLOPPY_DEVICE_DATA FloppyData
;
217 PCONFIGURATION_COMPONENT_DATA PeripheralKey
;
221 ULONG MaxDensity
[6] = {0, 360, 1200, 720, 1440, 2880};
224 for (FloppyNumber
= 0; FloppyNumber
< 2; FloppyNumber
++)
226 FloppyType
= GetFloppyType(FloppyNumber
);
228 if ((FloppyType
> 5) || (FloppyType
== 0))
231 if (!DiskResetController(FloppyNumber
))
234 Ptr
= GetInt1eTable();
236 /* Set 'Identifier' value */
237 sprintf(Identifier
, "FLOPPY%d", FloppyNumber
+ 1);
239 Size
= sizeof(CM_PARTIAL_RESOURCE_LIST
) +
240 sizeof(CM_FLOPPY_DEVICE_DATA
);
241 PartialResourceList
= FrLdrHeapAlloc(Size
, TAG_HW_RESOURCE_LIST
);
242 if (PartialResourceList
== NULL
)
244 ERR("Failed to allocate resource descriptor\n");
248 memset(PartialResourceList
, 0, Size
);
249 PartialResourceList
->Version
= 1;
250 PartialResourceList
->Revision
= 1;
251 PartialResourceList
->Count
= 1;
253 PartialDescriptor
= &PartialResourceList
->PartialDescriptors
[0];
254 PartialDescriptor
->Type
= CmResourceTypeDeviceSpecific
;
255 PartialDescriptor
->ShareDisposition
= CmResourceShareUndetermined
;
256 PartialDescriptor
->u
.DeviceSpecificData
.DataSize
= sizeof(CM_FLOPPY_DEVICE_DATA
);
258 FloppyData
= (PVOID
)(((ULONG_PTR
)PartialResourceList
) + sizeof(CM_PARTIAL_RESOURCE_LIST
));
259 FloppyData
->Version
= 2;
260 FloppyData
->Revision
= 0;
261 FloppyData
->MaxDensity
= MaxDensity
[FloppyType
];
262 FloppyData
->MountDensity
= 0;
263 RtlCopyMemory(&FloppyData
->StepRateHeadUnloadTime
, Ptr
, 11);
264 FloppyData
->MaximumTrackValue
= (FloppyType
== 1) ? 39 : 79;
265 FloppyData
->DataTransferRate
= 0;
267 FldrCreateComponentKey(ControllerKey
,
269 FloppyDiskPeripheral
,
281 PCONFIGURATION_COMPONENT_DATA
282 DetectBiosFloppyController(PCONFIGURATION_COMPONENT_DATA BusKey
)
284 PCONFIGURATION_COMPONENT_DATA ControllerKey
;
285 PCM_PARTIAL_RESOURCE_LIST PartialResourceList
;
286 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor
;
290 FloppyCount
= GetFloppyCount();
291 TRACE("Floppy count: %u\n", FloppyCount
);
293 /* Always create a BIOS disk controller, no matter if we have floppy drives or not */
294 Size
= sizeof(CM_PARTIAL_RESOURCE_LIST
) +
295 2 * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR
);
296 PartialResourceList
= FrLdrHeapAlloc(Size
, TAG_HW_RESOURCE_LIST
);
297 if (PartialResourceList
== NULL
)
299 ERR("Failed to allocate resource descriptor\n");
302 memset(PartialResourceList
, 0, Size
);
304 /* Initialize resource descriptor */
305 PartialResourceList
->Version
= 1;
306 PartialResourceList
->Revision
= 1;
307 PartialResourceList
->Count
= 3;
310 PartialDescriptor
= &PartialResourceList
->PartialDescriptors
[0];
311 PartialDescriptor
->Type
= CmResourceTypePort
;
312 PartialDescriptor
->ShareDisposition
= CmResourceShareDeviceExclusive
;
313 PartialDescriptor
->Flags
= CM_RESOURCE_PORT_IO
;
314 PartialDescriptor
->u
.Port
.Start
.LowPart
= 0x03F0;
315 PartialDescriptor
->u
.Port
.Start
.HighPart
= 0x0;
316 PartialDescriptor
->u
.Port
.Length
= 8;
319 PartialDescriptor
= &PartialResourceList
->PartialDescriptors
[1];
320 PartialDescriptor
->Type
= CmResourceTypeInterrupt
;
321 PartialDescriptor
->ShareDisposition
= CmResourceShareUndetermined
;
322 PartialDescriptor
->Flags
= CM_RESOURCE_INTERRUPT_LATCHED
;
323 PartialDescriptor
->u
.Interrupt
.Level
= 6;
324 PartialDescriptor
->u
.Interrupt
.Vector
= 6;
325 PartialDescriptor
->u
.Interrupt
.Affinity
= 0xFFFFFFFF;
327 /* Set DMA channel */
328 PartialDescriptor
= &PartialResourceList
->PartialDescriptors
[2];
329 PartialDescriptor
->Type
= CmResourceTypeDma
;
330 PartialDescriptor
->ShareDisposition
= CmResourceShareUndetermined
;
331 PartialDescriptor
->Flags
= 0;
332 PartialDescriptor
->u
.Dma
.Channel
= 2;
333 PartialDescriptor
->u
.Dma
.Port
= 0;
335 /* Create floppy disk controller */
336 FldrCreateComponentKey(BusKey
,
346 TRACE("Created key: DiskController\\0\n");
349 DetectBiosFloppyPeripheral(ControllerKey
);
351 return ControllerKey
;
355 DetectBiosDisks(PCONFIGURATION_COMPONENT_DATA SystemKey
,
356 PCONFIGURATION_COMPONENT_DATA BusKey
)
358 PCONFIGURATION_COMPONENT_DATA ControllerKey
, DiskKey
;
359 PCM_PARTIAL_RESOURCE_LIST PartialResourceList
;
360 PCM_INT13_DRIVE_PARAMETER Int13Drives
;
362 UCHAR DiskCount
, DriveNumber
;
366 /* The pre-enumeration of the BIOS disks was already done in InitializeBootDevices() */
367 DiskCount
= PcBiosDiskCount
;
369 /* Use the floppy disk controller as our controller */
370 ControllerKey
= DetectBiosFloppyController(BusKey
);
373 ERR("Failed to detect BIOS disk controller\n");
377 /* Allocate resource descriptor */
378 Size
= sizeof(CM_PARTIAL_RESOURCE_LIST
) +
379 sizeof(CM_INT13_DRIVE_PARAMETER
) * DiskCount
;
380 PartialResourceList
= FrLdrHeapAlloc(Size
, TAG_HW_RESOURCE_LIST
);
381 if (PartialResourceList
== NULL
)
383 ERR("Failed to allocate resource descriptor\n");
387 /* Initialize resource descriptor */
388 memset(PartialResourceList
, 0, Size
);
389 PartialResourceList
->Version
= 1;
390 PartialResourceList
->Revision
= 1;
391 PartialResourceList
->Count
= 1;
392 PartialResourceList
->PartialDescriptors
[0].Type
= CmResourceTypeDeviceSpecific
;
393 PartialResourceList
->PartialDescriptors
[0].ShareDisposition
= 0;
394 PartialResourceList
->PartialDescriptors
[0].Flags
= 0;
395 PartialResourceList
->PartialDescriptors
[0].u
.DeviceSpecificData
.DataSize
=
396 sizeof(CM_INT13_DRIVE_PARAMETER
) * DiskCount
;
398 /* Get harddisk Int13 geometry data */
399 Int13Drives
= (PVOID
)(((ULONG_PTR
)PartialResourceList
) + sizeof(CM_PARTIAL_RESOURCE_LIST
));
400 for (i
= 0; i
< DiskCount
; i
++)
402 DriveNumber
= 0x80 + i
;
404 if (MachDiskGetDriveGeometry(DriveNumber
, &Geometry
))
406 Int13Drives
[i
].DriveSelect
= DriveNumber
;
407 Int13Drives
[i
].MaxCylinders
= Geometry
.Cylinders
- 1;
408 Int13Drives
[i
].SectorsPerTrack
= (USHORT
)Geometry
.Sectors
;
409 Int13Drives
[i
].MaxHeads
= (USHORT
)Geometry
.Heads
- 1;
410 Int13Drives
[i
].NumberDrives
= DiskCount
;
412 TRACE("Disk %x: %u Cylinders %u Heads %u Sectors %u Bytes\n",
414 Geometry
.Cylinders
- 1,
417 Geometry
.BytesPerSector
);
421 /* Update the 'System' key's configuration data with BIOS INT13h information */
422 FldrSetConfigurationData(SystemKey
, PartialResourceList
, Size
);
424 /* Create and fill subkey for each harddisk */
425 for (i
= 0; i
< DiskCount
; i
++)
429 DriveNumber
= 0x80 + i
;
431 /* Get disk values */
432 PartialResourceList
= GetHarddiskConfigurationData(DriveNumber
, &Size
);
433 Identifier
= GetHarddiskIdentifier(DriveNumber
);
435 /* Create disk key */
436 FldrCreateComponentKey(ControllerKey
,
450 FrLdrCheckCpuCompatibility(VOID
)
452 INT CpuInformation
[4] = {-1};
455 /* Check if the processor first supports ID 1 */
456 __cpuid(CpuInformation
, 0);
458 NumberOfIds
= CpuInformation
[0];
460 if (NumberOfIds
== 0)
462 FrLdrBugCheckWithMessage(MISSING_HARDWARE_REQUIREMENTS
,
465 "ReactOS requires the CPUID instruction to return "
466 "more than one supported ID.\n\n");
469 /* NumberOfIds will be greater than 1 if the processor is new enough */
470 if (NumberOfIds
== 1)
474 /* Get information */
475 __cpuid(CpuInformation
, 1);
477 ProcessorFamily
= (CpuInformation
[0] >> 8) & 0xF;
479 /* If it's Family 4 or lower, bugcheck */
480 if (ProcessorFamily
< 5)
482 FrLdrBugCheckWithMessage(MISSING_HARDWARE_REQUIREMENTS
,
485 "Processor is too old (family %u < 5)\n"
486 "ReactOS requires a Pentium-level processor or newer.",