Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_SRC_3_19910112
-
stanford/5-swskit/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