Commit 1b53866b authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

Merge tag 's390x-2020-31-07' of https://github.com/frankjaa/kvm-unit-tests into HEAD

* IO tests from Pierre
* GCC 10 compile fix from Claudio
* CPU model test fix from Thomas
parents a3d5d6b9 c2f4799a
......@@ -16,7 +16,9 @@ struct psw {
};
#define PSW_MASK_EXT 0x0100000000000000UL
#define PSW_MASK_IO 0x0200000000000000UL
#define PSW_MASK_DAT 0x0400000000000000UL
#define PSW_MASK_WAIT 0x0002000000000000UL
#define PSW_MASK_PSTATE 0x0001000000000000UL
#define CR0_EXTM_SCLP 0x0000000000000200UL
......@@ -246,6 +248,18 @@ static inline void load_psw_mask(uint64_t mask)
: "+r" (tmp) : "a" (&psw) : "memory", "cc" );
}
static inline void wait_for_interrupt(uint64_t irq_mask)
{
uint64_t psw_mask = extract_psw_mask();
load_psw_mask(psw_mask | irq_mask | PSW_MASK_WAIT);
/*
* After being woken and having processed the interrupt, let's restore
* the PSW mask.
*/
load_psw_mask(psw_mask);
}
static inline void enter_pstate(void)
{
uint64_t mask;
......
......@@ -11,6 +11,7 @@
#define _ASM_S390_CPACF_H
#include <asm/facility.h>
#include <linux/compiler.h>
/*
* Instruction opcodes for the CPACF instructions
......@@ -145,7 +146,7 @@ typedef struct { unsigned char bytes[16]; } cpacf_mask_t;
*
* Returns 1 if @func is available for @opcode, 0 otherwise
*/
static inline void __cpacf_query(unsigned int opcode, cpacf_mask_t *mask)
static __always_inline void __cpacf_query(unsigned int opcode, cpacf_mask_t *mask)
{
register unsigned long r0 asm("0") = 0; /* query function */
register unsigned long r1 asm("1") = (unsigned long) mask;
......@@ -183,7 +184,7 @@ static inline int __cpacf_check_opcode(unsigned int opcode)
}
}
static inline int cpacf_query(unsigned int opcode, cpacf_mask_t *mask)
static __always_inline int cpacf_query(unsigned int opcode, cpacf_mask_t *mask)
{
if (__cpacf_check_opcode(opcode)) {
__cpacf_query(opcode, mask);
......
/*
* Clock utilities for s390
*
* Authors:
* Thomas Huth <thuth@redhat.com>
*
* Copied from the s390/intercept test by:
* Pierre Morel <pmorel@linux.ibm.com>
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2.
*/
#ifndef ASM_S390X_TIME_H
#define ASM_S390X_TIME_H
#define STCK_SHIFT_US (63 - 51)
#define STCK_MAX ((1UL << 52) - 1)
static inline uint64_t get_clock_us(void)
{
uint64_t clk;
asm volatile(" stck %0 " : : "Q"(clk) : "memory");
return clk >> STCK_SHIFT_US;
}
static inline uint64_t get_clock_ms(void)
{
return get_clock_us() / 1000;
}
static inline void udelay(unsigned long us)
{
unsigned long startclk = get_clock_us();
unsigned long c;
do {
c = get_clock_us();
if (c < startclk)
c += STCK_MAX;
} while (c < startclk + us);
}
static inline void mdelay(unsigned long ms)
{
udelay(ms * 1000);
}
#endif
/*
* CSS definitions
*
* Copyright IBM, Corp. 2020
* Author: Pierre Morel <pmorel@linux.ibm.com>
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2.
*/
#ifndef CSS_H
#define CSS_H
#define lowcore_ptr ((struct lowcore *)0x0)
/* subchannel ID bit 16 must always be one */
#define SCHID_ONE 0x00010000
#define CCW_F_CD 0x80
#define CCW_F_CC 0x40
#define CCW_F_SLI 0x20
#define CCW_F_SKP 0x10
#define CCW_F_PCI 0x08
#define CCW_F_IDA 0x04
#define CCW_F_S 0x02
#define CCW_F_MIDA 0x01
#define CCW_C_NOP 0x03
#define CCW_C_TIC 0x08
struct ccw1 {
uint8_t code;
uint8_t flags;
uint16_t count;
uint32_t data_address;
} __attribute__ ((aligned(8)));
#define ORB_CTRL_KEY 0xf0000000
#define ORB_CTRL_SPND 0x08000000
#define ORB_CTRL_STR 0x04000000
#define ORB_CTRL_MOD 0x02000000
#define ORB_CTRL_SYNC 0x01000000
#define ORB_CTRL_FMT 0x00800000
#define ORB_CTRL_PFCH 0x00400000
#define ORB_CTRL_ISIC 0x00200000
#define ORB_CTRL_ALCC 0x00100000
#define ORB_CTRL_SSIC 0x00080000
#define ORB_CTRL_CPTC 0x00040000
#define ORB_CTRL_C64 0x00020000
#define ORB_CTRL_I2K 0x00010000
#define ORB_CTRL_LPM 0x0000ff00
#define ORB_CTRL_ILS 0x00000080
#define ORB_CTRL_MIDAW 0x00000040
#define ORB_CTRL_ORBX 0x00000001
#define ORB_LPM_DFLT 0x00008000
struct orb {
uint32_t intparm;
uint32_t ctrl;
uint32_t cpa;
uint32_t prio;
uint32_t reserved[4];
} __attribute__ ((aligned(4)));
struct scsw {
#define SCSW_SC_PENDING 0x00000001
#define SCSW_SC_SECONDARY 0x00000002
#define SCSW_SC_PRIMARY 0x00000004
#define SCSW_SC_INTERMEDIATE 0x00000008
#define SCSW_SC_ALERT 0x00000010
uint32_t ctrl;
uint32_t ccw_addr;
#define SCSW_DEVS_DEV_END 0x04
#define SCSW_DEVS_SCH_END 0x08
uint8_t dev_stat;
#define SCSW_SCHS_PCI 0x80
#define SCSW_SCHS_IL 0x40
uint8_t sch_stat;
uint16_t count;
};
struct pmcw {
uint32_t intparm;
#define PMCW_DNV 0x0001
#define PMCW_ENABLE 0x0080
#define PMCW_ISC_MASK 0x3800
#define PMCW_ISC_SHIFT 11
uint16_t flags;
uint16_t devnum;
uint8_t lpm;
uint8_t pnom;
uint8_t lpum;
uint8_t pim;
uint16_t mbi;
uint8_t pom;
uint8_t pam;
uint8_t chpid[8];
uint32_t flags2;
};
#define PMCW_CHANNEL_TYPE(pmcw) (pmcw->flags2 >> 21)
struct schib {
struct pmcw pmcw;
struct scsw scsw;
uint8_t md[12];
} __attribute__ ((aligned(4)));
struct irb {
struct scsw scsw;
uint32_t esw[5];
uint32_t ecw[8];
uint32_t emw[8];
} __attribute__ ((aligned(4)));
#define CCW_CMD_SENSE_ID 0xe4
#define CSS_SENSEID_COMMON_LEN 8
struct senseid {
/* common part */
uint8_t reserved; /* always 0x'FF' */
uint16_t cu_type; /* control unit type */
uint8_t cu_model; /* control unit model */
uint16_t dev_type; /* device type */
uint8_t dev_model; /* device model */
uint8_t unused; /* padding byte */
uint8_t padding[256 - 8]; /* Extended part */
} __attribute__ ((aligned(4))) __attribute__ ((packed));
/* CSS low level access functions */
static inline int ssch(unsigned long schid, struct orb *addr)
{
register long long reg1 asm("1") = schid;
int cc;
asm volatile(
" ssch 0(%2)\n"
" ipm %0\n"
" srl %0,28\n"
: "=d" (cc)
: "d" (reg1), "a" (addr), "m" (*addr)
: "cc", "memory");
return cc;
}
static inline int stsch(unsigned long schid, struct schib *addr)
{
register unsigned long reg1 asm ("1") = schid;
int cc;
asm volatile(
" stsch 0(%3)\n"
" ipm %0\n"
" srl %0,28"
: "=d" (cc), "=m" (*addr)
: "d" (reg1), "a" (addr)
: "cc");
return cc;
}
static inline int msch(unsigned long schid, struct schib *addr)
{
register unsigned long reg1 asm ("1") = schid;
int cc;
asm volatile(
" msch 0(%3)\n"
" ipm %0\n"
" srl %0,28"
: "=d" (cc)
: "d" (reg1), "m" (*addr), "a" (addr)
: "cc");
return cc;
}
static inline int tsch(unsigned long schid, struct irb *addr)
{
register unsigned long reg1 asm ("1") = schid;
int cc;
asm volatile(
" tsch 0(%3)\n"
" ipm %0\n"
" srl %0,28"
: "=d" (cc), "=m" (*addr)
: "d" (reg1), "a" (addr)
: "cc");
return cc;
}
static inline int hsch(unsigned long schid)
{
register unsigned long reg1 asm("1") = schid;
int cc;
asm volatile(
" hsch\n"
" ipm %0\n"
" srl %0,28"
: "=d" (cc)
: "d" (reg1)
: "cc");
return cc;
}
static inline int xsch(unsigned long schid)
{
register unsigned long reg1 asm("1") = schid;
int cc;
asm volatile(
" xsch\n"
" ipm %0\n"
" srl %0,28"
: "=d" (cc)
: "d" (reg1)
: "cc");
return cc;
}
static inline int csch(unsigned long schid)
{
register unsigned long reg1 asm("1") = schid;
int cc;
asm volatile(
" csch\n"
" ipm %0\n"
" srl %0,28"
: "=d" (cc)
: "d" (reg1)
: "cc");
return cc;
}
static inline int rsch(unsigned long schid)
{
register unsigned long reg1 asm("1") = schid;
int cc;
asm volatile(
" rsch\n"
" ipm %0\n"
" srl %0,28"
: "=d" (cc)
: "d" (reg1)
: "cc");
return cc;
}
static inline int rchp(unsigned long chpid)
{
register unsigned long reg1 asm("1") = chpid;
int cc;
asm volatile(
" rchp\n"
" ipm %0\n"
" srl %0,28"
: "=d" (cc)
: "d" (reg1)
: "cc");
return cc;
}
/* Debug functions */
char *dump_pmcw_flags(uint16_t f);
char *dump_scsw_flags(uint32_t f);
void dump_scsw(struct scsw *scsw);
void dump_irb(struct irb *irbp);
void dump_schib(struct schib *sch);
struct ccw1 *dump_ccw(struct ccw1 *cp);
void dump_irb(struct irb *irbp);
void dump_pmcw(struct pmcw *p);
void dump_orb(struct orb *op);
int css_enumerate(void);
#define MAX_ENABLE_RETRIES 5
#define IO_SCH_ISC 3
int css_enable(int schid, int isc);
/* Library functions */
int start_ccw1_chain(unsigned int sid, struct ccw1 *ccw);
int start_single_ccw(unsigned int sid, int code, void *data, int count,
unsigned char flags);
void css_irq_io(void);
int css_residual_count(unsigned int schid);
void enable_io_isc(uint8_t isc);
int wait_and_check_io_completion(int schid);
#endif
/*
* Channel subsystem structures dumping
*
* Copyright (c) 2020 IBM Corp.
*
* Authors:
* Pierre Morel <pmorel@linux.ibm.com>
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2.
*
* Description:
* Provides the dumping functions for various structures used by subchannels:
* - ORB : Operation request block, describes the I/O operation and points to
* a CCW chain
* - CCW : Channel Command Word, describes the command, data and flow control
* - IRB : Interuption response Block, describes the result of an operation;
* holds a SCSW and model-dependent data.
* - SCHIB: SubCHannel Information Block composed of:
* - SCSW: SubChannel Status Word, status of the channel.
* - PMCW: Path Management Control Word
* You need the QEMU ccw-pong device in QEMU to answer the I/O transfers.
*/
#include <libcflat.h>
#include <stdint.h>
#include <string.h>
#include <css.h>
/*
* Try to have a more human representation of the SCSW flags:
* each letter in the two strings represents the first
* letter of the associated bit in the flag fields.
*/
static const char *scsw_str = "kkkkslccfpixuzen";
static const char *scsw_str2 = "1SHCrshcsdsAIPSs";
static char scsw_line[64] = {};
char *dump_scsw_flags(uint32_t f)
{
int i;
for (i = 0; i < 16; i++) {
if ((f << i) & 0x80000000)
scsw_line[i] = scsw_str[i];
else
scsw_line[i] = '_';
}
scsw_line[i] = ' ';
for (; i < 32; i++) {
if ((f << i) & 0x80000000)
scsw_line[i + 1] = scsw_str2[i - 16];
else
scsw_line[i + 1] = '_';
}
return scsw_line;
}
/*
* Try to have a more human representation of the PMCW flags
* each letter in the string represents the first
* letter of the associated bit in the flag fields.
*/
static const char *pmcw_str = "11iii111ellmmdtv";
static char pcmw_line[32] = {};
char *dump_pmcw_flags(uint16_t f)
{
int i;
for (i = 0; i < 16; i++) {
if ((f << i) & 0x8000)
pcmw_line[i] = pmcw_str[i];
else
pcmw_line[i] = '_';
}
return pcmw_line;
}
void dump_scsw(struct scsw *s)
{
dump_scsw_flags(s->ctrl);
printf("scsw->flags: %s\n", scsw_line);
printf("scsw->addr : %08x\n", s->ccw_addr);
printf("scsw->devs : %02x\n", s->dev_stat);
printf("scsw->schs : %02x\n", s->sch_stat);
printf("scsw->count: %04x\n", s->count);
}
void dump_irb(struct irb *irbp)
{
int i;
uint32_t *p = (uint32_t *)irbp;
dump_scsw(&irbp->scsw);
for (i = 0; i < sizeof(*irbp)/sizeof(*p); i++, p++)
printf("irb[%02x] : %08x\n", i, *p);
}
void dump_pmcw(struct pmcw *p)
{
int i;
printf("pmcw->intparm : %08x\n", p->intparm);
printf("pmcw->flags : %04x\n", p->flags);
dump_pmcw_flags(p->flags);
printf("pmcw->devnum : %04x\n", p->devnum);
printf("pmcw->lpm : %02x\n", p->lpm);
printf("pmcw->pnom : %02x\n", p->pnom);
printf("pmcw->lpum : %02x\n", p->lpum);
printf("pmcw->pim : %02x\n", p->pim);
printf("pmcw->mbi : %04x\n", p->mbi);
printf("pmcw->pom : %02x\n", p->pom);
printf("pmcw->pam : %02x\n", p->pam);
printf("pmcw->mbi : %04x\n", p->mbi);
for (i = 0; i < 8; i++)
printf("pmcw->chpid[%d]: %02x\n", i, p->chpid[i]);
printf("pmcw->flags2 : %08x\n", p->flags2);
}
void dump_schib(struct schib *sch)
{
struct pmcw *p = &sch->pmcw;
struct scsw *s = &sch->scsw;
printf("--SCHIB--\n");
dump_pmcw(p);
dump_scsw(s);
}
struct ccw1 *dump_ccw(struct ccw1 *cp)
{
printf("CCW: code: %02x flags: %02x count: %04x data: %08x\n", cp->code,
cp->flags, cp->count, cp->data_address);
if (cp->code == CCW_C_TIC)
return (struct ccw1 *)(long)cp->data_address;
return (cp->flags & CCW_F_CC) ? cp + 1 : NULL;
}
void dump_orb(struct orb *op)
{
struct ccw1 *cp;
printf("ORB: intparm : %08x\n", op->intparm);
printf("ORB: ctrl : %08x\n", op->ctrl);
printf("ORB: prio : %08x\n", op->prio);
cp = (struct ccw1 *)(long) (op->cpa);
while (cp)
cp = dump_ccw(cp);
}
/*
* Channel Subsystem tests library
*
* Copyright (c) 2020 IBM Corp
*
* Authors:
* Pierre Morel <pmorel@linux.ibm.com>
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2.
*/
#include <libcflat.h>
#include <alloc_phys.h>
#include <asm/page.h>
#include <string.h>
#include <interrupt.h>
#include <asm/arch_def.h>
#include <asm/time.h>
#include <asm/arch_def.h>
#include <css.h>
static struct schib schib;
/*
* css_enumerate:
* On success return the first subchannel ID found.
* On error return an invalid subchannel ID containing cc
*/
int css_enumerate(void)
{
struct pmcw *pmcw = &schib.pmcw;
int scn_found = 0;
int dev_found = 0;
int schid = 0;
int cc;
int scn;
for (scn = 0; scn < 0xffff; scn++) {
cc = stsch(scn | SCHID_ONE, &schib);
switch (cc) {
case 0: /* 0 means SCHIB stored */
break;
case 3: /* 3 means no more channels */
goto out;
default: /* 1 or 2 should never happen for STSCH */
report_abort("Unexpected error %d on subchannel %08x",
cc, scn | SCHID_ONE);
return cc;
}
/* We currently only support type 0, a.k.a. I/O channels */
if (PMCW_CHANNEL_TYPE(pmcw) != 0)
continue;
/* We ignore I/O channels without valid devices */
scn_found++;
if (!(pmcw->flags & PMCW_DNV))
continue;
/* We keep track of the first device as our test device */
if (!schid)
schid = scn | SCHID_ONE;
report_info("Found subchannel %08x", scn | SCHID_ONE);
dev_found++;
}
out:
report_info("Tested subchannels: %d, I/O subchannels: %d, I/O devices: %d",
scn, scn_found, dev_found);
return schid;
}
/*
* css_enable: enable the subchannel with the specified ISC