From patchwork Wed Jun 7 15:05:19 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Huth X-Patchwork-Id: 9771787 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 37D3E60364 for ; Wed, 7 Jun 2017 15:05:31 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 305122847D for ; Wed, 7 Jun 2017 15:05:31 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 24D85284B2; Wed, 7 Jun 2017 15:05:31 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 843072847D for ; Wed, 7 Jun 2017 15:05:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751190AbdFGPF2 (ORCPT ); Wed, 7 Jun 2017 11:05:28 -0400 Received: from mx1.redhat.com ([209.132.183.28]:47840 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750831AbdFGPF2 (ORCPT ); Wed, 7 Jun 2017 11:05:28 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id AC5163DBF2 for ; Wed, 7 Jun 2017 15:05:27 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com AC5163DBF2 Authentication-Results: ext-mx06.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx06.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=thuth@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com AC5163DBF2 Received: from thh440s.redhat.com (ovpn-116-81.ams2.redhat.com [10.36.116.81]) by smtp.corp.redhat.com (Postfix) with ESMTP id 56F9F171E7; Wed, 7 Jun 2017 15:05:20 +0000 (UTC) From: Thomas Huth To: David Hildenbrand , kvm@vger.kernel.org Cc: Paolo Bonzini , =?UTF-8?q?Radim=20Kr=C4=8Dm=C3=A1=C5=99?= Subject: [kvm-unit-tests PATCH v4] s390x: Interception tests Date: Wed, 7 Jun 2017 17:05:19 +0200 Message-Id: <1496847919-19595-1-git-send-email-thuth@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.30]); Wed, 07 Jun 2017 15:05:27 +0000 (UTC) Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Certain CPU instructions will cause an exit of the virtual machine. Run some of these instructions to check whether they are emulated right by KVM (or QEMU). Reviewed-by: David Hildenbrand Signed-off-by: Thomas Huth --- v4: - Use %%c0 for lctlg instead of 0 - Replaced "CPU ID" with "CPU address" in the STAP test lib/s390x/asm/interrupt.h | 20 ++++ s390x/Makefile | 1 + s390x/intercept.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++ s390x/unittests.cfg | 3 + 4 files changed, 251 insertions(+) create mode 100644 s390x/intercept.c diff --git a/lib/s390x/asm/interrupt.h b/lib/s390x/asm/interrupt.h index 383d312..41be039 100644 --- a/lib/s390x/asm/interrupt.h +++ b/lib/s390x/asm/interrupt.h @@ -15,4 +15,24 @@ void handle_pgm_int(void); void expect_pgm_int(void); void check_pgm_int_code(uint16_t code); +/* Activate low-address protection */ +static inline void low_prot_enable(void) +{ + uint64_t cr0; + + asm volatile (" stctg %%c0,%%c0,%0 " : : "Q"(cr0) : "memory"); + cr0 |= 1ULL << (63-35); + asm volatile (" lctlg %%c0,%%c0,%0 " : : "Q"(cr0)); +} + +/* Disable low-address protection */ +static inline void low_prot_disable(void) +{ + uint64_t cr0; + + asm volatile (" stctg %%c0,%%c0,%0 " : : "Q"(cr0) : "memory"); + cr0 &= ~(1ULL << (63-35)); + asm volatile (" lctlg %%c0,%%c0,%0 " : : "Q"(cr0)); +} + #endif diff --git a/s390x/Makefile b/s390x/Makefile index b48f8ab..a61e163 100644 --- a/s390x/Makefile +++ b/s390x/Makefile @@ -1,4 +1,5 @@ tests = $(TEST_DIR)/selftest.elf +tests += $(TEST_DIR)/intercept.elf all: directories test_cases diff --git a/s390x/intercept.c b/s390x/intercept.c new file mode 100644 index 0000000..4558860 --- /dev/null +++ b/s390x/intercept.c @@ -0,0 +1,227 @@ +/* + * Interception tests - for s390x CPU instruction that cause a VM exit + * + * Copyright (c) 2017 Red Hat Inc + * + * Authors: + * Thomas Huth + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License version 2. + */ +#include +#include +#include +#include + +static uint8_t pagebuf[PAGE_SIZE * 2] __attribute__((aligned(PAGE_SIZE * 2))); + +static unsigned long nr_iterations; +static unsigned long time_to_run; + +/* Test the STORE PREFIX instruction */ +static void test_stpx(void) +{ + uint32_t old_prefix = -1U, tst_prefix = -1U; + uint32_t new_prefix = (uint32_t)(intptr_t)pagebuf; + + /* Can we successfully change the prefix? */ + asm volatile ( + " stpx %0\n" + " spx %2\n" + " stpx %1\n" + " spx %0\n" + : "+Q"(old_prefix), "+Q"(tst_prefix) + : "Q"(new_prefix)); + report("store prefix", old_prefix == 0 && tst_prefix == new_prefix); + + expect_pgm_int(); + low_prot_enable(); + asm volatile(" stpx 0(%0) " : : "r"(8)); + low_prot_disable(); + check_pgm_int_code(PGM_INT_CODE_PROTECTION); + + expect_pgm_int(); + asm volatile(" stpx 0(%0) " : : "r"(1)); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + + expect_pgm_int(); + asm volatile(" stpx 0(%0) " : : "r"(-8)); + check_pgm_int_code(PGM_INT_CODE_ADDRESSING); +} + +/* Test the SET PREFIX instruction */ +static void test_spx(void) +{ + uint32_t new_prefix = (uint32_t)(intptr_t)pagebuf; + uint32_t old_prefix; + + memset(pagebuf, 0, PAGE_SIZE * 2); + + /* + * Temporarily change the prefix page to our buffer, and store + * some facility bits there ... at least some of them should be + * set in our buffer afterwards. + */ + asm volatile ( + " stpx %0\n" + " spx %1\n" + " stfl 0\n" + " spx %0\n" + : "+Q"(old_prefix) + : "Q"(new_prefix) + : "memory"); + report("stfl to new prefix", pagebuf[GEN_LC_STFL] != 0); + + expect_pgm_int(); + asm volatile(" spx 0(%0) " : : "r"(1)); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + + expect_pgm_int(); + asm volatile(" spx 0(%0) " : : "r"(-8)); + check_pgm_int_code(PGM_INT_CODE_ADDRESSING); +} + +/* Test the STORE CPU ADDRESS instruction */ +static void test_stap(void) +{ + uint16_t cpuid = 0xffff; + + asm volatile ("stap %0\n" : "+Q"(cpuid)); + report("get cpu address", cpuid != 0xffff); + + expect_pgm_int(); + low_prot_enable(); + asm volatile ("stap 0(%0)\n" : : "r"(8)); + low_prot_disable(); + check_pgm_int_code(PGM_INT_CODE_PROTECTION); + + expect_pgm_int(); + asm volatile ("stap 0(%0)\n" : : "r"(1)); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + + expect_pgm_int(); + asm volatile ("stap 0(%0)\n" : : "r"(-8)); + check_pgm_int_code(PGM_INT_CODE_ADDRESSING); +} + +/* Test the TEST BLOCK instruction */ +static void test_testblock(void) +{ + int cc; + + memset(pagebuf, 0xaa, PAGE_SIZE); + + asm volatile ( + " lghi %%r0,0\n" + " tb %1\n" + " ipm %0\n" + " srl %0,28\n" + : "=d" (cc) + : "a"(pagebuf + 0x123) + : "memory", "0", "cc"); + report("page cleared", + cc == 0 && pagebuf[0] == 0 && pagebuf[PAGE_SIZE - 1] == 0); + + expect_pgm_int(); + low_prot_enable(); + asm volatile (" tb %0 " : : "r"(4096)); + low_prot_disable(); + check_pgm_int_code(PGM_INT_CODE_PROTECTION); + + expect_pgm_int(); + asm volatile (" tb %0 " : : "r"(-4096)); + check_pgm_int_code(PGM_INT_CODE_ADDRESSING); +} + +static uint64_t get_clock_ms(void) +{ + uint64_t clk; + + asm volatile(" stck %0 " : : "Q"(clk) : "memory"); + + /* Bit 51 is incrememented each microsecond */ + return (clk >> (63 - 51)) / 1000; +} + +struct { + const char *name; + void (*func)(void); + bool run_it; +} tests[] = { + { "stpx", test_stpx, false }, + { "spx", test_spx, false }, + { "stap", test_stap, false }, + { "testblock", test_testblock, false }, + { NULL, NULL, false } +}; + +void parse_intercept_test_args(int argc, char **argv) +{ + int i, ti; + bool run_all = true; + + for (i = 1; i < argc; i++) { + if (!strcmp("-i", argv[i])) { + i++; + if (i >= argc) + report_abort("-i needs a parameter"); + nr_iterations = atol(argv[i]); + } else if (!strcmp("-t", argv[i])) { + i++; + if (i >= argc) + report_abort("-t needs a parameter"); + time_to_run = atol(argv[i]); + } else for (ti = 0; tests[ti].name != NULL; ti++) { + if (!strcmp(tests[ti].name, argv[i])) { + run_all = false; + tests[ti].run_it = true; + break; + } else if (tests[ti + 1].name == NULL) { + report_abort("Unsupported parameter '%s'", + argv[i]); + } + } + } + + if (run_all) { + for (ti = 0; tests[ti].name != NULL; ti++) + tests[ti].run_it = true; + } +} + +int main(int argc, char **argv) +{ + uint64_t startclk; + int ti; + + parse_intercept_test_args(argc, argv); + + if (nr_iterations == 0 && time_to_run == 0) + nr_iterations = 1; + + report_prefix_push("intercept"); + + startclk = get_clock_ms(); + for (;;) { + for (ti = 0; tests[ti].name != NULL; ti++) { + report_prefix_push(tests[ti].name); + if (tests[ti].run_it) + tests[ti].func(); + report_prefix_pop(); + } + if (nr_iterations) { + nr_iterations -= 1; + if (nr_iterations == 0) + break; + } + if (time_to_run) { + if (get_clock_ms() - startclk > time_to_run) + break; + } + } + + report_prefix_pop(); + + return report_summary(); +} diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg index 92e01ab..3b6b892 100644 --- a/s390x/unittests.cfg +++ b/s390x/unittests.cfg @@ -22,3 +22,6 @@ file = selftest.elf groups = selftest extra_params = -append 'test 123' + +[intercept] +file = intercept.elf