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 "pci.h"
17#include "sl.h"
18
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
84/* Just a ret instruction */
85static uint8_t const VOIDRET_INSTRUCTIONS[] = {0xc3};
86/* movl $0x80000003,%eax; ret */
87static uint8_t const UNSUPPORTEDRET_INSTRUCTIONS[] = {0xb8, 0x03, 0x00, 0x00, 0x80, 0xc3};
88
89
90/* We use the fake_efi_pages struct so that we only need to do one kernel
91 * memory allocation for all needed EFI data. Otherwise, small allocations
92 * like the FIRMWARE_VENDOR string would take up an entire page.
93 * NOTE WELL: Do NOT assume this struct has any particular layout within itself.
94 * It is absolutely not intended to be publicly exposed anywhere
95 * We say pages (plural) although right now we are well within the 1 page size
96 * and probably will stay that way.
97 */
98struct fake_efi_pages
99{
100 EFI_SYSTEM_TABLE_64 efiSystemTable;
101 EFI_RUNTIME_SERVICES_64 efiRuntimeServices;
102 EFI_CONFIGURATION_TABLE_64 efiConfigurationTable[MAX_CONFIGURATION_TABLE_ENTRIES];
103 EFI_CHAR16 firmwareVendor[sizeof(FIRMWARE_VENDOR)/sizeof(EFI_CHAR16)];
104 uint8_t voidret_instructions[sizeof(VOIDRET_INSTRUCTIONS)/sizeof(uint8_t)];
105 uint8_t unsupportedret_instructions[sizeof(UNSUPPORTEDRET_INSTRUCTIONS)/sizeof(uint8_t)];
106};
107
108EFI_SYSTEM_TABLE_64 *gST = NULL;
109Node *gEfiConfigurationTableNode = NULL;
110
111extern EFI_STATUS addConfigurationTable(EFI_GUID const *pGuid, void *table, char const *alias)
112{
113 EFI_UINTN i = gST->NumberOfTableEntries;
114 /* We only do adds, not modifications and deletes like InstallConfigurationTable */
115 if(i >= MAX_CONFIGURATION_TABLE_ENTRIES)
116 stop("Ran out of space for configuration tables. Increase the reserved size in the code.\n");
117
118 if(pGuid == NULL)
119 return EFI_INVALID_PARAMETER;
120
121 if(table != NULL)
122 {
123 /* FIXME
124 ((EFI_CONFIGURATION_TABLE_64 *)gST->ConfigurationTable)[i].VendorGuid = *pGuid;
125 ((EFI_CONFIGURATION_TABLE_64 *)gST->ConfigurationTable)[i].VendorTable = (EFI_PTR64)table;
126
127 ++gST->NumberOfTableEntries;
128 */
129 Node *tableNode = DT__AddChild(gEfiConfigurationTableNode, mallocStringForGuid(pGuid));
130
131 /* Use the pointer to the GUID we just stuffed into the system table */
132 DT__AddProperty(tableNode, "guid", sizeof(EFI_GUID), (void*)pGuid);
133
134 /* The "table" property is the 32-bit (in our implementation) physical address of the table */
135 DT__AddProperty(tableNode, "table", sizeof(void*) * 2, table);
136
137 /* Assume the alias pointer is a global or static piece of data */
138 if(alias != NULL)
139 DT__AddProperty(tableNode, "alias", strlen(alias)+1, (char*)alias);
140
141 return EFI_SUCCESS;
142 }
143 return EFI_UNSUPPORTED;
144}
145
146static inline void fixupEfiSystemTableCRC32(EFI_SYSTEM_TABLE_64 *efiSystemTable)
147{
148 efiSystemTable->Hdr.CRC32 = 0;
149 efiSystemTable->Hdr.CRC32 = crc32(0L, efiSystemTable, efiSystemTable->Hdr.HeaderSize);
150}
151
152/*
153What we do here is simply allocate a fake EFI system table and a fake EFI
154runtime services table.
155
156Because we build against modern headers with kBootArgsRevision 4 we
157also take care to set efiMode = 32.
158*/
159void
160setupEfiTables(void)
161{
162 struct fake_efi_pages *fakeEfiPages= (struct fake_efi_pages*)AllocateKernelMemory(sizeof(struct fake_efi_pages));
163
164 /* Zero out all the tables in case fields are added later */
165 bzero(fakeEfiPages, sizeof(struct fake_efi_pages));
166
167 /* --------------------------------------------------------------------
168 * Initialize some machine code that will return EFI_UNSUPPORTED for
169 * functions returning int and simply return for void functions.
170 */
171 memcpy(fakeEfiPages->voidret_instructions, VOIDRET_INSTRUCTIONS, sizeof(VOIDRET_INSTRUCTIONS));
172 memcpy(fakeEfiPages->unsupportedret_instructions, UNSUPPORTEDRET_INSTRUCTIONS, sizeof(UNSUPPORTEDRET_INSTRUCTIONS));
173
174 /* -------------------------------------------------------------------- */
175 /* System table */
176 EFI_SYSTEM_TABLE_64 *efiSystemTable = gST = &fakeEfiPages->efiSystemTable;
177 efiSystemTable->Hdr.Signature = EFI_SYSTEM_TABLE_SIGNATURE;
178 efiSystemTable->Hdr.Revision = EFI_SYSTEM_TABLE_REVISION;
179 efiSystemTable->Hdr.HeaderSize = sizeof(EFI_SYSTEM_TABLE_64);
180 efiSystemTable->Hdr.CRC32 = 0; /* Initialize to zero and then do CRC32 */
181 efiSystemTable->Hdr.Reserved = 0;
182
183 efiSystemTable->FirmwareVendor = (EFI_PTR32)&fakeEfiPages->firmwareVendor;
184 memcpy(fakeEfiPages->firmwareVendor, FIRMWARE_VENDOR, sizeof(FIRMWARE_VENDOR));
185 efiSystemTable->FirmwareRevision = FIRMWARE_REVISION;
186
187 /* XXX: We may need to have basic implementations of ConIn/ConOut/StdErr */
188 /* The EFI spec states that all handles are invalid after boot services have been
189 * exited so we can probably get by with leaving the handles as zero. */
190 efiSystemTable->ConsoleInHandle = 0;
191 efiSystemTable->ConIn = 0;
192
193 efiSystemTable->ConsoleOutHandle = 0;
194 efiSystemTable->ConOut = 0;
195
196 efiSystemTable->StandardErrorHandle = 0;
197 efiSystemTable->StdErr = 0;
198
199 efiSystemTable->RuntimeServices = ptov64((EFI_PTR32)&fakeEfiPages->efiRuntimeServices);
200 /* According to the EFI spec, BootServices aren't valid after the
201 * boot process is exited so we can probably do without it.
202 * Apple didn't provide a definition for it in pexpert/i386/efi.h
203 * so I'm guessing they don't use it.
204 */
205 efiSystemTable->BootServices = 0;
206
207 efiSystemTable->NumberOfTableEntries = 0;
208 efiSystemTable->ConfigurationTable = (EFI_PTR32)fakeEfiPages->efiConfigurationTable;
209
210
211 /* We're done. Now CRC32 the thing so the kernel will accept it */
212 fixupEfiSystemTableCRC32(efiSystemTable);
213
214 /* -------------------------------------------------------------------- */
215 /* Runtime services */
216 EFI_RUNTIME_SERVICES_64 *efiRuntimeServices = &fakeEfiPages->efiRuntimeServices;
217 efiRuntimeServices->Hdr.Signature = EFI_RUNTIME_SERVICES_SIGNATURE;
218 efiRuntimeServices->Hdr.Revision = EFI_RUNTIME_SERVICES_REVISION;
219 efiRuntimeServices->Hdr.HeaderSize = sizeof(EFI_RUNTIME_SERVICES_64);
220 efiRuntimeServices->Hdr.CRC32 = 0;
221 efiRuntimeServices->Hdr.Reserved = 0;
222
223 /* There are a number of function pointers in the efiRuntimeServices table.
224 * These are the Foundation (e.g. core) services and are expected to be present on
225 * all EFI-compliant machines. Some kernel extensions (notably AppleEFIRuntime)
226 * will call these without checking to see if they are null.
227 *
228 * We don't really feel like doing an EFI implementation in the bootloader
229 * but it is nice if we can at least prevent a complete crash by
230 * at least providing some sort of implementation until one can be provided
231 * nicely in a kext.
232 */
233 void (*voidret_fp)() = (void*)fakeEfiPages->voidret_instructions;
234 void (*unsupportedret_fp)() = (void*)fakeEfiPages->unsupportedret_instructions;
235 efiRuntimeServices->GetTime = ptov64((EFI_PTR32)unsupportedret_fp);
236 efiRuntimeServices->SetTime = ptov64((EFI_PTR32)unsupportedret_fp);
237 efiRuntimeServices->GetWakeupTime = ptov64((EFI_PTR32)unsupportedret_fp);
238 efiRuntimeServices->SetWakeupTime = ptov64((EFI_PTR32)unsupportedret_fp);
239 efiRuntimeServices->SetVirtualAddressMap = ptov64((EFI_PTR32)unsupportedret_fp);
240 efiRuntimeServices->ConvertPointer = ptov64((EFI_PTR32)unsupportedret_fp);
241 efiRuntimeServices->GetVariable = ptov64((EFI_PTR32)unsupportedret_fp);
242 efiRuntimeServices->GetNextVariableName = ptov64((EFI_PTR32)unsupportedret_fp);
243 efiRuntimeServices->SetVariable = ptov64((EFI_PTR32)unsupportedret_fp);
244 efiRuntimeServices->GetNextHighMonotonicCount = ptov64((EFI_PTR32)unsupportedret_fp);
245 efiRuntimeServices->ResetSystem = ptov64((EFI_PTR32)voidret_fp);
246
247 /* We're done. Now CRC32 the thing so the kernel will accept it */
248 efiRuntimeServices->Hdr.CRC32 = crc32(0L, efiRuntimeServices, efiRuntimeServices->Hdr.HeaderSize);
249
250
251 /* -------------------------------------------------------------------- */
252 /* Finish filling in the rest of the boot args that we need. */
253 bootArgs->efiSystemTable = (uint32_t)efiSystemTable;
254 bootArgs->efiMode = kBootArgsEfiMode64;
255
256 /* The bootArgs structure as a whole is bzero'd so we don't need to fill in
257 * things like efiRuntimeServices* and what not.
258 *
259 * In fact, the only code that seems to use that is the hibernate code so it
260 * knows not to save the pages. It even checks to make sure its nonzero.
261 */
262}
263
264/*
265In addition to the EFI tables there is also the EFI device tree node.
266In particular, we need /efi/platform to have an FSBFrequency key. Without it,
267the tsc_init function will panic very early on in kernel startup, before
268the console is available.
269*/
270
271/*==========================================================================
272 * FSB Frequency detection
273 */
274
275/* These should be const but DT__AddProperty takes char* */
276static char TSC_Frequency_prop[] = "TSCFrequency";
277static char FSB_Frequency_prop[] = "FSBFrequency";
278static char CPU_Frequency_prop[] = "CPUFrequency";
279
280/*==========================================================================
281 * SystemType
282 */
283
284static char SystemType_prop[] = "system-type";
285
286/*==========================================================================
287 * SMBIOS
288 */
289
290/* From Foundation/Efi/Guid/Smbios/SmBios.h */
291/* Modified to wrap Data4 array init with {} */
292#define EFI_SMBIOS_TABLE_GUID \
293 { \
294 0xeb9d2d31, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} \
295 }
296
297/* From Foundation/Efi/Guid/Smbios/SmBios.c */
298EFI_GUID const gEfiSmbiosTableGuid = EFI_SMBIOS_TABLE_GUID;
299
300#define SMBIOS_RANGE_START 0x000F0000
301#define SMBIOS_RANGE_END 0x000FFFFF
302
303/* '_SM_' in little endian: */
304#define SMBIOS_ANCHOR_UINT32_LE 0x5f4d535f
305
306#define EFI_ACPI_TABLE_GUID \
307 { \
308 0xeb9d2d30, 0x2d88, 0x11d3, { 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
309 }
310
311#define EFI_ACPI_20_TABLE_GUID \
312 { \
313 0x8868e871, 0xe4f1, 0x11d3, { 0xbc, 0x22, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } \
314 }
315
316EFI_GUID gEfiAcpiTableGuid = EFI_ACPI_TABLE_GUID;
317EFI_GUID gEfiAcpi20TableGuid = EFI_ACPI_20_TABLE_GUID;
318
319
320/*==========================================================================
321 * Fake EFI implementation
322 */
323
324/* These should be const but DT__AddProperty takes char* */
325static char FIRMWARE_REVISION_PROP[] = "firmware-revision";
326static char FIRMWARE_ABI_PROP[] = "firmware-abi";
327static char FIRMWARE_VENDOR_PROP[] = "firmware-vendor";
328static char FIRMWARE_ABI_PROP_VALUE[] = "EFI64";
329
330void
331setupEfiDeviceTree(void)
332{
333 Node *node;
334 node = DT__FindNode("/", false);
335 if (node == 0) {
336 stop("Couldn't get root node");
337 }
338
339 /* Export system-type */
340 verbose("Using system-type=0x%02x\n", Platform.Type);
341 DT__AddProperty(node, SystemType_prop, sizeof(Platform.Type), &Platform.Type);
342
343 /* We could also just do DT__FindNode("/efi/platform", true)
344 * But I think eventually we want to fill stuff in the efi node
345 * too so we might as well create it so we have a pointer for it too.
346 */
347 node = DT__AddChild(node, "efi");
348
349 DT__AddProperty(node, FIRMWARE_REVISION_PROP, sizeof(FIRMWARE_REVISION), (EFI_UINT32*)&FIRMWARE_REVISION);
350 DT__AddProperty(node, FIRMWARE_ABI_PROP, sizeof(FIRMWARE_ABI_PROP_VALUE), (char*)FIRMWARE_ABI_PROP_VALUE);
351 DT__AddProperty(node, FIRMWARE_VENDOR_PROP, sizeof(FIRMWARE_VENDOR), (EFI_CHAR16*)FIRMWARE_VENDOR);
352
353 /* TODO: Fill in other efi properties if necessary */
354
355 /* Set up the /efi/runtime-services table node similar to the way a child node of configuration-table
356 * is set up. That is, name and table properties */
357 Node *runtimeServicesNode = DT__AddChild(node, "runtime-services");
358
359 /* The value of the table property is the 32-bit physical address for the RuntimeServices table.
360 * Sice the EFI system table already has a pointer to it, we simply use the address of that pointer
361 * for the pointer to the property data. Warning.. DT finalization calls free on that but we're not
362 * the only thing to use a non-malloc'd pointer for something in the DT
363 */
364 DT__AddProperty(runtimeServicesNode, "table", sizeof(uint64_t), &gST->RuntimeServices);
365
366 /* Set up the /efi/configuration-table node which will eventually have several child nodes for
367 * all of the configuration tables needed by various kernel extensions.
368 */
369 gEfiConfigurationTableNode = DT__AddChild(node, "configuration-table");
370
371 /* Now fill in the /efi/platform Node */
372 Node *efiPlatformNode = DT__AddChild(node, "platform");
373
374 /* NOTE WELL: If you do add FSB Frequency detection, make sure to store
375 * the value in the fsbFrequency global and not an malloc'd pointer
376 * because the DT_AddProperty function does not copy its args.
377 */
378 if(Platform.CPU.FSBFrequency != 0)
379 DT__AddProperty(efiPlatformNode, FSB_Frequency_prop, sizeof(uint64_t), &Platform.CPU.FSBFrequency);
380
381/* Export TSC and CPU frequencies for use by the kernel or KEXTs
382 */
383 if(Platform.CPU.TSCFrequency != 0)
384 DT__AddProperty(efiPlatformNode, TSC_Frequency_prop, sizeof(uint64_t), &Platform.CPU.TSCFrequency);
385 if(Platform.CPU.CPUFrequency != 0)
386 DT__AddProperty(efiPlatformNode, CPU_Frequency_prop, sizeof(uint64_t), &Platform.CPU.CPUFrequency);
387
388 /* Fill /efi/device-properties node.
389 */
390 setupDeviceProperties(node);
391}
392
393/* Installs all the needed configuration table entries */
394void setupEfiConfigurationTable()
395{
396 smbios_p = (EFI_PTR32)getSmbios();
397 addConfigurationTable(&gEfiSmbiosTableGuid, &smbios_p, NULL);
398
399 // Setup ACPI with DSDT overrides (mackerintel's patch)
400 setupAcpi();
401
402 // We've obviously changed the count.. so fix up the CRC32
403 fixupEfiSystemTableCRC32(gST);
404}
405
406void setupEfiDevices(void)
407{
408setup_pci_devs(root_pci_dev);
409}
410
411/* Entrypoint from boot.c */
412void setupFakeEfi(void)
413{
414// Generate efi device strings
415setupEfiDevices();
416
417// Initialize the base table
418setupEfiTables();
419
420 // Initialize the device tree
421 setupEfiDeviceTree();
422
423 // Add configuration table entries to both the services table and the device tree
424 setupEfiConfigurationTable();
425}
426

Archive Download this file

Revision: 19