home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #20 / NN_1992_20.iso / spool / vmsnet / internal / 1306 < prev    next >
Encoding:
Internet Message Format  |  1992-09-11  |  22.4 KB

  1. Path: sparky!uunet!usc!sdd.hp.com!swrinde!network.ucsd.edu!mvb.saic.com!macro32
  2. From: raxco!galaxy.dnet!gleeve@uunet.UU.NET
  3. Newsgroups: vmsnet.internals
  4. Subject: Proposed methods for stealing DDT/FDT/etc.
  5. Message-ID: <9209102308.AA12245@relay1.UU.NET>
  6. Date: 10 Sep 92 21:21:31 GMT
  7. Organization: Macro32<==>Vmsnet.Internals Gateway
  8. Lines: 474
  9. X-Gateway-Source-Info: Mailing List
  10.  
  11. Subj:   RE: moving DDT for CDdriver etc.
  12.  
  13.    When building I/O intercept drivers to steal driver entry
  14. points, it is a problem to figure out how to tell a bit of intercept
  15. code where its' data is. If the intercept code is inside a device
  16. driver, one would like to find the driver UCB, but this is difficult
  17. in general. One can build a hash table of some sort, or perhaps
  18. search the I/O database for the unit of your intercept driver that
  19. points to the "victim" device, or use some otherwise unused UCB
  20. fields in the victim driver to point to the intercept UCB. These
  21. methods are either slow or vulnerable to collisions with other
  22. code, however.
  23.    Paul Sorensen made the suggestion to put a DDT copy physically
  24. into the UCB of the intercept driver so the victim device UCB
  25. pointer ucb$l_ddt will now point to a known offset within the intercept
  26. UCB. This gives an apparently robust way of locating the intercept
  27. UCB for one interceptor. The present discussion has to do with some
  28. extensions to the idea so that multiple intercept drivers handling
  29. I/O to a single device can be handled.
  30.    Such suggestions must be public and freely usable by anyone or
  31. they are useless. The suggestions here can be used for stealing
  32. FDT entries or start-io entries of normal drivers, and for other
  33. entry stealing where it is of any interest.
  34.    While the code is not tested yet, I am offering it for general
  35. interest now rather than later; I expect to use it myself shortly
  36. and will correct any bugs I find. The ideas came from me, from Jon
  37. Pinkley, and from Paul.
  38.  
  39.    The idea of pointing ucb$l_ddt at a DDT that physically resides
  40. in a UCB of some other device like CDdriver is a wonderful way to
  41. make finding the corresponding device fast and reliable.
  42.    It suffers from the disadvantage that it works for ONE driver
  43. only as proposed. That is, if I come along and try to steal the
  44. DDT again and happen to use the original driver's UCB$L_DDT
  45. link, the new DDT winds up in MY UCB, and CDdriver will find the
  46. DDT in the wrong place.
  47.    The example code here involves some chaining, but should scale
  48. well and provide fairly fast access to intercept data, while not
  49. breaking every time a second unrelated application tries to patch
  50. the same driver structures.
  51.  
  52.    Where a DDT or any of its fields need to be modified, the suggestion
  53. here is to put a copy of that DDT into the UCB of the intercept driver
  54. that holds the code that is being inserted. Using a driver for such
  55. patched code makes it very simple to track where the code is, and the
  56. UCB of the driver provides a convenient spot for locating data that
  57. must be kept track of for each unit. Process-specific or channel
  58. specific structures' listheads can also be kept there.
  59.    This note is offered to the public in the hope that a standard
  60. can be worked out, or that it can be used as one, as is, so that
  61. code that must steal DDTs or FDTs can interoperate no matter who
  62. the author is. So far Jon Pinkley and Glenn Everhart have contributed
  63. to the macro32-list discussions on this, and the code examples are
  64. provided by one of us (gce) to assist in the use of the ideas.
  65.  
  66.    The examples would live as pieces of a device driver for some
  67. intercept device. The relevant parts of UCB extension definitions,
  68. of the DPT_STORE macro, and the bits of code are provided here.
  69. These bits of code serve to steal a DDT from some victim driver and
  70. place it in the UCB of the intercept device, to locate the intercept
  71. UCB when executing intercept code with R5 pointing to the victim
  72. device's UCB, and to remove an intercepted DDT from the chain of
  73. these when it is necessary to disconnect. The FDT code replacement
  74. is embedded in the example code, so it can be seen how such FDT
  75. additions can be made using these methods. In practice the DDT and
  76. the FDT are obtained via the UCB pointer, which is why the DDB
  77. pointer is left intact.
  78.  
  79.                         NOTE!!!
  80.  
  81.    THIS CODE IS ALPHA EXAMPLES at this point, not final and tested
  82. code. It is intended to be approximately correct, but another note
  83. will be issued when it is tested. In particular it needs to have
  84. "XX" replaced by proper device name characters here and there,
  85. and needs several more $xxxDEF calls. It may also need assorted
  86. bugfixes.
  87.  
  88.  
  89.    The notion is to produce a UCB extension that looks like this:
  90.  
  91.         .long   ident-pattern        ; unique to driver that owns this ucb
  92.         .long   intercept-DDT-address; This is modified by another intercept
  93.                                      ; driver that intercepts the victim UCB
  94.                                      ; after we do.  This allows us to find
  95.                                      ; the our interceptor's DDT.
  96.         .long   previous-DDT-address ; where to find the next startio addr
  97.         .long   intercept-signature   ; same for all intercept drivers
  98. ucb$a_victimddt:
  99.         .blkb   ddt$k_length         ; note this changed in V5.5
  100.  
  101. As a particular example, one might have a UCB extension that would look
  102. like this:
  103.  
  104.         $ucbdef
  105.  
  106.         $defini UCB
  107. .=ucb$k_lcl_disk_length
  108. ;   other $def macros as desired
  109. ;
  110. ; DDT intercept fields
  111. ; following must be contiguous.
  112. $def    ucb$l_uniqid    .blkl   1       ;driver-unique ID, gets filled in
  113.                                         ; by DPT address for easy following
  114.                                         ; by SDA
  115. $def    ucb$l_intcddt   .blkl   1       ; Our interceptor's DDT address if
  116.                                         ; we are intercepted
  117. $def    ucb$l_prevddt   .blkl   1       ; previous DDT address
  118. $def    ucb$l_icsign    .blkl   1       ; unique pattern that identifies
  119.                                         ; this as a DDT intercept block
  120. $def    ucb$a_vicddt    .blkb   ddt$k_length
  121.                                         ; space for victim's DDT
  122.  
  123.         $defend UCB
  124.  
  125. ; Then at the DPT_STORE macros, some like this after the
  126. ; DPT_STORE REINIT call.
  127.  
  128.         DPT_STORE UCB,UCB$L_UNIQID,L,XX$DPT     ;store DPT address
  129.                                                 ; (change "XX" to device
  130.                                                 ; mnemonic correct values)
  131.         DPT_STORE UCB,UCB$L_ICSIGN,L,X^F012F024 ; Add unique pattern (that might
  132.                                                 ; bring back some memories in
  133.                                                 ; DOS-11 users)
  134.  
  135. ; HISTORICAL NOTE: under DOS-11, one would get F012 and F024 errors
  136. ; on odd address and illegal instruction traps. If we don't have
  137. ; this magic number HERE, on the other hand, we're likely to see
  138. ; bugchecks in VMS due to uncontrolled bashing of UCB fields!
  139.  
  140. The driver dispatch table points at various driver addresses. If
  141. this sort of thing is used, one should be aware that reloads of
  142. the victim driver will cause trouble, and reloads of intercept
  143. drivers can. It is probably wise to inhibit reloading of drivers
  144. whose DDTs are moved by this sort of code. The same is probably wise
  145. for intercept drivers unless they can be VERY careful about their
  146. FDT fields, and those of anyone who has stolen them, and about
  147. other entries, when they are reloaded. (If I reset the DDT FDT
  148. pointer (ddt$l_fdt) to point into my intercept driver, someone
  149. intercepts that and gets a pointer to my FDT code, and then I
  150. reload my intercept driver and move the FDT code, clearly the
  151. external pointer to me can cause problems. I can however unlink
  152. everything and relink later if my driver is reloaded.)
  153.  
  154.  
  155.    With such a standard in force, if I find the DDT from ucb$l_ddt
  156. of the victim device, I can check ident-pattern against my
  157. own program's pattern, and if it matches I just go ahead and use
  158. offsets from there to find my driver's UCB and thus its data
  159. (e.g. the CDdriver UCB). If it does not match, I chain using
  160. the previous-DDT-address and keep looking for my pattern, and
  161. when I find it (in usually one or two tries at most), I have
  162. found my UCB and can go on from there.
  163.    The pattern choice is arbitrary, but in this example is set
  164. to the DPT address since that makes SDA dumps show the intercept
  165. driver name, facilitating its use.
  166.         For such a standard to work, it must be used by anyone stealing
  167. the DDT for whatever purpose. This note is written in the hope this
  168. will be done and will impact user code as little as possible.
  169.  
  170.    I would suggest a similar strategy be used for stealing
  171. FDT table bits also. These can be made to coexist rather nicely
  172. if one has a structure somewhat along these lines for FDT routines
  173. to be added to a driver's FDT chain. In the examples here, the
  174. FDT chain just gets added to automatically in virtue of moving
  175. the DDT and chains "by itself" because that is the nature of the
  176. FDT scan.
  177.  
  178.  Note the identifier isn't really needed for functions but can be used
  179. for manipulations of FDT code if the FDT pieces live outside a driver.
  180.   Inside a driver they can be given the value of the DPT address as in
  181. the example code here.
  182.  
  183. ; offsets to FDT chaining data
  184. fdt_prev = -4
  185. fdt_idnt = -8
  186.         .long   ident-pattern; unique for each application
  187.         .long   old-fdt-table-address-from-before-patch
  188. xx_functable:
  189. newfdt: functab ,<mask for disk legal functions>
  190.         functab ,<mask for disk buffered functions>
  191.         functab myswitch,<mask of all 1's>
  192. myfdtbgn=.
  193.         functab mypatch1,<mask>
  194.         functab mypatch2,<mask>
  195.         ...
  196.         functab mypatchn,<mask>
  197. myfdtend=.
  198.         functab to_orig,<mask of all 1's>
  199.  
  200. ; myswitch -
  201. ;   Based on state of "myonoff" variable either enable or disable
  202. ; my FDT processing, allowing the FDT chain to remain always intact.
  203. ; This needs to be the first of a chain of FDT entries added to the
  204. ; FDT processing of a driver.
  205. myswitch:
  206.         tstl    myonoff
  207.         bneq    1$
  208.         rsb
  209. 1$:     addl2   #<myfdtend-myfdtbeg>,r8
  210.         rsb
  211.  
  212. ; to_orig -
  213. ;  This entry continues FDT processing at the point after the new
  214. ; entries by returning to the original FDT chain at the point where
  215. ; that chain begins. (It is presumed that FDT entries will always be
  216. ; added ahead of existing ones due to the nonreturning nature of
  217. ; FDT processing.) This is done instead of simply duplicating the
  218. ; DEC FDT entries because in this way multiple FDT patches can
  219. ; coexist, as would be impossible if this trick were not used. As
  220. ; can be seen, its overhead is minimal.
  221. to_orig:
  222.         movl    newfdt+fdt_prev,r8      ;point to original FDT point
  223.         addl2   #<16-12>,r8      ;pass the 2 entry masks
  224.                                  ;back up since sysqioreq adds 12
  225.         rsb                      ;off to the previous FDT routines.
  226. myonoff: .long   0       ;my global switch, can be before or
  227.                          ;after the rest, doesn't matter since only
  228.                          ;my code knows about it.
  229.  
  230.  
  231.  
  232.  
  233.  
  234.         Now in order for an intercept driver to steal entries from
  235. a victim driver, it would use code something like the following,
  236. in kernel mode. The initialization is outside the scope of the
  237. discussion.
  238.  
  239. ; steal DDT from host. Assumes that the intercept UCB address
  240. ; is in R5 (that is, the UCB in which we will place the DDT copy),
  241. ; and that the UCB of the device whose DDT we are stealing is
  242. ; pointed to by R11. All registers are preserved explicitly so that
  243. ; surrounding code cannot be clobbered. R0 is returned as a status
  244. ; code so that if it returns with low bit clear, it means something
  245. ; went wrong so the bash did NOT occur. This generally means some other
  246. ; code that does not follow this standard has grabbed the DDT already.
  247. ; The following example assumes the code lives in a driver so the
  248. ; unique ID field and magic number are set already.
  249.  
  250.         pushr   #^m<r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>
  251. ; Acquire victim's fork lock to synchronize all this.
  252.         movl    #ss$_normal,r0          ;assume success
  253.         forklock lockaddr=ucb$b_flck(r11),-
  254.          savipl=-(sp),preserve=YES
  255. ; find the current DDT address from the UCB (leaving the copy in
  256. ; the DDB alone)
  257.         movl    ucb$l_ddt(r11),r10      ;point at victim's DDB
  258. ; see if this DDT is the same as the original
  259.         movl    ucb$l_ddb(r11),r9       ;the ddb$l_ddt is the original
  260.         cmpl    ddb$l_ddt(r9),r10       ;bashing driver the first time?
  261.         beql    1$                      ;if eql yes
  262. ; driver was bashed already. Check that the current basher followed the
  263. ; standard. Then continue if it looks OK.
  264. p.magic=x^F012F024
  265.         cmpl    <ucb$l_icsign-ucb$a_vicddt>(r10),#p.magic
  266.                                         ;does the magic pattern exist?
  267. ; if magic pattern is missing things are badly messed.
  268.         beql    2$                      ;if eql looks like all's well
  269.         movl    #2,r0                   ;say things failed
  270.         brw     100$                    ;(brb might work too)
  271. 2$:
  272. ; set our new ddt address in the previous interceptor's slot
  273.         movab   ucb$a_vicddt(r5),<ucb$l_intcddt-ucb$a_vicddt>(r10)
  274.                                         ;store next-DDT address relative
  275.                                         ;to the original victim one
  276. 1$:
  277.         movl    r10,ucb$l_prevddt(r5)   ;set previous DDT address up
  278.         clrl    ucb$l_intcddt(r5)       ;clear intercepting DDT initially
  279. 3$:
  280.         pushl   r5
  281.         movc3   #ddt$k_length,(r10),ucb$a_vicddt(r5)    ;copy the DDT
  282.         popl    r5                      ;get UCB pointer back (movc3 bashes it)
  283. ;
  284. ; Here make whatever mods to the DDT you need to.
  285. ;
  286. ; FOR EXAMPLE make the following mods to the FDT pointer
  287. ; (These assume the standard proposed for FDT pointers)
  288.         movab   ucb$a_vicddt(r5),r8     ;get a base register for the DDT
  289.         movl    ddt$l_fdt(r10),xx_functable+fdt_prev    ;save old FDT address
  290.         movl    ucb$l_uniqid(r5),xx_functable+fdt_idnt ;save unique ID also
  291.         movab   xx_functable,ddt$l_fdt(r8) ;point at our FDT table
  292.         clrl    myonoff                 ;turn my FDTs on
  293. ;
  294. ; Finally clobber the victim device's DDT pointer to point to our new
  295. ; one.
  296.         movab   ucb$a_vicddt(r5),ucb$l_ddt(r11)
  297. ; Now the DDT used for the victim device unit is that of our UCB
  298. ; and will invoke whatever special processing we need. This processing in
  299. ; the example here causes the intercept driver's FDT routines to be
  300. ; used ahead of whatever was in the original driver's FDTs. Because
  301. ; the DDT is modified using the UCB pointer only, target device units
  302. ; that have not been patched in this way continue to use their old
  303. ; DDTs and FDTs unaltered.
  304. ;
  305. ; Processing complete; release victim's fork lock
  306. 100$:
  307.         forkunlock lockaddr=ucb$b_flck(r11),newipl=(sp)+,-
  308.          preserve=YES
  309.         popr    #^m<r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>
  310.  
  311.  
  312.  
  313. To locate the intercept UCB address from the DDT address, it is
  314. best to hold the forklock, so that in case someone tries to
  315. modify the list the lookups will not get clobbered. This code
  316. can be run from basically any desired IPL at start inside an
  317. intercept driver (DEC does it at fdt time in some examples).
  318.    Note that (omitting the error check code) the search loop
  319. is only
  320.  
  321. 2$: cmpl ...
  322.     beql 1$
  323.     movl ...
  324.     brb 2$
  325. 1$:
  326.  
  327.    which is really quite efficient.
  328.  
  329.  
  330. If we are in a bit of code that runs in an intercept driver, something
  331. like the following sequence could locate the intercept driver UCB
  332. and return its address in R11, assuming the standard above has been
  333. followed:
  334.  
  335. ; Assumes that R5 is the UCB address of the device that has had some
  336. ; code intercepted and that we are in some bit of code that knows
  337. ; it is in an intercept driver. Also assumes R11 may be used as
  338. ; scratch registers (as is true in FDT routines). Control returns at
  339. ; label "err" if the DDT appears to have been clobbered by
  340. ; something not following this standard, if conditional "chk.err"
  341. ; is defined.
  342. ;       Entry: R5 - victim device UCB address
  343. ;       Exit: R11 - intercept driver UCB address
  344.  
  345.         pushl   r10
  346.         movl    ucb$l_ddt(r5),r10       ;get the DDT we currently have
  347.         movab   xx$dpt,r11              ;magic pattern is DPT addr.
  348. ; lock this section with forklock so we can safely remove
  349. ; entries at fork also. Use victim device forklock.
  350.         forklock lock=ucb$b_flck(r5),savipl=-(sp),preserve=YES
  351. 2$:     cmpl    <ucb$l_uniqid-ucb$a_vicddt>(r10),R11
  352.                                         ;this our own driver?
  353.         beql    1$                      ;if eql yes, end search
  354.         .if     df,chk.err
  355. p.magic=x^F012F024
  356.         cmpl    <ucv$l_icsign-ucb$a_vicddt>(r10),#p.magic
  357.         bneq    err                     ;exit if this is nonstd bash
  358.         .endc   ;chk.err
  359. ; follow DDT block chain to next saved DDT.
  360.         movl    <ucb$l_prevddt-ucb$a_vicddt>(r10),r10
  361.                                         ;point R10 at the next DDT in the
  362.                                         ;chain
  363.         .if     df,chk.err
  364.         bgeq    err                     ; (error check if not negative)
  365.         .endc   ;chk.err
  366.         brb     2$                      ;then check again
  367. 1$:
  368. ; At this point R10 contains the DDT address within the intercept
  369. ; driver's UCB. Return the address of the intercept driver's UCB next.
  370.         movab   <0-ucb$a_vicddt>(r10),r11       ;point R11 at the intercept UCB
  371.         forkunlock lock=ucb$b_flck(r5),newipl=(sp)+,preserve=YES
  372.         popl    r10
  373.  
  374. ; xx$dpt addr is appl magic number, and the name must be defined
  375. ; within the intercept driver. (replace "xx" by the appropriate
  376. ; characters.)
  377.  
  378.  
  379.  
  380. To remove an entry from this DDT chain, we must synchronize again at
  381. fork IPL. The back chain can then be used to replace the entry in the
  382. UCB with the one needed. If we assume we enter with R5 as the victim
  383. driver UCB address, an entry can be removed like this:
  384. ;
  385. ; Entry: R5 points at victim device UCB and current driver is the one
  386. ; desiring to remove its entry from the DDT chain. Thus its xx$dpt: address
  387. ; is the one being sought. ("Current driver" here means the intercept
  388. ; driver.)
  389. ;   It is assumed that the driver knows that the DDT chain was patched
  390. ; so that its UCB contains an entry in the DDT chain
  391.         pushr   #^m<r0,r1,r2,r3,r10,r11>
  392.         movl    ucb$l_ddt(r5),r10       ;get the DDT we currently have
  393.         movl    ucb$l_ddb(r5),r1        ;get ddb of victim
  394.         movl    ddb$l_ddt(r1),r1        ;and real original DDT
  395.         movl    r10,r0                  ;save ucb$l_ddt addr for later
  396.         movab   xx$dpt,r11              ;magic pattern is DPT addr.
  397. ; lock this section with forklock so we can safely remove
  398. ; entries at fork also. Use victim device forklock.
  399.         forklock lock=ucb$b_flck(r5),savipl=-(sp),preserve=YES
  400. 2$:     cmpl    <ucb$l_uniqid-ucb$a_vicddt>(r10),R11
  401.                                         ;this our own driver?
  402.         beql    1$                      ;if eql yes, end search
  403.         .if     df,chk.err
  404. p.magic=x^F012F024
  405.         cmpl    <ucv$l_icsign-ucb$a_vicddt>(r10),#p.magic
  406.         bneq    err                     ;exit if this is nonstd bash
  407.         .endc   ;chk.err
  408. ; follow DDT block chain to next saved DDT.
  409.         movl    <ucb$l_prevddt-ucb$a_vicddt>(r10),r10
  410.                                         ;point R10 at the next DDT in the
  411.                                         ;chain
  412.         .if     df,chk.err
  413.         bgeq    err                     ; (error check if not negative)
  414.         .endc   ;chk.err
  415.         brb     2$                      ;then check again
  416. 1$:
  417. ; At this point R10 contains the DDT address within the intercept
  418. ; driver's UCB. Return the address of the intercept driver's UCB next.
  419.         tstl    <ucb$l_intcddt-ucb$a_vicddt>(r10)       ;were we intercepted?
  420.         bgeq    3$                      ;if geq no, skip back-fixup
  421. ; we were intercepted. Fix up next guy in line.
  422.         movl    <ucb$l_intcddt-ucb$a$_vicddt>(r10),r11  ;point at interceptor
  423.         movl    <ucb$l_prevddt-ucb$a_vicddt>(r10),<ucb$l_prevddt-ucb$a_vicddt>(r
  424. 11)
  425.                                         ;copy our prior DDT ptr to next one
  426.  
  427. 3$:
  428. ; if we intercepted someone, fix up our intercepted victim to skip by
  429. ; us also.
  430.         movl    <ucb$l_prevddt-ucb$a_vicddt>(r10),r2    ;did we intercept
  431.                                         ;original driver?
  432.         cmpl    r2,r1                   ;test if this is original
  433.         beql    5$                      ;if eql yes, no bash
  434. ; replace previous intercept address by ours (which might be zero)
  435.         movl    <ucb$l_intcddt-ucb$a$_vicddt>(r10),<ucb$l_intcddt-ucb$a_vicddt>(
  436. r2)
  437. 5$:
  438. ; Here remove FDT entries from the list if they were modified.
  439. ; This needs a scan of the FDT chain starting at the victim's
  440. ; ddt$l_fdt pointer and skipping around any entry that has address
  441. ; xx_functable:
  442. ;  The FDT chain is singly linked. The code here assumes everybody
  443. ; plays by the same rules!
  444. ; NOTE: Omit this code if we didn't insert our FDT code in the chain!!!
  445.         movl    ddt$l_fdt(r0),r1        ;start of FDT chain
  446.         movab   xx_functable,r2         ;address of our FDT table
  447.         clrl    r3
  448. 6$:     cmpl    r1,r2                   ;current fdt point at us?
  449.         beql    7$                      ;if eql yes, fix up chain
  450.         movl    r1,r3                   ;else store last pointer
  451.         movl    fdt_prev(r1),r1         ;and point at next
  452.         bgeq    8$                      ;if not sys addr, no messin'
  453.         brb     6$                      ;look till we find one.
  454. 7$:
  455. ;r3 is 0 or fdt pointing to our block next
  456. ;r1 points at our fdt block
  457.         tstl    r3                      ;if r3=0 nobody points at us
  458.         bgeq    8$                      ;so nothing to do
  459.         movl    fdt_prev(r1),fdt_prev(r3)               ;else point our next-fdt
  460.  pointer at
  461.                                         ;last fdt addr.
  462. 8$:
  463. ;
  464. ; Finally if the victim UCB DDT entry points at ours, make it point at
  465. ; our predecessor. If it points at a successor, we can leave it alone.
  466.         cmpl    r10,r0                  ;does victim ucb point at our DDT?
  467.         bneq    4$                      ;if not cannot replace it
  468.         movl    <ucb$l_prevddt-ucb$a_vicddt>(r10),ucb$l_ddt(r5)
  469. 4$:
  470.         forkunlock lock=ucb$b_flck(r5),newipl=(sp)+,preserve=YES
  471.         popr    #^m<r0,r1,r2,r3,r10,r11>
  472.  
  473.  
  474.    A final note:
  475.    The code here assumes that its standards are followed by all DDT
  476. bashing / FDT bashing. System crashes can occur if this is not so. But
  477. note: they do now. It is not possible to guard against kernel mode
  478. code that bashes vectors in Attila-the-Hun mode. Some bits more checking
  479. can be done for production code, but I hope we can come to closer accord
  480. over how these operations can coordinate.
  481.  
  482. Glenn Everhart
  483. Everhart@Raxco.com
  484. 215 358 5875
  485.