Merge branch 's390x-pull-2021-08-03' of https://gitlab.com/frankja/kvm-unit-tests.git into 'master'
* IO tests PV compatibility (Pierre)
* Backtrace support (Janosch)
* mvpg test (Claudio)
* Fixups (Thomas)
diff --git a/lib/s390x/asm-offsets.c b/lib/s390x/asm-offsets.c
index a19f14b..fbea327 100644
--- a/lib/s390x/asm-offsets.c
+++ b/lib/s390x/asm-offsets.c
@@ -54,8 +54,6 @@
OFFSET(GEN_LC_MCCK_NEW_PSW, lowcore, mcck_new_psw);
OFFSET(GEN_LC_IO_NEW_PSW, lowcore, io_new_psw);
OFFSET(GEN_LC_SW_INT_GRS, lowcore, sw_int_grs);
- OFFSET(GEN_LC_SW_INT_FPRS, lowcore, sw_int_fprs);
- OFFSET(GEN_LC_SW_INT_FPC, lowcore, sw_int_fpc);
OFFSET(GEN_LC_SW_INT_CRS, lowcore, sw_int_crs);
OFFSET(GEN_LC_SW_INT_PSW, lowcore, sw_int_psw);
OFFSET(GEN_LC_MCCK_EXT_SA_ADDR, lowcore, mcck_ext_sa_addr);
@@ -70,16 +68,23 @@
OFFSET(GEN_LC_ARS_SA, lowcore, ars_sa);
OFFSET(GEN_LC_CRS_SA, lowcore, crs_sa);
OFFSET(GEN_LC_PGM_INT_TDB, lowcore, pgm_int_tdb);
- OFFSET(__SF_SIE_CONTROL, stack_frame, empty1[0]);
- OFFSET(__SF_SIE_SAVEAREA, stack_frame, empty1[1]);
- OFFSET(__SF_SIE_REASON, stack_frame, empty1[2]);
- OFFSET(__SF_SIE_FLAGS, stack_frame, empty1[3]);
+ OFFSET(__SF_SIE_CONTROL, stack_frame, argument_area[0]);
+ OFFSET(__SF_SIE_SAVEAREA, stack_frame, argument_area[1]);
+ OFFSET(__SF_SIE_REASON, stack_frame, argument_area[2]);
+ OFFSET(__SF_SIE_FLAGS, stack_frame, argument_area[3]);
OFFSET(SIE_SAVEAREA_HOST_GRS, vm_save_area, host.grs[0]);
OFFSET(SIE_SAVEAREA_HOST_FPRS, vm_save_area, host.fprs[0]);
OFFSET(SIE_SAVEAREA_HOST_FPC, vm_save_area, host.fpc);
OFFSET(SIE_SAVEAREA_GUEST_GRS, vm_save_area, guest.grs[0]);
OFFSET(SIE_SAVEAREA_GUEST_FPRS, vm_save_area, guest.fprs[0]);
OFFSET(SIE_SAVEAREA_GUEST_FPC, vm_save_area, guest.fpc);
+ OFFSET(STACK_FRAME_INT_BACKCHAIN, stack_frame_int, back_chain);
+ OFFSET(STACK_FRAME_INT_FPC, stack_frame_int, fpc);
+ OFFSET(STACK_FRAME_INT_FPRS, stack_frame_int, fprs);
+ OFFSET(STACK_FRAME_INT_CRS, stack_frame_int, crs);
+ OFFSET(STACK_FRAME_INT_GRS0, stack_frame_int, grs0);
+ OFFSET(STACK_FRAME_INT_GRS1, stack_frame_int, grs1);
+ DEFINE(STACK_FRAME_INT_SIZE, sizeof(struct stack_frame_int));
return 0;
}
diff --git a/lib/s390x/asm/arch_def.h b/lib/s390x/asm/arch_def.h
index 9c4e330..7e2c5e6 100644
--- a/lib/s390x/asm/arch_def.h
+++ b/lib/s390x/asm/arch_def.h
@@ -8,13 +8,32 @@
#ifndef _ASM_S390X_ARCH_DEF_H_
#define _ASM_S390X_ARCH_DEF_H_
-/*
- * We currently only specify the stack frame members needed for the
- * SIE library code.
- */
struct stack_frame {
- unsigned long back_chain;
- unsigned long empty1[5];
+ struct stack_frame *back_chain;
+ uint64_t reserved;
+ /* GRs 2 - 5 */
+ uint64_t argument_area[4];
+ /* GRs 6 - 15 */
+ uint64_t grs[10];
+ /* FPRs 0, 2, 4, 6 */
+ int64_t fprs[4];
+};
+
+struct stack_frame_int {
+ struct stack_frame *back_chain;
+ uint64_t reserved;
+ /*
+ * The GRs are offset compatible with struct stack_frame so we
+ * can easily fetch GR14 for backtraces.
+ */
+ /* GRs 2 - 15 */
+ uint64_t grs0[14];
+ /* GRs 0 and 1 */
+ uint64_t grs1[2];
+ uint32_t reserved1;
+ uint32_t fpc;
+ uint64_t fprs[16];
+ uint64_t crs[16];
};
struct psw {
@@ -84,9 +103,7 @@
struct psw io_new_psw; /* 0x01f0 */
/* sw definition: save area for registers in interrupt handlers */
uint64_t sw_int_grs[16]; /* 0x0200 */
- uint64_t sw_int_fprs[16]; /* 0x0280 */
- uint32_t sw_int_fpc; /* 0x0300 */
- uint8_t pad_0x0304[0x0308 - 0x0304]; /* 0x0304 */
+ uint8_t pad_0x0280[0x0308 - 0x0280]; /* 0x0280 */
uint64_t sw_int_crs[16]; /* 0x0308 */
struct psw sw_int_psw; /* 0x0388 */
uint8_t pad_0x0310[0x11b0 - 0x0398]; /* 0x0398 */
@@ -173,6 +190,8 @@
uint64_t reserved : 15;
};
+#define SVC_LEAVE_PSTATE 1
+
static inline unsigned short stap(void)
{
unsigned short cpu_address;
@@ -276,6 +295,11 @@
load_psw_mask(mask);
}
+static inline void leave_pstate(void)
+{
+ asm volatile(" svc %0\n" : : "i" (SVC_LEAVE_PSTATE));
+}
+
static inline int stsi(void *addr, int fc, int sel1, int sel2)
{
register int r0 asm("0") = (fc << 28) | sel1;
diff --git a/lib/s390x/asm/interrupt.h b/lib/s390x/asm/interrupt.h
index 1a2e2cd..31e4766 100644
--- a/lib/s390x/asm/interrupt.h
+++ b/lib/s390x/asm/interrupt.h
@@ -14,8 +14,8 @@
#define EXT_IRQ_SERVICE_SIG 0x2401
void register_pgm_cleanup_func(void (*f)(void));
-void handle_pgm_int(void);
-void handle_ext_int(void);
+void handle_pgm_int(struct stack_frame_int *stack);
+void handle_ext_int(struct stack_frame_int *stack);
void handle_mcck_int(void);
void handle_io_int(void);
void handle_svc_int(void);
diff --git a/lib/s390x/asm/uv.h b/lib/s390x/asm/uv.h
index 39d2dc0..9c49184 100644
--- a/lib/s390x/asm/uv.h
+++ b/lib/s390x/asm/uv.h
@@ -79,4 +79,43 @@
return cc;
}
+static inline int share(unsigned long addr, u16 cmd)
+{
+ struct uv_cb_share uvcb = {
+ .header.cmd = cmd,
+ .header.len = sizeof(uvcb),
+ .paddr = addr
+ };
+ int cc;
+
+ cc = uv_call(0, (u64)&uvcb);
+ if (!cc && uvcb.header.rc == UVC_RC_EXECUTED)
+ return 0;
+
+ report_info("uv_call: cmd %04x cc %d response code: %04x", cc, cmd,
+ uvcb.header.rc);
+ return -1;
+}
+
+/*
+ * Guest 2 request to the Ultravisor to make a page shared with the
+ * hypervisor for IO.
+ *
+ * @addr: Real or absolute address of the page to be shared
+ */
+static inline int uv_set_shared(unsigned long addr)
+{
+ return share(addr, UVC_CMD_SET_SHARED_ACCESS);
+}
+
+/*
+ * Guest 2 request to the Ultravisor to make a page unshared.
+ *
+ * @addr: Real or absolute address of the page to be unshared
+ */
+static inline int uv_remove_shared(unsigned long addr)
+{
+ return share(addr, UVC_CMD_REMOVE_SHARED_ACCESS);
+}
+
#endif
diff --git a/lib/s390x/css.h b/lib/s390x/css.h
index d10d265..3e57445 100644
--- a/lib/s390x/css.h
+++ b/lib/s390x/css.h
@@ -281,8 +281,7 @@
/* 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);
+struct ccw1 *ccw_alloc(int code, void *data, int count, unsigned char flags);
void css_irq_io(void);
int css_residual_count(unsigned int schid);
diff --git a/lib/s390x/css_lib.c b/lib/s390x/css_lib.c
index 5af6f77..3c24480 100644
--- a/lib/s390x/css_lib.c
+++ b/lib/s390x/css_lib.c
@@ -16,6 +16,7 @@
#include <asm/time.h>
#include <asm/arch_def.h>
+#include <malloc_io.h>
#include <css.h>
static struct schib schib;
@@ -200,33 +201,20 @@
return ssch(sid, &orb);
}
-/*
- * In the future, we want to implement support for CCW chains;
- * for that, we will need to work with ccw1 pointers.
- */
-static struct ccw1 unique_ccw;
-
-int start_single_ccw(unsigned int sid, int code, void *data, int count,
- unsigned char flags)
+struct ccw1 *ccw_alloc(int code, void *data, int count, unsigned char flags)
{
- int cc;
- struct ccw1 *ccw = &unique_ccw;
+ struct ccw1 *ccw;
- report_prefix_push("start_subchannel");
- /* Build the CCW chain with a single CCW */
+ ccw = alloc_io_mem(sizeof(*ccw), 0);
+ if (!ccw)
+ return NULL;
+
ccw->code = code;
ccw->flags = flags;
ccw->count = count;
ccw->data_address = (int)(unsigned long)data;
- cc = start_ccw1_chain(sid, ccw);
- if (cc) {
- report(0, "cc = %d", cc);
- report_prefix_pop();
- return cc;
- }
- report_prefix_pop();
- return 0;
+ return ccw;
}
/* wait_and_check_io_completion:
diff --git a/lib/s390x/interrupt.c b/lib/s390x/interrupt.c
index 1ce3607..ce0003d 100644
--- a/lib/s390x/interrupt.c
+++ b/lib/s390x/interrupt.c
@@ -56,7 +56,7 @@
pgm_cleanup_func = f;
}
-static void fixup_pgm_int(void)
+static void fixup_pgm_int(struct stack_frame_int *stack)
{
/* If we have an error on SIE we directly move to sie_exit */
if (lc->pgm_old_psw.addr >= (uint64_t)&sie_entry &&
@@ -76,7 +76,13 @@
/* Handling for iep.c test case. */
if (lc->trans_exc_id & 0x80UL && lc->trans_exc_id & 0x04UL &&
!(lc->trans_exc_id & 0x08UL))
- lc->pgm_old_psw.addr = lc->sw_int_grs[14];
+ /*
+ * We branched to the instruction that caused
+ * the exception so we can use the return
+ * address in GR14 to jump back and continue
+ * executing test code.
+ */
+ lc->pgm_old_psw.addr = stack->grs0[12];
break;
case PGM_INT_CODE_SEGMENT_TRANSLATION:
case PGM_INT_CODE_PAGE_TRANSLATION:
@@ -115,11 +121,40 @@
/* suppressed/terminated/completed point already at the next address */
}
-void handle_pgm_int(void)
+static void print_int_regs(struct stack_frame_int *stack)
+{
+ printf("\n");
+ printf("GPRS:\n");
+ printf("%016lx %016lx %016lx %016lx\n",
+ stack->grs1[0], stack->grs1[1], stack->grs0[0], stack->grs0[1]);
+ printf("%016lx %016lx %016lx %016lx\n",
+ stack->grs0[2], stack->grs0[3], stack->grs0[4], stack->grs0[5]);
+ printf("%016lx %016lx %016lx %016lx\n",
+ stack->grs0[6], stack->grs0[7], stack->grs0[8], stack->grs0[9]);
+ printf("%016lx %016lx %016lx %016lx\n",
+ stack->grs0[10], stack->grs0[11], stack->grs0[12], stack->grs0[13]);
+ printf("\n");
+}
+
+static void print_pgm_info(struct stack_frame_int *stack)
+
+{
+ printf("\n");
+ printf("Unexpected program interrupt: %d on cpu %d at %#lx, ilen %d\n",
+ lc->pgm_int_code, stap(), lc->pgm_old_psw.addr,
+ lc->pgm_int_id);
+ print_int_regs(stack);
+ dump_stack();
+ report_summary();
+ abort();
+}
+
+void handle_pgm_int(struct stack_frame_int *stack)
{
if (!pgm_int_expected) {
/* Force sclp_busy to false, otherwise we will loop forever */
sclp_handle_ext();
+ print_pgm_info(stack);
report_abort("Unexpected program interrupt: %d on cpu %d at %#lx, ilen %d\n",
lc->pgm_int_code, stap(), lc->pgm_old_psw.addr,
lc->pgm_int_id);
@@ -130,10 +165,10 @@
if (pgm_cleanup_func)
(*pgm_cleanup_func)();
else
- fixup_pgm_int();
+ fixup_pgm_int(stack);
}
-void handle_ext_int(void)
+void handle_ext_int(struct stack_frame_int *stack)
{
if (!ext_int_expected &&
lc->ext_int_code != EXT_IRQ_SERVICE_SIG) {
@@ -143,13 +178,13 @@
}
if (lc->ext_int_code == EXT_IRQ_SERVICE_SIG) {
- lc->sw_int_crs[0] &= ~(1UL << 9);
+ stack->crs[0] &= ~(1UL << 9);
sclp_handle_ext();
} else {
ext_int_expected = false;
}
- if (!(lc->sw_int_crs[0] & CR0_EXTM_MASK))
+ if (!(stack->crs[0] & CR0_EXTM_MASK))
lc->ext_old_psw.mask &= ~PSW_MASK_EXT;
}
@@ -188,6 +223,14 @@
void handle_svc_int(void)
{
- report_abort("Unexpected supervisor call interrupt: on cpu %d at %#lx",
- stap(), lc->svc_old_psw.addr);
+ uint16_t code = lc->svc_int_code;
+
+ switch (code) {
+ case SVC_LEAVE_PSTATE:
+ lc->svc_old_psw.mask &= ~PSW_MASK_PSTATE;
+ break;
+ default:
+ report_abort("Unexpected supervisor call interrupt: code %#x on cpu %d at %#lx",
+ code, stap(), lc->svc_old_psw.addr);
+ }
}
diff --git a/lib/s390x/malloc_io.c b/lib/s390x/malloc_io.c
new file mode 100644
index 0000000..1dcf169
--- /dev/null
+++ b/lib/s390x/malloc_io.c
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * I/O page allocation
+ *
+ * Copyright (c) 2021 IBM Corp
+ *
+ * Authors:
+ * Pierre Morel <pmorel@linux.ibm.com>
+ *
+ * Using this interface provide host access to the allocated pages in
+ * case the guest is a protected guest.
+ * This is needed for I/O buffers.
+ *
+ */
+#include <libcflat.h>
+#include <asm/page.h>
+#include <asm/uv.h>
+#include <malloc_io.h>
+#include <alloc_page.h>
+#include <asm/facility.h>
+#include <bitops.h>
+
+static int share_pages(void *p, int count)
+{
+ int i = 0;
+
+ for (i = 0; i < count; i++, p += PAGE_SIZE)
+ if (uv_set_shared((unsigned long)p))
+ break;
+ return i;
+}
+
+static void unshare_pages(void *p, int count)
+{
+ int i;
+
+ for (i = count; i > 0; i--, p += PAGE_SIZE)
+ uv_remove_shared((unsigned long)p);
+}
+
+void *alloc_io_mem(int size, int flags)
+{
+ int order = get_order(size >> PAGE_SHIFT);
+ void *p;
+ int n;
+
+ assert(size);
+
+ p = alloc_pages_flags(order, AREA_DMA31 | flags);
+ if (!p || !test_facility(158))
+ return p;
+
+ n = share_pages(p, 1 << order);
+ if (n == 1 << order)
+ return p;
+
+ unshare_pages(p, n);
+ free_pages(p);
+ return NULL;
+}
+
+void free_io_mem(void *p, int size)
+{
+ int order = get_order(size >> PAGE_SHIFT);
+
+ assert(IS_ALIGNED((uintptr_t)p, PAGE_SIZE));
+
+ if (test_facility(158))
+ unshare_pages(p, 1 << order);
+ free_pages(p);
+}
diff --git a/lib/s390x/malloc_io.h b/lib/s390x/malloc_io.h
new file mode 100644
index 0000000..cc5fad7
--- /dev/null
+++ b/lib/s390x/malloc_io.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * I/O allocations
+ *
+ * Copyright (c) 2021 IBM Corp
+ *
+ * Authors:
+ * Pierre Morel <pmorel@linux.ibm.com>
+ *
+ */
+#ifndef _S390X_MALLOC_IO_H_
+#define _S390X_MALLOC_IO_H_
+
+/*
+ * Allocates a page aligned page bound range of contiguous real or
+ * absolute memory in the DMA31 region large enough to contain size
+ * bytes.
+ * If Protected Virtualisation facility is present, shares the pages
+ * with the host.
+ * If all the pages for the specified size cannot be reserved,
+ * the function rewinds the partial allocation and a NULL pointer
+ * is returned.
+ *
+ * @size: the minimal size allocated in byte.
+ * @flags: the flags used for the underlying page allocator.
+ *
+ * Errors:
+ * The allocation will assert the size parameter, will fail if the
+ * underlying page allocator fail or in the case of protected
+ * virtualisation if the sharing of the pages fails.
+ *
+ * Returns a pointer to the first page in case of success, NULL otherwise.
+ */
+void *alloc_io_mem(int size, int flags);
+
+/*
+ * Frees a previously memory space allocated by alloc_io_mem.
+ * If Protected Virtualisation facility is present, unshares the pages
+ * with the host.
+ * The address must be aligned on a page boundary otherwise an assertion
+ * breaks the program.
+ */
+void free_io_mem(void *p, int size);
+
+#endif /* _S390X_MALLOC_IO_H_ */
diff --git a/lib/s390x/sclp.h b/lib/s390x/sclp.h
index 9f81c0f..8523133 100644
--- a/lib/s390x/sclp.h
+++ b/lib/s390x/sclp.h
@@ -131,10 +131,15 @@
uint16_t highest_cpu;
uint8_t _reserved5[124 - 122]; /* 122-123 */
uint32_t hmfai;
- uint8_t reserved7[134 - 128];
+ uint8_t reserved7[134 - 128]; /* 128-133 */
uint8_t byte_134_diag318 : 1;
uint8_t : 7;
- struct CPUEntry entries[0];
+ /*
+ * At the end of the ReadInfo, there are also the CPU entries (see
+ * struct CPUEntry). When the Extended-Length SCCB (ELS) feature is
+ * enabled, the start of the CPU entries array begins at an offset
+ * denoted by the offset_cpu field, otherwise it's at offset 128.
+ */
} __attribute__((packed)) ReadInfo;
typedef struct ReadCpuInfo {
diff --git a/lib/s390x/stack.c b/lib/s390x/stack.c
index 0fcd1af..4cf80da 100644
--- a/lib/s390x/stack.c
+++ b/lib/s390x/stack.c
@@ -3,24 +3,32 @@
* s390x stack implementation
*
* Copyright (c) 2017 Red Hat Inc
+ * Copyright 2021 IBM Corp
*
* Authors:
* Thomas Huth <thuth@redhat.com>
* David Hildenbrand <david@redhat.com>
+ * Janosch Frank <frankja@de.ibm.com>
*/
#include <libcflat.h>
#include <stack.h>
+#include <asm/arch_def.h>
int backtrace_frame(const void *frame, const void **return_addrs, int max_depth)
{
- printf("TODO: Implement backtrace_frame(%p, %p, %d) function!\n",
- frame, return_addrs, max_depth);
- return 0;
+ int depth = 0;
+ struct stack_frame *stack = (struct stack_frame *)frame;
+
+ for (depth = 0; stack && depth < max_depth; depth++) {
+ return_addrs[depth] = (void *)stack->grs[8];
+ stack = stack->back_chain;
+ }
+
+ return depth;
}
int backtrace(const void **return_addrs, int max_depth)
{
- printf("TODO: Implement backtrace(%p, %d) function!\n",
- return_addrs, max_depth);
- return 0;
+ return backtrace_frame(__builtin_frame_address(0),
+ return_addrs, max_depth);
}
diff --git a/s390x/Makefile b/s390x/Makefile
index 08d85c9..b92de9c 100644
--- a/s390x/Makefile
+++ b/s390x/Makefile
@@ -20,6 +20,7 @@
tests += $(TEST_DIR)/css.elf
tests += $(TEST_DIR)/uv-guest.elf
tests += $(TEST_DIR)/sie.elf
+tests += $(TEST_DIR)/mvpg.elf
tests_binary = $(patsubst %.elf,%.bin,$(tests))
ifneq ($(HOST_KEY_DOCUMENT),)
@@ -39,6 +40,7 @@
CFLAGS += -I $(SRCDIR)/lib -I $(SRCDIR)/lib/s390x -I lib
CFLAGS += -O2
CFLAGS += -march=zEC12
+CFLAGS += -mbackchain
CFLAGS += -fno-delete-null-pointer-checks
LDFLAGS += -nostdlib -Wl,--build-id=none
@@ -64,6 +66,7 @@
cflatobjs += lib/s390x/vm.o
cflatobjs += lib/s390x/css_dump.o
cflatobjs += lib/s390x/css_lib.o
+cflatobjs += lib/s390x/malloc_io.o
OBJDIRS += lib/s390x
diff --git a/s390x/cpu.S b/s390x/cpu.S
index 5267f02..e2ad56c 100644
--- a/s390x/cpu.S
+++ b/s390x/cpu.S
@@ -18,7 +18,7 @@
*/
.globl diag308_load_reset
diag308_load_reset:
- SAVE_REGS
+ SAVE_REGS_STACK
/* Backup current PSW mask, as we have to restore it on success */
epsw %r0, %r1
st %r0, GEN_LC_SW_INT_PSW
@@ -31,6 +31,7 @@
ogr %r0, %r1
/* Store it at the reset PSW location (real 0x0) */
stg %r0, 0
+ stg %r15, GEN_LC_SW_INT_GRS + 15 * 8
/* Do the reset */
diag %r0,%r2,0x308
/* Failure path */
@@ -40,7 +41,8 @@
/* load a cr0 that has the AFP control bit which enables all FPRs */
0: larl %r1, initial_cr0
lctlg %c0, %c0, 0(%r1)
- RESTORE_REGS
+ lg %r15, GEN_LC_SW_INT_GRS + 15 * 8
+ RESTORE_REGS_STACK
lhi %r2, 1
larl %r0, 1f
stg %r0, GEN_LC_SW_INT_PSW + 8
diff --git a/s390x/css.c b/s390x/css.c
index 23a7b7c..1a61a5c 100644
--- a/s390x/css.c
+++ b/s390x/css.c
@@ -15,13 +15,15 @@
#include <interrupt.h>
#include <asm/arch_def.h>
+#include <malloc_io.h>
#include <css.h>
+#include <asm/barrier.h>
#define DEFAULT_CU_TYPE 0x3832 /* virtio-ccw */
static unsigned long cu_type = DEFAULT_CU_TYPE;
static int test_device_sid;
-static struct senseid senseid;
+static struct senseid *senseid;
static void test_enumerate(void)
{
@@ -55,6 +57,7 @@
*/
static void test_sense(void)
{
+ struct ccw1 *ccw;
int ret;
int len;
@@ -78,11 +81,23 @@
lowcore_ptr->io_int_param = 0;
- memset(&senseid, 0, sizeof(senseid));
- ret = start_single_ccw(test_device_sid, CCW_CMD_SENSE_ID,
- &senseid, sizeof(senseid), CCW_F_SLI);
- if (ret)
+ senseid = alloc_io_mem(sizeof(*senseid), 0);
+ if (!senseid) {
+ report(0, "Allocation of senseid");
+ goto error_senseid;
+ }
+
+ ccw = ccw_alloc(CCW_CMD_SENSE_ID, senseid, sizeof(*senseid), CCW_F_SLI);
+ if (!ccw) {
+ report(0, "Allocation of CCW");
+ goto error_ccw;
+ }
+
+ ret = start_ccw1_chain(test_device_sid, ccw);
+ if (ret) {
+ report(0, "Starting CCW chain");
goto error;
+ }
if (wait_and_check_io_completion(test_device_sid) < 0)
goto error;
@@ -95,7 +110,7 @@
if (ret < 0) {
report_info("no valid residual count");
} else if (ret != 0) {
- len = sizeof(senseid) - ret;
+ len = sizeof(*senseid) - ret;
if (ret && len < CSS_SENSEID_COMMON_LEN) {
report(0, "transferred a too short length: %d", ret);
goto error;
@@ -103,21 +118,25 @@
report_info("transferred a shorter length: %d", len);
}
- if (senseid.reserved != 0xff) {
- report(0, "transferred garbage: 0x%02x", senseid.reserved);
+ if (senseid->reserved != 0xff) {
+ report(0, "transferred garbage: 0x%02x", senseid->reserved);
goto error;
}
report_prefix_pop();
report_info("reserved 0x%02x cu_type 0x%04x cu_model 0x%02x dev_type 0x%04x dev_model 0x%02x",
- senseid.reserved, senseid.cu_type, senseid.cu_model,
- senseid.dev_type, senseid.dev_model);
+ senseid->reserved, senseid->cu_type, senseid->cu_model,
+ senseid->dev_type, senseid->dev_model);
- report(senseid.cu_type == cu_type, "cu_type expected 0x%04x got 0x%04x",
- (uint16_t) cu_type, senseid.cu_type);
+ report(senseid->cu_type == cu_type, "cu_type expected 0x%04x got 0x%04x",
+ (uint16_t)cu_type, senseid->cu_type);
error:
+ free_io_mem(ccw, sizeof(*ccw));
+error_ccw:
+ free_io_mem(senseid, sizeof(*senseid));
+error_senseid:
unregister_io_int_func(css_irq_io);
}
diff --git a/s390x/cstart64.S b/s390x/cstart64.S
index ace0c0d..666a956 100644
--- a/s390x/cstart64.S
+++ b/s390x/cstart64.S
@@ -92,34 +92,19 @@
.section .text
pgm_int:
- SAVE_REGS
- brasl %r14, handle_pgm_int
- RESTORE_REGS
- lpswe GEN_LC_PGM_OLD_PSW
+ CALL_INT_HANDLER handle_pgm_int, GEN_LC_PGM_OLD_PSW
ext_int:
- SAVE_REGS
- brasl %r14, handle_ext_int
- RESTORE_REGS
- lpswe GEN_LC_EXT_OLD_PSW
+ CALL_INT_HANDLER handle_ext_int, GEN_LC_EXT_OLD_PSW
mcck_int:
- SAVE_REGS
- brasl %r14, handle_mcck_int
- RESTORE_REGS
- lpswe GEN_LC_MCCK_OLD_PSW
+ CALL_INT_HANDLER handle_mcck_int, GEN_LC_MCCK_OLD_PSW
io_int:
- SAVE_REGS_STACK
- brasl %r14, handle_io_int
- RESTORE_REGS_STACK
- lpswe GEN_LC_IO_OLD_PSW
+ CALL_INT_HANDLER handle_io_int, GEN_LC_IO_OLD_PSW
svc_int:
- SAVE_REGS
- brasl %r14, handle_svc_int
- RESTORE_REGS
- lpswe GEN_LC_SVC_OLD_PSW
+ CALL_INT_HANDLER handle_svc_int, GEN_LC_SVC_OLD_PSW
.align 8
initial_psw:
diff --git a/s390x/macros.S b/s390x/macros.S
index 37a6a63..13cff29 100644
--- a/s390x/macros.S
+++ b/s390x/macros.S
@@ -3,75 +3,77 @@
* s390x assembly macros
*
* Copyright (c) 2017 Red Hat Inc
- * Copyright (c) 2020 IBM Corp.
+ * Copyright (c) 2020, 2021 IBM Corp.
*
* Authors:
+ * Janosch Frank <frankja@linux.ibm.com>
* Pierre Morel <pmorel@linux.ibm.com>
* David Hildenbrand <david@redhat.com>
*/
#include <asm/asm-offsets.h>
- .macro SAVE_REGS
- /* save grs 0-15 */
- stmg %r0, %r15, GEN_LC_SW_INT_GRS
- /* save crs 0-15 */
- stctg %c0, %c15, GEN_LC_SW_INT_CRS
- /* load a cr0 that has the AFP control bit which enables all FPRs */
- larl %r1, initial_cr0
- lctlg %c0, %c0, 0(%r1)
- /* save fprs 0-15 + fpc */
- la %r1, GEN_LC_SW_INT_FPRS
- .irp i, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
- std \i, \i * 8(%r1)
- .endr
- stfpc GEN_LC_SW_INT_FPC
- .endm
-
- .macro RESTORE_REGS
- /* restore fprs 0-15 + fpc */
- la %r1, GEN_LC_SW_INT_FPRS
- .irp i, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
- ld \i, \i * 8(%r1)
- .endr
- lfpc GEN_LC_SW_INT_FPC
- /* restore crs 0-15 */
- lctlg %c0, %c15, GEN_LC_SW_INT_CRS
- /* restore grs 0-15 */
- lmg %r0, %r15, GEN_LC_SW_INT_GRS
+/*
+ * Exception handler macro that saves registers on the stack,
+ * allocates stack space and calls the C handler function. Afterwards
+ * we re-load the registers and load the old PSW.
+ */
+ .macro CALL_INT_HANDLER c_func, old_psw
+ SAVE_REGS_STACK
+ /* Save the stack address in GR2 which is the first function argument */
+ lgr %r2, %r15
+ /* Allocate stack space for called C function, as specified in s390 ELF ABI */
+ slgfi %r15, 160
+ /*
+ * Save the address of the interrupt stack into the back chain
+ * of the called function.
+ */
+ stg %r2, STACK_FRAME_INT_BACKCHAIN(%r15)
+ brasl %r14, \c_func
+ algfi %r15, 160
+ RESTORE_REGS_STACK
+ lpswe \old_psw
.endm
/* Save registers on the stack (r15), so we can have stacked interrupts. */
.macro SAVE_REGS_STACK
- /* Allocate a stack frame for 15 general registers */
- slgfi %r15, 15 * 8
+ /* Allocate a full stack frame */
+ slgfi %r15, STACK_FRAME_INT_SIZE
/* Store registers r0 to r14 on the stack */
- stmg %r0, %r14, 0(%r15)
- /* Allocate a stack frame for 16 floating point registers */
- /* The size of a FP register is the size of an double word */
- slgfi %r15, 16 * 8
+ stmg %r2, %r15, STACK_FRAME_INT_GRS0(%r15)
+ stg %r0, STACK_FRAME_INT_GRS1(%r15)
+ stg %r1, STACK_FRAME_INT_GRS1 + 8(%r15)
+ /* Store the gr15 value before we allocated the new stack */
+ lgr %r0, %r15
+ algfi %r0, STACK_FRAME_INT_SIZE
+ stg %r0, 13 * 8 + STACK_FRAME_INT_GRS0(%r15)
+ stg %r0, STACK_FRAME_INT_BACKCHAIN(%r15)
+ /*
+ * Store CR0 and load initial CR0 so AFP is active and we can
+ * access all fprs to save them.
+ */
+ stctg %c0,%c15,STACK_FRAME_INT_CRS(%r15)
+ larl %r1, initial_cr0
+ lctlg %c0, %c0, 0(%r1)
/* Save fp register on stack: offset to SP is multiple of reg number */
.irp i, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
- std \i, \i * 8(%r15)
+ std \i, \i * 8 + STACK_FRAME_INT_FPRS(%r15)
.endr
- /* Save fpc, but keep stack aligned on 64bits */
- slgfi %r15, 8
- efpc %r0
- stg %r0, 0(%r15)
+ /* Save fpc */
+ stfpc STACK_FRAME_INT_FPC(%r15)
.endm
/* Restore the register in reverse order */
.macro RESTORE_REGS_STACK
/* Restore fpc */
- lfpc 0(%r15)
- algfi %r15, 8
+ lfpc STACK_FRAME_INT_FPC(%r15)
/* Restore fp register from stack: SP still where it was left */
/* and offset to SP is a multiple of reg number */
.irp i, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
- ld \i, \i * 8(%r15)
+ ld \i, \i * 8 + STACK_FRAME_INT_FPRS(%r15)
.endr
- /* Now that we're done, rewind the stack pointer by 16 double word */
- algfi %r15, 16 * 8
+ /* Load CR0 back */
+ lctlg %c0, %c15, STACK_FRAME_INT_CRS(%r15)
/* Load the registers from stack */
- lmg %r0, %r14, 0(%r15)
- /* Rewind the stack by 15 double word */
- algfi %r15, 15 * 8
+ lg %r0, STACK_FRAME_INT_GRS1(%r15)
+ lg %r1, STACK_FRAME_INT_GRS1 + 8(%r15)
+ lmg %r2, %r15, STACK_FRAME_INT_GRS0(%r15)
.endm
diff --git a/s390x/mvpg.c b/s390x/mvpg.c
new file mode 100644
index 0000000..5743d5b
--- /dev/null
+++ b/s390x/mvpg.c
@@ -0,0 +1,277 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Move Page instruction tests
+ *
+ * Copyright (c) 2021 IBM Corp
+ *
+ * Authors:
+ * Claudio Imbrenda <imbrenda@linux.ibm.com>
+ */
+#include <libcflat.h>
+#include <asm/asm-offsets.h>
+#include <asm-generic/barrier.h>
+#include <asm/interrupt.h>
+#include <asm/pgtable.h>
+#include <mmu.h>
+#include <asm/page.h>
+#include <asm/facility.h>
+#include <asm/mem.h>
+#include <asm/sigp.h>
+#include <smp.h>
+#include <alloc_page.h>
+#include <bitops.h>
+#include <vm.h>
+
+/* Used to build the appropriate test values for register 0 */
+#define KFC(x) ((x) << 10)
+#define CCO 0x100
+
+/* How much memory to allocate for the test */
+#define MEM_ORDER 12
+/* How many iterations to perform in the loops */
+#define ITER 8
+
+/* Used to generate the simple pattern */
+#define MAGIC 42
+
+static uint8_t source[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+static uint8_t buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+
+/* Keep track of fresh memory */
+static uint8_t *fresh;
+
+static inline int mvpg(unsigned long r0, void *dest, void *src)
+{
+ register unsigned long reg0 asm ("0") = r0;
+ int cc;
+
+ asm volatile(" mvpg %1,%2\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=&d" (cc) : "a" (dest), "a" (src), "d" (reg0)
+ : "memory", "cc");
+ return cc;
+}
+
+/*
+ * Initialize a page with a simple pattern
+ */
+static void init_page(uint8_t *p)
+{
+ int i;
+
+ for (i = 0; i < PAGE_SIZE; i++)
+ p[i] = i + MAGIC;
+}
+
+/*
+ * Check if the given page contains the simple pattern
+ */
+static int page_ok(const uint8_t *p)
+{
+ int i;
+
+ for (i = 0; i < PAGE_SIZE; i++)
+ if (p[i] != (uint8_t)(i + MAGIC))
+ return 0;
+ return 1;
+}
+
+static void test_exceptions(void)
+{
+ int i, expected;
+
+ report_prefix_push("exceptions");
+
+ /*
+ * Key Function Control values 4 and 5 are allowed only in supervisor
+ * state, and even then, only if the move-page-and-set-key facility
+ * is present (STFLE bit 149)
+ */
+ report_prefix_push("privileged");
+ if (test_facility(149)) {
+ expected = PGM_INT_CODE_PRIVILEGED_OPERATION;
+ for (i = 4; i < 6; i++) {
+ expect_pgm_int();
+ enter_pstate();
+ mvpg(KFC(i), buffer, source);
+ report(clear_pgm_int() == expected, "Key Function Control value %d", i);
+ }
+ } else {
+ report_skip("Key Function Control value %d", 4);
+ report_skip("Key Function Control value %d", 5);
+ i = 4;
+ }
+ report_prefix_pop();
+
+ /*
+ * Invalid values of the Key Function Control, or setting the
+ * reserved bits, should result in a specification exception
+ */
+ report_prefix_push("specification");
+ expected = PGM_INT_CODE_SPECIFICATION;
+ expect_pgm_int();
+ mvpg(KFC(3), buffer, source);
+ report(clear_pgm_int() == expected, "Key Function Control value 3");
+ for (; i < 32; i++) {
+ expect_pgm_int();
+ mvpg(KFC(i), buffer, source);
+ report(clear_pgm_int() == expected, "Key Function Control value %d", i);
+ }
+ report_prefix_pop();
+
+ /* Operands outside memory result in addressing exceptions, as usual */
+ report_prefix_push("addressing");
+ expected = PGM_INT_CODE_ADDRESSING;
+ expect_pgm_int();
+ mvpg(0, buffer, (void *)PAGE_MASK);
+ report(clear_pgm_int() == expected, "Second operand outside memory");
+
+ expect_pgm_int();
+ mvpg(0, (void *)PAGE_MASK, source);
+ report(clear_pgm_int() == expected, "First operand outside memory");
+ report_prefix_pop();
+
+ report_prefix_pop();
+}
+
+static void test_success(void)
+{
+ int cc;
+
+ report_prefix_push("success");
+ /* Test successful scenarios, both in supervisor and problem state */
+ cc = mvpg(0, buffer, source);
+ report(page_ok(buffer) && !cc, "Supervisor state MVPG successful");
+ memset(buffer, 0xff, PAGE_SIZE);
+
+ enter_pstate();
+ cc = mvpg(0, buffer, source);
+ leave_pstate();
+ report(page_ok(buffer) && !cc, "Problem state MVPG successful");
+
+ report_prefix_pop();
+}
+
+static void test_small_loop(const void *string)
+{
+ uint8_t *dest;
+ int i, cc;
+
+ /* Looping over cold and warm pages helps catch VSIE bugs */
+ report_prefix_push(string);
+ dest = fresh;
+ for (i = 0; i < ITER; i++) {
+ cc = mvpg(0, fresh, source);
+ report(page_ok(fresh) && !cc, "cold: %p, %p", source, fresh);
+ fresh += PAGE_SIZE;
+ }
+
+ for (i = 0; i < ITER; i++) {
+ memset(dest, 0, PAGE_SIZE);
+ cc = mvpg(0, dest, source);
+ report(page_ok(dest) && !cc, "warm: %p, %p", source, dest);
+ dest += PAGE_SIZE;
+ }
+ report_prefix_pop();
+}
+
+static void test_mmu_prot(void)
+{
+ int cc;
+
+ report_prefix_push("protection");
+ report_prefix_push("cco=0");
+
+ /* MVPG should still succeed when the source is read-only */
+ protect_page(source, PAGE_ENTRY_P);
+ cc = mvpg(0, fresh, source);
+ report(page_ok(fresh) && !cc, "source read only");
+ unprotect_page(source, PAGE_ENTRY_P);
+ fresh += PAGE_SIZE;
+
+ /*
+ * When the source or destination are invalid, a page translation
+ * exception should be raised; when the destination is read-only,
+ * a protection exception should be raised.
+ */
+ protect_page(fresh, PAGE_ENTRY_P);
+ expect_pgm_int();
+ mvpg(0, fresh, source);
+ report(clear_pgm_int() == PGM_INT_CODE_PROTECTION, "destination read only");
+ fresh += PAGE_SIZE;
+
+ protect_page(source, PAGE_ENTRY_I);
+ expect_pgm_int();
+ mvpg(0, fresh, source);
+ report(clear_pgm_int() == PGM_INT_CODE_PAGE_TRANSLATION, "source invalid");
+ unprotect_page(source, PAGE_ENTRY_I);
+ fresh += PAGE_SIZE;
+
+ protect_page(fresh, PAGE_ENTRY_I);
+ expect_pgm_int();
+ mvpg(0, fresh, source);
+ report(clear_pgm_int() == PGM_INT_CODE_PAGE_TRANSLATION, "destination invalid");
+ fresh += PAGE_SIZE;
+
+ report_prefix_pop();
+ report_prefix_push("cco=1");
+ /*
+ * Setting the CCO bit should suppress page translation exceptions,
+ * but not protection exceptions.
+ */
+ protect_page(fresh, PAGE_ENTRY_P);
+ expect_pgm_int();
+ mvpg(CCO, fresh, source);
+ report(clear_pgm_int() == PGM_INT_CODE_PROTECTION, "destination read only");
+ fresh += PAGE_SIZE;
+
+ /* Known issue in TCG: CCO flag is not honoured */
+ if (vm_is_tcg()) {
+ report_prefix_push("TCG");
+ report_skip("destination invalid");
+ report_skip("source invalid");
+ report_skip("source and destination invalid");
+ report_prefix_pop();
+ } else {
+ protect_page(fresh, PAGE_ENTRY_I);
+ cc = mvpg(CCO, fresh, source);
+ report(cc == 1, "destination invalid");
+ fresh += PAGE_SIZE;
+
+ protect_page(source, PAGE_ENTRY_I);
+ cc = mvpg(CCO, fresh, source);
+ report(cc == 2, "source invalid");
+ fresh += PAGE_SIZE;
+
+ protect_page(fresh, PAGE_ENTRY_I);
+ cc = mvpg(CCO, fresh, source);
+ report(cc == 2, "source and destination invalid");
+ fresh += PAGE_SIZE;
+ }
+
+ unprotect_page(source, PAGE_ENTRY_I);
+ report_prefix_pop();
+ report_prefix_pop();
+}
+
+int main(void)
+{
+ report_prefix_push("mvpg");
+
+ init_page(source);
+ fresh = alloc_pages_flags(MEM_ORDER, FLAG_DONTZERO | FLAG_FRESH);
+ assert(fresh);
+
+ test_exceptions();
+ test_success();
+ test_small_loop("nommu");
+
+ setup_vm();
+
+ test_small_loop("mmu");
+ test_mmu_prot();
+
+ report_prefix_pop();
+ return report_summary();
+}
diff --git a/s390x/sthyi.c b/s390x/sthyi.c
index d8dfc85..db90b56 100644
--- a/s390x/sthyi.c
+++ b/s390x/sthyi.c
@@ -128,7 +128,6 @@
report(sum, "core counts");
if (par->INFPVAL1 & PART_STSI_SUC) {
- report(par->INFPPNUM, "number");
report(memcmp(par->INFPPNAM, null_buf, sizeof(par->INFPPNAM)),
"name");
}
diff --git a/s390x/stsi.c b/s390x/stsi.c
index 4109b8d..87d4804 100644
--- a/s390x/stsi.c
+++ b/s390x/stsi.c
@@ -106,7 +106,7 @@
0x00, 0x03 };
/* EBCDIC for "KVM/" */
const uint8_t cpi_kvm[] = { 0xd2, 0xe5, 0xd4, 0x61 };
- const char *vm_name_ext = "kvm-unit-test";
+ const char vm_name_ext[] = "kvm-unit-test";
struct stsi_322 *data = (void *)pagebuf;
report_prefix_push("3.2.2");
diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg
index 2298be6..9f81a60 100644
--- a/s390x/unittests.cfg
+++ b/s390x/unittests.cfg
@@ -99,3 +99,7 @@
[sie]
file = sie.elf
+
+[mvpg]
+file = mvpg.elf
+timeout = 10