Index: head/sys/dev/ofw/openfirm.c =================================================================== --- head/sys/dev/ofw/openfirm.c (revision 326309) +++ head/sys/dev/ofw/openfirm.c (revision 326310) @@ -1,807 +1,813 @@ /* $NetBSD: Locore.c,v 1.7 2000/08/20 07:04:59 tsubai Exp $ */ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (C) 2000 Benno Rice. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include "ofw_if.h" static void OF_putchar(int c, void *arg); MALLOC_DEFINE(M_OFWPROP, "openfirm", "Open Firmware properties"); static ihandle_t stdout; static ofw_def_t *ofw_def_impl = NULL; static ofw_t ofw_obj; static struct ofw_kobj ofw_kernel_obj; static struct kobj_ops ofw_kernel_kops; struct xrefinfo { phandle_t xref; phandle_t node; device_t dev; SLIST_ENTRY(xrefinfo) next_entry; }; static SLIST_HEAD(, xrefinfo) xreflist = SLIST_HEAD_INITIALIZER(xreflist); static struct mtx xreflist_lock; static boolean_t xref_init_done; #define FIND_BY_XREF 0 #define FIND_BY_NODE 1 #define FIND_BY_DEV 2 /* * xref-phandle-device lookup helper routines. * * As soon as we are able to use malloc(), walk the node tree and build a list * of info that cross-references node handles, xref handles, and device_t * instances. This list exists primarily to allow association of a device_t * with an xref handle, but it is also used to speed up translation between xref * and node handles. Before malloc() is available we have to recursively search * the node tree each time we want to translate between a node and xref handle. * Afterwards we can do the translations by searching this much shorter list. */ static void xrefinfo_create(phandle_t node) { struct xrefinfo * xi; phandle_t child, xref; /* * Recursively descend from parent, looking for nodes with a property * named either "phandle", "ibm,phandle", or "linux,phandle". For each * such node found create an entry in the xreflist. */ for (child = OF_child(node); child != 0; child = OF_peer(child)) { xrefinfo_create(child); if (OF_getencprop(child, "phandle", &xref, sizeof(xref)) == -1 && OF_getencprop(child, "ibm,phandle", &xref, sizeof(xref)) == -1 && OF_getencprop(child, "linux,phandle", &xref, sizeof(xref)) == -1) continue; xi = malloc(sizeof(*xi), M_OFWPROP, M_WAITOK | M_ZERO); xi->node = child; xi->xref = xref; SLIST_INSERT_HEAD(&xreflist, xi, next_entry); } } static void xrefinfo_init(void *unsed) { /* * There is no locking during this init because it runs much earlier * than any of the clients/consumers of the xref list data, but we do * initialize the mutex that will be used for access later. */ mtx_init(&xreflist_lock, "OF xreflist lock", NULL, MTX_DEF); xrefinfo_create(OF_peer(0)); xref_init_done = true; } SYSINIT(xrefinfo, SI_SUB_KMEM, SI_ORDER_ANY, xrefinfo_init, NULL); static struct xrefinfo * xrefinfo_find(uintptr_t key, int find_by) { struct xrefinfo *rv, *xi; rv = NULL; mtx_lock(&xreflist_lock); SLIST_FOREACH(xi, &xreflist, next_entry) { if ((find_by == FIND_BY_XREF && (phandle_t)key == xi->xref) || (find_by == FIND_BY_NODE && (phandle_t)key == xi->node) || (find_by == FIND_BY_DEV && key == (uintptr_t)xi->dev)) { rv = xi; break; } } mtx_unlock(&xreflist_lock); return (rv); } static struct xrefinfo * xrefinfo_add(phandle_t node, phandle_t xref, device_t dev) { struct xrefinfo *xi; xi = malloc(sizeof(*xi), M_OFWPROP, M_WAITOK); xi->node = node; xi->xref = xref; xi->dev = dev; mtx_lock(&xreflist_lock); SLIST_INSERT_HEAD(&xreflist, xi, next_entry); mtx_unlock(&xreflist_lock); return (xi); } /* * OFW install routines. Highest priority wins, equal priority also * overrides allowing last-set to win. */ SET_DECLARE(ofw_set, ofw_def_t); boolean_t OF_install(char *name, int prio) { ofw_def_t *ofwp, **ofwpp; static int curr_prio = 0; + /* Allow OF layer to be uninstalled */ + if (name == NULL) { + ofw_def_impl = NULL; + return (FALSE); + } + /* * Try and locate the OFW kobj corresponding to the name. */ SET_FOREACH(ofwpp, ofw_set) { ofwp = *ofwpp; if (ofwp->name && !strcmp(ofwp->name, name) && prio >= curr_prio) { curr_prio = prio; ofw_def_impl = ofwp; return (TRUE); } } return (FALSE); } /* Initializer */ int OF_init(void *cookie) { phandle_t chosen; int rv; if (ofw_def_impl == NULL) return (-1); ofw_obj = &ofw_kernel_obj; /* * Take care of compiling the selected class, and * then statically initialize the OFW object. */ kobj_class_compile_static(ofw_def_impl, &ofw_kernel_kops); kobj_init_static((kobj_t)ofw_obj, ofw_def_impl); rv = OFW_INIT(ofw_obj, cookie); if ((chosen = OF_finddevice("/chosen")) != -1) if (OF_getencprop(chosen, "stdout", &stdout, sizeof(stdout)) == -1) stdout = -1; return (rv); } static void OF_putchar(int c, void *arg __unused) { char cbuf; if (c == '\n') { cbuf = '\r'; OF_write(stdout, &cbuf, 1); } cbuf = c; OF_write(stdout, &cbuf, 1); } void OF_printf(const char *fmt, ...) { va_list va; va_start(va, fmt); (void)kvprintf(fmt, OF_putchar, NULL, 10, va); va_end(va); } /* * Generic functions */ /* Test to see if a service exists. */ int OF_test(const char *name) { if (ofw_def_impl == NULL) return (-1); return (OFW_TEST(ofw_obj, name)); } int OF_interpret(const char *cmd, int nreturns, ...) { va_list ap; cell_t slots[16]; int i = 0; int status; if (ofw_def_impl == NULL) return (-1); status = OFW_INTERPRET(ofw_obj, cmd, nreturns, slots); if (status == -1) return (status); va_start(ap, nreturns); while (i < nreturns) *va_arg(ap, cell_t *) = slots[i++]; va_end(ap); return (status); } /* * Device tree functions */ /* Return the next sibling of this node or 0. */ phandle_t OF_peer(phandle_t node) { if (ofw_def_impl == NULL) return (0); return (OFW_PEER(ofw_obj, node)); } /* Return the first child of this node or 0. */ phandle_t OF_child(phandle_t node) { if (ofw_def_impl == NULL) return (0); return (OFW_CHILD(ofw_obj, node)); } /* Return the parent of this node or 0. */ phandle_t OF_parent(phandle_t node) { if (ofw_def_impl == NULL) return (0); return (OFW_PARENT(ofw_obj, node)); } /* Return the package handle that corresponds to an instance handle. */ phandle_t OF_instance_to_package(ihandle_t instance) { if (ofw_def_impl == NULL) return (-1); return (OFW_INSTANCE_TO_PACKAGE(ofw_obj, instance)); } /* Get the length of a property of a package. */ ssize_t OF_getproplen(phandle_t package, const char *propname) { if (ofw_def_impl == NULL) return (-1); return (OFW_GETPROPLEN(ofw_obj, package, propname)); } /* Check existence of a property of a package. */ int OF_hasprop(phandle_t package, const char *propname) { return (OF_getproplen(package, propname) >= 0 ? 1 : 0); } /* Get the value of a property of a package. */ ssize_t OF_getprop(phandle_t package, const char *propname, void *buf, size_t buflen) { if (ofw_def_impl == NULL) return (-1); return (OFW_GETPROP(ofw_obj, package, propname, buf, buflen)); } ssize_t OF_getencprop(phandle_t node, const char *propname, pcell_t *buf, size_t len) { ssize_t retval; int i; KASSERT(len % 4 == 0, ("Need a multiple of 4 bytes")); retval = OF_getprop(node, propname, buf, len); if (retval <= 0) return (retval); for (i = 0; i < len/4; i++) buf[i] = be32toh(buf[i]); return (retval); } /* * Recursively search the node and its parent for the given property, working * downward from the node to the device tree root. Returns the value of the * first match. */ ssize_t OF_searchprop(phandle_t node, const char *propname, void *buf, size_t len) { ssize_t rv; for (; node != 0; node = OF_parent(node)) if ((rv = OF_getprop(node, propname, buf, len)) != -1) return (rv); return (-1); } ssize_t OF_searchencprop(phandle_t node, const char *propname, void *buf, size_t len) { ssize_t rv; for (; node != 0; node = OF_parent(node)) if ((rv = OF_getencprop(node, propname, buf, len)) != -1) return (rv); return (-1); } /* * Store the value of a property of a package into newly allocated memory * (using the M_OFWPROP malloc pool and M_WAITOK). elsz is the size of a * single element, the number of elements is return in number. */ ssize_t OF_getprop_alloc(phandle_t package, const char *propname, int elsz, void **buf) { int len; *buf = NULL; if ((len = OF_getproplen(package, propname)) == -1 || len % elsz != 0) return (-1); *buf = malloc(len, M_OFWPROP, M_WAITOK); if (OF_getprop(package, propname, *buf, len) == -1) { free(*buf, M_OFWPROP); *buf = NULL; return (-1); } return (len / elsz); } ssize_t OF_getencprop_alloc(phandle_t package, const char *name, int elsz, void **buf) { ssize_t retval; pcell_t *cell; int i; retval = OF_getprop_alloc(package, name, elsz, buf); if (retval == -1) return (-1); if (retval * elsz % 4 != 0) { free(*buf, M_OFWPROP); *buf = NULL; return (-1); } cell = *buf; for (i = 0; i < retval * elsz / 4; i++) cell[i] = be32toh(cell[i]); return (retval); } /* Free buffer allocated by OF_getencprop_alloc or OF_getprop_alloc */ void OF_prop_free(void *buf) { free(buf, M_OFWPROP); } /* Get the next property of a package. */ int OF_nextprop(phandle_t package, const char *previous, char *buf, size_t size) { if (ofw_def_impl == NULL) return (-1); return (OFW_NEXTPROP(ofw_obj, package, previous, buf, size)); } /* Set the value of a property of a package. */ int OF_setprop(phandle_t package, const char *propname, const void *buf, size_t len) { if (ofw_def_impl == NULL) return (-1); return (OFW_SETPROP(ofw_obj, package, propname, buf,len)); } /* Convert a device specifier to a fully qualified pathname. */ ssize_t OF_canon(const char *device, char *buf, size_t len) { if (ofw_def_impl == NULL) return (-1); return (OFW_CANON(ofw_obj, device, buf, len)); } /* Return a package handle for the specified device. */ phandle_t OF_finddevice(const char *device) { if (ofw_def_impl == NULL) return (-1); return (OFW_FINDDEVICE(ofw_obj, device)); } /* Return the fully qualified pathname corresponding to an instance. */ ssize_t OF_instance_to_path(ihandle_t instance, char *buf, size_t len) { if (ofw_def_impl == NULL) return (-1); return (OFW_INSTANCE_TO_PATH(ofw_obj, instance, buf, len)); } /* Return the fully qualified pathname corresponding to a package. */ ssize_t OF_package_to_path(phandle_t package, char *buf, size_t len) { if (ofw_def_impl == NULL) return (-1); return (OFW_PACKAGE_TO_PATH(ofw_obj, package, buf, len)); } /* Look up effective phandle (see FDT/PAPR spec) */ static phandle_t OF_child_xref_phandle(phandle_t parent, phandle_t xref) { phandle_t child, rxref; /* * Recursively descend from parent, looking for a node with a property * named either "phandle", "ibm,phandle", or "linux,phandle" that * matches the xref we are looking for. */ for (child = OF_child(parent); child != 0; child = OF_peer(child)) { rxref = OF_child_xref_phandle(child, xref); if (rxref != -1) return (rxref); if (OF_getencprop(child, "phandle", &rxref, sizeof(rxref)) == -1 && OF_getencprop(child, "ibm,phandle", &rxref, sizeof(rxref)) == -1 && OF_getencprop(child, "linux,phandle", &rxref, sizeof(rxref)) == -1) continue; if (rxref == xref) return (child); } return (-1); } phandle_t OF_node_from_xref(phandle_t xref) { struct xrefinfo *xi; phandle_t node; if (xref_init_done) { if ((xi = xrefinfo_find(xref, FIND_BY_XREF)) == NULL) return (xref); return (xi->node); } if ((node = OF_child_xref_phandle(OF_peer(0), xref)) == -1) return (xref); return (node); } phandle_t OF_xref_from_node(phandle_t node) { struct xrefinfo *xi; phandle_t xref; if (xref_init_done) { if ((xi = xrefinfo_find(node, FIND_BY_NODE)) == NULL) return (node); return (xi->xref); } if (OF_getencprop(node, "phandle", &xref, sizeof(xref)) == -1 && OF_getencprop(node, "ibm,phandle", &xref, sizeof(xref)) == -1 && OF_getencprop(node, "linux,phandle", &xref, sizeof(xref)) == -1) return (node); return (xref); } device_t OF_device_from_xref(phandle_t xref) { struct xrefinfo *xi; if (xref_init_done) { if ((xi = xrefinfo_find(xref, FIND_BY_XREF)) == NULL) return (NULL); return (xi->dev); } panic("Attempt to find device before xreflist_init"); } phandle_t OF_xref_from_device(device_t dev) { struct xrefinfo *xi; if (xref_init_done) { if ((xi = xrefinfo_find((uintptr_t)dev, FIND_BY_DEV)) == NULL) return (0); return (xi->xref); } panic("Attempt to find xref before xreflist_init"); } int OF_device_register_xref(phandle_t xref, device_t dev) { struct xrefinfo *xi; /* * If the given xref handle doesn't already exist in the list then we * add a list entry. In theory this can only happen on a system where * nodes don't contain phandle properties and xref and node handles are * synonymous, so the xref handle is added as the node handle as well. */ if (xref_init_done) { if ((xi = xrefinfo_find(xref, FIND_BY_XREF)) == NULL) xrefinfo_add(xref, xref, dev); else xi->dev = dev; return (0); } panic("Attempt to register device before xreflist_init"); } /* Call the method in the scope of a given instance. */ int OF_call_method(const char *method, ihandle_t instance, int nargs, int nreturns, ...) { va_list ap; cell_t args_n_results[12]; int n, status; if (nargs > 6 || ofw_def_impl == NULL) return (-1); va_start(ap, nreturns); for (n = 0; n < nargs; n++) args_n_results[n] = va_arg(ap, cell_t); status = OFW_CALL_METHOD(ofw_obj, instance, method, nargs, nreturns, args_n_results); if (status != 0) return (status); for (; n < nargs + nreturns; n++) *va_arg(ap, cell_t *) = args_n_results[n]; va_end(ap); return (0); } /* * Device I/O functions */ /* Open an instance for a device. */ ihandle_t OF_open(const char *device) { if (ofw_def_impl == NULL) return (0); return (OFW_OPEN(ofw_obj, device)); } /* Close an instance. */ void OF_close(ihandle_t instance) { if (ofw_def_impl == NULL) return; OFW_CLOSE(ofw_obj, instance); } /* Read from an instance. */ ssize_t OF_read(ihandle_t instance, void *addr, size_t len) { if (ofw_def_impl == NULL) return (-1); return (OFW_READ(ofw_obj, instance, addr, len)); } /* Write to an instance. */ ssize_t OF_write(ihandle_t instance, const void *addr, size_t len) { if (ofw_def_impl == NULL) return (-1); return (OFW_WRITE(ofw_obj, instance, addr, len)); } /* Seek to a position. */ int OF_seek(ihandle_t instance, uint64_t pos) { if (ofw_def_impl == NULL) return (-1); return (OFW_SEEK(ofw_obj, instance, pos)); } /* * Memory functions */ /* Claim an area of memory. */ void * OF_claim(void *virt, size_t size, u_int align) { if (ofw_def_impl == NULL) return ((void *)-1); return (OFW_CLAIM(ofw_obj, virt, size, align)); } /* Release an area of memory. */ void OF_release(void *virt, size_t size) { if (ofw_def_impl == NULL) return; OFW_RELEASE(ofw_obj, virt, size); } /* * Control transfer functions */ /* Suspend and drop back to the Open Firmware interface. */ void OF_enter() { if (ofw_def_impl == NULL) return; OFW_ENTER(ofw_obj); } /* Shut down and drop back to the Open Firmware interface. */ void OF_exit() { if (ofw_def_impl == NULL) panic("OF_exit: Open Firmware not available"); /* Should not return */ OFW_EXIT(ofw_obj); for (;;) /* just in case */ ; } Index: head/sys/powerpc/ofw/ofw_machdep.c =================================================================== --- head/sys/powerpc/ofw/ofw_machdep.c (revision 326309) +++ head/sys/powerpc/ofw/ofw_machdep.c (revision 326310) @@ -1,617 +1,624 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (C) 1996 Wolfgang Solfrank. * Copyright (C) 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $NetBSD: ofw_machdep.c,v 1.5 2000/05/23 13:25:43 tsubai Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void *fdt; int ofw_real_mode; #ifdef AIM extern register_t ofmsr[5]; extern void *openfirmware_entry; char save_trap_init[0x2f00]; /* EXC_LAST */ char save_trap_of[0x2f00]; /* EXC_LAST */ int ofwcall(void *); static int openfirmware(void *args); __inline void ofw_save_trap_vec(char *save_trap_vec) { if (!ofw_real_mode) return; bcopy((void *)EXC_RST, save_trap_vec, EXC_LAST - EXC_RST); } static __inline void ofw_restore_trap_vec(char *restore_trap_vec) { if (!ofw_real_mode) return; bcopy(restore_trap_vec, (void *)EXC_RST, EXC_LAST - EXC_RST); __syncicache(EXC_RSVD, EXC_LAST - EXC_RSVD); } /* * Saved SPRG0-3 from OpenFirmware. Will be restored prior to the callback. */ register_t ofw_sprg0_save; static __inline void ofw_sprg_prepare(void) { if (ofw_real_mode) return; /* * Assume that interrupt are disabled at this point, or * SPRG1-3 could be trashed */ #ifdef __powerpc64__ __asm __volatile("mtsprg1 %0\n\t" "mtsprg2 %1\n\t" "mtsprg3 %2\n\t" : : "r"(ofmsr[2]), "r"(ofmsr[3]), "r"(ofmsr[4])); #else __asm __volatile("mfsprg0 %0\n\t" "mtsprg0 %1\n\t" "mtsprg1 %2\n\t" "mtsprg2 %3\n\t" "mtsprg3 %4\n\t" : "=&r"(ofw_sprg0_save) : "r"(ofmsr[1]), "r"(ofmsr[2]), "r"(ofmsr[3]), "r"(ofmsr[4])); #endif } static __inline void ofw_sprg_restore(void) { if (ofw_real_mode) return; /* * Note that SPRG1-3 contents are irrelevant. They are scratch * registers used in the early portion of trap handling when * interrupts are disabled. * * PCPU data cannot be used until this routine is called ! */ #ifndef __powerpc64__ __asm __volatile("mtsprg0 %0" :: "r"(ofw_sprg0_save)); #endif } #endif static int parse_ofw_memory(phandle_t node, const char *prop, struct mem_region *output) { cell_t address_cells, size_cells; cell_t OFmem[4 * PHYS_AVAIL_SZ]; int sz, i, j; phandle_t phandle; sz = 0; /* * Get #address-cells from root node, defaulting to 1 if it cannot * be found. */ phandle = OF_finddevice("/"); if (OF_getencprop(phandle, "#address-cells", &address_cells, sizeof(address_cells)) < (ssize_t)sizeof(address_cells)) address_cells = 1; if (OF_getencprop(phandle, "#size-cells", &size_cells, sizeof(size_cells)) < (ssize_t)sizeof(size_cells)) size_cells = 1; /* * Get memory. */ if (node == -1 || (sz = OF_getencprop(node, prop, OFmem, sizeof(OFmem))) <= 0) panic("Physical memory map not found"); i = 0; j = 0; while (i < sz/sizeof(cell_t)) { output[j].mr_start = OFmem[i++]; if (address_cells == 2) { output[j].mr_start <<= 32; output[j].mr_start += OFmem[i++]; } output[j].mr_size = OFmem[i++]; if (size_cells == 2) { output[j].mr_size <<= 32; output[j].mr_size += OFmem[i++]; } if (output[j].mr_start > BUS_SPACE_MAXADDR) continue; /* * Constrain memory to that which we can access. * 32-bit AIM can only reference 32 bits of address currently, * but Book-E can access 36 bits. */ if (((uint64_t)output[j].mr_start + (uint64_t)output[j].mr_size - 1) > BUS_SPACE_MAXADDR) { output[j].mr_size = BUS_SPACE_MAXADDR - output[j].mr_start + 1; } j++; } sz = j*sizeof(output[0]); return (sz); } static int excise_fdt_reserved(struct mem_region *avail, int asz) { struct { uint64_t address; uint64_t size; } fdtmap[16]; ssize_t fdtmapsize; phandle_t chosen; int i, j, k; chosen = OF_finddevice("/chosen"); fdtmapsize = OF_getprop(chosen, "fdtmemreserv", fdtmap, sizeof(fdtmap)); for (j = 0; j < fdtmapsize/sizeof(fdtmap[0]); j++) { fdtmap[j].address = be64toh(fdtmap[j].address) & ~PAGE_MASK; fdtmap[j].size = round_page(be64toh(fdtmap[j].size)); } KASSERT(j*sizeof(fdtmap[0]) < sizeof(fdtmap), ("Exceeded number of FDT reservations")); /* Add a virtual entry for the FDT itself */ if (fdt != NULL) { fdtmap[j].address = (vm_offset_t)fdt & ~PAGE_MASK; fdtmap[j].size = round_page(fdt_totalsize(fdt)); fdtmapsize += sizeof(fdtmap[0]); } for (i = 0; i < asz; i++) { for (j = 0; j < fdtmapsize/sizeof(fdtmap[0]); j++) { /* * Case 1: Exclusion region encloses complete * available entry. Drop it and move on. */ if (fdtmap[j].address <= avail[i].mr_start && fdtmap[j].address + fdtmap[j].size >= avail[i].mr_start + avail[i].mr_size) { for (k = i+1; k < asz; k++) avail[k-1] = avail[k]; asz--; i--; /* Repeat some entries */ continue; } /* * Case 2: Exclusion region starts in available entry. * Trim it to where the entry begins and append * a new available entry with the region after * the excluded region, if any. */ if (fdtmap[j].address >= avail[i].mr_start && fdtmap[j].address < avail[i].mr_start + avail[i].mr_size) { if (fdtmap[j].address + fdtmap[j].size < avail[i].mr_start + avail[i].mr_size) { avail[asz].mr_start = fdtmap[j].address + fdtmap[j].size; avail[asz].mr_size = avail[i].mr_start + avail[i].mr_size - avail[asz].mr_start; asz++; } avail[i].mr_size = fdtmap[j].address - avail[i].mr_start; } /* * Case 3: Exclusion region ends in available entry. * Move start point to where the exclusion zone ends. * The case of a contained exclusion zone has already * been caught in case 2. */ if (fdtmap[j].address + fdtmap[j].size >= avail[i].mr_start && fdtmap[j].address + fdtmap[j].size < avail[i].mr_start + avail[i].mr_size) { avail[i].mr_size += avail[i].mr_start; avail[i].mr_start = fdtmap[j].address + fdtmap[j].size; avail[i].mr_size -= avail[i].mr_start; } } } return (asz); } /* * This is called during powerpc_init, before the system is really initialized. * It shall provide the total and the available regions of RAM. * The available regions need not take the kernel into account. */ void ofw_mem_regions(struct mem_region *memp, int *memsz, struct mem_region *availp, int *availsz) { phandle_t phandle; int asz, msz; int res; char name[31]; asz = msz = 0; /* * Get memory from all the /memory nodes. */ for (phandle = OF_child(OF_peer(0)); phandle != 0; phandle = OF_peer(phandle)) { if (OF_getprop(phandle, "name", name, sizeof(name)) <= 0) continue; if (strncmp(name, "memory", sizeof(name)) != 0 && strncmp(name, "memory@", strlen("memory@")) != 0) continue; res = parse_ofw_memory(phandle, "reg", &memp[msz]); msz += res/sizeof(struct mem_region); if (OF_getproplen(phandle, "available") >= 0) res = parse_ofw_memory(phandle, "available", &availp[asz]); else res = parse_ofw_memory(phandle, "reg", &availp[asz]); asz += res/sizeof(struct mem_region); } phandle = OF_finddevice("/chosen"); if (OF_hasprop(phandle, "fdtmemreserv")) asz = excise_fdt_reserved(availp, asz); *memsz = msz; *availsz = asz; } void OF_initial_setup(void *fdt_ptr, void *junk, int (*openfirm)(void *)) { #ifdef AIM ofmsr[0] = mfmsr(); #ifdef __powerpc64__ ofmsr[0] &= ~PSL_SF; #else __asm __volatile("mfsprg0 %0" : "=&r"(ofmsr[1])); #endif __asm __volatile("mfsprg1 %0" : "=&r"(ofmsr[2])); __asm __volatile("mfsprg2 %0" : "=&r"(ofmsr[3])); __asm __volatile("mfsprg3 %0" : "=&r"(ofmsr[4])); openfirmware_entry = openfirm; if (ofmsr[0] & PSL_DR) ofw_real_mode = 0; else ofw_real_mode = 1; ofw_save_trap_vec(save_trap_init); #else ofw_real_mode = 1; #endif fdt = fdt_ptr; #ifdef FDT_DTB_STATIC /* Check for a statically included blob */ if (fdt == NULL) fdt = &fdt_static_dtb; #endif } boolean_t OF_bootstrap() { boolean_t status = FALSE; + int err = 0; #ifdef AIM if (openfirmware_entry != NULL) { if (ofw_real_mode) { status = OF_install(OFW_STD_REAL, 0); } else { #ifdef __powerpc64__ status = OF_install(OFW_STD_32BIT, 0); #else status = OF_install(OFW_STD_DIRECT, 0); #endif } if (status != TRUE) return status; - OF_init(openfirmware); + err = OF_init(openfirmware); } else #endif if (fdt != NULL) { status = OF_install(OFW_FDT, 0); if (status != TRUE) return status; - OF_init(fdt); - OF_interpret("perform-fixup", 0); + err = OF_init(fdt); + if (err == 0) + OF_interpret("perform-fixup", 0); } + + if (err != 0) { + OF_install(NULL, 0); + status = FALSE; + } return (status); } #ifdef AIM void ofw_quiesce(void) { struct { cell_t name; cell_t nargs; cell_t nreturns; } args; KASSERT(!pmap_bootstrapped, ("Cannot call ofw_quiesce after VM is up")); args.name = (cell_t)(uintptr_t)"quiesce"; args.nargs = 0; args.nreturns = 0; openfirmware(&args); } static int openfirmware_core(void *args) { int result; register_t oldmsr; if (openfirmware_entry == NULL) return (-1); /* * Turn off exceptions - we really don't want to end up * anywhere unexpected with PCPU set to something strange * or the stack pointer wrong. */ oldmsr = intr_disable(); ofw_sprg_prepare(); /* Save trap vectors */ ofw_save_trap_vec(save_trap_of); /* Restore initially saved trap vectors */ ofw_restore_trap_vec(save_trap_init); #ifndef __powerpc64__ /* * Clear battable[] translations */ if (!(cpu_features & PPC_FEATURE_64)) __asm __volatile("mtdbatu 2, %0\n" "mtdbatu 3, %0" : : "r" (0)); isync(); #endif result = ofwcall(args); /* Restore trap vecotrs */ ofw_restore_trap_vec(save_trap_of); ofw_sprg_restore(); intr_restore(oldmsr); return (result); } #ifdef SMP struct ofw_rv_args { void *args; int retval; volatile int in_progress; }; static void ofw_rendezvous_dispatch(void *xargs) { struct ofw_rv_args *rv_args = xargs; /* NOTE: Interrupts are disabled here */ if (PCPU_GET(cpuid) == 0) { /* * Execute all OF calls on CPU 0 */ rv_args->retval = openfirmware_core(rv_args->args); rv_args->in_progress = 0; } else { /* * Spin with interrupts off on other CPUs while OF has * control of the machine. */ while (rv_args->in_progress) cpu_spinwait(); } } #endif static int openfirmware(void *args) { int result; #ifdef SMP struct ofw_rv_args rv_args; #endif if (openfirmware_entry == NULL) return (-1); #ifdef SMP if (cold) { result = openfirmware_core(args); } else { rv_args.args = args; rv_args.in_progress = 1; smp_rendezvous(smp_no_rendezvous_barrier, ofw_rendezvous_dispatch, smp_no_rendezvous_barrier, &rv_args); result = rv_args.retval; } #else result = openfirmware_core(args); #endif return (result); } void OF_reboot() { struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t arg; } args; args.name = (cell_t)(uintptr_t)"interpret"; args.nargs = 1; args.nreturns = 0; args.arg = (cell_t)(uintptr_t)"reset-all"; openfirmware_core(&args); /* Don't do rendezvous! */ for (;;); /* just in case */ } #endif /* AIM */ void OF_getetheraddr(device_t dev, u_char *addr) { phandle_t node; node = ofw_bus_get_node(dev); OF_getprop(node, "local-mac-address", addr, ETHER_ADDR_LEN); } /* * Return a bus handle and bus tag that corresponds to the register * numbered regno for the device referenced by the package handle * dev. This function is intended to be used by console drivers in * early boot only. It works by mapping the address of the device's * register in the address space of its parent and recursively walk * the device tree upward this way. */ int OF_decode_addr(phandle_t dev, int regno, bus_space_tag_t *tag, bus_space_handle_t *handle, bus_size_t *sz) { bus_addr_t addr; bus_size_t size; pcell_t pci_hi; int flags, res; res = ofw_reg_to_paddr(dev, regno, &addr, &size, &pci_hi); if (res < 0) return (res); if (pci_hi == OFW_PADDR_NOT_PCI) { *tag = &bs_be_tag; flags = 0; } else { *tag = &bs_le_tag; flags = (pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) ? BUS_SPACE_MAP_PREFETCHABLE: 0; } if (sz != NULL) *sz = size; return (bus_space_map(*tag, addr, size, flags, handle)); }