.IF DF CA$_IO_DEBUG             ; if debug version
	.TITLE	SYSQIOREQ_MON - QUEUE I/O REQUEST SYSTEM SERVICE
.IFF
	.TITLE	SYSQIOREQ - QUEUE I/O REQUEST SYSTEM SERVICE
.ENDC
	.IDENT	'X-58'
;
;****************************************************************************
;*									    *
;*  COPYRIGHT (c) 1978, 1980, 1982, 1984, 1991, 1992, 1993, 1995 BY         *
;*  DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS.		    *
;*  ALL RIGHTS RESERVED.						    *
;* 									    *
;*  THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED   *
;*  ONLY IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE   *
;*  INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR  ANY  OTHER   *
;*  COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY   *
;*  OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE IS  HEREBY   *
;*  TRANSFERRED.							    *
;* 									    *
;*  THE INFORMATION IN THIS SOFTWARE IS  SUBJECT TO CHANGE WITHOUT NOTICE   *
;*  AND  SHOULD  NOT  BE  CONSTRUED AS  A COMMITMENT BY DIGITAL EQUIPMENT   *
;*  CORPORATION.							    *
;* 									    *
;*  DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE  OR  RELIABILITY OF ITS   *
;*  SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.		    *
;* 									    *
;*									    *
;****************************************************************************
;
;++
;
; AUTHOR:
;
;	D. N. CUTLER,	14-JUN-76
;
; FACILITY:
;
;	SYSTEM SERVICE QUEUE I/O REQUEST
;
; MODIFIED BY:
;
;	X-58	PAN	Phil Norwich			02-Apr-1996
;		1.  Add check for IO$M_SYNCSTS along with the BUFOBJ &
;               and TRUSTED checks.  This will permit EXEC & KERNEL 
;               callers of SYS$QIO to request that VIO cache hits
;               return a SS$_SYNCH response.  The actual return will
;               be done in the cache code.
;
;	X-57	JCH703b	John C. Hallyburton, Jr.	12-Mar-1996
;		EXE$ABORTIO should not be decrementing the PHD accounting
;		fields. This causes I/O counts to go below 0 since the
;		PHD fields will -also- be decremented later.
;
;	X-56	LSS0348		Leonard S. Szubowicz	14-Jun-1995
;		64-bits: In EXE$BUILDPKTx, preclude an error return from
;		IOC_STD$FILL_DIOBM by taking advantage of the new resource
;		wait feature that was added just for this purpose.
;
;	X-55	SAD0324		Stuart A. Davidson	20-MAR-1995
;		Add support for EFN128, the event with no flag.
;
;	X-54	DED		Denise E. Dumas		14-MAR-1995
;		Add Fast Path changes to EXE$INSIOQ/C
;
;	X-53	LSS0327		Leonard S. Szubowicz	13-Mar-1995
;		64-Bit Virtual Addressing: In routines EXE$BUILDPKTR and
;		EXE$BUILDPKTW the R3 input is now a 64-bit VA_PTE.  Use the
;		DIOBM to convert it to a 32-bit pseudo SVAPTE.
;
;       X-52	CEG             Clair Grant             03-MAR-1995
;               Fix use of section table offset in CCB$L_WIND.
;
;       X-51	CEG             Clair Grant             27-FEB-1995
;               Redefine the section table index to be a positive integer
;		representing the section table entry number rather than a
;		negative offset to the section table entry.
;
;	X-50	JCH703a	John C. Hallyburton, Jr.	21-Feb-1995
;		ABORTIO changes for Fast-IO IRPs. A few cosmetic 
;		edits to improve readability in this complex section.
;
;		Also interlock CCB$L_IOC updates since Fast-IO can be
;		updating the same longword at elevated IPL. If this costs
;		too much performance, we can make the code only do the
;		interlocked update if PCB$PS_FANDLE is nonzero.
;
;		Fix typo at ERROR+3 from X-48 where R4 was used when R14
;		was intended.
;
;	X-49	LSS0320		Leonard S. Szubowicz	15-Feb-1995
;		64-Bit Virtual Addressing: Now that it's being filled in by
;		the EXE_STD$xLOCK routines, use the FDT_CONTEXT$Q_QIO_R1_VALUE
;		cell to return a 64-bit fault VA to system service dispatcher.
;
;	X-48	EMB0345		Ellen M. Batbouta	 9-Feb-1995
;       	Kernel thread data structure changes:  Pass the KTB (not
;		PCB) address to SCH$RESOURCE_WAIT_SETUP.  Also store the
;		current kernel thread's PID in the IRP/ACB field, IRP$L_
;		THREAD_PID, in EXE$QIO.  In EXE$BUILDPKT*, store the current
;		kernel thread's PID in the IRP field, IRP$L_PID.
;
;	X-47	LSS0316		Leonard S. Szubowicz	 3-Feb-1995
;		64-Bit Virtual Addressing: Support 64-bit values on the $QIO
;		AST and ASTPRM parameters by setting up the IRP as an ACB64.
;		Also, merge X-40U1/40U2.
;		Remove ASSUME concerning IRP$B_SHD_FLAGS.  The cell has been
;		moved to the shadowing extension in the CDRP.
;
;	X-46	WDB64B1		Walter D. Blaschuk, Jr. 26-Jan-1995
;               64-Bit Virtual Addressing/Bufio Changes: Added support
;               for 32-bit and 64-bit diagnostic buffers.
;
;	X-45	PKW271		Paul K. M. Weiss	23-Jan-1995
;		Idiot, put the parameters to SCH$RESOURCE_WAIT_SETUP 
;		in the correct order.
;
;	X-44	PKW264		Paul K. M. Weiss	17-Jan-1995
;		Call SCH$RESOURCE_WAIT_SETUP instead of doing the work of
;		that routine by hand.  The routine changed for threads, and
;		rather than mimic the change here, let's do it right.
;
;	X-43	JCH703a	John C. Hallyburton, Jr.	22-Nov-1994
;		Fix blown register in X-42
;
;	X-42	JCH703	John C. Hallyburton, Jr.	18-Nov-1994
;		Do $QIO accounting in the PHD at $QIO time instead of
;		inside the DIRPOST AST. This allows Fast-IO finishes.
;		Also rework QIO_EXIT_NO_CONTEXT to default to the
;		fast case.
;
;	X-41	LSS0312		Leonard S. Szubowicz	11-Nov-1994
;		64-Bit Virtual Address Support: Pass 64-bits of P1-P6 to
;		device drivers; check for driver support for 64-bits; SYS$QIOW
;		service moved to SYSQIOREQ.C module; miscellaneous other
;		initial changes.  See "Chapter 20: QIO and Device Drivers"
;		in DOCD$:[EVMS.CMS_64B]DS-64BITS.PS.
;
;	X-40	LSS0308		Leonard S. Szubowicz	 6-May-1994
;		In routine EXE$QIO after the call to the FDT routine, move the
;		diagnostic check for the SS$_FDT_COMPL status into the full
;		checking version IO_ROUTINES_MON\SYSQIOREQ_MON. (See X-33)
;
;	X-39	EHL		Gene Leache		22-Mar-94
;		Change TSTW to TSTL
;
;	X-38	PJH		Paul J. Houlihan	19-Nov-1993
;		Process EXFUNC only if EXFUNC_SUPP bit set. A driver sets
;		this bit in the UCB if it supports the EXFUNC bit in the
;		$QIO interface. Even if support by a unit, the EXFUNC bit
;		is only supported on certain I/O functions.
;
;	X-37	PJH		Paul J. Houlihan	08-Oct-1993
;		Conditionalize the .TITLE for the debug version.
;
;	X-36	PJH		Paul J. Houlihan	20-Sep-1993
;		TP $QIO optimizations
;		- Validate function mask if EXFUNC set and copy new IO bits to STS.
;		- If TRUSTED set then skip probe of IOSB.
;		- Save address of CCB in IRP$PS_CCB for quick reference in IOPOST
;		  and set CCB_LOOKED_UP bit.
;		- Use fast inline memory allocation instead of EXE$ALLOC_IRP.
;
;	X-35	LSS296		Leonard S. Szubowicz	23-Sep-1993
;		Merge in edit X-32U1.
;		Fix for a problem introduced by edit X-21, reported in QAR 2033.
;		In EXE$QIO, IPL must be raised to IPL$_ASTDEL before direct I/O
;		count is decremented to prevent process deletion while there is
;		no way that the count will be credited back.  For process
;		deletion to succeed, the direct I/O count must be able to return
;		back to the direct I/O limit.
;
;	X-34	PKW222		Paul K. M. Weiss	16-Sep-1993
;		Enable WCM optimization now that Step 1 drivers are disabled.
;		Some minor code polishing changes.
;
;	X-33	LSS0291		Leonard S. Szubowicz	 9-Sep-1993
;		HLLDD: Complete removal of support for STEP=1 drivers, i.e.
;		remove code conditional to HLLDD$STEP1.  However, keep check
;		for SS$_FDT_COMPL status on return from driver FDT routine.
;
;	X-32	LSS0283		Leonard S. Szubowicz	19-Aug-1993
;		HLLDD: Use CALL_x macros instead of soon to be obsolete $x.
;		Also, add temporary bugcheck if a step 2 top-level FDT
;		routine does not return SS$_FDT_COMPL.
;
;	X-31	PKW213		Paul K. M. Weiss	 6-Aug-1993
;		Disable WCM optimization until step 1 drivers are disabled
;
;	X-30	PKW208		Paul K. M. Weiss	27-Jul-1993
;		Use SSFLAG_K_WCM_NO_SAVE and SSFLAG_K_STACK_ARGS
;		system service dispatcher optimizations in sys$qio
;
;	X-29	SAD0282		Stuart A. Davidson	 9-JUL-1993
;		Merge C2 changes from Blade:
;
;		X-14	SAD0275		Stuart A. Davidson	 3-MAY-1993
;		Correct X-10; Physical I/O to a shared device always 
;		requires some privilege, perhaps yielding both access
;		and privilege audits.
;
;       	X-11	DDP1386		Derrell D. Piper        9-DEC-1992
;		Reflect new location of no-audit bit.
;
;		X-10	SAD0192		Stuart A. Davidson	26-FEB-1992
;		Fix auditing for logical and physical I/O to shared devices.
;
;		T-4	DDP		Derrell D. Piper	18-SEP-1991
;		Use EXE$CHECK_DEVICE_ACCESS instead of EXE$CHKxxxACCES to
;		determine device accessibility.
;
;		T-3	SAD		Stuart A. Davidson	16-AUG-1991
;		Fix physical access to shared devices -- bad branch
;		required both LOG_IO and PHY_IO, rather than PHY_IO only.
;
;		T-2	SAD		Stuart A. Davidson	14-AUG-1991
;		Fix auditing of DIAGNOSE privilege -- don't check for privilege
;		if the device does not support it (diagnostic buffer size 0).
;
;		T-1	SAD0127		Stuart A. Davidson	29-JUL-1991
;		Fold in privilege auditing work from PHEONIX, and add
;		access checking for unshared devices.
;
;	X-28	NT014		Nora Tanner		11-Jun-1993
;		Filesystem structure promotions: RVT$W_TRANS, VCB$W_RVN,
;		VCB$W_TRANS have become longwords.
;		
;		Also, to give latent support for Dollar in epsilon, in
;		exe$qxqppkt now pull the XQP dispatcher address from
;		AQB$L_ASTADR.
;
;       X-27    WDB:HLL53       Walter D. Blaschuk, Jr. 15-Mar-1993
;               HLLDD Project: JtoC Jackets.
;               EVMS$IO_CMS:[IO.CMS]J2C_JACKETS.
;               Create several EXE_STD$* routines with standard call
;               interfaces from  EXE$* routines with JSB interfaces.
;               Also, replace the "JSB" to several routines with the
;               standard "CALLS" or use the  MACRO to push the input
;               and/or output parameters and do the standard "CALLS".
;
;       X-26    SAD0260         Stuart A. Davidson      22-APR-1993
;               Merge Blade changes for volume dismount sychronization.
;               Other blade changes for security policy and auditing have
;               not yet been merged.  Changes included:
;
;               X-13    GV208           Guy Verbist             20-Apr-1993
;               Remove check on IRP$V_VIRTUAL in RLRVIO - XXX$W_TRANS
;               should be incremented for all packets going through this
;               path.
;
;               X-12    RLRVIO          Robert L. Rappaport     11-Feb-1993
;               In EXE$QXQPPKT, increment xxx$W_TRANS for virtual I/O's
;               queued to the XQP.
;
;       X-25    WDB:HLL039A     Walter D. Blaschuk, Jr.       15-Apr-1993
;               HLLDD Project: Add Debug test to ABORTIO.
;               Put a test for SS$_FDT_COMPL in ABORTIO.  If yes just RET.
;
;       X-24    WDB:HLL039      Walter D. Blaschuk, Jr.       14-Feb-1993
;		HLLDD Project: FDT fix to illegal function processing.
;
;       X-23    WDB:HLL038      Walter D. Blaschuk, Jr.       09-Feb-1993
;		HLLDD Project: FDT revisions. Part 2 Remove some errors.
;
;       X-22    WDB:HLL030      Walter D. Blaschuk, Jr.       05-Feb-1993
;		HLLDD Project: FDT revisions.
;
;       X-21    WDB:HLL020      Walter D. Blaschuk, Jr.       29-Jan-1993
;               HLLDD Project: FDT processing changes.
;		EVMS$IO_DOC:[IO.CMS]FDT_DESIGN.
;		Break apart Step1 and Step 2 code.  Changes at buffer I/O
;		/Direct I/O  determination, illegal function code and the
;		FDT dispatching algorithm.  Make Step 1 code conditional.
;		Unconditionally allocate FDT Context area on QIO stack.
;
;       X-20    WDA             W.D. Arbo               28-Jan-1993
;               Add support for Step 2:  replace JSB to altstart routine
;               with CALLS.
;		
;	X-19	LSS0261		Leonard S. Szubowicz	18-Dec-1992
;		In routine EXE$ABORTIO, copy error status into IRP$L_IOST1
;		and clear IRP$L_IOST2 to prevent IOPOST from using potentially
;		unitialized values in these cells.  Also, set the new
;		IRP$V_ABORTIO bit in IRP$L_STS2 to indicate that this I/O has
;		been terminated via EXE$ABORTIO.  If set, IOPOST will not
;		queue this IRP to the ACP since no data transfer could have
;		taken place.  Most notably this prevents the inappropriate 
;		forwarding of aborted virtual erase IRPs to the XQP.
;
;	X-18	LSS0259		Leonard S. Szubowicz	16-Nov-1992
;		Remove R4 from input register lists for routines EXE$FINISHIO,
;		EXE$FINISHIOC, and EXE$QIODRVPKT.
;
;	X-17	IJC050		Ian Compton		06-Nov-1992
;		Remove $F11BDEF, as this is no longer used.
;
;	X-16	WMC016		Wayne Cardoza		06-Oct-1992
;		Ensure VCC enabled before call.
;
;	X-15	WMC015		Wayne Cardoza		01-Sep-1992
;		Add support for VCC paging I/O.
;
;	X-14	RWC089		Richard W. Critz, Jr.		 2-Jul-1992
;		Convert to new defintion of channel table.  Promote CCB fields.
;
;		NOTE WELL:  Only SDA and this module are allowed to access the
;		CCB table directly.  All others MUST use the routine defined in
;		[SYS]IOCHANUTILS.
;		
;	X-13	LSS0251		Leonard S. Szubowicz	15-Jun-1992
;		Merge of VAX/VMS V5.5-2 hooks for VCC, but exclude image file
;		cacheing.
;
;		X-4B1	PAN0001		Phil Norwich 		14-Oct-1991
;			Add VCC hooks
;			1.  Modify BUILDPKTR to allow Image Caching.
;			2.  Minor change to QIO IRP set up
;			3.  Add check for IO$M_NOVCACHE in QIO
;
;	X-12	LSS0249		Leonard S. Szubowicz	28-May-1992
;		Minor performance improvements to the $QIO code path, mostly
;		by the addition of .BRANCH_LIKELY directives.
;
;	X-11	JRK361		Jim Kauffman		12-May-1992
;		Make references to UCB$L_QLEN atomic for both uni and
;		and SMP systems
;
;	X-10	BJT302		Benjamin J. Thomas III	16-Apr-1992
;		Check the low bits of CHAN.  They must be clear else return
;		an error.  This is the first step in reclaiming these bits
;		for use as a channel index.
;
;	(new master pack generation renumbering due to rebuild)
;
;	X-13	MSH1211		Michael S. Harvey	17-Feb-1992
;		Eliminate $$SYSTEM_PRIM_DATADEF macro.
;
;	X-12	BJT294		Benjamin J. Thomas III	16-Jan-1992
;		Remove old QIO argument passing mechanism
;
;	X-11	MSH1203		Michael S. Harvey	8-Jan-1992
;		Promote CTL$GW_CHINDX to longword.
;
;	X-10	WMC010		Wayne Cardoza		13-Dec-1991
;		Fix inital clearing of event flags.
;
;	X-9	MSH1196		Michael S. Harvey	6-Dec-1991
;		More $PCBDEF promotion.
;
;	X-8	MSH1191		Michael S. Harvey	3-Dec-1991
;		Byte/word promotion: $PCBDEF.
;
;	X-7	LSS0235		Leonard S. Szubowicz	21-Nov-1991
;		More miscellaneous performance tweaks.  Also, since this module
;		is now Alpha-specific, remove the VAX-specific conditionals.
;
;	X-6	BJT274		Benjamin J. Thomas III	18-Nov-1991
;		Fill in QIO_Pn arguments in IRP
;
;	X-5	BJT271		Benjamin J. Thomas III	15-Nov-1991
;		Promote FUNC fields
;
;	X-4	WMC004		Wayne Cardoza		23-Oct-1991
;		Event flags have moved to PHD.
;
;	X-3	LSS0230		Leonard S. Szubowicz	17-Oct-1991
;		In routine EXE$ALTQUEPKT, use the IRP or TWP address in R3 as
;		the fork block for SMP$CPU_SWITCH.  This corrects a problem
;		introduced by edit X-27K2.
;
;	X-2	LSS0229		Leonard S. Szubowicz	 8-Oct-1991
;		In routines EXE$BLDPKTSWPR and EXE$BLDPKTSWPR do not alter
;		IRP$W_SIZE and IRP$B_TYPE.  This allows the SWAPPER to use
;		an IRP with type DYN$C_INIT and subtype DYN$C_MPWMAP.  Such an
;		IRP is assumed to have a preallocated KPB by EXE$KP_STARTIO.
;		Also, in EXE$BUILDPKTR set IRP$W_SIZE and IRP$B_TYPE more
;		efficiently and remove obsolete EXE$BLDPKTGSR/W entry points.
;
;	X-1	LSS0229		Leonard S. Szubowicz	 4-Oct-1991
;		Made this file architecture-specific to facilitate next edit
;		and also future performance work.  This resets the module ident.
;
;	X-13	HH0751		Hai Huang		30-Sep-1991
;		Use general nonpaged pool allocation routine.
;
;	X-11	BJT247		Benjamin J. Thomas III	27-Sep-1991
;		Fix assumptions about IRP$W_CHAN and IRP$W_STS
;
;	X-11	BJT242		Benjamin J. Thomas III	11-Sep-1991
;		Remove use of spinlock when updating PMS$GL_IOPFMSEQ.
;		Test PMS$GL_IOPFMPDB before calling PMS$START_RQ 
;
;	X-10	LSS0224		Leonard S. Szubowicz	 6-Sep-1991
;		Expand UCB$W_QLEN to a longword to avoid word granularity
;		problems within the same quadword.
;
;	X-9	DEE0123		David E. Eiche		05-Aug-1991
;		Undo LSS0217, now that Bliss32 (T1.0-013) generates a
;		frameless procedure for JSB linkage routines.  However,
;		continue to copy AP to R12 for use by FDT routines written
;		in Bliss.
;
;	X-8	LSS0218		Leonard S. Szubowicz	13-Jun-1991
;		On Alpha/VMS, avoid alignment faults when clearing an IOSB that
;		is not quadword aligned.  Also, remove dependence on CALLG
;		emulation and argument homing in SYS$QIOW.
;
;	X-7	LSS0219		Leonard S. Szubowicz	 6-Jun-1991
;		On Alpha/VMS, EXE$QIO must also include the Alpha non-scratch
;		registers R12-R15 in its preserved set to prevent them from 
;		being destroyed by JSB entry routines whose epilog code is
;		bypassed via the "poor man's unwind".
;
;	X-6	LSS0217		Leonard S. Szubowicz	30-May-1991
;		Temporary and only partial work-arounds for Alpha/VMS to
;		support some FDT routines written in Bliss (T1.0-010) which does
;		not generate a frameless procedure for JSB linkage routines.
;		Such a Bliss FDT routine will modify FP, thus, breaking the
;		"poor man's unwind" via RET which is used in all the standard
;		FDT exit routines.  In EXE$QIO save FP in the IRP and in
;		QIORETURN possibly restore FP from the IRP.  Also, allow access
;		to the P1-P6 parameters via R12.
;
;	X-27K8	ROW0739		Ralph O. Weber		07-MAR-1991 10:58
;		Change IRP$W_BCNT to IRP$L_BCNT wherever an IRP is being
;		initalized.  This causes the full longword byte count to
;		be initialized.  It is actually just a minor tweek on
;		Leonard's LSS0181 edit.
;
;	X-27K7	LSS0201		Leonard S. Szubowicz	20-Feb-1991
;		Add some assumes to assure that the negative CDRP$L_IOQFL
;		offset reaches back exactly to the start of the IRP.
;
;       X-27K6  MJC03702 	Mark Cotton		18-Jan-1991
;		Use XQP execlet entry points
;
;	X-27K5	RFH003		Robert F. Hoffman	9-Nov-1990
;		Add $QIODEF
;
;	X-27K4	RFH002		Robert F. Hoffman	9-Nov-1990
;		Add SSFLAG_K_WCM to QIO SYSTEM_SERVICE macro.  
;
;	X-27K3	RFH001		Robert F. Hoffman	5-Nov-1990
;		Add wait form system_service
;
;	X-27K2	LSS0181		Leonard S. Szubowicz	29-Oct-1990
;		Initial changes for EVMS: Declare all JSB entries; change
;		off module jumps to JSBs; remove dependence on precise layout
;		of the IRP; on EVAX setup AP to point to P1 arg by copying the
;		P1-P6 args to the start of the homed arg list; support non-SMP
;		drivers only on VAX; call SCH$WAIT_PROC on EVAX, JMP to
;		SCH$WAIT on VAX; call SMP$CPU_SWITCH on EVAX, SMP$SWITCH_CPU on
;		VAX; EXE$INSERT_IRP replaces EXE$INSERTIRP and returns status
;		in R0 instead of via the VAX condition codes.
;
;	X-27	EMB0427		Ellen M. Batbouta	27-Jul-1989
;		Before releasing the forklock in the common code path
;		for EXE$ABORTIO and EXE$FINISHIO, check to make sure
;		that there is a forklock associated with the device.	
;
;	X-26	EMB0403		Ellen M. Batbouta	18-May-1989
;		Preserve R5 across the call to the driver's alternate
;		startio routine in EXE$ALTQUEPKT.
;
;	X-25	RAB0010		Richard A. Bishop	10-Apr-1989
;		Add conditional code to force use of MEMORYALC 
;		pool routines when histogramming
;
;	X-24	EMB0356		Ellen M. Batbouta	17-Aug-1988
;		When checking for device affinity in EXE$ALTQUEPKT,
;		a special check must be made for affinity to the primary,
;		since primary affinity is specified by the value, zero.
;
;	X-23	EMB0337		Ellen M. Batbouta	15-Aug-1988
;		Add a new busy bit, ALTBSY, and wait queue to the UCB 
;		for the alternate startio routine, EXE$ALTQUEPKT.  This
;		will maintain the correct ordering of I/Os to the driver
;		when a CPU switch must be made because of device affinity.
;
;	X-22	RNG5022		Rod Gamache		7-Apr-1988
;		Make I/O's thru FINISHIO/ABORTIO complete synchronously.
;
;	X-21	RNG5021		Rod Gamache		31-Jan-1988
;		Don't access POST queue directly - use SMP$IOPOST_IRP.
;
;	X-20	ACG0544		Andrew C. Goldstein,	6-Jan-1988  14:23
;		Remove taking of forklock in EXE$QIOACPPKT
;
;	X-19	RNG5019		Rod Gamache		24-Apr-1987
;		Add support of unmodified device drivers by checking
;		UCB$B_FLCK for a FIPL vs. a FLCK.
;
;	X-18	SJF		Stu Farnham		20-Mar-1987
;		Fix broken branch.
;
;	X-17	SF04002		Stephen Fiorelli	07-Mar-1987
;		Use vms standard name for system_primitives_data.
;
;	X-16	WCT0032		Ward C. Travis		19-Feb-1987
;		Update remaining old lookaside  listhead references
;		to reflect that they are now interlocked queues.
;
;	X-15	RNG5015		Rod Gamache		15-Jan-1987
;		Make some performance optimizations.
;
;	X-14	WMC0014		Wayne Cardoza		06-Jan-1987
;		Change some JSBs to BSBWs.
;
;	X-13	ROW73864	Ralph O. Weber		04-DEC-1986 15:35
;		Change IOC$VERIFYCHAN part of the $QIO system service so that
;		it returns SS$_IVCHAN for unassigned channels, not SS$_NOPRIV.
;
;	X-12	ACG0525		Andrew C. Goldstein,	27-Aug-1986  15:47
;		Decrement process I/O count before queuing packet in BLDPKT.
;
;	X-11	RNG0011		Rod Gamache		29-Oct-1986
;		Fix branch errors.
;
;	X-10	RNG0010		Rod Gamache		22-Oct-1986
;		Make all AQB queues interlocked queues - implies that
;		entries can only be inserted on the tail of the AQB queue.
;
;	X-9	SSA0001		Stan Amway		13-Oct-1986
;		Add entry point EXE$BLDPKTMPW.
;
;	X-8	RNG0008		Rod Gamache		30-Sep-1986
;		Make sure mods to DIOCNT & BIOCNT are interlocked.
;
;	X-7	RNG0007		Rod Gamache		24-Sep-1986
;		Interlock access to PCB$L_EFCS.
;		Don't check packet type when going thru EXE$ALTQUEPKT.
;		Save R5 around calls to IOC$INITIATE.
;
;	X-4	SJF		Stu Farnham		1-Jul-1986
;		Use per-CPU IOPOST queue
;
;	V04-003	RNG4003		Rod Gamache		4-Jun-1986
;		Fix branch destination errors.
;
;	V04-001	SF04001		Stephen Fiorelli	24-Oct-1985
;		System_service macro used to declare entry point
;		and build system service descriptor block.
;		Added $SYSVECTORDEF.
;
;	V04-001	ACG0467		Andrew C. Goldstein,	12-Sep-1984  17:33
;		Fix protection holes in QIO device protection check
;
;--

	.PAGE
	.SBTTL	DECLARATIONS

;
; MACRO LIBRARY CALLS
;
 
	$ACBDEF				;DEFINE ACB OFFSETS
	$ARMDEF				;DEFINE ARM CONSTANTS
	$AQBDEF				;DEFINE AQB OFFSETS
	$CADEF				;DEFINE CONDITIONAL ASSEMBLY PARAMETERS
	$CCBDEF				;DEFINE CCB OFFSETS
	$CDRPDEF			;DEFINE CDRP OFFSETS
	$CPUDEF				;DEFINE CPU DATABASE OFFSETS
	$DDBDEF				;DEFINE DDB OFFSETS
	$DDTDEF				;DEFINE DDT OFFSETS
	$DEVDEF				;DEFINE DEV VALUES
	$DYNDEF				;DEFINE DATA STRUCTURE TYPE CODES
	$EFNDEF				;DEFINE EVENT FLAG NUMBERS
	$FDTARGDEF			;DEFINE FDT ARGUMENT OFFSETS
	$FDTDEF				;DEFINE FUNCTION DECISION TABLE OFFSETS
	$FDT_CONTEXTDEF			;DEFINE FDT CONTEXT STRUCTURE OFFSETS
	$IODEF				;DEFINE IO FUNCTION CODES
	$IPLDEF				;DEFINE INTERRUPT PRIORITY LEVELS
	$IRPDEF				;DEFINE IRP OFFSETS
	$NSADEF				;DEFINE SECURITY AUDITING ITEM CODES
	$ORBDEF				;DEFINE OBJECT RIGHTS BLOCK OFFSETS
	$PCBDEF				;DEFINE PCB OFFSETS
	$PHDDEF				;DEFINE PHD OFFSETS
	$PRDEF				;DEFINE PROCESSOR REGISTERS
	$PRIDEF				;DEFINE PRIORITY CLASS INCREMENTS
	$PRVDEF				;DEFINE PRIVILEGE BITS
	$PSLDEF				;DEFINE PROCESSOR STATUS FIELDS
	$QIODEF				;DEFINE QIO PARAMETER OFFSETS
	$RSNDEF				;DEFINE RESOURCE WAIT NUMBERS
	$RVTDEF                         ;DEFINE RVT OFFSETS
	$SECDEF				;DEFINE SEC OFFSETS
	$SSDEF				;DEFINE STATUS VALUES
	$UCBDEF				;DEFINE UCB OFFSETS
	$VCBDEF				;DEFINE VCB OFFSETS
	$VCCDEF
	$WCBDEF				;DEFINE WINDOW CONTROL BLOCK OFFSETS


;	HISTOGRAM = 1			; Enable request size histogram

	.IF	DF,HISTOGRAM
	.PRINT	;%SYSQIOREQ-I-HISTOGRAM,  assembling with allocation histogram code.
	.PRINT	;-SYSQIOREQ-I-HISTOGRAM,  MEMORYALC, PAGEFAULT, SYSENQDEQ also need
	.PRINT	;-SYSQIOREQ-I-HISTOGRAM,  histogramming enabled to be worthwhile.
	.ENDC				;

;
; LOCAL SYMBOLS
;
;
; TABLES FOR DETERMINING THE ACCESS DESIRED, BASED UPON THE I/O FUNCTION
; CODE.  THIS IS NECESSARY FOR THE FIRST TIME THROUGH PROTECTION CHECK DONE
; ON NON-MOUNTABLE (NON-FILES ORIENTED) DEVICES.
;

	.MACRO	ACCMSK	CODES
MASKL	= 0
MASKH	= 0

	.IRP	X,<CODES>
	.IF	GT	<IO$_'X&IO$_VIRTUAL>-31
MASKH	= MASKH!<1@<<IO$_'X&IO$_VIRTUAL>-32>>
	.IFF
MASKL	= MASKL!<1@<IO$_'X&IO$_VIRTUAL>>
	.ENDC;	GT	<IO$_'X&IO$_VIRTUAL>-31
	.ENDR ;	X,<CODES>
	.LONG	MASKL,MASKH
	.ENDM	ACCMSK

	DECLARE_PSECT	EXEC$NONPAGED_DATA

	.ALIGN	QUAD
	.SYMBOL_ALIGNMENT QUAD
READ_ACCESS:
	ACCMSK	<READPBLK,READLBLK,READVBLK,-
		 READHEAD,READTRACKD,REREADN,REREADP,-
		 READPROMPT,TTYREADALL,TTYREADPALL>
	.SYMBOL_ALIGNMENT NONE

	.ALIGN	QUAD
	.SYMBOL_ALIGNMENT QUAD
WRITE_ACCESS:
	ACCMSK	<WRITEPBLK,WRITELBLK,WRITEVBLK,-
		 WRITECHECK,WRITECHECKH,-
		 WRITEHEAD,WRITETRACKD,WRITERET>
	.SYMBOL_ALIGNMENT NONE

	.ALIGN	QUAD
	.SYMBOL_ALIGNMENT QUAD
EXFUNC_IO_FUNCTIONS:
	ACCMSK	<READPBLK,READLBLK,READVBLK,-
		 WRITEPBLK,WRITELBLK,WRITEVBLK,-
		 ACCESS,CREATE,DEACCESS,DELETE,-
		 MODIFY,MOUNT,ACPCONTROL>
	.SYMBOL_ALIGNMENT NONE

	.PAGE
	.SBTTL	QUEUE I/O REQUEST
;+
; EXE$QIO - QUEUE I/O REQUEST
;
; THIS SERVICE PROVIDES THE CAPABILITY TO INITIATE AN I/O OPERATION
; BY QUEUEING A REQUEST TO A DEVICE'S ASSOCIATED DRIVER.  ONCE THE
; OPERATION HAS BEEN INITIATED, CONTROL WILL RETURN TO THE CALLER
; WHO CAN SYNCHRONIZE I/O COMPLETION IN ONE OF THREE WAYS:
;
;	1) SPECIFY THE ADDRESS OF AN AST ROUTINE THAT WILL BE
;	   EXECUTED WHEN THE I/O COMPLETES.
;
;	2) WAIT FOR THE SPECIFIED EVENT FLAG TO BE SET.
;
;	3) POLL THE SPECIFIED I/O STATUS BLOCK FOR A COMPLETION
;	   STATUS.
;
; THIS ROUTINE VERIFIES THE FUNCTION INDEPENDENT PARAMETERS, ALLOCATES
; AN I/O REQUEST PACKET, COPIES THE FUNCTION INDEPENDENT PARAMETERS AND
; PROCESS INFORMATION TO THE I/O PACKET, CHECKS ACCESS TO THE DEVICE,
; AND CALLS THE DRIVER'S FUNCTION DECISION TABLE ROUTINE(S) THAT CORRESPOND
; TO THE SPECIFIED FUNCTION.  IT IS THEN UP TO THE FDT ROUTINE TO EITHER
; COMPLETE THE REQUEST IMMEDIATELY (STEP 1: EXE$ABORTIO OR EXE$FINISHIO OR
; STEP 2: EXE_STD$ABORTIO OR EXE_STD$FINISHIO) OR TO
; QUEUE THE I/O REQUEST FOR FURTHER PROCESSING BY THE DRIVER'S STARTIO
; ROUTINE (STEP 1 EXE$QIODRVPKT OR STEP 2 EXE_STD$QIODRVPKT).
;
; INPUTS:
;
;	QIO$_EFN(AP) 	= Event flag number.
;	QIO$_CHAN(AP) 	= I/O channel number.
;	QIO$_FUNC(AP) 	= I/O function code.
;	QIO$_IOSB(AP) 	= 64-bit address of I/O status block.
;	QIO$_ASTADR(AP) = 64-bit address of AST service routine.
;	QIO$_ASTPRM(AP) = 64-bit AST service routine parameter.
;	QIO$_P1(AP) to)	= 64-bit function dependent parameters.
;	QIO$_P6(AP
;
;	R4 = CURRENT PROCESS PCB ADDRESS.
;
; OUTPUTS:
;
;	R0 LOW BIT CLEAR INDICATES FAILURE TO INITIATE THE I/O REQUEST.
;
;		R0 = SS$_ABORT - A NETWORK LOGICAL LINK WAS BROKEN.
;
;		R0 = SS$_ACCVIO - THE I/O STATUS BLOCK CANNOT BE WRITTEN BY
;			THE CALLER.
;
;		R0 = SS$_DEVOFFLINE - THE SPECIFIED DEVICE IS OFFLINE.
;
;		R0 = SS$_EXQUOTA - THE PROCESS HAS EXCEEDED ITS BUFFERED I/O
;			QUOTA, DIRECT I/O QUOTA, OR BUFFERED I/O BYTE COUNT
;			QUOTA AND HAS DISABLED RESOURCE WAIT MODE.  OR, THE
;			PROCESS HAS EXCEEDED ITS AST LIMIT QUOTA.
;
;		R0 = SS$_ILLEFC - AN ILLEGAL EVENT FLAG NUMBER WAS SPECIFIED.
;
;		R0 = SS$_INSFMEM - INSUFFICIENT DYNAMIC MEMORY IS AVAILABLE
;			TO ALLOCATE AN I/O REQUEST PACKET AND THE PROCESS HAS
;			DISABLED RESOURCE WAIT MODE.
;
;		R0 = SS$_IVCHAN - AN INVALID CHANNEL NUMBER WAS SPECIFIED.
;
;		R0 = SS$_NOPRIV - THE SPECIFIED CHANNEL DOES NOT EXIST OR WAS
;			ASSIGNED FROM A MORE PRIVILEGED ACCESS MODE.  OR, THE
;			PROCESS DOES NOT HAVE THE PRIVILEGE TO PERFORM THE
;			SPECIFIED TYPE OF I/O FUNCTION ON THE DEVICE.
;
;		R0 = SS$_UNASEFC - UNASSOCIATED EVENT FLAG CLUSTER SPECIFIED.
;
;	R0 LOW BIT SET INDICATES SUCCESSFUL COMPLETION.
;
;		R0 = SS$_NORMAL - NORMAL COMPLETION.
;
; PARAMETER PASSING NOTE:
;
;	Because this system service has the SSFLAG_K_STACK_ARGS set, in the
;	pursuit of performance the 6 stack arguments, P1 through P6, have
;	NOT been copied to the kernel stack and are NOT accessible through
;	the normal parameter offsets.  Instead, the QIO$_P1 parameter contains
;	a pointer to these 6 additional parameters on the caller's stack.
;	Note further, that the NARGS parameter to the SYSTEM_SERVICE macro must
;	remain at 12, even though we appear to be using only the first stack
;	argument.  That one argument is a pointer to the 6 additional real
;	arguments, and we are counting on the system service dispatcher to have
;	probed those arguments.  If NARGS were set to 7, we would accept a call
;	that had only really passed 7 arguments, but would then access 12, which
;	could cause an accvio and crash, since only the first of the six stack
;	arguments would have been probed.
;
; 64-BIT SUPPORT:
;
;	Note that this routine supports the full 64-bits for some of its
;	parameters as noted above.  Because the QUAD_ARGS parameter to the
;	SYSTEM_SERVICE macro is used, a quadword reference to a routine
;	parameter, via an AP offset, will obtain the full 64-bits of that
;	parameter.  E.g. EVAX_LDQ R1,4(AP) loads R1 with the full 64-bit value
;	of the first parameter.  All code segments in this module that must
;	remain "64-bit safe" are enclosed by the .ENABL/.DSABL QUADWORD
;	directive.
;
;-

	ASSUME	QIO$_P1	EQ 7*4
	QIO$_STKARG_PTR	= QIO$_P1	;This is actually a pointer to P1-Pn vector

	.SYMBOL_ALIGNMENT QUAD		;When using QIO_P1 value as a pointer:
	QIO$_STKARG_P1	= 0*8		; Offset to the P1 stack parameter
	QIO$_STKARG_P2	= 1*8		; Offset to the P2 stack parameter
	QIO$_STKARG_P3	= 2*8		; Offset to the P3 stack parameter
	QIO$_STKARG_P4	= 3*8		; Offset to the P4 stack parameter
	QIO$_STKARG_P5	= 4*8		; Offset to the P5 stack parameter
	QIO$_STKARG_P6	= 5*8		; Offset to the P6 stack parameter
	.SYMBOL_ALIGNMENT NONE

	QIO$_P1	= -1			;Prevent use of QIO$_Pn offsets
	QIO$_P2	= -1
	QIO$_P3	= -1
	QIO$_P4	= -1
	QIO$_P5	= -1
	QIO$_P6	= -1



	DECLARE_PSECT	EXEC$NONPAGED_CODE

	SYSTEM_SERVICE	QIO,-
			<R2,R3,R4,R5,R6,R7,R8,R9,R10,R11,R12,R13,R14,R15>,-
			MODE=KERNEL,-
			NARG=12,-		; See "PARAMETER PASSING NOTE" above.
			QUAD_ARGS=TRUE,-
			TYPE=QIOW,-
			FLAGS=<SSFLAG_K_WCM_NO_SAVE!- ;may return SS$_WAIT_CALLERS_MODE
			       SSFLAG_K_STACK_ARGS!-  ;expects pointer to stack args
			       SSFLAG_K_64_BIT_ARGS>  ;no sign-extension check by dispatcher
;
;
; Allocate quadword aligned space from the stack for the FDT Context structure
; and initialize it as follows:
;
	SUBL	#FDT_CONTEXT$K_LENGTH,SP	; Allocate FDT Context structure
	MOVL	SP,R13				; Save address.
	MOVZWL	#SS$_NORMAL,-			; Initialize fields:
		FDT_CONTEXT$L_QIO_STATUS(R13)	;  QIO_STATUS = #SS$_NORMAL,

	; assume W_SIZE is long aligned and followed by TYPE and RMOD.

	ASSUME	<FDT_CONTEXT$W_SIZE&3>	EQ 0		
	ASSUME	FDT_CONTEXT$B_TYPE    EQ FDT_CONTEXT$W_SIZE+2	
	ASSUME	FDT_CONTEXT$B_SUBTYPE EQ FDT_CONTEXT$W_SIZE+3

	MOVL	#<<DYN$C_FDT_CONTEXT@24>!-	;  SUBTYPE = #DYN$C_FDT_CONTEXT,
		<DYN$C_MISC@16>!-		;  TYPE = #DYN$C_MISC,
		FDT_CONTEXT$K_LENGTH>,- 	;  SIZE = #FDT_CONTEXT$K_LENGTH.
		FDT_CONTEXT$W_SIZE(R13)		;
;
; Clear specified event flag.  For local event flags, this is done in line.
;
	MOVZBL	QIO$_EFN(AP),R3		;get event flag number
	.BRANCH_LIKELY
	CMPL	R3,#63			;if not local event flage
	BLEQU	10$
	CMPL	R3,#EFN$C_ENF		;event with no flag?
	BEQL	VCHAN                   ;if so, there is nothing to clear
	JSB	G^SCH$CLREF		;  clear via SCH$CLREF
	BRB	VCHAN			;else
10$:	MOVL	CTL$GL_PHD,R5		;  get PHD address
	EVAX_SLL  #1,R3,R0		;  create bit mask for event flag
20$:	EVAX_LDQL R1,PHD$Q_LEFC(R5)	;  get old contents
	EVAX_BIC  R1,R0,R1		;  clear specified event flag
	EVAX_STQC R1,PHD$Q_LEFC(R5)	;  save new flags
	.BRANCH_UNLIKELY
	EVAX_BEQ  R1,20$		;  if conditional store failed repeat
;
; Validate channel number, compute CCB address and acquire UCB address.
;
; ******************************* NOTE WELL **************************
; The section of code below is a direct translation of IOC$CHAN_TO_CCB
; and IOC$VERIFY_CHAN.  It is done inline, rather than by calling the
; routines, for performance reasons.  ALL OTHER USERS IN THE SYSTEM
; *MUST* USE THE ROUTINES DEFINED IN [SYS]IOCHANUTILS.B64.
; ***************************** END NOTE WELL ************************
VCHAN:
	MOVZWL	QIO$_CHAN(AP),R0	;Get channel number word
	BEQL	IVCHAN			;if chan is zero return invalid channel
	MOVL	R0,R9			;save channel to store in IRP$L_CHAN
	BICL3	#^C<^X000F>,R0,R6	;isolate low 4 bits
	ASHL	#-4,R0,R0		;shift remainder down
	ASHL	#12,R6,R6		;shift low 4 up
	BISL	R0,R6			;recombine the bits
	DECL	R6			;bias for 0-based array
	CMPL	R6,CTL$GL_CHINDX	;if channel numb greater than max assgn
	BGEQU	IVIDENT			;  return invalid channel number
	ASSUME	CCB$K_LENGTH EQ 32
	EVAX_SLL R6,#5,R6		;convert to byte offset
	ADDL	G^CTL$GA_CCB_TABLE,R6	;get address of corresponding CCB
	MOVPSL	R3			;read current PSL
	MOVZBL	CCB$B_AMOD(R6),R0	;get channel access mode
	.BRANCH_LIKELY
	BNEQ	OKCHAN			;if chan not assigned (acmode=0)
IVCHAN:	MOVZWL	#SS$_IVCHAN,R0		;  return invalid channel number error
	BRB	ERROR
IVIDENT:
	MOVZWL	#SS$_IVIDENT,R0		;if chan identifier is above highwater
	BRB	ERROR			;  mark, return invalid identifier
OKCHAN:	EXTZV	#PSL$V_PRVMOD,-		;extract PSL previous mode field
		#PSL$S_PRVMOD,R3,R11
	.BRANCH_LIKELY
	CMPL	R11,R0			;if caller acmode insufficient for chan
	BLSS	30$
	MOVZWL	#SS$_NOPRIV,R0		;  return privilege error
	BRW	ERROR
30$:	MOVL	CCB$L_UCB(R6),R5	;get assigned device UCB address
;
; If an access or deaccess operation is pending for this channel then wait for 
; it to complete, then retry the QIO.
;
	.BRANCH_LIKELY
 	BLBC	CCB$L_WIND(R6),	-	;if lbs access/deaccess pending
		NO_DEACC_PENDING
	GET_CURKTB 	PRESERVE=NO	; Get current KTB address in R14
	LOCK	LOCKNAME=SCHED,-	;  lock the SCHED database
		PRESERVE=NO
	PUSHL	#RSN$_ASTWAIT		;  Push resource number
	PUSHL	R14			;  Push PCB
	CALLS	#2, SCH$RESOURCE_WAIT_SETUP
					; R0 returns with SS$_WAIT_CALLERS_MODE
	MOVL		(AP),R25	;  Set up 64-bit param registers to reissue
	EVAX_LDQ	R16,1*4(AP)	;  (done here because of SSFLAG_K_WCM_NO_SAVE)
	EVAX_LDQ	R17,2*4(AP)
	EVAX_LDQ	R18,3*4(AP)
	EVAX_LDQ	R19,4*4(AP)
	EVAX_LDQ	R20,5*4(AP)
	EVAX_LDQ	R21,6*4(AP)
	RET				;  and wait in caller's mode.  Do not 
					;  setipl to 0. 
NO_DEACC_PENDING:
;
; Isolate function code. If EXFUNC set, validate any privileged bits
;
	MOVL	#IRP$M_CCB_LOOKED_UP,R12; Create STS initial state register,
					; want initial state to have
					; IRP$M_BUFIO clear and CCB_LOOKED_UP set
	;
	; If EXFUNC is set and this is a unit and function that supports EXFUNC
	; then check for any extended function bits that need validation.
	; The extended function bits are stored in the STS field as the FUNC
	; field cannot be used to pass information to IOPOST. Drivers reset
	; the FUNC field at will.
	;
	MOVL	QIO$_FUNC(AP),R10	; Get longword argument containing:
					; Extended function bits, I/O function code and modifiers
	BICL3	#^C<IO$M_FCODE>,R10,R7	; Clear all but I/O function code
	.BRANCH_LIKELY
	BBC	#IO$V_EXFUNC,R10,25$	; If no extended functions then skip
					;  validation of extended function bits
	BBC	#UCB$V_EXFUNC_SUPP,-	; Branch if device doesn't support EXFUNC
		UCB$L_STS(R5),25$
	BBC	R7,EXFUNC_IO_FUNCTIONS,- ; Branch if function doesn't support EXFUNC
		25$
	BITL	#<IO$M_BUFOBJ!IO$M_TRUSTED!IO$M_SYNCSTS>-
		    ,R10 ; Any privileged bits set ?
	BEQL	25$ 			; No, so skip validation
	CMPL	#PSL$C_EXEC,R11		; Kernel or EXEC mode caller?
	.BRANCH_LIKELY
	BGEQ	20$			; Yes, so go save bits in STS
	MOVL	#SS$_WRONGACMODE,R0	; Return WRONGACMODE as must be in 
	BRB	ERROR			;  kernel or exec mode to use priv bits
20$:
	BITL	#IO$M_SYNCSTS, R10
	BEQL    22$
	BISL	#IRP$M_SYNCSTS, R12	; Set SYNCSTS in STS state register
22$:
	ASSUME IO$V_BUFOBJ EQ IRP$V_BUFOBJ
	ASSUME IO$V_TRUSTED EQ IRP$V_TRUSTED
	BICL3	#^C<IO$M_BUFOBJ!IO$M_TRUSTED>,- ; Isolate Trusted and Buffer Object bits
		R10,R0			; 
	BISL	R0,R12			; Add bits to STS initial state register 
25$:	MOVZWL	R10,R10			; Pass only lower word I/O function code and modifiers
					;  by clearing out extended function bits
;
; If device is marked spooled and virtual function then acquire intermediate 
; UCB address.
;
	.BRANCH_LIKELY
	BBC	S^#DEV$V_SPL,-		;if device spooled
		UCB$L_DEVCHAR(R5),NSPOOL
	CMPL	S^#IO$_LOGICAL,R7	;and if logical I/O function
	BGEQ	NSPOOL
	MOVL	UCB$L_AMB(R5),R5	;  get intermediate device UCB address
NSPOOL:
;
; Check for a non-file-oriented device.  If it's one of those, then do a
; protection check if it is necessary.
;
	.BRANCH_LIKELY
	BBS	#DEV$V_FOD,-		;if not file-oriented device
		UCB$L_DEVCHAR(R5),20$
	MOVL	R6,R1			;SET DEACCESS AUDIT KEY
	BBC	R7,READ_ACCESS,10$	;XFER IF NOT A READ FUNCTION
	BBS	#CCB$V_RDCHKDON,-
		CCB$L_STS(R6),10$	;XFER IF HERE ALREADY
	MOVZBL	#ARM$M_READ,R0		;SET ACCESS REQUESTED
	; R0 = access requested, R1 = deaccess key, PCB(R4), UCB(R5)
	.SET_REGISTERS  READ=<R0,R1,R4,R5>,WRITTEN=<R0>
	JSB	G^EXE$CHECK_DEVICE_ACCESS	;DO ACCESS CHECK
	BLBC	R0,ERROR		;XFER IF NOT SUCCESSFUL
	BISB	#CCB$M_RDCHKDON,-
		CCB$L_STS(R6)		;NOTE PROT CHECK MADE
10$:	BBC	R7,WRITE_ACCESS,20$	;XFER IF NOT A WRITE FUNCTION
	BBS	#CCB$V_WRTCHKDON,-
		CCB$L_STS(R6),20$	;XFER IF HERE ALREADY
	MOVZBL	#ARM$M_WRITE,R0		;SET ACCESS REQUESTED
	; R0 = access requested, R1 = deaccess key, PCB(R4), UCB(R5)
	.SET_REGISTERS  READ=<R0,R1,R4,R5>,WRITTEN=<R0>
	JSB	G^EXE$CHECK_DEVICE_ACCESS	;DO ACCESS CHECK
	BLBC	R0,ERROR		;XFER IF NOT SUCCESSFUL
	BISB	#CCB$M_WRTCHKDON,-
		CCB$L_STS(R6)		;NOTE PROT CHECK MADE
;
; Check if device is offline.
;
20$:
	.BRANCH_LIKELY
	BBS	#UCB$V_ONLINE,-		;if device is offline
		UCB$L_STS(R5),PRIOSB
	CMPL	#IO$_DEACCESS,R7	;and if function is not deaccess
	BEQL	PRIOSB
	CMPL	#IO$_ACPCONTROL,R7	;and if function is not acpcontrol
	BEQL	PRIOSB			;  (to allow file close on offline dev)
	MOVZWL	#SS$_DEVOFFLINE,R0	;  return offline error
	BRB	ERROR
;
; Probe and clear IOSB if it is specified.
;
PRIOSB:
	.ENABL	QUADWORD
	EVAX_LDQ R1,QIO$_IOSB(AP)	;GET 64-BIT ADDRESS OF I/O STATUS BLOCK
	EVAX_BEQ R1,NOIOSB		;IF EQL NONE SPECIFIED

        .IF DF CA$_IO_DEBUG             ; if debug version
	BBC	#IRP$V_TRUSTED,R12,5$ 	;   and TRUSTED component
	BITL	#3,R1			;   and IOSB is not longword aligned
	BEQL	5$			;
	MOVZWL	#SS$_BADPARAM,R0	;   then return BADPARAM error
	BRB	ERROR
5$:
	.IFF                            ; If not debug version
	BBS	#IRP$V_TRUSTED,R12,10$	;  and trusted component then skip probe
        .ENDC

	IFNOWRT	#8,(R1),ACCVIO,PRVMOD=R11  ;CAN I/O STATUS BLOCK BE WRITTEN?
10$:	CLRL	(R1)			;clear I/O status block assuming that
	CLRL	4(R1)			;longword alignment is the common case
	.DSABL	QUADWORD

NOIOSB:
;
; Determine if the function is buffered or direct so that the appropriate
; I/O count can be charged.  However, we first must prevent process deletion
; by raising IPL to IPL$_ASTDEL.  We must prevent the delivery of a process
; deletion AST during the time that the count is decremented but before the IRP
; has been queued such that we can be assured that the count will eventually
; be credited.  Otherwise, process deletion could be deadlocked waiting for
; the I/O count to return to the I/O limit value.  Counts will have to
; be backed out if no I/O packet is available.
;
	SETIPL	#IPL$_ASTDEL		;Prevent process deletion
	MOVL	UCB$L_DDT(R5),R0	;Get address of DDT.
	MOVL	DDT$PS_FDT_2(R0),R8	;Get address of Step 2 FDT. 	
	MOVL	CTL$GL_PHD,R3		;Also get PHD for accounting
					;Swappable, must use P1 (CTL$) address
	EVAX_LDQ R0,FDT$Q_BUFFERED(R8)	;Get mask of buffered functions
	EVAX_SRL R0,R7,R0		;if (buffered I/O)
	BLBC	R0,DIRECT
	BISL	#IRP$M_BUFIO,R12	;  Set IRP$M_BUFIO in STS initial state register
	INCL	PHD$L_BIOCNT(R3)	;  Increment BIO accounting

	.PRESERVE ATOMICITY
	DECL	PCB$L_BIOCNT(R4)	;  charge for another buffered I/O
	.NOPRESERVE ATOMICITY

	.BRANCH_LIKELY
	BGEQ	OKCNT			;  if insufficient quota
	MOVAL	PCB$L_BIOCNT(R4),R2	;    R2 = address of BIO quota cell
	BRB	NOCNT			;    go check if should wait for quota

DIRECT:					;else (direct I/O)
	INCL	PHD$L_DIOCNT(R3)	;  Increment DIO accounting
	.PRESERVE ATOMICITY
	DECL	PCB$L_DIOCNT(R4)	;  charge for another direct I/O
	.NOPRESERVE ATOMICITY

	.BRANCH_LIKELY
	BGEQ	OKCNT			;  if insufficient quota
	MOVAL	PCB$L_DIOCNT(R4),R2	;    R2 = address of DIO quota cell
NOCNT:
	.PRESERVE ATOMICITY
	INCL	(R2)			;    backout quota charge
	.NOPRESERVE ATOMICITY

	SETIPL	#0			;    lower IPL to wait at IPL 0
	JSB	EXE$SNGLEQUOTA_LONG	;    check unit quota of I/O function type
	.BRANCH_UNLIKELY
	BLBC	R0,NOIRP		;    if still failed then return error

	.PRESERVE ATOMICITY
	DECL	(R2)			;    charge for I/O of type
	.NOPRESERVE ATOMICITY
OKCNT:

;
; Allocate an I/O request packet 
;
	ALONONPAGED_INLINE	SIZE=IRP$K_LENGTH ; Inline fast memory allocation
	.BRANCH_LIKELY
	BLBS	R0,GOT_IRP		; Branch if allocation succeeds

	; R0 = EXE_STD$ALLOCIRP(IRP(R2))   
	CALL_ALLOCIRP			;call to allocate an I/O request packet,	   
	.BRANCH_LIKELY                  ;and stall if can't get it
	BLBS	R0,GOT_IRP		;if allocation failed
; IRP allocation failure

NOIRP:	ASSUME	IRP$M_BUFIO EQ 1
	BLBC	R12,10$			;  if was buffered I/O
	.PRESERVE ATOMICITY
	INCL	PCB$L_BIOCNT(R4)	;    credit buffered I/O count
	.NOPRESERVE ATOMICITY
	DECL	PHD$L_BIOCNT(R3)	; undo bufio
	BRB	ERROR			;    return error status
10$:					;  else
	.PRESERVE ATOMICITY
	INCL	PCB$L_DIOCNT(R4)	;    credit direct I/O
	.NOPRESERVE ATOMICITY
	DECL	PHD$L_DIOCNT(R3)	; undo dirio
	BRB	ERROR			;    return error status

GOT_IRP:
;
; BUILD DEVICE INDEPENDENT PART OF I/O PACKET
;
; R1 = IRP size.
; R2 - IRP Address
; R4 - PCB Address
; R5 - UCB Address
; R6 - CCB Address
; R7 - Function code (original)
; R8 - FDT address
; R9 - Channel index
; R10 - Function code (transformed)
; R11 - Access mode
; R12 - STS initial state register, (if buffered I/O then IRP$M_BUFIO set)
;
	.PRESERVE ATOMICITY
	INCL	CCB$L_IOC(R6)		;increment outstanding I/O on channel
	.NOPRESERVE ATOMICITY
	MOVL	R2,R3			;R3 now points to IRP
   	EVAX_LDQ R0,QIO$_ASTADR(AP)	;R0 = 64-bit AST address
	EVAX_BEQ R0,5$			;if non-zero AST address
	BISL	#ACB$M_QUOTA,R11	;  note quota charge

	.PRESERVE ATOMICITY
	DECL	PCB$L_ASTCNT(R4)	;  charge quota
	.NOPRESERVE ATOMICITY

	; Assure that the IRP offsets allow us to use the IRP as an ACB64
	; in I/O postprocessing.
	;
	ASSUME	IRP$B_RMOD          EQ ACB$B_RMOD
	ASSUME	IRP$L_PID           EQ ACB$L_PID
	ASSUME	IRP$L_ACB64X_OFFSET EQ ACB$L_ACB64X
	ASSUME	IRP$L_ACB_FLAGS     EQ ACB$L_FLAGS
	ASSUME	IRP$L_THREAD_PID    EQ ACB$L_THREAD_PID
	ASSUME	IRP$L_KAST          EQ ACB$L_KAST
	ASSUME	IRP$PQ_ACB64_AST    EQ ACB64$PQ_AST
	ASSUME	IRP$Q_ACB64_ASTPRM  EQ ACB64$Q_ASTPRM
	ASSUME	IRP$PQ_ACB64_AST    EQ <ACB64X$PQ_AST   + IRP$PQ_ACB64_AST>
	ASSUME	IRP$Q_ACB64_ASTPRM  EQ <ACB64X$Q_ASTPRM + IRP$PQ_ACB64_AST>

5$:	BISL	#ACB$M_FLAGS_VALID,R11	;ACB_FLAGS in IRP will be valid

	ASSUME	<IRP$W_SIZE&3>	EQ 0	;assume IRP$W_SIZE offset is long aligned
	ASSUME	IRP$B_TYPE	EQ IRP$W_SIZE+2		;and followed by TYPE
	ASSUME	IRP$B_RMOD	EQ IRP$W_SIZE+3		;and RMOD
	ASHL	#24,R11,R11		;shift access mode into upper byte
	BISL3	#<<DYN$C_IRP@16>!IRP$C_LENGTH>,R11,-
		IRP$W_SIZE(R3)		;insert length, type, access mode
	MOVL	PCB$L_PID(R4),-		;insert process ID of current process
		IRP$L_PID(R3)
	GET_CURKTB			; Load current KTB address into R14
	MOVL	KTB$L_PID(R14),-   	; and then store the kernel thread's PID
		IRP$L_THREAD_PID(R3)     ; in the IRP.
	MOVL	R13,IRP$PS_FDT_CONTEXT(R3) ;insert FDT_CONTEXT address.

	MOVL	#IRP$PQ_ACB64_AST,-	;insert offset to ACB64X in the IRP
		IRP$L_ACB64X_OFFSET(R3)
	MOVL	#<ACB$M_64BITS ! ACB$M_THREAD_PID_VALID>,- ;identify "ACB64X" is to be used
		IRP$L_ACB_FLAGS(R3)                        ; and that the ACB thread PID field
							   ; is valid
	EVAX_LDQ R1,QIO$_ASTPRM(AP)	;R1 = 64-bit parameter for AST routine
	EVAX_STQ R0,-			;insert 64-bit AST routine address
		IRP$PQ_ACB64_AST(R3)
	EVAX_STQ R1,-			;insert 64-bit AST routine parameter
		IRP$Q_ACB64_ASTPRM(R3)

;
; If channel points to a section then convert section index to window address.
;
	MOVL	CCB$L_WIND(R6),-	;insert window address
		IRP$L_WIND(R3)
	.BRANCH_LIKELY
	BLEQ	NOSECT			;if CCB$L_WIND contain section offset
	CVTWL	CCB$L_WIND(R6),R0	;  sign extend it and
	MOVL	G^CTL$GL_PHD,R1		;  get address of process header
	ADDL	PHD$L_PST_BASE_OFFSET(R1),R1 ;  calculate base address of section table
	SUBL	R0,R1			; get section table entry addr
	MOVL	SEC$L_WINDOW(R1),-	; get address of real window
		IRP$L_WIND(R3)
NOSECT:
	MOVL	R5,IRP$L_UCB(R3)	;INSERT DEVICE UCB ADDRESS
	MOVL	R10,IRP$L_FUNC(R3)	;INSERT I/O FUNCTION CODE
	ASSUME	IRP$B_PRI       EQ   IRP$B_EFN+1
	ASSUME	IRP$B_CLN_INDX  EQ   IRP$B_EFN+2	;Normally unused, cleared
	;			     IRP$B_EFN+3	;Filler, cleared
	MOVL	KTB$L_PRIB(R14),R0	;GET KERNEL THREAD BASE PRIORITY
	ASHL	#8,R0,R0		;SHIFT IT UP 1 BYTE
	MOVB	QIO$_EFN(AP),R0		;FILL IN EFN IN LOW BYTE
	MOVL	R0,IRP$B_EFN(R3)	;INSERT EFN,PRI, CLEAR CLN_INDX, CLEAR FILLER
	.ENABL	QUADWORD
	MOVQ	QIO$_IOSB(AP),-		;INSERT I/O STATUS BLOCK ADDRESS (64-BITS)
		IRP$PQ_IOSB(R3)
	.DSABL	QUADWORD
        MOVL	R6,IRP$PS_CCB(R3) 	;Save CCB for quick reference in IOPOST
	MOVL	R12,IRP$L_STS(R3)	;Insert STS initial state
	MOVL	R9,IRP$L_CHAN(R3)	;INSERT NEGATIVE CHANNEL INDEX
	CLRL	IRP$L_SVAPTE(R3)	;CLEAR PTE ADDRESS
	CLRL	IRP$L_BOFF(R3)		;CLEAR BYTE OFFSET
	CLRL	IRP$L_BCNT(R3)		;CLEAR BYTE COUNT
	CLRL	IRP$L_STS2(R3)		;CLEAR STATUS EXTENSION
	.BRANCH_LIKELY
	BBC	#IO$V_NOVCACHE,-	;if nocache modifier set in func param
		QIO$_FUNC(AP),1$
	MOVL	#IRP$M_NOCACHE,-	;  set nocache bit in IRP$L_STS2
		IRP$L_STS2(R3)
1$:	CLRL	IRP$L_DIAGBUF(R3)	;INIT ORIGINAL PTE ADDR (LOG OR VIRT I/O
	MOVL	PCB$L_ARB(R4),-		;COPY ACCESS RIGHTS BLOCK ADDRESS
		IRP$L_ARB(R3)
;
; Copy the full 64-bits of the QIO device dependent arguments into the IRP.
; Also, if the upper 64-bits of the P1 parameter are significant, then the
; check to assure that the device driver has declared support for 64-bit
; addresses.
;
	.ENABL	QUADWORD
	ASSUME	QIO$_STKARG_PTR EQ 7*4	; QIO$_P1 is the first Alpha stack arg
	EVAX_LDQ R0,QIO$_STKARG_PTR(AP)	; R0 = ptr to params (see PARAMETER PASSING NOTE)
	EVAX_LDQ R1,QIO$_STKARG_P1(R0)	; Get P1 param (full 64-bits)
	EVAX_SEXTL R1,R11		; R11 = low 32-bit of P1, sign extended
	EVAX_STQ R1,IRP$Q_QIO_P1(R3)	; Copy 64-bit P1 param to IRP
   	EVAX_CMPEQ R1,R11,R11		; if upper 32-bit of P1 are significant
	.BRANCH_LIKELY
	BLBS	R11,2$
	EVAX_LDQ R11,FDT$Q_OK64BIT(R8)	;   Get driver mask of functions supporting 64-bits
	EVAX_SRL R11,R7,R11		;   if 64-bits not ok for this function
	BLBS	R11,2$
	MOVZWL	#SS$_NOT64DEVFUNC,R0	;     fail the QIO
	BRW	ABORT
2$:
	MOVQ	QIO$_STKARG_P2(R0),-	; Copy 64-bit P2 to IRP
		IRP$Q_QIO_P2(R3)
	MOVQ	QIO$_STKARG_P3(R0),-	; Copy 64-bit P3 to IRP
		IRP$Q_QIO_P3(R3)
	MOVQ	QIO$_STKARG_P4(R0),-	; Copy 64-bit P4 to IRP
		IRP$Q_QIO_P4(R3)
	MOVQ	QIO$_STKARG_P5(R0),-	; Copy 64-bit P5 to IRP
		IRP$Q_QIO_P5(R3)
	MOVQ	QIO$_STKARG_P6(R0),-	; Copy 64-bit P6 to IRP
		IRP$Q_QIO_P6(R3)
	.DSABL	QUADWORD

	.IF DF	CA$_MEASURE_IOT
	.BRANCH_LIKELY
	TSTL	G^PMS$GL_IOPFMPDB	;DATA COLLECTION ENABLED?
	BEQL	3$			;BR IF NO
	JSB	G^PMS$START_RQ		;INSERT START OF I/O REQUEST MESSAGE
	BRB	4$			;
3$:
	.ENDC

	MOVAB	G^PMS$GL_IOPFMSEQ,R11	; Point to sequence number
31$:	EVAX_LDLL	-		; Load the sequence number into R0
		R0,(R11)
	ADDL3	#1,R0,R1		; Incremented version in R1
	EVAX_STLC	-		; Attempt to store new sequence number
		R1,(R11)
	.BRANCH_UNLIKELY
	BLBC	R1,31$			; Failed, try again
	MOVL	R0,IRP$L_SEQNUM(R3)	; Success; also save previous in IRP

4$:	MOVL	UCB$L_DEVCHAR(R5),R11	;GET DEVICE CHARACTISTICS FOR MANY
					;COMPARES BELOW 
;
; CHECK IF REQUESTING PROCESS HAS PRIVILEGE TO ACCESS DEVICE
;
; NOTE: LOW BIT OF FUNCTION CODE WAS CLEARED ABOVE
;
	BICL3	#1,R7,R9		;PREPARE FOR VIRTUAL CHECK

	ASSUME	IO$_READVBLK-IO$_WRITEVBLK EQ 1 
	CMPL	S^#IO$_WRITEVBLK,R9	;VIRTUAL READ OR WRITE?
	BNEQ	CHKLOGPHY		;IF NEQ NO
;
; begin code only executed for IO$_READVBLK and IO$_WRITEVBLK

	BBC	S^#DEV$V_FOD,R11,5$ 	;IF CLR, NOT FILE DEVICE
	;
	; THE FOLLOWING TEST IS NECESSITATED BY THE SYSTEM INITIALIZATION SEQUENCE
	;
	TSTL	IRP$L_WIND(R3)		;WINDOW ADDRESS SPECIFIED?
	BNEQ	CHK_ASTCNT			;IF NEQ YES
	BBC	S^#DEV$V_MNT,R11,ABORT_NOPRIV	;IF CLR, DEVICE NOT MOUNTED
	BBC	S^#DEV$V_FOR,R11,CHK_ASTCNT	;IF CLR, MOUNTED STRUCTURED
	;
	; CONVERT VIRTUAL READ/WRITE FUNCTION TO ITS LOGICAL COUNTERPART
	;
5$:	SUBL	S^#IO$_READVBLK-IO$_READLBLK,R7 ;CONVERT TO LOGICAL FUNCTION
	SUBL	S^#IO$_READVBLK-IO$_READLBLK,IRP$L_FUNC(R3) ;
	BITL	#<DEV$M_SPL!-		;NOT SPOOLED,
		DEV$M_FOD!-		;NOT FILE DEVICE,
		DEV$M_SHR>,R11 		;AND NOT SHARABLE
	BNEQ	CHKLOGPHY		;BR IF SATISFIED

; end code only executed for IO$_READVBLK and IO$_WRITEVBLK
; 


;
; CHECK IF AST QUOTA IS EXCEEDED
; 
CHK_ASTCNT:
 	.BRANCH_LIKELY
	TSTL	PCB$L_ASTCNT(R4)	; AST quota was exceeded?
	BGEQ	STEP2_FDT		; NO  - Begin calling the FDT routines.
	EVAX_LDQ R0,-			; YES - Did this request need an AST?
		IRP$PQ_ACB64_AST(R3)
	EVAX_BEQ R0,STEP2_FDT		; NO  - Begin calling the FDT routines.
					; YES - Abort the I/O w/ SS$_EXQUOTA.

	MOVZWL	#SS$_EXQUOTA,R0		; Final QIO status: AST quota exceeded.
	CALL_ABORTIO DO_RET=NO		; Abort the I/O. Both Step 1 & 2 will
					;  return here.
	BRW	QIO_EXIT		; Deallocate FDT Context area & return.

;
; Request is not a virtual read/write (at least, not now!). See if this is some
; virtual I/O function, in which case no further access/privilege checks
; are needed.
;
CHKLOGPHY:
	CMPL	S^#IO$_LOGICAL,R7	;virtual I/O function?
	BLSS	CHK_ASTCNT		;if so, no further access checks.
;
; This is a logical or physical I/O request.  Several code paths here require
; a privilege check, which all require the device name to be passed for
; auditing.  The item list is set up once, here.
;
	CLRQ	-(SP)			;retadr + terminator	=> 8
	MOVL	UCB$L_ORB(R5),R1	;fetch ORB address
	PUSHL	ORB$L_NAME_POINTER(R1)	;bufadr			=> 4
	MOVZWL	ORB$W_NAME_LENGTH(R1),R1;get name length
	ADDL3	#<NSA$_DEVICE_NAME@16>,-
		R1,-(SP)		;bufsiz			=> 4 / 16
	AUDIT_S_ITMLST = 16		;size of auditing item list
	MOVL	SP,R9			;fetch address of item list

;
; 3 policies exist for logical/physical I/O functions, based on device
; type and status:
;
; 1. TCB controlled devices
;	spooled devices
;    	file oriented device, nothing mounted
;    	file oriented device, VMS volume mounted
; 2. Potentially shared devices
;	file oriented device, foreign volume mounted
;    	non-file oriented device, shareable
; 3. Single user devices
;	non-file oriented device, non-shareable
;
	BITL	#DEV$M_SPL,R11		;If spooled device
	BNEQ	020$			; test for privilege to access
	BITL	#DEV$M_FOD,R11		;Is the device file-oriented?
	BEQL	010$			; 
	BITL	#DEV$M_MNT,R11		;Is the device mounted?
	BEQL	020$			; no, case 1
	BITL	#DEV$M_FOR,R11		;Is the volume mounted foreign?
	BEQL	020$			; no, case 1
	BRB	030$			; yes, case 2
;	device is not spooled or file oriented
010$:	BITL	#DEV$M_SHR,R11		;shareable?
	BNEQ	030$			; yes, case 2
	BRW 	260$			; no - case 3
020$:	BRW	110$			;branch helper -- go to case 1

; Case 2, potentially shareable device:
;	file oriented, foreign mounted volume
;  or	non-file oriented, shareable 
;
; in this case, 
;   logical IO is allowed if
;	LOGICAL access or (LOG_IO or PHY_IO) privilege
;   physical IO is allowed if
;	(PHYSICAL access and LOG_IO privilege) or PHY_IO privilege

030$:	MOVL	R6,R1			;set deaccess audit key
	CMPL	S^#IO$_PHYSICAL,R7	;physical I/O function?
	BGEQ	060$			;br if physical I/O
 	BITL	#CCB$M_LOGCHKDON,-
		CCB$L_STS(R6)		;protection check already done?
	BNEQ	050$			;yes - skip redundant check
	MOVZBL	#ARM$M_LOGICAL,R0	;set access desired
	; R0 = access requested, R1 = deaccess key, PCB(R4), UCB(R5)
	.SET_REGISTERS  READ=<R0,R1,R4,R5>,WRITTEN=<R0>
	JSB	G^EXE$CHECK_DEVICE_ACCESS 	;perform protection check
	BLBS	R0,040$			;Last chance, try privilege
	BRW	120$			;...branch helper
040$:	BISL	#CCB$M_LOGCHKDON,-
		CCB$L_STS(R6)		;note protection check done
050$:	MOVAL	AUDIT_S_ITMLST(R9),SP	;remove item list from stack
	BRW	CHK_ASTCNT		;access is allowed
;
; case 2(b) - physical access
;  we do an unaudited test of PHY_IO priv to avoid an extraneous privilege
;  failure audit.  It is still possible to audit successful use of PHYSICAL
;  access, when lack of LOG_IO will fail the request.
;
;  Possible audit results:
;    1) successful PHY_IO privilege audit
;    2) failed LOGICAL access
;    3) successful LOGICAL access (audited the first time) and success LOGIO priv
;    4) successful LOGICAL access (audited the first time) and failed  LOGIO priv
;
060$:	IFNPRIV	PHY_IO,070$		;avoid excess nopriv audit
	$IFPRIV	PHY_IO,160$,-           ;request is allowed, with PHY_IO priv
		MSG=PHY_IO_8,-
		ITMLST=R9,-
		PRESERVE=NO		; (this should not fall through, but
					;  no real harm if it does)
070$:	BITL	#CCB$M_PHYCHKDON,-
		CCB$L_STS(R6)		;protection check already done?
	BNEQ	090$			;yes - skip redundant check
	MOVZBL	#ARM$M_PHYSICAL,R0	;set access desired
	; R0 = access requested, R1 = deaccess key, PCB(R4), UCB(R5)
	.SET_REGISTERS  READ=<R0,R1,R4,R5>,WRITTEN=<R0>
	JSB	G^EXE$CHECK_DEVICE_ACCESS 	;perform protection check
	BLBS	R0,080$			;access allowed -- remember that!
	BRW	ABORT			;return the failure
080$:	BISL	#CCB$M_PHYCHKDON,-
		CCB$L_STS(R6)		;note protection check done
090$:	$IFNPRIV LOG_IO,200$,-
		MSG=LOG_IO_7,-
		ITMLST=R9,-
		PRESERVE=NO		;br if caller has LOG_IO
	BRW	160$			;access allowed - go check for diagnostic

;
; Case 1, devices under full control of the TCB; access is allowed only 
;          by use of privilege
;
; If the device is spooled or not mounted foreign then only allow access if the
; caller is privileged.  I.e., you can't do I/O to a device that's not mounted
; unless you're privileged.  Note that during bootstrap this is exactly what
; we're doing.  How convenient that we also have all the privileges we need.
;
;
;   logical IO is allowed if
;	(LOG_IO or PHY_IO) privilege
;   physical IO is allowed if
;	PHY_IO privilege
;
110$:	CMPL	S^#IO$_PHYSICAL,R7	;physical I/O function?
	BGEQ	150$			;br if physical I/O
;  case 1(a) - logical I/O to TCB controlled device.
;	This is a fairly common code path for teh TCB itself, including
;	I/O done by the file system. As an optimization, we check the
;	PCB$V_NOAUDIT, and skip the audited privilege check if set.
;
	TSTL	PCB$L_NOAUDIT(R4)	;TCB process?
	BEQL	120$			;if not, do the audited priv test
	IFNPRIV	LOG_IO,120$		;br if caller doesn't have LOG_IO
	MOVAL	AUDIT_S_ITMLST(R9),SP	;remote item list from stack
	BRW	CHK_ASTCNT		;access is allowed
;   Privilege auditing hack:
;	Avoid a failed LOG_IO privilege audit if the user has PHY_IO 
;	but not LOG_IO, as the user has adequate privilege for this function.
; 
120$:	IFNPRIV	PHY_IO,130$             ;if no PHY_IO priv, just do audited
					;  test for LOG_IO
	IFNPRIV	LOG_IO,140$		;if no LOG_IO, do audited test for 
					;  PHY_IO (which we know we have)
130$:	$IFPRIV	LOG_IO,210$,-
		MSG=LOG_IO_1,-
		ITMLST=R9,-
		PRESERVE=NO		;br if caller has LOG_IO
	BRW	200$			;leave with NOPRIV
140$:	$IFPRIV	PHY_IO,210$,-
		MSG=PHY_IO_9,-
		ITMLST=R9,-
		PRESERVE=NO		;br if caller has PHY_IO
	BRW	200$			;leave with NOPRIV
;
; case 1(b) - physical I/O
;
150$:	$IFPRIV	PHY_IO,160$,-
		MSG=PHY_IO_8,-
		ITMLST=R9,-
		PRESERVE=NO		;br if caller has PHY_IO
	BRW	200$			;leave with NOPRIV

;
; Physical I/O access was granted.  We now need to perform a privilege check if
; we had an associated diagnostic buffer.  Note that because the logical and
; physical paths both come through here that we need to skip these checks if
; this is a logical function.
;
160$:	CMPL	S^#IO$_PHYSICAL,R7	;logical I/O? If (31 < R7) BR.
	BLSS	210$			;yes - just go do access
170$:	BISL	#IRP$M_PHYSIO,IRP$L_STS(R3) ;set physical I/O flag
	TSTL	IRP$L_QIO_P6(R3)	;Is a diagnostic buffer specified?
	BEQL	210$			;NO  - Branch to common exit.
	MOVL	UCB$L_DDT(R5),R10	;YES - Get address of ddt.
	MOVZWL	DDT$W_DIAGBUF(R10),R10	;get size of diag buffer incl. header.
					; bit 15 = 1 means 64-bit header to be
					;  allocated. 
	BEQL	210$			;if EQL no diagnostic functions

	$IFNPRIV DIAGNOSE,200$,-
		MSG=DIAGNOSE_5,-
		ITMLST=R9,-
		PRESERVE=NO		;process have DIAGNOSE?
	.ENABLE QUADWORD
	;
	; Allocate the diagnostic buffer.
	; 
	; This routine will determine if a 64 bit diagnostic buffer
	; header is requested by testing the upper bit of the size
	; field.  The size field was retrieved from the DDT$L_DIAGBUF, 
	; (which was previously set in the driver DDTAB.) The 
	; diagnostic buffer header, whether 32-bit or 64-bit requested, 
	; is allocated using EXE_STD$ALLOCBUF and the proper fields	
	; in the IRP and the header are initialized. Upon completion:
	; IRP$L_DIAGBUF = diagnostic buffer packet address
	; IRP$L_STS     = IRP$M_DIAGBUF
	; BUFIO$PS_UVA32= VA of diag buf if 32-bits diag buff was inputted
	;		    or BUFIO$K_64 (-1)
	; BUFIO$PQ_UVA64= VA of diag buf if 64-bits diag buff was inputted.
        ; STATUS        = SS$_NORMAL, SS$_NOT64BITFUNC, or something from
	;		    ALLOC_BUF.
	; 
	; Status(R0) = EXE_STD$ALLOC_DIAGBUF(irp(R3), diag_buf, pktdatsiz);

	$SETUP_CALL64	ARG_COUNT=3
	$PUSH_ARG64	R10		; p3 = diag buffer size incl header,
 	$PUSH_ARG64   	IRP$Q_QIO_P6(R3); p2 = user addr of diagnostic buffer, 
	$PUSH_ARG64	R3		; p1 = IRP address,
        $CALL64	EXE_STD$ALLOC_DIAGBUF	; Allocate & initialize the buffer.
	BLBC	R0,ABORT  		; If error call abortio. 

	BRW	CHK_ASTCNT

	.DSABL	QUADWORD

;
;common exit - restore the stack and continue
;
210$:	MOVAL	AUDIT_S_ITMLST(R9),SP	;remove item list from stack
	BRW	CHK_ASTCNT		;go do access

;
; Case 3, this is a non-shared, non file-structured device.  
;  If this is a logical I/O, grant access.
;  If this is a physical I/O request, 
;	grant access if the caller has LOG_IO or PHY_IO.  
;
260$:	CMPL	S^#IO$_PHYSICAL,R7	;logical I/O function?
	BLSS	210$			;yes - access allowed
;
;   Privilege auditing hack:
;	Avoid a failed LOG_IO privilege audit if the user has PHY_IO 
;	but not LOG_IO, as the user has adequate privilege for this function.
; 
	IFNPRIV	PHY_IO,270$
	IFNPRIV	LOG_IO,280$
270$:	$IFPRIV	LOG_IO,170$,-
		MSG=LOG_IO_6,-
		ITMLST=R9,-
		PRESERVE=NO		;br if caller has LOG_IO
	BRW	200$			;leave with NOPRIV
280$:	$IFPRIV	PHY_IO,170$,-
		MSG=PHY_IO_15,-
		ITMLST=R9,-
		PRESERVE=NO		;br if caller has PHY_IO
;	BRW	200$			;leave with NOPRIV
;
;common error - restore the stack and quit
;

200$:	MOVAL	AUDIT_S_ITMLST(R9),SP	;remote item list from stack
ABORT_NOPRIV:
	MOVZWL	#SS$_NOPRIV,R0		;SET NO PRIVILEGE STATUS
ABORT:	CALL_ABORTIO DO_RET=NO		; Abort the I/O. Both Step 1 & 2 will
					;  return here.
	BRW	QIO_EXIT		; Deallocate FDT Context area & return.

	.PAGE
	.SBTTL	STEP 2 FDT DISPATCHING
STEP2_FDT:

;******************************************************************************
;	Step 2 Function Decision Table Processing.
;
;	1) Push the upper level FDT routine parameters. (IRP,PCB,UCB,CCB)
;	2) Call the upper level FDT routine using func code as index.
;	3) Move the final QIO system service status from the FDT Context 
;		structure into R0.
;	4) Move the R1 value from the FDT Context structure in R1 for the
; 	 	System Service Dispatcher.
;	5) Deallocate the FDT Context structure from the stack.
;	6) Return to the caller of EXE$QIO.
;
; Register Usage:				
;	R3 = I/O Request packet.
;	R4 = PROCESS Control Block.
;	R5 = Unit Control Block.
;	R6 = Channel Control Block.
;	R7 = I/O Function code bit number.
;	R8 = Address of FDT. 
;	R13 = Address of FDT CONTEXT structure.
;
	ASSUME	FDTARG$_NARGS EQ 4	

	MOVL	FDT$PS_FUNC_RTN(R8)[R7],R0 

	PUSHL	R6			; Push CCB address,
	PUSHL	R5			; Push UCB address,
	PUSHL	R4			; Push PCB address,
	PUSHL	R3			; Push IRP address.
	CALLS	#FDTARG$_NARGS,(R0)     ; Make call upper level FDT routine.


        .IF	DEFINED,CA$_IO_DEBUG	; if debug version (SYSGEN SYSTEM_CHECK enabled)

	CMPL	R0,#SS$_FDT_COMPL	; Upper level FDT routines must return
	.BRANCH_LIKELY			; "FDT complete" status.
	BEQL	101$			; If not, something is wrong...
	BUG_CHECK INCONSTATE,FATAL
101$:
	.ENDC	;DEFINED,CA$_IO_DEBUG


QIO_EXIT:
	MOVL	FDT_CONTEXT$L_QIO_STATUS(R13),R0 ; Final QIO status to R0.
	EVAX_LDQ R1,FDT_CONTEXT$Q_QIO_R1_VALUE(R13)   ; 64-bit R1 value in R1.

QIO_EXIT_NO_CONTEXT:
	ASSUME	<SS$_WAIT_CALLERS_MODE&1> EQ 0
	ASSUME	<SS$_QIO_CROCK&1> EQ 0
	.BRANCH_LIKELY
	BLBS	R0,5$

	CMPL	R0,#SS$_WAIT_CALLERS_MODE ; Can not setipl.
	BEQL	10$			; 
	CMPL	R0,#SS$_QIO_CROCK	; Can not setipl.
	BEQL	10$			; 
5$:	SETIPL	#0			; Set IPL to User.
	RET				; Return to caller of EXE$QIO.

10$:	.ENABL	QUADWORD
	MOVL	(AP),R25		; Set up 64-bit param registers to reissue
	MOVQ	1*4(AP),R16		; (done here because of SSFLAG_K_WCM_NO_SAVE)
	MOVQ	2*4(AP),R17
	MOVQ	3*4(AP),R18
	MOVQ	4*4(AP),R19
	MOVQ	5*4(AP),R20
	MOVQ	6*4(AP),R21
	.DSABL	QUADWORD
	RET				; Return to system service dispatcher

	.PAGE
	.SBTTL	QIO ERROR AND EXCEPTION HANDLING ROUTINES
;
; MISCELLANEOUS ERROR HANDLING AND EXCEPTION HANDLING ROUTINES.  THESE HAVE
; BEEN MOVED OUT OF LINE TO MAKE THE COMMON PATH NEARLY BRANCH FREE.
;
; Final QIO status was not put in the FDT Context area for this path in
; order to reduce memory referemces.
;
ACCVIO:	MOVZWL	S^#SS$_ACCVIO,R0	; SET ACCESS VIOLATION STATUS
ERROR:	SETIPL	#0			; ALLOW INTERRUPTS
	PUSHL	R0			; SAVE FINAL QIO STATUS
	GET_CURKTB			; GET CURRENT KTB ADDRESS
	MOVL	KTB$L_PID(R14),R1	; GET KERNEL THREAD'S PID
	CLRL	R2			; SET PRIORITY CLASS INCREMENT
	MOVZBL	QIO$_EFN(AP),R3		; GET SPECIFIED EVENT FLAG NUMBER
	JSB	G^SCH$POSTEF		; POST SPECIFIED EVENT FLAG
	POPL	R0			; RESTORE FINAL QIO STATUS
	BRW 	QIO_EXIT_NO_CONTEXT	; R0 contains the final QIO status. 


	.PAGE
	.SBTTL	BUILD I/O PACKET FOR PAGE READ/WRITE
;+
; EXE$BUILDPKTR  - BUILD I/O PACKET FOR PAGE READ
; EXE$BUILDPKTW  - BUILD I/O PACKET FOR PAGE WRITE
; EXE$BLDPKTSWPR - BUILD I/O PACKET FOR SWAP READ
; EXE$BLDPKTSWPW - BUILD I/O PACKET FOR SWAP WRITE
; EXE$BLDPKTMPW  - BUILD I/O PACKET FOR MODIFIED PAGE WRITER
;
; This routine is called to fill out and queue an I/O packet
; for a swapping or paging read or write.
;
; COMMON INPUTS:
;
;	R0 		= virtual block number
;	R1 		= number of bytes to transfer (page increments)
;	R2 		= window address for mapping VBN to LBN
;	R3 		= PTE address (see entry point dependent requirements below)
;	R4		= current process control block address
;	     		  PCB$L_DIOCNT(R4) is assumed greater than zero
;	     		  and must be checked by the caller.
;	R5 		= I/O request packet address
;
;	IRP$B_PRI	= the priority at which the transfer is to be queued
;
; EXE$BUILDPKTR ADDITIONAL INPUTS:
;
;	R3		= 64-bit VA_PTE
;
;	IRP$Q_PARAM_1	= original process page table entry backing store
;			  address if page was a copy on reference page,
;			  with PFN$V_GBLBAK set if it was global CRF.
;			= 0 if not a copy on reference page
;	IRP$Q_PARAM_0	= slave PTE address if global CRF (IRP$Q_PARAM_3 = 0)
;			= slave PTE index if global not CRF (IRP$Q_PARAM_3 != 0)
;			= 0 if not global
;	IRP$Q_PARAM_2   = starting VA if upcall performed when pagefault 
;			  occurred.  Otherwise, this field will contain
;			  -1.
;	IRP$Q_PARAM_3	= PT PFN if global non-CRF
;			= 0 otherwise
;
; EXE$BUILDPKTW ADDITIONAL INPUTS:
;
;	R3		= 64-bit VA_PTE
;
;	IRP$W_SIZE	= filled in
;	IRP$B_TYPE	= filled in
;	IRP$B_EFN	= event flag number
;	IRP$B_RMOD	= request mode ! ACB$V_QUOTA.  If ACB$V_QUOTA is set,
;			  process requested an AST on page write completion
;	IRP$PQ_ACB64_AST= 64-bit AST routine address if requested
;	IRP$Q_ACB64_ASTPRM
;			= 64-bit AST parameter if specified
;	IRP$PQ_IOSB	= 64-bit address of I/O status block if specified.  If
;			  non-zero, then process expects I/O status returned.
;
; EXE$BLDPKTSWPR, EXE$BLDPKTSWPW, EXE$BLDPKTMPW ADDITIONAL INPUTS:
;
;	R3		= 32-bit S0/S1 address of the PTEs
;
;	IRP$W_SIZE	= filled in
;	IRP$B_TYPE	= filled in
;	IRP$L_IIRP_P1	= address of a special kernel AST routine to call
;
;
; OUTPUTS:
;
;	R0,R1,R2,R3,R4,R5 may be destroyed.
;-

	.ENABL	LSB

; Note that the differentiation between READ and WRITE operations is
; encoded in the setting of the IRP$M_FUNC bit.
;
;	IRP$M_FUNC = 1 => IO$_READPBLK	; Read operation
;	IRP$M_FUNC = 0 => IO$_WRITEPBLK	; Write operation


	UNIVERSAL_JSB	EXE$BLDPKTSWPR, INPUT=<R0,R1,R2,R3,R4,R5>,-
					SCRATCH=<R0,R1,R2,R3,R4,R5>
;EXE$BLDPKTSWPR::			;BUILD SWAP READ PACKET
	MOVL	#<IRP$M_SWAPIO ! IRP$M_VIRTUAL ! IRP$M_FUNC>,-
		IRP$L_STS(R5)
	.DSABL	FLAGGING		;%AMAC-I-BRANCHBET, branching between routines
	BRB	10$			;READ FUNCTION, TYPE/SIZE ALREADY SET
	.ENABL	FLAGGING


	UNIVERSAL_JSB	EXE$BLDPKTSWPW, INPUT=<R0,R1,R2,R3,R4,R5>,-
					SCRATCH=<R0,R1,R2,R3,R4,R5>
;EXE$BLDPKTSWPW::			;BUILD SWAP WRITE PACKET
	MOVL	#<IRP$M_SWAPIO ! IRP$M_VIRTUAL>,-
		IRP$L_STS(R5)
	.DSABL	FLAGGING		;%AMAC-I-BRANCHBET, branching between routines
	BRB	5$			;WRITE FUNCTION, TYPE/SIZE ALREADY SET
	.ENABL	FLAGGING


	UNIVERSAL_JSB	EXE$BLDPKTMPW, INPUT=<R0,R1,R2,R3,R4,R5>,-
				       SCRATCH=<R0,R1,R2,R3,R4,R5>
;EXE$BLDPKTMPW::			;BUILD I/O PACKET FOR MODIFIED PAGE WRITER
	MOVL	#<IRP$M_SWAPIO ! IRP$M_VIRTUAL>,-
		IRP$L_STS(R5)
	.DSABL	FLAGGING		;%AMAC-I-BRANCHBET, branching between routines
	BRB	5$			;WRITE FUNCTION, TYPE/SIZE ALREADY SET
	.ENABL	FLAGGING


	UNIVERSAL_JSB	EXE$BUILDPKTW, INPUT=<R0,R1,R2,R3,R4,R5>,-
				       SCRATCH=<R0,R1,R2,R3,R4,R5>
;EXE$BUILDPKTW::				;BUILD I/O PACKET FOR PAGE WRITE
	MOVL	#<IRP$M_PAGIO ! IRP$M_VIRTUAL>,-
		IRP$L_STS(R5)
5$:	INCL	WCB$L_WRITES(R2)	; Increment WRITE count
	MOVL	#IO$_WRITEPBLK,-	;  and set WRITE function code
		IRP$L_FUNC(R5)
	.DSABL	FLAGGING		;%AMAC-I-BRANCHBET, branching between routines
	BRB	20$			;TYPE/SIZE ALREADY SET IN PACKET
	.ENABL	FLAGGING


	UNIVERSAL_JSB	EXE$BUILDPKTR, INPUT=<R0,R1,R2,R3,R4,R5>,-
				       SCRATCH=<R0,R1,R2,R3,R4,R5>
;EXE$BUILDPKTR::				;BUILD I/O PACKET FOR PAGE READ
	MOVL	#<IRP$M_PAGIO ! IRP$M_VIRTUAL ! IRP$M_FUNC -
		  ! IRP$M_CACHE_PAGIO>,-
		IRP$L_STS(R5)

	ASSUME	<IRP$W_SIZE&3>	EQ 0	;ASSUME IRP$W_SIZE OFFSET IS LONG ALIGNED
	ASSUME	IRP$B_TYPE	EQ IRP$W_SIZE+2		;AND FOLLOWED BY TYPE
	ASSUME	IRP$B_RMOD	EQ IRP$W_SIZE+3		;AND RMOD

	MOVL	#<<DYN$C_IRP@16>!IRP$C_LENGTH>,- ;SET SIZE AND TYPE, CLEAR
		IRP$W_SIZE(R5)		;RMOD, OF PACKET

10$:	INCL	WCB$L_READS(R2)		; Increment READ count
	MOVL	#IO$_READPBLK,-		;  and set READ function code
		IRP$L_FUNC(R5)

20$:	CLRL	IRP$PS_FDT_CONTEXT(R5)	; Clear the FDT Context area since 
					;  this is not FDT context.
	;
	; If this is an entry into EXE$BUILDPKTR or EXE$BUILDPKTW, the PAGIO
	; bit will have been set and the R3 input contains a 64-bit VA_PTE that
	; needs to be converted into a 32-bit SVAPTE via the DIOBM in the IRP.
	; Otherwise, for a SWAPIO the R3 input is already a 32-bit system space
	; address.  It either points to the swapper map or into a MPWMAP IRP.
	;
	BBC	#IRP$V_PAGIO,-		; If this is a paging I/O
		IRP$L_STS(R5),40$
	PUSHR	#^M<R0,R1>		;   Save registers destroyed by FILL_DIOBM

	MOVL	MMG$GL_VPN_TO_VA,R0	;   R0 = Shift count, bytes-to-pages
	ADDL	MMG$GL_BWP_MASK,R1	;   R1 = byte count + (page size - 1)
	EVAX_SRL R1,R0,R1		;   R1 = Page count, rounded up

	MOVAL	IRP$L_SVAPTE(R5),R20	;   R20 = addr of 32-bit SVAPTE cell in IRP
	MOVAL	IRP$R_DIOBM(R5),R16	;   R16 = addr of DIOBM in IRP

	$SETUP_CALL64	ARG_COUNT=5
	$PUSH_ARG64	R20		;   Param5 = addr to return 32-bit SVAPTE
	$PUSH_ARG64	#0		;   Param4 = resource wait enabled; always returns success
	$PUSH_ARG64	R1		;   Param3 = page count
	$PUSH_ARG64	R3		;   Param2 = VA_PTE
	$PUSH_ARG64	R16		;   Param1 = DIOBM address
	$CALL64	IOC_STD$FILL_DIOBM	;   Setup the DIOBM (no error status possible)
	
	MOVL	IRP$L_SVAPTE(R5),R3	;   R3 = pseudo SVAPTE (written by FILL_DIOBM)
	POPR	#^M<R0,R1>		;   Restore saved R0,R1 values
	BRB	50$			; Else
40$:	MOVL	R3,IRP$L_SVAPTE(R5)	;   Put input R3 value into IRP$L_SVAPTE
50$:
	MOVL	R3,IRP$L_DIAGBUF(R5)	;NEED COPY OF ORIGINAL FOR SEGMENTED XFERS
	MOVL	R5,R3			;PACKET ADDRESS TO R3

	CLRL	IRP$L_CHAN(R3)		; Clear channel
	MOVL	WCB$L_ORGUCB(R2),R5	;GET UCB ADDRESS FROM WINDOW
	MOVL	R5,IRP$L_UCB(R3)	;SET UCB ADDRESS
	GET_CURKTB			;GET CURRENT KERNEL THREAD BLOCK ADDRESS
	MOVL	KTB$L_PID(R14),IRP$L_PID(R3) ;STORE KERNEL THREAD'S PID IN IRP
	MOVL	R0,IRP$L_SEGVBN(R3)	;STARTING VIRTUAL BLOCK NUMBER
	MOVL	R2,IRP$L_WIND(R3)	;WINDOW ADDRESS
	MOVL	PCB$L_ARB(R4),IRP$L_ARB(R3) ;ACCESS RIGHTS BLOCK ADDRESS

	CLRL	IRP$L_BOFF(R3)		;ZERO BYTE OFFSET
	MOVL	R1,IRP$L_BCNT(R3)	;SET BYTE COUNT
	CLRL	IRP$L_ABCNT(R3)		;ZERO ACCUMULATED BYTE COUNT
	MOVL	R1,IRP$L_OBCNT(R3)	;SET ORIGINAL BYTE COUNT
	CLRL	IRP$L_STS2(R3)		;CLEAR STATUS EXTENSION

	.IF	DF,CA$_MEASURE_IOT

	.BRANCH_LIKELY
	TSTL	G^PMS$GL_IOPFMPDB    	; Is data collection enabled ?
	BEQL	60$			; If not, br to sequence number increment
	JSB	G^PMS$START_RQ		; Handle data collection (will inc seq num)
	BRB	80$

; Collection is off, but bump sequence number

60$:	PUSHR	#^M<R0,R1,R2>		; Save R0, R1, R2
	MOVAB	G^PMS$GL_IOPFMSEQ,R2	; Point to sequence number
70$:	EVAX_LDLL	-		; Load the sequence number into R0
		R0,(R2)
	ADDL3	#1,R0,R1		; Incremented version in R1
	EVAX_STLC	-
		R1,(R2)			; Attempt to store new sequence number
	BLBC	R1,70$			; Failed, try again
	MOVL	R0,IRP$L_SEQNUM(R3)	; Success; also save previous in IRP
	POPR	#^M<R0,R1,R2>		; Restore R0, R1, R2
80$:
	.ENDC	;DF,CA$_MEASURE_IOT

	.PRESERVE ATOMICITY
	DECL	PCB$L_DIOCNT(R4)	;CHARGE DIRECT I/O TO PROCESS
	.NOPRESERVE ATOMICITY

	BBC	#CACHE_STATE$V_ON,-	;   if cacheing enabled
		CACHE$GL_STATE,90$           
	.SET_REGISTERS -
		READ	= <R0,R1,R2,R3,R4,R5>,-
		WRITTEN	= <R0,R1>
	JSB	CACHE$PAGEIO		; Try to use cache
	TSTL	R1			; Was I/O completed?
	BNEQ	90$			; No
	RSB

90$:	; IOC_STD$QNXTSEG1(VBN(R0),BCNT(R1),WCB(R2),IRP(R3),PCB(R4),UCB(R5),-
	;		   UCBOUT(R5))
	CALL_QNXTSEG1			;CALL TO QUEUE 1ST SEG OF I/O REQUEST
					;(R0,R1,R2,R4 scratched)
	RSB				;AND RETURN
	.DSABL	LSB

	.PAGE
	.SBTTL	COMPLETE I/O OPERATION
;+
; EXE_STD$ABORTIO  - Abort an I/O operation.
;
; This FDT Completion routine is called from upper level FDT routines and
; FDT support routines to abort an I/O operation. 
;
; FUNCTION:
;	The Final $QIO system service status is stored in the FDT Context area
; 	and in the IRP$L_IOST1. Clear pointer to FDT Context structure.
;	Undo PHD I/O accounting.
;	The IRP is inserted in the I/O postprocessing queue. A software 
;	interrupt is generated to initiate I/O post processing. 
;	Return SS$_FDT_COMPL to EXE$QIO.
;
; ENVIRONMENT:
;	THE FORKLOCKS ARE ACQUIRED TO INSURE THAT THE LOCK IS HELD WHILE
;	UPDATING OPERATIONS COUNT ON THE UCB. AND TO MAKE SURE THAT WE CAN
;	ALWAYS RELEASE THE FORKLOCK, IN CASE THE DRIVER HAD ACQUIRED IT.
;
; CALLING SEQUENCE:
;	#SS$_FDT_COMPL = EXE_STD$ABORTIO (IRP, PCB, UCB, FINAL_STATUS);
; INPUTS:
;	ARG$_IRP(AP) 		- I/O Request Packet	
;	ARG$_PCB(AP)		- Process Control Block
;	ARG$_UCB(AP)		- Unit Control Block
;	ARG$_FINAL_STATUS(AP)	- The final status parameter.
; OUTPUTS:
;	The Final $QIO system service status is stored in the FDT Context area.
;	SS$_FDT_COMPL is returned.
; RETURN VALUE:
;	R0 = SS$_FDT_COMPL
;-
$OFFDEF	ARG,<-		; Define the AP offset of the input parameters.
	IRP,-		; Offset of IRP parameter.
	PCB,-		; Offset of PCB parameter.
	UCB,-		; Offset of UCB parameter.
	FINAL_STATUS-	; Offset of final status parameter.
	>
 
	.ENABL	LSB

	UNIVERSAL_ENTRY EXE_STD$ABORTIO, -
		MASK=<M^<R3,R4,R5>>

;EXE_STD$ABORTIO::			; Abort an  I/O operation.
        MOVL    ARG$_FINAL_STATUS(AP),R5     ; Set final $QIO system service...
        CMPL    #SS$_FDT_COMPL,R5            ; ABORTIO called more than once?
        BEQL    99$                          ; YES - Just return.

        MOVL    ARG$_IRP(AP),R3              ; Get the IRP address input.
        MOVL    IRP$PS_FDT_CONTEXT(R3),R0    ; Get the FDT context structure.
        MOVL    R5,-                         ; Set final $QIO system service...
                FDT_CONTEXT$L_QIO_STATUS(R0) ;  status.
        CLRL    IRP$PS_FDT_CONTEXT(R3)  ; Clear FDT context pointer in IRP.

        MOVL    R5,IRP$L_IOST1(R3)      ; Set the error status in IOST1 and
        CLRL    IRP$L_IOST2(R3)         ;  clear IOST2 for IOPOST's benefit.

	BISL	#IRP$M_ABORTIO,-	; Set I/O terminated by EXE_STD$ABORTIO.
		IRP$L_STS2(R3)

	MOVL    ARG$_UCB(AP),R5         ; Get the UCB address input.

	FORKLOCK UCB$B_FLCK(R5),-
		PRESERVE=NO		;LOCK FORK THREADS/USE IPL OF LOCK

	BBS	#IRP$V_FASTIO,-		; Fast-IO? (= no ASTCNT update)
		IRP$L_STS(R3),QUEUE_INT

	BBCC	#ACB$V_QUOTA,IRP$B_RMOD(R3),- ; AST specified?
		QUEUE_INT0 		; NO  - Queue IRP and interrupt.
					; YES - 
	MOVL	ARG$_PCB(AP),R0		; Get the PCB address input.
	.PRESERVE ATOMICITY
	INCL	PCB$L_ASTCNT(R0)	;UPDATE AVAILABLE AST QUEUE ENTRIES
	.NOPRESERVE ATOMICITY

QUEUE_INT0:
	.ENABL	QUADWORD
	EVAX_STQ R31,IRP$PQ_IOSB(R3)	; Zap IOSB pointer for no IOSB write
	.DSABL	QUADWORD		;  in IOCIOPOST
;
; Make these I/O's complete synchronously by using per-CPU queue.
; Any changes below will have to be made in EXE_STD$FINISHIO as well.
;
QUEUE_INT: 
	find_cpu_data R1		; Get per-CPU database address
	INSQUE	(R3),@CPU$L_PSBL(R1)	; INSQUE into Q for this CPU.
	SOFTINT	#IPL$_IOPOST		; Signal I/O post interrupt

	FORKUNLOCK UCB$B_FLCK(R5),-
		PRESERVE=NO		;UNLOCK FORK THREADS/USE SAME IPL

	MOVZWL	#SS$_FDT_COMPL,R0	; Set FDT completion status.

99$:    RET                             ; Ret from FDT Completion Routine with:
                                        ;  1)$QIO status in FDT Context area,
                                        ;  2)IRP$PS_FDT_CONTEXT cleared,
                                        ;  3)SS$_FDT_COMPL in R0.
                                        ;             O R
                                        ; Ret if SS$_FDT_COMPL was inputted as
                                        ;  the final $QIO status.  This means
					;  ABORTIO was called more than once.
	.PAGE
	.SBTTL	Finish an I/O operation
;++
; EXE_STD$FINISHIO - Finish an I/O operation.
;
; This FDT Completion routine is called from upper level FDT routines and
; FDT support routines to finish an I/O operation. 
;
; EXE$FINISHIOC - OBSOLETE, May use $FINISHIOC or $FINISHIO_NOIOST macros.
;
; FUNCTION:
;  	Clear pointer to FDT Context structure.
;	The IRP is inserted in the I/O post processing queue. A software 
;	interrupt is generated to initiate I/O post processing. 
;	Return SS$_FDT_COMPL to EXE$QIO.
;
;	NOTE: Does not set the IRP$L_IOST1 and IRP$L_IOST2 fields.
;
; ENVIRONMENT:
;	THE FORKLOCKS ARE ACQUIRED TO INSURE THAT THE LOCK IS HELD WHILE
;	UPDATING OPERATIONS COUNT ON THE UCB. AND TO MAKE SURE THAT WE CAN
;	ALWAYS RELEASE THE FORKLOCK, IN CASE THE DRIVER HAD ACQUIRED IT.
;
; CALLING SEQUENCE:
;	#SS$_FDT_COMPL = EXE_STD$FINISHIO (IRP, UCB)
; INPUTS:
;	ARG$_IRP(AP) 		- I/O Request Packet	
;	ARG$_UCB(AP)		- Unit Control Block
; OUTPUTS:
;	The Final $QIO system service status is left as SS$_NORMAL in the FDT 
;	Context area.
; RETURN VALUE:
;	R0 = SS$_FDT_COMPL
;-
$OFFDEF	ARG,<-		; Define the AP offset of the input parameters.
	IRP,-		; Offset of IRP parameter.
	UCB-		; Offset of UCB parameter.
	>

	.ENABL	LSB

	UNIVERSAL_ENTRY EXE_STD$FINISHIO, -
		MASK=<M^<R3,R5>>

;EXE_STD$FINISHIO::			; Finish an I/O operation.
	MOVL	ARG$_IRP(AP),R3		; Get the IRP address input.
	MOVL	ARG$_UCB(AP),R5		; Get the UCB address input.

	CLRL	IRP$PS_FDT_CONTEXT(R3)	; Clear FDT context pointer in IRP...
					;  SS$_NORMAL is default final status.
	FORKLOCK UCB$B_FLCK(R5),-	;LOCK FORK THREADS/USE IPL OF LOCK
		PRESERVE=NO		; DON'T PRESERVE R0
	INCL	UCB$L_OPCNT(R5)		;INCREMENT OPERATIONS COMPLETED

; Make these I/O's complete synchronously by using per-CPU queue.
; Any changes below will have to be made in EXE_STD$ABORTIO as well.
;
	find_cpu_data R1		; Get per-CPU database address
	INSQUE	(R3),@CPU$L_PSBL(R1)	; INSQUE into Q for this CPU.
	SOFTINT	#IPL$_IOPOST		; Signal I/O post interrupt

	FORKUNLOCK UCB$B_FLCK(R5),-	;UNLOCK FORK THREADS/USE SAME IPL
		PRESERVE=NO		; DON'T PRESERVE R0

	MOVZWL	#SS$_FDT_COMPL,R0	; Set FDT completion status.

	RET				; Ret from FDT Completion Routine with:
					;  1)$QIO status in FDT Context area,
					;  2)IRP$PS_FDT_CONTEXT cleared,
					;  3)SS$_FDT_COMPL in R0.
	.DSABL	LSB


	.PAGE
	.SBTTL	QUEUE I/O REQUEST PACKET TO DRIVER
;+
; EXE_STD$QIODRVPKT - Queue the I/O Request Packet to the device driver.
;
; This FDT Completion routine is called from upper level FDT routines and
; FDT support routines to queue an I/O packet to the appropriate device driver.
;
; CALLING SEQUENCE:
;	#SS$_FDT_COMPL = EXE_STD$QIODRVPKT (IRP, UCB)
; INPUTS:
;	ARG$_IRP(AP) 		- I/O Request Packet	
;	ARG$_UCB(AP)		- Unit Control Block
; OUTPUTS:
;	The I/O request packet is queued by priority in the appropriate device
;	queue.  The default normal completion status is returned in the FDT 
;	Context structure as the final $QIO system service status.
; RETURN VALUE:
;	R0 = SS$_FDT_COMPL
;-
$OFFDEF	ARG,<-		; Define the AP offset of the input parameters.
	IRP,-		; Offset of IRP parameter.
	UCB-		; Offset of UCB parameter.
	>
 
	UNIVERSAL_ENTRY	EXE_STD$QIODRVPKT

;EXE_STD$QIODRVPKT::			; Queue the I/O Request Packet.
	MOVL	ARG$_IRP(AP),R1		; Get the IRP address input.

	CLRL	IRP$PS_FDT_CONTEXT(R1)	; Clear FDT context pointer in IRP...
					;  SS$_NORMAL is default final status.
        PUSHL   ARG$_UCB(AP)		; Push UCB address for input.
        PUSHL   R1                      ; Push IRP address for input.
        CALLS   #2,EXE_STD$INSIOQ       ; Insert the IRP on the device queue.
	MOVZWL	#SS$_FDT_COMPL,R0	; Set FDT completion status.

	RET				; Ret from EXE_STD$QIODRVPKT with:
					;  1)$QIO status in FDT Context area,
					;  2)IRP$PS_FDT_CONTEXT cleared,
					;  3)SS$_FDT_COMPL in R0.

	.PAGE
	.SBTTL	EXE$ALTQUEPKT - Call driver ALTSTART entry point

;
; EXE$ALTQUEPKT - activates a driver at its ALTSTART entry point
;
; Routine description:
;
;	Locates and calls a driver entry point supplied as an alternate
;	START I/O entry point. Does test for unit busy before the
;	call. Exits by returning to caller.
;
;	The routine expects to gain control at or below driver fork
;	level. The routine raises to driver fork IPL before the call,
;	and restores the previous IPL before returning to its caller.
;
; Inputs:
;
;	R3	- address of IRP
;	R5	- address of UCB
;
; Outputs:
;
;	Control returns to the requesting process.
;
;	R0,R1,R2,R3,R4 are destroyed.
;
;--
	UNIVERSAL_JSB	EXE$ALTQUEPKT,- 
			INPUT=<R3,R5>,-
			SCRATCH=<R0,R1,R2,R3,R4>
;EXE$ALTQUEPKT::			; Start I/O in driver.

        $COUNT_ENTRY    EXE$ALTQUEPKT	; Debug counting.
        CALL_ALTQUEPKT			; Push parms, call the STD routine.
        RSB                             ; Return to caller.

	.PAGE
	.SBTTL	EXE_STD$ALTQUEPKT - Call driver ALTSTART entry point

;++
; EXE_STD$ALTQUEPKT - activates a driver at its ALTSTART entry point
;
; Routine description:
;
;	Locates and calls a driver entry point supplied as an alternate
;	START I/O entry point. Does test for unit busy before the
;	call. Exits by returning to caller.
;
;	The routine expects to gain control at or below driver fork
;	level. The routine raises to driver fork IPL before the call,
;	and restores the previous IPL before returning to its caller.
;
; CALLING SEQUENCE:
;       EXE_STD$ALTQUEPKT (IRP, UCB)
;       NOTE: If calling sequence changes, the MACRO in SYSMAR must be changed.
; INPUTS:
;	ARG$_IRP(AP) - I/O Request Packet (R3)
;	ARG$_UCB(AP) - Unit Control Block (R5)
; OUTPUTS:
;	None
; RETURN VALUE:
;	None
;-
$OFFDEF ARG,<-
	IRP,-
	UCB -
	> 

	UNIVERSAL_ENTRY	EXE_STD$ALTQUEPKT, -
			MASK = <M^<R2,R3,R4,R5>>
;EXE_STD$ALTQUEPKT::			;INSERT IN I/O QUEUE

        $COUNT_ENTRY    EXE_STD$ALTQUEPKT	; Debug counting.

        MOVL    ARG$_UCB(AP),R5         ; Get the UCB from the AP. 
        MOVL    ARG$_IRP(AP),R3         ; Get the IRP from the AP. 

	FORKLOCK LOCK=UCB$B_FLCK(R5),-	; Lock the FORK spinlock
		SAVIPL=-(SP),-		; Save the current IPL
		PRESERVE=NO		; Don't preserve R0
;
; Check if device is busy - If so, place IRP on alternate startio wait queue.
; NOTE:  This wait queue should be a doubly linked list so that an INSQUE
; may be performed.  But since this would involve adding 2 longwords to the UCB
; and this is not a major release, this is not allowed.  So for now this
; will be a singly linked list.  This should be changed as soon as possible.
;
	.BRANCH_LIKELY
 	BBC	S^#UCB$V_ALTBSY,-	; If device is busy
		UCB$L_STS(R5),15$ 
	ASSUME	IRP$L_IOQFL EQ 0
	CLRL	(R3)   			;   Clear link to indicate end of queue.
	MOVAB	UCB$L_ALTIOWQ(R5),R0    ;   Search for the end of the
11$:	TSTL	(R0)			; 
	BEQL	12$			;   alternate IO wait queue.
	MOVL	(R0),R0			;   Follow the link.
	BRB	11$			;   Keep going until hit the end.
12$:	MOVL	R3,(R0)			;   Insert at end of queue.
	FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ;  Unlock the FORK spinlock
		NEWIPL=(SP)+,-		;   Restore previous IPL
		CONDITION=RESTORE,-	;   Conditionally release spinlock
		PRESERVE=NO		;   Don't preserve R0
	RET				;   return
;
; Device is not busy.  Invoke alternate startio routine.
;
15$: 	PUSHL	R5			; Preserve R5
 	ASSUME	SMP$V_ENABLED  EQ  0
	BLBC	G^SMP$GL_FLAGS,20$	; Br if SMP is not enabled
	find_cpu_data	R0		; Get address of CPU-specific database
	MOVL	CPU$L_PHY_CPUID(R0),R0	; Get our CPU ID
	BBC	R0,UCB$L_AFFINITY(R5),60$ ; Br if can't run on this CPU
20$:	MOVL	UCB$L_DDT(R5),R0	; Get address of unit's DDT.
        PUSHL   R5                      ; UCB (push args onto stack in reverse order)
        PUSHL   R3                      ; IRP
        CALLS   #2, @DDT$PS_ALTSTART_2(R0); Call alternate start I/O routine.

40$:	POPL	R5			; Restore R5
	FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Unlock the FORK spinlock
		NEWIPL=(SP)+,-		; Restore previous IPL
		CONDITION=RESTORE,-	; Conditionally release spinlock
		PRESERVE=NO		; Don't preserve R0
	RET				; Return to caller.

60$:	;
	; One last check to see if we can run on this CPU.  If UCB$L_AFFINITY
	; is equal to 0, then this implies that this device is tied to the
	; primary CPU.  So if this CPU is the primary, then the I/O can be
	; started now.  Otherwise, a switch to the right CPU must be made.
	;
	TSTL	UCB$L_AFFINITY(R5)	; Check for logical primary
	BNEQ	65$			; Br if not primary
    	CMPL	R0,G^SMP$GL_PRIMID	; Now is this CPU the primary?
	BEQL	20$			; Br if yes, all set to start I/O
	;
	; We will now fork onto the correct CPU to start this I/O.
	; We will have to fork on the CDRP portion of the IRP.  First,
	; set the busy bit to indicate that the device is busy with an I/O.
	; This is necessary since we are about to relinquish the fork-
	; lock and we need a way to guarantee that no other I/O will be
	; started before this I/O is started on the correct CPU.  The correct
	; ordering of I/O must be maintained.  
	;
65$:	BISL	#UCB$M_ALTBSY,UCB$L_STS(R5)	; Set busy bit.

	PUSHL	R3			; Fork block address (IRP or TWP)
	PUSHL	R5			; Address of UCB with affinity mask
	PUSHAB	68$			; Address of fork context routine PD
	CALLS	#3,G^SMP$CPU_SWITCH	; Create fork thread on correct CPU
	BRB	40$


;	FORK ROUTINE:

68$:	.CALL_ENTRY	INPUT = <R2,R3,R4,R5>
	;
	; This code executes as fork thread on correct CPU
	;
	; R3 = IRP address
	; R5 = UCB address
	;
	FORKLOCK LOCK=UCB$B_FLCK(R5),-	; Lock the FORK spinlock
		SAVIPL=-(SP),-		; Save the current IPL
		PRESERVE=NO		; Don't preserve R0
 	PUSHL	R5			; Preserve R5.
70$:	find_cpu_data	R0		; Get address of CPU-specific database
	MOVL	CPU$L_PHY_CPUID(R0),R0	; Get our CPU ID
	BBS	R0,UCB$L_AFFINITY(R5),75$ ; Br if can run on this CPU
	TSTL	UCB$L_AFFINITY(R5)	; Check for logical primary
	.DSABL	FLAGGING		; %AMAC-I-BRANCHBET, branching between routines
	BNEQ	65$			; Br if not primary
	.ENABL	FLAGGING
    	CMPL	R0,G^SMP$GL_PRIMID	; Now is this CPU the primary?
	.DSABL	FLAGGING		; %AMAC-I-BRANCHBET, branching between routines
	BNEQ	65$			; Br if not primary, go switch to it.
	.ENABL	FLAGGING
75$:	MOVL	UCB$L_DDT(R5),R0	; Get address of unit's DDT.
        PUSHL   R5                      ; UCB (push args onto stack in reverse order)
        PUSHL   R3                      ; IRP
        CALLS   #2, @DDT$PS_ALTSTART_2(R0); Call alternate start I/O routine.
;
; Check for any IRPs that may have been waiting while the busy bit was
; set.
;
 	MOVL	(SP),R5			; Retrieve R5 from the stack.
   	MOVL	UCB$L_ALTIOWQ(R5),R3	; Any IRPs in waiting
	BEQL	80$			; EQL, none waiting - done.
	ASSUME	IRP$L_IOQFL EQ 0
    	MOVL	(R3),UCB$L_ALTIOWQ(R5)	; Remove entry	
	BRB	70$			; and process it.
;
80$:	BICL	#UCB$M_ALTBSY,UCB$L_STS(R5)	; Clear busy bit.
	.DSABL	FLAGGING		; %AMAC-I-BRANCHBET, branching between routines
	BRW	40$	
	.ENABL	FLAGGING

	.PAGE
	.SBTTL	QUEUE I/O PACKET TO ACP OR XQP
;+
; EXE_STD$QIOACPPKT - Queue the I/O Request Packet to the ACP or XQP.
;
; This FDT Completion routine is called from upper level FDT routines and
; FDT support routines to queue an I/O packet to the appropriate ACP or XQP.
;
; CALLING SEQUENCE:
;	#SS$_FDT_COMPL = EXE_STD$QIOACPPKT (IRP, PCB, UCB)
; INPUTS:
;	ARG$_IRP(AP) 		- I/O Request Packet	
;	ARG$_PCB(AP)		- Process Control Block
;	ARG$_UCB(AP)		- Unit Control Block
; OUTPUTS:
;	The I/O request packet is queued by priority in the appropriate device
;	queue.  A normal completion status is returnedin the FDT Context 
;	structure as ther final $QIO system service status.
; RETURN VALUE:
;	R0 = SS$_FDT_COMPL
;-
$OFFDEF	ARG,<-		; Define the AP offset of the input parameters.
	IRP,-		; Offset of IRP parameter.
	PCB,-		; Offset of PCB parameter.
	UCB -		; Offset of UCB parameter.
	>
 
	UNIVERSAL_ENTRY	EXE_STD$QIOACPPKT, -
		MASK= <M^<R2,R3,R4,R5>>

;EXE_STD$QIOACPPKT::			; Queue the I/O Request Packet.
	MOVL	ARG$_IRP(AP),R3		; Get the IRP address input.
	MOVL	ARG$_PCB(AP),R4		; Get the PCB address input.
	MOVL	ARG$_UCB(AP),R5		; Get the UCB address input.

	CLRL	IRP$PS_FDT_CONTEXT(R3)	; Clear FDT context pointer in IRP...
					;  SS$_NORMAL is default final status.

	MOVL	UCB$L_VCB(R5),R2	;GET ADDRESS OF VCB
	MOVL	VCB$L_AQB(R2),R2	;GET ADDRESS OF ACP AQB
	TSTL	AQB$L_ACPPID(R2)	;GET ADDRESS OF AQB
	BNEQ	40$			;NEQ - IT'S FOR ACP, ELSE XQP

	SETIPL	#IPL$_ASTDEL,-		;ALLOW PAGEFAULTS
		ENVIRON=UNIPROCESSOR

	ASSUME	IRP$L_FQFL EQ IRP$K_CDRP
	MOVAB	IRP$L_FQFL(R3), R5	;USE CDRP PART OF IRP AS ACB

	; R0 = EXE_STD$QXQPPKT (PCB(R4), ACB(R5))
	CALL_QXQPPKT			;QUEUE I/O PACKET TO XQP
					;  (scratch R1,R2,R3)
	BRW	50$			; Set IPL, R0 and return.

40$:	$INSQTI	(R3),AQB$Q_ACPIQ(R2),R0 ;INSERT I/O PACKET AT END OF ACP QUEUE
					;NOTE: R0 IS DESTROYED BY $INSQTI MACRO
	BNEQ	50$			;IF NEQ NOT FIRST ENTRY IN QUEUE
	MOVL	AQB$L_ACPPID(R2),R1	;GET ACP PROCESS ID
	LOCK	LOCKNAME=SCHED,-	;LOCK SCHED DATABASE
		PRESERVE=NO		; Don't preserve R0
	JSB	G^SCH$WAKE		;WAKE UP ACP PROCESS (scratch R2,R4)
	UNLOCK	LOCKNAME=SCHED		;UNLOCK SCHED DATABASE
	.BRANCH_LIKELY
	BLBS	R0,50$			;BR IF SUCCESS FROM SCH$WAKE
	BUG_CHECK NONEXSTACP		;OTHERWISE NONEXISTENT ACP PROCESS

50$:	
	MOVZWL	#SS$_FDT_COMPL,R0	; Set FDT completion status.

	RET				; Ret from FDT Completion Routine with:
					;  1)$QIO status in FDT Context area,
					;  2)IRP$PS_FDT_CONTEXT cleared,
					;  3)SS$_FDT_COMPL in R0.
	.PAGE
	.SBTTL	EXE$QXQPPKT - QUEUE I/O PACKET TO XQP
;+
; EXE$QXQPPKT - INSERT I/O PACKET IN XQP QUEUE
;
; THIS ROUTINE IS CALLED TO INSERT AN I/O PACKET IN THE XQP QUEUE
; AND START THE THREAD OF EXECUTION IF IT IS THE ONLY REQUEST.
;
; CALLING SEQUENCE:
; 	BSB/JSB EXE$QXQPPKT - THIS IS EITHER CALLED FROM QIO OR
;	AS A SPECIAL KERNEL AST INVOKED BY IOPOST.
; INPUTS:
;	R4 = CURRENT PROCESS PCB ADDRESS.
;	R5 = ADDRESS OF TEMP ACB PART OF IRP.
; OUTPUTS:
;	R0 = status from SCH$QAST.
;	R1,R2,R3 may be destroyed.
;	
;	IF SUCCESS:
;	A KERNEL AST IS QUEUED TO THE DISPATCH ROUTINE OF THE XQP
;	IF NO PACKETS WERE ALREADY ON THE REQUEST QUEUE OF THE XQP.
;
;	THIS ROUTINE MUST BE CALLED AT IPL ASTDEL SO THAT THE
;	IRP CANNOT BE LOST (BECAUSE OF PROCESS DELETION) UNTIL IT
;	IS PLACED ON THE XQP REQUEST QUEUE.
;-
	UNIVERSAL_JSB	EXE$QXQPPKT, INPUT=<R4,R5>,-
				     OUTPUT=<R0>, -
				     SCRATCH=<R1,R2,R3>
;EXE$QXQPPKT::

        $COUNT_ENTRY    EXE$QXQPPKT	; Debug counting.
        CALL_QXQPPKT                    ; Push parms, call the STD routine.
        RSB                             ; Return to caller.

	.PAGE
	.SBTTL	EXE_STD$QXQPPKT - QUEUE I/O PACKET TO XQP
;+
; EXE_STD$QXQPPKT - INSERT I/O PACKET IN XQP QUEUE
;
; THIS ROUTINE IS CALLED TO INSERT AN  I/O PACKET IN  THE XQP QUEUE
; AND START THE THREAD OF EXECUTION IF IT IS THE ONLY REQUEST. THIS
; IS EITHER CALLED FROM  $QIO OR AS A SPECIAL KERNEL AST INVOKED BY
; IOPOST.
;
; ENVIRONMENT:
;	IF SUCCESS:
;	A KERNEL AST IS QUEUED TO THE DISPATCH ROUTINE OF THE XQP
;	IF NO PACKETS WERE ALREADY ON THE REQUEST QUEUE OF THE XQP.
;
;	THIS ROUTINE MUST BE CALLED AT IPL ASTDEL SO THAT THE
;	IRP CANNOT BE LOST (BECAUSE OF PROCESS DELETION) UNTIL IT
;	IS PLACED ON THE XQP REQUEST QUEUE.
;
; CALLING SEQUENCE:
;       Status = EXE_STD$QXQPPKT (PCB, ACB)
;       NOTE: If calling sequence changes, the MACRO in SYSMAR must be changed.
; INPUTS:
;	ARG$_PCB(AP) - Process Control Block (R4)
;	ARG$_ACB(AP) - AST Control Block (R5)
; OUTPUTS:
;	None
; RETURN VALUE:
;	R0 = status from SCH$QAST.
;-
$OFFDEF ARG,<-
	PCB,-			; Offset from the AP of the PCB.
	ACB -			; Offset from the AP of the ACB.
	> 

	UNIVERSAL_ENTRY	EXE_STD$QXQPPKT, -
			MASK = <M^<R2,R3,R4,R5>>
;EXE_STD$QXQPPKT::			;INSERT IN I/O QUEUE

        $COUNT_ENTRY    EXE_STD$QXQPPKT	; Debug counting.

        MOVL    ARG$_ACB(AP),R5         ; Get the ACB from the AP. 
        MOVL    ARG$_PCB(AP),R4         ; Get the PCB from the AP. 

	; Verify that the negative CDRP offset CDRP$L_IOQFL reaches exactly
	; back to the start of the IRP.
	;
	ASSUME	CDRP$L_IOQFL+IRP$K_CDRP  EQ  IRP$L_IOQFL
	ASSUME	IRP$L_IOQFL  EQ  0

        EXTZV   #IRP$V_FCODE,#IRP$S_FCODE,-
                CDRP$L_FUNC(R5), R0     ; GET FUNCTION CODE WITHOUT QUALIFIERS
        CMPL    R0, #IO$_READPBLK       ; IF READ PHYSICAL
        BEQL    10$
        CMPL    R0, #IO$_WRITEPBLK      ; OR WRITE DO SPECIAL PROCESSING
        BEQL    10$
        CMPL    R0, #IO$_DSE            ; (A WRITE IN DISGUISE)
        BNEQ    40$
10$:
        MOVL    CDRP$L_UCB(R5), R1      ; R1 => UCB.
        FORKLOCK LOCK=UCB$B_FLCK(R1),-  ; lock fork threads/use forklock IPL
                SAVIPL=R2,-             ; Save IPL in space prev allocated
                PRESERVE=NO
        MOVL    UCB$L_VCB(R1), R0
        TSTL    VCB$L_RVN(R0)           ; Volume Set?
        BNEQ    60$                     ; NEQ implies Yes
        .PRESERVE ATOMICITY
        INCL    VCB$L_TRANS(R0)         ; Increase Transaction Count
        .NOPRESERVE ATOMICITY
30$:
        FORKUNLOCK LOCK=UCB$B_FLCK(R1),-; release UCB fork lock
                NEWIPL=R2, -
                CONDITION=RESTORE, -
                PRESERVE=NO
40$:
        MOVAB   CDRP$L_IOQFL(R5), -     ;ADDRESS OF IRP
                ACB$L_ASTPRM(R5)        ;IS AST PARAMETER.
        MOVB    #PSL$C_KERNEL!ACB$M_NODELETE,- ;KERNEL MODE, DON'T DELETE IRP
                ACB$B_RMOD(R5)
        MOVL    PCB$L_PID(R4), ACB$L_PID(R5) ;COPY PID.
	; get the AQB to find the XQP dispatcher address
        MOVL    CDRP$L_UCB(R5), R1      ; R1 => UCB.
	MOVL	UCB$L_VCB(R1),R2	;GET ADDRESS OF VCB
	MOVL	VCB$L_AQB(R2),R2	;GET ADDRESS OF ACP AQB
        MOVL    AQB$L_ASTADR(R2), ACB$L_AST(R5) ;XQP DISPATCHER ADDRESS.
        MOVL    #PRI$_RESAVL, R2        ;SAME AS AFTER WAITING FOR A LOCK.
        JSB     G^SCH$QAST              ;QUEUE THE AST (scratch R1,R3)
        RET                             ;AND RETURN TO CALLER.

;
; Out of line code for Volume Set - increment RVT$L_TRANS
;
60$:                                    ; This is a volume set.
        MOVL    VCB$L_RVT(R0), R0       ; RVT Address to R0
        .PRESERVE ATOMICITY
        INCL    RVT$L_TRANS(R0)         ; Increase Transaction Count
        .NOPRESERVE ATOMICITY
        BRB     30$                     ; Rejoin common code.

        .PAGE
        .SBTTL  INSERT I/O PACKET IN UNIT QUEUE
;+
; EXE$INSIOQ - INSERT I/O PACKET IN UNIT QUEUE
;
; THIS ROUTINE IS CALLED TO INSERT AN I/O PACKET IN A UNIT QUEUE AND CALL
; THE APPROPRIATE I/O DRIVER IF THE UNIT IS NOT BUSY.
;
; INPUTS:
;       R3 = ADDRESS OF I/O REQUEST PACKET.
;       R5 = UCB ADDRESS OF DEVICE UNIT.
; OUTPUTS:
;       R0,R1,R2,R4 may be destroyed.
;
; ENVIRONMENT:
;       THE FORK SPINLOCK IS ACQUIRED, AND THEN UNCONDITIONALLY RELEASED.
;-
        UNIVERSAL_JSB   EXE$INSIOQ, INPUT=<R3,R5>, -
                                    OUTPUT=<R5>,-       ; prevent auto preserve
                                    SCRATCH=<R0,R1,R2,R4>
;EXE$INSIOQ::                           ;INSERT IN I/O QUEUE

        $COUNT_ENTRY    EXE$INSIOQ	; Debug counting.
        CALL_INSIOQ                     ; Push parms, call the STD routine.
        RSB                             ; Return to caller.
 
	.PAGE
	.SBTTL	INSERT I/O PACKET IN UNIT QUEUE
;+
; EXE_STD$INSIOQ - INSERT I/O PACKET IN UNIT QUEUE
;
; THIS ROUTINE IS CALLED TO INSERT AN I/O PACKET IN A UNIT QUEUE AND CALL
; THE APPROPRIATE I/O DRIVER IF THE UNIT IS NOT BUSY.
;
; ENVIRONMENT:
;	THE FORK SPINLOCK IS ACQUIRED, AND THEN UNCONDITIONALLY RELEASED.
;
; CALLING SEQUENCE:
;       EXE_STD$INSIOQ (IRP, UCB)
;       NOTE: If calling sequence changes, the MACRO in SYSMAR must be changed.
; INPUTS:
;	ARG$_IRP(AP) - I/O Request Packet 
;	ARG$_UCB(AP) - Unit Control Block (R5)
; OUTPUTS:
;	None
; RETURN VALUE:
;	None
;-
$OFFDEF ARG,<-
	IRP,-			; Offset from the AP of the IRP.
	UCB -			; Offset from the AP of the UCB.
	> 

	UNIVERSAL_ENTRY	EXE_STD$INSIOQ, -
			MASK = <M^<R2,R5>>
;EXE_STD$INSIOQ::			;INSERT IN I/O QUEUE

        $COUNT_ENTRY    EXE_STD$INSIOQ	; Debug counting.

        MOVL    ARG$_UCB(AP),R5         ; Get the UCB from the AP. 
	MOVZBL	UCB$B_FLCK(R5),R2	; Get flck index
	FORKLOCK LOCK=R2,-		; Lock the FORK spinlock
		SAVIPL=-(SP),-		; Save the current IPL
		PRESERVE=NO		; Don't preserve R0

	.PRESERVE ATOMICITY
	INCL	UCB$L_QLEN(R5)		; Bump device queue length
	.NOPRESERVE ATOMICITY

	BBSS	#UCB$V_BSY,UCB$L_STS(R5),20$ ;IF SET, THEN DEVICE IS BUSY
					
        MOVL    ARG$_IRP(AP),R1            ; Get the IRP from the AP. 
	BISL	#IRP$M_LOCK_RELEASEABLE, - ; Driver can release forklock
		IRP$L_STS(R1)	
					   ; Push input parameters...
        PUSHL   R5                         ;  Unit Control Block,
        PUSHL   R1 			   ;  I/O Request Packet.
        CALLS   #2,G^IOC_STD$INITIATE_NEW_IO  ; Make the call.

	VERIFY_LOCK_OWNERSHIP -	   	   ; See whether driver returned
		LOCKINDEX=R2		   ; with SCS/IOLOCK8 held

	BLBS	R0,15$ 			   ; If this CPU owns lock, release
	SETIPL	IPL = (SP)+,- 		   ; Else just setipl
		ENVIRON	= UNIPROCESSOR	   ; Lie to convince SETIPL to work     
	RET 

15$:	FORKUNLOCK LOCK=R2,-   		   ; Unlock the FORK spinlock
		NEWIPL=(SP)+,-		   ; Restore previous IPL
		PRESERVE=NO		   ; Don't preserve R0
	RET				   ;
20$:                                       ; Push input parameters...
        PUSHL   ARG$_IRP(AP)               ;  I/O Request Packet,
	PUSHAB	UCB$L_IOQFL(R5)		   ;  I/O Queue Listhead.

	.if	df,disc$ucbs
	BBC	DEV$V_FOD,UCB$L_DEVCHAR(R5),23$
	.iftf
; We ARE inserting an IRP to a UCB queue! Oughta be trustable!!!
	MOVL	UCB$L_DDT(R5),R0	   ; Find the pending-io cell
	MOVL	DDT$PS_PENDING_IO(R0),R0
	CALLS	#2,(R0)			   ; Make the call to insert the IRP
	.ift
	BRB	24$
23$:
        CALLS   #2,G^EXE_STD$INSERT_IRP    ; Make the call.
24$:
	.endc
	FORKUNLOCK LOCK=R2,-   		   ; Unlock the FORK spinlock
		NEWIPL=(SP)+,-		   ; Restore previous IPL
		PRESERVE=NO		   ; Don't preserve R0
	RET				   ; Return to caller.


	.PAGE
	.SBTTL	INSERT I/O PACKET AND CONDITIONALLY RELEASE SPINLOCK
;+
; EXE$INSIOQC - INSERT I/O PACKET AND CONDITIONALLY RELEASE SPINLOCK 
;
; THIS ROUTINE IS CALLED TO INSERT AN I/O PACKET IN A UNIT QUEUE AND CALL
; THE APPROPRIATE I/O DRIVER IF THE UNIT IS NOT BUSY.
;
; INPUTS:
;	R3 = ADDRESS OF I/O REQUEST PACKET.
;	R5 = UCB ADDRESS OF DEVICE UNIT.
; OUTPUTS:
;	R0,R1,R2,R4 may be destroyed.
;
; ENVIRONMENT:
;	THE FORK SPINLOCK IS ACQUIRED, AND THEN CONDITIONALLY RELEASED.
;-

	UNIVERSAL_JSB	EXE$INSIOQC, INPUT=<R3,R5>, -
				     OUTPUT=<R5>,-	; prevent auto preserve
				     SCRATCH=<R0,R1,R2,R4>
;EXE$INSIOQC::				;INSERT IN I/O QUEUE

        $COUNT_ENTRY    EXE$INSIOQC	; Debug counting.
        CALL_INSIOQC                    ; Push parms, call the STD routine.
        RSB                             ; Return to caller.
 
	.PAGE
	.SBTTL	INSERT I/O PACKET AND CONDITIONALLY RELEASE SPINLOCK
;+
; EXE_STD$INSIOQC - INSERT I/O PACKET AND CONDITIONALLY RELEASE SPINLOCK 
;
; THIS ROUTINE IS CALLED TO INSERT AN I/O PACKET IN A UNIT QUEUE AND CALL
; THE APPROPRIATE I/O DRIVER IF THE UNIT IS NOT BUSY.
;
; ENVIRONMENT:
;	THE FORK SPINLOCK IS ACQUIRED, AND THEN UNCONDITIONALLY RELEASED.
;
; CALLING SEQUENCE:
;       EXE_STD$INSIOQC (IRP, UCB) 
;       NOTE: If calling sequence changes, the MACRO in SYSMAR must be changed.
; INPUTS:
;	ARG$_IRP(AP) - I/O Request Packet (R3)
;	ARG$_UCB(AP) - Unit Control Block (R5)
; OUTPUTS:
;	None
; RETURN VALUE:
;	None
;-
$OFFDEF ARG,<-
	IRP,-			; Offset from the AP of the IRP.
	UCB -			; Offset from the AP of the UCB.
	> 

	UNIVERSAL_ENTRY	EXE_STD$INSIOQC, -
			MASK = <M^<R5>>
;EXE_STD$INSIOQC::			;INSERT IN I/O QUEUE

        $COUNT_ENTRY    EXE_STD$INSIOQC	; Debug counting.

        MOVL    ARG$_UCB(AP),R5         ; Get the UCB from the AP. 

	FORKLOCK LOCK=UCB$B_FLCK(R5),-  ; Lock the FORK spinlock
		SAVIPL=-(SP),-		; Save the current IPL
		PRESERVE=NO		; Don't preserve R0

	.PRESERVE ATOMICITY
	INCL	UCB$L_QLEN(R5)		; Bump device queue length
	.NOPRESERVE ATOMICITY

	BBSS	#UCB$V_BSY,UCB$L_STS(R5),20$ ;IF SET, THEN DEVICE IS BUSY
					; Push input parameters...
        MOVL    ARG$_IRP(AP),R1         ; Get the IRP from the AP. 
	BICL	#IRP$M_LOCK_RELEASEABLE, - ; Driver can not release forklock
		IRP$L_STS(R1)	
					   ; Push input parameters...
        PUSHL   R5                         ;  Unit Control Block,
        PUSHL   R1 			   ;  I/O Request Packet.
        CALLS   #2,G^IOC_STD$INITIATE_NEW_IO  ; Make the call.

	FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Unlock the FORK spinlock
		NEWIPL=(SP)+,-		; Restore previous IPL
		CONDITION=RESTORE,-	; Conditionally release lock
		PRESERVE=NO		; Don't preserve R0
	RET				; Return to caller.

20$:                                    ; Push input parameters...
        PUSHL   ARG$_IRP(AP)            ;  I/O Request Packet,
	PUSHAB	UCB$L_IOQFL(R5)		;  I/O Queue Listhead.
	.if	df,disc$ucbs
; Minimally use UCB if not a file oriented device
; If the conditional is NOT defined, just trust the UCBs. We are
; after all stashing things into the UCB I/O queue!
	BBC	DEV$V_FOD,UCB$L_DEVCHAR(R5),23$
	.iftf
; R0 is free here since the forkunlock following will trash it.
; We get the queue routine address (DDTAB makes it EXE_STD$INSERT_IRP
; by default) from the UCB via the DDT, making a clean intercept
; possible.
	MOVL	UCB$L_DDT(R5),R0	; Find the pending-io cell
	MOVL	DDT$PS_PENDING_IO(R0),R0
	CALLS	#2,(R0)			; Make the call to insert the IRP
	.ift
	BRB	24$
23$:
        CALLS   #2,G^EXE_STD$INSERT_IRP ; Make the call.
24$:
	.endc
	FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Unlock the FORK spinlock
		NEWIPL=(SP)+,-		; Restore previous IPL
		CONDITION=RESTORE,-	; Conditionally release lock
		PRESERVE=NO		; Don't preserve R0
	RET				; Return to caller.


	.PAGE
	.SBTTL	INSERT I/O PACKET IN QUEUE BY PRIORITY
;+
; EXE$INSERT_IRP - INSERT I/O PACKET IN QUEUE BY PRIORITY
;
; THIS ROUTINE IS CALLED TO INSERT AN I/O PACKET IN A SPECIFIED QUEUE BY
; PRIORITY.
;
; INPUTS:
;	R2 = ADDRESS OF QUEUE LISTHEAD.
;	R3 = ADDRESS OF I/O PACKET.
;
;	CURRENT IPL MUST BE THE FORK LEVEL OF THE RESPECTIVE DRIVER PROCESS
;	OR HIGHER.
; OUTPUTS:
;	THE I/O PACKET IS INSERTED IN THE SPECIFIED QUEUE BY PRIORITY.
;
;	R0 = 0 IF THIS ENTRY WAS FIRST ENTRY IN THE QUEUE.
;	   = 1 IF OTHER ENTRIES WERE ALREADY IN THE QUEUE.
;
;	R1 IS DESTROYED.
;	R2 AND R3 ARE PRESERVED ACROSS THE CALL.
;-
 
	UNIVERSAL_JSB	EXE$INSERT_IRP,-
			 INPUT=<R2,R3>,-
			 OUTPUT=<R0>,-
			 SCRATCH=<R1>

;EXE$INSERT_IRP::			;INSERT I/O PACKET IN QUEUE BY PRIORITY

        $COUNT_ENTRY    EXE$INSERT_IRP	; Debug counting.
        CALL_INSERT_IRP                 ; Push parms, call the STD routine.
        RSB                             ; Return to caller.
 
	.PAGE
	.SBTTL	INSERT I/O PACKET IN QUEUE BY PRIORITY
;+
; EXE_STD$INSERT_IRP - INSERT I/O PACKET IN QUEUE BY PRIORITY
;
; THIS ROUTINE IS CALLED TO INSERT AN I/O PACKET IN A SPECIFIED QUEUE BY
; PRIORITY.
;
; ENVIRONMENT:
;	CURRENT IPL MUST BE THE FORK LEVEL OF THE RESPECTIVE DRIVER PROCESS
;	OR HIGHER.
;	THE I/O PACKET IS INSERTED IN THE SPECIFIED QUEUE BY PRIORITY.
;
; CALLING SEQUENCE:
;       Status = EXE_STD$INSERT_IRP (QUEUE, IRP)
;       NOTE: If calling sequence changes, the MACRO in SYSMAR must be changed.
; INPUTS:
;	ARG$_QUEUE(AP) - Queue List Head (R0) 
;	ARG$_IRP(AP)   - I/O Request Packet (R3)
; OUTPUTS:
;	None
; RETURN VALUE:
;	R0 = 0 IF THIS ENTRY WAS FIRST ENTRY IN THE QUEUE
;	   = 1 IF OTHER ENTRIES WERE ALREADY IN THE QUEUE
;-
$OFFDEF ARG,<-
	QUEUE, -		; Offset from the AP of the queue list head. 
	IRP -			; Offset from the AP of the IRP.
	> 

	UNIVERSAL_ENTRY	EXE_STD$INSERT_IRP, -
			MASK = <M^<R3>>
;EXE_STD$INSERT_IRP::			;INSERT IN I/O QUEUE

        $COUNT_ENTRY EXE_STD$INSERT_IRP	; Debug counting.

        MOVL    ARG$_QUEUE(AP),R0       ; Get the queue listhead from the AP. 
        MOVL    ARG$_IRP(AP),R3         ; Get the IRP from the AP. 

	MOVL	R0,R1			;COPY LISTHEAD ADDRESS
10$:	MOVL	IRP$L_IOQBL(R1),R1	;GET ADDRESS OF NEXT ENTRY
	CMPL	R0,R1			;END OF QUEUE?
	BEQL	20$			;IF EQL YES
	CMPB	IRP$B_PRI(R3),IRP$B_PRI(R1) ;NEW ENTRY PRIORITY GREATER?
	BLSSU	10$			;IF LSS YES
20$:	INSQUE	IRP$L_IOQFL(R3),IRP$L_IOQFL(R1) ;INSERT PACKET IN I/O QUEUE
	BNEQ	30$
	CLRL	R0			;QUEUE WAS EMPTY, RETURN R0 = 0
	RET				; Return to caller.

30$:	MOVL	#1,R0			;QUEUE WAS NOT EMPTY, RETURN R0 = 1
	RET				; Return to caller.
 
	.END
