diff --git a/boot/bootdata/txtsetup.sif b/boot/bootdata/txtsetup.sif index 2b7b04002e2..2ddb0512ed3 100644 --- a/boot/bootdata/txtsetup.sif +++ b/boot/bootdata/txtsetup.sif @@ -212,10 +212,10 @@ Cabinet=reactos.cab [SetupData] DefaultPath = \ReactOS +SetupDebugOptions = "/DEBUG /KDSERIAL /DEBUGPORT=COM1 /FIRSTCHANCE" +;SetupDebugOptions = "/DEBUG /SOS /DEBUGPORT=SCREEN" +;SetupDebugOptions = "/DEBUG /DEBUGPORT=BOCHS" OsLoadOptions = "/NOGUIBOOT /NODEBUG" -DbgOsLoadOptions = "/NOGUIBOOT /KDSERIAL /DEBUGPORT=COM1 /FIRSTCHANCE" -;DbgOsLoadOptions = "/SOS /DEBUGPORT=SCREEN" -;DbgOsLoadOptions = "/NOGUIBOOT /DEBUGPORT=BOCHS" [NLS] AnsiCodepage = c_1252.nls diff --git a/boot/freeldr/FREELDR.INI b/boot/freeldr/FREELDR.INI index 0bd420d06dc..48561fc4475 100644 --- a/boot/freeldr/FREELDR.INI +++ b/boot/freeldr/FREELDR.INI @@ -115,14 +115,14 @@ ; will only be used to set the boot partition. ; Initrd - specifies the optional init ramdisk file name to be used. ; The same remarks about the path as for "Kernel" remain valid. -; CommandLine - specifies the command line options for the kernel. +; CommandLine - specifies the boot load options for the kernel. ; ["Windows(NT40|2003)" OSType] Section Commands: ; ; SystemPath - specifies the system root path (must be a valid ARC path): ; multi(0)disk(0)rdisk(0)partition(1)\reactos ; multi(0)disk(0)fdisk(0) -; Options - specifies the command line options for the kernel being booted. +; Options - specifies the boot load options for the kernel. ; Kernel - specifies the kernel file name (default: ntoskrnl.exe) ; Hal - specifies the HAL file name (default: hal.dll) ; @@ -133,8 +133,24 @@ ; ["ReactOSSetup" OSType] Section Commands: ; -; No options defined for the moment. This OS type is used to tell FreeLdr -; to start the SETUP portion of NT / ReactOS. +; This OS type is used to start the SETUP portion of NT / ReactOS. +; +; SystemPath - specifies the system root path (must be a valid ARC path): +; multi(0)disk(0)rdisk(0)partition(1)\reactos +; multi(0)disk(0)fdisk(0) +; Options - specifies extra boot load options for the kernel and the +; setup environment (see REMARK 2 below). +; +; REMARK: Contrary to the "Windows" type, this OS type does not support separate +; "Kernel" and "Hal" values. Instead, these values must be specified using the +; NT-compatible "/HAL=filename" and "/KERNEL=filename" option switches. +; +; REMARK 2: The SETUP portion retrieves the default boot load options from the +; TXTSETUP.SIF file present in the installation source (section "[SetupData]", +; values "OsLoadOptions" and "SetupDebugOptions"). Thus, any options specified +; using the "Options" value will supplement those already obtained from the +; TXTSETUP.SIF file, unless the "/SIFOPTIONSOVERRIDE" option switch is specified, +; in which case the "Options" value overrides those from TXTSETUP.SIF. [FREELOADER] diff --git a/boot/freeldr/freeldr/custom.c b/boot/freeldr/freeldr/custom.c index 7cfd38c8d40..2422107d58e 100644 --- a/boot/freeldr/freeldr/custom.c +++ b/boot/freeldr/freeldr/custom.c @@ -36,7 +36,8 @@ const CHAR BootDrivePrompt[] = "Enter the boot drive.\n\nExamples:\nfd0 - first const CHAR BootPartitionPrompt[] = "Enter the boot partition.\n\nEnter 0 for the active (bootable) partition."; const CHAR ARCPathPrompt[] = "Enter the boot ARC path.\n\nExamples:\nmulti(0)disk(0)rdisk(0)partition(1)\nmulti(0)disk(0)fdisk(0)"; const CHAR ReactOSSystemPathPrompt[] = "Enter the path to your ReactOS system directory.\n\nExamples:\n\\REACTOS\n\\ROS"; -const CHAR ReactOSOptionsPrompt[] = "Enter the options you want passed to the kernel.\n\nExamples:\n/DEBUG /DEBUGPORT=COM1 /BAUDRATE=115200\n/FASTDETECT /SOS /NOGUIBOOT\n/BASEVIDEO /MAXMEM=64\n/KERNEL=NTKRNLMP.EXE /HAL=HALMPS.DLL"; +const CHAR ReactOSOptionsPrompt[] = "Enter the load options you want passed to the kernel.\n\nExamples:\n/DEBUG /DEBUGPORT=COM1 /BAUDRATE=115200\n/FASTDETECT /SOS /NOGUIBOOT\n/BASEVIDEO /MAXMEM=64\n/KERNEL=NTKRNLMP.EXE /HAL=HALMPS.DLL"; +const CHAR ReactOSSetupOptionsPrompt[] = "Enter additional load options you want passed to the ReactOS Setup.\nThese options will supplement those obtained from the TXTSETUP.SIF\nfile, unless you also specify the /SIFOPTIONSOVERRIDE option switch.\n\nExample:\n/NOGUIBOOT /DEBUG /DEBUGPORT=COM1 /BAUDRATE=115200"; const CHAR CustomBootPrompt[] = "Press ENTER to boot your custom boot setup."; /* FUNCTIONS ******************************************************************/ @@ -659,7 +660,7 @@ EditCustomBootReactOS( return; } - if (!UiEditBox(ReactOSOptionsPrompt, ReactOSOptions, sizeof(ReactOSOptions))) + if (!UiEditBox(IsSetup ? ReactOSSetupOptionsPrompt : ReactOSOptionsPrompt, ReactOSOptions, sizeof(ReactOSOptions))) return; /* Modify the settings values and return if we were in edit mode */ @@ -686,7 +687,9 @@ EditCustomBootReactOS( return; /* Construct the ReactOS ARC system path */ - ConstructArcPath(ReactOSARCPath, ReactOSSystemPath, DriveMapGetBiosDriveNumber(BootDriveString), atoi(BootPartitionString)); + ConstructArcPath(ReactOSARCPath, ReactOSSystemPath, + DriveMapGetBiosDriveNumber(BootDriveString), + atoi(BootPartitionString)); /* Add the system path */ if (!IniAddSettingValueToSection(SectionId, "SystemPath", ReactOSARCPath)) diff --git a/boot/freeldr/freeldr/ntldr/ntldropts.c b/boot/freeldr/freeldr/ntldr/ntldropts.c index 2173924e224..ee3eb344cde 100644 --- a/boot/freeldr/freeldr/ntldr/ntldropts.c +++ b/boot/freeldr/freeldr/ntldr/ntldropts.c @@ -131,3 +131,83 @@ NtLdrGetOption( { return NtLdrGetOptionEx(Options, OptionName, NULL); } + +/* + * Appends or prepends new options to the ones originally contained + * in the buffer pointed by LoadOptions, of maximum size BufferSize. + */ +VOID +NtLdrAddOptions( + IN OUT PSTR LoadOptions, + IN ULONG BufferSize, + IN BOOLEAN Append, + IN PCSTR NewOptions OPTIONAL) +{ + ULONG OptionsLength; + ULONG NewOptsLength; + BOOLEAN AddSeparator; + + if (!LoadOptions || (BufferSize == 0)) + return; + // ASSERT(strlen(LoadOptions) + 1 <= BufferSize); + + if (!NewOptions || !*NewOptions) + return; + + if (Append) + { + OptionsLength = (ULONG)strlen(LoadOptions); + OptionsLength = min(OptionsLength, BufferSize-1); + + /* Add a whitespace separator if needed */ + if (OptionsLength != 0 && + (LoadOptions[OptionsLength-1] != ' ') && + (LoadOptions[OptionsLength-1] != '\t') && + (*NewOptions != '\0') && + (*NewOptions != ' ') && + (*NewOptions != '\t')) + { + RtlStringCbCatA(LoadOptions, BufferSize * sizeof(CHAR), " "); + } + + /* Append the options */ + RtlStringCbCatA(LoadOptions, BufferSize * sizeof(CHAR), NewOptions); + } + else + { + NewOptsLength = (ULONG)strlen(NewOptions); + NewOptsLength = min(NewOptsLength, BufferSize-1); + + /* Add a whitespace separator if needed */ + AddSeparator = FALSE; + if (NewOptsLength != 0 && + (NewOptions[NewOptsLength-1] != ' ') && + (NewOptions[NewOptsLength-1] != '\t') && + (*LoadOptions != '\0') && + (*LoadOptions != ' ') && + (*LoadOptions != '\t')) + { + AddSeparator = TRUE; + ++NewOptsLength; + } + + /* + * Move the original load options forward (possibly truncating them + * at the end if the buffer is not large enough) to make place for + * the options to prepend. + */ + OptionsLength = (ULONG)strlen(LoadOptions) + 1; + OptionsLength = min(OptionsLength, BufferSize - NewOptsLength); + RtlMoveMemory(LoadOptions + NewOptsLength, + LoadOptions, + OptionsLength * sizeof(CHAR)); + /* NULL-terminate */ + (LoadOptions + NewOptsLength)[OptionsLength-1] = '\0'; + + /* Restore the new options length back to its original value */ + if (AddSeparator) --NewOptsLength; + /* Prepend the options and add the whitespace separator if needed */ + strncpy(LoadOptions, NewOptions, NewOptsLength); + if (AddSeparator) LoadOptions[NewOptsLength] = ' '; + } +} diff --git a/boot/freeldr/freeldr/ntldr/ntldropts.h b/boot/freeldr/freeldr/ntldr/ntldropts.h index aab539ce92d..9b92eac8e74 100644 --- a/boot/freeldr/freeldr/ntldr/ntldropts.h +++ b/boot/freeldr/freeldr/ntldr/ntldropts.h @@ -29,3 +29,10 @@ PCSTR NtLdrGetOption( IN PCSTR Options, IN PCSTR OptionName); + +VOID +NtLdrAddOptions( + IN OUT PSTR LoadOptions, + IN ULONG BufferSize, + IN BOOLEAN Append, + IN PCSTR NewOptions OPTIONAL); diff --git a/boot/freeldr/freeldr/ntldr/setupldr.c b/boot/freeldr/freeldr/ntldr/setupldr.c index 548e4b7c21b..e80d15e03a6 100644 --- a/boot/freeldr/freeldr/ntldr/setupldr.c +++ b/boot/freeldr/freeldr/ntldr/setupldr.c @@ -15,8 +15,6 @@ #include DBG_DEFAULT_CHANNEL(WINDOWS); -#define TAG_BOOT_OPTIONS 'pOtB' - // TODO: Move to .h VOID AllocateAndInitLPB( @@ -175,6 +173,264 @@ SetupLdrScanBootDrivers(PLIST_ENTRY BootDriverListHead, HINF InfHandle, PCSTR Se /* SETUP STARTER **************************************************************/ +/* + * Update the options in the buffer pointed by LoadOptions, of maximum size + * BufferSize, by first removing any specified options, and then adding any + * other ones. + * + * OptionsToAdd is a NULL-terminated array of string buffer pointers that + * specify the options to be added into LoadOptions. Whether they are + * prepended or appended to LoadOptions is controlled via the Append + * parameter. The options are added in the order specified by the array. + * + * OptionsToRemove is a NULL-terminated array of string buffer pointers that + * specify the options to remove from LoadOptions. Specifying also there + * any options to add, has the effect of removing from LoadOptions any + * duplicates of the options to be added, before adding them later into + * LoadOptions. The options are removed in the order specified by the array. + * + * The options string buffers in the OptionsToRemove array have the format: + * "/option1 /option2[=] ..." + * + * An option in the OptionsToRemove list with a trailing '=' or ':' designates + * an option in LoadOptions with user-specific data appended after the sign. + * When such an option is being removed from LoadOptions, all the appended + * data is also removed until the next option. + */ +VOID +NtLdrUpdateLoadOptions( + IN OUT PSTR LoadOptions, + IN ULONG BufferSize, + IN BOOLEAN Append, + IN PCSTR OptionsToAdd[] OPTIONAL, + IN PCSTR OptionsToRemove[] OPTIONAL) +{ + PCSTR NextOptions, NextOpt; + PSTR Options, Option; + ULONG NextOptLength; + ULONG OptionLength; + + if (!LoadOptions || (BufferSize == 0)) + return; + // ASSERT(strlen(LoadOptions) + 1 <= BufferSize); + + /* Loop over the options to remove */ + for (; OptionsToRemove && *OptionsToRemove; ++OptionsToRemove) + { + NextOptions = *OptionsToRemove; + while ((NextOpt = NtLdrGetNextOption(&NextOptions, &NextOptLength))) + { + /* Scan the load options */ + Options = LoadOptions; + while ((Option = (PSTR)NtLdrGetNextOption((PCSTR*)&Options, &OptionLength))) + { + /* + * Check whether the option to find exactly matches the current + * load option, or is a prefix thereof if this is an option with + * appended data. + */ + if ((OptionLength >= NextOptLength) && + (_strnicmp(Option, NextOpt, NextOptLength) == 0)) + { + if ((OptionLength == NextOptLength) || + (NextOpt[NextOptLength-1] == '=') || + (NextOpt[NextOptLength-1] == ':')) + { + /* Eat any skipped option or whitespace separators */ + while ((Option > LoadOptions) && + (Option[-1] == '/' || + Option[-1] == ' ' || + Option[-1] == '\t')) + { + --Option; + } + + /* If the option was not preceded by a whitespace + * separator, insert one and advance the pointer. */ + if ((Option > LoadOptions) && + (Option[-1] != ' ') && + (Option[-1] != '\t') && + (*Options != '\0') /* && + ** Not necessary since NtLdrGetNextOption() ** + ** stripped any leading separators. ** + (*Options != ' ') && + (*Options != '\t') */) + { + *Option++ = ' '; + } + + /* Move the remaining options back, erasing the current one */ + ASSERT(Option <= Options); + RtlMoveMemory(Option, + Options, + (strlen(Options) + 1) * sizeof(CHAR)); + + /* Reset the iterator */ + Options = Option; + } + } + } + } + } + + /* Now loop over the options to add */ + for (; OptionsToAdd && *OptionsToAdd; ++OptionsToAdd) + { + NtLdrAddOptions(LoadOptions, + BufferSize, + Append, + *OptionsToAdd); + } +} + + +/* + * List of options and their corresponding higher priority ones, + * that are either checked before any other ones, or whose name + * includes another option name as a subset (e.g. NODEBUG vs. DEBUG). + * See also https://geoffchappell.com/notes/windows/boot/editoptions.htm + */ +static const struct +{ + PCSTR Options; + PCSTR ExtraOptions; + PCSTR HigherPriorOptions; +} HighPriorOptionsMap[] = +{ + /* NODEBUG has a higher precedence than DEBUG */ + {"/DEBUG/DEBUG=", NULL, "/NODEBUG"}, + + /* When using SCREEN debug port, we need boot video */ + {"/DEBUGPORT=SCREEN", NULL, "/NOGUIBOOT"}, + + /* DETECTHAL has a higher precedence than HAL= or KERNEL= */ + {"/HAL=/KERNEL=", NULL, "/DETECTHAL"}, + + /* NOPAE has a higher precedence than PAE */ + {"/PAE", NULL, "/NOPAE"}, + + /* NOEXECUTE(=) has a higher precedence than EXECUTE */ + {"/EXECUTE", "/NOEXECUTE=ALWAYSOFF", "/NOEXECUTE/NOEXECUTE="}, + /* NOEXECUTE(=) options are self-excluding and + * some have higher precedence than others. */ + {"/NOEXECUTE/NOEXECUTE=", NULL, "/NOEXECUTE/NOEXECUTE="}, + + /* SAFEBOOT(:) options are self-excluding */ + {"/SAFEBOOT/SAFEBOOT:", NULL, "/SAFEBOOT/SAFEBOOT:"}, +}; + +#define TAG_BOOT_OPTIONS 'pOtB' + +VOID +NtLdrGetHigherPriorityOptions( + IN PCSTR BootOptions, + OUT PSTR* ExtraOptions, + OUT PSTR* HigherPriorityOptions) +{ + ULONG i; + PCSTR NextOptions, NextOpt; + ULONG NextOptLength; + SIZE_T ExtraOptsSize = 0; + SIZE_T HighPriorOptsSize = 0; + + /* Masks specifying the presence (TRUE) or absence (FALSE) of the options */ + BOOLEAN Masks[RTL_NUMBER_OF(HighPriorOptionsMap)]; + + /* Just return if we cannot return anything */ + if (!ExtraOptions && !HigherPriorityOptions) + return; + + if (ExtraOptions) + *ExtraOptions = NULL; + if (HigherPriorityOptions) + *HigherPriorityOptions = NULL; + + /* Just return if no initial options were given */ + if (!BootOptions || !*BootOptions) + return; + + /* Determine the presence of the colliding options, and the + * maximum necessary sizes for the pointers to be allocated. */ + RtlZeroMemory(Masks, sizeof(Masks)); + for (i = 0; i < RTL_NUMBER_OF(HighPriorOptionsMap); ++i) + { + /* Loop over the given options to search for */ + NextOptions = HighPriorOptionsMap[i].Options; + while ((NextOpt = NtLdrGetNextOption(&NextOptions, &NextOptLength))) + { + /* If any of these options are present... */ + if (NtLdrGetOptionExN(BootOptions, NextOpt, NextOptLength, NULL)) + { + /* ... set the mask, retrieve the sizes and stop looking for these options */ + Masks[i] = TRUE; + if (ExtraOptions && HighPriorOptionsMap[i].ExtraOptions) + { + ExtraOptsSize += strlen(HighPriorOptionsMap[i].ExtraOptions) * sizeof(CHAR); + } + if (HigherPriorityOptions && HighPriorOptionsMap[i].HigherPriorOptions) + { + HighPriorOptsSize += strlen(HighPriorOptionsMap[i].HigherPriorOptions) * sizeof(CHAR); + } + break; + } + } + } + /* Count the NULL-terminator */ + if (ExtraOptions) + ExtraOptsSize += sizeof(ANSI_NULL); + if (HigherPriorityOptions) + HighPriorOptsSize += sizeof(ANSI_NULL); + + /* Allocate the string pointers */ + if (ExtraOptions) + { + *ExtraOptions = FrLdrHeapAlloc(ExtraOptsSize, TAG_BOOT_OPTIONS); + if (!*ExtraOptions) + return; + } + if (HigherPriorityOptions) + { + *HigherPriorityOptions = FrLdrHeapAlloc(HighPriorOptsSize, TAG_BOOT_OPTIONS); + if (!*HigherPriorityOptions) + { + if (ExtraOptions) + { + FrLdrHeapFree(*ExtraOptions, TAG_BOOT_OPTIONS); + *ExtraOptions = NULL; + } + return; + } + } + + /* Initialize the strings */ + if (ExtraOptions) + *(*ExtraOptions) = '\0'; + if (HigherPriorityOptions) + *(*HigherPriorityOptions) = '\0'; + + /* Go through the masks that determine the options to check */ + for (i = 0; i < RTL_NUMBER_OF(HighPriorOptionsMap); ++i) + { + if (Masks[i]) + { + /* Retrieve the strings */ + if (ExtraOptions && HighPriorOptionsMap[i].ExtraOptions) + { + RtlStringCbCatA(*ExtraOptions, + ExtraOptsSize, + HighPriorOptionsMap[i].ExtraOptions); + } + if (HigherPriorityOptions && HighPriorOptionsMap[i].HigherPriorOptions) + { + RtlStringCbCatA(*HigherPriorityOptions, + HighPriorOptsSize, + HighPriorOptionsMap[i].HigherPriorOptions); + } + } + } +} + + ARC_STATUS LoadReactOSSetup( IN ULONG Argc, @@ -196,9 +452,8 @@ LoadReactOSSetup( PSETUP_LOADER_BLOCK SetupBlock; CHAR BootPath[MAX_PATH]; CHAR FilePath[MAX_PATH]; - CHAR BootOptions2[256]; - PSTR BootOptions; - PCSTR LoadOptions; + CHAR UserBootOptions[256]; + PCSTR BootOptions; static PCSTR SourcePaths[] = { @@ -287,20 +542,22 @@ LoadReactOSSetup( TRACE("BootPath: '%s'\n", BootPath); - /* Retrieve the boot options */ - *BootOptions2 = ANSI_NULL; - ArgValue = GetArgumentValue(Argc, Argv, "Options"); - if (ArgValue && *ArgValue) - RtlStringCbCopyA(BootOptions2, sizeof(BootOptions2), ArgValue); + /* + * Retrieve the boot options. Any options present here will supplement or + * override those that will be specified in TXTSETUP.SIF's OsLoadOptions. + */ + BootOptions = GetArgumentValue(Argc, Argv, "Options"); + if (!BootOptions) + BootOptions = ""; - TRACE("BootOptions: '%s'\n", BootOptions2); + TRACE("BootOptions: '%s'\n", BootOptions); /* Check if a RAM disk file was given */ - FileName = (PSTR)NtLdrGetOptionEx(BootOptions2, "RDPATH=", &FileNameLength); + FileName = (PSTR)NtLdrGetOptionEx(BootOptions, "RDPATH=", &FileNameLength); if (FileName && (FileNameLength > 7)) { /* Load the RAM disk */ - Status = RamDiskInitialize(FALSE, BootOptions2, SystemPartition); + Status = RamDiskInitialize(FALSE, BootOptions, SystemPartition); if (Status != ESUCCESS) { FileName += 7; FileNameLength -= 7; @@ -335,34 +592,138 @@ LoadReactOSSetup( TRACE("BootPath: '%s', SystemPath: '%s'\n", BootPath, SystemPath); - /* Get load options - debug and non-debug */ - if (!InfFindFirstLine(InfHandle, "SetupData", "OsLoadOptions", &InfContext)) - { - ERR("Failed to find 'SetupData/OsLoadOptions'\n"); - return EINVAL; - } + // UseLocalSif = NtLdrGetOption(BootOptions, "USELOCALSIF"); - if (!InfGetDataField(&InfContext, 1, &LoadOptions)) + if (NtLdrGetOption(BootOptions, "SIFOPTIONSOVERRIDE")) { - ERR("Failed to get load options\n"); - return EINVAL; - } + PCSTR OptionsToRemove[2] = {"SIFOPTIONSOVERRIDE", NULL}; -#if DBG - /* Get debug load options and use them */ - if (InfFindFirstLine(InfHandle, "SetupData", "DbgOsLoadOptions", &InfContext)) + /* Do not use any load options from TXTSETUP.SIF, but + * use instead those passed from the command line. */ + RtlStringCbCopyA(UserBootOptions, sizeof(UserBootOptions), BootOptions); + + /* Remove the private switch from the options */ + NtLdrUpdateLoadOptions(UserBootOptions, + sizeof(UserBootOptions), + FALSE, + NULL, + OptionsToRemove); + + BootOptions = UserBootOptions; + } + else // if (!*BootOptions || NtLdrGetOption(BootOptions, "SIFOPTIONSADD")) { - PCSTR DbgLoadOptions; + PCSTR LoadOptions = NULL; + PCSTR DbgLoadOptions = NULL; + PSTR ExtraOptions, HigherPriorityOptions; + PSTR OptionsToAdd[3]; + PSTR OptionsToRemove[4]; - if (InfGetDataField(&InfContext, 1, &DbgLoadOptions)) - LoadOptions = DbgLoadOptions; - } + /* Load the options from TXTSETUP.SIF */ + if (InfFindFirstLine(InfHandle, "SetupData", "OsLoadOptions", &InfContext)) + { + if (!InfGetDataField(&InfContext, 1, &LoadOptions)) + WARN("Failed to get load options\n"); + } + +#if !DBG + /* Non-debug mode: get the debug load options only if /DEBUG was specified + * in the Argv command-line options (was e.g. added to the options when + * the user selected "Debugging Mode" in the advanced boot menu). */ + if (NtLdrGetOption(BootOptions, "DEBUG") || + NtLdrGetOption(BootOptions, "DEBUG=")) + { +#else + /* Debug mode: always get the debug load options */ +#endif + if (InfFindFirstLine(InfHandle, "SetupData", "SetupDebugOptions", &InfContext)) + { + if (!InfGetDataField(&InfContext, 1, &DbgLoadOptions)) + WARN("Failed to get debug load options\n"); + } + /* If none was found, default to enabling debugging */ + if (!DbgLoadOptions) + DbgLoadOptions = "/DEBUG"; +#if !DBG + } #endif - /* Copy LoadOptions (original string will be freed) */ - BootOptions = FrLdrTempAlloc(strlen(LoadOptions) + 1, TAG_BOOT_OPTIONS); - ASSERT(BootOptions); - strcpy(BootOptions, LoadOptions); + /* Initialize the load options with those from TXTSETUP.SIF */ + *UserBootOptions = ANSI_NULL; + if (LoadOptions && *LoadOptions) + RtlStringCbCopyA(UserBootOptions, sizeof(UserBootOptions), LoadOptions); + + /* Merge the debug load options if any */ + if (DbgLoadOptions) + { + RtlZeroMemory(OptionsToAdd, sizeof(OptionsToAdd)); + RtlZeroMemory(OptionsToRemove, sizeof(OptionsToRemove)); + + /* + * Retrieve any option patterns that we should remove from the + * SIF load options because they are of higher precedence than + * those specified in the debug load options to be added. + * Also always remove NODEBUG (even if the debug load options + * do not contain explicitly the DEBUG option), since we want + * to have debugging enabled if possible. + */ + OptionsToRemove[0] = "/NODEBUG"; + NtLdrGetHigherPriorityOptions(DbgLoadOptions, + &ExtraOptions, + &HigherPriorityOptions); + OptionsToAdd[1] = (ExtraOptions ? ExtraOptions : ""); + OptionsToRemove[1] = (HigherPriorityOptions ? HigherPriorityOptions : ""); + + /* + * Prepend the debug load options, so that in case it contains + * redundant options with respect to the SIF load options, the + * former can take precedence over the latter. + */ + OptionsToAdd[0] = (PSTR)DbgLoadOptions; + OptionsToRemove[2] = (PSTR)DbgLoadOptions; + NtLdrUpdateLoadOptions(UserBootOptions, + sizeof(UserBootOptions), + FALSE, + (PCSTR*)OptionsToAdd, + (PCSTR*)OptionsToRemove); + + if (ExtraOptions) + FrLdrHeapFree(ExtraOptions, TAG_BOOT_OPTIONS); + if (HigherPriorityOptions) + FrLdrHeapFree(HigherPriorityOptions, TAG_BOOT_OPTIONS); + } + + RtlZeroMemory(OptionsToAdd, sizeof(OptionsToAdd)); + RtlZeroMemory(OptionsToRemove, sizeof(OptionsToRemove)); + + /* + * Retrieve any option patterns that we should remove from the + * SIF load options because they are of higher precedence than + * those specified in the options to be added. + */ + NtLdrGetHigherPriorityOptions(BootOptions, + &ExtraOptions, + &HigherPriorityOptions); + OptionsToAdd[1] = (ExtraOptions ? ExtraOptions : ""); + OptionsToRemove[0] = (HigherPriorityOptions ? HigherPriorityOptions : ""); + + /* Finally, prepend the user-specified options that + * take precedence over those from TXTSETUP.SIF. */ + OptionsToAdd[0] = (PSTR)BootOptions; + OptionsToRemove[1] = (PSTR)BootOptions; + NtLdrUpdateLoadOptions(UserBootOptions, + sizeof(UserBootOptions), + FALSE, + (PCSTR*)OptionsToAdd, + (PCSTR*)OptionsToRemove); + + if (ExtraOptions) + FrLdrHeapFree(ExtraOptions, TAG_BOOT_OPTIONS); + if (HigherPriorityOptions) + FrLdrHeapFree(HigherPriorityOptions, TAG_BOOT_OPTIONS); + + BootOptions = UserBootOptions; + } TRACE("BootOptions: '%s'\n", BootOptions);