From: SMTP%"cmkrnl!cmkrnl.com!jeh@crash.cts.com" 14-SEP-1993 13:31:33.48 To: EVERHART CC: Subj: p.s. Date: Tue, 14 Sep 93 10:27:26 PDT Message-Id: <009728723541DA40.202000B4@cmkrnl.com> From: "Jamie Hanrahan, Kernel Mode Systems" Subject: p.s. To: EVERHART@arisia.gce.com X-VMS-Mail-To: UUCP%"EVERHART@arisia.gce.com" X-VMS-Mail-CC: JEH (You're back at ge? Where should I send the 8mm tape?) re intercept drivers in general... I've been doing some things where I needed to trace FDT and start I/O calls into existing drivers. My problem, as you mentioned in your writeup, was, how do we find the UCB of the intercept driver? The intercept is enabled by, and the intercept code resides in, a pseudodevice driver (call it IBDRIVER). To hook into an existing driver IBDRIVER allocates a block of pool of size target driver FDT size + 12 bytes + DDT$K_LENGTH + pool header overhead (12 bytes) + intercept data (including pointer to IB's UCB The target driver's DDT and FDT are copied to the intercept blcok. The FDT copy is the last thing in the intercept block. The FDT is not an exact copy: in between the legal function mask and the first function selection mask, IBDRIVER inserts its own function selection entry, with a mask of all 1's and a pointer to its own FDT intercept routine. DDT$L_FDT in the copy of the DDT is set to point to this new FDT, and of course UCB$L_DDT in the target's UCB is set to point to the new DDT. Now when QIO starts FDT processing on the target device, our FDT intercept routine is the first FDT routine that's run. In the FDT intercept routine, we have R8 pointing to the function selection mask that got us here. This is at a known offset from the start of the intercept block. So we can push the current register set on the stack, back up R8 to find the start of the intercept block, and from there we can find anyting else in the block, incl. pointer to IB's UCB. Start I/O is trickier. We copy the target driver's DDT to our block, and obviously we modify DDT$L_START to point to our code. The trick is in the code you point to. Here I rip off a technique from VMS's I/O interrupt dispatching: DDT$L_START in the new DDT is set to point not to somewhere inside IBDRIVER, but to the following instructions which is INSIDE the interrupt block: JSB @#somewhere 'somewhere' gets changed to actually point to the intercept code in IBDRIVER. Now, when this code is entered, we have a value on the stack that points back to the location in the intercept block just after the JSB! Since this is at a known offset into the intercept block, we can find the rest of the block. Including the pointer back to IB's UCB! Naturally the first thing we do is save R3 and R5 on the stack. This means we have to use 8(SP) to get the pointer back to the IB, but no problem. To allow the real driver's start I/O routine to continue after I've written my trace: When enabling the intercept I saved the real start I/O routine address in the interrupt block, so: MOVL intblock_l_savedstartio(Rn), 8(SP) ; allow for saved Rs POPL R3 POPL R5 RSB and the start I/O routine runs, and never knows anything different. Using a JSB this way would work for the FDT intercept too, but probably isn't necessary. The intercept blocks are queued to a listhead in the intercept driver's UCB. The controller init routine in the intercept driver has to be able to follow this queue and fix up the FDT and start I/O routine pointers if the intercept driver is reloaded. I've used this trick of "code in a data structure" before. When I queue an internal IRP to NETDRIVER's alternate entry point, with IRP$L_PID having the S0 address of my I/O completion routine, ordinarily I would have to make my driver non-reloadable unless I am sure that there are no IRPs out there pointing to me. Instead I set IRP$L_PID to point to a location in my UCB. The location in the UCB has a JMP @# to my routine. The controller init routine fixes the target of this jump so that things keep working if my driver is reloaded "out from under" the existing internal IRPs. Dave Cutler used this same stunt (calling the driver ISR from an instruction inside the "interrupt object", so that the return PC that's pushed on the stack lets you find the interrupt object and hence the rest of the relevant data structures) in NT's interrupt dispatching code, so I guess he still likes it after all these years! --- Jamie Hanrahan, Kernel Mode Systems, San Diego CA Internet: jeh@cmkrnl.com (JH645) Uucp: uunet!cmkrnl!jeh CIS: 74140,2055