-- Copyright (C) 1983  by Xerox Corporation. All rights reserved. 
-- PupBootServer.mesa, HGM, 18-Jun-85  5:58:57

DIRECTORY
  Process USING [Detach],
  String USING [AppendChar],

  BootServerDefs USING [
    pleaseStop, booting, slowBooting, statBootDir, statFile, statSun, statFileSent,
    statFileSentSlow, statMicrocodeBooted, statSlowMicrocodeBooted,
    statFileTroubles, statFileNeverStarted,
    statBusyDisk, statBusyBooting, statUnknown, FastBooter, SlowBooter,
    MicrocodeBooter, LookAtBootDir, bootStatsRequest, BootServerStats,
    lockBooterRequest, lockBooterReply, unlockBooterRequest, unlockBooterReply,
    microcodeRequest, microcodeVersionNumber,
    SlowMicrocodeBooter, slowMicrocodeRequest, sunBootRequest],
  BootServer USING [],
  BootServerBasics USING [BootFileNumber],
  BootServerFriends USING [BootFile, FindBootFile, FindBootFileByName, GetPointerToBootTable],
  Buffer USING [AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer],
  PupWireFormat USING [BcplLongNumber, MesaToBcplLongNumber, BcplSTRING],
  PupDefs USING [
    GetPupContentsBytes, Pair, PupAddress, PupBuffer,
    PupRouterBroadcastThis, PupRouterSendThis, ReturnPup, FastPath,
    SetPupContentsWords, SwapPupSourceAndDest],
  PupTypes USING [maxDataWordsPerGatewayPup, miscSrvSoc],
  Stats USING [StatIncr];
  
PupBootServer: PROGRAM
  IMPORTS
    Process, String,
    BootServerDefs, BootServerFriends, Buffer, PupWireFormat, PupDefs, Stats
  EXPORTS BootServer, BootServerDefs
  SHARES Buffer =
  -- network
  BEGIN OPEN Stats, PupDefs, BootServerDefs;

  BootFileNumber: TYPE = BootServerBasics.BootFileNumber;

  -- maybe we should allow several slow booters
  lock: BOOLEAN ← FALSE;

  -- This won't work with small buffers
  dwpb: CARDINAL = PupTypes.maxDataWordsPerGatewayPup;

  microcodeOffset: CARDINAL = 3000B;

  PupBootServer: PUBLIC PROCEDURE [b: PupBuffer] =
    BEGIN
    SELECT b.pup.pupType FROM
      bootFileSend => SendBootFile[b];
      microcodeRequest => SendMicrocode[b, FALSE];
      slowMicrocodeRequest => SendMicrocode[b, TRUE];
      bootDirReq => SendBootDir[b];
      bootDirReply =>
        BEGIN
        IF lock OR pleaseStop THEN Buffer.ReturnBuffer[b] ELSE LookAtBootDir[b];
        END;
      bootStatsRequest => BootServerStats[b];
      lockBooterRequest =>
        BEGIN lock ← TRUE; ReturnPup[b, lockBooterReply, 0]; END;
      unlockBooterRequest =>
        BEGIN lock ← FALSE; ReturnPup[b, unlockBooterReply, 0]; END;
      sunBootRequest => SendSunBootFile[b];
      ENDCASE => Buffer.ReturnBuffer[b];
    END;

  SendBootFile: PROCEDURE [b: PupBuffer] =
    BEGIN
    bf: BootServerFriends.BootFile;
    him: PupAddress;
    slow: BOOLEAN;
    code: WORD = b.pup.pupID.b;
    SwapPupSourceAndDest[b];  -- fixup defaults
    him ← b.pup.dest;
    Buffer.ReturnBuffer[b];
    IF lock THEN RETURN;
    slow ← ~FastPath[him];
    IF (booting AND ~slow) OR (slowBooting AND slow) THEN
      BEGIN StatIncr[statBusyBooting]; RETURN; END;
    IF (bf ← BootServerFriends.FindBootFile[CardinalToPupBFN[code]]) = NIL
      OR ~bf.pup THEN BEGIN StatIncr[statUnknown]; RETURN; END;
    StatIncr[statFile];
    IF slow THEN slowBooting ← TRUE ELSE booting ← TRUE;
    Process.Detach[FORK Booter[bf, him, IF slow THEN slow ELSE fast]];
    END;

  SendMicrocode: PROCEDURE [b: PupBuffer, slow: BOOL] =
    BEGIN
    bf: BootServerFriends.BootFile;
    him: PupAddress;
    version: WORD ← b.pup.pupID.a;
    code: WORD = microcodeOffset + b.pup.pupID.b;
    SwapPupSourceAndDest[b];  -- fixup defaults
    him ← b.pup.dest;
    Buffer.ReturnBuffer[b];
    IF lock THEN RETURN;
    IF version # microcodeVersionNumber THEN RETURN;
    IF booting THEN BEGIN StatIncr[statBusyBooting]; RETURN; END;
    IF (bf ← BootServerFriends.FindBootFile[CardinalToPupBFN[code]]) = NIL
      OR ~bf.pup THEN BEGIN StatIncr[statUnknown]; RETURN; END;
    StatIncr[statFile];
    booting ← TRUE;
    Process.Detach[FORK Booter[bf, him, IF slow THEN slowMicro ELSE micro]];
    END;

  SendSunBootFile: PROCEDURE [b: PupBuffer] =
    BEGIN
    bf: BootServerFriends.BootFile;
    him: PupAddress;
    slow: BOOLEAN;
    name: STRING = [100];
    SwapPupSourceAndDest[b];  -- fixup defaults
    him ← b.pup.dest;
    FOR i: CARDINAL IN [0..PupDefs.GetPupContentsBytes[b]) DO
      IF i > name.maxlength THEN LOOP;  -- Truncate
      String.AppendChar[name, b.pup.pupChars[i]];
      ENDLOOP;
    Buffer.ReturnBuffer[b];
    IF lock THEN RETURN;
    slow ← ~FastPath[him];
    IF (booting AND ~slow) OR (slowBooting AND slow) THEN
      BEGIN StatIncr[statBusyBooting]; RETURN; END;
    IF (bf ← BootServerFriends.FindBootFileByName[name]) = NIL
      OR ~bf.pup THEN BEGIN StatIncr[statUnknown]; RETURN; END;
    StatIncr[statSun];
    IF slow THEN slowBooting ← TRUE ELSE booting ← TRUE;
    Process.Detach[FORK Booter[bf, him, IF slow THEN slow ELSE fast]];
    END;

  Booter: PROCEDURE [
    bf: BootServerFriends.BootFile, who: PupAddress, what: {fast, slow, micro, slowMicro}] =
    BEGIN
    StatIncr[
      SELECT
      (SELECT what FROM
         fast => FastBooter[bf, who],
         slow => SlowBooter[bf, who],
         micro => MicrocodeBooter[bf, who],
         slowMicro => SlowMicrocodeBooter[bf, who],
         ENDCASE => ERROR) FROM
        fast => statFileSent,
        slow => statFileSentSlow,
        micro => statMicrocodeBooted,
        slowMicro => statSlowMicrocodeBooted,
        diskBusy => statBusyDisk,
        neverStarted => statFileNeverStarted,
        troubles => statFileTroubles,
        ENDCASE => ERROR];
    IF what = slow THEN slowBooting ← FALSE ELSE booting ← FALSE;
    END;


  first: LONG POINTER TO BootServerFriends.BootFile =
    BootServerFriends.GetPointerToBootTable[];

  -- If b is NIL, then we broadcast the info.
  SendBootDir: PUBLIC PROCEDURE [b: PupBuffer] =
    BEGIN
    pool: Buffer.AccessHandle ← NIL;
    me, him: PupAddress;
    word, size: CARDINAL ← 0;
    id: Pair;
    broadcast: BOOLEAN ← b = NIL;
    IF lock THEN BEGIN IF b # NIL THEN Buffer.ReturnBuffer[b]; RETURN; END;
    IF ~broadcast THEN
      BEGIN
      id ← b.pup.pupID;
      SwapPupSourceAndDest[b];  -- fixup broadcast
      me ← b.pup.source;
      him ← b.pup.dest;
      END
    ELSE
      BEGIN
      id ← [0, 0];
      pool ← Buffer.MakePool[send: 1, receive: 0];
      b ← Buffer.GetBuffer[pup, pool, send];
      me.socket ← him.socket ← PupTypes.miscSrvSoc;
      END;
    FOR bf: BootServerFriends.BootFile ← first↑, bf.next UNTIL bf = NIL DO
      IF bf.unknown OR ~bf.pup THEN LOOP;
      size ← ((1 + bf.fileName.length) + 1)/2;
      IF (word + size + 1 + 2) > dwpb THEN
        BEGIN  -- this buffer is full, send it and get another one
        b.pup.pupType ← bootDirReply;
        b.pup.pupID ← id;
        SetPupContentsWords[b, word];
        b.pup.dest ← him;
        b.pup.source ← me;
        IF broadcast THEN PupRouterBroadcastThis[b] ELSE PupRouterSendThis[b];
        word ← 0;
	IF pool = NIL THEN pool ← Buffer.MakePool[send: 1, receive: 0];
        b ← Buffer.GetBuffer[pup, pool, send];
        END;
      b.pup.pupWords[word] ← PupBFNToCardinal[bf.code];
      LOOPHOLE[@b.pup.pupWords[word + 1], LONG POINTER TO
        PupWireFormat.BcplLongNumber]↑ ← PupWireFormat.MesaToBcplLongNumber[
        bf.create];
      CopyString[bf.fileName, LOOPHOLE[@b.pup.pupWords + word + 1 + 2]];
      word ← word + size + 1 + 2;
      ENDLOOP;
    b.pup.pupType ← bootDirReply;
    b.pup.pupID ← id;
    SetPupContentsWords[b, word];
    b.pup.dest ← him;
    b.pup.source ← me;
    IF broadcast THEN PupRouterBroadcastThis[b] ELSE PupRouterSendThis[b];
    StatIncr[statBootDir];
    IF pool # NIL THEN Buffer.DestroyPool[pool];
    END;

  CopyString: PROCEDURE [
    s: LONG STRING, d: LONG POINTER TO PupWireFormat.BcplSTRING] =
    BEGIN
    d.length ← s.length;
    FOR i: CARDINAL IN [0..s.length) DO d.char[i] ← s[i]; ENDLOOP;
    END;

  magicSecondWord: WORD = 125026B;
  CardinalToPupBFN: PUBLIC PROCEDURE [n: CARDINAL] RETURNS [BootFileNumber] =
    BEGIN
    foo: RECORD [a, b, c: WORD] = [0, magicSecondWord, n];
    RETURN[LOOPHOLE[foo]];
    END;

  NotPupBootFileNumber: ERROR = CODE;
  PupBFNToCardinal: PUBLIC PROCEDURE [bfn: BootFileNumber] RETURNS [CARDINAL] =
    BEGIN
    foo: RECORD [a, b, c: WORD] = LOOPHOLE[bfn];
    IF foo.b # magicSecondWord THEN ERROR NotPupBootFileNumber;
    RETURN[foo.c];
    END;

  END.