1 /* openfw.c -- Open firmware support functions. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/types.h>
22 #include <grub/misc.h>
24 #include <grub/ieee1275/ieee1275.h>
27 enum grub_ieee1275_parse_type
32 GRUB_PARSE_DEVICE_TYPE
36 fill_alias (struct grub_ieee1275_devalias *alias)
40 if (grub_ieee1275_get_property (alias->phandle, "device_type", alias->type,
41 IEEE1275_MAX_PROP_LEN, &actual))
44 if (alias->parent_dev == alias->phandle)
47 if (grub_ieee1275_package_to_path (alias->phandle, alias->path,
48 IEEE1275_MAX_PATH_LEN, &actual))
51 if (grub_strcmp (alias->parent_path, alias->path) == 0)
54 if (grub_ieee1275_get_property (alias->phandle, "name", alias->name,
55 IEEE1275_MAX_PROP_LEN, &actual))
57 grub_dprintf ("devalias", "device path=%s\n", alias->path);
62 grub_ieee1275_devalias_free (struct grub_ieee1275_devalias *alias)
64 grub_free (alias->name);
65 grub_free (alias->type);
66 grub_free (alias->path);
67 grub_free (alias->parent_path);
71 alias->parent_path = 0;
72 alias->phandle = GRUB_IEEE1275_PHANDLE_INVALID;
76 grub_ieee1275_children_peer (struct grub_ieee1275_devalias *alias)
78 while (grub_ieee1275_peer (alias->phandle, &alias->phandle) != -1)
79 if (fill_alias (alias))
81 grub_ieee1275_devalias_free (alias);
85 grub_ieee1275_children_first (const char *devpath,
86 struct grub_ieee1275_devalias *alias)
88 grub_ieee1275_phandle_t dev;
90 grub_dprintf ("devalias", "iterating children of %s\n",
95 alias->parent_path = 0;
98 if (grub_ieee1275_finddevice (devpath, &dev))
101 if (grub_ieee1275_child (dev, &alias->phandle))
104 alias->type = grub_malloc (IEEE1275_MAX_PROP_LEN);
107 alias->path = grub_malloc (IEEE1275_MAX_PATH_LEN);
110 grub_free (alias->type);
113 alias->parent_path = grub_strdup (devpath);
114 if (!alias->parent_path)
116 grub_free (alias->path);
117 grub_free (alias->type);
121 alias->name = grub_malloc (IEEE1275_MAX_PROP_LEN);
124 grub_free (alias->path);
125 grub_free (alias->type);
126 grub_free (alias->parent_path);
129 if (!fill_alias (alias))
130 grub_ieee1275_children_peer (alias);
134 iterate_recursively (const char *path,
135 int (*hook) (struct grub_ieee1275_devalias *alias))
137 struct grub_ieee1275_devalias alias;
140 FOR_IEEE1275_DEVCHILDREN(path, alias)
145 ret = iterate_recursively (alias.path, hook);
149 grub_ieee1275_devalias_free (&alias);
154 grub_ieee1275_devices_iterate (int (*hook) (struct grub_ieee1275_devalias *alias))
156 return iterate_recursively ("/", hook);
160 grub_ieee1275_devalias_init_iterator (struct grub_ieee1275_devalias *alias)
164 alias->parent_path = 0;
167 grub_dprintf ("devalias", "iterating aliases\n");
169 if (grub_ieee1275_finddevice ("/aliases", &alias->parent_dev))
172 alias->name = grub_malloc (IEEE1275_MAX_PROP_LEN);
176 alias->type = grub_malloc (IEEE1275_MAX_PROP_LEN);
179 grub_free (alias->name);
184 alias->name[0] = '\0';
188 grub_ieee1275_devalias_next (struct grub_ieee1275_devalias *alias)
194 grub_ssize_t pathlen;
200 grub_free (alias->path);
203 tmp = grub_strdup (alias->name);
204 if (grub_ieee1275_next_property (alias->parent_dev, tmp,
208 grub_ieee1275_devalias_free (alias);
213 grub_dprintf ("devalias", "devalias name = %s\n", alias->name);
215 grub_ieee1275_get_property_length (alias->parent_dev, alias->name, &pathlen);
217 /* The property `name' is a special case we should skip. */
218 if (grub_strcmp (alias->name, "name") == 0)
221 /* Sun's OpenBoot often doesn't zero terminate the device alias
222 strings, so we will add a NULL byte at the end explicitly. */
225 alias->path = grub_malloc (pathlen + 1);
228 grub_ieee1275_devalias_free (alias);
232 if (grub_ieee1275_get_property (alias->parent_dev, alias->name, alias->path,
233 pathlen, &actual) || actual < 0)
235 grub_dprintf ("devalias", "get_property (%s) failed\n", alias->name);
236 grub_free (alias->path);
239 if (actual > pathlen)
241 alias->path[actual] = '\0';
242 alias->path[pathlen] = '\0';
244 if (grub_ieee1275_finddevice (alias->path, &alias->phandle))
246 grub_dprintf ("devalias", "finddevice (%s) failed\n", alias->path);
247 grub_free (alias->path);
252 if (grub_ieee1275_get_property (alias->phandle, "device_type", alias->type,
253 IEEE1275_MAX_PROP_LEN, &actual))
255 /* NAND device don't have device_type property. */
262 /* Call the "map" method of /chosen/mmu. */
264 grub_ieee1275_map (grub_addr_t phys, grub_addr_t virt, grub_size_t size,
268 struct grub_ieee1275_common_hdr common;
269 grub_ieee1275_cell_t method;
270 grub_ieee1275_cell_t ihandle;
271 grub_ieee1275_cell_t mode;
272 grub_ieee1275_cell_t size;
273 grub_ieee1275_cell_t virt;
275 grub_ieee1275_cell_t phys_high;
277 grub_ieee1275_cell_t phys_low;
278 grub_ieee1275_cell_t catch_result;
281 INIT_IEEE1275_COMMON (&args.common, "call-method",
288 args.method = (grub_ieee1275_cell_t) "map";
289 args.ihandle = grub_ieee1275_mmu;
293 args.phys_low = phys;
296 args.mode = mode; /* Format is WIMG0PP. */
297 args.catch_result = (grub_ieee1275_cell_t) -1;
299 if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
302 return args.catch_result;
306 grub_claimmap (grub_addr_t addr, grub_size_t size)
308 if (grub_ieee1275_claim (addr, size, 0, 0))
311 if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_REAL_MODE)
312 && grub_ieee1275_map (addr, addr, size, 0x00))
314 grub_error (GRUB_ERR_OUT_OF_MEMORY, "map failed: address 0x%llx, size 0x%llx\n",
315 (long long) addr, (long long) size);
316 grub_ieee1275_release (addr, size);
320 return GRUB_ERR_NONE;
323 /* Get the device arguments of the Open Firmware node name `path'. */
325 grub_ieee1275_get_devargs (const char *path)
327 char *colon = grub_strchr (path, ':');
332 return grub_strdup (colon + 1);
335 /* Get the device path of the Open Firmware node name `path'. */
337 grub_ieee1275_get_devname (const char *path)
339 char *colon = grub_strchr (path, ':');
340 int pathlen = grub_strlen (path);
341 struct grub_ieee1275_devalias curalias;
343 pathlen = (int)(colon - path);
345 /* Try to find an alias for this device. */
346 FOR_IEEE1275_DEVALIASES (curalias)
347 /* briQ firmware can change capitalization in /chosen/bootpath. */
348 if (grub_strncasecmp (curalias.path, path, pathlen) == 0
349 && curalias.path[pathlen] == 0)
352 newpath = grub_strdup (curalias.name);
353 grub_ieee1275_devalias_free (&curalias);
357 return grub_strndup (path, pathlen);
361 grub_ieee1275_parse_args (const char *path, enum grub_ieee1275_parse_type ptype)
363 char type[64]; /* XXX check size. */
364 char *device = grub_ieee1275_get_devname (path);
366 grub_ieee1275_phandle_t dev;
368 /* We need to know what type of device it is in order to parse the full
369 file path properly. */
370 if (grub_ieee1275_finddevice (device, &dev))
372 grub_error (GRUB_ERR_UNKNOWN_DEVICE, "device %s not found", device);
375 if (grub_ieee1275_get_property (dev, "device_type", &type, sizeof type, 0))
377 grub_error (GRUB_ERR_UNKNOWN_DEVICE,
378 "device %s lacks a device_type property", device);
384 case GRUB_PARSE_DEVICE:
385 ret = grub_strdup (device);
387 case GRUB_PARSE_DEVICE_TYPE:
388 ret = grub_strdup (type);
390 case GRUB_PARSE_FILENAME:
395 args = grub_ieee1275_get_devargs (path);
397 /* Shouldn't happen. */
400 /* The syntax of the device arguments is defined in the CHRP and PReP
401 IEEE1275 bindings: "[partition][,[filename]]". */
402 comma = grub_strchr (args, ',');
406 char *filepath = comma + 1;
408 /* Make sure filepath has leading backslash. */
409 if (filepath[0] != '\\')
410 ret = grub_xasprintf ("\\%s", filepath);
412 ret = grub_strdup (filepath);
417 case GRUB_PARSE_PARTITION:
422 if (grub_strcmp ("block", type) != 0)
425 args = grub_ieee1275_get_devargs (path);
427 /* Shouldn't happen. */
430 comma = grub_strchr (args, ',');
432 ret = grub_strdup (args);
434 ret = grub_strndup (args, (grub_size_t)(comma - args));
435 /* Consistently provide numbered partitions to GRUB.
436 OpenBOOT traditionally uses alphabetical partition
438 if (ret[0] >= 'a' && ret[0] <= 'z')
439 ret[0] = '1' + (ret[0] - 'a');
445 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
446 "unsupported type %s for device %s", type, device);
455 grub_ieee1275_get_device_type (const char *path)
457 return grub_ieee1275_parse_args (path, GRUB_PARSE_DEVICE_TYPE);
461 grub_ieee1275_get_aliasdevname (const char *path)
463 return grub_ieee1275_parse_args (path, GRUB_PARSE_DEVICE);
467 grub_ieee1275_get_filename (const char *path)
469 return grub_ieee1275_parse_args (path, GRUB_PARSE_FILENAME);
472 /* Convert a device name from IEEE1275 syntax to GRUB syntax. */
474 grub_ieee1275_encode_devname (const char *path)
476 char *device = grub_ieee1275_get_devname (path);
482 encoding = grub_malloc (sizeof ("ieee1275/") + 2 * grub_strlen (device)
483 + sizeof (",XXXXXXXXXXXX"));
490 partition = grub_ieee1275_parse_args (path, GRUB_PARSE_PARTITION);
492 optr = grub_stpcpy (encoding, "ieee1275/");
493 for (iptr = device; *iptr; )
499 if (partition && partition[0])
501 unsigned int partno = grub_strtoul (partition, 0, 0);
505 if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_0_BASED_PARTITIONS))
506 /* GRUB partition 1 is OF partition 0. */
509 grub_snprintf (optr, sizeof ("XXXXXXXXXXXX"), "%d", partno);
514 grub_free (partition);
520 /* Resolve aliases. */
522 grub_ieee1275_canonicalise_devname (const char *path)
526 struct grub_ieee1275_common_hdr common;
527 grub_ieee1275_cell_t path;
528 grub_ieee1275_cell_t buf;
529 grub_ieee1275_cell_t inlen;
530 grub_ieee1275_cell_t outlen;
534 grub_size_t bufsize = 64;
537 for (i = 0; i < 2; i++)
541 buf = grub_malloc (bufsize);
545 INIT_IEEE1275_COMMON (&args.common, "canon", 3, 1);
546 args.path = (grub_ieee1275_cell_t) path;
547 args.buf = (grub_ieee1275_cell_t) buf;
548 args.inlen = (grub_ieee1275_cell_t) (bufsize - 1);
550 if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
552 if (args.outlen > bufsize - 1)
554 bufsize = args.outlen + 2;
559 /* Shouldn't reach here. */
565 grub_ieee1275_get_boot_dev (void)
568 grub_ssize_t bootpath_size;
570 if (grub_ieee1275_get_property_length (grub_ieee1275_chosen, "bootpath",
572 || bootpath_size <= 0)
574 /* Should never happen. */
575 grub_printf ("/chosen/bootpath property missing!\n");
579 bootpath = (char *) grub_malloc ((grub_size_t) bootpath_size + 64);
585 grub_ieee1275_get_property (grub_ieee1275_chosen, "bootpath", bootpath,
586 (grub_size_t) bootpath_size + 1, 0);
587 bootpath[bootpath_size] = '\0';