-- Copyright (C) 1983 by Xerox Corporation. All rights reserved.
-- BootChannelDicentra.mesa, HGM, 5-Dec-83 20:40:38
<< This isn't really a Channel, but it saves a lot of space in the initial microcode. This module must be first on the chain. It responds to a deviceType of anyEthernet by adjusting the bootLocation so that some other channel will do the work.
General plan:
Look for a valid boot location in the EERom.
If not, try for one in page 0 of the Germ EProm. (Not implemented yet.)
If that fails too, use a wired in default location. (NS Booting.)
If the altBoot button is down, cycle magic numbers through the MP until altBoot goes up.
Currently, there are 3 clumps of numbers.
One for NS booting, a second for phone booting, and another for Pup booting.
The thousands digit is the device index.
>>
DIRECTORY
Boot USING [Location],
BootChannel USING [Create, Handle, Operation, Result],
Checksum USING [ComputeChecksum],
DeviceTypes USING [anyEthernet, ethernet, ethernetOne],
Environment USING [Byte, bytesPerWord],
Inline USING [BytePair, HighByte, LowByte],
NSConstants USING [bootServerSocket],
PilotMP USING [cGermAction],
ProcessorFace USING [mp, SetMP],
System USING [
broadcastHostNumber, GetClockPulses, HostNumber, MicrosecondsToPulses,
NetworkAddress, nullNetworkNumber, Pulses],
DicentraInputOutput USING [GetExternalStatus, IOAddress, Input, Output],
EERom USING [BootLocation, bootLocationVersion, bytesPerChip, WholeEERom],
MultibusAddresses USING [eprom, timeout],
PhoneFace USING [krockDeivceOffsetForBooting, phoneLine, TurnOff];
BootChannelDicentra: PROGRAM
IMPORTS
remainingChannels: BootChannel,
Checksum, ProcessorFace, System, Inline,
DicentraInputOutput, PhoneFace
EXPORTS BootChannel =
BEGIN
pupBootFileBase: CARDINAL = 140000B;
nsBootFileBase: BootFileNumberRep = [0, 0AA00H, 400H];
Create: PUBLIC PROC [
pLocation: LONG POINTER TO Boot.Location, operation: BootChannel.Operation]
RETURNS [result: BootChannel.Result, handle: BootChannel.Handle] =
BEGIN
PhoneFace.TurnOff[]; -- START Trap allocates CSB
IF pLocation.deviceType = DeviceTypes.ethernet
AND pLocation.deviceOrdinal >= PhoneFace.krockDeivceOffsetForBooting THEN
BEGIN -- Krockery because Phone Booting isn't in any interface
pLocation.deviceType ← PhoneFace.phoneLine;
pLocation.deviceOrdinal ←
pLocation.deviceOrdinal - PhoneFace.krockDeivceOffsetForBooting;
END;
IF pLocation.deviceType = DeviceTypes.anyEthernet THEN
BEGIN
SELECT TRUE FROM
GetLocationFromEERom[pLocation] => NULL;
GetLocationFromGermProm[pLocation] => NULL;
ENDCASE => GetDefaultLocation[pLocation];
IF DicentraInputOutput.GetExternalStatus[].altBoot THEN GetLocationFromButton[pLocation];
END;
RETURN remainingChannels.Create[pLocation, operation];
END;
GetLocationFromEERom: PROCEDURE [location: LONG POINTER TO Boot.Location] RETURNS [BOOLEAN] =
BEGIN
temp: EERom.BootLocation;
FetchBootLocation[@temp];
IF ~VersionValid[@temp] THEN RETURN[FALSE];
IF ~ChecksumValid[@temp] THEN RETURN[FALSE];
location↑ ← temp.location;
RETURN[TRUE];
END;
GetLocationFromGermProm: PROCEDURE [location: LONG POINTER TO Boot.Location] RETURNS [BOOLEAN] =
BEGIN
RETURN[FALSE];
END;
GetDefaultLocation: PROCEDURE [location: LONG POINTER TO Boot.Location] =
BEGIN
FabricateEthernetLocation[location, 0];
END;
GetLocationFromButton: PROCEDURE [location: LONG POINTER TO Boot.Location] =
BEGIN
teMP: CARDINAL = ProcessorFace.mp;
Where: TYPE = {ethernet, phone, ethernetOne};
mp: ARRAY Where OF CARDINAL = [240, 260, 280];
DeactivateWatchdog[]; -- Enable booting over slow lines
FOR device: CARDINAL IN [0..3) DO
FOR where: Where IN Where DO
FOR i: CARDINAL IN [0..5) DO
ProcessorFace.SetMP[mp[where] + i + device * 1000];
PauseAWhile[];
IF ~DicentraInputOutput.GetExternalStatus[].altBoot THEN
BEGIN
SELECT where FROM
ethernet => FabricateEthernetLocation[location, i];
phone => FabricatePhoneLocation[location, i];
ethernetOne => FabricateEthernetOneLocation[location, i];
ENDCASE => ERROR;
location.deviceOrdinal ← device;
ProcessorFace.SetMP[teMP];
ProcessorFace.SetMP[PilotMP.cGermAction]; -- Bug in GermOpsImpl
RETURN;
END;
ENDLOOP;
ENDLOOP;
ENDLOOP;
-- Maybe the MP is unplugged?
FabricateEthernetLocation[location, 0];
END;
PauseAWhile: PROCEDURE =
BEGIN
duration: System.Pulses = System.MicrosecondsToPulses[700000];
start: System.Pulses = System.GetClockPulses[];
UNTIL (System.GetClockPulses[]-start) > duration DO ENDLOOP;
END;
FabricateEthernetLocation: PROCEDURE [
location: LONG POINTER TO Boot.Location, bootFileOffset: CARDINAL] =
BEGIN
where: EthernetLocation;
bfn: BootFileNumberRep ← nsBootFileBase;
bfn.c ← bfn.c + bootFileOffset;
where ← [
bfn: LOOPHOLE[bfn],
address: [
net: System.nullNetworkNumber,
host: System.broadcastHostNumber,
socket: NSConstants.bootServerSocket] ];
location↑ ← [
deviceType: DeviceTypes.ethernet,
deviceOrdinal: 0,
vp: TRASH ];
LOOPHOLE[@location.vp, LONG POINTER TO EthernetLocation]↑ ← LOOPHOLE[where];
END;
FabricatePhoneLocation: PROCEDURE [
location: LONG POINTER TO Boot.Location, bootFileOffset: CARDINAL] =
BEGIN
FabricateEthernetLocation[location, bootFileOffset];
location.deviceType ← PhoneFace.phoneLine;
END;
FabricateEthernetOneLocation: PROCEDURE [
location: LONG POINTER TO Boot.Location, bootFileOffset: CARDINAL] =
BEGIN
location↑ ← [
deviceType: DeviceTypes.ethernetOne,
deviceOrdinal: 0,
vp: ethernetOne [
bootFileNumber: pupBootFileBase + bootFileOffset, net: 0, host: 0 ] ];
END;
BootFileNumber: TYPE = RECORD [System.HostNumber];
BootFileNumberRep: TYPE = MACHINE DEPENDENT RECORD [a, b, c: WORD];
EthernetLocation: TYPE = MACHINE DEPENDENT RECORD [
bfn(0): BootFileNumber, address(3): System.NetworkAddress];
defaultBootFileNumber: BootFileNumber = LOOPHOLE[nsBootFileBase];
-- This code should live in other modules. Having it here avoids dragging in trash.
eprom: DicentraInputOutput.IOAddress = MultibusAddresses.eprom;
timeout: DicentraInputOutput.IOAddress = MultibusAddresses.timeout;
DeactivateWatchdog: PROCEDURE =
BEGIN
DicentraInputOutput.Output[002H, timeout + 00CH]; -- Counter 3 Cntrl ← Trgr, Gate off
END;
eeRom: POINTER TO EERom.WholeEERom = LOOPHOLE[8000H];
writePulse: WORD = 0800H; -- NB: Low TRUE
firstChip: CARDINAL = 4;
ReadByte: PROCEDURE [loc: POINTER, offset: CARDINAL] RETURNS [data: Environment.Byte] =
BEGIN
byteIndex, chip, byteWithinChip: CARDINAL;
byteIndex ← Environment.bytesPerWord*LOOPHOLE[loc, CARDINAL] + offset;
chip ← firstChip + (byteIndex / EERom.bytesPerChip);
byteWithinChip ← byteIndex MOD EERom.bytesPerChip;
byteWithinChip ← byteWithinChip + writePulse;
DicentraInputOutput.Output[Inline.HighByte[byteWithinChip], eprom + 00DH]; -- Port A Data
DicentraInputOutput.Output[Inline.LowByte[byteWithinChip], eprom + 00EH]; -- Port B Data
DicentraInputOutput.Output[0FFH, timeout + 023H]; -- Port A Direction ← All in
DicentraInputOutput.Output[000H, eprom + 00FH]; -- Port C Data ← Output Enable
DicentraInputOutput.Output[chip, timeout + 00EH]; -- Port B Data
data ← DicentraInputOutput.Input[timeout + 00DH]; -- Port A Data
DicentraInputOutput.Output[008H, eprom + 00FH]; -- Port C Data ← No Output Enable
DicentraInputOutput.Output[00FH, timeout + 00EH]; -- Port B Data ← Idle
END;
FetchBootLocation: PROCEDURE [bits: LONG POINTER TO EERom.BootLocation] =
BEGIN
info: LONG POINTER TO PACKED ARRAY [0..0) OF Environment.Byte ← LOOPHOLE[bits];
address: POINTER ← @eeRom.bootLocation;
FOR i: CARDINAL IN [0..Environment.bytesPerWord*SIZE[EERom.BootLocation]) DO
info[i] ← ReadByte[address, i];
ENDLOOP;
END;
VersionValid: PROCEDURE [bootLocation: POINTER TO EERom.BootLocation] RETURNS [ok: BOOLEAN] =
BEGIN
RETURN[bootLocation.version = EERom.bootLocationVersion];
END;
ChecksumValid: PROCEDURE [bootLocation: POINTER TO EERom.BootLocation] RETURNS [ok: BOOLEAN] =
BEGIN
-- checksum is known to be the first word
checksum: WORD = Checksum.ComputeChecksum[0, SIZE[EERom.BootLocation]-1, bootLocation+1];
RETURN[checksum = bootLocation.checksum];
END;
END....