From patchwork Thu Oct 24 13:07:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Jones X-Patchwork-Id: 11209541 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 255EF13BD for ; Thu, 24 Oct 2019 13:07:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E549D21928 for ; Thu, 24 Oct 2019 13:07:54 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Q7GTwPLY" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2502336AbfJXNHx (ORCPT ); Thu, 24 Oct 2019 09:07:53 -0400 Received: from us-smtp-delivery-1.mimecast.com ([205.139.110.120]:55470 "EHLO us-smtp-1.mimecast.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2388377AbfJXNHx (ORCPT ); Thu, 24 Oct 2019 09:07:53 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1571922471; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=hdktmowMosU+qHQiA+aeRYFYmH7E+y/SH0pOtHuJyhk=; b=Q7GTwPLYwQyx9yE7p7QACmYwqXnC6tdKYvRqLtWHvhCNfCP2FnBs0l0LUWEHhy9UmvDW0a Kp/Ow7RKx625/w9QRiJyYpdfbhQpCXxj+ROcZ+XC8mHox/fC3Tg/OJnYSPBjwM66ReTIxm 8CeLhR+Y/+0sYDY4DWTeQURaLCXyxHQ= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-14-5bqa73AKOvCW58CxXP9b2g-1; Thu, 24 Oct 2019 09:07:21 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id C005A1800E06; Thu, 24 Oct 2019 13:07:20 +0000 (UTC) Received: from kamzik.brq.redhat.com (unknown [10.43.2.160]) by smtp.corp.redhat.com (Postfix) with ESMTP id C29FE5D70E; Thu, 24 Oct 2019 13:07:19 +0000 (UTC) From: Andrew Jones To: kvm@vger.kernel.org Cc: pbonzini@redhat.com, Alexander Graf Subject: [PULL 10/10] arm: Add PL031 test Date: Thu, 24 Oct 2019 15:07:01 +0200 Message-Id: <20191024130701.31238-11-drjones@redhat.com> In-Reply-To: <20191024130701.31238-1-drjones@redhat.com> References: <20191024130701.31238-1-drjones@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-MC-Unique: 5bqa73AKOvCW58CxXP9b2g-1 X-Mimecast-Spam-Score: 0 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Alexander Graf This patch adds a unit test for the PL031 RTC that is used in the virt machine. It just pokes basic functionality. I've mostly written it to familiarize myself with the device, but I suppose having the test around does not hurt, as it also exercises the GIC SPI interrupt path. Signed-off-by: Alexander Graf Reviewed-by: Andrew Jones --- arm/Makefile.common | 1 + arm/pl031.c | 262 ++++++++++++++++++++++++++++++++++++++++++++ lib/arm/asm/gic.h | 1 + 3 files changed, 264 insertions(+) create mode 100644 arm/pl031.c diff --git a/arm/Makefile.common b/arm/Makefile.common index f0c4b5d7620c..b8988f214d3b 100644 --- a/arm/Makefile.common +++ b/arm/Makefile.common @@ -11,6 +11,7 @@ tests-common += $(TEST_DIR)/pmu.flat tests-common += $(TEST_DIR)/gic.flat tests-common += $(TEST_DIR)/psci.flat tests-common += $(TEST_DIR)/sieve.flat +tests-common += $(TEST_DIR)/pl031.flat tests-all = $(tests-common) $(tests) all: directories $(tests-all) diff --git a/arm/pl031.c b/arm/pl031.c new file mode 100644 index 000000000000..5672f36f5fc2 --- /dev/null +++ b/arm/pl031.c @@ -0,0 +1,262 @@ +/* + * Verify PL031 functionality + * + * This test verifies whether the emulated PL031 behaves correctly. + * + * Copyright 2019 Amazon.com, Inc. or its affiliates. + * Author: Alexander Graf + * + * This work is licensed under the terms of the GNU LGPL, version 2. + */ +#include +#include +#include +#include +#include + +struct pl031_regs { + uint32_t dr; /* Data Register */ + uint32_t mr; /* Match Register */ + uint32_t lr; /* Load Register */ + union { + uint8_t cr; /* Control Register */ + uint32_t cr32; + }; + union { + uint8_t imsc; /* Interrupt Mask Set or Clear register */ + uint32_t imsc32; + }; + union { + uint8_t ris; /* Raw Interrupt Status */ + uint32_t ris32; + }; + union { + uint8_t mis; /* Masked Interrupt Status */ + uint32_t mis32; + }; + union { + uint8_t icr; /* Interrupt Clear Register */ + uint32_t icr32; + }; + uint32_t reserved[1008]; + uint32_t periph_id[4]; + uint32_t pcell_id[4]; +}; + +static u32 cntfrq; +static struct pl031_regs *pl031; +static int pl031_irq; +static void *gic_ispendr; +static void *gic_isenabler; +static volatile bool irq_triggered; + +static int check_id(void) +{ + uint32_t id[] = { 0x31, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; + int i; + + for (i = 0; i < ARRAY_SIZE(id); i++) + if (id[i] != readl(&pl031->periph_id[i])) + return 1; + + return 0; +} + +static int check_ro(void) +{ + uint32_t offs[] = { offsetof(struct pl031_regs, ris), + offsetof(struct pl031_regs, mis), + offsetof(struct pl031_regs, periph_id[0]), + offsetof(struct pl031_regs, periph_id[1]), + offsetof(struct pl031_regs, periph_id[2]), + offsetof(struct pl031_regs, periph_id[3]), + offsetof(struct pl031_regs, pcell_id[0]), + offsetof(struct pl031_regs, pcell_id[1]), + offsetof(struct pl031_regs, pcell_id[2]), + offsetof(struct pl031_regs, pcell_id[3]) }; + int i; + + for (i = 0; i < ARRAY_SIZE(offs); i++) { + uint32_t before32; + uint16_t before16; + uint8_t before8; + void *addr = (void*)pl031 + offs[i]; + uint32_t poison = 0xdeadbeefULL; + + before8 = readb(addr); + before16 = readw(addr); + before32 = readl(addr); + + writeb(poison, addr); + writew(poison, addr); + writel(poison, addr); + + if (before8 != readb(addr)) + return 1; + if (before16 != readw(addr)) + return 1; + if (before32 != readl(addr)) + return 1; + } + + return 0; +} + +static int check_rtc_freq(void) +{ + uint32_t seconds_to_wait = 2; + uint32_t before = readl(&pl031->dr); + uint64_t before_tick = get_cntvct(); + uint64_t target_tick = before_tick + (cntfrq * seconds_to_wait); + + /* Wait for 2 seconds */ + while (get_cntvct() < target_tick) ; + + if (readl(&pl031->dr) != before + seconds_to_wait) + return 1; + + return 0; +} + +static bool gic_irq_pending(void) +{ + uint32_t offset = (pl031_irq / 32) * 4; + + return readl(gic_ispendr + offset) & (1 << (pl031_irq & 31)); +} + +static void gic_irq_unmask(void) +{ + uint32_t offset = (pl031_irq / 32) * 4; + + writel(1 << (pl031_irq & 31), gic_isenabler + offset); +} + +static void irq_handler(struct pt_regs *regs) +{ + u32 irqstat = gic_read_iar(); + u32 irqnr = gic_iar_irqnr(irqstat); + + gic_write_eoir(irqstat); + + if (irqnr == pl031_irq) { + report(" RTC RIS == 1", readl(&pl031->ris) == 1); + report(" RTC MIS == 1", readl(&pl031->mis) == 1); + + /* Writing any value should clear IRQ status */ + writel(0x80000000ULL, &pl031->icr); + + report(" RTC RIS == 0", readl(&pl031->ris) == 0); + report(" RTC MIS == 0", readl(&pl031->mis) == 0); + irq_triggered = true; + } else { + report_info("Unexpected interrupt: %d\n", irqnr); + return; + } +} + +static int check_rtc_irq(void) +{ + uint32_t seconds_to_wait = 1; + uint32_t before = readl(&pl031->dr); + uint64_t before_tick = get_cntvct(); + uint64_t target_tick = before_tick + (cntfrq * (seconds_to_wait + 1)); + + report_info("Checking IRQ trigger (MR)"); + + irq_triggered = false; + + /* Fire IRQ in 1 second */ + writel(before + seconds_to_wait, &pl031->mr); + +#ifdef __aarch64__ + install_irq_handler(EL1H_IRQ, irq_handler); +#else + install_exception_handler(EXCPTN_IRQ, irq_handler); +#endif + + /* Wait until 2 seconds are over */ + while (get_cntvct() < target_tick) ; + + report(" RTC IRQ not delivered without mask", !gic_irq_pending()); + + /* Mask the IRQ so that it gets delivered */ + writel(1, &pl031->imsc); + report(" RTC IRQ pending now", gic_irq_pending()); + + /* Enable retrieval of IRQ */ + gic_irq_unmask(); + local_irq_enable(); + + report(" IRQ triggered", irq_triggered); + report(" RTC IRQ not pending anymore", !gic_irq_pending()); + if (!irq_triggered) { + report_info(" RTC RIS: %x", readl(&pl031->ris)); + report_info(" RTC MIS: %x", readl(&pl031->mis)); + report_info(" RTC IMSC: %x", readl(&pl031->imsc)); + report_info(" GIC IRQs pending: %08x %08x", readl(gic_ispendr), readl(gic_ispendr + 4)); + } + + local_irq_disable(); + return 0; +} + +static void rtc_irq_init(void) +{ + gic_enable_defaults(); + + switch (gic_version()) { + case 2: + gic_ispendr = gicv2_dist_base() + GICD_ISPENDR; + gic_isenabler = gicv2_dist_base() + GICD_ISENABLER; + break; + case 3: + gic_ispendr = gicv3_dist_base() + GICD_ISPENDR; + gic_isenabler = gicv3_dist_base() + GICD_ISENABLER; + break; + } +} + +static int rtc_fdt_init(void) +{ + const struct fdt_property *prop; + const void *fdt = dt_fdt(); + struct dt_pbus_reg base; + int node, len; + u32 *data; + int ret; + + node = fdt_node_offset_by_compatible(fdt, -1, "arm,pl031"); + if (node < 0) + return -1; + + prop = fdt_get_property(fdt, node, "interrupts", &len); + assert(prop && len == (3 * sizeof(u32))); + data = (u32 *)prop->data; + assert(data[0] == 0); /* SPI */ + pl031_irq = SPI(fdt32_to_cpu(data[1])); + + ret = dt_pbus_translate_node(node, 0, &base); + assert(!ret); + pl031 = ioremap(base.addr, base.size); + + return 0; +} + +int main(int argc, char **argv) +{ + cntfrq = get_cntfrq(); + rtc_irq_init(); + if (rtc_fdt_init()) { + report_skip("Skipping PL031 tests. No device present."); + return 0; + } + + report("Periph/PCell IDs match", !check_id()); + report("R/O fields are R/O", !check_ro()); + report("RTC ticks at 1HZ", !check_rtc_freq()); + report("RTC IRQ not pending yet", !gic_irq_pending()); + check_rtc_irq(); + + return report_summary(); +} diff --git a/lib/arm/asm/gic.h b/lib/arm/asm/gic.h index f6dfb907a7d5..1fc10a096259 100644 --- a/lib/arm/asm/gic.h +++ b/lib/arm/asm/gic.h @@ -41,6 +41,7 @@ #include #define PPI(irq) ((irq) + 16) +#define SPI(irq) ((irq) + GIC_FIRST_SPI) #ifndef __ASSEMBLY__ #include