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