mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-07-26 02:29:51 +02:00
1390 lines
54 KiB
C
1390 lines
54 KiB
C
/* DIAGNOSE.C (C) Copyright Roger Bowler, 2000-2012 */
|
|
/* ESA/390 Diagnose Functions */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
/* z/Architecture support - (C) Copyright Jan Jaeger, 1999-2012 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* This module implements miscellaneous diagnose functions */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Additional credits: */
|
|
/* Hercules-specific diagnose calls by Jay Maynard. */
|
|
/* Set/reset bad frame indicator call by Jan Jaeger. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#include "hstdinc.h"
|
|
|
|
#define _DIAGNOSE_C_
|
|
#define _HENGINE_DLL_
|
|
|
|
#include "hercules.h"
|
|
#include "opcode.h"
|
|
#include "inline.h"
|
|
|
|
#ifndef COMPILE_THIS_ONLY_ONCE
|
|
#define COMPILE_THIS_ONLY_ONCE
|
|
|
|
#undef ATTRIBUTE_PACKED
|
|
#if defined(_MSVC_)
|
|
#pragma pack(push)
|
|
#pragma pack(1)
|
|
#define ATTRIBUTE_PACKED
|
|
#else
|
|
#define ATTRIBUTE_PACKED __attribute__((packed))
|
|
#endif
|
|
|
|
/* Start of headers and defines copied from S390-tools-2.14.0 include/boot/ipl.h */
|
|
/* The copied headers have been modified */
|
|
|
|
/* IPL Parameter List header */
|
|
struct ipl_pl_hdr {
|
|
uint32_t len;
|
|
uint8_t flags;
|
|
uint8_t reserved1[2];
|
|
uint8_t version;
|
|
} ATTRIBUTE_PACKED;
|
|
#define IPL_FLAG_SECURE 0x40
|
|
#define IPL_MAX_SUPPORTED_VERSION 0
|
|
|
|
// /* IPL Parameter Block header */
|
|
// struct ipl_pb_hdr {
|
|
// uint32_t len;
|
|
// uint8_t pbt;
|
|
// } ATTRIBUTE_PACKED;
|
|
|
|
/* IPL Parameter Block 0 with common fields */
|
|
struct ipl_pb0_common {
|
|
uint32_t len;
|
|
uint8_t pbt;
|
|
uint8_t flags;
|
|
uint8_t reserved1[2];
|
|
uint8_t loadparm[8];
|
|
uint8_t reserved2[84];
|
|
} ATTRIBUTE_PACKED;
|
|
#define IPL_RB_COMPONENT_FLAG_SIGNED 0x80
|
|
#define IPL_RB_COMPONENT_FLAG_VERIFIED 0x40
|
|
/* Following define values copied from linux-5.8.17 arch/s390/include/uapi/asmi/ipl.h */
|
|
#define IPL_TYPE_UNKNOWN 1
|
|
#define IPL_TYPE_CCW 2
|
|
#define IPL_TYPE_FCP 4
|
|
#define IPL_TYPE_FCP_DUMP 8
|
|
#define IPL_TYPE_NSS 16
|
|
#define IPL_TYPE_NVME 32
|
|
/* Following define value copied from S390-tools-2.14.0 include/boot/ipl.h */
|
|
#define IPL_TYPE_PV 0x5
|
|
|
|
/* Following define value copied from linux-7.1-rc3 linux/arch/s390/include/uapi/asm/ipl.h */
|
|
#define IPL_PB0_FLAG_LOADPARM 0x80
|
|
|
|
/* IPL Parameter Block 0 for CCW */
|
|
struct ipl_pb0_ccw {
|
|
uint32_t len;
|
|
uint8_t pbt;
|
|
uint8_t flags;
|
|
uint8_t reserved1[2];
|
|
uint8_t loadparm[8];
|
|
uint8_t reserved2[84];
|
|
uint8_t reserved3[1];
|
|
uint8_t reserved6 : 5,
|
|
ssid : 3;
|
|
uint16_t devno;
|
|
uint8_t vm_flags;
|
|
uint8_t reserved4[3];
|
|
uint32_t vm_parm_len;
|
|
uint8_t nss_name[8];
|
|
uint8_t vm_parm[64];
|
|
uint8_t reserved5[8];
|
|
} ATTRIBUTE_PACKED;
|
|
|
|
// /* IPL Parameter Block 0 for FCP */
|
|
// struct ipl_pb0_fcp {
|
|
// uint32_t len;
|
|
// uint8_t pbt;
|
|
// uint8_t reserved1[3];
|
|
// uint8_t loadparm[8];
|
|
// uint8_t reserved2[304];
|
|
// uint8_t opt;
|
|
// uint8_t reserved3[3];
|
|
// uint8_t cssid;
|
|
// uint8_t reserved4[1];
|
|
// uint8_t devno;
|
|
// uint8_t reserved5[4];
|
|
// uint64_t wwpn;
|
|
// uint64_t lun;
|
|
// uint32_t bootprog;
|
|
// uint8_t reserved6[12];
|
|
// uint64_t br_lba;
|
|
// uint32_t scp_data_len;
|
|
// uint8_t reserved7[260];
|
|
// uint8_t scp_data[];
|
|
// } ATTRIBUTE_PACKED;
|
|
|
|
// /* Structure must not have any padding */
|
|
// struct ipl_pb0_pv_comp {
|
|
// uint64_t tweak_pref;
|
|
// uint64_t addr;
|
|
// uint64_t len;
|
|
// };
|
|
// STATIC_ASSERT(sizeof(struct ipl_pb0_pv_comp) == 3 * 8)
|
|
|
|
// /* IPL Parameter Block 0 for PV */
|
|
// struct ipl_pb0_pv {
|
|
// uint32_t len;
|
|
// uint8_t pbt;
|
|
// uint8_t reserved1[3];
|
|
// uint8_t loadparm[8];
|
|
// uint8_t reserved2[84];
|
|
// uint8_t reserved3[3];
|
|
// uint8_t version;
|
|
// uint8_t reserved4[4];
|
|
// uint32_t num_comp;
|
|
// uint64_t pv_hdr_addr;
|
|
// uint64_t pv_hdr_size;
|
|
// struct ipl_pb0_pv_comp components[];
|
|
// } ATTRIBUTE_PACKED;
|
|
// #define IPL_PARM_BLOCK_VERSION 0x1
|
|
|
|
struct ipl_parameter_block {
|
|
struct ipl_pl_hdr hdr;
|
|
union {
|
|
// struct ipl_pb_hdr pb0_hdr;
|
|
struct ipl_pb0_common common;
|
|
struct ipl_pb0_ccw ccw;
|
|
// struct ipl_pb0_fcp fcp;
|
|
// struct ipl_pb0_pv pv;
|
|
// char raw[PAGE_SIZE - sizeof(struct ipl_pl_hdr)];
|
|
};
|
|
} ATTRIBUTE_PACKED;
|
|
|
|
/* End of headers and defines copied from S390-tools-2.14.0 include/boot/ipl.h */
|
|
|
|
#if defined(_MSVC_)
|
|
#pragma pack(pop)
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/* Diagnose 308 function subcodes */
|
|
#define DIAG308_CLEAR_RESET 0
|
|
#define DIAG308_START_KERNEL 1
|
|
#define DIAG308_LOAD_NORMAL_RESET 1
|
|
#define DIAG308_REL_HSA 2
|
|
#define DIAG308_LOAD_CLEAR 3
|
|
#define DIAG308_LOAD_NORMAL_DUMP 4
|
|
#define DIAG308_SET 5
|
|
#define DIAG308_STORE 6
|
|
#define DIAG308_LOAD_NORMAL 7
|
|
#define DIAG308_SET_PV 8
|
|
#define DIAG308_UNPACK_PV 10
|
|
|
|
/* Diagnose 308 return codes */
|
|
#define DIAG308_RC_OK 0x0001
|
|
#define DIAG308_RC_NOCONFIG 0x0102
|
|
|
|
// predefine ipl.c s390_common_load_finish function here
|
|
// so it can be called from diagnose 308 subcode 1
|
|
int s390_common_load_finish(REGS *regs);
|
|
|
|
#endif // COMPILE_THIS_ONLY_ONCE
|
|
|
|
#ifndef STOP_CPUS_AND_IPL
|
|
#define STOP_CPUS_AND_IPL
|
|
#if defined( FEATURE_PROGRAM_DIRECTED_REIPL )
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Within diagnose 0x308 (re-ipl) a thread is started with the next code. */
|
|
/*---------------------------------------------------------------------------*/
|
|
static void *stop_cpus_and_ipl(int *ipltype)
|
|
{
|
|
int i;
|
|
char iplcmd[256];
|
|
int cpustates;
|
|
CPU_BITMAP mask;
|
|
|
|
panel_command("stopall");
|
|
|
|
WRMSG(HHC01900, "I");
|
|
sprintf(iplcmd, "%s %03X", ipltype, sysblk.ipldev);
|
|
do
|
|
{
|
|
OBTAIN_INTLOCK(NULL);
|
|
cpustates = CPUSTATE_STOPPED;
|
|
mask = sysblk.started_mask;
|
|
for(i = 0; mask; i++)
|
|
{
|
|
if(mask & 1)
|
|
{
|
|
WRMSG(HHC01901, "I", PTYPSTR(i), i);
|
|
if(IS_CPU_ONLINE(i) && sysblk.regs[i]->cpustate != CPUSTATE_STOPPED)
|
|
cpustates = sysblk.regs[i]->cpustate;
|
|
}
|
|
mask >>= 1;
|
|
}
|
|
RELEASE_INTLOCK(NULL);
|
|
if(cpustates != CPUSTATE_STOPPED)
|
|
{
|
|
WRMSG(HHC01902, "I");
|
|
SLEEP(1);
|
|
}
|
|
}
|
|
while(cpustates != CPUSTATE_STOPPED);
|
|
|
|
panel_command(iplcmd);
|
|
|
|
return NULL;
|
|
}
|
|
#endif /* defined( FEATURE_PROGRAM_DIRECTED_REIPL ) */
|
|
#endif // STOP_CPUS_AND_IPL
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Diagnose instruction */
|
|
/*-------------------------------------------------------------------*/
|
|
void ARCH_DEP( diagnose_call )( REGS* regs, int r1, int r3, int b2, VADR effective_addr2 )
|
|
{
|
|
#ifdef FEATURE_HERCULES_DIAGCALLS
|
|
U32 n; /* 32-bit operand value */
|
|
#endif /*FEATURE_HERCULES_DIAGCALLS*/
|
|
U32 code;
|
|
|
|
code = effective_addr2;
|
|
|
|
switch(code) {
|
|
|
|
|
|
#if defined(FEATURE_IO_ASSIST)
|
|
case 0x002:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 002: Update Interrupt Interlock Control Bit in PMCW */
|
|
/*---------------------------------------------------------------*/
|
|
|
|
ARCH_DEP(diagnose_002) (regs, r1, r3);
|
|
|
|
break;
|
|
#endif
|
|
|
|
|
|
case 0x01F:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 01F: Power Off */
|
|
/*---------------------------------------------------------------*/
|
|
|
|
/* If diag8opt is not enabled then we are not allowed
|
|
* to manipulate the real machine i.e. hercules itself
|
|
*/
|
|
if(!(sysblk.diag8opt & DIAG8CMD_ENABLE))
|
|
ARCH_DEP(program_interrupt)(regs, PGM_SPECIFICATION_EXCEPTION);
|
|
|
|
/* The poweroff diagnose is only valid on the 9221 */
|
|
if (sysblk.cpumodel != 0x9221
|
|
/* and r1/r3 must contain C'POWEROFF' in EBCDIC */
|
|
|| regs->GR_L(r1) != 0xD7D6E6C5
|
|
|| regs->GR_L(r3) != 0xD9D6C6C6)
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
|
|
regs->cpustate = CPUSTATE_STOPPING;
|
|
ON_IC_INTERRUPT(regs);
|
|
|
|
/* Release the configuration */
|
|
do_shutdown();
|
|
|
|
/* Power Off: exit hercules */
|
|
exit(0);
|
|
|
|
#if defined(FEATURE_HYPERVISOR) || defined(FEATURE_EMULATE_VM)
|
|
case 0x044:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 044: Voluntary Time Slice End */
|
|
/*---------------------------------------------------------------*/
|
|
ARCH_DEP(scpend_call) ();
|
|
break;
|
|
#endif
|
|
|
|
|
|
#ifdef FEATURE_MSSF_CALL
|
|
case 0x080:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 080: MSSF Call */
|
|
/*---------------------------------------------------------------*/
|
|
regs->psw.cc = ARCH_DEP(mssf_call) (r1, r3, regs);
|
|
break;
|
|
#endif /*FEATURE_MSSF_CALL*/
|
|
|
|
|
|
#if defined(FEATURE_HYPERVISOR) || defined(FEATURE_EMULATE_VM)
|
|
case 0x09C:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 09C: Voluntary Time Slice End With Target CPU */
|
|
/*---------------------------------------------------------------*/
|
|
ARCH_DEP(scpend_call) (); // (treat same as DIAG X'44')
|
|
break;
|
|
#endif
|
|
|
|
|
|
#if defined(FEATURE_HYPERVISOR)
|
|
case 0x204:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 204: LPAR RMF Interface */
|
|
/*---------------------------------------------------------------*/
|
|
ARCH_DEP(diag204_call) (r1, r3, regs);
|
|
regs->psw.cc = 0;
|
|
break;
|
|
|
|
case 0x224:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 224: CPU Names */
|
|
/*---------------------------------------------------------------*/
|
|
ARCH_DEP(diag224_call) (r1, r3, regs);
|
|
regs->psw.cc = 0;
|
|
break;
|
|
#endif /*defined(FEATURE_HYPERVISOR)*/
|
|
|
|
#if 0
|
|
case 0x21C:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 21C: ???? */
|
|
/*---------------------------------------------------------------*/
|
|
/*INCOMPLETE*/
|
|
regs->psw.cc = 0;
|
|
break;
|
|
#endif
|
|
|
|
#if 0
|
|
case 0x288;:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 288: Control Virtual Machine Time Bomb */
|
|
/*---------------------------------------------------------------*/
|
|
regs->psw.cc = ARCH_DEP(vm_timebomb) (r1, r3, regs);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef FEATURE_EMULATE_VM
|
|
case 0x000:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 000: Store Extended Identification Code */
|
|
/*---------------------------------------------------------------*/
|
|
ARCH_DEP(extid_call) (r1, r3, regs);
|
|
break;
|
|
|
|
case 0x008:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 008: Virtual Console Function */
|
|
/*---------------------------------------------------------------*/
|
|
/* If diag8opt is not enabled then we are not allowed
|
|
* to manipulate the real machine i.e. hercules itself
|
|
*/
|
|
if(!(sysblk.diag8opt & DIAG8CMD_ENABLE))
|
|
ARCH_DEP(program_interrupt)(regs, PGM_SPECIFICATION_EXCEPTION);
|
|
|
|
/* Process CP command and set condition code */
|
|
regs->psw.cc = ARCH_DEP(cpcmd_call) (r1, r3, regs);
|
|
break;
|
|
|
|
case 0x00C:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 00C: Pseudo Timer */
|
|
/*---------------------------------------------------------------*/
|
|
ARCH_DEP(pseudo_timer) (code, r1, r3, regs);
|
|
break;
|
|
|
|
case 0x024:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 024: Device Type and Features */
|
|
/*---------------------------------------------------------------*/
|
|
regs->psw.cc = ARCH_DEP(diag_devtype) (r1, r3, regs);
|
|
break;
|
|
|
|
case 0x05C:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 05C: Error Message Editing */
|
|
/*---------------------------------------------------------------*/
|
|
/* This function is implemented as a no-operation */
|
|
regs->psw.cc = 0;
|
|
break;
|
|
|
|
case 0x060:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 060: Virtual Machine Storage Size */
|
|
/*---------------------------------------------------------------*/
|
|
/* Load main storage size in bytes into R1 register */
|
|
regs->GR_L(r1) = regs->mainlim + 1;
|
|
break;
|
|
|
|
case 0x064:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 064: Named Saved Segment Manipulation */
|
|
/*---------------------------------------------------------------*/
|
|
/* Return code 44 cond code 2 means segment does not exist */
|
|
regs->GR_L(r3) = 44;
|
|
regs->psw.cc = 2;
|
|
break;
|
|
|
|
case 0x0A4:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 0A4: Synchronous I/O (Standard CMS Blocksize) */
|
|
/*---------------------------------------------------------------*/
|
|
regs->psw.cc = ARCH_DEP(syncblk_io) (r1, r3, regs);
|
|
// logmsg ("Diagnose X\'0A4\': CC=%d, R15=%8.8X\n", /*debug*/
|
|
// regs->psw.cc, regs->GR_L(15)); /*debug*/
|
|
break;
|
|
|
|
case 0x0A8:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 0A8: Synchronous General I/O */
|
|
/*---------------------------------------------------------------*/
|
|
regs->psw.cc = ARCH_DEP(syncgen_io) (r1, r3, regs);
|
|
// logmsg ("Diagnose X\'0A8\': CC=%d, R15=%8.8X\n", /*debug*/
|
|
// regs->psw.cc, regs->GR_L(15)); /*debug*/
|
|
break;
|
|
|
|
case 0x0B0:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 0B0: Access Re-IPL Data */
|
|
/*---------------------------------------------------------------*/
|
|
ARCH_DEP(access_reipl_data) (r1, r3, regs);
|
|
break;
|
|
|
|
case 0x0DC:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 0DC: Control Application Monitor Record Collection */
|
|
/*---------------------------------------------------------------*/
|
|
/* This function is implemented as a no-operation */
|
|
regs->GR_L(r3) = 0;
|
|
regs->psw.cc = 0;
|
|
break;
|
|
|
|
case 0x210:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 210: Retrieve Device Information */
|
|
/*---------------------------------------------------------------*/
|
|
regs->psw.cc = ARCH_DEP(device_info) (r1, r3, regs);
|
|
break;
|
|
|
|
case 0x214:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 214: Pending Page Release */
|
|
/*---------------------------------------------------------------*/
|
|
regs->psw.cc = ARCH_DEP(diag_ppagerel) (r1, r3, regs);
|
|
break;
|
|
|
|
|
|
case 0x220:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 220: TOD Epoch */
|
|
/*---------------------------------------------------------------*/
|
|
ODD_CHECK(r3, regs);
|
|
|
|
switch(regs->GR_L(r1))
|
|
{
|
|
case 0:
|
|
/* Obtain TOD features */
|
|
regs->GR_L(r3) =0xc0000000;
|
|
regs->GR_L(r3+1)=0x00000000;
|
|
break;
|
|
case 1:
|
|
/* Obtain TOD offset to real TOD in R2, R2+1 */
|
|
regs->GR_L(r3) = (regs->tod_epoch >> 24) & 0xFFFFFFFF;
|
|
regs->GR_L(r3+1)= (regs->tod_epoch << 8) & 0xFFFFFFFF;
|
|
break;
|
|
default:
|
|
ARCH_DEP(program_interrupt)(regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
break;
|
|
|
|
|
|
case 0x23C:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 23C: Address Space Services */
|
|
/*---------------------------------------------------------------*/
|
|
/* This function is implemented as a no-operation */
|
|
regs->GR_L(r3) = 0;
|
|
break;
|
|
|
|
|
|
#if defined(FEATURE_VM_BLOCKIO)
|
|
case 0x250:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 250: Standardized Block I/O */
|
|
/*---------------------------------------------------------------*/
|
|
regs->psw.cc = ARCH_DEP(vm_blockio) (r1, r3, regs);
|
|
break;
|
|
#endif /*defined(FEATURE_VM_BLOCKIO)*/
|
|
|
|
|
|
case 0x260:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 260: Access Certain Virtual Machine Information */
|
|
/*---------------------------------------------------------------*/
|
|
ARCH_DEP(vm_info) (r1, r3, regs);
|
|
break;
|
|
|
|
case 0x264:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 264: CP Communication */
|
|
/*---------------------------------------------------------------*/
|
|
/* This function is implemented as a no-operation */
|
|
PTT_ERR("*DIAG264",regs->GR_L(r1),regs->GR_L(r3),regs->psw.IA_L);
|
|
regs->psw.cc = 0;
|
|
break;
|
|
|
|
case 0x270:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 270: Pseudo Timer Extended */
|
|
/*---------------------------------------------------------------*/
|
|
ARCH_DEP(pseudo_timer) (code, r1, r3, regs);
|
|
break;
|
|
|
|
case 0x274:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 274: Set Timezone Interrupt Flag */
|
|
/*---------------------------------------------------------------*/
|
|
/* This function is implemented as a no-operation */
|
|
PTT_ERR("*DIAG274",regs->GR_L(r1),regs->GR_L(r3),regs->psw.IA_L);
|
|
regs->psw.cc = 0;
|
|
break;
|
|
#endif /*FEATURE_EMULATE_VM*/
|
|
|
|
#define DIAG308_DEBUG false
|
|
|
|
case 0x308:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose 308: IPL functions */
|
|
/*---------------------------------------------------------------*/
|
|
/* DIAG r1,r3,0x308 */
|
|
/*---------------------------------------------------------------*/
|
|
/* On entry */
|
|
/* R3 Function subcode */
|
|
/* R1 R1 must be an even numbered register defining an */
|
|
/* even-odd pair */
|
|
/* r1 (even) - an optional argument depending on subcode */
|
|
/* r1+1 (odd) - return code */
|
|
/*---------------------------------------------------------------*/
|
|
/* Note: Incomplete experimental implementation */
|
|
/*---------------------------------------------------------------*/
|
|
/* Note: Linux source and experimentation with s390 Ubuntu 26.04 */
|
|
/* was used to develop this version of Diag 308. */
|
|
/* Referenced Linux source was 7.1-rc3, */
|
|
/* s390-tools was v2.42.0 */
|
|
/* */
|
|
/* Linux was used to test */
|
|
/* DIAG308_START_KERNEL subcode 1 */
|
|
/* DIAG308_LOAD_CLEAR subcode 3 */
|
|
/* DIAG308_LOAD_NORMAL_DUMP subcode 4 */
|
|
/* DIAG308_SET subcode 5 */
|
|
/* DIAG308_STORE subcode 6 */
|
|
/* */
|
|
/* Other subcodes were not observed. */
|
|
/* */
|
|
/*---------------------------------------------------------------*/
|
|
/* Note: Linux source indicates that CC is modified, but Linux */
|
|
/* only uses the return code. CC is not modified. */
|
|
/*---------------------------------------------------------------*/
|
|
{
|
|
int rc = DIAG308_RC_OK; /* rc for diag */
|
|
int cpu = regs->cpuad; /* running on this cpu */
|
|
int clear = false; /* not clear IPL */
|
|
int subcode = regs->GR_L(r3); /* subcode */
|
|
|
|
if ( DIAG308_DEBUG )
|
|
{
|
|
logmsg(">>>> DIAG308: subcode=%d, cpu=%d\n", subcode, cpu);
|
|
}
|
|
|
|
PTT_ERR("*DIAG308",regs->GR_G(r1),regs->GR_G(r3),regs->psw.IA_L);
|
|
|
|
/* some validation */
|
|
/* Must be in ESA/390 or z/arch mode */
|
|
if( regs->arch_mode == ARCH_370_IDX )
|
|
ARCH_DEP(program_interrupt)(regs, PGM_SPECIFICATION_EXCEPTION);
|
|
|
|
/* Program check if running problem state. */
|
|
PRIV_CHECK(regs);
|
|
|
|
/* Program check if Rx is not an even register number */
|
|
if ( r1 & 1 )
|
|
{
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
|
|
/* clear on ipl? */
|
|
if ( subcode == DIAG308_CLEAR_RESET ||
|
|
subcode == DIAG308_LOAD_CLEAR )
|
|
{
|
|
clear = true;
|
|
}
|
|
|
|
switch( subcode )
|
|
{
|
|
case DIAG308_START_KERNEL:
|
|
/*-------------------------------------------------------*/
|
|
/* DIAG308_START_KERNEL: Subcode 1 */
|
|
/* DIAG308_LOAD_NORMAL_RESET: Subcode 1 (new name) */
|
|
/*-------------------------------------------------------*/
|
|
/* From Linux: ZIPL boot loader has loaded the kernel */
|
|
/* and need to start the kernel. An ESA/390 PSW is */
|
|
/* provided at address 0. CPU reset et al is required, */
|
|
/* similar to a normal IPL. Just use common_load_begin */
|
|
/* and s390_common_load_finish from ipl.c. */
|
|
/*-------------------------------------------------------*/
|
|
/* On entry */
|
|
/* r1 is not used */
|
|
/*-------------------------------------------------------*/
|
|
{
|
|
DBLWRD saved_iplpsw;
|
|
|
|
if ( DIAG308_DEBUG )
|
|
{
|
|
logmsg(">>>> DIAG308_START_KERNEL: enter clear=%d\n", clear);
|
|
logmsg(">>>> DIAG308_START_KERNEL: start 'common_load_begin'\n");
|
|
}
|
|
|
|
/* ESA/390 psw should be at address zero */
|
|
/* save */
|
|
memcpy( saved_iplpsw, regs->psa->iplpsw, sizeof( DBLWRD ) );
|
|
|
|
OBTAIN_INTLOCK( NULL );
|
|
{
|
|
/* Get started */
|
|
if ( ARCH_DEP( common_load_begin )( cpu, clear ) != 0 )
|
|
{
|
|
rc = DIAG308_RC_NOCONFIG;
|
|
}
|
|
}
|
|
RELEASE_INTLOCK( NULL );
|
|
|
|
/* NOTE: run_cpu() will allocate a TXF page map */
|
|
/* make sure that it is freed, if there is one */
|
|
if ( DIAG308_DEBUG )
|
|
logmsg(">>>> DIAG308_START_KERNEL: release txf_pagemap\n");
|
|
|
|
if ( regs->txf_pagesmap->altpageaddr )
|
|
{
|
|
TXF_FREEMAP( regs );
|
|
}
|
|
|
|
/* ESA/390 psw should be at address zero */
|
|
/* restore */
|
|
memcpy( regs->psa->iplpsw, saved_iplpsw, sizeof( DBLWRD ) );
|
|
|
|
if ( DIAG308_DEBUG )
|
|
logmsg(">>>> DIAG308_START_KERNEL: start 's390_common_load_finish'\n");
|
|
|
|
OBTAIN_INTLOCK( NULL );
|
|
{
|
|
/* finish; use ESA/390 version of common_load_finish */
|
|
/* as PSW is ESA/390 format */
|
|
if ( rc == DIAG308_RC_OK &&
|
|
s390_common_load_finish(regs) != 0
|
|
)
|
|
{
|
|
rc = DIAG308_RC_NOCONFIG;
|
|
}
|
|
}
|
|
RELEASE_INTLOCK( NULL );
|
|
|
|
regs->GR_L(r1+1) = rc;
|
|
|
|
if ( DIAG308_DEBUG )
|
|
logmsg(">>>> DIAG308_START_KERNEL: exit. rc=%d\n", rc);
|
|
|
|
break;
|
|
}
|
|
|
|
case DIAG308_LOAD_CLEAR: /* sub code 3 */
|
|
/*-------------------------------------------------------*/
|
|
/* Perform a clear IPL. */
|
|
/* same as DIAG308_LOAD_NORMAL_DUMP but a clear ipl */
|
|
/* On Ubuntu, a clear ccw ipl is requested by: */
|
|
/* echo 1 > /sys/firmware/reipl/ccw/clear */
|
|
/*-------------------------------------------------------*/
|
|
clear = true;
|
|
|
|
/* remove 'warning: this statement may fall through' with*/
|
|
// fall through
|
|
|
|
case DIAG308_LOAD_NORMAL_DUMP: /* sub code 4 */
|
|
/*-------------------------------------------------------*/
|
|
/* Perform an IPL. */
|
|
/* Note: only a CCW ipl is supported because DIAG308_SET */
|
|
/* only accepts a CCW IPL block */
|
|
/*-------------------------------------------------------*/
|
|
/* On entry */
|
|
/* r1 is not used */
|
|
/*-------------------------------------------------------*/
|
|
{
|
|
if ( DIAG308_DEBUG )
|
|
{
|
|
logmsg(">>>> DIAG308_LOAD_NORMAL_DUMP: enter. clear=%d\n", clear);
|
|
logmsg(">>>> DIAG308_LOAD_NORMAL_DUMP: start 'initial_cpu_reset'\n");
|
|
}
|
|
|
|
OBTAIN_INTLOCK( NULL );
|
|
{
|
|
/* Get started */
|
|
/* first: an inital cpu reset for this cpu */
|
|
if ( initial_cpu_reset( regs ) != 0 )
|
|
{
|
|
rc = DIAG308_RC_NOCONFIG;
|
|
}
|
|
}
|
|
RELEASE_INTLOCK( NULL );
|
|
|
|
if ( DIAG308_DEBUG )
|
|
logmsg(">>>> DIAG308_LOAD_NORMAL_DUMP: release txf_pagemap\n");
|
|
|
|
/* NOTE: run_cpu() will allocate a TXF page map */
|
|
/* make sure that it is freed, if there is one */
|
|
if ( regs->txf_pagesmap->altpageaddr )
|
|
{
|
|
TXF_FREEMAP( regs );
|
|
}
|
|
|
|
if ( DIAG308_DEBUG )
|
|
logmsg(">>>> DIAG308_LOAD_NORMAL_DUMP: start 'load_ipl'\n");
|
|
|
|
OBTAIN_INTLOCK( NULL );
|
|
{
|
|
/* second: do a ccw IPL */
|
|
if ( rc == DIAG308_RC_OK &&
|
|
load_ipl( sysblk.ipllcss, sysblk.ipldev, cpu, clear) != 0
|
|
)
|
|
{
|
|
rc = DIAG308_RC_NOCONFIG;
|
|
}
|
|
}
|
|
RELEASE_INTLOCK( NULL );
|
|
|
|
regs->GR_L(r1+1) = rc;
|
|
|
|
if ( DIAG308_DEBUG )
|
|
logmsg(">>>> DIAG308_LOAD_NORMAL_DUMP: exit. rc=%d\n", rc);
|
|
break;
|
|
}
|
|
|
|
case DIAG308_SET:
|
|
/*-------------------------------------------------------*/
|
|
/* DIAG308_SET: Subcode 5: Upload IPLB to PR/SM */
|
|
/*-------------------------------------------------------*/
|
|
/* set system ipllcs, ipldev and loadparm from */
|
|
/* a CCW IPL block */
|
|
/*-------------------------------------------------------*/
|
|
/* On entry */
|
|
/* r1 contains the real address of a 4K page */
|
|
/* which contains an IPL parameter block */
|
|
/*-------------------------------------------------------*/
|
|
{
|
|
RADR stgarea; /* Storage area real address */
|
|
struct ipl_parameter_block ipb;
|
|
U32 headsize;
|
|
U32 bodysize;
|
|
int lopasize;
|
|
|
|
if ( DIAG308_DEBUG )
|
|
logmsg(">>>> DIAG308_SET: enter\n");
|
|
|
|
/* Register Rx contains the real address of the storage area. */
|
|
if(regs->psw.amode64) {
|
|
stgarea = regs->GR_G(r1);
|
|
} else {
|
|
stgarea = regs->GR_L(r1);
|
|
}
|
|
|
|
/* Program check if the address contained in Rx */
|
|
/* is not on a 4K page boundary. */
|
|
if ( stgarea & 0xFFF)
|
|
{
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
|
|
/* Ensure that the 4K storage area is addressable. */
|
|
ARCH_DEP(validate_operand) (stgarea, USE_REAL_ADDR, 4095, ACCTYPE_READ, regs);
|
|
|
|
/* Get the IPL parameter block in real storage */
|
|
headsize = sizeof(struct ipl_pl_hdr);
|
|
bodysize = sizeof(struct ipl_pb0_ccw);
|
|
|
|
/* Fetch the IPL parameter block in real storage */
|
|
/* NOTE: fields are BIG ENDIAN; access values with CSWAP */
|
|
/* Note: vstorec copies a maximum of 256 bytes. */
|
|
ARCH_DEP(vfetchc) (&ipb, (headsize+bodysize-1), stgarea, USE_REAL_ADDR, regs);
|
|
|
|
/* Validate the IPL parameter block. */
|
|
/* validate structure */
|
|
if ( 0 ||
|
|
ipb.hdr.version > IPL_MAX_SUPPORTED_VERSION ||
|
|
CSWAP32(ipb.hdr.len) > (headsize + bodysize) ||
|
|
CSWAP32(ipb.ccw.len) != bodysize
|
|
)
|
|
{
|
|
WRMSG(HHC01960, "E", subcode, ipb.hdr.version, CSWAP32(ipb.hdr.len), CSWAP32(ipb.ccw.len) );
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
|
|
/* validate: only support CCW type IPL block*/
|
|
if ( ipb.ccw.pbt != IPL_TYPE_CCW)
|
|
{
|
|
WRMSG(HHC01961, "E", subcode, "IPL Block is not a CCW type block" );
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
|
|
/* LOADPARM? */
|
|
memset( &sysblk.loadparm, 0, sizeof(sysblk.loadparm) );
|
|
if ( (ipb.ccw.flags & IPL_PB0_FLAG_LOADPARM) == 0 )
|
|
{
|
|
if ( DIAG308_DEBUG )
|
|
logmsg(">>>> DIAG308_SET: LOADPARM flag not set in IPL block. ccw.flags: %2.2x\n", ipb.ccw.flags );
|
|
}
|
|
else
|
|
{
|
|
/* set LOADPARM */
|
|
if ( DIAG308_DEBUG )
|
|
logmsg(">>>> DIAG308_SET: IPLB ccw.flags: %2.2x, Loadparm= %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
|
|
ipb.ccw.flags,
|
|
ipb.common.loadparm[0], ipb.common.loadparm[1], ipb.common.loadparm[2], ipb.common.loadparm[3],
|
|
ipb.common.loadparm[4], ipb.common.loadparm[5], ipb.common.loadparm[6], ipb.common.loadparm[7] );
|
|
|
|
/* determine length of loadparm without trailing spaces */
|
|
for( lopasize = sizeof(ipb.common.loadparm); lopasize > 0; lopasize--)
|
|
{
|
|
if (ipb.common.loadparm[lopasize-1] != 0x40) /* EBCDIC space */
|
|
break;
|
|
}
|
|
|
|
if (lopasize > 0)
|
|
{
|
|
str_guest_to_host( ipb.common.loadparm, sysblk.loadparm, lopasize);
|
|
}
|
|
}
|
|
|
|
/* Set IPL device and subsystem ID */
|
|
STORE_HW(&sysblk.ipldev, ipb.ccw.devno);
|
|
sysblk.ipllcss = ipb.ccw.ssid;
|
|
|
|
if ( DIAG308_DEBUG )
|
|
logmsg(">>>> DIAG308_SET: Loadparm='%s', IPL lcss=%d, IPL device=%4.4X\n",
|
|
sysblk.loadparm, sysblk.ipllcss, sysblk.ipldev);
|
|
|
|
/* Successful */
|
|
regs->GR(r1+1) = DIAG308_RC_OK;
|
|
|
|
if ( DIAG308_DEBUG )
|
|
logmsg(">>>> DIAG308_SET: exit. rc=%d\n", rc);
|
|
|
|
break;
|
|
}
|
|
|
|
case DIAG308_STORE:
|
|
/*-------------------------------------------------------*/
|
|
/* DIAG308_STORE: Subcode 6: */
|
|
/* Retrieve current IPLB from PR/SM */
|
|
/*-------------------------------------------------------*/
|
|
/* On entry */
|
|
/* Rx Rx must be an even numbered register */
|
|
/* containing the real address of a 4K page */
|
|
/* aligned storage area into which the IPL */
|
|
/* parameter block will be copied. */
|
|
/* On return */
|
|
/* Rx+1 Contains the return code. */
|
|
/*-------------------------------------------------------*/
|
|
{
|
|
RADR stgarea; /* Storage area real address */
|
|
U32 headsize;
|
|
U32 bodysize;
|
|
struct ipl_parameter_block ipb;
|
|
int lopasize;
|
|
|
|
if ( DIAG308_DEBUG )
|
|
logmsg(">>>> DIAG308_STORE: enter\n");
|
|
|
|
/* Register Rx contains the real address of the storage area. */
|
|
if(regs->psw.amode64) {
|
|
stgarea = regs->GR_G(r1);
|
|
} else {
|
|
stgarea = regs->GR_L(r1);
|
|
}
|
|
|
|
/* Program check if */
|
|
/* the address contained in Rx is not on a 4K page boundary. */
|
|
if (stgarea & 0xFFF)
|
|
{
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
|
|
/* Ensure that the 4K storage area is addressable. */
|
|
ARCH_DEP(validate_operand) (stgarea, USE_REAL_ADDR, 4095, ACCTYPE_WRITE, regs);
|
|
|
|
/* Prepare the IPL parameter block. */
|
|
memset(&ipb, 0, sizeof(ipb));
|
|
|
|
/* Setup head and body values common to all. */
|
|
ipb.hdr.version = IPL_MAX_SUPPORTED_VERSION;
|
|
headsize = sizeof(ipb.hdr);
|
|
|
|
/* Setup head and body values specific to CCW. */
|
|
bodysize = sizeof(ipb.ccw);
|
|
STORE_FW(&ipb.hdr.len, (headsize + bodysize));
|
|
STORE_FW(&ipb.ccw.len, bodysize);
|
|
ipb.ccw.pbt = IPL_TYPE_CCW;
|
|
|
|
/* Set LOADPARM */
|
|
memset(ipb.ccw.loadparm, 0x40, sizeof(ipb.ccw.loadparm)); /* EBCDIC spaces */
|
|
lopasize = strlen(sysblk.loadparm);
|
|
if (lopasize)
|
|
{
|
|
str_host_to_guest(sysblk.loadparm, ipb.ccw.loadparm, lopasize);
|
|
ipb.ccw.flags |= IPL_PB0_FLAG_LOADPARM;
|
|
}
|
|
|
|
ipb.ccw.ssid = sysblk.ipllcss;
|
|
STORE_HW(&ipb.ccw.devno, sysblk.ipldev);
|
|
|
|
/* Set VM PARM, just to a 'hercules' iidentifier */
|
|
memset(ipb.ccw.vm_parm, 0x40, sizeof(ipb.ccw.vm_parm)); /* EBCDIC spaces */
|
|
// H E R C U L E S
|
|
STRLCPY(ipb.ccw.vm_parm, "\xC8\xC5\xD9\xC3\xE4\xD3\xC5\xE2"); /* just to show its us! */
|
|
ipb.ccw.vm_parm_len = 0; /* no vm parm; just our marker */
|
|
|
|
if ( DIAG308_DEBUG )
|
|
{
|
|
|
|
logmsg(">>>> DIAG308_STORE: IPLB ccw.flags: %2.2x, Loadparm= %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
|
|
ipb.ccw.flags,
|
|
ipb.common.loadparm[0], ipb.common.loadparm[1], ipb.common.loadparm[2], ipb.common.loadparm[3],
|
|
ipb.common.loadparm[4], ipb.common.loadparm[5], ipb.common.loadparm[6], ipb.common.loadparm[7] );
|
|
|
|
logmsg(">>>> DIAG308_STORE: IPLB ssid=%d, IPLB devno=%4.4X\n",
|
|
ipb.ccw.ssid, CSWAP16(ipb.ccw.devno) );
|
|
}
|
|
|
|
/* Store the IPL parameter block in real storage */
|
|
/* Note: vstorec copies a maximum of 256 bytes. */
|
|
ARCH_DEP(vstorec) (&ipb, (headsize+bodysize-1), stgarea, USE_REAL_ADDR, regs);
|
|
|
|
/* Successful */
|
|
regs->GR(r1+1) = DIAG308_RC_OK;
|
|
|
|
if ( DIAG308_DEBUG )
|
|
logmsg(">>>> DIAG308_STORE: exit. rc=%d\n", rc);
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
ARCH_DEP(program_interrupt)(regs, PGM_SPECIFICATION_EXCEPTION);
|
|
} /* end switch(subcode) */
|
|
|
|
break;
|
|
} /* end diag 0x308 */
|
|
|
|
#undef DIAG308_DEBUG
|
|
|
|
#ifdef FEATURE_HERCULES_DIAGCALLS
|
|
case 0xF00:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose F00: Hercules normal mode */
|
|
/*---------------------------------------------------------------*/
|
|
/* If diag8opt is not enabled then we are not allowed
|
|
* to manipulate the real machine i.e. hercules itself
|
|
*/
|
|
if(!(sysblk.diag8opt & DIAG8CMD_ENABLE))
|
|
ARCH_DEP(program_interrupt)(regs, PGM_SPECIFICATION_EXCEPTION);
|
|
|
|
sysblk.instbreak = 0;
|
|
SET_IC_TRACE;
|
|
break;
|
|
|
|
case 0xF04:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose F04: Hercules single step mode */
|
|
/*---------------------------------------------------------------*/
|
|
/* If diag8opt is not enabled then we are not allowed
|
|
* to manipulate the real machine i.e. hercules itself
|
|
*/
|
|
if(!(sysblk.diag8opt & DIAG8CMD_ENABLE))
|
|
ARCH_DEP(program_interrupt)(regs, PGM_SPECIFICATION_EXCEPTION);
|
|
|
|
sysblk.instbreak = 1;
|
|
SET_IC_TRACE;
|
|
break;
|
|
|
|
case 0xF08:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose F08: Return Hercules instruction counter (32) */
|
|
/*---------------------------------------------------------------*/
|
|
regs->GR_L( r1 ) = (U32) INSTCOUNT( regs );
|
|
break;
|
|
|
|
case 0xF09:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose F09: Return Hercules instruction counter (64) */
|
|
/*---------------------------------------------------------------*/
|
|
{
|
|
// Operand register r3 bits 32-47 specify the option code and
|
|
// bits 48-63 specify the CPU Address for option code 1. For
|
|
// option code 0, operand register r3 bits 48-63 are ignored.
|
|
// Bits 0-31 of operand register r3 are also always ignored.
|
|
// Any option code other than 0 or 1 causes a Specification
|
|
// Exception Program Interrupt to occur.
|
|
//
|
|
// Option 0 = instruction count for entire system (all CPUs
|
|
// together). Option 1 = instruction count for specific CPU
|
|
// identified in bits 48-63 of r3.
|
|
//
|
|
// The register and bits that the 64-bit instruction count
|
|
// is returned in depends on: 1) whether z/Architecture mode
|
|
// is active or not, and 2) whether the specified operand-1
|
|
// r1 register number is even or odd.
|
|
//
|
|
// If operand-1 register r1 is an even numbered register,
|
|
// then the high-order bits 0-31 of the 64-bit instruction
|
|
// count is returned in bits 32-63 of the even numbered
|
|
// register, the low-order bits 32-63 the 64-bit instruction
|
|
// count is returned in bits 32-63 of the r1+1 odd numbered
|
|
// register and bits 0-31 of each register remain unmodified.
|
|
//
|
|
// If operand-1 register r1 specifies an odd numbered register
|
|
// however, then the 64-bit instruction count is returned in
|
|
// bits 0-63 of register r1 in z/Architecture mode, whereas a
|
|
// Specification Exception Program Check Interrupt occurs in
|
|
// both ESA/390 and System/370 architecture modes as 64-bit
|
|
// registers don't exist in either of those architectures.
|
|
//
|
|
// Precluding a Specification Exception Program Interrupt,
|
|
// Condition Code 0 is returned for option 0, whereas for
|
|
// option 1, Condition Code 0 is only returned if the CPU
|
|
// specified in bits 48-63 of the operand-3 register r3 is
|
|
// currently valid and online. Otherwise if the specified
|
|
// CPU is offline or does not exist in the configuration,
|
|
// Condition Code 3 is returned and the r1 or r1 and r1+1
|
|
// return value register(s) is/are not modified.
|
|
|
|
U64 instcount=0; // Instruction count
|
|
U16 opt = regs->GR_LHH( r3 ); // Option code
|
|
|
|
if (regs->arch_mode != ARCH_900_IDX)// Not 64-bit architecture?
|
|
ODD_CHECK( r1, regs ); // 64-bit regs don't exist!
|
|
|
|
if (opt > 1) // Unsupported option?
|
|
{
|
|
regs->program_interrupt( regs, PGM_SPECIFICATION_EXCEPTION );
|
|
}
|
|
else if (opt == 1) // Count for specific CPU?
|
|
{
|
|
int cpu = regs->GR_LHL( r3 ); // Get desired CPU from r3
|
|
|
|
if (cpu < 0 || cpu >= sysblk.maxcpu || !IS_CPU_ONLINE( cpu ))
|
|
{
|
|
regs->psw.cc = 3; // CPU is invalid/offline
|
|
break; // We are done
|
|
}
|
|
|
|
/* Retrieve the requested CPU's instruction count */
|
|
instcount = sysblk.regs[ cpu ]->prevcount // (U64)
|
|
+ sysblk.regs[ cpu ]->instcount; // (U32)
|
|
regs->psw.cc = 0;
|
|
}
|
|
else if (opt == 0) // Global system counter?
|
|
{
|
|
instcount = sysblk.instcount; // Get total for ALL CPUs
|
|
regs->psw.cc = 0;
|
|
}
|
|
|
|
/* Return the instruction count in the requested register(s) */
|
|
if (r1 & 1)
|
|
regs->GR_G( r1 ) = instcount; // Contiguous 64-bit count
|
|
else
|
|
{
|
|
/* Place the high-order 32 bits of the 64-bit count into
|
|
bits 32-63 of the even numbered register and place the
|
|
low-order 32 bits of the 64-bit count into bits 32-63
|
|
of the r1+1 odd numbered register. For S/370 and S/390
|
|
mode this corresponds to bits 0-31 of the register pair
|
|
since registers are only 32 bits wide in 370 and 390.
|
|
*/
|
|
regs->GR_L( r1 ) = (U32)((instcount >> 32) & 0xFFFFFFFF);
|
|
regs->GR_L( r1 + 1) = (U32)((instcount ) & 0xFFFFFFFF);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 0xF0C:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose F0C: Set/reset bad frame indicator */
|
|
/*---------------------------------------------------------------*/
|
|
/* If diag8opt is not enabled then we are not allowed
|
|
* to manipulate the real machine i.e. hercules itself
|
|
*/
|
|
if(!(sysblk.diag8opt & DIAG8CMD_ENABLE))
|
|
ARCH_DEP(program_interrupt)(regs, PGM_SPECIFICATION_EXCEPTION);
|
|
|
|
/* Load 4K block address from R2 register */
|
|
n = regs->GR_L(r3) & ADDRESS_MAXWRAP(regs);
|
|
|
|
/* Convert real address to absolute address */
|
|
n = APPLY_PREFIXING (n, regs->PX);
|
|
|
|
/* Addressing exception if block is outside main storage */
|
|
if ( n > regs->mainlim )
|
|
{
|
|
ARCH_DEP(program_interrupt) (regs, PGM_ADDRESSING_EXCEPTION);
|
|
break;
|
|
}
|
|
|
|
/* Update bad-frame bit based on low-order bit of R1 register.
|
|
Note: we must use the internal "_xxx_storage_key" functions
|
|
to directly set/clear the internal STORKEY_BADFRM bit.
|
|
*/
|
|
if (regs->GR_L(r1) & STORKEY_BADFRM)
|
|
ARCH_DEP( _or_storage_key )( n, STORKEY_BADFRM, SKEY_K );
|
|
else
|
|
ARCH_DEP( _and_storage_key )( n, STORKEY_BADFRM, SKEY_K );
|
|
|
|
break;
|
|
|
|
case 0xF10:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose F10: Hercules CPU stop */
|
|
/*---------------------------------------------------------------*/
|
|
/* If diag8opt is not enabled then we are not allowed
|
|
* to manipulate the real machine i.e. hercules itself
|
|
*/
|
|
if(!(sysblk.diag8opt & DIAG8CMD_ENABLE))
|
|
ARCH_DEP(program_interrupt)(regs, PGM_SPECIFICATION_EXCEPTION);
|
|
|
|
regs->cpustate = CPUSTATE_STOPPING;
|
|
ON_IC_INTERRUPT(regs);
|
|
break;
|
|
|
|
case 0xF14:
|
|
/*-----------------------------------------------------------------*/
|
|
/* Diagnose F14: Hercules Get Microsecond Time, ONLY on */
|
|
/* - ARCH_900_IDX architecture */
|
|
/*-----------------------------------------------------------------*/
|
|
/* Only support ARCH_900_IDX architecture
|
|
*/
|
|
if( sysblk.arch_mode != ARCH_900_IDX)
|
|
ARCH_DEP(program_interrupt)(regs, PGM_SPECIFICATION_EXCEPTION);
|
|
|
|
// Convert seconds to microseconds
|
|
#define SEC_TO_US(sec) ((sec)*1000000)
|
|
// Convert nanoseconds to microseconds
|
|
#define NS_TO_US(ns) ((ns)/1000)
|
|
|
|
{
|
|
U64 ms;
|
|
|
|
#if defined( __linux__ )
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
|
ms = SEC_TO_US((uint64_t)ts.tv_sec) + NS_TO_US((uint64_t)ts.tv_nsec + 500);
|
|
|
|
#else
|
|
// microsecond resolution getimeofday
|
|
struct timeval tv;
|
|
gettimeofday( &tv, NULL );
|
|
ms = SEC_TO_US((uint64_t)tv.tv_sec) + tv.tv_usec;
|
|
|
|
#endif
|
|
|
|
regs->GR_G(r1) = ms;
|
|
}
|
|
|
|
#undef SEC_TO_US
|
|
#undef NS_TO_US
|
|
|
|
break;
|
|
|
|
#if defined(_FEATURE_HOST_RESOURCE_ACCESS_FACILITY)
|
|
case 0xF18:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose F18: Hercules Access Host Resource */
|
|
/*---------------------------------------------------------------*/
|
|
ARCH_DEP(diagf18_call) (r1, r3, regs);
|
|
break;
|
|
#endif /* defined(_FEATURE_HOST_RESOURCE_ACCESS_FACILITY) */
|
|
|
|
case 0xF1C:
|
|
/*-----------------------------------------------------------------*/
|
|
/* Diagnose F1C: Get 32-bits of Hercules feature list */
|
|
/* */
|
|
/* R1: returned 32-bits of hercules facility list. */
|
|
/* In Z/arch mode, result is returned in R1 bits 32-63. */
|
|
/* */
|
|
/* R3: unsigned byte index, in bits 24-31 or 56-63, used to */
|
|
/* select 32-bits within the Hercules facility list. */
|
|
/* */
|
|
/* CC: 0 - byte index is within the hercules facility list. */
|
|
/* R1 contains 32-bits of the facility list at R3 index. */
|
|
/* If there are fewer than 32-bits remaining in the */
|
|
/* facility list, the remaining bits are set to zero. */
|
|
/* R3 is unchanged. */
|
|
/* */
|
|
/* 3 - byte index is beyond the end of the hercules facility */
|
|
/* list. R1 is set to zero. R3 (bits 24-31 or 56 - 63) */
|
|
/* is updated with the Index of the last valid byte in */
|
|
/* the Hercules facility list. */
|
|
/* */
|
|
/* Note: see stfl.h for Hercules Facility List definitions */
|
|
/*-----------------------------------------------------------------*/
|
|
{
|
|
int i;
|
|
union { U32 w; BYTE b[4]; } temp;
|
|
U32 byte_index;
|
|
|
|
/* get byte offset into Hercules facility list bits */
|
|
byte_index = regs->GR_LHLCL(r3); // bits 56-63
|
|
byte_index += STFL_HERC_FIRST_BIT / 8; // (convert from bit offset to byte offset)
|
|
|
|
if ( byte_index >= STFL_HERC_BY_SIZE )
|
|
{
|
|
regs->psw.cc = 3; // (beyond end of list)
|
|
regs->GR_L(r1) = 0;
|
|
regs->GR_LHLCL(r3) = STFL_HERC_BY_SIZE - (STFL_HERC_FIRST_BIT / 8) -1;
|
|
|
|
}
|
|
else
|
|
{
|
|
regs->psw.cc = 0; // (within list)
|
|
|
|
temp.w = 0;
|
|
for (i=0; i < 4 && byte_index < STFL_HERC_BY_SIZE ; i++, byte_index++)
|
|
{
|
|
temp.b[i] = regs->facility_list[byte_index];
|
|
}
|
|
regs->GR_L(r1) = CSWAP32( temp.w );
|
|
}
|
|
|
|
#if 0 // (debug)
|
|
LOGMSG( "Diag F1C: STFL_IBM_BY_SIZE: %d, STFL_IBM_DW_SIZE: %lu\n", STFL_IBM_BY_SIZE, STFL_IBM_DW_SIZE );
|
|
LOGMSG( "Diag F1C: STFL_HERC_BY_SIZE: %lu, STFL_HERC_DW_SIZE: %lu\n", STFL_HERC_BY_SIZE, STFL_HERC_DW_SIZE );
|
|
LOGMSG( "Diag F1C: STFL_HERC_FIRST_BIT: %lu\n", STFL_HERC_FIRST_BIT );
|
|
for (i=0; i < (int) STFL_HERC_DW_SIZE; i++)
|
|
{
|
|
LOGMSG( "Diag F1C: facility_list DW%d: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
|
|
i,
|
|
regs->facility_list[i*8 +0],
|
|
regs->facility_list[i*8 +1],
|
|
regs->facility_list[i*8 +2],
|
|
regs->facility_list[i*8 +3],
|
|
regs->facility_list[i*8 +4],
|
|
regs->facility_list[i*8 +5],
|
|
regs->facility_list[i*8 +6],
|
|
regs->facility_list[i*8 +7] );
|
|
}
|
|
LOGMSG( "Diag F1C: CC: %d, r1: R%d, r3: R%d, R%d: %8.8X, R%d: %8.8X\n",
|
|
regs->psw.cc, r1, r3,
|
|
r1, regs->GR_L(r1),
|
|
r3, regs->GR_L(r3)
|
|
);
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case 0xFF8:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose FF8: Hercules Infinite Loop (Malfunctioning CPU) */
|
|
/*---------------------------------------------------------------*/
|
|
while(1); /* (loop forever) */
|
|
break; /* (never reached) */
|
|
|
|
case 0xFFC:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose FFC: Hercules SLOW Instruction (Malfunctioning CPU) */
|
|
/*---------------------------------------------------------------*/
|
|
SLEEP(300); /* (300 seconds = 5 minutes!) */
|
|
break;
|
|
|
|
case 0xFFD:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose FFD: Hercules Dummy "Slow(?)" Instruction */
|
|
/*---------------------------------------------------------------*/
|
|
{
|
|
/* r1 = microseconds */
|
|
unsigned int secs = regs->GR_L(r1) / ONE_MILLION;
|
|
unsigned int usecs = regs->GR_L(r1) % ONE_MILLION;
|
|
unsigned int i;
|
|
for (i=0; i < secs; ++i)
|
|
SLEEP(1); /* (sleep one second at a time) */
|
|
if (usecs)
|
|
USLEEP(usecs); /* (remaining microseconds, if any) */
|
|
break;
|
|
}
|
|
|
|
#endif /*FEATURE_HERCULES_DIAGCALLS*/
|
|
|
|
default:
|
|
/*---------------------------------------------------------------*/
|
|
/* Diagnose xxx: Invalid function code or Power-Off diagnose */
|
|
/*---------------------------------------------------------------*/
|
|
|
|
if( HDC4(debug_diagnose, code, r1, r3, regs) )
|
|
return;
|
|
|
|
/* Power Off diagnose on 4361, 9371, 9373, 9375, 9377, 9221: */
|
|
/* */
|
|
/* DS 0H */
|
|
/* DC X'8302',S(SHUTDATA) MUST BE R0 AND R2 */
|
|
/* ... */
|
|
/* DS 0H */
|
|
/* SHUTDATA DC X'0000FFFF' MUST BE X'0000FFFF' */
|
|
|
|
if (0 == r1 && 2 == r3
|
|
&& sysblk.cpuversion != 0xFF
|
|
&& (sysblk.cpumodel == 0x4361
|
|
|| (sysblk.cpumodel & 0xFFF9) == 0x9371 /* (937X) */
|
|
|| sysblk.cpumodel == 0x9221)
|
|
)
|
|
{
|
|
if (0x0000FFFF == ARCH_DEP(vfetch4)(effective_addr2, b2, regs))
|
|
{
|
|
/* If diag8opt is not enabled then we are not allowed
|
|
* to manipulate the real machine i.e. hercules itself
|
|
*/
|
|
if(!(sysblk.diag8opt & DIAG8CMD_ENABLE))
|
|
ARCH_DEP(program_interrupt)(regs, PGM_SPECIFICATION_EXCEPTION);
|
|
|
|
regs->cpustate = CPUSTATE_STOPPING;
|
|
ON_IC_INTERRUPT(regs);
|
|
|
|
/* Release the configuration */
|
|
do_shutdown();
|
|
|
|
/* Power Off: exit hercules */
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
#if defined(FEATURE_S370_CHANNEL) && defined(OPTION_NOP_MODEL158_DIAGNOSE)
|
|
if (regs->cpumodel != 0x0158)
|
|
#endif
|
|
ARCH_DEP(program_interrupt)(regs, PGM_SPECIFICATION_EXCEPTION);
|
|
return;
|
|
|
|
} /* end switch(code) */
|
|
|
|
return;
|
|
|
|
} /* end function diagnose_call */
|
|
|
|
|
|
#if !defined(_GEN_ARCH)
|
|
|
|
#if defined(_ARCH_NUM_1)
|
|
#define _GEN_ARCH _ARCH_NUM_1
|
|
#include "diagnose.c"
|
|
#endif
|
|
|
|
#if defined(_ARCH_NUM_2)
|
|
#undef _GEN_ARCH
|
|
#define _GEN_ARCH _ARCH_NUM_2
|
|
#include "diagnose.c"
|
|
#endif
|
|
|
|
#endif /*!defined(_GEN_ARCH)*/
|