+sanity check of "ntoskrnl.exe" 'KeNumberProcessors' data symbol value (==1)
[captive.git] / src / libcaptive / client / init.c
1 /* $Id$
2  * Init and cleanup code of libcaptive to be called by client application
3  * Copyright (C) 2002 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include "captive/client.h"     /* self */
23 #include "captive/ldr.h"
24 #include "captive/ldr_exports.h"
25 #include "captive/unicode.h"
26 #include "captive/rtl-file.h"
27 #include <glib/gtypes.h>
28 #include <glib/gmessages.h>
29 #include "reactos/internal/ldr.h"
30 #include "reactos/napi/types.h"
31 #include "reactos/internal/kd.h"        /* for KDB_LOADDRIVER_HOOK */
32 #include <fcntl.h>
33 #include <sys/mman.h>   /* for PROT_READ, MAP_SHARED */
34 #include "reactos/ddk/kefuncs.h"        /* for KeInitializeSpinLock() */
35 #include "reactos/internal/ntoskrnl.h"  /* for RtlpInitNlsTables() and IoInit() */
36 #include "reactos/internal/ps.h"        /* for PsInitProcessManagment() and PsInitThreadManagment() */
37 #include "reactos/ddk/iofuncs.h"        /* for IoCreateFile() */
38 #include "captive/storage.h"
39 #include "captive/signal.h"     /* for captive_signal_init() */
40 #include "reactos/ddk/psfuncs.h"        /* for PsGetCurrentThread() */
41 #include <stdio.h>
42 #include <popt.h>
43 #include <glib/gstrfuncs.h>
44 #include <glib/glist.h>
45 #include <glib/gutils.h>        /* for g_atexit() */
46 #include "giochannel-blind.h"
47 #include <glib-object.h>
48 #include "reactos/internal/se.h"        /* for SeInit2() */
49
50
51 /* Are we initialized? */
52 static gboolean active;
53
54 /* Module of fs module itself loaded by captive_w32_init() */
55 static PMODULE_OBJECT ModuleObject;
56
57 /* Driver in fs module loaded by captive_w32_init() */
58 DRIVER_OBJECT captive_DriverObject;
59 PDRIVER_REINITIALIZE captive_DriverObject_ReinitRoutine;
60 PVOID captive_DriverObject_ReinitRoutine_Context;
61
62 /* Structure holding the pointer to the toplevel IRP */
63 static TOP_LEVEL_IRP TopLevelIrp;       /* TODO:thread */
64
65
66 gchar *captive_option_filesystem;
67 enum captive_option_rwmode captive_option_rwmode=CAPTIVE_OPTION_RWMODE_BLIND;
68 enum captive_option_media captive_option_media=CAPTIVE_OPTION_MEDIA_DISK;
69 gboolean captive_debug_messages=FALSE;
70 GIOChannel *captive_image_iochannel;
71 /* Non-blinded original GIOChannel for flushing during shutdown. */
72 static GIOChannel *captive_image_iochannel_orig;
73 guint64 captive_image_size;
74 static GList *captive_load_module;
75
76
77 static gchar *captive_popt_optarg;
78
79
80 static void arg_filesystem(void)
81 {
82         g_free(captive_option_filesystem);
83         captive_option_filesystem=g_strdup(captive_popt_optarg);
84 }
85
86 static void arg_load_module(void)
87 {
88         captive_load_module=g_list_append(captive_load_module,g_strdup(captive_popt_optarg));
89 }
90
91 static void arg_ro(void)
92 {
93         captive_option_rwmode=CAPTIVE_OPTION_RWMODE_RO;
94 }
95 static void arg_blind(void)
96 {
97         captive_option_rwmode=CAPTIVE_OPTION_RWMODE_BLIND;
98 }
99 static void arg_rw(void)
100 {
101         captive_option_rwmode=CAPTIVE_OPTION_RWMODE_RW;
102 }
103
104 static void arg_cdrom(void)
105 {
106         captive_option_media=CAPTIVE_OPTION_MEDIA_CDROM;
107 }
108 static void arg_disk(void)
109 {
110         captive_option_media=CAPTIVE_OPTION_MEDIA_DISK;
111 }
112
113 static void arg_debug_messages(void)
114 {
115         captive_debug_messages=TRUE;
116 }
117
118
119 static void captive_popt_callback
120                 (poptContext con,enum poptCallbackReason reason,const struct poptOption *opt,const char *arg,const void *data);
121
122 const struct poptOption captive_popt[]={
123                 { argInfo:POPT_ARG_INTL_DOMAIN, arg:(void *)PACKAGE },
124                 { argInfo:POPT_ARG_CALLBACK,    arg:(void *)captive_popt_callback },
125 #define POPT_OFFSET 2
126 #define CAPTIVE_POPT(longname,argInfoP,argP,descripP,argDescripP) \
127                 { \
128                         longName: (longname), \
129                         shortName: 0, \
130                         argInfo: (argInfoP), \
131                         arg: (void *)argP, \
132                         val: 0, \
133                         descrip: (descripP), \
134                         argDescrip: (argDescripP), \
135                 }
136 #define CAPTIVE_POPT_STRING(longname,descripP,argDescripP) \
137                 CAPTIVE_POPT(longname,POPT_ARG_STRING,&captive_popt_optarg,descripP,argDescripP)
138 #define CAPTIVE_POPT_NONE(longname,descripP) \
139                 CAPTIVE_POPT(longname,POPT_ARG_NONE  ,NULL                ,descripP,NULL       )
140
141                 CAPTIVE_POPT_STRING("filesystem"    ,N_("Path to .sys or .so filesystem module file"),N_("pathname")),
142                 CAPTIVE_POPT_STRING("load-module"   ,N_("Path to any W32 module to load w/o initialization"),N_("pathname")),
143                 CAPTIVE_POPT_NONE(  "ro"            ,N_("Read/write mode: Any write access will be forbidden")),
144                 CAPTIVE_POPT_NONE(  "blind"         ,N_("Read/write mode: All writes are just simulated in memory (default)")),
145                 CAPTIVE_POPT_NONE(  "rw"            ,N_("Read/write mode: Write directly to the image file/device")),
146                 CAPTIVE_POPT_NONE(  "cdrom"         ,N_("Media type: CD-ROM")),
147                 CAPTIVE_POPT_NONE(  "disk"          ,N_("Media type: Disk (default)")),
148                 CAPTIVE_POPT_NONE(  "debug-messages",N_("Turn on debugging messages")),
149
150 #undef CAPTIVE_POPT_NONE
151 #undef CAPTIVE_POPT_STRING
152 #undef CAPTIVE_POPT
153                 POPT_TABLEEND
154                 };
155
156 static const struct poptOption captive_popt_standalone[]={
157                 CAPTIVE_POPT_INCLUDE,
158                 POPT_AUTOHELP
159                 POPT_TABLEEND
160                 };
161
162
163 static void (*const popt_func_table[])(void)={
164                 arg_filesystem,
165                 arg_load_module,
166                 arg_ro,
167                 arg_blind,
168                 arg_rw,
169                 arg_cdrom,
170                 arg_disk,
171                 arg_debug_messages,
172                 };
173
174
175 /* poptCallbackType captive_popt_callback */
176 static void captive_popt_callback
177                 (poptContext con,enum poptCallbackReason reason,const struct poptOption *opt,const char *arg,const void *data)
178 {
179 gint funci;
180
181         g_return_if_fail(reason==POPT_CALLBACK_REASON_OPTION);
182
183         funci=(opt-(captive_popt+POPT_OFFSET));
184         g_return_if_fail(funci>=0);
185         g_return_if_fail(funci<(gint)G_N_ELEMENTS(popt_func_table));
186         if (popt_func_table[funci])
187                 (*popt_func_table[funci])();
188         free(captive_popt_optarg);
189         captive_popt_optarg=NULL;       /* sanity, shouldn't be needed */
190 }
191
192
193 static gboolean captive_w32_init(void)
194 {
195 NTSTATUS err;
196 gboolean errbool;
197 GIOStatus erriostatus;
198
199         g_return_val_if_fail(captive_option_filesystem!=NULL,FALSE);
200
201         erriostatus=g_io_channel_set_encoding(captive_image_iochannel,
202                         NULL,   /* encoding; force binary data */
203                         NULL);  /* error */
204         g_assert(erriostatus==G_IO_STATUS_NORMAL);
205
206         /* captive_giochannel_size() only _after_ g_io_channel_set_encoding() ! */
207         captive_image_size=captive_giochannel_size(captive_image_iochannel);
208         g_return_val_if_fail(captive_image_size>0,FALSE);
209
210         /* Part of reactos/ntoskrnl/ke/main.c/KiSystemStartup() begins. */
211         /* ExpInitializeExecutive(); */
212                 /* Part of reactos/ntoskrnl/ke/main.c/ExpInitializeExecutive() begins
213                  * here as the rest of the function does a lot of hardware initializations.
214                  */
215                 /* LdrInit1(); */
216                         /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInit1() begins. */
217                         InitializeListHead(&ModuleTextListHead);
218                         /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInit1() ends. */
219                 KeLowerIrql(DISPATCH_LEVEL);
220                 /*...*/
221                 /* create default nls tables */
222                 RtlpInitNlsTables();
223                 /*...*/
224                 /* KeInit2() */
225                         /*...*/
226                         KeInitializeDispatcher();
227                         /*...*/
228                 KeLowerIrql(PASSIVE_LEVEL);
229                 errbool=SeInit1();
230                 g_assert(errbool==TRUE);
231                 ObInit();
232                 errbool=SeInit2();
233                 g_assert(errbool==TRUE);
234                 /* PiInitProcessManager(); */
235                         /* Part of reactos/ntoskrnl/ps/psmgr.c/PiInitProcessManager() begins. */
236                         PsInitProcessManagment();
237                         PsInitThreadManagment();
238                         /* Part of reactos/ntoskrnl/ps/psmgr.c/PiInitProcessManager() ends. */
239                 /*...*/
240                 IoInit();
241                 /*...*/
242                 /* LdrInitModuleManagement(); */
243                         /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInitModuleManagement() begins
244                          * here as the rest "Create module object for {NTOSKRNL,HAL}"
245                          * is dependent on {NTOSKRNL,HAL} PE image headers not provided by libcaptive.
246                          */
247                         /* Initialize the module list and spinlock */
248                         InitializeListHead(&ModuleListHead);
249                         KeInitializeSpinLock(&ModuleListLock);
250                         /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInitModuleManagement ends. */
251                 /*...*/
252                 /* Ntinit(); */
253                         NtInitializeEventImplementation();
254                         /*...*/
255                 /* Part of reactos/ntoskrnl/ke/main.c/ExpInitializeExecutive() ends. */
256         /* Part of reactos/ntoskrnl/ke/main.c/KiSystemStartup() ends. */
257
258         /* Simulate our PE headers and export the symbols of our complete libraries */
259         captive_kernel_exports();
260
261         errbool=captive_cdrom_init();
262         g_return_val_if_fail(errbool==TRUE,FALSE);
263         errbool=captive_disk_init();
264         g_return_val_if_fail(errbool==TRUE,FALSE);
265
266         while (captive_load_module) {
267 gchar *modulename=captive_load_module->data;
268 PMODULE_OBJECT ModuleObject_tmp;
269 NTSTATUS err;
270
271                 /* load the module */
272                 err=LdrLoadModule(
273                                 captive_utf8_to_UnicodeString_alloca(modulename),       /* ModuleName */
274                                 &ModuleObject_tmp);     /* ModuleObjectp */
275                 g_return_val_if_fail(NT_SUCCESS(err),FALSE);
276
277                 captive_load_module=g_list_remove(captive_load_module,modulename);
278                 }
279
280         /* Patch 'ntoskrnl.exe' loaded by 'captive_load_module' above. */
281         captive_kernel_patches();
282         {
283         CCHAR *KeNumberProcessorsp=captive_Module_GetExportAddress("ntoskrnl.exe","KeNumberProcessors");
284         
285                 g_assert(*KeNumberProcessorsp==1);
286                 }
287
288         /* Initialize 'FsRtlLegalAnsiCharacterArray'.
289          * It requires 'ntoskrnl.exe' loaded by 'captive_load_module' above;
290          * captive_kernel_patches() should not be needed.
291          */
292         captive_FsRtlLegalAnsiCharacterArray_init();
293
294         /* set TopLevelIrp() - FIXME: where is it set by native reactos? */
295         PsGetCurrentThread()->TopLevelIrp=&TopLevelIrp; /* otherwise Io{Get,Set}TopLevelIrp() would SIGSEGV */
296
297         /* Begin possible handling of foreign W32 binary code here */
298         /* If you want to disable SIGSEGV handler if not needed:
299          *      if (ModuleObject->Flags & MODULE_FLAG_PE)
300          */
301         captive_signal_init();
302
303         /* You must have already captive_signal_init() passed here as the module may
304          * call some functions from W32 ntoskrnl.exe.
305          */
306         captive_DriverObject_ReinitRoutine=NULL;
307         err=captive_LdrpLoadAndCallImage(
308                         &ModuleObject,  /* ModuleObjectp */
309                         captive_utf8_to_UnicodeString_alloca(captive_option_filesystem),        /* ModuleName */
310                         &captive_DriverObject,  /* DriverEntry_DriverObject */
311                         captive_utf8_to_UnicodeString_alloca("\\captive\\filesystem")); /* DriverEntry_RegistryPath */
312         g_return_val_if_fail(NT_SUCCESS(err),FALSE);
313         if (captive_DriverObject_ReinitRoutine) {
314                 (*captive_DriverObject_ReinitRoutine)(
315                                 &captive_DriverObject,  /* DriverObject */
316                                 captive_DriverObject_ReinitRoutine_Context,     /* Context */
317                                 1);     /* Count: # of calls of ReinitRoutine incl. the current one */
318                 }
319
320         return TRUE;
321 }
322
323
324 static void captive_shutdown_atexit(void);
325
326
327 static void     log_discard_func(const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data)
328 {
329         /* NOP */
330 }
331
332
333 /**
334  * captive_init:
335  * @captive_args: String with possible options to parse by popt.
336  * %NULL value is permitted.
337  * @image_iochannel: Host OS file of the disk image to mount.
338  * %NULL value is permitted (initialization would not apply in such case).
339  *
340  * Initializes %libcaptive and loads the specified filesystem.
341  * You can supply %NULL value for @image_iochannel - in such case no library
342  * initialization is done; only passed @captive_args are parsed. Function
343  * will return you %FALSE value as it is not yet initializied.
344  *
345  * You should supply only @captive_args with %NULL @image_iochannel if you
346  * need to parse+examine the arguments to properly initialize @image_iochannel.
347  *
348  * Returns: %TRUE if successfuly initialized.
349  */
350 gboolean captive_init(const gchar *captive_args,GIOChannel *image_iochannel)
351 {
352 gboolean errbool;
353 int errint;
354
355 #ifdef MAINTAINER_MODE
356         g_log_set_always_fatal(~(0
357                         |G_LOG_LEVEL_MESSAGE
358                         |G_LOG_LEVEL_INFO
359                         |G_LOG_LEVEL_DEBUG
360                         ));
361 #endif
362
363         g_return_val_if_fail(active==FALSE,FALSE);
364
365         /* Initialize GObject subsystem of GLib. */
366         g_type_init();
367
368         /* (optionally) parse the given 'captive_args' string */
369         if (captive_args) {
370 int captive_args_argc;
371 const char **captive_args_argv=NULL;
372 poptContext context;
373 gboolean r=FALSE;
374
375                 errint=poptParseArgvString(captive_args,&captive_args_argc,&captive_args_argv);
376                 if (errint!=0) {
377                         g_assert_not_reached(); /* argument parsing args_error */
378                         goto args_err;
379                         }
380                 context=poptGetContext(
381                                 PACKAGE,        /* name */
382                                 captive_args_argc,captive_args_argv,    /* argc,argv */
383                                 captive_popt_standalone,        /* options */
384                                 POPT_CONTEXT_KEEP_FIRST);
385                 if (context==NULL) {
386                         g_assert_not_reached(); /* argument recognization args_error */
387                         goto args_err_argv;
388                         }
389                 errint=poptReadDefaultConfig(context,
390                                 TRUE);  /* useEnv */
391                 if (errint!=0) {
392                         g_assert_not_reached(); /* argument recognization args_error */
393                         goto args_err_context;
394                         }
395                 errint=poptGetNextOpt(context);
396                 if (errint!=-1) {
397                         g_assert_not_reached(); /* some non-callbacked argument reached */
398                         goto args_err_context;
399                         }
400                 if (poptPeekArg(context)) {
401                         g_assert_not_reached(); /* some non-"--"-prefixed argument reached */
402                         goto args_err_context;
403                         }
404                 r=TRUE; /* success */
405 args_err_context:
406                 poptFreeContext(context);
407 args_err_argv:
408                 free(captive_args_argv);        /* may be NULL here */
409 args_err:
410                 if (!r) {
411                         puts("FIXME: HELP HERE");
412                         g_return_val_if_reached(r);
413                         }
414                 }
415
416         /* If we were just requested to parse the arguments. */
417         if (!image_iochannel)
418                 return FALSE;
419
420         if (!captive_debug_messages) {
421                 g_log_set_handler(
422                                 G_LOG_DOMAIN,   /* log_domain; "Captive" */
423                                 0       /* log_levels */
424                                                 | G_LOG_FLAG_RECURSION
425                                                 | G_LOG_FLAG_FATAL
426                                                 | G_LOG_LEVEL_MESSAGE
427                                                 | G_LOG_LEVEL_INFO
428                                                 | G_LOG_LEVEL_DEBUG,
429                                 log_discard_func,       /* log_func */
430                                 NULL);  /* user_data */
431                 }
432
433         if (captive_option_rwmode==CAPTIVE_OPTION_RWMODE_BLIND)
434                 captive_image_iochannel=(GIOChannel *)captive_giochannel_blind_new(image_iochannel);
435         else
436                 captive_image_iochannel=image_iochannel;
437         captive_image_iochannel_orig=image_iochannel;
438
439         /* Do not initialize 'captive_image_size' by captive_giochannel_size() here
440          * as we yet need to do g_io_channel_set_encoding().
441          */
442
443         errbool=captive_w32_init();
444         g_return_val_if_fail(errbool==TRUE,FALSE);
445
446         g_atexit(captive_shutdown_atexit);
447
448         active=TRUE;
449         return TRUE;
450 }
451
452
453 /**
454  * captive_shutdown:
455  *
456  * Closes down %libcaptive. It should flush all pending buffers and successfuly
457  * close the filesystem. Variable #captive_image_iochannel will be set to %NULL,
458  * you should close such channel yourself.
459  *
460  * Returns: %TRUE if successfuly shutdown.
461  */
462 gboolean captive_shutdown(void)
463 {
464 GIOStatus erriostatus;
465
466         g_return_val_if_fail(active==TRUE,FALSE);
467         g_return_val_if_fail(captive_image_iochannel!=NULL,FALSE);
468
469         /* Invoke all pending idle functions just to not to forget for anything... */
470         while (g_main_context_iteration(
471                         NULL,   /* context; NULL means default one */
472                         FALSE)) /* may_block */
473                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_main_context_iteration() proceeded",G_STRLOC);
474
475         captive_PoQueueShutdownWorkItem_hooklist_invoke();
476
477         /* FIXME: ntoskrnl/ex/power.c/NtShutdownSystem() does
478          * IoShutdownRegistered{Devices,FileSystems} order; is it correct?
479          */
480         IoShutdownRegisteredFileSystems();
481         IoShutdownRegisteredDevices();
482
483         /* libcaptive is not authorized to shutdown 'captive_image_channel'. */
484         erriostatus=g_io_channel_flush(
485                         captive_image_iochannel,        /* channel */
486                         NULL);  /* error */
487         g_assert(erriostatus==G_IO_STATUS_NORMAL);
488
489         /* 'captive_image_iochannel' may be blinded wrapper. */
490         if (captive_image_iochannel!=captive_image_iochannel_orig) {
491                 erriostatus=g_io_channel_flush(
492                                 captive_image_iochannel_orig,   /* channel */
493                                 NULL);  /* error */
494                 g_assert(erriostatus==G_IO_STATUS_NORMAL);
495                 }
496
497         captive_image_iochannel=NULL;
498
499         active=FALSE;
500         return TRUE;
501 }
502
503
504 static void captive_shutdown_atexit(void)
505 {
506         if (active)
507                 captive_shutdown();
508 }