summaryrefslogtreecommitdiffstats
path: root/private/ntos/boot/veneer/vrmain.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/boot/veneer/vrmain.c')
-rw-r--r--private/ntos/boot/veneer/vrmain.c1314
1 files changed, 1314 insertions, 0 deletions
diff --git a/private/ntos/boot/veneer/vrmain.c b/private/ntos/boot/veneer/vrmain.c
new file mode 100644
index 000000000..a90970420
--- /dev/null
+++ b/private/ntos/boot/veneer/vrmain.c
@@ -0,0 +1,1314 @@
+/*
+ * Copyright (c) 1994, 1996 FirePower Systems, Inc.
+ * Copyright 1994 FirmWorks, Mountain View CA USA. All rights reserved.
+ *
+ * $RCSfile: vrmain.c $
+ * $Revision: 1.41 $
+ * $Date: 1996/06/25 03:02:44 $
+ * $Locker: $
+ *
+ *
+ *
+ *
+ *
+ * HISTORY
+ * 09-21-94 Shin Iwamoto at FirePower Systems Inc.
+ * Added some information in the system parameter block,
+ * such as signature.
+ * 07-21-94 Shin Iwamoto at FirePower Systems Inc.
+ * Added calling VrEnvInitialize() and VrMemoryInitialize()
+ * in VrInitSystem().
+ * 07-20-94 Shin Iwamoto at FirePower Systems Inc.
+ * Moved here from VrInitSystem() and VrNotYet() originally
+ * in vrconfig.c.
+ *
+ */
+
+
+#include "veneer.h"
+
+
+int VrDebug = 0;
+BOOLEAN use_bat_mapping;
+
+/*
+ * Bootdev is either specified in the command line or if not is the device
+ * whence you booted.
+ * See create_argv() below for the magic used to fill in XYZZY.
+ */
+char *Bootpath = 0;
+char *Osloader = 0;
+char *SystemPath = 0;
+
+#define STR_XYZZY "xyzzy"
+#define STR_OSLOADER "\\os\\winnt\\osloader.exe"
+#define STR_OSLOADFN "\\WINNT"
+#define STR_OSLOADPART STR_XYZZY
+#define STR_LDIDENT "Windows NT 3.5"
+#define STR_FWSEARCH STR_XYZZY
+#define STR_FWTEST STR_XYZZY
+
+#define MAX_ARGC 16
+#define MAX_ENVC 16
+char *VrArgv[MAX_ARGC], *VrEnvp[MAX_ENVC];
+int VrArgc, VrEnvc;
+
+#define NEITHER 0
+#define ARGVONLY 1
+#define ENVONLY 2
+#define BOTH 3
+
+struct argv_tab {
+ char *key;
+ char *val;
+ int which;
+
+} argv_tab[MAX_ARGC] = {
+ { "OsLoader", STR_OSLOADER, BOTH },
+ { "SystemPartition", STR_XYZZY, BOTH },
+ { "OSLoadFilename", STR_OSLOADFN, BOTH },
+ { "OSLoadPartition", STR_OSLOADPART, BOTH },
+ { "OSLoadOptions", "nodebug", BOTH },
+ { "LoadIdentifier", STR_LDIDENT, BOTH },
+ { "AutoLoad", "yes", ENVONLY },
+ { "FWSearchPath", STR_FWSEARCH, ENVONLY },
+ { "LastKnownGood", "False", ENVONLY },
+ { "FWTEST", STR_FWTEST, ENVONLY }
+};
+
+STATIC VOID parse_args(VOID);
+STATIC VOID find_boot_dev(VOID);
+STATIC VOID collect_argv(VOID);
+STATIC VOID create_argv(VOID);
+STATIC VOID update_argv(char *, char *);
+STATIC VOID add_argv(char *, char *);
+STATIC VOID read_ARC_env_vars(VOID);
+STATIC VOID add_envp(char *, char *);
+STATIC VOID VrInitSystem (VOID);
+STATIC VOID VrInitSystemBlock (VOID);
+STATIC VOID VrNotYet(VOID);
+STATIC VOID VrKseg0(VOID);
+STATIC VOID move_amd_to_isa_hack(VOID);
+STATIC VOID move_ide_to_isa_hack(VOID);
+STATIC VOID move_scsi_children_to_ide_hack(VOID);
+STATIC VOID move_multi_to_root(PCONFIGURATION_NODE);
+STATIC CHAR *choose_args( char *, char *, int);
+PCHAR VrCanonicalName( IN PCHAR Variable);
+
+/* LONG claimreal(PVOID, ULONG); */
+STATIC LONG claimphys(PVOID, ULONG, ULONG);
+STATIC LONG map(PVOID, PVOID, ULONG, ULONG);
+STATIC VOID check_mmu_type (VOID);
+extern ULONG VrGetProcRev();
+
+typedef VOID (*VR_NOT_YET_ROUTINE) (VOID);
+
+main(VOID *resid, VOID *entry, int (cif_handler)(long *))
+{
+ ihandle bootih;
+ ULONG FileId;
+ ARC_STATUS res;
+ void (*jump_osloader)(int, char **, char**);
+ extern VOID Salutation();
+
+ Salutation();
+
+ VrInitSystemBlock();
+
+ check_mmu_type();
+
+ read_ARC_env_vars();
+
+ /*
+ * Do something with the arg string.
+ */
+ parse_args();
+
+ //
+ // Set up the "kseg0" translation.
+ //
+ VrKseg0();
+
+ //
+ // Build the device tree.
+ //
+ debug(VRDBG_MAIN, "Building the device tree...\n");
+ walk_obp((phandle) 0,
+ (PCONFIGURATION_NODE) 0,
+ (PCONFIGURATION_NODE) 0,
+ (PCONFIGURATION_NODE) 0
+ );
+
+ //
+ // Move all MultiFunction adapters (usually PCI or ISA,
+ // presumably) to be children of the System Class (i.e., RootNode).
+ // This is because the NT kernel prefers to limit the complexity
+ // of nested bus nodes.
+ //
+
+ debug(VRDBG_MAIN, "main: move all multi nodes to children of root...\n");
+ move_multi_to_root(RootNode);
+
+ debug(VRDBG_MAIN, "main: AMD nodes become children of isa...\n");
+ move_amd_to_isa_hack();
+
+ debug(VRDBG_MAIN, "Done with the device tree.\n");
+ if (VrDebug & VRDBG_DUMP) {
+ dump_tree(RootNode);
+ sleep(10);
+ } else if (VrDebug & VRDBG_CONFIG) {
+ quick_dump_tree(RootNode);
+ sleep(10);
+ }
+
+ //
+ // Build the system parameter block.
+ //
+ debug(VRDBG_MAIN, "main: Build the system parameter block...\n");
+ VrInitSystem();
+
+ //
+ // Determine the boot path and translate it to ARC form.
+ //
+ debug(VRDBG_MAIN, "main: find boot device...\n");
+ find_boot_dev();
+ debug(VRDBG_MAIN, "main: create argument and environment lists...\n");
+ create_argv();
+ warn("Booting from '%s'\n", VrArgv[0]);
+
+ res = VrOpen(VrArgv[0], ArcOpenReadOnly, &FileId);
+ if (res != ESUCCESS) {
+ fatal("VrOpen returned %x\n", res);
+ }
+
+ debug(VRDBG_MAIN, "main: setup boot ihandle, jump_osloader handle...\n");
+ bootih = FileTable[FileId].IHandle;
+ jump_osloader = load_file(bootih);
+ VrClose(FileId);
+ VrFlushAllCaches();
+
+ debug(VRDBG_MAIN, "main: create memory descriptor list...\n");
+ VrCreateMemoryDescriptors();
+ if (VrDebug & VRDBG_MEM) {
+ DisplayMemory();
+ }
+
+ if (VrDebug & VRDBG_HOLDIT) {
+
+ warn("Jumping to 0x%x\n", (char *)jump_osloader);
+ puts("This time for sure!");
+ OFEnter();
+ } else {
+ puts("\233H\233J"); // Clear screen
+ }
+ debug(VRDBG_MAIN, "main: launch OSLOADER!!! ...\n");
+ jump_osloader(VrArgc, VrArgv, VrEnvp);
+ OFExit();
+ return (0);
+}
+
+STATIC VOID
+check_mmu_type(VOID)
+{
+ ihandle ih;
+
+ ih = get_int_prop (OFFinddevice("/chosen"), "cpu");
+ if (ih == -1) {
+ use_bat_mapping = TRUE;
+ } else {
+ use_bat_mapping =
+ (OFGetproplen(OFInstanceToPackage(ih),"603-translation") == -1);
+ }
+}
+
+#define NEXT_TOKEN() if ((bootargs = strctok(NULL, ' ')) == NULL) return;
+
+STATIC VOID
+parse_args(VOID)
+{
+ phandle ph;
+ char *bootargs;
+ char *key, *val;
+ struct argv_tab *atp = argv_tab;
+
+ ph = OFFinddevice("/chosen");
+ if (ph == 0) {
+ warn("parse_args: No phandle for '/chosen'\n");
+ return;
+ }
+
+ debug(VRDBG_MAIN, "parse_args: /chosen phandle %x\n", ph);
+ bootargs = get_str_prop(ph, "bootargs", ALLOC);
+ if (bootargs == NULL || *bootargs == '\0') {
+ return;
+ }
+ debug(VRDBG_MAIN, "bootargs: '%s'\n", bootargs);
+
+ bootargs = strctok(bootargs, ' ');
+ if (bootargs[0] != '-') {
+
+ debug(VRDBG_MAIN, "Boot file '%s'\n", bootargs);
+ if (bootargs[0] == '\\') {
+
+ //
+ // We're just specifying the file to boot:
+ // update the OsLoader argument but nothing else.
+ //
+ update_argv("OsLoader", bootargs);
+
+ } else {
+
+ //
+ // Without a leading backslash, bootargs presumably
+ // contains a full device path.
+ // If so, update both Bootpath and OsLoader.
+ //
+ Bootpath = bootargs;
+ if (key = index(Bootpath, '\\')) {
+ val = (char *) malloc(strlen(key)+1);
+ strcpy(val, key);
+ update_argv("OsLoader", val);
+ *key = '\0';
+ }
+ }
+
+ debug(VRDBG_MAIN, "Bootpath '%s'\n", Bootpath);
+ for ( ; atp->key != NULL; ++atp) {
+ if (strcmp("OsLoader", atp->key) == 0) {
+ debug(VRDBG_MAIN, "OsLoader '%s'\n", atp->val);
+ break;
+ }
+ }
+ NEXT_TOKEN();
+ }
+
+ while (bootargs && (*bootargs != '\0')) {
+ if (strncmp(bootargs, "-vrdebug", 8) == 0) {
+ NEXT_TOKEN();
+ debug(VRDBG_MAIN, "-vrdebug: '%s'\n", bootargs);
+ VrDebug = atoi(bootargs);
+ continue;
+ }
+ if (strncmp(bootargs, "-env", 4) == 0) {
+ NEXT_TOKEN();
+ key = bootargs;
+ NEXT_TOKEN();
+ val = bootargs;
+ debug(VRDBG_MAIN, "-env: '%s' '%s'\n", key, val);
+ update_argv(key, val);
+ }
+ if (strncmp(bootargs, "-h", 2) == 0) {
+ VrDebug |= VRDBG_HOLDIT;
+ }
+ NEXT_TOKEN();
+ }
+}
+
+STATIC VOID
+update_argv(char *key, char *val)
+{
+ struct argv_tab *atp = argv_tab;
+
+ for ( ; atp->key != NULL; ++atp) {
+ if (strcmp(key, atp->key) == 0) {
+ atp->val = val;
+ return;
+ }
+ }
+ if (atp >= &argv_tab[MAX_ARGC]) {
+ warn("You can't define any more argument variables\n");
+ return;
+ }
+ atp->key = key;
+ atp->val = val;
+ atp->which = BOTH;
+}
+
+#define CSI '\233'
+#define ESC '\033'
+
+STATIC INT
+select_boot(VOID)
+{
+ char *ids[16];
+ char *prop, c;
+ int i= 0, choices, chosen, countdown, csi, count;
+ debug(VRDBG_ENTRY,"select_boot: VOID BEGIN....\n");
+
+ //
+ // Determine how long to display the list of options:
+ //
+ prop = VrGetEnvironmentVariable("COUNTDOWN");
+ if (prop == NULL) {
+ countdown = 10;
+ } else {
+ countdown = atoi(prop);
+ }
+
+ //
+ // get the text list of possible options.
+ //
+ prop = VrGetEnvironmentVariable("LOADIDENTIFIER");
+ if (prop == NULL) {
+ return(0);
+ }
+
+ //
+ // tokenize the string of load options, creating pointers to each
+ // component of the string. Don't use strctok, since we need to
+ // worry about detecting null entries in the OsLoadOptions list.
+ //
+ ids[i++] = prop; // stuff the first one in the array....
+ while ((prop = index(prop, ';')) != NULL) {
+ *prop = '\0'; // change the separator to a null
+ prop++;
+ ids[i++] = prop;
+ }
+ choices = i;
+ while (i < 16) {
+ ids[i++] = NULL;
+ }
+
+ //
+ // here be the wheel of fortune: Makes yer choice and be off wid ya
+ //
+ chosen = 0;
+ csi = 0;
+ warn("\233H\233J"); // Clear screen
+ Salutation();
+ puts("\233""24;1HMake selection using arrow keys and 'Enter', or press ESC to cancel");
+ countdown += VrGetRelativeTime();
+again:
+ for (i = 0; i < choices; ++i) {
+ if (i == chosen) {
+ warn("\233%d;1H\233K\233%d;3H\233""7m* %s\233""m\n",
+ i+3, i+3, ids[i]);
+ } else {
+ warn("\233%d;1H\233K\233%d;5H%s\n", i+3, i+3, ids[i]);
+ }
+ }
+ i = 0;
+ warn("\233%d;1H\233K\n", choices+5);
+ while (VrGetReadStatus(0)) {
+ if (countdown == -1) {
+ continue;
+ }
+ if (VrGetRelativeTime() > (unsigned) countdown) {
+ goto out;
+ }
+ if (VrGetRelativeTime() != (unsigned) i) {
+ warn("\233%d;1H\233K\233%d;5HSeconds remaining: %d\n",
+ choices+5, choices+5, countdown - VrGetRelativeTime());
+ i = VrGetRelativeTime();
+ }
+ }
+ countdown = -1;
+ (void) VrRead(0, &c, 1, &count);
+ if (csi) {
+ switch (c) {
+ case 'A': chosen = max(chosen - 1, 0); break;
+ case 'B': chosen = min(chosen + 1, choices-1); break;
+ }
+ csi = 0;
+ } else {
+ switch (c) {
+ case CSI: csi = 1; break;
+ case '\r': goto out;
+ case '\n': goto out;
+ case ESC: OFExit();
+
+ case 'k': // vi
+ case '\020': // emacs
+ case '+': // good guesses
+ case '<':
+ chosen = max(chosen - 1, 0); break;
+ case 'j':
+ case '\016':
+ case '-':
+ case '>':
+ chosen = min(chosen + 1, choices-1); break;
+
+ case '\t':
+ if (++chosen == choices) {
+ chosen = 0;
+ }
+ break;
+ }
+ }
+ goto again;
+
+out:
+ warn("\233H\233J"); // Clear screen
+ //
+ // Given the chosen number, pull out the corresponding string, and
+ // setup the Bootpath and SystemPath variables:
+ //
+ Bootpath = choose_args("OSLOADER", "OsLoader", chosen);
+ SystemPath = choose_args("SYSTEMPARTITION", "SystemPartition", chosen);
+
+ //
+ // get the rest of the options.....
+ //
+ choose_args("OSLOADPARTITION", "OSLoadPartition", chosen);
+ choose_args("OSLOADOPTIONS", "OSLoadOptions", chosen);
+ choose_args("LOADIDENTIFIER", "LoadIdentifier", chosen);
+ choose_args("OSLOADFILENAME", "OSLoadFilename", chosen);
+
+ debug(VRDBG_ENTRY,"select_boot: VOID ....END\n");
+ return(1);
+}
+
+STATIC CHAR *
+choose_args( char *EnvVar, char *Varrrg, int achoice )
+{
+ char *prop, *cp;
+ int i= 0;
+ debug(VRDBG_ENTRY,
+ "choose_args: EnvVar: %s Varrrg: %s achoice: 0x%x BEGIN....\n",
+ *EnvVar, *Varrrg, achoice);
+ prop = VrGetEnvironmentVariable(EnvVar);
+ //debug(VRDBG_TEST, "\n@%d: prop is currently...%s:\n",i,prop);
+ if (prop == NULL) {
+ return(0);
+ }
+ prop = strcsep(prop, ';');
+ for (i = 0; i < achoice; ++i) {
+ if ((prop = strcsep(NULL, ';')) == NULL) {
+ return(0);
+ }
+ }
+ cp = zalloc(strlen(prop) +1 );
+ strcpy(cp, prop);
+ update_argv(Varrrg, cp);
+ //debug(VRDBG_TEST, "@%d: cp is set to...%s:\n",i,cp);
+ debug(VRDBG_ENTRY, "choose_args: ....END\n");
+ return( cp );
+}
+
+STATIC VOID
+find_boot_dev(VOID)
+{
+ phandle ph;
+ char *bootpath;
+ PCONFIGURATION_NODE node;
+
+ if (Bootpath) {
+ debug(VRDBG_MAIN, "Bootpath has been set from the command line\n");
+ return;
+ }
+ if (select_boot()) {
+ debug(VRDBG_MAIN, "We chose a boot device from LOADIDENTIFIER menu.\n");
+ return;
+ }
+
+ // Use whatever device we booted the veneer from.
+ ph = OFFinddevice("/chosen");
+ bootpath = get_str_prop(ph, "bootpath", NOALLOC);
+ if (bootpath == NULL) {
+ warn("find_boot_dev: No property '/chosen:bootpath'\n");
+ return;
+ }
+ debug(VRDBG_MAIN, "find_boot_dev: bootpath (len %d) '%s'\n",
+ strlen(bootpath), bootpath);
+ node = PathToNode(bootpath);
+ if (node == NULL) {
+ warn("find_boot_dev: Couldn't find node for '%s'\n", bootpath);
+ return;
+ }
+ Bootpath = NodeToArcPath(node);
+ bootpath = (char *)malloc(strlen(Bootpath) + strlen("partition(1)") + 1);
+ strcpy(bootpath, Bootpath);
+ strcat(bootpath, "partition(1)"); /* XXX */
+ free(Bootpath);
+ Bootpath = bootpath;
+ debug(VRDBG_MAIN, "find_boot_dev: bootpath '%s'\n", Bootpath);
+}
+
+/*
+ *
+ * ROUTINE: VOID read_ARC_env_vars(VOID)
+ *
+ * DESCRIPTIN:
+ * Initialize the arc argument table with the values contained in open
+ * firmware.
+ *
+ */
+
+STATIC VOID
+read_ARC_env_vars(VOID)
+{
+ struct argv_tab *atp;
+ char *val, *newval;
+
+ //
+ // for each variable in the argv_tab array, find the actual value
+ // this system has in firmware.
+ //
+ for (atp = argv_tab; atp < &argv_tab[MAX_ARGC] && atp->key; ++atp) {
+ if ((val = VrGetEnvironmentVariable(atp->key)) != NULL) {
+ newval = (char *) malloc(strlen(val) + 1);
+ strcpy(newval, val);
+ atp->val = newval;
+ }
+ }
+}
+
+STATIC VOID
+create_argv(VOID)
+{
+ struct argv_tab *atp;
+ char *osloader, *old_osloader = "";
+ char *buf;
+ phandle ph;
+ extern char *VeneerVersion();
+
+ /*
+ * First instantiate the boot partition string in the
+ * OS Loader arguments table. By the way, when we find
+ * STR_OSLOADER, save it to produce the argv[0] and OsLoader
+ * arguments.
+ */
+ for (atp = argv_tab; atp < &argv_tab[MAX_ARGC] && atp->key; ++atp) {
+ if (strcmp(atp->val, STR_XYZZY) == 0) {
+ atp->val = Bootpath;
+ }
+ if (strcmp(atp->key, "OsLoader") == 0) {
+ old_osloader = atp->val;
+ }
+ }
+
+ /*
+ * Initialize the argv/envp arrays.
+ */
+ VrArgc = VrEnvc = 0;
+ bzero((PCHAR) VrArgv, MAX_ARGC * sizeof(PCHAR));
+ bzero((PCHAR) VrEnvp, MAX_ENVC * sizeof(PCHAR));
+ VrEnvp[VrEnvc] = "";
+
+ /*
+ * Construct argv[0], the boot string (special case).
+ */
+ if (old_osloader[0] == '\\') {
+ osloader = zalloc(strlen(Bootpath) + strlen(old_osloader) + 1);
+ strcpy(osloader, Bootpath);
+ strcat(osloader, old_osloader);
+ } else {
+ osloader = old_osloader;
+ }
+ add_argv("", osloader);
+
+ /*
+ * Now walk the argv table, building the argv and envp
+ * arrays. When we encounter OsLoader, be sure to use the
+ * buffer we just built, rather than the table value.
+ */
+ for (atp = argv_tab; atp < &argv_tab[MAX_ARGC] && atp->key; ++atp) {
+ if (strcmp(atp->key, "OsLoader") == 0) {
+ atp->val = osloader;
+ }
+ if (atp->which != ENVONLY) {
+ add_argv(atp->key, atp->val);
+ }
+ if (atp->which != ARGVONLY) {
+ add_envp(atp->key, atp->val);
+ }
+ if (strcmp(atp->key, "SystemPartition" ) == 0 ){
+ atp->val = SystemPath;
+ }
+ }
+
+ /*
+ * Record the version strings.
+ */
+ ph = OFFinddevice("/openprom");
+ add_envp("FirmwareVersion", get_str_prop(ph, "model", NOALLOC));
+ add_envp("VeneerVersion", VeneerVersion());
+
+ /*
+ * Finally, take care of the console paths, set at runtime.
+ */
+ buf = VrFindConsolePath("stdin");
+ add_argv("ConsoleIn", buf);
+ add_envp("ConsoleIn", buf);
+ free(buf);
+
+ buf = VrFindConsolePath("stdout");
+ add_argv("ConsoleOut", buf);
+ add_envp("ConsoleOut", buf);
+ free(buf);
+}
+
+
+STATIC VOID
+add_argv(PCHAR key, PCHAR val)
+{
+ char *buf;
+ int len;
+
+ len = strlen(key);
+ if (len) {
+ len += 1; // for '='
+ }
+ len += strlen(val);
+ buf = (char *) zalloc(len+1);
+ strcpy(buf, key);
+ if (*buf != '\0') {
+ strcat(buf, "=");
+ }
+ strcat(buf, val);
+ VrArgv[VrArgc] = buf;
+ if ((buf = index(buf, ';')) != NULL) {
+ *buf = '\0';
+ }
+ debug(VRDBG_ARGV, "Argv[%d]: %s\n", VrArgc, VrArgv[VrArgc]);
+ VrArgc += 1;
+}
+
+/*
+ * ROUTINE: VOID add_envp( PCHAR, PCHAR )
+ *
+ * DESCRIPTION:
+ * Add the passed in name string and value into an array
+ * of string/value pairs that describes the environment for
+ * the arc program to be executed. The entry in the array is
+ * of the form "name=value", and is added to the beginning of
+ * the array.
+ *
+ * RETURN:
+ * Returns nothing.
+ *
+ */
+
+STATIC VOID
+add_envp(PCHAR key, PCHAR val)
+{
+ char *buf;
+ int len;
+
+ len = strlen(key);
+ if (len) {
+ len += 1; // for '='
+ }
+ len += strlen(val);
+ buf = (char *) zalloc(len+1);
+ strcpy(buf, VrCanonicalName(key));
+ if (*buf != '\0') {
+ strcat(buf, "=");
+ }
+ strcat(buf, val);
+ VrEnvp[VrEnvc+1] = VrEnvp[VrEnvc];
+ VrEnvp[VrEnvc] = buf;
+ debug(VRDBG_ENV, " Env[%d]: %s\n", VrEnvc, VrEnvp[VrEnvc]);
+ VrEnvc += 1;
+}
+
+static
+int
+is_mp_capable(ULONG VerRev)
+{
+ ULONG ver = (VerRev >> 16) & 0xFFFF;
+ ULONG rev = VerRev & 0xFFFF;
+
+ switch(ver) {
+
+ case PPC_604:
+ if ( rev > 0x0304 )
+ return(1);
+ break;
+
+ case PPC_604E:
+ return(1);
+
+ default:
+ return(0);
+
+ }
+ return(0);
+
+}
+
+
+//
+// The routines that follow initialize the System Parameter Block,
+// Firmware Vector Table, and Restart Blocks.
+//
+// Note the use of MAP() and UNMAP() macros. The kernel requires that
+// addresses in the System Parameter Block and Restart Blocks be
+// VIRTUAL, not physical. Therefore, all addresses that may be presented
+// to the kernel must be mapped to KSEG0; i.e., they must be in the
+// range starting at 0x80000000.
+//
+
+STATIC PRESTART_BLOCK last_rstb = 0;
+
+STATIC PRESTART_BLOCK
+InitRestartBlocks(PCONFIGURATION_NODE node, PRESTART_BLOCK rstb)
+{
+ PRESTART_BLOCK new_rstb;
+
+ debug(VRDBG_ENTRY, "InitRestartBlocks: Begin(0x%x, 0x%x)...\n",node, rstb);
+ VRDBG(VRDBG_ENTRY, vr_dump_config_node(node));
+
+ if (node->Component.Class == ProcessorClass &&
+ node->Component.Type == CentralProcessor) {
+
+ // Figure out where the next node's space should be.
+ if (rstb) {
+ new_rstb = (PRESTART_BLOCK) ((PCHAR) rstb + sizeof(RESTART_BLOCK));
+ } else {
+ new_rstb = (PRESTART_BLOCK) ((PCHAR) SYSTEM_BLOCK +
+ SYSTEM_BLOCK->Length + SYSTEM_BLOCK->FirmwareVectorLength);
+ }
+
+ // Claim the space and turn it into a restart block.
+ if (CLAIM((VOID *)new_rstb, sizeof(RESTART_BLOCK)) == -1) {
+ fatal("Couldn't claim RESTART BLOCK\n");
+ }
+
+ if (rstb) {
+ rstb->NextRestartBlock = (PRESTART_BLOCK) UNMAP(new_rstb);
+ }
+ rstb = new_rstb;
+ last_rstb = new_rstb;
+
+ bzero((PCHAR) rstb, sizeof(RESTART_BLOCK));
+ rstb->Signature = ARC_RESTART_BLOCK_SIGNATURE;
+ rstb->Version = 1;
+ rstb->Revision = 2;
+ rstb->Length = sizeof(RESTART_BLOCK);
+ rstb->SaveAreaLength = sizeof(PPC_RESTART_STATE);
+// rstb->BootStatus.BootFinished = 1;
+ rstb->BootStatus.ProcessorReady = 1;
+ rstb->ProcessorId = node->Component.Key;
+ if (rstb->ProcessorId == 0) {
+ rstb->BootStatus.ProcessorStart = 1;
+ rstb->BootStatus.ProcessorRunning = 1;
+ } else {
+ rstb->BootStatus.ProcessorStart = 0;
+ }
+ }
+
+ if (node->Child) {
+ rstb = InitRestartBlocks(node->Child, rstb);
+ }
+ if (node->Peer) {
+ rstb = InitRestartBlocks(node->Peer, rstb);
+ }
+
+ debug(VRDBG_ENTRY, "InitRestartBlocks: ....Exit\n");
+ return (rstb);
+}
+
+STATIC VOID
+SumRestartBlocks(PRESTART_BLOCK rstb)
+{
+ PLONG up = (PLONG) rstb;
+ LONG accum = 0;
+
+ debug(VRDBG_ENTRY, "SumRestartBlocks: Begin(0x%x)....\n", rstb);
+ rstb->CheckSum = 0;
+ while (up < (PLONG) ((PCHAR) rstb + sizeof(RESTART_BLOCK))) {
+ accum += *up++;
+ }
+ rstb->CheckSum = -accum;
+ debug(VRDBG_ENTRY, "SumRestartBlocks: ....Exit\n", rstb);
+}
+
+STATIC int
+IdleCPU(PRESTART_BLOCK rstb, int stopFlag)
+{
+ STATIC PVOID IdleLoop = 0;
+ STATIC PULONG Bootp;
+ STATIC ULONG ProcRev=0;
+ STATIC INT mismatchFlag = 0;
+ ULONG IdleLoopSize;
+ ULONG res, timeout;
+ extern PVOID ArcPoll, EndArcPoll;
+
+ // cpu0 is always successful so that we can have a
+ // uniprocessor system in hand
+ if (rstb->ProcessorId == 0) {
+ ProcRev = VrGetProcRev();
+ if (!is_mp_capable(ProcRev))
+ mismatchFlag = 1;
+ return(0);
+ }
+
+ // The processor idle loop must be in FirmwarePermanent memory,
+ // so that it's not disturbed by kernel startup. Identify a piece
+ // of memory just after the last restart block and copy in the
+ // idle loop code.
+
+ if (IdleLoop == 0) {
+ IdleLoop = (PCHAR) last_rstb + sizeof(RESTART_BLOCK);
+ IdleLoopSize = (ULONG) &EndArcPoll - (ULONG) &ArcPoll;
+
+ // Pad to make room for BootStatus and SaveArea variables.
+ Bootp = (PULONG) IdleLoop;
+ (PCHAR) IdleLoop += 3 * sizeof(ULONG);
+ IdleLoopSize += 3 * sizeof(ULONG);
+
+ if (CLAIM(IdleLoop, IdleLoopSize) == -1) {
+ fatal("Couldn't claim MP idle loop\n");
+ }
+ bcopy((PCHAR) &ArcPoll, IdleLoop, IdleLoopSize);
+ VrFlushAllCaches();
+ }
+
+ //
+ // Set this processor spinning in the new idle loop.
+ // XXX - To do: change this to CIF.
+ // Since we're running in virtual mode that is mapped virtual 0
+ // to physical 0, this assignment translates directly into the
+ // real mode addresses the IdleCPU routine will end up executing.
+ //
+ Bootp[0] = 0; // processor version returned by the cpu
+ Bootp[1] = (ULONG) &rstb->BootStatus;
+ Bootp[2] = (ULONG) &rstb->u.SaveArea;
+
+ // Give the other processor plenty of time to start up: he may be
+ // executing debug printouts and other slow tasks before switching.
+ // Five seconds should be more than adequate.
+
+ debug(VRDBG_TMP, "Executing non 0 processor at 0x%x\n", IdleLoop);
+ res = 0;
+ if (OFInterpret(1, 3, &res, "cpu-execute-code", rstb->ProcessorId, IdleLoop) != 0) {
+ return(-1);
+ }
+ if (res == 0)
+ return(-1);
+
+ //
+ // wait a small amount of time for the other processor to
+ // start up. Since VrGetRelative time returns the time since
+ // power-on/reset, tack on a few seconds for the waiting period.
+ //
+ //timeout = VrGetRelativeTime() + (5 * 1000);
+ timeout = VrGetRelativeTime() + (5 * 1); // 5 seconds should be
+ // long enough
+
+ do {
+ if (Bootp[1] == 0x1234) {
+ debug(VRDBG_TMP,"ProcRev = 0x%x; return = 0x%x\n",
+ Bootp[0], Bootp[1]);
+ if ( Bootp[0] != ProcRev || mismatchFlag || stopFlag) {
+ Bootp[1] = 0xBAD;
+ return(-1);
+ } else {
+ Bootp[1] = 0xCAFE;
+ return(0);
+ }
+ }
+ } while (VrGetRelativeTime() < timeout);
+ fatal("Processor %d failed to enter MP idle loop.\n", rstb->ProcessorId);
+}
+
+
+/*
+ * Routine Description:
+ * This routine initializes the firmware vector in the system parameter
+ * block.
+ *
+ * Arguments:
+ * None.
+ *
+ * Return Value:
+ * None.
+ *
+ */
+
+STATIC VOID
+VrInitSystem(VOID)
+{
+ LONG i;
+ LONG FirmwareVectorLen;
+ PRESTART_BLOCK rstb, PrevRstb;
+ int NumOfCpu = 0 , NumProc = 0;
+ ULONG ProcMask = 0, maskscan = 0;
+ PCHAR evp;
+
+ debug(VRDBG_ENTRY, "VrInitSystem: BEGIN....\n");
+ //
+ // Initialize the system parameter block
+ //
+ FirmwareVectorLen = (ULONG)MaximumRoutine * sizeof(ULONG);
+ i = sizeof(SYSTEM_PARAMETER_BLOCK) + FirmwareVectorLen;
+ debug(VRDBG_ENTRY, "VrInitSystem: i(0x%x), FirmwareVectorLen(0x%x) set:\n",
+ i, FirmwareVectorLen);
+
+#if 0
+ //
+ // THis is now down in a different routine called by main.
+ //
+ //
+ // attempting to setup all the claiming early on
+ //
+ if (use_bat_mapping) {
+ res = claim((PVOID) SYSTEM_BLOCK, i);
+ } else {
+ res = claimreal((PVOID) SYSTEM_BLOCK, i);
+ }
+ if (res == -1) {
+ fatal("Couldn't claim SYSTEM PARAMETER BLOCK\n");
+ }
+ bzero((PCHAR)SYSTEM_BLOCK, i);
+#endif
+
+
+ debug(VRDBG_ENTRY, "VrInitSystem: Init SYSTEM_BLOCK.. \n");
+ SYSTEM_BLOCK->Signature = SYSTEM_BLOCK_SIGNATURE;
+ SYSTEM_BLOCK->Version = ARC_VERSION;
+ SYSTEM_BLOCK->Revision = ARC_REVISION;
+ SYSTEM_BLOCK->Length = sizeof(SYSTEM_PARAMETER_BLOCK);
+
+ SYSTEM_BLOCK->FirmwareVector =
+ (PVOID) UNMAP((PCHAR) SYSTEM_BLOCK + SYSTEM_BLOCK->Length);
+ SYSTEM_BLOCK->FirmwareVectorLength = FirmwareVectorLen;
+
+ //
+ // Initialize the restart blocks.
+ //
+ debug(VRDBG_ENTRY, "VrInitSystem: Init restart blocks\n");
+ SYSTEM_BLOCK->RestartBlock = (PRESTART_BLOCK) UNMAP((PCHAR) SYSTEM_BLOCK +
+ SYSTEM_BLOCK->Length + SYSTEM_BLOCK->FirmwareVectorLength);
+ if (InitRestartBlocks(RootNode, 0)) {
+ rstb = (PRESTART_BLOCK) MAP(SYSTEM_BLOCK->RestartBlock);
+ PrevRstb = 0;
+ NumOfCpu = 0;
+ ProcMask = 0xFFFFFFFF;
+ if ( (evp = VrGetEnvironmentVariable("PROCESSORS")) != NULL )
+ ProcMask = atoi(evp) | 1;
+ maskscan = 1;
+ while (rstb) {
+ SumRestartBlocks(rstb);
+
+ if (IdleCPU(rstb,(ProcMask&maskscan) == 0 ) == -1) {
+ ProcMask &= ~maskscan; // clear the bit
+ // remove the restart block
+ PrevRstb->NextRestartBlock = rstb->NextRestartBlock;
+ } else {
+ NumOfCpu++;
+ PrevRstb = rstb;
+ }
+ maskscan <<= 1;
+ rstb = (PRESTART_BLOCK) MAP(PrevRstb->NextRestartBlock);
+ }
+ } else {
+ SYSTEM_BLOCK->RestartBlock = 0;
+ }
+
+
+ //
+ // Temporarily make all firmware vector to point to an error routine.
+ //
+ for (i=LoadRoutine; i < MaximumRoutine; i++) {
+ (VR_NOT_YET_ROUTINE)SYSTEM_BLOCK->FirmwareVector[i] = VrNotYet;
+ }
+
+ //
+ // Initialize the firmware vectors for other functions.
+ //
+ VrEnvInitialize();
+ VrMemoryInitialize();
+ VrIoInitialize();
+ VrDisplayInitialize();
+ VrLoadInitialize();
+ VrRestartInitialize();
+ VrConfigInitialize();
+ VrTimeInitialize();
+ debug(VRDBG_ENTRY, "VrInitSystem: .....END\n");
+}
+
+STATIC VOID
+VrNotYet( VOID )
+{
+ fatal("This ARC function is not yet implemented\n");
+}
+
+
+STATIC VOID
+move_multi_to_root(PCONFIGURATION_NODE node)
+{
+ PCONFIGURATION_NODE child = node->Child;
+ PCONFIGURATION_NODE peer = node->Peer;
+ PCONFIGURATION_NODE n;
+ ULONG key;
+
+ debug(VRDBG_ENTRY, "move_multi_to_root: node 0x%x\n", node);
+ if (node == 0) {
+ debug(VRDBG_TREE, "move_multi_to_root: node is 0, return.\n");
+ return;
+ }
+ debug(VRDBG_TREE, "MMTR: move node: 0x%x to child of ROOT\n",node);
+ VRDBG(VRDBG_TREE, vr_dump_config_node(node));
+
+ // Examine the first child, and keep promoting it/them until
+ // the first child is no longer of type multi.
+
+ while ( (child) && (child->Component.Type == MultiFunctionAdapter)) {
+ for (n = RootNode->Child, key = 0; n->Peer; n = n->Peer) {
+ if (n->Component.Type == MultiFunctionAdapter) {
+ key = max(key, n->Component.Key);
+ }
+ }
+ if (n->Component.Type == MultiFunctionAdapter) {
+ key = max(key, n->Component.Key);
+ }
+ child->Parent = RootNode;
+ n->Peer = child;
+ node->Child = child->Peer;
+ child->Peer = 0;
+ child->Component.Key = key + 1;
+ child = node->Child;
+ }
+
+ // Process the entire Child branch.
+
+ move_multi_to_root(child);
+
+ // Now for the Peer branch: first, if our parent is the root,
+ // there's no need to promote the peer node, so skip the next step.
+
+ if (node->Parent != RootNode) {
+
+ // As before, promote peers until the peer isn't a multi node...
+
+ while (peer && peer->Component.Type == MultiFunctionAdapter) {
+ for (n = RootNode->Child, key = 0; n->Peer; n = n->Peer) {
+ if (n->Component.Type == MultiFunctionAdapter) {
+ key = max(key, n->Component.Key);
+ }
+ }
+ if (n->Component.Type == MultiFunctionAdapter) {
+ key = max(key, n->Component.Key);
+ }
+ peer->Parent = RootNode;
+ n->Peer = peer;
+ node->Peer = peer->Peer;
+ peer->Peer = 0;
+ peer->Component.Key = key + 1;
+ peer = node->Peer;
+ }
+ }
+
+ // ...and process the Peer branch. Since our traversal is depth-first
+ // and promoted nodes are appended to the RootNode's Child's Peer branch
+ // (and are thus processed last), this routine should suffice to traverse
+ // the entire tree.
+
+ move_multi_to_root(peer);
+}
+
+
+LONG
+claimreal(PVOID addr, ULONG size)
+{
+ if (claimphys((PVOID) MAP(addr), size, 0) == -1) {
+ return(-1);
+ }
+ return(map((PVOID) MAP(addr), addr, size, (ULONG) -1));
+}
+
+STATIC LONG
+claimphys(PVOID physical, ULONG size, ULONG align)
+{
+ static ihandle memih = 0;
+ ULONG base;
+
+ if (memih == 0) {
+ if((memih = get_int_prop(OFFinddevice("/chosen"), "memory")) == 0) {
+ fatal("Couldn't open the memory node");
+ }
+ }
+ return(OFCallMethod(1, 5, &base,
+ "claim", memih, align, size, (ULONG) physical));
+}
+
+STATIC LONG
+map(PVOID physical, PVOID virtual, ULONG size, ULONG mode)
+{
+ static ihandle mmuih = 0;
+
+ if (mmuih == 0) {
+ if((mmuih = get_int_prop(OFFinddevice("/chosen"), "mmu")) == 0) {
+ fatal("Couldn't open the MMU node");
+ }
+ }
+ return(OFCallMethod(0, 6, 0,
+ "map", mmuih, mode, size, virtual, (ULONG) physical));
+}
+
+/*
+ * Because the PowerPC port is based on the MIPS port, and no one saw fit
+ * to re-examine assumptions in the light of the PowerPC architecture,
+ * the NT kernel et al. are expected to reside in kseg0 (8000.0000-a000.000).
+ * Set up a virtual mapping for this region.
+ */
+STATIC VOID
+VrKseg0(VOID)
+{
+ ihandle ih;
+
+ debug(VRDBG_MAIN, "Mapping in kseg0...\n");
+ ih = get_int_prop(OFFinddevice("/chosen"), "mmu");
+ if (ih == 0) {
+ fatal("Couldn't open the MMU node; kseg0 translation not set up.\n");
+ }
+ OFCallMethod(0, 6, 0, "map", ih, -2, 0x800000, 0x80000000, 0);
+ return;
+}
+
+/*
+ * XXX - This is a hack for the AMD79C974 Ethernet chip driver: the driver
+ * assumes the chip is on the ISA bus.
+ */
+
+STATIC void
+move_amd_to_isa_hack(void)
+{
+ phandle ph;
+ PCONFIGURATION_NODE amdnode, peernode, isanode;
+
+ if ((ph = OFFinddevice("/pci/AMD,79c970@4")) == -1) {
+ debug(VRDBG_MAIN, "No AMD ethernet found\n");
+ return;
+ }
+
+ peernode = PathToNode("/pci");
+ peernode = peernode->Child;
+
+ if (peernode->OfPhandle == ph) {
+ amdnode = peernode;
+ peernode->Parent->Child = peernode->Peer;
+ } else {
+ while (peernode->Peer && (peernode->Peer->OfPhandle != ph)) {
+ peernode = peernode->Peer;
+ }
+ amdnode = peernode->Peer;
+ peernode->Peer = amdnode->Peer;
+ }
+
+ isanode = PathToNode("/pci/isa");
+ if (isanode->Child == NULL) {
+ isanode->Child = amdnode;
+ amdnode->Peer = NULL;
+ } else {
+ peernode = isanode->Child;
+ while (peernode->Peer) {
+ peernode = peernode->Peer;
+ }
+ amdnode->Peer = peernode->Peer;
+ peernode->Peer = amdnode;
+ }
+ amdnode->Parent = isanode;
+}
+
+STATIC void
+move_ide_to_isa_hack(void)
+{
+ PCONFIGURATION_NODE node, idenode = 0, isanode;
+ int lastkey = 0;
+
+ isanode = PathToNode("/pci/isa");
+ node = isanode->Child;
+ while (node) {
+ if (strcmp(node->ComponentName, "disk") == 0) {
+ lastkey = max(lastkey, (int) node->Component.Key);
+ }
+ if (strcmp(node->Component.Identifier, "IDE") == 0) {
+ idenode = node;
+ }
+ node = node->Peer;
+ }
+ if (idenode == 0) {
+ return;
+ }
+
+ node = isanode->Child;
+ while (node->Peer != idenode) {
+ node = node->Peer;
+ }
+ node->Peer = node->Peer->Peer; /* Bypass ide node */
+ while (node->Peer) {
+ node = node->Peer;
+ }
+ node->Peer = idenode->Child;
+
+ while (node->Peer) {
+ node->Peer->Parent = node->Parent;
+ if (strcmp(node->Peer->ComponentName, "disk") == 0) {
+ node->Peer->Component.Key = ++lastkey;
+ }
+ node = node->Peer;
+ }
+}
+
+/*
+ * move_scsi_children_to_ide_hack() moves the children (e.g. disk and cdrom)
+ * of the SCSI node to be children of the IDE node, and changes the IDE's
+ * phandle pointer to point to the Open Firmware SCSI node.
+ *
+ * This egregious hack is necessary for the initial release of IBM's "Harley"
+ * evaluation system. On that system, IBM's portable boot loader misrepresents
+ * the hardware by reporting the SCSI disk and SCSI CD-ROM devices as children
+ * of the IDE node in the ARC tree! The IDE and SCSI nodes themselves are in
+ * the correct places in the ARC tree, but the children are in the wrong
+ * place. The enviroment variables that specify the locations of the OSLOADER
+ * and so forth collude in this fiction by specifying paths like
+ * multi(1)scsi(0)disk(0)rdisk(3)partition(2), which is the path through
+ * the IDE node (which, for reasons not specific to Harley, is of class
+ * "scsi"). I do not know exactly what NT does in order to compensate for
+ * this lie.
+ */
+
+STATIC void
+move_scsi_children_to_ide_hack(void)
+{
+ PCONFIGURATION_NODE node, idenode = 0, scsinode = 0;
+
+ if (OFGetproplen(OFFinddevice("/openprom"),"arc-scsi-to-ide") < 0) {
+ return;
+ }
+
+ idenode = PathToNode("/pci/isa/ide");
+ scsinode = PathToNode("/pci/scsi");
+
+ if (idenode == 0 || scsinode == 0) {
+ return;
+ }
+
+ /* Move SCSI children underneath IDE */
+ idenode->Child = scsinode->Child;
+
+ scsinode->Child = 0;
+
+ /* Reparent SCSI children to IDE */
+ for (node = idenode->Child; node != 0; node = node->Peer) {
+ node->Parent = idenode; /* Reparent nodes */
+ }
+
+ /* Point the IDE node to the Open Firmware SCSI node so Open will work */
+ idenode->OfPhandle = scsinode->OfPhandle;
+
+ idenode->Component.Type = ScsiAdapter;
+ idenode->ComponentName = "scsi";
+}
+
+STATIC VOID
+VrInitSystemBlock()
+{
+ LONG i;
+ LONG FirmwareVectorLen;
+ //
+ // Initialize the system parameter block
+ //
+ FirmwareVectorLen = (ULONG)MaximumRoutine * sizeof(ULONG);
+ i = sizeof(SYSTEM_PARAMETER_BLOCK) + FirmwareVectorLen;
+
+ if (CLAIM((PVOID) SYSTEM_BLOCK, i) == -1) {
+ fatal("Couldn't claim SYSTEM PARAMETER BLOCK\n");
+ }
+ bzero((PCHAR)SYSTEM_BLOCK, i);
+ return;
+}
+