/*
* 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)
static bool
is_filled(char *ptr, char fill, size_t size)
{
while (size--)
if (*ptr++ != fill)
return false;
return true;
}
#define IS_ZERO(arr_) \
is_filled(arr_, '\0', sizeof(arr_) + MUST_BE_ARRAY(arr_))
#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_ZERO(hdr->infmtype))
PRINT_FIELD_EBCDIC(", ", *hdr, infmtype);
if (id_val || !IS_ZERO(hdr->infmmanu))
PRINT_FIELD_EBCDIC(", ", *hdr, infmmanu);
if (id_val || !IS_ZERO(hdr->infmseq))
PRINT_FIELD_EBCDIC(", ", *hdr, infmseq);
if (id_val || !IS_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_ZERO(hdr->reserved_1__))
PRINT_FIELD_HEX_ARRAY(", ", *hdr, reserved_1__);
if (id_val || !IS_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_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_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_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_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_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_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];
const char *command_descr =
xlookup(s390_runtime_instr_commands, command);
tprintf("%d", command);
tprints_comment(command_descr ? command_descr :
"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 */