| /* | 
 |  * s390-specific syscalls decoders. | 
 |  * | 
 |  * Copyright (c) 2018 The strace developers. | 
 |  * All rights reserved. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  * 1. Redistributions of source code must retain the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer. | 
 |  * 2. Redistributions in binary form must reproduce the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer in the | 
 |  *    documentation and/or other materials provided with the distribution. | 
 |  * 3. The name of the author may not be used to endorse or promote products | 
 |  *    derived from this software without specific prior written permission. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 
 |  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 
 |  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 
 |  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 
 |  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
 |  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
 |  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
 |  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
 |  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #include "defs.h" | 
 |  | 
 | #if defined S390 || defined S390X | 
 |  | 
 | #include <sys/user.h> | 
 |  | 
 | #include "print_fields.h" | 
 |  | 
 | #include "xlat/s390_guarded_storage_commands.h" | 
 | #include "xlat/s390_runtime_instr_commands.h" | 
 | #include "xlat/s390_sthyi_function_codes.h" | 
 |  | 
 | /* | 
 |  * Since, for some reason, kernel doesn't expose all these nice constants and | 
 |  * structures in UAPI, we have to re-declare them ourselves. | 
 |  */ | 
 |  | 
 | /** | 
 |  * "The header section is placed at the beginning of the response buffer and | 
 |  * identifies the location and length of all other sections. Valid sections have | 
 |  * nonzero offset values in the header. Each section provides information about | 
 |  * validity of fields within that section." | 
 |  */ | 
 | struct sthyi_hdr { | 
 | 	/** | 
 | 	 * Header Flag Byte 1 - These flag settings indicate the environment | 
 | 	 * that the instruction was executed in and may influence the value of | 
 | 	 * the validity bits. The validity bits, and not these flags, should be | 
 | 	 * used to determine if a field is valid. | 
 | 	 *  - 0x80 - Global Performance Data unavailable | 
 | 	 *  - 0x40 - One or more hypervisor levels below this level does not | 
 | 	 *           support the STHYI instruction. When this flag is set the | 
 | 	 *           value of INFGPDU is not meaningful because the state of the | 
 | 	 *           Global Performance Data setting cannot be determined. | 
 | 	 *  - 0x20 - Virtualization stack is incomplete. This bit indicates one | 
 | 	 *           of two cases: | 
 | 	 *   - One or more hypervisor levels does not support the STHYI | 
 | 	 *     instruction. For this case, INFSTHYI will also be set. | 
 | 	 *   - There were more than three levels of guest/hypervisor information | 
 | 	 *     to report. | 
 | 	 *  - 0x10 - Execution environment is not within a logical partition. | 
 | 	 */ | 
 | 	uint8_t  infhflg1; | 
 | 	uint8_t  infhflg2; /**< Header Flag Byte 2 reserved for IBM(R) use */ | 
 | 	uint8_t  infhval1; /**< Header Validity Byte 1 reserved for IBM use */ | 
 | 	uint8_t  infhval2; /**< Header Validity Byte 2 reserved for IBM use */ | 
 | 	char     reserved_1__[3]; /**< Reserved for future IBM use */ | 
 | 	uint8_t  infhygct; /**< Count of Hypervisor and Guest Sections */ | 
 | 	uint16_t infhtotl; /**< Total length of response buffer */ | 
 | 	uint16_t infhdln;  /**< Length of Header Section mapped by INF0HDR */ | 
 | 	uint16_t infmoff;  /**< Offset to Machine Section mapped by INF0MAC */ | 
 | 	uint16_t infmlen;  /**< Length of Machine Section */ | 
 | 	uint16_t infpoff;  /**< Offset to Partition Section mapped by INF0PAR */ | 
 | 	uint16_t infplen;  /**< Length of Partition Section */ | 
 | 	uint16_t infhoff1; /**< Offset to Hypervisor Section1 mapped by INF0HYP */ | 
 | 	uint16_t infhlen1; /**< Length of Hypervisor Section1 */ | 
 | 	uint16_t infgoff1; /**< Offset to Guest Section1 mapped by INF0GST */ | 
 | 	uint16_t infglen1; /**< Length of Guest Section1 */ | 
 | 	uint16_t infhoff2; /**< Offset to Hypervisor Section2 mapped by INF0HYP */ | 
 | 	uint16_t infhlen2; /**< Length of Hypervisor Section2 */ | 
 | 	uint16_t infgoff2; /**< Offset to Guest Section2 mapped by INF0GST */ | 
 | 	uint16_t infglen2; /**< Length of Guest Section2 */ | 
 | 	uint16_t infhoff3; /**< Offset to Hypervisor Section3 mapped by INF0HYP */ | 
 | 	uint16_t infhlen3; /**< Length of Hypervisor Section3 */ | 
 | 	uint16_t infgoff3; /**< Offset to Guest Section3 mapped by INF0GST */ | 
 | 	uint16_t infglen3; /**< Length of Guest Section3 */ | 
 | 	/* 44 bytes in total */ | 
 | } ATTRIBUTE_PACKED; | 
 |  | 
 | struct sthyi_machine { | 
 | 	uint8_t  infmflg1; /**< Machine Flag Byte 1 reserved for IBM use */ | 
 | 	uint8_t  infmflg2; /**< Machine Flag Byte 2 reserved for IBM use */ | 
 | 	/** | 
 | 	 * Machine Validity Byte 1 | 
 | 	 *  - 0x80 - Processor Count Validity. When this bit is on, it indicates | 
 | 	 *           that INFMSCPS, INFMDCPS, INFMSIFL, and INFMDIFL contain | 
 | 	 *           valid counts. The validity bit may be off when: | 
 | 	 *   - STHYI support is not available on a lower level hypervisor, or | 
 | 	 *   - Global Performance Data is not enabled. | 
 | 	 *  - 0x40 - Machine ID Validity. This bit being on indicates that a | 
 | 	 *           SYSIB 1.1.1 was obtained from STSI and information reported | 
 | 	 *           in the following fields is valid: INFMTYPE, INFMMANU, | 
 | 	 *           INFMSEQ, and INFMPMAN. | 
 | 	 *  - 0x20 - Machine Name Validity. This bit being on indicates that the | 
 | 	 *           INFMNAME field is valid. | 
 | 	 */ | 
 | 	uint8_t  infmval1; | 
 | 	uint8_t  infmval2; /**< Machine Validity Byte 2 reserved for IBM use */ | 
 | 	/** | 
 | 	 * Number of shared CPs configured in the machine or in the physical | 
 | 	 * partition if the system is physically partitioned. | 
 | 	 */ | 
 | 	uint16_t infmscps; | 
 | 	/** | 
 | 	 * Number of dedicated CPs configured in this machine or in the physical | 
 | 	 * partition if the system is physically partitioned. | 
 | 	 */ | 
 | 	uint16_t infmdcps; | 
 | 	/** | 
 | 	 * Number of shared IFLs configured in this machine or in the physical | 
 | 	 * partition if the system is physically partitioned. | 
 | 	 */ | 
 | 	uint16_t infmsifl; | 
 | 	/** | 
 | 	 * Number of dedicated IFLs configured in this machine or in the | 
 | 	 * physical partition if the system is physically partitioned. | 
 | 	 */ | 
 | 	uint16_t infmdifl; | 
 | 	char     infmname[8];  /**< EBCDIC Machine Name */ | 
 | 	char     infmtype[4];  /**< EBCDIC Type */ | 
 | 	char     infmmanu[16]; /**< EBCDIC Manufacturer */ | 
 | 	char     infmseq[16];  /**< EBCDIC Sequence Code */ | 
 | 	char     infmpman[4];  /**< EBCDIC Plant of Manufacture */ | 
 | 	/* 60 bytes in total*/ | 
 | } ATTRIBUTE_PACKED; | 
 |  | 
 | struct sthyi_partition { | 
 | 	/** | 
 | 	 * Partition Flag Byte 1 | 
 | 	 *  - 0x80 - Multithreading (MT) is enabled. | 
 | 	 */ | 
 | 	uint8_t  infpflg1; | 
 | 	/** Partition Flag Byte 2 reserved for IBM use */ | 
 | 	uint8_t  infpflg2; | 
 | 	/** | 
 | 	 * Partition Validity Byte 1 | 
 | 	 *  - 0x80 - Processor count validity. This bit being on indicates that | 
 | 	 *           INFPSCPS, INFPDCPS, INFPSIFL, and INFPDIFL contain valid | 
 | 	 *           counts. | 
 | 	 *  - 0x40 - Partition weight-based capped capacity validity. This bit | 
 | 	 *           being on indicates that INFPWBCP and INFPWBIF are valid | 
 | 	 *  - 0x20 - Partition absolute capped capacity validity. This bit being | 
 | 	 *           on indicates that INFPABCP and INFPABIF are valid. | 
 | 	 *  - 0x10 - Partition ID validity. This bit being on indicates that a | 
 | 	 *           SYSIB 2.2.2 was obtained from STSI and information reported | 
 | 	 *           in the following fields is valid: INFPPNUM and INFPPNAM. | 
 | 	 *  - 0x08 - LPAR group absolute capacity capping information validity. | 
 | 	 *           This bit being on indicates that INFPLGNM, INFPLGCP, and | 
 | 	 *           INFPLGIF are valid. | 
 | 	 */ | 
 | 	uint8_t  infpval1; | 
 | 	/** Partition Validity Byte 2 reserved for IBM use */ | 
 | 	uint8_t  infpval2; | 
 | 	/** Logical partition number */ | 
 | 	uint16_t infppnum; | 
 | 	/** | 
 | 	 * Number of shared logical CPs configured for this partition.  Count | 
 | 	 * of cores when MT is enabled. | 
 | 	 */ | 
 | 	uint16_t infpscps; | 
 | 	/** | 
 | 	 * Number of dedicated logical CPs configured for this partition.  Count | 
 | 	 * of cores when MT is enabled. | 
 | 	 */ | 
 | 	uint16_t infpdcps; | 
 | 	/** | 
 | 	 * Number of shared logical IFLs configured for this partition.  Count | 
 | 	 * of cores when MT is enabled. | 
 | 	 */ | 
 | 	uint16_t infpsifl; | 
 | 	/** | 
 | 	 * Number of dedicated logical IFLs configured for this partition. | 
 | 	 * Count of cores when MT is enabled. | 
 | 	 */ | 
 | 	uint16_t infpdifl; | 
 | 	/** Reserved for future IBM use */ | 
 | 	char     reserved_1__[2]; | 
 | 	/** EBCIDIC Logical partition name */ | 
 | 	char     infppnam[8]; | 
 | 	/** | 
 | 	 * Partition weight-based capped capacity for CPs, a scaled number where | 
 | 	 * 0x00010000 represents one  core.  Zero if not capped. | 
 | 	 */ | 
 | 	uint32_t infpwbcp; | 
 | 	/** | 
 | 	 * Partition absolute capped capacity for CPs, a scaled number where | 
 | 	 * 0x00010000 represents one  core.  Zero if not capped. | 
 | 	 */ | 
 | 	uint32_t infpabcp; | 
 | 	/** | 
 | 	 * Partition weight-based capped capacity for IFLs, a scaled number | 
 | 	 * where 0x00010000 represents one  core.  Zero if not capped. | 
 | 	 */ | 
 | 	uint32_t infpwbif; | 
 | 	/** | 
 | 	 * Partition absolute capped capacity for IFLs, a scaled number where | 
 | 	 * 0x00010000 represents one  core.  Zero if not capped. | 
 | 	 */ | 
 | 	uint32_t infpabif; | 
 | 	/** | 
 | 	 * EBCIDIC LPAR group name. Binary zeros when the partition is not in | 
 | 	 * an LPAR group. EBCDIC and padded with blanks on the right when in a | 
 | 	 * group. The group name is reported only when there is a group cap on | 
 | 	 * CP or IFL CPU types and the partition has the capped CPU type. | 
 | 	 */ | 
 | 	char     infplgnm[8]; | 
 | 	/** | 
 | 	 * LPAR group absolute capacity value for CP CPU type when nonzero. This | 
 | 	 * field will be nonzero only when INFPLGNM is nonzero and a cap is | 
 | 	 * defined for the LPAR group for the CP CPU type. When nonzero, | 
 | 	 * contains a scaled number where 0x00010000 represents one core. | 
 | 	 */ | 
 | 	uint32_t infplgcp; | 
 | 	/** | 
 | 	 * LPAR group absolute capacity value for IFL CPU type when nonzero. | 
 | 	 * This field will be nonzero only when INFPLGNM is nonzero and a cap | 
 | 	 * is defined for the LPAR group for the IFL CPU type. When nonzero, | 
 | 	 * contains a scaled number where 0x00010000 represents one core. | 
 | 	 */ | 
 | 	uint32_t infplgif; | 
 | 	/* 56 bytes */ | 
 | } ATTRIBUTE_PACKED; | 
 |  | 
 | struct sthyi_hypervisor { | 
 | 	/** | 
 | 	 * Hypervisor Flag Byte 1 | 
 | 	 *  - 0x80 - Guest CPU usage hard limiting is using the consumption | 
 | 	 *           method. | 
 | 	 *  - 0x40 - If on, LIMITHARD caps use prorated core time for capping. | 
 | 	 *           If off, raw CPU time is used. | 
 | 	 */ | 
 | 	uint8_t infyflg1; | 
 | 	uint8_t infyflg2; /**< Hypervisor Flag Byte 2 reserved for IBM use */ | 
 | 	uint8_t infyval1; /**< Hypervisor Validity Byte 1 reserved for IBM use */ | 
 | 	uint8_t infyval2; /**< Hypervisor Validity Byte 2 reserved for IBM use */ | 
 | 	/** | 
 | 	 * Hypervisor Type | 
 | 	 *  - 1 - z/VM is the hypervisor. | 
 | 	 */ | 
 | 	uint8_t infytype; | 
 | 	char    reserved_1__[1]; /**< Reserved for future IBM use */ | 
 | 	/** | 
 | 	 * Threads in use per CP core. Only valid when MT enabled | 
 | 	 * (INFPFLG1 0x80 is ON). | 
 | 	 */ | 
 | 	uint8_t infycpt; | 
 | 	/** | 
 | 	 * Threads in use per IFL core. Only valid when MT enabled | 
 | 	 * (INFPFLG1 0x80 is ON). | 
 | 	 */ | 
 | 	uint8_t infyiflt; | 
 | 	/** | 
 | 	 * EBCID System Identifier. Left justified and padded with blanks. | 
 | 	 * This field will be blanks if non-existent. | 
 | 	 */ | 
 | 	char     infysyid[8]; | 
 | 	/** | 
 | 	 * EBCID Cluster Name. Left justified and padded with blanks. This is | 
 | 	 * the name on the SSI statement in the system configuration file. This | 
 | 	 * field will be blanks if nonexistent. | 
 | 	 */ | 
 | 	char     infyclnm[8]; | 
 | 	/** | 
 | 	 * Total number of CPs shared among guests of this hypervisor. | 
 | 	 * Number of cores when MT enabled. | 
 | 	 */ | 
 | 	uint16_t infyscps; | 
 | 	/** | 
 | 	 * Total number of CPs dedicated to guests of this hypervisor. | 
 | 	 * Number of cores when MT enabled. | 
 | 	 */ | 
 | 	uint16_t infydcps; | 
 | 	/** | 
 | 	 * Total number of IFLs shared among guests of this hypervisor. | 
 | 	 * Number of cores when MT enabled. | 
 | 	 */ | 
 | 	uint16_t infysifl; | 
 | 	/** | 
 | 	 * Total number of IFLs dedicated to guests of this hypervisor. | 
 | 	 * Number of cores when MT enabled. | 
 | 	 */ | 
 | 	uint16_t infydifl; | 
 | 	/* 32 bytes */ | 
 | } ATTRIBUTE_PACKED; | 
 |  | 
 | struct sthyi_guest { | 
 | 	/** | 
 | 	 * Guest Flag Byte 1 | 
 | 	 *  - 0x80 - Guest is mobility enabled | 
 | 	 *  - 0x40 - Guest has multiple virtual CPU types | 
 | 	 *  - 0x20 - Guest CP dispatch type has LIMITHARD cap | 
 | 	 *  - 0x10 - Guest IFL dispatch type has LIMITHARD cap | 
 | 	 *  - 0x08 - Virtual CPs are thread dispatched | 
 | 	 *  - 0x04 - Virtual IFLs are thread dispatched | 
 | 	 */ | 
 | 	uint8_t  infgflg1; | 
 | 	uint8_t  infgflg2;    /**< Guest Flag Byte 2 reserved for IBM use */ | 
 | 	uint8_t  infgval1;    /**< Guest Validity Byte 1 reserved for IBM use */ | 
 | 	uint8_t  infgval2;    /**< Guest Validity Byte 2 reserved for IBM use */ | 
 | 	char     infgusid[8]; /**< EBCDIC Userid */ | 
 | 	uint16_t infgscps;    /**< Number of guest shared CPs */ | 
 | 	uint16_t infgdcps;    /**< Number of guest dedicated CPs */ | 
 | 	/** | 
 | 	 * Dispatch type for guest CPs.  This field is valid if INFGSCPS or | 
 | 	 * INFGDCPS is greater than zero. | 
 | 	 *  - 0 - General Purpose (CP) | 
 | 	 */ | 
 | 	uint8_t  infgcpdt; | 
 | 	char     reserved_1__[3]; /**< Reserved for future IBM use */ | 
 | 	/** | 
 | 	 * Guest current capped capacity for shared virtual CPs, a scaled number | 
 | 	 * where 0x00010000 represents one  core.   This field is zero to | 
 | 	 * indicate not capped when: | 
 | 	 *  - There is no CP individual limit (that is, the "Guest CP dispatch | 
 | 	 *    type has LIMITHARD cap" bit in field INFGFLG1 is OFF). | 
 | 	 *  - There are no shared CPs on the system (that is, INFYSCPS = 0). | 
 | 	 *    If there is a CP limit but there are no shared CPs or virtual CPs, | 
 | 	 *    the limit is meaningless and does not apply to anything. | 
 | 	 */ | 
 | 	uint32_t infgcpcc; | 
 | 	uint16_t infgsifl; /**< Number of guest shared IFLs */ | 
 | 	uint16_t infgdifl; /**< Number of guest dedicated IFLs */ | 
 | 	/** | 
 | 	 * Dispatch type for guest IFLs. This field is valid if INFGSIFL or | 
 | 	 * INFGDIFL is greater than zero. | 
 | 	 *  - 0 - General Purpose (CP) | 
 | 	 *  - 3 - Integrated Facility for Linux (IFL) | 
 | 	 */ | 
 | 	uint8_t  infgifdt; | 
 | 	char     reserved_2__[3]; /**< Reserved for future IBM use */ | 
 | 	/** | 
 | 	 * Guest current capped capacity for shared virtual IFLs,  a scaled | 
 | 	 * number where 0x00010000 represents one core.   This field is zero | 
 | 	 * to indicate not capped with an IFL limit when: | 
 | 	 *  - There is no IFL individual limit (that is, the "Guest IFL dispatch | 
 | 	 *    type has LIMITHARD cap" bit in field INFGFLG1 is OFF). | 
 | 	 *  - The guest's IFLs are dispatched on CPs (that is, INFGIFDT = 00). | 
 | 	 *    When the guest's IFLs are dispatched on CPs, the CP individual | 
 | 	 *    limit (in INFGCPCC) is applied to the guest's virtual IFLs and | 
 | 	 *    virtual CPs. | 
 | 	 */ | 
 | 	uint32_t infgifcc; | 
 | 	/** | 
 | 	 * CPU Pool Capping Flags | 
 | 	 *  - 0x80 - CPU Pool's CP virtual type has LIMITHARD cap | 
 | 	 *  - 0x40 - CPU Pool's CP virtual type has CAPACITY cap | 
 | 	 *  - 0x20 - CPU Pool's IFL virtual type has LIMITHARD cap | 
 | 	 *  - 0x10 - CPU Pool's IFL virtual type has CAPACITY cap | 
 | 	 *  - 0x08 - CPU Pool uses prorated core time. | 
 | 	 */ | 
 | 	uint8_t  infgpflg; | 
 | 	char     reserved_3__[3]; /**< Reserved for future IBM use */ | 
 | 	/** | 
 | 	 * EBCDIC CPU Pool Name. This field will be blanks if the guest is not | 
 | 	 * in a CPU Pool. | 
 | 	 */ | 
 | 	char     infgpnam[8]; | 
 | 	/** | 
 | 	 * CPU Pool capped capacity for shared virtual CPs, a scaled number | 
 | 	 * where 0x00010000 represents one  core.  This field will be zero if | 
 | 	 * not capped. | 
 | 	 */ | 
 | 	uint32_t infgpccc; | 
 | 	/** | 
 | 	 * CPU Pool capped capacity for shared virtual IFLs, a scaled number | 
 | 	 * where 0x00010000 represents one  core.  This field will be zero if | 
 | 	 * not capped. | 
 | 	 */ | 
 | 	uint32_t infgpicc; | 
 | 	/* 56 bytes */ | 
 | } ATTRIBUTE_PACKED; | 
 |  | 
 |  | 
 | static void | 
 | decode_ebcdic(const char *ebcdic, char *ascii, size_t size) | 
 | { | 
 | 	/* | 
 | 	 * This is mostly Linux's EBCDIC-ASCII conversion table, except for | 
 | 	 * various non-representable characters that are converted to spaces for | 
 | 	 * readability purposes, as it is intended to be a hint for the string | 
 | 	 * contents and not precise conversion. | 
 | 	 */ | 
 | 	static char conv_table[] = | 
 | 		 "\0\1\2\3 \11 \177   \13\14\15\16\17" | 
 | 		 "\20\21\22\23 \n\10 \30\31  \34\35\36\37" | 
 | 		 "  \34  \n\27\33     \5\6\7" | 
 | 		 "  \26    \4    \24\25 \32" | 
 | 		 "          " " .<(+|" | 
 | 		 "&         " "!$*);~" | 
 | 		 "-/        " "|,%_>?" | 
 | 		 "         `" ":#@'=\"" | 
 | 		 " abcdefghi" "      " | 
 | 		 " jklmnopqr" "      " | 
 | 		 " ~stuvwxyz" "      " | 
 | 		 "^         " "[]    " | 
 | 		 "{ABCDEFGHI" "      " | 
 | 		 "}JKLMNOPQR" "      " | 
 | 		"\\ STUVWXYZ" "      " | 
 | 		 "0123456789" "      "; | 
 |  | 
 | 	while (size--) | 
 | 		*ascii++ = conv_table[(unsigned char) *ebcdic++]; | 
 | } | 
 |  | 
 | #define DECODE_EBCDIC(ebcdic_, ascii_) \ | 
 | 	decode_ebcdic((ebcdic_), (ascii_), \ | 
 | 		      sizeof(ebcdic_) + MUST_BE_ARRAY(ebcdic_)) | 
 | #define PRINT_EBCDIC(ebcdic_) \ | 
 | 	do { \ | 
 | 		char ascii_str[sizeof(ebcdic_) + MUST_BE_ARRAY(ebcdic_)]; \ | 
 | 		\ | 
 | 		DECODE_EBCDIC(ebcdic_, ascii_str); \ | 
 | 		print_quoted_string(ascii_str, sizeof(ascii_str), \ | 
 | 				    QUOTE_EMIT_COMMENT); \ | 
 | 	} while (0) | 
 |  | 
 | #define PRINT_FIELD_EBCDIC(prefix_, where_, field_) \ | 
 | 	do { \ | 
 | 		PRINT_FIELD_HEX_ARRAY(prefix_, where_, field_); \ | 
 | 		PRINT_EBCDIC((where_).field_); \ | 
 | 	} while (0) | 
 |  | 
 | #define PRINT_FIELD_WEIGHT(prefix_, where_, field_) \ | 
 | 	do { \ | 
 | 		PRINT_FIELD_X(prefix_, where_, field_); \ | 
 | 		if ((where_).field_) \ | 
 | 			tprintf_comment("%u %u/65536 cores", \ | 
 | 				(where_).field_ >> 16, \ | 
 | 				(where_).field_ & 0xFFFF); \ | 
 | 		else \ | 
 | 			tprints_comment("unlimited"); \ | 
 | 	} while (0) | 
 |  | 
 |  | 
 | #define IS_BLANK(arr_) /* 0x40 is space in EBCDIC */ \ | 
 | 	is_filled(arr_, '\x40', sizeof(arr_) + MUST_BE_ARRAY(arr_)) | 
 |  | 
 | #define CHECK_SIZE(hdr_, size_, name_, ...) \ | 
 | 	do { \ | 
 | 		if ((size_) < sizeof(*(hdr_))) { \ | 
 | 			tprintf_comment("Invalid " name_ " with size " \ | 
 | 					"%hu < %zu expected", \ | 
 | 					##__VA_ARGS__, \ | 
 | 					(size_), sizeof(*(hdr_))); \ | 
 | 			print_quoted_string((char *) (hdr_), (size_), \ | 
 | 					    QUOTE_FORCE_HEX); \ | 
 | 			\ | 
 | 			return; \ | 
 | 		} \ | 
 | 	} while (0) | 
 |  | 
 | #define PRINT_UNKNOWN_TAIL(hdr_, size_) \ | 
 | 	do { \ | 
 | 		if ((size_) > sizeof(*(hdr_)) && \ | 
 | 		    !is_filled((char *) ((hdr_) + 1), '\0', \ | 
 | 		               (size_) - sizeof(*(hdr_)))) \ | 
 | 			print_quoted_string((char *) ((hdr_) + 1), \ | 
 | 					    (size_) - sizeof(*(hdr_)), \ | 
 | 					    QUOTE_FORCE_HEX); \ | 
 | 	} while (0) | 
 |  | 
 | static void | 
 | print_sthyi_machine(struct tcb *tcp, struct sthyi_machine *hdr, uint16_t size, | 
 | 		    bool *dummy) | 
 | { | 
 | 	int cnt_val, name_val, id_val; | 
 |  | 
 | 	CHECK_SIZE(hdr, size, "machine structure"); | 
 |  | 
 | 	tprints("/* machine */ {"); | 
 | 	if (!abbrev(tcp)) { | 
 | 		if (hdr->infmflg1) { /* Reserved */ | 
 | 			PRINT_FIELD_0X("", *hdr, infmflg1); | 
 | 			tprints(", "); | 
 | 		} | 
 | 		if (hdr->infmflg2) { /* Reserved */ | 
 | 			PRINT_FIELD_0X(", ", *hdr, infmflg2); | 
 | 			tprints(", "); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	PRINT_FIELD_0X("", *hdr, infmval1); | 
 | 	cnt_val  = !!(hdr->infmval1 & 0x80); | 
 | 	id_val   = !!(hdr->infmval1 & 0x40); | 
 | 	name_val = !!(hdr->infmval1 & 0x20); | 
 |  | 
 | 	if (!abbrev(tcp)) { | 
 | 		if (hdr->infmval1) | 
 | 			tprintf_comment("processor count validity: %d, " | 
 | 					"machine ID validity: %d, " | 
 | 					"machine name validity: %d%s%#.0x%s", | 
 | 					cnt_val, id_val, name_val, | 
 | 					hdr->infmval1 & 0x1F ? ", " : "", | 
 | 					hdr->infmval1 & 0x1F, | 
 | 					hdr->infmval1 & 0x1F ? " - ???" : ""); | 
 | 		if (hdr->infmval2) | 
 | 			PRINT_FIELD_0X(", ", *hdr, infmval2); | 
 | 	} | 
 |  | 
 | 	if (cnt_val || hdr->infmscps) | 
 | 		PRINT_FIELD_U(", ", *hdr, infmscps); | 
 | 	if (cnt_val || hdr->infmdcps) | 
 | 		PRINT_FIELD_U(", ", *hdr, infmdcps); | 
 | 	if (cnt_val || hdr->infmsifl) | 
 | 		PRINT_FIELD_U(", ", *hdr, infmsifl); | 
 | 	if (cnt_val || hdr->infmdifl) | 
 | 		PRINT_FIELD_U(", ", *hdr, infmdifl); | 
 |  | 
 | 	if (!abbrev(tcp)) { | 
 | 		if (name_val || hdr->infmname) | 
 | 			PRINT_FIELD_EBCDIC(", ", *hdr, infmname); | 
 |  | 
 | 		if (id_val || !IS_ARRAY_ZERO(hdr->infmtype)) | 
 | 			PRINT_FIELD_EBCDIC(", ", *hdr, infmtype); | 
 | 		if (id_val || !IS_ARRAY_ZERO(hdr->infmmanu)) | 
 | 			PRINT_FIELD_EBCDIC(", ", *hdr, infmmanu); | 
 | 		if (id_val || !IS_ARRAY_ZERO(hdr->infmseq)) | 
 | 			PRINT_FIELD_EBCDIC(", ", *hdr, infmseq); | 
 | 		if (id_val || !IS_ARRAY_ZERO(hdr->infmpman)) | 
 | 			PRINT_FIELD_EBCDIC(", ", *hdr, infmpman); | 
 |  | 
 | 		PRINT_UNKNOWN_TAIL(hdr, size); | 
 | 	} else { | 
 | 		tprints(", ..."); | 
 | 	} | 
 |  | 
 | 	tprints("}"); | 
 | } | 
 |  | 
 | static void | 
 | print_sthyi_partition(struct tcb *tcp, struct sthyi_partition *hdr, | 
 | 		      uint16_t size, bool *mt) | 
 | { | 
 | 	int cnt_val, wcap_val, acap_val, id_val, lpar_val; | 
 |  | 
 | 	*mt = false; | 
 |  | 
 | 	CHECK_SIZE(hdr, size, "partition structure"); | 
 |  | 
 | 	*mt = !!(hdr->infpflg1 & 0x80); | 
 |  | 
 | 	PRINT_FIELD_0X("/* partition */ {", *hdr, infpflg1); | 
 | 	if (!abbrev(tcp) && hdr->infpflg1) | 
 | 		tprintf_comment("%s%s%#.0x%s", | 
 | 			hdr->infpflg1 & 0x80 ? | 
 | 				"0x80 - multithreading is enabled" : "", | 
 | 			(hdr->infpflg1 & 0x80) && (hdr->infpflg1 & 0x7F) ? | 
 | 				", " : "", | 
 | 			hdr->infpflg1 & 0x7F, | 
 | 			hdr->infpflg1 & 0x7F ? " - ???" : ""); | 
 | 	if (!abbrev(tcp) && hdr->infpflg2) /* Reserved */ | 
 | 		PRINT_FIELD_0X(", ", *hdr, infpflg2); | 
 |  | 
 | 	PRINT_FIELD_0X(", ", *hdr, infpval1); | 
 | 	cnt_val  = !!(hdr->infpval1 & 0x80); | 
 | 	wcap_val = !!(hdr->infpval1 & 0x40); | 
 | 	acap_val = !!(hdr->infpval1 & 0x20); | 
 | 	id_val   = !!(hdr->infpval1 & 0x10); | 
 | 	lpar_val = !!(hdr->infpval1 & 0x08); | 
 |  | 
 | 	if (!abbrev(tcp) && hdr->infpval1) | 
 | 		tprintf_comment("processor count validity: %d, " | 
 | 				"partition weight-based capacity validity: %d, " | 
 | 				"partition absolute capacity validity: %d, " | 
 | 				"partition ID validity: %d, " | 
 | 				"LPAR group absolute capacity capping " | 
 | 				"information validity: %d%s%#.0x%s", | 
 | 				cnt_val, wcap_val, acap_val, id_val, lpar_val, | 
 | 				hdr->infpval1 & 0x07 ? ", " : "", | 
 | 				hdr->infpval1 & 0x07, | 
 | 				hdr->infpval1 & 0x07 ? " - ???" : ""); | 
 | 	if (!abbrev(tcp) && hdr->infpval2) /* Reserved */ | 
 | 		PRINT_FIELD_0X(", ", *hdr, infpval2); | 
 |  | 
 | 	if (id_val || hdr->infppnum) | 
 | 		PRINT_FIELD_U(", ", *hdr, infppnum); | 
 |  | 
 | 	if (cnt_val || hdr->infpscps) | 
 | 		PRINT_FIELD_U(", ", *hdr, infpscps); | 
 | 	if (cnt_val || hdr->infpdcps) | 
 | 		PRINT_FIELD_U(", ", *hdr, infpdcps); | 
 | 	if (cnt_val || hdr->infpsifl) | 
 | 		PRINT_FIELD_U(", ", *hdr, infpsifl); | 
 | 	if (cnt_val || hdr->infpdifl) | 
 | 		PRINT_FIELD_U(", ", *hdr, infpdifl); | 
 |  | 
 | 	if (!abbrev(tcp) && !IS_ARRAY_ZERO(hdr->reserved_1__)) | 
 | 		PRINT_FIELD_HEX_ARRAY(", ", *hdr, reserved_1__); | 
 |  | 
 | 	if (id_val || !IS_ARRAY_ZERO(hdr->infppnam)) | 
 | 		PRINT_FIELD_EBCDIC(", ", *hdr, infppnam); | 
 |  | 
 | 	if (!abbrev(tcp)) { | 
 | 		if (wcap_val || hdr->infpwbcp) | 
 | 			PRINT_FIELD_WEIGHT(", ", *hdr, infpwbcp); | 
 | 		if (acap_val || hdr->infpabcp) | 
 | 			PRINT_FIELD_WEIGHT(", ", *hdr, infpabcp); | 
 | 		if (wcap_val || hdr->infpwbif) | 
 | 			PRINT_FIELD_WEIGHT(", ", *hdr, infpwbif); | 
 | 		if (acap_val || hdr->infpabif) | 
 | 			PRINT_FIELD_WEIGHT(", ", *hdr, infpabif); | 
 |  | 
 | 		if (!IS_ARRAY_ZERO(hdr->infplgnm)) { | 
 | 			PRINT_FIELD_EBCDIC(", ", *hdr, infplgnm); | 
 |  | 
 | 			PRINT_FIELD_WEIGHT(", ", *hdr, infplgcp); | 
 | 			PRINT_FIELD_WEIGHT(", ", *hdr, infplgif); | 
 | 		} else { | 
 | 			if (lpar_val) | 
 | 				PRINT_FIELD_HEX_ARRAY(", ", *hdr, infplgnm); | 
 | 			if (hdr->infplgcp) | 
 | 				PRINT_FIELD_X(", ", *hdr, infplgcp); | 
 | 			if (hdr->infplgif) | 
 | 				PRINT_FIELD_X(", ", *hdr, infplgif); | 
 | 		} | 
 |  | 
 | 		PRINT_UNKNOWN_TAIL(hdr, size); | 
 | 	} else { | 
 | 		tprints(", ..."); | 
 | 	} | 
 |  | 
 | 	tprints("}"); | 
 | } | 
 |  | 
 | static void | 
 | print_sthyi_hypervisor(struct tcb *tcp, struct sthyi_hypervisor *hdr, | 
 | 		       uint16_t size, int num, bool mt) | 
 | { | 
 | 	CHECK_SIZE(hdr, size, "hypervisor %d structure", num); | 
 |  | 
 | 	tprintf("/* hypervisor %d */ ", num); | 
 | 	PRINT_FIELD_0X("{", *hdr, infyflg1); | 
 | 	if (!abbrev(tcp) && hdr->infyflg1) | 
 | 		tprintf_comment("%s%s%s%s%#.0x%s", | 
 | 			hdr->infyflg1 & 0x80 ? | 
 | 				"0x80 - guest CPU usage had limiting is using " | 
 | 				"the consumption method" : "", | 
 | 			(hdr->infyflg1 & 0x80) && (hdr->infyflg1 & 0x40) ? | 
 | 				", " : "", | 
 | 			hdr->infyflg1 & 0x40 ? | 
 | 				"0x40 - LIMITHARD caps use prorated core time " | 
 | 				"for capping" : "", | 
 | 			(hdr->infyflg1 & 0xC0) && (hdr->infyflg1 & 0x3F) ? | 
 | 				", " : "", | 
 | 			hdr->infyflg1 & 0x3F, | 
 | 			hdr->infyflg1 & 0x3F ? " - ???" : ""); | 
 |  | 
 | 	if (!abbrev(tcp)) { | 
 | 		if (hdr->infyflg2) /* Reserved */ | 
 | 			PRINT_FIELD_0X(", ", *hdr, infyflg2); | 
 | 		if (hdr->infyval1) /* Reserved */ | 
 | 			PRINT_FIELD_0X(", ", *hdr, infyval1); | 
 | 		if (hdr->infyval2) /* Reserved */ | 
 | 			PRINT_FIELD_0X(", ", *hdr, infyval2); | 
 |  | 
 | 		PRINT_FIELD_U(", ", *hdr, infytype); | 
 | 		switch (hdr->infytype) { | 
 | 		case 1: | 
 | 			tprints_comment("z/VM is the hypervisor"); | 
 | 			break; | 
 | 		default: | 
 | 			tprints_comment("unknown hypervisor type"); | 
 | 		} | 
 |  | 
 | 		if (!IS_ARRAY_ZERO(hdr->reserved_1__)) | 
 | 			PRINT_FIELD_HEX_ARRAY(", ", *hdr, reserved_1__); | 
 |  | 
 | 		if (mt || hdr->infycpt) | 
 | 			PRINT_FIELD_U(", ", *hdr, infycpt); | 
 | 		if (mt || hdr->infyiflt) | 
 | 			PRINT_FIELD_U(", ", *hdr, infyiflt); | 
 | 	} | 
 |  | 
 | 	if (!abbrev(tcp) || !IS_BLANK(hdr->infysyid)) | 
 | 		PRINT_FIELD_EBCDIC(", ", *hdr, infysyid); | 
 | 	if (!abbrev(tcp) || !IS_BLANK(hdr->infyclnm)) | 
 | 		PRINT_FIELD_EBCDIC(", ", *hdr, infyclnm); | 
 |  | 
 | 	if (!abbrev(tcp) || hdr->infyscps) | 
 | 		PRINT_FIELD_U(", ", *hdr, infyscps); | 
 | 	if (!abbrev(tcp) || hdr->infydcps) | 
 | 		PRINT_FIELD_U(", ", *hdr, infydcps); | 
 | 	if (!abbrev(tcp) || hdr->infysifl) | 
 | 		PRINT_FIELD_U(", ", *hdr, infysifl); | 
 | 	if (!abbrev(tcp) || hdr->infydifl) | 
 | 		PRINT_FIELD_U(", ", *hdr, infydifl); | 
 |  | 
 | 	if (!abbrev(tcp)) { | 
 | 		PRINT_UNKNOWN_TAIL(hdr, size); | 
 | 	} else { | 
 | 		tprints(", ..."); | 
 | 	} | 
 |  | 
 | 	tprints("}"); | 
 | } | 
 |  | 
 | static void | 
 | print_sthyi_guest(struct tcb *tcp, struct sthyi_guest *hdr, uint16_t size, | 
 | 		  int num, bool mt) | 
 | { | 
 | 	CHECK_SIZE(hdr, size, "guest %d structure", num); | 
 |  | 
 | 	tprintf("/* guest %d */ ", num); | 
 | 	PRINT_FIELD_0X("{", *hdr, infgflg1); | 
 | 	if (!abbrev(tcp) && hdr->infgflg1) | 
 | 		tprintf_comment("%s%s%s%s%s%s%s%s%s%s%s%s%#.0x%s", | 
 | 			hdr->infgflg1 & 0x80 ? | 
 | 				"0x80 - guest is mobility enabled" : "", | 
 | 			(hdr->infgflg1 & 0x80) && (hdr->infgflg1 & 0x40) ? | 
 | 				", " : "", | 
 | 			hdr->infgflg1 & 0x40 ? | 
 | 				"0x40 - guest has multiple virtual CPU types" : | 
 | 				"", | 
 | 			(hdr->infgflg1 & 0xC0) && (hdr->infgflg1 & 0x20) ? | 
 | 				", " : "", | 
 | 			hdr->infgflg1 & 0x20 ? | 
 | 				"0x20 - guest CP dispatch type has LIMITHARD " | 
 | 				"cap" : "", | 
 | 			(hdr->infgflg1 & 0xE0) && (hdr->infgflg1 & 0x10) ? | 
 | 				", " : "", | 
 | 			hdr->infgflg1 & 0x10 ? | 
 | 				"0x10 - guest IFL dispatch type has LIMITHARD " | 
 | 				"cap" : "", | 
 | 			(hdr->infgflg1 & 0xF0) && (hdr->infgflg1 & 0x08) ? | 
 | 				", " : "", | 
 | 			hdr->infgflg1 & 0x08 ? | 
 | 				"0x08 - virtual CPs are thread dispatched" : | 
 | 				"", | 
 | 			(hdr->infgflg1 & 0xF8) && (hdr->infgflg1 & 0x04) ? | 
 | 				", " : "", | 
 | 			hdr->infgflg1 & 0x04 ? | 
 | 				"0x04 - virtual IFLs are thread dispatched" : | 
 | 				"", | 
 | 			(hdr->infgflg1 & 0xFC) && (hdr->infgflg1 & 0x03) ? | 
 | 				", " : "", | 
 | 			hdr->infgflg1 & 0x03, | 
 | 			hdr->infgflg1 & 0x03 ? " - ???" : ""); | 
 | 	if (!abbrev(tcp)) { | 
 | 		if (hdr->infgflg2) /* Reserved */ | 
 | 			PRINT_FIELD_0X(", ", *hdr, infgflg2); | 
 | 		if (hdr->infgval1) /* Reserved */ | 
 | 			PRINT_FIELD_0X(", ", *hdr, infgval1); | 
 | 		if (hdr->infgval2) /* Reserved */ | 
 | 			PRINT_FIELD_0X(", ", *hdr, infgval2); | 
 | 	} | 
 |  | 
 | 	PRINT_FIELD_EBCDIC(", ", *hdr, infgusid); | 
 |  | 
 | 	if (!abbrev(tcp) || hdr->infgscps) | 
 | 		PRINT_FIELD_U(", ", *hdr, infgscps); | 
 | 	if (!abbrev(tcp) || hdr->infgdcps) | 
 | 		PRINT_FIELD_U(", ", *hdr, infgdcps); | 
 |  | 
 | 	if (!abbrev(tcp)) { | 
 | 		PRINT_FIELD_U(", ", *hdr, infgcpdt); | 
 | 		switch (hdr->infgcpdt) { | 
 | 		case 0: | 
 | 			tprints_comment("General Purpose (CP)"); | 
 | 			break; | 
 | 		default: | 
 | 			tprints_comment("unknown"); | 
 | 		} | 
 |  | 
 | 		if (!IS_ARRAY_ZERO(hdr->reserved_1__)) | 
 | 			PRINT_FIELD_HEX_ARRAY(", ", *hdr, reserved_1__); | 
 | 	} | 
 |  | 
 | 	if (!abbrev(tcp) || hdr->infgcpcc) | 
 | 		PRINT_FIELD_WEIGHT(", ", *hdr, infgcpcc); | 
 |  | 
 | 	if (!abbrev(tcp) || hdr->infgsifl) | 
 | 		PRINT_FIELD_U(", ", *hdr, infgsifl); | 
 | 	if (!abbrev(tcp) || hdr->infgdifl) | 
 | 		PRINT_FIELD_U(", ", *hdr, infgdifl); | 
 |  | 
 | 	if (!abbrev(tcp)) { | 
 | 		PRINT_FIELD_U(", ", *hdr, infgifdt); | 
 | 		switch (hdr->infgifdt) { | 
 | 		case 0: | 
 | 			tprints_comment("General Purpose (CP)"); | 
 | 			break; | 
 | 		case 3: | 
 | 			tprints_comment("Integrated Facility for Linux (IFL)"); | 
 | 			break; | 
 | 		default: | 
 | 			tprints_comment("unknown"); | 
 | 		} | 
 |  | 
 | 		if (!IS_ARRAY_ZERO(hdr->reserved_2__)) | 
 | 			PRINT_FIELD_HEX_ARRAY(", ", *hdr, reserved_2__); | 
 | 	} | 
 |  | 
 | 	if (!abbrev(tcp) || hdr->infgifcc) | 
 | 		PRINT_FIELD_WEIGHT(", ", *hdr, infgifcc); | 
 |  | 
 | 	PRINT_FIELD_0X(", ", *hdr, infgpflg); | 
 | 	if (!abbrev(tcp) && hdr->infgpflg) | 
 | 		tprintf_comment("%s%s%s%s%s%s%s%s%s%s%#.0x%s", | 
 | 			hdr->infgpflg & 0x80 ? | 
 | 				"0x80 - CPU pool's CP virtual type has " | 
 | 				"LIMITHARD cap" : "", | 
 | 			(hdr->infgpflg & 0x80) && (hdr->infgpflg & 0x40) ? | 
 | 				", " : "", | 
 | 			hdr->infgpflg & 0x40 ? | 
 | 				"0x40 - CPU pool's CP virtual type has " | 
 | 				"CAPACITY cap" : "", | 
 | 			(hdr->infgpflg & 0xC0) && (hdr->infgpflg & 0x20) ? | 
 | 				", " : "", | 
 | 			hdr->infgpflg & 0x20 ? | 
 | 				"0x20 - CPU pool's IFL virtual type has " | 
 | 				"LIMITHARD cap" : "", | 
 | 			(hdr->infgpflg & 0xE0) && (hdr->infgpflg & 0x10) ? | 
 | 				", " : "", | 
 | 			hdr->infgpflg & 0x10 ? | 
 | 				"0x10 - CPU pool's IFL virtual type has " | 
 | 				"CAPACITY cap" : "", | 
 | 			(hdr->infgpflg & 0xF0) && (hdr->infgpflg & 0x08) ? | 
 | 				", " : "", | 
 | 			hdr->infgpflg & 0x08 ? | 
 | 				"0x08 - CPU pool uses prorated core time" : "", | 
 | 			(hdr->infgpflg & 0xF8) && (hdr->infgpflg & 0x07) ? | 
 | 				", " : "", | 
 | 			hdr->infgpflg & 0x07, | 
 | 			hdr->infgpflg & 0x07 ? " - ???" : ""); | 
 |  | 
 | 	if (!abbrev(tcp)) { | 
 | 		if (!IS_ARRAY_ZERO(hdr->reserved_3__)) | 
 | 			PRINT_FIELD_HEX_ARRAY(", ", *hdr, reserved_3__); | 
 |  | 
 | 		if (!IS_BLANK(hdr->infgpnam)) | 
 | 			PRINT_FIELD_EBCDIC(", ", *hdr, infgpnam); | 
 |  | 
 | 		PRINT_FIELD_WEIGHT(", ", *hdr, infgpccc); | 
 | 		PRINT_FIELD_WEIGHT(", ", *hdr, infgpicc); | 
 |  | 
 | 		PRINT_UNKNOWN_TAIL(hdr, size); | 
 | 	} else { | 
 | 		tprints(", ..."); | 
 | 	} | 
 |  | 
 | 	tprints("}"); | 
 | } | 
 |  | 
 | #define STHYI_PRINT_STRUCT(l_, name_) \ | 
 | 	do { \ | 
 | 		if (hdr->inf ##l_## off && hdr->inf ##l_## off + \ | 
 | 		    hdr->inf ##l_## len <= sizeof(data)) { \ | 
 | 			tprints(", "); \ | 
 | 			print_sthyi_ ##name_(tcp, (struct sthyi_ ##name_ *) \ | 
 | 					     (data + hdr->inf ##l_## off), \ | 
 | 					     hdr->inf ##l_## len, &mt); \ | 
 | 		} \ | 
 | 	} while (0) | 
 |  | 
 | #define STHYI_PRINT_HV_STRUCT(l_, n_, name_) \ | 
 | 	do { \ | 
 | 		if (hdr->inf ##l_## off ##n_ && hdr->inf ##l_## off ##n_ + \ | 
 | 		    hdr->inf ##l_## len ##n_ <= sizeof(data)) { \ | 
 | 			tprints(", "); \ | 
 | 			print_sthyi_ ##name_(tcp, (struct sthyi_ ##name_ *) \ | 
 | 					     (data + hdr->inf ##l_## off ##n_), \ | 
 | 					     hdr->inf ##l_## len ##n_, n_, mt); \ | 
 | 		} \ | 
 | 	} while (0) | 
 |  | 
 | static void | 
 | print_sthyi_buf(struct tcb *tcp, kernel_ulong_t ptr) | 
 | { | 
 | 	char data[PAGE_SIZE]; | 
 | 	struct sthyi_hdr *hdr = (struct sthyi_hdr *) data; | 
 | 	bool mt = false; | 
 |  | 
 | 	if (umove_or_printaddr(tcp, ptr, &data)) | 
 | 		return; | 
 |  | 
 | 	tprints("{"); | 
 |  | 
 | 	/* Header */ | 
 | 	PRINT_FIELD_0X("/* header */ {", *hdr, infhflg1); | 
 |  | 
 | 	if (abbrev(tcp)) { | 
 | 		tprints(", ..."); | 
 | 		goto sthyi_sections; | 
 | 	} | 
 |  | 
 | 	if (hdr->infhflg1) | 
 | 		tprintf_comment("%s%s%s%s%s%s%s%s%#.0x%s", | 
 | 			hdr->infhflg1 & 0x80 ? | 
 | 				"0x80 - Global Performance Data unavailable" : | 
 | 				"", | 
 | 			(hdr->infhflg1 & 0x80) && (hdr->infhflg1 & 0x40) ? | 
 | 				", " : "", | 
 | 			hdr->infhflg1 & 0x40 ? | 
 | 				"0x40 - One or more hypervisor levels below " | 
 | 				"this level does not support the STHYI " | 
 | 				"instruction" : "", | 
 | 			(hdr->infhflg1 & 0xC0) && (hdr->infhflg1 & 0x20) ? | 
 | 				", " : "", | 
 | 			hdr->infhflg1 & 0x20 ? | 
 | 				"0x20 - Virtualization stack is incomplete" : | 
 | 				"", | 
 | 			(hdr->infhflg1 & 0xE0) && (hdr->infhflg1 & 0x10) ? | 
 | 				", " : "", | 
 | 			hdr->infhflg1 & 0x10 ? | 
 | 				"0x10 - Execution environment is not within " | 
 | 				"a logical partition" : "", | 
 | 			(hdr->infhflg1 & 0xF0) && (hdr->infhflg1 & 0x0F) ? | 
 | 				", " : "", | 
 | 			hdr->infhflg1 & 0x0F, | 
 | 			hdr->infhflg1 & 0x0F ? " - ???" : ""); | 
 | 	if (hdr->infhflg2) /* Reserved */ | 
 | 		PRINT_FIELD_0X(", ", *hdr, infhflg2); | 
 | 	if (hdr->infhval1) /* Reserved */ | 
 | 		PRINT_FIELD_0X(", ", *hdr, infhval1); | 
 | 	if (hdr->infhval2) /* Reserved */ | 
 | 		PRINT_FIELD_0X(", ", *hdr, infhval2); | 
 |  | 
 | 	if (!IS_ARRAY_ZERO(hdr->reserved_1__)) | 
 | 		PRINT_FIELD_HEX_ARRAY(", ", *hdr, reserved_1__); | 
 |  | 
 | 	PRINT_FIELD_U(", ", *hdr, infhygct); | 
 | 	PRINT_FIELD_U(", ", *hdr, infhtotl); | 
 |  | 
 | 	PRINT_FIELD_U(", ", *hdr, infhdln); | 
 | 	PRINT_FIELD_U(", ", *hdr, infmoff); | 
 | 	PRINT_FIELD_U(", ", *hdr, infmlen); | 
 | 	PRINT_FIELD_U(", ", *hdr, infpoff); | 
 | 	PRINT_FIELD_U(", ", *hdr, infplen); | 
 |  | 
 | 	PRINT_FIELD_U(", ", *hdr, infhoff1); | 
 | 	PRINT_FIELD_U(", ", *hdr, infhlen1); | 
 | 	PRINT_FIELD_U(", ", *hdr, infgoff1); | 
 | 	PRINT_FIELD_U(", ", *hdr, infglen1); | 
 | 	PRINT_FIELD_U(", ", *hdr, infhoff2); | 
 | 	PRINT_FIELD_U(", ", *hdr, infhlen2); | 
 | 	PRINT_FIELD_U(", ", *hdr, infgoff2); | 
 | 	PRINT_FIELD_U(", ", *hdr, infglen2); | 
 | 	PRINT_FIELD_U(", ", *hdr, infhoff3); | 
 | 	PRINT_FIELD_U(", ", *hdr, infhlen3); | 
 | 	PRINT_FIELD_U(", ", *hdr, infgoff3); | 
 | 	PRINT_FIELD_U(", ", *hdr, infglen3); | 
 |  | 
 | 	PRINT_UNKNOWN_TAIL(hdr, hdr->infhdln); | 
 |  | 
 | sthyi_sections: | 
 | 	tprints("}"); | 
 |  | 
 | 	STHYI_PRINT_STRUCT(m, machine); | 
 | 	STHYI_PRINT_STRUCT(p, partition); | 
 |  | 
 | 	STHYI_PRINT_HV_STRUCT(h, 1, hypervisor); | 
 | 	STHYI_PRINT_HV_STRUCT(g, 1, guest); | 
 | 	STHYI_PRINT_HV_STRUCT(h, 2, hypervisor); | 
 | 	STHYI_PRINT_HV_STRUCT(g, 2, guest); | 
 | 	STHYI_PRINT_HV_STRUCT(h, 3, hypervisor); | 
 | 	STHYI_PRINT_HV_STRUCT(g, 3, guest); | 
 |  | 
 | 	tprints("}"); | 
 | } | 
 |  | 
 | /** | 
 |  * Wrapper for the s390 STHYI instruction that provides hypervisor information. | 
 |  * | 
 |  * See https://www.ibm.com/support/knowledgecenter/SSB27U_6.3.0/com.ibm.zvm.v630.hcpb4/hcpb4sth.htm | 
 |  * for the instruction documentation. | 
 |  * | 
 |  * The difference in the kernel wrapper is that it doesn't require the 4K | 
 |  * alignment for the resp_buffer page (as it just copies from the internal | 
 |  * cache). | 
 |  */ | 
 | SYS_FUNC(s390_sthyi) | 
 | { | 
 | 	/* in, function ID from s390_sthyi_function_codes */ | 
 | 	kernel_ulong_t function_code = tcp->u_arg[0]; | 
 | 	/* out, pointer to page-sized buffer */ | 
 | 	kernel_ulong_t resp_buffer_ptr = tcp->u_arg[1]; | 
 | 	/* out, pointer to u64 containing function result */ | 
 | 	kernel_ulong_t return_code_ptr = tcp->u_arg[2]; | 
 | 	/* in, should be 0, at the moment */ | 
 | 	kernel_ulong_t flags = tcp->u_arg[3]; | 
 |  | 
 | 	if (entering(tcp)) { | 
 | 		printxval64(s390_sthyi_function_codes, function_code, | 
 | 			    "STHYI_FC_???"); | 
 | 		tprints(", "); | 
 | 	} else { | 
 | 		switch (function_code) { | 
 | 		case STHYI_FC_CP_IFL_CAP: | 
 | 			print_sthyi_buf(tcp, resp_buffer_ptr); | 
 | 			break; | 
 |  | 
 | 		default: | 
 | 			printaddr(resp_buffer_ptr); | 
 | 		} | 
 |  | 
 | 		tprints(", "); | 
 | 		printnum_int64(tcp, return_code_ptr, "%" PRIu64); | 
 | 		tprintf(", %#" PRI_klx, flags); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Structures are written based on | 
 |  * https://www-304.ibm.com/support/docview.wss?uid=isg29c69415c1e82603c852576700058075a&aid=1#page=85 | 
 |  */ | 
 |  | 
 | struct guard_storage_control_block { | 
 | 	uint64_t reserved; | 
 | 	/** | 
 | 	 * Guard Storage Designation | 
 | 	 *  - Bits 0..J, J == 64-GSC - Guard Storage Origin (GSO) | 
 | 	 *  - Bits 53..55 - Guard Load Shift (GLS) | 
 | 	 *  - Bits 58..63 - Guard Storage Characteristic (GSC), this is J from | 
 | 	 *                  the first item, valud values are 25..56. | 
 | 	 */ | 
 | 	uint64_t gsd; | 
 | 	uint64_t gssm;     /**< Guard Storage Section Mask */ | 
 | 	uint64_t gs_epl_a; /**< Guard Storage Event Parameter List Address */ | 
 | }; | 
 |  | 
 | struct guard_storage_event_parameter_list { | 
 | 	uint8_t  pad1; | 
 | 	/** | 
 | 	 * Guard Storage Event Addressing Mode | 
 | 	 *  - 0x40 - Extended addressing mode (E) | 
 | 	 *  - 0x80 - Basic addressing mode (B) | 
 | 	 */ | 
 | 	uint8_t  gs_eam; | 
 | 	/** | 
 | 	 * Guard Storage Event Cause indication | 
 | 	 *  - 0x01 - CPU was in transaction execution mode (TX) | 
 | 	 *  - 0x02 - CPU was in constrained transaction execution mode (CX) | 
 | 	 *  - 0x80 - Instruction causing the event: 0 - LGG, 1 - LLGFGS | 
 | 	 */ | 
 | 	uint8_t  gs_eci; | 
 | 	/** | 
 | 	 * Guard Storage Event Access Information | 
 | 	 *  - 0x01 - DAT mode | 
 | 	 *  - Bits 1..2 - Address space indication | 
 | 	 *  - Bits 4..7 - AR number | 
 | 	 */ | 
 | 	uint8_t  gs_eai; | 
 | 	uint32_t pad2; | 
 | 	uint64_t gs_eha; /**< Guard Storage Event Handler Address */ | 
 | 	uint64_t gs_eia; /**< Guard Storage Event Instruction Address */ | 
 | 	uint64_t gs_eoa; /**< Guard Storage Event Operation Address */ | 
 | 	uint64_t gs_eir; /**< Guard Storage Event Intermediate Result */ | 
 | 	uint64_t gs_era; /**< Guard Storage Event Return Address */ | 
 | }; | 
 |  | 
 | static void | 
 | guard_storage_print_gsepl(struct tcb *tcp, uint64_t addr) | 
 | { | 
 | 	struct guard_storage_event_parameter_list gsepl; | 
 |  | 
 | 	/* Since it is 64-bit even on 31-bit s390... */ | 
 | 	if (sizeof(addr) > current_klongsize && | 
 | 	    addr >= (1ULL << (current_klongsize * 8))) { | 
 | 		tprintf("%#" PRIx64, addr); | 
 |  | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	if (umove_or_printaddr(tcp, addr, &gsepl)) | 
 | 		return; | 
 |  | 
 | 	tprints("[{"); | 
 |  | 
 | 	if (!abbrev(tcp)) { | 
 | 		if (gsepl.pad1) { | 
 | 			PRINT_FIELD_0X("", gsepl, pad1); | 
 | 			tprints(", "); | 
 | 		} | 
 |  | 
 | 		PRINT_FIELD_0X("",   gsepl, gs_eam); | 
 | 		tprintf_comment("extended addressing mode: %u, " | 
 | 				"basic addressing mode: %u", | 
 | 				!!(gsepl.gs_eam & 0x2), !!(gsepl.gs_eam & 0x1)); | 
 |  | 
 | 		PRINT_FIELD_0X(", ", gsepl, gs_eci); | 
 | 		tprintf_comment("CPU in TX: %u, CPU in CX: %u, instruction: %s", | 
 | 				!!(gsepl.gs_eci & 0x80), | 
 | 				!!(gsepl.gs_eci & 0x40), | 
 | 				gsepl.gs_eci & 0x01 ? "LLGFGS" : "LGG"); | 
 |  | 
 | 		PRINT_FIELD_0X(", ", gsepl, gs_eai); | 
 | 		tprintf_comment("DAT: %u, address space indication: %u, " | 
 | 				"AR number: %u", | 
 | 				!!(gsepl.gs_eai & 0x40), | 
 | 				(gsepl.gs_eai >> 4) & 0x3, | 
 | 				gsepl.gs_eai & 0xF); | 
 |  | 
 | 		if (gsepl.pad2) | 
 | 			PRINT_FIELD_0X(", ", gsepl, pad2); | 
 |  | 
 | 		tprints(", "); | 
 | 	} | 
 |  | 
 | 	PRINT_FIELD_X("", gsepl, gs_eha); | 
 |  | 
 | 	if (!abbrev(tcp)) { | 
 | 		PRINT_FIELD_X(", ", gsepl, gs_eia); | 
 | 		PRINT_FIELD_X(", ", gsepl, gs_eoa); | 
 | 		PRINT_FIELD_X(", ", gsepl, gs_eir); | 
 | 		PRINT_FIELD_X(", ", gsepl, gs_era); | 
 | 	} else { | 
 | 		tprints(", ..."); | 
 | 	} | 
 |  | 
 | 	tprints("}]"); | 
 | } | 
 |  | 
 | # define DIV_ROUND_UP(x,y) (((x) + ((y) - 1)) / (y)) | 
 |  | 
 | static void | 
 | guard_storage_print_gscb(struct tcb *tcp, kernel_ulong_t addr) | 
 | { | 
 | 	struct guard_storage_control_block gscb; | 
 |  | 
 | 	if (umove_or_printaddr(tcp, addr, &gscb)) | 
 | 		return; | 
 |  | 
 | 	tprints("{"); | 
 |  | 
 | 	if (gscb.reserved) { | 
 | 		PRINT_FIELD_0X("", gscb, reserved); | 
 | 		tprints(", "); | 
 | 	} | 
 |  | 
 | 	PRINT_FIELD_0X("", gscb, gsd); | 
 |  | 
 | 	if (!abbrev(tcp)) { | 
 | 		unsigned int gsc = gscb.gsd & 0x3F; | 
 | 		bool gsc_valid = gsc >= 25 && gsc <= 56; | 
 | 		tprintf_comment("GS origin: %#*.*" PRIx64 "%s, " | 
 | 				"guard load shift: %" PRIu64 ", " | 
 | 				"GS characteristic: %u", | 
 | 				gsc_valid ? 2 + DIV_ROUND_UP(64 - gsc, 4) : 0, | 
 | 				gsc_valid ? DIV_ROUND_UP(64 - gsc, 4) : 0, | 
 | 				gsc_valid ? gscb.gsd >> gsc : 0, | 
 | 				gsc_valid ? "" : "[invalid]", | 
 | 				(gscb.gsd >> 8) & 0x7, gsc); | 
 | 	} | 
 |  | 
 | 	PRINT_FIELD_0X(", ", gscb, gssm); | 
 |  | 
 | 	tprints(", gs_epl_a="); | 
 | 	guard_storage_print_gsepl(tcp, gscb.gs_epl_a); | 
 |  | 
 | 	tprints("}"); | 
 | } | 
 |  | 
 | SYS_FUNC(s390_guarded_storage) | 
 | { | 
 | 	int command = (int) tcp->u_arg[0]; | 
 | 	kernel_ulong_t gs_cb = tcp->u_arg[1]; | 
 |  | 
 | 	printxval(s390_guarded_storage_commands, command, "GS_???"); | 
 |  | 
 | 	switch (command) { | 
 | 	case GS_ENABLE: | 
 | 	case GS_DISABLE: | 
 | 	case GS_CLEAR_BC_CB: | 
 | 	case GS_BROADCAST: | 
 | 		break; | 
 |  | 
 | 	case GS_SET_BC_CB: | 
 | 		tprints(", "); | 
 | 		guard_storage_print_gscb(tcp, gs_cb); | 
 | 		break; | 
 |  | 
 | 	default: | 
 | 		tprints(", "); | 
 | 		printaddr(gs_cb); | 
 | 	} | 
 |  | 
 | 	return RVAL_DECODED; | 
 | } | 
 |  | 
 | SYS_FUNC(s390_runtime_instr) | 
 | { | 
 | 	int command = (int) tcp->u_arg[0]; | 
 | 	int signum = (int) tcp->u_arg[1]; | 
 |  | 
 |  | 
 | 	printxval_d(s390_runtime_instr_commands, command, | 
 | 		    "S390_RUNTIME_INSTR_???"); | 
 |  | 
 | 	/* | 
 | 	 * signum is ignored since Linux 4.4, but let's print it for start | 
 | 	 * command anyway. | 
 | 	 */ | 
 | 	switch (command) { | 
 | 	case S390_RUNTIME_INSTR_START: | 
 | 		tprints(", "); | 
 | 		tprints(signame(signum)); | 
 | 		break; | 
 |  | 
 | 	case S390_RUNTIME_INSTR_STOP: | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	return RVAL_DECODED; | 
 | } | 
 |  | 
 | SYS_FUNC(s390_pci_mmio_write) | 
 | { | 
 | 	kernel_ulong_t mmio_addr = tcp->u_arg[0]; | 
 | 	kernel_ulong_t user_buf  = tcp->u_arg[1]; | 
 | 	kernel_ulong_t length    = tcp->u_arg[2]; | 
 |  | 
 | 	tprintf("%#" PRI_klx ", ", mmio_addr); | 
 | 	printstr_ex(tcp, user_buf, length, QUOTE_FORCE_HEX); | 
 | 	tprintf(", %" PRI_klu, length); | 
 |  | 
 | 	return RVAL_DECODED; | 
 | } | 
 |  | 
 | SYS_FUNC(s390_pci_mmio_read) | 
 | { | 
 | 	kernel_ulong_t mmio_addr = tcp->u_arg[0]; | 
 | 	kernel_ulong_t user_buf  = tcp->u_arg[1]; | 
 | 	kernel_ulong_t length    = tcp->u_arg[2]; | 
 |  | 
 | 	if (entering(tcp)) { | 
 | 		tprintf("%#" PRI_klx ", ", mmio_addr); | 
 | 	} else { | 
 | 		if (!syserror(tcp)) | 
 | 			printstr_ex(tcp, user_buf, length, QUOTE_FORCE_HEX); | 
 | 		else | 
 | 			printaddr(user_buf); | 
 |  | 
 | 		tprintf(", %" PRI_klu, length); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | #endif /* defined S390 || defined S390X */ |