Google
 

Trailing-Edge - PDP-10 Archives - BB-H311C-RM - swskit-documentation/write-protect.mem
There is 1 other file named write-protect.mem in the archive. Click here to see a list.


+---------------+
! d i g i t a l !   I N T E R O F F I C E  M E M O R A N D U M
+---------------+

TO:     TOPS-20 Group
        TOPS-20 Support Group

                                       DATE: July 30, 1980

                                       FROM: Judy Hall

                                       DEPT: DEC-20 S. E. Dept.

                                       LOC: MR 1-2/E37

                                       EXT: 6421

SUBJ:  Write-protecting the Resident Monitor

Recently I changed the monitor so that resident code  could  be
write-protected.   This  document  provides some information on
what I did and some folklore that I picked up in the process.

NOTE:  The intended  audience  for  this  document  is  monitor
programmers  and  supporters.  My work does not affect users or
user documentation.  Extensive  knowledge  of  the  monitor  is
assumed.

1.0 THE OBJECTIVE

The purpose of this project was to allow the monitor to run  in
a  mode  in  which  all resident code was write-protected.  The
swappable code has long been write-protected, and we have  from
time to time detected bugs because of it.  We have also tracked
very obscure crashes to resident code that  had  been  modified
unintentionally.

2.0 THE PIECES

2.1.  Allowing writable, permanent resident data

The resident monitor consists  of  several  PSECT's,  including
RSVAR   (resident   variables)   and   RSCOD  (resident  code).
Locations are created in RSVAR through the RS macro.   However,
in the past people have sometimes defined resident data via the
BLOCK pseudo-op, causing the locations to appear in RSCOD.  One
of the reasons for this is that the RSVAR area is zeroed by the
monitor when it is coming up.  Therefore, if a location was  to
be given an initial value before monitor startup, and if it had
to be resident, the location was added to  the  resident  code.
Lots  of  other  locations  were created this way simply out of
habit.

I have  created  a  new  PSECT,  RSDAT.   It  is  resident  and
                                                         Page 2


write-enabled,  and  it  is not zeroed as the monitor comes up.
Locations are placed in this PSECT via the RSI macro, which  is
invoked as follows:

        RSI NAME,LIST,TOTAL

where

NAME is the symbol associated with the first (or only) location
        to be reserved
LIST is one or more values, to be placed  in  successive  words
        beginning with the first
TOTAL is the number of words to be  reserved.   If  it  is  not
        specified,  the number of words included in the list is
        allocated.

If more values are provided in LIST than are accounted  for  in
TOTAL, an assembly error is generated.

I have also begun the  process  of  converting  definitions  of
resident  data  (including  FFF)  to use this macro.  There are
many statements of the form
        A: BLOCK n
and
        B: C

within the RSCOD PSECT.  As long as the  monitor  never  stores
into  these  locations,  it  is  not  necessary  to  change the
definitions.  However, in the future, all resident data that is
write-enabled  should  be  placed either in the RSVAR PSECT via
the RS macro (if  the  location  should  be  zeroed  at  system
startup)  or  in  the  RSDAT  PSECT  via  the RSI macro (if the
location should not be zeroed).

Another  common  offender  was  JSR's.   The  typical  sequence
occurred in APRSRV:

        BUGMON: 0
                code
                .
                .
                .

Code to enter this routine would contain

                JSR BUGMON

which stored into this resident location.  Such  locations  are
now  two-word  blocks  defined in STG to be in the RSDAT PSECT.
The second word transfers control to the original  code.   Thus
for BUGMON, we see

        BUGMON: 0
                JRST BUGM0
                                                         Page 3


and in APRSRV, the code that was executed  now  has  the  label
BUGM0.

2.2 Write-protecting the code

Write-protecting the resident monitor occurs in two stages.  In
PGRINI,  when  the monitor's map is being established, RSCOD is
write-protected if DBUGSW is 0 or 1.  Later, when the swappable
monitor  has been read in, MEXEC calls SWPMWP if DBUGSW is 0 or
1.

SWPMWP/SWPMWE   has   been   enhanced   to   write-protect   or
write-enable  the resident monitor in addition to the swappable
monitor.  It seemed natural for the two pieces of code to be in
the  same  state.   The name of the routine is unchanged purely
because of history.  Everyone knows the  names,  and  we  often
call these routines by hand while the monitor is running.

2.3.  Dealing with DDT

Originally I changed the BUGHLT code to avoid executing  HLTADR
(where EDDT's breakpoint 12 is normally defined) if the monitor
was write-protected.  I did this for two reasons:  1) I assumed
that  the JSR would try to store into the resident monitor, and
2) I assumed  that  EDDT's  attempt  to  replace  all  resident
instructions on which there were breakpoints would fail.

I found that the first assumption is incorrect;  EDDT is in the
INCOD  PSECT,  which  is  write-enabled.   Therefore  executing
HLTADR will indeed get the monitor  into  EDDT.   However,  any
attempt  to  proceed  the  breakpoint  seems  to cause it to be
reexecuted.  Obviously DDT can't replace the JSR at HLTADR, but
I had assumed it would simulate the original instruction.

This led me to try the same thing with the  swappable  monitor,
since   that   has   been   write-protected   for   years.    I
write-enabled, set an EDDT breakpoint, write-protected, and hit
the   breakpoint.    Again   I   could   not  proceed  until  I
write-enabled  and  replaced  the   JSR   with   the   original
instruction.

The particular problem of BUGHLT's was solved as follows:   the
location on which the breakpoint is placed looks like this:

        HLTAD0::XCT HLTADR

HLTADR is a location in RSDAT  containing  a  NOP  instruction.
Thus  DDT  can  proceed  the  breakpoint  by restoring the NOP.
BUGCHK's and BUGINF's are handled similarly.

Note, however, that breakpoints in  write-protected  code  will
still exhibit the behavior I described.

2.4.  Teletype data base changes
                                                         Page 4


As part of the reorganization of  the  teletype  data  base  in
Release  3,  transfer  vectors were created for each line type.
An offset (TT1LIN) in each vector  contained  data,  which  was
initialized during system startup.  I have changed this so that
a resident table, called TT1LIN, is indexed by line  type.   It
resides  in  the  RSDAT  PSECT  and  contains the data formerly
stored into the transfer vector.

2.5 Expanding FFF

I moved the definition of FFF into POSTLD and increased  it  to
400  words.  Moving it to POSTLD should cause it to be the last
location in RSDAT.  Thus the patch space will  be  even  larger
than 400 words if there is room left on the page in which RSDAT
ends.  (Currently RSDAT is only one page long.) The increase to
400 words was requested by Doug Ruby so that customers can have
room to apply all the patches that we send them.

2.6 Cleaning up System Startup

The code in STG near SYSGO1 contains a BLT that zeroes  all  of
the  RSVAR  PSECT.   It  then  assigns  initial values to those
locations in RSVAR whose initial values are non-zero.   I  have
moved most of these locations into RSDAT, assigning the initial
values at assembly time.  Thus STG no longer initializes them.

3.0 FOLKLORE LEARNED THE HARD WAY

3.1.  Adding a PSECT

Adding a PSECT proved to be non-trivial at best.  I came upon a
lot of "unwritten rules".  This section is an attempt to record
some of them.

3.1.1.  Guiding LINK

The LINK control file (LNKMON.CCL, for example) had to  contain
the  new  PSECT.   Its  placement  was  guided by the following
considerations:

        1.  RSCOD must be first.  The monitor assumes this, and
        it assumes that MONCOR contains the highest location in
        the RSCOD PSECT.

        2.  PSECTS ending in "VAR" are assumed to be zeroed  at
        system  startup,  and the symbol table is linked on top
        of them.  Thus RSDAT had to precede  the  first  zeroed
        PSECT (PPVAR).

Since RSDAT preceded the symbol table, the /SYMSEG  switch  had
to  point  to the end of this PSECT.  And since the patch space
precedes the symbol table, the /PATCHSIZE switch was  added  to
the same line.  Of course, the starting locations of the PSECTS
also had to be changed to accommodate RSDAT.
                                                         Page 5


3.1.2.  Creating a PSECT name

Each   PSECT   has   associated   with   it    several    names
(XXXXX,XXXXXP,XXXXXL,  XXXXXZ).  The first two are generated by
the addition of the PSECT name to LDINIT  (the  PBEGIN  macro).
The last two are generated by the addition of the PSECT name to
POSTLD (the PEND macro).  And of course these names all have to
go into GLOBS.

3.1.3.  Getting the PSECT saved

It's not enough to persuade LINK to link a new PSECT;  you must
also  convince  POSTLD  to  save it when it creates the monitor
file (MONITR.EXE).  This is done via the SSEG macro.

3.1.4.  Getting the PSECT reported

When you "RUN MON", POSTLD generates  a  table  describing  the
monitor map.  A series of invocations of the PRPSC provides the
necessary data.  For each PSECT, the arguments are  that  PSECT
and  the  one that follows.  Therefore, adding a PSECT requires
both the addition of an invocation of the macro and changes  to
existing invocations.

3.1.5.  Fixing monitor startup

This varies from case to case, and getting it  wrong  can  have
confusing effects.  For RSDAT there were three parts:

        1.  Getting it into MMAP -  This  required  a  call  to
        PGRIGR,  which causes the pages to be added to MMAP and
        write-enabled.  A new entry point, PGRIGP, would  cause
        them to be write-protected.

        2.  Getting it locked in memory - This required a  call
        to  BSMGP,  providing the starting and ending addresses
        of the PSECT.  By convention, PSECTS for which this  is
        done  have  a cell defined in PAGEM that contains these
        values.

        3.  Getting it into DDT's map - MDDT and  EDDT  use  an
        alternate  map, which is pointed to by SYMBAS.  Because
        DDT uses locations in RSDAT, this PSECT had to be added
        to  DDT's  map.   This required storing some additional
        indirect pointers in DDT's map (in PGRINI).

3.2.  Patch space

PAT.., the default patch space when you  type  $<  to  DDT,  is
placed by LINK in the last n locations before the symbol table.
For years, PAT..  coincided with SWPF, but  the  reorganization
of  the  monitor's  address  space in Release 4 caused it to be
part of the INCOD PSECT.  In fact, it could wind  up  anywhere,
depending  on  the  placement  of  the symbol table during LINK
time.  (This is controlled by the /SYMSEG switch  in  the  LINK
                                                         Page 6


control file.)

Since we have both FFF and SWPF, I thought there  was  no  need
for  PAT..   at all.  The /PATCHSIZE switch in LINK establishes
the size of this  space.   I  set  it  to  1.   That's  when  I
discovered that DDT uses this space if you define a new symbol.
So we're back to a patchsize of 100.

3.3.  The meaning of DBUGSW

In the course of this project I found myself trying  to  recall
the  distinction  among  the  three  possible values of DBUGSW.
Following is a list of the modules, a label  or  routine  name,
and a brief description of each reference to DBUGSW.

DBUGSW -

APRSRV

        BUGH0 - IF DBUGSW IS 0, DON'T EXECUTE HLTADR, WHERE THE
                BP  IS.   EVEN  IF  DBUGSW  IS  NON-ZERO, DON'T
                EXECUTE  HLTADR  IF  DDTPRS  IS  0  (DDT  ISN'T
                LOCKED).

DSKALC

        FSIINI - IF DBUGSW  IS  2,  ALLOW  USER  TO  CHOOSE  TO
                REWRITE THE HOME BLOCKS

DTESRV

        TAKACK - IF DBUGSW IS 2, WAKE UP  FORK  0  TO  ACK  ALL
                DEVICES (?)

        DTEPOL - IF DBUGSW IS 2, DON'T SET THE SYSTEM DATE  AND
                TIME (?)

IMPPHY

        AN2CMD -  IF  DBUGSW  IS  NON-ZERO,  SAVE  HISTORY   OF
                COMMANDS IN A RING BUFFER

MEXEC

        SYSINP -  BEFORE  STARTING  THE  EXEC,  PRINT  [CAUTION
                --SYSTEM IS STAND-ALONE] IF DBUGSW IS 2

        RUNDD - IF DBUGSW IS 0 OR 1, PRINT "SYSTEM  RESTARTING,
                WAIT".   IF  DBUGSW  IS 2, SKIP RUNNING CHECKD.
                IF DBUGSW IS 2, PRINT [SYSTEM  IS  STAND-ALONE]
                ON ALL TERMINALS.

        LOKSM3 -  IF  DBUGSW  IS  0  OR  1,  CALL   SWPMWP   TO
                WRITE-PROTECT THE MONITOR
                                                         Page 7


NETWRK

        NETINI - IF DBUGSW IS 2, DON'T TURN ON THE NETWORK

SCHED

        SWHLT - IF DBUGSW IS NON-ZERO OR  DCHKSW  IS  NON-ZERO,
                EXECUTE  CHKADR  IN  ORDER  TO HIT A BREAKPOINT
                (BUT DON'T DO THIS IF DDTPRS IS 0)

PAGEM

        PGRINI - IF DBUGSW IS 0 OR 1, INITIALIZE RESIDENT  CODE
                TO BE WRITE-PROTECTED.  IF 2, WRITE-ENABLE IT.

In addition, SYSJOB decides what command file to read according
to   DBUGSW.    It  treats  0  and  1  as  representing  normal
time-sharing.

It seems to come down to the following:  

A.  0 is the standard value for running a time-sharing  system.
With  it,  the  monitor is write-protected, and a BUGHLT causes
the monitor to halt and be reloaded.

B.  1 is just like  0,  with  one  exception.   When  a  BUGHLT
occurs,  the  monitor  executes  HLTADR,  on  which  there is a
breakpoint.  This allows  a  monitor  wizard  to  look  at  the
problem,  gather  information,  and  possibly  get  around  the
problem, avoiding a reload.  (Note that EDDT must be in memory;
thus if DBUGSW is 1, EDDTF should be 1 also.)

C.  2 is the standard value  for  debugging.   The  monitor  is
write-enabled, and BUGHLT's execute the HLTADR breakpoint.

4.0 CONCLUSIONS

When adding resident data, you  now  have  a  choice  of  three
PSECT's:

        RSVAR -- Write-enabled
                Zeroed at system startup

        RSDAT -- Write-enabled
                Not zeroed at system startup

        RSCOD -- Write-protected
                Not zeroed at system startup