--- trunk/src/promemul/of.c 2007/10/08 16:18:51 14 +++ trunk/src/promemul/of.c 2007/10/08 16:19:37 22 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 Anders Gavare. All rights reserved. + * Copyright (C) 2005-2006 Anders Gavare. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,12 +25,19 @@ * SUCH DAMAGE. * * - * $Id: of.c,v 1.3 2005/09/01 13:27:13 debug Exp $ + * $Id: of.c,v 1.18 2006/02/16 05:57:10 debug Exp $ * * OpenFirmware emulation. * - * Development notes: Care must be taken so that the code in here works - * with (at least) POWER/PowerPC, ARM, and SPARC. + * NOTE: OpenFirmware is used on quite a variety of different hardware archs, + * at least POWER/PowerPC, ARM, and SPARC, so the code in this module + * must always remain architecture agnostic. + * + * NOTE: Some things, e.g. 32-bit integers as returned by the "getprop" + * service, are always fixed to big-endian. (According to the standard.) + * + * TODO: o) 64-bit OpenFirmware? + * o) More devices... */ #include @@ -38,26 +45,23 @@ #include #include +#define OF_C + #include "console.h" #include "cpu.h" +#include "device.h" +#include "devices.h" +#include "diskimage.h" #include "machine.h" #include "memory.h" #include "misc.h" +#include "of.h" -#define N_MAX_ARGS 10 -#define ARG_MAX_LEN 4096 +/* #define debug fatal */ extern int quiet_mode; - -/* TODO: IMPORTANT! Change this into something else, to allow multiple - opens of the same device: */ -#define HANDLE_STDIN 0 -#define HANDLE_STDOUT 1 -#define HANDLE_STDERR 2 -#define HANDLE_MMU 3 -#define HANDLE_MEMORY 4 -#define HANDLE_CHOSEN 5 +extern int verbose; /* @@ -86,18 +90,1000 @@ /* + * of_store_32bit_in_host(): + * + * Store big-endian. OpenFirmware properties returned by getprop etc are + * always big-endian, even on little-endian machines. + */ +static void of_store_32bit_in_host(unsigned char *d, uint32_t x) +{ + d[0] = x >> 24; d[1] = x >> 16; + d[2] = x >> 8; d[3] = x; +} + + +/* + * find_device_handle(): + * + * name may consist of multiple names, separaed with slashes. + */ +static int find_device_handle(struct of_data *ofd, char *name) +{ + int handle = 1, cur_parent = 1; + + if (name[0] == 0) + return 0; + + for (;;) { + struct of_device *od = ofd->of_devices; + char tmp[200]; + int i; + + while (name[0] == '/') + name++; + if (name[0] == '\0') + break; + snprintf(tmp, sizeof(tmp), "%s", name); + i = 0; + while (tmp[i] != '\0' && tmp[i] != '/') + i++; + tmp[i] = '\0'; + + OF_FIND(od, strcmp(od->name, tmp) == 0 && + od->parent == cur_parent); + if (od == NULL) + return -1; + + handle = cur_parent = od->handle; + name += strlen(tmp); + } + + return handle; +} + + +/*****************************************************************************/ + + +OF_SERVICE(call_method_2_2) +{ + fatal("[ of: call_method_2_2('%s'): TODO ]\n", arg[0]); + return -1; +} + + +OF_SERVICE(call_method_3_4) +{ + fatal("[ of: call_method_3_4('%s'): TODO ]\n", arg[0]); + return -1; +} + + +OF_SERVICE(call_method_5_2) +{ + if (strcmp(arg[0], "set-colors") == 0) { + /* Used by OpenBSD/macppc: */ + struct vfb_data *v = cpu->machine->of_data->vfb_data; + int color = OF_GET_ARG(3); + uint64_t ptr = OF_GET_ARG(4); + unsigned char rgb[3]; + cpu->memory_rw(cpu, cpu->mem, ptr, rgb, 3, MEM_READ, + CACHE_DATA | NO_EXCEPTIONS); + if (v != NULL) { + memcpy(v->rgb_palette + 3 * color, rgb, 3); + v->update_x1 = v->update_y1 = 0; + v->update_x2 = v->xsize - 1; + v->update_y2 = v->ysize - 1; + } + } else { + fatal("[ of: call_method_5_2('%s'): TODO ]\n", arg[0]); + return -1; + } + return 0; +} + + +OF_SERVICE(call_method_6_1) +{ + fatal("[ of: call_method_6_1('%s'): TODO ]\n", arg[0]); + return -1; +} + + +OF_SERVICE(call_method_6_2) +{ + fatal("[ of: call_method_6_2('%s'): TODO ]\n", arg[0]); + return -1; +} + + +OF_SERVICE(child) +{ + struct of_device *od = cpu->machine->of_data->of_devices; + int handle = OF_GET_ARG(0); + OF_FIND(od, od->parent == handle); + store_32bit_word(cpu, base + retofs, od == NULL? 0 : od->handle); + return 0; +} + + +OF_SERVICE(exit) +{ + cpu->running = 0; + return 0; +} + + +OF_SERVICE(finddevice) +{ + int h = find_device_handle(cpu->machine->of_data, arg[0]); + store_32bit_word(cpu, base + retofs, h); + return h>0? 0 : -1; +} + + +OF_SERVICE(getprop) +{ + struct of_device *od = cpu->machine->of_data->of_devices; + struct of_device_property *pr; + int handle = OF_GET_ARG(0), i, len_returned = 0; + uint64_t buf = OF_GET_ARG(2); + uint64_t max = OF_GET_ARG(3); + + OF_FIND(od, od->handle == handle); + if (od == NULL) { + fatal("[ of: WARNING: getprop handle=%i; no such handle ]\n", + handle); + return -1; + } + + pr = od->properties; + OF_FIND(pr, strcmp(pr->name, arg[1]) == 0); + if (pr == NULL) { + fatal("[ of: WARNING: getprop: no property '%s' at handle" + " %i (device '%s') ]\n", arg[1], handle, od->name); + /* exit(1); */ + return -1; + } + + if (pr->data == NULL) { + fatal("[ of: WARNING: property '%s' of '%s' has no data! ]\n", + arg[1], od->name); + goto ret; + } + + /* Return the property into emulated RAM: */ + len_returned = pr->len <= max? pr->len : max; + + for (i=0; imemory_rw(cpu, cpu->mem, buf + i, pr->data + i, + 1, MEM_WRITE, CACHE_DATA | NO_EXCEPTIONS)) { + fatal("[ of: getprop memory_rw() error ]\n"); + exit(1); + } + } + +ret: + store_32bit_word(cpu, base + retofs, len_returned); + return 0; +} + + +OF_SERVICE(getproplen) +{ + struct of_device *od = cpu->machine->of_data->of_devices; + struct of_device_property *pr; + int handle = OF_GET_ARG(0); + + OF_FIND(od, od->handle == handle); + if (od == NULL) { + fatal("[ of: WARNING: getproplen handle=%i; no such handle ]\n", + handle); + exit(1); + /* return -1; */ + } + + pr = od->properties; + OF_FIND(pr, strcmp(pr->name, arg[1]) == 0); + if (pr == NULL) { + fatal("[ of: WARNING: getproplen: no property '%s' at handle" + " %i (device '%s') ]\n", arg[1], handle, od->name); + exit(1); + } + + store_32bit_word(cpu, base + retofs, pr->len); + return 0; +} + + +OF_SERVICE(instance_to_package) +{ + int handle = OF_GET_ARG(0); + /* TODO: actually do something here? :-) */ + store_32bit_word(cpu, base + retofs, handle); + return 0; +} + + +OF_SERVICE(interpret_1) +{ + if (strcmp(arg[0], "#lines 2 - to line#") == 0) { + } else { + fatal("[ of: interpret_1('%s'): TODO ]\n", arg[0]); + return -1; + } + return 0; +} + + +OF_SERVICE(interpret_2) +{ + store_32bit_word(cpu, base + retofs, 0); /* ? TODO */ + if (strcmp(arg[0], "#columns") == 0) { + store_32bit_word(cpu, base + retofs + 4, 80); + } else if (strcmp(arg[0], "#lines") == 0) { + store_32bit_word(cpu, base + retofs + 4, 40); + } else if (strcmp(arg[0], "char-height") == 0) { + store_32bit_word(cpu, base + retofs + 4, 15); + } else if (strcmp(arg[0], "char-width") == 0) { + store_32bit_word(cpu, base + retofs + 4, 10); + } else if (strcmp(arg[0], "line#") == 0) { + store_32bit_word(cpu, base + retofs + 4, 0); + } else if (strcmp(arg[0], "font-adr") == 0) { + store_32bit_word(cpu, base + retofs + 4, 0); + } else { + fatal("[ of: interpret_2('%s'): TODO ]\n", arg[0]); + return -1; + } + return 0; +} + + +OF_SERVICE(package_to_path) +{ + fatal("[ of: package-to-path: TODO ]\n"); + return -1; +} + + +OF_SERVICE(parent) +{ + struct of_device *od = cpu->machine->of_data->of_devices; + int handle = OF_GET_ARG(0); + OF_FIND(od, od->handle == handle); + store_32bit_word(cpu, base + retofs, od == NULL? 0 : od->parent); + return 0; +} + + +OF_SERVICE(peer) +{ + struct of_device *od = cpu->machine->of_data->of_devices; + int handle = OF_GET_ARG(0), parent = 0, peer = 0, seen_self = 1; + + if (handle == 0) { + /* Return the handle of the root node (1): */ + store_32bit_word(cpu, base + retofs, 1); + return 0; + } + + OF_FIND(od, od->handle == handle); + if (od == NULL) { + fatal("[ of: peer(): can't find handle %i ]\n", handle); + exit(1); + } + parent = od->parent; + seen_self = 0; + + od = cpu->machine->of_data->of_devices; + + while (od != NULL) { + if (od->parent == parent) { + if (seen_self) { + peer = od->handle; + break; + } + if (od->handle == handle) + seen_self = 1; + } + od = od->next; + } + store_32bit_word(cpu, base + retofs, peer); + return 0; +} + + +OF_SERVICE(read) +{ + /* int handle = OF_GET_ARG(0); */ + uint64_t ptr = OF_GET_ARG(1); + /* int len = OF_GET_ARG(2); */ + int c; + unsigned char ch; + + /* TODO: check handle! This just reads chars from the console! */ + /* TODO: This is blocking! */ + + c = console_readchar(cpu->machine->main_console_handle); + ch = c; + if (!cpu->memory_rw(cpu, cpu->mem, ptr, &ch, 1, MEM_WRITE, + CACHE_DATA | NO_EXCEPTIONS)) { + fatal("[ of: read: memory_rw() error ]\n"); + exit(1); + } + + store_32bit_word(cpu, base + retofs, c == -1? 0 : 1); + return c == -1? -1 : 0; +} + + +OF_SERVICE(write) +{ + /* int handle = OF_GET_ARG(0); */ + uint64_t ptr = OF_GET_ARG(1); + int n_written = 0, i, len = OF_GET_ARG(2); + + /* TODO: check handle! This just dumps the data to the console! */ + + for (i=0; imemory_rw(cpu, cpu->mem, ptr + i, &ch, + 1, MEM_READ, CACHE_DATA | NO_EXCEPTIONS)) { + fatal("[ of: write: memory_rw() error ]\n"); + exit(1); + } + if (ch != 7) + console_putchar(cpu->machine->main_console_handle, ch); + n_written ++; + } + + store_32bit_word(cpu, base + retofs, n_written); + return 0; +} + + +/*****************************************************************************/ + + +/* + * of_get_unused_device_handle(): + * + * Returns an unused device handle number (1 or higher). + */ +static int of_get_unused_device_handle(struct of_data *of_data) +{ + int max_handle = 0; + struct of_device *od = of_data->of_devices; + + while (od != NULL) { + if (od->handle > max_handle) + max_handle = od->handle; + od = od->next; + } + + return max_handle + 1; +} + + +/* + * of_add_device(): + * + * Adds a device. + */ +static struct of_device *of_add_device(struct of_data *of_data, char *name, + char *parentname) +{ + struct of_device *od = malloc(sizeof(struct of_device)); + if (od == NULL) + goto bad; + memset(od, 0, sizeof(struct of_device)); + + od->name = strdup(name); + if (od->name == NULL) + goto bad; + + od->handle = of_get_unused_device_handle(of_data); + od->parent = find_device_handle(of_data, parentname); + if (od->parent < 0) { + fatal("of_add_device(): adding '%s' to parent '%s' failed: " + "parent not found!\n", name, parentname); + exit(1); + } + + od->next = of_data->of_devices; + of_data->of_devices = od; + return od; + +bad: + fatal("of_add_device(): out of memory\n"); + exit(1); +} + + +/* + * of_add_prop(): + * + * Adds a property to a device. + */ +static void of_add_prop(struct of_data *of_data, char *devname, + char *propname, unsigned char *data, uint32_t len, int flags) +{ + struct of_device_property *pr = + malloc(sizeof(struct of_device_property)); + struct of_device *od = of_data->of_devices; + int h = find_device_handle(of_data, devname); + + OF_FIND(od, od->handle == h); + if (od == NULL) { + fatal("of_add_prop(): device '%s' not registered\n", devname); + exit(1); + } + + if (pr == NULL) + goto bad; + memset(pr, 0, sizeof(struct of_device_property)); + + pr->name = strdup(propname); + if (pr->name == NULL) + goto bad; + pr->data = data; + pr->len = len; + pr->flags = flags; + + pr->next = od->properties; + od->properties = pr; + return; + +bad: + fatal("of_add_device(): out of memory\n"); + exit(1); +} + + +/* + * of_add_service(): + * + * Adds a service. + */ +static void of_add_service(struct of_data *of_data, char *name, + int (*f)(OF_SERVICE_ARGS), int n_args, int n_ret_args) +{ + struct of_service *os = malloc(sizeof(struct of_service)); + if (os == NULL) + goto bad; + memset(os, 0, sizeof(struct of_service)); + + os->name = strdup(name); + if (os->name == NULL) + goto bad; + + os->f = f; + os->n_args = n_args; + os->n_ret_args = n_ret_args; + + os->next = of_data->of_services; + of_data->of_services = os; + return; + +bad: + fatal("of_add_service(): out of memory\n"); + exit(1); +} + + +/* + * of_dump_devices(): + * + * Debug dump helper. + */ +static void of_dump_devices(struct of_data *ofd, int parent) +{ + int iadd = DEBUG_INDENTATION; + struct of_device *od = ofd->of_devices; + + while (od != NULL) { + struct of_device_property *pr = od->properties; + if (od->parent != parent) { + od = od->next; + continue; + } + debug("\"%s\"\n", od->name, od->handle); + debug_indentation(iadd); + while (pr != NULL) { + debug("(%s: ", pr->name); + if (pr->flags == OF_PROP_STRING) + debug("\"%s\"", pr->data); + else + debug("%i bytes", pr->len); + debug(")\n"); + pr = pr->next; + } + of_dump_devices(ofd, od->handle); + debug_indentation(-iadd); + od = od->next; + } +} + + +/* + * of_dump_all(): + * + * Debug dump. + */ +static void of_dump_all(struct of_data *ofd) +{ + int iadd = DEBUG_INDENTATION; + struct of_service *os; + + debug("openfirmware debug dump:\n"); + debug_indentation(iadd); + + /* Devices: */ + of_dump_devices(ofd, 0); + + /* Services: */ + os = ofd->of_services; + while (os != NULL) { + debug("service '%s'", os->name); + if (os->n_ret_args > 0 || os->n_args > 0) { + debug(" ("); + if (os->n_args > 0) { + debug("%i arg%s", os->n_args, + os->n_args > 1? "s" : ""); + if (os->n_ret_args > 0) + debug(", "); + } + if (os->n_ret_args > 0) + debug("%i return value%s", os->n_ret_args, + os->n_ret_args > 1? "s" : ""); + debug(")"); + } + debug("\n"); + os = os->next; + } + + debug_indentation(-iadd); +} + + +/* + * of_add_prop_int32(): + * + * Helper function. + */ +static void of_add_prop_int32(struct of_data *ofd, + char *devname, char *propname, uint32_t x) +{ + unsigned char *p = malloc(sizeof(int32_t)); + if (p == NULL) { + fatal("of_add_prop_int32(): out of memory\n"); + exit(1); + } + of_store_32bit_in_host(p, x); + of_add_prop(ofd, devname, propname, p, sizeof(int32_t), + OF_PROP_INT); +} + + +/* + * of_add_prop_str(): + * + * Helper function. + */ +static void of_add_prop_str(struct machine *machine, struct of_data *ofd, + char *devname, char *propname, char *data) +{ + char *p = strdup(data); + if (p == NULL) { + fatal("of_add_prop_str(): out of memory\n"); + exit(1); + } + + of_add_prop(ofd, devname, propname, (unsigned char *)p, strlen(p) + 1, + OF_PROP_STRING); +} + + +/* + * of_emul_init_isa(): + */ +void of_emul_init_isa(struct machine *machine) +{ + struct of_data *ofd = machine->of_data; + unsigned char *isa_ranges; + + of_add_device(ofd, "isa", "/"); + isa_ranges = malloc(32); + if (isa_ranges == NULL) + goto bad; + memset(isa_ranges, 0, 32); + /* 2 *: isa_phys_hi, isa_phys_lo, parent_phys_start, size */ + /* MEM space: */ + of_store_32bit_in_host(isa_ranges + 0, 0); + of_store_32bit_in_host(isa_ranges + 4, 0xc0000000); + /* I/O space: low bit if isa_phys_hi set */ + of_store_32bit_in_host(isa_ranges + 16, 1); + of_store_32bit_in_host(isa_ranges + 20, 0xd0000000); + + of_add_prop(ofd, "/isa", "ranges", isa_ranges, 32, 0); + + return; + +bad: + fatal("of_emul_init_isa(): out of memory\n"); + exit(1); +} + + +/* + * of_emul_init_adb(): + */ +void of_emul_init_adb(struct machine *machine) +{ + struct of_data *ofd = machine->of_data; + unsigned char *adb_interrupts, *adb_reg; + + adb_interrupts = malloc(4 * sizeof(uint32_t)); + adb_reg = malloc(8 * sizeof(uint32_t)); + if (adb_interrupts == NULL || adb_reg == NULL) + goto bad; + + of_add_device(ofd, "adb", "/bandit/gc"); + of_add_prop_str(machine, ofd, "/bandit/gc/adb", "name", "via-cuda"); + of_store_32bit_in_host(adb_interrupts + 0, 25); + of_store_32bit_in_host(adb_interrupts + 4, 0); + of_store_32bit_in_host(adb_interrupts + 8, 0); + of_store_32bit_in_host(adb_interrupts + 12, 0); + of_add_prop(ofd, "/bandit/gc/adb", "interrupts", adb_interrupts, + 4*sizeof(uint32_t), 0); + of_store_32bit_in_host(adb_reg + 0, 0x16000); + of_store_32bit_in_host(adb_reg + 4, 0x2000); + of_store_32bit_in_host(adb_reg + 8, 0); + of_store_32bit_in_host(adb_reg + 12, 0); + of_store_32bit_in_host(adb_reg + 16, 0); + of_store_32bit_in_host(adb_reg + 20, 0); + of_store_32bit_in_host(adb_reg + 24, 0); + of_store_32bit_in_host(adb_reg + 28, 0); + of_add_prop(ofd, "/bandit/gc/adb", "reg", adb_reg, + 8*sizeof(uint32_t), 0); + + return; + +bad: + fatal("of_emul_init_adb(): out of memory\n"); + exit(1); +} + + +/* + * of_emul_init_zs(): + */ +void of_emul_init_zs(struct machine *machine) +{ + struct of_data *ofd = machine->of_data; + unsigned char *zs_interrupts, *zs_reg; + + zs_reg = malloc(6 * sizeof(uint32_t)); + if (zs_reg == NULL) + goto bad; + + /* The controller: */ + of_add_device(ofd, "zs", "/bandit/gc"); + of_add_prop_str(machine, ofd, "/bandit/gc/zs", "device_type", "serial"); + of_add_prop_str(machine, ofd, "/bandit/gc/zs", "name", "escc"); + of_store_32bit_in_host(zs_reg + 0, 0x13000); + of_store_32bit_in_host(zs_reg + 4, 0x40); + of_store_32bit_in_host(zs_reg + 8, 0x100); + of_store_32bit_in_host(zs_reg + 12, 0x100); + of_store_32bit_in_host(zs_reg + 16, 0x200); + of_store_32bit_in_host(zs_reg + 20, 0x100); + of_add_prop(ofd, "/bandit/gc/zs", "reg", zs_reg, 6*sizeof(uint32_t), 0); + + /* Port 1: */ + zs_interrupts = malloc(3 * sizeof(uint32_t)); + zs_reg = malloc(6 * sizeof(uint32_t)); + if (zs_interrupts == NULL || zs_reg == NULL) + goto bad; + + of_add_device(ofd, "zstty1", "/bandit/gc/zs"); + of_add_prop_str(machine, ofd, "/bandit/gc/zs/zstty1", "name", "ch-a"); + of_store_32bit_in_host(zs_interrupts + 0, 16); + of_store_32bit_in_host(zs_interrupts + 4, 0); + of_store_32bit_in_host(zs_interrupts + 8, 0); + of_add_prop(ofd, "/bandit/gc/zs/zstty1", "interrupts", zs_interrupts, + 3*sizeof(uint32_t), 0); + of_store_32bit_in_host(zs_reg + 0, 0x13800); + of_store_32bit_in_host(zs_reg + 4, 0x100); + of_store_32bit_in_host(zs_reg + 8, 0x100); + of_store_32bit_in_host(zs_reg + 12, 0x100); + of_store_32bit_in_host(zs_reg + 16, 0x200); + of_store_32bit_in_host(zs_reg + 20, 0x100); + of_add_prop(ofd, "/bandit/gc/zs/zstty1", + "reg", zs_reg, 6*sizeof(uint32_t), 0); + + /* Port 0: */ + zs_interrupts = malloc(3 * sizeof(uint32_t)); + zs_reg = malloc(6 * sizeof(uint32_t)); + if (zs_interrupts == NULL || zs_reg == NULL) + goto bad; + + of_add_device(ofd, "zstty0", "/bandit/gc/zs"); + of_add_prop_str(machine, ofd, "/bandit/gc/zs/zstty0", "name", "ch-b"); + of_store_32bit_in_host(zs_interrupts + 0, 15); + of_store_32bit_in_host(zs_interrupts + 4, 0); + of_store_32bit_in_host(zs_interrupts + 8, 0); + of_add_prop(ofd, "/bandit/gc/zs/zstty0", "interrupts", zs_interrupts, + 3*sizeof(uint32_t), 0); + of_store_32bit_in_host(zs_reg + 0, 0x13400); + of_store_32bit_in_host(zs_reg + 4, 0x100); + of_store_32bit_in_host(zs_reg + 8, 0x100); + of_store_32bit_in_host(zs_reg + 12, 0x100); + of_store_32bit_in_host(zs_reg + 16, 0x200); + of_store_32bit_in_host(zs_reg + 20, 0x100); + of_add_prop(ofd, "/bandit/gc/zs/zstty0", + "reg", zs_reg, 6*sizeof(uint32_t), 0); + + return; + +bad: + fatal("of_emul_init_zs(): out of memory\n"); + exit(1); +} + + +/* + * of_emul_init_uninorth(): + */ +void of_emul_init_uninorth(struct machine *machine) +{ + struct of_data *ofd = machine->of_data; + unsigned char *uninorth_reg, *uninorth_bus_range, *uninorth_ranges; + unsigned char *macio_aa, *ata_interrupts, *ata_reg; + struct of_device *ic; + char *n = "pci@e2000000"; + char *macio = "mac-io"; + + of_add_device(ofd, n, "/"); + of_add_prop_str(machine, ofd, n, "name", "pci"); + of_add_prop_str(machine, ofd, n, "device_type", "pci"); + of_add_prop_str(machine, ofd, n, "compatible", "uni-north"); + + uninorth_reg = malloc(2 * sizeof(uint32_t)); + uninorth_bus_range = malloc(2 * sizeof(uint32_t)); + uninorth_ranges = malloc(12 * sizeof(uint32_t)); + macio_aa = malloc(5 * sizeof(uint32_t)); + ata_interrupts = malloc(6 * sizeof(uint32_t)); + ata_reg = malloc(8 * sizeof(uint32_t)); + if (uninorth_ranges == NULL || uninorth_bus_range == NULL || + uninorth_reg == NULL || macio_aa == NULL || + ata_interrupts == NULL || ata_reg == NULL) + goto bad; + + of_store_32bit_in_host(uninorth_reg + 0, 0xe2000000); + of_store_32bit_in_host(uninorth_reg + 4, 0); /* not used? */ + of_add_prop(ofd, n, "reg", uninorth_reg, 2*sizeof(uint32_t), 0); + + of_store_32bit_in_host(uninorth_bus_range + 0, 0); + of_store_32bit_in_host(uninorth_bus_range + 4, 0); + of_add_prop(ofd, n, "bus-range", uninorth_bus_range, + 2*sizeof(uint32_t), 0); + + /* MEM: */ + of_store_32bit_in_host(uninorth_ranges + 0, 0x02000000); + of_store_32bit_in_host(uninorth_ranges + 4, 0); + of_store_32bit_in_host(uninorth_ranges + 8, 0); + of_store_32bit_in_host(uninorth_ranges + 12, 0xd0000000); + of_store_32bit_in_host(uninorth_ranges + 16, 0); + of_store_32bit_in_host(uninorth_ranges + 20, 0x04000000); + /* IO: */ + of_store_32bit_in_host(uninorth_ranges + 24, 0x01000000); + of_store_32bit_in_host(uninorth_ranges + 28, 0); + of_store_32bit_in_host(uninorth_ranges + 32, 0); + of_store_32bit_in_host(uninorth_ranges + 36, 0xe2000000); + of_store_32bit_in_host(uninorth_ranges + 40, 0); + of_store_32bit_in_host(uninorth_ranges + 44, 0x01000000); + of_add_prop(ofd, n, "ranges", uninorth_ranges, + 12*sizeof(uint32_t), 0); + + ic = of_add_device(ofd, macio, "/"); + memset(macio_aa, 0, 20); + of_store_32bit_in_host(macio_aa + 0, 15 << 11); /* pci tag */ + of_store_32bit_in_host(macio_aa + 8, 0xf3000000); + of_add_prop(ofd, macio, "assigned-addresses", macio_aa, + 5*sizeof(uint32_t), 0); +/* of_add_prop(ofd, n, "assigned-addresses", macio_aa, + 5*sizeof(uint32_t), 0); */ + of_add_prop_int32(ofd, "/chosen", "interrupt-controller", ic->handle); + + of_add_device(ofd, "bandit", "/"); + of_add_device(ofd, "gc", "/bandit"); + of_add_prop(ofd, "/bandit/gc", "assigned-addresses", macio_aa, + 5*sizeof(uint32_t), 0); + + if (diskimage_exist(machine, 0, DISKIMAGE_IDE) || + diskimage_exist(machine, 1, DISKIMAGE_IDE)) { + of_add_device(ofd, "ata", "/bandit/gc"); + of_add_prop_str(machine, ofd, "/bandit/gc/ata", "name", "ata"); + of_add_prop_str(machine, ofd, "/bandit/gc/ata", "compatible", + "heathrow-ata"); + of_store_32bit_in_host(ata_interrupts + 0, 13); + of_store_32bit_in_host(ata_interrupts + 4, 0); + of_store_32bit_in_host(ata_interrupts + 8, 0); + of_store_32bit_in_host(ata_interrupts + 12, 0); + of_store_32bit_in_host(ata_interrupts + 16, 0); + of_store_32bit_in_host(ata_interrupts + 20, 0); + of_add_prop(ofd, "/bandit/gc/ata", "interrupts", ata_interrupts, + 6*sizeof(uint32_t), 0); + of_store_32bit_in_host(ata_reg + 0, 0x20000); + of_store_32bit_in_host(ata_reg + 4, 0); + of_store_32bit_in_host(ata_reg + 8, 0x21000); + of_store_32bit_in_host(ata_reg + 12, 0x22000); + of_store_32bit_in_host(ata_reg + 16, 0); + of_store_32bit_in_host(ata_reg + 20, 0); + of_store_32bit_in_host(ata_reg + 24, 0); + of_store_32bit_in_host(ata_reg + 28, 0); + of_add_prop(ofd, "/bandit/gc/ata", "reg", ata_reg, + 8*sizeof(uint32_t), 0); + device_add(machine, "wdc addr=0xf3020000 irq=21 " + "addr_mult=0x10"); + } + + return; + +bad: + fatal("of_emul_init_uninorth(): out of memory\n"); + exit(1); +} + + +/* + * of_emul_init(): + * + * This function creates an OpenFirmware emulation instance. + */ +struct of_data *of_emul_init(struct machine *machine, struct vfb_data *vfb_data, + uint64_t fb_addr, int fb_xsize, int fb_ysize) +{ + unsigned char *memory_reg, *memory_av; + unsigned char *zs_assigned_addresses; + struct of_device *mmu, *devstdout, *devstdin; + struct of_data *ofd = malloc(sizeof(struct of_data)); + int i; + + if (ofd == NULL) + goto bad; + memset(ofd, 0, sizeof(struct of_data)); + + ofd->vfb_data = vfb_data; + + /* Devices: */ + + /* Root = device 1 */ + of_add_device(ofd, "", ""); + + of_add_device(ofd, "io", "/"); + devstdin = of_add_device(ofd, "stdin", "/io"); + devstdout = of_add_device(ofd, "stdout", "/io"); + + if (machine->use_x11) { + fatal("!\n! TODO: keyboard + framebuffer for MacPPC\n!\n"); + + of_add_prop_str(machine, ofd, "/io/stdin", "name", + "keyboard"); + of_add_prop_str(machine, ofd, "/io", "name", "adb"); + + of_add_prop_str(machine, ofd, "/io/stdout", "device_type", + "display"); + of_add_prop_int32(ofd, "/io/stdout", "width", fb_xsize); + of_add_prop_int32(ofd, "/io/stdout", "height", fb_ysize); + of_add_prop_int32(ofd, "/io/stdout", "linebytes", fb_xsize * 1); + of_add_prop_int32(ofd, "/io/stdout", "depth", 8); + of_add_prop_int32(ofd, "/io/stdout", "address", fb_addr); + } else { + zs_assigned_addresses = malloc(12); + if (zs_assigned_addresses == NULL) + goto bad; + memset(zs_assigned_addresses, 0, 12); + of_add_prop_str(machine, ofd, "/io/stdin", "name", "ch-b"); + of_add_prop_str(machine, ofd, "/io/stdin", "device_type", + "serial"); + of_add_prop_int32(ofd, "/io/stdin", "reg", 0xf3013000); + of_add_prop(ofd, "/io/stdin", "assigned-addresses", + zs_assigned_addresses, 12, 0); + + of_add_prop_str(machine, ofd, "/io/stdout", "device_type", + "serial"); + } + + of_add_device(ofd, "cpus", "/"); + for (i=0; incpus; i++) { + char tmp[50]; + snprintf(tmp, sizeof(tmp), "@%x", i); + of_add_device(ofd, tmp, "/cpus"); + snprintf(tmp, sizeof(tmp), "/cpus/@%x", i); + of_add_prop_str(machine, ofd, tmp, "device_type", "cpu"); + of_add_prop_int32(ofd, tmp, "timebase-frequency", + machine->emulated_hz / 4); + of_add_prop_int32(ofd, tmp, "clock-frequency", + machine->emulated_hz); + of_add_prop_int32(ofd, tmp, "reg", i); + } + + mmu = of_add_device(ofd, "mmu", "/"); + + /* TODO: */ + of_add_prop(ofd, "/mmu", "translations", NULL, 0, 0); + + of_add_device(ofd, "chosen", "/"); + of_add_prop_int32(ofd, "/chosen", "mmu", mmu->handle); + of_add_prop_int32(ofd, "/chosen", "stdin", devstdin->handle); + of_add_prop_int32(ofd, "/chosen", "stdout", devstdout->handle); + + of_add_device(ofd, "memory", "/"); + memory_reg = malloc(2 * sizeof(uint32_t)); + memory_av = malloc(2 * sizeof(uint32_t)); + if (memory_reg == NULL || memory_av == NULL) + goto bad; + of_store_32bit_in_host(memory_reg + 0, 0); + of_store_32bit_in_host(memory_reg + 4, machine->physical_ram_in_mb<<20); + of_store_32bit_in_host(memory_av + 0, 10 << 20); + of_store_32bit_in_host(memory_av + 4, + (machine->physical_ram_in_mb - 10) << 20); + of_add_prop(ofd, "/memory", "reg", memory_reg, 2 * sizeof(uint32_t), 0); + of_add_prop(ofd, "/memory", "available",memory_av,2*sizeof(uint32_t),0); + of_add_prop_str(machine, ofd, "/memory","device_type","memory"/*?*/); + + /* Services: */ + of_add_service(ofd, "call-method", of__call_method_2_2, 2, 2); + of_add_service(ofd, "call-method", of__call_method_3_4, 3, 4); + of_add_service(ofd, "call-method", of__call_method_5_2, 5, 2); + of_add_service(ofd, "call-method", of__call_method_6_1, 6, 1); + of_add_service(ofd, "call-method", of__call_method_6_2, 6, 2); + of_add_service(ofd, "child", of__child, 1, 1); + of_add_service(ofd, "exit", of__exit, 0, 0); + of_add_service(ofd, "finddevice", of__finddevice, 1, 1); + of_add_service(ofd, "getprop", of__getprop, 4, 1); + of_add_service(ofd, "getproplen", of__getproplen, 2, 1); + of_add_service(ofd, "instance-to-package", + of__instance_to_package, 1, 1); + of_add_service(ofd, "interpret", of__interpret_1, 1, 1); + of_add_service(ofd, "interpret", of__interpret_2, 1, 2); + of_add_service(ofd, "package-to-path", of__package_to_path, 3, 1); + of_add_service(ofd, "parent", of__parent, 1, 1); + of_add_service(ofd, "peer", of__peer, 1, 1); + of_add_service(ofd, "read", of__read, 3, 1); + of_add_service(ofd, "write", of__write, 3, 1); + + if (verbose >= 2) + of_dump_all(ofd); + + machine->of_data = ofd; + return ofd; + +bad: + fatal("of_emul_init(): out of memory\n"); + exit(1); +} + + +/* * of_emul(): * * OpenFirmware call emulation. */ int of_emul(struct cpu *cpu) { - int i, nargs, nret, ofs, handle, retval; + int i, nargs, nret, ofs, retval = 0; char service[50]; - char arg[N_MAX_ARGS][ARG_MAX_LEN]; - char tmpstr[ARG_MAX_LEN]; + char *arg[OF_N_MAX_ARGS]; uint64_t base, ptr; - uint64_t buf, buflen; + struct of_service *os; + struct of_data *of_data = cpu->machine->of_data; + + if (of_data == NULL) { + fatal("of_emul(): no of_data struct?\n"); + exit(1); + } /* * The first argument register points to "prom_args": @@ -115,8 +1101,7 @@ case ARCH_PPC: base = cpu->cd.ppc.gpr[3]; break; - default: - fatal("of_emul(): TODO: unimplemented arch\n"); + default:fatal("of_emul(): unimplemented arch (TODO)\n"); exit(1); } @@ -130,20 +1115,27 @@ debug("[ of: %s(", service); ofs = 12; for (i=0; i 0) debug(", "); - if (i >= N_MAX_ARGS) { + if (i >= OF_N_MAX_ARGS) { fatal("TOO MANY ARGS!"); continue; } ptr = load_32bit_word(cpu, base + ofs); - readstr(cpu, ptr, arg[i], ARG_MAX_LEN); - if (arg[i][0]) - debug("\"%s\"", arg[i]); - else { - int x = ptr; - if (x > -256 && x < 256) - debug("%i", x); + arg[i] = malloc(OF_ARG_MAX_LEN + 1); + if (arg[i] == NULL) { + fatal("out of memory\n"); + exit(1); + } + memset(arg[i], 0, OF_ARG_MAX_LEN + 1); + x = ptr; + if (x > -256 && x < 256) { + debug("%i", x); + } else { + readstr(cpu, ptr, arg[i], OF_ARG_MAX_LEN); + if (arg[i][0]) + debug("\"%s\"", arg[i]); else debug("0x%x", x); } @@ -151,106 +1143,32 @@ } debug(") ]\n"); - /* Return value: */ - retval = 0; - /* Note: base + ofs points to the first return slot. */ - if (strcmp(service, "exit") == 0) { - cpu->running = 0; - } else if (strcmp(service, "finddevice") == 0) { - /* Return a handle in ret[0]: */ - if (nret < 1) { - fatal("[ of: finddevice(\"%s\"): nret < 1! ]\n", - arg[0]); - } else if (strcmp(arg[0], "/memory") == 0) { - store_32bit_word(cpu, base + ofs, HANDLE_MEMORY); - } else if (strcmp(arg[0], "/chosen") == 0) { - store_32bit_word(cpu, base + ofs, HANDLE_CHOSEN); - } else { - /* Device not found. */ - fatal("[ of: finddevice(\"%s\"): not yet" - " implemented ]\n", arg[0]); - retval = -1; - } - } else if (strcmp(service, "getprop") == 0) { - handle = load_32bit_word(cpu, base + 12 + 4*0); - ptr = load_32bit_word(cpu, base + 12 + 4*1); - buf = load_32bit_word(cpu, base + 12 + 4*2); - buflen = load_32bit_word(cpu, base + 12 + 4*3); - readstr(cpu, ptr, tmpstr, sizeof(tmpstr)); - - /* TODO: rewrite this */ - switch (handle) { - case HANDLE_MEMORY: - if (strcmp(tmpstr, "available") == 0) { - store_32bit_word(cpu, base + ofs, 2*8); - /* TODO. {start, size} */ - store_32bit_word(cpu, buf, 0); - store_32bit_word(cpu, buf+4, - cpu->machine->physical_ram_in_mb * 1048576 - - 65536); - store_32bit_word(cpu, buf+8, 0); - store_32bit_word(cpu, buf+12, 0); - } else if (strcmp(tmpstr, "reg") == 0) { - /* TODO */ - store_32bit_word(cpu, base + ofs, 33*8); - store_32bit_word(cpu, buf, 0); - store_32bit_word(cpu, buf+4, - cpu->machine->physical_ram_in_mb * 1048576); - store_32bit_word(cpu, buf+8, 0); - store_32bit_word(cpu, buf+12, 0); - } else { - fatal("[ of: getprop(%i,\"%s\"): not yet" - " implemented ]\n", (int)handle, arg[1]); - retval = -1; - } + os = of_data->of_services; + while (os != NULL) { + if (strcmp(service, os->name) == 0 && + nargs == os->n_args && nret == os->n_ret_args) { + retval = os->f(cpu, arg, base, ofs); break; - case HANDLE_CHOSEN: - if (strcmp(tmpstr, "stdin") == 0) { - if (buflen >= 4) - store_32bit_word(cpu, buf, - HANDLE_STDIN); - store_32bit_word(cpu, base + ofs, 4); - } else if (strcmp(tmpstr, "stdout") == 0) { - if (buflen >= 4) - store_32bit_word(cpu, buf, - HANDLE_STDOUT); - store_32bit_word(cpu, base + ofs, 4); - } else if (strcmp(tmpstr, "mmu") == 0) { - if (buflen >= 4) - store_32bit_word(cpu, buf, - HANDLE_MMU); - store_32bit_word(cpu, base + ofs, 4); - } else { - fatal("[ of: getprop(%i,\"%s\"): not yet" - " implemented ]\n", (int)handle, arg[1]); - retval = -1; - } - break; - default: - fatal("[ of: getprop(%i,\"%s\"): not yet" - " implemented ]\n", (int)handle, arg[1]); - retval = -1; - } - } else if (strcmp(service, "instance-to-package") == 0) { - /* TODO: a package handle */ - store_32bit_word(cpu, base + ofs, 1000); - } else if (strcmp(service, "getproplen") == 0) { - /* TODO */ - store_32bit_word(cpu, base + ofs, 0); - } else if (strcmp(service, "peer") == 0) { - /* TODO */ - store_32bit_word(cpu, base + ofs, 0); - } else { + } + os = os->next; + } + + if (os == NULL) { quiet_mode = 0; cpu_register_dump(cpu->machine, cpu, 1, 0); printf("\n"); - fatal("[ of_emul(): unimplemented service \"%s\" ]\n", service); + fatal("[ of: unimplemented service \"%s\" with %i input " + "args and %i output values ]\n", service, nargs, nret); cpu->running = 0; cpu->dead = 1; } + for (i=0; imachine->arch) { case ARCH_ARM: cpu->cd.arm.r[0] = retval; @@ -258,8 +1176,7 @@ case ARCH_PPC: cpu->cd.ppc.gpr[3] = retval; break; - default: - fatal("of_emul(): TODO: unimplemented arch (Retval)\n"); + default:fatal("of_emul(): TODO: unimplemented arch (Retval)\n"); exit(1); }