[install] Update driver version to match package version
[sanbootconf.git] / installer / setup.c
1 /*
2  * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <windows.h>
23 #include <initguid.h>
24 #include <devguid.h>
25 #include <setupapi.h>
26 #include <cfgmgr32.h>
27 #include "setupdi.h"
28 #include "registry.h"
29
30 #define eprintf(...) fprintf ( stderr, __VA_ARGS__ )
31 #define array_size(x) ( sizeof ( (x) ) / sizeof ( (x)[0] ) )
32
33 #pragma warning(disable: 4702) /* Unreachable code */
34
35 /**
36  * Find service group
37  *
38  * @v sgo               Service group order array
39  * @v group_name        Service group name
40  * @ret entry           Entry within service group order array, or NULL
41  */
42 static LPWSTR * find_service_group ( LPWSTR *sgo, LPCWSTR group_name ) {
43         LPWSTR *tmp;
44
45         for ( tmp = sgo ; *tmp ; tmp++ ) {
46                 if ( _wcsicmp ( *tmp, group_name ) == 0 )
47                         return tmp;
48         }
49         eprintf ( "Cannot find service group \"%S\"\n", group_name );
50         return NULL;
51 }
52
53 /**
54  * Move service group
55  *
56  * @v sgo               Service group order array
57  * @v group_name        Service group name to move
58  * @v before_name       Service group name to move before
59  * @ret err             Error status
60  */
61 static LONG move_service_group ( LPWSTR *sgo, LPCWSTR group_name,
62                                  LPCWSTR before_name ) {
63         LPWSTR *group;
64         LPWSTR *before;
65         LPWSTR tmp;
66
67         group = find_service_group ( sgo, group_name );
68         if ( ! group )
69                 return ERROR_FILE_NOT_FOUND;
70         before = find_service_group ( sgo, before_name );
71         if ( ! before )
72                 return ERROR_FILE_NOT_FOUND;
73
74         while ( group > before ) {
75                 tmp = *group;
76                 *group = *( group - 1 );
77                 group--;
78                 *group = tmp;
79         }
80
81         return ERROR_SUCCESS;
82 }
83
84 /**
85  * Fix service group order
86  *
87  * @ret err             Error status
88  */
89 static LONG fix_service_group_order ( void ) {
90         WCHAR sgo_key_name[] =
91                 L"SYSTEM\\CurrentControlSet\\Control\\ServiceGroupOrder";
92         WCHAR sgo_value_name[] = L"List";
93         WCHAR sgo_backup_value_name[] = L"List.pre-sanbootconf";
94         LPWSTR *sgo;
95         LONG err;
96
97         /* Read existing service group order */
98         err = reg_query_multi_sz ( HKEY_LOCAL_MACHINE, sgo_key_name,
99                                    sgo_value_name, &sgo );
100         if ( err != ERROR_SUCCESS ) {
101                 eprintf ( "Cannot read service group order: %x\n", err );
102                 goto err_query_sgo;
103         }
104
105         /* Back up key (if no backup already exists) */
106         err = reg_value_exists ( HKEY_LOCAL_MACHINE, sgo_key_name,
107                                  sgo_backup_value_name );
108         if ( err == ERROR_FILE_NOT_FOUND ) {
109                 err = reg_set_multi_sz ( HKEY_LOCAL_MACHINE, sgo_key_name,
110                                          sgo_backup_value_name, sgo );
111                 if ( err != ERROR_SUCCESS ) {
112                         eprintf ( "Could not back up service group order: "
113                                   "%x\n", err );
114                         goto err_set_sgo_backup;
115                 }
116         }
117         if ( err != ERROR_SUCCESS ) {
118                 eprintf ( "Cannot check service group order backup: %x\n",
119                           err );
120                 goto err_exists_sgo_backup;
121         }
122
123         /* Move service groups to required places */
124         err = move_service_group ( sgo, L"PNP_TDI", L"SCSI miniport" );
125         if ( err != ERROR_SUCCESS )
126                 return err;
127         err = move_service_group ( sgo, L"Base", L"PNP_TDI" );
128         if ( err != ERROR_SUCCESS )
129                 return err;
130         err = move_service_group ( sgo, L"NDIS", L"Base" );
131         if ( err != ERROR_SUCCESS )
132                 return err;
133         err = move_service_group ( sgo, L"NDIS Wrapper", L"NDIS" );
134         if ( err != ERROR_SUCCESS )
135                 return err;
136
137         /* Write out modified service group order */
138         err = reg_set_multi_sz ( HKEY_LOCAL_MACHINE, sgo_key_name,
139                                  sgo_value_name, sgo );
140         if ( err != ERROR_SUCCESS ) {
141                 eprintf ( "Could not update service group order: %x\n", err );
142                 goto err_set_sgo;
143         }
144
145         /* Success */
146         err = ERROR_SUCCESS;
147
148  err_set_sgo:
149  err_exists_sgo_backup:
150  err_set_sgo_backup:
151         free ( sgo );
152  err_query_sgo:
153         return err;
154 }
155
156 /**
157  * Check to see if a service exists
158  *
159  * @v service_name      Service name
160  * @ret err             Error status
161  */
162 static LONG service_exists ( LPWSTR service_name ) {
163         WCHAR services_key_name[] =
164                 L"SYSTEM\\CurrentControlSet\\Services";
165         HKEY services;
166         LONG err;
167
168         /* Open Services key */
169         err = reg_open ( HKEY_LOCAL_MACHINE, services_key_name, &services );
170         if ( err != ERROR_SUCCESS )
171                 goto err_reg_open;
172
173         /* Check service key existence */
174         err = reg_key_exists ( services, service_name );
175
176         reg_close ( services );
177  err_reg_open:
178         return err;
179 }
180
181 /**
182  * Set service group
183  *
184  * @v service_name      Service name
185  * @v group_name        Group name
186  * @ret err             Error status
187  */
188 static LONG set_service_group ( LPWSTR service_name, LPWSTR group_name ) {
189         WCHAR services_key_name[] =
190                 L"SYSTEM\\CurrentControlSet\\Services";
191         HKEY services;
192         LONG err;
193
194         printf ( "Moving \"%S\" service to \"%S\" group\n",
195                  service_name, group_name );
196
197         /* Open Services key */
198         err = reg_open ( HKEY_LOCAL_MACHINE, services_key_name, &services );
199         if ( err != ERROR_SUCCESS )
200                 goto err_reg_open;
201
202         /* Set service group */
203         err = reg_set_sz ( services, service_name, L"Group", group_name );
204         if ( err != ERROR_SUCCESS ) {
205                 eprintf ( "Could not move \"%S\" service to \"%S\" group: "
206                           "%x\n", service_name, group_name, err );
207                 goto err_set_sz;
208         }
209
210         /* Success */
211         err = ERROR_SUCCESS;
212
213  err_set_sz:
214  err_reg_open:
215         reg_close ( services );
216         return err;
217 }
218
219 /**
220  * Set service to start at boot time
221  *
222  * @v service_name      Service name
223  * @ret err             Error status
224  */
225 static LONG set_boot_start ( LPWSTR service_name ) {
226         WCHAR services_key_name[] =
227                 L"SYSTEM\\CurrentControlSet\\Services";
228         HKEY services;
229         LPWSTR *depend_on;
230         LPWSTR *tmp;
231         LONG err;
232
233         printf ( "Marking \"%S\" service as boot-start\n", service_name );
234
235         /* Open Services key */
236         err = reg_open ( HKEY_LOCAL_MACHINE, services_key_name, &services );
237         if ( err != ERROR_SUCCESS )
238                 goto err_reg_open;
239
240         /* Look up service dependencies, if any */
241         err = reg_query_multi_sz ( services, service_name, L"DependOnService",
242                                    &depend_on );
243         if ( err == ERROR_SUCCESS ) {
244                 /* Recurse into services that we depend on */
245                 for ( tmp = depend_on ; *tmp ; tmp++ ) {
246                         printf ( "...\"%S\" depends on \"%S\"\n",
247                                  service_name, *tmp );
248                         err = set_boot_start ( *tmp );
249                         if ( err != ERROR_SUCCESS ) {
250                                 free ( depend_on );
251                                 return err;
252                         }
253                 }
254                 free ( depend_on );
255         }
256
257         /* Set Start=0 for the service in question */
258         err = reg_set_dword ( services, service_name, L"Start", 0 );
259         if ( err != ERROR_SUCCESS ) {
260                 eprintf ( "Could not mark \"%S\" service as boot-start: %x\n",
261                           service_name, err );
262                 goto err_set_dword;
263         }
264
265         /* Success */
266         err = ERROR_SUCCESS;
267
268  err_set_dword:
269  err_reg_open:
270         reg_close ( services );
271         return err;
272 }
273
274 /**
275  * Set all NIC services to start at boot time
276  *
277  * @ret err             Error status
278  */
279 static LONG set_boot_start_nics ( void ) {
280         HDEVINFO dev_info_list;
281         DWORD dev_index;
282         SP_DEVINFO_DATA dev_info;
283         WCHAR name[256];
284         WCHAR service[256];
285         ULONG status;
286         ULONG problem;
287         LONG err;
288
289         /* Get NIC list */
290         dev_info_list = SetupDiGetClassDevs ( &GUID_DEVCLASS_NET, NULL,
291                                               NULL, DIGCF_PRESENT );
292         if ( dev_info_list == INVALID_HANDLE_VALUE ) {
293                 err = GetLastError();
294                 eprintf ( "Could not get NIC list: %x\n", err );
295                 goto err_setupdigetclassdevs;
296         }
297
298         /* Enumerate NICs */
299         for ( dev_index = 0 ; ; dev_index++ ) {
300                 dev_info.cbSize = sizeof ( dev_info );
301                 if ( ! SetupDiEnumDeviceInfo ( dev_info_list, dev_index,
302                                                &dev_info ) ) {
303                         err = GetLastError();
304                         if ( err == ERROR_NO_MORE_ITEMS )
305                                 break;
306                         eprintf ( "Could not enumerate devices: %x\n", err );
307                         goto err_setupdienumdeviceinfo;
308                 }
309
310                 /* Fetch a name for the device, if available */
311                 if ( ! ( SetupDiGetDeviceRegistryPropertyW( dev_info_list,
312                                                             &dev_info,
313                                                             SPDRP_FRIENDLYNAME,
314                                                             NULL,
315                                                             ( (PBYTE) name ),
316                                                             sizeof ( name ),
317                                                             NULL ) ||
318                          SetupDiGetDeviceRegistryPropertyW( dev_info_list,
319                                                             &dev_info,
320                                                             SPDRP_DEVICEDESC,
321                                                             NULL,
322                                                             ( (PBYTE) name ),
323                                                             sizeof ( name ),
324                                                             NULL ) ) ) {
325                         err = GetLastError();
326                         eprintf ( "Could not get device name: %x\n", err );
327                         goto err_setupdigetdeviceregistryproperty;
328                 }
329
330                 /* Fetch service name */
331                 if ( ! SetupDiGetDeviceRegistryPropertyW ( dev_info_list,
332                                                            &dev_info,
333                                                            SPDRP_SERVICE,
334                                                            NULL,
335                                                            ( (PBYTE) service ),
336                                                            sizeof ( service ),
337                                                            NULL ) ) {
338                         err = GetLastError();
339                         eprintf ( "Could not get service name: %x\n", err );
340                         goto err_setupdigetdeviceregistryproperty;
341                 }
342
343                 /* See if this is a hidden device */
344                 err = CM_Get_DevNode_Status ( &status, &problem,
345                                               dev_info.DevInst, 0 );
346                 if ( err != CR_SUCCESS ) {
347                         eprintf ( "Could not get device status: %x\n", err );
348                         goto err_cm_get_devnode_status;
349                 }
350
351                 /* Skip if this is a hidden device.  This is something
352                  * of a heuristic.  It's a fairly safe bet that
353                  * anything showing up as non-hidden is a real NIC;
354                  * the remainder tend to be "WAN Miniport" devices et
355                  * al., which will crash if set as boot-start.
356                  */
357                 if ( status & DN_NO_SHOW_IN_DM )
358                         continue;
359
360                 printf ( "Found NIC \"%S\"\n", name );
361
362                 /* Mark NIC service as boot-start */
363                 err = set_boot_start ( service );
364                 if ( err != ERROR_SUCCESS )
365                         goto err_set_boot_start;
366         }
367
368         /* Success */
369         err = 0;
370
371  err_set_boot_start:
372  err_cm_get_devnode_status:
373  err_setupdigetdeviceregistryproperty:
374  err_setupdienumdeviceinfo:
375         SetupDiDestroyDeviceInfoList ( dev_info_list );
376  err_setupdigetclassdevs:
377         return err;
378 }
379
380 /**
381  * Fix up various registry settings
382  *
383  * @ret err             Error status
384  */
385 static LONG fix_registry ( void ) {
386         WCHAR ddms_key_name[] =
387                 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
388         WCHAR dpe_key_name[] =
389                 L"SYSTEM\\CurrentControlSet\\Control\\Session Manager"
390                 L"\\Memory Management";
391         LONG err;
392
393         /* Disable DHCP media sensing */
394         err = reg_set_dword ( HKEY_LOCAL_MACHINE, ddms_key_name,
395                               L"DisableDhcpMediaSense", 1 );
396         if ( err != ERROR_SUCCESS ) {
397                 eprintf ( "Could not disable DHCP media sensing: %x\n", err );
398                 return err;
399         }
400
401         /* Disable kernel paging */
402         err = reg_set_dword ( HKEY_LOCAL_MACHINE, dpe_key_name,
403                               L"DisablePagingExecutive", 1 );
404         if ( err != ERROR_SUCCESS ) {
405                 eprintf ( "Could not disable kernel paging: %x\n", err );
406                 return err;
407         }
408
409         /* Update ServiceGroupOrder */
410         if ( ( err = fix_service_group_order() ) != ERROR_SUCCESS )
411                 return err;
412
413         /*
414          * Set relevant services to be boot-start
415          */
416         if ( ( err = set_boot_start ( L"NDIS" ) ) != ERROR_SUCCESS )
417                 return err;
418         if ( ( err = set_boot_start ( L"Tcpip" ) ) != ERROR_SUCCESS )
419                 return err;
420         if ( ( err = set_boot_start ( L"iScsiPrt" ) ) != ERROR_SUCCESS )
421                 return err;
422         /* PSched service is not present on Win2k3 */
423         if ( ( ( err = service_exists ( L"PSched" ) ) == ERROR_SUCCESS ) &&
424              ( ( err = set_boot_start ( L"PSched" ) ) != ERROR_SUCCESS ) )
425                 return err;
426         /* ibiou and ibsrp are present only when WinOF is installed */
427         if ( ( ( err = service_exists ( L"ibiou" ) ) == ERROR_SUCCESS ) &&
428              ( ( err = set_boot_start ( L"ibiou" ) ) != ERROR_SUCCESS ) )
429                 return err;
430         if ( ( ( err = service_exists ( L"ibsrp" ) ) == ERROR_SUCCESS ) &&
431              ( ( err = set_boot_start ( L"ibsrp" ) ) != ERROR_SUCCESS ) )
432                 return err;
433         if ( ( err = set_boot_start_nics() ) != ERROR_SUCCESS )
434                 return err;
435
436         /*
437          * Move relevant services to NDIS group
438          */
439         if ( ( ( err = service_exists ( L"ibbus" ) ) == ERROR_SUCCESS ) &&
440              ( ( err = set_service_group ( L"ibbus",
441                                            L"NDIS" ) ) != ERROR_SUCCESS ) )
442                 return err;
443         if ( ( ( err = service_exists ( L"winmad" ) ) == ERROR_SUCCESS ) &&
444              ( ( err = set_service_group ( L"winmad",
445                                            L"NDIS" ) ) != ERROR_SUCCESS ) )
446                 return err;
447         if ( ( ( err = service_exists ( L"winverbs" ) ) == ERROR_SUCCESS ) &&
448              ( ( err = set_service_group ( L"winverbs",
449                                            L"NDIS" ) ) != ERROR_SUCCESS ) )
450                 return err;
451         if ( ( ( err = service_exists ( L"mlx4_hca" ) ) == ERROR_SUCCESS ) &&
452              ( ( err = set_service_group ( L"mlx4_hca",
453                                            L"NDIS" ) ) != ERROR_SUCCESS ) )
454                 return err;
455
456         return ERROR_SUCCESS;
457 }
458
459 /**
460  * Main entry point
461  *
462  * @v argc              Number of arguments
463  * @v argv              Argument list
464  * @ret exit            Exit status
465  */
466 int __cdecl main ( int argc, char **argv ) {
467         CHAR bin_path[MAX_PATH];
468         CHAR inf_rel_path[MAX_PATH];
469         CHAR inf_path[MAX_PATH];
470         CHAR *file_part;
471         WCHAR inf_path_w[MAX_PATH];
472         WCHAR hw_id[] = L"ROOT\\sanbootconf";
473         DWORD len;
474         CHAR key;
475
476         printf ( "SAN Boot Configuration Driver Installation\n" );
477         printf ( "==========================================\n\n" );
478
479         /* Check for iSCSI initiator existence */
480         if ( service_exists ( L"iScsiPrt" ) != 0 ) {
481                 eprintf ( "\nYou must install the Microsoft iSCSI initiator "
482                           "before installing this\nsoftware!\n" );
483                 goto fail;
484         }
485
486         /* Fix up registry before installing driver */
487         if ( fix_registry() != 0 )
488                 goto fail;
489
490         /* Locate .inf file */
491         len = GetFullPathName ( argv[0], array_size ( bin_path ), bin_path,
492                                 &file_part );
493         if ( ( len == 0 ) || ( len >= array_size ( bin_path ) ) )
494                 goto fail;
495         if ( file_part )
496                 *file_part = 0;
497         _snprintf ( inf_rel_path, sizeof ( inf_rel_path ),
498                     "%s\\..\\sanbootconf.inf", bin_path );
499         len = GetFullPathName ( inf_rel_path, array_size ( inf_path ),
500                                 inf_path, NULL );
501         if ( ( len == 0 ) || ( len >= array_size ( inf_path ) ) )
502                 goto fail;
503         printf ( "Installing from \"%s\"\n", inf_path );
504
505         /* Install/update driver */
506         _snwprintf ( inf_path_w, array_size ( inf_path_w ), L"%S", inf_path );
507         if ( install_or_update_driver ( inf_path_w, hw_id ) != 0 )
508                 goto fail;
509
510         /* Success */
511         printf ( "SAN Boot Configuration Driver installed successfully\n" );
512
513         ( VOID ) argc;
514         exit ( EXIT_SUCCESS );
515
516  fail:
517         eprintf ( "\n*** INSTALLATION FAILED ***\n" );
518         eprintf ( "Press Enter to continue\n" );
519         scanf ( "%c", &key );
520         exit ( EXIT_FAILURE );
521 }