summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/net/bpf_exp.l1
-rw-r--r--tools/net/bpf_exp.y11
-rw-r--r--tools/net/bpf_jit_disasm.c20
-rw-r--r--tools/power/acpi/Makefile28
-rw-r--r--tools/power/acpi/common/cmfsize.c101
-rw-r--r--tools/power/acpi/common/getopt.c239
-rw-r--r--tools/power/acpi/man/acpidump.885
-rw-r--r--tools/power/acpi/os_specific/service_layers/oslinuxtbl.c1329
-rw-r--r--tools/power/acpi/os_specific/service_layers/osunixdir.c204
-rw-r--r--tools/power/acpi/os_specific/service_layers/osunixmap.c151
-rw-r--r--tools/power/acpi/tools/acpidump/acpidump.c559
-rw-r--r--tools/power/acpi/tools/acpidump/acpidump.h130
-rw-r--r--tools/power/acpi/tools/acpidump/apdump.c451
-rw-r--r--tools/power/acpi/tools/acpidump/apfiles.c228
-rw-r--r--tools/power/acpi/tools/acpidump/apmain.c351
-rw-r--r--tools/power/acpi/tools/ec/Makefile22
-rw-r--r--tools/power/acpi/tools/ec/ec_access.c238
-rw-r--r--tools/power/cpupower/Makefile12
-rw-r--r--tools/power/cpupower/README24
-rw-r--r--tools/power/cpupower/ToDo1
-rw-r--r--tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c2
-rw-r--r--tools/power/cpupower/man/cpupower-frequency-info.13
-rw-r--r--tools/power/cpupower/man/cpupower-idle-set.110
-rw-r--r--tools/power/cpupower/man/cpupower-info.12
-rw-r--r--tools/power/cpupower/man/cpupower-set.131
-rw-r--r--tools/power/cpupower/utils/cpufreq-info.c110
-rw-r--r--tools/power/cpupower/utils/cpuidle-set.c75
-rw-r--r--tools/power/cpupower/utils/cpupower-info.c42
-rw-r--r--tools/power/cpupower/utils/cpupower-set.c43
-rw-r--r--tools/power/cpupower/utils/cpupower.c14
-rw-r--r--tools/power/x86/turbostat/turbostat.c4
-rw-r--r--tools/testing/selftests/Makefile1
-rw-r--r--tools/testing/selftests/net/Makefile8
-rw-r--r--tools/testing/selftests/powerpc/Makefile2
-rw-r--r--tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h5
-rw-r--r--tools/testing/selftests/powerpc/harness.c15
-rw-r--r--tools/testing/selftests/powerpc/mm/Makefile18
-rw-r--r--tools/testing/selftests/powerpc/mm/hugetlb_vs_thp_test.c72
-rw-r--r--tools/testing/selftests/powerpc/pmu/Makefile26
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/Makefile32
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c106
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c59
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c93
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c89
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c58
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c117
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/ebb.c727
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/ebb.h78
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S365
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c86
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c92
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c86
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c131
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S43
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c79
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c164
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c100
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c91
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c109
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c61
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c106
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c93
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/reg.h49
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c39
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c91
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c83
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/trace.c300
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/trace.h41
-rw-r--r--tools/testing/selftests/powerpc/pmu/event.c26
-rw-r--r--tools/testing/selftests/powerpc/pmu/event.h4
-rw-r--r--tools/testing/selftests/powerpc/pmu/lib.c252
-rw-r--r--tools/testing/selftests/powerpc/pmu/lib.h41
-rw-r--r--tools/testing/selftests/powerpc/pmu/loop.S73
-rw-r--r--tools/testing/selftests/powerpc/subunit.h5
-rw-r--r--tools/testing/selftests/powerpc/tm/Makefile15
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-resched-dscr.c90
-rw-r--r--tools/testing/selftests/powerpc/utils.h12
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/configinit.sh2
-rw-r--r--tools/testing/selftests/rcutorture/bin/functions.sh48
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-build.sh6
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh2
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh2
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-recheck.sh24
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh47
-rw-r--r--tools/testing/selftests/rcutorture/bin/kvm.sh142
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/parse-torture.sh (renamed from tools/testing/selftests/rcutorture/bin/parse-rcutorture.sh)22
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE02-T25
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot1
-rw-r--r--tools/testing/selftests/sysctl/Makefile19
-rw-r--r--tools/testing/selftests/sysctl/common_tests109
-rw-r--r--tools/testing/selftests/sysctl/run_numerictests10
-rw-r--r--tools/testing/selftests/sysctl/run_stringtests77
-rw-r--r--tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c349
-rw-r--r--tools/usb/ffs-aio-example/multibuff/host_app/Makefile13
-rw-r--r--tools/usb/ffs-aio-example/multibuff/host_app/test.c146
-rw-r--r--tools/usb/ffs-aio-example/simple/device_app/aio_simple.c335
-rw-r--r--tools/usb/ffs-aio-example/simple/host_app/Makefile13
-rw-r--r--tools/usb/ffs-aio-example/simple/host_app/test.c148
-rw-r--r--tools/vm/page-types.c35
99 files changed, 9394 insertions, 935 deletions
diff --git a/tools/net/bpf_exp.l b/tools/net/bpf_exp.l
index bf7be77..833a966 100644
--- a/tools/net/bpf_exp.l
+++ b/tools/net/bpf_exp.l
@@ -92,6 +92,7 @@ extern void yyerror(const char *str);
"#"?("cpu") { return K_CPU; }
"#"?("vlan_tci") { return K_VLANT; }
"#"?("vlan_pr") { return K_VLANP; }
+"#"?("rand") { return K_RAND; }
":" { return ':'; }
"," { return ','; }
diff --git a/tools/net/bpf_exp.y b/tools/net/bpf_exp.y
index d15efc9..e6306c5 100644
--- a/tools/net/bpf_exp.y
+++ b/tools/net/bpf_exp.y
@@ -56,7 +56,7 @@ static void bpf_set_jmp_label(char *label, enum jmp_type type);
%token OP_LDXI
%token K_PKT_LEN K_PROTO K_TYPE K_NLATTR K_NLATTR_NEST K_MARK K_QUEUE K_HATYPE
-%token K_RXHASH K_CPU K_IFIDX K_VLANT K_VLANP K_POFF
+%token K_RXHASH K_CPU K_IFIDX K_VLANT K_VLANP K_POFF K_RAND
%token ':' ',' '[' ']' '(' ')' 'x' 'a' '+' 'M' '*' '&' '#' '%'
@@ -164,6 +164,9 @@ ldb
| OP_LDB K_POFF {
bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0,
SKF_AD_OFF + SKF_AD_PAY_OFFSET); }
+ | OP_LDB K_RAND {
+ bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0,
+ SKF_AD_OFF + SKF_AD_RANDOM); }
;
ldh
@@ -212,6 +215,9 @@ ldh
| OP_LDH K_POFF {
bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0,
SKF_AD_OFF + SKF_AD_PAY_OFFSET); }
+ | OP_LDH K_RAND {
+ bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0,
+ SKF_AD_OFF + SKF_AD_RANDOM); }
;
ldi
@@ -265,6 +271,9 @@ ld
| OP_LD K_POFF {
bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0,
SKF_AD_OFF + SKF_AD_PAY_OFFSET); }
+ | OP_LD K_RAND {
+ bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0,
+ SKF_AD_OFF + SKF_AD_RANDOM); }
| OP_LD 'M' '[' number ']' {
bpf_set_curr_instr(BPF_LD | BPF_MEM, 0, 0, $4); }
| OP_LD '[' 'x' '+' number ']' {
diff --git a/tools/net/bpf_jit_disasm.c b/tools/net/bpf_jit_disasm.c
index cfe0cdc..c5baf9c 100644
--- a/tools/net/bpf_jit_disasm.c
+++ b/tools/net/bpf_jit_disasm.c
@@ -43,8 +43,7 @@ static void get_exec_path(char *tpath, size_t size)
free(path);
}
-static void get_asm_insns(uint8_t *image, size_t len, unsigned long base,
- int opcodes)
+static void get_asm_insns(uint8_t *image, size_t len, int opcodes)
{
int count, i, pc = 0;
char tpath[256];
@@ -107,13 +106,13 @@ static void put_klog_buff(char *buff)
}
static int get_last_jit_image(char *haystack, size_t hlen,
- uint8_t *image, size_t ilen,
- unsigned long *base)
+ uint8_t *image, size_t ilen)
{
char *ptr, *pptr, *tmp;
off_t off = 0;
int ret, flen, proglen, pass, ulen = 0;
regmatch_t pmatch[1];
+ unsigned long base;
regex_t regex;
if (hlen == 0)
@@ -136,7 +135,7 @@ static int get_last_jit_image(char *haystack, size_t hlen,
ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
ret = sscanf(ptr, "flen=%d proglen=%d pass=%d image=%lx",
- &flen, &proglen, &pass, base);
+ &flen, &proglen, &pass, &base);
if (ret != 4)
return 0;
@@ -162,7 +161,7 @@ static int get_last_jit_image(char *haystack, size_t hlen,
assert(ulen == proglen);
printf("%d bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
proglen, pass, flen);
- printf("%lx + <x>:\n", *base);
+ printf("%lx + <x>:\n", base);
regfree(&regex);
return ulen;
@@ -172,8 +171,7 @@ int main(int argc, char **argv)
{
int len, klen, opcodes = 0;
char *kbuff;
- unsigned long base;
- uint8_t image[4096];
+ static uint8_t image[32768];
if (argc > 1) {
if (!strncmp("-o", argv[argc - 1], 2)) {
@@ -189,9 +187,9 @@ int main(int argc, char **argv)
kbuff = get_klog_buff(&klen);
- len = get_last_jit_image(kbuff, klen, image, sizeof(image), &base);
- if (len > 0 && base > 0)
- get_asm_insns(image, len, base, opcodes);
+ len = get_last_jit_image(kbuff, klen, image, sizeof(image));
+ if (len > 0)
+ get_asm_insns(image, len, opcodes);
put_klog_buff(kbuff);
diff --git a/tools/power/acpi/Makefile b/tools/power/acpi/Makefile
index c2c0f20..e5a3c4b 100644
--- a/tools/power/acpi/Makefile
+++ b/tools/power/acpi/Makefile
@@ -19,6 +19,8 @@ OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd)
$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
endif
+SUBDIRS = tools/ec
+
# --- CONFIGURATION BEGIN ---
# Set the following to `true' to make a unstripped, unoptimized
@@ -68,7 +70,8 @@ WARNINGS += $(call cc-supports,-Wstrict-prototypes)
WARNINGS += $(call cc-supports,-Wdeclaration-after-statement)
KERNEL_INCLUDE := ../../../include
-CFLAGS += -D_LINUX -DDEFINE_ALTERNATE_TYPES -I$(KERNEL_INCLUDE)
+ACPICA_INCLUDE := ../../../drivers/acpi/acpica
+CFLAGS += -D_LINUX -I$(KERNEL_INCLUDE) -I$(ACPICA_INCLUDE)
CFLAGS += $(WARNINGS)
ifeq ($(strip $(V)),false)
@@ -92,10 +95,29 @@ endif
# --- ACPIDUMP BEGIN ---
vpath %.c \
- tools/acpidump
+ ../../../drivers/acpi/acpica\
+ tools/acpidump\
+ common\
+ os_specific/service_layers
+
+CFLAGS += -DACPI_DUMP_APP -Itools/acpidump
DUMP_OBJS = \
- acpidump.o
+ apdump.o\
+ apfiles.o\
+ apmain.o\
+ osunixdir.o\
+ osunixmap.o\
+ tbprint.o\
+ tbxfroot.o\
+ utbuffer.o\
+ utexcep.o\
+ utmath.o\
+ utstring.o\
+ utxferror.o\
+ oslinuxtbl.o\
+ cmfsize.o\
+ getopt.o
DUMP_OBJS := $(addprefix $(OUTPUT)tools/acpidump/,$(DUMP_OBJS))
diff --git a/tools/power/acpi/common/cmfsize.c b/tools/power/acpi/common/cmfsize.c
new file mode 100644
index 0000000..5140e5e
--- /dev/null
+++ b/tools/power/acpi/common/cmfsize.c
@@ -0,0 +1,101 @@
+/******************************************************************************
+ *
+ * Module Name: cfsize - Common get file size function
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acapps.h"
+#include <stdio.h>
+
+#define _COMPONENT ACPI_TOOLS
+ACPI_MODULE_NAME("cmfsize")
+
+/*******************************************************************************
+ *
+ * FUNCTION: cm_get_file_size
+ *
+ * PARAMETERS: file - Open file descriptor
+ *
+ * RETURN: File Size. On error, -1 (ACPI_UINT32_MAX)
+ *
+ * DESCRIPTION: Get the size of a file. Uses seek-to-EOF. File must be open.
+ * Does not disturb the current file pointer. Uses perror for
+ * error messages.
+ *
+ ******************************************************************************/
+u32 cm_get_file_size(FILE * file)
+{
+ long file_size;
+ long current_offset;
+
+ /* Save the current file pointer, seek to EOF to obtain file size */
+
+ current_offset = ftell(file);
+ if (current_offset < 0) {
+ goto offset_error;
+ }
+
+ if (fseek(file, 0, SEEK_END)) {
+ goto seek_error;
+ }
+
+ file_size = ftell(file);
+ if (file_size < 0) {
+ goto offset_error;
+ }
+
+ /* Restore original file pointer */
+
+ if (fseek(file, current_offset, SEEK_SET)) {
+ goto seek_error;
+ }
+
+ return ((u32)file_size);
+
+offset_error:
+ perror("Could not get file offset");
+ return (ACPI_UINT32_MAX);
+
+seek_error:
+ perror("Could not seek file");
+ return (ACPI_UINT32_MAX);
+}
diff --git a/tools/power/acpi/common/getopt.c b/tools/power/acpi/common/getopt.c
new file mode 100644
index 0000000..a302f52
--- /dev/null
+++ b/tools/power/acpi/common/getopt.c
@@ -0,0 +1,239 @@
+/******************************************************************************
+ *
+ * Module Name: getopt
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+/*
+ * ACPICA getopt() implementation
+ *
+ * Option strings:
+ * "f" - Option has no arguments
+ * "f:" - Option requires an argument
+ * "f^" - Option has optional single-char sub-options
+ * "f|" - Option has required single-char sub-options
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acapps.h"
+
+#define ACPI_OPTION_ERROR(msg, badchar) \
+ if (acpi_gbl_opterr) {fprintf (stderr, "%s%c\n", msg, badchar);}
+
+int acpi_gbl_opterr = 1;
+int acpi_gbl_optind = 1;
+int acpi_gbl_sub_opt_char = 0;
+char *acpi_gbl_optarg;
+
+static int current_char_ptr = 1;
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_getopt_argument
+ *
+ * PARAMETERS: argc, argv - from main
+ *
+ * RETURN: 0 if an argument was found, -1 otherwise. Sets acpi_gbl_Optarg
+ * to point to the next argument.
+ *
+ * DESCRIPTION: Get the next argument. Used to obtain arguments for the
+ * two-character options after the original call to acpi_getopt.
+ * Note: Either the argument starts at the next character after
+ * the option, or it is pointed to by the next argv entry.
+ * (After call to acpi_getopt, we need to backup to the previous
+ * argv entry).
+ *
+ ******************************************************************************/
+
+int acpi_getopt_argument(int argc, char **argv)
+{
+ acpi_gbl_optind--;
+ current_char_ptr++;
+
+ if (argv[acpi_gbl_optind][(int)(current_char_ptr + 1)] != '\0') {
+ acpi_gbl_optarg =
+ &argv[acpi_gbl_optind++][(int)(current_char_ptr + 1)];
+ } else if (++acpi_gbl_optind >= argc) {
+ ACPI_OPTION_ERROR("Option requires an argument: -", 'v');
+
+ current_char_ptr = 1;
+ return (-1);
+ } else {
+ acpi_gbl_optarg = argv[acpi_gbl_optind++];
+ }
+
+ current_char_ptr = 1;
+ return (0);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_getopt
+ *
+ * PARAMETERS: argc, argv - from main
+ * opts - options info list
+ *
+ * RETURN: Option character or EOF
+ *
+ * DESCRIPTION: Get the next option
+ *
+ ******************************************************************************/
+
+int acpi_getopt(int argc, char **argv, char *opts)
+{
+ int current_char;
+ char *opts_ptr;
+
+ if (current_char_ptr == 1) {
+ if (acpi_gbl_optind >= argc ||
+ argv[acpi_gbl_optind][0] != '-' ||
+ argv[acpi_gbl_optind][1] == '\0') {
+ return (EOF);
+ } else if (strcmp(argv[acpi_gbl_optind], "--") == 0) {
+ acpi_gbl_optind++;
+ return (EOF);
+ }
+ }
+
+ /* Get the option */
+
+ current_char = argv[acpi_gbl_optind][current_char_ptr];
+
+ /* Make sure that the option is legal */
+
+ if (current_char == ':' ||
+ (opts_ptr = strchr(opts, current_char)) == NULL) {
+ ACPI_OPTION_ERROR("Illegal option: -", current_char);
+
+ if (argv[acpi_gbl_optind][++current_char_ptr] == '\0') {
+ acpi_gbl_optind++;
+ current_char_ptr = 1;
+ }
+
+ return ('?');
+ }
+
+ /* Option requires an argument? */
+
+ if (*++opts_ptr == ':') {
+ if (argv[acpi_gbl_optind][(int)(current_char_ptr + 1)] != '\0') {
+ acpi_gbl_optarg =
+ &argv[acpi_gbl_optind++][(int)
+ (current_char_ptr + 1)];
+ } else if (++acpi_gbl_optind >= argc) {
+ ACPI_OPTION_ERROR("Option requires an argument: -",
+ current_char);
+
+ current_char_ptr = 1;
+ return ('?');
+ } else {
+ acpi_gbl_optarg = argv[acpi_gbl_optind++];
+ }
+
+ current_char_ptr = 1;
+ }
+
+ /* Option has an optional argument? */
+
+ else if (*opts_ptr == '+') {
+ if (argv[acpi_gbl_optind][(int)(current_char_ptr + 1)] != '\0') {
+ acpi_gbl_optarg =
+ &argv[acpi_gbl_optind++][(int)
+ (current_char_ptr + 1)];
+ } else if (++acpi_gbl_optind >= argc) {
+ acpi_gbl_optarg = NULL;
+ } else {
+ acpi_gbl_optarg = argv[acpi_gbl_optind++];
+ }
+
+ current_char_ptr = 1;
+ }
+
+ /* Option has optional single-char arguments? */
+
+ else if (*opts_ptr == '^') {
+ if (argv[acpi_gbl_optind][(int)(current_char_ptr + 1)] != '\0') {
+ acpi_gbl_optarg =
+ &argv[acpi_gbl_optind][(int)(current_char_ptr + 1)];
+ } else {
+ acpi_gbl_optarg = "^";
+ }
+
+ acpi_gbl_sub_opt_char = acpi_gbl_optarg[0];
+ acpi_gbl_optind++;
+ current_char_ptr = 1;
+ }
+
+ /* Option has a required single-char argument? */
+
+ else if (*opts_ptr == '|') {
+ if (argv[acpi_gbl_optind][(int)(current_char_ptr + 1)] != '\0') {
+ acpi_gbl_optarg =
+ &argv[acpi_gbl_optind][(int)(current_char_ptr + 1)];
+ } else {
+ ACPI_OPTION_ERROR
+ ("Option requires a single-character suboption: -",
+ current_char);
+
+ current_char_ptr = 1;
+ return ('?');
+ }
+
+ acpi_gbl_sub_opt_char = acpi_gbl_optarg[0];
+ acpi_gbl_optind++;
+ current_char_ptr = 1;
+ }
+
+ /* Option with no arguments */
+
+ else {
+ if (argv[acpi_gbl_optind][++current_char_ptr] == '\0') {
+ current_char_ptr = 1;
+ acpi_gbl_optind++;
+ }
+
+ acpi_gbl_optarg = NULL;
+ }
+
+ return (current_char);
+}
diff --git a/tools/power/acpi/man/acpidump.8 b/tools/power/acpi/man/acpidump.8
index adfa991..38f095d 100644
--- a/tools/power/acpi/man/acpidump.8
+++ b/tools/power/acpi/man/acpidump.8
@@ -1,18 +1,64 @@
.TH ACPIDUMP 8
.SH NAME
-acpidump \- Dump system's ACPI tables to an ASCII file.
+acpidump \- dump a system's ACPI tables to an ASCII file
+
.SH SYNOPSIS
-.ft B
-.B acpidump > acpidump.out
+.B acpidump
+.RI [ options ]
+.br
+
.SH DESCRIPTION
-\fBacpidump \fP dumps the systems ACPI tables to an ASCII file
-appropriate for attaching to a bug report.
+.B acpidump
+dumps the systems ACPI tables to an ASCII file appropriate for
+attaching to a bug report.
Subsequently, they can be processed by utilities in the ACPICA package.
-.SS Options
-no options worth worrying about.
-.PP
-.SH EXAMPLE
+
+.SH OPTIONS
+acpidump options are as follow:
+.TP
+.B Options
+.TP
+.B \-b
+Dump tables to binary files
+.TP
+.B \-c
+Dump customized tables
+.TP
+.B \-h \-?
+This help message
+.TP
+.B \-o <File>
+Redirect output to file
+.TP
+.B \-r <Address>
+Dump tables from specified RSDP
+.TP
+.B \-s
+Print table summaries only
+.TP
+.B \-v
+Display version information
+.TP
+.B \-z
+Verbose mode
+.TP
+.B Table Options
+.TP
+.B \-a <Address>
+Get table via a physical address
+.TP
+.B \-f <BinaryFile>
+Get table via a binary file
+.TP
+.B \-n <Signature>
+Get table via a name/signature
+.TP
+Invocation without parameters dumps all available tables
+.TP
+Multiple mixed instances of -a, -f, and -n are supported
+
+.SH EXAMPLES
.nf
# acpidump > acpidump.out
@@ -50,10 +96,25 @@ ACPICA: https://acpica.org/
.ta
.nf
/dev/mem
+/sys/firmware/acpi/tables/*
/sys/firmware/acpi/tables/dynamic/*
+/sys/firmware/efi/systab
.fi
-.PP
.SH AUTHOR
-.nf
-Written by Len Brown <len.brown@intel.com>
+.TP
+Original by:
+ Len Brown <len.brown@intel.com>
+.TP
+Written by:
+ Chao Guan <chao.guan@intel.com>
+.TP
+Updated by:
+ Bob Moore <robert.moore@intel.com>
+ Lv Zheng <lv.zheng@intel.com>
+
+.SH SEE ALSO
+\&\fIacpixtract\fR\|(8), \fIiasl\fR\|(8).
+
+.SH COPYRIGHT
+COPYRIGHT (c) 2013, Intel Corporation.
diff --git a/tools/power/acpi/os_specific/service_layers/oslinuxtbl.c b/tools/power/acpi/os_specific/service_layers/oslinuxtbl.c
new file mode 100644
index 0000000..28c5200
--- /dev/null
+++ b/tools/power/acpi/os_specific/service_layers/oslinuxtbl.c
@@ -0,0 +1,1329 @@
+/******************************************************************************
+ *
+ * Module Name: oslinuxtbl - Linux OSL for obtaining ACPI tables
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpidump.h"
+
+#define _COMPONENT ACPI_OS_SERVICES
+ACPI_MODULE_NAME("oslinuxtbl")
+
+#ifndef PATH_MAX
+#define PATH_MAX 256
+#endif
+/* List of information about obtained ACPI tables */
+typedef struct osl_table_info {
+ struct osl_table_info *next;
+ u32 instance;
+ char signature[ACPI_NAME_SIZE];
+
+} osl_table_info;
+
+/* Local prototypes */
+
+static acpi_status osl_table_initialize(void);
+
+static acpi_status
+osl_table_name_from_file(char *filename, char *signature, u32 *instance);
+
+static acpi_status osl_add_table_to_list(char *signature, u32 instance);
+
+static acpi_status
+osl_read_table_from_file(char *filename,
+ acpi_size file_offset,
+ char *signature, struct acpi_table_header **table);
+
+static acpi_status
+osl_map_table(acpi_size address,
+ char *signature, struct acpi_table_header **table);
+
+static void osl_unmap_table(struct acpi_table_header *table);
+
+static acpi_physical_address osl_find_rsdp_via_efi(void);
+
+static acpi_status osl_load_rsdp(void);
+
+static acpi_status osl_list_customized_tables(char *directory);
+
+static acpi_status
+osl_get_customized_table(char *pathname,
+ char *signature,
+ u32 instance,
+ struct acpi_table_header **table,
+ acpi_physical_address * address);
+
+static acpi_status osl_list_bios_tables(void);
+
+static acpi_status
+osl_get_bios_table(char *signature,
+ u32 instance,
+ struct acpi_table_header **table,
+ acpi_physical_address * address);
+
+static acpi_status osl_get_last_status(acpi_status default_status);
+
+/* File locations */
+
+#define DYNAMIC_TABLE_DIR "/sys/firmware/acpi/tables/dynamic"
+#define STATIC_TABLE_DIR "/sys/firmware/acpi/tables"
+#define EFI_SYSTAB "/sys/firmware/efi/systab"
+
+/* Should we get dynamically loaded SSDTs from DYNAMIC_TABLE_DIR? */
+
+u8 gbl_dump_dynamic_tables = TRUE;
+
+/* Initialization flags */
+
+u8 gbl_table_list_initialized = FALSE;
+
+/* Local copies of main ACPI tables */
+
+struct acpi_table_rsdp gbl_rsdp;
+struct acpi_table_fadt *gbl_fadt = NULL;
+struct acpi_table_rsdt *gbl_rsdt = NULL;
+struct acpi_table_xsdt *gbl_xsdt = NULL;
+
+/* Table addresses */
+
+acpi_physical_address gbl_fadt_address = 0;
+acpi_physical_address gbl_rsdp_address = 0;
+
+/* Revision of RSD PTR */
+
+u8 gbl_revision = 0;
+
+struct osl_table_info *gbl_table_list_head = NULL;
+u32 gbl_table_count = 0;
+
+/******************************************************************************
+ *
+ * FUNCTION: osl_get_last_status
+ *
+ * PARAMETERS: default_status - Default error status to return
+ *
+ * RETURN: Status; Converted from errno.
+ *
+ * DESCRIPTION: Get last errno and conver it to acpi_status.
+ *
+ *****************************************************************************/
+
+static acpi_status osl_get_last_status(acpi_status default_status)
+{
+
+ switch (errno) {
+ case EACCES:
+ case EPERM:
+
+ return (AE_ACCESS);
+
+ case ENOENT:
+
+ return (AE_NOT_FOUND);
+
+ case ENOMEM:
+
+ return (AE_NO_MEMORY);
+
+ default:
+
+ return (default_status);
+ }
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_os_get_table_by_address
+ *
+ * PARAMETERS: address - Physical address of the ACPI table
+ * table - Where a pointer to the table is returned
+ *
+ * RETURN: Status; Table buffer is returned if AE_OK.
+ * AE_NOT_FOUND: A valid table was not found at the address
+ *
+ * DESCRIPTION: Get an ACPI table via a physical memory address.
+ *
+ *****************************************************************************/
+
+acpi_status
+acpi_os_get_table_by_address(acpi_physical_address address,
+ struct acpi_table_header ** table)
+{
+ u32 table_length;
+ struct acpi_table_header *mapped_table;
+ struct acpi_table_header *local_table = NULL;
+ acpi_status status = AE_OK;
+
+ /* Get main ACPI tables from memory on first invocation of this function */
+
+ status = osl_table_initialize();
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Map the table and validate it */
+
+ status = osl_map_table(address, NULL, &mapped_table);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Copy table to local buffer and return it */
+
+ table_length = ap_get_table_length(mapped_table);
+ if (table_length == 0) {
+ status = AE_BAD_HEADER;
+ goto exit;
+ }
+
+ local_table = calloc(1, table_length);
+ if (!local_table) {
+ status = AE_NO_MEMORY;
+ goto exit;
+ }
+
+ ACPI_MEMCPY(local_table, mapped_table, table_length);
+
+exit:
+ osl_unmap_table(mapped_table);
+ *table = local_table;
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_os_get_table_by_name
+ *
+ * PARAMETERS: signature - ACPI Signature for desired table. Must be
+ * a null terminated 4-character string.
+ * instance - Multiple table support for SSDT/UEFI (0...n)
+ * Must be 0 for other tables.
+ * table - Where a pointer to the table is returned
+ * address - Where the table physical address is returned
+ *
+ * RETURN: Status; Table buffer and physical address returned if AE_OK.
+ * AE_LIMIT: Instance is beyond valid limit
+ * AE_NOT_FOUND: A table with the signature was not found
+ *
+ * NOTE: Assumes the input signature is uppercase.
+ *
+ *****************************************************************************/
+
+acpi_status
+acpi_os_get_table_by_name(char *signature,
+ u32 instance,
+ struct acpi_table_header ** table,
+ acpi_physical_address * address)
+{
+ acpi_status status;
+
+ /* Get main ACPI tables from memory on first invocation of this function */
+
+ status = osl_table_initialize();
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Not a main ACPI table, attempt to extract it from the RSDT/XSDT */
+
+ if (!gbl_dump_customized_tables) {
+
+ /* Attempt to get the table from the memory */
+
+ status =
+ osl_get_bios_table(signature, instance, table, address);
+ } else {
+ /* Attempt to get the table from the static directory */
+
+ status = osl_get_customized_table(STATIC_TABLE_DIR, signature,
+ instance, table, address);
+ }
+
+ if (ACPI_FAILURE(status) && status == AE_LIMIT) {
+ if (gbl_dump_dynamic_tables) {
+
+ /* Attempt to get a dynamic table */
+
+ status =
+ osl_get_customized_table(DYNAMIC_TABLE_DIR,
+ signature, instance, table,
+ address);
+ }
+ }
+
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: osl_add_table_to_list
+ *
+ * PARAMETERS: signature - Table signature
+ * instance - Table instance
+ *
+ * RETURN: Status; Successfully added if AE_OK.
+ * AE_NO_MEMORY: Memory allocation error
+ *
+ * DESCRIPTION: Insert a table structure into OSL table list.
+ *
+ *****************************************************************************/
+
+static acpi_status osl_add_table_to_list(char *signature, u32 instance)
+{
+ struct osl_table_info *new_info;
+ struct osl_table_info *next;
+ u32 next_instance = 0;
+ u8 found = FALSE;
+
+ new_info = calloc(1, sizeof(struct osl_table_info));
+ if (!new_info) {
+ return (AE_NO_MEMORY);
+ }
+
+ ACPI_MOVE_NAME(new_info->signature, signature);
+
+ if (!gbl_table_list_head) {
+ gbl_table_list_head = new_info;
+ } else {
+ next = gbl_table_list_head;
+ while (1) {
+ if (ACPI_COMPARE_NAME(next->signature, signature)) {
+ if (next->instance == instance) {
+ found = TRUE;
+ }
+ if (next->instance >= next_instance) {
+ next_instance = next->instance + 1;
+ }
+ }
+
+ if (!next->next) {
+ break;
+ }
+ next = next->next;
+ }
+ next->next = new_info;
+ }
+
+ if (found) {
+ if (instance) {
+ fprintf(stderr,
+ "%4.4s: Warning unmatched table instance %d, expected %d\n",
+ signature, instance, next_instance);
+ }
+ instance = next_instance;
+ }
+
+ new_info->instance = instance;
+ gbl_table_count++;
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_os_get_table_by_index
+ *
+ * PARAMETERS: index - Which table to get
+ * table - Where a pointer to the table is returned
+ * instance - Where a pointer to the table instance no. is
+ * returned
+ * address - Where the table physical address is returned
+ *
+ * RETURN: Status; Table buffer and physical address returned if AE_OK.
+ * AE_LIMIT: Index is beyond valid limit
+ *
+ * DESCRIPTION: Get an ACPI table via an index value (0 through n). Returns
+ * AE_LIMIT when an invalid index is reached. Index is not
+ * necessarily an index into the RSDT/XSDT.
+ *
+ *****************************************************************************/
+
+acpi_status
+acpi_os_get_table_by_index(u32 index,
+ struct acpi_table_header ** table,
+ u32 *instance, acpi_physical_address * address)
+{
+ struct osl_table_info *info;
+ acpi_status status;
+ u32 i;
+
+ /* Get main ACPI tables from memory on first invocation of this function */
+
+ status = osl_table_initialize();
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Validate Index */
+
+ if (index >= gbl_table_count) {
+ return (AE_LIMIT);
+ }
+
+ /* Point to the table list entry specified by the Index argument */
+
+ info = gbl_table_list_head;
+ for (i = 0; i < index; i++) {
+ info = info->next;
+ }
+
+ /* Now we can just get the table via the signature */
+
+ status = acpi_os_get_table_by_name(info->signature, info->instance,
+ table, address);
+
+ if (ACPI_SUCCESS(status)) {
+ *instance = info->instance;
+ }
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: osl_find_rsdp_via_efi
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: RSDP address if found
+ *
+ * DESCRIPTION: Find RSDP address via EFI.
+ *
+ *****************************************************************************/
+
+static acpi_physical_address osl_find_rsdp_via_efi(void)
+{
+ FILE *file;
+ char buffer[80];
+ unsigned long address = 0;
+
+ file = fopen(EFI_SYSTAB, "r");
+ if (file) {
+ while (fgets(buffer, 80, file)) {
+ if (sscanf(buffer, "ACPI20=0x%lx", &address) == 1) {
+ break;
+ }
+ }
+ fclose(file);
+ }
+
+ return ((acpi_physical_address) (address));
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: osl_load_rsdp
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Scan and load RSDP.
+ *
+ *****************************************************************************/
+
+static acpi_status osl_load_rsdp(void)
+{
+ struct acpi_table_header *mapped_table;
+ u8 *rsdp_address;
+ acpi_physical_address rsdp_base;
+ acpi_size rsdp_size;
+
+ /* Get RSDP from memory */
+
+ rsdp_size = sizeof(struct acpi_table_rsdp);
+ if (gbl_rsdp_base) {
+ rsdp_base = gbl_rsdp_base;
+ } else {
+ rsdp_base = osl_find_rsdp_via_efi();
+ }
+
+ if (!rsdp_base) {
+ rsdp_base = ACPI_HI_RSDP_WINDOW_BASE;
+ rsdp_size = ACPI_HI_RSDP_WINDOW_SIZE;
+ }
+
+ rsdp_address = acpi_os_map_memory(rsdp_base, rsdp_size);
+ if (!rsdp_address) {
+ return (osl_get_last_status(AE_BAD_ADDRESS));
+ }
+
+ /* Search low memory for the RSDP */
+
+ mapped_table = ACPI_CAST_PTR(struct acpi_table_header,
+ acpi_tb_scan_memory_for_rsdp(rsdp_address,
+ rsdp_size));
+ if (!mapped_table) {
+ acpi_os_unmap_memory(rsdp_address, rsdp_size);
+ return (AE_NOT_FOUND);
+ }
+
+ gbl_rsdp_address =
+ rsdp_base + (ACPI_CAST8(mapped_table) - rsdp_address);
+
+ ACPI_MEMCPY(&gbl_rsdp, mapped_table, sizeof(struct acpi_table_rsdp));
+ acpi_os_unmap_memory(rsdp_address, rsdp_size);
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: osl_can_use_xsdt
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: TRUE if XSDT is allowed to be used.
+ *
+ * DESCRIPTION: This function collects logic that can be used to determine if
+ * XSDT should be used instead of RSDT.
+ *
+ *****************************************************************************/
+
+static u8 osl_can_use_xsdt(void)
+{
+ if (gbl_revision && !acpi_gbl_do_not_use_xsdt) {
+ return (TRUE);
+ } else {
+ return (FALSE);
+ }
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: osl_table_initialize
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Initialize ACPI table data. Get and store main ACPI tables to
+ * local variables. Main ACPI tables include RSDT, FADT, RSDT,
+ * and/or XSDT.
+ *
+ *****************************************************************************/
+
+static acpi_status osl_table_initialize(void)
+{
+ acpi_status status;
+ acpi_physical_address address;
+
+ if (gbl_table_list_initialized) {
+ return (AE_OK);
+ }
+
+ /* Get RSDP from memory */
+
+ status = osl_load_rsdp();
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Get XSDT from memory */
+
+ if (gbl_rsdp.revision && !gbl_do_not_dump_xsdt) {
+ if (gbl_xsdt) {
+ free(gbl_xsdt);
+ gbl_xsdt = NULL;
+ }
+
+ gbl_revision = 2;
+ status = osl_get_bios_table(ACPI_SIG_XSDT, 0,
+ ACPI_CAST_PTR(struct
+ acpi_table_header *,
+ &gbl_xsdt), &address);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ /* Get RSDT from memory */
+
+ if (gbl_rsdp.rsdt_physical_address) {
+ if (gbl_rsdt) {
+ free(gbl_rsdt);
+ gbl_rsdt = NULL;
+ }
+
+ status = osl_get_bios_table(ACPI_SIG_RSDT, 0,
+ ACPI_CAST_PTR(struct
+ acpi_table_header *,
+ &gbl_rsdt), &address);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ /* Get FADT from memory */
+
+ if (gbl_fadt) {
+ free(gbl_fadt);
+ gbl_fadt = NULL;
+ }
+
+ status = osl_get_bios_table(ACPI_SIG_FADT, 0,
+ ACPI_CAST_PTR(struct acpi_table_header *,
+ &gbl_fadt),
+ &gbl_fadt_address);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ if (!gbl_dump_customized_tables) {
+
+ /* Add mandatory tables to global table list first */
+
+ status = osl_add_table_to_list(ACPI_RSDP_NAME, 0);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ status = osl_add_table_to_list(ACPI_SIG_RSDT, 0);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ if (gbl_revision == 2) {
+ status = osl_add_table_to_list(ACPI_SIG_XSDT, 0);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ status = osl_add_table_to_list(ACPI_SIG_DSDT, 0);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ status = osl_add_table_to_list(ACPI_SIG_FACS, 0);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /* Add all tables found in the memory */
+
+ status = osl_list_bios_tables();
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ } else {
+ /* Add all tables found in the static directory */
+
+ status = osl_list_customized_tables(STATIC_TABLE_DIR);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ if (gbl_dump_dynamic_tables) {
+
+ /* Add all dynamically loaded tables in the dynamic directory */
+
+ status = osl_list_customized_tables(DYNAMIC_TABLE_DIR);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+
+ gbl_table_list_initialized = TRUE;
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: osl_list_bios_tables
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status; Table list is initialized if AE_OK.
+ *
+ * DESCRIPTION: Add ACPI tables to the table list from memory.
+ *
+ * NOTE: This works on Linux as table customization does not modify the
+ * addresses stored in RSDP/RSDT/XSDT/FADT.
+ *
+ *****************************************************************************/
+
+static acpi_status osl_list_bios_tables(void)
+{
+ struct acpi_table_header *mapped_table = NULL;
+ u8 *table_data;
+ u8 number_of_tables;
+ u8 item_size;
+ acpi_physical_address table_address = 0;
+ acpi_status status = AE_OK;
+ u32 i;
+
+ if (osl_can_use_xsdt()) {
+ item_size = sizeof(u64);
+ table_data =
+ ACPI_CAST8(gbl_xsdt) + sizeof(struct acpi_table_header);
+ number_of_tables =
+ (u8)((gbl_xsdt->header.length -
+ sizeof(struct acpi_table_header))
+ / item_size);
+ } else { /* Use RSDT if XSDT is not available */
+
+ item_size = sizeof(u32);
+ table_data =
+ ACPI_CAST8(gbl_rsdt) + sizeof(struct acpi_table_header);
+ number_of_tables =
+ (u8)((gbl_rsdt->header.length -
+ sizeof(struct acpi_table_header))
+ / item_size);
+ }
+
+ /* Search RSDT/XSDT for the requested table */
+
+ for (i = 0; i < number_of_tables; ++i, table_data += item_size) {
+ if (osl_can_use_xsdt()) {
+ table_address =
+ (acpi_physical_address) (*ACPI_CAST64(table_data));
+ } else {
+ table_address =
+ (acpi_physical_address) (*ACPI_CAST32(table_data));
+ }
+
+ /* Skip NULL entries in RSDT/XSDT */
+
+ if (!table_address) {
+ continue;
+ }
+
+ status = osl_map_table(table_address, NULL, &mapped_table);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ osl_add_table_to_list(mapped_table->signature, 0);
+ osl_unmap_table(mapped_table);
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: osl_get_bios_table
+ *
+ * PARAMETERS: signature - ACPI Signature for common table. Must be
+ * a null terminated 4-character string.
+ * instance - Multiple table support for SSDT/UEFI (0...n)
+ * Must be 0 for other tables.
+ * table - Where a pointer to the table is returned
+ * address - Where the table physical address is returned
+ *
+ * RETURN: Status; Table buffer and physical address returned if AE_OK.
+ * AE_LIMIT: Instance is beyond valid limit
+ * AE_NOT_FOUND: A table with the signature was not found
+ *
+ * DESCRIPTION: Get a BIOS provided ACPI table
+ *
+ * NOTE: Assumes the input signature is uppercase.
+ *
+ *****************************************************************************/
+
+static acpi_status
+osl_get_bios_table(char *signature,
+ u32 instance,
+ struct acpi_table_header **table,
+ acpi_physical_address * address)
+{
+ struct acpi_table_header *local_table = NULL;
+ struct acpi_table_header *mapped_table = NULL;
+ u8 *table_data;
+ u8 number_of_tables;
+ u8 item_size;
+ u32 current_instance = 0;
+ acpi_physical_address table_address = 0;
+ u32 table_length = 0;
+ acpi_status status = AE_OK;
+ u32 i;
+
+ /* Handle special tables whose addresses are not in RSDT/XSDT */
+
+ if (ACPI_COMPARE_NAME(signature, ACPI_RSDP_NAME) ||
+ ACPI_COMPARE_NAME(signature, ACPI_SIG_RSDT) ||
+ ACPI_COMPARE_NAME(signature, ACPI_SIG_XSDT) ||
+ ACPI_COMPARE_NAME(signature, ACPI_SIG_DSDT) ||
+ ACPI_COMPARE_NAME(signature, ACPI_SIG_FACS)) {
+ if (instance > 0) {
+ return (AE_LIMIT);
+ }
+
+ /*
+ * Get the appropriate address, either 32-bit or 64-bit. Be very
+ * careful about the FADT length and validate table addresses.
+ * Note: The 64-bit addresses have priority.
+ */
+ if (ACPI_COMPARE_NAME(signature, ACPI_SIG_DSDT)) {
+ if ((gbl_fadt->header.length >= MIN_FADT_FOR_XDSDT) &&
+ gbl_fadt->Xdsdt) {
+ table_address =
+ (acpi_physical_address) gbl_fadt->Xdsdt;
+ } else
+ if ((gbl_fadt->header.length >= MIN_FADT_FOR_DSDT)
+ && gbl_fadt->dsdt) {
+ table_address =
+ (acpi_physical_address) gbl_fadt->dsdt;
+ }
+ } else if (ACPI_COMPARE_NAME(signature, ACPI_SIG_FACS)) {
+ if ((gbl_fadt->header.length >= MIN_FADT_FOR_XFACS) &&
+ gbl_fadt->Xfacs) {
+ table_address =
+ (acpi_physical_address) gbl_fadt->Xfacs;
+ } else
+ if ((gbl_fadt->header.length >= MIN_FADT_FOR_FACS)
+ && gbl_fadt->facs) {
+ table_address =
+ (acpi_physical_address) gbl_fadt->facs;
+ }
+ } else if (ACPI_COMPARE_NAME(signature, ACPI_SIG_XSDT)) {
+ if (!gbl_revision) {
+ return (AE_BAD_SIGNATURE);
+ }
+ table_address =
+ (acpi_physical_address) gbl_rsdp.
+ xsdt_physical_address;
+ } else if (ACPI_COMPARE_NAME(signature, ACPI_SIG_RSDT)) {
+ table_address =
+ (acpi_physical_address) gbl_rsdp.
+ rsdt_physical_address;
+ } else {
+ table_address =
+ (acpi_physical_address) gbl_rsdp_address;
+ signature = ACPI_SIG_RSDP;
+ }
+
+ /* Now we can get the requested special table */
+
+ status = osl_map_table(table_address, signature, &mapped_table);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ table_length = ap_get_table_length(mapped_table);
+ } else { /* Case for a normal ACPI table */
+
+ if (osl_can_use_xsdt()) {
+ item_size = sizeof(u64);
+ table_data =
+ ACPI_CAST8(gbl_xsdt) +
+ sizeof(struct acpi_table_header);
+ number_of_tables =
+ (u8)((gbl_xsdt->header.length -
+ sizeof(struct acpi_table_header))
+ / item_size);
+ } else { /* Use RSDT if XSDT is not available */
+
+ item_size = sizeof(u32);
+ table_data =
+ ACPI_CAST8(gbl_rsdt) +
+ sizeof(struct acpi_table_header);
+ number_of_tables =
+ (u8)((gbl_rsdt->header.length -
+ sizeof(struct acpi_table_header))
+ / item_size);
+ }
+
+ /* Search RSDT/XSDT for the requested table */
+
+ for (i = 0; i < number_of_tables; ++i, table_data += item_size) {
+ if (osl_can_use_xsdt()) {
+ table_address =
+ (acpi_physical_address) (*ACPI_CAST64
+ (table_data));
+ } else {
+ table_address =
+ (acpi_physical_address) (*ACPI_CAST32
+ (table_data));
+ }
+
+ /* Skip NULL entries in RSDT/XSDT */
+
+ if (!table_address) {
+ continue;
+ }
+
+ status =
+ osl_map_table(table_address, NULL, &mapped_table);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ table_length = mapped_table->length;
+
+ /* Does this table match the requested signature? */
+
+ if (!ACPI_COMPARE_NAME
+ (mapped_table->signature, signature)) {
+ osl_unmap_table(mapped_table);
+ mapped_table = NULL;
+ continue;
+ }
+
+ /* Match table instance (for SSDT/UEFI tables) */
+
+ if (current_instance != instance) {
+ osl_unmap_table(mapped_table);
+ mapped_table = NULL;
+ current_instance++;
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ if (!mapped_table) {
+ return (AE_LIMIT);
+ }
+
+ if (table_length == 0) {
+ status = AE_BAD_HEADER;
+ goto exit;
+ }
+
+ /* Copy table to local buffer and return it */
+
+ local_table = calloc(1, table_length);
+ if (!local_table) {
+ status = AE_NO_MEMORY;
+ goto exit;
+ }
+
+ ACPI_MEMCPY(local_table, mapped_table, table_length);
+ *address = table_address;
+ *table = local_table;
+
+exit:
+ osl_unmap_table(mapped_table);
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: osl_list_customized_tables
+ *
+ * PARAMETERS: directory - Directory that contains the tables
+ *
+ * RETURN: Status; Table list is initialized if AE_OK.
+ *
+ * DESCRIPTION: Add ACPI tables to the table list from a directory.
+ *
+ *****************************************************************************/
+
+static acpi_status osl_list_customized_tables(char *directory)
+{
+ void *table_dir;
+ u32 instance;
+ char temp_name[ACPI_NAME_SIZE];
+ char *filename;
+ acpi_status status = AE_OK;
+
+ /* Open the requested directory */
+
+ table_dir = acpi_os_open_directory(directory, "*", REQUEST_FILE_ONLY);
+ if (!table_dir) {
+ return (osl_get_last_status(AE_NOT_FOUND));
+ }
+
+ /* Examine all entries in this directory */
+
+ while ((filename = acpi_os_get_next_filename(table_dir))) {
+
+ /* Extract table name and instance number */
+
+ status =
+ osl_table_name_from_file(filename, temp_name, &instance);
+
+ /* Ignore meaningless files */
+
+ if (ACPI_FAILURE(status)) {
+ continue;
+ }
+
+ /* Add new info node to global table list */
+
+ status = osl_add_table_to_list(temp_name, instance);
+ if (ACPI_FAILURE(status)) {
+ break;
+ }
+ }
+
+ acpi_os_close_directory(table_dir);
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: osl_map_table
+ *
+ * PARAMETERS: address - Address of the table in memory
+ * signature - Optional ACPI Signature for desired table.
+ * Null terminated 4-character string.
+ * table - Where a pointer to the mapped table is
+ * returned
+ *
+ * RETURN: Status; Mapped table is returned if AE_OK.
+ * AE_NOT_FOUND: A valid table was not found at the address
+ *
+ * DESCRIPTION: Map entire ACPI table into caller's address space.
+ *
+ *****************************************************************************/
+
+static acpi_status
+osl_map_table(acpi_size address,
+ char *signature, struct acpi_table_header **table)
+{
+ struct acpi_table_header *mapped_table;
+ u32 length;
+
+ if (!address) {
+ return (AE_BAD_ADDRESS);
+ }
+
+ /*
+ * Map the header so we can get the table length.
+ * Use sizeof (struct acpi_table_header) as:
+ * 1. it is bigger than 24 to include RSDP->Length
+ * 2. it is smaller than sizeof (struct acpi_table_rsdp)
+ */
+ mapped_table =
+ acpi_os_map_memory(address, sizeof(struct acpi_table_header));
+ if (!mapped_table) {
+ fprintf(stderr, "Could not map table header at 0x%8.8X%8.8X\n",
+ ACPI_FORMAT_UINT64(address));
+ return (osl_get_last_status(AE_BAD_ADDRESS));
+ }
+
+ /* If specified, signature must match */
+
+ if (signature) {
+ if (ACPI_VALIDATE_RSDP_SIG(signature)) {
+ if (!ACPI_VALIDATE_RSDP_SIG(mapped_table->signature)) {
+ acpi_os_unmap_memory(mapped_table,
+ sizeof(struct
+ acpi_table_header));
+ return (AE_BAD_SIGNATURE);
+ }
+ } else
+ if (!ACPI_COMPARE_NAME(signature, mapped_table->signature))
+ {
+ acpi_os_unmap_memory(mapped_table,
+ sizeof(struct acpi_table_header));
+ return (AE_BAD_SIGNATURE);
+ }
+ }
+
+ /* Map the entire table */
+
+ length = ap_get_table_length(mapped_table);
+ acpi_os_unmap_memory(mapped_table, sizeof(struct acpi_table_header));
+ if (length == 0) {
+ return (AE_BAD_HEADER);
+ }
+
+ mapped_table = acpi_os_map_memory(address, length);
+ if (!mapped_table) {
+ fprintf(stderr,
+ "Could not map table at 0x%8.8X%8.8X length %8.8X\n",
+ ACPI_FORMAT_UINT64(address), length);
+ return (osl_get_last_status(AE_INVALID_TABLE_LENGTH));
+ }
+
+ (void)ap_is_valid_checksum(mapped_table);
+
+ *table = mapped_table;
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: osl_unmap_table
+ *
+ * PARAMETERS: table - A pointer to the mapped table
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Unmap entire ACPI table.
+ *
+ *****************************************************************************/
+
+static void osl_unmap_table(struct acpi_table_header *table)
+{
+ if (table) {
+ acpi_os_unmap_memory(table, ap_get_table_length(table));
+ }
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: osl_table_name_from_file
+ *
+ * PARAMETERS: filename - File that contains the desired table
+ * signature - Pointer to 4-character buffer to store
+ * extracted table signature.
+ * instance - Pointer to integer to store extracted
+ * table instance number.
+ *
+ * RETURN: Status; Table name is extracted if AE_OK.
+ *
+ * DESCRIPTION: Extract table signature and instance number from a table file
+ * name.
+ *
+ *****************************************************************************/
+
+static acpi_status
+osl_table_name_from_file(char *filename, char *signature, u32 *instance)
+{
+
+ /* Ignore meaningless files */
+
+ if (strlen(filename) < ACPI_NAME_SIZE) {
+ return (AE_BAD_SIGNATURE);
+ }
+
+ /* Extract instance number */
+
+ if (isdigit((int)filename[ACPI_NAME_SIZE])) {
+ sscanf(&filename[ACPI_NAME_SIZE], "%d", instance);
+ } else if (strlen(filename) != ACPI_NAME_SIZE) {
+ return (AE_BAD_SIGNATURE);
+ } else {
+ *instance = 0;
+ }
+
+ /* Extract signature */
+
+ ACPI_MOVE_NAME(signature, filename);
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: osl_read_table_from_file
+ *
+ * PARAMETERS: filename - File that contains the desired table
+ * file_offset - Offset of the table in file
+ * signature - Optional ACPI Signature for desired table.
+ * A null terminated 4-character string.
+ * table - Where a pointer to the table is returned
+ *
+ * RETURN: Status; Table buffer is returned if AE_OK.
+ *
+ * DESCRIPTION: Read a ACPI table from a file.
+ *
+ *****************************************************************************/
+
+static acpi_status
+osl_read_table_from_file(char *filename,
+ acpi_size file_offset,
+ char *signature, struct acpi_table_header **table)
+{
+ FILE *table_file;
+ struct acpi_table_header header;
+ struct acpi_table_header *local_table = NULL;
+ u32 table_length;
+ s32 count;
+ acpi_status status = AE_OK;
+
+ /* Open the file */
+
+ table_file = fopen(filename, "rb");
+ if (table_file == NULL) {
+ fprintf(stderr, "Could not open table file: %s\n", filename);
+ return (osl_get_last_status(AE_NOT_FOUND));
+ }
+
+ fseek(table_file, file_offset, SEEK_SET);
+
+ /* Read the Table header to get the table length */
+
+ count = fread(&header, 1, sizeof(struct acpi_table_header), table_file);
+ if (count != sizeof(struct acpi_table_header)) {
+ fprintf(stderr, "Could not read table header: %s\n", filename);
+ status = AE_BAD_HEADER;
+ goto exit;
+ }
+
+ /* If signature is specified, it must match the table */
+
+ if (signature) {
+ if (ACPI_VALIDATE_RSDP_SIG(signature)) {
+ if (!ACPI_VALIDATE_RSDP_SIG(header.signature)) {
+ fprintf(stderr,
+ "Incorrect RSDP signature: found %8.8s\n",
+ header.signature);
+ status = AE_BAD_SIGNATURE;
+ goto exit;
+ }
+ } else if (!ACPI_COMPARE_NAME(signature, header.signature)) {
+ fprintf(stderr,
+ "Incorrect signature: Expecting %4.4s, found %4.4s\n",
+ signature, header.signature);
+ status = AE_BAD_SIGNATURE;
+ goto exit;
+ }
+ }
+
+ table_length = ap_get_table_length(&header);
+ if (table_length == 0) {
+ status = AE_BAD_HEADER;
+ goto exit;
+ }
+
+ /* Read the entire table into a local buffer */
+
+ local_table = calloc(1, table_length);
+ if (!local_table) {
+ fprintf(stderr,
+ "%4.4s: Could not allocate buffer for table of length %X\n",
+ header.signature, table_length);
+ status = AE_NO_MEMORY;
+ goto exit;
+ }
+
+ fseek(table_file, file_offset, SEEK_SET);
+
+ count = fread(local_table, 1, table_length, table_file);
+ if (count != table_length) {
+ fprintf(stderr, "%4.4s: Could not read table content\n",
+ header.signature);
+ status = AE_INVALID_TABLE_LENGTH;
+ goto exit;
+ }
+
+ /* Validate checksum */
+
+ (void)ap_is_valid_checksum(local_table);
+
+exit:
+ fclose(table_file);
+ *table = local_table;
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: osl_get_customized_table
+ *
+ * PARAMETERS: pathname - Directory to find Linux customized table
+ * signature - ACPI Signature for desired table. Must be
+ * a null terminated 4-character string.
+ * instance - Multiple table support for SSDT/UEFI (0...n)
+ * Must be 0 for other tables.
+ * table - Where a pointer to the table is returned
+ * address - Where the table physical address is returned
+ *
+ * RETURN: Status; Table buffer is returned if AE_OK.
+ * AE_LIMIT: Instance is beyond valid limit
+ * AE_NOT_FOUND: A table with the signature was not found
+ *
+ * DESCRIPTION: Get an OS customized table.
+ *
+ *****************************************************************************/
+
+static acpi_status
+osl_get_customized_table(char *pathname,
+ char *signature,
+ u32 instance,
+ struct acpi_table_header **table,
+ acpi_physical_address * address)
+{
+ void *table_dir;
+ u32 current_instance = 0;
+ char temp_name[ACPI_NAME_SIZE];
+ char table_filename[PATH_MAX];
+ char *filename;
+ acpi_status status;
+
+ /* Open the directory for customized tables */
+
+ table_dir = acpi_os_open_directory(pathname, "*", REQUEST_FILE_ONLY);
+ if (!table_dir) {
+ return (osl_get_last_status(AE_NOT_FOUND));
+ }
+
+ /* Attempt to find the table in the directory */
+
+ while ((filename = acpi_os_get_next_filename(table_dir))) {
+
+ /* Ignore meaningless files */
+
+ if (!ACPI_COMPARE_NAME(filename, signature)) {
+ continue;
+ }
+
+ /* Extract table name and instance number */
+
+ status =
+ osl_table_name_from_file(filename, temp_name,
+ &current_instance);
+
+ /* Ignore meaningless files */
+
+ if (ACPI_FAILURE(status) || current_instance != instance) {
+ continue;
+ }
+
+ /* Create the table pathname */
+
+ if (instance != 0) {
+ sprintf(table_filename, "%s/%4.4s%d", pathname,
+ temp_name, instance);
+ } else {
+ sprintf(table_filename, "%s/%4.4s", pathname,
+ temp_name);
+ }
+ break;
+ }
+
+ acpi_os_close_directory(table_dir);
+
+ if (!filename) {
+ return (AE_LIMIT);
+ }
+
+ /* There is no physical address saved for customized tables, use zero */
+
+ *address = 0;
+ status = osl_read_table_from_file(table_filename, 0, NULL, table);
+
+ return (status);
+}
diff --git a/tools/power/acpi/os_specific/service_layers/osunixdir.c b/tools/power/acpi/os_specific/service_layers/osunixdir.c
new file mode 100644
index 0000000..733f9e4
--- /dev/null
+++ b/tools/power/acpi/os_specific/service_layers/osunixdir.c
@@ -0,0 +1,204 @@
+/******************************************************************************
+ *
+ * Module Name: osunixdir - Unix directory access interfaces
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <fnmatch.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+/*
+ * Allocated structure returned from os_open_directory
+ */
+typedef struct external_find_info {
+ char *dir_pathname;
+ DIR *dir_ptr;
+ char temp_buffer[256];
+ char *wildcard_spec;
+ char requested_file_type;
+
+} external_find_info;
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_open_directory
+ *
+ * PARAMETERS: dir_pathname - Full pathname to the directory
+ * wildcard_spec - string of the form "*.c", etc.
+ *
+ * RETURN: A directory "handle" to be used in subsequent search operations.
+ * NULL returned on failure.
+ *
+ * DESCRIPTION: Open a directory in preparation for a wildcard search
+ *
+ ******************************************************************************/
+
+void *acpi_os_open_directory(char *dir_pathname,
+ char *wildcard_spec, char requested_file_type)
+{
+ struct external_find_info *external_info;
+ DIR *dir;
+
+ /* Allocate the info struct that will be returned to the caller */
+
+ external_info = calloc(1, sizeof(struct external_find_info));
+ if (!external_info) {
+ return (NULL);
+ }
+
+ /* Get the directory stream */
+
+ dir = opendir(dir_pathname);
+ if (!dir) {
+ fprintf(stderr, "Cannot open directory - %s\n", dir_pathname);
+ free(external_info);
+ return (NULL);
+ }
+
+ /* Save the info in the return structure */
+
+ external_info->wildcard_spec = wildcard_spec;
+ external_info->requested_file_type = requested_file_type;
+ external_info->dir_pathname = dir_pathname;
+ external_info->dir_ptr = dir;
+ return (external_info);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_get_next_filename
+ *
+ * PARAMETERS: dir_handle - Created via acpi_os_open_directory
+ *
+ * RETURN: Next filename matched. NULL if no more matches.
+ *
+ * DESCRIPTION: Get the next file in the directory that matches the wildcard
+ * specification.
+ *
+ ******************************************************************************/
+
+char *acpi_os_get_next_filename(void *dir_handle)
+{
+ struct external_find_info *external_info = dir_handle;
+ struct dirent *dir_entry;
+ char *temp_str;
+ int str_len;
+ struct stat temp_stat;
+ int err;
+
+ while ((dir_entry = readdir(external_info->dir_ptr))) {
+ if (!fnmatch
+ (external_info->wildcard_spec, dir_entry->d_name, 0)) {
+ if (dir_entry->d_name[0] == '.') {
+ continue;
+ }
+
+ str_len = strlen(dir_entry->d_name) +
+ strlen(external_info->dir_pathname) + 2;
+
+ temp_str = calloc(str_len, 1);
+ if (!temp_str) {
+ fprintf(stderr,
+ "Could not allocate buffer for temporary string\n");
+ return (NULL);
+ }
+
+ strcpy(temp_str, external_info->dir_pathname);
+ strcat(temp_str, "/");
+ strcat(temp_str, dir_entry->d_name);
+
+ err = stat(temp_str, &temp_stat);
+ if (err == -1) {
+ fprintf(stderr,
+ "Cannot stat file (should not happen) - %s\n",
+ temp_str);
+ free(temp_str);
+ return (NULL);
+ }
+
+ free(temp_str);
+
+ if ((S_ISDIR(temp_stat.st_mode)
+ && (external_info->requested_file_type ==
+ REQUEST_DIR_ONLY))
+ || ((!S_ISDIR(temp_stat.st_mode)
+ && external_info->requested_file_type ==
+ REQUEST_FILE_ONLY))) {
+
+ /* copy to a temp buffer because dir_entry struct is on the stack */
+
+ strcpy(external_info->temp_buffer,
+ dir_entry->d_name);
+ return (external_info->temp_buffer);
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_close_directory
+ *
+ * PARAMETERS: dir_handle - Created via acpi_os_open_directory
+ *
+ * RETURN: None.
+ *
+ * DESCRIPTION: Close the open directory and cleanup.
+ *
+ ******************************************************************************/
+
+void acpi_os_close_directory(void *dir_handle)
+{
+ struct external_find_info *external_info = dir_handle;
+
+ /* Close the directory and free allocations */
+
+ closedir(external_info->dir_ptr);
+ free(dir_handle);
+}
diff --git a/tools/power/acpi/os_specific/service_layers/osunixmap.c b/tools/power/acpi/os_specific/service_layers/osunixmap.c
new file mode 100644
index 0000000..99b47b6
--- /dev/null
+++ b/tools/power/acpi/os_specific/service_layers/osunixmap.c
@@ -0,0 +1,151 @@
+/******************************************************************************
+ *
+ * Module Name: osunixmap - Unix OSL for file mappings
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpidump.h"
+#include <unistd.h>
+#include <sys/mman.h>
+#ifdef _free_BSD
+#include <sys/param.h>
+#endif
+
+#define _COMPONENT ACPI_OS_SERVICES
+ACPI_MODULE_NAME("osunixmap")
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifdef _free_BSD
+#define MMAP_FLAGS MAP_SHARED
+#else
+#define MMAP_FLAGS MAP_PRIVATE
+#endif
+#define SYSTEM_MEMORY "/dev/mem"
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_os_get_page_size
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Page size of the platform.
+ *
+ * DESCRIPTION: Obtain page size of the platform.
+ *
+ ******************************************************************************/
+static acpi_size acpi_os_get_page_size(void)
+{
+
+#ifdef PAGE_SIZE
+ return PAGE_SIZE;
+#else
+ return sysconf(_SC_PAGESIZE);
+#endif
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_os_map_memory
+ *
+ * PARAMETERS: where - Physical address of memory to be mapped
+ * length - How much memory to map
+ *
+ * RETURN: Pointer to mapped memory. Null on error.
+ *
+ * DESCRIPTION: Map physical memory into local address space.
+ *
+ *****************************************************************************/
+
+void *acpi_os_map_memory(acpi_physical_address where, acpi_size length)
+{
+ u8 *mapped_memory;
+ acpi_physical_address offset;
+ acpi_size page_size;
+ int fd;
+
+ fd = open(SYSTEM_MEMORY, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ fprintf(stderr, "Cannot open %s\n", SYSTEM_MEMORY);
+ return (NULL);
+ }
+
+ /* Align the offset to use mmap */
+
+ page_size = acpi_os_get_page_size();
+ offset = where % page_size;
+
+ /* Map the table header to get the length of the full table */
+
+ mapped_memory = mmap(NULL, (length + offset), PROT_READ, MMAP_FLAGS,
+ fd, (where - offset));
+ if (mapped_memory == MAP_FAILED) {
+ fprintf(stderr, "Cannot map %s\n", SYSTEM_MEMORY);
+ close(fd);
+ return (NULL);
+ }
+
+ close(fd);
+ return (ACPI_CAST8(mapped_memory + offset));
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_os_unmap_memory
+ *
+ * PARAMETERS: where - Logical address of memory to be unmapped
+ * length - How much memory to unmap
+ *
+ * RETURN: None.
+ *
+ * DESCRIPTION: Delete a previously created mapping. Where and Length must
+ * correspond to a previous mapping exactly.
+ *
+ *****************************************************************************/
+
+void acpi_os_unmap_memory(void *where, acpi_size length)
+{
+ acpi_physical_address offset;
+ acpi_size page_size;
+
+ page_size = acpi_os_get_page_size();
+ offset = (acpi_physical_address) where % page_size;
+ munmap((u8 *)where - offset, (length + offset));
+}
diff --git a/tools/power/acpi/tools/acpidump/acpidump.c b/tools/power/acpi/tools/acpidump/acpidump.c
deleted file mode 100644
index a84553a..0000000
--- a/tools/power/acpi/tools/acpidump/acpidump.c
+++ /dev/null
@@ -1,559 +0,0 @@
-/*
- * (c) Alexey Starikovskiy, Intel, 2005-2006.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-#ifdef DEFINE_ALTERNATE_TYPES
-/* hack to enable building old application with new headers -lenb */
-#define acpi_fadt_descriptor acpi_table_fadt
-#define acpi_rsdp_descriptor acpi_table_rsdp
-#define DSDT_SIG ACPI_SIG_DSDT
-#define FACS_SIG ACPI_SIG_FACS
-#define FADT_SIG ACPI_SIG_FADT
-#define xfirmware_ctrl Xfacs
-#define firmware_ctrl facs
-
-typedef int s32;
-typedef unsigned char u8;
-typedef unsigned short u16;
-typedef unsigned int u32;
-typedef unsigned long long u64;
-typedef long long s64;
-#endif
-
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <getopt.h>
-
-#include <dirent.h>
-
-#include <acpi/acconfig.h>
-#include <acpi/platform/acenv.h>
-#include <acpi/actypes.h>
-#include <acpi/actbl.h>
-
-static inline u8 checksum(u8 * buffer, u32 length)
-{
- u8 sum = 0, *i = buffer;
- buffer += length;
- for (; i < buffer; sum += *(i++));
- return sum;
-}
-
-static unsigned long psz, addr, length;
-static int print, connect, skip;
-static u8 select_sig[4];
-
-static unsigned long read_efi_systab( void )
-{
- char buffer[80];
- unsigned long addr;
- FILE *f = fopen("/sys/firmware/efi/systab", "r");
- if (f) {
- while (fgets(buffer, 80, f)) {
- if (sscanf(buffer, "ACPI20=0x%lx", &addr) == 1)
- return addr;
- }
- fclose(f);
- }
- return 0;
-}
-
-static u8 *acpi_map_memory(unsigned long where, unsigned length)
-{
- unsigned long offset;
- u8 *there;
- int fd = open("/dev/mem", O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "acpi_os_map_memory: cannot open /dev/mem\n");
- exit(1);
- }
- offset = where % psz;
- there = mmap(NULL, length + offset, PROT_READ, MAP_PRIVATE,
- fd, where - offset);
- close(fd);
- if (there == MAP_FAILED) return 0;
- return (there + offset);
-}
-
-static void acpi_unmap_memory(u8 * there, unsigned length)
-{
- unsigned long offset = (unsigned long)there % psz;
- munmap(there - offset, length + offset);
-}
-
-static struct acpi_table_header *acpi_map_table(unsigned long where, char *sig)
-{
- unsigned size;
- struct acpi_table_header *tbl = (struct acpi_table_header *)
- acpi_map_memory(where, sizeof(struct acpi_table_header));
- if (!tbl || (sig && memcmp(sig, tbl->signature, 4))) return 0;
- size = tbl->length;
- acpi_unmap_memory((u8 *) tbl, sizeof(struct acpi_table_header));
- return (struct acpi_table_header *)acpi_map_memory(where, size);
-}
-
-static void acpi_unmap_table(struct acpi_table_header *tbl)
-{
- acpi_unmap_memory((u8 *)tbl, tbl->length);
-}
-
-static struct acpi_rsdp_descriptor *acpi_scan_for_rsdp(u8 *begin, u32 length)
-{
- struct acpi_rsdp_descriptor *rsdp;
- u8 *i, *end = begin + length;
- /* Search from given start address for the requested length */
- for (i = begin; i < end; i += ACPI_RSDP_SCAN_STEP) {
- /* The signature and checksum must both be correct */
- if (memcmp((char *)i, "RSD PTR ", 8)) continue;
- rsdp = (struct acpi_rsdp_descriptor *)i;
- /* Signature matches, check the appropriate checksum */
- if (!checksum((u8 *) rsdp, (rsdp->revision < 2) ?
- ACPI_RSDP_CHECKSUM_LENGTH :
- ACPI_RSDP_XCHECKSUM_LENGTH))
- /* Checksum valid, we have found a valid RSDP */
- return rsdp;
- }
- /* Searched entire block, no RSDP was found */
- return 0;
-}
-
-/*
- * Output data
- */
-static void acpi_show_data(int fd, u8 * data, int size)
-{
- char buffer[256];
- int len;
- int i, remain = size;
- while (remain > 0) {
- len = snprintf(buffer, 256, " %04x:", size - remain);
- for (i = 0; i < 16 && i < remain; i++) {
- len +=
- snprintf(&buffer[len], 256 - len, " %02x", data[i]);
- }
- for (; i < 16; i++) {
- len += snprintf(&buffer[len], 256 - len, " ");
- }
- len += snprintf(&buffer[len], 256 - len, " ");
- for (i = 0; i < 16 && i < remain; i++) {
- buffer[len++] = (isprint(data[i])) ? data[i] : '.';
- }
- buffer[len++] = '\n';
- write(fd, buffer, len);
- data += 16;
- remain -= 16;
- }
-}
-
-/*
- * Output ACPI table
- */
-static void acpi_show_table(int fd, struct acpi_table_header *table, unsigned long addr)
-{
- char buff[80];
- int len = snprintf(buff, 80, "%.4s @ %p\n", table->signature, (void *)addr);
- write(fd, buff, len);
- acpi_show_data(fd, (u8 *) table, table->length);
- buff[0] = '\n';
- write(fd, buff, 1);
-}
-
-static void write_table(int fd, struct acpi_table_header *tbl, unsigned long addr)
-{
- static int select_done = 0;
- if (!select_sig[0]) {
- if (print) {
- acpi_show_table(fd, tbl, addr);
- } else {
- write(fd, tbl, tbl->length);
- }
- } else if (!select_done && !memcmp(select_sig, tbl->signature, 4)) {
- if (skip > 0) {
- --skip;
- return;
- }
- if (print) {
- acpi_show_table(fd, tbl, addr);
- } else {
- write(fd, tbl, tbl->length);
- }
- select_done = 1;
- }
-}
-
-static void acpi_dump_FADT(int fd, struct acpi_table_header *tbl, unsigned long xaddr) {
- struct acpi_fadt_descriptor x;
- unsigned long addr;
- size_t len = sizeof(struct acpi_fadt_descriptor);
- if (len > tbl->length) len = tbl->length;
- memcpy(&x, tbl, len);
- x.header.length = len;
- if (checksum((u8 *)tbl, len)) {
- fprintf(stderr, "Wrong checksum for FADT!\n");
- }
- if (x.header.length >= 148 && x.Xdsdt) {
- addr = (unsigned long)x.Xdsdt;
- if (connect) {
- x.Xdsdt = lseek(fd, 0, SEEK_CUR);
- }
- } else if (x.header.length >= 44 && x.dsdt) {
- addr = (unsigned long)x.dsdt;
- if (connect) {
- x.dsdt = lseek(fd, 0, SEEK_CUR);
- }
- } else {
- fprintf(stderr, "No DSDT in FADT!\n");
- goto no_dsdt;
- }
- tbl = acpi_map_table(addr, DSDT_SIG);
- if (!tbl) goto no_dsdt;
- if (checksum((u8 *)tbl, tbl->length))
- fprintf(stderr, "Wrong checksum for DSDT!\n");
- write_table(fd, tbl, addr);
- acpi_unmap_table(tbl);
-no_dsdt:
- if (x.header.length >= 140 && x.xfirmware_ctrl) {
- addr = (unsigned long)x.xfirmware_ctrl;
- if (connect) {
- x.xfirmware_ctrl = lseek(fd, 0, SEEK_CUR);
- }
- } else if (x.header.length >= 40 && x.firmware_ctrl) {
- addr = (unsigned long)x.firmware_ctrl;
- if (connect) {
- x.firmware_ctrl = lseek(fd, 0, SEEK_CUR);
- }
- } else {
- fprintf(stderr, "No FACS in FADT!\n");
- goto no_facs;
- }
- tbl = acpi_map_table(addr, FACS_SIG);
- if (!tbl) goto no_facs;
- /* do not checksum FACS */
- write_table(fd, tbl, addr);
- acpi_unmap_table(tbl);
-no_facs:
- write_table(fd, (struct acpi_table_header *)&x, xaddr);
-}
-
-static int acpi_dump_SDT(int fd, struct acpi_rsdp_descriptor *rsdp)
-{
- struct acpi_table_header *sdt, *tbl = 0;
- int xsdt = 1, i, num;
- char *offset;
- unsigned long addr;
- if (rsdp->revision > 1 && rsdp->xsdt_physical_address) {
- tbl = acpi_map_table(rsdp->xsdt_physical_address, "XSDT");
- }
- if (!tbl && rsdp->rsdt_physical_address) {
- xsdt = 0;
- tbl = acpi_map_table(rsdp->rsdt_physical_address, "RSDT");
- }
- if (!tbl) return 0;
- sdt = malloc(tbl->length);
- memcpy(sdt, tbl, tbl->length);
- acpi_unmap_table(tbl);
- if (checksum((u8 *)sdt, sdt->length))
- fprintf(stderr, "Wrong checksum for %s!\n", (xsdt)?"XSDT":"RSDT");
- num = (sdt->length - sizeof(struct acpi_table_header))/((xsdt)?sizeof(u64):sizeof(u32));
- offset = (char *)sdt + sizeof(struct acpi_table_header);
- for (i = 0; i < num; ++i, offset += ((xsdt) ? sizeof(u64) : sizeof(u32))) {
- addr = (xsdt) ? (unsigned long)(*(u64 *)offset):
- (unsigned long)(*(u32 *)offset);
- if (!addr) continue;
- tbl = acpi_map_table(addr, 0);
- if (!tbl) continue;
- if (!memcmp(tbl->signature, FADT_SIG, 4)) {
- acpi_dump_FADT(fd, tbl, addr);
- } else {
- if (checksum((u8 *)tbl, tbl->length))
- fprintf(stderr, "Wrong checksum for generic table!\n");
- write_table(fd, tbl, addr);
- }
- acpi_unmap_table(tbl);
- if (connect) {
- if (xsdt)
- (*(u64*)offset) = lseek(fd, 0, SEEK_CUR);
- else
- (*(u32*)offset) = lseek(fd, 0, SEEK_CUR);
- }
- }
- if (xsdt) {
- addr = (unsigned long)rsdp->xsdt_physical_address;
- if (connect) {
- rsdp->xsdt_physical_address = lseek(fd, 0, SEEK_CUR);
- }
- } else {
- addr = (unsigned long)rsdp->rsdt_physical_address;
- if (connect) {
- rsdp->rsdt_physical_address = lseek(fd, 0, SEEK_CUR);
- }
- }
- write_table(fd, sdt, addr);
- free (sdt);
- return 1;
-}
-
-#define DYNAMIC_SSDT "/sys/firmware/acpi/tables/dynamic"
-
-static void acpi_dump_dynamic_SSDT(int fd)
-{
- struct stat file_stat;
- char filename[256], *ptr;
- DIR *tabledir;
- struct dirent *entry;
- FILE *fp;
- int count, readcount, length;
- struct acpi_table_header table_header, *ptable;
-
- if (stat(DYNAMIC_SSDT, &file_stat) == -1) {
- /* The directory doesn't exist */
- return;
- }
- tabledir = opendir(DYNAMIC_SSDT);
- if(!tabledir){
- /*can't open the directory */
- return;
- }
-
- while ((entry = readdir(tabledir)) != 0){
- /* skip the file of . /.. */
- if (entry->d_name[0] == '.')
- continue;
-
- sprintf(filename, "%s/%s", DYNAMIC_SSDT, entry->d_name);
- fp = fopen(filename, "r");
- if (fp == NULL) {
- fprintf(stderr, "Can't open the file of %s\n",
- filename);
- continue;
- }
- /* Read the Table header to parse the table length */
- count = fread(&table_header, 1, sizeof(struct acpi_table_header), fp);
- if (count < sizeof(table_header)) {
- /* the length is lessn than ACPI table header. skip it */
- fclose(fp);
- continue;
- }
- length = table_header.length;
- ptr = malloc(table_header.length);
- fseek(fp, 0, SEEK_SET);
- readcount = 0;
- while(!feof(fp) && readcount < length) {
- count = fread(ptr + readcount, 1, 256, fp);
- readcount += count;
- }
- fclose(fp);
- ptable = (struct acpi_table_header *) ptr;
- if (checksum((u8 *) ptable, ptable->length))
- fprintf(stderr, "Wrong checksum "
- "for dynamic SSDT table!\n");
- write_table(fd, ptable, 0);
- free(ptr);
- }
- closedir(tabledir);
- return;
-}
-
-static void usage(const char *progname)
-{
- puts("Usage:");
- printf("%s [--addr 0x1234][--table DSDT][--output filename]"
- "[--binary][--length 0x456][--help]\n", progname);
- puts("\t--addr 0x1234 or -a 0x1234 -- look for tables at this physical address");
- puts("\t--table DSDT or -t DSDT -- only dump table with DSDT signature");
- puts("\t--output filename or -o filename -- redirect output from stdin to filename");
- puts("\t--binary or -b -- dump data in binary form rather than in hex-dump format");
- puts("\t--length 0x456 or -l 0x456 -- works only with --addr, dump physical memory"
- "\n\t\tregion without trying to understand it's contents");
- puts("\t--skip 2 or -s 2 -- skip 2 tables of the given name and output only 3rd one");
- puts("\t--help or -h -- this help message");
- exit(0);
-}
-
-static struct option long_options[] = {
- {"addr", 1, 0, 0},
- {"table", 1, 0, 0},
- {"output", 1, 0, 0},
- {"binary", 0, 0, 0},
- {"length", 1, 0, 0},
- {"skip", 1, 0, 0},
- {"help", 0, 0, 0},
- {0, 0, 0, 0}
-};
-int main(int argc, char **argv)
-{
- int option_index, c, fd;
- u8 *raw;
- struct acpi_rsdp_descriptor rsdpx, *x = 0;
- char *filename = 0;
- char buff[80];
- memset(select_sig, 0, 4);
- print = 1;
- connect = 0;
- addr = length = 0;
- skip = 0;
- while (1) {
- option_index = 0;
- c = getopt_long(argc, argv, "a:t:o:bl:s:h",
- long_options, &option_index);
- if (c == -1)
- break;
-
- switch (c) {
- case 0:
- switch (option_index) {
- case 0:
- addr = strtoul(optarg, (char **)NULL, 16);
- break;
- case 1:
- memcpy(select_sig, optarg, 4);
- break;
- case 2:
- filename = optarg;
- break;
- case 3:
- print = 0;
- break;
- case 4:
- length = strtoul(optarg, (char **)NULL, 16);
- break;
- case 5:
- skip = strtoul(optarg, (char **)NULL, 10);
- break;
- case 6:
- usage(argv[0]);
- exit(0);
- }
- break;
- case 'a':
- addr = strtoul(optarg, (char **)NULL, 16);
- break;
- case 't':
- memcpy(select_sig, optarg, 4);
- break;
- case 'o':
- filename = optarg;
- break;
- case 'b':
- print = 0;
- break;
- case 'l':
- length = strtoul(optarg, (char **)NULL, 16);
- break;
- case 's':
- skip = strtoul(optarg, (char **)NULL, 10);
- break;
- case 'h':
- usage(argv[0]);
- exit(0);
- default:
- printf("Unknown option!\n");
- usage(argv[0]);
- exit(0);
- }
- }
-
- fd = STDOUT_FILENO;
- if (filename) {
- fd = creat(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
- if (fd < 0)
- return fd;
- }
-
- if (!select_sig[0] && !print) {
- connect = 1;
- }
-
- psz = sysconf(_SC_PAGESIZE);
- if (length && addr) {
- /* We know length and address, it means we just want a memory dump */
- if (!(raw = acpi_map_memory(addr, length)))
- goto not_found;
- write(fd, raw, length);
- acpi_unmap_memory(raw, length);
- close(fd);
- return 0;
- }
-
- length = sizeof(struct acpi_rsdp_descriptor);
- if (!addr) {
- addr = read_efi_systab();
- if (!addr) {
- addr = ACPI_HI_RSDP_WINDOW_BASE;
- length = ACPI_HI_RSDP_WINDOW_SIZE;
- }
- }
-
- if (!(raw = acpi_map_memory(addr, length)) ||
- !(x = acpi_scan_for_rsdp(raw, length)))
- goto not_found;
-
- /* Find RSDP and print all found tables */
- memcpy(&rsdpx, x, sizeof(struct acpi_rsdp_descriptor));
- acpi_unmap_memory(raw, length);
- if (connect) {
- lseek(fd, sizeof(struct acpi_rsdp_descriptor), SEEK_SET);
- }
- if (!acpi_dump_SDT(fd, &rsdpx))
- goto not_found;
- if (connect) {
- lseek(fd, 0, SEEK_SET);
- write(fd, x, (rsdpx.revision < 2) ?
- ACPI_RSDP_CHECKSUM_LENGTH : ACPI_RSDP_XCHECKSUM_LENGTH);
- } else if (!select_sig[0] || !memcmp("RSD PTR ", select_sig, 4)) {
- addr += (long)x - (long)raw;
- length = snprintf(buff, 80, "RSD PTR @ %p\n", (void *)addr);
- write(fd, buff, length);
- acpi_show_data(fd, (u8 *) & rsdpx, (rsdpx.revision < 2) ?
- ACPI_RSDP_CHECKSUM_LENGTH : ACPI_RSDP_XCHECKSUM_LENGTH);
- buff[0] = '\n';
- write(fd, buff, 1);
- }
- acpi_dump_dynamic_SSDT(fd);
- close(fd);
- return 0;
-not_found:
- close(fd);
- fprintf(stderr, "ACPI tables were not found. If you know location "
- "of RSD PTR table (from dmesg, etc), "
- "supply it with either --addr or -a option\n");
- return 1;
-}
diff --git a/tools/power/acpi/tools/acpidump/acpidump.h b/tools/power/acpi/tools/acpidump/acpidump.h
new file mode 100644
index 0000000..46f5195
--- /dev/null
+++ b/tools/power/acpi/tools/acpidump/acpidump.h
@@ -0,0 +1,130 @@
+/******************************************************************************
+ *
+ * Module Name: acpidump.h - Include file for acpi_dump utility
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+/*
+ * Global variables. Defined in main.c only, externed in all other files
+ */
+#ifdef _DECLARE_GLOBALS
+#define EXTERN
+#define INIT_GLOBAL(a,b) a=b
+#define DEFINE_ACPI_GLOBALS 1
+#else
+#define EXTERN extern
+#define INIT_GLOBAL(a,b) a
+#endif
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "actables.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+/* Globals */
+
+EXTERN u8 INIT_GLOBAL(gbl_summary_mode, FALSE);
+EXTERN u8 INIT_GLOBAL(gbl_verbose_mode, FALSE);
+EXTERN u8 INIT_GLOBAL(gbl_binary_mode, FALSE);
+EXTERN u8 INIT_GLOBAL(gbl_dump_customized_tables, FALSE);
+EXTERN u8 INIT_GLOBAL(gbl_do_not_dump_xsdt, FALSE);
+EXTERN FILE INIT_GLOBAL(*gbl_output_file, NULL);
+EXTERN char INIT_GLOBAL(*gbl_output_filename, NULL);
+EXTERN u64 INIT_GLOBAL(gbl_rsdp_base, 0);
+
+/* Globals required for use with ACPICA modules */
+
+#ifdef _DECLARE_GLOBALS
+u8 acpi_gbl_integer_byte_width = 8;
+#endif
+
+/* Action table used to defer requested options */
+
+struct ap_dump_action {
+ char *argument;
+ u32 to_be_done;
+};
+
+#define AP_MAX_ACTIONS 32
+
+#define AP_DUMP_ALL_TABLES 0
+#define AP_DUMP_TABLE_BY_ADDRESS 1
+#define AP_DUMP_TABLE_BY_NAME 2
+#define AP_DUMP_TABLE_BY_FILE 3
+
+#define AP_MAX_ACPI_FILES 256 /* Prevent infinite loops */
+
+/* Minimum FADT sizes for various table addresses */
+
+#define MIN_FADT_FOR_DSDT (ACPI_FADT_OFFSET (dsdt) + sizeof (u32))
+#define MIN_FADT_FOR_FACS (ACPI_FADT_OFFSET (facs) + sizeof (u32))
+#define MIN_FADT_FOR_XDSDT (ACPI_FADT_OFFSET (Xdsdt) + sizeof (u64))
+#define MIN_FADT_FOR_XFACS (ACPI_FADT_OFFSET (Xfacs) + sizeof (u64))
+
+/*
+ * apdump - Table get/dump routines
+ */
+int ap_dump_table_from_file(char *pathname);
+
+int ap_dump_table_by_name(char *signature);
+
+int ap_dump_table_by_address(char *ascii_address);
+
+int ap_dump_all_tables(void);
+
+u8 ap_is_valid_header(struct acpi_table_header *table);
+
+u8 ap_is_valid_checksum(struct acpi_table_header *table);
+
+u32 ap_get_table_length(struct acpi_table_header *table);
+
+/*
+ * apfiles - File I/O utilities
+ */
+int ap_open_output_file(char *pathname);
+
+int ap_write_to_binary_file(struct acpi_table_header *table, u32 instance);
+
+struct acpi_table_header *ap_get_table_from_file(char *pathname,
+ u32 *file_size);
diff --git a/tools/power/acpi/tools/acpidump/apdump.c b/tools/power/acpi/tools/acpidump/apdump.c
new file mode 100644
index 0000000..3cac123
--- /dev/null
+++ b/tools/power/acpi/tools/acpidump/apdump.c
@@ -0,0 +1,451 @@
+/******************************************************************************
+ *
+ * Module Name: apdump - Dump routines for ACPI tables (acpidump)
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpidump.h"
+
+/* Local prototypes */
+
+static int
+ap_dump_table_buffer(struct acpi_table_header *table,
+ u32 instance, acpi_physical_address address);
+
+/******************************************************************************
+ *
+ * FUNCTION: ap_is_valid_header
+ *
+ * PARAMETERS: table - Pointer to table to be validated
+ *
+ * RETURN: TRUE if the header appears to be valid. FALSE otherwise
+ *
+ * DESCRIPTION: Check for a valid ACPI table header
+ *
+ ******************************************************************************/
+
+u8 ap_is_valid_header(struct acpi_table_header *table)
+{
+
+ if (!ACPI_VALIDATE_RSDP_SIG(table->signature)) {
+
+ /* Make sure signature is all ASCII and a valid ACPI name */
+
+ if (!acpi_ut_valid_acpi_name(table->signature)) {
+ fprintf(stderr,
+ "Table signature (0x%8.8X) is invalid\n",
+ *(u32 *)table->signature);
+ return (FALSE);
+ }
+
+ /* Check for minimum table length */
+
+ if (table->length < sizeof(struct acpi_table_header)) {
+ fprintf(stderr, "Table length (0x%8.8X) is invalid\n",
+ table->length);
+ return (FALSE);
+ }
+ }
+
+ return (TRUE);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: ap_is_valid_checksum
+ *
+ * PARAMETERS: table - Pointer to table to be validated
+ *
+ * RETURN: TRUE if the checksum appears to be valid. FALSE otherwise.
+ *
+ * DESCRIPTION: Check for a valid ACPI table checksum.
+ *
+ ******************************************************************************/
+
+u8 ap_is_valid_checksum(struct acpi_table_header *table)
+{
+ acpi_status status;
+ struct acpi_table_rsdp *rsdp;
+
+ if (ACPI_VALIDATE_RSDP_SIG(table->signature)) {
+ /*
+ * Checksum for RSDP.
+ * Note: Other checksums are computed during the table dump.
+ */
+ rsdp = ACPI_CAST_PTR(struct acpi_table_rsdp, table);
+ status = acpi_tb_validate_rsdp(rsdp);
+ } else {
+ status = acpi_tb_verify_checksum(table, table->length);
+ }
+
+ if (ACPI_FAILURE(status)) {
+ fprintf(stderr, "%4.4s: Warning: wrong checksum in table\n",
+ table->signature);
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: ap_get_table_length
+ *
+ * PARAMETERS: table - Pointer to the table
+ *
+ * RETURN: Table length
+ *
+ * DESCRIPTION: Obtain table length according to table signature.
+ *
+ ******************************************************************************/
+
+u32 ap_get_table_length(struct acpi_table_header *table)
+{
+ struct acpi_table_rsdp *rsdp;
+
+ /* Check if table is valid */
+
+ if (!ap_is_valid_header(table)) {
+ return (0);
+ }
+
+ if (ACPI_VALIDATE_RSDP_SIG(table->signature)) {
+ rsdp = ACPI_CAST_PTR(struct acpi_table_rsdp, table);
+ return (rsdp->length);
+ }
+
+ /* Normal ACPI table */
+
+ return (table->length);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: ap_dump_table_buffer
+ *
+ * PARAMETERS: table - ACPI table to be dumped
+ * instance - ACPI table instance no. to be dumped
+ * address - Physical address of the table
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Dump an ACPI table in standard ASCII hex format, with a
+ * header that is compatible with the acpi_xtract utility.
+ *
+ ******************************************************************************/
+
+static int
+ap_dump_table_buffer(struct acpi_table_header *table,
+ u32 instance, acpi_physical_address address)
+{
+ u32 table_length;
+
+ table_length = ap_get_table_length(table);
+
+ /* Print only the header if requested */
+
+ if (gbl_summary_mode) {
+ acpi_tb_print_table_header(address, table);
+ return (0);
+ }
+
+ /* Dump to binary file if requested */
+
+ if (gbl_binary_mode) {
+ return (ap_write_to_binary_file(table, instance));
+ }
+
+ /*
+ * Dump the table with header for use with acpixtract utility.
+ * Note: simplest to just always emit a 64-bit address. acpi_xtract
+ * utility can handle this.
+ */
+ printf("%4.4s @ 0x%8.8X%8.8X\n", table->signature,
+ ACPI_FORMAT_UINT64(address));
+
+ acpi_ut_dump_buffer(ACPI_CAST_PTR(u8, table), table_length,
+ DB_BYTE_DISPLAY, 0);
+ printf("\n");
+ return (0);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: ap_dump_all_tables
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get all tables from the RSDT/XSDT (or at least all of the
+ * tables that we can possibly get).
+ *
+ ******************************************************************************/
+
+int ap_dump_all_tables(void)
+{
+ struct acpi_table_header *table;
+ u32 instance = 0;
+ acpi_physical_address address;
+ acpi_status status;
+ int table_status;
+ u32 i;
+
+ /* Get and dump all available ACPI tables */
+
+ for (i = 0; i < AP_MAX_ACPI_FILES; i++) {
+ status =
+ acpi_os_get_table_by_index(i, &table, &instance, &address);
+ if (ACPI_FAILURE(status)) {
+
+ /* AE_LIMIT means that no more tables are available */
+
+ if (status == AE_LIMIT) {
+ return (0);
+ } else if (i == 0) {
+ fprintf(stderr,
+ "Could not get ACPI tables, %s\n",
+ acpi_format_exception(status));
+ return (-1);
+ } else {
+ fprintf(stderr,
+ "Could not get ACPI table at index %u, %s\n",
+ i, acpi_format_exception(status));
+ continue;
+ }
+ }
+
+ table_status = ap_dump_table_buffer(table, instance, address);
+ free(table);
+
+ if (table_status) {
+ break;
+ }
+ }
+
+ /* Something seriously bad happened if the loop terminates here */
+
+ return (-1);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: ap_dump_table_by_address
+ *
+ * PARAMETERS: ascii_address - Address for requested ACPI table
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get an ACPI table via a physical address and dump it.
+ *
+ ******************************************************************************/
+
+int ap_dump_table_by_address(char *ascii_address)
+{
+ acpi_physical_address address;
+ struct acpi_table_header *table;
+ acpi_status status;
+ int table_status;
+ u64 long_address;
+
+ /* Convert argument to an integer physical address */
+
+ status = acpi_ut_strtoul64(ascii_address, 0, &long_address);
+ if (ACPI_FAILURE(status)) {
+ fprintf(stderr, "%s: Could not convert to a physical address\n",
+ ascii_address);
+ return (-1);
+ }
+
+ address = (acpi_physical_address) long_address;
+ status = acpi_os_get_table_by_address(address, &table);
+ if (ACPI_FAILURE(status)) {
+ fprintf(stderr, "Could not get table at 0x%8.8X%8.8X, %s\n",
+ ACPI_FORMAT_UINT64(address),
+ acpi_format_exception(status));
+ return (-1);
+ }
+
+ table_status = ap_dump_table_buffer(table, 0, address);
+ free(table);
+ return (table_status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: ap_dump_table_by_name
+ *
+ * PARAMETERS: signature - Requested ACPI table signature
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Get an ACPI table via a signature and dump it. Handles
+ * multiple tables with the same signature (SSDTs).
+ *
+ ******************************************************************************/
+
+int ap_dump_table_by_name(char *signature)
+{
+ char local_signature[ACPI_NAME_SIZE + 1];
+ u32 instance;
+ struct acpi_table_header *table;
+ acpi_physical_address address;
+ acpi_status status;
+ int table_status;
+
+ if (strlen(signature) != ACPI_NAME_SIZE) {
+ fprintf(stderr,
+ "Invalid table signature [%s]: must be exactly 4 characters\n",
+ signature);
+ return (-1);
+ }
+
+ /* Table signatures are expected to be uppercase */
+
+ strcpy(local_signature, signature);
+ acpi_ut_strupr(local_signature);
+
+ /* To be friendly, handle tables whose signatures do not match the name */
+
+ if (ACPI_COMPARE_NAME(local_signature, "FADT")) {
+ strcpy(local_signature, ACPI_SIG_FADT);
+ } else if (ACPI_COMPARE_NAME(local_signature, "MADT")) {
+ strcpy(local_signature, ACPI_SIG_MADT);
+ }
+
+ /* Dump all instances of this signature (to handle multiple SSDTs) */
+
+ for (instance = 0; instance < AP_MAX_ACPI_FILES; instance++) {
+ status = acpi_os_get_table_by_name(local_signature, instance,
+ &table, &address);
+ if (ACPI_FAILURE(status)) {
+
+ /* AE_LIMIT means that no more tables are available */
+
+ if (status == AE_LIMIT) {
+ return (0);
+ }
+
+ fprintf(stderr,
+ "Could not get ACPI table with signature [%s], %s\n",
+ local_signature, acpi_format_exception(status));
+ return (-1);
+ }
+
+ table_status = ap_dump_table_buffer(table, instance, address);
+ free(table);
+
+ if (table_status) {
+ break;
+ }
+ }
+
+ /* Something seriously bad happened if the loop terminates here */
+
+ return (-1);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: ap_dump_table_from_file
+ *
+ * PARAMETERS: pathname - File containing the binary ACPI table
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Dump an ACPI table from a binary file
+ *
+ ******************************************************************************/
+
+int ap_dump_table_from_file(char *pathname)
+{
+ struct acpi_table_header *table;
+ u32 file_size = 0;
+ int table_status = -1;
+
+ /* Get the entire ACPI table from the file */
+
+ table = ap_get_table_from_file(pathname, &file_size);
+ if (!table) {
+ return (-1);
+ }
+
+ /* File must be at least as long as the table length */
+
+ if (table->length > file_size) {
+ fprintf(stderr,
+ "Table length (0x%X) is too large for input file (0x%X) %s\n",
+ table->length, file_size, pathname);
+ goto exit;
+ }
+
+ if (gbl_verbose_mode) {
+ fprintf(stderr,
+ "Input file: %s contains table [%4.4s], 0x%X (%u) bytes\n",
+ pathname, table->signature, file_size, file_size);
+ }
+
+ table_status = ap_dump_table_buffer(table, 0, 0);
+
+exit:
+ free(table);
+ return (table_status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_os* print functions
+ *
+ * DESCRIPTION: Used for linkage with ACPICA modules
+ *
+ ******************************************************************************/
+
+void ACPI_INTERNAL_VAR_XFACE acpi_os_printf(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vfprintf(stdout, fmt, args);
+ va_end(args);
+}
+
+void acpi_os_vprintf(const char *fmt, va_list args)
+{
+ vfprintf(stdout, fmt, args);
+}
diff --git a/tools/power/acpi/tools/acpidump/apfiles.c b/tools/power/acpi/tools/acpidump/apfiles.c
new file mode 100644
index 0000000..4488acc
--- /dev/null
+++ b/tools/power/acpi/tools/acpidump/apfiles.c
@@ -0,0 +1,228 @@
+/******************************************************************************
+ *
+ * Module Name: apfiles - File-related functions for acpidump utility
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpidump.h"
+#include "acapps.h"
+
+/******************************************************************************
+ *
+ * FUNCTION: ap_open_output_file
+ *
+ * PARAMETERS: pathname - Output filename
+ *
+ * RETURN: Open file handle
+ *
+ * DESCRIPTION: Open a text output file for acpidump. Checks if file already
+ * exists.
+ *
+ ******************************************************************************/
+
+int ap_open_output_file(char *pathname)
+{
+ struct stat stat_info;
+ FILE *file;
+
+ /* If file exists, prompt for overwrite */
+
+ if (!stat(pathname, &stat_info)) {
+ fprintf(stderr,
+ "Target path already exists, overwrite? [y|n] ");
+
+ if (getchar() != 'y') {
+ return (-1);
+ }
+ }
+
+ /* Point stdout to the file */
+
+ file = freopen(pathname, "w", stdout);
+ if (!file) {
+ perror("Could not open output file");
+ return (-1);
+ }
+
+ /* Save the file and path */
+
+ gbl_output_file = file;
+ gbl_output_filename = pathname;
+ return (0);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: ap_write_to_binary_file
+ *
+ * PARAMETERS: table - ACPI table to be written
+ * instance - ACPI table instance no. to be written
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Write an ACPI table to a binary file. Builds the output
+ * filename from the table signature.
+ *
+ ******************************************************************************/
+
+int ap_write_to_binary_file(struct acpi_table_header *table, u32 instance)
+{
+ char filename[ACPI_NAME_SIZE + 16];
+ char instance_str[16];
+ FILE *file;
+ size_t actual;
+ u32 table_length;
+
+ /* Obtain table length */
+
+ table_length = ap_get_table_length(table);
+
+ /* Construct lower-case filename from the table local signature */
+
+ if (ACPI_VALIDATE_RSDP_SIG(table->signature)) {
+ ACPI_MOVE_NAME(filename, ACPI_RSDP_NAME);
+ } else {
+ ACPI_MOVE_NAME(filename, table->signature);
+ }
+ filename[0] = (char)ACPI_TOLOWER(filename[0]);
+ filename[1] = (char)ACPI_TOLOWER(filename[1]);
+ filename[2] = (char)ACPI_TOLOWER(filename[2]);
+ filename[3] = (char)ACPI_TOLOWER(filename[3]);
+ filename[ACPI_NAME_SIZE] = 0;
+
+ /* Handle multiple SSDts - create different filenames for each */
+
+ if (instance > 0) {
+ sprintf(instance_str, "%u", instance);
+ strcat(filename, instance_str);
+ }
+
+ strcat(filename, ACPI_TABLE_FILE_SUFFIX);
+
+ if (gbl_verbose_mode) {
+ fprintf(stderr,
+ "Writing [%4.4s] to binary file: %s 0x%X (%u) bytes\n",
+ table->signature, filename, table->length,
+ table->length);
+ }
+
+ /* Open the file and dump the entire table in binary mode */
+
+ file = fopen(filename, "wb");
+ if (!file) {
+ perror("Could not open output file");
+ return (-1);
+ }
+
+ actual = fwrite(table, 1, table_length, file);
+ if (actual != table_length) {
+ perror("Error writing binary output file");
+ fclose(file);
+ return (-1);
+ }
+
+ fclose(file);
+ return (0);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: ap_get_table_from_file
+ *
+ * PARAMETERS: pathname - File containing the binary ACPI table
+ * out_file_size - Where the file size is returned
+ *
+ * RETURN: Buffer containing the ACPI table. NULL on error.
+ *
+ * DESCRIPTION: Open a file and read it entirely into a new buffer
+ *
+ ******************************************************************************/
+
+struct acpi_table_header *ap_get_table_from_file(char *pathname,
+ u32 *out_file_size)
+{
+ struct acpi_table_header *buffer = NULL;
+ FILE *file;
+ u32 file_size;
+ size_t actual;
+
+ /* Must use binary mode */
+
+ file = fopen(pathname, "rb");
+ if (!file) {
+ perror("Could not open input file");
+ return (NULL);
+ }
+
+ /* Need file size to allocate a buffer */
+
+ file_size = cm_get_file_size(file);
+ if (file_size == ACPI_UINT32_MAX) {
+ fprintf(stderr,
+ "Could not get input file size: %s\n", pathname);
+ goto cleanup;
+ }
+
+ /* Allocate a buffer for the entire file */
+
+ buffer = calloc(1, file_size);
+ if (!buffer) {
+ fprintf(stderr,
+ "Could not allocate file buffer of size: %u\n",
+ file_size);
+ goto cleanup;
+ }
+
+ /* Read the entire file */
+
+ actual = fread(buffer, 1, file_size, file);
+ if (actual != file_size) {
+ fprintf(stderr, "Could not read input file: %s\n", pathname);
+ free(buffer);
+ buffer = NULL;
+ goto cleanup;
+ }
+
+ *out_file_size = file_size;
+
+cleanup:
+ fclose(file);
+ return (buffer);
+}
diff --git a/tools/power/acpi/tools/acpidump/apmain.c b/tools/power/acpi/tools/acpidump/apmain.c
new file mode 100644
index 0000000..51e8d63
--- /dev/null
+++ b/tools/power/acpi/tools/acpidump/apmain.c
@@ -0,0 +1,351 @@
+/******************************************************************************
+ *
+ * Module Name: apmain - Main module for the acpidump utility
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#define _DECLARE_GLOBALS
+#include "acpidump.h"
+#include "acapps.h"
+
+/*
+ * acpidump - A portable utility for obtaining system ACPI tables and dumping
+ * them in an ASCII hex format suitable for binary extraction via acpixtract.
+ *
+ * Obtaining the system ACPI tables is an OS-specific operation.
+ *
+ * This utility can be ported to any host operating system by providing a
+ * module containing system-specific versions of these interfaces:
+ *
+ * acpi_os_get_table_by_address
+ * acpi_os_get_table_by_index
+ * acpi_os_get_table_by_name
+ *
+ * See the ACPICA Reference Guide for the exact definitions of these
+ * interfaces. Also, see these ACPICA source code modules for example
+ * implementations:
+ *
+ * source/os_specific/service_layers/oswintbl.c
+ * source/os_specific/service_layers/oslinuxtbl.c
+ */
+
+/* Local prototypes */
+
+static void ap_display_usage(void);
+
+static int ap_do_options(int argc, char **argv);
+
+static void ap_insert_action(char *argument, u32 to_be_done);
+
+/* Table for deferred actions from command line options */
+
+struct ap_dump_action action_table[AP_MAX_ACTIONS];
+u32 current_action = 0;
+
+#define AP_UTILITY_NAME "ACPI Binary Table Dump Utility"
+#define AP_SUPPORTED_OPTIONS "?a:bcf:hn:o:r:svxz"
+
+/******************************************************************************
+ *
+ * FUNCTION: ap_display_usage
+ *
+ * DESCRIPTION: Usage message for the acpi_dump utility
+ *
+ ******************************************************************************/
+
+static void ap_display_usage(void)
+{
+
+ ACPI_USAGE_HEADER("acpidump [options]");
+
+ ACPI_OPTION("-b", "Dump tables to binary files");
+ ACPI_OPTION("-c", "Dump customized tables");
+ ACPI_OPTION("-h -?", "This help message");
+ ACPI_OPTION("-o <File>", "Redirect output to file");
+ ACPI_OPTION("-r <Address>", "Dump tables from specified RSDP");
+ ACPI_OPTION("-s", "Print table summaries only");
+ ACPI_OPTION("-v", "Display version information");
+ ACPI_OPTION("-z", "Verbose mode");
+
+ printf("\nTable Options:\n");
+
+ ACPI_OPTION("-a <Address>", "Get table via a physical address");
+ ACPI_OPTION("-f <BinaryFile>", "Get table via a binary file");
+ ACPI_OPTION("-n <Signature>", "Get table via a name/signature");
+ ACPI_OPTION("-x", "Do not use but dump XSDT");
+ ACPI_OPTION("-x -x", "Do not use or dump XSDT");
+
+ printf("\n"
+ "Invocation without parameters dumps all available tables\n"
+ "Multiple mixed instances of -a, -f, and -n are supported\n\n");
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: ap_insert_action
+ *
+ * PARAMETERS: argument - Pointer to the argument for this action
+ * to_be_done - What to do to process this action
+ *
+ * RETURN: None. Exits program if action table becomes full.
+ *
+ * DESCRIPTION: Add an action item to the action table
+ *
+ ******************************************************************************/
+
+static void ap_insert_action(char *argument, u32 to_be_done)
+{
+
+ /* Insert action and check for table overflow */
+
+ action_table[current_action].argument = argument;
+ action_table[current_action].to_be_done = to_be_done;
+
+ current_action++;
+ if (current_action > AP_MAX_ACTIONS) {
+ fprintf(stderr, "Too many table options (max %u)\n",
+ AP_MAX_ACTIONS);
+ exit(-1);
+ }
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: ap_do_options
+ *
+ * PARAMETERS: argc/argv - Standard argc/argv
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Command line option processing. The main actions for getting
+ * and dumping tables are deferred via the action table.
+ *
+ *****************************************************************************/
+
+static int ap_do_options(int argc, char **argv)
+{
+ int j;
+ acpi_status status;
+
+ /* Command line options */
+
+ while ((j = acpi_getopt(argc, argv, AP_SUPPORTED_OPTIONS)) != EOF)
+ switch (j) {
+ /*
+ * Global options
+ */
+ case 'b': /* Dump all input tables to binary files */
+
+ gbl_binary_mode = TRUE;
+ continue;
+
+ case 'c': /* Dump customized tables */
+
+ gbl_dump_customized_tables = TRUE;
+ continue;
+
+ case 'h':
+ case '?':
+
+ ap_display_usage();
+ exit(0);
+
+ case 'o': /* Redirect output to a single file */
+
+ if (ap_open_output_file(acpi_gbl_optarg)) {
+ exit(-1);
+ }
+ continue;
+
+ case 'r': /* Dump tables from specified RSDP */
+
+ status =
+ acpi_ut_strtoul64(acpi_gbl_optarg, 0,
+ &gbl_rsdp_base);
+ if (ACPI_FAILURE(status)) {
+ fprintf(stderr,
+ "%s: Could not convert to a physical address\n",
+ acpi_gbl_optarg);
+ exit(-1);
+ }
+ continue;
+
+ case 's': /* Print table summaries only */
+
+ gbl_summary_mode = TRUE;
+ continue;
+
+ case 'x': /* Do not use XSDT */
+
+ if (!acpi_gbl_do_not_use_xsdt) {
+ acpi_gbl_do_not_use_xsdt = TRUE;
+ } else {
+ gbl_do_not_dump_xsdt = TRUE;
+ }
+ continue;
+
+ case 'v': /* Revision/version */
+
+ printf(ACPI_COMMON_SIGNON(AP_UTILITY_NAME));
+ exit(0);
+
+ case 'z': /* Verbose mode */
+
+ gbl_verbose_mode = TRUE;
+ fprintf(stderr, ACPI_COMMON_SIGNON(AP_UTILITY_NAME));
+ continue;
+
+ /*
+ * Table options
+ */
+ case 'a': /* Get table by physical address */
+
+ ap_insert_action(acpi_gbl_optarg,
+ AP_DUMP_TABLE_BY_ADDRESS);
+ break;
+
+ case 'f': /* Get table from a file */
+
+ ap_insert_action(acpi_gbl_optarg,
+ AP_DUMP_TABLE_BY_FILE);
+ break;
+
+ case 'n': /* Get table by input name (signature) */
+
+ ap_insert_action(acpi_gbl_optarg,
+ AP_DUMP_TABLE_BY_NAME);
+ break;
+
+ default:
+
+ ap_display_usage();
+ exit(-1);
+ }
+
+ /* If there are no actions, this means "get/dump all tables" */
+
+ if (current_action == 0) {
+ ap_insert_action(NULL, AP_DUMP_ALL_TABLES);
+ }
+
+ return (0);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: main
+ *
+ * PARAMETERS: argc/argv - Standard argc/argv
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: C main function for acpidump utility
+ *
+ ******************************************************************************/
+
+int ACPI_SYSTEM_XFACE main(int argc, char *argv[])
+{
+ int status = 0;
+ struct ap_dump_action *action;
+ u32 file_size;
+ u32 i;
+
+ ACPI_DEBUG_INITIALIZE(); /* For debug version only */
+
+ /* Process command line options */
+
+ if (ap_do_options(argc, argv)) {
+ return (-1);
+ }
+
+ /* Get/dump ACPI table(s) as requested */
+
+ for (i = 0; i < current_action; i++) {
+ action = &action_table[i];
+ switch (action->to_be_done) {
+ case AP_DUMP_ALL_TABLES:
+
+ status = ap_dump_all_tables();
+ break;
+
+ case AP_DUMP_TABLE_BY_ADDRESS:
+
+ status = ap_dump_table_by_address(action->argument);
+ break;
+
+ case AP_DUMP_TABLE_BY_NAME:
+
+ status = ap_dump_table_by_name(action->argument);
+ break;
+
+ case AP_DUMP_TABLE_BY_FILE:
+
+ status = ap_dump_table_from_file(action->argument);
+ break;
+
+ default:
+
+ fprintf(stderr,
+ "Internal error, invalid action: 0x%X\n",
+ action->to_be_done);
+ return (-1);
+ }
+
+ if (status) {
+ return (status);
+ }
+ }
+
+ if (gbl_output_file) {
+ if (gbl_verbose_mode) {
+
+ /* Summary for the output file */
+
+ file_size = cm_get_file_size(gbl_output_file);
+ fprintf(stderr,
+ "Output file %s contains 0x%X (%u) bytes\n\n",
+ gbl_output_filename, file_size, file_size);
+ }
+
+ fclose(gbl_output_file);
+ }
+
+ return (status);
+}
diff --git a/tools/power/acpi/tools/ec/Makefile b/tools/power/acpi/tools/ec/Makefile
new file mode 100644
index 0000000..b7b0b92
--- /dev/null
+++ b/tools/power/acpi/tools/ec/Makefile
@@ -0,0 +1,22 @@
+ec_access: ec_access.o
+ $(ECHO) " LD " $@
+ $(QUIET) $(LD) $(CFLAGS) $(LDFLAGS) $< -o $@
+ $(QUIET) $(STRIPCMD) $@
+
+%.o: %.c
+ $(ECHO) " CC " $@
+ $(QUIET) $(CC) -c $(CFLAGS) -o $@ $<
+
+all: ec_access
+
+install:
+ $(INSTALL) -d $(DESTDIR)${sbindir}
+ $(INSTALL_PROGRAM) ec_access $(DESTDIR)${sbindir}
+
+uninstall:
+ - rm -f $(DESTDIR)${sbindir}/ec_access
+
+clean:
+ -rm -f $(OUTPUT)ec_access
+
+.PHONY: all install uninstall
diff --git a/tools/power/acpi/tools/ec/ec_access.c b/tools/power/acpi/tools/ec/ec_access.c
new file mode 100644
index 0000000..6b8aaed
--- /dev/null
+++ b/tools/power/acpi/tools/ec/ec_access.c
@@ -0,0 +1,238 @@
+/*
+ * ec_access.c
+ *
+ * Copyright (C) 2010 SUSE Linux Products GmbH
+ * Author:
+ * Thomas Renninger <trenn@suse.de>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ */
+
+#include <fcntl.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#define EC_SPACE_SIZE 256
+#define SYSFS_PATH "/sys/kernel/debug/ec/ec0/io"
+
+/* TBD/Enhancements:
+ - Provide param for accessing different ECs (not supported by kernel yet)
+*/
+
+static int read_mode = -1;
+static int sleep_time;
+static int write_byte_offset = -1;
+static int read_byte_offset = -1;
+static uint8_t write_value = -1;
+
+void usage(char progname[], int exit_status)
+{
+ printf("Usage:\n");
+ printf("1) %s -r [-s sleep]\n", basename(progname));
+ printf("2) %s -b byte_offset\n", basename(progname));
+ printf("3) %s -w byte_offset -v value\n\n", basename(progname));
+
+ puts("\t-r [-s sleep] : Dump EC registers");
+ puts("\t If sleep is given, sleep x seconds,");
+ puts("\t re-read EC registers and show changes");
+ puts("\t-b offset : Read value at byte_offset (in hex)");
+ puts("\t-w offset -v value : Write value at byte_offset");
+ puts("\t-h : Print this help\n\n");
+ puts("Offsets and values are in hexadecimal number sytem.");
+ puts("The offset and value must be between 0 and 0xff.");
+ exit(exit_status);
+}
+
+void parse_opts(int argc, char *argv[])
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "rs:b:w:v:h")) != -1) {
+
+ switch (c) {
+ case 'r':
+ if (read_mode != -1)
+ usage(argv[0], EXIT_FAILURE);
+ read_mode = 1;
+ break;
+ case 's':
+ if (read_mode != -1 && read_mode != 1)
+ usage(argv[0], EXIT_FAILURE);
+
+ sleep_time = atoi(optarg);
+ if (sleep_time <= 0) {
+ sleep_time = 0;
+ usage(argv[0], EXIT_FAILURE);
+ printf("Bad sleep time: %s\n", optarg);
+ }
+ break;
+ case 'b':
+ if (read_mode != -1)
+ usage(argv[0], EXIT_FAILURE);
+ read_mode = 1;
+ read_byte_offset = strtoul(optarg, NULL, 16);
+ break;
+ case 'w':
+ if (read_mode != -1)
+ usage(argv[0], EXIT_FAILURE);
+ read_mode = 0;
+ write_byte_offset = strtoul(optarg, NULL, 16);
+ break;
+ case 'v':
+ write_value = strtoul(optarg, NULL, 16);
+ break;
+ case 'h':
+ usage(argv[0], EXIT_SUCCESS);
+ default:
+ fprintf(stderr, "Unknown option!\n");
+ usage(argv[0], EXIT_FAILURE);
+ }
+ }
+ if (read_mode == 0) {
+ if (write_byte_offset < 0 ||
+ write_byte_offset >= EC_SPACE_SIZE) {
+ fprintf(stderr, "Wrong byte offset 0x%.2x, valid: "
+ "[0-0x%.2x]\n",
+ write_byte_offset, EC_SPACE_SIZE - 1);
+ usage(argv[0], EXIT_FAILURE);
+ }
+ if (write_value < 0 ||
+ write_value >= 255) {
+ fprintf(stderr, "Wrong byte offset 0x%.2x, valid:"
+ "[0-0xff]\n", write_byte_offset);
+ usage(argv[0], EXIT_FAILURE);
+ }
+ }
+ if (read_mode == 1 && read_byte_offset != -1) {
+ if (read_byte_offset < -1 ||
+ read_byte_offset >= EC_SPACE_SIZE) {
+ fprintf(stderr, "Wrong byte offset 0x%.2x, valid: "
+ "[0-0x%.2x]\n",
+ read_byte_offset, EC_SPACE_SIZE - 1);
+ usage(argv[0], EXIT_FAILURE);
+ }
+ }
+ /* Add additional parameter checks here */
+}
+
+void dump_ec(int fd)
+{
+ char buf[EC_SPACE_SIZE];
+ char buf2[EC_SPACE_SIZE];
+ int byte_off, bytes_read;
+
+ bytes_read = read(fd, buf, EC_SPACE_SIZE);
+
+ if (bytes_read == -1)
+ err(EXIT_FAILURE, "Could not read from %s\n", SYSFS_PATH);
+
+ if (bytes_read != EC_SPACE_SIZE)
+ fprintf(stderr, "Could only read %d bytes\n", bytes_read);
+
+ printf(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
+ for (byte_off = 0; byte_off < bytes_read; byte_off++) {
+ if ((byte_off % 16) == 0)
+ printf("\n%.2X: ", byte_off);
+ printf(" %.2x ", (uint8_t)buf[byte_off]);
+ }
+ printf("\n");
+
+ if (!sleep_time)
+ return;
+
+ printf("\n");
+ lseek(fd, 0, SEEK_SET);
+ sleep(sleep_time);
+
+ bytes_read = read(fd, buf2, EC_SPACE_SIZE);
+
+ if (bytes_read == -1)
+ err(EXIT_FAILURE, "Could not read from %s\n", SYSFS_PATH);
+
+ if (bytes_read != EC_SPACE_SIZE)
+ fprintf(stderr, "Could only read %d bytes\n", bytes_read);
+
+ printf(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
+ for (byte_off = 0; byte_off < bytes_read; byte_off++) {
+ if ((byte_off % 16) == 0)
+ printf("\n%.2X: ", byte_off);
+
+ if (buf[byte_off] == buf2[byte_off])
+ printf(" %.2x ", (uint8_t)buf2[byte_off]);
+ else
+ printf("*%.2x ", (uint8_t)buf2[byte_off]);
+ }
+ printf("\n");
+}
+
+void read_ec_val(int fd, int byte_offset)
+{
+ uint8_t buf;
+ int error;
+
+ error = lseek(fd, byte_offset, SEEK_SET);
+ if (error != byte_offset)
+ err(EXIT_FAILURE, "Cannot set offset to 0x%.2x", byte_offset);
+
+ error = read(fd, &buf, 1);
+ if (error != 1)
+ err(EXIT_FAILURE, "Could not read byte 0x%.2x from %s\n",
+ byte_offset, SYSFS_PATH);
+ printf("0x%.2x\n", buf);
+ return;
+}
+
+void write_ec_val(int fd, int byte_offset, uint8_t value)
+{
+ int error;
+
+ error = lseek(fd, byte_offset, SEEK_SET);
+ if (error != byte_offset)
+ err(EXIT_FAILURE, "Cannot set offset to 0x%.2x", byte_offset);
+
+ error = write(fd, &value, 1);
+ if (error != 1)
+ err(EXIT_FAILURE, "Cannot write value 0x%.2x to offset 0x%.2x",
+ value, byte_offset);
+}
+
+int main(int argc, char *argv[])
+{
+ int file_mode = O_RDONLY;
+ int fd;
+
+ parse_opts(argc, argv);
+
+ if (read_mode == 0)
+ file_mode = O_WRONLY;
+ else if (read_mode == 1)
+ file_mode = O_RDONLY;
+ else
+ usage(argv[0], EXIT_FAILURE);
+
+ fd = open(SYSFS_PATH, file_mode);
+ if (fd == -1)
+ err(EXIT_FAILURE, "%s", SYSFS_PATH);
+
+ if (read_mode)
+ if (read_byte_offset == -1)
+ dump_ec(fd);
+ else if (read_byte_offset < 0 ||
+ read_byte_offset >= EC_SPACE_SIZE)
+ usage(argv[0], EXIT_FAILURE);
+ else
+ read_ec_val(fd, read_byte_offset);
+ else
+ write_ec_val(fd, write_byte_offset, write_value);
+ close(fd);
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile
index cbfec92..2e2ba2e 100644
--- a/tools/power/cpupower/Makefile
+++ b/tools/power/cpupower/Makefile
@@ -62,7 +62,7 @@ LIB_MAJ= 0.0.0
LIB_MIN= 0
PACKAGE = cpupower
-PACKAGE_BUGREPORT = cpufreq@vger.kernel.org
+PACKAGE_BUGREPORT = linux-pm@vger.kernel.org
LANGUAGES = de fr it cs pt
@@ -274,6 +274,8 @@ install-man:
$(INSTALL_DATA) -D man/cpupower.1 $(DESTDIR)${mandir}/man1/cpupower.1
$(INSTALL_DATA) -D man/cpupower-frequency-set.1 $(DESTDIR)${mandir}/man1/cpupower-frequency-set.1
$(INSTALL_DATA) -D man/cpupower-frequency-info.1 $(DESTDIR)${mandir}/man1/cpupower-frequency-info.1
+ $(INSTALL_DATA) -D man/cpupower-idle-set.1 $(DESTDIR)${mandir}/man1/cpupower-idle-set.1
+ $(INSTALL_DATA) -D man/cpupower-idle-info.1 $(DESTDIR)${mandir}/man1/cpupower-idle-info.1
$(INSTALL_DATA) -D man/cpupower-set.1 $(DESTDIR)${mandir}/man1/cpupower-set.1
$(INSTALL_DATA) -D man/cpupower-info.1 $(DESTDIR)${mandir}/man1/cpupower-info.1
$(INSTALL_DATA) -D man/cpupower-monitor.1 $(DESTDIR)${mandir}/man1/cpupower-monitor.1
@@ -295,8 +297,12 @@ uninstall:
- rm -f $(DESTDIR)${libdir}/libcpupower.*
- rm -f $(DESTDIR)${includedir}/cpufreq.h
- rm -f $(DESTDIR)${bindir}/utils/cpupower
- - rm -f $(DESTDIR)${mandir}/man1/cpufreq-set.1
- - rm -f $(DESTDIR)${mandir}/man1/cpufreq-info.1
+ - rm -f $(DESTDIR)${mandir}/man1/cpupower.1
+ - rm -f $(DESTDIR)${mandir}/man1/cpupower-frequency-set.1
+ - rm -f $(DESTDIR)${mandir}/man1/cpupower-frequency-info.1
+ - rm -f $(DESTDIR)${mandir}/man1/cpupower-set.1
+ - rm -f $(DESTDIR)${mandir}/man1/cpupower-info.1
+ - rm -f $(DESTDIR)${mandir}/man1/cpupower-monitor.1
- for HLANG in $(LANGUAGES); do \
rm -f $(DESTDIR)${localedir}/$$HLANG/LC_MESSAGES/cpupower.mo; \
done;
diff --git a/tools/power/cpupower/README b/tools/power/cpupower/README
index fd9d4c0..1c68f47 100644
--- a/tools/power/cpupower/README
+++ b/tools/power/cpupower/README
@@ -1,6 +1,4 @@
-The cpufrequtils package (homepage:
-http://www.kernel.org/pub/linux/utils/kernel/cpufreq/cpufrequtils.html )
-consists of the following elements:
+The cpupower package consists of the following elements:
requirements
------------
@@ -11,10 +9,10 @@ providing cpuid.h is needed.
For both it's not explicitly checked for (yet).
-libcpufreq
+libcpupower
----------
-"libcpufreq" is a library which offers a unified access method for userspace
+"libcpupower" is a library which offers a unified access method for userspace
tools and programs to the cpufreq core and drivers in the Linux kernel. This
allows for code reduction in userspace tools, a clean implementation of
the interaction to the cpufreq core, and support for both the sysfs and proc
@@ -28,22 +26,22 @@ make
su
make install
-should suffice on most systems. It builds default libcpufreq,
-cpufreq-set and cpufreq-info files and installs them in /usr/lib and
-/usr/bin, respectively. If you want to set up the paths differently and/or
-want to configure the package to your specific needs, you need to open
-"Makefile" with an editor of your choice and edit the block marked
-CONFIGURATION.
+should suffice on most systems. It builds libcpupower to put in
+/usr/lib; cpupower, cpufreq-bench_plot.sh to put in /usr/bin; and
+cpufreq-bench to put in /usr/sbin. If you want to set up the paths
+differently and/or want to configure the package to your specific
+needs, you need to open "Makefile" with an editor of your choice and
+edit the block marked CONFIGURATION.
THANKS
------
Many thanks to Mattia Dongili who wrote the autotoolization and
-libtoolization, the manpages and the italian language file for cpufrequtils;
+libtoolization, the manpages and the italian language file for cpupower;
to Dave Jones for his feedback and his dump_psb tool; to Bruno Ducrot for his
powernow-k8-decode and intel_gsic tools as well as the french language file;
and to various others commenting on the previous (pre-)releases of
-cpufrequtils.
+cpupower.
Dominik Brodowski
diff --git a/tools/power/cpupower/ToDo b/tools/power/cpupower/ToDo
index 874b78b..6e8b89f 100644
--- a/tools/power/cpupower/ToDo
+++ b/tools/power/cpupower/ToDo
@@ -3,7 +3,6 @@ ToDos sorted by priority:
- Use bitmask functions to parse CPU topology more robust
(current implementation has issues on AMD)
- Try to read out boost states and frequencies on Intel
-- Adjust README
- Somewhere saw the ability to read power consumption of
RAM from HW on Intel SandyBridge -> another monitor?
- Add another c1e debug idle monitor
diff --git a/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c b/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c
index 0f10b81..5224ee5 100644
--- a/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c
+++ b/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c
@@ -18,7 +18,7 @@
* 5.) if the third value, "diff_pmtmr", changes between 2. and 4., the
* TSC-based delay routine on the Linux kernel does not correctly
* handle the cpufreq transition. Please report this to
- * cpufreq@vger.kernel.org
+ * linux-pm@vger.kernel.org
*/
#include <linux/kernel.h>
diff --git a/tools/power/cpupower/man/cpupower-frequency-info.1 b/tools/power/cpupower/man/cpupower-frequency-info.1
index 4a1918e..9c85a38 100644
--- a/tools/power/cpupower/man/cpupower-frequency-info.1
+++ b/tools/power/cpupower/man/cpupower-frequency-info.1
@@ -50,6 +50,9 @@ Prints out information like provided by the /proc/cpufreq interface in 2.4. and
\fB\-m\fR \fB\-\-human\fR
human\-readable output for the \-f, \-w, \-s and \-y parameters.
.TP
+\fB\-n\fR \fB\-\-no-rounding\fR
+Output frequencies and latencies without rounding off values.
+.TP
.SH "REMARKS"
.LP
By default only values of core zero are displayed. How to display settings of
diff --git a/tools/power/cpupower/man/cpupower-idle-set.1 b/tools/power/cpupower/man/cpupower-idle-set.1
index 6b16072..3e6799d 100644
--- a/tools/power/cpupower/man/cpupower-idle-set.1
+++ b/tools/power/cpupower/man/cpupower-idle-set.1
@@ -13,11 +13,17 @@ sleep states. This can be handy for power vs performance tuning.
.SH "OPTIONS"
.LP
.TP
-\fB\-d\fR \fB\-\-disable\fR
+\fB\-d\fR \fB\-\-disable\fR <STATE_NO>
Disable a specific processor sleep state.
.TP
-\fB\-e\fR \fB\-\-enable\fR
+\fB\-e\fR \fB\-\-enable\fR <STATE_NO>
Enable a specific processor sleep state.
+.TP
+\fB\-D\fR \fB\-\-disable-by-latency\fR <LATENCY>
+Disable all idle states with a equal or higher latency than <LATENCY>
+.TP
+\fB\-E\fR \fB\-\-enable-all\fR
+Enable all idle states if not enabled already.
.SH "REMARKS"
.LP
diff --git a/tools/power/cpupower/man/cpupower-info.1 b/tools/power/cpupower/man/cpupower-info.1
index 58e2119..340bcd0 100644
--- a/tools/power/cpupower/man/cpupower-info.1
+++ b/tools/power/cpupower/man/cpupower-info.1
@@ -3,7 +3,7 @@
cpupower\-info \- Shows processor power related kernel or hardware configurations
.SH SYNOPSIS
.ft B
-.B cpupower info [ \-b ] [ \-s ] [ \-m ]
+.B cpupower info [ \-b ]
.SH DESCRIPTION
\fBcpupower info \fP shows kernel configurations or processor hardware
diff --git a/tools/power/cpupower/man/cpupower-set.1 b/tools/power/cpupower/man/cpupower-set.1
index 9dbd536..2bcc696 100644
--- a/tools/power/cpupower/man/cpupower-set.1
+++ b/tools/power/cpupower/man/cpupower-set.1
@@ -3,7 +3,7 @@
cpupower\-set \- Set processor power related kernel or hardware configurations
.SH SYNOPSIS
.ft B
-.B cpupower set [ \-b VAL ] [ \-s VAL ] [ \-m VAL ]
+.B cpupower set [ \-b VAL ]
.SH DESCRIPTION
@@ -55,35 +55,6 @@ Use \fBcpupower -c all info -b\fP to verify.
This options needs the msr kernel driver (CONFIG_X86_MSR) loaded.
.RE
-.PP
-\-\-sched\-mc, \-m [ VAL ]
-.RE
-\-\-sched\-smt, \-s [ VAL ]
-.RS 4
-\-\-sched\-mc utilizes cores in one processor package/socket first before
-processes are scheduled to other processor packages/sockets.
-
-\-\-sched\-smt utilizes thread siblings of one processor core first before
-processes are scheduled to other cores.
-
-The impact on power consumption and performance (positiv or negativ) heavily
-depends on processor support for deep sleep states, frequency scaling and
-frequency boost modes and their dependencies between other thread siblings
-and processor cores.
-
-Taken over from kernel documentation:
-
-Adjust the kernel's multi-core scheduler support.
-
-Possible values are:
-.RS 2
-0 - No power saving load balance (default value)
-
-1 - Fill one thread/core/package first for long running threads
-
-2 - Also bias task wakeups to semi-idle cpu package for power
-savings
-.RE
.SH "SEE ALSO"
cpupower-info(1), cpupower-monitor(1), powertop(1)
diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c
index 28953c9..b4b90a9 100644
--- a/tools/power/cpupower/utils/cpufreq-info.c
+++ b/tools/power/cpupower/utils/cpufreq-info.c
@@ -82,29 +82,42 @@ static void proc_cpufreq_output(void)
}
}
+static int no_rounding;
static void print_speed(unsigned long speed)
{
unsigned long tmp;
- if (speed > 1000000) {
- tmp = speed % 10000;
- if (tmp >= 5000)
- speed += 10000;
- printf("%u.%02u GHz", ((unsigned int) speed/1000000),
- ((unsigned int) (speed%1000000)/10000));
- } else if (speed > 100000) {
- tmp = speed % 1000;
- if (tmp >= 500)
- speed += 1000;
- printf("%u MHz", ((unsigned int) speed / 1000));
- } else if (speed > 1000) {
- tmp = speed % 100;
- if (tmp >= 50)
- speed += 100;
- printf("%u.%01u MHz", ((unsigned int) speed/1000),
- ((unsigned int) (speed%1000)/100));
- } else
- printf("%lu kHz", speed);
+ if (no_rounding) {
+ if (speed > 1000000)
+ printf("%u.%06u GHz", ((unsigned int) speed/1000000),
+ ((unsigned int) speed%1000000));
+ else if (speed > 100000)
+ printf("%u MHz", (unsigned int) speed);
+ else if (speed > 1000)
+ printf("%u.%03u MHz", ((unsigned int) speed/1000),
+ (unsigned int) (speed%1000));
+ else
+ printf("%lu kHz", speed);
+ } else {
+ if (speed > 1000000) {
+ tmp = speed%10000;
+ if (tmp >= 5000)
+ speed += 10000;
+ printf("%u.%02u GHz", ((unsigned int) speed/1000000),
+ ((unsigned int) (speed%1000000)/10000));
+ } else if (speed > 100000) {
+ tmp = speed%1000;
+ if (tmp >= 500)
+ speed += 1000;
+ printf("%u MHz", ((unsigned int) speed/1000));
+ } else if (speed > 1000) {
+ tmp = speed%100;
+ if (tmp >= 50)
+ speed += 100;
+ printf("%u.%01u MHz", ((unsigned int) speed/1000),
+ ((unsigned int) (speed%1000)/100));
+ }
+ }
return;
}
@@ -113,26 +126,38 @@ static void print_duration(unsigned long duration)
{
unsigned long tmp;
- if (duration > 1000000) {
- tmp = duration % 10000;
- if (tmp >= 5000)
- duration += 10000;
- printf("%u.%02u ms", ((unsigned int) duration/1000000),
- ((unsigned int) (duration%1000000)/10000));
- } else if (duration > 100000) {
- tmp = duration % 1000;
- if (tmp >= 500)
- duration += 1000;
- printf("%u us", ((unsigned int) duration / 1000));
- } else if (duration > 1000) {
- tmp = duration % 100;
- if (tmp >= 50)
- duration += 100;
- printf("%u.%01u us", ((unsigned int) duration/1000),
- ((unsigned int) (duration%1000)/100));
- } else
- printf("%lu ns", duration);
-
+ if (no_rounding) {
+ if (duration > 1000000)
+ printf("%u.%06u ms", ((unsigned int) duration/1000000),
+ ((unsigned int) duration%1000000));
+ else if (duration > 100000)
+ printf("%u us", ((unsigned int) duration/1000));
+ else if (duration > 1000)
+ printf("%u.%03u us", ((unsigned int) duration/1000),
+ ((unsigned int) duration%1000));
+ else
+ printf("%lu ns", duration);
+ } else {
+ if (duration > 1000000) {
+ tmp = duration%10000;
+ if (tmp >= 5000)
+ duration += 10000;
+ printf("%u.%02u ms", ((unsigned int) duration/1000000),
+ ((unsigned int) (duration%1000000)/10000));
+ } else if (duration > 100000) {
+ tmp = duration%1000;
+ if (tmp >= 500)
+ duration += 1000;
+ printf("%u us", ((unsigned int) duration / 1000));
+ } else if (duration > 1000) {
+ tmp = duration%100;
+ if (tmp >= 50)
+ duration += 100;
+ printf("%u.%01u us", ((unsigned int) duration/1000),
+ ((unsigned int) (duration%1000)/100));
+ } else
+ printf("%lu ns", duration);
+ }
return;
}
@@ -525,6 +550,7 @@ static struct option info_opts[] = {
{ .name = "latency", .has_arg = no_argument, .flag = NULL, .val = 'y'},
{ .name = "proc", .has_arg = no_argument, .flag = NULL, .val = 'o'},
{ .name = "human", .has_arg = no_argument, .flag = NULL, .val = 'm'},
+ { .name = "no-rounding", .has_arg = no_argument, .flag = NULL, .val = 'n'},
{ },
};
@@ -538,7 +564,8 @@ int cmd_freq_info(int argc, char **argv)
int output_param = 0;
do {
- ret = getopt_long(argc, argv, "oefwldpgrasmyb", info_opts, NULL);
+ ret = getopt_long(argc, argv, "oefwldpgrasmybn", info_opts,
+ NULL);
switch (ret) {
case '?':
output_param = '?';
@@ -575,6 +602,9 @@ int cmd_freq_info(int argc, char **argv)
}
human = 1;
break;
+ case 'n':
+ no_rounding = 1;
+ break;
default:
fprintf(stderr, "invalid or unknown argument\n");
return EXIT_FAILURE;
diff --git a/tools/power/cpupower/utils/cpuidle-set.c b/tools/power/cpupower/utils/cpuidle-set.c
index c78141c..d45d8d7 100644
--- a/tools/power/cpupower/utils/cpuidle-set.c
+++ b/tools/power/cpupower/utils/cpuidle-set.c
@@ -13,8 +13,14 @@
#include "helpers/sysfs.h"
static struct option info_opts[] = {
- { .name = "disable", .has_arg = required_argument, .flag = NULL, .val = 'd'},
- { .name = "enable", .has_arg = required_argument, .flag = NULL, .val = 'e'},
+ { .name = "disable",
+ .has_arg = required_argument, .flag = NULL, .val = 'd'},
+ { .name = "enable",
+ .has_arg = required_argument, .flag = NULL, .val = 'e'},
+ { .name = "disable-by-latency",
+ .has_arg = required_argument, .flag = NULL, .val = 'D'},
+ { .name = "enable-all",
+ .has_arg = no_argument, .flag = NULL, .val = 'E'},
{ },
};
@@ -23,11 +29,13 @@ int cmd_idle_set(int argc, char **argv)
{
extern char *optarg;
extern int optind, opterr, optopt;
- int ret = 0, cont = 1, param = 0, idlestate = 0;
- unsigned int cpu = 0;
+ int ret = 0, cont = 1, param = 0, disabled;
+ unsigned long long latency = 0, state_latency;
+ unsigned int cpu = 0, idlestate = 0, idlestates = 0;
+ char *endptr;
do {
- ret = getopt_long(argc, argv, "d:e:", info_opts, NULL);
+ ret = getopt_long(argc, argv, "d:e:ED:", info_opts, NULL);
if (ret == -1)
break;
switch (ret) {
@@ -53,6 +61,27 @@ int cmd_idle_set(int argc, char **argv)
param = ret;
idlestate = atoi(optarg);
break;
+ case 'D':
+ if (param) {
+ param = -1;
+ cont = 0;
+ break;
+ }
+ param = ret;
+ latency = strtoull(optarg, &endptr, 10);
+ if (*endptr != '\0') {
+ printf(_("Bad latency value: %s\n"), optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'E':
+ if (param) {
+ param = -1;
+ cont = 0;
+ break;
+ }
+ param = ret;
+ break;
case -1:
cont = 0;
break;
@@ -79,8 +108,14 @@ int cmd_idle_set(int argc, char **argv)
if (!bitmask_isbitset(cpus_chosen, cpu))
continue;
- switch (param) {
+ if (sysfs_is_cpu_online(cpu) != 1)
+ continue;
+
+ idlestates = sysfs_get_idlestate_count(cpu);
+ if (idlestates <= 0)
+ continue;
+ switch (param) {
case 'd':
ret = sysfs_idlestate_disable(cpu, idlestate, 1);
if (ret == 0)
@@ -107,6 +142,34 @@ int cmd_idle_set(int argc, char **argv)
printf(_("Idlestate %u not enabled on CPU %u\n"),
idlestate, cpu);
break;
+ case 'D':
+ for (idlestate = 0; idlestate < idlestates; idlestate++) {
+ disabled = sysfs_is_idlestate_disabled
+ (cpu, idlestate);
+ state_latency = sysfs_get_idlestate_latency
+ (cpu, idlestate);
+ printf("CPU: %u - idlestate %u - state_latency: %llu - latency: %llu\n",
+ cpu, idlestate, state_latency, latency);
+ if (disabled == 1 || latency > state_latency)
+ continue;
+ ret = sysfs_idlestate_disable
+ (cpu, idlestate, 1);
+ if (ret == 0)
+ printf(_("Idlestate %u disabled on CPU %u\n"), idlestate, cpu);
+ }
+ break;
+ case 'E':
+ for (idlestate = 0; idlestate < idlestates; idlestate++) {
+ disabled = sysfs_is_idlestate_disabled
+ (cpu, idlestate);
+ if (disabled == 1) {
+ ret = sysfs_idlestate_disable
+ (cpu, idlestate, 0);
+ if (ret == 0)
+ printf(_("Idlestate %u enabled on CPU %u\n"), idlestate, cpu);
+ }
+ }
+ break;
default:
/* Not reachable with proper args checking */
printf(_("Invalid or unknown argument\n"));
diff --git a/tools/power/cpupower/utils/cpupower-info.c b/tools/power/cpupower/utils/cpupower-info.c
index 3f68632..136d979 100644
--- a/tools/power/cpupower/utils/cpupower-info.c
+++ b/tools/power/cpupower/utils/cpupower-info.c
@@ -18,8 +18,6 @@
static struct option set_opts[] = {
{ .name = "perf-bias", .has_arg = optional_argument, .flag = NULL, .val = 'b'},
- { .name = "sched-mc", .has_arg = optional_argument, .flag = NULL, .val = 'm'},
- { .name = "sched-smt", .has_arg = optional_argument, .flag = NULL, .val = 's'},
{ },
};
@@ -37,8 +35,6 @@ int cmd_info(int argc, char **argv)
union {
struct {
- int sched_mc:1;
- int sched_smt:1;
int perf_bias:1;
};
int params;
@@ -49,23 +45,13 @@ int cmd_info(int argc, char **argv)
textdomain(PACKAGE);
/* parameter parsing */
- while ((ret = getopt_long(argc, argv, "msb", set_opts, NULL)) != -1) {
+ while ((ret = getopt_long(argc, argv, "b", set_opts, NULL)) != -1) {
switch (ret) {
case 'b':
if (params.perf_bias)
print_wrong_arg_exit();
params.perf_bias = 1;
break;
- case 'm':
- if (params.sched_mc)
- print_wrong_arg_exit();
- params.sched_mc = 1;
- break;
- case 's':
- if (params.sched_smt)
- print_wrong_arg_exit();
- params.sched_smt = 1;
- break;
default:
print_wrong_arg_exit();
}
@@ -78,25 +64,6 @@ int cmd_info(int argc, char **argv)
if (bitmask_isallclear(cpus_chosen))
bitmask_setbit(cpus_chosen, 0);
- if (params.sched_mc) {
- ret = sysfs_get_sched("mc");
- printf(_("System's multi core scheduler setting: "));
- if (ret < 0)
- /* if sysfs file is missing it's: errno == ENOENT */
- printf(_("not supported\n"));
- else
- printf("%d\n", ret);
- }
- if (params.sched_smt) {
- ret = sysfs_get_sched("smt");
- printf(_("System's thread sibling scheduler setting: "));
- if (ret < 0)
- /* if sysfs file is missing it's: errno == ENOENT */
- printf(_("not supported\n"));
- else
- printf("%d\n", ret);
- }
-
/* Add more per cpu options here */
if (!params.perf_bias)
return ret;
@@ -125,11 +92,12 @@ int cmd_info(int argc, char **argv)
if (params.perf_bias) {
ret = msr_intel_get_perf_bias(cpu);
if (ret < 0) {
- printf(_("Could not read perf-bias value\n"));
- break;
+ fprintf(stderr,
+ _("Could not read perf-bias value[%d]\n"), ret);
+ exit(EXIT_FAILURE);
} else
printf(_("perf-bias: %d\n"), ret);
}
}
- return ret;
+ return 0;
}
diff --git a/tools/power/cpupower/utils/cpupower-set.c b/tools/power/cpupower/utils/cpupower-set.c
index bcf1d2f..573c75f 100644
--- a/tools/power/cpupower/utils/cpupower-set.c
+++ b/tools/power/cpupower/utils/cpupower-set.c
@@ -19,8 +19,6 @@
static struct option set_opts[] = {
{ .name = "perf-bias", .has_arg = required_argument, .flag = NULL, .val = 'b'},
- { .name = "sched-mc", .has_arg = required_argument, .flag = NULL, .val = 'm'},
- { .name = "sched-smt", .has_arg = required_argument, .flag = NULL, .val = 's'},
{ },
};
@@ -38,13 +36,11 @@ int cmd_set(int argc, char **argv)
union {
struct {
- int sched_mc:1;
- int sched_smt:1;
int perf_bias:1;
};
int params;
} params;
- int sched_mc = 0, sched_smt = 0, perf_bias = 0;
+ int perf_bias = 0;
int ret = 0;
setlocale(LC_ALL, "");
@@ -52,7 +48,7 @@ int cmd_set(int argc, char **argv)
params.params = 0;
/* parameter parsing */
- while ((ret = getopt_long(argc, argv, "m:s:b:",
+ while ((ret = getopt_long(argc, argv, "b:",
set_opts, NULL)) != -1) {
switch (ret) {
case 'b':
@@ -66,28 +62,6 @@ int cmd_set(int argc, char **argv)
}
params.perf_bias = 1;
break;
- case 'm':
- if (params.sched_mc)
- print_wrong_arg_exit();
- sched_mc = atoi(optarg);
- if (sched_mc < 0 || sched_mc > 2) {
- printf(_("--sched-mc param out "
- "of range [0-%d]\n"), 2);
- print_wrong_arg_exit();
- }
- params.sched_mc = 1;
- break;
- case 's':
- if (params.sched_smt)
- print_wrong_arg_exit();
- sched_smt = atoi(optarg);
- if (sched_smt < 0 || sched_smt > 2) {
- printf(_("--sched-smt param out "
- "of range [0-%d]\n"), 2);
- print_wrong_arg_exit();
- }
- params.sched_smt = 1;
- break;
default:
print_wrong_arg_exit();
}
@@ -96,19 +70,6 @@ int cmd_set(int argc, char **argv)
if (!params.params)
print_wrong_arg_exit();
- if (params.sched_mc) {
- ret = sysfs_set_sched("mc", sched_mc);
- if (ret)
- fprintf(stderr, _("Error setting sched-mc %s\n"),
- (ret == -ENODEV) ? "not supported" : "");
- }
- if (params.sched_smt) {
- ret = sysfs_set_sched("smt", sched_smt);
- if (ret)
- fprintf(stderr, _("Error setting sched-smt %s\n"),
- (ret == -ENODEV) ? "not supported" : "");
- }
-
/* Default is: set all CPUs */
if (bitmask_isallclear(cpus_chosen))
bitmask_setall(cpus_chosen);
diff --git a/tools/power/cpupower/utils/cpupower.c b/tools/power/cpupower/utils/cpupower.c
index 7efc570..7cdcf88 100644
--- a/tools/power/cpupower/utils/cpupower.c
+++ b/tools/power/cpupower/utils/cpupower.c
@@ -12,6 +12,9 @@
#include <string.h>
#include <unistd.h>
#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
#include "builtin.h"
#include "helpers/helpers.h"
@@ -169,6 +172,8 @@ int main(int argc, const char *argv[])
{
const char *cmd;
unsigned int i, ret;
+ struct stat statbuf;
+ struct utsname uts;
cpus_chosen = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
@@ -195,6 +200,15 @@ int main(int argc, const char *argv[])
get_cpu_info(0, &cpupower_cpu_info);
run_as_root = !getuid();
+ if (run_as_root) {
+ ret = uname(&uts);
+ if (!ret && !strcmp(uts.machine, "x86_64") &&
+ stat("/dev/cpu/0/msr", &statbuf) != 0) {
+ if (system("modprobe msr") == -1)
+ fprintf(stderr, _("MSR access not available.\n"));
+ }
+ }
+
for (i = 0; i < ARRAY_SIZE(commands); i++) {
struct cmd_struct *p = commands + i;
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
index 7c9d8e7..d0396af 100644
--- a/tools/power/x86/turbostat/turbostat.c
+++ b/tools/power/x86/turbostat/turbostat.c
@@ -1971,13 +1971,13 @@ int set_temperature_target(struct thread_data *t, struct core_data *c, struct pk
if (get_msr(0, MSR_IA32_TEMPERATURE_TARGET, &msr))
goto guess;
- target_c_local = (msr >> 16) & 0x7F;
+ target_c_local = (msr >> 16) & 0xFF;
if (verbose)
fprintf(stderr, "cpu%d: MSR_IA32_TEMPERATURE_TARGET: 0x%08llx (%d C)\n",
cpu, msr, target_c_local);
- if (target_c_local < 85 || target_c_local > 127)
+ if (!target_c_local)
goto guess;
tcc_activation_temp = target_c_local;
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 32487ed..e66e710 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -10,6 +10,7 @@ TARGETS += timers
TARGETS += vm
TARGETS += powerpc
TARGETS += user
+TARGETS += sysctl
all:
for TARGET in $(TARGETS); do \
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 750512b..c7493b8 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -14,6 +14,12 @@ all: $(NET_PROGS)
run_tests: all
@/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]"
@/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]"
-
+ @if /sbin/modprobe test_bpf ; then \
+ /sbin/rmmod test_bpf; \
+ echo "test_bpf: ok"; \
+ else \
+ echo "test_bpf: [FAIL]"; \
+ exit 1; \
+ fi
clean:
$(RM) $(NET_PROGS)
diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile
index 316194f..54833a7 100644
--- a/tools/testing/selftests/powerpc/Makefile
+++ b/tools/testing/selftests/powerpc/Makefile
@@ -13,7 +13,7 @@ CFLAGS := -Wall -O2 -flto -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CUR
export CC CFLAGS
-TARGETS = pmu copyloops
+TARGETS = pmu copyloops mm tm
endif
diff --git a/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h b/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h
index ccd9c84..d1dc374 100644
--- a/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h
+++ b/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h
@@ -46,12 +46,15 @@
#define R20 r20
#define R21 r21
#define R22 r22
+#define R29 r29
+#define R30 r30
+#define R31 r31
#define STACKFRAMESIZE 256
-#define STK_PARAM(i) (48 + ((i)-3)*8)
#define STK_REG(i) (112 + ((i)-14)*8)
#define _GLOBAL(A) FUNC_START(test_ ## A)
+#define _GLOBAL_TOC(A) _GLOBAL(A)
#define PPC_MTOCRF(A, B) mtocrf A, B
diff --git a/tools/testing/selftests/powerpc/harness.c b/tools/testing/selftests/powerpc/harness.c
index e80c42a..8ebc58a 100644
--- a/tools/testing/selftests/powerpc/harness.c
+++ b/tools/testing/selftests/powerpc/harness.c
@@ -30,12 +30,15 @@ int run_test(int (test_function)(void), char *name)
pid = fork();
if (pid == 0) {
+ setpgid(0, 0);
exit(test_function());
} else if (pid == -1) {
perror("fork");
return 1;
}
+ setpgid(pid, pid);
+
/* Wake us up in timeout seconds */
alarm(TIMEOUT);
terminated = false;
@@ -50,17 +53,20 @@ wait:
if (terminated) {
printf("!! force killing %s\n", name);
- kill(pid, SIGKILL);
+ kill(-pid, SIGKILL);
return 1;
} else {
printf("!! killing %s\n", name);
- kill(pid, SIGTERM);
+ kill(-pid, SIGTERM);
terminated = true;
alarm(KILL_TIMEOUT);
goto wait;
}
}
+ /* Kill anything else in the process group that is still running */
+ kill(-pid, SIGTERM);
+
if (WIFEXITED(status))
status = WEXITSTATUS(status);
else {
@@ -99,7 +105,10 @@ int test_harness(int (test_function)(void), char *name)
rc = run_test(test_function, name);
- test_finish(name, rc);
+ if (rc == MAGIC_SKIP_RETURN_VALUE)
+ test_skip(name);
+ else
+ test_finish(name, rc);
return rc;
}
diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile
new file mode 100644
index 0000000..357ccbd
--- /dev/null
+++ b/tools/testing/selftests/powerpc/mm/Makefile
@@ -0,0 +1,18 @@
+noarg:
+ $(MAKE) -C ../
+
+PROGS := hugetlb_vs_thp_test
+
+all: $(PROGS)
+
+$(PROGS): ../harness.c
+
+run_tests: all
+ @-for PROG in $(PROGS); do \
+ ./$$PROG; \
+ done;
+
+clean:
+ rm -f $(PROGS)
+
+.PHONY: all run_tests clean
diff --git a/tools/testing/selftests/powerpc/mm/hugetlb_vs_thp_test.c b/tools/testing/selftests/powerpc/mm/hugetlb_vs_thp_test.c
new file mode 100644
index 0000000..3d8e5b0
--- /dev/null
+++ b/tools/testing/selftests/powerpc/mm/hugetlb_vs_thp_test.c
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+/* This must match the huge page & THP size */
+#define SIZE (16 * 1024 * 1024)
+
+static int test_body(void)
+{
+ void *addr;
+ char *p;
+
+ addr = (void *)0xa0000000;
+
+ p = mmap(addr, SIZE, PROT_READ | PROT_WRITE,
+ MAP_HUGETLB | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (p != MAP_FAILED) {
+ /*
+ * Typically the mmap will fail because no huge pages are
+ * allocated on the system. But if there are huge pages
+ * allocated the mmap will succeed. That's fine too, we just
+ * munmap here before continuing.
+ */
+ munmap(addr, SIZE);
+ }
+
+ p = mmap(addr, SIZE, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (p == MAP_FAILED) {
+ printf("Mapping failed @ %p\n", addr);
+ perror("mmap");
+ return 1;
+ }
+
+ /*
+ * Either a user or kernel access is sufficient to trigger the bug.
+ * A kernel access is easier to spot & debug, as it will trigger the
+ * softlockup or RCU stall detectors, and when the system is kicked
+ * into xmon we get a backtrace in the kernel.
+ *
+ * A good option is:
+ * getcwd(p, SIZE);
+ *
+ * For the purposes of this testcase it's preferable to spin in
+ * userspace, so the harness can kill us if we get stuck. That way we
+ * see a test failure rather than a dead system.
+ */
+ *p = 0xf;
+
+ munmap(addr, SIZE);
+
+ return 0;
+}
+
+static int test_main(void)
+{
+ int i;
+
+ /* 10,000 because it's a "bunch", and completes reasonably quickly */
+ for (i = 0; i < 10000; i++)
+ if (test_body())
+ return 1;
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(test_main, "hugetlb_vs_thp");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/Makefile b/tools/testing/selftests/powerpc/pmu/Makefile
index 7216f00..b9ff0db 100644
--- a/tools/testing/selftests/powerpc/pmu/Makefile
+++ b/tools/testing/selftests/powerpc/pmu/Makefile
@@ -4,7 +4,7 @@ noarg:
PROGS := count_instructions
EXTRA_SOURCES := ../harness.c event.c
-all: $(PROGS)
+all: $(PROGS) sub_all
$(PROGS): $(EXTRA_SOURCES)
@@ -12,12 +12,30 @@ $(PROGS): $(EXTRA_SOURCES)
count_instructions: loop.S count_instructions.c $(EXTRA_SOURCES)
$(CC) $(CFLAGS) -m64 -o $@ $^
-run_tests: all
+run_tests: all sub_run_tests
@-for PROG in $(PROGS); do \
./$$PROG; \
done;
-clean:
+clean: sub_clean
rm -f $(PROGS) loop.o
-.PHONY: all run_tests clean
+
+SUB_TARGETS = ebb
+
+sub_all:
+ @for TARGET in $(SUB_TARGETS); do \
+ $(MAKE) -C $$TARGET all; \
+ done;
+
+sub_run_tests: all
+ @for TARGET in $(SUB_TARGETS); do \
+ $(MAKE) -C $$TARGET run_tests; \
+ done;
+
+sub_clean:
+ @for TARGET in $(SUB_TARGETS); do \
+ $(MAKE) -C $$TARGET clean; \
+ done;
+
+.PHONY: all run_tests clean sub_all sub_run_tests sub_clean
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/Makefile b/tools/testing/selftests/powerpc/pmu/ebb/Makefile
new file mode 100644
index 0000000..edbba2a
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/Makefile
@@ -0,0 +1,32 @@
+noarg:
+ $(MAKE) -C ../../
+
+# The EBB handler is 64-bit code and everything links against it
+CFLAGS += -m64
+
+PROGS := reg_access_test event_attributes_test cycles_test \
+ cycles_with_freeze_test pmc56_overflow_test \
+ ebb_vs_cpu_event_test cpu_event_vs_ebb_test \
+ cpu_event_pinned_vs_ebb_test task_event_vs_ebb_test \
+ task_event_pinned_vs_ebb_test multi_ebb_procs_test \
+ multi_counter_test pmae_handling_test \
+ close_clears_pmcc_test instruction_count_test \
+ fork_cleanup_test ebb_on_child_test \
+ ebb_on_willing_child_test back_to_back_ebbs_test \
+ lost_exception_test no_handler_test
+
+all: $(PROGS)
+
+$(PROGS): ../../harness.c ../event.c ../lib.c ebb.c ebb_handler.S trace.c
+
+instruction_count_test: ../loop.S
+
+lost_exception_test: ../lib.c
+
+run_tests: all
+ @-for PROG in $(PROGS); do \
+ ./$$PROG; \
+ done;
+
+clean:
+ rm -f $(PROGS)
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c b/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c
new file mode 100644
index 0000000..66ea765
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+#define NUMBER_OF_EBBS 50
+
+/*
+ * Test that if we overflow the counter while in the EBB handler, we take
+ * another EBB on exiting from the handler.
+ *
+ * We do this by counting with a stupidly low sample period, causing us to
+ * overflow the PMU while we're still in the EBB handler, leading to another
+ * EBB.
+ *
+ * We get out of what would otherwise be an infinite loop by leaving the
+ * counter frozen once we've taken enough EBBs.
+ */
+
+static void ebb_callee(void)
+{
+ uint64_t siar, val;
+
+ val = mfspr(SPRN_BESCR);
+ if (!(val & BESCR_PMEO)) {
+ ebb_state.stats.spurious++;
+ goto out;
+ }
+
+ ebb_state.stats.ebb_count++;
+ trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
+
+ /* Resets the PMC */
+ count_pmc(1, sample_period);
+
+out:
+ if (ebb_state.stats.ebb_count == NUMBER_OF_EBBS)
+ /* Reset but leave counters frozen */
+ reset_ebb_with_clear_mask(MMCR0_PMAO);
+ else
+ /* Unfreezes */
+ reset_ebb();
+
+ /* Do some stuff to chew some cycles and pop the counter */
+ siar = mfspr(SPRN_SIAR);
+ trace_log_reg(ebb_state.trace, SPRN_SIAR, siar);
+
+ val = mfspr(SPRN_PMC1);
+ trace_log_reg(ebb_state.trace, SPRN_PMC1, val);
+
+ val = mfspr(SPRN_MMCR0);
+ trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
+}
+
+int back_to_back_ebbs(void)
+{
+ struct event event;
+
+ event_init_named(&event, 0x1001e, "cycles");
+ event_leader_ebb_init(&event);
+
+ event.attr.exclude_kernel = 1;
+ event.attr.exclude_hv = 1;
+ event.attr.exclude_idle = 1;
+
+ FAIL_IF(event_open(&event));
+
+ setup_ebb_handler(ebb_callee);
+
+ FAIL_IF(ebb_event_enable(&event));
+
+ sample_period = 5;
+
+ ebb_freeze_pmcs();
+ mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+ ebb_global_enable();
+ ebb_unfreeze_pmcs();
+
+ while (ebb_state.stats.ebb_count < NUMBER_OF_EBBS)
+ FAIL_IF(core_busy_loop());
+
+ ebb_global_disable();
+ ebb_freeze_pmcs();
+
+ count_pmc(1, sample_period);
+
+ dump_ebb_state();
+
+ event_close(&event);
+
+ FAIL_IF(ebb_state.stats.ebb_count != NUMBER_OF_EBBS);
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(back_to_back_ebbs, "back_to_back_ebbs");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c b/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c
new file mode 100644
index 0000000..0f0423d
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that closing the EBB event clears MMCR0_PMCC, preventing further access
+ * by userspace to the PMU hardware.
+ */
+
+int close_clears_pmcc(void)
+{
+ struct event event;
+
+ event_init_named(&event, 0x1001e, "cycles");
+ event_leader_ebb_init(&event);
+
+ FAIL_IF(event_open(&event));
+
+ ebb_enable_pmc_counting(1);
+ setup_ebb_handler(standard_ebb_callee);
+ ebb_global_enable();
+ FAIL_IF(ebb_event_enable(&event));
+
+ mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+ while (ebb_state.stats.ebb_count < 1)
+ FAIL_IF(core_busy_loop());
+
+ ebb_global_disable();
+ event_close(&event);
+
+ FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+ /* The real test is here, do we take a SIGILL when writing PMU regs now
+ * that we have closed the event. We expect that we will. */
+
+ FAIL_IF(catch_sigill(write_pmc1));
+
+ /* We should still be able to read EBB regs though */
+ mfspr(SPRN_EBBHR);
+ mfspr(SPRN_EBBRR);
+ mfspr(SPRN_BESCR);
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(close_clears_pmcc, "close_clears_pmcc");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c
new file mode 100644
index 0000000..d3ed64d
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests a pinned cpu event vs an EBB - in that order. The pinned cpu event
+ * should remain and the EBB event should fail to enable.
+ */
+
+static int setup_cpu_event(struct event *event, int cpu)
+{
+ event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+ event->attr.pinned = 1;
+
+ event->attr.exclude_kernel = 1;
+ event->attr.exclude_hv = 1;
+ event->attr.exclude_idle = 1;
+
+ SKIP_IF(require_paranoia_below(1));
+ FAIL_IF(event_open_with_cpu(event, cpu));
+ FAIL_IF(event_enable(event));
+
+ return 0;
+}
+
+int cpu_event_pinned_vs_ebb(void)
+{
+ union pipe read_pipe, write_pipe;
+ struct event event;
+ int cpu, rc;
+ pid_t pid;
+
+ cpu = pick_online_cpu();
+ FAIL_IF(cpu < 0);
+ FAIL_IF(bind_to_cpu(cpu));
+
+ FAIL_IF(pipe(read_pipe.fds) == -1);
+ FAIL_IF(pipe(write_pipe.fds) == -1);
+
+ pid = fork();
+ if (pid == 0) {
+ /* NB order of pipes looks reversed */
+ exit(ebb_child(write_pipe, read_pipe));
+ }
+
+ /* We setup the cpu event first */
+ rc = setup_cpu_event(&event, cpu);
+ if (rc) {
+ kill_child_and_wait(pid);
+ return rc;
+ }
+
+ /* Signal the child to install its EBB event and wait */
+ if (sync_with_child(read_pipe, write_pipe))
+ /* If it fails, wait for it to exit */
+ goto wait;
+
+ /* Signal the child to run */
+ FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+wait:
+ /* We expect it to fail to read the event */
+ FAIL_IF(wait_for_child(pid) != 2);
+
+ FAIL_IF(event_disable(&event));
+ FAIL_IF(event_read(&event));
+
+ event_report(&event);
+
+ /* The cpu event should have run */
+ FAIL_IF(event.result.value == 0);
+ FAIL_IF(event.result.enabled != event.result.running);
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(cpu_event_pinned_vs_ebb, "cpu_event_pinned_vs_ebb");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c
new file mode 100644
index 0000000..8b972c2
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests a cpu event vs an EBB - in that order. The EBB should force the cpu
+ * event off the PMU.
+ */
+
+static int setup_cpu_event(struct event *event, int cpu)
+{
+ event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+ event->attr.exclude_kernel = 1;
+ event->attr.exclude_hv = 1;
+ event->attr.exclude_idle = 1;
+
+ SKIP_IF(require_paranoia_below(1));
+ FAIL_IF(event_open_with_cpu(event, cpu));
+ FAIL_IF(event_enable(event));
+
+ return 0;
+}
+
+int cpu_event_vs_ebb(void)
+{
+ union pipe read_pipe, write_pipe;
+ struct event event;
+ int cpu, rc;
+ pid_t pid;
+
+ cpu = pick_online_cpu();
+ FAIL_IF(cpu < 0);
+ FAIL_IF(bind_to_cpu(cpu));
+
+ FAIL_IF(pipe(read_pipe.fds) == -1);
+ FAIL_IF(pipe(write_pipe.fds) == -1);
+
+ pid = fork();
+ if (pid == 0) {
+ /* NB order of pipes looks reversed */
+ exit(ebb_child(write_pipe, read_pipe));
+ }
+
+ /* We setup the cpu event first */
+ rc = setup_cpu_event(&event, cpu);
+ if (rc) {
+ kill_child_and_wait(pid);
+ return rc;
+ }
+
+ /* Signal the child to install its EBB event and wait */
+ if (sync_with_child(read_pipe, write_pipe))
+ /* If it fails, wait for it to exit */
+ goto wait;
+
+ /* Signal the child to run */
+ FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+wait:
+ /* We expect the child to succeed */
+ FAIL_IF(wait_for_child(pid));
+
+ FAIL_IF(event_disable(&event));
+ FAIL_IF(event_read(&event));
+
+ event_report(&event);
+
+ /* The cpu event may have run */
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(cpu_event_vs_ebb, "cpu_event_vs_ebb");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c
new file mode 100644
index 0000000..8590fc1
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+/*
+ * Basic test that counts user cycles and takes EBBs.
+ */
+int cycles(void)
+{
+ struct event event;
+
+ event_init_named(&event, 0x1001e, "cycles");
+ event_leader_ebb_init(&event);
+
+ event.attr.exclude_kernel = 1;
+ event.attr.exclude_hv = 1;
+ event.attr.exclude_idle = 1;
+
+ FAIL_IF(event_open(&event));
+
+ ebb_enable_pmc_counting(1);
+ setup_ebb_handler(standard_ebb_callee);
+ ebb_global_enable();
+ FAIL_IF(ebb_event_enable(&event));
+
+ mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+ while (ebb_state.stats.ebb_count < 10) {
+ FAIL_IF(core_busy_loop());
+ FAIL_IF(ebb_check_mmcr0());
+ }
+
+ ebb_global_disable();
+ ebb_freeze_pmcs();
+
+ count_pmc(1, sample_period);
+
+ dump_ebb_state();
+
+ event_close(&event);
+
+ FAIL_IF(ebb_state.stats.ebb_count == 0);
+ FAIL_IF(!ebb_check_count(1, sample_period, 100));
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(cycles, "cycles");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c
new file mode 100644
index 0000000..754b3f2
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test of counting cycles while using MMCR0_FC (freeze counters) to only count
+ * parts of the code. This is complicated by the fact that FC is set by the
+ * hardware when the event overflows. We may take the EBB after we have set FC,
+ * so we have to be careful about whether we clear FC at the end of the EBB
+ * handler or not.
+ */
+
+static bool counters_frozen = false;
+static int ebbs_while_frozen = 0;
+
+static void ebb_callee(void)
+{
+ uint64_t mask, val;
+
+ mask = MMCR0_PMAO | MMCR0_FC;
+
+ val = mfspr(SPRN_BESCR);
+ if (!(val & BESCR_PMEO)) {
+ ebb_state.stats.spurious++;
+ goto out;
+ }
+
+ ebb_state.stats.ebb_count++;
+ trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
+
+ val = mfspr(SPRN_MMCR0);
+ trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
+
+ if (counters_frozen) {
+ trace_log_string(ebb_state.trace, "frozen");
+ ebbs_while_frozen++;
+ mask &= ~MMCR0_FC;
+ }
+
+ count_pmc(1, sample_period);
+out:
+ reset_ebb_with_clear_mask(mask);
+}
+
+int cycles_with_freeze(void)
+{
+ struct event event;
+ uint64_t val;
+ bool fc_cleared;
+
+ event_init_named(&event, 0x1001e, "cycles");
+ event_leader_ebb_init(&event);
+
+ event.attr.exclude_kernel = 1;
+ event.attr.exclude_hv = 1;
+ event.attr.exclude_idle = 1;
+
+ FAIL_IF(event_open(&event));
+
+ setup_ebb_handler(ebb_callee);
+ ebb_global_enable();
+ FAIL_IF(ebb_event_enable(&event));
+
+ mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+ fc_cleared = false;
+
+ /* Make sure we loop until we take at least one EBB */
+ while ((ebb_state.stats.ebb_count < 20 && !fc_cleared) ||
+ ebb_state.stats.ebb_count < 1)
+ {
+ counters_frozen = false;
+ mb();
+ mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+
+ FAIL_IF(core_busy_loop());
+
+ counters_frozen = true;
+ mb();
+ mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
+
+ val = mfspr(SPRN_MMCR0);
+ if (! (val & MMCR0_FC)) {
+ printf("Outside of loop, FC NOT set MMCR0 0x%lx\n", val);
+ fc_cleared = true;
+ }
+ }
+
+ ebb_global_disable();
+ ebb_freeze_pmcs();
+
+ count_pmc(1, sample_period);
+
+ dump_ebb_state();
+
+ printf("EBBs while frozen %d\n", ebbs_while_frozen);
+
+ event_close(&event);
+
+ FAIL_IF(ebb_state.stats.ebb_count == 0);
+ FAIL_IF(fc_cleared);
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(cycles_with_freeze, "cycles_with_freeze");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb.c
new file mode 100644
index 0000000..1b46be9
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb.c
@@ -0,0 +1,727 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#define _GNU_SOURCE /* For CPU_ZERO etc. */
+
+#include <sched.h>
+#include <sys/wait.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include "trace.h"
+#include "reg.h"
+#include "ebb.h"
+
+
+void (*ebb_user_func)(void);
+
+void ebb_hook(void)
+{
+ if (ebb_user_func)
+ ebb_user_func();
+}
+
+struct ebb_state ebb_state;
+
+u64 sample_period = 0x40000000ull;
+
+void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask)
+{
+ u64 val;
+
+ /* 2) clear MMCR0[PMAO] - docs say BESCR[PMEO] should do this */
+ /* 3) set MMCR0[PMAE] - docs say BESCR[PME] should do this */
+ val = mfspr(SPRN_MMCR0);
+ mtspr(SPRN_MMCR0, (val & ~mmcr0_clear_mask) | MMCR0_PMAE);
+
+ /* 4) clear BESCR[PMEO] */
+ mtspr(SPRN_BESCRR, BESCR_PMEO);
+
+ /* 5) set BESCR[PME] */
+ mtspr(SPRN_BESCRS, BESCR_PME);
+
+ /* 6) rfebb 1 - done in our caller */
+}
+
+void reset_ebb(void)
+{
+ reset_ebb_with_clear_mask(MMCR0_PMAO | MMCR0_FC);
+}
+
+/* Called outside of the EBB handler to check MMCR0 is sane */
+int ebb_check_mmcr0(void)
+{
+ u64 val;
+
+ val = mfspr(SPRN_MMCR0);
+ if ((val & (MMCR0_FC | MMCR0_PMAO)) == MMCR0_FC) {
+ /* It's OK if we see FC & PMAO, but not FC by itself */
+ printf("Outside of loop, only FC set 0x%llx\n", val);
+ return 1;
+ }
+
+ return 0;
+}
+
+bool ebb_check_count(int pmc, u64 sample_period, int fudge)
+{
+ u64 count, upper, lower;
+
+ count = ebb_state.stats.pmc_count[PMC_INDEX(pmc)];
+
+ lower = ebb_state.stats.ebb_count * (sample_period - fudge);
+
+ if (count < lower) {
+ printf("PMC%d count (0x%llx) below lower limit 0x%llx (-0x%llx)\n",
+ pmc, count, lower, lower - count);
+ return false;
+ }
+
+ upper = ebb_state.stats.ebb_count * (sample_period + fudge);
+
+ if (count > upper) {
+ printf("PMC%d count (0x%llx) above upper limit 0x%llx (+0x%llx)\n",
+ pmc, count, upper, count - upper);
+ return false;
+ }
+
+ printf("PMC%d count (0x%llx) is between 0x%llx and 0x%llx delta +0x%llx/-0x%llx\n",
+ pmc, count, lower, upper, count - lower, upper - count);
+
+ return true;
+}
+
+void standard_ebb_callee(void)
+{
+ int found, i;
+ u64 val;
+
+ val = mfspr(SPRN_BESCR);
+ if (!(val & BESCR_PMEO)) {
+ ebb_state.stats.spurious++;
+ goto out;
+ }
+
+ ebb_state.stats.ebb_count++;
+ trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
+
+ val = mfspr(SPRN_MMCR0);
+ trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
+
+ found = 0;
+ for (i = 1; i <= 6; i++) {
+ if (ebb_state.pmc_enable[PMC_INDEX(i)])
+ found += count_pmc(i, sample_period);
+ }
+
+ if (!found)
+ ebb_state.stats.no_overflow++;
+
+out:
+ reset_ebb();
+}
+
+extern void ebb_handler(void);
+
+void setup_ebb_handler(void (*callee)(void))
+{
+ u64 entry;
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+ entry = (u64)ebb_handler;
+#else
+ struct opd
+ {
+ u64 entry;
+ u64 toc;
+ } *opd;
+
+ opd = (struct opd *)ebb_handler;
+ entry = opd->entry;
+#endif
+ printf("EBB Handler is at %#llx\n", entry);
+
+ ebb_user_func = callee;
+
+ /* Ensure ebb_user_func is set before we set the handler */
+ mb();
+ mtspr(SPRN_EBBHR, entry);
+
+ /* Make sure the handler is set before we return */
+ mb();
+}
+
+void clear_ebb_stats(void)
+{
+ memset(&ebb_state.stats, 0, sizeof(ebb_state.stats));
+}
+
+void dump_summary_ebb_state(void)
+{
+ printf("ebb_state:\n" \
+ " ebb_count = %d\n" \
+ " spurious = %d\n" \
+ " negative = %d\n" \
+ " no_overflow = %d\n" \
+ " pmc[1] count = 0x%llx\n" \
+ " pmc[2] count = 0x%llx\n" \
+ " pmc[3] count = 0x%llx\n" \
+ " pmc[4] count = 0x%llx\n" \
+ " pmc[5] count = 0x%llx\n" \
+ " pmc[6] count = 0x%llx\n",
+ ebb_state.stats.ebb_count, ebb_state.stats.spurious,
+ ebb_state.stats.negative, ebb_state.stats.no_overflow,
+ ebb_state.stats.pmc_count[0], ebb_state.stats.pmc_count[1],
+ ebb_state.stats.pmc_count[2], ebb_state.stats.pmc_count[3],
+ ebb_state.stats.pmc_count[4], ebb_state.stats.pmc_count[5]);
+}
+
+static char *decode_mmcr0(u32 value)
+{
+ static char buf[16];
+
+ buf[0] = '\0';
+
+ if (value & (1 << 31))
+ strcat(buf, "FC ");
+ if (value & (1 << 26))
+ strcat(buf, "PMAE ");
+ if (value & (1 << 7))
+ strcat(buf, "PMAO ");
+
+ return buf;
+}
+
+static char *decode_bescr(u64 value)
+{
+ static char buf[16];
+
+ buf[0] = '\0';
+
+ if (value & (1ull << 63))
+ strcat(buf, "GE ");
+ if (value & (1ull << 32))
+ strcat(buf, "PMAE ");
+ if (value & 1)
+ strcat(buf, "PMAO ");
+
+ return buf;
+}
+
+void dump_ebb_hw_state(void)
+{
+ u64 bescr;
+ u32 mmcr0;
+
+ mmcr0 = mfspr(SPRN_MMCR0);
+ bescr = mfspr(SPRN_BESCR);
+
+ printf("HW state:\n" \
+ "MMCR0 0x%016x %s\n" \
+ "EBBHR 0x%016lx\n" \
+ "BESCR 0x%016llx %s\n" \
+ "PMC1 0x%016lx\n" \
+ "PMC2 0x%016lx\n" \
+ "PMC3 0x%016lx\n" \
+ "PMC4 0x%016lx\n" \
+ "PMC5 0x%016lx\n" \
+ "PMC6 0x%016lx\n" \
+ "SIAR 0x%016lx\n",
+ mmcr0, decode_mmcr0(mmcr0), mfspr(SPRN_EBBHR), bescr,
+ decode_bescr(bescr), mfspr(SPRN_PMC1), mfspr(SPRN_PMC2),
+ mfspr(SPRN_PMC3), mfspr(SPRN_PMC4), mfspr(SPRN_PMC5),
+ mfspr(SPRN_PMC6), mfspr(SPRN_SIAR));
+}
+
+void dump_ebb_state(void)
+{
+ dump_summary_ebb_state();
+
+ dump_ebb_hw_state();
+
+ trace_buffer_print(ebb_state.trace);
+}
+
+int count_pmc(int pmc, uint32_t sample_period)
+{
+ uint32_t start_value;
+ u64 val;
+
+ /* 0) Read PMC */
+ start_value = pmc_sample_period(sample_period);
+
+ val = read_pmc(pmc);
+ if (val < start_value)
+ ebb_state.stats.negative++;
+ else
+ ebb_state.stats.pmc_count[PMC_INDEX(pmc)] += val - start_value;
+
+ trace_log_reg(ebb_state.trace, SPRN_PMC1 + pmc - 1, val);
+
+ /* 1) Reset PMC */
+ write_pmc(pmc, start_value);
+
+ /* Report if we overflowed */
+ return val >= COUNTER_OVERFLOW;
+}
+
+int ebb_event_enable(struct event *e)
+{
+ int rc;
+
+ /* Ensure any SPR writes are ordered vs us */
+ mb();
+
+ rc = ioctl(e->fd, PERF_EVENT_IOC_ENABLE);
+ if (rc)
+ return rc;
+
+ rc = event_read(e);
+
+ /* Ditto */
+ mb();
+
+ return rc;
+}
+
+void ebb_freeze_pmcs(void)
+{
+ mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
+ mb();
+}
+
+void ebb_unfreeze_pmcs(void)
+{
+ /* Unfreeze counters */
+ mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+ mb();
+}
+
+void ebb_global_enable(void)
+{
+ /* Enable EBBs globally and PMU EBBs */
+ mtspr(SPRN_BESCR, 0x8000000100000000ull);
+ mb();
+}
+
+void ebb_global_disable(void)
+{
+ /* Disable EBBs & freeze counters, events are still scheduled */
+ mtspr(SPRN_BESCRR, BESCR_PME);
+ mb();
+}
+
+void event_ebb_init(struct event *e)
+{
+ e->attr.config |= (1ull << 63);
+}
+
+void event_bhrb_init(struct event *e, unsigned ifm)
+{
+ e->attr.config |= (1ull << 62) | ((u64)ifm << 60);
+}
+
+void event_leader_ebb_init(struct event *e)
+{
+ event_ebb_init(e);
+
+ e->attr.exclusive = 1;
+ e->attr.pinned = 1;
+}
+
+int core_busy_loop(void)
+{
+ int rc;
+
+ asm volatile (
+ "li 3, 0x3030\n"
+ "std 3, -96(1)\n"
+ "li 4, 0x4040\n"
+ "std 4, -104(1)\n"
+ "li 5, 0x5050\n"
+ "std 5, -112(1)\n"
+ "li 6, 0x6060\n"
+ "std 6, -120(1)\n"
+ "li 7, 0x7070\n"
+ "std 7, -128(1)\n"
+ "li 8, 0x0808\n"
+ "std 8, -136(1)\n"
+ "li 9, 0x0909\n"
+ "std 9, -144(1)\n"
+ "li 10, 0x1010\n"
+ "std 10, -152(1)\n"
+ "li 11, 0x1111\n"
+ "std 11, -160(1)\n"
+ "li 14, 0x1414\n"
+ "std 14, -168(1)\n"
+ "li 15, 0x1515\n"
+ "std 15, -176(1)\n"
+ "li 16, 0x1616\n"
+ "std 16, -184(1)\n"
+ "li 17, 0x1717\n"
+ "std 17, -192(1)\n"
+ "li 18, 0x1818\n"
+ "std 18, -200(1)\n"
+ "li 19, 0x1919\n"
+ "std 19, -208(1)\n"
+ "li 20, 0x2020\n"
+ "std 20, -216(1)\n"
+ "li 21, 0x2121\n"
+ "std 21, -224(1)\n"
+ "li 22, 0x2222\n"
+ "std 22, -232(1)\n"
+ "li 23, 0x2323\n"
+ "std 23, -240(1)\n"
+ "li 24, 0x2424\n"
+ "std 24, -248(1)\n"
+ "li 25, 0x2525\n"
+ "std 25, -256(1)\n"
+ "li 26, 0x2626\n"
+ "std 26, -264(1)\n"
+ "li 27, 0x2727\n"
+ "std 27, -272(1)\n"
+ "li 28, 0x2828\n"
+ "std 28, -280(1)\n"
+ "li 29, 0x2929\n"
+ "std 29, -288(1)\n"
+ "li 30, 0x3030\n"
+ "li 31, 0x3131\n"
+
+ "li 3, 0\n"
+ "0: "
+ "addi 3, 3, 1\n"
+ "cmpwi 3, 100\n"
+ "blt 0b\n"
+
+ /* Return 1 (fail) unless we get through all the checks */
+ "li 0, 1\n"
+
+ /* Check none of our registers have been corrupted */
+ "cmpwi 4, 0x4040\n"
+ "bne 1f\n"
+ "cmpwi 5, 0x5050\n"
+ "bne 1f\n"
+ "cmpwi 6, 0x6060\n"
+ "bne 1f\n"
+ "cmpwi 7, 0x7070\n"
+ "bne 1f\n"
+ "cmpwi 8, 0x0808\n"
+ "bne 1f\n"
+ "cmpwi 9, 0x0909\n"
+ "bne 1f\n"
+ "cmpwi 10, 0x1010\n"
+ "bne 1f\n"
+ "cmpwi 11, 0x1111\n"
+ "bne 1f\n"
+ "cmpwi 14, 0x1414\n"
+ "bne 1f\n"
+ "cmpwi 15, 0x1515\n"
+ "bne 1f\n"
+ "cmpwi 16, 0x1616\n"
+ "bne 1f\n"
+ "cmpwi 17, 0x1717\n"
+ "bne 1f\n"
+ "cmpwi 18, 0x1818\n"
+ "bne 1f\n"
+ "cmpwi 19, 0x1919\n"
+ "bne 1f\n"
+ "cmpwi 20, 0x2020\n"
+ "bne 1f\n"
+ "cmpwi 21, 0x2121\n"
+ "bne 1f\n"
+ "cmpwi 22, 0x2222\n"
+ "bne 1f\n"
+ "cmpwi 23, 0x2323\n"
+ "bne 1f\n"
+ "cmpwi 24, 0x2424\n"
+ "bne 1f\n"
+ "cmpwi 25, 0x2525\n"
+ "bne 1f\n"
+ "cmpwi 26, 0x2626\n"
+ "bne 1f\n"
+ "cmpwi 27, 0x2727\n"
+ "bne 1f\n"
+ "cmpwi 28, 0x2828\n"
+ "bne 1f\n"
+ "cmpwi 29, 0x2929\n"
+ "bne 1f\n"
+ "cmpwi 30, 0x3030\n"
+ "bne 1f\n"
+ "cmpwi 31, 0x3131\n"
+ "bne 1f\n"
+
+ /* Load junk into all our registers before we reload them from the stack. */
+ "li 3, 0xde\n"
+ "li 4, 0xad\n"
+ "li 5, 0xbe\n"
+ "li 6, 0xef\n"
+ "li 7, 0xde\n"
+ "li 8, 0xad\n"
+ "li 9, 0xbe\n"
+ "li 10, 0xef\n"
+ "li 11, 0xde\n"
+ "li 14, 0xad\n"
+ "li 15, 0xbe\n"
+ "li 16, 0xef\n"
+ "li 17, 0xde\n"
+ "li 18, 0xad\n"
+ "li 19, 0xbe\n"
+ "li 20, 0xef\n"
+ "li 21, 0xde\n"
+ "li 22, 0xad\n"
+ "li 23, 0xbe\n"
+ "li 24, 0xef\n"
+ "li 25, 0xde\n"
+ "li 26, 0xad\n"
+ "li 27, 0xbe\n"
+ "li 28, 0xef\n"
+ "li 29, 0xdd\n"
+
+ "ld 3, -96(1)\n"
+ "cmpwi 3, 0x3030\n"
+ "bne 1f\n"
+ "ld 4, -104(1)\n"
+ "cmpwi 4, 0x4040\n"
+ "bne 1f\n"
+ "ld 5, -112(1)\n"
+ "cmpwi 5, 0x5050\n"
+ "bne 1f\n"
+ "ld 6, -120(1)\n"
+ "cmpwi 6, 0x6060\n"
+ "bne 1f\n"
+ "ld 7, -128(1)\n"
+ "cmpwi 7, 0x7070\n"
+ "bne 1f\n"
+ "ld 8, -136(1)\n"
+ "cmpwi 8, 0x0808\n"
+ "bne 1f\n"
+ "ld 9, -144(1)\n"
+ "cmpwi 9, 0x0909\n"
+ "bne 1f\n"
+ "ld 10, -152(1)\n"
+ "cmpwi 10, 0x1010\n"
+ "bne 1f\n"
+ "ld 11, -160(1)\n"
+ "cmpwi 11, 0x1111\n"
+ "bne 1f\n"
+ "ld 14, -168(1)\n"
+ "cmpwi 14, 0x1414\n"
+ "bne 1f\n"
+ "ld 15, -176(1)\n"
+ "cmpwi 15, 0x1515\n"
+ "bne 1f\n"
+ "ld 16, -184(1)\n"
+ "cmpwi 16, 0x1616\n"
+ "bne 1f\n"
+ "ld 17, -192(1)\n"
+ "cmpwi 17, 0x1717\n"
+ "bne 1f\n"
+ "ld 18, -200(1)\n"
+ "cmpwi 18, 0x1818\n"
+ "bne 1f\n"
+ "ld 19, -208(1)\n"
+ "cmpwi 19, 0x1919\n"
+ "bne 1f\n"
+ "ld 20, -216(1)\n"
+ "cmpwi 20, 0x2020\n"
+ "bne 1f\n"
+ "ld 21, -224(1)\n"
+ "cmpwi 21, 0x2121\n"
+ "bne 1f\n"
+ "ld 22, -232(1)\n"
+ "cmpwi 22, 0x2222\n"
+ "bne 1f\n"
+ "ld 23, -240(1)\n"
+ "cmpwi 23, 0x2323\n"
+ "bne 1f\n"
+ "ld 24, -248(1)\n"
+ "cmpwi 24, 0x2424\n"
+ "bne 1f\n"
+ "ld 25, -256(1)\n"
+ "cmpwi 25, 0x2525\n"
+ "bne 1f\n"
+ "ld 26, -264(1)\n"
+ "cmpwi 26, 0x2626\n"
+ "bne 1f\n"
+ "ld 27, -272(1)\n"
+ "cmpwi 27, 0x2727\n"
+ "bne 1f\n"
+ "ld 28, -280(1)\n"
+ "cmpwi 28, 0x2828\n"
+ "bne 1f\n"
+ "ld 29, -288(1)\n"
+ "cmpwi 29, 0x2929\n"
+ "bne 1f\n"
+
+ /* Load 0 (success) to return */
+ "li 0, 0\n"
+
+ "1: mr %0, 0\n"
+
+ : "=r" (rc)
+ : /* no inputs */
+ : "3", "4", "5", "6", "7", "8", "9", "10", "11", "14",
+ "15", "16", "17", "18", "19", "20", "21", "22", "23",
+ "24", "25", "26", "27", "28", "29", "30", "31",
+ "memory"
+ );
+
+ return rc;
+}
+
+int core_busy_loop_with_freeze(void)
+{
+ int rc;
+
+ mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+ rc = core_busy_loop();
+ mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
+
+ return rc;
+}
+
+int ebb_child(union pipe read_pipe, union pipe write_pipe)
+{
+ struct event event;
+ uint64_t val;
+
+ FAIL_IF(wait_for_parent(read_pipe));
+
+ event_init_named(&event, 0x1001e, "cycles");
+ event_leader_ebb_init(&event);
+
+ event.attr.exclude_kernel = 1;
+ event.attr.exclude_hv = 1;
+ event.attr.exclude_idle = 1;
+
+ FAIL_IF(event_open(&event));
+
+ ebb_enable_pmc_counting(1);
+ setup_ebb_handler(standard_ebb_callee);
+ ebb_global_enable();
+
+ FAIL_IF(event_enable(&event));
+
+ if (event_read(&event)) {
+ /*
+ * Some tests expect to fail here, so don't report an error on
+ * this line, and return a distinguisable error code. Tell the
+ * parent an error happened.
+ */
+ notify_parent_of_error(write_pipe);
+ return 2;
+ }
+
+ mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+ FAIL_IF(notify_parent(write_pipe));
+ FAIL_IF(wait_for_parent(read_pipe));
+ FAIL_IF(notify_parent(write_pipe));
+
+ while (ebb_state.stats.ebb_count < 20) {
+ FAIL_IF(core_busy_loop());
+
+ /* To try and hit SIGILL case */
+ val = mfspr(SPRN_MMCRA);
+ val |= mfspr(SPRN_MMCR2);
+ val |= mfspr(SPRN_MMCR0);
+ }
+
+ ebb_global_disable();
+ ebb_freeze_pmcs();
+
+ count_pmc(1, sample_period);
+
+ dump_ebb_state();
+
+ event_close(&event);
+
+ FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+ return 0;
+}
+
+static jmp_buf setjmp_env;
+
+static void sigill_handler(int signal)
+{
+ printf("Took sigill\n");
+ longjmp(setjmp_env, 1);
+}
+
+static struct sigaction sigill_action = {
+ .sa_handler = sigill_handler,
+};
+
+int catch_sigill(void (*func)(void))
+{
+ if (sigaction(SIGILL, &sigill_action, NULL)) {
+ perror("sigaction");
+ return 1;
+ }
+
+ if (setjmp(setjmp_env) == 0) {
+ func();
+ return 1;
+ }
+
+ return 0;
+}
+
+void write_pmc1(void)
+{
+ mtspr(SPRN_PMC1, 0);
+}
+
+void write_pmc(int pmc, u64 value)
+{
+ switch (pmc) {
+ case 1: mtspr(SPRN_PMC1, value); break;
+ case 2: mtspr(SPRN_PMC2, value); break;
+ case 3: mtspr(SPRN_PMC3, value); break;
+ case 4: mtspr(SPRN_PMC4, value); break;
+ case 5: mtspr(SPRN_PMC5, value); break;
+ case 6: mtspr(SPRN_PMC6, value); break;
+ }
+}
+
+u64 read_pmc(int pmc)
+{
+ switch (pmc) {
+ case 1: return mfspr(SPRN_PMC1);
+ case 2: return mfspr(SPRN_PMC2);
+ case 3: return mfspr(SPRN_PMC3);
+ case 4: return mfspr(SPRN_PMC4);
+ case 5: return mfspr(SPRN_PMC5);
+ case 6: return mfspr(SPRN_PMC6);
+ }
+
+ return 0;
+}
+
+static void term_handler(int signal)
+{
+ dump_summary_ebb_state();
+ dump_ebb_hw_state();
+ abort();
+}
+
+struct sigaction term_action = {
+ .sa_handler = term_handler,
+};
+
+static void __attribute__((constructor)) ebb_init(void)
+{
+ clear_ebb_stats();
+
+ if (sigaction(SIGTERM, &term_action, NULL))
+ perror("sigaction");
+
+ ebb_state.trace = trace_buffer_allocate(1 * 1024 * 1024);
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb.h b/tools/testing/selftests/powerpc/pmu/ebb/ebb.h
new file mode 100644
index 0000000..e62bde0
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#ifndef _SELFTESTS_POWERPC_PMU_EBB_EBB_H
+#define _SELFTESTS_POWERPC_PMU_EBB_EBB_H
+
+#include "../event.h"
+#include "../lib.h"
+#include "trace.h"
+#include "reg.h"
+
+#define PMC_INDEX(pmc) ((pmc)-1)
+
+#define NUM_PMC_VALUES 128
+
+struct ebb_state
+{
+ struct {
+ u64 pmc_count[6];
+ volatile int ebb_count;
+ int spurious;
+ int negative;
+ int no_overflow;
+ } stats;
+
+ bool pmc_enable[6];
+ struct trace_buffer *trace;
+};
+
+extern struct ebb_state ebb_state;
+
+#define COUNTER_OVERFLOW 0x80000000ull
+
+static inline uint32_t pmc_sample_period(uint32_t value)
+{
+ return COUNTER_OVERFLOW - value;
+}
+
+static inline void ebb_enable_pmc_counting(int pmc)
+{
+ ebb_state.pmc_enable[PMC_INDEX(pmc)] = true;
+}
+
+bool ebb_check_count(int pmc, u64 sample_period, int fudge);
+void event_leader_ebb_init(struct event *e);
+void event_ebb_init(struct event *e);
+void event_bhrb_init(struct event *e, unsigned ifm);
+void setup_ebb_handler(void (*callee)(void));
+void standard_ebb_callee(void);
+int ebb_event_enable(struct event *e);
+void ebb_global_enable(void);
+void ebb_global_disable(void);
+void ebb_freeze_pmcs(void);
+void ebb_unfreeze_pmcs(void);
+void event_ebb_init(struct event *e);
+void event_leader_ebb_init(struct event *e);
+int count_pmc(int pmc, uint32_t sample_period);
+void dump_ebb_state(void);
+void dump_summary_ebb_state(void);
+void dump_ebb_hw_state(void);
+void clear_ebb_stats(void);
+void write_pmc(int pmc, u64 value);
+u64 read_pmc(int pmc);
+void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask);
+void reset_ebb(void);
+int ebb_check_mmcr0(void);
+
+extern u64 sample_period;
+
+int core_busy_loop(void);
+int core_busy_loop_with_freeze(void);
+int ebb_child(union pipe read_pipe, union pipe write_pipe);
+int catch_sigill(void (*func)(void));
+void write_pmc1(void);
+
+#endif /* _SELFTESTS_POWERPC_PMU_EBB_EBB_H */
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S b/tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S
new file mode 100644
index 0000000..14274ea
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <ppc-asm.h>
+#include "reg.h"
+
+
+/* ppc-asm.h defines most of the reg aliases, but not r1/r2. */
+#define r1 1
+#define r2 2
+
+#define RFEBB .long 0x4c000924
+
+/* Stack layout:
+ *
+ * ^
+ * User stack |
+ * Back chain ------+ <- r1 <-------+
+ * ... |
+ * Red zone / ABI Gap |
+ * ... |
+ * vr63 <+ |
+ * vr0 | |
+ * VSCR | |
+ * FSCR | |
+ * r31 | Save area |
+ * r0 | |
+ * XER | |
+ * CTR | |
+ * LR | |
+ * CCR <+ |
+ * ... <+ |
+ * LR | Caller frame |
+ * CCR | |
+ * Back chain <+ <- updated r1 --------+
+ *
+ */
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+#define ABIGAP 512
+#else
+#define ABIGAP 288
+#endif
+
+#define NR_GPR 32
+#define NR_SPR 6
+#define NR_VSR 64
+
+#define SAVE_AREA ((NR_GPR + NR_SPR) * 8 + (NR_VSR * 16))
+#define CALLER_FRAME 112
+
+#define STACK_FRAME (ABIGAP + SAVE_AREA + CALLER_FRAME)
+
+#define CCR_SAVE (CALLER_FRAME)
+#define LR_SAVE (CCR_SAVE + 8)
+#define CTR_SAVE (LR_SAVE + 8)
+#define XER_SAVE (CTR_SAVE + 8)
+#define GPR_SAVE(n) (XER_SAVE + 8 + (8 * n))
+#define FSCR_SAVE (GPR_SAVE(31) + 8)
+#define VSCR_SAVE (FSCR_SAVE + 8)
+#define VSR_SAVE(n) (VSCR_SAVE + 8 + (16 * n))
+
+#define SAVE_GPR(n) std n,GPR_SAVE(n)(r1)
+#define REST_GPR(n) ld n,GPR_SAVE(n)(r1)
+#define TRASH_GPR(n) lis n,0xaaaa
+
+#define SAVE_VSR(n, b) li b, VSR_SAVE(n); stxvd2x n,b,r1
+#define LOAD_VSR(n, b) li b, VSR_SAVE(n); lxvd2x n,b,r1
+
+#define LOAD_REG_IMMEDIATE(reg,expr) \
+ lis reg,(expr)@highest; \
+ ori reg,reg,(expr)@higher; \
+ rldicr reg,reg,32,31; \
+ oris reg,reg,(expr)@h; \
+ ori reg,reg,(expr)@l;
+
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+#define ENTRY_POINT(name) \
+ .type FUNC_NAME(name),@function; \
+ .globl FUNC_NAME(name); \
+ FUNC_NAME(name):
+
+#define RESTORE_TOC(name) \
+ /* Restore our TOC pointer using our entry point */ \
+ LOAD_REG_IMMEDIATE(r12, name) \
+0: addis r2,r12,(.TOC.-0b)@ha; \
+ addi r2,r2,(.TOC.-0b)@l;
+
+#else
+#define ENTRY_POINT(name) FUNC_START(name)
+#define RESTORE_TOC(name) \
+ /* Restore our TOC pointer via our opd entry */ \
+ LOAD_REG_IMMEDIATE(r2, name) \
+ ld r2,8(r2);
+#endif
+
+ .text
+
+ENTRY_POINT(ebb_handler)
+ stdu r1,-STACK_FRAME(r1)
+ SAVE_GPR(0)
+ mflr r0
+ std r0,LR_SAVE(r1)
+ mfcr r0
+ std r0,CCR_SAVE(r1)
+ mfctr r0
+ std r0,CTR_SAVE(r1)
+ mfxer r0
+ std r0,XER_SAVE(r1)
+ SAVE_GPR(2)
+ SAVE_GPR(3)
+ SAVE_GPR(4)
+ SAVE_GPR(5)
+ SAVE_GPR(6)
+ SAVE_GPR(7)
+ SAVE_GPR(8)
+ SAVE_GPR(9)
+ SAVE_GPR(10)
+ SAVE_GPR(11)
+ SAVE_GPR(12)
+ SAVE_GPR(13)
+ SAVE_GPR(14)
+ SAVE_GPR(15)
+ SAVE_GPR(16)
+ SAVE_GPR(17)
+ SAVE_GPR(18)
+ SAVE_GPR(19)
+ SAVE_GPR(20)
+ SAVE_GPR(21)
+ SAVE_GPR(22)
+ SAVE_GPR(23)
+ SAVE_GPR(24)
+ SAVE_GPR(25)
+ SAVE_GPR(26)
+ SAVE_GPR(27)
+ SAVE_GPR(28)
+ SAVE_GPR(29)
+ SAVE_GPR(30)
+ SAVE_GPR(31)
+ SAVE_VSR(0, r3)
+ mffs f0
+ stfd f0, FSCR_SAVE(r1)
+ mfvscr f0
+ stfd f0, VSCR_SAVE(r1)
+ SAVE_VSR(1, r3)
+ SAVE_VSR(2, r3)
+ SAVE_VSR(3, r3)
+ SAVE_VSR(4, r3)
+ SAVE_VSR(5, r3)
+ SAVE_VSR(6, r3)
+ SAVE_VSR(7, r3)
+ SAVE_VSR(8, r3)
+ SAVE_VSR(9, r3)
+ SAVE_VSR(10, r3)
+ SAVE_VSR(11, r3)
+ SAVE_VSR(12, r3)
+ SAVE_VSR(13, r3)
+ SAVE_VSR(14, r3)
+ SAVE_VSR(15, r3)
+ SAVE_VSR(16, r3)
+ SAVE_VSR(17, r3)
+ SAVE_VSR(18, r3)
+ SAVE_VSR(19, r3)
+ SAVE_VSR(20, r3)
+ SAVE_VSR(21, r3)
+ SAVE_VSR(22, r3)
+ SAVE_VSR(23, r3)
+ SAVE_VSR(24, r3)
+ SAVE_VSR(25, r3)
+ SAVE_VSR(26, r3)
+ SAVE_VSR(27, r3)
+ SAVE_VSR(28, r3)
+ SAVE_VSR(29, r3)
+ SAVE_VSR(30, r3)
+ SAVE_VSR(31, r3)
+ SAVE_VSR(32, r3)
+ SAVE_VSR(33, r3)
+ SAVE_VSR(34, r3)
+ SAVE_VSR(35, r3)
+ SAVE_VSR(36, r3)
+ SAVE_VSR(37, r3)
+ SAVE_VSR(38, r3)
+ SAVE_VSR(39, r3)
+ SAVE_VSR(40, r3)
+ SAVE_VSR(41, r3)
+ SAVE_VSR(42, r3)
+ SAVE_VSR(43, r3)
+ SAVE_VSR(44, r3)
+ SAVE_VSR(45, r3)
+ SAVE_VSR(46, r3)
+ SAVE_VSR(47, r3)
+ SAVE_VSR(48, r3)
+ SAVE_VSR(49, r3)
+ SAVE_VSR(50, r3)
+ SAVE_VSR(51, r3)
+ SAVE_VSR(52, r3)
+ SAVE_VSR(53, r3)
+ SAVE_VSR(54, r3)
+ SAVE_VSR(55, r3)
+ SAVE_VSR(56, r3)
+ SAVE_VSR(57, r3)
+ SAVE_VSR(58, r3)
+ SAVE_VSR(59, r3)
+ SAVE_VSR(60, r3)
+ SAVE_VSR(61, r3)
+ SAVE_VSR(62, r3)
+ SAVE_VSR(63, r3)
+
+ TRASH_GPR(2)
+ TRASH_GPR(3)
+ TRASH_GPR(4)
+ TRASH_GPR(5)
+ TRASH_GPR(6)
+ TRASH_GPR(7)
+ TRASH_GPR(8)
+ TRASH_GPR(9)
+ TRASH_GPR(10)
+ TRASH_GPR(11)
+ TRASH_GPR(12)
+ TRASH_GPR(14)
+ TRASH_GPR(15)
+ TRASH_GPR(16)
+ TRASH_GPR(17)
+ TRASH_GPR(18)
+ TRASH_GPR(19)
+ TRASH_GPR(20)
+ TRASH_GPR(21)
+ TRASH_GPR(22)
+ TRASH_GPR(23)
+ TRASH_GPR(24)
+ TRASH_GPR(25)
+ TRASH_GPR(26)
+ TRASH_GPR(27)
+ TRASH_GPR(28)
+ TRASH_GPR(29)
+ TRASH_GPR(30)
+ TRASH_GPR(31)
+
+ RESTORE_TOC(ebb_handler)
+
+ /*
+ * r13 is our TLS pointer. We leave whatever value was in there when the
+ * EBB fired. That seems to be OK because once set the TLS pointer is not
+ * changed - but presumably that could change in future.
+ */
+
+ bl ebb_hook
+ nop
+
+ /* r2 may be changed here but we don't care */
+
+ lfd f0, FSCR_SAVE(r1)
+ mtfsf 0xff,f0
+ lfd f0, VSCR_SAVE(r1)
+ mtvscr f0
+ LOAD_VSR(0, r3)
+ LOAD_VSR(1, r3)
+ LOAD_VSR(2, r3)
+ LOAD_VSR(3, r3)
+ LOAD_VSR(4, r3)
+ LOAD_VSR(5, r3)
+ LOAD_VSR(6, r3)
+ LOAD_VSR(7, r3)
+ LOAD_VSR(8, r3)
+ LOAD_VSR(9, r3)
+ LOAD_VSR(10, r3)
+ LOAD_VSR(11, r3)
+ LOAD_VSR(12, r3)
+ LOAD_VSR(13, r3)
+ LOAD_VSR(14, r3)
+ LOAD_VSR(15, r3)
+ LOAD_VSR(16, r3)
+ LOAD_VSR(17, r3)
+ LOAD_VSR(18, r3)
+ LOAD_VSR(19, r3)
+ LOAD_VSR(20, r3)
+ LOAD_VSR(21, r3)
+ LOAD_VSR(22, r3)
+ LOAD_VSR(23, r3)
+ LOAD_VSR(24, r3)
+ LOAD_VSR(25, r3)
+ LOAD_VSR(26, r3)
+ LOAD_VSR(27, r3)
+ LOAD_VSR(28, r3)
+ LOAD_VSR(29, r3)
+ LOAD_VSR(30, r3)
+ LOAD_VSR(31, r3)
+ LOAD_VSR(32, r3)
+ LOAD_VSR(33, r3)
+ LOAD_VSR(34, r3)
+ LOAD_VSR(35, r3)
+ LOAD_VSR(36, r3)
+ LOAD_VSR(37, r3)
+ LOAD_VSR(38, r3)
+ LOAD_VSR(39, r3)
+ LOAD_VSR(40, r3)
+ LOAD_VSR(41, r3)
+ LOAD_VSR(42, r3)
+ LOAD_VSR(43, r3)
+ LOAD_VSR(44, r3)
+ LOAD_VSR(45, r3)
+ LOAD_VSR(46, r3)
+ LOAD_VSR(47, r3)
+ LOAD_VSR(48, r3)
+ LOAD_VSR(49, r3)
+ LOAD_VSR(50, r3)
+ LOAD_VSR(51, r3)
+ LOAD_VSR(52, r3)
+ LOAD_VSR(53, r3)
+ LOAD_VSR(54, r3)
+ LOAD_VSR(55, r3)
+ LOAD_VSR(56, r3)
+ LOAD_VSR(57, r3)
+ LOAD_VSR(58, r3)
+ LOAD_VSR(59, r3)
+ LOAD_VSR(60, r3)
+ LOAD_VSR(61, r3)
+ LOAD_VSR(62, r3)
+ LOAD_VSR(63, r3)
+
+ ld r0,XER_SAVE(r1)
+ mtxer r0
+ ld r0,CTR_SAVE(r1)
+ mtctr r0
+ ld r0,LR_SAVE(r1)
+ mtlr r0
+ ld r0,CCR_SAVE(r1)
+ mtcr r0
+ REST_GPR(0)
+ REST_GPR(2)
+ REST_GPR(3)
+ REST_GPR(4)
+ REST_GPR(5)
+ REST_GPR(6)
+ REST_GPR(7)
+ REST_GPR(8)
+ REST_GPR(9)
+ REST_GPR(10)
+ REST_GPR(11)
+ REST_GPR(12)
+ REST_GPR(13)
+ REST_GPR(14)
+ REST_GPR(15)
+ REST_GPR(16)
+ REST_GPR(17)
+ REST_GPR(18)
+ REST_GPR(19)
+ REST_GPR(20)
+ REST_GPR(21)
+ REST_GPR(22)
+ REST_GPR(23)
+ REST_GPR(24)
+ REST_GPR(25)
+ REST_GPR(26)
+ REST_GPR(27)
+ REST_GPR(28)
+ REST_GPR(29)
+ REST_GPR(30)
+ REST_GPR(31)
+ addi r1,r1,STACK_FRAME
+ RFEBB
+FUNC_END(ebb_handler)
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c
new file mode 100644
index 0000000..c45f948
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests we can setup an EBB on our child. Nothing interesting happens, because
+ * even though the event is enabled and running the child hasn't enabled the
+ * actual delivery of the EBBs.
+ */
+
+static int victim_child(union pipe read_pipe, union pipe write_pipe)
+{
+ int i;
+
+ FAIL_IF(wait_for_parent(read_pipe));
+ FAIL_IF(notify_parent(write_pipe));
+
+ /* Parent creates EBB event */
+
+ FAIL_IF(wait_for_parent(read_pipe));
+ FAIL_IF(notify_parent(write_pipe));
+
+ /* Check the EBB is enabled by writing PMC1 */
+ write_pmc1();
+
+ /* EBB event is enabled here */
+ for (i = 0; i < 1000000; i++) ;
+
+ return 0;
+}
+
+int ebb_on_child(void)
+{
+ union pipe read_pipe, write_pipe;
+ struct event event;
+ pid_t pid;
+
+ FAIL_IF(pipe(read_pipe.fds) == -1);
+ FAIL_IF(pipe(write_pipe.fds) == -1);
+
+ pid = fork();
+ if (pid == 0) {
+ /* NB order of pipes looks reversed */
+ exit(victim_child(write_pipe, read_pipe));
+ }
+
+ FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+ /* Child is running now */
+
+ event_init_named(&event, 0x1001e, "cycles");
+ event_leader_ebb_init(&event);
+
+ event.attr.exclude_kernel = 1;
+ event.attr.exclude_hv = 1;
+ event.attr.exclude_idle = 1;
+
+ FAIL_IF(event_open_with_pid(&event, pid));
+ FAIL_IF(ebb_event_enable(&event));
+
+ FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+ /* Child should just exit happily */
+ FAIL_IF(wait_for_child(pid));
+
+ event_close(&event);
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(ebb_on_child, "ebb_on_child");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c
new file mode 100644
index 0000000..11acf1d
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests we can setup an EBB on our child. The child expects this and enables
+ * EBBs, which are then delivered to the child, even though the event is
+ * created by the parent.
+ */
+
+static int victim_child(union pipe read_pipe, union pipe write_pipe)
+{
+ FAIL_IF(wait_for_parent(read_pipe));
+
+ /* Setup our EBB handler, before the EBB event is created */
+ ebb_enable_pmc_counting(1);
+ setup_ebb_handler(standard_ebb_callee);
+ ebb_global_enable();
+
+ FAIL_IF(notify_parent(write_pipe));
+
+ while (ebb_state.stats.ebb_count < 20) {
+ FAIL_IF(core_busy_loop());
+ }
+
+ ebb_global_disable();
+ ebb_freeze_pmcs();
+
+ count_pmc(1, sample_period);
+
+ dump_ebb_state();
+
+ FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+ return 0;
+}
+
+/* Tests we can setup an EBB on our child - if it's expecting it */
+int ebb_on_willing_child(void)
+{
+ union pipe read_pipe, write_pipe;
+ struct event event;
+ pid_t pid;
+
+ FAIL_IF(pipe(read_pipe.fds) == -1);
+ FAIL_IF(pipe(write_pipe.fds) == -1);
+
+ pid = fork();
+ if (pid == 0) {
+ /* NB order of pipes looks reversed */
+ exit(victim_child(write_pipe, read_pipe));
+ }
+
+ /* Signal the child to setup its EBB handler */
+ FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+ /* Child is running now */
+
+ event_init_named(&event, 0x1001e, "cycles");
+ event_leader_ebb_init(&event);
+
+ event.attr.exclude_kernel = 1;
+ event.attr.exclude_hv = 1;
+ event.attr.exclude_idle = 1;
+
+ FAIL_IF(event_open_with_pid(&event, pid));
+ FAIL_IF(ebb_event_enable(&event));
+
+ /* Child show now take EBBs and then exit */
+ FAIL_IF(wait_for_child(pid));
+
+ event_close(&event);
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(ebb_on_willing_child, "ebb_on_willing_child");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c
new file mode 100644
index 0000000..be4dd5a
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests an EBB vs a cpu event - in that order. The EBB should force the cpu
+ * event off the PMU.
+ */
+
+static int setup_cpu_event(struct event *event, int cpu)
+{
+ event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+ event->attr.exclude_kernel = 1;
+ event->attr.exclude_hv = 1;
+ event->attr.exclude_idle = 1;
+
+ SKIP_IF(require_paranoia_below(1));
+ FAIL_IF(event_open_with_cpu(event, cpu));
+ FAIL_IF(event_enable(event));
+
+ return 0;
+}
+
+int ebb_vs_cpu_event(void)
+{
+ union pipe read_pipe, write_pipe;
+ struct event event;
+ int cpu, rc;
+ pid_t pid;
+
+ cpu = pick_online_cpu();
+ FAIL_IF(cpu < 0);
+ FAIL_IF(bind_to_cpu(cpu));
+
+ FAIL_IF(pipe(read_pipe.fds) == -1);
+ FAIL_IF(pipe(write_pipe.fds) == -1);
+
+ pid = fork();
+ if (pid == 0) {
+ /* NB order of pipes looks reversed */
+ exit(ebb_child(write_pipe, read_pipe));
+ }
+
+ /* Signal the child to install its EBB event and wait */
+ FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+ /* Now try to install our CPU event */
+ rc = setup_cpu_event(&event, cpu);
+ if (rc) {
+ kill_child_and_wait(pid);
+ return rc;
+ }
+
+ /* Signal the child to run */
+ FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+ /* .. and wait for it to complete */
+ FAIL_IF(wait_for_child(pid));
+ FAIL_IF(event_disable(&event));
+ FAIL_IF(event_read(&event));
+
+ event_report(&event);
+
+ /* The cpu event may have run, but we don't expect 100% */
+ FAIL_IF(event.result.enabled >= event.result.running);
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(ebb_vs_cpu_event, "ebb_vs_cpu_event");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c b/tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c
new file mode 100644
index 0000000..7e78153
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test various attributes of the EBB event are enforced.
+ */
+int event_attributes(void)
+{
+ struct event event, leader;
+
+ event_init(&event, 0x1001e);
+ event_leader_ebb_init(&event);
+ /* Expected to succeed */
+ FAIL_IF(event_open(&event));
+ event_close(&event);
+
+
+ event_init(&event, 0x001e); /* CYCLES - no PMC specified */
+ event_leader_ebb_init(&event);
+ /* Expected to fail, no PMC specified */
+ FAIL_IF(event_open(&event) == 0);
+
+
+ event_init(&event, 0x2001e);
+ event_leader_ebb_init(&event);
+ event.attr.exclusive = 0;
+ /* Expected to fail, not exclusive */
+ FAIL_IF(event_open(&event) == 0);
+
+
+ event_init(&event, 0x3001e);
+ event_leader_ebb_init(&event);
+ event.attr.freq = 1;
+ /* Expected to fail, sets freq */
+ FAIL_IF(event_open(&event) == 0);
+
+
+ event_init(&event, 0x4001e);
+ event_leader_ebb_init(&event);
+ event.attr.sample_period = 1;
+ /* Expected to fail, sets sample_period */
+ FAIL_IF(event_open(&event) == 0);
+
+
+ event_init(&event, 0x1001e);
+ event_leader_ebb_init(&event);
+ event.attr.enable_on_exec = 1;
+ /* Expected to fail, sets enable_on_exec */
+ FAIL_IF(event_open(&event) == 0);
+
+
+ event_init(&event, 0x1001e);
+ event_leader_ebb_init(&event);
+ event.attr.inherit = 1;
+ /* Expected to fail, sets inherit */
+ FAIL_IF(event_open(&event) == 0);
+
+
+ event_init(&leader, 0x1001e);
+ event_leader_ebb_init(&leader);
+ FAIL_IF(event_open(&leader));
+
+ event_init(&event, 0x20002);
+ event_ebb_init(&event);
+
+ /* Expected to succeed */
+ FAIL_IF(event_open_with_group(&event, leader.fd));
+ event_close(&leader);
+ event_close(&event);
+
+
+ event_init(&leader, 0x1001e);
+ event_leader_ebb_init(&leader);
+ FAIL_IF(event_open(&leader));
+
+ event_init(&event, 0x20002);
+
+ /* Expected to fail, event doesn't request EBB, leader does */
+ FAIL_IF(event_open_with_group(&event, leader.fd) == 0);
+ event_close(&leader);
+
+
+ event_init(&leader, 0x1001e);
+ event_leader_ebb_init(&leader);
+ /* Clear the EBB flag */
+ leader.attr.config &= ~(1ull << 63);
+
+ FAIL_IF(event_open(&leader));
+
+ event_init(&event, 0x20002);
+ event_ebb_init(&event);
+
+ /* Expected to fail, leader doesn't request EBB */
+ FAIL_IF(event_open_with_group(&event, leader.fd) == 0);
+ event_close(&leader);
+
+
+ event_init(&leader, 0x1001e);
+ event_leader_ebb_init(&leader);
+ leader.attr.exclusive = 0;
+ /* Expected to fail, leader isn't exclusive */
+ FAIL_IF(event_open(&leader) == 0);
+
+
+ event_init(&leader, 0x1001e);
+ event_leader_ebb_init(&leader);
+ leader.attr.pinned = 0;
+ /* Expected to fail, leader isn't pinned */
+ FAIL_IF(event_open(&leader) == 0);
+
+ event_init(&event, 0x1001e);
+ event_leader_ebb_init(&event);
+ /* Expected to fail, not a task event */
+ SKIP_IF(require_paranoia_below(1));
+ FAIL_IF(event_open_with_cpu(&event, 0) == 0);
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(event_attributes, "event_attributes");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S b/tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S
new file mode 100644
index 0000000..b866a05
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <ppc-asm.h>
+
+ .text
+
+FUNC_START(thirty_two_instruction_loop)
+ cmpwi r3,0
+ beqlr
+ addi r4,r3,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1 # 28 addi's
+ subi r3,r3,1
+ b FUNC_NAME(thirty_two_instruction_loop)
+FUNC_END(thirty_two_instruction_loop)
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c b/tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c
new file mode 100644
index 0000000..9e7af6e
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that a fork clears the PMU state of the child. eg. BESCR/EBBHR/EBBRR
+ * are cleared, and MMCR0_PMCC is reset, preventing the child from accessing
+ * the PMU.
+ */
+
+static struct event event;
+
+static int child(void)
+{
+ /* Even though we have EBE=0 we can still see the EBB regs */
+ FAIL_IF(mfspr(SPRN_BESCR) != 0);
+ FAIL_IF(mfspr(SPRN_EBBHR) != 0);
+ FAIL_IF(mfspr(SPRN_EBBRR) != 0);
+
+ FAIL_IF(catch_sigill(write_pmc1));
+
+ /* We can still read from the event, though it is on our parent */
+ FAIL_IF(event_read(&event));
+
+ return 0;
+}
+
+/* Tests that fork clears EBB state */
+int fork_cleanup(void)
+{
+ pid_t pid;
+
+ event_init_named(&event, 0x1001e, "cycles");
+ event_leader_ebb_init(&event);
+
+ FAIL_IF(event_open(&event));
+
+ ebb_enable_pmc_counting(1);
+ setup_ebb_handler(standard_ebb_callee);
+ ebb_global_enable();
+
+ FAIL_IF(ebb_event_enable(&event));
+
+ mtspr(SPRN_MMCR0, MMCR0_FC);
+ mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+ /* Don't need to actually take any EBBs */
+
+ pid = fork();
+ if (pid == 0)
+ exit(child());
+
+ /* Child does the actual testing */
+ FAIL_IF(wait_for_child(pid));
+
+ /* After fork */
+ event_close(&event);
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(fork_cleanup, "fork_cleanup");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c b/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c
new file mode 100644
index 0000000..f8190fa
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/prctl.h>
+
+#include "ebb.h"
+
+
+/*
+ * Run a calibrated instruction loop and count instructions executed using
+ * EBBs. Make sure the counts look right.
+ */
+
+extern void thirty_two_instruction_loop(uint64_t loops);
+
+static bool counters_frozen = true;
+
+static int do_count_loop(struct event *event, uint64_t instructions,
+ uint64_t overhead, bool report)
+{
+ int64_t difference, expected;
+ double percentage;
+
+ clear_ebb_stats();
+
+ counters_frozen = false;
+ mb();
+ mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+
+ thirty_two_instruction_loop(instructions >> 5);
+
+ counters_frozen = true;
+ mb();
+ mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
+
+ count_pmc(4, sample_period);
+
+ event->result.value = ebb_state.stats.pmc_count[4-1];
+ expected = instructions + overhead;
+ difference = event->result.value - expected;
+ percentage = (double)difference / event->result.value * 100;
+
+ if (report) {
+ printf("Looped for %lu instructions, overhead %lu\n", instructions, overhead);
+ printf("Expected %lu\n", expected);
+ printf("Actual %llu\n", event->result.value);
+ printf("Error %ld, %f%%\n", difference, percentage);
+ printf("Took %d EBBs\n", ebb_state.stats.ebb_count);
+ }
+
+ if (difference < 0)
+ difference = -difference;
+
+ /* Tolerate a difference of up to 0.0001 % */
+ difference *= 10000 * 100;
+ if (difference / event->result.value)
+ return -1;
+
+ return 0;
+}
+
+/* Count how many instructions it takes to do a null loop */
+static uint64_t determine_overhead(struct event *event)
+{
+ uint64_t current, overhead;
+ int i;
+
+ do_count_loop(event, 0, 0, false);
+ overhead = event->result.value;
+
+ for (i = 0; i < 100; i++) {
+ do_count_loop(event, 0, 0, false);
+ current = event->result.value;
+ if (current < overhead) {
+ printf("Replacing overhead %lu with %lu\n", overhead, current);
+ overhead = current;
+ }
+ }
+
+ return overhead;
+}
+
+static void pmc4_ebb_callee(void)
+{
+ uint64_t val;
+
+ val = mfspr(SPRN_BESCR);
+ if (!(val & BESCR_PMEO)) {
+ ebb_state.stats.spurious++;
+ goto out;
+ }
+
+ ebb_state.stats.ebb_count++;
+ count_pmc(4, sample_period);
+out:
+ if (counters_frozen)
+ reset_ebb_with_clear_mask(MMCR0_PMAO);
+ else
+ reset_ebb();
+}
+
+int instruction_count(void)
+{
+ struct event event;
+ uint64_t overhead;
+
+ event_init_named(&event, 0x400FA, "PM_RUN_INST_CMPL");
+ event_leader_ebb_init(&event);
+ event.attr.exclude_kernel = 1;
+ event.attr.exclude_hv = 1;
+ event.attr.exclude_idle = 1;
+
+ FAIL_IF(event_open(&event));
+ FAIL_IF(ebb_event_enable(&event));
+
+ sample_period = COUNTER_OVERFLOW;
+
+ setup_ebb_handler(pmc4_ebb_callee);
+ mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+ ebb_global_enable();
+
+ overhead = determine_overhead(&event);
+ printf("Overhead of null loop: %lu instructions\n", overhead);
+
+ /* Run for 1M instructions */
+ FAIL_IF(do_count_loop(&event, 0x100000, overhead, true));
+
+ /* Run for 10M instructions */
+ FAIL_IF(do_count_loop(&event, 0xa00000, overhead, true));
+
+ /* Run for 100M instructions */
+ FAIL_IF(do_count_loop(&event, 0x6400000, overhead, true));
+
+ /* Run for 1G instructions */
+ FAIL_IF(do_count_loop(&event, 0x40000000, overhead, true));
+
+ /* Run for 16G instructions */
+ FAIL_IF(do_count_loop(&event, 0x400000000, overhead, true));
+
+ /* Run for 64G instructions */
+ FAIL_IF(do_count_loop(&event, 0x1000000000, overhead, true));
+
+ /* Run for 128G instructions */
+ FAIL_IF(do_count_loop(&event, 0x2000000000, overhead, true));
+
+ ebb_global_disable();
+ event_close(&event);
+
+ printf("Finished OK\n");
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(instruction_count, "instruction_count");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c b/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c
new file mode 100644
index 0000000..0c9dd9b
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that tries to trigger CPU_FTR_PMAO_BUG. Which is a hardware defect
+ * where an exception triggers but we context switch before it is delivered and
+ * lose the exception.
+ */
+
+static int test_body(void)
+{
+ int i, orig_period, max_period;
+ struct event event;
+
+ /* We use PMC4 to make sure the kernel switches all counters correctly */
+ event_init_named(&event, 0x40002, "instructions");
+ event_leader_ebb_init(&event);
+
+ event.attr.exclude_kernel = 1;
+ event.attr.exclude_hv = 1;
+ event.attr.exclude_idle = 1;
+
+ FAIL_IF(event_open(&event));
+
+ ebb_enable_pmc_counting(4);
+ setup_ebb_handler(standard_ebb_callee);
+ ebb_global_enable();
+ FAIL_IF(ebb_event_enable(&event));
+
+ /*
+ * We want a low sample period, but we also want to get out of the EBB
+ * handler without tripping up again.
+ *
+ * This value picked after much experimentation.
+ */
+ orig_period = max_period = sample_period = 400;
+
+ mtspr(SPRN_PMC4, pmc_sample_period(sample_period));
+
+ while (ebb_state.stats.ebb_count < 1000000) {
+ /*
+ * We are trying to get the EBB exception to race exactly with
+ * us entering the kernel to do the syscall. We then need the
+ * kernel to decide our timeslice is up and context switch to
+ * the other thread. When we come back our EBB will have been
+ * lost and we'll spin in this while loop forever.
+ */
+
+ for (i = 0; i < 100000; i++)
+ sched_yield();
+
+ /* Change the sample period slightly to try and hit the race */
+ if (sample_period >= (orig_period + 200))
+ sample_period = orig_period;
+ else
+ sample_period++;
+
+ if (sample_period > max_period)
+ max_period = sample_period;
+ }
+
+ ebb_freeze_pmcs();
+ ebb_global_disable();
+
+ count_pmc(4, sample_period);
+ mtspr(SPRN_PMC4, 0xdead);
+
+ dump_summary_ebb_state();
+ dump_ebb_hw_state();
+
+ event_close(&event);
+
+ FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+ /* We vary our sample period so we need extra fudge here */
+ FAIL_IF(!ebb_check_count(4, orig_period, 2 * (max_period - orig_period)));
+
+ return 0;
+}
+
+static int lost_exception(void)
+{
+ return eat_cpu(test_body);
+}
+
+int main(void)
+{
+ return test_harness(lost_exception, "lost_exception");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c b/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c
new file mode 100644
index 0000000..67d78af
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test counting multiple events using EBBs.
+ */
+int multi_counter(void)
+{
+ struct event events[6];
+ int i, group_fd;
+
+ event_init_named(&events[0], 0x1001C, "PM_CMPLU_STALL_THRD");
+ event_init_named(&events[1], 0x2D016, "PM_CMPLU_STALL_FXU");
+ event_init_named(&events[2], 0x30006, "PM_CMPLU_STALL_OTHER_CMPL");
+ event_init_named(&events[3], 0x4000A, "PM_CMPLU_STALL");
+ event_init_named(&events[4], 0x600f4, "PM_RUN_CYC");
+ event_init_named(&events[5], 0x500fa, "PM_RUN_INST_CMPL");
+
+ event_leader_ebb_init(&events[0]);
+ for (i = 1; i < 6; i++)
+ event_ebb_init(&events[i]);
+
+ group_fd = -1;
+ for (i = 0; i < 6; i++) {
+ events[i].attr.exclude_kernel = 1;
+ events[i].attr.exclude_hv = 1;
+ events[i].attr.exclude_idle = 1;
+
+ FAIL_IF(event_open_with_group(&events[i], group_fd));
+ if (group_fd == -1)
+ group_fd = events[0].fd;
+ }
+
+ ebb_enable_pmc_counting(1);
+ ebb_enable_pmc_counting(2);
+ ebb_enable_pmc_counting(3);
+ ebb_enable_pmc_counting(4);
+ ebb_enable_pmc_counting(5);
+ ebb_enable_pmc_counting(6);
+ setup_ebb_handler(standard_ebb_callee);
+
+ FAIL_IF(ioctl(events[0].fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP));
+ FAIL_IF(event_read(&events[0]));
+
+ ebb_global_enable();
+
+ mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+ mtspr(SPRN_PMC2, pmc_sample_period(sample_period));
+ mtspr(SPRN_PMC3, pmc_sample_period(sample_period));
+ mtspr(SPRN_PMC4, pmc_sample_period(sample_period));
+ mtspr(SPRN_PMC5, pmc_sample_period(sample_period));
+ mtspr(SPRN_PMC6, pmc_sample_period(sample_period));
+
+ while (ebb_state.stats.ebb_count < 50) {
+ FAIL_IF(core_busy_loop());
+ FAIL_IF(ebb_check_mmcr0());
+ }
+
+ ebb_global_disable();
+ ebb_freeze_pmcs();
+
+ count_pmc(1, sample_period);
+ count_pmc(2, sample_period);
+ count_pmc(3, sample_period);
+ count_pmc(4, sample_period);
+ count_pmc(5, sample_period);
+ count_pmc(6, sample_period);
+
+ dump_ebb_state();
+
+ for (i = 0; i < 6; i++)
+ event_close(&events[i]);
+
+ FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(multi_counter, "multi_counter");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c b/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c
new file mode 100644
index 0000000..b8dc371
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test running multiple EBB using processes at once on a single CPU. They
+ * should all run happily without interfering with each other.
+ */
+
+static bool child_should_exit;
+
+static void sigint_handler(int signal)
+{
+ child_should_exit = true;
+}
+
+struct sigaction sigint_action = {
+ .sa_handler = sigint_handler,
+};
+
+static int cycles_child(void)
+{
+ struct event event;
+
+ if (sigaction(SIGINT, &sigint_action, NULL)) {
+ perror("sigaction");
+ return 1;
+ }
+
+ event_init_named(&event, 0x1001e, "cycles");
+ event_leader_ebb_init(&event);
+
+ event.attr.exclude_kernel = 1;
+ event.attr.exclude_hv = 1;
+ event.attr.exclude_idle = 1;
+
+ FAIL_IF(event_open(&event));
+
+ ebb_enable_pmc_counting(1);
+ setup_ebb_handler(standard_ebb_callee);
+ ebb_global_enable();
+
+ FAIL_IF(ebb_event_enable(&event));
+
+ mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+ while (!child_should_exit) {
+ FAIL_IF(core_busy_loop());
+ FAIL_IF(ebb_check_mmcr0());
+ }
+
+ ebb_global_disable();
+ ebb_freeze_pmcs();
+
+ count_pmc(1, sample_period);
+
+ dump_summary_ebb_state();
+
+ event_close(&event);
+
+ FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+ return 0;
+}
+
+#define NR_CHILDREN 4
+
+int multi_ebb_procs(void)
+{
+ pid_t pids[NR_CHILDREN];
+ int cpu, rc, i;
+
+ cpu = pick_online_cpu();
+ FAIL_IF(cpu < 0);
+ FAIL_IF(bind_to_cpu(cpu));
+
+ for (i = 0; i < NR_CHILDREN; i++) {
+ pids[i] = fork();
+ if (pids[i] == 0)
+ exit(cycles_child());
+ }
+
+ /* Have them all run for "a while" */
+ sleep(10);
+
+ rc = 0;
+ for (i = 0; i < NR_CHILDREN; i++) {
+ /* Tell them to stop */
+ kill(pids[i], SIGINT);
+ /* And wait */
+ rc |= wait_for_child(pids[i]);
+ }
+
+ return rc;
+}
+
+int main(void)
+{
+ return test_harness(multi_ebb_procs, "multi_ebb_procs");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c b/tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c
new file mode 100644
index 0000000..2f9bf8e
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#include "ebb.h"
+
+
+/* Test that things work sanely if we have no handler */
+
+static int no_handler_test(void)
+{
+ struct event event;
+ u64 val;
+ int i;
+
+ event_init_named(&event, 0x1001e, "cycles");
+ event_leader_ebb_init(&event);
+
+ event.attr.exclude_kernel = 1;
+ event.attr.exclude_hv = 1;
+ event.attr.exclude_idle = 1;
+
+ FAIL_IF(event_open(&event));
+ FAIL_IF(ebb_event_enable(&event));
+
+ val = mfspr(SPRN_EBBHR);
+ FAIL_IF(val != 0);
+
+ /* Make sure it overflows quickly */
+ sample_period = 1000;
+ mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+ /* Spin to make sure the event has time to overflow */
+ for (i = 0; i < 1000; i++)
+ mb();
+
+ dump_ebb_state();
+
+ /* We expect to see the PMU frozen & PMAO set */
+ val = mfspr(SPRN_MMCR0);
+ FAIL_IF(val != 0x0000000080000080);
+
+ event_close(&event);
+
+ dump_ebb_state();
+
+ /* The real test is that we never took an EBB at 0x0 */
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(no_handler_test,"no_handler_test");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c b/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c
new file mode 100644
index 0000000..986500f
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <sched.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that the kernel properly handles PMAE across context switches.
+ *
+ * We test this by calling into the kernel inside our EBB handler, where PMAE
+ * is clear. A cpu eater companion thread is running on the same CPU as us to
+ * encourage the scheduler to switch us.
+ *
+ * The kernel must make sure that when it context switches us back in, it
+ * honours the fact that we had PMAE clear.
+ *
+ * Observed to hit the failing case on the first EBB with a broken kernel.
+ */
+
+static bool mmcr0_mismatch;
+static uint64_t before, after;
+
+static void syscall_ebb_callee(void)
+{
+ uint64_t val;
+
+ val = mfspr(SPRN_BESCR);
+ if (!(val & BESCR_PMEO)) {
+ ebb_state.stats.spurious++;
+ goto out;
+ }
+
+ ebb_state.stats.ebb_count++;
+ count_pmc(1, sample_period);
+
+ before = mfspr(SPRN_MMCR0);
+
+ /* Try and get ourselves scheduled, to force a PMU context switch */
+ sched_yield();
+
+ after = mfspr(SPRN_MMCR0);
+ if (before != after)
+ mmcr0_mismatch = true;
+
+out:
+ reset_ebb();
+}
+
+static int test_body(void)
+{
+ struct event event;
+
+ event_init_named(&event, 0x1001e, "cycles");
+ event_leader_ebb_init(&event);
+
+ event.attr.exclude_kernel = 1;
+ event.attr.exclude_hv = 1;
+ event.attr.exclude_idle = 1;
+
+ FAIL_IF(event_open(&event));
+
+ setup_ebb_handler(syscall_ebb_callee);
+ ebb_global_enable();
+
+ FAIL_IF(ebb_event_enable(&event));
+
+ mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+ while (ebb_state.stats.ebb_count < 20 && !mmcr0_mismatch)
+ FAIL_IF(core_busy_loop());
+
+ ebb_global_disable();
+ ebb_freeze_pmcs();
+
+ count_pmc(1, sample_period);
+
+ dump_ebb_state();
+
+ if (mmcr0_mismatch)
+ printf("Saw MMCR0 before 0x%lx after 0x%lx\n", before, after);
+
+ event_close(&event);
+
+ FAIL_IF(ebb_state.stats.ebb_count == 0);
+ FAIL_IF(mmcr0_mismatch);
+
+ return 0;
+}
+
+int pmae_handling(void)
+{
+ return eat_cpu(test_body);
+}
+
+int main(void)
+{
+ return test_harness(pmae_handling, "pmae_handling");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c b/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c
new file mode 100644
index 0000000..a503fa7
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that PMC5 & 6 are frozen (ie. don't overflow) when they are not being
+ * used. Tests the MMCR0_FC56 logic in the kernel.
+ */
+
+static int pmc56_overflowed;
+
+static void ebb_callee(void)
+{
+ uint64_t val;
+
+ val = mfspr(SPRN_BESCR);
+ if (!(val & BESCR_PMEO)) {
+ ebb_state.stats.spurious++;
+ goto out;
+ }
+
+ ebb_state.stats.ebb_count++;
+ count_pmc(2, sample_period);
+
+ val = mfspr(SPRN_PMC5);
+ if (val >= COUNTER_OVERFLOW)
+ pmc56_overflowed++;
+
+ count_pmc(5, COUNTER_OVERFLOW);
+
+ val = mfspr(SPRN_PMC6);
+ if (val >= COUNTER_OVERFLOW)
+ pmc56_overflowed++;
+
+ count_pmc(6, COUNTER_OVERFLOW);
+
+out:
+ reset_ebb();
+}
+
+int pmc56_overflow(void)
+{
+ struct event event;
+
+ /* Use PMC2 so we set PMCjCE, which enables PMC5/6 */
+ event_init(&event, 0x2001e);
+ event_leader_ebb_init(&event);
+
+ event.attr.exclude_kernel = 1;
+ event.attr.exclude_hv = 1;
+ event.attr.exclude_idle = 1;
+
+ FAIL_IF(event_open(&event));
+
+ setup_ebb_handler(ebb_callee);
+ ebb_global_enable();
+
+ FAIL_IF(ebb_event_enable(&event));
+
+ mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+ mtspr(SPRN_PMC5, 0);
+ mtspr(SPRN_PMC6, 0);
+
+ while (ebb_state.stats.ebb_count < 10)
+ FAIL_IF(core_busy_loop());
+
+ ebb_global_disable();
+ ebb_freeze_pmcs();
+
+ count_pmc(2, sample_period);
+
+ dump_ebb_state();
+
+ printf("PMC5/6 overflow %d\n", pmc56_overflowed);
+
+ event_close(&event);
+
+ FAIL_IF(ebb_state.stats.ebb_count == 0 || pmc56_overflowed != 0);
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(pmc56_overflow, "pmc56_overflow");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/reg.h b/tools/testing/selftests/powerpc/pmu/ebb/reg.h
new file mode 100644
index 0000000..5921b0d
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/reg.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#ifndef _SELFTESTS_POWERPC_REG_H
+#define _SELFTESTS_POWERPC_REG_H
+
+#define __stringify_1(x) #x
+#define __stringify(x) __stringify_1(x)
+
+#define mfspr(rn) ({unsigned long rval; \
+ asm volatile("mfspr %0," __stringify(rn) \
+ : "=r" (rval)); rval; })
+#define mtspr(rn, v) asm volatile("mtspr " __stringify(rn) ",%0" : \
+ : "r" ((unsigned long)(v)) \
+ : "memory")
+
+#define mb() asm volatile("sync" : : : "memory");
+
+#define SPRN_MMCR2 769
+#define SPRN_MMCRA 770
+#define SPRN_MMCR0 779
+#define MMCR0_PMAO 0x00000080
+#define MMCR0_PMAE 0x04000000
+#define MMCR0_FC 0x80000000
+#define SPRN_EBBHR 804
+#define SPRN_EBBRR 805
+#define SPRN_BESCR 806 /* Branch event status & control register */
+#define SPRN_BESCRS 800 /* Branch event status & control set (1 bits set to 1) */
+#define SPRN_BESCRSU 801 /* Branch event status & control set upper */
+#define SPRN_BESCRR 802 /* Branch event status & control REset (1 bits set to 0) */
+#define SPRN_BESCRRU 803 /* Branch event status & control REset upper */
+
+#define BESCR_PMEO 0x1 /* PMU Event-based exception Occurred */
+#define BESCR_PME (0x1ul << 32) /* PMU Event-based exception Enable */
+
+#define SPRN_PMC1 771
+#define SPRN_PMC2 772
+#define SPRN_PMC3 773
+#define SPRN_PMC4 774
+#define SPRN_PMC5 775
+#define SPRN_PMC6 776
+
+#define SPRN_SIAR 780
+#define SPRN_SDAR 781
+#define SPRN_SIER 768
+
+#endif /* _SELFTESTS_POWERPC_REG_H */
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c b/tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c
new file mode 100644
index 0000000..0cae66f
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+#include "reg.h"
+
+
+/*
+ * Test basic access to the EBB regs, they should be user accessible with no
+ * kernel interaction required.
+ */
+int reg_access(void)
+{
+ uint64_t val, expected;
+
+ expected = 0x8000000100000000ull;
+ mtspr(SPRN_BESCR, expected);
+ val = mfspr(SPRN_BESCR);
+
+ FAIL_IF(val != expected);
+
+ expected = 0x0000000001000000ull;
+ mtspr(SPRN_EBBHR, expected);
+ val = mfspr(SPRN_EBBHR);
+
+ FAIL_IF(val != expected);
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(reg_access, "reg_access");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c
new file mode 100644
index 0000000..d56607e
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests a pinned per-task event vs an EBB - in that order. The pinned per-task
+ * event should prevent the EBB event from being enabled.
+ */
+
+static int setup_child_event(struct event *event, pid_t child_pid)
+{
+ event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+ event->attr.pinned = 1;
+
+ event->attr.exclude_kernel = 1;
+ event->attr.exclude_hv = 1;
+ event->attr.exclude_idle = 1;
+
+ FAIL_IF(event_open_with_pid(event, child_pid));
+ FAIL_IF(event_enable(event));
+
+ return 0;
+}
+
+int task_event_pinned_vs_ebb(void)
+{
+ union pipe read_pipe, write_pipe;
+ struct event event;
+ pid_t pid;
+ int rc;
+
+ FAIL_IF(pipe(read_pipe.fds) == -1);
+ FAIL_IF(pipe(write_pipe.fds) == -1);
+
+ pid = fork();
+ if (pid == 0) {
+ /* NB order of pipes looks reversed */
+ exit(ebb_child(write_pipe, read_pipe));
+ }
+
+ /* We setup the task event first */
+ rc = setup_child_event(&event, pid);
+ if (rc) {
+ kill_child_and_wait(pid);
+ return rc;
+ }
+
+ /* Signal the child to install its EBB event and wait */
+ if (sync_with_child(read_pipe, write_pipe))
+ /* If it fails, wait for it to exit */
+ goto wait;
+
+ /* Signal the child to run */
+ FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+wait:
+ /* We expect it to fail to read the event */
+ FAIL_IF(wait_for_child(pid) != 2);
+ FAIL_IF(event_disable(&event));
+ FAIL_IF(event_read(&event));
+
+ event_report(&event);
+
+ FAIL_IF(event.result.value == 0);
+ /*
+ * For reasons I don't understand enabled is usually just slightly
+ * lower than running. Would be good to confirm why.
+ */
+ FAIL_IF(event.result.enabled == 0);
+ FAIL_IF(event.result.running == 0);
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(task_event_pinned_vs_ebb, "task_event_pinned_vs_ebb");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c
new file mode 100644
index 0000000..eba3219
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests a per-task event vs an EBB - in that order. The EBB should push the
+ * per-task event off the PMU.
+ */
+
+static int setup_child_event(struct event *event, pid_t child_pid)
+{
+ event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+ event->attr.exclude_kernel = 1;
+ event->attr.exclude_hv = 1;
+ event->attr.exclude_idle = 1;
+
+ FAIL_IF(event_open_with_pid(event, child_pid));
+ FAIL_IF(event_enable(event));
+
+ return 0;
+}
+
+int task_event_vs_ebb(void)
+{
+ union pipe read_pipe, write_pipe;
+ struct event event;
+ pid_t pid;
+ int rc;
+
+ FAIL_IF(pipe(read_pipe.fds) == -1);
+ FAIL_IF(pipe(write_pipe.fds) == -1);
+
+ pid = fork();
+ if (pid == 0) {
+ /* NB order of pipes looks reversed */
+ exit(ebb_child(write_pipe, read_pipe));
+ }
+
+ /* We setup the task event first */
+ rc = setup_child_event(&event, pid);
+ if (rc) {
+ kill_child_and_wait(pid);
+ return rc;
+ }
+
+ /* Signal the child to install its EBB event and wait */
+ if (sync_with_child(read_pipe, write_pipe))
+ /* If it fails, wait for it to exit */
+ goto wait;
+
+ /* Signal the child to run */
+ FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+wait:
+ /* The EBB event should push the task event off so the child should succeed */
+ FAIL_IF(wait_for_child(pid));
+ FAIL_IF(event_disable(&event));
+ FAIL_IF(event_read(&event));
+
+ event_report(&event);
+
+ /* The task event may have run, or not so we can't assert anything about it */
+
+ return 0;
+}
+
+int main(void)
+{
+ return test_harness(task_event_vs_ebb, "task_event_vs_ebb");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/trace.c b/tools/testing/selftests/powerpc/pmu/ebb/trace.c
new file mode 100644
index 0000000..251e66a
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/trace.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include "trace.h"
+
+
+struct trace_buffer *trace_buffer_allocate(u64 size)
+{
+ struct trace_buffer *tb;
+
+ if (size < sizeof(*tb)) {
+ fprintf(stderr, "Error: trace buffer too small\n");
+ return NULL;
+ }
+
+ tb = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (tb == MAP_FAILED) {
+ perror("mmap");
+ return NULL;
+ }
+
+ tb->size = size;
+ tb->tail = tb->data;
+ tb->overflow = false;
+
+ return tb;
+}
+
+static bool trace_check_bounds(struct trace_buffer *tb, void *p)
+{
+ return p < ((void *)tb + tb->size);
+}
+
+static bool trace_check_alloc(struct trace_buffer *tb, void *p)
+{
+ /*
+ * If we ever overflowed don't allow any more input. This prevents us
+ * from dropping a large item and then later logging a small one. The
+ * buffer should just stop when overflow happened, not be patchy. If
+ * you're overflowing, make your buffer bigger.
+ */
+ if (tb->overflow)
+ return false;
+
+ if (!trace_check_bounds(tb, p)) {
+ tb->overflow = true;
+ return false;
+ }
+
+ return true;
+}
+
+static void *trace_alloc(struct trace_buffer *tb, int bytes)
+{
+ void *p, *newtail;
+
+ p = tb->tail;
+ newtail = tb->tail + bytes;
+ if (!trace_check_alloc(tb, newtail))
+ return NULL;
+
+ tb->tail = newtail;
+
+ return p;
+}
+
+static struct trace_entry *trace_alloc_entry(struct trace_buffer *tb, int payload_size)
+{
+ struct trace_entry *e;
+
+ e = trace_alloc(tb, sizeof(*e) + payload_size);
+ if (e)
+ e->length = payload_size;
+
+ return e;
+}
+
+int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value)
+{
+ struct trace_entry *e;
+ u64 *p;
+
+ e = trace_alloc_entry(tb, sizeof(reg) + sizeof(value));
+ if (!e)
+ return -ENOSPC;
+
+ e->type = TRACE_TYPE_REG;
+ p = (u64 *)e->data;
+ *p++ = reg;
+ *p++ = value;
+
+ return 0;
+}
+
+int trace_log_counter(struct trace_buffer *tb, u64 value)
+{
+ struct trace_entry *e;
+ u64 *p;
+
+ e = trace_alloc_entry(tb, sizeof(value));
+ if (!e)
+ return -ENOSPC;
+
+ e->type = TRACE_TYPE_COUNTER;
+ p = (u64 *)e->data;
+ *p++ = value;
+
+ return 0;
+}
+
+int trace_log_string(struct trace_buffer *tb, char *str)
+{
+ struct trace_entry *e;
+ char *p;
+ int len;
+
+ len = strlen(str);
+
+ /* We NULL terminate to make printing easier */
+ e = trace_alloc_entry(tb, len + 1);
+ if (!e)
+ return -ENOSPC;
+
+ e->type = TRACE_TYPE_STRING;
+ p = (char *)e->data;
+ memcpy(p, str, len);
+ p += len;
+ *p = '\0';
+
+ return 0;
+}
+
+int trace_log_indent(struct trace_buffer *tb)
+{
+ struct trace_entry *e;
+
+ e = trace_alloc_entry(tb, 0);
+ if (!e)
+ return -ENOSPC;
+
+ e->type = TRACE_TYPE_INDENT;
+
+ return 0;
+}
+
+int trace_log_outdent(struct trace_buffer *tb)
+{
+ struct trace_entry *e;
+
+ e = trace_alloc_entry(tb, 0);
+ if (!e)
+ return -ENOSPC;
+
+ e->type = TRACE_TYPE_OUTDENT;
+
+ return 0;
+}
+
+static void trace_print_header(int seq, int prefix)
+{
+ printf("%*s[%d]: ", prefix, "", seq);
+}
+
+static char *trace_decode_reg(int reg)
+{
+ switch (reg) {
+ case 769: return "SPRN_MMCR2"; break;
+ case 770: return "SPRN_MMCRA"; break;
+ case 779: return "SPRN_MMCR0"; break;
+ case 804: return "SPRN_EBBHR"; break;
+ case 805: return "SPRN_EBBRR"; break;
+ case 806: return "SPRN_BESCR"; break;
+ case 800: return "SPRN_BESCRS"; break;
+ case 801: return "SPRN_BESCRSU"; break;
+ case 802: return "SPRN_BESCRR"; break;
+ case 803: return "SPRN_BESCRRU"; break;
+ case 771: return "SPRN_PMC1"; break;
+ case 772: return "SPRN_PMC2"; break;
+ case 773: return "SPRN_PMC3"; break;
+ case 774: return "SPRN_PMC4"; break;
+ case 775: return "SPRN_PMC5"; break;
+ case 776: return "SPRN_PMC6"; break;
+ case 780: return "SPRN_SIAR"; break;
+ case 781: return "SPRN_SDAR"; break;
+ case 768: return "SPRN_SIER"; break;
+ }
+
+ return NULL;
+}
+
+static void trace_print_reg(struct trace_entry *e)
+{
+ u64 *p, *reg, *value;
+ char *name;
+
+ p = (u64 *)e->data;
+ reg = p++;
+ value = p;
+
+ name = trace_decode_reg(*reg);
+ if (name)
+ printf("register %-10s = 0x%016llx\n", name, *value);
+ else
+ printf("register %lld = 0x%016llx\n", *reg, *value);
+}
+
+static void trace_print_counter(struct trace_entry *e)
+{
+ u64 *value;
+
+ value = (u64 *)e->data;
+ printf("counter = %lld\n", *value);
+}
+
+static void trace_print_string(struct trace_entry *e)
+{
+ char *str;
+
+ str = (char *)e->data;
+ puts(str);
+}
+
+#define BASE_PREFIX 2
+#define PREFIX_DELTA 8
+
+static void trace_print_entry(struct trace_entry *e, int seq, int *prefix)
+{
+ switch (e->type) {
+ case TRACE_TYPE_REG:
+ trace_print_header(seq, *prefix);
+ trace_print_reg(e);
+ break;
+ case TRACE_TYPE_COUNTER:
+ trace_print_header(seq, *prefix);
+ trace_print_counter(e);
+ break;
+ case TRACE_TYPE_STRING:
+ trace_print_header(seq, *prefix);
+ trace_print_string(e);
+ break;
+ case TRACE_TYPE_INDENT:
+ trace_print_header(seq, *prefix);
+ puts("{");
+ *prefix += PREFIX_DELTA;
+ break;
+ case TRACE_TYPE_OUTDENT:
+ *prefix -= PREFIX_DELTA;
+ if (*prefix < BASE_PREFIX)
+ *prefix = BASE_PREFIX;
+ trace_print_header(seq, *prefix);
+ puts("}");
+ break;
+ default:
+ trace_print_header(seq, *prefix);
+ printf("entry @ %p type %d\n", e, e->type);
+ break;
+ }
+}
+
+void trace_buffer_print(struct trace_buffer *tb)
+{
+ struct trace_entry *e;
+ int i, prefix;
+ void *p;
+
+ printf("Trace buffer dump:\n");
+ printf(" address %p \n", tb);
+ printf(" tail %p\n", tb->tail);
+ printf(" size %llu\n", tb->size);
+ printf(" overflow %s\n", tb->overflow ? "TRUE" : "false");
+ printf(" Content:\n");
+
+ p = tb->data;
+
+ i = 0;
+ prefix = BASE_PREFIX;
+
+ while (trace_check_bounds(tb, p) && p < tb->tail) {
+ e = p;
+
+ trace_print_entry(e, i, &prefix);
+
+ i++;
+ p = (void *)e + sizeof(*e) + e->length;
+ }
+}
+
+void trace_print_location(struct trace_buffer *tb)
+{
+ printf("Trace buffer 0x%llx bytes @ %p\n", tb->size, tb);
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/trace.h b/tools/testing/selftests/powerpc/pmu/ebb/trace.h
new file mode 100644
index 0000000..926458e
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/ebb/trace.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#ifndef _SELFTESTS_POWERPC_PMU_EBB_TRACE_H
+#define _SELFTESTS_POWERPC_PMU_EBB_TRACE_H
+
+#include "utils.h"
+
+#define TRACE_TYPE_REG 1
+#define TRACE_TYPE_COUNTER 2
+#define TRACE_TYPE_STRING 3
+#define TRACE_TYPE_INDENT 4
+#define TRACE_TYPE_OUTDENT 5
+
+struct trace_entry
+{
+ u8 type;
+ u8 length;
+ u8 data[0];
+};
+
+struct trace_buffer
+{
+ u64 size;
+ bool overflow;
+ void *tail;
+ u8 data[0];
+};
+
+struct trace_buffer *trace_buffer_allocate(u64 size);
+int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value);
+int trace_log_counter(struct trace_buffer *tb, u64 value);
+int trace_log_string(struct trace_buffer *tb, char *str);
+int trace_log_indent(struct trace_buffer *tb);
+int trace_log_outdent(struct trace_buffer *tb);
+void trace_buffer_print(struct trace_buffer *tb);
+void trace_print_location(struct trace_buffer *tb);
+
+#endif /* _SELFTESTS_POWERPC_PMU_EBB_TRACE_H */
diff --git a/tools/testing/selftests/powerpc/pmu/event.c b/tools/testing/selftests/powerpc/pmu/event.c
index 2b2d11d..184b368 100644
--- a/tools/testing/selftests/powerpc/pmu/event.c
+++ b/tools/testing/selftests/powerpc/pmu/event.c
@@ -39,7 +39,13 @@ void event_init_named(struct event *e, u64 config, char *name)
event_init_opts(e, config, PERF_TYPE_RAW, name);
}
+void event_init(struct event *e, u64 config)
+{
+ event_init_opts(e, config, PERF_TYPE_RAW, "event");
+}
+
#define PERF_CURRENT_PID 0
+#define PERF_NO_PID -1
#define PERF_NO_CPU -1
#define PERF_NO_GROUP -1
@@ -59,6 +65,16 @@ int event_open_with_group(struct event *e, int group_fd)
return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, group_fd);
}
+int event_open_with_pid(struct event *e, pid_t pid)
+{
+ return event_open_with_options(e, pid, PERF_NO_CPU, PERF_NO_GROUP);
+}
+
+int event_open_with_cpu(struct event *e, int cpu)
+{
+ return event_open_with_options(e, PERF_NO_PID, cpu, PERF_NO_GROUP);
+}
+
int event_open(struct event *e)
{
return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, PERF_NO_GROUP);
@@ -69,6 +85,16 @@ void event_close(struct event *e)
close(e->fd);
}
+int event_enable(struct event *e)
+{
+ return ioctl(e->fd, PERF_EVENT_IOC_ENABLE);
+}
+
+int event_disable(struct event *e)
+{
+ return ioctl(e->fd, PERF_EVENT_IOC_DISABLE);
+}
+
int event_reset(struct event *e)
{
return ioctl(e->fd, PERF_EVENT_IOC_RESET);
diff --git a/tools/testing/selftests/powerpc/pmu/event.h b/tools/testing/selftests/powerpc/pmu/event.h
index e699319..a0ea6b1 100644
--- a/tools/testing/selftests/powerpc/pmu/event.h
+++ b/tools/testing/selftests/powerpc/pmu/event.h
@@ -29,8 +29,12 @@ void event_init_named(struct event *e, u64 config, char *name);
void event_init_opts(struct event *e, u64 config, int type, char *name);
int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd);
int event_open_with_group(struct event *e, int group_fd);
+int event_open_with_pid(struct event *e, pid_t pid);
+int event_open_with_cpu(struct event *e, int cpu);
int event_open(struct event *e);
void event_close(struct event *e);
+int event_enable(struct event *e);
+int event_disable(struct event *e);
int event_reset(struct event *e);
int event_read(struct event *e);
void event_report_justified(struct event *e, int name_width, int result_width);
diff --git a/tools/testing/selftests/powerpc/pmu/lib.c b/tools/testing/selftests/powerpc/pmu/lib.c
new file mode 100644
index 0000000..0f6a473
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/lib.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#define _GNU_SOURCE /* For CPU_ZERO etc. */
+
+#include <errno.h>
+#include <sched.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include "utils.h"
+#include "lib.h"
+
+
+int pick_online_cpu(void)
+{
+ cpu_set_t mask;
+ int cpu;
+
+ CPU_ZERO(&mask);
+
+ if (sched_getaffinity(0, sizeof(mask), &mask)) {
+ perror("sched_getaffinity");
+ return -1;
+ }
+
+ /* We prefer a primary thread, but skip 0 */
+ for (cpu = 8; cpu < CPU_SETSIZE; cpu += 8)
+ if (CPU_ISSET(cpu, &mask))
+ return cpu;
+
+ /* Search for anything, but in reverse */
+ for (cpu = CPU_SETSIZE - 1; cpu >= 0; cpu--)
+ if (CPU_ISSET(cpu, &mask))
+ return cpu;
+
+ printf("No cpus in affinity mask?!\n");
+ return -1;
+}
+
+int bind_to_cpu(int cpu)
+{
+ cpu_set_t mask;
+
+ printf("Binding to cpu %d\n", cpu);
+
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+
+ return sched_setaffinity(0, sizeof(mask), &mask);
+}
+
+#define PARENT_TOKEN 0xAA
+#define CHILD_TOKEN 0x55
+
+int sync_with_child(union pipe read_pipe, union pipe write_pipe)
+{
+ char c = PARENT_TOKEN;
+
+ FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
+ FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
+ if (c != CHILD_TOKEN) /* sometimes expected */
+ return 1;
+
+ return 0;
+}
+
+int wait_for_parent(union pipe read_pipe)
+{
+ char c;
+
+ FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
+ FAIL_IF(c != PARENT_TOKEN);
+
+ return 0;
+}
+
+int notify_parent(union pipe write_pipe)
+{
+ char c = CHILD_TOKEN;
+
+ FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
+
+ return 0;
+}
+
+int notify_parent_of_error(union pipe write_pipe)
+{
+ char c = ~CHILD_TOKEN;
+
+ FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
+
+ return 0;
+}
+
+int wait_for_child(pid_t child_pid)
+{
+ int rc;
+
+ if (waitpid(child_pid, &rc, 0) == -1) {
+ perror("waitpid");
+ return 1;
+ }
+
+ if (WIFEXITED(rc))
+ rc = WEXITSTATUS(rc);
+ else
+ rc = 1; /* Signal or other */
+
+ return rc;
+}
+
+int kill_child_and_wait(pid_t child_pid)
+{
+ kill(child_pid, SIGTERM);
+
+ return wait_for_child(child_pid);
+}
+
+static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe)
+{
+ volatile int i = 0;
+
+ /*
+ * We are just here to eat cpu and die. So make sure we can be killed,
+ * and also don't do any custom SIGTERM handling.
+ */
+ signal(SIGTERM, SIG_DFL);
+
+ notify_parent(write_pipe);
+ wait_for_parent(read_pipe);
+
+ /* Soak up cpu forever */
+ while (1) i++;
+
+ return 0;
+}
+
+pid_t eat_cpu(int (test_function)(void))
+{
+ union pipe read_pipe, write_pipe;
+ int cpu, rc;
+ pid_t pid;
+
+ cpu = pick_online_cpu();
+ FAIL_IF(cpu < 0);
+ FAIL_IF(bind_to_cpu(cpu));
+
+ if (pipe(read_pipe.fds) == -1)
+ return -1;
+
+ if (pipe(write_pipe.fds) == -1)
+ return -1;
+
+ pid = fork();
+ if (pid == 0)
+ exit(eat_cpu_child(write_pipe, read_pipe));
+
+ if (sync_with_child(read_pipe, write_pipe)) {
+ rc = -1;
+ goto out;
+ }
+
+ printf("main test running as pid %d\n", getpid());
+
+ rc = test_function();
+out:
+ kill(pid, SIGKILL);
+
+ return rc;
+}
+
+struct addr_range libc, vdso;
+
+int parse_proc_maps(void)
+{
+ char execute, name[128];
+ uint64_t start, end;
+ FILE *f;
+ int rc;
+
+ f = fopen("/proc/self/maps", "r");
+ if (!f) {
+ perror("fopen");
+ return -1;
+ }
+
+ do {
+ /* This skips line with no executable which is what we want */
+ rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n",
+ &start, &end, &execute, name);
+ if (rc <= 0)
+ break;
+
+ if (execute != 'x')
+ continue;
+
+ if (strstr(name, "libc")) {
+ libc.first = start;
+ libc.last = end - 1;
+ } else if (strstr(name, "[vdso]")) {
+ vdso.first = start;
+ vdso.last = end - 1;
+ }
+ } while(1);
+
+ fclose(f);
+
+ return 0;
+}
+
+#define PARANOID_PATH "/proc/sys/kernel/perf_event_paranoid"
+
+bool require_paranoia_below(int level)
+{
+ unsigned long current;
+ char *end, buf[16];
+ FILE *f;
+ int rc;
+
+ rc = -1;
+
+ f = fopen(PARANOID_PATH, "r");
+ if (!f) {
+ perror("fopen");
+ goto out;
+ }
+
+ if (!fgets(buf, sizeof(buf), f)) {
+ printf("Couldn't read " PARANOID_PATH "?\n");
+ goto out_close;
+ }
+
+ current = strtoul(buf, &end, 10);
+
+ if (end == buf) {
+ printf("Couldn't parse " PARANOID_PATH "?\n");
+ goto out_close;
+ }
+
+ if (current >= level)
+ goto out;
+
+ rc = 0;
+out_close:
+ fclose(f);
+out:
+ return rc;
+}
diff --git a/tools/testing/selftests/powerpc/pmu/lib.h b/tools/testing/selftests/powerpc/pmu/lib.h
new file mode 100644
index 0000000..ca5d72a
--- /dev/null
+++ b/tools/testing/selftests/powerpc/pmu/lib.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#ifndef __SELFTESTS_POWERPC_PMU_LIB_H
+#define __SELFTESTS_POWERPC_PMU_LIB_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+union pipe {
+ struct {
+ int read_fd;
+ int write_fd;
+ };
+ int fds[2];
+};
+
+extern int pick_online_cpu(void);
+extern int bind_to_cpu(int cpu);
+extern int kill_child_and_wait(pid_t child_pid);
+extern int wait_for_child(pid_t child_pid);
+extern int sync_with_child(union pipe read_pipe, union pipe write_pipe);
+extern int wait_for_parent(union pipe read_pipe);
+extern int notify_parent(union pipe write_pipe);
+extern int notify_parent_of_error(union pipe write_pipe);
+extern pid_t eat_cpu(int (test_function)(void));
+extern bool require_paranoia_below(int level);
+
+struct addr_range {
+ uint64_t first, last;
+};
+
+extern struct addr_range libc, vdso;
+
+int parse_proc_maps(void);
+
+#endif /* __SELFTESTS_POWERPC_PMU_LIB_H */
diff --git a/tools/testing/selftests/powerpc/pmu/loop.S b/tools/testing/selftests/powerpc/pmu/loop.S
index 8820e3d..20c1f08 100644
--- a/tools/testing/selftests/powerpc/pmu/loop.S
+++ b/tools/testing/selftests/powerpc/pmu/loop.S
@@ -3,44 +3,41 @@
* Licensed under GPLv2.
*/
+#include <ppc-asm.h>
+
.text
- .global thirty_two_instruction_loop
- .type .thirty_two_instruction_loop,@function
- .section ".opd","aw",@progbits
-thirty_two_instruction_loop:
- .quad .thirty_two_instruction_loop, .TOC.@tocbase, 0
- .previous
-.thirty_two_instruction_loop:
- cmpwi %r3,0
+FUNC_START(thirty_two_instruction_loop)
+ cmpdi r3,0
beqlr
- addi %r4,%r3,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1
- addi %r4,%r4,1 # 28 addi's
- subi %r3,%r3,1
- b .thirty_two_instruction_loop
+ addi r4,r3,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1
+ addi r4,r4,1 # 28 addi's
+ subi r3,r3,1
+ b FUNC_NAME(thirty_two_instruction_loop)
+FUNC_END(thirty_two_instruction_loop)
diff --git a/tools/testing/selftests/powerpc/subunit.h b/tools/testing/selftests/powerpc/subunit.h
index 98a2292..9c6c4e9 100644
--- a/tools/testing/selftests/powerpc/subunit.h
+++ b/tools/testing/selftests/powerpc/subunit.h
@@ -26,6 +26,11 @@ static inline void test_error(char *name)
printf("error: %s\n", name);
}
+static inline void test_skip(char *name)
+{
+ printf("skip: %s\n", name);
+}
+
static inline void test_success(char *name)
{
printf("success: %s\n", name);
diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile
new file mode 100644
index 0000000..51267f4
--- /dev/null
+++ b/tools/testing/selftests/powerpc/tm/Makefile
@@ -0,0 +1,15 @@
+PROGS := tm-resched-dscr
+
+all: $(PROGS)
+
+$(PROGS):
+
+run_tests: all
+ @-for PROG in $(PROGS); do \
+ ./$$PROG; \
+ done;
+
+clean:
+ rm -f $(PROGS) *.o
+
+.PHONY: all run_tests clean
diff --git a/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c b/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c
new file mode 100644
index 0000000..ee98e38
--- /dev/null
+++ b/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c
@@ -0,0 +1,90 @@
+/* Test context switching to see if the DSCR SPR is correctly preserved
+ * when within a transaction.
+ *
+ * Note: We assume that the DSCR has been left at the default value (0)
+ * for all CPUs.
+ *
+ * Method:
+ *
+ * Set a value into the DSCR.
+ *
+ * Start a transaction, and suspend it (*).
+ *
+ * Hard loop checking to see if the transaction has become doomed.
+ *
+ * Now that we *may* have been preempted, record the DSCR and TEXASR SPRS.
+ *
+ * If the abort was because of a context switch, check the DSCR value.
+ * Otherwise, try again.
+ *
+ * (*) If the transaction is not suspended we can't see the problem because
+ * the transaction abort handler will restore the DSCR to it's checkpointed
+ * value before we regain control.
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <asm/tm.h>
+
+#define TBEGIN ".long 0x7C00051D ;"
+#define TEND ".long 0x7C00055D ;"
+#define TCHECK ".long 0x7C00059C ;"
+#define TSUSPEND ".long 0x7C0005DD ;"
+#define TRESUME ".long 0x7C2005DD ;"
+#define SPRN_TEXASR 0x82
+#define SPRN_DSCR 0x03
+
+int main(void) {
+ uint64_t rv, dscr1 = 1, dscr2, texasr;
+
+ printf("Check DSCR TM context switch: ");
+ fflush(stdout);
+ for (;;) {
+ rv = 1;
+ asm __volatile__ (
+ /* set a known value into the DSCR */
+ "ld 3, %[dscr1];"
+ "mtspr %[sprn_dscr], 3;"
+
+ /* start and suspend a transaction */
+ TBEGIN
+ "beq 1f;"
+ TSUSPEND
+
+ /* hard loop until the transaction becomes doomed */
+ "2: ;"
+ TCHECK
+ "bc 4, 0, 2b;"
+
+ /* record DSCR and TEXASR */
+ "mfspr 3, %[sprn_dscr];"
+ "std 3, %[dscr2];"
+ "mfspr 3, %[sprn_texasr];"
+ "std 3, %[texasr];"
+
+ TRESUME
+ TEND
+ "li %[rv], 0;"
+ "1: ;"
+ : [rv]"=r"(rv), [dscr2]"=m"(dscr2), [texasr]"=m"(texasr)
+ : [dscr1]"m"(dscr1)
+ , [sprn_dscr]"i"(SPRN_DSCR), [sprn_texasr]"i"(SPRN_TEXASR)
+ : "memory", "r3"
+ );
+ assert(rv); /* make sure the transaction aborted */
+ if ((texasr >> 56) != TM_CAUSE_RESCHED) {
+ putchar('.');
+ fflush(stdout);
+ continue;
+ }
+ if (dscr2 != dscr1) {
+ printf(" FAIL\n");
+ exit(EXIT_FAILURE);
+ } else {
+ printf(" OK\n");
+ exit(EXIT_SUCCESS);
+ }
+ }
+}
diff --git a/tools/testing/selftests/powerpc/utils.h b/tools/testing/selftests/powerpc/utils.h
index 0de0644..a93777a 100644
--- a/tools/testing/selftests/powerpc/utils.h
+++ b/tools/testing/selftests/powerpc/utils.h
@@ -31,6 +31,18 @@ do { \
} \
} while (0)
+/* The test harness uses this, yes it's gross */
+#define MAGIC_SKIP_RETURN_VALUE 99
+
+#define SKIP_IF(x) \
+do { \
+ if ((x)) { \
+ fprintf(stderr, \
+ "[SKIP] Test skipped on line %d\n", __LINE__); \
+ return MAGIC_SKIP_RETURN_VALUE; \
+ } \
+} while (0)
+
#define _str(s) #s
#define str(s) _str(s)
diff --git a/tools/testing/selftests/rcutorture/bin/configinit.sh b/tools/testing/selftests/rcutorture/bin/configinit.sh
index a1be6e6..9c3f3d3 100755
--- a/tools/testing/selftests/rcutorture/bin/configinit.sh
+++ b/tools/testing/selftests/rcutorture/bin/configinit.sh
@@ -62,7 +62,7 @@ grep '^grep' < $T/u.sh > $T/upd.sh
echo "cat - $c" >> $T/upd.sh
make mrproper
make $buildloc distclean > $builddir/Make.distclean 2>&1
-make $buildloc defconfig > $builddir/Make.defconfig.out 2>&1
+make $buildloc $TORTURE_DEFCONFIG > $builddir/Make.defconfig.out 2>&1
mv $builddir/.config $builddir/.config.sav
sh $T/upd.sh < $builddir/.config.sav > $builddir/.config
cp $builddir/.config $builddir/.config.new
diff --git a/tools/testing/selftests/rcutorture/bin/functions.sh b/tools/testing/selftests/rcutorture/bin/functions.sh
index 9b17e81..d01b865 100644
--- a/tools/testing/selftests/rcutorture/bin/functions.sh
+++ b/tools/testing/selftests/rcutorture/bin/functions.sh
@@ -76,15 +76,39 @@ configfrag_hotplug_cpu () {
grep -q '^CONFIG_HOTPLUG_CPU=y$' "$1"
}
+# identify_boot_image qemu-cmd
+#
+# Returns the relative path to the kernel build image. This will be
+# arch/<arch>/boot/bzImage unless overridden with the TORTURE_BOOT_IMAGE
+# environment variable.
+identify_boot_image () {
+ if test -n "$TORTURE_BOOT_IMAGE"
+ then
+ echo $TORTURE_BOOT_IMAGE
+ else
+ case "$1" in
+ qemu-system-x86_64|qemu-system-i386)
+ echo arch/x86/boot/bzImage
+ ;;
+ qemu-system-ppc64)
+ echo arch/powerpc/boot/bzImage
+ ;;
+ *)
+ echo ""
+ ;;
+ esac
+ fi
+}
+
# identify_qemu builddir
#
# Returns our best guess as to which qemu command is appropriate for
-# the kernel at hand. Override with the RCU_QEMU_CMD environment variable.
+# the kernel at hand. Override with the TORTURE_QEMU_CMD environment variable.
identify_qemu () {
local u="`file "$1"`"
- if test -n "$RCU_QEMU_CMD"
+ if test -n "$TORTURE_QEMU_CMD"
then
- echo $RCU_QEMU_CMD
+ echo $TORTURE_QEMU_CMD
elif echo $u | grep -q x86-64
then
echo qemu-system-x86_64
@@ -98,7 +122,7 @@ identify_qemu () {
echo Cannot figure out what qemu command to use! 1>&2
echo file $1 output: $u
# Usually this will be one of /usr/bin/qemu-system-*
- # Use RCU_QEMU_CMD environment variable or appropriate
+ # Use TORTURE_QEMU_CMD environment variable or appropriate
# argument to top-level script.
exit 1
fi
@@ -107,14 +131,14 @@ identify_qemu () {
# identify_qemu_append qemu-cmd
#
# Output arguments for the qemu "-append" string based on CPU type
-# and the RCU_QEMU_INTERACTIVE environment variable.
+# and the TORTURE_QEMU_INTERACTIVE environment variable.
identify_qemu_append () {
case "$1" in
qemu-system-x86_64|qemu-system-i386)
echo noapic selinux=0 initcall_debug debug
;;
esac
- if test -n "$RCU_QEMU_INTERACTIVE"
+ if test -n "$TORTURE_QEMU_INTERACTIVE"
then
echo root=/dev/sda
else
@@ -124,8 +148,8 @@ identify_qemu_append () {
# identify_qemu_args qemu-cmd serial-file
#
-# Output arguments for qemu arguments based on the RCU_QEMU_MAC
-# and RCU_QEMU_INTERACTIVE environment variables.
+# Output arguments for qemu arguments based on the TORTURE_QEMU_MAC
+# and TORTURE_QEMU_INTERACTIVE environment variables.
identify_qemu_args () {
case "$1" in
qemu-system-x86_64|qemu-system-i386)
@@ -133,17 +157,17 @@ identify_qemu_args () {
qemu-system-ppc64)
echo -enable-kvm -M pseries -cpu POWER7 -nodefaults
echo -device spapr-vscsi
- if test -n "$RCU_QEMU_INTERACTIVE" -a -n "$RCU_QEMU_MAC"
+ if test -n "$TORTURE_QEMU_INTERACTIVE" -a -n "$TORTURE_QEMU_MAC"
then
- echo -device spapr-vlan,netdev=net0,mac=$RCU_QEMU_MAC
+ echo -device spapr-vlan,netdev=net0,mac=$TORTURE_QEMU_MAC
echo -netdev bridge,br=br0,id=net0
- elif test -n "$RCU_QEMU_INTERACTIVE"
+ elif test -n "$TORTURE_QEMU_INTERACTIVE"
then
echo -net nic -net user
fi
;;
esac
- if test -n "$RCU_QEMU_INTERACTIVE"
+ if test -n "$TORTURE_QEMU_INTERACTIVE"
then
echo -monitor stdio -serial pty -S
else
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-build.sh b/tools/testing/selftests/rcutorture/bin/kvm-build.sh
index 197901e..7c1e56b 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-build.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-build.sh
@@ -45,9 +45,9 @@ T=/tmp/test-linux.sh.$$
trap 'rm -rf $T' 0
mkdir $T
-cat ${config_template} | grep -v CONFIG_RCU_TORTURE_TEST > $T/config
+grep -v 'CONFIG_[A-Z]*_TORTURE_TEST' < ${config_template} > $T/config
cat << ___EOF___ >> $T/config
-CONFIG_INITRAMFS_SOURCE="$RCU_INITRD"
+CONFIG_INITRAMFS_SOURCE="$TORTURE_INITRD"
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_CONSOLE=y
___EOF___
@@ -60,7 +60,7 @@ then
exit 2
fi
ncpus=`cpus2use.sh`
-make O=$builddir -j$ncpus $RCU_KMAKE_ARG > $builddir/Make.out 2>&1
+make O=$builddir -j$ncpus $TORTURE_KMAKE_ARG > $builddir/Make.out 2>&1
retval=$?
if test $retval -ne 0 || grep "rcu[^/]*": < $builddir/Make.out | egrep -q "Stop|Error|error:|warning:" || egrep -q "Stop|Error|error:" < $builddir/Make.out
then
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh
index 829186e..7f1ff1a 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh
@@ -35,7 +35,7 @@ configfile=`echo $i | sed -e 's/^.*\///'`
ncs=`grep "Writes: Total:" $i/console.log 2> /dev/null | tail -1 | sed -e 's/^.* Total: //' -e 's/ .*$//'`
if test -z "$ncs"
then
- echo $configfile
+ echo "$configfile -------"
else
title="$configfile ------- $ncs acquisitions/releases"
dur=`sed -e 's/^.* locktorture.shutdown_secs=//' -e 's/ .*$//' < $i/qemu-cmd 2> /dev/null`
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh
index d75b1dc5..307c4b9 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh
@@ -35,7 +35,7 @@ configfile=`echo $i | sed -e 's/^.*\///'`
ngps=`grep ver: $i/console.log 2> /dev/null | tail -1 | sed -e 's/^.* ver: //' -e 's/ .*$//'`
if test -z "$ngps"
then
- echo $configfile
+ echo "$configfile -------"
else
title="$configfile ------- $ngps grace periods"
dur=`sed -e 's/^.* rcutorture.shutdown_secs=//' -e 's/ .*$//' < $i/qemu-cmd 2> /dev/null`
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh
index a44daaa..ee1f6ca 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh
@@ -25,6 +25,7 @@
# Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
PATH=`pwd`/tools/testing/selftests/rcutorture/bin:$PATH; export PATH
+. tools/testing/selftests/rcutorture/bin/functions.sh
for rd in "$@"
do
firsttime=1
@@ -39,13 +40,24 @@ do
fi
TORTURE_SUITE="`cat $i/../TORTURE_SUITE`"
kvm-recheck-${TORTURE_SUITE}.sh $i
- configcheck.sh $i/.config $i/ConfigFragment
- parse-build.sh $i/Make.out $configfile
- parse-rcutorture.sh $i/console.log $configfile
- parse-console.sh $i/console.log $configfile
- if test -r $i/Warnings
+ if test -f "$i/console.log"
then
- cat $i/Warnings
+ configcheck.sh $i/.config $i/ConfigFragment
+ parse-build.sh $i/Make.out $configfile
+ parse-torture.sh $i/console.log $configfile
+ parse-console.sh $i/console.log $configfile
+ if test -r $i/Warnings
+ then
+ cat $i/Warnings
+ fi
+ else
+ if test -f "$i/qemu-cmd"
+ then
+ print_bug qemu failed
+ else
+ print_bug Build failed
+ fi
+ echo " $i"
fi
done
done
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh
index 94b28bb..27e544e 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh
@@ -94,9 +94,17 @@ fi
# CONFIG_YENTA=n
if kvm-build.sh $config_template $builddir $T
then
+ QEMU="`identify_qemu $builddir/vmlinux`"
+ BOOT_IMAGE="`identify_boot_image $QEMU`"
cp $builddir/Make*.out $resdir
cp $builddir/.config $resdir
- cp $builddir/arch/x86/boot/bzImage $resdir
+ if test -n "$BOOT_IMAGE"
+ then
+ cp $builddir/$BOOT_IMAGE $resdir
+ else
+ echo No identifiable boot image, not running KVM, see $resdir.
+ echo Do the torture scripts know about your architecture?
+ fi
parse-build.sh $resdir/Make.out $title
if test -f $builddir.wait
then
@@ -104,6 +112,7 @@ then
fi
else
cp $builddir/Make*.out $resdir
+ cp $builddir/.config $resdir || :
echo Build failed, not running KVM, see $resdir.
if test -f $builddir.wait
then
@@ -124,9 +133,6 @@ cd $KVM
kstarttime=`awk 'BEGIN { print systime() }' < /dev/null`
echo ' ---' `date`: Starting kernel
-# Determine the appropriate flavor of qemu command.
-QEMU="`identify_qemu $builddir/vmlinux`"
-
# Generate -smp qemu argument.
qemu_args="-nographic $qemu_args"
cpu_count=`configNR_CPUS.sh $config_template`
@@ -151,27 +157,38 @@ boot_args="`configfrag_boot_params "$boot_args" "$config_template"`"
# Generate kernel-version-specific boot parameters
boot_args="`per_version_boot_params "$boot_args" $builddir/.config $seconds`"
-echo $QEMU $qemu_args -m 512 -kernel $builddir/arch/x86/boot/bzImage -append \"$qemu_append $boot_args\" > $resdir/qemu-cmd
-if test -n "$RCU_BUILDONLY"
+echo $QEMU $qemu_args -m 512 -kernel $builddir/$BOOT_IMAGE -append \"$qemu_append $boot_args\" > $resdir/qemu-cmd
+if test -n "$TORTURE_BUILDONLY"
then
echo Build-only run specified, boot/test omitted.
exit 0
fi
-$QEMU $qemu_args -m 512 -kernel $builddir/arch/x86/boot/bzImage -append "$qemu_append $boot_args" &
+( $QEMU $qemu_args -m 512 -kernel $builddir/$BOOT_IMAGE -append "$qemu_append $boot_args"; echo $? > $resdir/qemu-retval ) &
qemu_pid=$!
commandcompleted=0
echo Monitoring qemu job at pid $qemu_pid
-for ((i=0;i<$seconds;i++))
+while :
do
+ kruntime=`awk 'BEGIN { print systime() - '"$kstarttime"' }' < /dev/null`
if kill -0 $qemu_pid > /dev/null 2>&1
then
+ if test $kruntime -ge $seconds
+ then
+ break;
+ fi
sleep 1
else
commandcompleted=1
- kruntime=`awk 'BEGIN { print systime() - '"$kstarttime"' }' < /dev/null`
if test $kruntime -lt $seconds
then
echo Completed in $kruntime vs. $seconds >> $resdir/Warnings 2>&1
+ grep "^(qemu) qemu:" $resdir/kvm-test-1-run.sh.out >> $resdir/Warnings 2>&1
+ killpid="`sed -n "s/^(qemu) qemu: terminating on signal [0-9]* from pid \([0-9]*\).*$/\1/p" $resdir/Warnings`"
+ if test -n "$killpid"
+ then
+ echo "ps -fp $killpid" >> $resdir/Warnings 2>&1
+ ps -fp $killpid >> $resdir/Warnings 2>&1
+ fi
else
echo ' ---' `date`: Kernel done
fi
@@ -181,23 +198,25 @@ done
if test $commandcompleted -eq 0
then
echo Grace period for qemu job at pid $qemu_pid
- for ((i=0;i<=$grace;i++))
+ while :
do
+ kruntime=`awk 'BEGIN { print systime() - '"$kstarttime"' }' < /dev/null`
if kill -0 $qemu_pid > /dev/null 2>&1
then
- sleep 1
+ :
else
break
fi
- if test $i -eq $grace
+ if test $kruntime -ge $((seconds + grace))
then
- kruntime=`awk 'BEGIN { print systime() - '"$kstarttime"' }'`
echo "!!! Hang at $kruntime vs. $seconds seconds" >> $resdir/Warnings 2>&1
kill -KILL $qemu_pid
+ break
fi
+ sleep 1
done
fi
cp $builddir/console.log $resdir
-parse-${TORTURE_SUITE}torture.sh $resdir/console.log $title
+parse-torture.sh $resdir/console.log $title
parse-console.sh $resdir/console.log $title
diff --git a/tools/testing/selftests/rcutorture/bin/kvm.sh b/tools/testing/selftests/rcutorture/bin/kvm.sh
index 5a78cbf..40285c5 100644
--- a/tools/testing/selftests/rcutorture/bin/kvm.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm.sh
@@ -38,9 +38,10 @@ dur=30
dryrun=""
KVM="`pwd`/tools/testing/selftests/rcutorture"; export KVM
PATH=${KVM}/bin:$PATH; export PATH
-builddir="${KVM}/b1"
-RCU_INITRD="$KVM/initrd"; export RCU_INITRD
-RCU_KMAKE_ARG=""; export RCU_KMAKE_ARG
+TORTURE_DEFCONFIG=defconfig
+TORTURE_BOOT_IMAGE=""
+TORTURE_INITRD="$KVM/initrd"; export TORTURE_INITRD
+TORTURE_KMAKE_ARG=""
TORTURE_SUITE=rcu
resdir=""
configs=""
@@ -53,11 +54,12 @@ kversion=""
usage () {
echo "Usage: $scriptname optional arguments:"
echo " --bootargs kernel-boot-arguments"
- echo " --builddir absolute-pathname"
+ echo " --bootimage relative-path-to-kernel-boot-image"
echo " --buildonly"
echo " --configs \"config-file list\""
echo " --cpus N"
echo " --datestamp string"
+ echo " --defconfig string"
echo " --dryrun sched|script"
echo " --duration minutes"
echo " --interactive"
@@ -67,7 +69,6 @@ usage () {
echo " --no-initrd"
echo " --qemu-args qemu-system-..."
echo " --qemu-cmd qemu-system-..."
- echo " --relbuilddir relative-pathname"
echo " --results absolute-pathname"
echo " --torture rcu"
exit 1
@@ -78,17 +79,16 @@ do
case "$1" in
--bootargs)
checkarg --bootargs "(list of kernel boot arguments)" "$#" "$2" '.*' '^--'
- RCU_BOOTARGS="$2"
+ TORTURE_BOOTARGS="$2"
shift
;;
- --builddir)
- checkarg --builddir "(absolute pathname)" "$#" "$2" '^/' '^error'
- builddir=$2
- gotbuilddir=1
+ --bootimage)
+ checkarg --bootimage "(relative path to kernel boot image)" "$#" "$2" '[a-zA-Z0-9][a-zA-Z0-9_]*' '^--'
+ TORTURE_BOOT_IMAGE="$2"
shift
;;
--buildonly)
- RCU_BUILDONLY=1; export RCU_BUILDONLY
+ TORTURE_BUILDONLY=1
;;
--configs)
checkarg --configs "(list of config files)" "$#" "$2" '^[^/]*$' '^--'
@@ -105,6 +105,11 @@ do
ds=$2
shift
;;
+ --defconfig)
+ checkarg --defconfig "defconfigtype" "$#" "$2" '^[^/][^/]*$' '^--'
+ TORTURE_DEFCONFIG=$2
+ shift
+ ;;
--dryrun)
checkarg --dryrun "sched|script" $# "$2" 'sched\|script' '^--'
dryrun=$2
@@ -116,11 +121,11 @@ do
shift
;;
--interactive)
- RCU_QEMU_INTERACTIVE=1; export RCU_QEMU_INTERACTIVE
+ TORTURE_QEMU_INTERACTIVE=1; export TORTURE_QEMU_INTERACTIVE
;;
--kmake-arg)
checkarg --kmake-arg "(kernel make arguments)" $# "$2" '.*' '^error$'
- RCU_KMAKE_ARG="$2"; export RCU_KMAKE_ARG
+ TORTURE_KMAKE_ARG="$2"
shift
;;
--kversion)
@@ -130,27 +135,20 @@ do
;;
--mac)
checkarg --mac "(MAC address)" $# "$2" '^\([0-9a-fA-F]\{2\}:\)\{5\}[0-9a-fA-F]\{2\}$' error
- RCU_QEMU_MAC=$2; export RCU_QEMU_MAC
+ TORTURE_QEMU_MAC=$2
shift
;;
--no-initrd)
- RCU_INITRD=""; export RCU_INITRD
+ TORTURE_INITRD=""; export TORTURE_INITRD
;;
--qemu-args)
checkarg --qemu-args "-qemu args" $# "$2" '^-' '^error'
- RCU_QEMU_ARG="$2"
+ TORTURE_QEMU_ARG="$2"
shift
;;
--qemu-cmd)
checkarg --qemu-cmd "(qemu-system-...)" $# "$2" 'qemu-system-' '^--'
- RCU_QEMU_CMD="$2"; export RCU_QEMU_CMD
- shift
- ;;
- --relbuilddir)
- checkarg --relbuilddir "(relative pathname)" "$#" "$2" '^[^/]*$' '^--'
- relbuilddir=$2
- gotrelbuilddir=1
- builddir=${KVM}/${relbuilddir}
+ TORTURE_QEMU_CMD="$2"
shift
;;
--results)
@@ -184,30 +182,6 @@ then
resdir=$KVM/res
fi
-if test "$dryrun" = ""
-then
- if ! test -e $resdir
- then
- mkdir -p "$resdir" || :
- fi
- mkdir $resdir/$ds
-
- # Be noisy only if running the script.
- echo Results directory: $resdir/$ds
- echo $scriptname $args
-
- touch $resdir/$ds/log
- echo $scriptname $args >> $resdir/$ds/log
- echo ${TORTURE_SUITE} > $resdir/$ds/TORTURE_SUITE
-
- pwd > $resdir/$ds/testid.txt
- if test -d .git
- then
- git status >> $resdir/$ds/testid.txt
- git rev-parse HEAD >> $resdir/$ds/testid.txt
- fi
-fi
-
# Create a file of test-name/#cpus pairs, sorted by decreasing #cpus.
touch $T/cfgcpu
for CF in $configs
@@ -274,7 +248,39 @@ END {
# Generate a script to execute the tests in appropriate batches.
cat << ___EOF___ > $T/script
+CONFIGFRAG="$CONFIGFRAG"; export CONFIGFRAG
+KVM="$KVM"; export KVM
+KVPATH="$KVPATH"; export KVPATH
+PATH="$PATH"; export PATH
+TORTURE_BOOT_IMAGE="$TORTURE_BOOT_IMAGE"; export TORTURE_BOOT_IMAGE
+TORTURE_BUILDONLY="$TORTURE_BUILDONLY"; export TORTURE_BUILDONLY
+TORTURE_DEFCONFIG="$TORTURE_DEFCONFIG"; export TORTURE_DEFCONFIG
+TORTURE_INITRD="$TORTURE_INITRD"; export TORTURE_INITRD
+TORTURE_KMAKE_ARG="$TORTURE_KMAKE_ARG"; export TORTURE_KMAKE_ARG
+TORTURE_QEMU_CMD="$TORTURE_QEMU_CMD"; export TORTURE_QEMU_CMD
+TORTURE_QEMU_INTERACTIVE="$TORTURE_QEMU_INTERACTIVE"; export TORTURE_QEMU_INTERACTIVE
+TORTURE_QEMU_MAC="$TORTURE_QEMU_MAC"; export TORTURE_QEMU_MAC
TORTURE_SUITE="$TORTURE_SUITE"; export TORTURE_SUITE
+if ! test -e $resdir
+then
+ mkdir -p "$resdir" || :
+fi
+mkdir $resdir/$ds
+echo Results directory: $resdir/$ds
+echo $scriptname $args
+touch $resdir/$ds/log
+echo $scriptname $args >> $resdir/$ds/log
+echo ${TORTURE_SUITE} > $resdir/$ds/TORTURE_SUITE
+pwd > $resdir/$ds/testid.txt
+if test -d .git
+then
+ git status >> $resdir/$ds/testid.txt
+ git rev-parse HEAD >> $resdir/$ds/testid.txt
+ if ! git diff HEAD > $T/git-diff 2>&1
+ then
+ cp $T/git-diff $resdir/$ds
+ fi
+fi
___EOF___
awk < $T/cfgcpu.pack \
-v CONFIGDIR="$CONFIGFRAG/$kversion/" \
@@ -282,8 +288,8 @@ awk < $T/cfgcpu.pack \
-v ncpus=$cpus \
-v rd=$resdir/$ds/ \
-v dur=$dur \
- -v RCU_QEMU_ARG=$RCU_QEMU_ARG \
- -v RCU_BOOTARGS=$RCU_BOOTARGS \
+ -v TORTURE_QEMU_ARG="$TORTURE_QEMU_ARG" \
+ -v TORTURE_BOOTARGS="$TORTURE_BOOTARGS" \
'BEGIN {
i = 0;
}
@@ -320,7 +326,7 @@ function dump(first, pastlast)
print "touch " builddir ".wait";
print "mkdir " builddir " > /dev/null 2>&1 || :";
print "mkdir " rd cfr[jn] " || :";
- print "kvm-test-1-run.sh " CONFIGDIR cf[j], builddir, rd cfr[jn], dur " \"" RCU_QEMU_ARG "\" \"" RCU_BOOTARGS "\" > " rd cfr[jn] "/kvm-test-1-run.sh.out 2>&1 &"
+ print "kvm-test-1-run.sh " CONFIGDIR cf[j], builddir, rd cfr[jn], dur " \"" TORTURE_QEMU_ARG "\" \"" TORTURE_BOOTARGS "\" > " rd cfr[jn] "/kvm-test-1-run.sh.out 2>&1 &"
print "echo ", cfr[jn], cpusr[jn] ovf ": Waiting for build to complete. `date`";
print "echo ", cfr[jn], cpusr[jn] ovf ": Waiting for build to complete. `date` >> " rd "/log";
print "while test -f " builddir ".wait"
@@ -374,28 +380,26 @@ END {
dump(first, i);
}' >> $T/script
+cat << ___EOF___ >> $T/script
+echo
+echo
+echo " --- `date` Test summary:"
+echo Results directory: $resdir/$ds
+if test -z "$TORTURE_BUILDONLY"
+then
+ kvm-recheck.sh $resdir/$ds
+fi
+___EOF___
+
if test "$dryrun" = script
then
- # Dump out the script, but define the environment variables that
- # it needs to run standalone.
- echo CONFIGFRAG="$CONFIGFRAG; export CONFIGFRAG"
- echo KVM="$KVM; export KVM"
- echo KVPATH="$KVPATH; export KVPATH"
- echo PATH="$PATH; export PATH"
- echo RCU_BUILDONLY="$RCU_BUILDONLY; export RCU_BUILDONLY"
- echo RCU_INITRD="$RCU_INITRD; export RCU_INITRD"
- echo RCU_KMAKE_ARG="$RCU_KMAKE_ARG; export RCU_KMAKE_ARG"
- echo RCU_QEMU_CMD="$RCU_QEMU_CMD; export RCU_QEMU_CMD"
- echo RCU_QEMU_INTERACTIVE="$RCU_QEMU_INTERACTIVE; export RCU_QEMU_INTERACTIVE"
- echo RCU_QEMU_MAC="$RCU_QEMU_MAC; export RCU_QEMU_MAC"
- echo "mkdir -p "$resdir" || :"
- echo "mkdir $resdir/$ds"
cat $T/script
exit 0
elif test "$dryrun" = sched
then
# Extract the test run schedule from the script.
- egrep 'start batch|Starting build\.' $T/script |
+ egrep 'Start batch|Starting build\.' $T/script |
+ grep -v ">>" |
sed -e 's/:.*$//' -e 's/^echo //'
exit 0
else
@@ -404,9 +408,3 @@ else
fi
# Tracing: trace_event=rcu:rcu_grace_period,rcu:rcu_future_grace_period,rcu:rcu_grace_period_init,rcu:rcu_nocb_wake,rcu:rcu_preempt_task,rcu:rcu_unlock_preempted_task,rcu:rcu_quiescent_state_report,rcu:rcu_fqs,rcu:rcu_callback,rcu:rcu_kfree_callback,rcu:rcu_batch_start,rcu:rcu_invoke_callback,rcu:rcu_invoke_kfree_callback,rcu:rcu_batch_end,rcu:rcu_torture_read,rcu:rcu_barrier
-
-echo
-echo
-echo " --- `date` Test summary:"
-echo Results directory: $resdir/$ds
-kvm-recheck.sh $resdir/$ds
diff --git a/tools/testing/selftests/rcutorture/bin/parse-rcutorture.sh b/tools/testing/selftests/rcutorture/bin/parse-torture.sh
index dd0a275..3455560 100755
--- a/tools/testing/selftests/rcutorture/bin/parse-rcutorture.sh
+++ b/tools/testing/selftests/rcutorture/bin/parse-torture.sh
@@ -1,14 +1,14 @@
#!/bin/sh
#
-# Check the console output from an rcutorture run for goodness.
+# Check the console output from a torture run for goodness.
# The "file" is a pathname on the local system, and "title" is
# a text string for error-message purposes.
#
-# The file must contain rcutorture output, but can be interspersed
-# with other dmesg text.
+# The file must contain torture output, but can be interspersed
+# with other dmesg text, as in console-log output.
#
# Usage:
-# sh parse-rcutorture.sh file title
+# sh parse-torture.sh file title
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -28,7 +28,7 @@
#
# Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
-T=/tmp/parse-rcutorture.sh.$$
+T=/tmp/parse-torture.sh.$$
file="$1"
title="$2"
@@ -36,13 +36,13 @@ trap 'rm -f $T.seq' 0
. functions.sh
-# check for presence of rcutorture.txt file
+# check for presence of torture output file.
if test -f "$file" -a -r "$file"
then
:
else
- echo $title unreadable rcutorture.txt file: $file
+ echo $title unreadable torture output file: $file
exit 1
fi
@@ -76,9 +76,9 @@ BEGIN {
END {
if (badseq) {
if (badseqno1 == badseqno2 && badseqno2 == ver)
- print "RCU GP HANG at " ver " rcutorture stat " badseqnr;
+ print "GP HANG at " ver " torture stat " badseqnr;
else
- print "BAD SEQ " badseqno1 ":" badseqno2 " last:" ver " RCU version " badseqnr;
+ print "BAD SEQ " badseqno1 ":" badseqno2 " last:" ver " version " badseqnr;
}
}' > $T.seq
@@ -91,13 +91,13 @@ then
exit 2
fi
else
- if grep -q RCU_HOTPLUG $file
+ if grep -q "_HOTPLUG:" $file
then
print_warning HOTPLUG FAILURES $title `cat $T.seq`
echo " " $file
exit 3
fi
- echo $title no success message, `grep --binary-files=text 'ver:' $file | wc -l` successful RCU version messages
+ echo $title no success message, `grep --binary-files=text 'ver:' $file | wc -l` successful version messages
if test -s $T.seq
then
print_warning $title `cat $T.seq`
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE02-T b/tools/testing/selftests/rcutorture/configs/rcu/TREE02-T
new file mode 100644
index 0000000..61c8d9c
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE02-T
@@ -0,0 +1,25 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=8
+CONFIG_PREEMPT_NONE=n
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=y
+#CHECK#CONFIG_TREE_PREEMPT_RCU=y
+CONFIG_HZ_PERIODIC=n
+CONFIG_NO_HZ_IDLE=y
+CONFIG_NO_HZ_FULL=n
+CONFIG_RCU_FAST_NO_HZ=n
+CONFIG_RCU_TRACE=y
+CONFIG_HOTPLUG_CPU=n
+CONFIG_SUSPEND=n
+CONFIG_HIBERNATION=n
+CONFIG_RCU_FANOUT=3
+CONFIG_RCU_FANOUT_LEAF=3
+CONFIG_RCU_FANOUT_EXACT=n
+CONFIG_RCU_NOCB_CPU=n
+CONFIG_DEBUG_LOCK_ALLOC=y
+CONFIG_PROVE_LOCKING=n
+CONFIG_PROVE_RCU_DELAY=n
+CONFIG_RCU_CPU_STALL_INFO=n
+CONFIG_RCU_CPU_STALL_VERBOSE=y
+CONFIG_RCU_BOOST=n
+CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot
new file mode 100644
index 0000000..3b42b8b
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot
@@ -0,0 +1 @@
+rcutorture.torture_type=sched
diff --git a/tools/testing/selftests/sysctl/Makefile b/tools/testing/selftests/sysctl/Makefile
new file mode 100644
index 0000000..0a92ada
--- /dev/null
+++ b/tools/testing/selftests/sysctl/Makefile
@@ -0,0 +1,19 @@
+# Makefile for sysctl selftests.
+# Expects kernel.sysctl_writes_strict=1.
+
+# No binaries, but make sure arg-less "make" doesn't trigger "run_tests".
+all:
+
+# Allow specific tests to be selected.
+test_num:
+ @/bin/sh ./run_numerictests
+
+test_string:
+ @/bin/sh ./run_stringtests
+
+run_tests: all test_num test_string
+
+# Nothing to clean up.
+clean:
+
+.PHONY: all run_tests clean test_num test_string
diff --git a/tools/testing/selftests/sysctl/common_tests b/tools/testing/selftests/sysctl/common_tests
new file mode 100644
index 0000000..17d534b
--- /dev/null
+++ b/tools/testing/selftests/sysctl/common_tests
@@ -0,0 +1,109 @@
+#!/bin/sh
+
+TEST_FILE=$(mktemp)
+
+echo "== Testing sysctl behavior against ${TARGET} =="
+
+set_orig()
+{
+ echo "${ORIG}" > "${TARGET}"
+}
+
+set_test()
+{
+ echo "${TEST_STR}" > "${TARGET}"
+}
+
+verify()
+{
+ local seen
+ seen=$(cat "$1")
+ if [ "${seen}" != "${TEST_STR}" ]; then
+ return 1
+ fi
+ return 0
+}
+
+trap 'set_orig; rm -f "${TEST_FILE}"' EXIT
+
+rc=0
+
+echo -n "Writing test file ... "
+echo "${TEST_STR}" > "${TEST_FILE}"
+if ! verify "${TEST_FILE}"; then
+ echo "FAIL" >&2
+ exit 1
+else
+ echo "ok"
+fi
+
+echo -n "Checking sysctl is not set to test value ... "
+if verify "${TARGET}"; then
+ echo "FAIL" >&2
+ exit 1
+else
+ echo "ok"
+fi
+
+echo -n "Writing sysctl from shell ... "
+set_test
+if ! verify "${TARGET}"; then
+ echo "FAIL" >&2
+ exit 1
+else
+ echo "ok"
+fi
+
+echo -n "Resetting sysctl to original value ... "
+set_orig
+if verify "${TARGET}"; then
+ echo "FAIL" >&2
+ exit 1
+else
+ echo "ok"
+fi
+
+# Now that we've validated the sanity of "set_test" and "set_orig",
+# we can use those functions to set starting states before running
+# specific behavioral tests.
+
+echo -n "Writing entire sysctl in single write ... "
+set_orig
+dd if="${TEST_FILE}" of="${TARGET}" bs=4096 2>/dev/null
+if ! verify "${TARGET}"; then
+ echo "FAIL" >&2
+ rc=1
+else
+ echo "ok"
+fi
+
+echo -n "Writing middle of sysctl after synchronized seek ... "
+set_test
+dd if="${TEST_FILE}" of="${TARGET}" bs=1 seek=1 skip=1 2>/dev/null
+if ! verify "${TARGET}"; then
+ echo "FAIL" >&2
+ rc=1
+else
+ echo "ok"
+fi
+
+echo -n "Writing beyond end of sysctl ... "
+set_orig
+dd if="${TEST_FILE}" of="${TARGET}" bs=20 seek=2 2>/dev/null
+if verify "${TARGET}"; then
+ echo "FAIL" >&2
+ rc=1
+else
+ echo "ok"
+fi
+
+echo -n "Writing sysctl with multiple long writes ... "
+set_orig
+(perl -e 'print "A" x 50;'; echo "${TEST_STR}") | \
+ dd of="${TARGET}" bs=50 2>/dev/null
+if verify "${TARGET}"; then
+ echo "FAIL" >&2
+ rc=1
+else
+ echo "ok"
+fi
diff --git a/tools/testing/selftests/sysctl/run_numerictests b/tools/testing/selftests/sysctl/run_numerictests
new file mode 100644
index 0000000..8510f93
--- /dev/null
+++ b/tools/testing/selftests/sysctl/run_numerictests
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+SYSCTL="/proc/sys"
+TARGET="${SYSCTL}/vm/swappiness"
+ORIG=$(cat "${TARGET}")
+TEST_STR=$(( $ORIG + 1 ))
+
+. ./common_tests
+
+exit $rc
diff --git a/tools/testing/selftests/sysctl/run_stringtests b/tools/testing/selftests/sysctl/run_stringtests
new file mode 100644
index 0000000..90a9293
--- /dev/null
+++ b/tools/testing/selftests/sysctl/run_stringtests
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+SYSCTL="/proc/sys"
+TARGET="${SYSCTL}/kernel/domainname"
+ORIG=$(cat "${TARGET}")
+TEST_STR="Testing sysctl"
+
+. ./common_tests
+
+# Only string sysctls support seeking/appending.
+MAXLEN=65
+
+echo -n "Writing entire sysctl in short writes ... "
+set_orig
+dd if="${TEST_FILE}" of="${TARGET}" bs=1 2>/dev/null
+if ! verify "${TARGET}"; then
+ echo "FAIL" >&2
+ rc=1
+else
+ echo "ok"
+fi
+
+echo -n "Writing middle of sysctl after unsynchronized seek ... "
+set_test
+dd if="${TEST_FILE}" of="${TARGET}" bs=1 seek=1 2>/dev/null
+if verify "${TARGET}"; then
+ echo "FAIL" >&2
+ rc=1
+else
+ echo "ok"
+fi
+
+echo -n "Checking sysctl maxlen is at least $MAXLEN ... "
+set_orig
+perl -e 'print "A" x ('"${MAXLEN}"'-2), "B";' | \
+ dd of="${TARGET}" bs="${MAXLEN}" 2>/dev/null
+if ! grep -q B "${TARGET}"; then
+ echo "FAIL" >&2
+ rc=1
+else
+ echo "ok"
+fi
+
+echo -n "Checking sysctl keeps original string on overflow append ... "
+set_orig
+perl -e 'print "A" x ('"${MAXLEN}"'-1), "B";' | \
+ dd of="${TARGET}" bs=$(( MAXLEN - 1 )) 2>/dev/null
+if grep -q B "${TARGET}"; then
+ echo "FAIL" >&2
+ rc=1
+else
+ echo "ok"
+fi
+
+echo -n "Checking sysctl stays NULL terminated on write ... "
+set_orig
+perl -e 'print "A" x ('"${MAXLEN}"'-1), "B";' | \
+ dd of="${TARGET}" bs="${MAXLEN}" 2>/dev/null
+if grep -q B "${TARGET}"; then
+ echo "FAIL" >&2
+ rc=1
+else
+ echo "ok"
+fi
+
+echo -n "Checking sysctl stays NULL terminated on overwrite ... "
+set_orig
+perl -e 'print "A" x ('"${MAXLEN}"'-1), "BB";' | \
+ dd of="${TARGET}" bs=$(( $MAXLEN + 1 )) 2>/dev/null
+if grep -q B "${TARGET}"; then
+ echo "FAIL" >&2
+ rc=1
+else
+ echo "ok"
+fi
+
+exit $rc
diff --git a/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c
new file mode 100644
index 0000000..87216a0
--- /dev/null
+++ b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c
@@ -0,0 +1,349 @@
+#define _BSD_SOURCE /* for endian.h */
+
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <sys/eventfd.h>
+
+#include "libaio.h"
+#define IOCB_FLAG_RESFD (1 << 0)
+
+#include <linux/usb/functionfs.h>
+
+#define BUF_LEN 8192
+#define BUFS_MAX 128
+#define AIO_MAX (BUFS_MAX*2)
+
+/******************** Descriptors and Strings *******************************/
+
+static const struct {
+ struct usb_functionfs_descs_head header;
+ struct {
+ struct usb_interface_descriptor intf;
+ struct usb_endpoint_descriptor_no_audio bulk_sink;
+ struct usb_endpoint_descriptor_no_audio bulk_source;
+ } __attribute__ ((__packed__)) fs_descs, hs_descs;
+} __attribute__ ((__packed__)) descriptors = {
+ .header = {
+ .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC),
+ .length = htole32(sizeof(descriptors)),
+ .fs_count = 3,
+ .hs_count = 3,
+ },
+ .fs_descs = {
+ .intf = {
+ .bLength = sizeof(descriptors.fs_descs.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .iInterface = 1,
+ },
+ .bulk_sink = {
+ .bLength = sizeof(descriptors.fs_descs.bulk_sink),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ },
+ .bulk_source = {
+ .bLength = sizeof(descriptors.fs_descs.bulk_source),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ },
+ },
+ .hs_descs = {
+ .intf = {
+ .bLength = sizeof(descriptors.hs_descs.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .iInterface = 1,
+ },
+ .bulk_sink = {
+ .bLength = sizeof(descriptors.hs_descs.bulk_sink),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = htole16(512),
+ },
+ .bulk_source = {
+ .bLength = sizeof(descriptors.hs_descs.bulk_source),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = htole16(512),
+ },
+ },
+};
+
+#define STR_INTERFACE "AIO Test"
+
+static const struct {
+ struct usb_functionfs_strings_head header;
+ struct {
+ __le16 code;
+ const char str1[sizeof(STR_INTERFACE)];
+ } __attribute__ ((__packed__)) lang0;
+} __attribute__ ((__packed__)) strings = {
+ .header = {
+ .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
+ .length = htole32(sizeof(strings)),
+ .str_count = htole32(1),
+ .lang_count = htole32(1),
+ },
+ .lang0 = {
+ htole16(0x0409), /* en-us */
+ STR_INTERFACE,
+ },
+};
+
+/********************** Buffer structure *******************************/
+
+struct io_buffer {
+ struct iocb **iocb;
+ unsigned char **buf;
+ unsigned cnt;
+ unsigned len;
+ unsigned requested;
+};
+
+/******************** Endpoints handling *******************************/
+
+static void display_event(struct usb_functionfs_event *event)
+{
+ static const char *const names[] = {
+ [FUNCTIONFS_BIND] = "BIND",
+ [FUNCTIONFS_UNBIND] = "UNBIND",
+ [FUNCTIONFS_ENABLE] = "ENABLE",
+ [FUNCTIONFS_DISABLE] = "DISABLE",
+ [FUNCTIONFS_SETUP] = "SETUP",
+ [FUNCTIONFS_SUSPEND] = "SUSPEND",
+ [FUNCTIONFS_RESUME] = "RESUME",
+ };
+ switch (event->type) {
+ case FUNCTIONFS_BIND:
+ case FUNCTIONFS_UNBIND:
+ case FUNCTIONFS_ENABLE:
+ case FUNCTIONFS_DISABLE:
+ case FUNCTIONFS_SETUP:
+ case FUNCTIONFS_SUSPEND:
+ case FUNCTIONFS_RESUME:
+ printf("Event %s\n", names[event->type]);
+ }
+}
+
+static void handle_ep0(int ep0, bool *ready)
+{
+ int ret;
+ struct usb_functionfs_event event;
+
+ ret = read(ep0, &event, sizeof(event));
+ if (!ret) {
+ perror("unable to read event from ep0");
+ return;
+ }
+ display_event(&event);
+ switch (event.type) {
+ case FUNCTIONFS_SETUP:
+ if (event.u.setup.bRequestType & USB_DIR_IN)
+ write(ep0, NULL, 0);
+ else
+ read(ep0, NULL, 0);
+ break;
+
+ case FUNCTIONFS_ENABLE:
+ *ready = true;
+ break;
+
+ case FUNCTIONFS_DISABLE:
+ *ready = false;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void init_bufs(struct io_buffer *iobuf, unsigned n, unsigned len)
+{
+ unsigned i;
+ iobuf->buf = malloc(n*sizeof(*iobuf->buf));
+ iobuf->iocb = malloc(n*sizeof(*iobuf->iocb));
+ iobuf->cnt = n;
+ iobuf->len = len;
+ iobuf->requested = 0;
+ for (i = 0; i < n; ++i) {
+ iobuf->buf[i] = malloc(len*sizeof(**iobuf->buf));
+ iobuf->iocb[i] = malloc(sizeof(**iobuf->iocb));
+ }
+ iobuf->cnt = n;
+}
+
+void delete_bufs(struct io_buffer *iobuf)
+{
+ unsigned i;
+ for (i = 0; i < iobuf->cnt; ++i) {
+ free(iobuf->buf[i]);
+ free(iobuf->iocb[i]);
+ }
+ free(iobuf->buf);
+ free(iobuf->iocb);
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ unsigned i, j;
+ char *ep_path;
+
+ int ep0, ep1;
+
+ io_context_t ctx;
+
+ int evfd;
+ fd_set rfds;
+
+ struct io_buffer iobuf[2];
+ int actual = 0;
+ bool ready;
+
+ if (argc != 2) {
+ printf("ffs directory not specified!\n");
+ return 1;
+ }
+
+ ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);
+ if (!ep_path) {
+ perror("malloc");
+ return 1;
+ }
+
+ /* open endpoint files */
+ sprintf(ep_path, "%s/ep0", argv[1]);
+ ep0 = open(ep_path, O_RDWR);
+ if (ep0 < 0) {
+ perror("unable to open ep0");
+ return 1;
+ }
+ if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {
+ perror("unable do write descriptors");
+ return 1;
+ }
+ if (write(ep0, &strings, sizeof(strings)) < 0) {
+ perror("unable to write strings");
+ return 1;
+ }
+ sprintf(ep_path, "%s/ep1", argv[1]);
+ ep1 = open(ep_path, O_RDWR);
+ if (ep1 < 0) {
+ perror("unable to open ep1");
+ return 1;
+ }
+
+ free(ep_path);
+
+ memset(&ctx, 0, sizeof(ctx));
+ /* setup aio context to handle up to AIO_MAX requests */
+ if (io_setup(AIO_MAX, &ctx) < 0) {
+ perror("unable to setup aio");
+ return 1;
+ }
+
+ evfd = eventfd(0, 0);
+ if (evfd < 0) {
+ perror("unable to open eventfd");
+ return 1;
+ }
+
+ for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i)
+ init_bufs(&iobuf[i], BUFS_MAX, BUF_LEN);
+
+ while (1) {
+ FD_ZERO(&rfds);
+ FD_SET(ep0, &rfds);
+ FD_SET(evfd, &rfds);
+
+ ret = select(((ep0 > evfd) ? ep0 : evfd)+1,
+ &rfds, NULL, NULL, NULL);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("select");
+ break;
+ }
+
+ if (FD_ISSET(ep0, &rfds))
+ handle_ep0(ep0, &ready);
+
+ /* we are waiting for function ENABLE */
+ if (!ready)
+ continue;
+
+ /*
+ * when we're preparing new data to submit,
+ * second buffer being transmitted
+ */
+ for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i) {
+ if (iobuf[i].requested)
+ continue;
+ /* prepare requests */
+ for (j = 0; j < iobuf[i].cnt; ++j) {
+ io_prep_pwrite(iobuf[i].iocb[j], ep1,
+ iobuf[i].buf[j],
+ iobuf[i].len, 0);
+ /* enable eventfd notification */
+ iobuf[i].iocb[j]->u.c.flags |= IOCB_FLAG_RESFD;
+ iobuf[i].iocb[j]->u.c.resfd = evfd;
+ }
+ /* submit table of requests */
+ ret = io_submit(ctx, iobuf[i].cnt, iobuf[i].iocb);
+ if (ret >= 0) {
+ iobuf[i].requested = ret;
+ printf("submit: %d requests buf: %d\n", ret, i);
+ } else
+ perror("unable to submit reqests");
+ }
+
+ /* if event is ready to read */
+ if (!FD_ISSET(evfd, &rfds))
+ continue;
+
+ uint64_t ev_cnt;
+ ret = read(evfd, &ev_cnt, sizeof(ev_cnt));
+ if (ret < 0) {
+ perror("unable to read eventfd");
+ break;
+ }
+
+ struct io_event e[BUFS_MAX];
+ /* we read aio events */
+ ret = io_getevents(ctx, 1, BUFS_MAX, e, NULL);
+ if (ret > 0) /* if we got events */
+ iobuf[actual].requested -= ret;
+
+ /* if all req's from iocb completed */
+ if (!iobuf[actual].requested)
+ actual = (actual + 1)%(sizeof(iobuf)/sizeof(*iobuf));
+ }
+
+ /* free resources */
+
+ for (i = 0; i < sizeof(iobuf)/sizeof(*iobuf); ++i)
+ delete_bufs(&iobuf[i]);
+ io_destroy(ctx);
+
+ close(ep1);
+ close(ep0);
+
+ return 0;
+}
diff --git a/tools/usb/ffs-aio-example/multibuff/host_app/Makefile b/tools/usb/ffs-aio-example/multibuff/host_app/Makefile
new file mode 100644
index 0000000..8c4a6f0
--- /dev/null
+++ b/tools/usb/ffs-aio-example/multibuff/host_app/Makefile
@@ -0,0 +1,13 @@
+CC = gcc
+LIBUSB_CFLAGS = $(shell pkg-config --cflags libusb-1.0)
+LIBUSB_LIBS = $(shell pkg-config --libs libusb-1.0)
+WARNINGS = -Wall -Wextra
+CFLAGS = $(LIBUSB_CFLAGS) $(WARNINGS)
+LDFLAGS = $(LIBUSB_LIBS)
+
+all: test
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
+
+clean:
+ $(RM) test
diff --git a/tools/usb/ffs-aio-example/multibuff/host_app/test.c b/tools/usb/ffs-aio-example/multibuff/host_app/test.c
new file mode 100644
index 0000000..b0ad874
--- /dev/null
+++ b/tools/usb/ffs-aio-example/multibuff/host_app/test.c
@@ -0,0 +1,146 @@
+#include <libusb.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define VENDOR 0x1d6b
+#define PRODUCT 0x0105
+
+/* endpoints indexes */
+
+#define EP_BULK_IN (1 | LIBUSB_ENDPOINT_IN)
+#define EP_BULK_OUT (2 | LIBUSB_ENDPOINT_OUT)
+
+#define BUF_LEN 8192
+
+/*
+ * struct test_state - describes test program state
+ * @list: list of devices returned by libusb_get_device_list function
+ * @found: pointer to struct describing tested device
+ * @ctx: context, set to NULL
+ * @handle: handle of tested device
+ * @attached: indicates that device was attached to kernel, and has to be
+ * reattached at the end of test program
+ */
+
+struct test_state {
+ libusb_device *found;
+ libusb_context *ctx;
+ libusb_device_handle *handle;
+ int attached;
+};
+
+/*
+ * test_init - initialize test program
+ */
+
+int test_init(struct test_state *state)
+{
+ int i, ret;
+ ssize_t cnt;
+ libusb_device **list;
+
+ state->found = NULL;
+ state->ctx = NULL;
+ state->handle = NULL;
+ state->attached = 0;
+
+ ret = libusb_init(&state->ctx);
+ if (ret) {
+ printf("cannot init libusb: %s\n", libusb_error_name(ret));
+ return 1;
+ }
+
+ cnt = libusb_get_device_list(state->ctx, &list);
+ if (cnt <= 0) {
+ printf("no devices found\n");
+ goto error1;
+ }
+
+ for (i = 0; i < cnt; ++i) {
+ libusb_device *dev = list[i];
+ struct libusb_device_descriptor desc;
+ ret = libusb_get_device_descriptor(dev, &desc);
+ if (ret) {
+ printf("unable to get device descriptor: %s\n",
+ libusb_error_name(ret));
+ goto error2;
+ }
+ if (desc.idVendor == VENDOR && desc.idProduct == PRODUCT) {
+ state->found = dev;
+ break;
+ }
+ }
+
+ if (!state->found) {
+ printf("no devices found\n");
+ goto error2;
+ }
+
+ ret = libusb_open(state->found, &state->handle);
+ if (ret) {
+ printf("cannot open device: %s\n", libusb_error_name(ret));
+ goto error2;
+ }
+
+ if (libusb_claim_interface(state->handle, 0)) {
+ ret = libusb_detach_kernel_driver(state->handle, 0);
+ if (ret) {
+ printf("unable to detach kernel driver: %s\n",
+ libusb_error_name(ret));
+ goto error3;
+ }
+ state->attached = 1;
+ ret = libusb_claim_interface(state->handle, 0);
+ if (ret) {
+ printf("cannot claim interface: %s\n",
+ libusb_error_name(ret));
+ goto error4;
+ }
+ }
+
+ return 0;
+
+error4:
+ if (state->attached == 1)
+ libusb_attach_kernel_driver(state->handle, 0);
+
+error3:
+ libusb_close(state->handle);
+
+error2:
+ libusb_free_device_list(list, 1);
+
+error1:
+ libusb_exit(state->ctx);
+ return 1;
+}
+
+/*
+ * test_exit - cleanup test program
+ */
+
+void test_exit(struct test_state *state)
+{
+ libusb_release_interface(state->handle, 0);
+ if (state->attached == 1)
+ libusb_attach_kernel_driver(state->handle, 0);
+ libusb_close(state->handle);
+ libusb_exit(state->ctx);
+}
+
+int main(void)
+{
+ struct test_state state;
+
+ if (test_init(&state))
+ return 1;
+
+ while (1) {
+ static unsigned char buffer[BUF_LEN];
+ int bytes;
+ libusb_bulk_transfer(state.handle, EP_BULK_IN, buffer, BUF_LEN,
+ &bytes, 500);
+ }
+ test_exit(&state);
+}
diff --git a/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c
new file mode 100644
index 0000000..f558664
--- /dev/null
+++ b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c
@@ -0,0 +1,335 @@
+#define _BSD_SOURCE /* for endian.h */
+
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <sys/eventfd.h>
+
+#include "libaio.h"
+#define IOCB_FLAG_RESFD (1 << 0)
+
+#include <linux/usb/functionfs.h>
+
+#define BUF_LEN 8192
+
+/******************** Descriptors and Strings *******************************/
+
+static const struct {
+ struct usb_functionfs_descs_head header;
+ struct {
+ struct usb_interface_descriptor intf;
+ struct usb_endpoint_descriptor_no_audio bulk_sink;
+ struct usb_endpoint_descriptor_no_audio bulk_source;
+ } __attribute__ ((__packed__)) fs_descs, hs_descs;
+} __attribute__ ((__packed__)) descriptors = {
+ .header = {
+ .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC),
+ .length = htole32(sizeof(descriptors)),
+ .fs_count = 3,
+ .hs_count = 3,
+ },
+ .fs_descs = {
+ .intf = {
+ .bLength = sizeof(descriptors.fs_descs.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .iInterface = 1,
+ },
+ .bulk_sink = {
+ .bLength = sizeof(descriptors.fs_descs.bulk_sink),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ },
+ .bulk_source = {
+ .bLength = sizeof(descriptors.fs_descs.bulk_source),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ },
+ },
+ .hs_descs = {
+ .intf = {
+ .bLength = sizeof(descriptors.hs_descs.intf),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .iInterface = 1,
+ },
+ .bulk_sink = {
+ .bLength = sizeof(descriptors.hs_descs.bulk_sink),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ },
+ .bulk_source = {
+ .bLength = sizeof(descriptors.hs_descs.bulk_source),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ },
+ },
+};
+
+#define STR_INTERFACE "AIO Test"
+
+static const struct {
+ struct usb_functionfs_strings_head header;
+ struct {
+ __le16 code;
+ const char str1[sizeof(STR_INTERFACE)];
+ } __attribute__ ((__packed__)) lang0;
+} __attribute__ ((__packed__)) strings = {
+ .header = {
+ .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
+ .length = htole32(sizeof(strings)),
+ .str_count = htole32(1),
+ .lang_count = htole32(1),
+ },
+ .lang0 = {
+ htole16(0x0409), /* en-us */
+ STR_INTERFACE,
+ },
+};
+
+/******************** Endpoints handling *******************************/
+
+static void display_event(struct usb_functionfs_event *event)
+{
+ static const char *const names[] = {
+ [FUNCTIONFS_BIND] = "BIND",
+ [FUNCTIONFS_UNBIND] = "UNBIND",
+ [FUNCTIONFS_ENABLE] = "ENABLE",
+ [FUNCTIONFS_DISABLE] = "DISABLE",
+ [FUNCTIONFS_SETUP] = "SETUP",
+ [FUNCTIONFS_SUSPEND] = "SUSPEND",
+ [FUNCTIONFS_RESUME] = "RESUME",
+ };
+ switch (event->type) {
+ case FUNCTIONFS_BIND:
+ case FUNCTIONFS_UNBIND:
+ case FUNCTIONFS_ENABLE:
+ case FUNCTIONFS_DISABLE:
+ case FUNCTIONFS_SETUP:
+ case FUNCTIONFS_SUSPEND:
+ case FUNCTIONFS_RESUME:
+ printf("Event %s\n", names[event->type]);
+ }
+}
+
+static void handle_ep0(int ep0, bool *ready)
+{
+ struct usb_functionfs_event event;
+ int ret;
+
+ struct pollfd pfds[1];
+ pfds[0].fd = ep0;
+ pfds[0].events = POLLIN;
+
+ ret = poll(pfds, 1, 0);
+
+ if (ret && (pfds[0].revents & POLLIN)) {
+ ret = read(ep0, &event, sizeof(event));
+ if (!ret) {
+ perror("unable to read event from ep0");
+ return;
+ }
+ display_event(&event);
+ switch (event.type) {
+ case FUNCTIONFS_SETUP:
+ if (event.u.setup.bRequestType & USB_DIR_IN)
+ write(ep0, NULL, 0);
+ else
+ read(ep0, NULL, 0);
+ break;
+
+ case FUNCTIONFS_ENABLE:
+ *ready = true;
+ break;
+
+ case FUNCTIONFS_DISABLE:
+ *ready = false;
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int i, ret;
+ char *ep_path;
+
+ int ep0;
+ int ep[2];
+
+ io_context_t ctx;
+
+ int evfd;
+ fd_set rfds;
+
+ char *buf_in, *buf_out;
+ struct iocb *iocb_in, *iocb_out;
+ int req_in = 0, req_out = 0;
+ bool ready;
+
+ if (argc != 2) {
+ printf("ffs directory not specified!\n");
+ return 1;
+ }
+
+ ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);
+ if (!ep_path) {
+ perror("malloc");
+ return 1;
+ }
+
+ /* open endpoint files */
+ sprintf(ep_path, "%s/ep0", argv[1]);
+ ep0 = open(ep_path, O_RDWR);
+ if (ep0 < 0) {
+ perror("unable to open ep0");
+ return 1;
+ }
+ if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {
+ perror("unable do write descriptors");
+ return 1;
+ }
+ if (write(ep0, &strings, sizeof(strings)) < 0) {
+ perror("unable to write strings");
+ return 1;
+ }
+ for (i = 0; i < 2; ++i) {
+ sprintf(ep_path, "%s/ep%d", argv[1], i+1);
+ ep[i] = open(ep_path, O_RDWR);
+ if (ep[i] < 0) {
+ printf("unable to open ep%d: %s\n", i+1,
+ strerror(errno));
+ return 1;
+ }
+ }
+
+ free(ep_path);
+
+ memset(&ctx, 0, sizeof(ctx));
+ /* setup aio context to handle up to 2 requests */
+ if (io_setup(2, &ctx) < 0) {
+ perror("unable to setup aio");
+ return 1;
+ }
+
+ evfd = eventfd(0, 0);
+ if (evfd < 0) {
+ perror("unable to open eventfd");
+ return 1;
+ }
+
+ /* alloc buffers and requests */
+ buf_in = malloc(BUF_LEN);
+ buf_out = malloc(BUF_LEN);
+ iocb_in = malloc(sizeof(*iocb_in));
+ iocb_out = malloc(sizeof(*iocb_out));
+
+ while (1) {
+ FD_ZERO(&rfds);
+ FD_SET(ep0, &rfds);
+ FD_SET(evfd, &rfds);
+
+ ret = select(((ep0 > evfd) ? ep0 : evfd)+1,
+ &rfds, NULL, NULL, NULL);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("select");
+ break;
+ }
+
+ if (FD_ISSET(ep0, &rfds))
+ handle_ep0(ep0, &ready);
+
+ /* we are waiting for function ENABLE */
+ if (!ready)
+ continue;
+
+ /* if something was submitted we wait for event */
+ if (FD_ISSET(evfd, &rfds)) {
+ uint64_t ev_cnt;
+ ret = read(evfd, &ev_cnt, sizeof(ev_cnt));
+ if (ret < 0) {
+ perror("unable to read eventfd");
+ break;
+ }
+
+ struct io_event e[2];
+ /* we wait for one event */
+ ret = io_getevents(ctx, 1, 2, e, NULL);
+ /* if we got event */
+ for (i = 0; i < ret; ++i) {
+ if (e[i].obj->aio_fildes == ep[0]) {
+ printf("ev=in; ret=%lu\n", e[i].res);
+ req_in = 0;
+ } else if (e[i].obj->aio_fildes == ep[1]) {
+ printf("ev=out; ret=%lu\n", e[i].res);
+ req_out = 0;
+ }
+ }
+ }
+
+ if (!req_in) { /* if IN transfer not requested*/
+ /* prepare write request */
+ io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0);
+ /* enable eventfd notification */
+ iocb_in->u.c.flags |= IOCB_FLAG_RESFD;
+ iocb_in->u.c.resfd = evfd;
+ /* submit table of requests */
+ ret = io_submit(ctx, 1, &iocb_in);
+ if (ret >= 0) { /* if ret > 0 request is queued */
+ req_in = 1;
+ printf("submit: in\n");
+ } else
+ perror("unable to submit request");
+ }
+ if (!req_out) { /* if OUT transfer not requested */
+ /* prepare read request */
+ io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0);
+ /* enable eventfs notification */
+ iocb_out->u.c.flags |= IOCB_FLAG_RESFD;
+ iocb_out->u.c.resfd = evfd;
+ /* submit table of requests */
+ ret = io_submit(ctx, 1, &iocb_out);
+ if (ret >= 0) { /* if ret > 0 request is queued */
+ req_out = 1;
+ printf("submit: out\n");
+ } else
+ perror("unable to submit request");
+ }
+ }
+
+ /* free resources */
+
+ io_destroy(ctx);
+
+ free(buf_in);
+ free(buf_out);
+ free(iocb_in);
+ free(iocb_out);
+
+ for (i = 0; i < 2; ++i)
+ close(ep[i]);
+ close(ep0);
+
+ return 0;
+}
diff --git a/tools/usb/ffs-aio-example/simple/host_app/Makefile b/tools/usb/ffs-aio-example/simple/host_app/Makefile
new file mode 100644
index 0000000..8c4a6f0
--- /dev/null
+++ b/tools/usb/ffs-aio-example/simple/host_app/Makefile
@@ -0,0 +1,13 @@
+CC = gcc
+LIBUSB_CFLAGS = $(shell pkg-config --cflags libusb-1.0)
+LIBUSB_LIBS = $(shell pkg-config --libs libusb-1.0)
+WARNINGS = -Wall -Wextra
+CFLAGS = $(LIBUSB_CFLAGS) $(WARNINGS)
+LDFLAGS = $(LIBUSB_LIBS)
+
+all: test
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
+
+clean:
+ $(RM) test
diff --git a/tools/usb/ffs-aio-example/simple/host_app/test.c b/tools/usb/ffs-aio-example/simple/host_app/test.c
new file mode 100644
index 0000000..64b6a57
--- /dev/null
+++ b/tools/usb/ffs-aio-example/simple/host_app/test.c
@@ -0,0 +1,148 @@
+#include <libusb.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define VENDOR 0x1d6b
+#define PRODUCT 0x0105
+
+/* endpoints indexes */
+
+#define EP_BULK_IN (1 | LIBUSB_ENDPOINT_IN)
+#define EP_BULK_OUT (2 | LIBUSB_ENDPOINT_OUT)
+
+#define BUF_LEN 8192
+
+/*
+ * struct test_state - describes test program state
+ * @list: list of devices returned by libusb_get_device_list function
+ * @found: pointer to struct describing tested device
+ * @ctx: context, set to NULL
+ * @handle: handle of tested device
+ * @attached: indicates that device was attached to kernel, and has to be
+ * reattached at the end of test program
+ */
+
+struct test_state {
+ libusb_device *found;
+ libusb_context *ctx;
+ libusb_device_handle *handle;
+ int attached;
+};
+
+/*
+ * test_init - initialize test program
+ */
+
+int test_init(struct test_state *state)
+{
+ int i, ret;
+ ssize_t cnt;
+ libusb_device **list;
+
+ state->found = NULL;
+ state->ctx = NULL;
+ state->handle = NULL;
+ state->attached = 0;
+
+ ret = libusb_init(&state->ctx);
+ if (ret) {
+ printf("cannot init libusb: %s\n", libusb_error_name(ret));
+ return 1;
+ }
+
+ cnt = libusb_get_device_list(state->ctx, &list);
+ if (cnt <= 0) {
+ printf("no devices found\n");
+ goto error1;
+ }
+
+ for (i = 0; i < cnt; ++i) {
+ libusb_device *dev = list[i];
+ struct libusb_device_descriptor desc;
+ ret = libusb_get_device_descriptor(dev, &desc);
+ if (ret) {
+ printf("unable to get device descriptor: %s\n",
+ libusb_error_name(ret));
+ goto error2;
+ }
+ if (desc.idVendor == VENDOR && desc.idProduct == PRODUCT) {
+ state->found = dev;
+ break;
+ }
+ }
+
+ if (!state->found) {
+ printf("no devices found\n");
+ goto error2;
+ }
+
+ ret = libusb_open(state->found, &state->handle);
+ if (ret) {
+ printf("cannot open device: %s\n", libusb_error_name(ret));
+ goto error2;
+ }
+
+ if (libusb_claim_interface(state->handle, 0)) {
+ ret = libusb_detach_kernel_driver(state->handle, 0);
+ if (ret) {
+ printf("unable to detach kernel driver: %s\n",
+ libusb_error_name(ret));
+ goto error3;
+ }
+ state->attached = 1;
+ ret = libusb_claim_interface(state->handle, 0);
+ if (ret) {
+ printf("cannot claim interface: %s\n",
+ libusb_error_name(ret));
+ goto error4;
+ }
+ }
+
+ return 0;
+
+error4:
+ if (state->attached == 1)
+ libusb_attach_kernel_driver(state->handle, 0);
+
+error3:
+ libusb_close(state->handle);
+
+error2:
+ libusb_free_device_list(list, 1);
+
+error1:
+ libusb_exit(state->ctx);
+ return 1;
+}
+
+/*
+ * test_exit - cleanup test program
+ */
+
+void test_exit(struct test_state *state)
+{
+ libusb_release_interface(state->handle, 0);
+ if (state->attached == 1)
+ libusb_attach_kernel_driver(state->handle, 0);
+ libusb_close(state->handle);
+ libusb_exit(state->ctx);
+}
+
+int main(void)
+{
+ struct test_state state;
+
+ if (test_init(&state))
+ return 1;
+
+ while (1) {
+ static unsigned char buffer[BUF_LEN];
+ int bytes;
+ libusb_bulk_transfer(state.handle, EP_BULK_IN, buffer, BUF_LEN,
+ &bytes, 500);
+ libusb_bulk_transfer(state.handle, EP_BULK_OUT, buffer, BUF_LEN,
+ &bytes, 500);
+ }
+ test_exit(&state);
+}
diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c
index 05654f5..c4d6d2e 100644
--- a/tools/vm/page-types.c
+++ b/tools/vm/page-types.c
@@ -32,6 +32,8 @@
#include <assert.h>
#include <ftw.h>
#include <time.h>
+#include <setjmp.h>
+#include <signal.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
@@ -824,21 +826,38 @@ static void show_file(const char *name, const struct stat *st)
atime, now - st->st_atime);
}
+static sigjmp_buf sigbus_jmp;
+
+static void * volatile sigbus_addr;
+
+static void sigbus_handler(int sig, siginfo_t *info, void *ucontex)
+{
+ (void)sig;
+ (void)ucontex;
+ sigbus_addr = info ? info->si_addr : NULL;
+ siglongjmp(sigbus_jmp, 1);
+}
+
+static struct sigaction sigbus_action = {
+ .sa_sigaction = sigbus_handler,
+ .sa_flags = SA_SIGINFO,
+};
+
static void walk_file(const char *name, const struct stat *st)
{
uint8_t vec[PAGEMAP_BATCH];
uint64_t buf[PAGEMAP_BATCH], flags;
unsigned long nr_pages, pfn, i;
+ off_t off, end = st->st_size;
int fd;
- off_t off;
ssize_t len;
void *ptr;
int first = 1;
fd = checked_open(name, O_RDONLY|O_NOATIME|O_NOFOLLOW);
- for (off = 0; off < st->st_size; off += len) {
- nr_pages = (st->st_size - off + page_size - 1) / page_size;
+ for (off = 0; off < end; off += len) {
+ nr_pages = (end - off + page_size - 1) / page_size;
if (nr_pages > PAGEMAP_BATCH)
nr_pages = PAGEMAP_BATCH;
len = nr_pages * page_size;
@@ -855,11 +874,19 @@ static void walk_file(const char *name, const struct stat *st)
if (madvise(ptr, len, MADV_RANDOM))
fatal("madvice failed: %s", name);
+ if (sigsetjmp(sigbus_jmp, 1)) {
+ end = off + sigbus_addr ? sigbus_addr - ptr : 0;
+ fprintf(stderr, "got sigbus at offset %lld: %s\n",
+ (long long)end, name);
+ goto got_sigbus;
+ }
+
/* populate ptes */
for (i = 0; i < nr_pages ; i++) {
if (vec[i] & 1)
(void)*(volatile int *)(ptr + i * page_size);
}
+got_sigbus:
/* turn off harvesting reference bits */
if (madvise(ptr, len, MADV_SEQUENTIAL))
@@ -910,6 +937,7 @@ static void walk_page_cache(void)
kpageflags_fd = checked_open(PROC_KPAGEFLAGS, O_RDONLY);
pagemap_fd = checked_open("/proc/self/pagemap", O_RDONLY);
+ sigaction(SIGBUS, &sigbus_action, NULL);
if (stat(opt_file, &st))
fatal("stat failed: %s\n", opt_file);
@@ -925,6 +953,7 @@ static void walk_page_cache(void)
close(kpageflags_fd);
close(pagemap_fd);
+ signal(SIGBUS, SIG_DFL);
}
static void parse_file(const char *name)
OpenPOWER on IntegriCloud