]> code.delx.au - refind/blobdiff - refind/main.c
Preliminary OS spoofing code for Macs.
[refind] / refind / main.c
index 9e2ee29735d2d5e751c2795d48d7de6ee59c2c6a..2c39bae099836919e781eb5aa5f62d5475c684dd 100644 (file)
  * Modifications copyright (c) 2012-2015 Roderick W. Smith
  *
  * Modifications distributed under the terms of the GNU General Public
- * License (GPL) version 3 (GPLv3), a copy of which must be distributed
- * with this source code or binaries made from it.
+ * License (GPL) version 3 (GPLv3), or (at your option) any later version.
  *
  */
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
 
 #include "global.h"
 #include "config.h"
@@ -143,11 +156,14 @@ REFIT_MENU_SCREEN MainMenu       = { L"Main Menu", NULL, 0, NULL, 0, NULL, 0, L"
                                      L"Insert or F2 for more options; Esc to refresh" };
 static REFIT_MENU_SCREEN AboutMenu      = { L"About", NULL, 0, NULL, 0, NULL, 0, NULL, L"Press Enter to return to main menu", L"" };
 
-REFIT_CONFIG GlobalConfig = { FALSE, TRUE, FALSE, FALSE, TRUE,  0, 0, 0, DONT_CHANGE_TEXT_MODE, 20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC,
-                              0, 0, { DEFAULT_BIG_ICON_SIZE / 4, DEFAULT_SMALL_ICON_SIZE, DEFAULT_BIG_ICON_SIZE }, BANNER_NOSCALE,
-                              NULL, NULL, NULL, CONFIG_FILE_NAME, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              { TAG_SHELL, TAG_MEMTEST, TAG_GDISK, TAG_APPLE_RECOVERY, TAG_WINDOWS_RECOVERY, TAG_MOK_TOOL,
-                                TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, TAG_FIRMWARE, 0, 0, 0, 0, 0, 0, 0, 0 }
+REFIT_CONFIG GlobalConfig = { FALSE, TRUE, FALSE, FALSE, TRUE, 0, 0, 0, DONT_CHANGE_TEXT_MODE,
+                              20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC,
+                              0, 0, { DEFAULT_BIG_ICON_SIZE / 4, DEFAULT_SMALL_ICON_SIZE, DEFAULT_BIG_ICON_SIZE },
+                              BANNER_NOSCALE, NULL, NULL, NULL, NULL, CONFIG_FILE_NAME, NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                              { TAG_SHELL, TAG_MEMTEST, TAG_GDISK, TAG_APPLE_RECOVERY, TAG_WINDOWS_RECOVERY,
+                                TAG_MOK_TOOL, TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, TAG_FIRMWARE,
+                                0, 0, 0, 0, 0, 0, 0, 0 }
                             };
 
 EFI_GUID GlobalGuid = EFI_GLOBAL_VARIABLE;
@@ -173,7 +189,7 @@ static VOID AboutrEFInd(VOID)
 
     if (AboutMenu.EntryCount == 0) {
         AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
-        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.9.1");
+        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.9.2.2");
         AddMenuInfoLine(&AboutMenu, L"");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2015 Roderick W. Smith");
@@ -276,7 +292,7 @@ EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
                              IN BOOLEAN IsDriver)
 {
     EFI_STATUS              Status, ReturnStatus;
-    EFI_HANDLE              ChildImageHandle;
+    EFI_HANDLE              ChildImageHandle, ChildImageHandle2;
     EFI_LOADED_IMAGE        *ChildLoadedImage = NULL;
     REFIT_VOLUME            *Volume = NULL;
     UINTN                   DevicePathIndex;
@@ -300,7 +316,7 @@ EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
     if (Verbose)
     Print(L"Starting %s\nUsing load options '%s'\n", ImageTitle, FullLoadOptions ? FullLoadOptions : L"");
 
-    // load the image into memory (and execute it, in the case of a shim/MOK image).
+    // load the image into memory
     ReturnStatus = Status = EFI_NOT_FOUND;  // in case the list is empty
     for (DevicePathIndex = 0; DevicePaths[DevicePathIndex] != NULL; DevicePathIndex++) {
         FindVolumeAndFilename(DevicePaths[DevicePathIndex], &Volume, &Filename);
@@ -325,6 +341,22 @@ EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
             //                                            ImageData, ImageSize, &ChildImageHandle);
             ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
                                                         NULL, 0, &ChildImageHandle);
+            if (secure_mode() && ShimLoaded()) {
+                // Load ourself into memory. This is a trick to work around a bug in Shim 0.8,
+                // which ties itself into the BS->LoadImage() and BS->StartImage() functions and
+                // then unregisters itself from the EFI system table when its replacement
+                // StartImage() function is called *IF* the previous LoadImage() was for the same
+                // program. The result is that rEFInd can validate only the first program it
+                // launches (often a filesystem driver). Loading a second program (rEFInd itself,
+                // here, to keep it smaller than a kernel) works around this problem. See the
+                // replacements.c file in Shim, and especially its start_image() function, for
+                // the source of the problem.
+                // NOTE: This doesn't check the return status or handle errors. It could
+                // conceivably do weird things if, say, rEFInd were on a USB drive that the
+                // user pulls before launching a program.
+                refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, GlobalConfig.SelfDevicePath,
+                                    NULL, 0, &ChildImageHandle2);
+            }
         } else {
             Print(L"Invalid loader file!\n");
             ReturnStatus = EFI_LOAD_ERROR;
@@ -2088,20 +2120,19 @@ static BOOLEAN SecureBootUninstall(VOID) {
 // If an error is encountered, leaves the value alone (it should be set to
 // CONFIG_FILE_NAME when GlobalConfig is initialized).
 static VOID SetConfigFilename(EFI_HANDLE ImageHandle) {
-EFI_LOADED_IMAGE *Info;
-CHAR16 *Options, *FileName;
-EFI_STATUS Status;
-INTN Where;
+    EFI_LOADED_IMAGE *Info;
+    CHAR16 *Options, *FileName, *SubString;
+    EFI_STATUS Status;
 
-Status = refit_call3_wrapper(BS->HandleProtocol, ImageHandle, &LoadedImageProtocol, (VOID **) &Info);
+    Status = refit_call3_wrapper(BS->HandleProtocol, ImageHandle, &LoadedImageProtocol, (VOID **) &Info);
     if ((Status == EFI_SUCCESS) && (Info->LoadOptionsSize > 0)) {
         Options = (CHAR16 *) Info->LoadOptions;
-        Where = FindSubString(L" -c ", Options);
-        if (Where >= 0) {
-            FileName = StrDuplicate(&Options[Where + 4]);
-            Where = FindSubString(L" ", FileName);
-            if (Where > 0)
-                FileName[Where] = L'\0';
+        SubString = MyStrStr(Options, L" -c ");
+        if (SubString) {
+            FileName = StrDuplicate(&SubString[4]);
+            if (FileName) {
+                LimitStringLength(FileName, 256);
+            }
 
             if (FileExists(SelfDir, FileName)) {
                 GlobalConfig.ConfigFilename = FileName;
@@ -2113,6 +2144,75 @@ Status = refit_call3_wrapper(BS->HandleProtocol, ImageHandle, &LoadedImageProtoc
     } // if
 } // VOID SetConfigFilename()
 
+
+#define EFI_APPLE_SET_OS_PROTOCOL_GUID  \
+  { 0xc5c5da95, 0x7d5c, 0x45e6, \
+    { 0xb2, 0xf1, 0x3f, 0xd5, 0x2b, 0xb1, 0x00, 0x77 } \
+  }
+
+typedef struct efi_apple_set_os_interface {
+    UINT64 version;
+    EFI_STATUS EFIAPI (*set_os_version) (IN CHAR8 *version);
+    EFI_STATUS EFIAPI (*set_os_vendor) (IN CHAR8 *vendor);
+} efi_apple_set_os_interface;
+
+// Function to tell the firmware that OS X is being launched. This is
+// required to work around problems on some Macs that don't fully
+// initialize some hardware (especially video displays) when third-party
+// OSes are launched in EFI mode.
+static EFI_STATUS SetAppleOSInfo() {
+//    CHAR8 apple_os_version[] = "Mac OS X 10.9";
+    CHAR16 *AppleOSVersion = NULL;
+    CHAR8 *AppleOSVersion8 = NULL;
+//    CHAR8 apple_os_vendor[]  = "Apple Inc.";
+    EFI_STATUS Status;
+    EFI_GUID apple_set_os_guid = EFI_APPLE_SET_OS_PROTOCOL_GUID;
+    efi_apple_set_os_interface *set_os = NULL;
+
+    Status = refit_call3_wrapper(BS->LocateProtocol, &apple_set_os_guid, NULL, (VOID**) &set_os);
+
+    // Not a Mac, so ignore the call....
+    if ((Status != EFI_SUCCESS) || (!set_os)) {
+        Print(L"Not a Mac!\n");
+        PauseForKey();
+        return EFI_SUCCESS;
+    }
+
+    if ((set_os->version != 0) && GlobalConfig.SpoofOSXVersion) {
+        AppleOSVersion = StrDuplicate(L"Mac OS X");
+        MergeStrings(&AppleOSVersion, GlobalConfig.SpoofOSXVersion, ' ');
+        Print(L"Setting OS version to '%s'\n", AppleOSVersion);
+        AppleOSVersion8 = AllocateZeroPool((StrLen(AppleOSVersion) + 1) * sizeof(CHAR8));
+        UnicodeStrToAsciiStr(AppleOSVersion, AppleOSVersion8);
+        if (AppleOSVersion8) {
+            Print(L"Calling set_os_version()\n");
+            Status = refit_call1_wrapper (set_os->set_os_version, AppleOSVersion8);
+            Print(L"Returned %lx\n", Status);
+            if (EFI_ERROR(Status))
+                Print(L"ERROR! Returned %x\n", Status);
+        } else {
+            Status = EFI_OUT_OF_RESOURCES;
+            Print(L"Out of resources!\n");
+        }
+    }
+
+    if (/* (Status == EFI_SUCCESS) && */ (set_os->version == 2)) {
+        Print(L"Setting OS vendor....");
+        Status = refit_call1_wrapper (set_os->set_os_vendor, "Apple Inc.");
+        Print(L"Returned %x\n", Status);
+    }
+
+    if (Status != EFI_SUCCESS) {
+        Print(L"Unable to set firmware boot type!\n");
+    }
+
+    MyFreePool(AppleOSVersion);
+    MyFreePool(AppleOSVersion8);
+    Print(L"Returning %x\n", Status);
+    PauseForKey();
+    return (Status);
+} // EFI_STATUS SetAppleOSInfo()
+
 //
 // main entry point
 //
@@ -2142,6 +2242,10 @@ efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
     SetConfigFilename(ImageHandle);
     ReadConfig(GlobalConfig.ConfigFilename);
 
+    if (GlobalConfig.SpoofOSXVersion) {
+        SetAppleOSInfo();
+    }
+
     InitScreen();
     WarnIfLegacyProblems();
     MainMenu.TimeoutSeconds = GlobalConfig.Timeout;