037802b576
* s390.c (SYS_FUNC(s390_runtime_instr)): Replace manual command argument decoding with a printxvals_ex call.
1264 lines
38 KiB
C
1264 lines
38 KiB
C
/*
|
|
* 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];
|
|
|
|
|
|
printxvals_ex(command, "S390_RUNTIME_INSTR_???",
|
|
XLAT_STYLE_VERBOSE | XLAT_STYLE_FMT_D,
|
|
s390_runtime_instr_commands, NULL);
|
|
|
|
/*
|
|
* 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 */
|