Received: by 2002:ac0:aa62:0:0:0:0:0 with SMTP id w31-v6csp1933334ima; Thu, 25 Oct 2018 07:14:04 -0700 (PDT) X-Google-Smtp-Source: AJdET5f7zYxRG1lcfuIbuEjr8PlHtqi+ceXi/bNTsg8R+VX3VZX2/QR6qXivdzQhTgtLhjlPQUng X-Received: by 2002:a62:7893:: with SMTP id t141-v6mr1711034pfc.259.1540476844513; Thu, 25 Oct 2018 07:14:04 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1540476844; cv=none; d=google.com; s=arc-20160816; b=xWDzV0PICi8RjBCMOR9QR5LKI0Y5hvMbz+ChzrTe6xZ0q2RWG3vXsyK9Sf03NyP8Lm hN6pZinkkcez6A7v0bWdP78cN1hJzuxKoKIeXIqZ+a+l/R1swWTEH5kG3Nqf3jpM+rvp POeBIjuwjg6/mR8JFh4yq6yynVO/EydILZbdGCf8zJda51AKNgRperS3r/HvVU6X82Gt zB68qZO+b8/cuLYFyf3z/TbCh76jJYgx8XFkqJpRdt21KkPCtS+bDjf5/8edZq6cL50C 8ZFv2NlAuCIEL8PVBaspIJrSe8z93Bh0+Off0odgUdfu9avMkGGyKM6eAA8gWfUxA3PB 55OA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature; bh=tO3naSM3YT6zHdg1DgfFUWl3aPF0lBIqTnW8jO0GYhs=; b=MZXzIKiLh2yzGswjfKw0GhkN4pq76wsqKPxXErYzuqsxcLcbLhCKmtcp8QqTnTkuyw Z5Z4zTcY5/eX71ImYHZK2Bi5s19SGpdPlXPW/gLH4JT+UXUvZqsUAe35q2J6woyJzDM7 2phv+28hhYnJpD1CN8MSI/gN9lLKWV633GUua8HwxHJYztRRi96HAuvjUGpNVPWr0pGg Ch2Yg0g70uMdCZntX04FlxzVhktLtb9i++Xdrl6BULUc7yu56GRNdVM0iNSF3/ASlkrX 5DH/gKTZC1sFfHznHGlJhU4de38XKTzoo8AOSc7XX5Ncnihqh0ScABqgs5leSotBh6b3 8hBQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=ADzRNqWP; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id f35-v6si7995202plh.357.2018.10.25.07.13.38; Thu, 25 Oct 2018 07:14:04 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=ADzRNqWP; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728378AbeJYWoo (ORCPT + 99 others); Thu, 25 Oct 2018 18:44:44 -0400 Received: from mail.kernel.org ([198.145.29.99]:53710 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728299AbeJYWon (ORCPT ); Thu, 25 Oct 2018 18:44:43 -0400 Received: from sasha-vm.mshome.net (unknown [167.98.65.38]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id D16A42084A; Thu, 25 Oct 2018 14:11:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1540476707; bh=GCijDhvCw21F+utfm6XATO7hGcbf1JlIxzwWM379jdM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ADzRNqWPdVss27oV/Man+16mJAOOLw4mgIV1YmIjkgLuBmpRraMHPN6jyU+DPu1KM Pi8nR9v7K6u4PvhGnHpTaNbFDGJUEPEI5lyTrHHE32wdZD0C9D9iZUX9YR0ouqjqUi dZ9SXyIOoH1IaGou0Mvp4kEMtiqMpMNr4amwfU7k= From: Sasha Levin To: stable@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Michael Neuling , Michael Ellerman , Sasha Levin Subject: [PATCH AUTOSEL 4.14 37/46] selftests/powerpc: Add ptrace hw breakpoint test Date: Thu, 25 Oct 2018 10:10:44 -0400 Message-Id: <20181025141053.213330-37-sashal@kernel.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181025141053.213330-1-sashal@kernel.org> References: <20181025141053.213330-1-sashal@kernel.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Michael Neuling [ Upstream commit 9c2ddfe55c42bf4b9bc336a0650ab78f9222a159 ] This test the ptrace hw breakpoints via PTRACE_SET_DEBUGREG and PPC_PTRACE_SETHWDEBUG. This test was use to find the bugs fixed by these recent commits: 4f7c06e26e powerpc/ptrace: Fix setting 512B aligned breakpoints with PTRACE_SET_DEBUGREG cd6ef7eebf powerpc/ptrace: Fix enforcement of DAWR constraints Signed-off-by: Michael Neuling [mpe: Add SPDX tag, clang format it] Signed-off-by: Michael Ellerman Signed-off-by: Sasha Levin --- .../selftests/powerpc/ptrace/.gitignore | 1 + .../testing/selftests/powerpc/ptrace/Makefile | 2 +- .../selftests/powerpc/ptrace/ptrace-hwbreak.c | 342 ++++++++++++++++++ 3 files changed, 344 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c diff --git a/tools/testing/selftests/powerpc/ptrace/.gitignore b/tools/testing/selftests/powerpc/ptrace/.gitignore index 349acfafc95b..9dcc16ea8179 100644 --- a/tools/testing/selftests/powerpc/ptrace/.gitignore +++ b/tools/testing/selftests/powerpc/ptrace/.gitignore @@ -8,3 +8,4 @@ ptrace-vsx ptrace-tm-vsx ptrace-tm-spd-vsx ptrace-tm-spr +ptrace-hwbreak diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile index 480305266504..0e2f4601d1a8 100644 --- a/tools/testing/selftests/powerpc/ptrace/Makefile +++ b/tools/testing/selftests/powerpc/ptrace/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \ ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \ - ptrace-tm-spd-vsx ptrace-tm-spr + ptrace-tm-spd-vsx ptrace-tm-spr ptrace-hwbreak include ../../lib.mk diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c b/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c new file mode 100644 index 000000000000..3066d310f32b --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Ptrace test for hw breakpoints + * + * Based on tools/testing/selftests/breakpoints/breakpoint_test.c + * + * This test forks and the parent then traces the child doing various + * types of ptrace enabled breakpoints + * + * Copyright (C) 2018 Michael Neuling, IBM Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ptrace.h" + +/* Breakpoint access modes */ +enum { + BP_X = 1, + BP_RW = 2, + BP_W = 4, +}; + +static pid_t child_pid; +static struct ppc_debug_info dbginfo; + +static void get_dbginfo(void) +{ + int ret; + + ret = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo); + if (ret) { + perror("Can't get breakpoint info\n"); + exit(-1); + } +} + +static bool hwbreak_present(void) +{ + return (dbginfo.num_data_bps != 0); +} + +static bool dawr_present(void) +{ + return !!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_DAWR); +} + +static void set_breakpoint_addr(void *addr) +{ + int ret; + + ret = ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, addr); + if (ret) { + perror("Can't set breakpoint addr\n"); + exit(-1); + } +} + +static int set_hwbreakpoint_addr(void *addr, int range) +{ + int ret; + + struct ppc_hw_breakpoint info; + + info.version = 1; + info.trigger_type = PPC_BREAKPOINT_TRIGGER_RW; + info.addr_mode = PPC_BREAKPOINT_MODE_EXACT; + if (range > 0) + info.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE; + info.condition_mode = PPC_BREAKPOINT_CONDITION_NONE; + info.addr = (__u64)addr; + info.addr2 = (__u64)addr + range; + info.condition_value = 0; + + ret = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, &info); + if (ret < 0) { + perror("Can't set breakpoint\n"); + exit(-1); + } + return ret; +} + +static int del_hwbreakpoint_addr(int watchpoint_handle) +{ + int ret; + + ret = ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, watchpoint_handle); + if (ret < 0) { + perror("Can't delete hw breakpoint\n"); + exit(-1); + } + return ret; +} + +#define DAWR_LENGTH_MAX 512 + +/* Dummy variables to test read/write accesses */ +static unsigned long long + dummy_array[DAWR_LENGTH_MAX / sizeof(unsigned long long)] + __attribute__((aligned(512))); +static unsigned long long *dummy_var = dummy_array; + +static void write_var(int len) +{ + long long *plval; + char *pcval; + short *psval; + int *pival; + + switch (len) { + case 1: + pcval = (char *)dummy_var; + *pcval = 0xff; + break; + case 2: + psval = (short *)dummy_var; + *psval = 0xffff; + break; + case 4: + pival = (int *)dummy_var; + *pival = 0xffffffff; + break; + case 8: + plval = (long long *)dummy_var; + *plval = 0xffffffffffffffffLL; + break; + } +} + +static void read_var(int len) +{ + char cval __attribute__((unused)); + short sval __attribute__((unused)); + int ival __attribute__((unused)); + long long lval __attribute__((unused)); + + switch (len) { + case 1: + cval = *(char *)dummy_var; + break; + case 2: + sval = *(short *)dummy_var; + break; + case 4: + ival = *(int *)dummy_var; + break; + case 8: + lval = *(long long *)dummy_var; + break; + } +} + +/* + * Do the r/w accesses to trigger the breakpoints. And run + * the usual traps. + */ +static void trigger_tests(void) +{ + int len, ret; + + ret = ptrace(PTRACE_TRACEME, 0, NULL, 0); + if (ret) { + perror("Can't be traced?\n"); + return; + } + + /* Wake up father so that it sets up the first test */ + kill(getpid(), SIGUSR1); + + /* Test write watchpoints */ + for (len = 1; len <= sizeof(long); len <<= 1) + write_var(len); + + /* Test read/write watchpoints (on read accesses) */ + for (len = 1; len <= sizeof(long); len <<= 1) + read_var(len); + + /* Test when breakpoint is unset */ + + /* Test write watchpoints */ + for (len = 1; len <= sizeof(long); len <<= 1) + write_var(len); + + /* Test read/write watchpoints (on read accesses) */ + for (len = 1; len <= sizeof(long); len <<= 1) + read_var(len); +} + +static void check_success(const char *msg) +{ + const char *msg2; + int status; + + /* Wait for the child to SIGTRAP */ + wait(&status); + + msg2 = "Failed"; + + if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { + msg2 = "Child process hit the breakpoint"; + } + + printf("%s Result: [%s]\n", msg, msg2); +} + +static void launch_watchpoints(char *buf, int mode, int len, + struct ppc_debug_info *dbginfo, bool dawr) +{ + const char *mode_str; + unsigned long data = (unsigned long)(dummy_var); + int wh, range; + + data &= ~0x7UL; + + if (mode == BP_W) { + data |= (1UL << 1); + mode_str = "write"; + } else { + data |= (1UL << 0); + data |= (1UL << 1); + mode_str = "read"; + } + + /* Set DABR_TRANSLATION bit */ + data |= (1UL << 2); + + /* use PTRACE_SET_DEBUGREG breakpoints */ + set_breakpoint_addr((void *)data); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len); + check_success(buf); + /* Unregister hw brkpoint */ + set_breakpoint_addr(NULL); + + data = (data & ~7); /* remove dabr control bits */ + + /* use PPC_PTRACE_SETHWDEBUG breakpoint */ + if (!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE)) + return; /* not supported */ + wh = set_hwbreakpoint_addr((void *)data, 0); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len); + check_success(buf); + /* Unregister hw brkpoint */ + del_hwbreakpoint_addr(wh); + + /* try a wider range */ + range = 8; + if (dawr) + range = 512 - ((int)data & (DAWR_LENGTH_MAX - 1)); + wh = set_hwbreakpoint_addr((void *)data, range); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len); + check_success(buf); + /* Unregister hw brkpoint */ + del_hwbreakpoint_addr(wh); +} + +/* Set the breakpoints and check the child successfully trigger them */ +static int launch_tests(bool dawr) +{ + char buf[1024]; + int len, i, status; + + struct ppc_debug_info dbginfo; + + i = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo); + if (i) { + perror("Can't set breakpoint info\n"); + exit(-1); + } + if (!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE)) + printf("WARNING: Kernel doesn't support PPC_PTRACE_SETHWDEBUG\n"); + + /* Write watchpoint */ + for (len = 1; len <= sizeof(long); len <<= 1) + launch_watchpoints(buf, BP_W, len, &dbginfo, dawr); + + /* Read-Write watchpoint */ + for (len = 1; len <= sizeof(long); len <<= 1) + launch_watchpoints(buf, BP_RW, len, &dbginfo, dawr); + + ptrace(PTRACE_CONT, child_pid, NULL, 0); + + /* + * Now we have unregistered the breakpoint, access by child + * should not cause SIGTRAP. + */ + + wait(&status); + + if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { + printf("FAIL: Child process hit the breakpoint, which is not expected\n"); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + return TEST_FAIL; + } + + if (WIFEXITED(status)) + printf("Child exited normally\n"); + + return TEST_PASS; +} + +static int ptrace_hwbreak(void) +{ + pid_t pid; + int ret; + bool dawr; + + pid = fork(); + if (!pid) { + trigger_tests(); + return 0; + } + + wait(NULL); + + child_pid = pid; + + get_dbginfo(); + SKIP_IF(!hwbreak_present()); + dawr = dawr_present(); + + ret = launch_tests(dawr); + + wait(NULL); + + return ret; +} + +int main(int argc, char **argv, char **envp) +{ + return test_harness(ptrace_hwbreak, "ptrace-hwbreak"); +} -- 2.17.1