~hamo/ubuntu/precise/grub2/grub2.hi_res

« back to all changes in this revision

Viewing changes to grub-core/loader/xnu.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Robert Millan, Updated translations
  • Date: 2010-11-22 12:24:56 UTC
  • mfrom: (1.26.4 upstream) (17.3.36 sid)
  • mto: (17.3.43 sid)
  • mto: This revision was merged to the branch mainline in revision 89.
  • Revision ID: james.westby@ubuntu.com-20101122122456-y82z3sfb7k4zfdcc
Tags: 1.99~20101122-1
[ Colin Watson ]
* New Bazaar snapshot.  Too many changes to list in full, but some of the
  more user-visible ones are as follows:
  - GRUB script:
    + Function parameters, "break", "continue", "shift", "setparams",
      "return", and "!".
    + "export" command supports multiple variable names.
    + Multi-line quoted strings support.
    + Wildcard expansion.
  - sendkey support.
  - USB hotunplugging and USB serial support.
  - Rename CD-ROM to cd on BIOS.
  - Add new --boot-directory option to grub-install, grub-reboot, and
    grub-set-default; the old --root-directory option is still accepted
    but was often confusing.
  - Basic btrfs detection/UUID support (but no file reading yet).
  - bash-completion for utilities.
  - If a device is listed in device.map, always assume that it is
    BIOS-visible rather than using extra layers such as LVM or RAID.
  - Add grub-mknetdir script (closes: #550658).
  - Remove deprecated "root" command.
  - Handle RAID devices containing virtio components.
  - GRUB Legacy configuration file support (via grub-menulst2cfg).
  - Keyboard layout support (via grub-mklayout and grub-kbdcomp).
  - Check generated grub.cfg for syntax errors before saving.
  - Pause execution for at most ten seconds if any errors are displayed,
    so that the user has a chance to see them.
  - Support submenus.
  - Write embedding zone using Reed-Solomon, so that it's robust against
    being partially overwritten (closes: #550702, #591416, #593347).
  - GRUB_DISABLE_LINUX_RECOVERY and GRUB_DISABLE_NETBSD_RECOVERY merged
    into a single GRUB_DISABLE_RECOVERY variable.
  - Fix loader memory allocation failure (closes: #551627).
  - Don't call savedefault on recovery entries (closes: #589325).
  - Support triple-indirect blocks on ext2 (closes: #543924).
  - Recognise DDF1 fake RAID (closes: #603354).

[ Robert Millan ]
* Use dpkg architecture wildcards.

[ Updated translations ]
* Slovenian (Vanja Cvelbar).  Closes: #604003
* Dzongkha (dawa pemo via Tenzin Dendup).  Closes: #604102

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* xnu.c - load xnu kernel. Thanks to Florian Idelberger for all the
 
2
   time he spent testing this
 
3
 */
 
4
/*
 
5
 *  GRUB  --  GRand Unified Bootloader
 
6
 *  Copyright (C) 2009  Free Software Foundation, Inc.
 
7
 *
 
8
 *  GRUB is free software: you can redistribute it and/or modify
 
9
 *  it under the terms of the GNU General Public License as published by
 
10
 *  the Free Software Foundation, either version 3 of the License, or
 
11
 *  (at your option) any later version.
 
12
 *
 
13
 *  GRUB is distributed in the hope that it will be useful,
 
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
 *  GNU General Public License for more details.
 
17
 *
 
18
 *  You should have received a copy of the GNU General Public License
 
19
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 
20
 */
 
21
 
 
22
#include <grub/file.h>
 
23
#include <grub/xnu.h>
 
24
#include <grub/cpu/xnu.h>
 
25
#include <grub/mm.h>
 
26
#include <grub/dl.h>
 
27
#include <grub/loader.h>
 
28
#include <grub/machoload.h>
 
29
#include <grub/macho.h>
 
30
#include <grub/cpu/macho.h>
 
31
#include <grub/command.h>
 
32
#include <grub/misc.h>
 
33
#include <grub/extcmd.h>
 
34
#include <grub/env.h>
 
35
#include <grub/i18n.h>
 
36
 
 
37
struct grub_xnu_devtree_key *grub_xnu_devtree_root = 0;
 
38
static int driverspackagenum = 0;
 
39
static int driversnum = 0;
 
40
int grub_xnu_is_64bit = 0;
 
41
 
 
42
grub_addr_t grub_xnu_heap_target_start = 0;
 
43
grub_size_t grub_xnu_heap_size = 0;
 
44
struct grub_relocator *grub_xnu_relocator;
 
45
 
 
46
static grub_err_t
 
47
grub_xnu_register_memory (char *prefix, int *suffix,
 
48
                          grub_addr_t addr, grub_size_t size);
 
49
grub_err_t
 
50
grub_xnu_heap_malloc (int size, void **src, grub_addr_t *target)
 
51
{
 
52
  grub_err_t err;
 
53
  grub_relocator_chunk_t ch;
 
54
  
 
55
  err = grub_relocator_alloc_chunk_addr (grub_xnu_relocator, &ch,
 
56
                                         grub_xnu_heap_target_start
 
57
                                         + grub_xnu_heap_size, size);
 
58
  if (err)
 
59
    return err;
 
60
 
 
61
  *src = get_virtual_current_address (ch);
 
62
  *target = grub_xnu_heap_target_start + grub_xnu_heap_size;
 
63
  grub_xnu_heap_size += size;
 
64
  grub_dprintf ("xnu", "val=%p\n", *src);
 
65
  return GRUB_ERR_NONE;
 
66
}
 
67
 
 
68
/* Make sure next block of the heap will be aligned.
 
69
   Please notice: aligned are pointers AFTER relocation
 
70
   and not the current ones. */
 
71
grub_err_t
 
72
grub_xnu_align_heap (int align)
 
73
{
 
74
  grub_xnu_heap_size
 
75
    = ALIGN_UP (grub_xnu_heap_target_start+ grub_xnu_heap_size, align)
 
76
    - grub_xnu_heap_target_start;
 
77
  return GRUB_ERR_NONE;
 
78
}
 
79
 
 
80
/* Free subtree pointed by CUR. */
 
81
void
 
82
grub_xnu_free_devtree (struct grub_xnu_devtree_key *cur)
 
83
{
 
84
  struct grub_xnu_devtree_key *d;
 
85
  while (cur)
 
86
    {
 
87
      grub_free (cur->name);
 
88
      if (cur->datasize == -1)
 
89
        grub_xnu_free_devtree (cur->first_child);
 
90
      else if (cur->data)
 
91
        grub_free (cur->data);
 
92
      d = cur->next;
 
93
      grub_free (cur);
 
94
      cur = d;
 
95
    }
 
96
}
 
97
 
 
98
/* Compute the size of device tree in xnu format. */
 
99
static grub_size_t
 
100
grub_xnu_writetree_get_size (struct grub_xnu_devtree_key *start, char *name)
 
101
{
 
102
  grub_size_t ret;
 
103
  struct grub_xnu_devtree_key *cur;
 
104
 
 
105
  /* Key header. */
 
106
  ret = 2 * sizeof (grub_uint32_t);
 
107
 
 
108
  /* "name" value. */
 
109
  ret += 32 + sizeof (grub_uint32_t)
 
110
    + grub_strlen (name) + 4
 
111
    - (grub_strlen (name) % 4);
 
112
 
 
113
  for (cur = start; cur; cur = cur->next)
 
114
    if (cur->datasize != -1)
 
115
      {
 
116
        int align_overhead;
 
117
 
 
118
        align_overhead = 4 - (cur->datasize % 4);
 
119
        if (align_overhead == 4)
 
120
          align_overhead = 0;
 
121
        ret += 32 + sizeof (grub_uint32_t) + cur->datasize + align_overhead;
 
122
      }
 
123
    else
 
124
      ret += grub_xnu_writetree_get_size (cur->first_child, cur->name);
 
125
  return ret;
 
126
}
 
127
 
 
128
/* Write devtree in XNU format at curptr assuming the head is named NAME.*/
 
129
static void *
 
130
grub_xnu_writetree_toheap_real (void *curptr,
 
131
                                struct grub_xnu_devtree_key *start, char *name)
 
132
{
 
133
  struct grub_xnu_devtree_key *cur;
 
134
  int nkeys = 0, nvals = 0;
 
135
  for (cur = start; cur; cur = cur->next)
 
136
    {
 
137
      if (cur->datasize == -1)
 
138
        nkeys++;
 
139
      else
 
140
        nvals++;
 
141
    }
 
142
  /* For the name. */
 
143
  nvals++;
 
144
 
 
145
  *((grub_uint32_t *) curptr) = nvals;
 
146
  curptr = ((grub_uint32_t *) curptr) + 1;
 
147
  *((grub_uint32_t *) curptr) = nkeys;
 
148
  curptr = ((grub_uint32_t *) curptr) + 1;
 
149
 
 
150
  /* First comes "name" value. */
 
151
  grub_memset (curptr, 0, 32);
 
152
  grub_memcpy (curptr, "name", 4);
 
153
  curptr = ((grub_uint8_t *) curptr) + 32;
 
154
  *((grub_uint32_t *)curptr) = grub_strlen (name) + 1;
 
155
  curptr = ((grub_uint32_t *) curptr) + 1;
 
156
  grub_memcpy (curptr, name, grub_strlen (name));
 
157
  curptr = ((grub_uint8_t *) curptr) + grub_strlen (name);
 
158
  grub_memset (curptr, 0, 4 - (grub_strlen (name) % 4));
 
159
  curptr = ((grub_uint8_t *) curptr) + (4 - (grub_strlen (name) % 4));
 
160
 
 
161
  /* Then the other values. */
 
162
  for (cur = start; cur; cur = cur->next)
 
163
    if (cur->datasize != -1)
 
164
      {
 
165
        int align_overhead;
 
166
 
 
167
        align_overhead = 4 - (cur->datasize % 4);
 
168
        if (align_overhead == 4)
 
169
          align_overhead = 0;
 
170
        grub_memset (curptr, 0, 32);
 
171
        grub_strncpy (curptr, cur->name, 31);
 
172
        curptr = ((grub_uint8_t *) curptr) + 32;
 
173
        *((grub_uint32_t *) curptr) = cur->datasize;
 
174
        curptr = ((grub_uint32_t *) curptr) + 1;
 
175
        grub_memcpy (curptr, cur->data, cur->datasize);
 
176
        curptr = ((grub_uint8_t *) curptr) + cur->datasize;
 
177
        grub_memset (curptr, 0, align_overhead);
 
178
        curptr = ((grub_uint8_t *) curptr) + align_overhead;
 
179
      }
 
180
 
 
181
  /* And then the keys. Recursively use this function. */
 
182
  for (cur = start; cur; cur = cur->next)
 
183
    if (cur->datasize == -1)
 
184
      if (!(curptr = grub_xnu_writetree_toheap_real (curptr,
 
185
                                                     cur->first_child,
 
186
                                                     cur->name)))
 
187
        return 0;
 
188
  return curptr;
 
189
}
 
190
 
 
191
grub_err_t
 
192
grub_xnu_writetree_toheap (grub_addr_t *target, grub_size_t *size)
 
193
{
 
194
  struct grub_xnu_devtree_key *chosen;
 
195
  struct grub_xnu_devtree_key *memorymap;
 
196
  struct grub_xnu_devtree_key *driverkey;
 
197
  struct grub_xnu_extdesc *extdesc;
 
198
  grub_err_t err;
 
199
  void *src;
 
200
 
 
201
  err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
 
202
  if (err)
 
203
    return err;
 
204
 
 
205
  /* Device tree itself is in the memory map of device tree. */
 
206
  /* Create a dummy value in memory-map. */
 
207
  chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
 
208
  if (! chosen)
 
209
    return grub_errno;
 
210
  memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
 
211
  if (! memorymap)
 
212
    return grub_errno;
 
213
 
 
214
  driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey));
 
215
  if (! driverkey)
 
216
    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
 
217
  driverkey->name = grub_strdup ("DeviceTree");
 
218
  if (! driverkey->name)
 
219
    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
 
220
  driverkey->datasize = sizeof (*extdesc);
 
221
  driverkey->next = memorymap->first_child;
 
222
  memorymap->first_child = driverkey;
 
223
  driverkey->data = extdesc
 
224
    = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc));
 
225
  if (! driverkey->data)
 
226
    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
 
227
 
 
228
  /* Allocate the space based on the size with dummy value. */
 
229
  *size = grub_xnu_writetree_get_size (grub_xnu_devtree_root, "/");
 
230
  err = grub_xnu_heap_malloc (ALIGN_UP (*size + 1, GRUB_XNU_PAGESIZE),
 
231
                              &src, target);
 
232
  if (err)
 
233
    return err;
 
234
 
 
235
  /* Put real data in the dummy. */
 
236
  extdesc->addr = *target;
 
237
  extdesc->size = (grub_uint32_t) *size;
 
238
 
 
239
  /* Write the tree to heap. */
 
240
  grub_xnu_writetree_toheap_real (src, grub_xnu_devtree_root, "/");
 
241
  return GRUB_ERR_NONE;
 
242
}
 
243
 
 
244
/* Find a key or value in parent key. */
 
245
struct grub_xnu_devtree_key *
 
246
grub_xnu_find_key (struct grub_xnu_devtree_key *parent, char *name)
 
247
{
 
248
  struct grub_xnu_devtree_key *cur;
 
249
  for (cur = parent; cur; cur = cur->next)
 
250
    if (grub_strcmp (cur->name, name) == 0)
 
251
      return cur;
 
252
  return 0;
 
253
}
 
254
 
 
255
struct grub_xnu_devtree_key *
 
256
grub_xnu_create_key (struct grub_xnu_devtree_key **parent, char *name)
 
257
{
 
258
  struct grub_xnu_devtree_key *ret;
 
259
  ret = grub_xnu_find_key (*parent, name);
 
260
  if (ret)
 
261
    return ret;
 
262
  ret = (struct grub_xnu_devtree_key *) grub_zalloc (sizeof (*ret));
 
263
  if (! ret)
 
264
    {
 
265
      grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create key %s", name);
 
266
      return 0;
 
267
    }
 
268
  ret->name = grub_strdup (name);
 
269
  if (! ret->name)
 
270
    {
 
271
      grub_free (ret);
 
272
      grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create key %s", name);
 
273
      return 0;
 
274
    }
 
275
  ret->datasize = -1;
 
276
  ret->next = *parent;
 
277
  *parent = ret;
 
278
  return ret;
 
279
}
 
280
 
 
281
struct grub_xnu_devtree_key *
 
282
grub_xnu_create_value (struct grub_xnu_devtree_key **parent, char *name)
 
283
{
 
284
  struct grub_xnu_devtree_key *ret;
 
285
  ret = grub_xnu_find_key (*parent, name);
 
286
  if (ret)
 
287
    {
 
288
      if (ret->datasize == -1)
 
289
        grub_xnu_free_devtree (ret->first_child);
 
290
      else if (ret->datasize)
 
291
        grub_free (ret->data);
 
292
      ret->datasize = 0;
 
293
      ret->data = 0;
 
294
      return ret;
 
295
    }
 
296
  ret = (struct grub_xnu_devtree_key *) grub_zalloc (sizeof (*ret));
 
297
  if (! ret)
 
298
    {
 
299
      grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create value %s", name);
 
300
      return 0;
 
301
    }
 
302
  ret->name = grub_strdup (name);
 
303
  if (! ret->name)
 
304
    {
 
305
      grub_free (ret);
 
306
      grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create value %s", name);
 
307
      return 0;
 
308
    }
 
309
  ret->next = *parent;
 
310
  *parent = ret;
 
311
  return ret;
 
312
}
 
313
 
 
314
static grub_err_t
 
315
grub_xnu_unload (void)
 
316
{
 
317
  grub_cpu_xnu_unload ();
 
318
 
 
319
  grub_xnu_free_devtree (grub_xnu_devtree_root);
 
320
  grub_xnu_devtree_root = 0;
 
321
 
 
322
  /* Free loaded image. */
 
323
  driversnum = 0;
 
324
  driverspackagenum = 0;
 
325
  grub_relocator_unload (grub_xnu_relocator);
 
326
  grub_xnu_relocator = NULL;
 
327
  grub_xnu_heap_target_start = 0;
 
328
  grub_xnu_heap_size = 0;
 
329
  grub_xnu_unlock ();
 
330
  return GRUB_ERR_NONE;
 
331
}
 
332
 
 
333
static grub_err_t
 
334
grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)),
 
335
                     int argc, char *args[])
 
336
{
 
337
  grub_err_t err;
 
338
  grub_macho_t macho;
 
339
  grub_uint32_t startcode, endcode;
 
340
  int i;
 
341
  char *ptr, *loadaddr;
 
342
  grub_addr_t loadaddr_target;
 
343
 
 
344
  if (argc < 1)
 
345
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
 
346
 
 
347
  grub_xnu_unload ();
 
348
 
 
349
  macho = grub_macho_open (args[0]);
 
350
  if (! macho)
 
351
    return grub_errno;
 
352
  if (! grub_macho_contains_macho32 (macho))
 
353
    {
 
354
      grub_macho_close (macho);
 
355
      return grub_error (GRUB_ERR_BAD_OS,
 
356
                         "kernel doesn't contain suitable 32-bit architecture");
 
357
    }
 
358
 
 
359
  err = grub_macho_size32 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
 
360
  if (err)
 
361
    {
 
362
      grub_macho_close (macho);
 
363
      grub_xnu_unload ();
 
364
      return err;
 
365
    }
 
366
 
 
367
  grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
 
368
                (unsigned long) endcode, (unsigned long) startcode);
 
369
 
 
370
  grub_xnu_relocator = grub_relocator_new ();
 
371
  if (!grub_xnu_relocator)
 
372
    return grub_errno;
 
373
  grub_xnu_heap_target_start = startcode;
 
374
  err = grub_xnu_heap_malloc (endcode - startcode, (void **) &loadaddr,
 
375
                              &loadaddr_target);
 
376
 
 
377
  if (err)
 
378
    {
 
379
      grub_macho_close (macho);
 
380
      grub_xnu_unload ();
 
381
      return err;
 
382
    }
 
383
 
 
384
  /* Load kernel. */
 
385
  err = grub_macho_load32 (macho, loadaddr - startcode, GRUB_MACHO_NOBSS);
 
386
  if (err)
 
387
    {
 
388
      grub_macho_close (macho);
 
389
      grub_xnu_unload ();
 
390
      return err;
 
391
    }
 
392
 
 
393
  grub_xnu_entry_point = grub_macho_get_entry_point32 (macho);
 
394
  if (! grub_xnu_entry_point)
 
395
    {
 
396
      grub_macho_close (macho);
 
397
      grub_xnu_unload ();
 
398
      return grub_error (GRUB_ERR_BAD_OS, "couldn't find entry point");
 
399
    }
 
400
 
 
401
  grub_macho_close (macho);
 
402
 
 
403
  err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
 
404
  if (err)
 
405
    {
 
406
      grub_xnu_unload ();
 
407
      return err;
 
408
    }
 
409
 
 
410
  /* Copy parameters to kernel command line. */
 
411
  ptr = grub_xnu_cmdline;
 
412
  for (i = 1; i < argc; i++)
 
413
    {
 
414
      if (ptr + grub_strlen (args[i]) + 1
 
415
          >= grub_xnu_cmdline + sizeof (grub_xnu_cmdline))
 
416
        break;
 
417
      grub_memcpy (ptr, args[i], grub_strlen (args[i]));
 
418
      ptr += grub_strlen (args[i]);
 
419
      *ptr = ' ';
 
420
      ptr++;
 
421
    }
 
422
 
 
423
  /* Replace last space by '\0'. */
 
424
  if (ptr != grub_xnu_cmdline)
 
425
    *(ptr - 1) = 0;
 
426
 
 
427
  grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);
 
428
 
 
429
  grub_xnu_lock ();
 
430
  grub_xnu_is_64bit = 0;
 
431
 
 
432
  return 0;
 
433
}
 
434
 
 
435
static grub_err_t
 
436
grub_cmd_xnu_kernel64 (grub_command_t cmd __attribute__ ((unused)),
 
437
                       int argc, char *args[])
 
438
{
 
439
  grub_err_t err;
 
440
  grub_macho_t macho;
 
441
  grub_uint64_t startcode, endcode;
 
442
  int i;
 
443
  char *ptr, *loadaddr;
 
444
  grub_addr_t loadaddr_target;
 
445
 
 
446
  if (argc < 1)
 
447
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
 
448
 
 
449
  grub_xnu_unload ();
 
450
 
 
451
  macho = grub_macho_open (args[0]);
 
452
  if (! macho)
 
453
    return grub_errno;
 
454
  if (! grub_macho_contains_macho64 (macho))
 
455
    {
 
456
      grub_macho_close (macho);
 
457
      return grub_error (GRUB_ERR_BAD_OS,
 
458
                         "kernel doesn't contain suitable 64-bit architecture");
 
459
    }
 
460
 
 
461
  err = grub_macho_size64 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
 
462
  if (err)
 
463
    {
 
464
      grub_macho_close (macho);
 
465
      grub_xnu_unload ();
 
466
      return err;
 
467
    }
 
468
 
 
469
  startcode &= 0x0fffffff;
 
470
  endcode &= 0x0fffffff;
 
471
 
 
472
  grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
 
473
                (unsigned long) endcode, (unsigned long) startcode);
 
474
 
 
475
  grub_xnu_relocator = grub_relocator_new ();
 
476
  if (!grub_xnu_relocator)
 
477
    return grub_errno;
 
478
  grub_xnu_heap_target_start = startcode;
 
479
  err = grub_xnu_heap_malloc (endcode - startcode, (void **) &loadaddr,
 
480
                              &loadaddr_target);
 
481
 
 
482
  if (err)
 
483
    {
 
484
      grub_macho_close (macho);
 
485
      grub_xnu_unload ();
 
486
      return err;
 
487
    }
 
488
 
 
489
  /* Load kernel. */
 
490
  err = grub_macho_load64 (macho, loadaddr - startcode, GRUB_MACHO_NOBSS);
 
491
  if (err)
 
492
    {
 
493
      grub_macho_close (macho);
 
494
      grub_xnu_unload ();
 
495
      return err;
 
496
    }
 
497
 
 
498
  grub_xnu_entry_point = grub_macho_get_entry_point64 (macho) & 0x0fffffff;
 
499
  if (! grub_xnu_entry_point)
 
500
    {
 
501
      grub_macho_close (macho);
 
502
      grub_xnu_unload ();
 
503
      return grub_error (GRUB_ERR_BAD_OS, "couldn't find entry point");
 
504
    }
 
505
 
 
506
  grub_macho_close (macho);
 
507
 
 
508
  err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
 
509
  if (err)
 
510
    {
 
511
      grub_xnu_unload ();
 
512
      return err;
 
513
    }
 
514
 
 
515
  /* Copy parameters to kernel command line. */
 
516
  ptr = grub_xnu_cmdline;
 
517
  for (i = 1; i < argc; i++)
 
518
    {
 
519
      if (ptr + grub_strlen (args[i]) + 1
 
520
          >= grub_xnu_cmdline + sizeof (grub_xnu_cmdline))
 
521
        break;
 
522
      grub_memcpy (ptr, args[i], grub_strlen (args[i]));
 
523
      ptr += grub_strlen (args[i]);
 
524
      *ptr = ' ';
 
525
      ptr++;
 
526
    }
 
527
 
 
528
  /* Replace last space by '\0'. */
 
529
  if (ptr != grub_xnu_cmdline)
 
530
    *(ptr - 1) = 0;
 
531
 
 
532
  grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);
 
533
 
 
534
  grub_xnu_lock ();
 
535
  grub_xnu_is_64bit = 1;
 
536
 
 
537
  return 0;
 
538
}
 
539
 
 
540
/* Register a memory in a memory map under name PREFIXSUFFIX
 
541
   and increment SUFFIX. */
 
542
static grub_err_t
 
543
grub_xnu_register_memory (char *prefix, int *suffix,
 
544
                          grub_addr_t addr, grub_size_t size)
 
545
{
 
546
  struct grub_xnu_devtree_key *chosen;
 
547
  struct grub_xnu_devtree_key *memorymap;
 
548
  struct grub_xnu_devtree_key *driverkey;
 
549
  struct grub_xnu_extdesc *extdesc;
 
550
 
 
551
  if (! grub_xnu_heap_size)
 
552
    return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
 
553
 
 
554
  chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
 
555
  if (! chosen)
 
556
    return grub_errno;
 
557
  memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
 
558
  if (! memorymap)
 
559
    return grub_errno;
 
560
 
 
561
  driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey));
 
562
  if (! driverkey)
 
563
    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register memory");
 
564
  if (suffix)
 
565
    {
 
566
      driverkey->name = grub_xasprintf ("%s%d", prefix, (*suffix)++);
 
567
      if (!driverkey->name)
 
568
        return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register memory");
 
569
    }
 
570
  else
 
571
    driverkey->name = grub_strdup (prefix);
 
572
  if (! driverkey->name)
 
573
    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register extension");
 
574
  driverkey->datasize = sizeof (*extdesc);
 
575
  driverkey->next = memorymap->first_child;
 
576
  memorymap->first_child = driverkey;
 
577
  driverkey->data = extdesc
 
578
    = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc));
 
579
  if (! driverkey->data)
 
580
    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register extension");
 
581
  extdesc->addr = addr;
 
582
  extdesc->size = (grub_uint32_t) size;
 
583
  return GRUB_ERR_NONE;
 
584
}
 
585
 
 
586
static inline char *
 
587
get_name_ptr (char *name)
 
588
{
 
589
  char *p = name, *p2;
 
590
  /* Skip Info.plist.  */
 
591
  p2 = grub_strrchr (p, '/');
 
592
  if (!p2)
 
593
    return name;
 
594
  if (p2 == name)
 
595
    return name + 1;
 
596
  p = p2 - 1;
 
597
 
 
598
  p2 = grub_strrchr (p, '/');
 
599
  if (!p2)
 
600
    return name;
 
601
  if (p2 == name)
 
602
    return name + 1;
 
603
  if (grub_memcmp (p2, "/Contents/", sizeof ("/Contents/") - 1) != 0)
 
604
    return p2 + 1;
 
605
 
 
606
  p = p2 - 1;
 
607
 
 
608
  p2 = grub_strrchr (p, '/');
 
609
  if (!p2)
 
610
    return name;
 
611
  return p2 + 1;
 
612
}
 
613
 
 
614
/* Load .kext. */
 
615
static grub_err_t
 
616
grub_xnu_load_driver (char *infoplistname, grub_file_t binaryfile)
 
617
{
 
618
  grub_macho_t macho;
 
619
  grub_err_t err;
 
620
  grub_file_t infoplist;
 
621
  struct grub_xnu_extheader *exthead;
 
622
  int neededspace = sizeof (*exthead);
 
623
  grub_uint8_t *buf, *buf0;
 
624
  grub_addr_t buf_target;
 
625
  grub_size_t infoplistsize = 0, machosize = 0;
 
626
  char *name, *nameend;
 
627
  int namelen;
 
628
 
 
629
  name = get_name_ptr (infoplistname);
 
630
  nameend = grub_strchr (name, '/');
 
631
 
 
632
  if (nameend)
 
633
    namelen = nameend - name;
 
634
  else
 
635
    namelen = grub_strlen (name);
 
636
 
 
637
  neededspace += namelen + 1;
 
638
 
 
639
  if (! grub_xnu_heap_size)
 
640
    return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
 
641
 
 
642
  /* Compute the needed space. */
 
643
  if (binaryfile)
 
644
    {
 
645
      macho = grub_macho_file (binaryfile);
 
646
      if (! macho || ! grub_macho_contains_macho32 (macho))
 
647
        {
 
648
          if (macho)
 
649
            grub_macho_close (macho);
 
650
          return grub_error (GRUB_ERR_BAD_OS,
 
651
                             "extension doesn't contain suitable architecture");
 
652
        }
 
653
      if (grub_xnu_is_64bit)
 
654
        machosize = grub_macho_filesize64 (macho);
 
655
      else
 
656
        machosize = grub_macho_filesize32 (macho);
 
657
      neededspace += machosize;
 
658
    }
 
659
  else
 
660
    macho = 0;
 
661
 
 
662
  if (infoplistname)
 
663
    infoplist = grub_file_open (infoplistname);
 
664
  else
 
665
    infoplist = 0;
 
666
  grub_errno = GRUB_ERR_NONE;
 
667
  if (infoplist)
 
668
    {
 
669
      infoplistsize = grub_file_size (infoplist);
 
670
      neededspace += infoplistsize + 1;
 
671
    }
 
672
  else
 
673
    infoplistsize = 0;
 
674
 
 
675
  /* Allocate the space. */
 
676
  err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
 
677
  if (err)
 
678
    return err;
 
679
  err = grub_xnu_heap_malloc (neededspace, (void **) &buf0, &buf_target);
 
680
  if (err)
 
681
    return err;
 
682
  buf = buf0;
 
683
 
 
684
  exthead = (struct grub_xnu_extheader *) buf;
 
685
  grub_memset (exthead, 0, sizeof (*exthead));
 
686
  buf += sizeof (*exthead);
 
687
 
 
688
  /* Load the binary. */
 
689
  if (macho)
 
690
    {
 
691
      exthead->binaryaddr = buf_target + (buf - buf0);
 
692
      exthead->binarysize = machosize;
 
693
      if (grub_xnu_is_64bit)
 
694
        err = grub_macho_readfile64 (macho, buf);
 
695
      else
 
696
        err = grub_macho_readfile32 (macho, buf);
 
697
      if (err)
 
698
        {
 
699
          grub_macho_close (macho);
 
700
          return err;
 
701
        }
 
702
      grub_macho_close (macho);
 
703
      buf += machosize;
 
704
    }
 
705
  grub_errno = GRUB_ERR_NONE;
 
706
 
 
707
  /* Load the plist. */
 
708
  if (infoplist)
 
709
    {
 
710
      exthead->infoplistaddr = buf_target + (buf - buf0);
 
711
      exthead->infoplistsize = infoplistsize + 1;
 
712
      if (grub_file_read (infoplist, buf, infoplistsize)
 
713
          != (grub_ssize_t) (infoplistsize))
 
714
        {
 
715
          grub_file_close (infoplist);
 
716
          grub_error_push ();
 
717
          return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s: ",
 
718
                             infoplistname);
 
719
        }
 
720
      grub_file_close (infoplist);
 
721
      buf[infoplistsize] = 0;
 
722
      buf += infoplistsize + 1;
 
723
    }
 
724
  grub_errno = GRUB_ERR_NONE;
 
725
 
 
726
  exthead->nameaddr = (buf - buf0) + buf_target;
 
727
  exthead->namesize = namelen + 1;
 
728
  grub_memcpy (buf, name, namelen);
 
729
  buf[namelen] = 0;
 
730
  buf += namelen + 1;
 
731
 
 
732
  /* Announce to kernel */
 
733
  return grub_xnu_register_memory ("Driver-", &driversnum, buf_target,
 
734
                                   neededspace);
 
735
}
 
736
 
 
737
/* Load mkext. */
 
738
static grub_err_t
 
739
grub_cmd_xnu_mkext (grub_command_t cmd __attribute__ ((unused)),
 
740
                    int argc, char *args[])
 
741
{
 
742
  grub_file_t file;
 
743
  void *loadto;
 
744
  grub_addr_t loadto_target;
 
745
  grub_err_t err;
 
746
  grub_off_t readoff = 0;
 
747
  grub_ssize_t readlen = -1;
 
748
  struct grub_macho_fat_header head;
 
749
  struct grub_macho_fat_arch *archs;
 
750
  int narchs, i;
 
751
 
 
752
  if (argc != 1)
 
753
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
 
754
 
 
755
  if (! grub_xnu_heap_size)
 
756
    return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
 
757
 
 
758
  file = grub_file_open (args[0]);
 
759
  if (! file)
 
760
    return grub_error (GRUB_ERR_FILE_NOT_FOUND,
 
761
                       "couldn't load driver package");
 
762
 
 
763
  /* Sometimes caches are fat binary. Errgh. */
 
764
  if (grub_file_read (file, &head, sizeof (head))
 
765
      != (grub_ssize_t) (sizeof (head)))
 
766
    {
 
767
      /* I don't know the internal structure of package but
 
768
         can hardly imagine a valid package shorter than 20 bytes. */
 
769
      grub_file_close (file);
 
770
      grub_error_push ();
 
771
      return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[0]);
 
772
    }
 
773
 
 
774
  /* Find the corresponding architecture. */
 
775
  if (grub_be_to_cpu32 (head.magic) == GRUB_MACHO_FAT_MAGIC)
 
776
    {
 
777
      narchs = grub_be_to_cpu32 (head.nfat_arch);
 
778
      archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
 
779
      if (! archs)
 
780
        {
 
781
          grub_file_close (file);
 
782
          grub_error_push ();
 
783
          return grub_error (GRUB_ERR_OUT_OF_MEMORY,
 
784
                             "couldn't read file %s", args[0]);
 
785
 
 
786
        }
 
787
      if (grub_file_read (file, archs,
 
788
                          sizeof (struct grub_macho_fat_arch) * narchs)
 
789
          != (grub_ssize_t) sizeof(struct grub_macho_fat_arch) * narchs)
 
790
        {
 
791
          grub_free (archs);
 
792
          grub_error_push ();
 
793
          return grub_error (GRUB_ERR_READ_ERROR, "cannot read fat header");
 
794
        }
 
795
      for (i = 0; i < narchs; i++)
 
796
        {
 
797
          if (!grub_xnu_is_64bit && GRUB_MACHO_CPUTYPE_IS_HOST32
 
798
              (grub_be_to_cpu32 (archs[i].cputype)))
 
799
            {
 
800
              readoff = grub_be_to_cpu32 (archs[i].offset);
 
801
              readlen = grub_be_to_cpu32 (archs[i].size);
 
802
            }
 
803
          if (grub_xnu_is_64bit && GRUB_MACHO_CPUTYPE_IS_HOST64
 
804
              (grub_be_to_cpu32 (archs[i].cputype)))
 
805
            {
 
806
              readoff = grub_be_to_cpu32 (archs[i].offset);
 
807
              readlen = grub_be_to_cpu32 (archs[i].size);
 
808
            }
 
809
        }
 
810
      grub_free (archs);
 
811
    }
 
812
  else
 
813
    {
 
814
      /* It's a flat file. Some sane people still exist. */
 
815
      readoff = 0;
 
816
      readlen = grub_file_size (file);
 
817
    }
 
818
 
 
819
  if (readlen == -1)
 
820
    {
 
821
      grub_file_close (file);
 
822
      return grub_error (GRUB_ERR_BAD_OS, "no suitable architecture is found");
 
823
    }
 
824
 
 
825
  /* Allocate space. */
 
826
  err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
 
827
  if (err)
 
828
    {
 
829
      grub_file_close (file);
 
830
      return err;
 
831
    }
 
832
 
 
833
  err = grub_xnu_heap_malloc (readlen, &loadto, &loadto_target);
 
834
  if (err)
 
835
    {
 
836
      grub_file_close (file);
 
837
      return err;
 
838
    }
 
839
 
 
840
  /* Read the file. */
 
841
  grub_file_seek (file, readoff);
 
842
  if (grub_file_read (file, loadto, readlen) != (grub_ssize_t) (readlen))
 
843
    {
 
844
      grub_file_close (file);
 
845
      grub_error_push ();
 
846
      return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[0]);
 
847
    }
 
848
  grub_file_close (file);
 
849
 
 
850
  /* Pass it to kernel. */
 
851
  return grub_xnu_register_memory ("DriversPackage-", &driverspackagenum,
 
852
                                   loadto_target, readlen);
 
853
}
 
854
 
 
855
static grub_err_t
 
856
grub_cmd_xnu_ramdisk (grub_command_t cmd __attribute__ ((unused)),
 
857
                      int argc, char *args[])
 
858
{
 
859
  grub_file_t file;
 
860
  void *loadto;
 
861
  grub_addr_t loadto_target;
 
862
  grub_err_t err;
 
863
  grub_size_t size;
 
864
 
 
865
  if (argc != 1)
 
866
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
 
867
 
 
868
  if (! grub_xnu_heap_size)
 
869
    return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
 
870
 
 
871
  file = grub_file_open (args[0]);
 
872
  if (! file)
 
873
    return grub_error (GRUB_ERR_FILE_NOT_FOUND,
 
874
                       "couldn't load ramdisk");
 
875
 
 
876
  err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
 
877
  if (err)
 
878
    return err;
 
879
 
 
880
  size = grub_file_size (file);
 
881
 
 
882
  err = grub_xnu_heap_malloc (size, &loadto, &loadto_target);
 
883
  if (err)
 
884
    return err;
 
885
  if (grub_file_read (file, loadto, size)
 
886
      != (grub_ssize_t) (size))
 
887
    {
 
888
      grub_file_close (file);
 
889
      grub_error_push ();
 
890
      return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[0]);
 
891
    }
 
892
  return grub_xnu_register_memory ("RAMDisk", 0, loadto_target, size);
 
893
}
 
894
 
 
895
/* Returns true if the kext should be loaded according to plist
 
896
   and osbundlereq. Also fill BINNAME. */
 
897
static int
 
898
grub_xnu_check_os_bundle_required (char *plistname, char *osbundlereq,
 
899
                                   char **binname)
 
900
{
 
901
  grub_file_t file;
 
902
  char *buf = 0, *tagstart = 0, *ptr1 = 0, *keyptr = 0;
 
903
  char *stringptr = 0, *ptr2 = 0;
 
904
  grub_size_t size;
 
905
  int depth = 0;
 
906
  int ret;
 
907
  int osbundlekeyfound = 0, binnamekeyfound = 0;
 
908
  if (binname)
 
909
    *binname = 0;
 
910
 
 
911
  file = grub_file_open (plistname);
 
912
  if (! file)
 
913
    {
 
914
      grub_file_close (file);
 
915
      grub_error_push ();
 
916
      grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", plistname);
 
917
      return 0;
 
918
    }
 
919
 
 
920
  size = grub_file_size (file);
 
921
  buf = grub_malloc (size);
 
922
  if (! buf)
 
923
    {
 
924
      grub_file_close (file);
 
925
      grub_error_push ();
 
926
      grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't read file %s", plistname);
 
927
      return 0;
 
928
    }
 
929
  if (grub_file_read (file, buf, size) != (grub_ssize_t) (size))
 
930
    {
 
931
      grub_file_close (file);
 
932
      grub_error_push ();
 
933
      grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", plistname);
 
934
      return 0;
 
935
    }
 
936
  grub_file_close (file);
 
937
 
 
938
  /* Set the return value for the case when no OSBundleRequired tag is found. */
 
939
  if (osbundlereq)
 
940
    ret = grub_strword (osbundlereq, "all") || grub_strword (osbundlereq, "-");
 
941
  else
 
942
    ret = 1;
 
943
 
 
944
  /* Parse plist. It's quite dirty and inextensible but does its job. */
 
945
  for (ptr1 = buf; ptr1 < buf + size; ptr1++)
 
946
    switch (*ptr1)
 
947
      {
 
948
      case '<':
 
949
        tagstart = ptr1;
 
950
        *ptr1 = 0;
 
951
        if (keyptr && depth == 4
 
952
            && grub_strcmp (keyptr, "OSBundleRequired") == 0)
 
953
          osbundlekeyfound = 1;
 
954
        if (keyptr && depth == 4 &&
 
955
            grub_strcmp (keyptr, "CFBundleExecutable") == 0)
 
956
          binnamekeyfound = 1;
 
957
        if (stringptr && osbundlekeyfound && osbundlereq && depth == 4)
 
958
          {
 
959
            for (ptr2 = stringptr; *ptr2; ptr2++)
 
960
              *ptr2 = grub_tolower (*ptr2);
 
961
            ret = grub_strword (osbundlereq, stringptr)
 
962
              || grub_strword (osbundlereq, "all");
 
963
          }
 
964
        if (stringptr && binnamekeyfound && binname && depth == 4)
 
965
          {
 
966
            if (*binname)
 
967
              grub_free (*binname);
 
968
            *binname = grub_strdup (stringptr);
 
969
          }
 
970
 
 
971
        *ptr1 = '<';
 
972
        keyptr = 0;
 
973
        stringptr = 0;
 
974
        break;
 
975
      case '>':
 
976
        if (! tagstart)
 
977
          {
 
978
            grub_free (buf);
 
979
            grub_error (GRUB_ERR_BAD_OS, "can't parse %s", plistname);
 
980
            return 0;
 
981
          }
 
982
        *ptr1 = 0;
 
983
        if (tagstart[1] == '?' || ptr1[-1] == '/')
 
984
          {
 
985
            osbundlekeyfound = 0;
 
986
            *ptr1 = '>';
 
987
            break;
 
988
          }
 
989
        if (depth == 3 && grub_strcmp (tagstart + 1, "key") == 0)
 
990
          keyptr = ptr1 + 1;
 
991
        if (depth == 3 && grub_strcmp (tagstart + 1, "string") == 0)
 
992
          stringptr = ptr1 + 1;
 
993
        else if (grub_strcmp (tagstart + 1, "/key") != 0)
 
994
          {
 
995
            osbundlekeyfound = 0;
 
996
            binnamekeyfound = 0;
 
997
          }
 
998
        *ptr1 = '>';
 
999
 
 
1000
        if (tagstart[1] == '/')
 
1001
          depth--;
 
1002
        else
 
1003
          depth++;
 
1004
        break;
 
1005
      }
 
1006
  grub_free (buf);
 
1007
 
 
1008
  return ret;
 
1009
}
 
1010
 
 
1011
/* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED */
 
1012
grub_err_t
 
1013
grub_xnu_scan_dir_for_kexts (char *dirname, char *osbundlerequired,
 
1014
                             int maxrecursion)
 
1015
{
 
1016
  grub_device_t dev;
 
1017
  char *device_name;
 
1018
  grub_fs_t fs;
 
1019
  const char *path;
 
1020
 
 
1021
  auto int load_hook (const char *filename,
 
1022
                      const struct grub_dirhook_info *info);
 
1023
  int load_hook (const char *filename, const struct grub_dirhook_info *info)
 
1024
  {
 
1025
    char *newdirname;
 
1026
    if (! info->dir)
 
1027
      return 0;
 
1028
    if (filename[0] == '.')
 
1029
      return 0;
 
1030
 
 
1031
    if (grub_strlen (filename) < 5 ||
 
1032
        grub_memcmp (filename + grub_strlen (filename) - 5, ".kext", 5) != 0)
 
1033
      return 0;
 
1034
 
 
1035
    newdirname
 
1036
      = grub_malloc (grub_strlen (dirname) + grub_strlen (filename) + 2);
 
1037
 
 
1038
    /* It's a .kext. Try to load it. */
 
1039
    if (newdirname)
 
1040
      {
 
1041
        grub_strcpy (newdirname, dirname);
 
1042
        newdirname[grub_strlen (newdirname) + 1] = 0;
 
1043
        newdirname[grub_strlen (newdirname)] = '/';
 
1044
        grub_strcpy (newdirname + grub_strlen (newdirname), filename);
 
1045
        grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
 
1046
                                     maxrecursion);
 
1047
        if (grub_errno == GRUB_ERR_BAD_OS)
 
1048
          grub_errno = GRUB_ERR_NONE;
 
1049
        grub_free (newdirname);
 
1050
      }
 
1051
    return 0;
 
1052
  }
 
1053
 
 
1054
  if (! grub_xnu_heap_size)
 
1055
    return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
 
1056
 
 
1057
  device_name = grub_file_get_device_name (dirname);
 
1058
  dev = grub_device_open (device_name);
 
1059
  if (dev)
 
1060
    {
 
1061
      fs = grub_fs_probe (dev);
 
1062
      path = grub_strchr (dirname, ')');
 
1063
      if (! path)
 
1064
        path = dirname;
 
1065
      else
 
1066
        path++;
 
1067
 
 
1068
      if (fs)
 
1069
        (fs->dir) (dev, path, load_hook);
 
1070
      grub_device_close (dev);
 
1071
    }
 
1072
  grub_free (device_name);
 
1073
 
 
1074
  return GRUB_ERR_NONE;
 
1075
}
 
1076
 
 
1077
/* Load extension DIRNAME. (extensions are directories in xnu) */
 
1078
grub_err_t
 
1079
grub_xnu_load_kext_from_dir (char *dirname, char *osbundlerequired,
 
1080
                             int maxrecursion)
 
1081
{
 
1082
  grub_device_t dev;
 
1083
  char *plistname = 0;
 
1084
  char *newdirname;
 
1085
  char *newpath;
 
1086
  char *device_name;
 
1087
  grub_fs_t fs;
 
1088
  const char *path;
 
1089
  char *binsuffix;
 
1090
  int usemacos = 0;
 
1091
  grub_file_t binfile;
 
1092
 
 
1093
  auto int load_hook (const char *filename,
 
1094
                      const struct grub_dirhook_info *info);
 
1095
 
 
1096
  int load_hook (const char *filename, const struct grub_dirhook_info *info)
 
1097
  {
 
1098
    if (grub_strlen (filename) > 15)
 
1099
      return 0;
 
1100
    grub_strcpy (newdirname + grub_strlen (dirname) + 1, filename);
 
1101
 
 
1102
    /* If the kext contains directory "Contents" all real stuff is in
 
1103
       this directory. */
 
1104
    if (info->dir && grub_strcasecmp (filename, "Contents") == 0)
 
1105
      grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
 
1106
                                   maxrecursion - 1);
 
1107
 
 
1108
    /* Directory "Plugins" contains nested kexts. */
 
1109
    if (info->dir && grub_strcasecmp (filename, "Plugins") == 0)
 
1110
      grub_xnu_scan_dir_for_kexts (newdirname, osbundlerequired,
 
1111
                                   maxrecursion - 1);
 
1112
 
 
1113
    /* Directory "MacOS" contains executable, otherwise executable is
 
1114
       on the top. */
 
1115
    if (info->dir && grub_strcasecmp (filename, "MacOS") == 0)
 
1116
      usemacos = 1;
 
1117
 
 
1118
    /* Info.plist is the file which governs our future actions. */
 
1119
    if (! info->dir && grub_strcasecmp (filename, "Info.plist") == 0
 
1120
        && ! plistname)
 
1121
      plistname = grub_strdup (newdirname);
 
1122
    return 0;
 
1123
  }
 
1124
 
 
1125
  newdirname = grub_malloc (grub_strlen (dirname) + 20);
 
1126
  if (! newdirname)
 
1127
    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate buffer");
 
1128
  grub_strcpy (newdirname, dirname);
 
1129
  newdirname[grub_strlen (dirname)] = '/';
 
1130
  newdirname[grub_strlen (dirname) + 1] = 0;
 
1131
  device_name = grub_file_get_device_name (dirname);
 
1132
  dev = grub_device_open (device_name);
 
1133
  if (dev)
 
1134
    {
 
1135
      fs = grub_fs_probe (dev);
 
1136
      path = grub_strchr (dirname, ')');
 
1137
      if (! path)
 
1138
        path = dirname;
 
1139
      else
 
1140
        path++;
 
1141
 
 
1142
      newpath = grub_strchr (newdirname, ')');
 
1143
      if (! newpath)
 
1144
        newpath = newdirname;
 
1145
      else
 
1146
        newpath++;
 
1147
 
 
1148
      /* Look at the directory. */
 
1149
      if (fs)
 
1150
        (fs->dir) (dev, path, load_hook);
 
1151
 
 
1152
      if (plistname && grub_xnu_check_os_bundle_required
 
1153
          (plistname, osbundlerequired, &binsuffix))
 
1154
        {
 
1155
          if (binsuffix)
 
1156
            {
 
1157
              /* Open the binary. */
 
1158
              char *binname = grub_malloc (grub_strlen (dirname)
 
1159
                                           + grub_strlen (binsuffix)
 
1160
                                           + sizeof ("/MacOS/"));
 
1161
              grub_strcpy (binname, dirname);
 
1162
              if (usemacos)
 
1163
                grub_strcpy (binname + grub_strlen (binname), "/MacOS/");
 
1164
              else
 
1165
                grub_strcpy (binname + grub_strlen (binname), "/");
 
1166
              grub_strcpy (binname + grub_strlen (binname), binsuffix);
 
1167
              grub_dprintf ("xnu", "%s:%s\n", plistname, binname);
 
1168
              binfile = grub_file_open (binname);
 
1169
              if (! binfile)
 
1170
                grub_errno = GRUB_ERR_NONE;
 
1171
 
 
1172
              /* Load the extension. */
 
1173
              grub_xnu_load_driver (plistname, binfile);
 
1174
              grub_free (binname);
 
1175
              grub_free (binsuffix);
 
1176
            }
 
1177
          else
 
1178
            {
 
1179
              grub_dprintf ("xnu", "%s:0\n", plistname);
 
1180
              grub_xnu_load_driver (plistname, 0);
 
1181
            }
 
1182
        }
 
1183
      grub_free (plistname);
 
1184
      grub_device_close (dev);
 
1185
    }
 
1186
  grub_free (device_name);
 
1187
 
 
1188
  return GRUB_ERR_NONE;
 
1189
}
 
1190
 
 
1191
 
 
1192
static int locked=0;
 
1193
static grub_dl_t my_mod;
 
1194
 
 
1195
/* Load the kext. */
 
1196
static grub_err_t
 
1197
grub_cmd_xnu_kext (grub_command_t cmd __attribute__ ((unused)),
 
1198
                   int argc, char *args[])
 
1199
{
 
1200
  grub_file_t binfile = 0;
 
1201
  if (argc == 2)
 
1202
    {
 
1203
      /* User explicitly specified plist and binary. */
 
1204
      if (grub_strcmp (args[1], "-") != 0)
 
1205
        {
 
1206
          binfile = grub_file_open (args[1]);
 
1207
          if (! binfile)
 
1208
            {
 
1209
              grub_error (GRUB_ERR_BAD_OS, "can't open file");
 
1210
              return GRUB_ERR_NONE;
 
1211
            }
 
1212
        }
 
1213
      return grub_xnu_load_driver (grub_strcmp (args[0], "-") ? args[0] : 0,
 
1214
                                   binfile);
 
1215
    }
 
1216
 
 
1217
  /* load kext normally. */
 
1218
  if (argc == 1)
 
1219
    return grub_xnu_load_kext_from_dir (args[0], 0, 10);
 
1220
 
 
1221
  return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
 
1222
}
 
1223
 
 
1224
/* Load a directory containing kexts. */
 
1225
static grub_err_t
 
1226
grub_cmd_xnu_kextdir (grub_command_t cmd __attribute__ ((unused)),
 
1227
                      int argc, char *args[])
 
1228
{
 
1229
  if (argc != 1 && argc != 2)
 
1230
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "directory name required");
 
1231
 
 
1232
  if (argc == 1)
 
1233
    return grub_xnu_scan_dir_for_kexts (args[0],
 
1234
                                        "console,root,local-root,network-root",
 
1235
                                        10);
 
1236
  else
 
1237
    {
 
1238
      char *osbundlerequired = grub_strdup (args[1]), *ptr;
 
1239
      grub_err_t err;
 
1240
      if (! osbundlerequired)
 
1241
        return grub_error (GRUB_ERR_OUT_OF_MEMORY,
 
1242
                           "couldn't allocate string temporary space");
 
1243
      for (ptr = osbundlerequired; *ptr; ptr++)
 
1244
        *ptr = grub_tolower (*ptr);
 
1245
      err = grub_xnu_scan_dir_for_kexts (args[0], osbundlerequired, 10);
 
1246
      grub_free (osbundlerequired);
 
1247
      return err;
 
1248
    }
 
1249
}
 
1250
 
 
1251
static inline int
 
1252
hextoval (char c)
 
1253
{
 
1254
  if (c >= '0' && c <= '9')
 
1255
    return c - '0';
 
1256
  if (c >= 'a' && c <= 'z')
 
1257
    return c - 'a' + 10;
 
1258
  if (c >= 'A' && c <= 'Z')
 
1259
    return c - 'A' + 10;
 
1260
  return 0;
 
1261
}
 
1262
 
 
1263
static inline void
 
1264
unescape (char *name, char *curdot, char *nextdot, int *len)
 
1265
{
 
1266
  char *ptr, *dptr;
 
1267
  dptr = name;
 
1268
  for (ptr = curdot; ptr < nextdot;)
 
1269
    if (ptr + 2 < nextdot && *ptr == '%')
 
1270
      {
 
1271
        *dptr = (hextoval (ptr[1]) << 4) | (hextoval (ptr[2]));
 
1272
        ptr += 3;
 
1273
        dptr++;
 
1274
      }
 
1275
    else
 
1276
      {
 
1277
        *dptr = *ptr;
 
1278
        ptr++;
 
1279
        dptr++;
 
1280
      }
 
1281
  *len = dptr - name;
 
1282
}
 
1283
 
 
1284
grub_err_t
 
1285
grub_xnu_fill_devicetree (void)
 
1286
{
 
1287
  auto int iterate_env (struct grub_env_var *var);
 
1288
  int iterate_env (struct grub_env_var *var)
 
1289
  {
 
1290
    char *nextdot = 0, *curdot;
 
1291
    struct grub_xnu_devtree_key **curkey = &grub_xnu_devtree_root;
 
1292
    struct grub_xnu_devtree_key *curvalue;
 
1293
    char *name = 0, *data;
 
1294
    int len;
 
1295
 
 
1296
    if (grub_memcmp (var->name, "XNU.DeviceTree.",
 
1297
                     sizeof ("XNU.DeviceTree.") - 1) != 0)
 
1298
      return 0;
 
1299
 
 
1300
    curdot = var->name + sizeof ("XNU.DeviceTree.") - 1;
 
1301
    nextdot = grub_strchr (curdot, '.');
 
1302
    if (nextdot)
 
1303
      nextdot++;
 
1304
    while (nextdot)
 
1305
      {
 
1306
        name = grub_realloc (name, nextdot - curdot + 1);
 
1307
 
 
1308
        if (!name)
 
1309
          return 1;
 
1310
 
 
1311
        unescape (name, curdot, nextdot, &len);
 
1312
        name[len - 1] = 0;
 
1313
 
 
1314
        curkey = &(grub_xnu_create_key (curkey, name)->first_child);
 
1315
 
 
1316
        curdot = nextdot;
 
1317
        nextdot = grub_strchr (nextdot, '.');
 
1318
        if (nextdot)
 
1319
          nextdot++;
 
1320
      }
 
1321
 
 
1322
    nextdot = curdot + grub_strlen (curdot) + 1;
 
1323
 
 
1324
    name = grub_realloc (name, nextdot - curdot + 1);
 
1325
   
 
1326
    if (!name)
 
1327
      return 1;
 
1328
   
 
1329
    unescape (name, curdot, nextdot, &len);
 
1330
    name[len] = 0;
 
1331
 
 
1332
    curvalue = grub_xnu_create_value (curkey, name);
 
1333
    grub_free (name);
 
1334
   
 
1335
    data = grub_malloc (grub_strlen (var->value) + 1);
 
1336
    if (!data)
 
1337
      return 1;
 
1338
   
 
1339
    unescape (data, var->value, var->value + grub_strlen (var->value),
 
1340
              &len);
 
1341
    curvalue->datasize = len;
 
1342
    curvalue->data = data;
 
1343
 
 
1344
    return 0;
 
1345
  }
 
1346
 
 
1347
  grub_env_iterate (iterate_env);
 
1348
 
 
1349
  return grub_errno;
 
1350
}
 
1351
 
 
1352
struct grub_video_bitmap *grub_xnu_bitmap = 0;
 
1353
grub_xnu_bitmap_mode_t grub_xnu_bitmap_mode;
 
1354
 
 
1355
/* Option array indices.  */
 
1356
#define XNU_SPLASH_CMD_ARGINDEX_MODE 0
 
1357
 
 
1358
static const struct grub_arg_option xnu_splash_cmd_options[] =
 
1359
  {
 
1360
    {"mode", 'm', 0, "Background image mode.", "stretch|normal",
 
1361
     ARG_TYPE_STRING},
 
1362
    {0, 0, 0, 0, 0, 0}
 
1363
  };
 
1364
 
 
1365
static grub_err_t
 
1366
grub_cmd_xnu_splash (grub_extcmd_context_t ctxt,
 
1367
                     int argc, char *args[])
 
1368
{
 
1369
  grub_err_t err;
 
1370
  if (argc != 1)
 
1371
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
 
1372
 
 
1373
  if (ctxt->state[XNU_SPLASH_CMD_ARGINDEX_MODE].set &&
 
1374
      grub_strcmp (ctxt->state[XNU_SPLASH_CMD_ARGINDEX_MODE].arg,
 
1375
                   "stretch") == 0)
 
1376
    grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_STRETCH;
 
1377
  else
 
1378
    grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_CENTER;
 
1379
 
 
1380
  err = grub_video_bitmap_load (&grub_xnu_bitmap, args[0]);
 
1381
  if (err)
 
1382
    grub_xnu_bitmap = 0;
 
1383
 
 
1384
  return err;
 
1385
}
 
1386
 
 
1387
 
 
1388
#ifndef GRUB_MACHINE_EMU
 
1389
static grub_err_t
 
1390
grub_cmd_xnu_resume (grub_command_t cmd __attribute__ ((unused)),
 
1391
                     int argc, char *args[])
 
1392
{
 
1393
  if (argc != 1)
 
1394
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
 
1395
 
 
1396
  return grub_xnu_resume (args[0]);
 
1397
}
 
1398
#endif
 
1399
 
 
1400
void
 
1401
grub_xnu_lock ()
 
1402
{
 
1403
  if (!locked)
 
1404
    grub_dl_ref (my_mod);
 
1405
  locked = 1;
 
1406
}
 
1407
 
 
1408
void
 
1409
grub_xnu_unlock ()
 
1410
{
 
1411
  if (locked)
 
1412
    grub_dl_unref (my_mod);
 
1413
  locked = 0;
 
1414
}
 
1415
 
 
1416
static grub_command_t cmd_kernel64, cmd_kernel, cmd_mkext, cmd_kext;
 
1417
static grub_command_t cmd_kextdir, cmd_ramdisk, cmd_resume;
 
1418
static grub_extcmd_t cmd_splash;
 
1419
 
 
1420
GRUB_MOD_INIT(xnu)
 
1421
{
 
1422
  cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0,
 
1423
                                      N_("Load XNU image."));
 
1424
  cmd_kernel64 = grub_register_command ("xnu_kernel64", grub_cmd_xnu_kernel64,
 
1425
                                        0, N_("Load 64-bit XNU image."));
 
1426
  cmd_mkext = grub_register_command ("xnu_mkext", grub_cmd_xnu_mkext, 0,
 
1427
                                     N_("Load XNU extension package."));
 
1428
  cmd_kext = grub_register_command ("xnu_kext", grub_cmd_xnu_kext, 0,
 
1429
                                    N_("Load XNU extension."));
 
1430
  cmd_kextdir = grub_register_command ("xnu_kextdir", grub_cmd_xnu_kextdir,
 
1431
                                       N_("DIRECTORY [OSBundleRequired]"),
 
1432
                                       N_("Load XNU extension directory."));
 
1433
  cmd_ramdisk = grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk, 0,
 
1434
                                       "Load XNU ramdisk. "
 
1435
                                       "It will be seen as md0.");
 
1436
  cmd_splash = grub_register_extcmd ("xnu_splash",
 
1437
                                     grub_cmd_xnu_splash, 0, 0,
 
1438
                                     N_("Load a splash image for XNU."),
 
1439
                                     xnu_splash_cmd_options);
 
1440
 
 
1441
#ifndef GRUB_MACHINE_EMU
 
1442
  cmd_resume = grub_register_command ("xnu_resume", grub_cmd_xnu_resume,
 
1443
                                      0, N_("Load XNU hibernate image."));
 
1444
#endif
 
1445
 
 
1446
  grub_cpu_xnu_init ();
 
1447
 
 
1448
  my_mod = mod;
 
1449
}
 
1450
 
 
1451
GRUB_MOD_FINI(xnu)
 
1452
{
 
1453
#ifndef GRUB_MACHINE_EMU
 
1454
  grub_unregister_command (cmd_resume);
 
1455
#endif
 
1456
  grub_unregister_command (cmd_mkext);
 
1457
  grub_unregister_command (cmd_kext);
 
1458
  grub_unregister_command (cmd_kextdir);
 
1459
  grub_unregister_command (cmd_ramdisk);
 
1460
  grub_unregister_command (cmd_kernel);
 
1461
  grub_unregister_extcmd (cmd_splash);
 
1462
  grub_unregister_command (cmd_kernel64);
 
1463
 
 
1464
  grub_cpu_xnu_fini ();
 
1465
}