update for HEAD-2003091401
[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                                ModuleObject->Base,
227                                ModuleObject->Length);
228   if (!NT_SUCCESS(Status))
229     {
230       DPRINT1("IopInitializeDriver() failed (Status %lx)\n", Status);
231       LdrUnloadModule(ModuleObject);
232       IopFreeDeviceNode(DeviceNode);
233     }
234
235   return(Status);
236 }
237
238
239 NTSTATUS STDCALL
240 NtUnloadDriver(IN PUNICODE_STRING DriverServiceName)
241 {
242   DPRINT("DriverServiceName: '%wZ'\n", DriverServiceName);
243
244   return(STATUS_NOT_IMPLEMENTED);
245 }
246
247
248 static NTSTATUS STDCALL
249 IopCreateGroupListEntry(PWSTR ValueName,
250                         ULONG ValueType,
251                         PVOID ValueData,
252                         ULONG ValueLength,
253                         PVOID Context,
254                         PVOID EntryContext)
255 {
256   PSERVICE_GROUP Group;
257
258   if (ValueType == REG_SZ)
259     {
260       DPRINT("GroupName: '%S'\n", (PWCHAR)ValueData);
261
262       Group = ExAllocatePool(NonPagedPool,
263                              sizeof(SERVICE_GROUP));
264       if (Group == NULL)
265         {
266           return(STATUS_INSUFFICIENT_RESOURCES);
267         }
268
269       RtlZeroMemory(Group, sizeof(SERVICE_GROUP));
270
271       if (!RtlCreateUnicodeString(&Group->GroupName,
272                                   (PWSTR)ValueData))
273         {
274           return(STATUS_INSUFFICIENT_RESOURCES);
275         }
276
277
278       InsertTailList(&GroupListHead,
279                      &Group->GroupListEntry);
280     }
281
282   return(STATUS_SUCCESS);
283 }
284
285
286 static NTSTATUS STDCALL
287 IopCreateServiceListEntry(PUNICODE_STRING ServiceName)
288 {
289   RTL_QUERY_REGISTRY_TABLE QueryTable[6];
290   PSERVICE Service;
291   NTSTATUS Status;
292
293   DPRINT("ServiceName: '%wZ'\n", ServiceName);
294
295   /* Allocate service entry */
296   Service = (PSERVICE)ExAllocatePool(NonPagedPool, sizeof(SERVICE));
297   if (Service == NULL)
298     {
299       DPRINT1("ExAllocatePool() failed\n");
300       return(STATUS_INSUFFICIENT_RESOURCES);
301     }
302   RtlZeroMemory(Service, sizeof(SERVICE));
303
304   /* Get service data */
305   RtlZeroMemory(&QueryTable,
306                 sizeof(QueryTable));
307
308   QueryTable[0].Name = L"Start";
309   QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
310   QueryTable[0].EntryContext = &Service->Start;
311
312   QueryTable[1].Name = L"Type";
313   QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
314   QueryTable[1].EntryContext = &Service->Type;
315
316   QueryTable[2].Name = L"ErrorControl";
317   QueryTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
318   QueryTable[2].EntryContext = &Service->ErrorControl;
319
320   QueryTable[3].Name = L"Group";
321   QueryTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
322   QueryTable[3].EntryContext = &Service->ServiceGroup;
323
324   QueryTable[4].Name = L"ImagePath";
325   QueryTable[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
326   QueryTable[4].EntryContext = &Service->ImagePath;
327
328   Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
329                                   ServiceName->Buffer,
330                                   QueryTable,
331                                   NULL,
332                                   NULL);
333   if (!NT_SUCCESS(Status) || Service->Start > 1)
334     {
335       RtlFreeUnicodeString(&Service->ServiceGroup);
336       RtlFreeUnicodeString(&Service->ImagePath);
337       ExFreePool(Service);
338       return(Status);
339     }
340
341   /* Copy service name */
342   Service->ServiceName.Length = ServiceName->Length;
343   Service->ServiceName.MaximumLength = ServiceName->Length + sizeof(WCHAR);
344   Service->ServiceName.Buffer = ExAllocatePool(NonPagedPool,
345                                                Service->ServiceName.MaximumLength);
346   RtlCopyMemory(Service->ServiceName.Buffer,
347                 ServiceName->Buffer,
348                 ServiceName->Length);
349   Service->ServiceName.Buffer[ServiceName->Length / sizeof(WCHAR)] = 0;
350
351   /* Build registry path */
352   Service->RegistryPath.MaximumLength = MAX_PATH * sizeof(WCHAR);
353   Service->RegistryPath.Buffer = ExAllocatePool(NonPagedPool,
354                                                 MAX_PATH * sizeof(WCHAR));
355   wcscpy(Service->RegistryPath.Buffer,
356          L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
357   wcscat(Service->RegistryPath.Buffer,
358          Service->ServiceName.Buffer);
359   Service->RegistryPath.Length = wcslen(Service->RegistryPath.Buffer) * sizeof(WCHAR);
360
361   DPRINT("ServiceName: '%wZ'\n", &Service->ServiceName);
362   DPRINT("RegistryPath: '%wZ'\n", &Service->RegistryPath);
363   DPRINT("ServiceGroup: '%wZ'\n", &Service->ServiceGroup);
364   DPRINT("ImagePath: '%wZ'\n", &Service->ImagePath);
365   DPRINT("Start %lx  Type %lx  ErrorControl %lx\n",
366          Service->Start, Service->Type, Service->ErrorControl);
367
368   /* Append service entry */
369   InsertTailList(&ServiceListHead,
370                  &Service->ServiceListEntry);
371
372   return(STATUS_SUCCESS);
373 }
374
375
376 NTSTATUS
377 IoCreateDriverList(VOID)
378 {
379   RTL_QUERY_REGISTRY_TABLE QueryTable[2];
380   PKEY_BASIC_INFORMATION KeyInfo = NULL;
381   OBJECT_ATTRIBUTES ObjectAttributes;
382   UNICODE_STRING ServicesKeyName;
383   UNICODE_STRING SubKeyName;
384   HANDLE KeyHandle;
385   NTSTATUS Status;
386   ULONG Index;
387
388   ULONG KeyInfoLength = 0;
389   ULONG ReturnedLength;
390
391   DPRINT("IoCreateDriverList() called\n");
392
393   /* Initialize basic variables */
394   InitializeListHead(&GroupListHead);
395   InitializeListHead(&ServiceListHead);
396
397   /* Build group order list */
398   RtlZeroMemory(&QueryTable,
399                 sizeof(QueryTable));
400
401   QueryTable[0].Name = L"List";
402   QueryTable[0].QueryRoutine = IopCreateGroupListEntry;
403
404   Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
405                                   L"ServiceGroupOrder",
406                                   QueryTable,
407                                   NULL,
408                                   NULL);
409   if (!NT_SUCCESS(Status))
410     return(Status);
411
412   /* Enumerate services and create the service list */
413   RtlInitUnicodeStringFromLiteral(&ServicesKeyName,
414                        L"\\Registry\\Machine\\System\\CurrentControlSet\\Services");
415
416   InitializeObjectAttributes(&ObjectAttributes,
417                              &ServicesKeyName,
418                              OBJ_CASE_INSENSITIVE,
419                              NULL,
420                              NULL);
421
422   Status = NtOpenKey(&KeyHandle,
423                      0x10001,
424                      &ObjectAttributes);
425   if (!NT_SUCCESS(Status))
426     {
427       return(Status);
428     }
429
430   KeyInfoLength = sizeof(KEY_BASIC_INFORMATION) + MAX_PATH * sizeof(WCHAR);
431   KeyInfo = ExAllocatePool(NonPagedPool, KeyInfoLength);
432   if (KeyInfo == NULL)
433     {
434       NtClose(KeyHandle);
435       return(STATUS_INSUFFICIENT_RESOURCES);
436     }
437
438   Index = 0;
439   while (TRUE)
440     {
441       Status = NtEnumerateKey(KeyHandle,
442                               Index,
443                               KeyBasicInformation,
444                               KeyInfo,
445                               KeyInfoLength,
446                               &ReturnedLength);
447       if (NT_SUCCESS(Status))
448         {
449           if (KeyInfo->NameLength < MAX_PATH * sizeof(WCHAR))
450             {
451
452               SubKeyName.Length = KeyInfo->NameLength;
453               SubKeyName.MaximumLength = KeyInfo->NameLength + sizeof(WCHAR);
454               SubKeyName.Buffer = KeyInfo->Name;
455               SubKeyName.Buffer[SubKeyName.Length / sizeof(WCHAR)] = 0;
456
457               DPRINT("KeyName: '%wZ'\n", &SubKeyName);
458               IopCreateServiceListEntry(&SubKeyName);
459             }
460         }
461
462       if (!NT_SUCCESS(Status))
463         break;
464
465       Index++;
466     }
467
468   ExFreePool(KeyInfo);
469   NtClose(KeyHandle);
470
471   DPRINT("IoCreateDriverList() done\n");
472
473   return(STATUS_SUCCESS);
474 }
475
476
477 VOID
478 LdrLoadAutoConfigDrivers(VOID)
479 {
480   PLIST_ENTRY GroupEntry;
481   PLIST_ENTRY ServiceEntry;
482   PSERVICE_GROUP CurrentGroup;
483   PSERVICE CurrentService;
484   NTSTATUS Status;
485
486   CHAR TextBuffer [256];
487   ULONG x, y, cx, cy;
488
489   DPRINT("LdrLoadAutoConfigDrivers() called\n");
490
491   GroupEntry = GroupListHead.Flink;
492   while (GroupEntry != &GroupListHead)
493     {
494       CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
495
496       DPRINT("Group: %wZ\n", &CurrentGroup->GroupName);
497
498       ServiceEntry = ServiceListHead.Flink;
499       while (ServiceEntry != &ServiceListHead)
500         {
501           CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
502
503           if ((RtlCompareUnicodeString(&CurrentGroup->GroupName, &CurrentService->ServiceGroup, TRUE) == 0) &&
504               (CurrentService->Start == 1 /*SERVICE_SYSTEM_START*/))
505             {
506
507               HalQueryDisplayParameters(&x, &y, &cx, &cy);
508               RtlFillMemory(TextBuffer, x, ' ');
509               TextBuffer[x] = '\0';
510               HalSetDisplayParameters(0, y-1);
511               HalDisplayString(TextBuffer);
512
513               sprintf(TextBuffer, "Loading %S...\n", CurrentService->ServiceName.Buffer);
514               HalSetDisplayParameters(0, y-1);
515               HalDisplayString(TextBuffer);
516               HalSetDisplayParameters(cx, cy);
517
518               DPRINT("  Path: %wZ\n", &CurrentService->RegistryPath);
519               Status = NtLoadDriver(&CurrentService->RegistryPath);
520               if (!NT_SUCCESS(Status))
521                 {
522                   DPRINT("NtLoadDriver() failed (Status %lx)\n", Status);
523 #if 0
524                   if (CurrentService->ErrorControl == 1)
525                     {
526                       /* Log error */
527
528                     }
529                   else if (CurrentService->ErrorControl == 2)
530                     {
531                       if (IsLastKnownGood == FALSE)
532                         {
533                           /* Boot last known good configuration */
534
535                         }
536                     }
537                   else if (CurrentService->ErrorControl == 3)
538                     {
539                       if (IsLastKnownGood == FALSE)
540                         {
541                           /* Boot last known good configuration */
542
543                         }
544                       else
545                         {
546                           /* BSOD! */
547
548                         }
549                     }
550 #endif
551                 }
552             }
553           ServiceEntry = ServiceEntry->Flink;
554         }
555
556       GroupEntry = GroupEntry->Flink;
557     }
558
559   DPRINT("LdrLoadAutoConfigDrivers() done\n");
560 }
561
562
563 NTSTATUS
564 IoDestroyDriverList(VOID)
565 {
566   PLIST_ENTRY GroupEntry;
567   PLIST_ENTRY ServiceEntry;
568   PSERVICE_GROUP CurrentGroup;
569   PSERVICE CurrentService;
570
571   DPRINT("IoDestroyDriverList() called\n");
572
573   /* Destroy group list */
574   GroupEntry = GroupListHead.Flink;
575   while (GroupEntry != &GroupListHead)
576     {
577       CurrentGroup = CONTAINING_RECORD(GroupEntry, SERVICE_GROUP, GroupListEntry);
578
579       RtlFreeUnicodeString(&CurrentGroup->GroupName);
580       RemoveEntryList(GroupEntry);
581       ExFreePool(CurrentGroup);
582
583       GroupEntry = GroupListHead.Flink;
584     }
585
586   /* Destroy service list */
587   ServiceEntry = ServiceListHead.Flink;
588   while (ServiceEntry != &ServiceListHead)
589     {
590       CurrentService = CONTAINING_RECORD(ServiceEntry, SERVICE, ServiceListEntry);
591
592       RtlFreeUnicodeString(&CurrentService->ServiceName);
593       RtlFreeUnicodeString(&CurrentService->RegistryPath);
594       RtlFreeUnicodeString(&CurrentService->ServiceGroup);
595       RtlFreeUnicodeString(&CurrentService->ImagePath);
596       RemoveEntryList(ServiceEntry);
597       ExFreePool(CurrentService);
598
599       ServiceEntry = ServiceListHead.Flink;
600     }
601
602   DPRINT("IoDestroyDriverList() done\n");
603
604   return(STATUS_SUCCESS);
605 }
606
607 /* EOF */