#
# s390-tools/zipl/boot/sclp.S
#   Mini SCLP driver.
#
# (C) Copyright IBM Corp. 2004
#
# Author(s): Peter Oberparleiter (Peter.Oberparleiter@de.ibm.com)
#


LC_EXT_NEW_PSW		= 0x58			# addr of ext int handler
LC_EXT_INT_PARAM	= 0x80			# addr of ext int parameter
LC_EXT_INT_CODE		= 0x86			# addr of ext int code

SCCB_ADDR		= 0x8000		# addr of sccb work area

SCCB_LENGTH		= 0			# offset of length field
SCCB_RESPONSE		= 6			# offset of response field
SCCB_DATA		= 8			# offset of data
SCCB_PARAM		= 24			# offset of load parameter

EVBUF_LENGTH		= 0			# offset of length field
EVBUF_TYPE		= 2			# offset of type field
EVBUF_DATA		= 6			# offset of data

GDS_ID_MDSMU		= 0x1310		# vector keys and ids
GDS_ID_CPMSU		= 0x1212
GDS_ID_TextCmd		= 0x1320
GDS_KEY_SelfDefTextMsg	= 0x31


#
# Subroutine which waits synchronously until either an external interruption
# or a timeout occurs.
#
# Parameters:
#   R2  = 0 for no timeout, non-zero for timeout in (approximated) seconds
#
# Returns:
#   R2  = 0 on interrupt, 2 on timeout
#   R3  = external interruption parameter if R2=0
#

_sclp_wait_int:
	stm    %r6,%r15,24(%r15)		# save registers

	basr   %r13,0				# get base register
.LbaseS1:
	ahi    %r15,-96				# create stack frame

	la     %r8,LC_EXT_NEW_PSW		# register int handler
	mvc    .LoldpswS1-.LbaseS1(8,%r13),0(%r8)
	mvc    0(8,%r8),.LextpswS1-.LbaseS1(%r13)

	lhi    %r6,0x0200			# cr mask for ext int (cr0.54)
	ltr    %r2,%r2
	jz     .LsetctS1

	ahi    %r6,0x0800			# cr mask for clock int (cr0.52)

	stck   .LtimeS1-.LbaseS1(%r13)		# initiate timeout
	al     %r2,.LtimeS1-.LbaseS1(%r13)
	st     %r2,.LtimeS1-.LbaseS1(%r13)
	sckc   .LtimeS1-.LbaseS1(%r13)

.LsetctS1:
	stctl  %c0,%c0,.LctlS1-.LbaseS1(%r13)	# enable required interrupts
	l      %r0,.LctlS1-.LbaseS1(%r13)
	lhi    %r1,~(0x200 | 0x800)		# clear old values
	nr     %r1,%r0
	or     %r1,%r6				# set new value
	st     %r1,.LctlS1-.LbaseS1(%r13)
	lctl   %c0,%c0,.LctlS1-.LbaseS1(%r13)
	st     %r0,.LctlS1-.LbaseS1(%r13)
	lhi    %r2,2				# return code for timeout

.LloopS1:
	lpsw   .LwaitpswS1-.LbaseS1(%r13)	# wait until interrupt

.LwaitS1:
	lh     %r7,LC_EXT_INT_CODE
	chi    %r7,0x1004			# timeout?
	je     .LtimeoutS1
	chi    %r7,0x2401			# service int?
	jne    .LloopS1

	sr     %r2,%r2
	l      %r3,LC_EXT_INT_PARAM

.LtimeoutS1:
	lctl   %c0,%c0,.LctlS1-.LbaseS1(%r13)	# restore interrupt setting

	# restore old handler
	mvc    0(8,%r8),.LoldpswS1-.LbaseS1(%r13)

	lm     %r6,%r15,120(%r15)		# restore registers

	br     %r14				# return to caller

	.align 8
.LoldpswS1:
	.long  0, 0				# old ext int PSW
.LextpswS1:
	.long  0x00080000, 0x80000000+.LwaitS1	# PSW to handle ext int
.LwaitpswS1:
	.long  0x010a0000, 0x00000000+.LloopS1	# PSW to wait for ext int
.LtimeS1:
	.quad  0				# current time
.LctlS1:
	.long  0				# CT0 contents


#
# Subroutine to synchronously issue a service call.
#
# Parameters:
#   R2  = command word
#   R3  = sccb address
#
# Returns:
#   R2  = 0 on success, 1 on failure
#   R3  = sccb response code if R2 = 0
#

_sclp_servc:
	stm    %r6,%r15,24(%r15)		# save registers
	ahi    %r15,-96				# create stack frame

	lr     %r6,%r2				# save command word
	lr     %r7,%r3				# save sccb address

.LretryS2:
	lhi    %r2,1				# error return code

	.insn  rre,0xb2200000,%r6,%r7		# servc
	brc    1,.LendS2			# exit if not operational
	brc    8,.LnotbusyS2			# go on if not busy

	sr     %r2,%r2				# wait until no longer busy
	bras   %r14,_sclp_wait_int

	j      .LretryS2			# retry

.LnotbusyS2:
	sr     %r2,%r2				# wait until result
	bras   %r14,_sclp_wait_int

	sr     %r2,%r2
	lh     %r3,SCCB_RESPONSE(%r7)

.LendS2:
	lm     %r6,%r15,120(%r15)		# restore registers

	br     %r14


#
# Subroutine to set up the SCLP interface.
#
# Parameters:
#   R2  = 0 to activate, non-zero to deactivate
#
# Returns:
#   R2  = 0 on success, non-zero on failure
#

_sclp_setup:
	stm    %r6,%r15,24(%r15)		# save registers

	basr   %r13,0				# get base register
.LbaseS3:
	ahi    %r15,-96				# create stack frame

	l      %r6,.LsccbS0-.LbaseS3(%r13)	# prepare init mask sccb
	mvc    0(.LinitendS3-.LinitsccbS3,%r6),.LinitsccbS3-.LbaseS3(%r13)

	ltr    %r2,%r2				# initialization?
	jz     .LdoinitS3			# go ahead

	# clear masks
	xc     .LinitmaskS3-.LinitsccbS3(8,%r6),.LinitmaskS3-.LinitsccbS3(%r6)

.LdoinitS3:
	l      %r2,.LwritemaskS3-.LbaseS3(%r13)	# get command word
	lr     %r3,%r6				# get sccb address
	bras   %r14,_sclp_servc			# issue service call

	ltr    %r2,%r2				# servc successful?
	jnz    .LerrorS3

	chi    %r3,0x20				# write mask successful?
	jne    .LerrorS3

	# check masks
	la     %r2,.LinitmaskS3-.LinitsccbS3(%r6)

	l      %r1,0(%r2)			# receive mask ok?
	n      %r1,12(%r2)
	cl     %r1,0(%r2)
	jne    .LerrorS3

	l      %r1,4(%r2)			# send mask ok?
	n      %r1,8(%r2)
	cl     %r1,4(%r2)
	sr     %r2,%r2
	je     .LendS3

.LerrorS3:
	lhi    %r2,1				# error return code

.LendS3:
	lm     %r6,%r15,120(%r15)		# restore registers

	br     %r14

.LwritemaskS3:
	.long  0x00780005			# SCLP command for write mask
.LinitsccbS3:
	.word  .LinitendS3-.LinitsccbS3
	.byte  0,0,0,0
	.word  0
	.word  0
	.word  4
.LinitmaskS3:
	.long  0x80000000
	.long  0x40000000
	.long  0
	.long  0
.LinitendS3:


#
# Subroutine which prints a given text to the SCLP console.
#
# Parameters:
#   R2  = address of nil-terminated EBCDIC text
#
# Returns:
#   R2  = 0 on success, 1 on failure
#

_sclp_print:
	stm    %r6,%r15,24(%r15)		# save registers

	basr   %r13,0				# get base register
.LbaseS4:
	ahi    %r15,-96				# create stack frame

	l      %r8,.LsccbS0-.LbaseS4(%r13)	# prepare write data sccb
	mvc    0(.LmtoS4-.LwritesccbS4,%r8),.LwritesccbS4-.LbaseS4(%r13)

	la     %r7,.LmtoS4-.LwritesccbS4(%r8)	# current mto addr
	sr     %r0,%r0

.LinitmtoS4:
	# initialize mto
	mvc    0(.LmtoendS4-.LmtoS4,%r7),.LmtoS4-.LbaseS4(%r13)
	lhi    %r6,.LmtoendS4-.LmtoS4		# current mto length

.LloopS4:
	ic     %r0,0(%r2)			# get character
	ahi    %r2,1

	ltr    %r0,%r0				# end of string?
	jz     .LfinalizemtoS4

	chi    %r0,0x15				# end of line (NL)?
	jz     .LfinalizemtoS4

	stc    %r0,0(%r6,%r7)			# copy to mto
	ahi    %r6,1
	j      .LloopS4

.LfinalizemtoS4:
	sth    %r6,0(%r7)			# update mto length

	lh     %r9,.LmdbS4-.LwritesccbS4(%r8)	# update mdb length
	ar     %r9,%r6
	sth    %r9,.LmdbS4-.LwritesccbS4(%r8)

	lh     %r9,.LevbufS4-.LwritesccbS4(%r8)	# update evbuf length
	ar     %r9,%r6
	sth    %r9,.LevbufS4-.LwritesccbS4(%r8)

	lh     %r9,0(%r8)			# update sccb length
	ar     %r9,%r6
	sth    %r9,0(%r8)

	ar     %r7,%r6				# update current mto adress
	ltr    %r0,%r0				# more characters?
	jnz    .LinitmtoS4

	l      %r2,.LwritedataS4-.LbaseS4(%r13)	# write data
	lr     %r3,%r8
	bras   %r14,_sclp_servc

	ltr    %r2,%r2				# servc successful?
	jnz    .LendS4

	chi    %r3,0x20				# write data successful?
	je     .LendS4

	lhi    %r2,1				# error return code

.LendS4:
	lm     %r6,%r15,120(%r15)		# restore registers

	br     %r14

.LwritedataS4:
	.long  0x00760005			# SCLP command for write data
.LwritesccbS4:
	# sccb
	.word  .LmtoS4-.LwritesccbS4
	.byte  0
	.byte  0,0,0
	.word  0

	# evbuf
.LevbufS4:
	.word  .LmtoS4-.LevbufS4
	.byte  0x02
	.byte  0
	.word  0

.LmdbS4:
	# mdb
	.word  .LmtoS4-.LmdbS4
	.word  1
	.long  0xd4c4c240
	.long  1

	# go
.LgoS4:
	.word  .LmtoS4-.LgoS4
	.word  1
	.long  0
	.byte  0,0,0,0,0,0,0,0
	.byte  0,0,0
	.byte  0
	.byte  0,0,0,0,0,0,0
	.byte  0
	.word  0
	.byte  0,0,0,0,0,0,0,0,0,0
	.byte  0,0,0,0,0,0,0,0
	.byte  0,0,0,0,0,0,0,0

.LmtoS4:
	.word  .LmtoendS4-.LmtoS4
	.word  4
	.word  0x1000
	.byte  0
	.byte  0,0,0
.LmtoendS4:


#
# Subroutine which reads text from the SCLP console.
#
# Parameters:
#   R2  = 0 for no timeout, non-zero for timeout in (approximated) seconds
#   R3  = destination address for input
#
# Returns:
#   R2  = 0 on input, 1 on failure, 2 on timeout
#   R3  = length of input data
#

_sclp_read:
	stm    %r6,%r15,24(%r15)		# save registers

	basr   %r13,0				# get base register
.LbaseS5:
	ahi    %r15,-96				# create stack frame

	l      %r9,.LsccbS0-.LbaseS5(%r13)	# get sccb address

	lr     %r10,%r2				# save timeout
	lr     %r11,%r3				# save destination address

.LretryS5:
	# prepare sccb
	mvc    0(.LreadevbufS5-.LreadsccbS5,%r9),.LreadsccbS5-.LbaseS5(%r13)

	l      %r2,.LreaddataS5-.LbaseS5(%r13)	# read data
	lr     %r3,%r9
	bras   %r14,_sclp_servc

	ltr    %r2,%r2				# servc successful?
	jnz    .LendS5

	chi    %r3,0x20				# read event data successful?
	je     .LprocessS5			# process input

	chi    %r3,0x60f0			# no buffer?
	lhi    %r2,1
	jne    .LendS5				# anything else is an error

	lr     %r2,%r10				# wait for input or timeout
	bras   %r14,_sclp_wait_int

	ltr    %r2,%r2				# got int?
	jz     .LretryS5
	j      .LendS5				# just timeout

.LprocessS5:
	la     %r1,SCCB_DATA(%r9)		# get event data end address
	ah     %r1,EVBUF_LENGTH(%r1)
	st     %r1,.LendarrayS5-.LbaseS5(%r13)

	la     %r9,EVBUF_DATA+SCCB_DATA(%r9)	# get event data address

	lr     %r3,%r11				# current dest address
	sr     %r8,%r8				# current level

.LloopS5:
	chi    %r8,-4				# at end of buffer?
	je     .LsuccessS5

	# at end of vector?
	c      %r9,.LendarrayS5-.LbaseS5(%r8,%r13)
	jl     .LcontinueS5

	ahi    %r8,-4				# go up one level
	j      .LloopS5
.LcontinueS5:

	chi    %r8,8				# check for subvector
	jh     .LsubvectorS5

	lh     %r6,0(%r9)			# get vector length
	lh     %r0,2(%r9)			# get vector ID
	lhi    %r7,4				# vector header is 4 bytes

	j      .LprocessvecS5

.LsubvectorS5:
	sr     %r6,%r6				# get subvector length
	ic     %r6,0(%r9)

	sr     %r0,%r0				# get subvector key
	ic     %r0,1(%r9)

	lhi    %r7,2				# subvector header is 2 bytes

.LprocessvecS5:
	cr     %r6,%r7				# check for empty vector?
	je     .LendprocessS5

	# check for matching vector
	c      %r0,.LkeyarrayS5-.LbaseS5(%r8,%r13)
	jne    .LendprocessS5

	chi    %r8,16				# check for text vector
	je     .LcopytextS5

	ahi    %r8,4				# go down one level

	lr     %r0,%r9				# get end address for vector
	ar     %r0,%r6
	st     %r0,.LendarrayS5-.LbaseS5(%r8,%r13)

	ar     %r9,%r7				# skip header
	j      .LloopS5

.LcopytextS5:
	sr     %r6,%r7				# get length without header
	ar     %r9,%r7				# get addr after header
	sr     %r0,%r0

.LcopyloopS5:
	ic     %r0,0(%r9)			# copy text to dest
	stc    %r0,0(%r3)

	ahi    %r9,1
	ahi    %r3,1
	brct   %r6,.LcopyloopS5

.LendprocessS5:
	ar     %r9,%r6				# continue with next vector
	j      .LloopS5

.LsuccessS5:
	sr     %r2,%r2
	sr     %r3,%r11

.LendS5:
	lm     %r6,%r15,120(%r15)		# restore registers

	br     %r14

.LreaddataS5:
	.long  0x00770005			# SCLP command for read data
.LendarrayS5:
	.long  0,0,0,0,0			# end addr array for read
.LkeyarrayS5:
	.long  GDS_ID_MDSMU			# vector ID/key array for read
	.long  GDS_ID_CPMSU
	.long  GDS_ID_TextCmd
	.long  GDS_KEY_SelfDefTextMsg
	.long  0x30
.LreadsccbS5:
	.word  0x1000
	.byte  0
	.byte  0,0,0
	.word  0
.LreadevbufS5:


#
# Subroutine to read load parameter.
#
# Parameters:
#   R2  = destination address for load parameter (8 bytes)
#
# Returns:
#   R2  = 0 on success, 1 on failure
#

_sclp_param:
	stm    %r6,%r15,24(%r15)		# save registers

	basr   %r13,0				# get base register
.LbaseS6:
	ahi    %r15,-96				# create stack frame

	lr     %r8,%r2				# save dest address
	l      %r6,.LsccbS0-.LbaseS6(%r13)	# get sccb address

	# prepare sccb
	mvc    0(.LinfosccbendS6-.LinfosccbS6,%r6),.LinfosccbS6-.LbaseS6(%r13)

	la     %r7,.LreadinfoS6-.LbaseS6(%r13)
.LretryS6:
	l      %r2,0(%r7)			# get command word
	ltr    %r2,%r2
	jz     .LerrorS6

	lr     %r3,%r6				# get sccb address
	bras   %r14,_sclp_servc

	ltr    %r2,%r2				# servc successful?
	jnz    .LerrorS6

	chi    %r3,0x10				# read info successful?
	je     .LgotinfoS6

	ahi    %r7,4				# next try
	j      .LretryS6

.LgotinfoS6:
	mvc    0(8,%r8),SCCB_PARAM(%r6)		# copy parameter to dest address
	sr     %r2,%r2
	j      .LendS6

.LerrorS6:
	lhi    %r2,1				# failure

.LendS6:
	lm     %r6,%r15,120(%r15)		# restore registers

	br     %r14

.LreadinfoS6:
	.long  0x00120001
	.long  0x00020001
	.long  0
.LinfosccbS6:
	.word  0x1000
	.byte  0
	.byte  0,0
	.byte  0
	.word  0
.LinfosccbendS6:

	# Global constants
.LsccbS0:
	.long  SCCB_ADDR

