2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2009 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/file.h>
21 #include <grub/disk.h>
23 #include <grub/cpu/xnu.h>
25 #include <grub/loader.h>
26 #include <grub/autoefi.h>
27 #include <grub/i386/tsc.h>
28 #include <grub/i386/pit.h>
29 #include <grub/misc.h>
30 #include <grub/term.h>
32 char grub_xnu_cmdline
[1024];
34 /* Aliases set for some tables. */
41 struct tbl_alias table_aliases
[] =
43 {GRUB_EFI_ACPI_20_TABLE_GUID
, "ACPI_20"},
44 {GRUB_EFI_ACPI_TABLE_GUID
, "ACPI"},
47 /* The following function is used to be able to debug xnu loader
51 grub_xnu_launch (void)
53 grub_printf ("Fake launch %x:%p:%p", grub_xnu_entry_point
, grub_xnu_arg1
,
59 static void (*grub_xnu_launch
) (void) = 0;
63 utf16_strlen (grub_uint16_t
*in
)
66 for (i
= 0; in
[i
]; i
++);
70 /* Read frequency from a string in MHz and return it in Hz. */
72 readfrequency (const char *str
)
74 grub_uint64_t num
= 0;
82 digit
= grub_tolower (*str
) - '0';
88 num
= num
* 10 + digit
;
99 digit
= grub_tolower (*str
) - '0';
106 num
= num
+ mul
* digit
;
116 /* Thanks to Kabyl for precious information about Intel architecture. */
120 const grub_uint64_t sane_value
= 100000000;
121 grub_uint32_t manufacturer
[3], max_cpuid
, capabilities
, msrlow
;
122 grub_uint64_t start_tsc
;
123 grub_uint64_t end_tsc
;
124 grub_uint64_t tsc_ticks_per_ms
;
126 if (! grub_cpu_is_cpuid_supported ())
130 asm volatile ("movl $0, %%eax\n"
143 "=d" (manufacturer
[1]), "=c" (manufacturer
[2]));
145 /* Only Intel for now is done. */
146 if (grub_memcmp (manufacturer
+ 1, "ineIntel", 12) != 0)
150 asm volatile ("movl $0, %%eax\n"
152 : "=a" (max_cpuid
), "=b" (manufacturer
[0]),
153 "=d" (manufacturer
[1]), "=c" (manufacturer
[2]));
155 /* Only Intel for now is done. */
156 if (grub_memcmp (manufacturer
, "GenuineIntel", 12) != 0)
160 /* Check Speedstep. */
165 asm volatile ("movl $1, %%eax\n"
177 : "=c" (capabilities
):
180 asm volatile ("movl $1, %%eax\n"
182 : "=c" (capabilities
):
183 : "%rax", "%rbx", "%rdx");
186 if (! (capabilities
& (1 << 7)))
189 /* Calibrate the TSC rate. */
191 start_tsc
= grub_get_tsc ();
192 grub_pit_wait (0xffff);
193 end_tsc
= grub_get_tsc ();
195 tsc_ticks_per_ms
= grub_divmod64 (end_tsc
- start_tsc
, 55, 0);
197 /* Read the multiplier. */
198 asm volatile ("movl $0x198, %%ecx\n"
204 return grub_divmod64 (2000 * tsc_ticks_per_ms
,
205 ((msrlow
>> 7) & 0x3e) + ((msrlow
>> 14) & 1), 0);
208 /* Fill device tree. */
209 /* FIXME: some entries may be platform-agnostic. Move them to loader/xnu.c. */
211 grub_cpu_xnu_fill_devicetree (void)
213 struct grub_xnu_devtree_key
*efikey
;
214 struct grub_xnu_devtree_key
*cfgtablekey
;
215 struct grub_xnu_devtree_key
*curval
;
216 struct grub_xnu_devtree_key
*runtimesrvkey
;
217 struct grub_xnu_devtree_key
*platformkey
;
221 err
= grub_autoefi_prepare ();
225 /* The value "model". */
226 /* FIXME: may this value be sometimes different? */
227 curval
= grub_xnu_create_value (&grub_xnu_devtree_root
, "model");
230 curval
->datasize
= sizeof ("ACPI");
231 curval
->data
= grub_strdup ("ACPI");
232 curval
= grub_xnu_create_value (&grub_xnu_devtree_root
, "compatible");
235 curval
->datasize
= sizeof ("ACPI");
236 curval
->data
= grub_strdup ("ACPI");
239 efikey
= grub_xnu_create_key (&grub_xnu_devtree_root
, "efi");
243 /* Information about firmware. */
244 curval
= grub_xnu_create_value (&(efikey
->first_child
), "firmware-revision");
247 curval
->datasize
= (SYSTEM_TABLE_SIZEOF (firmware_revision
));
248 curval
->data
= grub_malloc (curval
->datasize
);
250 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "couldn't create device tree");
251 grub_memcpy (curval
->data
, (SYSTEM_TABLE_VAR(firmware_revision
)),
254 curval
= grub_xnu_create_value (&(efikey
->first_child
), "firmware-vendor");
258 2 * (utf16_strlen (SYSTEM_TABLE_PTR (firmware_vendor
)) + 1);
259 curval
->data
= grub_malloc (curval
->datasize
);
261 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "couldn't create device tree");
262 grub_memcpy (curval
->data
, SYSTEM_TABLE_PTR (firmware_vendor
),
265 curval
= grub_xnu_create_value (&(efikey
->first_child
), "firmware-abi");
268 curval
->datasize
= sizeof ("EFI32");
269 curval
->data
= grub_malloc (curval
->datasize
);
271 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "couldn't create device tree");
272 if (SIZEOF_OF_UINTN
== 4)
273 grub_memcpy (curval
->data
, "EFI32", curval
->datasize
);
275 grub_memcpy (curval
->data
, "EFI64", curval
->datasize
);
277 /* The key "platform". */
278 platformkey
= grub_xnu_create_key (&(efikey
->first_child
),
283 /* Pass FSB frequency to the kernel. */
284 curval
= grub_xnu_create_value (&(platformkey
->first_child
), "FSBFrequency");
287 curval
->datasize
= sizeof (grub_uint64_t
);
288 curval
->data
= grub_malloc (curval
->datasize
);
290 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "couldn't create device tree");
292 /* First see if user supplies the value. */
293 char *fsbvar
= grub_env_get ("fsb");
295 *((grub_uint64_t
*) curval
->data
) = 0;
297 *((grub_uint64_t
*) curval
->data
) = readfrequency (fsbvar
);
298 /* Try autodetect. */
299 if (! *((grub_uint64_t
*) curval
->data
))
300 *((grub_uint64_t
*) curval
->data
) = guessfsb ();
301 grub_dprintf ("xnu", "fsb autodetected as %llu\n",
302 (unsigned long long) *((grub_uint64_t
*) curval
->data
));
304 cfgtablekey
= grub_xnu_create_key (&(efikey
->first_child
),
305 "configuration-table");
309 /* Fill "configuration-table" key. */
310 for (i
= 0; i
< SYSTEM_TABLE (num_table_entries
); i
++)
313 struct grub_xnu_devtree_key
*curkey
;
314 grub_efi_guid_t guid
;
317 /* Retrieve current key. */
318 #ifdef GRUB_MACHINE_EFI
321 grub_efi_system_table
->configuration_table
[i
].vendor_table
;
322 guid
= grub_efi_system_table
->configuration_table
[i
].vendor_guid
;
325 if (SIZEOF_OF_UINTN
== 4)
327 ptr
= UINT_TO_PTR (((grub_efiemu_configuration_table32_t
*)
328 SYSTEM_TABLE_PTR (configuration_table
))[i
]
331 ((grub_efiemu_configuration_table32_t
*)
332 SYSTEM_TABLE_PTR (configuration_table
))[i
].vendor_guid
;
336 ptr
= UINT_TO_PTR (((grub_efiemu_configuration_table64_t
*)
337 SYSTEM_TABLE_PTR (configuration_table
))[i
]
340 ((grub_efiemu_configuration_table64_t
*)
341 SYSTEM_TABLE_PTR (configuration_table
))[i
].vendor_guid
;
345 /* The name of key for new table. */
346 grub_sprintf (guidbuf
, "%08x-%04x-%04x-%02x%02x-",
347 guid
.data1
, guid
.data2
, guid
.data3
, guid
.data4
[0],
349 for (j
= 2; j
< 8; j
++)
350 grub_sprintf (guidbuf
+ grub_strlen (guidbuf
), "%02x", guid
.data4
[j
]);
351 /* For some reason GUID has to be in uppercase. */
352 for (j
= 0; guidbuf
[j
] ; j
++)
353 if (guidbuf
[j
] >= 'a' && guidbuf
[j
] <= 'f')
354 guidbuf
[j
] += 'A' - 'a';
355 curkey
= grub_xnu_create_key (&(cfgtablekey
->first_child
), guidbuf
);
359 curval
= grub_xnu_create_value (&(curkey
->first_child
), "guid");
362 curval
->datasize
= sizeof (guid
);
363 curval
->data
= grub_malloc (curval
->datasize
);
365 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
366 "couldn't create device tree");
367 grub_memcpy (curval
->data
, &guid
, curval
->datasize
);
369 /* The value "table". */
370 curval
= grub_xnu_create_value (&(curkey
->first_child
), "table");
373 curval
->datasize
= SIZEOF_OF_UINTN
;
374 curval
->data
= grub_malloc (curval
->datasize
);
376 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
377 "couldn't create device tree");
378 if (SIZEOF_OF_UINTN
== 4)
379 *((grub_uint32_t
*)curval
->data
) = PTR_TO_UINT32 (ptr
);
381 *((grub_uint64_t
*)curval
->data
) = PTR_TO_UINT64 (ptr
);
384 for (j
= 0; j
< sizeof (table_aliases
) / sizeof (table_aliases
[0]); j
++)
385 if (grub_memcmp (&table_aliases
[j
].guid
, &guid
, sizeof (guid
)) == 0)
387 if (j
!= sizeof (table_aliases
) / sizeof (table_aliases
[0]))
389 curval
= grub_xnu_create_value (&(curkey
->first_child
), "alias");
392 curval
->datasize
= grub_strlen (table_aliases
[j
].name
) + 1;
393 curval
->data
= grub_malloc (curval
->datasize
);
395 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
396 "couldn't create device tree");
397 grub_memcpy (curval
->data
, table_aliases
[j
].name
, curval
->datasize
);
401 /* Create and fill "runtime-services" key. */
402 runtimesrvkey
= grub_xnu_create_key (&(efikey
->first_child
),
406 curval
= grub_xnu_create_value (&(runtimesrvkey
->first_child
), "table");
409 curval
->datasize
= SIZEOF_OF_UINTN
;
410 curval
->data
= grub_malloc (curval
->datasize
);
412 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
413 "couldn't create device tree");
414 if (SIZEOF_OF_UINTN
== 4)
415 *((grub_uint32_t
*) curval
->data
)
416 = PTR_TO_UINT32 (SYSTEM_TABLE_PTR (runtime_services
));
418 *((grub_uint64_t
*) curval
->data
)
419 = PTR_TO_UINT64 (SYSTEM_TABLE_PTR (runtime_services
));
421 return GRUB_ERR_NONE
;
428 struct grub_xnu_boot_params
*bootparams_relloc
;
429 grub_off_t bootparams_relloc_off
;
430 grub_off_t mmap_relloc_off
;
432 grub_efi_uintn_t memory_map_size
= 0;
433 grub_efi_memory_descriptor_t
*memory_map
;
434 grub_efi_uintn_t map_key
= 0;
435 grub_efi_uintn_t descriptor_size
= 0;
436 grub_efi_uint32_t descriptor_version
= 0;
437 grub_uint64_t firstruntimeaddr
, lastruntimeaddr
;
439 grub_size_t devtreelen
;
442 /* Page-align to avoid following parts to be inadvertently freed. */
443 err
= grub_xnu_align_heap (GRUB_XNU_PAGESIZE
);
447 /* Pass memory map to kernel. */
452 descriptor_version
= 0;
454 if (grub_autoefi_get_memory_map (&memory_map_size
, memory_map
,
455 &map_key
, &descriptor_size
,
456 &descriptor_version
) < 0)
459 memory_map
= grub_xnu_heap_malloc (memory_map_size
);
463 if (grub_autoefi_get_memory_map (&memory_map_size
, memory_map
,
464 &map_key
, &descriptor_size
,
465 &descriptor_version
) <= 0)
467 mmap_relloc_off
= (grub_uint8_t
*) memory_map
468 - (grub_uint8_t
*) grub_xnu_heap_start
;
470 firstruntimeaddr
= (grub_uint64_t
) (-1);
472 for (i
= 0; (unsigned) i
< memory_map_size
/ descriptor_size
; i
++)
474 grub_efi_memory_descriptor_t
*curdesc
= (grub_efi_memory_descriptor_t
*)
475 ((char *) memory_map
+ descriptor_size
* i
);
477 /* Some EFI implementations set physical_start to 0 which
479 curdesc
->virtual_start
= curdesc
->physical_start
;
481 if (curdesc
->type
== GRUB_EFI_RUNTIME_SERVICES_DATA
482 || curdesc
->type
== GRUB_EFI_RUNTIME_SERVICES_CODE
)
484 if (firstruntimeaddr
> curdesc
->physical_start
)
485 firstruntimeaddr
= curdesc
->physical_start
;
486 if (lastruntimeaddr
< curdesc
->physical_start
487 + curdesc
->num_pages
* 4096)
488 lastruntimeaddr
= curdesc
->physical_start
489 + curdesc
->num_pages
* 4096;
493 /* Relocate the boot parameters to heap. */
494 bootparams_relloc
= grub_xnu_heap_malloc (sizeof (*bootparams_relloc
));
495 if (! bootparams_relloc
)
497 bootparams_relloc_off
= (grub_uint8_t
*) bootparams_relloc
498 - (grub_uint8_t
*) grub_xnu_heap_start
;
499 err
= grub_xnu_writetree_toheap (&devtree
, &devtreelen
);
502 bootparams_relloc
= (struct grub_xnu_boot_params
*)
503 (bootparams_relloc_off
+ (grub_uint8_t
*) grub_xnu_heap_start
);
505 grub_memcpy (bootparams_relloc
->cmdline
, grub_xnu_cmdline
,
506 sizeof (bootparams_relloc
->cmdline
));
508 bootparams_relloc
->devtree
= ((char *) devtree
- grub_xnu_heap_start
)
509 + grub_xnu_heap_will_be_at
;
510 bootparams_relloc
->devtreelen
= devtreelen
;
512 bootparams_relloc
->heap_start
= grub_xnu_heap_will_be_at
;
513 bootparams_relloc
->heap_size
= grub_xnu_heap_size
;
515 bootparams_relloc
->efi_mmap
= grub_xnu_heap_will_be_at
+ mmap_relloc_off
;
516 bootparams_relloc
->efi_mmap_size
= memory_map_size
;
517 bootparams_relloc
->efi_mem_desc_size
= descriptor_size
;
518 bootparams_relloc
->efi_mem_desc_version
= descriptor_version
;
520 bootparams_relloc
->efi_runtime_first_page
= firstruntimeaddr
522 bootparams_relloc
->efi_runtime_npages
523 = ((lastruntimeaddr
+ GRUB_XNU_PAGESIZE
- 1) / GRUB_XNU_PAGESIZE
)
524 - (firstruntimeaddr
/ GRUB_XNU_PAGESIZE
);
525 bootparams_relloc
->efi_uintnbits
= SIZEOF_OF_UINTN
* 8;
526 bootparams_relloc
->efi_system_table
527 = PTR_TO_UINT32 (grub_autoefi_system_table
);
529 bootparams_relloc
->verminor
= GRUB_XNU_BOOTARGS_VERMINOR
;
530 bootparams_relloc
->vermajor
= GRUB_XNU_BOOTARGS_VERMAJOR
;
532 /* Parameters for asm helper. */
533 grub_xnu_stack
= bootparams_relloc
->heap_start
534 + bootparams_relloc
->heap_size
+ GRUB_XNU_PAGESIZE
;
535 grub_xnu_arg1
= bootparams_relloc_off
+ grub_xnu_heap_will_be_at
;
537 grub_xnu_launch
= (void (*) (void))
538 (grub_xnu_heap_start
+ grub_xnu_heap_size
);
540 grub_dprintf ("xnu", "eip=%x\n", grub_xnu_entry_point
);
541 grub_dprintf ("xnu", "launch=%p\n", grub_xnu_launch
);
543 const char *debug
= grub_env_get ("debug");
545 if (debug
&& (grub_strword (debug
, "all") || grub_strword (debug
, "xnu")))
547 grub_printf ("Press any key to launch xnu\n");
552 err
= grub_xnu_set_video (bootparams_relloc
);
553 if (err
!= GRUB_ERR_NONE
)
556 grub_errno
= GRUB_ERR_NONE
;
557 grub_printf ("Booting in blind mode\n");
559 bootparams_relloc
->lfb_mode
= 0;
560 bootparams_relloc
->lfb_width
= 0;
561 bootparams_relloc
->lfb_height
= 0;
562 bootparams_relloc
->lfb_depth
= 0;
563 bootparams_relloc
->lfb_line_len
= 0;
564 bootparams_relloc
->lfb_base
= 0;
567 grub_memcpy (grub_xnu_heap_start
+ grub_xnu_heap_size
,
568 grub_xnu_launcher_start
,
569 grub_xnu_launcher_end
- grub_xnu_launcher_start
);
572 if (! grub_autoefi_finish_boot_services ())
573 return grub_error (GRUB_ERR_IO
, "can't exit boot services");
577 /* Never reaches here. */