	.TITLE	LICENSES
	.IDENT	/01-000/
;++
;
;  Facility:	LICENSES
;
;  Functional Description:
;
;	This program circumvents DEC's LICENSE stuff by "replacing" the
;	the SYS$LOOKUP_LICENSE and SYS$GRANT_LICENSE system services.
;
;	At this point, it doesn't look like SYS$RELEASE_LICENSE has to be
;	changed.
;
;
;--

	.LIBRARY	/SYS$LIBRARY:LIB.MLB/
	.LINK		"SYS$SYSTEM:SYS.STB"/selective_search
	.LINK		"SYS$SYSTEM:SYSDEF.STB"/selective_search

	.DSABL	GLOBAL				; Declare external references
;
;  External routines
;
	.EXTRN	EXE$ALOPHYCNTG			; Allocate contiguous pages
	.EXTRN	MMG$SVAPTECHK			; Get PTE address
	.EXTRN	MMG$PTEREF			; Get PTE address for P1 addr
	.EXTRN	SMP$INVALID			; Invalidate an address
	.EXTRN	LIB$PUT_OUTPUT			; Print string to SYS$OUTPUT
;
;  Global variables used here:
;
	.EXTRN	SYS$GRANT_LICENSE		; Grant license system service
	.EXTRN	SYS$LOOKUP_LICENSE		; Lookup license system service
	.EXTRN	CTL$GL_PCB			; Process Control Block pointer

	$IPLDEF
	$OPDEF
	$PCBDEF
	$PFNDEF
	$PRDEF
	$PRTDEF
	$PTEDEF
	$SSDEF					; System service status symbols
	$UCBDEF					; Define UCB symbols

	.IF DEFINED UCB$L_DLCK			;* If DLCK exists, it's VMS V5!
	VMS_V5 = 1				;* Set the VMS_V5 flag
	.ENDC					;*

.IF NOT_DEFINED VMS_V5
.PRINT	0	;WARNING-- Program will not work below VMS V5.0!!!
.ENDC
	.PSECT	_LICENSES_DATA,NOEXE,WRT,LONG,SHR

ENABLED_MESSAGE:	.ASCID	/License trapping enabled/
DISABLED_MESSAGE:	.ASCID	/License trapping disabled/

START:
NEW_LOOKUP_LICENSE:
	TSTL	ENABLED				; Is trapping enabled?
	BEQL	ORIG_LOOKUP			; No - branch to original code
	MOVL	#1,R0				; Set success status
	RET					; Return to caller
LL_CODE_LENGTH = .-NEW_LOOKUP_LICENSE
NEW_GRANT_LICENSE_OFFSET = .-START
	TSTL	ENABLED				; Is trapping enabled?
	BEQL	ORIG_GRANT			; No - branch to original code
	MOVL	#1,R0				; Set success status
	RET					; Return to caller

VERIFY_OFFSET=.-START
	.ASCII	/FUBR/				; Identifier so we know it's us

ENABLED_OFFSET=.-START
ENABLED:	.LONG	1			; Start out enabled
OLD_LOOKUP=.-START
ORIG_LOOKUP:
	.BLKB	6				; Original LOOKUP_LICENSE code
	.ALIGN	LONG
OLD_GRANT=.-START
ORIG_GRANT:
	.BLKB	6				; Original GRANT_LICENSE code
CODE_LENGTH = .-START				; End of new code

	ASSUME	CODE_LENGTH LE 512

	.ALIGN	LONG

NEW_GRANT_CODE:
	JMP	@#0				; 
NEW_GRANT_LENGTH = .-NEW_GRANT_CODE

	.PSECT	_LICENSES_CODE,EXE,NOWRT,LONG,PIC,SHR
	.ENTRY	LICENSES,^M<R2,R3,R4,R5,R6,R7,R8,R9,R10,R11>

	MOVAL	G^SYS$LOOKUP_LICENSE,R9		; Get address of system service
	MOVL	4(R9),R8			; Get offset to real code
	CMPC3	#LL_CODE_LENGTH,NEW_LOOKUP_LICENSE,(R8)
	BNEQ	40$				; Branch if not changed
	$CMKRNL_S -				; Just toggle the ENABLED flag
		ROUTIN=TOGGLE_ENABLE		; ...
	TSTL	R0				; Was it disabled?
	BNEQ	10$				; No - enabled or ABORT
	PUSHAQ	DISABLED_MESSAGE		; Push disabled message address
	BRB	20$				; ... and go print it
 10$:	BLBC	R0,30$				; Branch if abort
	PUSHAQ	ENABLED_MESSAGE			; Push enabled message address
 20$:	CALLS	#1,G^LIB$PUT_OUTPUT		; ... and print it
 30$:	RET					; ... And return to caller

 40$:	$CMKRNL_S -
		ROUTIN=50$
	BLBC	R0,45$
	PUSHAQ	ENABLED_MESSAGE			; Push enabled message address
	CALLS	#1,G^LIB$PUT_OUTPUT		; ... and print it
 45$:	RET

 50$:	.WORD	^M<R2,R3,R4,R5,R6,R7,R8,R9,R10,R11>
	MOVAL	G^SYS$LOOKUP_LICENSE,R9		; Point to system service
	MOVAL	G^SYS$GRANT_LICENSE,R8		; Point to system service

	MOVC3	#6,2(R9),ORIG_LOOKUP		; Save original code for both
	MOVC3	#6,2(R8),ORIG_GRANT		; ... services

	MOVL	#PRT$C_URKW,R6			; New protection code for pages
	MOVAL	2(R9),R2			; Get address of beginning VA
	BSBW	CHANGE_PROT			; Change the protection code
	BLBC	R0,60$				; Exit if error
	MOVAL	7(R9),R2			; And address of ending VA
	BSBW	CHANGE_PROT			; Change the protection code
	BLBC	R0,60$				; Exit if error

	MOVL	#PRT$C_URKW,R6			; New protection code for pages
	MOVAL	2(R8),R2			; Get address of beginning VA
	BSBW	CHANGE_PROT			; Change the protection code
	BLBC	R0,60$				; Exit if error
	MOVAL	7(R8),R2			; And address of ending VA
	BSBW	CHANGE_PROT			; Change the protection code
	BLBS	R0,70$				; Exit if error
 60$:	BRW	110$

 70$:	BSBW	GET_SYSPAGES			; Get a page in system memory
	BLBC	R0,60$				; Exit if error.
	MOVL	#PRT$C_URKW,R6	;NEW
;	MOVL	#PRT$C_UW,R6	;DEBUG
	BSBW	CHANGE_PROT	;NEW		; Go change the protection
	BLBC	R0,60$		;NEW		; Branch on error
	MOVL	R2,R10				; Copy the address to R10

	MOVC5	#CODE_LENGTH,START,-		; Load pre-CREPRC routine.
		#0,#512,(R10)			;   into system memory

	MOVAL	NEW_GRANT_LICENSE_OFFSET(R10),NEW_GRANT_CODE+2

 80$:	CMPC3	#CODE_LENGTH,NEW_LOOKUP_LICENSE,@4(R9)
	BEQL	90$				; Skip if already done
	MOVL	R10,4(R9)			; Make service point to us
 90$:	CMPB	#OP$_CHMK,2(R8)			; Is it still CHMK?
	BNEQ	100$
	MOVC3	#6,NEW_GRANT_CODE,2(R8)		; Make service point to us
	MOVL	#SS$_NORMAL,R0
	BRB	110$
 100$:	MOVL	#SS$_ABORT,R0			; Return aborted flag
 110$:	RET					; Return to caller

	.ENTRY	TOGGLE_ENABLE,^M<>
	MOVAL	G^SYS$LOOKUP_LICENSE,R9		; Get address of system service
	MOVL	4(R9),R8			; Get offset to real code
	MOVL	#SS$_ABORT,R0			; Assume the worst...
	CMPL	#^A/FUBR/,VERIFY_OFFSET(R8)	; Is it really ours?
	BNEQ	10$				; Branch if it is not ours
	MOVL	#SS$_NORMAL,R0
	BBCS	#0,ENABLED_OFFSET(R8),10$	; Branch & enable if disabled
	CLRL	ENABLED_OFFSET(R8)		; Clear the ENABLED flag
	CLRL	R0				; Return disabled
 10$:	RET

	.SBTTL	GET_SYSPAGES - Get physically-contiguous pages of system memory
;
; Routine:	GET_SYSPAGES
;
; Functional Description:
;
;	Allocate physically contiguous pages of system memory.  This routine
;	is necessary because the pages used for the new CREPRC have to be set
;	to USER-mode READ.  Since the protection on the pages have to be
;	changed, the pages must not be used by any other code.
;
;	This routine replaces the original GET_SYSPAGE that was hacked out
;	of EXTEND_PAGE in MEMORYALC.  The original routine is located at the
;	bottom of this file.
;
; Output:
;
;	R0	= Status
;	R2	= Virtual address of the first page allocated
;
; Side affects:
;
;	Registers R1,R3 are destroyed.
;
GET_SYSPAGES:
	MOVZWL	#SS$_INSFMEM,R0			; Assume error
	TSTB	10$				; Fault in entire routine before
						; ...  raising IPL
	MOVL	#1,R1				; Allocate one page
;
;  EXE$ALOPHYCNTG wants IPL at SYNCH so we don't get swapped out.
;
	SETIPL	#IPL$_SYNCH,-			; Raise our IPL to SYNCH
		ENVIRON=UNIPROCESSOR		; ...
	JSB	G^EXE$ALOPHYCNTG		; Allocate contiguous pages of
						; ... memory
	SETIPL	#0,ENVIRON=UNIPROCESSOR		; Lower IPL to 0
 10$:	RSB					; Return to caller



	.SBTTL	CHANGE_PROT - Change protection on memory pages
;
; Input:
;
;	R2	= Virtual address to change protection for
;	R6	= Protection code to set (only lower 4 bits are used)
;
; Output:
;
;	R0	= System status code
;	Protection code is set for the virtual address
;
CHANGE_PROT:
	PUSHR	#^M<R1,R2,R3,R4,R5,R6>
	MOVL	G^CTL$GL_PCB,R4		; Get our PCB address
	MOVL	PCB$L_PHD(R4),R5	; Get address of PHD
 10$:
	TSTB	60$			; - Fault in rest of code
	SAVIPL	-(SP)			; Save our IPL for later
;
;  MMG$PTEREF compares the address to PHD$L_FREP0VA(R5) to see if it is a
;  length violation.  The comments indicate this should work for system and
;  process space addresses, but PHD$L_FREP0VA is always (?) a P0 address, so
;  the check fails and a length violation is always returned for a system
;  address.
;
;  Solution:  For system addresses (bit 31 in the address is set), call
;	MMG$SVAPTECHK to get the PTE virtual address.  This routine will
;	bugcheck on a length violation, but for us, we know the address
;	is valid because we just allocated the page.
;	     
;
	BBC	#31,R2,20$		; Branch if address is in process space
;
;  It's probably not necessary to acquire the MMG spinlock, but what the hey!
;  It lets us drop into common to unlock the MMG spinlock after invalidating
;  the address.
;
	LOCK	LOCKNAME=MMG,-		; Acquire the MMG spinlock
		LOCKIPL=#IPL$_SYNCH	; ...   Raise IPL to SYNCH
	JSB	G^MMG$SVAPTECHK		; Go get the PTE for the address
	BRB	30$			; Go change the protection on it
;
;  Note:  MMG$PTEREF acquires the MMG spinlock, so we don't need to.
;	  If we did, we would need a separate UNLOCK to release the
;	  one acuired by MMG$PTEREF.
;
 20$:	JSB	G^MMG$PTEREF		; - Get the PTE for the address
	BLBC	R0,40$			; - Branch on error
;
;  Invalidate the address for all CPUs and change the protection for the page
;  to the value stored in R6.   We don't need grab the INVALIDATE spinlock
;  because SMP$INVALID will acquire and release it.
;
;  Note: SMP$INVALID does not RESTORE the INVALIDATE spinlock; it RELEASEs it,
;	 meaning that all locks are released.
;
 30$:	INVALIDATE_TB	R2,-		; - Invalidate the address in R2
		INST1=<INSV R6,#PTE$V_PROT,#PTE$S_PROT,(R3)>
;
;  Release the MMG spinlock that was acquired by MMG$PTEREF.
;
	UNLOCK	LOCKNAME=MMG,-		; - Restore Memory Management spinlock
		NEWIPL=(SP)+,-		; - ... set IPL back down to what it was
		CONDITION=RESTORE	; - ... Just release one lock
	BRB	50$			; Skip over stack fixup
 40$:	TSTL	(SP)+			; Pop off saved IPL
 50$:
	POPR	#^M<R1,R2,R3,R4,R5,R6>	; Restore registers
	RSB				; Return to caller

 60$:	.BYTE	IPL$_SYNCH		; IPL to avoid swapping
	ASSUME	<.-10$> LE 512		; Make sure code to lock down is
					; ... less than one page
	.END	LICENSES
