Chameleon

Chameleon Svn Source Tree

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

1/*
2 * Copyright 2007 David F. Elliott. All rights reserved.
3 */
4
5#include "libsaio.h"
6#include "bootstruct.h" /* for bootArgs */
7#include "efi.h"
8#include "acpi.h"
9#include "fake_efi.h"
10#include "efi_tables.h"
11#include "freq_detect.h"
12#include "dsdt_patcher.h"
13#include "smbios_patcher.h"
14#include "device_inject.h"
15#include "pci.h"
16#include "sl.h"
17
18extern struct SMBEntryPoint * getSmbios();
19extern void setup_pci_devs(pci_dt_t *pci_dt);
20
21/*
22Modern Darwin kernels require some amount of EFI because Apple machines all
23have EFI. Modifying the kernel source to not require EFI is of course
24possible but would have to be maintained as a separate patch because it is
25unlikely that Apple wishes to add legacy support to their kernel.
26
27As you can see from the Apple-supplied code in bootstruct.c, it seems that
28the intention was clearly to modify this booter to provide EFI-like structures
29to the kernel rather than modifying the kernel to handle non-EFI stuff. This
30makes a lot of sense from an engineering point of view as it means the kernel
31for the as yet unreleased EFI-only Macs could still be booted by the non-EFI
32DTK systems so long as the kernel checked to ensure the boot tables were
33filled in appropriately. Modern xnu requires a system table and a runtime
34services table and performs no checks whatsoever to ensure the pointers to
35these tables are non-NULL. Therefore, any modern xnu kernel will page fault
36early on in the boot process if the system table pointer is zero.
37
38Even before that happens, the tsc_init function in modern xnu requires the FSB
39Frequency to be a property in the /efi/platform node of the device tree or else
40it panics the bootstrap process very early on.
41
42As of this writing, the current implementation found here is good enough
43to make the currently available xnu kernel boot without modification on a
44system with an appropriate processor. With a minor source modification to
45the tsc_init function to remove the explicit check for Core or Core 2
46processors the kernel can be made to boot on other processors so long as
47the code can be executed by the processor and the machine contains the
48necessary hardware.
49*/
50
51
52/*==========================================================================
53 * Utility function to make a device tree string from an EFI_GUID
54 */
55
56static inline char * mallocStringForGuid(EFI_GUID const *pGuid)
57{
58 char *string = malloc(37);
59 efi_guid_unparse_upper(pGuid, string);
60 return string;
61}
62
63
64/*==========================================================================
65 * Function to map 32 bit physical address to 64 bit virtual address
66 */
67static uint64_t ptov64(uint32_t addr)
68{
69 return ((uint64_t)addr | 0xFFFFFF8000000000ULL);
70}
71
72
73/*==========================================================================
74 * Fake EFI implementation
75 */
76
77/* Identify ourselves as the EFI firmware vendor */
78static EFI_CHAR16 const FIRMWARE_VENDOR[] = {'C','h','a','m','e','l','e','o','n','_','2','.','0', 0};
79static EFI_UINT32 const FIRMWARE_REVISION = 132; /* FIXME: Find a constant for this. */
80
81/* Default platform system_id (fix by IntVar) */
82static 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
83//static EFI_CHAR8 const SYSTEM_ID[] = {0xf5,0x0e,0xd0,0x89,0x0c,0xa8,0xf4,0xeb,0xc0,0x5c,0xa3,0x2a,0xdf,0xfa,0xdb,0x72};
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 * SMBIOS
283 */
284
285/* From Foundation/Efi/Guid/Smbios/SmBios.h */
286/* Modified to wrap Data4 array init with {} */
287#define EFI_SMBIOS_TABLE_GUID \
288 { \
289 0xeb9d2d31, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} \
290 }
291
292/* From Foundation/Efi/Guid/Smbios/SmBios.c */
293EFI_GUID const gEfiSmbiosTableGuid = EFI_SMBIOS_TABLE_GUID;
294
295#define SMBIOS_RANGE_START 0x000F0000
296#define SMBIOS_RANGE_END 0x000FFFFF
297
298/* '_SM_' in little endian: */
299#define SMBIOS_ANCHOR_UINT32_LE 0x5f4d535f
300
301#define EFI_ACPI_TABLE_GUID \
302 { \
303 0xeb9d2d30, 0x2d88, 0x11d3, { 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
304 }
305
306#define EFI_ACPI_20_TABLE_GUID \
307 { \
308 0x8868e871, 0xe4f1, 0x11d3, { 0xbc, 0x22, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } \
309 }
310
311EFI_GUID gEfiAcpiTableGuid = EFI_ACPI_TABLE_GUID;
312EFI_GUID gEfiAcpi20TableGuid = EFI_ACPI_20_TABLE_GUID;
313
314
315/*==========================================================================
316 * Fake EFI implementation
317 */
318
319/* These should be const but DT__AddProperty takes char* */
320static char FIRMWARE_REVISION_PROP[] = "firmware-revision";
321static char FIRMWARE_ABI_PROP[] = "firmware-abi";
322static char FIRMWARE_VENDOR_PROP[] = "firmware-vendor";
323static char FIRMWARE_ABI_PROP_VALUE[] = "EFI64";
324static char SYSTEM_ID_PROP[] = "system-id";
325static char PLATFORM_UUID_PROP[] = "IOPlatformUUID";
326
327static EFI_CHAR8* getUUIDFromString(const char * szInUUID)
328{
329 char szUUID[16+1], *p=szUUID;
330 int size=0, x;
331 void* ret;
332
333 if (!szInUUID || strlen(szInUUID)<16) return (EFI_CHAR8*) 0;
334
335 while(*szInUUID) if (*szInUUID!='-') *p++=*szInUUID++; else szInUUID++;
336 *p='\0';
337 ret = convertHexStr2Binary(szUUID, &size);
338 if (!ret || size!=16)
339 {
340 verbose("UUID: cannot convert string <%s> to valid UUID.\n", szUUID);
341 return (EFI_CHAR8*) 0;
342 }
343 return (EFI_CHAR8*) ret; // new allocated buffer containing the converted string to bin
344}
345
346void
347setupEfiDeviceTree(void)
348{
349 Node *node;
350 const char * sz_system_id=0;
351 EFI_CHAR8* ret=0;
352 node = DT__FindNode("/", false);
353 if (node == 0) {
354 stop("Couldn't get root node");
355 }
356
357 /* We could also just do DT__FindNode("/efi/platform", true)
358 * But I think eventually we want to fill stuff in the efi node
359 * too so we might as well create it so we have a pointer for it too.
360 */
361 node = DT__AddChild(node, "efi");
362
363 DT__AddProperty(node, FIRMWARE_REVISION_PROP, sizeof(FIRMWARE_REVISION), (EFI_UINT32*)&FIRMWARE_REVISION);
364 DT__AddProperty(node, FIRMWARE_ABI_PROP, sizeof(FIRMWARE_ABI_PROP_VALUE), (char*)FIRMWARE_ABI_PROP_VALUE);
365 DT__AddProperty(node, FIRMWARE_VENDOR_PROP, sizeof(FIRMWARE_VENDOR), (EFI_CHAR16*)FIRMWARE_VENDOR);
366
367 /* TODO: Fill in other efi properties if necessary */
368
369 /* Set up the /efi/runtime-services table node similar to the way a child node of configuration-table
370 * is set up. That is, name and table properties */
371 Node *runtimeServicesNode = DT__AddChild(node, "runtime-services");
372
373 /* The value of the table property is the 32-bit physical address for the RuntimeServices table.
374 * Sice the EFI system table already has a pointer to it, we simply use the address of that pointer
375 * for the pointer to the property data. Warning.. DT finalization calls free on that but we're not
376 * the only thing to use a non-malloc'd pointer for something in the DT
377 */
378 DT__AddProperty(runtimeServicesNode, "table", sizeof(uint64_t), &gST->RuntimeServices);
379
380 /* Set up the /efi/configuration-table node which will eventually have several child nodes for
381 * all of the configuration tables needed by various kernel extensions.
382 */
383 gEfiConfigurationTableNode = DT__AddChild(node, "configuration-table");
384
385 /* Now fill in the /efi/platform Node */
386 Node *efiPlatformNode = DT__AddChild(node, "platform");
387
388 /* NOTE WELL: If you do add FSB Frequency detection, make sure to store
389 * the value in the fsbFrequency global and not an malloc'd pointer
390 * because the DT_AddProperty function does not copy its args.
391 */
392 if(fsbFrequency != 0)
393 DT__AddProperty(efiPlatformNode, FSB_Frequency_prop, sizeof(uint64_t), &fsbFrequency);
394
395 // rek: Give the user a chance to set a fixed/reproduceable system UUID from the bootConfig
396 sz_system_id = newStringForKey("SystemID", &bootInfo->bootConfig);
397 ret = getUUIDFromString(sz_system_id);
398 if (sz_system_id)
399 {
400 if (ret) {
401verbose("Customizing SystemID with : %s\n", sz_system_id);
402DT__AddProperty(efiPlatformNode, SYSTEM_ID_PROP, sizeof(SYSTEM_ID), (EFI_UINT32*) ret);
403 }
404 free((void*) sz_system_id);
405 }
406 else
407 // unable to determine UUID for host. Error: 35 fix
408 DT__AddProperty(efiPlatformNode, SYSTEM_ID_PROP, sizeof(SYSTEM_ID), (EFI_UINT32*)&SYSTEM_ID);
409
410/* Export TSC and CPU frequencies for use by the kernel or KEXTs
411 */
412 if(tscFrequency != 0)
413 DT__AddProperty(efiPlatformNode, TSC_Frequency_prop, sizeof(uint64_t), &tscFrequency);
414 if(cpuFrequency != 0)
415 DT__AddProperty(efiPlatformNode, CPU_Frequency_prop, sizeof(uint64_t), &cpuFrequency);
416
417 /* Fill /efi/device-properties node.
418 */
419 setupDeviceProperties(node);
420}
421
422/* Installs all the needed configuration table entries */
423void setupEfiConfigurationTable()
424{
425 smbios_p = (EFI_PTR32)getSmbios();
426 addConfigurationTable(&gEfiSmbiosTableGuid, &smbios_p, NULL);
427
428 // Setup ACPI with DSDT overrides (mackerintel's patch)
429 setupAcpi();
430
431 // We've obviously changed the count.. so fix up the CRC32
432 fixupEfiSystemTableCRC32(gST);
433}
434
435void setupEfiDevices(void)
436{
437setup_pci_devs(root_pci_dev);
438}
439
440/* Entrypoint from boot.c */
441void setupFakeEfi(void)
442{
443// Generate efi device strings
444setupEfiDevices();
445
446// Initialize the base table
447setupEfiTables();
448
449 // Initialize the device tree
450 setupEfiDeviceTree();
451
452 // Add configuration table entries to both the services table and the device tree
453 setupEfiConfigurationTable();
454}
455

Archive Download this file

Revision: 24