123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167 |
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2009 Free Software Foundation, Inc.
- *
- * GRUB is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GRUB is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <grub/env.h>
- #include <grub/file.h>
- #include <grub/disk.h>
- #include <grub/xnu.h>
- #include <grub/cpu/xnu.h>
- #include <grub/mm.h>
- #include <grub/loader.h>
- #include <grub/autoefi.h>
- #include <grub/i386/tsc.h>
- #include <grub/i386/cpuid.h>
- #include <grub/efi/api.h>
- #include <grub/i386/pit.h>
- #include <grub/misc.h>
- #include <grub/charset.h>
- #include <grub/term.h>
- #include <grub/command.h>
- #include <grub/i18n.h>
- #include <grub/bitmap_scale.h>
- #include <grub/cpu/io.h>
- #include <grub/random.h>
- #define min(a,b) (((a) < (b)) ? (a) : (b))
- #define max(a,b) (((a) > (b)) ? (a) : (b))
- #define DEFAULT_VIDEO_MODE "auto"
- char grub_xnu_cmdline[1024];
- grub_uint32_t grub_xnu_entry_point, grub_xnu_arg1, grub_xnu_stack;
- /* Aliases set for some tables. */
- struct tbl_alias
- {
- grub_efi_guid_t guid;
- const char *name;
- };
- static struct tbl_alias table_aliases[] =
- {
- {GRUB_EFI_ACPI_20_TABLE_GUID, "ACPI_20"},
- {GRUB_EFI_ACPI_TABLE_GUID, "ACPI"},
- };
- struct grub_xnu_devprop_device_descriptor
- {
- struct grub_xnu_devprop_device_descriptor *next;
- struct grub_xnu_devprop_device_descriptor **prev;
- struct property_descriptor *properties;
- struct grub_efi_device_path *path;
- int pathlen;
- };
- static int
- utf16_strlen (grub_uint16_t *in)
- {
- int i;
- for (i = 0; in[i]; i++);
- return i;
- }
- /* Read frequency from a string in MHz and return it in Hz. */
- static grub_uint64_t
- readfrequency (const char *str)
- {
- grub_uint64_t num = 0;
- int mul = 1000000;
- int found = 0;
- while (*str)
- {
- unsigned long digit;
- digit = grub_tolower (*str) - '0';
- if (digit > 9)
- break;
- found = 1;
- num = num * 10 + digit;
- str++;
- }
- num *= 1000000;
- if (*str == '.')
- {
- str++;
- while (*str)
- {
- unsigned long digit;
- digit = grub_tolower (*str) - '0';
- if (digit > 9)
- break;
- found = 1;
- mul /= 10;
- num = num + mul * digit;
- str++;
- }
- }
- if (! found)
- return 0;
- return num;
- }
- /* Thanks to Kabyl for precious information about Intel architecture. */
- static grub_uint64_t
- guessfsb (void)
- {
- const grub_uint64_t sane_value = 100000000;
- grub_uint32_t manufacturer[3], max_cpuid, capabilities, msrlow;
- grub_uint32_t a, b, d, divisor;
- if (! grub_cpu_is_cpuid_supported ())
- return sane_value;
- grub_cpuid (0, max_cpuid, manufacturer[0], manufacturer[2], manufacturer[1]);
- /* Only Intel for now is done. */
- if (grub_memcmp (manufacturer, "GenuineIntel", 12) != 0)
- return sane_value;
- /* Check Speedstep. */
- if (max_cpuid < 1)
- return sane_value;
- grub_cpuid (1, a, b, capabilities, d);
- if (! (capabilities & (1 << 7)))
- return sane_value;
- /* Read the multiplier. */
- asm volatile ("movl $0x198, %%ecx\n"
- "rdmsr"
- : "=d" (msrlow)
- :
- : "%ecx", "%eax");
- grub_uint64_t v;
- grub_uint32_t r;
- /* (2000ULL << 32) / grub_tsc_rate */
- /* Assumption: TSC frequency is over 2 MHz. */
- v = 0xffffffff / grub_tsc_rate;
- v *= 2000;
- /* v is at most 2000 off from (2000ULL << 32) / grub_tsc_rate.
- Since grub_tsc_rate < 2^32/2^11=2^21, so no overflow.
- */
- r = (2000ULL << 32) - v * grub_tsc_rate;
- v += r / grub_tsc_rate;
- divisor = ((msrlow >> 7) & 0x3e) | ((msrlow >> 14) & 1);
- if (divisor == 0)
- return sane_value;
- return grub_divmod64 (v, divisor, 0);
- }
- struct property_descriptor
- {
- struct property_descriptor *next;
- struct property_descriptor **prev;
- grub_uint8_t *name;
- grub_uint16_t *name16;
- int name16len;
- int length;
- void *data;
- };
- static struct grub_xnu_devprop_device_descriptor *devices = 0;
- grub_err_t
- grub_xnu_devprop_remove_property (struct grub_xnu_devprop_device_descriptor *dev,
- char *name)
- {
- struct property_descriptor *prop;
- prop = grub_named_list_find (GRUB_AS_NAMED_LIST (dev->properties), name);
- if (!prop)
- return GRUB_ERR_NONE;
- grub_free (prop->name);
- grub_free (prop->name16);
- grub_free (prop->data);
- grub_list_remove (GRUB_AS_LIST (prop));
- return GRUB_ERR_NONE;
- }
- grub_err_t
- grub_xnu_devprop_remove_device (struct grub_xnu_devprop_device_descriptor *dev)
- {
- void *t;
- struct property_descriptor *prop;
- grub_list_remove (GRUB_AS_LIST (dev));
- for (prop = dev->properties; prop; )
- {
- grub_free (prop->name);
- grub_free (prop->name16);
- grub_free (prop->data);
- t = prop;
- prop = prop->next;
- grub_free (t);
- }
- grub_free (dev->path);
- grub_free (dev);
- return GRUB_ERR_NONE;
- }
- struct grub_xnu_devprop_device_descriptor *
- grub_xnu_devprop_add_device (struct grub_efi_device_path *path, int length)
- {
- struct grub_xnu_devprop_device_descriptor *ret;
- ret = grub_zalloc (sizeof (*ret));
- if (!ret)
- return 0;
- ret->path = grub_malloc (length);
- if (!ret->path)
- {
- grub_free (ret);
- return 0;
- }
- ret->pathlen = length;
- grub_memcpy (ret->path, path, length);
- grub_list_push (GRUB_AS_LIST_P (&devices), GRUB_AS_LIST (ret));
- return ret;
- }
- static grub_err_t
- grub_xnu_devprop_add_property (struct grub_xnu_devprop_device_descriptor *dev,
- grub_uint8_t *utf8, grub_uint16_t *utf16,
- int utf16len, void *data, int datalen)
- {
- struct property_descriptor *prop;
- prop = grub_malloc (sizeof (*prop));
- if (!prop)
- return grub_errno;
- prop->name = utf8;
- prop->name16 = utf16;
- prop->name16len = utf16len;
- prop->length = datalen;
- prop->data = grub_malloc (prop->length);
- if (!prop->data)
- {
- grub_free (prop->name);
- grub_free (prop->name16);
- grub_free (prop);
- return grub_errno;
- }
- grub_memcpy (prop->data, data, prop->length);
- grub_list_push (GRUB_AS_LIST_P (&dev->properties),
- GRUB_AS_LIST (prop));
- return GRUB_ERR_NONE;
- }
- grub_err_t
- grub_xnu_devprop_add_property_utf8 (struct grub_xnu_devprop_device_descriptor *dev,
- char *name, void *data, int datalen)
- {
- grub_uint8_t *utf8;
- grub_uint16_t *utf16;
- int len, utf16len;
- grub_err_t err;
- utf8 = (grub_uint8_t *) grub_strdup (name);
- if (!utf8)
- return grub_errno;
- len = grub_strlen (name);
- utf16 = grub_malloc (sizeof (grub_uint16_t) * len);
- if (!utf16)
- {
- grub_free (utf8);
- return grub_errno;
- }
- utf16len = grub_utf8_to_utf16 (utf16, len, utf8, len, NULL);
- if (utf16len < 0)
- {
- grub_free (utf8);
- grub_free (utf16);
- return grub_errno;
- }
- err = grub_xnu_devprop_add_property (dev, utf8, utf16,
- utf16len, data, datalen);
- if (err)
- {
- grub_free (utf8);
- grub_free (utf16);
- return err;
- }
- return GRUB_ERR_NONE;
- }
- grub_err_t
- grub_xnu_devprop_add_property_utf16 (struct grub_xnu_devprop_device_descriptor *dev,
- grub_uint16_t *name, int namelen,
- void *data, int datalen)
- {
- grub_uint8_t *utf8;
- grub_uint16_t *utf16;
- grub_err_t err;
- utf16 = grub_malloc (sizeof (grub_uint16_t) * namelen);
- if (!utf16)
- return grub_errno;
- grub_memcpy (utf16, name, sizeof (grub_uint16_t) * namelen);
- utf8 = grub_malloc (namelen * 4 + 1);
- if (!utf8)
- {
- grub_free (utf16);
- return grub_errno;
- }
- *grub_utf16_to_utf8 ((grub_uint8_t *) utf8, name, namelen) = '\0';
- err = grub_xnu_devprop_add_property (dev, utf8, utf16,
- namelen, data, datalen);
- if (err)
- {
- grub_free (utf8);
- grub_free (utf16);
- return err;
- }
- return GRUB_ERR_NONE;
- }
- void
- grub_cpu_xnu_unload (void)
- {
- struct grub_xnu_devprop_device_descriptor *dev1, *dev2;
- for (dev1 = devices; dev1; )
- {
- dev2 = dev1->next;
- grub_xnu_devprop_remove_device (dev1);
- dev1 = dev2;
- }
- }
- static grub_err_t
- grub_cpu_xnu_fill_devprop (void)
- {
- struct grub_xnu_devtree_key *efikey;
- int total_length = sizeof (struct grub_xnu_devprop_header);
- struct grub_xnu_devtree_key *devprop;
- struct grub_xnu_devprop_device_descriptor *device;
- void *ptr;
- struct grub_xnu_devprop_header *head;
- void *t;
- int numdevs = 0;
- /* The key "efi". */
- efikey = grub_xnu_create_key (&grub_xnu_devtree_root, "efi");
- if (! efikey)
- return grub_errno;
- for (device = devices; device; device = device->next)
- {
- struct property_descriptor *propdesc;
- total_length += sizeof (struct grub_xnu_devprop_device_header);
- total_length += device->pathlen;
- for (propdesc = device->properties; propdesc; propdesc = propdesc->next)
- {
- total_length += sizeof (grub_uint32_t);
- total_length += sizeof (grub_uint16_t)
- * (propdesc->name16len + 1);
- total_length += sizeof (grub_uint32_t);
- total_length += propdesc->length;
- }
- numdevs++;
- }
- devprop = grub_xnu_create_value (&(efikey->first_child), "device-properties");
- if (!devprop)
- return grub_errno;
- devprop->data = grub_malloc (total_length);
- devprop->datasize = total_length;
- ptr = devprop->data;
- head = ptr;
- ptr = head + 1;
- head->length = total_length;
- head->alwaysone = 1;
- head->num_devices = numdevs;
- for (device = devices; device; )
- {
- struct grub_xnu_devprop_device_header *devhead;
- struct property_descriptor *propdesc;
- devhead = ptr;
- devhead->num_values = 0;
- ptr = devhead + 1;
- grub_memcpy (ptr, device->path, device->pathlen);
- ptr = (char *) ptr + device->pathlen;
- for (propdesc = device->properties; propdesc; )
- {
- grub_uint32_t *len;
- grub_uint16_t *name;
- void *data;
- len = ptr;
- *len = 2 * propdesc->name16len + sizeof (grub_uint16_t)
- + sizeof (grub_uint32_t);
- ptr = len + 1;
- name = ptr;
- grub_memcpy (name, propdesc->name16, 2 * propdesc->name16len);
- name += propdesc->name16len;
- /* NUL terminator. */
- *name = 0;
- ptr = name + 1;
- len = ptr;
- *len = propdesc->length + sizeof (grub_uint32_t);
- data = len + 1;
- ptr = data;
- grub_memcpy (ptr, propdesc->data, propdesc->length);
- ptr = (char *) ptr + propdesc->length;
- grub_free (propdesc->name);
- grub_free (propdesc->name16);
- grub_free (propdesc->data);
- t = propdesc;
- propdesc = propdesc->next;
- grub_free (t);
- devhead->num_values++;
- }
- devhead->length = (char *) ptr - (char *) devhead;
- t = device;
- device = device->next;
- grub_free (t);
- }
- devices = 0;
- return GRUB_ERR_NONE;
- }
- static grub_err_t
- grub_cmd_devprop_load (grub_command_t cmd __attribute__ ((unused)),
- int argc, char *args[])
- {
- grub_file_t file;
- void *buf, *bufstart, *bufend;
- struct grub_xnu_devprop_header *head;
- grub_size_t size;
- unsigned i, j;
- if (argc != 1)
- return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
- file = grub_file_open (args[0]);
- if (! file)
- return grub_errno;
- size = grub_file_size (file);
- buf = grub_malloc (size);
- if (!buf)
- {
- grub_file_close (file);
- return grub_errno;
- }
- if (grub_file_read (file, buf, size) != (grub_ssize_t) size)
- {
- grub_file_close (file);
- return grub_errno;
- }
- grub_file_close (file);
- bufstart = buf;
- bufend = (char *) buf + size;
- head = buf;
- buf = head + 1;
- for (i = 0; i < grub_le_to_cpu32 (head->num_devices) && buf < bufend; i++)
- {
- struct grub_efi_device_path *dp, *dpstart;
- struct grub_xnu_devprop_device_descriptor *dev;
- struct grub_xnu_devprop_device_header *devhead;
- devhead = buf;
- buf = devhead + 1;
- dpstart = buf;
- do
- {
- dp = buf;
- buf = (char *) buf + GRUB_EFI_DEVICE_PATH_LENGTH (dp);
- }
- while (!GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp) && buf < bufend);
- dev = grub_xnu_devprop_add_device (dpstart, (char *) buf
- - (char *) dpstart);
- for (j = 0; j < grub_le_to_cpu32 (devhead->num_values) && buf < bufend;
- j++)
- {
- grub_uint32_t *namelen;
- grub_uint32_t *datalen;
- grub_uint16_t *utf16;
- void *data;
- grub_err_t err;
- namelen = buf;
- buf = namelen + 1;
- if (buf >= bufend)
- break;
- utf16 = buf;
- buf = (char *) buf + *namelen - sizeof (grub_uint32_t);
- if (buf >= bufend)
- break;
- datalen = buf;
- buf = datalen + 1;
- if (buf >= bufend)
- break;
- data = buf;
- buf = (char *) buf + *datalen - sizeof (grub_uint32_t);
- if (buf >= bufend)
- break;
- err = grub_xnu_devprop_add_property_utf16
- (dev, utf16, (*namelen - sizeof (grub_uint32_t)
- - sizeof (grub_uint16_t)) / sizeof (grub_uint16_t),
- data, *datalen - sizeof (grub_uint32_t));
- if (err)
- {
- grub_free (bufstart);
- return err;
- }
- }
- }
- grub_free (bufstart);
- return GRUB_ERR_NONE;
- }
- /* Fill device tree. */
- /* FIXME: some entries may be platform-agnostic. Move them to loader/xnu.c. */
- static grub_err_t
- grub_cpu_xnu_fill_devicetree (grub_uint64_t *fsbfreq_out)
- {
- struct grub_xnu_devtree_key *efikey;
- struct grub_xnu_devtree_key *chosenkey;
- struct grub_xnu_devtree_key *cfgtablekey;
- struct grub_xnu_devtree_key *curval;
- struct grub_xnu_devtree_key *runtimesrvkey;
- struct grub_xnu_devtree_key *platformkey;
- unsigned i, j;
- grub_err_t err;
- chosenkey = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
- if (! chosenkey)
- return grub_errno;
- /* Random seed. */
- curval = grub_xnu_create_value (&(chosenkey->first_child), "random-seed");
- if (! curval)
- return grub_errno;
- curval->datasize = 64;
- curval->data = grub_malloc (curval->datasize);
- if (! curval->data)
- return grub_errno;
- /* Our random is not peer-reviewed but xnu uses this seed only for
- ASLR in kernel. */
- err = grub_crypto_get_random (curval->data, curval->datasize);
- if (err)
- return err;
- /* The value "model". */
- /* FIXME: may this value be sometimes different? */
- curval = grub_xnu_create_value (&grub_xnu_devtree_root, "model");
- if (! curval)
- return grub_errno;
- curval->datasize = sizeof ("ACPI");
- curval->data = grub_strdup ("ACPI");
- curval = grub_xnu_create_value (&grub_xnu_devtree_root, "compatible");
- if (! curval)
- return grub_errno;
- curval->datasize = sizeof ("ACPI");
- curval->data = grub_strdup ("ACPI");
- /* The key "efi". */
- efikey = grub_xnu_create_key (&grub_xnu_devtree_root, "efi");
- if (! efikey)
- return grub_errno;
- /* Information about firmware. */
- curval = grub_xnu_create_value (&(efikey->first_child), "firmware-revision");
- if (! curval)
- return grub_errno;
- curval->datasize = (SYSTEM_TABLE_SIZEOF (firmware_revision));
- curval->data = grub_malloc (curval->datasize);
- if (! curval->data)
- return grub_errno;
- grub_memcpy (curval->data, (SYSTEM_TABLE_VAR(firmware_revision)),
- curval->datasize);
- curval = grub_xnu_create_value (&(efikey->first_child), "firmware-vendor");
- if (! curval)
- return grub_errno;
- curval->datasize =
- 2 * (utf16_strlen (SYSTEM_TABLE_PTR (firmware_vendor)) + 1);
- curval->data = grub_malloc (curval->datasize);
- if (! curval->data)
- return grub_errno;
- grub_memcpy (curval->data, SYSTEM_TABLE_PTR (firmware_vendor),
- curval->datasize);
- curval = grub_xnu_create_value (&(efikey->first_child), "firmware-abi");
- if (! curval)
- return grub_errno;
- curval->datasize = sizeof ("EFI32");
- curval->data = grub_malloc (curval->datasize);
- if (! curval->data)
- return grub_errno;
- if (SIZEOF_OF_UINTN == 4)
- grub_memcpy (curval->data, "EFI32", curval->datasize);
- else
- grub_memcpy (curval->data, "EFI64", curval->datasize);
- /* The key "platform". */
- platformkey = grub_xnu_create_key (&(efikey->first_child),
- "platform");
- if (! platformkey)
- return grub_errno;
- /* Pass FSB frequency to the kernel. */
- curval = grub_xnu_create_value (&(platformkey->first_child), "FSBFrequency");
- if (! curval)
- return grub_errno;
- curval->datasize = sizeof (grub_uint64_t);
- curval->data = grub_malloc (curval->datasize);
- if (!curval->data)
- return grub_errno;
- /* First see if user supplies the value. */
- const char *fsbvar = grub_env_get ("fsb");
- grub_uint64_t fsbfreq = 0;
- if (fsbvar)
- fsbfreq = readfrequency (fsbvar);
- /* Try autodetect. */
- if (! fsbfreq)
- fsbfreq = guessfsb ();
- *((grub_uint64_t *) curval->data) = fsbfreq;
- *fsbfreq_out = fsbfreq;
- grub_dprintf ("xnu", "fsb autodetected as %llu\n",
- (unsigned long long) *((grub_uint64_t *) curval->data));
- cfgtablekey = grub_xnu_create_key (&(efikey->first_child),
- "configuration-table");
- if (!cfgtablekey)
- return grub_errno;
- /* Fill "configuration-table" key. */
- for (i = 0; i < SYSTEM_TABLE (num_table_entries); i++)
- {
- void *ptr;
- struct grub_xnu_devtree_key *curkey;
- grub_efi_packed_guid_t guid;
- char guidbuf[64];
- /* Retrieve current key. */
- #ifdef GRUB_MACHINE_EFI
- {
- ptr = (void *)
- grub_efi_system_table->configuration_table[i].vendor_table;
- guid = grub_efi_system_table->configuration_table[i].vendor_guid;
- }
- #else
- if (SIZEOF_OF_UINTN == 4)
- {
- ptr = (void *) (grub_addr_t) ((grub_efiemu_configuration_table32_t *)
- SYSTEM_TABLE_PTR (configuration_table))[i]
- .vendor_table;
- guid =
- ((grub_efiemu_configuration_table32_t *)
- SYSTEM_TABLE_PTR (configuration_table))[i].vendor_guid;
- }
- else
- {
- ptr = (void *) (grub_addr_t) ((grub_efiemu_configuration_table64_t *)
- SYSTEM_TABLE_PTR (configuration_table))[i]
- .vendor_table;
- guid =
- ((grub_efiemu_configuration_table64_t *)
- SYSTEM_TABLE_PTR (configuration_table))[i].vendor_guid;
- }
- #endif
- /* The name of key for new table. */
- grub_snprintf (guidbuf, sizeof (guidbuf), "%08x-%04x-%04x-%02x%02x-",
- guid.data1, guid.data2, guid.data3, guid.data4[0],
- guid.data4[1]);
- for (j = 2; j < 8; j++)
- grub_snprintf (guidbuf + grub_strlen (guidbuf),
- sizeof (guidbuf) - grub_strlen (guidbuf),
- "%02x", guid.data4[j]);
- /* For some reason GUID has to be in uppercase. */
- for (j = 0; guidbuf[j] ; j++)
- if (guidbuf[j] >= 'a' && guidbuf[j] <= 'f')
- guidbuf[j] += 'A' - 'a';
- curkey = grub_xnu_create_key (&(cfgtablekey->first_child), guidbuf);
- if (! curkey)
- return grub_errno;
- curval = grub_xnu_create_value (&(curkey->first_child), "guid");
- if (! curval)
- return grub_errno;
- curval->datasize = sizeof (guid);
- curval->data = grub_malloc (curval->datasize);
- if (! curval->data)
- return grub_errno;
- grub_memcpy (curval->data, &guid, curval->datasize);
- /* The value "table". */
- curval = grub_xnu_create_value (&(curkey->first_child), "table");
- if (! curval)
- return grub_errno;
- curval->datasize = SIZEOF_OF_UINTN;
- curval->data = grub_malloc (curval->datasize);
- if (! curval->data)
- return grub_errno;
- if (SIZEOF_OF_UINTN == 4)
- *((grub_uint32_t *) curval->data) = (grub_addr_t) ptr;
- else
- *((grub_uint64_t *) curval->data) = (grub_addr_t) ptr;
- /* Create alias. */
- for (j = 0; j < ARRAY_SIZE(table_aliases); j++)
- if (grub_memcmp (&table_aliases[j].guid, &guid, sizeof (guid)) == 0)
- break;
- if (j != ARRAY_SIZE(table_aliases))
- {
- curval = grub_xnu_create_value (&(curkey->first_child), "alias");
- if (!curval)
- return grub_errno;
- curval->datasize = grub_strlen (table_aliases[j].name) + 1;
- curval->data = grub_malloc (curval->datasize);
- if (!curval->data)
- return grub_errno;
- grub_memcpy (curval->data, table_aliases[j].name, curval->datasize);
- }
- }
- /* Create and fill "runtime-services" key. */
- runtimesrvkey = grub_xnu_create_key (&(efikey->first_child),
- "runtime-services");
- if (! runtimesrvkey)
- return grub_errno;
- curval = grub_xnu_create_value (&(runtimesrvkey->first_child), "table");
- if (! curval)
- return grub_errno;
- curval->datasize = SIZEOF_OF_UINTN;
- curval->data = grub_malloc (curval->datasize);
- if (! curval->data)
- return grub_errno;
- if (SIZEOF_OF_UINTN == 4)
- *((grub_uint32_t *) curval->data)
- = (grub_addr_t) SYSTEM_TABLE_PTR (runtime_services);
- else
- *((grub_uint64_t *) curval->data)
- = (grub_addr_t) SYSTEM_TABLE_PTR (runtime_services);
- return GRUB_ERR_NONE;
- }
- grub_err_t
- grub_xnu_boot_resume (void)
- {
- struct grub_relocator32_state state;
- state.esp = grub_xnu_stack;
- state.ebp = grub_xnu_stack;
- state.eip = grub_xnu_entry_point;
- state.eax = grub_xnu_arg1;
- return grub_relocator32_boot (grub_xnu_relocator, state, 0);
- }
- /* Setup video for xnu. */
- static grub_err_t
- grub_xnu_set_video (struct grub_xnu_boot_params_common *params)
- {
- struct grub_video_mode_info mode_info;
- char *tmp;
- const char *modevar;
- void *framebuffer;
- grub_err_t err;
- struct grub_video_bitmap *bitmap = NULL;
- modevar = grub_env_get ("gfxpayload");
- /* Consider only graphical 32-bit deep modes. */
- if (! modevar || *modevar == 0)
- err = grub_video_set_mode (DEFAULT_VIDEO_MODE,
- GRUB_VIDEO_MODE_TYPE_PURE_TEXT
- | GRUB_VIDEO_MODE_TYPE_DEPTH_MASK,
- 32 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS);
- else
- {
- tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
- if (! tmp)
- return grub_errno;
- err = grub_video_set_mode (tmp,
- GRUB_VIDEO_MODE_TYPE_PURE_TEXT
- | GRUB_VIDEO_MODE_TYPE_DEPTH_MASK,
- 32 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS);
- grub_free (tmp);
- }
- if (err)
- return err;
- err = grub_video_get_info (&mode_info);
- if (err)
- return err;
- if (grub_xnu_bitmap)
- {
- if (grub_xnu_bitmap_mode == GRUB_XNU_BITMAP_STRETCH)
- err = grub_video_bitmap_create_scaled (&bitmap,
- mode_info.width,
- mode_info.height,
- grub_xnu_bitmap,
- GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
- else
- bitmap = grub_xnu_bitmap;
- }
- if (bitmap)
- {
- if (grub_xnu_bitmap_mode == GRUB_XNU_BITMAP_STRETCH)
- err = grub_video_bitmap_create_scaled (&bitmap,
- mode_info.width,
- mode_info.height,
- grub_xnu_bitmap,
- GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
- else
- bitmap = grub_xnu_bitmap;
- }
- if (bitmap)
- {
- int x, y;
- x = mode_info.width - bitmap->mode_info.width;
- x /= 2;
- y = mode_info.height - bitmap->mode_info.height;
- y /= 2;
- err = grub_video_blit_bitmap (bitmap,
- GRUB_VIDEO_BLIT_REPLACE,
- x > 0 ? x : 0,
- y > 0 ? y : 0,
- x < 0 ? -x : 0,
- y < 0 ? -y : 0,
- min (bitmap->mode_info.width,
- mode_info.width),
- min (bitmap->mode_info.height,
- mode_info.height));
- }
- if (err)
- {
- grub_print_error ();
- grub_errno = GRUB_ERR_NONE;
- bitmap = 0;
- }
- err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
- if (err)
- return err;
- params->lfb_width = mode_info.width;
- params->lfb_height = mode_info.height;
- params->lfb_depth = mode_info.bpp;
- params->lfb_line_len = mode_info.pitch;
- params->lfb_base = (grub_addr_t) framebuffer;
- params->lfb_mode = bitmap ? GRUB_XNU_VIDEO_SPLASH
- : GRUB_XNU_VIDEO_TEXT_IN_VIDEO;
- return GRUB_ERR_NONE;
- }
- static int
- total_ram_hook (grub_uint64_t addr __attribute__ ((unused)), grub_uint64_t size,
- grub_memory_type_t type,
- void *data)
- {
- grub_uint64_t *result = data;
- if (type != GRUB_MEMORY_AVAILABLE)
- return 0;
- *result += size;
- return 0;
- }
- static grub_uint64_t
- get_total_ram (void)
- {
- grub_uint64_t result = 0;
- grub_mmap_iterate (total_ram_hook, &result);
- return result;
- }
- /* Boot xnu. */
- grub_err_t
- grub_xnu_boot (void)
- {
- union grub_xnu_boot_params_any *bootparams;
- struct grub_xnu_boot_params_common *bootparams_common;
- void *bp_in;
- grub_addr_t bootparams_target;
- grub_err_t err;
- grub_efi_uintn_t memory_map_size = 0;
- void *memory_map;
- grub_addr_t memory_map_target;
- grub_efi_uintn_t map_key = 0;
- grub_efi_uintn_t descriptor_size = 0;
- grub_efi_uint32_t descriptor_version = 0;
- grub_uint64_t firstruntimepage, lastruntimepage;
- grub_uint64_t curruntimepage;
- grub_addr_t devtree_target;
- grub_size_t devtreelen;
- int i;
- struct grub_relocator32_state state;
- grub_uint64_t fsbfreq = 100000000;
- int v2 = (grub_xnu_darwin_version >= 11);
- grub_uint32_t efi_system_table = 0;
- err = grub_autoefi_prepare ();
- if (err)
- return err;
- err = grub_cpu_xnu_fill_devprop ();
- if (err)
- return err;
- err = grub_cpu_xnu_fill_devicetree (&fsbfreq);
- if (err)
- return err;
- err = grub_xnu_fill_devicetree ();
- if (err)
- return err;
- /* Page-align to avoid following parts to be inadvertently freed. */
- err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
- if (err)
- return err;
- /* Pass memory map to kernel. */
- memory_map_size = 0;
- memory_map = 0;
- map_key = 0;
- descriptor_size = 0;
- descriptor_version = 0;
- grub_dprintf ("xnu", "eip=%x, efi=%p\n", grub_xnu_entry_point,
- grub_autoefi_system_table);
- const char *debug = grub_env_get ("debug");
- if (debug && (grub_strword (debug, "all") || grub_strword (debug, "xnu")))
- {
- grub_puts_ (N_("Press any key to launch xnu"));
- grub_getkey ();
- }
- /* Relocate the boot parameters to heap. */
- err = grub_xnu_heap_malloc (sizeof (*bootparams),
- &bp_in, &bootparams_target);
- if (err)
- return err;
- bootparams = bp_in;
- grub_memset (bootparams, 0, sizeof (*bootparams));
- if (v2)
- {
- bootparams_common = &bootparams->v2.common;
- bootparams->v2.fsbfreq = fsbfreq;
- bootparams->v2.ram_size = get_total_ram();
- }
- else
- bootparams_common = &bootparams->v1.common;
- /* Set video. */
- err = grub_xnu_set_video (bootparams_common);
- if (err != GRUB_ERR_NONE)
- {
- grub_print_error ();
- grub_errno = GRUB_ERR_NONE;
- grub_puts_ (N_("Booting in blind mode"));
- bootparams_common->lfb_mode = 0;
- bootparams_common->lfb_width = 0;
- bootparams_common->lfb_height = 0;
- bootparams_common->lfb_depth = 0;
- bootparams_common->lfb_line_len = 0;
- bootparams_common->lfb_base = 0;
- }
- if (grub_autoefi_get_memory_map (&memory_map_size, memory_map,
- &map_key, &descriptor_size,
- &descriptor_version) < 0)
- return grub_errno;
- /* We will do few allocations later. Reserve some space for possible
- memory map growth. */
- memory_map_size += 20 * descriptor_size;
- err = grub_xnu_heap_malloc (memory_map_size,
- &memory_map, &memory_map_target);
- if (err)
- return err;
- err = grub_xnu_writetree_toheap (&devtree_target, &devtreelen);
- if (err)
- return err;
- grub_memcpy (bootparams_common->cmdline, grub_xnu_cmdline,
- sizeof (bootparams_common->cmdline));
- bootparams_common->devtree = devtree_target;
- bootparams_common->devtreelen = devtreelen;
- err = grub_autoefi_finish_boot_services (&memory_map_size, memory_map,
- &map_key, &descriptor_size,
- &descriptor_version);
- if (err)
- return err;
- if (v2)
- bootparams->v2.efi_system_table = (grub_addr_t) grub_autoefi_system_table;
- else
- bootparams->v1.efi_system_table = (grub_addr_t) grub_autoefi_system_table;
- firstruntimepage = (((grub_addr_t) grub_xnu_heap_target_start
- + grub_xnu_heap_size + GRUB_XNU_PAGESIZE - 1)
- / GRUB_XNU_PAGESIZE) + 20;
- curruntimepage = firstruntimepage;
- for (i = 0; (unsigned) i < memory_map_size / descriptor_size; i++)
- {
- grub_efi_memory_descriptor_t *curdesc = (grub_efi_memory_descriptor_t *)
- ((char *) memory_map + descriptor_size * i);
- curdesc->virtual_start = curdesc->physical_start;
- if (curdesc->type == GRUB_EFI_RUNTIME_SERVICES_DATA
- || curdesc->type == GRUB_EFI_RUNTIME_SERVICES_CODE)
- {
- curdesc->virtual_start = curruntimepage << 12;
- curruntimepage += curdesc->num_pages;
- if (curdesc->physical_start
- <= (grub_addr_t) grub_autoefi_system_table
- && curdesc->physical_start + (curdesc->num_pages << 12)
- > (grub_addr_t) grub_autoefi_system_table)
- efi_system_table
- = (grub_addr_t) grub_autoefi_system_table
- - curdesc->physical_start + curdesc->virtual_start;
- if (SIZEOF_OF_UINTN == 8 && grub_xnu_is_64bit)
- curdesc->virtual_start |= 0xffffff8000000000ULL;
- }
- }
- lastruntimepage = curruntimepage;
- if (v2)
- {
- bootparams->v2.efi_uintnbits = SIZEOF_OF_UINTN * 8;
- bootparams->v2.verminor = GRUB_XNU_BOOTARGSV2_VERMINOR;
- bootparams->v2.vermajor = GRUB_XNU_BOOTARGSV2_VERMAJOR;
- bootparams->v2.efi_system_table = efi_system_table;
- }
- else
- {
- bootparams->v1.efi_uintnbits = SIZEOF_OF_UINTN * 8;
- bootparams->v1.verminor = GRUB_XNU_BOOTARGSV1_VERMINOR;
- bootparams->v1.vermajor = GRUB_XNU_BOOTARGSV1_VERMAJOR;
- bootparams->v1.efi_system_table = efi_system_table;
- }
- bootparams_common->efi_runtime_first_page = firstruntimepage;
- bootparams_common->efi_runtime_npages = lastruntimepage - firstruntimepage;
- bootparams_common->efi_mem_desc_size = descriptor_size;
- bootparams_common->efi_mem_desc_version = descriptor_version;
- bootparams_common->efi_mmap = memory_map_target;
- bootparams_common->efi_mmap_size = memory_map_size;
- bootparams_common->heap_start = grub_xnu_heap_target_start;
- bootparams_common->heap_size = curruntimepage * GRUB_XNU_PAGESIZE - grub_xnu_heap_target_start;
- /* Parameters for asm helper. */
- grub_xnu_stack = bootparams_common->heap_start
- + bootparams_common->heap_size + GRUB_XNU_PAGESIZE;
- grub_xnu_arg1 = bootparams_target;
- grub_autoefi_set_virtual_address_map (memory_map_size, descriptor_size,
- descriptor_version, memory_map);
- state.eip = grub_xnu_entry_point;
- state.eax = grub_xnu_arg1;
- state.esp = grub_xnu_stack;
- state.ebp = grub_xnu_stack;
- /* XNU uses only APIC. Disable PIC. */
- grub_outb (0xff, 0x21);
- grub_outb (0xff, 0xa1);
- return grub_relocator32_boot (grub_xnu_relocator, state, 0);
- }
- static grub_command_t cmd_devprop_load;
- void
- grub_cpu_xnu_init (void)
- {
- cmd_devprop_load = grub_register_command ("xnu_devprop_load",
- grub_cmd_devprop_load,
- /* TRANSLATORS: `device-properties'
- is a variable name,
- not a program. */
- 0, N_("Load `device-properties' dump."));
- }
- void
- grub_cpu_xnu_fini (void)
- {
- grub_unregister_command (cmd_devprop_load);
- }
|