123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594 |
- /* openfw.c -- Open firmware support functions. */
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc.
- *
- * GRUB is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GRUB is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <grub/types.h>
- #include <grub/err.h>
- #include <grub/misc.h>
- #include <grub/mm.h>
- #include <grub/ieee1275/ieee1275.h>
- #include <grub/net.h>
- enum grub_ieee1275_parse_type
- {
- GRUB_PARSE_FILENAME,
- GRUB_PARSE_PARTITION,
- GRUB_PARSE_DEVICE,
- GRUB_PARSE_DEVICE_TYPE
- };
- static int
- fill_alias (struct grub_ieee1275_devalias *alias)
- {
- grub_ssize_t actual;
- if (grub_ieee1275_get_property (alias->phandle, "device_type", alias->type,
- IEEE1275_MAX_PROP_LEN, &actual))
- alias->type[0] = 0;
- if (alias->parent_dev == alias->phandle)
- return 0;
- if (grub_ieee1275_package_to_path (alias->phandle, alias->path,
- IEEE1275_MAX_PATH_LEN, &actual))
- return 0;
- if (grub_strcmp (alias->parent_path, alias->path) == 0)
- return 0;
- if (grub_ieee1275_get_property (alias->phandle, "name", alias->name,
- IEEE1275_MAX_PROP_LEN, &actual))
- return 0;
- grub_dprintf ("devalias", "device path=%s\n", alias->path);
- return 1;
- }
- void
- grub_ieee1275_devalias_free (struct grub_ieee1275_devalias *alias)
- {
- grub_free (alias->name);
- grub_free (alias->type);
- grub_free (alias->path);
- grub_free (alias->parent_path);
- alias->name = 0;
- alias->type = 0;
- alias->path = 0;
- alias->parent_path = 0;
- alias->phandle = GRUB_IEEE1275_PHANDLE_INVALID;
- }
- void
- grub_ieee1275_children_peer (struct grub_ieee1275_devalias *alias)
- {
- while (grub_ieee1275_peer (alias->phandle, &alias->phandle) != -1)
- if (fill_alias (alias))
- return;
- grub_ieee1275_devalias_free (alias);
- }
- void
- grub_ieee1275_children_first (const char *devpath,
- struct grub_ieee1275_devalias *alias)
- {
- grub_ieee1275_phandle_t dev;
- grub_dprintf ("devalias", "iterating children of %s\n",
- devpath);
- alias->name = 0;
- alias->path = 0;
- alias->parent_path = 0;
- alias->type = 0;
- if (grub_ieee1275_finddevice (devpath, &dev))
- return;
- if (grub_ieee1275_child (dev, &alias->phandle))
- return;
- alias->type = grub_malloc (IEEE1275_MAX_PROP_LEN);
- if (!alias->type)
- return;
- alias->path = grub_malloc (IEEE1275_MAX_PATH_LEN);
- if (!alias->path)
- {
- grub_free (alias->type);
- return;
- }
- alias->parent_path = grub_strdup (devpath);
- if (!alias->parent_path)
- {
- grub_free (alias->path);
- grub_free (alias->type);
- return;
- }
- alias->name = grub_malloc (IEEE1275_MAX_PROP_LEN);
- if (!alias->name)
- {
- grub_free (alias->path);
- grub_free (alias->type);
- grub_free (alias->parent_path);
- return;
- }
- if (!fill_alias (alias))
- grub_ieee1275_children_peer (alias);
- }
- static int
- iterate_recursively (const char *path,
- int (*hook) (struct grub_ieee1275_devalias *alias))
- {
- struct grub_ieee1275_devalias alias;
- int ret = 0;
- FOR_IEEE1275_DEVCHILDREN(path, alias)
- {
- ret = hook (&alias);
- if (ret)
- break;
- ret = iterate_recursively (alias.path, hook);
- if (ret)
- break;
- }
- grub_ieee1275_devalias_free (&alias);
- return ret;
- }
- int
- grub_ieee1275_devices_iterate (int (*hook) (struct grub_ieee1275_devalias *alias))
- {
- return iterate_recursively ("/", hook);
- }
- void
- grub_ieee1275_devalias_init_iterator (struct grub_ieee1275_devalias *alias)
- {
- alias->name = 0;
- alias->path = 0;
- alias->parent_path = 0;
- alias->type = 0;
- grub_dprintf ("devalias", "iterating aliases\n");
- if (grub_ieee1275_finddevice ("/aliases", &alias->parent_dev))
- return;
- alias->name = grub_malloc (IEEE1275_MAX_PROP_LEN);
- if (!alias->name)
- return;
- alias->type = grub_malloc (IEEE1275_MAX_PROP_LEN);
- if (!alias->type)
- {
- grub_free (alias->name);
- alias->name = 0;
- return;
- }
- alias->name[0] = '\0';
- }
- int
- grub_ieee1275_devalias_next (struct grub_ieee1275_devalias *alias)
- {
- if (!alias->name)
- return 0;
- while (1)
- {
- grub_ssize_t pathlen;
- grub_ssize_t actual;
- char *tmp;
- if (alias->path)
- {
- grub_free (alias->path);
- alias->path = 0;
- }
- tmp = grub_strdup (alias->name);
- if (grub_ieee1275_next_property (alias->parent_dev, tmp,
- alias->name) <= 0)
- {
- grub_free (tmp);
- grub_ieee1275_devalias_free (alias);
- return 0;
- }
- grub_free (tmp);
- grub_dprintf ("devalias", "devalias name = %s\n", alias->name);
- grub_ieee1275_get_property_length (alias->parent_dev, alias->name, &pathlen);
- /* The property `name' is a special case we should skip. */
- if (grub_strcmp (alias->name, "name") == 0)
- continue;
- /* Sun's OpenBoot often doesn't zero terminate the device alias
- strings, so we will add a NULL byte at the end explicitly. */
- pathlen += 1;
- alias->path = grub_malloc (pathlen + 1);
- if (! alias->path)
- {
- grub_ieee1275_devalias_free (alias);
- return 0;
- }
- if (grub_ieee1275_get_property (alias->parent_dev, alias->name, alias->path,
- pathlen, &actual) || actual < 0)
- {
- grub_dprintf ("devalias", "get_property (%s) failed\n", alias->name);
- grub_free (alias->path);
- continue;
- }
- if (actual > pathlen)
- actual = pathlen;
- alias->path[actual] = '\0';
- alias->path[pathlen] = '\0';
- if (grub_ieee1275_finddevice (alias->path, &alias->phandle))
- {
- grub_dprintf ("devalias", "finddevice (%s) failed\n", alias->path);
- grub_free (alias->path);
- alias->path = 0;
- continue;
- }
- if (grub_ieee1275_get_property (alias->phandle, "device_type", alias->type,
- IEEE1275_MAX_PROP_LEN, &actual))
- {
- /* NAND device don't have device_type property. */
- alias->type[0] = 0;
- }
- return 1;
- }
- }
- /* Call the "map" method of /chosen/mmu. */
- int
- grub_ieee1275_map (grub_addr_t phys, grub_addr_t virt, grub_size_t size,
- grub_uint32_t mode)
- {
- struct map_args {
- struct grub_ieee1275_common_hdr common;
- grub_ieee1275_cell_t method;
- grub_ieee1275_cell_t ihandle;
- grub_ieee1275_cell_t mode;
- grub_ieee1275_cell_t size;
- grub_ieee1275_cell_t virt;
- #ifdef __sparc__
- grub_ieee1275_cell_t phys_high;
- #endif
- grub_ieee1275_cell_t phys_low;
- grub_ieee1275_cell_t catch_result;
- } args;
- INIT_IEEE1275_COMMON (&args.common, "call-method",
- #ifdef __sparc__
- 7,
- #else
- 6,
- #endif
- 1);
- args.method = (grub_ieee1275_cell_t) "map";
- args.ihandle = grub_ieee1275_mmu;
- #ifdef __sparc__
- args.phys_high = 0;
- #endif
- args.phys_low = phys;
- args.virt = virt;
- args.size = size;
- args.mode = mode; /* Format is WIMG0PP. */
- args.catch_result = (grub_ieee1275_cell_t) -1;
- if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
- return -1;
- return args.catch_result;
- }
- grub_err_t
- grub_claimmap (grub_addr_t addr, grub_size_t size)
- {
- if (grub_ieee1275_claim (addr, size, 0, 0))
- return -1;
- if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_REAL_MODE)
- && grub_ieee1275_map (addr, addr, size, 0x00))
- {
- grub_error (GRUB_ERR_OUT_OF_MEMORY, "map failed: address 0x%llx, size 0x%llx\n",
- (long long) addr, (long long) size);
- grub_ieee1275_release (addr, size);
- return grub_errno;
- }
- return GRUB_ERR_NONE;
- }
- /* Get the device arguments of the Open Firmware node name `path'. */
- static char *
- grub_ieee1275_get_devargs (const char *path)
- {
- char *colon = grub_strchr (path, ':');
- if (! colon)
- return 0;
- return grub_strdup (colon + 1);
- }
- /* Get the device path of the Open Firmware node name `path'. */
- char *
- grub_ieee1275_get_devname (const char *path)
- {
- char *colon = grub_strchr (path, ':');
- int pathlen = grub_strlen (path);
- struct grub_ieee1275_devalias curalias;
- if (colon)
- pathlen = (int)(colon - path);
- /* Try to find an alias for this device. */
- FOR_IEEE1275_DEVALIASES (curalias)
- /* briQ firmware can change capitalization in /chosen/bootpath. */
- if (grub_strncasecmp (curalias.path, path, pathlen) == 0
- && curalias.path[pathlen] == 0)
- {
- char *newpath;
- newpath = grub_strdup (curalias.name);
- grub_ieee1275_devalias_free (&curalias);
- return newpath;
- }
- return grub_strndup (path, pathlen);
- }
- static char *
- grub_ieee1275_parse_args (const char *path, enum grub_ieee1275_parse_type ptype)
- {
- char type[64]; /* XXX check size. */
- char *device = grub_ieee1275_get_devname (path);
- char *ret = 0;
- grub_ieee1275_phandle_t dev;
- /* We need to know what type of device it is in order to parse the full
- file path properly. */
- if (grub_ieee1275_finddevice (device, &dev))
- {
- grub_error (GRUB_ERR_UNKNOWN_DEVICE, "device %s not found", device);
- goto fail;
- }
- if (grub_ieee1275_get_property (dev, "device_type", &type, sizeof type, 0))
- {
- grub_error (GRUB_ERR_UNKNOWN_DEVICE,
- "device %s lacks a device_type property", device);
- goto fail;
- }
- switch (ptype)
- {
- case GRUB_PARSE_DEVICE:
- ret = grub_strdup (device);
- break;
- case GRUB_PARSE_DEVICE_TYPE:
- ret = grub_strdup (type);
- break;
- case GRUB_PARSE_FILENAME:
- {
- char *comma;
- char *args;
- args = grub_ieee1275_get_devargs (path);
- if (!args)
- /* Shouldn't happen. */
- return 0;
- /* The syntax of the device arguments is defined in the CHRP and PReP
- IEEE1275 bindings: "[partition][,[filename]]". */
- comma = grub_strchr (args, ',');
- if (comma)
- {
- char *filepath = comma + 1;
- /* Make sure filepath has leading backslash. */
- if (filepath[0] != '\\')
- ret = grub_xasprintf ("\\%s", filepath);
- else
- ret = grub_strdup (filepath);
- }
- grub_free (args);
- }
- break;
- case GRUB_PARSE_PARTITION:
- {
- char *comma;
- char *args;
- if (grub_strcmp ("block", type) != 0)
- goto unknown;
- args = grub_ieee1275_get_devargs (path);
- if (!args)
- /* Shouldn't happen. */
- return 0;
- comma = grub_strchr (args, ',');
- if (!comma)
- ret = grub_strdup (args);
- else
- ret = grub_strndup (args, (grub_size_t)(comma - args));
- /* Consistently provide numbered partitions to GRUB.
- OpenBOOT traditionally uses alphabetical partition
- specifiers. */
- if (ret[0] >= 'a' && ret[0] <= 'z')
- ret[0] = '1' + (ret[0] - 'a');
- grub_free (args);
- }
- break;
- default:
- unknown:
- grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
- "unsupported type %s for device %s", type, device);
- }
- fail:
- grub_free (device);
- return ret;
- }
- char *
- grub_ieee1275_get_device_type (const char *path)
- {
- return grub_ieee1275_parse_args (path, GRUB_PARSE_DEVICE_TYPE);
- }
- char *
- grub_ieee1275_get_aliasdevname (const char *path)
- {
- return grub_ieee1275_parse_args (path, GRUB_PARSE_DEVICE);
- }
- char *
- grub_ieee1275_get_filename (const char *path)
- {
- return grub_ieee1275_parse_args (path, GRUB_PARSE_FILENAME);
- }
- /* Convert a device name from IEEE1275 syntax to GRUB syntax. */
- char *
- grub_ieee1275_encode_devname (const char *path)
- {
- char *device = grub_ieee1275_get_devname (path);
- char *partition;
- char *encoding;
- char *optr;
- const char *iptr;
- if (! device)
- return 0;
- encoding = grub_malloc (sizeof ("ieee1275/") + 2 * grub_strlen (device)
- + sizeof (",XXXXXXXXXXXX"));
- if (!encoding)
- {
- grub_free (device);
- return 0;
- }
- partition = grub_ieee1275_parse_args (path, GRUB_PARSE_PARTITION);
- optr = grub_stpcpy (encoding, "ieee1275/");
- for (iptr = device; *iptr; )
- {
- if (*iptr == ',')
- *optr++ ='\\';
- *optr++ = *iptr++;
- }
- if (partition && partition[0])
- {
- unsigned int partno = grub_strtoul (partition, 0, 0);
- *optr++ = ',';
- if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_0_BASED_PARTITIONS))
- /* GRUB partition 1 is OF partition 0. */
- partno++;
- grub_snprintf (optr, sizeof ("XXXXXXXXXXXX"), "%d", partno);
- }
- else
- *optr = '\0';
- grub_free (partition);
- grub_free (device);
- return encoding;
- }
- /* Resolve aliases. */
- char *
- grub_ieee1275_canonicalise_devname (const char *path)
- {
- struct canon_args
- {
- struct grub_ieee1275_common_hdr common;
- grub_ieee1275_cell_t path;
- grub_ieee1275_cell_t buf;
- grub_ieee1275_cell_t inlen;
- grub_ieee1275_cell_t outlen;
- }
- args;
- char *buf = NULL;
- grub_size_t bufsize = 64;
- int i;
- for (i = 0; i < 2; i++)
- {
- grub_free (buf);
- buf = grub_malloc (bufsize);
- if (!buf)
- return NULL;
- INIT_IEEE1275_COMMON (&args.common, "canon", 3, 1);
- args.path = (grub_ieee1275_cell_t) path;
- args.buf = (grub_ieee1275_cell_t) buf;
- args.inlen = (grub_ieee1275_cell_t) (bufsize - 1);
- if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
- return 0;
- if (args.outlen > bufsize - 1)
- {
- bufsize = args.outlen + 2;
- continue;
- }
- return buf;
- }
- /* Shouldn't reach here. */
- grub_free (buf);
- return NULL;
- }
- char *
- grub_ieee1275_get_boot_dev (void)
- {
- char *bootpath;
- grub_ssize_t bootpath_size;
- if (grub_ieee1275_get_property_length (grub_ieee1275_chosen, "bootpath",
- &bootpath_size)
- || bootpath_size <= 0)
- {
- /* Should never happen. */
- grub_printf ("/chosen/bootpath property missing!\n");
- return NULL;
- }
- bootpath = (char *) grub_malloc ((grub_size_t) bootpath_size + 64);
- if (! bootpath)
- {
- grub_print_error ();
- return NULL;
- }
- grub_ieee1275_get_property (grub_ieee1275_chosen, "bootpath", bootpath,
- (grub_size_t) bootpath_size + 1, 0);
- bootpath[bootpath_size] = '\0';
- return bootpath;
- }
|