Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / loader / i386 / xnu.c
blobe0506a67633e334eb207aa4df4947c93b4c29d84
1 /*
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/>.
19 #include <grub/env.h>
20 #include <grub/file.h>
21 #include <grub/disk.h>
22 #include <grub/xnu.h>
23 #include <grub/cpu/xnu.h>
24 #include <grub/mm.h>
25 #include <grub/loader.h>
26 #include <grub/autoefi.h>
27 #include <grub/i386/tsc.h>
28 #include <grub/i386/cpuid.h>
29 #include <grub/efi/api.h>
30 #include <grub/i386/pit.h>
31 #include <grub/misc.h>
32 #include <grub/charset.h>
33 #include <grub/term.h>
34 #include <grub/command.h>
35 #include <grub/i18n.h>
36 #include <grub/bitmap_scale.h>
37 #include <grub/cpu/io.h>
39 #define min(a,b) (((a) < (b)) ? (a) : (b))
40 #define max(a,b) (((a) > (b)) ? (a) : (b))
42 #define DEFAULT_VIDEO_MODE "auto"
44 char grub_xnu_cmdline[1024];
45 grub_uint32_t grub_xnu_entry_point, grub_xnu_arg1, grub_xnu_stack;
47 /* Aliases set for some tables. */
48 struct tbl_alias
50 grub_efi_guid_t guid;
51 const char *name;
54 static struct tbl_alias table_aliases[] =
56 {GRUB_EFI_ACPI_20_TABLE_GUID, "ACPI_20"},
57 {GRUB_EFI_ACPI_TABLE_GUID, "ACPI"},
60 struct grub_xnu_devprop_device_descriptor
62 struct grub_xnu_devprop_device_descriptor *next;
63 struct grub_xnu_devprop_device_descriptor **prev;
64 struct property_descriptor *properties;
65 struct grub_efi_device_path *path;
66 int pathlen;
69 static int
70 utf16_strlen (grub_uint16_t *in)
72 int i;
73 for (i = 0; in[i]; i++);
74 return i;
77 /* Read frequency from a string in MHz and return it in Hz. */
78 static grub_uint64_t
79 readfrequency (const char *str)
81 grub_uint64_t num = 0;
82 int mul = 1000000;
83 int found = 0;
85 while (*str)
87 unsigned long digit;
89 digit = grub_tolower (*str) - '0';
90 if (digit > 9)
91 break;
93 found = 1;
95 num = num * 10 + digit;
96 str++;
98 num *= 1000000;
99 if (*str == '.')
101 str++;
102 while (*str)
104 unsigned long digit;
106 digit = grub_tolower (*str) - '0';
107 if (digit > 9)
108 break;
110 found = 1;
112 mul /= 10;
113 num = num + mul * digit;
114 str++;
117 if (! found)
118 return 0;
120 return num;
123 /* Thanks to Kabyl for precious information about Intel architecture. */
124 static grub_uint64_t
125 guessfsb (void)
127 const grub_uint64_t sane_value = 100000000;
128 grub_uint32_t manufacturer[3], max_cpuid, capabilities, msrlow;
129 grub_uint32_t a, b, d, divisor;
131 if (! grub_cpu_is_cpuid_supported ())
132 return sane_value;
134 grub_cpuid (0, max_cpuid, manufacturer[0], manufacturer[2], manufacturer[1]);
136 /* Only Intel for now is done. */
137 if (grub_memcmp (manufacturer, "GenuineIntel", 12) != 0)
138 return sane_value;
140 /* Check Speedstep. */
141 if (max_cpuid < 1)
142 return sane_value;
144 grub_cpuid (1, a, b, capabilities, d);
146 if (! (capabilities & (1 << 7)))
147 return sane_value;
149 /* Read the multiplier. */
150 asm volatile ("movl $0x198, %%ecx\n"
151 "rdmsr"
152 : "=d" (msrlow)
154 : "%ecx", "%eax");
156 grub_uint64_t v;
157 grub_uint32_t r;
159 /* (2000ULL << 32) / grub_tsc_rate */
160 /* Assumption: TSC frequency is over 2 MHz. */
161 v = 0xffffffff / grub_tsc_rate;
162 v *= 2000;
163 /* v is at most 2000 off from (2000ULL << 32) / grub_tsc_rate.
164 Since grub_tsc_rate < 2^32/2^11=2^21, so no overflow.
166 r = (2000ULL << 32) - v * grub_tsc_rate;
167 v += r / grub_tsc_rate;
169 divisor = ((msrlow >> 7) & 0x3e) | ((msrlow >> 14) & 1);
170 if (divisor == 0)
171 return sane_value;
172 return grub_divmod64 (v, divisor, 0);
175 struct property_descriptor
177 struct property_descriptor *next;
178 struct property_descriptor **prev;
179 grub_uint8_t *name;
180 grub_uint16_t *name16;
181 int name16len;
182 int length;
183 void *data;
186 static struct grub_xnu_devprop_device_descriptor *devices = 0;
188 grub_err_t
189 grub_xnu_devprop_remove_property (struct grub_xnu_devprop_device_descriptor *dev,
190 char *name)
192 struct property_descriptor *prop;
193 prop = grub_named_list_find (GRUB_AS_NAMED_LIST (dev->properties), name);
194 if (!prop)
195 return GRUB_ERR_NONE;
197 grub_free (prop->name);
198 grub_free (prop->name16);
199 grub_free (prop->data);
201 grub_list_remove (GRUB_AS_LIST (prop));
203 return GRUB_ERR_NONE;
206 grub_err_t
207 grub_xnu_devprop_remove_device (struct grub_xnu_devprop_device_descriptor *dev)
209 void *t;
210 struct property_descriptor *prop;
212 grub_list_remove (GRUB_AS_LIST (dev));
214 for (prop = dev->properties; prop; )
216 grub_free (prop->name);
217 grub_free (prop->name16);
218 grub_free (prop->data);
219 t = prop;
220 prop = prop->next;
221 grub_free (t);
224 grub_free (dev->path);
225 grub_free (dev);
227 return GRUB_ERR_NONE;
230 struct grub_xnu_devprop_device_descriptor *
231 grub_xnu_devprop_add_device (struct grub_efi_device_path *path, int length)
233 struct grub_xnu_devprop_device_descriptor *ret;
235 ret = grub_zalloc (sizeof (*ret));
236 if (!ret)
237 return 0;
239 ret->path = grub_malloc (length);
240 if (!ret->path)
242 grub_free (ret);
243 return 0;
245 ret->pathlen = length;
246 grub_memcpy (ret->path, path, length);
248 grub_list_push (GRUB_AS_LIST_P (&devices), GRUB_AS_LIST (ret));
250 return ret;
253 static grub_err_t
254 grub_xnu_devprop_add_property (struct grub_xnu_devprop_device_descriptor *dev,
255 grub_uint8_t *utf8, grub_uint16_t *utf16,
256 int utf16len, void *data, int datalen)
258 struct property_descriptor *prop;
260 prop = grub_malloc (sizeof (*prop));
261 if (!prop)
262 return grub_errno;
264 prop->name = utf8;
265 prop->name16 = utf16;
266 prop->name16len = utf16len;
268 prop->length = datalen;
269 prop->data = grub_malloc (prop->length);
270 if (!prop->data)
272 grub_free (prop);
273 grub_free (prop->name);
274 grub_free (prop->name16);
275 return grub_errno;
277 grub_memcpy (prop->data, data, prop->length);
278 grub_list_push (GRUB_AS_LIST_P (&dev->properties),
279 GRUB_AS_LIST (prop));
280 return GRUB_ERR_NONE;
283 grub_err_t
284 grub_xnu_devprop_add_property_utf8 (struct grub_xnu_devprop_device_descriptor *dev,
285 char *name, void *data, int datalen)
287 grub_uint8_t *utf8;
288 grub_uint16_t *utf16;
289 int len, utf16len;
290 grub_err_t err;
292 utf8 = (grub_uint8_t *) grub_strdup (name);
293 if (!utf8)
294 return grub_errno;
296 len = grub_strlen (name);
297 utf16 = grub_malloc (sizeof (grub_uint16_t) * len);
298 if (!utf16)
300 grub_free (utf8);
301 return grub_errno;
304 utf16len = grub_utf8_to_utf16 (utf16, len, utf8, len, NULL);
305 if (utf16len < 0)
307 grub_free (utf8);
308 grub_free (utf16);
309 return grub_errno;
312 err = grub_xnu_devprop_add_property (dev, utf8, utf16,
313 utf16len, data, datalen);
314 if (err)
316 grub_free (utf8);
317 grub_free (utf16);
318 return err;
321 return GRUB_ERR_NONE;
324 grub_err_t
325 grub_xnu_devprop_add_property_utf16 (struct grub_xnu_devprop_device_descriptor *dev,
326 grub_uint16_t *name, int namelen,
327 void *data, int datalen)
329 grub_uint8_t *utf8;
330 grub_uint16_t *utf16;
331 grub_err_t err;
333 utf16 = grub_malloc (sizeof (grub_uint16_t) * namelen);
334 if (!utf16)
335 return grub_errno;
336 grub_memcpy (utf16, name, sizeof (grub_uint16_t) * namelen);
338 utf8 = grub_malloc (namelen * 4 + 1);
339 if (!utf8)
341 grub_free (utf8);
342 return grub_errno;
345 *grub_utf16_to_utf8 ((grub_uint8_t *) utf8, name, namelen) = '\0';
347 err = grub_xnu_devprop_add_property (dev, utf8, utf16,
348 namelen, data, datalen);
349 if (err)
351 grub_free (utf8);
352 grub_free (utf16);
353 return err;
356 return GRUB_ERR_NONE;
359 void
360 grub_cpu_xnu_unload (void)
362 struct grub_xnu_devprop_device_descriptor *dev1, *dev2;
364 for (dev1 = devices; dev1; )
366 dev2 = dev1->next;
367 grub_xnu_devprop_remove_device (dev1);
368 dev1 = dev2;
372 static grub_err_t
373 grub_cpu_xnu_fill_devprop (void)
375 struct grub_xnu_devtree_key *efikey;
376 int total_length = sizeof (struct grub_xnu_devprop_header);
377 struct grub_xnu_devtree_key *devprop;
378 struct grub_xnu_devprop_device_descriptor *device;
379 void *ptr;
380 struct grub_xnu_devprop_header *head;
381 void *t;
382 int numdevs = 0;
384 /* The key "efi". */
385 efikey = grub_xnu_create_key (&grub_xnu_devtree_root, "efi");
386 if (! efikey)
387 return grub_errno;
389 for (device = devices; device; device = device->next)
391 struct property_descriptor *propdesc;
392 total_length += sizeof (struct grub_xnu_devprop_device_header);
393 total_length += device->pathlen;
395 for (propdesc = device->properties; propdesc; propdesc = propdesc->next)
397 total_length += sizeof (grub_uint32_t);
398 total_length += sizeof (grub_uint16_t)
399 * (propdesc->name16len + 1);
400 total_length += sizeof (grub_uint32_t);
401 total_length += propdesc->length;
403 numdevs++;
406 devprop = grub_xnu_create_value (&(efikey->first_child), "device-properties");
407 if (!devprop)
408 return grub_errno;
410 devprop->data = grub_malloc (total_length);
411 devprop->datasize = total_length;
413 ptr = devprop->data;
414 head = ptr;
415 ptr = head + 1;
416 head->length = total_length;
417 head->alwaysone = 1;
418 head->num_devices = numdevs;
419 for (device = devices; device; )
421 struct grub_xnu_devprop_device_header *devhead;
422 struct property_descriptor *propdesc;
423 devhead = ptr;
424 devhead->num_values = 0;
425 ptr = devhead + 1;
427 grub_memcpy (ptr, device->path, device->pathlen);
428 ptr = (char *) ptr + device->pathlen;
430 for (propdesc = device->properties; propdesc; )
432 grub_uint32_t *len;
433 grub_uint16_t *name;
434 void *data;
436 len = ptr;
437 *len = 2 * propdesc->name16len + sizeof (grub_uint16_t)
438 + sizeof (grub_uint32_t);
439 ptr = len + 1;
441 name = ptr;
442 grub_memcpy (name, propdesc->name16, 2 * propdesc->name16len);
443 name += propdesc->name16len;
445 /* NUL terminator. */
446 *name = 0;
447 ptr = name + 1;
449 len = ptr;
450 *len = propdesc->length + sizeof (grub_uint32_t);
451 data = len + 1;
452 ptr = data;
453 grub_memcpy (ptr, propdesc->data, propdesc->length);
454 ptr = (char *) ptr + propdesc->length;
456 grub_free (propdesc->name);
457 grub_free (propdesc->name16);
458 grub_free (propdesc->data);
459 t = propdesc;
460 propdesc = propdesc->next;
461 grub_free (t);
462 devhead->num_values++;
465 devhead->length = (char *) ptr - (char *) devhead;
466 t = device;
467 device = device->next;
468 grub_free (t);
471 devices = 0;
473 return GRUB_ERR_NONE;
476 static grub_err_t
477 grub_cmd_devprop_load (grub_command_t cmd __attribute__ ((unused)),
478 int argc, char *args[])
480 grub_file_t file;
481 void *buf, *bufstart, *bufend;
482 struct grub_xnu_devprop_header *head;
483 grub_size_t size;
484 unsigned i, j;
486 if (argc != 1)
487 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
489 file = grub_file_open (args[0]);
490 if (! file)
491 return grub_errno;
492 size = grub_file_size (file);
493 buf = grub_malloc (size);
494 if (!buf)
496 grub_file_close (file);
497 return grub_errno;
499 if (grub_file_read (file, buf, size) != (grub_ssize_t) size)
501 grub_file_close (file);
502 return grub_errno;
504 grub_file_close (file);
506 bufstart = buf;
507 bufend = (char *) buf + size;
508 head = buf;
509 buf = head + 1;
510 for (i = 0; i < grub_le_to_cpu32 (head->num_devices) && buf < bufend; i++)
512 struct grub_efi_device_path *dp, *dpstart;
513 struct grub_xnu_devprop_device_descriptor *dev;
514 struct grub_xnu_devprop_device_header *devhead;
516 devhead = buf;
517 buf = devhead + 1;
518 dpstart = buf;
522 dp = buf;
523 buf = (char *) buf + GRUB_EFI_DEVICE_PATH_LENGTH (dp);
525 while (!GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp) && buf < bufend);
527 dev = grub_xnu_devprop_add_device (dpstart, (char *) buf
528 - (char *) dpstart);
530 for (j = 0; j < grub_le_to_cpu32 (devhead->num_values) && buf < bufend;
531 j++)
533 grub_uint32_t *namelen;
534 grub_uint32_t *datalen;
535 grub_uint16_t *utf16;
536 void *data;
537 grub_err_t err;
539 namelen = buf;
540 buf = namelen + 1;
541 if (buf >= bufend)
542 break;
544 utf16 = buf;
545 buf = (char *) buf + *namelen - sizeof (grub_uint32_t);
546 if (buf >= bufend)
547 break;
549 datalen = buf;
550 buf = datalen + 1;
551 if (buf >= bufend)
552 break;
554 data = buf;
555 buf = (char *) buf + *datalen - sizeof (grub_uint32_t);
556 if (buf >= bufend)
557 break;
558 err = grub_xnu_devprop_add_property_utf16
559 (dev, utf16, (*namelen - sizeof (grub_uint32_t)
560 - sizeof (grub_uint16_t)) / sizeof (grub_uint16_t),
561 data, *datalen - sizeof (grub_uint32_t));
562 if (err)
564 grub_free (bufstart);
565 return err;
570 grub_free (bufstart);
571 return GRUB_ERR_NONE;
574 /* Fill device tree. */
575 /* FIXME: some entries may be platform-agnostic. Move them to loader/xnu.c. */
576 static grub_err_t
577 grub_cpu_xnu_fill_devicetree (grub_uint64_t *fsbfreq_out)
579 struct grub_xnu_devtree_key *efikey;
580 struct grub_xnu_devtree_key *cfgtablekey;
581 struct grub_xnu_devtree_key *curval;
582 struct grub_xnu_devtree_key *runtimesrvkey;
583 struct grub_xnu_devtree_key *platformkey;
584 unsigned i, j;
586 /* The value "model". */
587 /* FIXME: may this value be sometimes different? */
588 curval = grub_xnu_create_value (&grub_xnu_devtree_root, "model");
589 if (! curval)
590 return grub_errno;
591 curval->datasize = sizeof ("ACPI");
592 curval->data = grub_strdup ("ACPI");
593 curval = grub_xnu_create_value (&grub_xnu_devtree_root, "compatible");
594 if (! curval)
595 return grub_errno;
596 curval->datasize = sizeof ("ACPI");
597 curval->data = grub_strdup ("ACPI");
599 /* The key "efi". */
600 efikey = grub_xnu_create_key (&grub_xnu_devtree_root, "efi");
601 if (! efikey)
602 return grub_errno;
604 /* Information about firmware. */
605 curval = grub_xnu_create_value (&(efikey->first_child), "firmware-revision");
606 if (! curval)
607 return grub_errno;
608 curval->datasize = (SYSTEM_TABLE_SIZEOF (firmware_revision));
609 curval->data = grub_malloc (curval->datasize);
610 if (! curval->data)
611 return grub_errno;
612 grub_memcpy (curval->data, (SYSTEM_TABLE_VAR(firmware_revision)),
613 curval->datasize);
615 curval = grub_xnu_create_value (&(efikey->first_child), "firmware-vendor");
616 if (! curval)
617 return grub_errno;
618 curval->datasize =
619 2 * (utf16_strlen (SYSTEM_TABLE_PTR (firmware_vendor)) + 1);
620 curval->data = grub_malloc (curval->datasize);
621 if (! curval->data)
622 return grub_errno;
623 grub_memcpy (curval->data, SYSTEM_TABLE_PTR (firmware_vendor),
624 curval->datasize);
626 curval = grub_xnu_create_value (&(efikey->first_child), "firmware-abi");
627 if (! curval)
628 return grub_errno;
629 curval->datasize = sizeof ("EFI32");
630 curval->data = grub_malloc (curval->datasize);
631 if (! curval->data)
632 return grub_errno;
633 if (SIZEOF_OF_UINTN == 4)
634 grub_memcpy (curval->data, "EFI32", curval->datasize);
635 else
636 grub_memcpy (curval->data, "EFI64", curval->datasize);
638 /* The key "platform". */
639 platformkey = grub_xnu_create_key (&(efikey->first_child),
640 "platform");
641 if (! platformkey)
642 return grub_errno;
644 /* Pass FSB frequency to the kernel. */
645 curval = grub_xnu_create_value (&(platformkey->first_child), "FSBFrequency");
646 if (! curval)
647 return grub_errno;
648 curval->datasize = sizeof (grub_uint64_t);
649 curval->data = grub_malloc (curval->datasize);
650 if (!curval->data)
651 return grub_errno;
653 /* First see if user supplies the value. */
654 const char *fsbvar = grub_env_get ("fsb");
655 grub_uint64_t fsbfreq = 0;
656 if (fsbvar)
657 fsbfreq = readfrequency (fsbvar);
658 /* Try autodetect. */
659 if (! fsbfreq)
660 fsbfreq = guessfsb ();
661 *((grub_uint64_t *) curval->data) = fsbfreq;
662 *fsbfreq_out = fsbfreq;
663 grub_dprintf ("xnu", "fsb autodetected as %llu\n",
664 (unsigned long long) *((grub_uint64_t *) curval->data));
666 cfgtablekey = grub_xnu_create_key (&(efikey->first_child),
667 "configuration-table");
668 if (!cfgtablekey)
669 return grub_errno;
671 /* Fill "configuration-table" key. */
672 for (i = 0; i < SYSTEM_TABLE (num_table_entries); i++)
674 void *ptr;
675 struct grub_xnu_devtree_key *curkey;
676 grub_efi_packed_guid_t guid;
677 char guidbuf[64];
679 /* Retrieve current key. */
680 #ifdef GRUB_MACHINE_EFI
682 ptr = (void *)
683 grub_efi_system_table->configuration_table[i].vendor_table;
684 guid = grub_efi_system_table->configuration_table[i].vendor_guid;
686 #else
687 if (SIZEOF_OF_UINTN == 4)
689 ptr = (void *) (grub_addr_t) ((grub_efiemu_configuration_table32_t *)
690 SYSTEM_TABLE_PTR (configuration_table))[i]
691 .vendor_table;
692 guid =
693 ((grub_efiemu_configuration_table32_t *)
694 SYSTEM_TABLE_PTR (configuration_table))[i].vendor_guid;
696 else
698 ptr = (void *) (grub_addr_t) ((grub_efiemu_configuration_table64_t *)
699 SYSTEM_TABLE_PTR (configuration_table))[i]
700 .vendor_table;
701 guid =
702 ((grub_efiemu_configuration_table64_t *)
703 SYSTEM_TABLE_PTR (configuration_table))[i].vendor_guid;
705 #endif
707 /* The name of key for new table. */
708 grub_snprintf (guidbuf, sizeof (guidbuf), "%08x-%04x-%04x-%02x%02x-",
709 guid.data1, guid.data2, guid.data3, guid.data4[0],
710 guid.data4[1]);
711 for (j = 2; j < 8; j++)
712 grub_snprintf (guidbuf + grub_strlen (guidbuf),
713 sizeof (guidbuf) - grub_strlen (guidbuf),
714 "%02x", guid.data4[j]);
715 /* For some reason GUID has to be in uppercase. */
716 for (j = 0; guidbuf[j] ; j++)
717 if (guidbuf[j] >= 'a' && guidbuf[j] <= 'f')
718 guidbuf[j] += 'A' - 'a';
719 curkey = grub_xnu_create_key (&(cfgtablekey->first_child), guidbuf);
720 if (! curkey)
721 return grub_errno;
723 curval = grub_xnu_create_value (&(curkey->first_child), "guid");
724 if (! curval)
725 return grub_errno;
726 curval->datasize = sizeof (guid);
727 curval->data = grub_malloc (curval->datasize);
728 if (! curval->data)
729 return grub_errno;
730 grub_memcpy (curval->data, &guid, curval->datasize);
732 /* The value "table". */
733 curval = grub_xnu_create_value (&(curkey->first_child), "table");
734 if (! curval)
735 return grub_errno;
736 curval->datasize = SIZEOF_OF_UINTN;
737 curval->data = grub_malloc (curval->datasize);
738 if (! curval->data)
739 return grub_errno;
740 if (SIZEOF_OF_UINTN == 4)
741 *((grub_uint32_t *) curval->data) = (grub_addr_t) ptr;
742 else
743 *((grub_uint64_t *) curval->data) = (grub_addr_t) ptr;
745 /* Create alias. */
746 for (j = 0; j < ARRAY_SIZE(table_aliases); j++)
747 if (grub_memcmp (&table_aliases[j].guid, &guid, sizeof (guid)) == 0)
748 break;
749 if (j != ARRAY_SIZE(table_aliases))
751 curval = grub_xnu_create_value (&(curkey->first_child), "alias");
752 if (!curval)
753 return grub_errno;
754 curval->datasize = grub_strlen (table_aliases[j].name) + 1;
755 curval->data = grub_malloc (curval->datasize);
756 if (!curval->data)
757 return grub_errno;
758 grub_memcpy (curval->data, table_aliases[j].name, curval->datasize);
762 /* Create and fill "runtime-services" key. */
763 runtimesrvkey = grub_xnu_create_key (&(efikey->first_child),
764 "runtime-services");
765 if (! runtimesrvkey)
766 return grub_errno;
767 curval = grub_xnu_create_value (&(runtimesrvkey->first_child), "table");
768 if (! curval)
769 return grub_errno;
770 curval->datasize = SIZEOF_OF_UINTN;
771 curval->data = grub_malloc (curval->datasize);
772 if (! curval->data)
773 return grub_errno;
774 if (SIZEOF_OF_UINTN == 4)
775 *((grub_uint32_t *) curval->data)
776 = (grub_addr_t) SYSTEM_TABLE_PTR (runtime_services);
777 else
778 *((grub_uint64_t *) curval->data)
779 = (grub_addr_t) SYSTEM_TABLE_PTR (runtime_services);
781 return GRUB_ERR_NONE;
784 grub_err_t
785 grub_xnu_boot_resume (void)
787 struct grub_relocator32_state state;
789 state.esp = grub_xnu_stack;
790 state.ebp = grub_xnu_stack;
791 state.eip = grub_xnu_entry_point;
792 state.eax = grub_xnu_arg1;
794 return grub_relocator32_boot (grub_xnu_relocator, state, 0);
797 /* Setup video for xnu. */
798 static grub_err_t
799 grub_xnu_set_video (struct grub_xnu_boot_params_common *params)
801 struct grub_video_mode_info mode_info;
802 char *tmp;
803 const char *modevar;
804 void *framebuffer;
805 grub_err_t err;
806 struct grub_video_bitmap *bitmap = NULL;
808 modevar = grub_env_get ("gfxpayload");
809 /* Consider only graphical 32-bit deep modes. */
810 if (! modevar || *modevar == 0)
811 err = grub_video_set_mode (DEFAULT_VIDEO_MODE,
812 GRUB_VIDEO_MODE_TYPE_PURE_TEXT
813 | GRUB_VIDEO_MODE_TYPE_DEPTH_MASK,
814 32 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS);
815 else
817 tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
818 if (! tmp)
819 return grub_errno;
820 err = grub_video_set_mode (tmp,
821 GRUB_VIDEO_MODE_TYPE_PURE_TEXT
822 | GRUB_VIDEO_MODE_TYPE_DEPTH_MASK,
823 32 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS);
824 grub_free (tmp);
827 if (err)
828 return err;
830 err = grub_video_get_info (&mode_info);
831 if (err)
832 return err;
834 if (grub_xnu_bitmap)
836 if (grub_xnu_bitmap_mode == GRUB_XNU_BITMAP_STRETCH)
837 err = grub_video_bitmap_create_scaled (&bitmap,
838 mode_info.width,
839 mode_info.height,
840 grub_xnu_bitmap,
841 GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
842 else
843 bitmap = grub_xnu_bitmap;
846 if (bitmap)
848 if (grub_xnu_bitmap_mode == GRUB_XNU_BITMAP_STRETCH)
849 err = grub_video_bitmap_create_scaled (&bitmap,
850 mode_info.width,
851 mode_info.height,
852 grub_xnu_bitmap,
853 GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
854 else
855 bitmap = grub_xnu_bitmap;
858 if (bitmap)
860 int x, y;
862 x = mode_info.width - bitmap->mode_info.width;
863 x /= 2;
864 y = mode_info.height - bitmap->mode_info.height;
865 y /= 2;
866 err = grub_video_blit_bitmap (bitmap,
867 GRUB_VIDEO_BLIT_REPLACE,
868 x > 0 ? x : 0,
869 y > 0 ? y : 0,
870 x < 0 ? -x : 0,
871 y < 0 ? -y : 0,
872 min (bitmap->mode_info.width,
873 mode_info.width),
874 min (bitmap->mode_info.height,
875 mode_info.height));
877 if (err)
879 grub_print_error ();
880 grub_errno = GRUB_ERR_NONE;
881 bitmap = 0;
884 err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
885 if (err)
886 return err;
888 params->lfb_width = mode_info.width;
889 params->lfb_height = mode_info.height;
890 params->lfb_depth = mode_info.bpp;
891 params->lfb_line_len = mode_info.pitch;
893 params->lfb_base = (grub_addr_t) framebuffer;
894 params->lfb_mode = bitmap ? GRUB_XNU_VIDEO_SPLASH
895 : GRUB_XNU_VIDEO_TEXT_IN_VIDEO;
897 return GRUB_ERR_NONE;
900 /* Boot xnu. */
901 grub_err_t
902 grub_xnu_boot (void)
904 union grub_xnu_boot_params_any *bootparams;
905 struct grub_xnu_boot_params_common *bootparams_common;
906 void *bp_in;
907 grub_addr_t bootparams_target;
908 grub_err_t err;
909 grub_efi_uintn_t memory_map_size = 0;
910 void *memory_map;
911 grub_addr_t memory_map_target;
912 grub_efi_uintn_t map_key = 0;
913 grub_efi_uintn_t descriptor_size = 0;
914 grub_efi_uint32_t descriptor_version = 0;
915 grub_uint64_t firstruntimepage, lastruntimepage;
916 grub_uint64_t curruntimepage;
917 grub_addr_t devtree_target;
918 grub_size_t devtreelen;
919 int i;
920 struct grub_relocator32_state state;
921 grub_uint64_t fsbfreq = 100000000;
922 int v2 = (grub_xnu_darwin_version >= 11);
923 grub_uint32_t efi_system_table = 0;
925 err = grub_autoefi_prepare ();
926 if (err)
927 return err;
929 err = grub_cpu_xnu_fill_devprop ();
930 if (err)
931 return err;
933 err = grub_cpu_xnu_fill_devicetree (&fsbfreq);
934 if (err)
935 return err;
937 err = grub_xnu_fill_devicetree ();
938 if (err)
939 return err;
941 /* Page-align to avoid following parts to be inadvertently freed. */
942 err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
943 if (err)
944 return err;
946 /* Pass memory map to kernel. */
947 memory_map_size = 0;
948 memory_map = 0;
949 map_key = 0;
950 descriptor_size = 0;
951 descriptor_version = 0;
953 grub_dprintf ("xnu", "eip=%x, efi=%p\n", grub_xnu_entry_point,
954 grub_autoefi_system_table);
956 const char *debug = grub_env_get ("debug");
958 if (debug && (grub_strword (debug, "all") || grub_strword (debug, "xnu")))
960 grub_puts_ (N_("Press any key to launch xnu"));
961 grub_getkey ();
964 /* Relocate the boot parameters to heap. */
965 err = grub_xnu_heap_malloc (sizeof (*bootparams),
966 &bp_in, &bootparams_target);
967 if (err)
968 return err;
969 bootparams = bp_in;
971 grub_memset (bootparams, 0, sizeof (*bootparams));
972 if (v2)
974 bootparams_common = &bootparams->v2.common;
975 bootparams->v2.fsbfreq = fsbfreq;
977 else
978 bootparams_common = &bootparams->v1.common;
980 /* Set video. */
981 err = grub_xnu_set_video (bootparams_common);
982 if (err != GRUB_ERR_NONE)
984 grub_print_error ();
985 grub_errno = GRUB_ERR_NONE;
986 grub_puts_ (N_("Booting in blind mode"));
988 bootparams_common->lfb_mode = 0;
989 bootparams_common->lfb_width = 0;
990 bootparams_common->lfb_height = 0;
991 bootparams_common->lfb_depth = 0;
992 bootparams_common->lfb_line_len = 0;
993 bootparams_common->lfb_base = 0;
996 if (grub_autoefi_get_memory_map (&memory_map_size, memory_map,
997 &map_key, &descriptor_size,
998 &descriptor_version) < 0)
999 return grub_errno;
1001 /* We will do few allocations later. Reserve some space for possible
1002 memory map growth. */
1003 memory_map_size += 20 * descriptor_size;
1004 err = grub_xnu_heap_malloc (memory_map_size,
1005 &memory_map, &memory_map_target);
1006 if (err)
1007 return err;
1009 err = grub_xnu_writetree_toheap (&devtree_target, &devtreelen);
1010 if (err)
1011 return err;
1013 grub_memcpy (bootparams_common->cmdline, grub_xnu_cmdline,
1014 sizeof (bootparams_common->cmdline));
1016 bootparams_common->devtree = devtree_target;
1017 bootparams_common->devtreelen = devtreelen;
1019 err = grub_autoefi_finish_boot_services (&memory_map_size, memory_map,
1020 &map_key, &descriptor_size,
1021 &descriptor_version);
1022 if (err)
1023 return err;
1025 if (v2)
1026 bootparams->v2.efi_system_table = (grub_addr_t) grub_autoefi_system_table;
1027 else
1028 bootparams->v1.efi_system_table = (grub_addr_t) grub_autoefi_system_table;
1030 firstruntimepage = (((grub_addr_t) grub_xnu_heap_target_start
1031 + grub_xnu_heap_size + GRUB_XNU_PAGESIZE - 1)
1032 / GRUB_XNU_PAGESIZE) + 20;
1033 curruntimepage = firstruntimepage;
1035 for (i = 0; (unsigned) i < memory_map_size / descriptor_size; i++)
1037 grub_efi_memory_descriptor_t *curdesc = (grub_efi_memory_descriptor_t *)
1038 ((char *) memory_map + descriptor_size * i);
1040 curdesc->virtual_start = curdesc->physical_start;
1042 if (curdesc->type == GRUB_EFI_RUNTIME_SERVICES_DATA
1043 || curdesc->type == GRUB_EFI_RUNTIME_SERVICES_CODE)
1045 curdesc->virtual_start = curruntimepage << 12;
1046 curruntimepage += curdesc->num_pages;
1047 if (curdesc->physical_start
1048 <= (grub_addr_t) grub_autoefi_system_table
1049 && curdesc->physical_start + (curdesc->num_pages << 12)
1050 > (grub_addr_t) grub_autoefi_system_table)
1051 efi_system_table
1052 = (grub_addr_t) grub_autoefi_system_table
1053 - curdesc->physical_start + curdesc->virtual_start;
1054 if (SIZEOF_OF_UINTN == 8 && grub_xnu_is_64bit)
1055 curdesc->virtual_start |= 0xffffff8000000000ULL;
1059 lastruntimepage = curruntimepage;
1061 if (v2)
1063 bootparams->v2.efi_uintnbits = SIZEOF_OF_UINTN * 8;
1064 bootparams->v2.verminor = GRUB_XNU_BOOTARGSV2_VERMINOR;
1065 bootparams->v2.vermajor = GRUB_XNU_BOOTARGSV2_VERMAJOR;
1066 bootparams->v2.efi_system_table = efi_system_table;
1068 else
1070 bootparams->v1.efi_uintnbits = SIZEOF_OF_UINTN * 8;
1071 bootparams->v1.verminor = GRUB_XNU_BOOTARGSV1_VERMINOR;
1072 bootparams->v1.vermajor = GRUB_XNU_BOOTARGSV1_VERMAJOR;
1073 bootparams->v1.efi_system_table = efi_system_table;
1076 bootparams_common->efi_runtime_first_page = firstruntimepage;
1077 bootparams_common->efi_runtime_npages = lastruntimepage - firstruntimepage;
1078 bootparams_common->efi_mem_desc_size = descriptor_size;
1079 bootparams_common->efi_mem_desc_version = descriptor_version;
1080 bootparams_common->efi_mmap = memory_map_target;
1081 bootparams_common->efi_mmap_size = memory_map_size;
1082 bootparams_common->heap_start = grub_xnu_heap_target_start;
1083 bootparams_common->heap_size = grub_xnu_heap_size;
1085 /* Parameters for asm helper. */
1086 grub_xnu_stack = bootparams_common->heap_start
1087 + bootparams_common->heap_size + GRUB_XNU_PAGESIZE;
1088 grub_xnu_arg1 = bootparams_target;
1090 grub_autoefi_set_virtual_address_map (memory_map_size, descriptor_size,
1091 descriptor_version, memory_map);
1093 state.eip = grub_xnu_entry_point;
1094 state.eax = grub_xnu_arg1;
1095 state.esp = grub_xnu_stack;
1096 state.ebp = grub_xnu_stack;
1098 /* XNU uses only APIC. Disable PIC. */
1099 grub_outb (0xff, 0x21);
1100 grub_outb (0xff, 0xa1);
1102 return grub_relocator32_boot (grub_xnu_relocator, state, 0);
1105 static grub_command_t cmd_devprop_load;
1107 void
1108 grub_cpu_xnu_init (void)
1110 cmd_devprop_load = grub_register_command ("xnu_devprop_load",
1111 grub_cmd_devprop_load,
1112 /* TRANSLATORS: `device-properties'
1113 is a variable name,
1114 not a program. */
1115 0, N_("Load `device-properties' dump."));
1118 void
1119 grub_cpu_xnu_fini (void)
1121 grub_unregister_command (cmd_devprop_load);