:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / ntoskrnl / io / driver.c
1 /* $Id$
2  *
3  * COPYRIGHT:      See COPYING in the top level directory
4  * PROJECT:        ReactOS kernel
5  * FILE:           ntoskrnl/io/driver.c
6  * PURPOSE:        Manage devices
7  * PROGRAMMER:     David Welch (welch@cwcom.net)
8  * UPDATE HISTORY:
9  *                 15/05/98: Created
10  */
11
12 /* INCLUDES ****************************************************************/
13
14 #include <limits.h>
15 #include <ddk/ntddk.h>
16 #include <internal/io.h>
17 #include <internal/po.h>
18 #include <internal/ldr.h>
19 #include <internal/id.h>
20 #include <internal/pool.h>
21 #include <internal/registry.h>
22
23 #include <roscfg.h>
24
25 #define NDEBUG
26 #include <internal/debug.h>
27
28
29 typedef struct _SERVICE_GROUP
30 {
31   LIST_ENTRY GroupListEntry;
32   UNICODE_STRING GroupName;
33
34   BOOLEAN ServicesRunning;
35
36 } SERVICE_GROUP, *PSERVICE_GROUP;
37
38
39 typedef struct _SERVICE
40 {
41   LIST_ENTRY ServiceListEntry;
42   UNICODE_STRING ServiceName;
43   UNICODE_STRING RegistryPath;
44   UNICODE_STRING ServiceGroup;
45   UNICODE_STRING ImagePath;
46
47   ULONG Start;
48   ULONG Type;
49   ULONG ErrorControl;
50   ULONG Tag;
51
52   BOOLEAN ServiceRunning;       // needed ??
53
54 } SERVICE, *PSERVICE;
55
56
57 /* GLOBALS *******************************************************************/
58
59 static LIST_ENTRY GroupListHead = {NULL, NULL};
60 static LIST_ENTRY ServiceListHead  = {NULL, NULL};
61
62 POBJECT_TYPE EXPORTED IoDriverObjectType = NULL;
63
64 #define TAG_DRIVER             TAG('D', 'R', 'V', 'R')
65 #define TAG_DRIVER_EXTENSION   TAG('D', 'R', 'V', 'E')
66
67
68 /* FUNCTIONS ***************************************************************/
69
70 NTSTATUS STDCALL
71 IopCreateDriver(PVOID ObjectBody,
72                 PVOID Parent,
73                 PWSTR RemainingPath,
74                 POBJECT_ATTRIBUTES ObjectAttributes)
75 {
76   DPRINT("LdrCreateModule(ObjectBody %x, Parent %x, RemainingPath %S)\n",
77          ObjectBody,
78          Parent,
79          RemainingPath);
80   if (RemainingPath != NULL && wcschr(RemainingPath + 1, '\\') != NULL)
81     {
82       return(STATUS_UNSUCCESSFUL);
83     }
84
85   return(STATUS_SUCCESS);
86 }
87
88
89 VOID
90 IopInitDriverImplementation(VOID)
91 {
92   /*  Register the process object type  */
93   IoDriverObjectType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE));
94   IoDriverObjectType->Tag = TAG('D', 'R', 'V', 'R');
95   IoDriverObjectType->TotalObjects = 0;
96   IoDriverObjectType->TotalHandles = 0;
97   IoDriverObjectType->MaxObjects = ULONG_MAX;
98   IoDriverObjectType->MaxHandles = ULONG_MAX;
99   IoDriverObjectType->PagedPoolCharge = 0;
100   IoDriverObjectType->NonpagedPoolCharge = sizeof(DRIVER_OBJECT);
101   IoDriverObjectType->Dump = NULL;
102   IoDriverObjectType->Open = NULL;
103   IoDriverObjectType->Close = NULL;
104   IoDriverObjectType->Delete = NULL;
105   IoDriverObjectType->Parse = NULL;
106   IoDriverObjectType->Security = NULL;
107   IoDriverObjectType->QueryName = NULL;
108   IoDriverObjectType->OkayToClose = NULL;
109   IoDriverObjectType->Create = IopCreateDriver;
110   IoDriverObjectType->DuplicationNotify = NULL;
111   RtlInitUnicodeStringFromLiteral(&IoDriverObjectType->TypeName, L"Driver");
112 }
113
114 /**********************************************************************
115  * NAME                                                 EXPORTED
116  *      NtLoadDriver
117  *
118  * DESCRIPTION
119  *      Loads a device driver.
120  *      
121  * ARGUMENTS
122  *      DriverServiceName
123  *              Name of the service to load (registry key).
124  *              
125  * RETURN VALUE
126  *      Status.
127  *
128  * REVISIONS
129  */
130 NTSTATUS STDCALL
131 NtLoadDriver(IN PUNICODE_STRING DriverServiceName)
132 {
133   RTL_QUERY_REGISTRY_TABLE QueryTable[3];
134   WCHAR FullImagePathBuffer[MAX_PATH];
135   UNICODE_STRING ImagePath;
136   UNICODE_STRING FullImagePath;
137   NTSTATUS Status;
138   ULONG Type;
139   PDEVICE_NODE DeviceNode;
140   PMODULE_OBJECT ModuleObject;
141   LPWSTR Start;
142
143   DPRINT("NtLoadDriver(%wZ) called\n", DriverServiceName);
144
145   RtlInitUnicodeString(&ImagePath, NULL);
146
147   /* Get service data */
148   RtlZeroMemory(&QueryTable,
149                 sizeof(QueryTable));
150
151   QueryTable[0].Name = L"Type";
152   QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
153   QueryTable[0].EntryContext = &Type;
154
155   QueryTable[1].Name = L"ImagePath";
156   QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
157   QueryTable[1].EntryContext = &ImagePath;
158
159   Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
160                                   DriverServiceName->Buffer,
161                                   QueryTable,
162                                   NULL,
163                                   NULL);
164   if (!NT_SUCCESS(Status))
165     {
166       DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
167       RtlFreeUnicodeString(&ImagePath);
168       return(Status);
169     }
170
171   if (ImagePath.Length == 0)
172     {
173       wcscpy(FullImagePathBuffer, L"\\SystemRoot\\system32\\drivers");
174       wcscat(FullImagePathBuffer, wcsrchr(DriverServiceName->Buffer, L'\\'));
175       wcscat(FullImagePathBuffer, L".sys");
176     }
177   else if (ImagePath.Buffer[0] != L'\\')
178     {
179       wcscpy(FullImagePathBuffer, L"\\SystemRoot\\");
180       wcscat(FullImagePathBuffer, ImagePath.Buffer);
181     }
182   else
183     {
184       wcscpy(FullImagePathBuffer, ImagePath.Buffer);
185     }
186
187   RtlFreeUnicodeString(&ImagePath);
188   RtlInitUnicodeString(&FullImagePath, FullImagePathBuffer);
189
190   DPRINT("FullImagePath: '%S'\n", FullImagePathBuffer);
191   DPRINT("Type %lx\n", Type);
192
193   /* Use IopRootDeviceNode for now */
194   Status = IopCreateDeviceNode(IopRootDeviceNode, NULL, &DeviceNode);
195   if (!NT_SUCCESS(Status))
196     {
197       DPRINT1("IopCreateDeviceNode() failed (Status %lx)\n", Status);
198       return(Status);
199     }
200
201   ModuleObject = LdrGetModuleObject(DriverServiceName);
202   if (ModuleObject != NULL)
203     {
204       return(STATUS_IMAGE_ALREADY_LOADED);
205     }
206
207   Status = LdrLoadModule(&FullImagePath, &ModuleObject);
208   if (!NT_SUCCESS(Status))
209     {
210       DPRINT1("LdrLoadModule() failed (Status %lx)\n", Status);
211       IopFreeDeviceNode(DeviceNode);
212       return(Status);
213     }
214
215   /* Set a service name for the device node */
216   Start = wcsrchr(DriverServiceName->Buffer, L'\\');
217   if (Start == NULL)
218     Start = DriverServiceName->Buffer;
219   else
220     Start++;
221   RtlCreateUnicodeString(&DeviceNode->ServiceName, Start);
222
223   Status = IopInitializeDriver(ModuleObject->EntryPoint,
224                                DeviceNode,
225                                (Type == 2 || Type == 8));
226   if (!NT_SUCCESS(Status))
227     {
228       DPRINT1("IopInitializeDriver() failed (Status %lx)\n", Status);
229       LdrUnloadModule(ModuleObject);
230       IopFreeDeviceNode(DeviceNode);
231     }
232
233   return(Status);
234 }
235
236
237 NTSTATUS STDCALL
238 NtUnloadDriver(IN PUNICODE_STRING DriverServiceName)
239 {
240   DPRINT("DriverServiceName: '%wZ'\n", DriverServiceName);
241
242   return(STATUS_NOT_IMPLEMENTED);
243 }
244
245
246 static NTSTATUS STDCALL
247 IopCreateGroupListEntry(PWSTR ValueName,
248                         ULONG ValueType,
249                         PVOID ValueData,
250                         ULONG ValueLength,
251                         PVOID Context,
252                         PVOID EntryContext)
253 {
254   PSERVICE_GROUP Group;
255
256   if (ValueType == REG_SZ)
257     {
258       DPRINT("GroupName: '%S'\n", (PWCHAR)ValueData);
259
260       Group = ExAllocatePool(NonPagedPool,
261                              sizeof(SERVICE_GROUP));
262       if (Group == NULL)
263         {
264           return(STATUS_INSUFFICIENT_RESOURCES);
265         }
266
267       RtlZeroMemory(Group, sizeof(SERVICE_GROUP));
268
269       if (!RtlCreateUnicodeString(&Group->GroupName,
270                                   (PWSTR)ValueData))
271         {
272           return(STATUS_INSUFFICIENT_RESOURCES);
273         }
274
275
276       InsertTailList(&GroupListHead,
277                      &Group->GroupListEntry);
278     }
279
280   return(STATUS_SUCCESS);
281 }
282
283
284 static NTSTATUS STDCALL
285 IopCreateServiceListEntry(PUNICODE_STRING ServiceName)
286 {
287   RTL_QUERY_REGISTRY_TABLE QueryTable[6];
288   PSERVICE Service;
289   NTSTATUS Status;
290
291   DPRINT("ServiceName: '%wZ'\n", ServiceName);
292
293   /* Allocate service entry */
294   Service = (PSERVICE)ExAllocatePool(NonPagedPool, sizeof(SERVICE));
295   if (Service == NULL)
296     {
297       DPRINT1("ExAllocatePool() failed\n");
298       return(STATUS_INSUFFICIENT_RESOURCES);
299     }
300   RtlZeroMemory(Service, sizeof(SERVICE));
301
302   /* Get service data */
303   RtlZeroMemory(&QueryTable,
304                 sizeof(QueryTable));
305
306   QueryTable[0].Name = L"Start";
307   QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
308   QueryTable[0].EntryContext = &Service->Start;
309
310   QueryTable[1].Name = L"Type";
311   QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
312   QueryTable[1].EntryContext = &Service->Type;
313
314   QueryTable[2].Name = L"ErrorControl";
315   QueryTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
316   QueryTable[2].EntryContext = &Service->ErrorControl;
317
318   QueryTable[3].Name = L"Group";
319   QueryTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
320   QueryTable[3].EntryContext = &Service->ServiceGroup;
321
322   QueryTable[4].Name = L"ImagePath";
323   QueryTable[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
324   QueryTable[4].EntryContext = &Service->ImagePath;
325
326   Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
327                                   ServiceName->Buffer,
328                                   QueryTable,
329                                   NULL,
330                                   NULL);
331   if (!NT_SUCCESS(Status) || Service->Start > 1)
332     {
333       RtlFreeUnicodeString(&Service->ServiceGroup);
334       RtlFreeUnicodeString(&Service->ImagePath);
335       ExFreePool(Service);
336       return(Status);
337     }
338
339   /* Copy service name */
340   Service->ServiceName.Length = ServiceName->Length;
341   Service->ServiceName.MaximumLength = ServiceName->Length + sizeof(WCHAR);
342   Service->ServiceName.Buffer = ExAllocatePool(NonPagedPool,
343                                                Service->ServiceName.MaximumLength);
344   RtlCopyMemory(Service->ServiceName.Buffer,
345                 ServiceName->Buffer,
346                 ServiceName->Length);
347   Service->ServiceName.Buffer[ServiceName->Length / sizeof(WCHAR)] = 0;
348
349   /* Build registry path */
350   Service->RegistryPath.MaximumLength = MAX_PATH * sizeof(WCHAR);
351   Service->RegistryPath.Buffer = ExAllocatePool(NonPagedPool,
352                                                 MAX_PATH * sizeof(WCHAR));
353   wcscpy(Service->RegistryPath.Buffer,
354          L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
355   wcscat(Service->RegistryPath.Buffer,
356          Service->ServiceName.Buffer);
357   Service->RegistryPath.Length = wcslen(Service->RegistryPath.Buffer) * sizeof(WCHAR);
358
359   DPRINT("ServiceName: '%wZ'\n", &Service->ServiceName);
360   DPRINT("RegistryPath: '%wZ'\n", &Service->RegistryPath);
361   DPRINT("ServiceGroup: '%wZ'\n", &Service->ServiceGroup);
362   DPRINT("ImagePath: '%wZ'\n", &Service->ImagePath);
363   DPRINT("Start %lx  Type %lx  ErrorControl %lx\n",
364          Service->Start, Service->Type, Service->ErrorControl);
365
366   /* Append service entry */
367   InsertTailList(&ServiceListHead,
368                  &Service->ServiceListEntry);
369
370   return(STATUS_SUCCESS);
371 }
372
373
374 NTSTATUS
375 IoCreateDriverList(VOID)
376 {
377   RTL_QUERY_REGISTRY_TABLE QueryTable[2];
378   PKEY_BASIC_INFORMATION KeyInfo = NULL;
379   OBJECT_ATTRIBUTES ObjectAttributes;
380   UNICODE_STRING ServicesKeyName;
381   UNICODE_STRING SubKeyName;
382   HANDLE KeyHandle;
383   NTSTATUS Status;
384   ULONG Index;
385
386   ULONG KeyInfoLength = 0;
387   ULONG ReturnedLength;
388
389   DPRINT("IoCreateDriverList() called\n");
390
391   /* Initialize basic variables */
392   InitializeListHead(&GroupListHead);
393   InitializeListHead(&ServiceListHead);
394
395   /* Build group order list */
396   RtlZeroMemory(&QueryTable,
397                 sizeof(QueryTable));
398
399   QueryTable[0].Name = L"List";
400   QueryTable[0].QueryRoutine = IopCreateGroupListEntry;
401
402   Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
403                                   L"ServiceGroupOrder",
404                                   QueryTable,
405                                   NULL,
406                                   NULL);
407   if (!NT_SUCCESS(Status))
408     return(Status);
409
410   /* Enumerate services and create the service list */
411   RtlInitUnicodeStringFromLiteral(&ServicesKeyName,
412                        L"\\Registry\\Machine\\System\\CurrentControlSet\\Services");
413
414   InitializeObjectAttributes(&ObjectAttributes,
415                              &ServicesKeyName,
416                              OBJ_CASE_INSENSITIVE,
417                              NULL,
418                              NULL);
419
420   Status = NtOpenKey(&KeyHandle,
421                      0x10001,
422                      &ObjectAttributes);
423   if (!NT_SUCCESS(Status))
424     {
425       return(Status);
426     }
427
428   KeyInfoLength = sizeof(KEY_BASIC_INFORMATION) + MAX_PATH * sizeof(WCHAR);
429   KeyInfo = ExAllocatePool(NonPagedPool, KeyInfoLength);
430   if (KeyInfo == NULL)
431     {
432       NtClose(KeyHandle);
433       return(STATUS_INSUFFICIENT_RESOURCES);
434     }
435
436   Index = 0;
437   while (TRUE)
438     {
439       Status = NtEnumerateKey(KeyHandle,
440                               Index,
441                               KeyBasicInformation,
442                               KeyInfo,
443                               KeyInfoLength,
444                               &ReturnedLength);
445       if (NT_SUCCESS(Status))
446         {
447           if (KeyInfo->NameLength < MAX_PATH * sizeof(WCHAR))
448             {
449
450               SubKeyName.Length = KeyInfo->NameLength;
451               SubKeyName.MaximumLength = KeyInfo->NameLength + sizeof(WCHAR);
452               SubKeyName.Buffer = KeyInfo->Name;
453               SubKeyName.Buffer[SubKeyName.Length / sizeof(WCHAR)] = 0;
454
455               DPRINT("KeyName: '%wZ'\n", &SubKeyName);
456               IopCreateServiceListEntry(&SubKeyName);
457             }
458         }
459
460       if (!NT_SUCCESS(Status))
461         break;
462
463       Index++;
464     }
465
466   ExFreePool(KeyInfo);
467   NtClose(KeyHandle);
468
469   DPRINT("IoCreateDriverList() done\n");
470
471   return(STATUS_SUCCESS);
472 }
473
474
475 VOID
476 LdrLoadAutoConfigDrivers(VOID)
477 {
478   PLIST_ENTRY GroupEntry;
479   PLIST_ENTRY ServiceEntry;
480   PSERVICE_GROUP CurrentGroup;
481   PSERVICE CurrentService;
482   NTSTATUS Status;
483
484   CHAR TextBuffer [256];
485   ULONG x, y, cx, cy;
486
487   DPRINT("LdrLoadAutoConfigDrivers() called\n");
488
489   GroupEntry = GroupListHead.Flink;
490   while (GroupEntry != &GroupListHead)
491     {
492       CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
493
494       DPRINT("Group: %wZ\n", &CurrentGroup->GroupName);
495
496       ServiceEntry = ServiceListHead.Flink;
497       while (ServiceEntry != &ServiceListHead)
498         {
499           CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
500
501           if ((RtlCompareUnicodeString(&CurrentGroup->GroupName, &CurrentService->ServiceGroup, TRUE) == 0) &&
502               (CurrentService->Start == 1 /*SERVICE_SYSTEM_START*/))
503             {
504
505               HalQueryDisplayParameters(&x, &y, &cx, &cy);
506               RtlFillMemory(TextBuffer, x, ' ');
507               TextBuffer[x] = '\0';
508               HalSetDisplayParameters(0, y-1);
509               HalDisplayString(TextBuffer);
510
511               sprintf(TextBuffer, "Loading %S...\n", CurrentService->ServiceName.Buffer);
512               HalSetDisplayParameters(0, y-1);
513               HalDisplayString(TextBuffer);
514               HalSetDisplayParameters(cx, cy);
515
516               DPRINT("  Path: %wZ\n", &CurrentService->RegistryPath);
517               Status = NtLoadDriver(&CurrentService->RegistryPath);
518               if (!NT_SUCCESS(Status))
519                 {
520                   DPRINT("NtLoadDriver() failed (Status %lx)\n", Status);
521 #if 0
522                   if (CurrentService->ErrorControl == 1)
523                     {
524                       /* Log error */
525
526                     }
527                   else if (CurrentService->ErrorControl == 2)
528                     {
529                       if (IsLastKnownGood == FALSE)
530                         {
531                           /* Boot last known good configuration */
532
533                         }
534                     }
535                   else if (CurrentService->ErrorControl == 3)
536                     {
537                       if (IsLastKnownGood == FALSE)
538                         {
539                           /* Boot last known good configuration */
540
541                         }
542                       else
543                         {
544                           /* BSOD! */
545
546                         }
547                     }
548 #endif
549                 }
550             }
551           ServiceEntry = ServiceEntry->Flink;
552         }
553
554       GroupEntry = GroupEntry->Flink;
555     }
556
557   DPRINT("LdrLoadAutoConfigDrivers() done\n");
558 }
559
560
561 NTSTATUS
562 IoDestroyDriverList(VOID)
563 {
564   PLIST_ENTRY GroupEntry;
565   PLIST_ENTRY ServiceEntry;
566   PSERVICE_GROUP CurrentGroup;
567   PSERVICE CurrentService;
568
569   DPRINT("IoDestroyDriverList() called\n");
570
571   /* Destroy group list */
572   GroupEntry = GroupListHead.Flink;
573   while (GroupEntry != &GroupListHead)
574     {
575       CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
576
577       RtlFreeUnicodeString(&CurrentGroup->GroupName);
578       RemoveEntryList(GroupEntry);
579       ExFreePool(CurrentGroup);
580
581       GroupEntry = GroupListHead.Flink;
582     }
583
584   /* Destroy service list */
585   ServiceEntry = ServiceListHead.Flink;
586   while (ServiceEntry != &ServiceListHead)
587     {
588       CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
589
590       RtlFreeUnicodeString(&CurrentService->ServiceName);
591       RtlFreeUnicodeString(&CurrentService->RegistryPath);
592       RtlFreeUnicodeString(&CurrentService->ServiceGroup);
593       RtlFreeUnicodeString(&CurrentService->ImagePath);
594       RemoveEntryList(ServiceEntry);
595       ExFreePool(CurrentService);
596
597       ServiceEntry = ServiceListHead.Flink;
598     }
599
600   DPRINT("IoDestroyDriverList() done\n");
601
602   return(STATUS_SUCCESS);
603 }
604
605 /* EOF */