patch-2.1.79 linux/arch/ppc/kernel/prom.c
Next file: linux/arch/ppc/kernel/ptrace.c
Previous file: linux/arch/ppc/kernel/process.c
Back to the patch index
Back to the overall index
- Lines: 758
- Date:
Mon Jan 12 15:18:13 1998
- Orig file:
v2.1.78/linux/arch/ppc/kernel/prom.c
- Orig date:
Thu Sep 4 17:07:29 1997
diff -u --recursive --new-file v2.1.78/linux/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c
@@ -8,33 +8,28 @@
* Paul Mackerras August 1996.
* Copyright (C) 1996 Paul Mackerras.
*/
-
#include <stdarg.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/string.h>
-#include <linux/blk.h>
+#include <linux/init.h>
#include <asm/prom.h>
#include <asm/page.h>
#include <asm/processor.h>
-#define getpromprop(node, name, buf, len) \
- ((int)call_prom("getprop", 4, 1, (node), (name), (buf), (len)))
-
-ihandle prom_stdout;
-ihandle prom_chosen;
-
-char command_line[256];
-int screen_initialized = 0;
-
-char prom_display_path[128];
+/*
+ * Properties whose value is longer than this get excluded from our
+ * copy of the device tree. This way we don't waste space storing
+ * things like "driver,AAPL,MacOS,PowerPC" properties.
+ */
+#define MAX_PROPERTY_LENGTH 1024
struct prom_args {
const char *service;
int nargs;
int nret;
void *args[10];
-} prom_args;
+};
struct pci_address {
unsigned a_hi;
@@ -55,39 +50,83 @@
unsigned size_lo;
};
-void (*prom_entry)(void *);
-extern int prom_trashed;
+char *prom_display_paths[FB_MAX] __initdata = { 0, };
+unsigned int prom_num_displays = 0;
+
+prom_entry prom = 0;
+ihandle prom_chosen = 0, prom_stdout = 0;
+
+extern char *klimit;
+char *bootpath = 0;
+char *bootdevice = 0;
-static int prom_callback(struct prom_args *);
+unsigned int rtas_data = 0;
+unsigned int rtas_entry = 0;
+
+static struct device_node *allnodes = 0;
+
+static void *call_prom(const char *service, int nargs, int nret, ...);
+static void prom_print(const char *msg);
+static void prom_exit(void);
+static unsigned long copy_device_tree(unsigned long, unsigned long);
static unsigned long inspect_node(phandle, struct device_node *, unsigned long,
- unsigned long, unsigned long);
-static void check_display(void);
+ unsigned long, struct device_node ***);
+static unsigned long finish_node(struct device_node *, unsigned long,
+ unsigned long);
+static unsigned long check_display(unsigned long);
static int prom_next_node(phandle *);
-extern int pmac_display_supported(const char *);
-extern void enter_prom(void *);
+extern void enter_rtas(void *);
+extern unsigned long reloc_offset(void);
-void
+/*
+ * prom_init() is called very early on, before the kernel text
+ * and data have been mapped to KERNELBASE. At this point the code
+ * is running at whatever address it has been loaded at, so
+ * references to extern and static variables must be relocated
+ * explicitly. The procedure reloc_offset() returns the the address
+ * we're currently running at minus the address we were linked at.
+ * (Note that strings count as static variables.)
+ *
+ * Because OF may have mapped I/O devices into the area starting at
+ * KERNELBASE, particularly on CHRP machines, we can't safely call
+ * OF once the kernel has been mapped to KERNELBASE. Therefore all
+ * OF calls should be done within prom_init(), and prom_init()
+ * and all routines called within it must be careful to relocate
+ * references as necessary.
+ *
+ * Note that the bss is cleared *after* prom_init runs, so we have
+ * to make sure that any static or extern variables it accesses
+ * are put in the data segment.
+ */
+#define PTRRELOC(x) ((typeof(x))((unsigned long)(x) + offset))
+#define PTRUNRELOC(x) ((typeof(x))((unsigned long)(x) - offset))
+#define RELOC(x) (*PTRRELOC(&(x)))
+
+#define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long))
+
+static void
prom_exit()
{
struct prom_args args;
+ unsigned long offset = reloc_offset();
args.service = "exit";
args.nargs = 0;
args.nret = 0;
- enter_prom(&args);
+ RELOC(prom)(&args);
for (;;) /* should never get here */
;
}
-void *
+static void *
call_prom(const char *service, int nargs, int nret, ...)
{
va_list list;
int i;
+ unsigned long offset = reloc_offset();
+ struct prom_args prom_args;
- if (prom_trashed)
- panic("prom called after its memory was reclaimed");
prom_args.service = service;
prom_args.nargs = nargs;
prom_args.nret = nret;
@@ -97,26 +136,26 @@
va_end(list);
for (i = 0; i < nret; ++i)
prom_args.args[i + nargs] = 0;
- enter_prom(&prom_args);
+ RELOC(prom)(&prom_args);
return prom_args.args[nargs];
}
-void
+static void
prom_print(const char *msg)
{
const char *p, *q;
- const char *crlf = "\r\n";
+ unsigned long offset = reloc_offset();
- if (screen_initialized)
- return;
for (p = msg; *p != 0; p = q) {
for (q = p; *q != 0 && *q != '\n'; ++q)
;
if (q > p)
- call_prom("write", 3, 1, prom_stdout, p, q - p);
+ call_prom(RELOC("write"), 3, 1, RELOC(prom_stdout),
+ p, q - p);
if (*q != 0) {
++q;
- call_prom("write", 3, 1, prom_stdout, crlf, 2);
+ call_prom(RELOC("write"), 3, 1, RELOC(prom_stdout),
+ RELOC("\r\n"), 2);
}
}
}
@@ -126,285 +165,311 @@
* handling exceptions and the MMU hash table for us.
*/
void
-prom_init(char *params, int unused, void (*pp)(void *))
+prom_init(int r3, int r4, prom_entry pp)
{
+ unsigned long mem;
+ ihandle prom_rtas;
+ unsigned int rtas_size;
+ unsigned long offset = reloc_offset();
+ int l;
+ char *p, *d;
+
/* First get a handle for the stdout device */
- if ( ! have_of() )
- return;
- prom_entry = pp;
- prom_chosen = call_prom("finddevice", 1, 1, "/chosen");
- if (prom_chosen == (void *)-1)
+ RELOC(prom) = pp;
+ RELOC(prom_chosen) = call_prom(RELOC("finddevice"), 1, 1,
+ RELOC("/chosen"));
+ if (RELOC(prom_chosen) == (void *)-1)
+ prom_exit();
+ if ((int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen),
+ RELOC("stdout"), &RELOC(prom_stdout),
+ sizeof(prom_stdout)) <= 0)
prom_exit();
- call_prom("getprop", 4, 1, prom_chosen, "stdout", &prom_stdout,
- (void *) sizeof(prom_stdout));
- /*
- * If we were booted via quik, params points to the physical address
- * of the command-line parameters.
- * If we were booted from an xcoff image (i.e. netbooted or
- * booted from floppy), we get the command line from the bootargs
- * property of the /chosen node. If an initial ramdisk is present,
- * params and unused are used for initrd_start and initrd_size,
- * otherwise they contain 0xdeadbeef.
- */
- command_line[0] = 0;
- if ((unsigned long) params >= 0x4000
- && (unsigned long) params < 0x800000
- && unused == 0) {
- strncpy(command_line, params+KERNELBASE, sizeof(command_line));
- } else {
-#ifdef CONFIG_BLK_DEV_INITRD
- if ((unsigned long) params - KERNELBASE < 0x800000
- && unused != 0 && unused != 0xdeadbeef) {
- initrd_start = (unsigned long) params;
- initrd_end = initrd_start + unused;
- ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0);
+ /* Get the boot device and translate it to a full OF pathname. */
+ mem = (unsigned long) RELOC(klimit) + offset;
+ p = (char *) mem;
+ l = (int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen),
+ RELOC("bootpath"), p, 1<<20);
+ if (l > 0) {
+ p[l] = 0; /* should already be null-terminated */
+ RELOC(bootpath) = PTRUNRELOC(p);
+ mem += l + 1;
+ d = (char *) mem;
+ *d = 0;
+ call_prom(RELOC("canon"), 3, 1, p, d, 1<<20);
+ RELOC(bootdevice) = PTRUNRELOC(d);
+ mem = ALIGN(mem + strlen(d) + 1);
+ }
+
+ mem = check_display(mem);
+
+ prom_print(RELOC("copying OF device tree..."));
+ mem = copy_device_tree(mem, mem + (1<<20));
+ prom_print(RELOC("done\n"));
+
+ prom_rtas = call_prom(RELOC("finddevice"), 1, 1, RELOC("/rtas"));
+ if (prom_rtas != (void *) -1) {
+ rtas_size = 0;
+ call_prom(RELOC("getprop"), 4, 1, prom_rtas,
+ RELOC("rtas-size"), &rtas_size, sizeof(rtas_size));
+ prom_print(RELOC("instantiating rtas..."));
+ if (rtas_size == 0) {
+ RELOC(rtas_data) = 0;
+ } else {
+ mem = (mem + 4095) & -4096; /* round to page bdry */
+ RELOC(rtas_data) = mem - KERNELBASE;
+ mem += rtas_size;
}
-#endif
- call_prom("getprop", 4, 1, prom_chosen, "bootargs",
- command_line, sizeof(command_line));
+ RELOC(rtas_entry) = (unsigned int)
+ call_prom(RELOC("instantiate-rtas"), 1, 1,
+ RELOC(rtas_data));
+ if (RELOC(rtas_entry) == -1)
+ prom_print(RELOC(" failed\n"));
+ else
+ prom_print(RELOC(" done\n"));
}
- command_line[sizeof(command_line) - 1] = 0;
- check_display();
+ RELOC(klimit) = (char *) (mem - offset);
}
/*
* If we have a display that we don't know how to drive,
* we will want to try to execute OF's open method for it
- * later. However, OF may fall over if we do that after
- * we've taken over the MMU and done set_prom_callback.
+ * later. However, OF will probably fall over if we do that
+ * we've taken over the MMU.
* So we check whether we will need to open the display,
* and if so, open it now.
*/
-static void
-check_display()
+static unsigned long
+check_display(unsigned long mem)
{
phandle node;
ihandle ih;
- char type[16], name[64], path[128];
+ unsigned long offset = reloc_offset();
+ char type[16], *path;
for (node = 0; prom_next_node(&node); ) {
type[0] = 0;
- getpromprop(node, "device_type", type, sizeof(type));
- if (strcmp(type, "display") != 0)
- continue;
- name[0] = 0;
- getpromprop(node, "name", name, sizeof(name));
- if (pmac_display_supported(name))
- /* we have a supported display */
- return;
- }
- printk(KERN_INFO "No supported display found\n");
- for (node = 0; prom_next_node(&node); ) {
- type[0] = 0;
- getpromprop(node, "device_type", type, sizeof(type));
- if (strcmp(type, "display") != 0)
+ call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"),
+ type, sizeof(type));
+ if (strcmp(type, RELOC("display")) != 0)
continue;
/* It seems OF doesn't null-terminate the path :-( */
- memset(path, 0, sizeof(path));
- if ((int) call_prom("package-to-path", 3, 1,
- node, path, sizeof(path) - 1) < 0) {
- printk(KERN_WARNING "can't get path for display %p\n",
- node);
+ path = (char *) mem;
+ memset(path, 0, 256);
+ if ((int) call_prom(RELOC("package-to-path"), 3, 1,
+ node, path, 255) < 0)
continue;
- }
- ih = call_prom("open", 1, 1, path);
+ prom_print(RELOC("opening display "));
+ prom_print(path);
+ ih = call_prom(RELOC("open"), 1, 1, path);
if (ih == 0 || ih == (ihandle) -1) {
- printk(KERN_WARNING "couldn't open display %s\n",
- path);
+ prom_print(RELOC("... failed\n"));
continue;
}
- printk(KERN_INFO "Opened display device %s using "
- "Open Firmware\n", path);
- strcpy(prom_display_path, path);
- break;
+ prom_print(RELOC("... ok\n"));
+ mem += strlen(path) + 1;
+ RELOC(prom_display_paths[RELOC(prom_num_displays)++])
+ = PTRUNRELOC(path);
+ if (RELOC(prom_num_displays) >= FB_MAX)
+ break;
}
+ return ALIGN(mem);
}
static int
prom_next_node(phandle *nodep)
{
phandle node;
+ unsigned long offset = reloc_offset();
if ((node = *nodep) != 0
- && (*nodep = call_prom("child", 1, 1, node)) != 0)
+ && (*nodep = call_prom(RELOC("child"), 1, 1, node)) != 0)
return 1;
- if ((*nodep = call_prom("peer", 1, 1, node)) != 0)
+ if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0)
return 1;
for (;;) {
- if ((node = call_prom("parent", 1, 1, node)) == 0)
+ if ((node = call_prom(RELOC("parent"), 1, 1, node)) == 0)
return 0;
- if ((*nodep = call_prom("peer", 1, 1, node)) != 0)
+ if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0)
return 1;
}
}
/*
- * Callback routine for the PROM to call us.
- * No services are implemented yet :-)
- */
-static int
-prom_callback(struct prom_args *argv)
-{
- printk("uh oh, prom callback '%s' (%d/%d)\n", argv->service,
- argv->nargs, argv->nret);
- return -1;
-}
-
-/*
- * Register a callback with the Open Firmware PROM so it can ask
- * us to map/unmap memory, etc.
- */
-void
-set_prom_callback()
-{
- call_prom("set-callback", 1, 1, prom_callback);
-}
-
-void
-abort()
-{
-#ifdef CONFIG_XMON
- xmon(0);
-#endif
- prom_exit();
-}
-
-/*
* Make a copy of the device tree from the PROM.
*/
-
-static struct device_node *allnodes;
-static struct device_node **allnextp;
-
-#define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long))
-
-unsigned long
+static unsigned long
copy_device_tree(unsigned long mem_start, unsigned long mem_end)
{
phandle root;
-
- root = call_prom("peer", 1, 1, (phandle)0);
- if (root == (phandle)0)
- panic("couldn't get device tree root\n");
- allnextp = &allnodes;
- mem_start = inspect_node(root, 0, 0, mem_start, mem_end);
+ unsigned long new_start;
+ struct device_node **allnextp;
+ unsigned long offset = reloc_offset();
+
+ root = call_prom(RELOC("peer"), 1, 1, (phandle)0);
+ if (root == (phandle)0) {
+ prom_print(RELOC("couldn't get device tree root\n"));
+ prom_exit();
+ }
+ allnextp = &RELOC(allnodes);
+ mem_start = ALIGN(mem_start);
+ new_start = inspect_node(root, 0, mem_start, mem_end, &allnextp);
*allnextp = 0;
- return mem_start;
+ return new_start;
}
static unsigned long
-inspect_node(phandle node, struct device_node *dad, unsigned long base_address,
- unsigned long mem_start, unsigned long mem_end)
+inspect_node(phandle node, struct device_node *dad,
+ unsigned long mem_start, unsigned long mem_end,
+ struct device_node ***allnextpp)
{
- struct reg_property *reg, *rp;
- struct pci_reg_property *pci_addrs;
- int l, i;
+ int l;
phandle child;
struct device_node *np;
struct property *pp, **prev_propp;
- char *prev_name;
+ char *prev_name, *namep;
+ unsigned char *valp;
+ unsigned long offset = reloc_offset();
np = (struct device_node *) mem_start;
mem_start += sizeof(struct device_node);
memset(np, 0, sizeof(*np));
np->node = node;
- *allnextp = np;
- allnextp = &np->allnext;
- np->parent = dad;
+ **allnextpp = PTRUNRELOC(np);
+ *allnextpp = &np->allnext;
if (dad != 0) {
+ np->parent = PTRUNRELOC(dad);
/* we temporarily use the `next' field as `last_child'. */
if (dad->next == 0)
- dad->child = np;
+ dad->child = PTRUNRELOC(np);
else
- dad->next->sibling = np;
+ dad->next->sibling = PTRUNRELOC(np);
dad->next = np;
}
/* get and store all properties */
prev_propp = &np->properties;
- prev_name = 0;
+ prev_name = RELOC("");
for (;;) {
pp = (struct property *) mem_start;
- pp->name = (char *) (pp + 1);
- if ((int) call_prom("nextprop", 3, 1, node, prev_name,
- pp->name) <= 0)
+ namep = (char *) (pp + 1);
+ pp->name = PTRUNRELOC(namep);
+ if ((int) call_prom(RELOC("nextprop"), 3, 1, node, prev_name,
+ namep) <= 0)
break;
- mem_start = ALIGN((unsigned long)pp->name
- + strlen(pp->name) + 1);
- pp->value = (unsigned char *) mem_start;
+ mem_start = ALIGN((unsigned long)namep + strlen(namep) + 1);
+ prev_name = namep;
+ valp = (unsigned char *) mem_start;
+ pp->value = PTRUNRELOC(valp);
pp->length = (int)
- call_prom("getprop", 4, 1, node, pp->name, pp->value,
- mem_end - mem_start);
+ call_prom(RELOC("getprop"), 4, 1, node, namep,
+ valp, mem_end - mem_start);
if (pp->length < 0)
- panic("hey, where did property %s go?", pp->name);
+ continue;
+#ifdef MAX_PROPERTY_LENGTH
+ if (pp->length > MAX_PROPERTY_LENGTH)
+ continue; /* ignore this property */
+#endif
mem_start = ALIGN(mem_start + pp->length);
- prev_name = pp->name;
- *prev_propp = pp;
+ *prev_propp = PTRUNRELOC(pp);
prev_propp = &pp->next;
}
*prev_propp = 0;
+ /* get the node's full name */
+ l = (int) call_prom(RELOC("package-to-path"), 3, 1, node,
+ (char *) mem_start, mem_end - mem_start);
+ if (l >= 0) {
+ np->full_name = PTRUNRELOC((char *) mem_start);
+ *(char *)(mem_start + l) = 0;
+ mem_start = ALIGN(mem_start + l + 1);
+ }
+
+ /* do all our children */
+ child = call_prom(RELOC("child"), 1, 1, node);
+ while (child != (void *)0) {
+ mem_start = inspect_node(child, np, mem_start, mem_end,
+ allnextpp);
+ child = call_prom(RELOC("peer"), 1, 1, child);
+ }
+
+ return mem_start;
+}
+
+void
+finish_device_tree(void)
+{
+ unsigned long mem = (unsigned long) klimit;
+
+ mem = finish_node(allnodes, mem, 0UL);
+ printk(KERN_INFO "device tree used %lu bytes\n",
+ mem - (unsigned long) allnodes);
+ klimit = (char *) mem;
+}
+
+static unsigned long
+finish_node(struct device_node *np, unsigned long mem_start,
+ unsigned long base_address)
+{
+ struct reg_property *rp;
+ struct pci_reg_property *pci_addrs;
+ struct address_range *adr;
+ struct device_node *child;
+ int i, l;
+
np->name = get_property(np, "name", 0);
np->type = get_property(np, "device_type", 0);
/* get all the device addresses and interrupts */
- reg = (struct reg_property *) mem_start;
+ adr = (struct address_range *) mem_start;
pci_addrs = (struct pci_reg_property *)
get_property(np, "assigned-addresses", &l);
i = 0;
if (pci_addrs != 0) {
while ((l -= sizeof(struct pci_reg_property)) >= 0) {
/* XXX assumes PCI addresses mapped 1-1 to physical */
- reg[i].address = pci_addrs[i].addr.a_lo;
- reg[i].size = pci_addrs[i].size_lo;
+ adr[i].space = pci_addrs[i].addr.a_hi;
+ adr[i].address = pci_addrs[i].addr.a_lo;
+ adr[i].size = pci_addrs[i].size_lo;
++i;
}
} else {
rp = (struct reg_property *) get_property(np, "reg", &l);
if (rp != 0) {
while ((l -= sizeof(struct reg_property)) >= 0) {
- reg[i].address = rp[i].address + base_address;
- reg[i].size = rp[i].size;
+ adr[i].space = 0;
+ adr[i].address = rp[i].address + base_address;
+ adr[i].size = rp[i].size;
++i;
}
}
}
if (i > 0) {
- np->addrs = reg;
+ np->addrs = adr;
np->n_addrs = i;
- mem_start += i * sizeof(struct reg_property);
+ mem_start += i * sizeof(struct address_range);
}
np->intrs = (int *) get_property(np, "AAPL,interrupts", &l);
+ if (np->intrs == 0)
+ np->intrs = (int *) get_property(np, "interrupts", &l);
if (np->intrs != 0)
np->n_intrs = l / sizeof(int);
- /* get the node's full name */
- l = (int) call_prom("package-to-path", 3, 1, node,
- (char *) mem_start, mem_end - mem_start);
- if (l >= 0) {
- np->full_name = (char *) mem_start;
- np->full_name[l] = 0;
- mem_start = ALIGN(mem_start + l + 1);
- }
-
- if (np->type != 0 && strcmp(np->type, "dbdma") == 0 && np->n_addrs > 0)
+ if (np->type != 0 && np->n_addrs > 0
+ && (strcmp(np->type, "dbdma") == 0
+ || strcmp(np->type, "mac-io") == 0))
base_address = np->addrs[0].address;
- child = call_prom("child", 1, 1, node);
- while (child != (void *)0) {
- mem_start = inspect_node(child, np, base_address,
- mem_start, mem_end);
- child = call_prom("peer", 1, 1, child);
- }
+ for (child = np->child; child != NULL; child = child->sibling)
+ mem_start = finish_node(child, mem_start, base_address);
return mem_start;
}
/*
- * Construct a return a list of the device_nodes with a given name.
+ * Construct and return a list of the device_nodes with a given name.
*/
struct device_node *
find_devices(const char *name)
@@ -423,7 +488,7 @@
}
/*
- * Construct a return a list of the device_nodes with a given type.
+ * Construct and return a list of the device_nodes with a given type.
*/
struct device_node *
find_type_devices(const char *type)
@@ -442,6 +507,31 @@
}
/*
+ * Construct and return a list of the device_nodes with a given type
+ * and compatible property.
+ */
+struct device_node *
+find_compatible_devices(const char *type, const char *compat)
+{
+ struct device_node *head, **prevp, *np;
+ const char *cp;
+
+ prevp = &head;
+ for (np = allnodes; np != 0; np = np->allnext) {
+ if (type != NULL
+ && !(np->type != 0 && strcasecmp(np->type, type) == 0))
+ continue;
+ cp = (char *) get_property(np, "compatible", NULL);
+ if (cp != NULL && strcasecmp(cp, compat) == 0) {
+ *prevp = np;
+ prevp = &np->next;
+ }
+ }
+ *prevp = 0;
+ return head;
+}
+
+/*
* Find the device_node with a given full_name.
*/
struct device_node *
@@ -456,6 +546,20 @@
}
/*
+ * Find the device_node with a given phandle.
+ */
+struct device_node *
+find_phandle(phandle ph)
+{
+ struct device_node *np;
+
+ for (np = allnodes; np != 0; np = np->allnext)
+ if (np->node == ph)
+ return np;
+ return NULL;
+}
+
+/*
* Find a property with a given name for a given node
* and return the value.
*/
@@ -521,4 +625,49 @@
pp->length);
}
}
+}
+
+int
+call_rtas(const char *service, int nargs, int nret,
+ unsigned long *outputs, ...)
+{
+ va_list list;
+ int i;
+ struct device_node *rtas;
+ int *tokp;
+ union {
+ unsigned long words[16];
+ double align;
+ } u;
+
+ rtas = find_devices("rtas");
+ if (rtas == NULL)
+ return -1;
+ tokp = (int *) get_property(rtas, service, NULL);
+ if (tokp == NULL) {
+ printk(KERN_ERR "No RTAS service called %s\n", service);
+ return -1;
+ }
+ u.words[0] = *tokp;
+ u.words[1] = nargs;
+ u.words[2] = nret;
+ va_start(list, outputs);
+ for (i = 0; i < nargs; ++i)
+ u.words[i+3] = va_arg(list, unsigned long);
+ va_end(list);
+ enter_rtas(&u);
+ if (nret > 1 && outputs != NULL)
+ for (i = 0; i < nret-1; ++i)
+ outputs[i] = u.words[i+nargs+4];
+ return u.words[nargs+3];
+}
+
+void
+abort()
+{
+#ifdef CONFIG_XMON
+ extern void xmon(void *);
+ xmon(0);
+#endif
+ prom_exit();
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov