/*
 *  dumpcommon.S
 *
 *  Common routines for dump records
 *    Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
 *    Author(s): Michael Holzheu  <holzheu@de.ibm.com>
 *
 * Uses extern functions:
 *  - _dump_mem (device dependent function to write dump)
 *
 * Functions:
 *  - _enable_device
 *  - _ssch
 *  - _wait4de
 *  - _panik
 *  - _take_dump
 *  - _store_status
 */


/* General defines */

#define PAGE_SIZE        0x1000                /* 4096 */
#define HEADER_SIZE      0x1000                /* 4096 */
#define END_MARKER_SIZE  0x10
#define DUMP_END_MARKER  0x44554d50,0x5f454e44 /* DUMP_END */
#define IPL_SC           0xb8                  /* Address of ipl subchannel */
#define S390_DUMP_MAGIC  0xa8190173,0x618f23fd /* magic number */
#define ARCH_S390_ID     0x1                   /* arch flag for s390  */
#define ARCH_S390X_ID    0x2                   /* arch flag for s390x */
 
/* Error codes */

#define OK               0x00000000  /* Dump completed successfully */
#define EMEM             0x00000001  /* Device too small for dump */
#define EDEV_INVAL       0x00000002  /* Device not supported */
#define EMEMCOUNT        0x00000003  /* Mem counting did not work */
#define EENABLE_DEV      0x00000100  /* enable device failed */
#define EDISABLE_DEV     0x00000101  /* disable device failed */
#define EDSSCH           0x00000102  /* start subchannel failed */

#define __LC_ARCH_MODE_ID 163        /* here is the arch flag in the lowcore */
#define __LC_EXT_NEW_PSW_S390  0x058
#define __LC_EXT_NEW_PSW_S390X 0x01b0
#define __LC_EXT_INT_CODE_ADDR 0x86

#define ZERO_MEM_START     0x3000
#define ZERO_MEM_SIZE      0x3000

#define SCCB_START     0x3000
#define SCCBR_OFF      6
#define SCPINCR1_OFF   8
#define SCPA1_OFF      10
#define SCPA2_OFF      100
#define SCPINCR2_OFF   104

#define ZERO_PAGE_START 0x4000
#define TMP_PAGE_START  0x5000

################################################################################
# MACRO: dump_header
################################################################################

.macro dump_header

.align 8
#
# The Dump header
#
.Ldh_dumpheader:
.Ldh_magic_number:.long S390_DUMP_MAGIC
.Ldh_version:     .long 0x00000003
.Ldh_header_size: .long HEADER_SIZE
.Ldh_dump_level:  .long 0x00000004              # DUMP_ALL
.Ldh_page_size:   .long PAGE_SIZE
.Ldh_mem_size:    .long 0x00000000,0x00000000
.Ldh_mem_start:   .long 0x00000000,0x00000000
.Ldh_mem_end:     .long 0x00000000,0x00000000
.Ldh_num_pages:   .long 0x00000000
.Ldh_pad:         .long 0x00000000
.Ldh_time:        .long 0x00000000,0x00000000
.Ldh_cpuid:       .long 0x00000000,0x00000000
.Ldh_arch:        .long ARCH_S390_ID
.Ldh_vol_nr:      .long 0x00000000
#if defined(__s390x__)
.Ldh_build_arch:  .long ARCH_S390X_ID
#else
.Ldh_build_arch:  .long ARCH_S390_ID
#endif
.Ldh_real_mem_size: .long 0x00000000,0x00000000

#
# Dump End Marker
#
.align 8
.Ld_endmarker:    .long DUMP_END_MARKER
.Ld_end_time:     .long 0x00000000,0x00000000

.endm  /* dump_header */




#if defined(__s390x__)


/******************************** 64 BIT **************************************/


################################################################################
# MACRO: dump_io_subroutines_64
# - _enable_device_64
# - _ssch_64
# - _wait4de_64
# - _panik_64
################################################################################

.macro dump_io_subroutines_64

################################################################################
# Enable I/O on the ipl device.
#   -r2 : device subchannel id
################################################################################

_enable_device_64:
        stmg   %r6,%r15,48(%r15)
        basr   %r13,0                   # base register
0:      aghi   %r15,-200
        lgr    %r1,%r2
        lghi   %r2,EENABLE_DEV          # set panik code early
        stsch  1f-0b(%r13)
        oi     1f-0b+5(%r13),0x84       # enable ssch and multipath mode
        msch   1f-0b(%r13)
        bnz    _panik_64-0b(%r13)       # subchannel busy or in error ?
        lctl   %c6,%c6,3f-0b(%r13)      # enable all interrupt classes
        lmg    %r6,%r15,248(%r15)
        br     %r14
        .align 8
1:      .fill  64,1,0
3:      .long  0xff000000               # CR6 initial value

################################################################################
# Start I/O
#  -r2: device subchannel id
#  -r3: address of orb
#  -r4: address of irb
#  -r5: retry count
################################################################################

_ssch_64:
        stmg   %r6,%r15,48(%r15)
        basr   %r13,0                    # base register
0:      aghi   %r15,-200                 # create stack frame
        lgr    %r12,%r2                  # save subchannel id
        lgr    %r11,%r3                  # save orb
        lgr    %r10,%r4                  # save irb
        lgr    %r9,%r5                   # save retry count
1:      lgr    %r1,%r12
        ssch   0(%r11)                   # go
        bnz    5f-0b(%r13)               # houston, we have a problem
2:      lgr    %r2,%r12                  # call _wait4de with subchannel id
        lgr    %r3,%r10                  # and irb address as parameters
        bas    %r14,_wait4de_64-0b(%r13) # wait until DE or error
        tm     9(%r10),0xff              # test channel status
        bnz    5f-0b(%r13)
        tm     8(%r10),0xd2              # test device status
        bz     4f-0b(%r13)
        bct    %r9,1b-0b(%r13)           # something went wrong, retry.
4:      lmg    %r6,%r15,248(%r15)
        br     %r14

5:      lghi   %r2,EDSSCH
        bas    %r4,_panik_64-0b(%r13)

################################################################################
# Wait for interrupt subroutine
#  -r2: device subchannel id
#  -r3: address of irb
################################################################################

_wait4de_64:
        stmg   %r6,%r15,48(%r15)
        basr   %r13,0                    # base register
0:      aghi   %r15,-200                 # create stack frame
        lr     %r1,%r2
        mvc    496(16),6f-0b(%r13)       # set i/o new psw
1:      lpswe  5f-0b(%r13)
2:      stnsm  .Lpsw-0b(%r13),0xfd       # disable io interrupts
        c      %r1,0xb8                  # compare subchannel id
        bne    1b-0b(%r13)               # unequal -> continue waiting
        tsch   0(%r3)
        tm     9(%r3),0xff               # test channel status
        bnz    4f-0b(%r13)

3:      tm     8(%r3),0x02               # got unit check ?
        bnz    4f-0b(%r13)

        tm     8(%r3),0x04               # got device end ?
        bz     1b-0b(%r13)               # still busy -> continue waiting

4:      lmg   %r6,%r15,248(%r15)
        br     %r14
        .align 16
5:      .long  0x02020001,0x80000000,0x00000000,0x00000000+1b
6:      .long  0x00000001,0x80000000,0x00000000,0x00000000+2b
.Lpsw:  .long  0x0

################################################################################
# Panik routine. Loads a disabled wait psw
#   -r2 : panik code
################################################################################

_panik_64:
        basr   %r1,0
0:      stg    %r2,1f-0b+8(%r1)         # store code in address part of psw
        lpswe  1f-0b(%r1)
        .align 8
1:      .long  0x00020000,0x80000000,0x00000000,0x00000000

.endm



################################################################################
# MACRO: dump_common_fn_64
# - _take_dump_64
# - _count_mem_64
# - _store_status_64
# - _copy_lowcore_64
################################################################################

.macro dump_common_fn_64

################################################################################
# Take the dump
#  - no parameters
################################################################################

_take_dump_64:
        stmg  %r6,%r15,48(%r15)
        basr  %r13,0
.Lbase: aghi  %r15,-200                          # create stack frame

	sckc  .Lclock_comparator-.Lbase(%r13)    # set clock comparator to future
	spt   .Lcpu_timer-.Lbase(%r13)           # set cpu timer to future

	lghi  %r6,ZERO_MEM_START                 # clear memory
	lghi  %r7,ZERO_MEM_START + ZERO_MEM_SIZE
	sgr   %r7,%r6
	sgr   %r8,%r8
	sgr   %r9,%r9
	mvcle %r6,%r8,0
	jo    .-4

        bas   %r14,_count_mem_64-.Lbase(%r13)    # count memory
        llgf  %r14,.Ldump_mem_64-.Lbase(%r13)
        basr  %r14,%r14                          # dump memory
        lghi  %r2,OK
        bas   %r14,_panik_64-.Lbase(%r13)        # everything ok: stop now

.align 8
.Lclock_comparator:
	.quad  0xffffffffffffffff
.Lcpu_timer:
	.quad  0x0fffffffffffffff

################################################################################
# Find out memory size:
# Use Read SCP or Read SCP forced to do this
#  - no parameters
################################################################################

_count_mem_64:
        stmg  %r6,%r15,48(%r15)
        basr  %r13,0                          # base register
0:      aghi  %r15,-200                       # create stack frame

	lghi  %r12, SCCB_START                # setup sccb
	lghi  %r8,0x1000
	sth   %r8,0(%r12)
	lghi  %r8,0x80
	stc   %r8,5(%r12)

        l     %r2,.Lrcp-0b(%r13)     # Read SCP forced command word
.Lservicecall:

        stctg %r0,%r0,.Lcr-0b(%r13)  # get cr0
        la    %r1,0x200              # set bit 22
        og    %r1,.Lcr-0b(%r13)      # or old cr0 with r1
        stg   %r1,.Lcr-0b(%r13)
        lctlg %r0,%r0,.Lcr-0b(%r13)  # load modified cr0

        mvc   __LC_EXT_NEW_PSW_S390X(8),.Lpcmsk-0b(%r13) # set postcall psw
        larl  %r1,.Lsclph
        stg   %r1,__LC_EXT_NEW_PSW_S390X+8    # set handler

	lr    %r1,%r12
        .insn rre,0xb2200000,%r2,%r1    # service call
        ipm   %r1
        srl   %r1,28                    # get cc code
        xr    %r3,%r3
        chi   %r1,3
        be    .Lmem_panik-0b(%r13)      # leave
        chi   %r1,2
        be    .Lservicecall-0b(%r13)

.Lwait:
        stosm .Lpmask-0b(%r13),0x01     # authorize ext interrupts
        lpsw  .Lwaitsclp-0b(%r13)
.Lsclph:
	stnsm .Lpmask-0b(%r13),0xfe     # disable ext interrupts again
        lh    %r1,__LC_EXT_INT_CODE_ADDR
        chi   %r1,0x2401                # check external interrupt code
        bne   .Lwait-0b(%r13)           # ignore all no service interrupts
        lh    %r1,SCCBR_OFF(%r12)
        chi   %r1,0x10                  # 0x0010 is the sucess code
        je    .Lprocsccb                # let's process the sccb
        chi   %r1,0x1f0
        bne   .Lmem_panik-0b(%r13)      # unhandled error code
        c     %r2,.Lrcp-0b(%r13)        # Did we try Read SCP forced
        bne   .Lmem_panik-0b(%r13)      # if no, give up
        l     %r2,.Lrcp2-0b(%r13)       # try with Read SCP
        b     .Lservicecall-0b(%r13)
.Lprocsccb:
        lghi  %r1,0
	icm   %r1,3,SCPINCR1_OFF(%r12) # use this one if != 0
        jnz   .Lscnd
        lg    %r1,SCPINCR2_OFF(%r12)    # otherwise use this one
.Lscnd:
        xr    %r3,%r3                   # same logic
        ic    %r3,SCPA1_OFF(%r12)
        chi   %r3,0x00
        jne   .Lcompmem
        l     %r3,SCPA2_OFF(%r12)
.Lcompmem:
        mlgr  %r2,%r1                   # mem in MB on 128-bit
        l     %r1,.Lonemb-0b(%r13)
        mlgr  %r2,%r1                   # mem size in bytes in %r3

        stg   %r3,.Ldh_real_mem_size-0b(%r13)
        lg    %r6,.Lmem_upper_limit-0b(%r13)  # check if we have an upper limit
        clr   %r3,%r6
        bl    .Lsavemem-0b(%r13)
        lgr   %r3,%r6                        # upper mem limit set -> use it!
.Lsavemem:
        stg   %r3,.Ldh_mem_size-0b(%r13)     # store memory size
        stg   %r3,.Ldh_mem_end-0b(%r13)      # store memory end
        srlg  %r12,%r3,12                    # calculate page count (/ 4096)
        st    %r12,.Ldh_num_pages-0b(%r13)   # store page count

        lmg   %r6,%r15,248(%r15)
        br    %r14
.Lmem_panik:
        lghi   %r2,EMEMCOUNT
        bas    %r4,_panik_64-0b(%r13)

.Lpcmsk:.quad  0x0000000180000000
.Lpmask:
        .byte 0
        .align 8
.Lcr:
        .quad 0x00  # place holder for cr0
.Lwaitsclp:
        .long 0x020A0000
        .quad .Lsclph
.Lrcp:
        .int 0x00120001 # Read SCP forced code
.Lrcp2:
        .int 0x00020001 # Read SCP code
.Lonemb:
        .int 0x100000


################################################################################
# store status of all cpus in their lowcores
#  - no parameters
################################################################################

_store_status_64:
        stmg  %r6,%r15,48(%r15)
        basr  %r13,0                          # base register
0:      aghi  %r15,-200
        lghi  %r7,0x0                         # base register for 0 page

        ######## move lowcore info (assume user has made store  ########
        ######## status) to prefix-page                         ########

        bas   %r14,_copy_lowcore_64-0b(%r13)

        ######## stop all cpus and store status in prefix pages ########

        lghi  %r8,0                           # first cpu
        stap  .Lcurrent_cpu_64+2-0b(%r13)     # store current cpu address

1:
        cl    %r8,.Lcurrent_cpu_64-0b(%r13)   # is ipl cpu ?
        be    4f-0b(%r13)                     # if yes get next cpu
2:
        lgr  %r9,%r7
        sigp %r9,%r8,0x9                      # store status of cpu
        bc   8,3f-0b(%r13)                    # accepted
        bc   4,4f-0b(%r13)                    # status stored: next cpu
        bc   2,2b-0b(%r13)                    # busy:          try again
        bc   1,4f-0b(%r13)                    # not op:        next cpu
3:
        bas   %r14,_copy_lowcore_64-0b(%r13)
4:
        aghi  %r8,1                           # next cpu (r8 +=1)
        cl    %r8,.Llast_cpu_64-0b(%r13)      # is last possible cpu ?
        bl    1b-0b(%r13)                     # jump if not last cpu

        lmg   %r6,%r15,248(%r15)
        br    %r14                            # return to caller
.Lcurrent_cpu_64:
        .long 0x0
.Llast_cpu_64:
        .long 0x0000ffff

################################################################################
# copy lowcore 64
#  - no parameters
################################################################################

_copy_lowcore_64:
        stmg   %r6,%r15,48(%r15)
        basr   %r13,0                         # base register
0:      aghi   %r15,-200

        lghi  %r2,0x1000                      # offset for first page
        llgf  %r3,792(%r2)                    # get prefix page of current cpu

        ###### check if lowcore address looks valid ######

        cl    %r3,.Llinux_start_64-0b(%r13)   # looks valid ?
        bl    .Lcpy_locore_exit_64-0b(%r13)   # if < linux-start addr
        llgf  %r6,.Lpage_align_64-0b(%r13)    # check page alignment
        nr    %r3,%r6
        cl    %r3,792(%r2)                    # 4888
        bnz   .Lcpy_locore_exit_64-0b(%r13)   # if not page aligned

        ###### copy lowcore                         ######

        llgf  %r3,792(%r2)                    # get prefix page of current cpu
        lghi  %r5,0x1000                      # first page 
        agr   %r3,%r5                         # get base register for second
                                              # page of prefix pages

        # |-----------------------------------------------------------|
        # | Decimal |  Length   | Data                                |
        # | Address |  in Bytes |                                     |
        # |_________|___________|_____________________________________|
        # | 163     | 1         | Architectural Mode ID               |
        # | 4608    | 128       | Fl-pt registers 0-15                |
        # | 4736    | 128       | General registers 0-15              |
        # | 4864    | 16        | Current PSW                         |
        # | 4888    | 4         | Prefix register                     |
        # | 4892    | 4         | Fl-pt control register              |
        # | 4900    | 4         | TOD programmable register           |
        # | 4904    | 8         | CPU timer                           |
        # | 4912    | 1         | Zeros                               |
        # | 4913    | 7         | Bits 0-55 of clock comparator       |
        # | 4928    | 64        | Access registers 0-15               |
        # | 4992    | 128       | Control registers 0-15              |
        # |_________|___________|_____________________________________|

        mvc   512(256,%r3),512(%r2)      # 4608
        mvc   768(16,%r3),768(%r2)
        mvc   792(8,%r3),792(%r2)        # 4888
        mvc   804(20,%r3),804(%r2)       # 4900
        mvc   832(192,%r3),832(%r2)      # 4928

.Lcpy_locore_exit_64:
        lmg   %r6,%r15,248(%r15)
        br    %r14                            # return to caller
.Lpage_align_64:
        .long -4096
.Llinux_start_64:
        .long  0x10000

.align 4
.Ldump_mem_64:    .long _dump_mem_64          # address of function

.endm /* dump_common_fn_64 */


################################################################################
# MACRO: dump_idal_64
# - _create_ida_list_64
################################################################################

.macro dump_idal_64
################################################################################
# This function creates a indirect data addressing list
# Parameters:
#  -r2:  start address
#  -r3:  length
#  -r4:  address of ida list
################################################################################
_create_ida_list_64:
        stmg    %r6,%r15,48(%r15)
        basr    %r13,0                        # base register
0:      aghi    %r15,-200                     # create stack frame

        # setup new pgc psw for finding invalid pages

        lghi    %r9,0
        mvc     .Lold_pgc_psw-0b(16,%r13),464(%r9)      # save old psw
        mvc     464(16,%r9),.Lpage_invalid_psw-0b(%r13) # setup pgm check new

        lgr     %r14,%r4                      # copy address of ida list
        lgr     %r11,%r2                      # copy start address
        lgr     %r9,%r3                       # copy length
        agr     %r9,%r11                      # compute end address
1:
        l       %r7,0(%r11)                   # test page
        lr      %r7,%r11
        b       .Lpage_ok-0b(%r13)
.Lpage_invalid:
        lghi    %r7,ZERO_PAGE_START
.Lpage_ok:
        stg     %r7,0(%r14)                   # store address
        aghi    %r14,8                        # update ida pointer
        aghi    %r11,4096                     # update data address
        clr     %r11,%r9                      # enough ?
        bl      1b-0b(%r13)                   # branch if r11 < r9

        lghi    %r9,0
        mvc     464(16,%r9),.Lold_pgc_psw-0b(%r13) # restore pgm check new
        lmg     %r6,%r15,248(%r15)
        br      %r14                          # return to caller
.Lpage_invalid_psw:
        .long 0x00000001,0x80000000,0x00000000,0x00000000 + .Lpage_invalid
.Lold_pgc_psw:
        .long 0x0,0x0,0x0,0x0

.endm /* dump_idal_64 */


#else /* __s390x__ */



/******************************** 32 BIT **************************************/



################################################################################
# MACRO: dump_io_subroutines_32
# - _enable_device_32
# - _ssch_32
# - _wait4de_32
################################################################################

.macro dump_io_subroutines_32

################################################################################
# Enable I/O on the ipl device.
#   -r2 : device subchannel id
################################################################################

_enable_device_32:
        stm    %r6,%r15,24(%r15)
        basr   %r13,0                   # base register
0:      s      %r15,1f-0b(%r13)
        lr     %r1,%r2
        l      %r2,4f-0b(%r13)          # set panik code early
        stsch  2f-0b(%r13)
        oi     2f-0b+5(%r13),0x84       # enable ssch and multipath mode
        msch   2f-0b(%r13)
        bnz    _panik_32-0b(%r13)       # subchannel busy or in error ?
        lctl   %c6,%c6,3f-0b(%r13)      # enable all interrupt classes
        lm     %r6,%r15,120(%r15)
        br     %r14
1:      .long  96
        .align 8
2:      .fill  64,1,0
3:      .long  0xff000000               # CR6 initial value
4:      .long  EENABLE_DEV

################################################################################
# Start I/O
#  -r2: device subchannel id
#  -r3: address of orb
#  -r4: address of irb
#  -r5: retry count
################################################################################

_ssch_32:
        stm    %r6,%r15,24(%r15)
        basr   %r13,0                    # base register
0:      s      %r15,.Lc96-0b(%r13)       # create stack frame
        lr     %r12,%r2                  # save subchannel id
        lr     %r11,%r3                  # save orb
        lr     %r10,%r4                  # save irb
        lr     %r9,%r5                   # save retry count
1:      lr     %r1,%r12
        stnsm  .Lpsw-0b(%r13),0xfd       # disable io interrupts
        ssch   0(%r11)                   # go
        bnz    5f-0b(%r13)               # houston, we have a problem

2:      lr     %r2,%r12                  # call _wait4de with subchannel id
        lr     %r3,%r10                  # and irb address as parameters
        bas    %r14,_wait4de_32-0b(%r13) # wait until DE or error
        tm     9(%r10),0xff              # test channel status
        bnz    5f-0b(%r13)
        tm     8(%r10),0xd2              # f3 test device status
        bz     4f-0b(%r13)
        bct    %r9,1b-0b(%r13)           # something went wrong, retry.

4:      lm     %r6,%r15,120(%r15)
        br     %r14

5:      l      %r2,6f-0b(%r13)
        bas    %r4,_panik_32-0b(%r13)
6:      .long  EDSSCH

################################################################################
# Wait for interrupt subroutine
#  -r2: device subchannel id
#  -r3: address of irb
################################################################################

_wait4de_32:
        stm    %r6,%r15,24(%r15)
        basr   %r13,0                   # base register
0:      s      %r15,.Lc96-0b(%r13)      # create stack frame
        lr     %r1,%r2
        mvc    0x78(8),6f-0b(%r13)      # set i/o new psw
1:      lpsw   5f-0b(%r13)
2:      stnsm  .Lpsw-0b(%r13),0xfd      # disable io interrupts
        c      %r1,0xb8                 # compare subchannel id
        bne    1b-0b(%r13)              # unequal -> continue waiting
        tsch   0(%r3)
        tm     9(%r3),0xff              # test channel status
        bnz    4f-0b(%r13)

3:      tm     8(%r3),0x02              # got unit check ?
        bnz    4f-0b(%r13)

        tm     8(%r3),0x04              # got device end ?
        bz     1b-0b(%r13)              # still busy -> continue waiting

4:      lm     %r6,%r15,120(%r15)
        br     %r14
        .align 8
5:      .long  0x020a0000,0x80000000+1b
6:      .long  0x00080000,0x80000000+2b # io new psw
.Lpsw:  .long  0x0

################################################################################
# Panik routine. Loads a disabled wait psw
#   -r2 : panik code
################################################################################

_panik_32:
        basr   %r1,0
0:      st     %r2,1f-0b+4(%r1)         # store code in address part of psw
        lpsw   1f-0b(%r1)
        .align 8
1:      .long  0x000a0000,0x00000000

.endm /* dump_io_subroutines_32 */







################################################################################
# MACRO: dump_common_fn_32
# - _take_dump_32
# - _count_mem_32
# - _wait4de_32
# - _store_status_32
# - _copy_lowcore_32
################################################################################

.macro dump_common_fn_32


################################################################################
# Take the dump
#  - no parameters
################################################################################

_take_dump_32:
        stm   %r6,%r15,24(%r15)
        basr  %r13,0
.Lbase: s     %r15,.Lc96-.Lbase(%r13)            # create stack frame

	sckc  .Lclock_comparator-.Lbase(%r13)    # set clock comparator to future
	spt   .Lcpu_timer-.Lbase(%r13)           # set cpu timer to future

        lhi    %r6,ZERO_MEM_START                # clear memory
        lhi    %r7,ZERO_MEM_START + ZERO_MEM_SIZE
        sr     %r7,%r6
        sr     %r8,%r8
        sr     %r9,%r9
        mvcle  %r6,%r8,0
        jo     .-4

        bas   %r14,_store_status_32-.Lbase(%r13) # store status
        bas   %r14,_count_mem_32-.Lbase(%r13)    # count memory 
        l     %r14,.Ldump_mem_32-.Lbase(%r13)
        basr  %r14,%r14                          # dump memory
        la    %r2,OK                         
        bas   %r14,_panik_32-.Lbase(%r13)        # everything ok: stop now
.align 8
.Lclock_comparator:
	.quad  0xffffffffffffffff
.Lcpu_timer:
	.quad  0x0fffffffffffffff

################################################################################
# Find out memory size:
# Use Read SCP or Read SCP forced to do this
#  - no parameters
################################################################################

_count_mem_32:
        stm    %r6,%r15,24(%r15)
        basr   %r13,0                         # base register
0:      s      %r15,.Lc96-0b(%r13)            # create stack frame


        lhi    %r12, SCCB_START               # setup sccb
        lhi    %r8,0x1000
        sth    %r8,0(%r12)
        lhi    %r8,0x80
        stc    %r8,5(%r12)



        l     %r2,.Lrcp-0b(%r13)     # Read SCP forced command word
.Lservicecall:

        stctl %r0, %r0,.Lcr-0b(%r13) # get cr0
        la    %r1,0x200              # set bit 22
        o     %r1,.Lcr-0b(%r13)      # or old cr0 with r1
        st    %r1,.Lcr-0b(%r13)
        lctl  %r0, %r0,.Lcr-0b(%r13) # load modified cr0

        mvc   __LC_EXT_NEW_PSW_S390(8),.Lpcext-0b(%r13) # set postcall psw
        la    %r1, .Lsclph-0b(%r13)
        a     %r1,__LC_EXT_NEW_PSW_S390+4    # set handler
        st    %r1,__LC_EXT_NEW_PSW_S390+4

	lr    %r1,%r12
        .insn rre,0xb2200000,%r2,%r1    # service call
        ipm   %r1
        srl   %r1,28                    # get cc code
        xr    %r3, %r3
        chi   %r1,3
        be    .Lmem_panik-0b(%r13)      # leave
        chi   %r1,2
        be    .Lservicecall-0b(%r13)
.Lwait:
	stosm .Lpmask-0b(%r13),0x01     # authorize ext interrupts
        lpsw  .Lwaitsclp-0b(%r13)
.Lsclph:
	stnsm .Lpmask-0b(%r13),0xfe     # disable ext interrupts again
        lh    %r1,__LC_EXT_INT_CODE_ADDR
        chi   %r1,0x2401                # check external interrupt code
        bne   .Lwait-0b(%r13)           # ignore all no service interrupts 
	lh    %r1,SCCBR_OFF(%r12)
        chi   %r1,0x10                  # 0x0010 is the sucess code
        je    .Lprocsccb                # let's process the sccb
        chi   %r1,0x1f0
        bne   .Lmem_panik-0b(%r13)      # unhandled error code
        c     %r2, .Lrcp-0b(%r13)       # Did we try Read SCP forced
        bne   .Lmem_panik-0b(%r13)      # if no, give up
        l     %r2, .Lrcp2-0b(%r13)      # try with Read SCP
        b     .Lservicecall-0b(%r13)
.Lprocsccb:
	lhi   %r1,0
        icm   %r1,3,SCPINCR1_OFF(%r12)  # use this one if != 0
        jnz   .Lscnd
	lhi   %r1,0x800                 # otherwise report 2GB
.Lscnd:
        lhi   %r3,0x800                 # limit reported memory to 2GB
	cr    %r1,%r3
	jl    .Lno2gb
	lr    %r1,%r3
.Lno2gb:
        xr    %r3,%r3                   # same logic
        ic    %r3,SCPA1_OFF(%r12)
        chi   %r3,0x00
        jne   .Lcompmem
        l     %r3,SCPA2_OFF(%r12)
.Lcompmem:
        mr    %r2,%r1                   # mem in MB on 128-bit
        l     %r1,.Lonemb-0b(%r13)
        mr    %r2,%r1                   # mem size in bytes in %r3

        st    %r3,.Ldh_real_mem_size+4-0b(%r13)
        cl    %r6,.Lmem_upper_limit+4-0b(%r13) # check if we have an upper limit
        bl    .Lsavemem-0b(%r13)
        l     %r3,.Lmem_upper_limit+4-0b(%r13) # upper mem limit set -> use it!
.Lsavemem:
        st    %r3,.Ldh_mem_size+4-0b(%r13)     # store memory size
        st    %r3,.Ldh_mem_end+4-0b(%r13)      # store memory end
        srl   %r3,12                           # calculate page count (/ 4096)
        st    %r3,.Ldh_num_pages-0b(%r13)      # store page count

        lm    %r6,%r15,120(%r15)
        br    %r14
.Lmem_panik:
        lhi    %r2,EMEMCOUNT
        bas    %r4,_panik_32-0b(%r13)
.Lpmask:
        .byte 0
.align 8
.Lpcext:.long  0x00080000,0x80000000
.Lcr:
        .long 0x00                      # place holder for cr0
.Lwaitsclp:
        .long 0x020A0000
        .long .Lsclph
.Lrcp:
        .int 0x00120001                 # Read SCP forced code
.Lrcp2:
        .int 0x00020001                 # Read SCP code
.Lonemb:
        .int 0x100000


# expand Macros

   dump_common_store_status_32

# data 

.align 4
.Ldump_mem_32:       .long _dump_mem_32      # address of function

.endm    /* dump_common_fn_32 */


################################################################################
# MACRO: dump_idal_32
# - _create_ida_list_32
################################################################################

.macro dump_idal_32
################################################################################
# This function creates a indirect data addressing list
# Parameters:
#  -r2:  start address
#  -r3:  length
#  -r4:  address of ida list
################################################################################
_create_ida_list_32:
        stm    %r6,%r15,24(%r15)
        basr   %r13,0                         # base register
0:      s      %r15,.Lc96-0b(%r13)            # create stack frame

        # setup new pgc psw for finding invalid pages

        lhi     %r9,0
        mvc     .Lold_pgc_psw-0b(8,%r13),104(%r9)      # save old psw
        mvc     104(8,%r9),.Lpage_invalid_psw-0b(%r13) # setup pgm check new

        lr      %r14,%r4                      # copy address of ida list
        lr      %r11,%r2                      # copy start address
        lr      %r9,%r3                       # copy length
        ar      %r9,%r11                      # compute end address
1:
        l       %r7,0(%r11)                   # test page
        lr      %r7,%r11
        b       .Lpage_ok-0b(%r13)
.Lpage_invalid:
        lhi     %r7,ZERO_PAGE_START
.Lpage_ok:
        st      %r7,0(%r14)                   # store address
        ahi     %r14,4                        # update ida pointer
        ahi     %r11,2048                     # update data address
        clr     %r11,%r9                      # enough ?
        bl      1b-0b(%r13)                   # branch if r11 < r9

	lhi     %r9,0
        mvc     104(8,%r9),.Lold_pgc_psw-0b(%r13) # restore pgm check new
        lm      %r6,%r15,120(%r15)
        br      %r14                          # return to caller

.Lpage_invalid_psw:
        .long 0x00080000,0x80000000 + .Lpage_invalid
.Lold_pgc_psw:
        .long 0x0,0x0

.endm /* dump_idal_32 */

#endif /* __s390x__ */




################################################################################
# MACRO: dump_common_store_status_32
# - _store_status_32
# - _copy_lowcore_32
################################################################################


.macro dump_common_store_status_32

################################################################################
# store status of all cpus in their lowcores
#  - no parameters
################################################################################

_store_status_32:
        stm   %r6,%r15,24(%r15)
        basr  %r13,0                          # base register
0:      s     %r15,.Lc96-0b(%r13)
        la    %r7,0x0                         # base register for 0 page

        ######## move lowcore info (assume user has made store  ########
        ######## status) to prefix-page                         ########

        bas   %r14,_copy_lowcore_32-0b(%r13)

        ######## stop all cpus and store status in prefix pages ########

        la    %r8,0                           # first cpu
        stap  .Lcurrent_cpu+2-0b(%r13)        # store current cpu address

1:
        cl    %r8,.Lcurrent_cpu-0b(%r13)      # is ipl cpu ?
        be    4f-0b(%r13)                     # if yes get next cpu
2:
        lr    %r9,%r7
        sigp  %r9,%r8,0x9                     # stop & store status of cpu
        bc   8,3f-0b(%r13)                    # accepted
        bc   4,4f-0b(%r13)                    # status stored: next cpu
        bc   2,2b-0b(%r13)                    # busy:          try again
        bc   1,4f-0b(%r13)                    # not op:        next cpu
3:
        bas   %r14,_copy_lowcore_32-0b(%r13)
4:
        la    %r8,1(%r8)                      # next cpu (r8 +=1)
        cl    %r8,.Llast_cpu-0b(%r13)         # is last possible cpu ?
        bl    1b-0b(%r13)                     # jump if not last cpu
.Lstore_status_exit:
        lm      %r6,%r15,120(%r15)
        br      %r14                          # return to caller
.Lcurrent_cpu:
        .long 0x0
.Llast_cpu:
        .long 0x0000ffff

################################################################################
# copy lowcore to prefix page
#  - no parameters
################################################################################

_copy_lowcore_32:
        stm    %r6,%r15,24(%r15)
        basr   %r13,0                         # base register
0:      s      %r15,.Lc96-0b(%r13)

        la     %r2,0                          # base register for lowcore
        l      %r3,0x108(%r2)                 # get prefix page from lowcore

        ###### check if lowcore address looks valid ######

        cl    %r3,.Llinux_start-0b(%r13)      # looks valid ?
        bl    .Lcpy_locore_exit-0b(%r13)      # if < linux-start addr
        l     %r6,.Lpage_align-0b(%r13)       # check page alignment
        nr    %r3,%r6
        cl    %r3,0x108(%r2)
        bnz   .Lcpy_locore_exit-0b(%r13)      # if not page aligned

        ###### copy lowcore                         ######

        # |-----------------------------------------------------------|
        # | Decimal |  Length   | Data                                |
        # | Address |  in Bytes |                                     |
        # |_________|___________|_____________________________________|
        # | 212     | 4         | Extended save area address          |
        # | 216     | 8         | CPU timer                           |
        # | 224     | 8         | Clock comparator                    |
        # | 256     | 8         | Current PSW                         |
        # | 264     | 4         | Prefix register                     |
        # | 288     | 64        | Access registers 0 through 15       |
        # | 352     | 32        | Floating-point registers 0 through 6|
        # | 384     | 64        | General registers 0 through 15      |
        # | 448     | 64        | Control registers 0 through 15      |
        # |_________|___________|_____________________________________|

        mvc   212(20,%r3),212(%r2)
        mvc   256(12,%r3),256(%r2)
        mvc   288(224,%r3),288(%r2)

.Lcpy_locore_exit:
        lm    %r6,%r15,120(%r15)
        br    %r14                            # return to caller
.Lpage_align:
        .long -4096
.Llinux_start:
        .long  0x10000

.endm /* dump_common_store_status_32 */
