[FREELDR] Limit the usage of DiskStopFloppyMotor() in hardware/platform-specific...
[reactos.git] / boot / freeldr / freeldr / bootmgr.c
index 5856128..bfb8d43 100644 (file)
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+/* INCLUDES *******************************************************************/
+
 #include <freeldr.h>
 
-ARC_DISK_SIGNATURE reactos_arc_disk_info[32]; // ARC Disk Information
-ULONG reactos_disk_count = 0;
-CHAR reactos_arc_strings[32][256];
+#include <debug.h>
+DBG_DEFAULT_CHANNEL(INIFILE);
+
+/* GLOBALS ********************************************************************/
 
 typedef
 VOID
-(*OS_LOADING_METHOD)(IN OperatingSystemItem* OperatingSystem,
-                     IN USHORT OperatingSystemVersion);
+(*EDIT_OS_ENTRY_PROC)(
+    IN OUT OperatingSystemItem* OperatingSystem);
 
-struct
+static VOID
+EditCustomBootReactOSSetup(
+    IN OUT OperatingSystemItem* OperatingSystem)
 {
-    CHAR BootType[80];
-    USHORT OperatingSystemVersion;
-    OS_LOADING_METHOD Load;
+    EditCustomBootReactOS(OperatingSystem, TRUE);
+}
+
+static VOID
+EditCustomBootNTOS(
+    IN OUT OperatingSystemItem* OperatingSystem)
+{
+    EditCustomBootReactOS(OperatingSystem, FALSE);
+}
+
+static const struct
+{
+    PCSTR BootType;
+    EDIT_OS_ENTRY_PROC EditOsEntry;
+    ARC_ENTRY_POINT OsLoader;
 } OSLoadingMethods[] =
 {
-#ifdef FREELDR_REACTOS_SETUP
-    {"ReactOSSetup", 0                , LoadReactOSSetup     },
-#endif
+    {"ReactOSSetup", EditCustomBootReactOSSetup, LoadReactOSSetup},
 
 #ifdef _M_IX86
-    {"BootSector"  , 0                , LoadAndBootBootSector},
-    {"Drive"       , 0                , LoadAndBootDrive     },
-    {"Partition"   , 0                , LoadAndBootPartition },
-
-    {"Linux"       , 0                , LoadAndBootLinux     },
+    {"Drive"       , EditCustomBootDisk      , LoadAndBootDrive     },
+    {"Partition"   , EditCustomBootPartition , LoadAndBootPartition },
+    {"BootSector"  , EditCustomBootSectorFile, LoadAndBootBootSector},
 
-    {"Windows"     , 0                , LoadAndBootWindows   },
-    {"WindowsNT40" , _WIN32_WINNT_NT4 , LoadAndBootWindows   },
+    {"Linux"       , EditCustomBootLinux, LoadAndBootLinux  },
+    {"WindowsNT40" , EditCustomBootNTOS , LoadAndBootWindows},
 #endif
-    {"Windows2003" , _WIN32_WINNT_WS03, LoadAndBootWindows   },
-
-//  {"Not found"   , 0                , NULL                 }
+    {"Windows"     , EditCustomBootNTOS , LoadAndBootWindows},
+    {"Windows2003" , EditCustomBootNTOS , LoadAndBootWindows},
 };
 
-VOID LoadOperatingSystem(IN OperatingSystemItem* OperatingSystem)
+/* FUNCTIONS ******************************************************************/
+
+/*
+ * This function converts the list of key=value options in the given operating
+ * system section into an ARC-compatible argument vector, providing in addition
+ * the extra mandatory Software Loading Environment Variables, following the
+ * ARC specification.
+ */
+PCHAR*
+BuildArgvForOsLoader(
+    IN PCSTR LoadIdentifier,
+    IN ULONG_PTR SectionId,
+    OUT PULONG pArgc)
 {
-    ULONG_PTR SectionId;
-    PCSTR SectionName = OperatingSystem->SystemPartition;
-    CHAR BootType[80];
+    SIZE_T Size;
+    ULONG Count;
     ULONG i;
+    ULONG Argc;
+    PCHAR* Argv;
+    PCHAR* Args;
+    PCHAR SettingName, SettingValue;
+
+    *pArgc = 0;
+
+    ASSERT(SectionId != 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 SystemPartition,
+     * the LoadIdentifier (optional), and the items in the OS section.
+     */
+    Argc = 2 + (LoadIdentifier ? 1 : 0) + Count;
+
+    /* Calculate the total size needed for the string buffer of the argument vector */
+    Size = 0;
+    /* i == 0: Program name */
+    /* i == 1: SystemPartition : from where FreeLdr has been started */
+    Size += (strlen("SystemPartition=") + strlen(FrldrBootPath) + 1) * sizeof(CHAR);
+    /* i == 2: LoadIdentifier  : ASCII string that may be used to associate an identifier with a set of load parameters */
+    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: SystemPartition */
+    {
+        strcpy(SettingName, "SystemPartition=");
+        strcat(SettingName, FrldrBootPath);
 
-    // Try to open the operating system section in the .ini file
-    if (IniOpenSection(SectionName, &SectionId))
+        *Args++ = SettingName;
+        SettingName += (strlen(SettingName) + 1);
+    }
+    /* i == 2: LoadIdentifier */
+    if (LoadIdentifier)
     {
-        // Try to read the boot type
-        IniReadSettingByName(SectionId, "BootType", BootType, sizeof(BootType));
+        strcpy(SettingName, "LoadIdentifier=");
+        strcat(SettingName, LoadIdentifier);
+
+        *Args++ = SettingName;
+        SettingName += (strlen(SettingName) + 1);
     }
-    else
+    for (i = 0; i < Count; ++i)
     {
-        BootType[0] = ANSI_NULL;
+        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 (BootType[0] == ANSI_NULL && SectionName[0] != ANSI_NULL)
+#if DBG
+    /* Dump the argument vector for debugging */
+    for (i = 0; i < Argc; ++i)
     {
-        // Try to infere the boot type value
-#ifdef _M_IX86
-        ULONG FileId;
-        if (ArcOpen((PSTR)SectionName, OpenReadOnly, &FileId) == ESUCCESS)
-        {
-            ArcClose(FileId);
-            strcpy(BootType, "BootSector");
-        }
-        else
-#endif
-        {
-            strcpy(BootType, "Windows");
-        }
+        TRACE("Argv[%lu]: '%s'\n", i, Argv[i]);
     }
+#endif
+
+    *pArgc = Argc;
+    return Argv;
+}
+
+VOID LoadOperatingSystem(IN OperatingSystemItem* OperatingSystem)
+{
+    ULONG_PTR SectionId = OperatingSystem->SectionId;
+    ULONG i;
+    ULONG Argc;
+    PCHAR* Argv;
+    CHAR BootType[80];
+
+    /* The operating system section has been opened by InitOperatingSystemList() */
+    ASSERT(SectionId != 0);
+
+    /* 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);
 
-    // Install the drive mapper according to this section drive mappings
-#if defined(_M_IX86) && !defined(_MSC_VER)
-    DriveMapMapDrivesInSection(SectionName);
+#ifdef _M_IX86
+    /* Install the drive mapper according to this section drive mappings */
+    DriveMapMapDrivesInSection(SectionId);
 #endif
 
-    // Loop through the OS loading method table and find a suitable OS to boot
+    /* 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)
         {
-            OSLoadingMethods[i].Load(OperatingSystem,
-                                     OSLoadingMethods[i].OperatingSystemVersion);
+            Argv = BuildArgvForOsLoader(OperatingSystem->LoadIdentifier, SectionId, &Argc);
+            if (Argv)
+            {
+                OSLoadingMethods[i].OsLoader(Argc, Argv, NULL);
+                FrLdrHeapFree(Argv, TAG_STRING);
+            }
             return;
         }
     }
 }
 
-ULONG GetDefaultOperatingSystem(OperatingSystemItem* OperatingSystemList, ULONG OperatingSystemCount)
+#ifdef HAS_OPTION_MENU_EDIT_CMDLINE
+
+VOID EditOperatingSystemEntry(IN OperatingSystemItem* OperatingSystem)
 {
-    CHAR    DefaultOSText[80];
-    PCSTR    DefaultOSName;
-    ULONG_PTR    SectionId;
-    ULONG    DefaultOS = 0;
-    ULONG    Idx;
+    ULONG_PTR SectionId = OperatingSystem->SectionId;
+    ULONG i;
+    CHAR BootType[80];
 
-    if (!IniOpenSection("FreeLoader", &SectionId))
-    {
-        return 0;
-    }
+    /* The operating system section has been opened by InitOperatingSystemList() */
+    ASSERT(SectionId != 0);
 
-    DefaultOSName = CmdLineGetDefaultOS();
-    if (NULL == DefaultOSName)
-    {
-        if (IniReadSettingByName(SectionId, "DefaultOS", DefaultOSText, sizeof(DefaultOSText)))
-        {
-            DefaultOSName = DefaultOSText;
-        }
-    }
+    /* 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);
 
-    if (NULL != DefaultOSName)
+    /* Loop through the OS loading method table and find a suitable OS entry editor */
+    for (i = 0; i < sizeof(OSLoadingMethods) / sizeof(OSLoadingMethods[0]); ++i)
     {
-        for (Idx=0; Idx<OperatingSystemCount; Idx++)
+        if (_stricmp(BootType, OSLoadingMethods[i].BootType) == 0)
         {
-            if (_stricmp(DefaultOSName, OperatingSystemList[Idx].SystemPartition) == 0)
-            {
-                DefaultOS = Idx;
-                break;
-            }
+            OSLoadingMethods[i].EditOsEntry(OperatingSystem);
+            return;
         }
     }
-
-    return DefaultOS;
 }
 
-LONG GetTimeOut(VOID)
+#endif // HAS_OPTION_MENU_EDIT_CMDLINE
+
+static LONG
+GetTimeOut(
+    IN ULONG_PTR FrLdrSectionId)
 {
-    CHAR    TimeOutText[20];
-    LONG        TimeOut;
-    ULONG_PTR    SectionId;
+    LONG TimeOut = -1;
+    CHAR TimeOutText[20];
 
     TimeOut = CmdLineGetTimeOut();
-    if (0 <= TimeOut)
-    {
+    if (TimeOut >= 0)
         return TimeOut;
-    }
 
-    if (!IniOpenSection("FreeLoader", &SectionId))
-    {
-        return -1;
-    }
+    TimeOut = -1;
 
-    if (IniReadSettingByName(SectionId, "TimeOut", TimeOutText, sizeof(TimeOutText)))
+    if ((FrLdrSectionId != 0) &&
+        IniReadSettingByName(FrLdrSectionId, "TimeOut", TimeOutText, sizeof(TimeOutText)))
     {
         TimeOut = atoi(TimeOutText);
     }
-    else
-    {
-        TimeOut = -1;
-    }
 
     return TimeOut;
 }
 
-BOOLEAN MainBootMenuKeyPressFilter(ULONG KeyPress)
+BOOLEAN
+MainBootMenuKeyPressFilter(
+    IN ULONG KeyPress,
+    IN ULONG SelectedMenuItem,
+    IN PVOID Context OPTIONAL)
 {
-    if (KeyPress == KEY_F8)
+    switch (KeyPress)
     {
-        DoOptionsMenu();
+    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
 
-    // We didn't handle the key
-    return FALSE;
+    default:
+        /* We didn't handle the key */
+        return FALSE;
+    }
 }
 
 VOID RunLoader(VOID)
 {
-    ULONG_PTR    SectionId;
-    ULONG        OperatingSystemCount;
-    OperatingSystemItem*    OperatingSystemList;
-    PCSTR*        OperatingSystemDisplayNames;
-    ULONG        DefaultOperatingSystem;
-    LONG        TimeOut;
-    ULONG        SelectedOperatingSystem;
-    ULONG    i;
+    ULONG_PTR SectionId;
+    LONG      TimeOut;
+    ULONG     OperatingSystemCount;
+    OperatingSystemItem* OperatingSystemList;
+    PCSTR*    OperatingSystemDisplayNames;
+    ULONG     DefaultOperatingSystem;
+    ULONG     SelectedOperatingSystem;
+    ULONG     i;
 
     if (!MachInitializeBootDevices())
     {
-        UiMessageBoxCritical("Error when detecting hardware");
+        UiMessageBoxCritical("Error when detecting hardware.");
         return;
     }
 
 #ifdef _M_IX86
-    // Load additional SCSI driver (if any)
+    /* Load additional SCSI driver (if any) */
     if (LoadBootDeviceDriver() != ESUCCESS)
     {
-        UiMessageBoxCritical("Unable to load additional boot device driver");
+        UiMessageBoxCritical("Unable to load additional boot device drivers.");
     }
 #endif
 
     if (!IniFileInitialize())
     {
-        UiMessageBoxCritical("Error initializing .ini file");
+        UiMessageBoxCritical("Error initializing .ini file.");
         return;
     }
 
+    /* Open the [FreeLoader] section */
     if (!IniOpenSection("FreeLoader", &SectionId))
     {
         UiMessageBoxCritical("Section [FreeLoader] not found in freeldr.ini.");
         return;
     }
-    TimeOut = GetTimeOut();
 
+    /* Debugger main initialization */
+    DebugInit(SectionId);
+
+    /* Retrieve the default timeout */
+    TimeOut = GetTimeOut(SectionId);
+
+    /* UI main initialization */
     if (!UiInitialize(TRUE))
     {
         UiMessageBoxCritical("Unable to initialize UI.");
         return;
     }
 
-    OperatingSystemList = InitOperatingSystemList(&OperatingSystemCount);
+    OperatingSystemList = InitOperatingSystemList(SectionId,
+                                                  &OperatingSystemCount,
+                                                  &DefaultOperatingSystem);
     if (!OperatingSystemList)
     {
         UiMessageBox("Unable to read operating systems section in freeldr.ini.\nPress ENTER to reboot.");
-        goto reboot;
+        goto Reboot;
     }
-
     if (OperatingSystemCount == 0)
     {
         UiMessageBox("There were no operating systems listed in freeldr.ini.\nPress ENTER to reboot.");
-        goto reboot;
+        goto Reboot;
     }
 
-    DefaultOperatingSystem = GetDefaultOperatingSystem(OperatingSystemList, OperatingSystemCount);
-
-    //
-    // Create list of display names
-    //
+    /* Create list of display names */
     OperatingSystemDisplayNames = FrLdrTempAlloc(sizeof(PCSTR) * OperatingSystemCount, 'mNSO');
     if (!OperatingSystemDisplayNames)
-    {
-        goto reboot;
-    }
+        goto Reboot;
+
     for (i = 0; i < OperatingSystemCount; i++)
     {
         OperatingSystemDisplayNames[i] = OperatingSystemList[i].LoadIdentifier;
     }
 
-    //
-    // Find all the message box settings and run them
-    //
-    UiShowMessageBoxesInSection("FreeLoader");
+    /* Find all the message box settings and run them */
+    UiShowMessageBoxesInSection(SectionId);
 
     for (;;)
     {
-        // Redraw the backdrop
+        /* Redraw the backdrop */
         UiDrawBackdrop();
 
-        // Show the operating system list menu
+        /* 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.",
@@ -278,19 +384,21 @@ VOID RunLoader(VOID)
                            TimeOut,
                            &SelectedOperatingSystem,
                            FALSE,
-                           MainBootMenuKeyPressFilter))
+                           MainBootMenuKeyPressFilter,
+                           OperatingSystemList))
         {
             UiMessageBox("Press ENTER to reboot.");
-            goto reboot;
+            goto Reboot;
         }
 
         TimeOut = -1;
 
-        // Load the chosen operating system
+        /* Load the chosen operating system */
         LoadOperatingSystem(&OperatingSystemList[SelectedOperatingSystem]);
     }
 
-reboot:
+Reboot:
     UiUnInitialize("Rebooting...");
+    IniCleanup();
     return;
 }