Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f26ed983 authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

Merge tag 'kvm-s390-next-4.8-1' of...

Merge tag 'kvm-s390-next-4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux into HEAD

KVM: s390: Features and fixes for 4.8 part1

Four bigger things:
1. The implementation of the STHYI opcode in the kernel. This is used
   in libraries like qclib [1] to provide enough information for a
   capacity and usage based software licence pricing. The STHYI content
   is defined by the related z/VM documentation [2]. Its data can be
   composed by accessing several other interfaces provided by LPAR or
   the machine. This information is partially sensitive or root-only
   so the kernel does the necessary filtering.
2. Preparation for nested virtualization (VSIE). KVM should query the
   proper sclp interfaces for the availability of some features before
   using it. In the past we have been sloppy and simply assumed that
   several features are available. With this we should be able to handle
   most cases of a missing feature.
3. CPU model interfaces extended by some additional features that are
   not covered by a facility bit in STFLE. For example all the crypto
   instructions of the coprocessor provide a query function. As reality
   tends to be more complex (e.g. export regulations might block some
   algorithms) we have to provide additional interfaces to query or
   set these non-stfle features.
4. Several fixes and changes detected and fixed when doing 1-3.

All features change base s390 code. All relevant patches have an ACK
from the s390 or component maintainers.

The next pull request for 4.8 (part2) will contain the implementation
of VSIE.

[1] http://www.ibm.com/developerworks/linux/linux390/qclib.html
[2] https://www.ibm.com/support/knowledgecenter/SSB27U_6.3.0/com.ibm.zvm.v630.hcpb4/hcpb4sth.htm
parents bb3541f1 a7e19ab5
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -2520,6 +2520,7 @@ Parameters: struct kvm_device_attr
Returns: 0 on success, -1 on error
Returns: 0 on success, -1 on error
Errors:
Errors:
  ENXIO:  The group or attribute is unknown/unsupported for this device
  ENXIO:  The group or attribute is unknown/unsupported for this device
          or hardware support is missing.
  EPERM:  The attribute cannot (currently) be accessed this way
  EPERM:  The attribute cannot (currently) be accessed this way
          (e.g. read-only attribute, or attribute that only makes
          (e.g. read-only attribute, or attribute that only makes
          sense when the device is in a different state)
          sense when the device is in a different state)
@@ -2547,6 +2548,7 @@ Parameters: struct kvm_device_attr
Returns: 0 on success, -1 on error
Returns: 0 on success, -1 on error
Errors:
Errors:
  ENXIO:  The group or attribute is unknown/unsupported for this device
  ENXIO:  The group or attribute is unknown/unsupported for this device
          or hardware support is missing.


Tests whether a device supports a particular attribute.  A successful
Tests whether a device supports a particular attribute.  A successful
return indicates the attribute is implemented.  It does not necessarily
return indicates the attribute is implemented.  It does not necessarily
+86 −1
Original line number Original line Diff line number Diff line
@@ -20,7 +20,8 @@ Enables Collaborative Memory Management Assist (CMMA) for the virtual machine.


1.2. ATTRIBUTE: KVM_S390_VM_MEM_CLR_CMMA
1.2. ATTRIBUTE: KVM_S390_VM_MEM_CLR_CMMA
Parameters: none
Parameters: none
Returns: 0
Returns: -EINVAL if CMMA was not enabled
         0 otherwise


Clear the CMMA status for all guest pages, so any pages the guest marked
Clear the CMMA status for all guest pages, so any pages the guest marked
as unused are again used any may not be reclaimed by the host.
as unused are again used any may not be reclaimed by the host.
@@ -85,6 +86,90 @@ Returns: -EBUSY in case 1 or more vcpus are already activated (only in write
	    -ENOMEM if not enough memory is available to process the ioctl
	    -ENOMEM if not enough memory is available to process the ioctl
	    0 in case of success
	    0 in case of success


2.3. ATTRIBUTE: KVM_S390_VM_CPU_MACHINE_FEAT (r/o)

Allows user space to retrieve available cpu features. A feature is available if
provided by the hardware and supported by kvm. In theory, cpu features could
even be completely emulated by kvm.

struct kvm_s390_vm_cpu_feat {
        __u64 feat[16]; # Bitmap (1 = feature available), MSB 0 bit numbering
};

Parameters: address of a buffer to load the feature list from.
Returns:    -EFAULT if the given address is not accessible from kernel space.
	    0 in case of success.

2.4. ATTRIBUTE: KVM_S390_VM_CPU_PROCESSOR_FEAT (r/w)

Allows user space to retrieve or change enabled cpu features for all VCPUs of a
VM. Features that are not available cannot be enabled.

See 2.3. for a description of the parameter struct.

Parameters: address of a buffer to store/load the feature list from.
Returns:    -EFAULT if the given address is not accessible from kernel space.
	    -EINVAL if a cpu feature that is not available is to be enabled.
	    -EBUSY if at least one VCPU has already been defined.
	    0 in case of success.

2.5. ATTRIBUTE: KVM_S390_VM_CPU_MACHINE_SUBFUNC (r/o)

Allows user space to retrieve available cpu subfunctions without any filtering
done by a set IBC. These subfunctions are indicated to the guest VCPU via
query or "test bit" subfunctions and used e.g. by cpacf functions, plo and ptff.

A subfunction block is only valid if KVM_S390_VM_CPU_MACHINE contains the
STFL(E) bit introducing the affected instruction. If the affected instruction
indicates subfunctions via a "query subfunction", the response block is
contained in the returned struct. If the affected instruction
indicates subfunctions via a "test bit" mechanism, the subfunction codes are
contained in the returned struct in MSB 0 bit numbering.

struct kvm_s390_vm_cpu_subfunc {
       u8 plo[32];           # always valid (ESA/390 feature)
       u8 ptff[16];          # valid with TOD-clock steering
       u8 kmac[16];          # valid with Message-Security-Assist
       u8 kmc[16];           # valid with Message-Security-Assist
       u8 km[16];            # valid with Message-Security-Assist
       u8 kimd[16];          # valid with Message-Security-Assist
       u8 klmd[16];          # valid with Message-Security-Assist
       u8 pckmo[16];         # valid with Message-Security-Assist-Extension 3
       u8 kmctr[16];         # valid with Message-Security-Assist-Extension 4
       u8 kmf[16];           # valid with Message-Security-Assist-Extension 4
       u8 kmo[16];           # valid with Message-Security-Assist-Extension 4
       u8 pcc[16];           # valid with Message-Security-Assist-Extension 4
       u8 ppno[16];          # valid with Message-Security-Assist-Extension 5
       u8 reserved[1824];    # reserved for future instructions
};

Parameters: address of a buffer to load the subfunction blocks from.
Returns:    -EFAULT if the given address is not accessible from kernel space.
	    0 in case of success.

2.6. ATTRIBUTE: KVM_S390_VM_CPU_PROCESSOR_SUBFUNC (r/w)

Allows user space to retrieve or change cpu subfunctions to be indicated for
all VCPUs of a VM. This attribute will only be available if kernel and
hardware support are in place.

The kernel uses the configured subfunction blocks for indication to
the guest. A subfunction block will only be used if the associated STFL(E) bit
has not been disabled by user space (so the instruction to be queried is
actually available for the guest).

As long as no data has been written, a read will fail. The IBC will be used
to determine available subfunctions in this case, this will guarantee backward
compatibility.

See 2.5. for a description of the parameter struct.

Parameters: address of a buffer to store/load the subfunction blocks from.
Returns:    -EFAULT if the given address is not accessible from kernel space.
	    -EINVAL when reading, if there was no write yet.
	    -EBUSY if at least one VCPU has already been defined.
	    0 in case of success.

3. GROUP: KVM_S390_VM_TOD
3. GROUP: KVM_S390_VM_TOD
Architectures: s390
Architectures: s390


+107 −268
Original line number Original line Diff line number Diff line
@@ -19,29 +19,10 @@
#include <asm/ebcdic.h>
#include <asm/ebcdic.h>
#include "hypfs.h"
#include "hypfs.h"


#define LPAR_NAME_LEN 8		/* lpar name len in diag 204 data */
#define CPU_NAME_LEN 16		/* type name len of cpus in diag224 name table */
#define TMP_SIZE 64		/* size of temporary buffers */
#define TMP_SIZE 64		/* size of temporary buffers */


#define DBFS_D204_HDR_VERSION	0
#define DBFS_D204_HDR_VERSION	0


/* diag 204 subcodes */
enum diag204_sc {
	SUBC_STIB4 = 4,
	SUBC_RSI = 5,
	SUBC_STIB6 = 6,
	SUBC_STIB7 = 7
};

/* The two available diag 204 data formats */
enum diag204_format {
	INFO_SIMPLE = 0,
	INFO_EXT = 0x00010000
};

/* bit is set in flags, when physical cpu info is included in diag 204 data */
#define LPAR_PHYS_FLG  0x80

static char *diag224_cpu_names;			/* diag 224 name table */
static char *diag224_cpu_names;			/* diag 224 name table */
static enum diag204_sc diag204_store_sc;	/* used subcode for store */
static enum diag204_sc diag204_store_sc;	/* used subcode for store */
static enum diag204_format diag204_info_type;	/* used diag 204 data format */
static enum diag204_format diag204_info_type;	/* used diag 204 data format */
@@ -53,7 +34,7 @@ static int diag204_buf_pages; /* number of pages for diag204 data */
static struct dentry *dbfs_d204_file;
static struct dentry *dbfs_d204_file;


/*
/*
 * DIAG 204 data structures and member access functions.
 * DIAG 204 member access functions.
 *
 *
 * Since we have two different diag 204 data formats for old and new s390
 * Since we have two different diag 204 data formats for old and new s390
 * machines, we do not access the structs directly, but use getter functions for
 * machines, we do not access the structs directly, but use getter functions for
@@ -62,302 +43,173 @@ static struct dentry *dbfs_d204_file;


/* Time information block */
/* Time information block */


struct info_blk_hdr {
	__u8  npar;
	__u8  flags;
	__u16 tslice;
	__u16 phys_cpus;
	__u16 this_part;
	__u64 curtod;
} __attribute__ ((packed));

struct x_info_blk_hdr {
	__u8  npar;
	__u8  flags;
	__u16 tslice;
	__u16 phys_cpus;
	__u16 this_part;
	__u64 curtod1;
	__u64 curtod2;
	char reserved[40];
} __attribute__ ((packed));

static inline int info_blk_hdr__size(enum diag204_format type)
static inline int info_blk_hdr__size(enum diag204_format type)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return sizeof(struct info_blk_hdr);
		return sizeof(struct diag204_info_blk_hdr);
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return sizeof(struct x_info_blk_hdr);
		return sizeof(struct diag204_x_info_blk_hdr);
}
}


static inline __u8 info_blk_hdr__npar(enum diag204_format type, void *hdr)
static inline __u8 info_blk_hdr__npar(enum diag204_format type, void *hdr)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return ((struct info_blk_hdr *)hdr)->npar;
		return ((struct diag204_info_blk_hdr *)hdr)->npar;
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return ((struct x_info_blk_hdr *)hdr)->npar;
		return ((struct diag204_x_info_blk_hdr *)hdr)->npar;
}
}


static inline __u8 info_blk_hdr__flags(enum diag204_format type, void *hdr)
static inline __u8 info_blk_hdr__flags(enum diag204_format type, void *hdr)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return ((struct info_blk_hdr *)hdr)->flags;
		return ((struct diag204_info_blk_hdr *)hdr)->flags;
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return ((struct x_info_blk_hdr *)hdr)->flags;
		return ((struct diag204_x_info_blk_hdr *)hdr)->flags;
}
}


static inline __u16 info_blk_hdr__pcpus(enum diag204_format type, void *hdr)
static inline __u16 info_blk_hdr__pcpus(enum diag204_format type, void *hdr)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return ((struct info_blk_hdr *)hdr)->phys_cpus;
		return ((struct diag204_info_blk_hdr *)hdr)->phys_cpus;
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return ((struct x_info_blk_hdr *)hdr)->phys_cpus;
		return ((struct diag204_x_info_blk_hdr *)hdr)->phys_cpus;
}
}


/* Partition header */
/* Partition header */


struct part_hdr {
	__u8 pn;
	__u8 cpus;
	char reserved[6];
	char part_name[LPAR_NAME_LEN];
} __attribute__ ((packed));

struct x_part_hdr {
	__u8  pn;
	__u8  cpus;
	__u8  rcpus;
	__u8  pflag;
	__u32 mlu;
	char  part_name[LPAR_NAME_LEN];
	char  lpc_name[8];
	char  os_name[8];
	__u64 online_cs;
	__u64 online_es;
	__u8  upid;
	char  reserved1[3];
	__u32 group_mlu;
	char  group_name[8];
	char  reserved2[32];
} __attribute__ ((packed));

static inline int part_hdr__size(enum diag204_format type)
static inline int part_hdr__size(enum diag204_format type)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return sizeof(struct part_hdr);
		return sizeof(struct diag204_part_hdr);
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return sizeof(struct x_part_hdr);
		return sizeof(struct diag204_x_part_hdr);
}
}


static inline __u8 part_hdr__rcpus(enum diag204_format type, void *hdr)
static inline __u8 part_hdr__rcpus(enum diag204_format type, void *hdr)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return ((struct part_hdr *)hdr)->cpus;
		return ((struct diag204_part_hdr *)hdr)->cpus;
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return ((struct x_part_hdr *)hdr)->rcpus;
		return ((struct diag204_x_part_hdr *)hdr)->rcpus;
}
}


static inline void part_hdr__part_name(enum diag204_format type, void *hdr,
static inline void part_hdr__part_name(enum diag204_format type, void *hdr,
				       char *name)
				       char *name)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		memcpy(name, ((struct part_hdr *)hdr)->part_name,
		memcpy(name, ((struct diag204_part_hdr *)hdr)->part_name,
		       LPAR_NAME_LEN);
		       DIAG204_LPAR_NAME_LEN);
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		memcpy(name, ((struct x_part_hdr *)hdr)->part_name,
		memcpy(name, ((struct diag204_x_part_hdr *)hdr)->part_name,
		       LPAR_NAME_LEN);
		       DIAG204_LPAR_NAME_LEN);
	EBCASC(name, LPAR_NAME_LEN);
	EBCASC(name, DIAG204_LPAR_NAME_LEN);
	name[LPAR_NAME_LEN] = 0;
	name[DIAG204_LPAR_NAME_LEN] = 0;
	strim(name);
	strim(name);
}
}


struct cpu_info {
	__u16 cpu_addr;
	char  reserved1[2];
	__u8  ctidx;
	__u8  cflag;
	__u16 weight;
	__u64 acc_time;
	__u64 lp_time;
} __attribute__ ((packed));

struct x_cpu_info {
	__u16 cpu_addr;
	char  reserved1[2];
	__u8  ctidx;
	__u8  cflag;
	__u16 weight;
	__u64 acc_time;
	__u64 lp_time;
	__u16 min_weight;
	__u16 cur_weight;
	__u16 max_weight;
	char  reseved2[2];
	__u64 online_time;
	__u64 wait_time;
	__u32 pma_weight;
	__u32 polar_weight;
	char  reserved3[40];
} __attribute__ ((packed));

/* CPU info block */
/* CPU info block */


static inline int cpu_info__size(enum diag204_format type)
static inline int cpu_info__size(enum diag204_format type)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return sizeof(struct cpu_info);
		return sizeof(struct diag204_cpu_info);
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return sizeof(struct x_cpu_info);
		return sizeof(struct diag204_x_cpu_info);
}
}


static inline __u8 cpu_info__ctidx(enum diag204_format type, void *hdr)
static inline __u8 cpu_info__ctidx(enum diag204_format type, void *hdr)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return ((struct cpu_info *)hdr)->ctidx;
		return ((struct diag204_cpu_info *)hdr)->ctidx;
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return ((struct x_cpu_info *)hdr)->ctidx;
		return ((struct diag204_x_cpu_info *)hdr)->ctidx;
}
}


static inline __u16 cpu_info__cpu_addr(enum diag204_format type, void *hdr)
static inline __u16 cpu_info__cpu_addr(enum diag204_format type, void *hdr)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return ((struct cpu_info *)hdr)->cpu_addr;
		return ((struct diag204_cpu_info *)hdr)->cpu_addr;
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return ((struct x_cpu_info *)hdr)->cpu_addr;
		return ((struct diag204_x_cpu_info *)hdr)->cpu_addr;
}
}


static inline __u64 cpu_info__acc_time(enum diag204_format type, void *hdr)
static inline __u64 cpu_info__acc_time(enum diag204_format type, void *hdr)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return ((struct cpu_info *)hdr)->acc_time;
		return ((struct diag204_cpu_info *)hdr)->acc_time;
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return ((struct x_cpu_info *)hdr)->acc_time;
		return ((struct diag204_x_cpu_info *)hdr)->acc_time;
}
}


static inline __u64 cpu_info__lp_time(enum diag204_format type, void *hdr)
static inline __u64 cpu_info__lp_time(enum diag204_format type, void *hdr)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return ((struct cpu_info *)hdr)->lp_time;
		return ((struct diag204_cpu_info *)hdr)->lp_time;
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return ((struct x_cpu_info *)hdr)->lp_time;
		return ((struct diag204_x_cpu_info *)hdr)->lp_time;
}
}


static inline __u64 cpu_info__online_time(enum diag204_format type, void *hdr)
static inline __u64 cpu_info__online_time(enum diag204_format type, void *hdr)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return 0;	/* online_time not available in simple info */
		return 0;	/* online_time not available in simple info */
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return ((struct x_cpu_info *)hdr)->online_time;
		return ((struct diag204_x_cpu_info *)hdr)->online_time;
}
}


/* Physical header */
/* Physical header */


struct phys_hdr {
	char reserved1[1];
	__u8 cpus;
	char reserved2[6];
	char mgm_name[8];
} __attribute__ ((packed));

struct x_phys_hdr {
	char reserved1[1];
	__u8 cpus;
	char reserved2[6];
	char mgm_name[8];
	char reserved3[80];
} __attribute__ ((packed));

static inline int phys_hdr__size(enum diag204_format type)
static inline int phys_hdr__size(enum diag204_format type)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return sizeof(struct phys_hdr);
		return sizeof(struct diag204_phys_hdr);
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return sizeof(struct x_phys_hdr);
		return sizeof(struct diag204_x_phys_hdr);
}
}


static inline __u8 phys_hdr__cpus(enum diag204_format type, void *hdr)
static inline __u8 phys_hdr__cpus(enum diag204_format type, void *hdr)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return ((struct phys_hdr *)hdr)->cpus;
		return ((struct diag204_phys_hdr *)hdr)->cpus;
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return ((struct x_phys_hdr *)hdr)->cpus;
		return ((struct diag204_x_phys_hdr *)hdr)->cpus;
}
}


/* Physical CPU info block */
/* Physical CPU info block */


struct phys_cpu {
	__u16 cpu_addr;
	char  reserved1[2];
	__u8  ctidx;
	char  reserved2[3];
	__u64 mgm_time;
	char  reserved3[8];
} __attribute__ ((packed));

struct x_phys_cpu {
	__u16 cpu_addr;
	char  reserved1[2];
	__u8  ctidx;
	char  reserved2[3];
	__u64 mgm_time;
	char  reserved3[80];
} __attribute__ ((packed));

static inline int phys_cpu__size(enum diag204_format type)
static inline int phys_cpu__size(enum diag204_format type)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return sizeof(struct phys_cpu);
		return sizeof(struct diag204_phys_cpu);
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return sizeof(struct x_phys_cpu);
		return sizeof(struct diag204_x_phys_cpu);
}
}


static inline __u16 phys_cpu__cpu_addr(enum diag204_format type, void *hdr)
static inline __u16 phys_cpu__cpu_addr(enum diag204_format type, void *hdr)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return ((struct phys_cpu *)hdr)->cpu_addr;
		return ((struct diag204_phys_cpu *)hdr)->cpu_addr;
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return ((struct x_phys_cpu *)hdr)->cpu_addr;
		return ((struct diag204_x_phys_cpu *)hdr)->cpu_addr;
}
}


static inline __u64 phys_cpu__mgm_time(enum diag204_format type, void *hdr)
static inline __u64 phys_cpu__mgm_time(enum diag204_format type, void *hdr)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return ((struct phys_cpu *)hdr)->mgm_time;
		return ((struct diag204_phys_cpu *)hdr)->mgm_time;
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return ((struct x_phys_cpu *)hdr)->mgm_time;
		return ((struct diag204_x_phys_cpu *)hdr)->mgm_time;
}
}


static inline __u64 phys_cpu__ctidx(enum diag204_format type, void *hdr)
static inline __u64 phys_cpu__ctidx(enum diag204_format type, void *hdr)
{
{
	if (type == INFO_SIMPLE)
	if (type == DIAG204_INFO_SIMPLE)
		return ((struct phys_cpu *)hdr)->ctidx;
		return ((struct diag204_phys_cpu *)hdr)->ctidx;
	else /* INFO_EXT */
	else /* DIAG204_INFO_EXT */
		return ((struct x_phys_cpu *)hdr)->ctidx;
		return ((struct diag204_x_phys_cpu *)hdr)->ctidx;
}
}


/* Diagnose 204 functions */
/* Diagnose 204 functions */

static inline int __diag204(unsigned long subcode, unsigned long size, void *addr)
{
	register unsigned long _subcode asm("0") = subcode;
	register unsigned long _size asm("1") = size;

	asm volatile(
		"	diag	%2,%0,0x204\n"
		"0:\n"
		EX_TABLE(0b,0b)
		: "+d" (_subcode), "+d" (_size) : "d" (addr) : "memory");
	if (_subcode)
		return -1;
	return _size;
}

static int diag204(unsigned long subcode, unsigned long size, void *addr)
{
	diag_stat_inc(DIAG_STAT_X204);
	return __diag204(subcode, size, addr);
}

/*
/*
 * For the old diag subcode 4 with simple data format we have to use real
 * For the old diag subcode 4 with simple data format we have to use real
 * memory. If we use subcode 6 or 7 with extended data format, we can (and
 * memory. If we use subcode 6 or 7 with extended data format, we can (and
@@ -409,12 +261,12 @@ static void *diag204_get_buffer(enum diag204_format fmt, int *pages)
		*pages = diag204_buf_pages;
		*pages = diag204_buf_pages;
		return diag204_buf;
		return diag204_buf;
	}
	}
	if (fmt == INFO_SIMPLE) {
	if (fmt == DIAG204_INFO_SIMPLE) {
		*pages = 1;
		*pages = 1;
		return diag204_alloc_rbuf();
		return diag204_alloc_rbuf();
	} else {/* INFO_EXT */
	} else {/* DIAG204_INFO_EXT */
		*pages = diag204((unsigned long)SUBC_RSI |
		*pages = diag204((unsigned long)DIAG204_SUBC_RSI |
				 (unsigned long)INFO_EXT, 0, NULL);
				 (unsigned long)DIAG204_INFO_EXT, 0, NULL);
		if (*pages <= 0)
		if (*pages <= 0)
			return ERR_PTR(-ENOSYS);
			return ERR_PTR(-ENOSYS);
		else
		else
@@ -441,18 +293,18 @@ static int diag204_probe(void)
	void *buf;
	void *buf;
	int pages, rc;
	int pages, rc;


	buf = diag204_get_buffer(INFO_EXT, &pages);
	buf = diag204_get_buffer(DIAG204_INFO_EXT, &pages);
	if (!IS_ERR(buf)) {
	if (!IS_ERR(buf)) {
		if (diag204((unsigned long)SUBC_STIB7 |
		if (diag204((unsigned long)DIAG204_SUBC_STIB7 |
			    (unsigned long)INFO_EXT, pages, buf) >= 0) {
			    (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) {
			diag204_store_sc = SUBC_STIB7;
			diag204_store_sc = DIAG204_SUBC_STIB7;
			diag204_info_type = INFO_EXT;
			diag204_info_type = DIAG204_INFO_EXT;
			goto out;
			goto out;
		}
		}
		if (diag204((unsigned long)SUBC_STIB6 |
		if (diag204((unsigned long)DIAG204_SUBC_STIB6 |
			    (unsigned long)INFO_EXT, pages, buf) >= 0) {
			    (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) {
			diag204_store_sc = SUBC_STIB6;
			diag204_store_sc = DIAG204_SUBC_STIB6;
			diag204_info_type = INFO_EXT;
			diag204_info_type = DIAG204_INFO_EXT;
			goto out;
			goto out;
		}
		}
		diag204_free_buffer();
		diag204_free_buffer();
@@ -460,15 +312,15 @@ static int diag204_probe(void)


	/* subcodes 6 and 7 failed, now try subcode 4 */
	/* subcodes 6 and 7 failed, now try subcode 4 */


	buf = diag204_get_buffer(INFO_SIMPLE, &pages);
	buf = diag204_get_buffer(DIAG204_INFO_SIMPLE, &pages);
	if (IS_ERR(buf)) {
	if (IS_ERR(buf)) {
		rc = PTR_ERR(buf);
		rc = PTR_ERR(buf);
		goto fail_alloc;
		goto fail_alloc;
	}
	}
	if (diag204((unsigned long)SUBC_STIB4 |
	if (diag204((unsigned long)DIAG204_SUBC_STIB4 |
		    (unsigned long)INFO_SIMPLE, pages, buf) >= 0) {
		    (unsigned long)DIAG204_INFO_SIMPLE, pages, buf) >= 0) {
		diag204_store_sc = SUBC_STIB4;
		diag204_store_sc = DIAG204_SUBC_STIB4;
		diag204_info_type = INFO_SIMPLE;
		diag204_info_type = DIAG204_INFO_SIMPLE;
		goto out;
		goto out;
	} else {
	} else {
		rc = -ENOSYS;
		rc = -ENOSYS;
@@ -508,20 +360,6 @@ static void *diag204_store(void)


/* Diagnose 224 functions */
/* Diagnose 224 functions */


static int diag224(void *ptr)
{
	int rc = -EOPNOTSUPP;

	diag_stat_inc(DIAG_STAT_X224);
	asm volatile(
		"	diag	%1,%2,0x224\n"
		"0:	lhi	%0,0x0\n"
		"1:\n"
		EX_TABLE(0b,1b)
		: "+d" (rc) :"d" (0), "d" (ptr) : "memory");
	return rc;
}

static int diag224_get_name_table(void)
static int diag224_get_name_table(void)
{
{
	/* memory must be below 2GB */
	/* memory must be below 2GB */
@@ -543,9 +381,9 @@ static void diag224_delete_name_table(void)


static int diag224_idx2name(int index, char *name)
static int diag224_idx2name(int index, char *name)
{
{
	memcpy(name, diag224_cpu_names + ((index + 1) * CPU_NAME_LEN),
	memcpy(name, diag224_cpu_names + ((index + 1) * DIAG204_CPU_NAME_LEN),
		CPU_NAME_LEN);
	       DIAG204_CPU_NAME_LEN);
	name[CPU_NAME_LEN] = 0;
	name[DIAG204_CPU_NAME_LEN] = 0;
	strim(name);
	strim(name);
	return 0;
	return 0;
}
}
@@ -601,7 +439,7 @@ __init int hypfs_diag_init(void)
		pr_err("The hardware system does not support hypfs\n");
		pr_err("The hardware system does not support hypfs\n");
		return -ENODATA;
		return -ENODATA;
	}
	}
	if (diag204_info_type == INFO_EXT) {
	if (diag204_info_type == DIAG204_INFO_EXT) {
		rc = hypfs_dbfs_create_file(&dbfs_file_d204);
		rc = hypfs_dbfs_create_file(&dbfs_file_d204);
		if (rc)
		if (rc)
			return rc;
			return rc;
@@ -649,7 +487,7 @@ static int hypfs_create_cpu_files(struct dentry *cpus_dir, void *cpu_info)
			      cpu_info__lp_time(diag204_info_type, cpu_info));
			      cpu_info__lp_time(diag204_info_type, cpu_info));
	if (IS_ERR(rc))
	if (IS_ERR(rc))
		return PTR_ERR(rc);
		return PTR_ERR(rc);
	if (diag204_info_type == INFO_EXT) {
	if (diag204_info_type == DIAG204_INFO_EXT) {
		rc = hypfs_create_u64(cpu_dir, "onlinetime",
		rc = hypfs_create_u64(cpu_dir, "onlinetime",
				      cpu_info__online_time(diag204_info_type,
				      cpu_info__online_time(diag204_info_type,
							    cpu_info));
							    cpu_info));
@@ -665,12 +503,12 @@ static void *hypfs_create_lpar_files(struct dentry *systems_dir, void *part_hdr)
{
{
	struct dentry *cpus_dir;
	struct dentry *cpus_dir;
	struct dentry *lpar_dir;
	struct dentry *lpar_dir;
	char lpar_name[LPAR_NAME_LEN + 1];
	char lpar_name[DIAG204_LPAR_NAME_LEN + 1];
	void *cpu_info;
	void *cpu_info;
	int i;
	int i;


	part_hdr__part_name(diag204_info_type, part_hdr, lpar_name);
	part_hdr__part_name(diag204_info_type, part_hdr, lpar_name);
	lpar_name[LPAR_NAME_LEN] = 0;
	lpar_name[DIAG204_LPAR_NAME_LEN] = 0;
	lpar_dir = hypfs_mkdir(systems_dir, lpar_name);
	lpar_dir = hypfs_mkdir(systems_dir, lpar_name);
	if (IS_ERR(lpar_dir))
	if (IS_ERR(lpar_dir))
		return lpar_dir;
		return lpar_dir;
@@ -753,7 +591,8 @@ int hypfs_diag_create_files(struct dentry *root)
			goto err_out;
			goto err_out;
		}
		}
	}
	}
	if (info_blk_hdr__flags(diag204_info_type, time_hdr) & LPAR_PHYS_FLG) {
	if (info_blk_hdr__flags(diag204_info_type, time_hdr) &
	    DIAG204_LPAR_PHYS_FLG) {
		ptr = hypfs_create_phys_files(root, part_hdr);
		ptr = hypfs_create_phys_files(root, part_hdr);
		if (IS_ERR(ptr)) {
		if (IS_ERR(ptr)) {
			rc = PTR_ERR(ptr);
			rc = PTR_ERR(ptr);
+10 −0
Original line number Original line Diff line number Diff line
@@ -20,6 +20,9 @@
#define CPACF_KMC		0xb92f		/* MSA	*/
#define CPACF_KMC		0xb92f		/* MSA	*/
#define CPACF_KIMD		0xb93e		/* MSA	*/
#define CPACF_KIMD		0xb93e		/* MSA	*/
#define CPACF_KLMD		0xb93f		/* MSA	*/
#define CPACF_KLMD		0xb93f		/* MSA	*/
#define CPACF_PCKMO		0xb928		/* MSA3 */
#define CPACF_KMF		0xb92a		/* MSA4 */
#define CPACF_KMO		0xb92b		/* MSA4 */
#define CPACF_PCC		0xb92c		/* MSA4 */
#define CPACF_PCC		0xb92c		/* MSA4 */
#define CPACF_KMCTR		0xb92d		/* MSA4 */
#define CPACF_KMCTR		0xb92d		/* MSA4 */
#define CPACF_PPNO		0xb93c		/* MSA5 */
#define CPACF_PPNO		0xb93c		/* MSA5 */
@@ -136,6 +139,7 @@ static inline void __cpacf_query(unsigned int opcode, unsigned char *status)
	register unsigned long r1 asm("1") = (unsigned long) status;
	register unsigned long r1 asm("1") = (unsigned long) status;


	asm volatile(
	asm volatile(
		"	spm 0\n" /* pckmo doesn't change the cc */
		/* Parameter registers are ignored, but may not be 0 */
		/* Parameter registers are ignored, but may not be 0 */
		"0:	.insn	rrf,%[opc] << 16,2,2,2,0\n"
		"0:	.insn	rrf,%[opc] << 16,2,2,2,0\n"
		"	brc	1,0b\n"	/* handle partial completion */
		"	brc	1,0b\n"	/* handle partial completion */
@@ -157,6 +161,12 @@ static inline int cpacf_query(unsigned int opcode, unsigned int func)
		if (!test_facility(17))	/* check for MSA */
		if (!test_facility(17))	/* check for MSA */
			return 0;
			return 0;
		break;
		break;
	case CPACF_PCKMO:
		if (!test_facility(76))	/* check for MSA3 */
			return 0;
		break;
	case CPACF_KMF:
	case CPACF_KMO:
	case CPACF_PCC:
	case CPACF_PCC:
	case CPACF_KMCTR:
	case CPACF_KMCTR:
		if (!test_facility(77))	/* check for MSA4 */
		if (!test_facility(77))	/* check for MSA4 */
+149 −0
Original line number Original line Diff line number Diff line
@@ -78,4 +78,153 @@ struct diag210 {


extern int diag210(struct diag210 *addr);
extern int diag210(struct diag210 *addr);


/* bit is set in flags, when physical cpu info is included in diag 204 data */
#define DIAG204_LPAR_PHYS_FLG 0x80
#define DIAG204_LPAR_NAME_LEN 8		/* lpar name len in diag 204 data */
#define DIAG204_CPU_NAME_LEN 16		/* type name len of cpus in diag224 name table */

/* diag 204 subcodes */
enum diag204_sc {
	DIAG204_SUBC_STIB4 = 4,
	DIAG204_SUBC_RSI = 5,
	DIAG204_SUBC_STIB6 = 6,
	DIAG204_SUBC_STIB7 = 7
};

/* The two available diag 204 data formats */
enum diag204_format {
	DIAG204_INFO_SIMPLE = 0,
	DIAG204_INFO_EXT = 0x00010000
};

enum diag204_cpu_flags {
	DIAG204_CPU_ONLINE = 0x20,
	DIAG204_CPU_CAPPED = 0x40,
};

struct diag204_info_blk_hdr {
	__u8  npar;
	__u8  flags;
	__u16 tslice;
	__u16 phys_cpus;
	__u16 this_part;
	__u64 curtod;
} __packed;

struct diag204_x_info_blk_hdr {
	__u8  npar;
	__u8  flags;
	__u16 tslice;
	__u16 phys_cpus;
	__u16 this_part;
	__u64 curtod1;
	__u64 curtod2;
	char reserved[40];
} __packed;

struct diag204_part_hdr {
	__u8 pn;
	__u8 cpus;
	char reserved[6];
	char part_name[DIAG204_LPAR_NAME_LEN];
} __packed;

struct diag204_x_part_hdr {
	__u8  pn;
	__u8  cpus;
	__u8  rcpus;
	__u8  pflag;
	__u32 mlu;
	char  part_name[DIAG204_LPAR_NAME_LEN];
	char  lpc_name[8];
	char  os_name[8];
	__u64 online_cs;
	__u64 online_es;
	__u8  upid;
	__u8  reserved:3;
	__u8  mtid:5;
	char  reserved1[2];
	__u32 group_mlu;
	char  group_name[8];
	char  hardware_group_name[8];
	char  reserved2[24];
} __packed;

struct diag204_cpu_info {
	__u16 cpu_addr;
	char  reserved1[2];
	__u8  ctidx;
	__u8  cflag;
	__u16 weight;
	__u64 acc_time;
	__u64 lp_time;
} __packed;

struct diag204_x_cpu_info {
	__u16 cpu_addr;
	char  reserved1[2];
	__u8  ctidx;
	__u8  cflag;
	__u16 weight;
	__u64 acc_time;
	__u64 lp_time;
	__u16 min_weight;
	__u16 cur_weight;
	__u16 max_weight;
	char  reseved2[2];
	__u64 online_time;
	__u64 wait_time;
	__u32 pma_weight;
	__u32 polar_weight;
	__u32 cpu_type_cap;
	__u32 group_cpu_type_cap;
	char  reserved3[32];
} __packed;

struct diag204_phys_hdr {
	char reserved1[1];
	__u8 cpus;
	char reserved2[6];
	char mgm_name[8];
} __packed;

struct diag204_x_phys_hdr {
	char reserved1[1];
	__u8 cpus;
	char reserved2[6];
	char mgm_name[8];
	char reserved3[80];
} __packed;

struct diag204_phys_cpu {
	__u16 cpu_addr;
	char  reserved1[2];
	__u8  ctidx;
	char  reserved2[3];
	__u64 mgm_time;
	char  reserved3[8];
} __packed;

struct diag204_x_phys_cpu {
	__u16 cpu_addr;
	char  reserved1[2];
	__u8  ctidx;
	char  reserved2[1];
	__u16 weight;
	__u64 mgm_time;
	char  reserved3[80];
} __packed;

struct diag204_x_part_block {
	struct diag204_x_part_hdr hdr;
	struct diag204_x_cpu_info cpus[];
} __packed;

struct diag204_x_phys_block {
	struct diag204_x_phys_hdr hdr;
	struct diag204_x_phys_cpu cpus[];
} __packed;

int diag204(unsigned long subcode, unsigned long size, void *addr);
int diag224(void *ptr);
#endif /* _ASM_S390_DIAG_H */
#endif /* _ASM_S390_DIAG_H */
Loading