mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1314 lines
32 KiB
1314 lines
32 KiB
/*
|
|
* 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;
|
|
}
|
|
|