4 * Copyright (C) 2003 Eric Kohl
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "../../reactos/registry.h"
37 #define CLOCK_TICK_RATE (1193182)
38 #define LATCH (CLOCK_TICK_RATE / HZ)
43 typedef struct _CM_INT13_DRIVE_PARAMETER
50 } CM_INT13_DRIVE_PARAMETER
, *PCM_INT13_DRIVE_PARAMETER
;
53 typedef struct _CM_DISK_GEOMETRY_DEVICE_DATA
56 U32 NumberOfCylinders
;
59 } CM_DISK_GEOMETRY_DEVICE_DATA
, *PCM_DISK_GEOMETRY_DEVICE_DATA
;
64 typedef struct _CM_PNP_BIOS_DEVICE_NODE
71 } __attribute__((packed
)) CM_PNP_BIOS_DEVICE_NODE
, *PCM_PNP_BIOS_DEVICE_NODE
;
74 typedef struct _CM_PNP_BIOS_INSTALLATION_CHECK
82 U16 RealModeEntryOffset
;
83 U16 RealModeEntrySegment
;
84 U16 ProtectedModeEntryOffset
;
85 U32 ProtectedModeCodeBaseAddress
;
87 U16 RealModeDataBaseAddress
;
88 U32 ProtectedModeDataBaseAddress
;
89 } __attribute__((packed
)) CM_PNP_BIOS_INSTALLATION_CHECK
, *PCM_PNP_BIOS_INSTALLATION_CHECK
;
93 static char Hex
[] = "0123456789ABCDEF";
94 static unsigned int delay_count
= 1;
97 /* FUNCTIONS ****************************************************************/
101 __KeStallExecutionProcessor(U32 Loops
)
103 register unsigned int i
;
104 for (i
= 0; i
< Loops
; i
++);
107 VOID
KeStallExecutionProcessor(U32 Microseconds
)
109 __KeStallExecutionProcessor((delay_count
* Microseconds
) / 1000);
118 WRITE_PORT_UCHAR((PU8
)0x43, 0x00);
119 Count
= READ_PORT_UCHAR((PU8
)0x40);
120 Count
|= READ_PORT_UCHAR((PU8
)0x40) << 8;
127 WaitFor8254Wraparound(VOID
)
133 CurCount
= Read8254Timer();
137 PrevCount
= CurCount
;
138 CurCount
= Read8254Timer();
139 Delta
= CurCount
- PrevCount
;
142 * This limit for delta seems arbitrary, but it isn't, it's
143 * slightly above the level of error a buggy Mercury/Neptune
144 * chipset timer can cause.
152 HalpCalibrateStallExecution(VOID
)
158 /* Initialise timer interrupt with MILLISECOND ms interval */
159 WRITE_PORT_UCHAR((PU8
)0x43, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */
160 WRITE_PORT_UCHAR((PU8
)0x40, LATCH
& 0xff); /* LSB */
161 WRITE_PORT_UCHAR((PU8
)0x40, LATCH
>> 8); /* MSB */
163 /* Stage 1: Coarse calibration */
165 WaitFor8254Wraparound();
170 delay_count
<<= 1; /* Next delay count to try */
172 WaitFor8254Wraparound();
174 __KeStallExecutionProcessor(delay_count
); /* Do the delay */
176 CurCount
= Read8254Timer();
177 } while (CurCount
> LATCH
/ 2);
179 delay_count
>>= 1; /* Get bottom value for delay */
181 /* Stage 2: Fine calibration */
183 calib_bit
= delay_count
; /* Which bit are we going to test */
185 for(i
=0;i
<PRECISION
;i
++) {
186 calib_bit
>>= 1; /* Next bit to calibrate */
187 if(!calib_bit
) break; /* If we have done all bits, stop */
189 delay_count
|= calib_bit
; /* Set the bit in delay_count */
191 WaitFor8254Wraparound();
193 __KeStallExecutionProcessor(delay_count
); /* Do the delay */
195 CurCount
= Read8254Timer();
196 if (CurCount
<= LATCH
/ 2) /* If a tick has passed, turn the */
197 delay_count
&= ~calib_bit
; /* calibrated bit back off */
200 /* We're finished: Do the finishing touches */
202 delay_count
/= (MILLISEC
/ 2); /* Calculate delay_count for 1ms */
207 SetComponentInformation(HKEY ComponentKey
,
212 CM_COMPONENT_INFORMATION CompInfo
;
215 CompInfo
.Flags
= Flags
;
216 CompInfo
.Version
= 0;
218 CompInfo
.Affinity
= Affinity
;
220 /* Set 'Component Information' value */
221 Error
= RegSetValue(ComponentKey
,
222 "Component Information",
225 sizeof(CM_COMPONENT_INFORMATION
));
226 if (Error
!= ERROR_SUCCESS
)
228 DbgPrint((DPRINT_HWDETECT
, "RegSetValue() failed (Error %u)\n", (int)Error
));
234 DetectPnpBios(HKEY SystemKey
, U32
*BusNumber
)
236 PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor
;
237 PCM_PNP_BIOS_DEVICE_NODE DeviceNode
;
238 PCM_PNP_BIOS_INSTALLATION_CHECK InstData
;
252 InstData
= (PCM_PNP_BIOS_INSTALLATION_CHECK
)PnpBiosSupported();
253 if (InstData
== NULL
|| strncmp(InstData
->Signature
, "$PnP", 4))
255 DbgPrint((DPRINT_HWDETECT
, "PnP-BIOS not supported\n"));
258 DbgPrint((DPRINT_HWDETECT
, "Signature '%c%c%c%c'\n",
259 InstData
->Signature
[0], InstData
->Signature
[1],
260 InstData
->Signature
[2], InstData
->Signature
[3]));
263 x
= PnpBiosGetDeviceNodeCount(&NodeSize
, &NodeCount
);
264 if (x
!= 0 || NodeSize
== 0 || NodeCount
== 0)
266 DbgPrint((DPRINT_HWDETECT
, "PnP-BIOS failed to enumerate device nodes\n"));
269 DbgPrint((DPRINT_HWDETECT
, "PnP-BIOS supported\n"));
270 DbgPrint((DPRINT_HWDETECT
, "MaxNodeSize %u NodeCount %u\n", NodeSize
, NodeCount
));
271 DbgPrint((DPRINT_HWDETECT
, "Estimated buffer size %u\n", NodeSize
* NodeCount
));
273 /* Create new bus key */
275 "MultifunctionAdapter\\%u", *BusNumber
);
276 Error
= RegCreateKey(SystemKey
,
279 if (Error
!= ERROR_SUCCESS
)
281 DbgPrint((DPRINT_HWDETECT
, "RegCreateKey() failed (Error %u)\n", (int)Error
));
285 /* Increment bus number */
288 /* Set 'Identifier' value */
289 Error
= RegSetValue(BusKey
,
294 if (Error
!= ERROR_SUCCESS
)
296 DbgPrint((DPRINT_HWDETECT
, "RegSetValue() failed (Error %u)\n", (int)Error
));
300 /* Set 'Configuration Data' value */
301 Size
= sizeof(CM_FULL_RESOURCE_DESCRIPTOR
) + (NodeSize
* NodeCount
);
302 FullResourceDescriptor
= MmAllocateMemory(Size
);
303 if (FullResourceDescriptor
== NULL
)
305 DbgPrint((DPRINT_HWDETECT
,
306 "Failed to allocate resource descriptor\n"));
309 memset(FullResourceDescriptor
, 0, Size
);
311 /* Initialize resource descriptor */
312 FullResourceDescriptor
->InterfaceType
= Internal
;
313 FullResourceDescriptor
->BusNumber
= 0;
314 FullResourceDescriptor
->PartialResourceList
.Count
= 1;
315 FullResourceDescriptor
->PartialResourceList
.PartialDescriptors
[0].Type
=
316 CmResourceTypeDeviceSpecific
;
317 FullResourceDescriptor
->PartialResourceList
.PartialDescriptors
[0].ShareDisposition
=
318 CmResourceShareUndetermined
;
319 // FullResourceDescriptor->PartialResourceList.PartialDescriptors[0].Flags =
321 Ptr
= (char *)(((PVOID
)&FullResourceDescriptor
->PartialResourceList
.PartialDescriptors
[0]) +
322 sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR
));
324 /* Set instalation check data */
325 memcpy (Ptr
, InstData
, sizeof(CM_PNP_BIOS_INSTALLATION_CHECK
));
326 Ptr
+= sizeof(CM_PNP_BIOS_INSTALLATION_CHECK
);
328 /* Copy device nodes */
330 PnpBufferSize
= sizeof(CM_PNP_BIOS_INSTALLATION_CHECK
);
331 for (i
= 0; i
< 0xFF; i
++)
335 x
= PnpBiosGetDeviceNode(&NodeNumber
, (PVOID
)DISKREADBUFFER
);
338 DeviceNode
= (PCM_PNP_BIOS_DEVICE_NODE
)DISKREADBUFFER
;
340 DbgPrint((DPRINT_HWDETECT
,
341 "Node: %u Size %u (0x%x)\n",
345 // printf("Node: %u Size %u (0x%x)\n",
348 // DeviceNode->Size);
354 Ptr
+= DeviceNode
->Size
;
355 PnpBufferSize
+= DeviceNode
->Size
;
358 if (FoundNodeCount
>= NodeCount
)
363 /* Set real data size */
364 FullResourceDescriptor
->PartialResourceList
.PartialDescriptors
[0].u
.DeviceSpecificData
.DataSize
=
366 Size
= sizeof(CM_FULL_RESOURCE_DESCRIPTOR
) + PnpBufferSize
;
368 DbgPrint((DPRINT_HWDETECT
, "Real buffer size: %u\n", PnpBufferSize
));
369 DbgPrint((DPRINT_HWDETECT
, "Resource size: %u\n", Size
));
371 /* Set 'Configuration Data' value */
372 Error
= RegSetValue(BusKey
,
373 "Configuration Data",
374 REG_FULL_RESOURCE_DESCRIPTOR
,
375 (PU8
) FullResourceDescriptor
,
377 MmFreeMemory(FullResourceDescriptor
);
378 if (Error
!= ERROR_SUCCESS
)
380 DbgPrint((DPRINT_HWDETECT
,
381 "RegSetValue(Configuration Data) failed (Error %u)\n",
389 SetHarddiskConfigurationData(HKEY DiskKey
,
392 PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor
;
393 PCM_DISK_GEOMETRY_DEVICE_DATA DiskGeometry
;
394 EXTENDED_GEOMETRY ExtGeometry
;
399 /* Set 'Configuration Data' value */
400 Size
= sizeof(CM_FULL_RESOURCE_DESCRIPTOR
) +
401 sizeof(CM_DISK_GEOMETRY_DEVICE_DATA
);
402 FullResourceDescriptor
= MmAllocateMemory(Size
);
403 if (FullResourceDescriptor
== NULL
)
405 DbgPrint((DPRINT_HWDETECT
,
406 "Failed to allocate a full resource descriptor\n"));
410 memset(FullResourceDescriptor
, 0, Size
);
411 FullResourceDescriptor
->InterfaceType
= InterfaceTypeUndefined
;
412 FullResourceDescriptor
->BusNumber
= 0;
413 FullResourceDescriptor
->PartialResourceList
.Count
= 1;
414 // FullResourceDescriptor->PartialResourceList.PartialDescriptors[0].Type =
415 // FullResourceDescriptor->PartialResourceList.PartialDescriptors[0].ShareDisposition =
416 // FullResourceDescriptor->PartialResourceList.PartialDescriptors[0].Flags =
417 FullResourceDescriptor
->PartialResourceList
.PartialDescriptors
[0].u
.DeviceSpecificData
.DataSize
=
418 sizeof(CM_DISK_GEOMETRY_DEVICE_DATA
);
420 /* Get pointer to geometry data */
421 DiskGeometry
= ((PVOID
)FullResourceDescriptor
) + sizeof(CM_FULL_RESOURCE_DESCRIPTOR
);
423 /* Get the disk geometry */
424 ExtGeometry
.Size
= sizeof(EXTENDED_GEOMETRY
);
425 if (DiskGetExtendedDriveParameters(DriveNumber
, &ExtGeometry
, ExtGeometry
.Size
))
427 DiskGeometry
->BytesPerSector
= ExtGeometry
.BytesPerSector
;
428 DiskGeometry
->NumberOfCylinders
= ExtGeometry
.Cylinders
;
429 DiskGeometry
->SectorsPerTrack
= ExtGeometry
.SectorsPerTrack
;
430 DiskGeometry
->NumberOfHeads
= ExtGeometry
.Heads
;
432 else if(DiskGetDriveParameters(DriveNumber
, &Geometry
))
434 DiskGeometry
->BytesPerSector
= Geometry
.BytesPerSector
;
435 DiskGeometry
->NumberOfCylinders
= Geometry
.Cylinders
;
436 DiskGeometry
->SectorsPerTrack
= Geometry
.Sectors
;
437 DiskGeometry
->NumberOfHeads
= Geometry
.Heads
;
441 DbgPrint((DPRINT_HWDETECT
, "Reading disk geometry failed\n"));
442 MmFreeMemory(FullResourceDescriptor
);
445 DbgPrint((DPRINT_HWDETECT
,
446 "Disk %x: %u Cylinders %u Heads %u Sectors %u Bytes\n",
448 DiskGeometry
->NumberOfCylinders
,
449 DiskGeometry
->NumberOfHeads
,
450 DiskGeometry
->SectorsPerTrack
,
451 DiskGeometry
->BytesPerSector
));
453 Error
= RegSetValue(DiskKey
,
454 "Configuration Data",
455 REG_FULL_RESOURCE_DESCRIPTOR
,
456 (PU8
) FullResourceDescriptor
,
458 MmFreeMemory(FullResourceDescriptor
);
459 if (Error
!= ERROR_SUCCESS
)
461 DbgPrint((DPRINT_HWDETECT
,
462 "RegSetValue(Configuration Data) failed (Error %u)\n",
469 SetHarddiskIdentifier(HKEY DiskKey
,
472 PMASTER_BOOT_RECORD Mbr
;
481 if (!DiskReadLogicalSectors(DriveNumber
, 0ULL, 1, (PVOID
)DISKREADBUFFER
))
483 DbgPrint((DPRINT_HWDETECT
, "Reading MBR failed\n"));
487 Buffer
= (U32
*)DISKREADBUFFER
;
488 Mbr
= (PMASTER_BOOT_RECORD
)DISKREADBUFFER
;
490 Signature
= Mbr
->Signature
;
491 DbgPrint((DPRINT_HWDETECT
, "Signature: %x\n", Signature
));
493 /* Calculate the MBR checksum */
495 for (i
= 0; i
< 128; i
++)
497 Checksum
+= Buffer
[i
];
499 Checksum
= ~Checksum
+ 1;
500 DbgPrint((DPRINT_HWDETECT
, "Checksum: %x\n", Checksum
));
502 /* Convert checksum and signature to identifier string */
503 Identifier
[0] = Hex
[(Checksum
>> 28) & 0x0F];
504 Identifier
[1] = Hex
[(Checksum
>> 24) & 0x0F];
505 Identifier
[2] = Hex
[(Checksum
>> 20) & 0x0F];
506 Identifier
[3] = Hex
[(Checksum
>> 16) & 0x0F];
507 Identifier
[4] = Hex
[(Checksum
>> 12) & 0x0F];
508 Identifier
[5] = Hex
[(Checksum
>> 8) & 0x0F];
509 Identifier
[6] = Hex
[(Checksum
>> 4) & 0x0F];
510 Identifier
[7] = Hex
[Checksum
& 0x0F];
512 Identifier
[9] = Hex
[(Signature
>> 28) & 0x0F];
513 Identifier
[10] = Hex
[(Signature
>> 24) & 0x0F];
514 Identifier
[11] = Hex
[(Signature
>> 20) & 0x0F];
515 Identifier
[12] = Hex
[(Signature
>> 16) & 0x0F];
516 Identifier
[13] = Hex
[(Signature
>> 12) & 0x0F];
517 Identifier
[14] = Hex
[(Signature
>> 8) & 0x0F];
518 Identifier
[15] = Hex
[(Signature
>> 4) & 0x0F];
519 Identifier
[16] = Hex
[Signature
& 0x0F];
520 Identifier
[17] = '-';
521 Identifier
[18] = 'A';
523 DbgPrint((DPRINT_HWDETECT
, "Identifier: %xsn", Identifier
));
526 Error
= RegSetValue(DiskKey
,
531 if (Error
!= ERROR_SUCCESS
)
533 DbgPrint((DPRINT_HWDETECT
,
534 "RegSetValue(Identifier) failed (Error %u)\n",
541 DetectBiosDisks(HKEY SystemKey
,
544 PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor
;
545 PCM_INT13_DRIVE_PARAMETER Int13Drives
;
554 /* Count the number of visible drives */
555 DiskReportError(FALSE
);
557 while (DiskReadLogicalSectors(0x80 + DiskCount
, 0ULL, 1, (PVOID
)DISKREADBUFFER
))
561 DiskReportError(TRUE
);
562 DbgPrint((DPRINT_HWDETECT
, "BIOS reports %d harddisk%s\n",
563 (int)DiskCount
, (DiskCount
== 1) ? "": "s"));
565 /* Allocate resource descriptor */
566 Size
= sizeof(CM_FULL_RESOURCE_DESCRIPTOR
) +
567 sizeof(CM_INT13_DRIVE_PARAMETER
) * DiskCount
;
568 FullResourceDescriptor
= MmAllocateMemory(Size
);
569 if (FullResourceDescriptor
== NULL
)
571 DbgPrint((DPRINT_HWDETECT
,
572 "Failed to allocate resource descriptor\n"));
576 /* Initialize resource descriptor */
577 memset(FullResourceDescriptor
, 0, Size
);
578 FullResourceDescriptor
->InterfaceType
= InterfaceTypeUndefined
;
579 FullResourceDescriptor
->BusNumber
= -1;
580 FullResourceDescriptor
->PartialResourceList
.Count
= 1;
581 // FullResourceDescriptor->PartialResourceList.PartialDescriptors[0].Type =
582 // FullResourceDescriptor->PartialResourceList.PartialDescriptors[0].ShareDisposition =
583 // FullResourceDescriptor->PartialResourceList.PartialDescriptors[0].Flags =
584 FullResourceDescriptor
->PartialResourceList
.PartialDescriptors
[0].u
.DeviceSpecificData
.DataSize
=
585 sizeof(CM_INT13_DRIVE_PARAMETER
) * DiskCount
;
587 /* Get harddisk Int13 geometry data */
588 Int13Drives
= ((PVOID
)FullResourceDescriptor
) + sizeof(CM_FULL_RESOURCE_DESCRIPTOR
);
589 for (i
= 0; i
< DiskCount
; i
++)
591 if (DiskGetDriveParameters(0x80 + i
, &Geometry
))
593 Int13Drives
[i
].DriveSelect
= 0x80 + i
;
594 Int13Drives
[i
].MaxCylinders
= Geometry
.Cylinders
- 1;
595 Int13Drives
[i
].SectorsPerTrack
= Geometry
.Sectors
;
596 Int13Drives
[i
].MaxHeads
= Geometry
.Heads
- 1;
597 Int13Drives
[i
].NumberDrives
= DiskCount
;
599 DbgPrint((DPRINT_HWDETECT
,
600 "Disk %x: %u Cylinders %u Heads %u Sectors %u Bytes\n",
602 Geometry
.Cylinders
- 1,
605 Geometry
.BytesPerSector
));
609 /* Set 'Configuration Data' value */
610 Error
= RegSetValue(SystemKey
,
611 "Configuration Data",
612 REG_FULL_RESOURCE_DESCRIPTOR
,
613 (PU8
) FullResourceDescriptor
,
615 MmFreeMemory(FullResourceDescriptor
);
616 if (Error
!= ERROR_SUCCESS
)
618 DbgPrint((DPRINT_HWDETECT
,
619 "RegSetValue(Configuration Data) failed (Error %u)\n",
624 /* Create and fill subkey for each harddisk */
625 for (i
= 0; i
< DiskCount
; i
++)
627 /* Create disk key */
629 "DiskController\\0\\DiskPeripheral\\%u",
632 Error
= RegCreateKey(BusKey
,
635 if (Error
!= ERROR_SUCCESS
)
637 DbgPrint((DPRINT_HWDETECT
, "Failed to create drive key\n"));
640 DbgPrint((DPRINT_HWDETECT
, "Created key: %s\n", Buffer
));
642 /* Set disk values */
643 SetHarddiskConfigurationData(DiskKey
, 0x80 + i
);
644 SetHarddiskIdentifier(DiskKey
, 0x80 + i
);
650 DetectIsaBios(HKEY SystemKey
, U32
*BusNumber
)
652 PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor
;
658 /* Create new bus key */
660 "MultifunctionAdapter\\%u", *BusNumber
);
661 Error
= RegCreateKey(SystemKey
,
664 if (Error
!= ERROR_SUCCESS
)
666 DbgPrint((DPRINT_HWDETECT
, "RegCreateKey() failed (Error %u)\n", (int)Error
));
670 /* Increment bus number */
673 /* Set 'Identifier' value */
674 Error
= RegSetValue(BusKey
,
679 if (Error
!= ERROR_SUCCESS
)
681 DbgPrint((DPRINT_HWDETECT
, "RegSetValue() failed (Error %u)\n", (int)Error
));
685 /* Set 'Configuration Data' value */
686 Size
= sizeof(CM_FULL_RESOURCE_DESCRIPTOR
) -
687 sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR
);
688 FullResourceDescriptor
= MmAllocateMemory(Size
);
689 if (FullResourceDescriptor
== NULL
)
691 DbgPrint((DPRINT_HWDETECT
,
692 "Failed to allocate resource descriptor\n"));
696 /* Initialize resource descriptor */
697 memset(FullResourceDescriptor
, 0, Size
);
698 FullResourceDescriptor
->InterfaceType
= Isa
;
699 FullResourceDescriptor
->BusNumber
= 0;
700 FullResourceDescriptor
->PartialResourceList
.Count
= 0;
702 /* Set 'Configuration Data' value */
703 Error
= RegSetValue(SystemKey
,
704 "Configuration Data",
705 REG_FULL_RESOURCE_DESCRIPTOR
,
706 (PU8
) FullResourceDescriptor
,
708 MmFreeMemory(FullResourceDescriptor
);
709 if (Error
!= ERROR_SUCCESS
)
711 DbgPrint((DPRINT_HWDETECT
,
712 "RegSetValue(Configuration Data) failed (Error %u)\n",
718 /* Detect ISA/BIOS devices */
719 DetectBiosDisks(SystemKey
, BusKey
);
721 DetectBiosFloppyDisks(SystemKey
, BusKey
);
723 DetectBiosSerialPorts();
724 DetectBiosParallelPorts();
726 DetectBiosKeyboard();
730 /* FIXME: Detect more ISA devices */
741 DbgPrint((DPRINT_HWDETECT
, "DetectHardware()\n"));
743 HalpCalibrateStallExecution ();
745 /* Create the 'System' key */
746 Error
= RegCreateKey(NULL
,
747 "\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System",
749 if (Error
!= ERROR_SUCCESS
)
751 DbgPrint((DPRINT_HWDETECT
, "RegCreateKey() failed (Error %u)\n", (int)Error
));
758 DetectCPUs(SystemKey
);
762 DetectPciBios(&BusNumber
);
763 DetectApmBios(&BusNumber
);
765 DetectPnpBios(SystemKey
, &BusNumber
);
766 DetectIsaBios(SystemKey
, &BusNumber
);
768 DetectAcpiBios(&BusNumber
);
772 DbgPrint((DPRINT_HWDETECT
, "DetectHardware() Done\n"));
775 printf("*** System stopped ***\n");