| /* |
| * 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 */ |