[FREELDR] Diverse enhancements.
[reactos.git] / boot / freeldr / freeldr / bootmgr.c
index 2295e50..8c97eea 100644 (file)
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+/* INCLUDES *******************************************************************/
+
 #include <freeldr.h>
+#include <debug.h>
+
+DBG_DEFAULT_CHANNEL(INIFILE);
+
+/* GLOBALS ********************************************************************/
 
-ARC_DISK_SIGNATURE reactos_arc_disk_info[32]; // ARC Disk Information
-unsigned long reactos_disk_count = 0;
-char reactos_arc_hardware_data[HW_MAX_ARC_HEAP_SIZE] = {0};
-char reactos_arc_strings[32][256];
+typedef
+VOID
+(*EDIT_OS_ENTRY_PROC)(
+    IN ULONG_PTR SectionId OPTIONAL);
 
-ULONG   GetDefaultOperatingSystem(OperatingSystemItem* OperatingSystemList, ULONG       OperatingSystemCount)
+static VOID
+EditCustomBootReactOSSetup(
+    IN ULONG_PTR SectionId OPTIONAL)
 {
-       CHAR    DefaultOSText[80];
-       PCSTR   DefaultOSName;
-       ULONG_PTR       SectionId;
-       ULONG   DefaultOS = 0;
-       ULONG   Idx;
-
-       if (!IniOpenSection("FreeLoader", &SectionId))
-       {
-               return 0;
-       }
-
-       DefaultOSName = CmdLineGetDefaultOS();
-       if (NULL == DefaultOSName)
-       {
-               if (IniReadSettingByName(SectionId, "DefaultOS", DefaultOSText, sizeof(DefaultOSText)))
-               {
-                       DefaultOSName = DefaultOSText;
-               }
-       }
-
-       if (NULL != DefaultOSName)
-       {
-               for (Idx=0; Idx<OperatingSystemCount; Idx++)
-               {
-                       if (_stricmp(DefaultOSName, OperatingSystemList[Idx].SystemPartition) == 0)
-                       {
-                               DefaultOS = Idx;
-                               break;
-                       }
-               }
-       }
-
-       return DefaultOS;
+    EditCustomBootReactOS(SectionId, TRUE);
 }
 
-LONG GetTimeOut(VOID)
+static VOID
+EditCustomBootNTOS(
+    IN ULONG_PTR SectionId OPTIONAL)
 {
-       CHAR    TimeOutText[20];
-       LONG            TimeOut;
-       ULONG_PTR       SectionId;
-
-       TimeOut = CmdLineGetTimeOut();
-       if (0 <= TimeOut)
-       {
-               return TimeOut;
-       }
-
-       if (!IniOpenSection("FreeLoader", &SectionId))
-       {
-               return -1;
-       }
-
-       if (IniReadSettingByName(SectionId, "TimeOut", TimeOutText, sizeof(TimeOutText)))
-       {
-               TimeOut = atoi(TimeOutText);
-       }
-       else
-       {
-               TimeOut = -1;
-       }
-
-       return TimeOut;
+    EditCustomBootReactOS(SectionId, FALSE);
 }
 
-BOOLEAN MainBootMenuKeyPressFilter(ULONG KeyPress)
+static const struct
+{
+    PCSTR BootType;
+    EDIT_OS_ENTRY_PROC EditOsEntry;
+    ARC_ENTRY_POINT OsLoader;
+} OSLoadingMethods[] =
 {
-       if (KeyPress == KEY_F8)
-       {
-               DoOptionsMenu();
+    {"ReactOSSetup", EditCustomBootReactOSSetup, LoadReactOSSetup},
 
-               return TRUE;
-       }
+#ifdef _M_IX86
+    {"Drive"       , EditCustomBootDisk      , LoadAndBootDrive     },
+    {"Partition"   , EditCustomBootPartition , LoadAndBootPartition },
+    {"BootSector"  , EditCustomBootSectorFile, LoadAndBootBootSector},
+
+    {"Linux"       , EditCustomBootLinux, LoadAndBootLinux  },
+    {"WindowsNT40" , EditCustomBootNTOS , LoadAndBootWindows},
+#endif
+    {"Windows"     , EditCustomBootNTOS , LoadAndBootWindows},
+    {"Windows2003" , EditCustomBootNTOS , LoadAndBootWindows},
+};
 
-       // We didn't handle the key
-       return FALSE;
+/* FUNCTIONS ******************************************************************/
+
+PCHAR*
+BuildArgvForOsLoader(
+    IN PCSTR LoadIdentifier,
+    IN ULONG_PTR SectionId,
+    OUT PULONG pArgc)
+{
+    SIZE_T Size;
+    ULONG Count;
+    ULONG i;
+    ULONG Argc;
+    PCHAR* Argv;
+    PCHAR* Args;
+    PCHAR SettingName, SettingValue;
+
+    /*
+     * Convert the list of key=value options in the given operating system section
+     * into a ARC-compatible argument vector.
+     */
+
+    *pArgc = 0;
+
+    /* Validate the LoadIdentifier (to make tests simpler later) */
+    if (LoadIdentifier && !*LoadIdentifier)
+        LoadIdentifier = NULL;
+
+    /* Count the number of operating systems in the section */
+    Count = IniGetNumSectionItems(SectionId);
+
+    /* The argument vector contains the program name, the LoadIdentifier (optional), and the items in the OS section */
+    Argc = 1 + Count;
+    if (LoadIdentifier)
+        ++Argc;
+
+    /* Calculate the total size needed for the string buffer of the argument vector */
+    Size = 0;
+    /* i == 0: Program name */
+    /* i == 1: LoadIdentifier */
+    if (LoadIdentifier)
+    {
+        Size += (strlen("LoadIdentifier=") + strlen(LoadIdentifier) + 1) * sizeof(CHAR);
+    }
+    for (i = 0; i < Count; ++i)
+    {
+        Size += IniGetSectionSettingNameSize(SectionId, i);  // Counts also the NULL-terminator, that we transform into the '=' sign separator.
+        Size += IniGetSectionSettingValueSize(SectionId, i); // Counts also the NULL-terminator.
+    }
+    Size += sizeof(ANSI_NULL); // Final NULL-terminator.
+
+    /* Allocate memory to hold the argument vector: pointers and string buffer */
+    Argv = FrLdrHeapAlloc(Argc * sizeof(PCHAR) + Size, TAG_STRING);
+    if (!Argv)
+        return NULL;
+
+    /* Initialize the argument vector: loop through the section and copy the key=value options */
+    SettingName = (PCHAR)((ULONG_PTR)Argv + (Argc * sizeof(PCHAR)));
+    Args = Argv;
+    /* i == 0: Program name */
+    *Args++ = NULL;
+    /* i == 1: LoadIdentifier */
+    if (LoadIdentifier)
+    {
+        strcpy(SettingName, "LoadIdentifier=");
+        strcat(SettingName, LoadIdentifier);
+
+        *Args++ = SettingName;
+        SettingName += (strlen(SettingName) + 1);
+    }
+    for (i = 0; i < Count; ++i)
+    {
+        Size = IniGetSectionSettingNameSize(SectionId, i);
+        SettingValue = SettingName + Size;
+        IniReadSettingByNumber(SectionId, i,
+                               SettingName, Size,
+                               SettingValue, IniGetSectionSettingValueSize(SectionId, i));
+        SettingName[Size - 1] = '=';
+
+        *Args++ = SettingName;
+        SettingName += (strlen(SettingName) + 1);
+    }
+
+#if DBG
+    /* Dump the argument vector for debugging */
+    for (i = 0; i < Argc; ++i)
+    {
+        TRACE("Argv[%lu]: '%s'\n", i, Argv[i]);
+    }
+#endif
+
+    *pArgc = Argc;
+    return Argv;
 }
 
-VOID RunLoader(VOID)
+VOID LoadOperatingSystem(IN OperatingSystemItem* OperatingSystem)
 {
-       CHAR    SettingValue[80];
-       CHAR BootType[80];
-       ULONG_PTR       SectionId;
-       ULONG           OperatingSystemCount;
-       OperatingSystemItem*    OperatingSystemList;
-       PCSTR   *OperatingSystemDisplayNames;
-       PCSTR SectionName;
-       ULONG   i;
-       ULONG           DefaultOperatingSystem;
-       LONG            TimeOut;
-       ULONG           SelectedOperatingSystem;
-
-       // FIXME: if possible, only detect and register ARC devices...
-       if (!MachHwDetect())
-       {
-               UiMessageBoxCritical("Error when detecting hardware");
-               return;
-       }
+    ULONG_PTR SectionId;
+    PCSTR SectionName = OperatingSystem->SectionName;
+    ULONG i;
+    ULONG Argc;
+    PCHAR* Argv;
+    CHAR BootType[80];
+
+    /* Try to open the operating system section in the .ini file */
+    if (!IniOpenSection(SectionName, &SectionId))
+    {
+        UiMessageBox("Section [%s] not found in freeldr.ini.", SectionName);
+        return;
+    }
+
+    /* Try to read the boot type */
+    *BootType = ANSI_NULL;
+    IniReadSettingByName(SectionId, "BootType", BootType, sizeof(BootType));
+
+    /* We must have the "BootType" value (it has been possibly added by InitOperatingSystemList()) */
+    ASSERT(*BootType);
 
 #ifdef _M_IX86
-       // Load additional SCSI driver (if any)
-       if (LoadBootDeviceDriver() != ESUCCESS)
-       {
-               UiMessageBoxCritical("Unable to load additional boot device driver");
-       }
+    /* Install the drive mapper according to this section drive mappings */
+    DriveMapMapDrivesInSection(SectionName);
 #endif
 
-       if (!IniFileInitialize())
-       {
-               UiMessageBoxCritical("Error initializing .ini file");
-               return;
-       }
-
-       if (!IniOpenSection("FreeLoader", &SectionId))
-       {
-               UiMessageBoxCritical("Section [FreeLoader] not found in freeldr.ini.");
-               return;
-       }
-       TimeOut = GetTimeOut();
-
-       if (!UiInitialize(TRUE))
-       {
-               UiMessageBoxCritical("Unable to initialize UI.");
-               return;
-       }
-
-       OperatingSystemList = InitOperatingSystemList(&OperatingSystemCount);
-       if (!OperatingSystemList)
-       {
-               UiMessageBox("Unable to read operating systems section in freeldr.ini.\nPress ENTER to reboot.");
-               goto reboot;
-       }
-
-       if (OperatingSystemCount == 0)
-       {
-               UiMessageBox("There were no operating systems listed in freeldr.ini.\nPress ENTER to reboot.");
-               goto reboot;
-       }
-
-       DefaultOperatingSystem = GetDefaultOperatingSystem(OperatingSystemList, OperatingSystemCount);
-
-       //
-       // Create list of display names
-       //
-       OperatingSystemDisplayNames = MmHeapAlloc(sizeof(PCSTR) * OperatingSystemCount);
-       if (!OperatingSystemDisplayNames)
-       {
-               goto reboot;
-       }
-       for (i = 0; i < OperatingSystemCount; i++)
-       {
-               OperatingSystemDisplayNames[i] = OperatingSystemList[i].LoadIdentifier;
-       }
-
-       //
-       // Find all the message box settings and run them
-       //
-       UiShowMessageBoxesInSection("FreeLoader");
-
-       for (;;)
-       {
-
-               // Redraw the backdrop
-               UiDrawBackdrop();
-
-               // Show the operating system list menu
-               if (!UiDisplayMenu("Please select the operating system to start:",
-                                  OperatingSystemDisplayNames,
-                                  OperatingSystemCount,
-                                  DefaultOperatingSystem,
-                                  TimeOut,
-                                  &SelectedOperatingSystem,
-                                  FALSE,
-                                  MainBootMenuKeyPressFilter))
-               {
-                       UiMessageBox("Press ENTER to reboot.");
-                       goto reboot;
-               }
-
-               TimeOut = -1;
-
-               // Try to open the operating system section in the .ini file
-               SettingValue[0] = ANSI_NULL;
-               SectionName = OperatingSystemList[SelectedOperatingSystem].SystemPartition;
-               if (IniOpenSection(SectionName, &SectionId))
-               {
-                       // Try to read the boot type
-                       IniReadSettingByName(SectionId, "BootType", BootType, sizeof(BootType));
-               }
-               else
-                       BootType[0] = ANSI_NULL;
-
-               if (BootType[0] == ANSI_NULL && SectionName[0] != ANSI_NULL)
-               {
-                       // Try to infere boot type value
-#ifdef _M_IX86
-                       ULONG FileId;
-                       if (ArcOpen((CHAR*)SectionName, OpenReadOnly, &FileId) == ESUCCESS)
-                       {
-                               ArcClose(FileId);
-                               strcpy(BootType, "BootSector");
-                       }
-                       else
-#endif
-                       {
-                               strcpy(BootType, "Windows");
-                       }
-               }
-
-               // Get OS setting value
-               IniOpenSection("Operating Systems", &SectionId);
-               IniReadSettingByName(SectionId, SectionName, SettingValue, sizeof(SettingValue));
-
-               // Install the drive mapper according to this sections drive mappings
-#if defined(_M_IX86) && !defined(_MSC_VER)
-               DriveMapMapDrivesInSection(SectionName);
+    /* Loop through the OS loading method table and find a suitable OS to boot */
+    for (i = 0; i < sizeof(OSLoadingMethods) / sizeof(OSLoadingMethods[0]); ++i)
+    {
+        if (_stricmp(BootType, OSLoadingMethods[i].BootType) == 0)
+        {
+            Argv = BuildArgvForOsLoader(OperatingSystem->LoadIdentifier, SectionId, &Argc);
+            if (Argv)
+            {
+                OSLoadingMethods[i].OsLoader(Argc, Argv, NULL);
+                FrLdrHeapFree(Argv, TAG_STRING);
+            }
+            return;
+        }
+    }
+}
+
+#ifdef HAS_OPTION_MENU_EDIT_CMDLINE
+
+VOID EditOperatingSystemEntry(IN OperatingSystemItem* OperatingSystem)
+{
+    ULONG_PTR SectionId;
+    PCSTR SectionName = OperatingSystem->SectionName;
+    ULONG i;
+    CHAR BootType[80];
+
+    /* Try to open the operating system section in the .ini file */
+    if (!IniOpenSection(SectionName, &SectionId))
+    {
+        UiMessageBox("Section [%s] not found in freeldr.ini.", SectionName);
+        return;
+    }
+
+    /* Try to read the boot type */
+    *BootType = ANSI_NULL;
+    IniReadSettingByName(SectionId, "BootType", BootType, sizeof(BootType));
+
+    /* We must have the "BootType" value (it has been possibly added by InitOperatingSystemList()) */
+    ASSERT(*BootType);
+
+    /* Loop through the OS loading method table and find a suitable OS entry editor */
+    for (i = 0; i < sizeof(OSLoadingMethods) / sizeof(OSLoadingMethods[0]); ++i)
+    {
+        if (_stricmp(BootType, OSLoadingMethods[i].BootType) == 0)
+        {
+            OSLoadingMethods[i].EditOsEntry(SectionId);
+            return;
+        }
+    }
+}
+
+#endif // HAS_OPTION_MENU_EDIT_CMDLINE
+
+LONG GetTimeOut(VOID)
+{
+    CHAR    TimeOutText[20];
+    LONG        TimeOut;
+    ULONG_PTR    SectionId;
+
+    TimeOut = CmdLineGetTimeOut();
+    if (TimeOut >= 0)
+        return TimeOut;
+
+    if (!IniOpenSection("FreeLoader", &SectionId))
+        return -1;
+
+    if (IniReadSettingByName(SectionId, "TimeOut", TimeOutText, sizeof(TimeOutText)))
+        TimeOut = atoi(TimeOutText);
+    else
+        TimeOut = -1;
+
+    return TimeOut;
+}
+
+BOOLEAN
+MainBootMenuKeyPressFilter(
+    IN ULONG KeyPress,
+    IN ULONG SelectedMenuItem,
+    IN PVOID Context OPTIONAL)
+{
+    switch (KeyPress)
+    {
+    case KEY_F8:
+        DoOptionsMenu(&((OperatingSystemItem*)Context)[SelectedMenuItem]);
+        return TRUE;
+
+#ifdef HAS_OPTION_MENU_EDIT_CMDLINE
+    case KEY_F10:
+        EditOperatingSystemEntry(&((OperatingSystemItem*)Context)[SelectedMenuItem]);
+        return TRUE;
 #endif
 
-#ifdef FREELDR_REACTOS_SETUP
-        // WinLdr-style boot
-        LoadReactOSSetup();
-#elif defined(_M_IX86)
-               if (_stricmp(BootType, "Windows") == 0)
-               {
-                       LoadAndBootWindows(SectionName, SettingValue, 0);
-               }
-               else if (_stricmp(BootType, "WindowsNT40") == 0)
-               {
-                       LoadAndBootWindows(SectionName, SettingValue, _WIN32_WINNT_NT4);
-               }
-               else if (_stricmp(BootType, "Windows2003") == 0)
-               {
-                       LoadAndBootWindows(SectionName, SettingValue, _WIN32_WINNT_WS03);
-               }
-               else if (_stricmp(BootType, "Linux") == 0)
-               {
-                       LoadAndBootLinux(SectionName, OperatingSystemDisplayNames[SelectedOperatingSystem]);
-               }
-               else if (_stricmp(BootType, "BootSector") == 0)
-               {
-                       LoadAndBootBootSector(SectionName);
-               }
-               else if (_stricmp(BootType, "Partition") == 0)
-               {
-                       LoadAndBootPartition(SectionName);
-               }
-               else if (_stricmp(BootType, "Drive") == 0)
-               {
-                       LoadAndBootDrive(SectionName);
-               }
-#else
-               LoadAndBootWindows(SectionName, SettingValue, _WIN32_WINNT_WS03);
+    default:
+        /* We didn't handle the key */
+        return FALSE;
+    }
+}
+
+VOID RunLoader(VOID)
+{
+    ULONG_PTR SectionId;
+    LONG      TimeOut;
+    ULONG     OperatingSystemCount;
+    OperatingSystemItem* OperatingSystemList;
+    PCSTR*    OperatingSystemDisplayNames;
+    ULONG     DefaultOperatingSystem;
+    ULONG     SelectedOperatingSystem;
+    ULONG     i;
+
+    if (!MachInitializeBootDevices())
+    {
+        UiMessageBoxCritical("Error when detecting hardware.");
+        return;
+    }
+
+#ifdef _M_IX86
+    /* Load additional SCSI driver (if any) */
+    if (LoadBootDeviceDriver() != ESUCCESS)
+    {
+        UiMessageBoxCritical("Unable to load additional boot device drivers.");
+    }
 #endif
-       }
 
-reboot:
-       UiUnInitialize("Rebooting...");
-       return;
+    if (!IniFileInitialize())
+    {
+        UiMessageBoxCritical("Error initializing .ini file.");
+        return;
+    }
+
+    /* Debugger main initialization */
+    DebugInit(TRUE);
+
+    if (!IniOpenSection("FreeLoader", &SectionId))
+    {
+        UiMessageBoxCritical("Section [FreeLoader] not found in freeldr.ini.");
+        return;
+    }
+
+    TimeOut = GetTimeOut();
+
+    /* UI main initialization */
+    if (!UiInitialize(TRUE))
+    {
+        UiMessageBoxCritical("Unable to initialize UI.");
+        return;
+    }
+
+    OperatingSystemList = InitOperatingSystemList(&OperatingSystemCount,
+                                                  &DefaultOperatingSystem);
+    if (!OperatingSystemList)
+    {
+        UiMessageBox("Unable to read operating systems section in freeldr.ini.\nPress ENTER to reboot.");
+        goto Reboot;
+    }
+    if (OperatingSystemCount == 0)
+    {
+        UiMessageBox("There were no operating systems listed in freeldr.ini.\nPress ENTER to reboot.");
+        goto Reboot;
+    }
+
+    /* Create list of display names */
+    OperatingSystemDisplayNames = FrLdrTempAlloc(sizeof(PCSTR) * OperatingSystemCount, 'mNSO');
+    if (!OperatingSystemDisplayNames)
+        goto Reboot;
+
+    for (i = 0; i < OperatingSystemCount; i++)
+    {
+        OperatingSystemDisplayNames[i] = OperatingSystemList[i].LoadIdentifier;
+    }
+
+    /* Find all the message box settings and run them */
+    UiShowMessageBoxesInSection("FreeLoader");
+
+    for (;;)
+    {
+        /* Redraw the backdrop */
+        UiDrawBackdrop();
+
+        /* Show the operating system list menu */
+        if (!UiDisplayMenu("Please select the operating system to start:",
+                           "For troubleshooting and advanced startup options for "
+                               "ReactOS, press F8.",
+                           TRUE,
+                           OperatingSystemDisplayNames,
+                           OperatingSystemCount,
+                           DefaultOperatingSystem,
+                           TimeOut,
+                           &SelectedOperatingSystem,
+                           FALSE,
+                           MainBootMenuKeyPressFilter,
+                           OperatingSystemList))
+        {
+            UiMessageBox("Press ENTER to reboot.");
+            goto Reboot;
+        }
+
+        TimeOut = -1;
+
+        /* Load the chosen operating system */
+        LoadOperatingSystem(&OperatingSystemList[SelectedOperatingSystem]);
+    }
+
+Reboot:
+    UiUnInitialize("Rebooting...");
+    IniCleanup();
+    return;
 }