From 6e7a315eb67cb6c113cf37e1d66c4f11a51a2b3e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 18:29:51 +0200 Subject: Adding upstream version 2.06. Signed-off-by: Daniel Baumann --- grub-core/kern/ieee1275/openfw.c | 593 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 593 insertions(+) create mode 100644 grub-core/kern/ieee1275/openfw.c (limited to 'grub-core/kern/ieee1275/openfw.c') diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c new file mode 100644 index 0000000..4d493ab --- /dev/null +++ b/grub-core/kern/ieee1275/openfw.c @@ -0,0 +1,593 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include +#include + +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; +} -- cgit v1.2.3