]> code.delx.au - refind/blob - gptsync/os_efi.c
Work around keyboard input problem in gptsync on some Macs. Minor
[refind] / gptsync / os_efi.c
1 /*
2 * gptsync/os_efi.c
3 * EFI glue for gptsync
4 *
5 * Copyright (c) 2006 Christoph Pfisterer
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37 #include "gptsync.h"
38 #include "refit_call_wrapper.h"
39 #ifdef __MAKEWITH_TIANO
40 //#include "tiano_includes.h"
41 #include "AutoGen.h"
42 #endif
43
44 // variables
45
46 EFI_BLOCK_IO *BlockIO = NULL;
47
48 //
49 // sector I/O functions
50 //
51
52 // Returns size of disk in blocks
53 UINT64 disk_size(VOID) {
54 return (UINT64) (BlockIO->Media->LastBlock + 1);
55 } // UINT64 disk_size()
56
57 UINTN read_sector(UINT64 lba, UINT8 *buffer)
58 {
59 EFI_STATUS Status;
60
61 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, lba, 512, buffer);
62 if (EFI_ERROR(Status)) {
63 // TODO: report error
64 return 1;
65 }
66 return 0;
67 }
68
69 UINTN write_sector(UINT64 lba, UINT8 *buffer)
70 {
71 EFI_STATUS Status;
72
73 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, lba, 512, buffer);
74 if (EFI_ERROR(Status)) {
75 // TODO: report error
76 return 1;
77 }
78 return 0;
79 }
80
81 //
82 // Keyboard input
83 //
84
85 static BOOLEAN ReadAllKeyStrokes(VOID)
86 {
87 EFI_STATUS Status;
88 BOOLEAN GotKeyStrokes;
89 EFI_INPUT_KEY Key;
90
91 GotKeyStrokes = FALSE;
92 for (;;) {
93 Status = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &Key);
94 if (Status == EFI_SUCCESS) {
95 GotKeyStrokes = TRUE;
96 continue;
97 }
98 break;
99 }
100 return GotKeyStrokes;
101 }
102
103 static VOID PauseForKey(VOID)
104 {
105 UINTN Index;
106
107 Print(L"\n* Hit any key to continue *");
108
109 if (ReadAllKeyStrokes()) { // remove buffered key strokes
110 refit_call1_wrapper(BS->Stall, 5000000); // 5 seconds delay
111 ReadAllKeyStrokes(); // empty the buffer again
112 }
113
114 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &Index);
115 ReadAllKeyStrokes(); // empty the buffer to protect the menu
116
117 Print(L"\n");
118 }
119
120 UINTN input_boolean(CHARN *prompt, BOOLEAN *bool_out)
121 {
122 EFI_STATUS Status;
123 UINTN Index;
124 EFI_INPUT_KEY Key;
125
126 Print(prompt);
127
128 ReadAllKeyStrokes(); // Remove buffered key strokes
129 do {
130 refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &Index);
131 Status = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &Key);
132 if (EFI_ERROR(Status) && Status != EFI_NOT_READY)
133 return 1;
134 } while (Status == EFI_NOT_READY);
135
136 if (Key.UnicodeChar == 'y' || Key.UnicodeChar == 'Y') {
137 Print(L"Yes\n");
138 *bool_out = TRUE;
139 } else {
140 Print(L"No\n");
141 *bool_out = FALSE;
142 }
143
144 ReadAllKeyStrokes();
145 return 0;
146 }
147
148 #ifdef __MAKEWITH_TIANO
149
150 // EFI_GUID gEfiDxeServicesTableGuid = { 0x05AD34BA, 0x6F02, 0x4214, { 0x95, 0x2E, 0x4D, 0xA0, 0x39, 0x8E, 0x2B, 0xB9 }};
151
152 // Minimal initialization function
153 static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
154 gST = SystemTable;
155 // gImageHandle = ImageHandle;
156 gBS = SystemTable->BootServices;
157 // gRS = SystemTable->RuntimeServices;
158 gRT = SystemTable->RuntimeServices; // Some BDS functions need gRT to be set
159
160 // InitializeConsoleSim();
161 }
162
163 // EFI_GUID gEfiBlockIoProtocolGuid = { 0x964E5B21, 0x6459, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }};
164
165 #define LibLocateHandle gBS->LocateHandleBuffer
166 #define BlockIoProtocol gEfiBlockIoProtocolGuid
167
168 #endif
169
170 // Performs a case-insensitive string comparison. This function is necesary
171 // because some EFIs have buggy StriCmp() functions that actually perform
172 // case-sensitive comparisons.
173 // Returns TRUE if strings are identical, FALSE otherwise.
174 static BOOLEAN MyStriCmp(IN CHAR16 *FirstString, IN CHAR16 *SecondString) {
175 if (FirstString && SecondString) {
176 while ((*FirstString != L'\0') && ((*FirstString & ~0x20) == (*SecondString & ~0x20))) {
177 FirstString++;
178 SecondString++;
179 }
180 return (*FirstString == *SecondString);
181 } else {
182 return FALSE;
183 }
184 } // BOOLEAN MyStriCmp()
185
186 // Check firmware vendor; get verification to continue if it's not Apple.
187 // Returns TRUE if Apple firmware or if user assents to use, FALSE otherwise.
188 static BOOLEAN VerifyGoOn(VOID) {
189 BOOLEAN GoOn = TRUE;
190 UINTN invalid;
191
192 if (!MyStriCmp(L"Apple", ST->FirmwareVendor)) {
193 Print(L"Your firmware is made by %s.\n", ST->FirmwareVendor);
194 Print(L"Ordinarily, a hybrid MBR (which this program creates) should be used ONLY on\n");
195 Print(L"Apple Macs that dual-boot with Windows or some other BIOS-mode OS. Are you\n");
196 invalid = input_boolean(STR("SURE you want to continue? [y/N] "), &GoOn);
197 if (invalid)
198 GoOn = FALSE;
199 }
200 return GoOn;
201 } // BOOLEAN VerifyGoOn()
202
203 //
204 // main entry point
205 //
206
207 EFI_STATUS
208 EFIAPI
209 efi_main (IN EFI_HANDLE ImageHandle,
210 IN EFI_SYSTEM_TABLE *SystemTable)
211 {
212 EFI_STATUS Status;
213 UINTN SyncStatus;
214 UINTN Index;
215 UINTN HandleCount;
216 EFI_HANDLE *HandleBuffer;
217 EFI_HANDLE DeviceHandle;
218 EFI_DEVICE_PATH *DevicePath, *NextDevicePath;
219 BOOLEAN Usable;
220
221 InitializeLib(ImageHandle, SystemTable);
222
223 Status = refit_call5_wrapper(BS->LocateHandleBuffer, ByProtocol, &BlockIoProtocol, NULL, &HandleCount, &HandleBuffer);
224 if (EFI_ERROR (Status)) {
225 Status = EFI_NOT_FOUND;
226 return Status;
227 }
228
229 if (!VerifyGoOn())
230 return EFI_ABORTED;
231
232 for (Index = 0; Index < HandleCount; Index++) {
233
234 DeviceHandle = HandleBuffer[Index];
235
236 // check device path
237 DevicePath = DevicePathFromHandle(DeviceHandle);
238 Usable = TRUE;
239 while (DevicePath != NULL && !IsDevicePathEndType(DevicePath)) {
240 NextDevicePath = NextDevicePathNode(DevicePath);
241
242 if (DevicePathType(DevicePath) == MESSAGING_DEVICE_PATH &&
243 (DevicePathSubType(DevicePath) == MSG_USB_DP ||
244 DevicePathSubType(DevicePath) == MSG_USB_CLASS_DP ||
245 DevicePathSubType(DevicePath) == MSG_1394_DP ||
246 DevicePathSubType(DevicePath) == MSG_FIBRECHANNEL_DP))
247 Usable = FALSE; // USB/FireWire/FC device
248 if (DevicePathType(DevicePath) == MEDIA_DEVICE_PATH)
249 Usable = FALSE; // partition, El Torito entry, legacy BIOS device
250
251 DevicePath = NextDevicePath;
252 }
253 if (!Usable)
254 continue;
255
256 Status = refit_call3_wrapper(BS->HandleProtocol, DeviceHandle, &BlockIoProtocol, (VOID **) &BlockIO);
257 if (EFI_ERROR(Status)) {
258 // TODO: report error
259 BlockIO = NULL;
260 } else {
261 if (BlockIO->Media->BlockSize != 512)
262 BlockIO = NULL; // optical media
263 else
264 break;
265 }
266
267 }
268
269 FreePool (HandleBuffer);
270
271 if (BlockIO == NULL) {
272 Print(L"Internal hard disk device not found!\n");
273 return EFI_NOT_FOUND;
274 }
275
276 SyncStatus = gptsync();
277
278 if (SyncStatus == 0)
279 PauseForKey();
280
281
282 if (SyncStatus)
283 return EFI_NOT_FOUND;
284 return EFI_SUCCESS;
285 }