Chameleon

Chameleon Svn Source Tree

Root/branches/JrCs/i386/libsaio/fake_efi.c

1/*
2 * Copyright 2007 David F. Elliott. All rights reserved.
3 */
4
5#include "libsaio.h"
6#include "boot.h"
7#include "bootstruct.h" /* for bootArgs */
8#include "efi.h"
9#include "acpi.h"
10#include "fake_efi.h"
11#include "efi_tables.h"
12#include "platform.h"
13#include "dsdt_patcher.h"
14#include "smbios_patcher.h"
15#include "device_inject.h"
16#include "convert.h"
17#include "pci.h"
18#include "sl.h"
19
20extern void setup_pci_devs(pci_dt_t *pci_dt);
21
22/*
23Modern Darwin kernels require some amount of EFI because Apple machines all
24have EFI. Modifying the kernel source to not require EFI is of course
25possible but would have to be maintained as a separate patch because it is
26unlikely that Apple wishes to add legacy support to their kernel.
27
28As you can see from the Apple-supplied code in bootstruct.c, it seems that
29the intention was clearly to modify this booter to provide EFI-like structures
30to the kernel rather than modifying the kernel to handle non-EFI stuff. This
31makes a lot of sense from an engineering point of view as it means the kernel
32for the as yet unreleased EFI-only Macs could still be booted by the non-EFI
33DTK systems so long as the kernel checked to ensure the boot tables were
34filled in appropriately. Modern xnu requires a system table and a runtime
35services table and performs no checks whatsoever to ensure the pointers to
36these tables are non-NULL. Therefore, any modern xnu kernel will page fault
37early on in the boot process if the system table pointer is zero.
38
39Even before that happens, the tsc_init function in modern xnu requires the FSB
40Frequency to be a property in the /efi/platform node of the device tree or else
41it panics the bootstrap process very early on.
42
43As of this writing, the current implementation found here is good enough
44to make the currently available xnu kernel boot without modification on a
45system with an appropriate processor. With a minor source modification to
46the tsc_init function to remove the explicit check for Core or Core 2
47processors the kernel can be made to boot on other processors so long as
48the code can be executed by the processor and the machine contains the
49necessary hardware.
50*/
51
52
53/*==========================================================================
54 * Utility function to make a device tree string from an EFI_GUID
55 */
56
57static inline char * mallocStringForGuid(EFI_GUID const *pGuid)
58{
59 char *string = malloc(37);
60 efi_guid_unparse_upper(pGuid, string);
61 return string;
62}
63
64
65/*==========================================================================
66 * Function to map 32 bit physical address to 64 bit virtual address
67 */
68static uint64_t ptov64(uint32_t addr)
69{
70 return ((uint64_t)addr | 0xFFFFFF8000000000ULL);
71}
72
73
74/*==========================================================================
75 * Fake EFI implementation
76 */
77
78/* Identify ourselves as the EFI firmware vendor */
79static EFI_CHAR16 const FIRMWARE_VENDOR[] = {'C','h','a','m','e','l','e','o','n','_','2','.','0', 0};
80static EFI_UINT32 const FIRMWARE_REVISION = 132; /* FIXME: Find a constant for this. */
81
82/* Default platform system_id (fix by IntVar) */
83static EFI_CHAR8 const SYSTEM_ID[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10}; //random value gen by uuidgen
84
85/* Just a ret instruction */
86static uint8_t const VOIDRET_INSTRUCTIONS[] = {0xc3};
87/* movl $0x80000003,%eax; ret */
88static uint8_t const UNSUPPORTEDRET_INSTRUCTIONS[] = {0xb8, 0x03, 0x00, 0x00, 0x80, 0xc3};
89
90
91/* We use the fake_efi_pages struct so that we only need to do one kernel
92 * memory allocation for all needed EFI data. Otherwise, small allocations
93 * like the FIRMWARE_VENDOR string would take up an entire page.
94 * NOTE WELL: Do NOT assume this struct has any particular layout within itself.
95 * It is absolutely not intended to be publicly exposed anywhere
96 * We say pages (plural) although right now we are well within the 1 page size
97 * and probably will stay that way.
98 */
99struct fake_efi_pages
100{
101 EFI_SYSTEM_TABLE_64 efiSystemTable;
102 EFI_RUNTIME_SERVICES_64 efiRuntimeServices;
103 EFI_CONFIGURATION_TABLE_64 efiConfigurationTable[MAX_CONFIGURATION_TABLE_ENTRIES];
104 EFI_CHAR16 firmwareVendor[sizeof(FIRMWARE_VENDOR)/sizeof(EFI_CHAR16)];
105 uint8_t voidret_instructions[sizeof(VOIDRET_INSTRUCTIONS)/sizeof(uint8_t)];
106 uint8_t unsupportedret_instructions[sizeof(UNSUPPORTEDRET_INSTRUCTIONS)/sizeof(uint8_t)];
107};
108
109EFI_SYSTEM_TABLE_64 *gST = NULL;
110Node *gEfiConfigurationTableNode = NULL;
111
112extern EFI_STATUS addConfigurationTable(EFI_GUID const *pGuid, void *table, char const *alias)
113{
114 EFI_UINTN i = gST->NumberOfTableEntries;
115 /* We only do adds, not modifications and deletes like InstallConfigurationTable */
116 if(i >= MAX_CONFIGURATION_TABLE_ENTRIES)
117 stop("Ran out of space for configuration tables. Increase the reserved size in the code.\n");
118
119 if(pGuid == NULL)
120 return EFI_INVALID_PARAMETER;
121
122 if(table != NULL)
123 {
124 /* FIXME
125 ((EFI_CONFIGURATION_TABLE_64 *)gST->ConfigurationTable)[i].VendorGuid = *pGuid;
126 ((EFI_CONFIGURATION_TABLE_64 *)gST->ConfigurationTable)[i].VendorTable = (EFI_PTR64)table;
127
128 ++gST->NumberOfTableEntries;
129 */
130 Node *tableNode = DT__AddChild(gEfiConfigurationTableNode, mallocStringForGuid(pGuid));
131
132 /* Use the pointer to the GUID we just stuffed into the system table */
133 DT__AddProperty(tableNode, "guid", sizeof(EFI_GUID), (void*)pGuid);
134
135 /* The "table" property is the 32-bit (in our implementation) physical address of the table */
136 DT__AddProperty(tableNode, "table", sizeof(void*) * 2, table);
137
138 /* Assume the alias pointer is a global or static piece of data */
139 if(alias != NULL)
140 DT__AddProperty(tableNode, "alias", strlen(alias)+1, (char*)alias);
141
142 return EFI_SUCCESS;
143 }
144 return EFI_UNSUPPORTED;
145}
146
147static inline void fixupEfiSystemTableCRC32(EFI_SYSTEM_TABLE_64 *efiSystemTable)
148{
149 efiSystemTable->Hdr.CRC32 = 0;
150 efiSystemTable->Hdr.CRC32 = crc32(0L, efiSystemTable, efiSystemTable->Hdr.HeaderSize);
151}
152
153/*
154What we do here is simply allocate a fake EFI system table and a fake EFI
155runtime services table.
156
157Because we build against modern headers with kBootArgsRevision 4 we
158also take care to set efiMode = 32.
159*/
160void
161setupEfiTables(void)
162{
163 struct fake_efi_pages *fakeEfiPages= (struct fake_efi_pages*)AllocateKernelMemory(sizeof(struct fake_efi_pages));
164
165 /* Zero out all the tables in case fields are added later */
166 bzero(fakeEfiPages, sizeof(struct fake_efi_pages));
167
168 /* --------------------------------------------------------------------
169 * Initialize some machine code that will return EFI_UNSUPPORTED for
170 * functions returning int and simply return for void functions.
171 */
172 memcpy(fakeEfiPages->voidret_instructions, VOIDRET_INSTRUCTIONS, sizeof(VOIDRET_INSTRUCTIONS));
173 memcpy(fakeEfiPages->unsupportedret_instructions, UNSUPPORTEDRET_INSTRUCTIONS, sizeof(UNSUPPORTEDRET_INSTRUCTIONS));
174
175 /* -------------------------------------------------------------------- */
176 /* System table */
177 EFI_SYSTEM_TABLE_64 *efiSystemTable = gST = &fakeEfiPages->efiSystemTable;
178 efiSystemTable->Hdr.Signature = EFI_SYSTEM_TABLE_SIGNATURE;
179 efiSystemTable->Hdr.Revision = EFI_SYSTEM_TABLE_REVISION;
180 efiSystemTable->Hdr.HeaderSize = sizeof(EFI_SYSTEM_TABLE_64);
181 efiSystemTable->Hdr.CRC32 = 0; /* Initialize to zero and then do CRC32 */
182 efiSystemTable->Hdr.Reserved = 0;
183
184 efiSystemTable->FirmwareVendor = (EFI_PTR32)&fakeEfiPages->firmwareVendor;
185 memcpy(fakeEfiPages->firmwareVendor, FIRMWARE_VENDOR, sizeof(FIRMWARE_VENDOR));
186 efiSystemTable->FirmwareRevision = FIRMWARE_REVISION;
187
188 /* XXX: We may need to have basic implementations of ConIn/ConOut/StdErr */
189 /* The EFI spec states that all handles are invalid after boot services have been
190 * exited so we can probably get by with leaving the handles as zero. */
191 efiSystemTable->ConsoleInHandle = 0;
192 efiSystemTable->ConIn = 0;
193
194 efiSystemTable->ConsoleOutHandle = 0;
195 efiSystemTable->ConOut = 0;
196
197 efiSystemTable->StandardErrorHandle = 0;
198 efiSystemTable->StdErr = 0;
199
200 efiSystemTable->RuntimeServices = ptov64((EFI_PTR32)&fakeEfiPages->efiRuntimeServices);
201 /* According to the EFI spec, BootServices aren't valid after the
202 * boot process is exited so we can probably do without it.
203 * Apple didn't provide a definition for it in pexpert/i386/efi.h
204 * so I'm guessing they don't use it.
205 */
206 efiSystemTable->BootServices = 0;
207
208 efiSystemTable->NumberOfTableEntries = 0;
209 efiSystemTable->ConfigurationTable = (EFI_PTR32)fakeEfiPages->efiConfigurationTable;
210
211
212 /* We're done. Now CRC32 the thing so the kernel will accept it */
213 fixupEfiSystemTableCRC32(efiSystemTable);
214
215 /* -------------------------------------------------------------------- */
216 /* Runtime services */
217 EFI_RUNTIME_SERVICES_64 *efiRuntimeServices = &fakeEfiPages->efiRuntimeServices;
218 efiRuntimeServices->Hdr.Signature = EFI_RUNTIME_SERVICES_SIGNATURE;
219 efiRuntimeServices->Hdr.Revision = EFI_RUNTIME_SERVICES_REVISION;
220 efiRuntimeServices->Hdr.HeaderSize = sizeof(EFI_RUNTIME_SERVICES_64);
221 efiRuntimeServices->Hdr.CRC32 = 0;
222 efiRuntimeServices->Hdr.Reserved = 0;
223
224 /* There are a number of function pointers in the efiRuntimeServices table.
225 * These are the Foundation (e.g. core) services and are expected to be present on
226 * all EFI-compliant machines. Some kernel extensions (notably AppleEFIRuntime)
227 * will call these without checking to see if they are null.
228 *
229 * We don't really feel like doing an EFI implementation in the bootloader
230 * but it is nice if we can at least prevent a complete crash by
231 * at least providing some sort of implementation until one can be provided
232 * nicely in a kext.
233 */
234 void (*voidret_fp)() = (void*)fakeEfiPages->voidret_instructions;
235 void (*unsupportedret_fp)() = (void*)fakeEfiPages->unsupportedret_instructions;
236 efiRuntimeServices->GetTime = ptov64((EFI_PTR32)unsupportedret_fp);
237 efiRuntimeServices->SetTime = ptov64((EFI_PTR32)unsupportedret_fp);
238 efiRuntimeServices->GetWakeupTime = ptov64((EFI_PTR32)unsupportedret_fp);
239 efiRuntimeServices->SetWakeupTime = ptov64((EFI_PTR32)unsupportedret_fp);
240 efiRuntimeServices->SetVirtualAddressMap = ptov64((EFI_PTR32)unsupportedret_fp);
241 efiRuntimeServices->ConvertPointer = ptov64((EFI_PTR32)unsupportedret_fp);
242 efiRuntimeServices->GetVariable = ptov64((EFI_PTR32)unsupportedret_fp);
243 efiRuntimeServices->GetNextVariableName = ptov64((EFI_PTR32)unsupportedret_fp);
244 efiRuntimeServices->SetVariable = ptov64((EFI_PTR32)unsupportedret_fp);
245 efiRuntimeServices->GetNextHighMonotonicCount = ptov64((EFI_PTR32)unsupportedret_fp);
246 efiRuntimeServices->ResetSystem = ptov64((EFI_PTR32)voidret_fp);
247
248 /* We're done. Now CRC32 the thing so the kernel will accept it */
249 efiRuntimeServices->Hdr.CRC32 = crc32(0L, efiRuntimeServices, efiRuntimeServices->Hdr.HeaderSize);
250
251
252 /* -------------------------------------------------------------------- */
253 /* Finish filling in the rest of the boot args that we need. */
254 bootArgs->efiSystemTable = (uint32_t)efiSystemTable;
255 bootArgs->efiMode = kBootArgsEfiMode64;
256
257 /* The bootArgs structure as a whole is bzero'd so we don't need to fill in
258 * things like efiRuntimeServices* and what not.
259 *
260 * In fact, the only code that seems to use that is the hibernate code so it
261 * knows not to save the pages. It even checks to make sure its nonzero.
262 */
263}
264
265/*
266In addition to the EFI tables there is also the EFI device tree node.
267In particular, we need /efi/platform to have an FSBFrequency key. Without it,
268the tsc_init function will panic very early on in kernel startup, before
269the console is available.
270*/
271
272/*==========================================================================
273 * FSB Frequency detection
274 */
275
276/* These should be const but DT__AddProperty takes char* */
277static char TSC_Frequency_prop[] = "TSCFrequency";
278static char FSB_Frequency_prop[] = "FSBFrequency";
279static char CPU_Frequency_prop[] = "CPUFrequency";
280
281/*==========================================================================
282 * SystemType
283 */
284
285static char SystemType_prop[] = "system-type";
286
287/*==========================================================================
288 * SMBIOS
289 */
290
291/* From Foundation/Efi/Guid/Smbios/SmBios.h */
292/* Modified to wrap Data4 array init with {} */
293#define EFI_SMBIOS_TABLE_GUID \
294 { \
295 0xeb9d2d31, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} \
296 }
297
298/* From Foundation/Efi/Guid/Smbios/SmBios.c */
299EFI_GUID const gEfiSmbiosTableGuid = EFI_SMBIOS_TABLE_GUID;
300
301#define SMBIOS_RANGE_START 0x000F0000
302#define SMBIOS_RANGE_END 0x000FFFFF
303
304/* '_SM_' in little endian: */
305#define SMBIOS_ANCHOR_UINT32_LE 0x5f4d535f
306
307#define EFI_ACPI_TABLE_GUID \
308 { \
309 0xeb9d2d30, 0x2d88, 0x11d3, { 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
310 }
311
312#define EFI_ACPI_20_TABLE_GUID \
313 { \
314 0x8868e871, 0xe4f1, 0x11d3, { 0xbc, 0x22, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } \
315 }
316
317EFI_GUID gEfiAcpiTableGuid = EFI_ACPI_TABLE_GUID;
318EFI_GUID gEfiAcpi20TableGuid = EFI_ACPI_20_TABLE_GUID;
319
320
321/*==========================================================================
322 * Fake EFI implementation
323 */
324
325/* These should be const but DT__AddProperty takes char* */
326static char FIRMWARE_REVISION_PROP[] = "firmware-revision";
327static char FIRMWARE_ABI_PROP[] = "firmware-abi";
328static char FIRMWARE_VENDOR_PROP[] = "firmware-vendor";
329static char FIRMWARE_ABI_PROP_VALUE[] = "EFI64";
330static const char const SYSTEM_ID_PROP[] = "system-id";
331
332/* return a binary UUID value from the overriden SystemID,
333 * or from a fixed value if none found
334 */
335static const EFI_CHAR8* getSystemID()
336{
337 bool pause = FALSE;
338 int len;
339 const char* StrSystemId = NULL;
340 const EFI_CHAR8* SystemId = NULL;
341
342 // unable to determine UUID for host. Error: 35 fix
343
344 getValueForKey(kSystemID, &StrSystemId, &len, &bootInfo->bootConfig);
345 if (StrSystemId != NULL) {
346 SystemId = newUUIDFromString(StrSystemId);
347 if (SystemId == NULL) {
348 error("Error: invalid SystemID '%s'\n", StrSystemId);
349 pause = TRUE;
350 }
351 }
352
353 if (SystemId == NULL) {
354 // EFI_CHAR8* ret = getUUIDFromString(sysId);
355 //
356 // if(!sysId || !ret) { // try bios dmi info UUID extraction
357 // ret = getSmbiosUUID();
358 // sysId = 0;
359 // }
360 // if(!ret) // no bios dmi UUID available, set a fixed value for system-id
361 SystemId = SYSTEM_ID;
362 error("Using a fixed SystemID: '%s'\n", getStringFromUUID(SystemId));
363 //
364 // verbose("Customizing SystemID with : %s\n", getStringFromUUID(ret)); // apply a nice formatting to the displayed output
365 }
366
367 if (pause) getc();
368
369 return SystemId;
370}
371
372void
373setupEfiDeviceTree(void)
374{
375 Node *node;
376 node = DT__FindNode("/", false);
377 if (node == 0) {
378 stop("Couldn't get root node");
379 }
380
381 /* Export system-type */
382 verbose("Using system-type=0x%02x\n", Platform.Type);
383 DT__AddProperty(node, SystemType_prop, sizeof(Platform.Type), &Platform.Type);
384
385 /* We could also just do DT__FindNode("/efi/platform", true)
386 * But I think eventually we want to fill stuff in the efi node
387 * too so we might as well create it so we have a pointer for it too.
388 */
389 node = DT__AddChild(node, "efi");
390
391 DT__AddProperty(node, FIRMWARE_REVISION_PROP, sizeof(FIRMWARE_REVISION), (EFI_UINT32*)&FIRMWARE_REVISION);
392 DT__AddProperty(node, FIRMWARE_ABI_PROP, sizeof(FIRMWARE_ABI_PROP_VALUE), (char*)FIRMWARE_ABI_PROP_VALUE);
393 DT__AddProperty(node, FIRMWARE_VENDOR_PROP, sizeof(FIRMWARE_VENDOR), (EFI_CHAR16*)FIRMWARE_VENDOR);
394
395 /* TODO: Fill in other efi properties if necessary */
396
397 /* Set up the /efi/runtime-services table node similar to the way a child node of configuration-table
398 * is set up. That is, name and table properties */
399 Node *runtimeServicesNode = DT__AddChild(node, "runtime-services");
400
401 /* The value of the table property is the 32-bit physical address for the RuntimeServices table.
402 * Sice the EFI system table already has a pointer to it, we simply use the address of that pointer
403 * for the pointer to the property data. Warning.. DT finalization calls free on that but we're not
404 * the only thing to use a non-malloc'd pointer for something in the DT
405 */
406 DT__AddProperty(runtimeServicesNode, "table", sizeof(uint64_t), &gST->RuntimeServices);
407
408 /* Set up the /efi/configuration-table node which will eventually have several child nodes for
409 * all of the configuration tables needed by various kernel extensions.
410 */
411 gEfiConfigurationTableNode = DT__AddChild(node, "configuration-table");
412
413 /* Now fill in the /efi/platform Node */
414 Node *efiPlatformNode = DT__AddChild(node, "platform");
415
416 /* NOTE WELL: If you do add FSB Frequency detection, make sure to store
417 * the value in the fsbFrequency global and not an malloc'd pointer
418 * because the DT_AddProperty function does not copy its args.
419 */
420 if(Platform.CPU.FSBFrequency != 0)
421 DT__AddProperty(efiPlatformNode, FSB_Frequency_prop, sizeof(uint64_t), &Platform.CPU.FSBFrequency);
422
423/* Export TSC and CPU frequencies for use by the kernel or KEXTs
424 */
425 if(Platform.CPU.TSCFrequency != 0)
426 DT__AddProperty(efiPlatformNode, TSC_Frequency_prop, sizeof(uint64_t), &Platform.CPU.TSCFrequency);
427 if(Platform.CPU.CPUFrequency != 0)
428 DT__AddProperty(efiPlatformNode, CPU_Frequency_prop, sizeof(uint64_t), &Platform.CPU.CPUFrequency);
429
430 /* Set EFI system-id. */
431const EFI_CHAR8* systemId = getSystemID();
432verbose("Customizing %s with: %s\n", SYSTEM_ID_PROP, getStringFromUUID(systemId));
433DT__AddProperty(efiPlatformNode, SYSTEM_ID_PROP, UUID_LEN, systemId);
434
435 /* Fill /efi/device-properties node.
436 */
437 setupDeviceProperties(node);
438}
439
440/* Installs all the needed configuration table entries */
441void setupEfiConfigurationTable()
442{
443 smbios_p = (EFI_PTR32)getSmbios();
444 addConfigurationTable(&gEfiSmbiosTableGuid, &smbios_p, NULL);
445
446 // Setup ACPI with DSDT overrides (mackerintel's patch)
447 setupAcpi();
448
449 // We've obviously changed the count.. so fix up the CRC32
450 fixupEfiSystemTableCRC32(gST);
451}
452
453void setupEfiDevices(void)
454{
455setup_pci_devs(root_pci_dev);
456}
457
458/* Entrypoint from boot.c */
459void setupFakeEfi(void)
460{
461// Generate efi device strings
462setupEfiDevices();
463
464// Initialize the base table
465setupEfiTables();
466
467 // Initialize the device tree
468 setupEfiDeviceTree();
469
470 // Add configuration table entries to both the services table and the device tree
471 setupEfiConfigurationTable();
472}
473

Archive Download this file

Revision: 78