/* * 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; }