Added new "csr_rotate" option for "showtools", and matching
[refind] / refind / apple.c
1 /*
2 * refind/apple.c
3 * Functions specific to Apple computers
4 *
5 * Copyright (c) 2015 Roderick W. Smith
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include "global.h"
23 #include "config.h"
24 #include "lib.h"
25 #include "screen.h"
26 #include "apple.h"
27 #include "refit_call_wrapper.h"
28
29 CHAR16 *gCsrStatus = NULL;
30
31 // Get CSR (Apple's System Integrity Protection [SIP], or "rootless") status
32 // byte. Return values:
33 // -2 = Call succeeded but returned value of unexpected length, so result
34 // is suspect
35 // -1 = Call failed; likely not an Apple, or an Apple running OS X version
36 // that doesn't support CSR/SIP
37 // 0-127 = Valid values (as of 11/2015)
38 // 128-255 = High bit set unexpectedly, but value still returned
39 INTN GetCsrStatus(VOID) {
40 CHAR8 *CsrValues;
41 UINTN CsrLength;
42 EFI_GUID CsrGuid = CSR_GUID;
43 EFI_STATUS Status;
44
45 Status = EfivarGetRaw(&CsrGuid, L"csr-active-config", &CsrValues, &CsrLength);
46 if (Status == EFI_SUCCESS) {
47 if (CsrLength == 4) {
48 return CsrValues[0];
49 } else {
50 return -2;
51 }
52 } else {
53 return -1;
54 }
55 } // INTN GetCsrStatus()
56
57 // Store string describing CSR status byte in gCsrStatus variable, which appears
58 // on the Info page.
59 VOID RecordgCsrStatus(INTN CsrStatus) {
60 if (gCsrStatus == NULL)
61 gCsrStatus = AllocateZeroPool(256 * sizeof(CHAR16));
62
63 switch (CsrStatus) {
64 case -2:
65 SPrint(gCsrStatus, 255, L" System Integrity Protection status is unrecognized");
66 break;
67 case -1:
68 SPrint(gCsrStatus, 255, L"System Integrity Protection status is unrecorded");
69 break;
70 case SIP_ENABLED:
71 SPrint(gCsrStatus, 255, L" System Integrity Protection is enabled (0x%02x)", CsrStatus);
72 break;
73 case SIP_DISABLED:
74 SPrint(gCsrStatus, 255, L" System Integrity Protection is disabled (0x%02x)", CsrStatus);
75 break;
76 default:
77 SPrint(gCsrStatus, 255, L" System Integrity Protection status: 0x%02x", CsrStatus);
78 } // switch
79 } // VOID RecordgCsrStatus
80
81 // Find the current CSR status and reset it to the next one in the
82 // GlobalConfig.CsrValues list, or to the first value if the current
83 // value is not on the list.
84 // Returns the value to which the CSR is being set.
85 INTN RotateCsrValue(VOID) {
86 INTN CurrentValue;
87 UINTN Index = 0;
88 CHAR16 *CurrentValueAsString = NULL;
89 CHAR16 *TargetValueAsString = NULL;
90 CHAR16 *ListItem;
91 CHAR8 TargetCsr[4];
92 EFI_GUID CsrGuid = CSR_GUID;
93 EFI_STATUS Status;
94 EG_PIXEL BGColor;
95
96 BGColor.b = 255;
97 BGColor.g = 175;
98 BGColor.r = 100;
99 BGColor.a = 0;
100 CurrentValue = GetCsrStatus();
101 if ((CurrentValue >= 0) && GlobalConfig.CsrValues) {
102 CurrentValueAsString = PoolPrint(L"%02x", CurrentValue);
103 while (TargetValueAsString == NULL) {
104 ListItem = FindCommaDelimited(GlobalConfig.CsrValues, Index++);
105 if (ListItem) {
106 if (MyStriCmp(ListItem, CurrentValueAsString)) {
107 TargetValueAsString = FindCommaDelimited(GlobalConfig.CsrValues, Index);
108 if (TargetValueAsString == NULL)
109 TargetValueAsString = FindCommaDelimited(GlobalConfig.CsrValues, 0);
110 }
111 } else {
112 TargetValueAsString = FindCommaDelimited(GlobalConfig.CsrValues, 0);
113 } // if/else
114 MyFreePool(ListItem);
115 } // while
116 TargetCsr[0] = (CHAR8) StrToHex(TargetValueAsString, 0, 2);
117 Status = EfivarSetRaw(&CsrGuid, L"csr-active-config", TargetCsr, 4, TRUE);
118 if (Status == EFI_SUCCESS) {
119 switch (TargetCsr[0]) {
120 case SIP_ENABLED:
121 egDisplayMessage(PoolPrint(L"Set System Integrity Protection to enabled (0x%x)", (UINTN) TargetCsr[0]), &BGColor);
122 break;
123 case SIP_DISABLED:
124 egDisplayMessage(PoolPrint(L"Set System Integrity Protection status to disabled (0x%x)", (UINTN) TargetCsr[0]), &BGColor);
125 break;
126 default:
127 egDisplayMessage(PoolPrint(L"Set System Integrity Protection status to 0x%x", (UINTN) TargetCsr[0]), &BGColor);
128 }
129 RecordgCsrStatus((INTN) TargetCsr[0]);
130 } else {
131 egDisplayMessage(L"Error setting System Integrity Protection status", &BGColor);
132 }
133 PauseSeconds(3);
134 } // if
135 return (INTN) TargetCsr[0];
136 } // INTN RotateCsrValue()
137
138
139 /*
140 * The below definitions and SetAppleOSInfo() function are based on a GRUB patch
141 * by Andreas Heider:
142 * https://lists.gnu.org/archive/html/grub-devel/2013-12/msg00442.html
143 */
144
145 #define EFI_APPLE_SET_OS_PROTOCOL_GUID \
146 { 0xc5c5da95, 0x7d5c, 0x45e6, \
147 { 0xb2, 0xf1, 0x3f, 0xd5, 0x2b, 0xb1, 0x00, 0x77 } \
148 }
149
150 typedef struct EfiAppleSetOsInterface {
151 UINT64 Version;
152 EFI_STATUS EFIAPI (*SetOsVersion) (IN CHAR8 *Version);
153 EFI_STATUS EFIAPI (*SetOsVendor) (IN CHAR8 *Vendor);
154 } EfiAppleSetOsInterface;
155
156 // Function to tell the firmware that OS X is being launched. This is
157 // required to work around problems on some Macs that don't fully
158 // initialize some hardware (especially video displays) when third-party
159 // OSes are launched in EFI mode.
160 EFI_STATUS SetAppleOSInfo() {
161 CHAR16 *AppleOSVersion = NULL;
162 CHAR8 *AppleOSVersion8 = NULL;
163 EFI_STATUS Status;
164 EFI_GUID apple_set_os_guid = EFI_APPLE_SET_OS_PROTOCOL_GUID;
165 EfiAppleSetOsInterface *SetOs = NULL;
166
167 Status = refit_call3_wrapper(BS->LocateProtocol, &apple_set_os_guid, NULL, (VOID**) &SetOs);
168
169 // Not a Mac, so ignore the call....
170 if ((Status != EFI_SUCCESS) || (!SetOs))
171 return EFI_SUCCESS;
172
173 if ((SetOs->Version != 0) && GlobalConfig.SpoofOSXVersion) {
174 AppleOSVersion = StrDuplicate(L"Mac OS X");
175 MergeStrings(&AppleOSVersion, GlobalConfig.SpoofOSXVersion, ' ');
176 if (AppleOSVersion) {
177 AppleOSVersion8 = AllocateZeroPool((StrLen(AppleOSVersion) + 1) * sizeof(CHAR8));
178 UnicodeStrToAsciiStr(AppleOSVersion, AppleOSVersion8);
179 if (AppleOSVersion8) {
180 Status = refit_call1_wrapper (SetOs->SetOsVersion, AppleOSVersion8);
181 if (!EFI_ERROR(Status))
182 Status = EFI_SUCCESS;
183 MyFreePool(AppleOSVersion8);
184 } else {
185 Status = EFI_OUT_OF_RESOURCES;
186 Print(L"Out of resources in SetAppleOSInfo!\n");
187 }
188 if ((Status == EFI_SUCCESS) && (SetOs->Version == 2))
189 Status = refit_call1_wrapper (SetOs->SetOsVendor, "Apple Inc.");
190 MyFreePool(AppleOSVersion);
191 } // if (AppleOSVersion)
192 } // if
193 if (Status != EFI_SUCCESS)
194 Print(L"Unable to set firmware boot type!\n");
195
196 return (Status);
197 } // EFI_STATUS SetAppleOSInfo()