diff options
Diffstat (limited to 'grub-core/kern/ieee1275')
-rw-r--r-- | grub-core/kern/ieee1275/cmain.c | 220 | ||||
-rw-r--r-- | grub-core/kern/ieee1275/ieee1275.c | 809 | ||||
-rw-r--r-- | grub-core/kern/ieee1275/init.c | 322 | ||||
-rw-r--r-- | grub-core/kern/ieee1275/mmap.c | 83 | ||||
-rw-r--r-- | grub-core/kern/ieee1275/openfw.c | 593 |
5 files changed, 2027 insertions, 0 deletions
diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c new file mode 100644 index 0000000..20cbbd7 --- /dev/null +++ b/grub-core/kern/ieee1275/cmain.c @@ -0,0 +1,220 @@ +/* cmain.c - Startup code for the PowerPC. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2004,2005,2006,2007,2008 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/kernel.h> +#include <grub/misc.h> +#include <grub/types.h> +#include <grub/ieee1275/ieee1275.h> + +int (*grub_ieee1275_entry_fn) (void *) GRUB_IEEE1275_ENTRY_FN_ATTRIBUTE; + +grub_ieee1275_phandle_t grub_ieee1275_chosen; +grub_ieee1275_ihandle_t grub_ieee1275_mmu; + +static grub_uint32_t grub_ieee1275_flags; + + + +int +grub_ieee1275_test_flag (enum grub_ieee1275_flag flag) +{ + return (grub_ieee1275_flags & (1 << flag)); +} + +void +grub_ieee1275_set_flag (enum grub_ieee1275_flag flag) +{ + grub_ieee1275_flags |= (1 << flag); +} + +static void +grub_ieee1275_find_options (void) +{ + grub_ieee1275_phandle_t root; + grub_ieee1275_phandle_t options; + grub_ieee1275_phandle_t openprom; + grub_ieee1275_phandle_t bootrom; + int rc; + grub_uint32_t realmode = 0; + char tmp[256]; + int is_smartfirmware = 0; + int is_olpc = 0; + int is_qemu = 0; + grub_ssize_t actual; + +#ifdef __sparc__ + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_PARTITION_0); +#endif + + grub_ieee1275_finddevice ("/", &root); + grub_ieee1275_finddevice ("/options", &options); + grub_ieee1275_finddevice ("/openprom", &openprom); + + rc = grub_ieee1275_get_integer_property (options, "real-mode?", &realmode, + sizeof realmode, 0); + if (((rc >= 0) && realmode) || (grub_ieee1275_mmu == 0)) + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_REAL_MODE); + + rc = grub_ieee1275_get_property (openprom, "CodeGen-copyright", + tmp, sizeof (tmp), 0); + if (rc >= 0 && !grub_strncmp (tmp, "SmartFirmware(tm)", + sizeof ("SmartFirmware(tm)") - 1)) + is_smartfirmware = 1; + + rc = grub_ieee1275_get_property (root, "architecture", + tmp, sizeof (tmp), 0); + if (rc >= 0 && !grub_strcmp (tmp, "OLPC")) + is_olpc = 1; + + rc = grub_ieee1275_get_property (root, "model", + tmp, sizeof (tmp), 0); + if (rc >= 0 && (!grub_strcmp (tmp, "Emulated PC") + || !grub_strcmp (tmp, "IBM pSeries (emulated by qemu)"))) { + is_qemu = 1; + } + + if (rc >= 0 && grub_strncmp (tmp, "IBM", 3) == 0) + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_TREE_SCANNING_FOR_DISKS); + + /* Old Macs have no key repeat, newer ones have fully working one. + The ones inbetween when repeated key generates an escaoe sequence + only the escape is repeated. With this workaround however a fast + e.g. down arrow-ESC is perceived as down arrow-down arrow which is + also annoying but is less so than the original bug of exiting from + the current window on arrow repeat. To avoid unaffected users suffering + from this workaround match only exact models known to have this bug. + */ + if (rc >= 0 && grub_strcmp (tmp, "PowerBook3,3") == 0) + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_BROKEN_REPEAT); + + rc = grub_ieee1275_get_property (root, "compatible", + tmp, sizeof (tmp), &actual); + if (rc >= 0) + { + char *ptr; + + if (grub_strncmp (tmp, "sun4v", 5) == 0) + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_RAW_DEVNAMES); + for (ptr = tmp; ptr - tmp < actual; ptr += grub_strlen (ptr) + 1) + { + if (grub_memcmp (ptr, "MacRISC", sizeof ("MacRISC") - 1) == 0 + && (ptr[sizeof ("MacRISC") - 1] == 0 + || grub_isdigit (ptr[sizeof ("MacRISC") - 1]))) + { + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_BROKEN_ADDRESS_CELLS); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_OFNET_SUFFIX); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_VIRT_TO_REAL_BROKEN); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CURSORONOFF_ANSI_BROKEN); + break; + } + } + } + + if (is_smartfirmware) + { + /* Broken in all versions */ + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_BROKEN_OUTPUT); + + /* There are two incompatible ways of checking the version number. Try + both. */ + rc = grub_ieee1275_get_property (openprom, "SmartFirmware-version", + tmp, sizeof (tmp), 0); + if (rc < 0) + rc = grub_ieee1275_get_property (openprom, "firmware-version", + tmp, sizeof (tmp), 0); + if (rc >= 0) + { + /* It is tempting to implement a version parser to set the flags for + e.g. 1.3 and below. However, there's a special situation here. + 3rd party updates which fix the partition bugs are common, and for + some reason their fixes aren't being merged into trunk. So for + example we know that 1.2 and 1.3 are broken, but there's 1.2.99 + and 1.3.99 which are known good (and applying this workaround + would cause breakage). */ + if (!grub_strcmp (tmp, "1.0") + || !grub_strcmp (tmp, "1.1") + || !grub_strcmp (tmp, "1.2") + || !grub_strcmp (tmp, "1.3")) + { + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_PARTITION_0); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_0_BASED_PARTITIONS); + } + } + } + + if (is_olpc) + { + /* OLPC / XO laptops have three kinds of storage devices: + + - NAND flash. These are accessible via OFW callbacks, but: + - Follow strange semantics, imposed by hardware constraints. + - Its ABI is undocumented, and not stable. + They lack "device_type" property, which conveniently makes GRUB + skip them. + + - USB drives. Not accessible, because OFW shuts down the controller + in order to prevent collisions with applications accessing it + directly. Even worse, attempts to access it will NOT return + control to the caller, so we have to avoid probing them. + + - SD cards. These work fine. + + To avoid breakage, we only need to skip USB probing. However, + since detecting SD cards is more reliable, we do that instead. + */ + + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_OFDISK_SDCARD_ONLY); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_HAS_CURSORONOFF); + } + + if (is_qemu) + { + /* OpenFirmware hangs on qemu if one requests any memory below 1.5 MiB. */ + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM); + + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_HAS_CURSORONOFF); + } + + if (! grub_ieee1275_finddevice ("/rom/boot-rom", &bootrom) + || ! grub_ieee1275_finddevice ("/boot-rom", &bootrom)) + { + rc = grub_ieee1275_get_property (bootrom, "model", tmp, sizeof (tmp), 0); + if (rc >= 0 && !grub_strncmp (tmp, "PPC Open Hack'Ware", + sizeof ("PPC Open Hack'Ware") - 1)) + { + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_BROKEN_OUTPUT); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CANNOT_SET_COLORS); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CANNOT_INTERPRET); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_ANSI); + } + } +} + +void +grub_ieee1275_init (void) +{ + grub_ieee1275_finddevice ("/chosen", &grub_ieee1275_chosen); + + if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen, "mmu", &grub_ieee1275_mmu, + sizeof grub_ieee1275_mmu, 0) < 0) + grub_ieee1275_mmu = 0; + + grub_ieee1275_find_options (); +} diff --git a/grub-core/kern/ieee1275/ieee1275.c b/grub-core/kern/ieee1275/ieee1275.c new file mode 100644 index 0000000..86f81a3 --- /dev/null +++ b/grub-core/kern/ieee1275/ieee1275.c @@ -0,0 +1,809 @@ +/* of.c - Access the Open Firmware client interface. */ +/* + * 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/ieee1275/ieee1275.h> +#include <grub/types.h> +#include <grub/misc.h> + +#define IEEE1275_PHANDLE_INVALID ((grub_ieee1275_cell_t) -1) +#define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_cell_t) 0) +#define IEEE1275_CELL_INVALID ((grub_ieee1275_cell_t) -1) + + + +int +grub_ieee1275_finddevice (const char *name, grub_ieee1275_phandle_t *phandlep) +{ + struct find_device_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t device; + grub_ieee1275_cell_t phandle; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "finddevice", 1, 1); + args.device = (grub_ieee1275_cell_t) name; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + *phandlep = args.phandle; + if (args.phandle == IEEE1275_PHANDLE_INVALID) + return -1; + return 0; +} + +int +grub_ieee1275_get_property (grub_ieee1275_phandle_t phandle, + const char *property, void *buf, + grub_size_t size, grub_ssize_t *actual) +{ + struct get_property_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t phandle; + grub_ieee1275_cell_t prop; + grub_ieee1275_cell_t buf; + grub_ieee1275_cell_t buflen; + grub_ieee1275_cell_t size; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "getprop", 4, 1); + args.phandle = phandle; + args.prop = (grub_ieee1275_cell_t) property; + args.buf = (grub_ieee1275_cell_t) buf; + args.buflen = (grub_ieee1275_cell_t) size; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + if (actual) + *actual = (grub_ssize_t) args.size; + if (args.size == IEEE1275_CELL_INVALID) + return -1; + return 0; +} + +int +grub_ieee1275_get_integer_property (grub_ieee1275_phandle_t phandle, + const char *property, grub_uint32_t *buf, + grub_size_t size, grub_ssize_t *actual) +{ + int ret; + ret = grub_ieee1275_get_property (phandle, property, (void *) buf, size, actual); +#ifndef GRUB_CPU_WORDS_BIGENDIAN + /* Integer properties are always in big endian. */ + if (ret == 0) + { + unsigned int i; + size /= sizeof (grub_uint32_t); + for (i = 0; i < size; i++) + buf[i] = grub_be_to_cpu32 (buf[i]); + } +#endif + return ret; +} + +int +grub_ieee1275_next_property (grub_ieee1275_phandle_t phandle, char *prev_prop, + char *prop) +{ + struct get_property_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t phandle; + grub_ieee1275_cell_t prev_prop; + grub_ieee1275_cell_t next_prop; + grub_ieee1275_cell_t flags; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "nextprop", 3, 1); + args.phandle = phandle; + args.prev_prop = (grub_ieee1275_cell_t) prev_prop; + args.next_prop = (grub_ieee1275_cell_t) prop; + args.flags = (grub_ieee1275_cell_t) -1; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + return (int) args.flags; +} + +int +grub_ieee1275_get_property_length (grub_ieee1275_phandle_t phandle, + const char *prop, grub_ssize_t *length) +{ + struct get_property_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t phandle; + grub_ieee1275_cell_t prop; + grub_ieee1275_cell_t length; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "getproplen", 2, 1); + args.phandle = phandle; + args.prop = (grub_ieee1275_cell_t) prop; + args.length = (grub_ieee1275_cell_t) -1; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + *length = args.length; + if (args.length == IEEE1275_CELL_INVALID) + return -1; + return 0; +} + +int +grub_ieee1275_instance_to_package (grub_ieee1275_ihandle_t ihandle, + grub_ieee1275_phandle_t *phandlep) +{ + struct instance_to_package_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t phandle; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "instance-to-package", 1, 1); + args.ihandle = ihandle; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + *phandlep = args.phandle; + if (args.phandle == IEEE1275_PHANDLE_INVALID) + return -1; + return 0; +} + +int +grub_ieee1275_package_to_path (grub_ieee1275_phandle_t phandle, + char *path, grub_size_t len, + grub_ssize_t *actual) +{ + struct instance_to_package_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t phandle; + grub_ieee1275_cell_t buf; + grub_ieee1275_cell_t buflen; + grub_ieee1275_cell_t actual; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "package-to-path", 3, 1); + args.phandle = phandle; + args.buf = (grub_ieee1275_cell_t) path; + args.buflen = (grub_ieee1275_cell_t) len; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + if (actual) + *actual = args.actual; + if (args.actual == IEEE1275_CELL_INVALID) + return -1; + return 0; +} + +int +grub_ieee1275_instance_to_path (grub_ieee1275_ihandle_t ihandle, + char *path, grub_size_t len, + grub_ssize_t *actual) +{ + struct instance_to_path_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t buf; + grub_ieee1275_cell_t buflen; + grub_ieee1275_cell_t actual; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "instance-to-path", 3, 1); + args.ihandle = ihandle; + args.buf = (grub_ieee1275_cell_t) path; + args.buflen = (grub_ieee1275_cell_t) len; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + if (actual) + *actual = args.actual; + if (args.actual == IEEE1275_CELL_INVALID) + return -1; + return 0; +} + +int +grub_ieee1275_write (grub_ieee1275_ihandle_t ihandle, const void *buffer, + grub_size_t len, grub_ssize_t *actualp) +{ + struct write_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t buf; + grub_ieee1275_cell_t len; + grub_ieee1275_cell_t actual; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "write", 3, 1); + args.ihandle = ihandle; + args.buf = (grub_ieee1275_cell_t) buffer; + args.len = (grub_ieee1275_cell_t) len; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + if (actualp) + *actualp = args.actual; + return 0; +} + +int +grub_ieee1275_read (grub_ieee1275_ihandle_t ihandle, void *buffer, + grub_size_t len, grub_ssize_t *actualp) +{ + struct write_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t buf; + grub_ieee1275_cell_t len; + grub_ieee1275_cell_t actual; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "read", 3, 1); + args.ihandle = ihandle; + args.buf = (grub_ieee1275_cell_t) buffer; + args.len = (grub_ieee1275_cell_t) len; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + if (actualp) + *actualp = args.actual; + return 0; +} + +int +grub_ieee1275_seek (grub_ieee1275_ihandle_t ihandle, grub_disk_addr_t pos, + grub_ssize_t *result) +{ + struct write_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t pos_hi; + grub_ieee1275_cell_t pos_lo; + grub_ieee1275_cell_t result; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "seek", 3, 1); + args.ihandle = ihandle; + /* To prevent stupid gcc warning. */ +#if GRUB_IEEE1275_CELL_SIZEOF >= 8 + args.pos_hi = 0; + args.pos_lo = pos; +#else + args.pos_hi = (grub_ieee1275_cell_t) (pos >> (8 * GRUB_IEEE1275_CELL_SIZEOF)); + args.pos_lo = (grub_ieee1275_cell_t) + (pos & ((1ULL << (8 * GRUB_IEEE1275_CELL_SIZEOF)) - 1)); +#endif + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + if (result) + *result = args.result; + return 0; +} + +int +grub_ieee1275_peer (grub_ieee1275_phandle_t node, + grub_ieee1275_phandle_t *result) +{ + struct peer_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t node; + grub_ieee1275_cell_t result; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "peer", 1, 1); + args.node = node; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + *result = args.result; + if (args.result == 0) + return -1; + return 0; +} + +int +grub_ieee1275_child (grub_ieee1275_phandle_t node, + grub_ieee1275_phandle_t *result) +{ + struct child_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t node; + grub_ieee1275_cell_t result; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "child", 1, 1); + args.node = node; + args.result = IEEE1275_PHANDLE_INVALID; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + *result = args.result; + if (args.result == 0) + return -1; + return 0; +} + +int +grub_ieee1275_parent (grub_ieee1275_phandle_t node, + grub_ieee1275_phandle_t *result) +{ + struct parent_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t node; + grub_ieee1275_cell_t result; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "parent", 1, 1); + args.node = node; + args.result = IEEE1275_PHANDLE_INVALID; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + *result = args.result; + return 0; +} + +int +grub_ieee1275_interpret (const char *command, grub_ieee1275_cell_t *catch) +{ + struct enter_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t command; + grub_ieee1275_cell_t catch; + } + args; + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CANNOT_INTERPRET)) + return -1; + + INIT_IEEE1275_COMMON (&args.common, "interpret", 1, 1); + args.command = (grub_ieee1275_cell_t) command; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + if (catch) + *catch = args.catch; + return 0; +} + +int +grub_ieee1275_enter (void) +{ + struct enter_args + { + struct grub_ieee1275_common_hdr common; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "enter", 0, 0); + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + return 0; +} + +void +grub_ieee1275_exit (void) +{ + struct exit_args + { + struct grub_ieee1275_common_hdr common; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "exit", 0, 0); + + IEEE1275_CALL_ENTRY_FN (&args); + for (;;) ; +} + +int +grub_ieee1275_open (const char *path, grub_ieee1275_ihandle_t *result) +{ + struct open_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t path; + grub_ieee1275_cell_t result; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "open", 1, 1); + args.path = (grub_ieee1275_cell_t) path; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + *result = args.result; + if (args.result == IEEE1275_IHANDLE_INVALID) + return -1; + return 0; +} + +int +grub_ieee1275_close (grub_ieee1275_ihandle_t ihandle) +{ + struct close_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t ihandle; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "close", 1, 0); + args.ihandle = ihandle; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + return 0; +} + +int +grub_ieee1275_decode_unit4 (grub_ieee1275_ihandle_t ihandle, + void *addr, grub_size_t size, + grub_uint32_t *phy_lo, grub_uint32_t *phy_hi, + grub_uint32_t *lun_lo, grub_uint32_t *lun_hi) +{ + struct decode_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t size; + grub_ieee1275_cell_t addr; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t tgt_h; + grub_ieee1275_cell_t tgt_l; + grub_ieee1275_cell_t lun_h; + grub_ieee1275_cell_t lun_l; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 4, 5); + args.method = (grub_ieee1275_cell_t) "decode-unit"; + args.ihandle = ihandle; + args.size = size; + args.addr = (grub_ieee1275_cell_t) addr; + args.catch_result = 1; + + if ((IEEE1275_CALL_ENTRY_FN (&args) == -1) || (args.catch_result)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "decode-unit failed\n"); + return -1; + } + + *phy_lo = args.tgt_l; + *phy_hi = args.tgt_h; + *lun_lo = args.lun_l; + *lun_hi = args.lun_h; + return 0; +} + +char * +grub_ieee1275_encode_uint4 (grub_ieee1275_ihandle_t ihandle, + grub_uint32_t phy_lo, grub_uint32_t phy_hi, + grub_uint32_t lun_lo, grub_uint32_t lun_hi, + grub_size_t *size) +{ + char *addr; + struct encode_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t tgt_h; + grub_ieee1275_cell_t tgt_l; + grub_ieee1275_cell_t lun_h; + grub_ieee1275_cell_t lun_l; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t size; + grub_ieee1275_cell_t addr; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 6, 3); + args.method = (grub_ieee1275_cell_t) "encode-unit"; + args.ihandle = ihandle; + + args.tgt_l = phy_lo; + args.tgt_h = phy_hi; + args.lun_l = lun_lo; + args.lun_h = lun_hi; + args.catch_result = 1; + + if ((IEEE1275_CALL_ENTRY_FN (&args) == -1) || (args.catch_result)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "encode-unit failed\n"); + return 0; + } + + addr = (void *)args.addr; + *size = args.size; + addr = grub_strdup ((char *)args.addr); + return addr; +} + +int +grub_ieee1275_claim (grub_addr_t addr, grub_size_t size, unsigned int align, + grub_addr_t *result) +{ + struct claim_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t addr; + grub_ieee1275_cell_t size; + grub_ieee1275_cell_t align; + grub_ieee1275_cell_t base; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "claim", 3, 1); + args.addr = (grub_ieee1275_cell_t) addr; + args.size = (grub_ieee1275_cell_t) size; + args.align = (grub_ieee1275_cell_t) align; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + if (result) + *result = args.base; + if (args.base == IEEE1275_CELL_INVALID) + return -1; + return 0; +} + +int +grub_ieee1275_release (grub_addr_t addr, grub_size_t size) +{ + struct release_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t addr; + grub_ieee1275_cell_t size; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "release", 2, 0); + args.addr = addr; + args.size = size; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + return 0; +} + +int +grub_ieee1275_set_property (grub_ieee1275_phandle_t phandle, + const char *propname, const void *buf, + grub_size_t size, grub_ssize_t *actual) +{ + struct set_property_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t phandle; + grub_ieee1275_cell_t propname; + grub_ieee1275_cell_t buf; + grub_ieee1275_cell_t size; + grub_ieee1275_cell_t actual; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "setprop", 4, 1); + args.size = (grub_ieee1275_cell_t) size; + args.buf = (grub_ieee1275_cell_t) buf; + args.propname = (grub_ieee1275_cell_t) propname; + args.phandle = (grub_ieee1275_cell_t) phandle; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + *actual = args.actual; + if ((args.actual == IEEE1275_CELL_INVALID) || (args.actual != args.size)) + return -1; + return 0; +} + +int +grub_ieee1275_set_color (grub_ieee1275_ihandle_t ihandle, + int index, int r, int g, int b) +{ + struct set_color_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t index; + grub_ieee1275_cell_t b; + grub_ieee1275_cell_t g; + grub_ieee1275_cell_t r; + grub_ieee1275_cell_t catch_result; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 6, 1); + args.method = (grub_ieee1275_cell_t) "color!"; + args.ihandle = ihandle; + args.index = index; + args.r = r; + args.g = g; + args.b = b; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + return args.catch_result; +} + +int +grub_ieee1275_milliseconds (grub_uint32_t *msecs) +{ + struct milliseconds_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t msecs; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "milliseconds", 0, 1); + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + *msecs = args.msecs; + return 0; +} + +int +grub_ieee1275_set_address (grub_ieee1275_ihandle_t ihandle, + grub_uint32_t target, grub_uint32_t lun) +{ + struct set_address + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t tgt; + grub_ieee1275_cell_t lun; + grub_ieee1275_cell_t catch_result; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 4, 1); + + /* + * IEEE 1275-1994 Standard for Boot (Initialization Configuration) + * Firmware: Core Requirements and Practices + * E.3.2.2 Bus-specific methods for bus nodes + * + * A package implementing the scsi-2 device type shall implement the + * following bus-specific method: + * + * set-address ( unit# target# -- ) + * Sets the SCSI target number (0x0..0xf) and unit number (0..7) to which + * subsequent commands apply. + */ + args.method = (grub_ieee1275_cell_t) "set-address"; + args.ihandle = ihandle; + args.tgt = target; + args.lun = lun; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + return args.catch_result; +} + +int +grub_ieee1275_no_data_command (grub_ieee1275_ihandle_t ihandle, + const void *cmd_addr, grub_ssize_t *result) +{ + struct set_address + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t cmd_addr; + grub_ieee1275_cell_t error; + grub_ieee1275_cell_t catch_result; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 3, 2); + + /* + * IEEE 1275-1994 Standard for Boot (Initialization Configuration) + * Firmware: Core Requirements and Practices + * + * E.3.2.2 Bus-specific methods for bus nodes + * + * A package implementing the scsi-2 device type shall implement the + * following bus-specific method: + * + * no-data-command ( cmd-addr -- error? ) + * Executes a simple SCSI command, automatically retrying under + * certain conditions. cmd-addr is the address of a 6-byte command buffer + * containing an SCSI command that does not have a data transfer phase. + * Executes the command, retrying indefinitely with the same retry criteria + * as retry-command. + * + * error? is nonzero if an error occurred, zero otherwise. + * NOTE no-data-command is a convenience function. It provides + * no capabilities that are not present in retry-command, but for + * those commands that meet its restrictions, it is easier to use. + */ + args.method = (grub_ieee1275_cell_t) "no-data-command"; + args.ihandle = ihandle; + args.cmd_addr = (grub_ieee1275_cell_t) cmd_addr; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + if (result) + *result = args.error; + + return args.catch_result; +} + +int +grub_ieee1275_get_block_size (grub_ieee1275_ihandle_t ihandle) +{ + struct size_args_ieee1275 + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t result; + grub_ieee1275_cell_t size; + } args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 2, 2); + args.method = (grub_ieee1275_cell_t) "block-size"; + args.ihandle = ihandle; + args.result = 1; + + if ((IEEE1275_CALL_ENTRY_FN (&args) == -1) || (args.result)) + return 0; + + return args.size; +} diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c new file mode 100644 index 0000000..d483e35 --- /dev/null +++ b/grub-core/kern/ieee1275/init.c @@ -0,0 +1,322 @@ +/* init.c -- Initialize GRUB on the newworld mac (PPC). */ +/* + * 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/kernel.h> +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/partition.h> +#include <grub/normal.h> +#include <grub/fs.h> +#include <grub/setjmp.h> +#include <grub/env.h> +#include <grub/misc.h> +#include <grub/time.h> +#include <grub/ieee1275/console.h> +#include <grub/ieee1275/ofdisk.h> +#ifdef __sparc__ +#include <grub/ieee1275/obdisk.h> +#endif +#include <grub/ieee1275/ieee1275.h> +#include <grub/net.h> +#include <grub/offsets.h> +#include <grub/memory.h> +#include <grub/loader.h> +#ifdef __i386__ +#include <grub/cpu/tsc.h> +#endif +#ifdef __sparc__ +#include <grub/machine/kernel.h> +#endif + +/* The minimal heap size we can live with. */ +#define HEAP_MIN_SIZE (unsigned long) (2 * 1024 * 1024) + +/* The maximum heap size we're going to claim */ +#ifdef __i386__ +#define HEAP_MAX_SIZE (unsigned long) (64 * 1024 * 1024) +#else +#define HEAP_MAX_SIZE (unsigned long) (32 * 1024 * 1024) +#endif + +/* If possible, we will avoid claiming heap above this address, because it + seems to cause relocation problems with OSes that link at 4 MiB */ +#ifdef __i386__ +#define HEAP_MAX_ADDR (unsigned long) (64 * 1024 * 1024) +#else +#define HEAP_MAX_ADDR (unsigned long) (32 * 1024 * 1024) +#endif + +extern char _start[]; +extern char _end[]; + +#ifdef __sparc__ +grub_addr_t grub_ieee1275_original_stack; +#endif + +void +grub_exit (void) +{ + grub_ieee1275_exit (); +} + +/* Translate an OF filesystem path (separated by backslashes), into a GRUB + path (separated by forward slashes). */ +static void +grub_translate_ieee1275_path (char *filepath) +{ + char *backslash; + + backslash = grub_strchr (filepath, '\\'); + while (backslash != 0) + { + *backslash = '/'; + backslash = grub_strchr (filepath, '\\'); + } +} + +void (*grub_ieee1275_net_config) (const char *dev, char **device, char **path, + char *bootpath); +void +grub_machine_get_bootlocation (char **device, char **path) +{ + char *bootpath; + char *filename; + char *type; + + bootpath = grub_ieee1275_get_boot_dev (); + if (! bootpath) + return; + + /* Transform an OF device path to a GRUB path. */ + + type = grub_ieee1275_get_device_type (bootpath); + if (type && grub_strcmp (type, "network") == 0) + { + char *dev, *canon; + char *ptr; + dev = grub_ieee1275_get_aliasdevname (bootpath); + canon = grub_ieee1275_canonicalise_devname (dev); + if (! canon) + return; + ptr = canon + grub_strlen (canon) - 1; + while (ptr > canon && (*ptr == ',' || *ptr == ':')) + ptr--; + ptr++; + *ptr = 0; + + if (grub_ieee1275_net_config) + grub_ieee1275_net_config (canon, device, path, bootpath); + grub_free (dev); + grub_free (canon); + } + else + *device = grub_ieee1275_encode_devname (bootpath); + grub_free (type); + + filename = grub_ieee1275_get_filename (bootpath); + if (filename) + { + char *lastslash = grub_strrchr (filename, '\\'); + + /* Truncate at last directory. */ + if (lastslash) + { + *lastslash = '\0'; + grub_translate_ieee1275_path (filename); + + *path = filename; + } + } + grub_free (bootpath); +} + +/* Claim some available memory in the first /memory node. */ +#ifdef __sparc__ +static void +grub_claim_heap (void) +{ + grub_mm_init_region ((void *) (grub_modules_get_end () + + GRUB_KERNEL_MACHINE_STACK_SIZE), 0x200000); +} +#else +/* Helper for grub_claim_heap. */ +static int +heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, + void *data) +{ + unsigned long *total = data; + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM)) + { + if (addr + len <= 0x180000) + return 0; + + if (addr < 0x180000) + { + len = addr + len - 0x180000; + addr = 0x180000; + } + } + len -= 1; /* Required for some firmware. */ + + /* Never exceed HEAP_MAX_SIZE */ + if (*total + len > HEAP_MAX_SIZE) + len = HEAP_MAX_SIZE - *total; + + /* Avoid claiming anything above HEAP_MAX_ADDR, if possible. */ + if ((addr < HEAP_MAX_ADDR) && /* if it's too late, don't bother */ + (addr + len > HEAP_MAX_ADDR) && /* if it wasn't available anyway, don't bother */ + (*total + (HEAP_MAX_ADDR - addr) > HEAP_MIN_SIZE)) /* only limit ourselves when we can afford to */ + len = HEAP_MAX_ADDR - addr; + + /* In theory, firmware should already prevent this from happening by not + listing our own image in /memory/available. The check below is intended + as a safeguard in case that doesn't happen. However, it doesn't protect + us from corrupting our module area, which extends up to a + yet-undetermined region above _end. */ + if ((addr < (grub_addr_t) _end) && ((addr + len) > (grub_addr_t) _start)) + { + grub_printf ("Warning: attempt to claim over our own code!\n"); + len = 0; + } + + if (len) + { + grub_err_t err; + /* Claim and use it. */ + err = grub_claimmap (addr, len); + if (err) + return err; + grub_mm_init_region ((void *) (grub_addr_t) addr, len); + } + + *total += len; + if (*total >= HEAP_MAX_SIZE) + return 1; + + return 0; +} + +static void +grub_claim_heap (void) +{ + unsigned long total = 0; + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM)) + heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN, + 1, &total); + else + grub_machine_mmap_iterate (heap_init, &total); +} +#endif + +static void +grub_parse_cmdline (void) +{ + grub_ssize_t actual; + char args[256]; + + if (grub_ieee1275_get_property (grub_ieee1275_chosen, "bootargs", &args, + sizeof args, &actual) == 0 + && actual > 1) + { + int i = 0; + + while (i < actual) + { + char *command = &args[i]; + char *end; + char *val; + + end = grub_strchr (command, ';'); + if (end == 0) + i = actual; /* No more commands after this one. */ + else + { + *end = '\0'; + i += end - command + 1; + while (grub_isspace(args[i])) + i++; + } + + /* Process command. */ + val = grub_strchr (command, '='); + if (val) + { + *val = '\0'; + grub_env_set (command, val + 1); + } + } + } +} + +grub_addr_t grub_modbase; + +void +grub_machine_init (void) +{ + grub_modbase = ALIGN_UP((grub_addr_t) _end + + GRUB_KERNEL_MACHINE_MOD_GAP, + GRUB_KERNEL_MACHINE_MOD_ALIGN); + grub_ieee1275_init (); + + grub_console_init_early (); + grub_claim_heap (); + grub_console_init_lately (); +#ifdef __sparc__ + grub_obdisk_init (); +#else + grub_ofdisk_init (); +#endif + grub_parse_cmdline (); + +#ifdef __i386__ + grub_tsc_init (); +#else + grub_install_get_time_ms (grub_rtc_get_time_ms); +#endif +} + +void +grub_machine_fini (int flags) +{ + if (flags & GRUB_LOADER_FLAG_NORETURN) + { +#ifdef __sparc__ + grub_obdisk_fini (); +#else + grub_ofdisk_fini (); +#endif + grub_console_fini (); + } +} + +grub_uint64_t +grub_rtc_get_time_ms (void) +{ + grub_uint32_t msecs = 0; + + grub_ieee1275_milliseconds (&msecs); + + return msecs; +} diff --git a/grub-core/kern/ieee1275/mmap.c b/grub-core/kern/ieee1275/mmap.c new file mode 100644 index 0000000..bf325ea --- /dev/null +++ b/grub-core/kern/ieee1275/mmap.c @@ -0,0 +1,83 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2004,2005,2007,2008 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/memory.h> +#include <grub/ieee1275/ieee1275.h> +#include <grub/types.h> + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + grub_ieee1275_phandle_t root; + grub_ieee1275_phandle_t memory; + grub_uint32_t available[128]; + grub_ssize_t available_size; + grub_uint32_t address_cells = 1; + grub_uint32_t size_cells = 1; + int i; + + /* Determine the format of each entry in `available'. */ + grub_ieee1275_finddevice ("/", &root); + grub_ieee1275_get_integer_property (root, "#address-cells", &address_cells, + sizeof address_cells, 0); + grub_ieee1275_get_integer_property (root, "#size-cells", &size_cells, + sizeof size_cells, 0); + + if (size_cells > address_cells) + address_cells = size_cells; + + /* Load `/memory/available'. */ + if (grub_ieee1275_finddevice ("/memory", &memory)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "couldn't find /memory node"); + if (grub_ieee1275_get_integer_property (memory, "available", available, + sizeof available, &available_size)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "couldn't examine /memory/available property"); + if (available_size < 0 || (grub_size_t) available_size > sizeof (available)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "/memory response buffer exceeded"); + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_ADDRESS_CELLS)) + { + address_cells = 1; + size_cells = 1; + } + + /* Decode each entry and call `hook'. */ + i = 0; + available_size /= sizeof (grub_uint32_t); + while (i < available_size) + { + grub_uint64_t address; + grub_uint64_t size; + + address = available[i++]; + if (address_cells == 2) + address = (address << 32) | available[i++]; + + size = available[i++]; + if (size_cells == 2) + size = (size << 32) | available[i++]; + + if (hook (address, size, GRUB_MEMORY_AVAILABLE, hook_data)) + break; + } + + return grub_errno; +} 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 <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; +} |