diff options
Diffstat (limited to 'qemu/roms/openbios/libopenbios/client.c')
-rw-r--r-- | qemu/roms/openbios/libopenbios/client.c | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/qemu/roms/openbios/libopenbios/client.c b/qemu/roms/openbios/libopenbios/client.c new file mode 100644 index 000000000..8b3d582b6 --- /dev/null +++ b/qemu/roms/openbios/libopenbios/client.c @@ -0,0 +1,367 @@ +/* + * Creation Date: <2003/11/25 14:29:08 samuel> + * Time-stamp: <2004/03/27 01:13:53 samuel> + * + * <client.c> + * + * OpenFirmware client interface + * + * Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 + * + */ + +#include "config.h" +#include "libopenbios/bindings.h" +#include "libopenbios/of.h" + +/* Uncomment to enable debug printout of client interface calls */ +//#define DEBUG_CIF +//#define DUMP_IO + +/* OF client interface. r3 points to the argument array. On return, + * r3 should contain 0==true or -1==false. r4-r12,cr0,cr1 may + * be modified freely. + * + * -1 should only be returned if the control transfer to OF fails + * (it doesn't) or if the function is unimplemented. + */ + +#define PROM_MAX_ARGS 10 +typedef struct prom_args { + prom_uarg_t service; + prom_arg_t nargs; + prom_arg_t nret; + prom_uarg_t args[PROM_MAX_ARGS]; +} __attribute__((packed)) prom_args_t; + +static inline const char * +arg2pointer(prom_uarg_t value) +{ + return (char*)(uintptr_t)value; +} + +static inline const char * +get_service(prom_args_t *pb) +{ + return arg2pointer(pb->service); +} + +#ifdef DEBUG_CIF +static void memdump(const char *mem, unsigned long size) +{ + int i; + + if (size == (unsigned long) -1) + return; + + for (i = 0; i < size; i += 16) { + int j; + + printk("0x%08lx ", (unsigned long)mem + i); + + for (j = 0; j < 16 && i + j < size; j++) + printk(" %02x", *(unsigned char*)(mem + i + j)); + + for ( ; j < 16; j++) + printk(" __"); + + printk(" "); + + for (j = 0; j < 16 && i + j < size; j++) { + unsigned char c = *(mem + i + j); + if (isprint(c)) + printk("%c", c); + else + printk("."); + } + printk("\n"); + } +} + +static void dump_service(prom_args_t *pb) +{ + int i; + const char *service = get_service(pb); + if (strcmp(service, "test") == 0) { + printk("test(\"%s\") = ", arg2pointer(pb->args[0])); + } else if (strcmp(service, "peer") == 0) { + printk("peer(0x" FMT_prom_uargx ") = ", pb->args[0]); + } else if (strcmp(service, "child") == 0) { + printk("child(0x" FMT_prom_uargx ") = ", pb->args[0]); + } else if (strcmp(service, "parent") == 0) { + printk("parent(0x" FMT_prom_uargx ") = ", pb->args[0]); + } else if (strcmp(service, "instance-to-package") == 0) { + printk("instance-to-package(0x" FMT_prom_uargx ") = ", pb->args[0]); + } else if (strcmp(service, "getproplen") == 0) { + printk("getproplen(0x" FMT_prom_uargx ", \"%s\") = ", + pb->args[0], arg2pointer(pb->args[1])); + } else if (strcmp(service, "getprop") == 0) { + printk("getprop(0x" FMT_prom_uargx ", \"%s\", 0x" FMT_prom_uargx ", " FMT_prom_arg ") = ", + pb->args[0], arg2pointer(pb->args[1]), + pb->args[2], pb->args[3]); + } else if (strcmp(service, "nextprop") == 0) { + printk("nextprop(0x" FMT_prom_uargx ", \"%s\", 0x" FMT_prom_uargx ") = ", + pb->args[0], arg2pointer(pb->args[1]), pb->args[2]); + } else if (strcmp(service, "setprop") == 0) { + printk("setprop(0x" FMT_prom_uargx ", \"%s\", 0x" FMT_prom_uargx ", " FMT_prom_arg ")\n", + pb->args[0], arg2pointer(pb->args[1]), + pb->args[2], pb->args[3]); + memdump(arg2pointer(pb->args[2]), pb->args[3]); + printk(" = "); + } else if (strcmp(service, "canon") == 0) { + printk("canon(\"%s\", 0x" FMT_prom_uargx ", " FMT_prom_arg ")\n", + arg2pointer(pb->args[0]), pb->args[1], pb->args[2]); + } else if (strcmp(service, "finddevice") == 0) { + printk("finddevice(\"%s\") = ", arg2pointer(pb->args[0])); + } else if (strcmp(service, "instance-to-path") == 0) { + printk("instance-to-path(0x" FMT_prom_uargx ", 0x" FMT_prom_uargx ", " FMT_prom_arg ") = ", + pb->args[0], pb->args[1], pb->args[2]); + } else if (strcmp(service, "package-to-path") == 0) { + printk("package-to-path(0x" FMT_prom_uargx ", 0x" FMT_prom_uargx ", " FMT_prom_arg ") = ", + pb->args[0], pb->args[1], pb->args[2]); + } else if (strcmp(service, "open") == 0) { + printk("open(\"%s\") = ", arg2pointer(pb->args[0])); + } else if (strcmp(service, "close") == 0) { + printk("close(0x" FMT_prom_uargx ")\n", pb->args[0]); + } else if (strcmp(service, "read") == 0) { +#ifdef DUMP_IO + printk("read(0x" FMT_prom_uargx ", 0x" FMT_prom_uargx ", " FMT_prom_arg ") = ", + pb->args[0], pb->args[1], pb->args[2]); +#endif + } else if (strcmp(service, "write") == 0) { +#ifdef DUMP_IO + printk("write(0x" FMT_prom_uargx ", 0x" FMT_prom_uargx ", " FMT_prom_arg ")\n", + pb->args[0], pb->args[1], pb->args[2]); + memdump(arg2pointer(pb->args[1]), pb->args[2]); + printk(" = "); +#endif + } else if (strcmp(service, "seek") == 0) { +#ifdef DUMP_IO + printk("seek(0x" FMT_prom_uargx ", 0x" FMT_prom_uargx ", 0x" FMT_prom_uargx ") = ", + pb->args[0], pb->args[1], pb->args[2]); +#endif + } else if (strcmp(service, "claim") == 0) { + printk("claim(0x" FMT_prom_uargx ", " FMT_prom_arg ", " FMT_prom_arg ") = ", + pb->args[0], pb->args[1], pb->args[2]); + } else if (strcmp(service, "release") == 0) { + printk("release(0x" FMT_prom_uargx ", " FMT_prom_arg ")\n", + pb->args[0], pb->args[1]); + } else if (strcmp(service, "boot") == 0) { + printk("boot \"%s\"\n", arg2pointer(pb->args[0])); + } else if (strcmp(service, "enter") == 0) { + printk("enter()\n"); + } else if (strcmp(service, "exit") == 0) { + printk("exit()\n"); + } else if (strcmp(service, "test-method") == 0) { + printk("test-method(0x" FMT_prom_uargx ", \"%s\") = ", + pb->args[0], arg2pointer(pb->args[1])); + } else { + printk("of_client_interface: %s", service); + for( i = 0; i < pb->nargs; i++ ) + printk(" " FMT_prom_uargx, pb->args[i]); + printk("\n"); + } +} + +static void dump_return(prom_args_t *pb) +{ + int i; + const char *service = get_service(pb); + if (strcmp(service, "test") == 0) { + printk(FMT_prom_arg "\n", pb->args[pb->nargs]); + } else if (strcmp(service, "peer") == 0) { + printk("0x" FMT_prom_uargx "\n", pb->args[pb->nargs]); + } else if (strcmp(service, "child") == 0) { + printk("0x" FMT_prom_uargx "\n", pb->args[pb->nargs]); + } else if (strcmp(service, "parent") == 0) { + printk("0x" FMT_prom_uargx "\n", pb->args[pb->nargs]); + } else if (strcmp(service, "instance-to-package") == 0) { + printk("0x" FMT_prom_uargx "\n", pb->args[pb->nargs]); + } else if (strcmp(service, "getproplen") == 0) { + printk("0x" FMT_prom_uargx "\n", pb->args[pb->nargs]); + } else if (strcmp(service, "getprop") == 0) { + printk(FMT_prom_arg "\n", pb->args[pb->nargs]); + if ((prom_arg_t)pb->args[pb->nargs] != -1) + memdump(arg2pointer(pb->args[2]), MIN(pb->args[3], pb->args[pb->nargs])); + } else if (strcmp(service, "nextprop") == 0) { + printk(FMT_prom_arg "\n", pb->args[pb->nargs]); + memdump(arg2pointer(pb->args[2]), 32); + } else if (strcmp(service, "setprop") == 0) { + printk(FMT_prom_arg "\n", pb->args[pb->nargs]); + } else if (strcmp(service, "canon") == 0) { + printk(FMT_prom_arg "\n", pb->args[pb->nargs]); + memdump(arg2pointer(pb->args[1]), pb->args[pb->nargs]); + } else if (strcmp(service, "finddevice") == 0) { + printk("0x" FMT_prom_uargx "\n", pb->args[pb->nargs]); + } else if (strcmp(service, "instance-to-path") == 0) { + printk(FMT_prom_arg "\n", pb->args[pb->nargs]); + memdump(arg2pointer(pb->args[1]), pb->args[pb->nargs]); + } else if (strcmp(service, "package-to-path") == 0) { + printk(FMT_prom_arg "\n", pb->args[pb->nargs]); + memdump(arg2pointer(pb->args[1]), pb->args[pb->nargs]); + } else if (strcmp(service, "open") == 0) { + printk("0x" FMT_prom_uargx "\n", pb->args[pb->nargs]); + } else if (strcmp(service, "close") == 0) { + /* do nothing */ + } else if (strcmp(service, "read") == 0) { +#ifdef DUMP_IO + printk(FMT_prom_arg "\n", pb->args[pb->nargs]); + memdump(arg2pointer(pb->args[1]), pb->args[pb->nargs]); +#endif + } else if (strcmp(service, "write") == 0) { +#ifdef DUMP_IO + printk(FMT_prom_arg "\n", pb->args[pb->nargs]); +#endif + } else if (strcmp(service, "seek") == 0) { +#ifdef DUMP_IO + printk(FMT_prom_arg "\n", pb->args[pb->nargs]); +#endif + } else if (strcmp(service, "claim") == 0) { + printk("0x" FMT_prom_uargx "\n", pb->args[pb->nargs]); + } else if (strcmp(service, "release") == 0) { + /* do nothing */ + } else if (strcmp(service, "boot") == 0) { + /* do nothing */ + } else if (strcmp(service, "enter") == 0) { + /* do nothing */ + } else if (strcmp(service, "exit") == 0) { + /* do nothing */ + } else if (strcmp(service, "test-method") == 0) { + printk("0x" FMT_prom_uargx "\n", pb->args[pb->nargs]); + } else { + printk("of_client_interface return:"); + for (i = 0; i < pb->nret; i++) { + printk(" " FMT_prom_uargx, pb->args[pb->nargs + i]); + } + printk("\n"); + } +} +#endif + +/* call-method, interpret */ +static int +handle_calls(prom_args_t *pb) +{ + int i, j, dstacksave; + ucell val; + +#ifdef DEBUG_CIF + printk("%s %s ([" FMT_prom_arg "] -- [" FMT_prom_arg "])\n", + get_service(pb), arg2pointer(pb->args[0]), pb->nargs, pb->nret); +#endif + + dstacksave = dstackcnt; + for (i = pb->nargs - 1; i >= 0; i--) + PUSH(pb->args[i]); + + push_str(get_service(pb)); + fword("client-call-iface"); + + /* Ignore client-call-iface return */ + POP(); + + /* If the catch result is non-zero, restore stack and exit */ + val = POP(); + if (val) { + printk("%s %s failed with error " FMT_ucellx "\n", get_service(pb), arg2pointer(pb->args[0]), val); + dstackcnt = dstacksave; + return 0; + } + + /* Store catch result */ + pb->args[pb->nargs] = val; + + j = dstackcnt; + for (i = 1; i < pb->nret; i++, j--) { + if (dstackcnt > dstacksave) { + pb->args[pb->nargs + i] = POP(); + } + } + +#ifdef DEBUG_CIF + /* useful for debug but not necessarily an error */ + if (j != dstacksave) { + printk("%s '%s': possible argument error (" FMT_prom_arg "--" FMT_prom_arg ") got %d\n", + get_service(pb), arg2pointer(pb->args[0]), + pb->nargs - 2, pb->nret, j - dstacksave); + } + + printk("handle_calls return:"); + for (i = 0; i < pb->nret; i++) { + printk(" " FMT_prom_uargx, pb->args[pb->nargs + i]); + } + printk("\n"); +#endif + + dstackcnt = dstacksave; + return 0; +} + +int +of_client_interface(int *params) +{ + prom_args_t *pb = (prom_args_t*)params; + ucell val; + int i, j, dstacksave; + + if (pb->nargs < 0 || pb->nret < 0 || + pb->nargs + pb->nret > PROM_MAX_ARGS) + return -1; + +#ifdef DEBUG_CIF + dump_service(pb); +#endif + + /* call-method exceptions are special */ + if (!strcmp("call-method", get_service(pb)) || !strcmp("interpret", get_service(pb))) + return handle_calls(pb); + + dstacksave = dstackcnt; + for (i = pb->nargs - 1; i >= 0; i--) + PUSH(pb->args[i]); + + push_str(get_service(pb)); + fword("client-iface"); + + val = POP(); + if (val) { + if (val == -1) { + printk("Unimplemented service %s ([" FMT_prom_arg "] -- [" FMT_prom_arg "])\n", + get_service(pb), pb->nargs, pb->nret); + } else { +#ifdef DEBUG_CIF + printk("Error calling client interface: " FMT_ucellx "\n", val); +#endif + } + + dstackcnt = dstacksave; + return -1; + } + + j = dstackcnt; + for (i = 0; i < pb->nret; i++, j--) { + if (dstackcnt > dstacksave) { + pb->args[pb->nargs + i] = POP(); + } + } + +#ifdef DEBUG_CIF + if (j != dstacksave) { + printk("service %s: possible argument error (%d %d)\n", + get_service(pb), i, j - dstacksave); + + /* Some clients request less parameters than the CIF method + returns, e.g. getprop with OpenSolaris. Hence we drop any + stack parameters on exit after issuing a warning above */ + } + + dump_return(pb); +#endif + + dstackcnt = dstacksave; + return 0; +} |