[SHELL32][BOOTDATA] Implement Command Prompt here (#2029)
[reactos.git] / boot / freeldr / freeldr / arch / i386 / hardware.c
1 /*
2 * FreeLoader
3 *
4 * Copyright (C) 2003, 2004 Eric Kohl
5 * Copyright (C) 2009 Hervé Poussineau
6 *
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.
11 *
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.
16 *
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.
20 */
21
22 #include <freeldr.h>
23
24 #include <debug.h>
25 DBG_DEFAULT_CHANNEL(HWDETECT);
26
27 #define MILLISEC (10)
28 #define PRECISION (8)
29
30 #define HZ (100)
31 #define CLOCK_TICK_RATE (1193182)
32 #define LATCH (CLOCK_TICK_RATE / HZ)
33
34 static unsigned int delay_count = 1;
35
36 /* Used for BIOS disks pre-enumeration performed when detecting the boot devices in InitializeBootDevices() */
37 extern UCHAR PcBiosDiskCount;
38
39 /* This function is slightly different in its PC and XBOX versions */
40 GET_HARDDISK_CONFIG_DATA GetHarddiskConfigurationData = NULL;
41
42 PCHAR
43 GetHarddiskIdentifier(UCHAR DriveNumber);
44
45 /* FUNCTIONS *****************************************************************/
46
47 static
48 VOID
49 __StallExecutionProcessor(ULONG Loops)
50 {
51 register volatile unsigned int i;
52 for (i = 0; i < Loops; i++);
53 }
54
55 VOID StallExecutionProcessor(ULONG Microseconds)
56 {
57 ULONGLONG LoopCount = ((ULONGLONG)delay_count * (ULONGLONG)Microseconds) / 1000ULL;
58 __StallExecutionProcessor((ULONG)LoopCount);
59 }
60
61 static
62 ULONG
63 Read8254Timer(VOID)
64 {
65 ULONG Count;
66
67 WRITE_PORT_UCHAR((PUCHAR)0x43, 0x00);
68 Count = READ_PORT_UCHAR((PUCHAR)0x40);
69 Count |= READ_PORT_UCHAR((PUCHAR)0x40) << 8;
70
71 return Count;
72 }
73
74 static
75 VOID
76 WaitFor8254Wraparound(VOID)
77 {
78 ULONG CurCount;
79 ULONG PrevCount = ~0;
80 LONG Delta;
81
82 CurCount = Read8254Timer();
83
84 do
85 {
86 PrevCount = CurCount;
87 CurCount = Read8254Timer();
88 Delta = CurCount - PrevCount;
89
90 /*
91 * This limit for delta seems arbitrary, but it isn't, it's
92 * slightly above the level of error a buggy Mercury/Neptune
93 * chipset timer can cause.
94 */
95 }
96 while (Delta < 300);
97 }
98
99 VOID
100 HalpCalibrateStallExecution(VOID)
101 {
102 ULONG i;
103 ULONG calib_bit;
104 ULONG CurCount;
105
106 /* Initialise timer interrupt with MILLISECOND ms interval */
107 WRITE_PORT_UCHAR((PUCHAR)0x43, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */
108 WRITE_PORT_UCHAR((PUCHAR)0x40, LATCH & 0xff); /* LSB */
109 WRITE_PORT_UCHAR((PUCHAR)0x40, LATCH >> 8); /* MSB */
110
111 /* Stage 1: Coarse calibration */
112
113 delay_count = 1;
114
115 do
116 {
117 /* Next delay count to try */
118 delay_count <<= 1;
119
120 WaitFor8254Wraparound();
121
122 /* Do the delay */
123 __StallExecutionProcessor(delay_count);
124
125 CurCount = Read8254Timer();
126 }
127 while (CurCount > LATCH / 2);
128
129 /* Get bottom value for delay */
130 delay_count >>= 1;
131
132 /* Stage 2: Fine calibration */
133
134 /* Which bit are we going to test */
135 calib_bit = delay_count;
136
137 for (i = 0; i < PRECISION; i++)
138 {
139 /* Next bit to calibrate */
140 calib_bit >>= 1;
141
142 /* If we have done all bits, stop */
143 if (!calib_bit) break;
144
145 /* Set the bit in delay_count */
146 delay_count |= calib_bit;
147
148 WaitFor8254Wraparound();
149
150 /* Do the delay */
151 __StallExecutionProcessor(delay_count);
152
153 CurCount = Read8254Timer();
154 /* If a tick has passed, turn the calibrated bit back off */
155 if (CurCount <= LATCH / 2)
156 delay_count &= ~calib_bit;
157 }
158
159 /* We're finished: Do the finishing touches */
160
161 /* Calculate delay_count for 1ms */
162 delay_count /= (MILLISEC / 2);
163 }
164
165
166 static
167 UCHAR
168 GetFloppyType(UCHAR DriveNumber)
169 {
170 UCHAR Data;
171
172 WRITE_PORT_UCHAR((PUCHAR)0x70, 0x10);
173 Data = READ_PORT_UCHAR((PUCHAR)0x71);
174
175 if (DriveNumber == 0)
176 return Data >> 4;
177 else if (DriveNumber == 1)
178 return Data & 0x0F;
179
180 return 0;
181 }
182
183 static
184 PVOID
185 GetInt1eTable(VOID)
186 {
187 PUSHORT SegPtr = (PUSHORT)0x7A;
188 PUSHORT OfsPtr = (PUSHORT)0x78;
189
190 return (PVOID)((ULONG_PTR)(((ULONG)(*SegPtr)) << 4) + (ULONG)(*OfsPtr));
191 }
192
193 static
194 VOID
195 DetectBiosFloppyPeripheral(PCONFIGURATION_COMPONENT_DATA ControllerKey)
196 {
197 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
198 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
199 PCM_FLOPPY_DEVICE_DATA FloppyData;
200 CHAR Identifier[20];
201 PCONFIGURATION_COMPONENT_DATA PeripheralKey;
202 ULONG Size;
203 UCHAR FloppyNumber;
204 UCHAR FloppyType;
205 ULONG MaxDensity[6] = {0, 360, 1200, 720, 1440, 2880};
206 PUCHAR Ptr;
207
208 for (FloppyNumber = 0; FloppyNumber < 2; FloppyNumber++)
209 {
210 FloppyType = GetFloppyType(FloppyNumber);
211
212 if ((FloppyType > 5) || (FloppyType == 0))
213 continue;
214
215 if (!DiskResetController(FloppyNumber))
216 continue;
217
218 Ptr = GetInt1eTable();
219
220 /* Set 'Identifier' value */
221 sprintf(Identifier, "FLOPPY%d", FloppyNumber + 1);
222
223 Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
224 sizeof(CM_FLOPPY_DEVICE_DATA);
225 PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
226 if (PartialResourceList == NULL)
227 {
228 ERR("Failed to allocate resource descriptor\n");
229 return;
230 }
231
232 memset(PartialResourceList, 0, Size);
233 PartialResourceList->Version = 1;
234 PartialResourceList->Revision = 1;
235 PartialResourceList->Count = 1;
236
237 PartialDescriptor = &PartialResourceList->PartialDescriptors[0];
238 PartialDescriptor->Type = CmResourceTypeDeviceSpecific;
239 PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
240 PartialDescriptor->u.DeviceSpecificData.DataSize = sizeof(CM_FLOPPY_DEVICE_DATA);
241
242 FloppyData = (PVOID)(((ULONG_PTR)PartialResourceList) + sizeof(CM_PARTIAL_RESOURCE_LIST));
243 FloppyData->Version = 2;
244 FloppyData->Revision = 0;
245 FloppyData->MaxDensity = MaxDensity[FloppyType];
246 FloppyData->MountDensity = 0;
247 RtlCopyMemory(&FloppyData->StepRateHeadUnloadTime, Ptr, 11);
248 FloppyData->MaximumTrackValue = (FloppyType == 1) ? 39 : 79;
249 FloppyData->DataTransferRate = 0;
250
251 FldrCreateComponentKey(ControllerKey,
252 PeripheralClass,
253 FloppyDiskPeripheral,
254 Input | Output,
255 FloppyNumber,
256 0xFFFFFFFF,
257 Identifier,
258 PartialResourceList,
259 Size,
260 &PeripheralKey);
261 }
262 }
263
264 static
265 PCONFIGURATION_COMPONENT_DATA
266 DetectBiosFloppyController(PCONFIGURATION_COMPONENT_DATA BusKey)
267 {
268 PCONFIGURATION_COMPONENT_DATA ControllerKey;
269 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
270 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
271 ULONG Size;
272 ULONG FloppyCount;
273
274 FloppyCount = MachGetFloppyCount();
275 TRACE("Floppy count: %u\n", FloppyCount);
276
277 /* Always create a BIOS disk controller, no matter if we have floppy drives or not */
278 Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
279 2 * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
280 PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
281 if (PartialResourceList == NULL)
282 {
283 ERR("Failed to allocate resource descriptor\n");
284 return NULL;
285 }
286 memset(PartialResourceList, 0, Size);
287
288 /* Initialize resource descriptor */
289 PartialResourceList->Version = 1;
290 PartialResourceList->Revision = 1;
291 PartialResourceList->Count = 3;
292
293 /* Set IO Port */
294 PartialDescriptor = &PartialResourceList->PartialDescriptors[0];
295 PartialDescriptor->Type = CmResourceTypePort;
296 PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
297 PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
298 PartialDescriptor->u.Port.Start.LowPart = 0x03F0;
299 PartialDescriptor->u.Port.Start.HighPart = 0x0;
300 PartialDescriptor->u.Port.Length = 8;
301
302 /* Set Interrupt */
303 PartialDescriptor = &PartialResourceList->PartialDescriptors[1];
304 PartialDescriptor->Type = CmResourceTypeInterrupt;
305 PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
306 PartialDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
307 PartialDescriptor->u.Interrupt.Level = 6;
308 PartialDescriptor->u.Interrupt.Vector = 6;
309 PartialDescriptor->u.Interrupt.Affinity = 0xFFFFFFFF;
310
311 /* Set DMA channel */
312 PartialDescriptor = &PartialResourceList->PartialDescriptors[2];
313 PartialDescriptor->Type = CmResourceTypeDma;
314 PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
315 PartialDescriptor->Flags = 0;
316 PartialDescriptor->u.Dma.Channel = 2;
317 PartialDescriptor->u.Dma.Port = 0;
318
319 /* Create floppy disk controller */
320 FldrCreateComponentKey(BusKey,
321 ControllerClass,
322 DiskController,
323 Output | Input,
324 0x0,
325 0xFFFFFFFF,
326 NULL,
327 PartialResourceList,
328 Size,
329 &ControllerKey);
330 TRACE("Created key: DiskController\\0\n");
331
332 if (FloppyCount)
333 DetectBiosFloppyPeripheral(ControllerKey);
334
335 return ControllerKey;
336 }
337
338 VOID
339 DetectBiosDisks(PCONFIGURATION_COMPONENT_DATA SystemKey,
340 PCONFIGURATION_COMPONENT_DATA BusKey)
341 {
342 PCONFIGURATION_COMPONENT_DATA ControllerKey, DiskKey;
343 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
344 PCM_INT13_DRIVE_PARAMETER Int13Drives;
345 GEOMETRY Geometry;
346 UCHAR DiskCount, DriveNumber;
347 USHORT i;
348 ULONG Size;
349
350 /* The pre-enumeration of the BIOS disks was already done in InitializeBootDevices() */
351 DiskCount = PcBiosDiskCount;
352
353 /* Use the floppy disk controller as our controller */
354 ControllerKey = DetectBiosFloppyController(BusKey);
355 if (!ControllerKey)
356 {
357 ERR("Failed to detect BIOS disk controller\n");
358 return;
359 }
360
361 /* Allocate resource descriptor */
362 Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
363 sizeof(CM_INT13_DRIVE_PARAMETER) * DiskCount;
364 PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
365 if (PartialResourceList == NULL)
366 {
367 ERR("Failed to allocate resource descriptor\n");
368 return;
369 }
370
371 /* Initialize resource descriptor */
372 memset(PartialResourceList, 0, Size);
373 PartialResourceList->Version = 1;
374 PartialResourceList->Revision = 1;
375 PartialResourceList->Count = 1;
376 PartialResourceList->PartialDescriptors[0].Type = CmResourceTypeDeviceSpecific;
377 PartialResourceList->PartialDescriptors[0].ShareDisposition = 0;
378 PartialResourceList->PartialDescriptors[0].Flags = 0;
379 PartialResourceList->PartialDescriptors[0].u.DeviceSpecificData.DataSize =
380 sizeof(CM_INT13_DRIVE_PARAMETER) * DiskCount;
381
382 /* Get harddisk Int13 geometry data */
383 Int13Drives = (PVOID)(((ULONG_PTR)PartialResourceList) + sizeof(CM_PARTIAL_RESOURCE_LIST));
384 for (i = 0; i < DiskCount; i++)
385 {
386 DriveNumber = 0x80 + i;
387
388 if (MachDiskGetDriveGeometry(DriveNumber, &Geometry))
389 {
390 Int13Drives[i].DriveSelect = DriveNumber;
391 Int13Drives[i].MaxCylinders = Geometry.Cylinders - 1;
392 Int13Drives[i].SectorsPerTrack = (USHORT)Geometry.Sectors;
393 Int13Drives[i].MaxHeads = (USHORT)Geometry.Heads - 1;
394 Int13Drives[i].NumberDrives = DiskCount;
395
396 TRACE("Disk %x: %u Cylinders %u Heads %u Sectors %u Bytes\n",
397 DriveNumber,
398 Geometry.Cylinders - 1,
399 Geometry.Heads - 1,
400 Geometry.Sectors,
401 Geometry.BytesPerSector);
402 }
403 }
404
405 /* Update the 'System' key's configuration data with BIOS INT13h information */
406 FldrSetConfigurationData(SystemKey, PartialResourceList, Size);
407
408 /* Create and fill subkey for each harddisk */
409 for (i = 0; i < DiskCount; i++)
410 {
411 PCHAR Identifier;
412
413 DriveNumber = 0x80 + i;
414
415 /* Get disk values */
416 PartialResourceList = GetHarddiskConfigurationData(DriveNumber, &Size);
417 Identifier = GetHarddiskIdentifier(DriveNumber);
418
419 /* Create disk key */
420 FldrCreateComponentKey(ControllerKey,
421 PeripheralClass,
422 DiskPeripheral,
423 Output | Input,
424 0x0,
425 0xFFFFFFFF,
426 Identifier,
427 PartialResourceList,
428 Size,
429 &DiskKey);
430 }
431 }
432
433 VOID
434 FrLdrCheckCpuCompatibility(VOID)
435 {
436 INT CpuInformation[4] = {-1};
437 ULONG NumberOfIds;
438
439 /* Check if the processor first supports ID 1 */
440 __cpuid(CpuInformation, 0);
441
442 NumberOfIds = CpuInformation[0];
443
444 if (NumberOfIds == 0)
445 {
446 FrLdrBugCheckWithMessage(MISSING_HARDWARE_REQUIREMENTS,
447 __FILE__,
448 __LINE__,
449 "ReactOS requires the CPUID instruction to return "
450 "more than one supported ID.\n\n");
451 }
452
453 /* NumberOfIds will be greater than 1 if the processor is new enough */
454 if (NumberOfIds == 1)
455 {
456 INT ProcessorFamily;
457
458 /* Get information */
459 __cpuid(CpuInformation, 1);
460
461 ProcessorFamily = (CpuInformation[0] >> 8) & 0xF;
462
463 /* If it's Family 4 or lower, bugcheck */
464 if (ProcessorFamily < 5)
465 {
466 FrLdrBugCheckWithMessage(MISSING_HARDWARE_REQUIREMENTS,
467 __FILE__,
468 __LINE__,
469 "Processor is too old (family %u < 5)\n"
470 "ReactOS requires a Pentium-level processor or newer.",
471 ProcessorFamily);
472 }
473 }
474 }
475
476 /* EOF */