diff options
Diffstat (limited to 'tools')
112 files changed, 9499 insertions, 1040 deletions
diff --git a/tools/net/bpf_exp.l b/tools/net/bpf_exp.l index bf7be77ddd6..833a96611da 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 d15efc989ef..e6306c51c26 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 cfe0cdcda3d..c5baf9c591b 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(®ex); 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/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt index 4464ad770d5..f6480cbf309 100644 --- a/tools/perf/Documentation/perf-bench.txt +++ b/tools/perf/Documentation/perf-bench.txt @@ -16,6 +16,10 @@ This 'perf bench' command is a general framework for benchmark suites. COMMON OPTIONS -------------- +-r:: +--repeat=:: +Specify amount of times to repeat the run (default 10). + -f:: --format=:: Specify format style. diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h index eba46709b27..3c4dd44d45c 100644 --- a/tools/perf/bench/bench.h +++ b/tools/perf/bench/bench.h @@ -43,5 +43,6 @@ extern int bench_futex_requeue(int argc, const char **argv, const char *prefix); #define BENCH_FORMAT_UNKNOWN -1 extern int bench_format; +extern unsigned int bench_repeat; #endif diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c index a16255876f1..732403bfd31 100644 --- a/tools/perf/bench/futex-requeue.c +++ b/tools/perf/bench/futex-requeue.c @@ -29,13 +29,6 @@ static u_int32_t futex1 = 0, futex2 = 0; */ static unsigned int nrequeue = 1; -/* - * There can be significant variance from run to run, - * the more repeats, the more exact the overall avg and - * the better idea of the futex latency. - */ -static unsigned int repeat = 10; - static pthread_t *worker; static bool done = 0, silent = 0; static pthread_mutex_t thread_lock; @@ -46,7 +39,6 @@ static unsigned int ncpus, threads_starting, nthreads = 0; static const struct option options[] = { OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), OPT_UINTEGER('q', "nrequeue", &nrequeue, "Specify amount of threads to requeue at once"), - OPT_UINTEGER('r', "repeat", &repeat, "Specify amount of times to repeat the run"), OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), OPT_END() }; @@ -146,7 +138,7 @@ int bench_futex_requeue(int argc, const char **argv, pthread_cond_init(&thread_parent, NULL); pthread_cond_init(&thread_worker, NULL); - for (j = 0; j < repeat && !done; j++) { + for (j = 0; j < bench_repeat && !done; j++) { unsigned int nrequeued = 0; struct timeval start, end, runtime; diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c index d096169b161..50022cbce87 100644 --- a/tools/perf/bench/futex-wake.c +++ b/tools/perf/bench/futex-wake.c @@ -30,15 +30,8 @@ static u_int32_t futex1 = 0; */ static unsigned int nwakes = 1; -/* - * There can be significant variance from run to run, - * the more repeats, the more exact the overall avg and - * the better idea of the futex latency. - */ -static unsigned int repeat = 10; - pthread_t *worker; -static bool done = 0, silent = 0; +static bool done = false, silent = false; static pthread_mutex_t thread_lock; static pthread_cond_t thread_parent, thread_worker; static struct stats waketime_stats, wakeup_stats; @@ -47,7 +40,6 @@ static unsigned int ncpus, threads_starting, nthreads = 0; static const struct option options[] = { OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), OPT_UINTEGER('w', "nwakes", &nwakes, "Specify amount of threads to wake at once"), - OPT_UINTEGER('r', "repeat", &repeat, "Specify amount of times to repeat the run"), OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), OPT_END() }; @@ -149,7 +141,7 @@ int bench_futex_wake(int argc, const char **argv, pthread_cond_init(&thread_parent, NULL); pthread_cond_init(&thread_worker, NULL); - for (j = 0; j < repeat && !done; j++) { + for (j = 0; j < bench_repeat && !done; j++) { unsigned int nwoken = 0; struct timeval start, end, runtime; diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c index 5ce71d3b72c..e622c3e96ef 100644 --- a/tools/perf/bench/mem-memcpy.c +++ b/tools/perf/bench/mem-memcpy.c @@ -189,6 +189,11 @@ int bench_mem_memcpy(int argc, const char **argv, argc = parse_options(argc, argv, options, bench_mem_memcpy_usage, 0); + if (no_prefault && only_prefault) { + fprintf(stderr, "Invalid options: -o and -n are mutually exclusive\n"); + return 1; + } + if (use_cycle) init_cycle(); diff --git a/tools/perf/bench/mem-memset.c b/tools/perf/bench/mem-memset.c index 9af79d2b18e..2a65468619f 100644 --- a/tools/perf/bench/mem-memset.c +++ b/tools/perf/bench/mem-memset.c @@ -181,6 +181,11 @@ int bench_mem_memset(int argc, const char **argv, argc = parse_options(argc, argv, options, bench_mem_memset_usage, 0); + if (no_prefault && only_prefault) { + fprintf(stderr, "Invalid options: -o and -n are mutually exclusive\n"); + return 1; + } + if (use_cycle) init_cycle(); diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c index cc1190a0849..52a56599a54 100644 --- a/tools/perf/bench/sched-messaging.c +++ b/tools/perf/bench/sched-messaging.c @@ -28,6 +28,7 @@ #include <sys/time.h> #include <sys/poll.h> #include <limits.h> +#include <err.h> #define DATASIZE 100 @@ -50,12 +51,6 @@ struct receiver_context { int wakefd; }; -static void barf(const char *msg) -{ - fprintf(stderr, "%s (error: %s)\n", msg, strerror(errno)); - exit(1); -} - static void fdpair(int fds[2]) { if (use_pipes) { @@ -66,7 +61,7 @@ static void fdpair(int fds[2]) return; } - barf(use_pipes ? "pipe()" : "socketpair()"); + err(EXIT_FAILURE, use_pipes ? "pipe()" : "socketpair()"); } /* Block until we're ready to go */ @@ -77,11 +72,11 @@ static void ready(int ready_out, int wakefd) /* Tell them we're ready. */ if (write(ready_out, &dummy, 1) != 1) - barf("CLIENT: ready write"); + err(EXIT_FAILURE, "CLIENT: ready write"); /* Wait for "GO" signal */ if (poll(&pollfd, 1, -1) != 1) - barf("poll"); + err(EXIT_FAILURE, "poll"); } /* Sender sprays loops messages down each file descriptor */ @@ -101,7 +96,7 @@ again: ret = write(ctx->out_fds[j], data + done, sizeof(data)-done); if (ret < 0) - barf("SENDER: write"); + err(EXIT_FAILURE, "SENDER: write"); done += ret; if (done < DATASIZE) goto again; @@ -131,7 +126,7 @@ static void *receiver(struct receiver_context* ctx) again: ret = read(ctx->in_fds[0], data + done, DATASIZE - done); if (ret < 0) - barf("SERVER: read"); + err(EXIT_FAILURE, "SERVER: read"); done += ret; if (done < DATASIZE) goto again; @@ -144,14 +139,14 @@ static pthread_t create_worker(void *ctx, void *(*func)(void *)) { pthread_attr_t attr; pthread_t childid; - int err; + int ret; if (!thread_mode) { /* process mode */ /* Fork the receiver. */ switch (fork()) { case -1: - barf("fork()"); + err(EXIT_FAILURE, "fork()"); break; case 0: (*func) (ctx); @@ -165,19 +160,17 @@ static pthread_t create_worker(void *ctx, void *(*func)(void *)) } if (pthread_attr_init(&attr) != 0) - barf("pthread_attr_init:"); + err(EXIT_FAILURE, "pthread_attr_init:"); #ifndef __ia64__ if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0) - barf("pthread_attr_setstacksize"); + err(EXIT_FAILURE, "pthread_attr_setstacksize"); #endif - err = pthread_create(&childid, &attr, func, ctx); - if (err != 0) { - fprintf(stderr, "pthread_create failed: %s (%d)\n", - strerror(err), err); - exit(-1); - } + ret = pthread_create(&childid, &attr, func, ctx); + if (ret != 0) + err(EXIT_FAILURE, "pthread_create failed"); + return childid; } @@ -207,14 +200,14 @@ static unsigned int group(pthread_t *pth, + num_fds * sizeof(int)); if (!snd_ctx) - barf("malloc()"); + err(EXIT_FAILURE, "malloc()"); for (i = 0; i < num_fds; i++) { int fds[2]; struct receiver_context *ctx = malloc(sizeof(*ctx)); if (!ctx) - barf("malloc()"); + err(EXIT_FAILURE, "malloc()"); /* Create the pipe between client and server */ @@ -281,7 +274,7 @@ int bench_sched_messaging(int argc, const char **argv, pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t)); if (!pth_tab) - barf("main:malloc()"); + err(EXIT_FAILURE, "main:malloc()"); fdpair(readyfds); fdpair(wakefds); @@ -294,13 +287,13 @@ int bench_sched_messaging(int argc, const char **argv, /* Wait for everyone to be ready */ for (i = 0; i < total_children; i++) if (read(readyfds[0], &dummy, 1) != 1) - barf("Reading for readyfds"); + err(EXIT_FAILURE, "Reading for readyfds"); gettimeofday(&start, NULL); /* Kick them off */ if (write(wakefds[1], &dummy, 1) != 1) - barf("Writing to start them"); + err(EXIT_FAILURE, "Writing to start them"); /* Reap them all */ for (i = 0; i < total_children; i++) @@ -332,5 +325,7 @@ int bench_sched_messaging(int argc, const char **argv, break; } + free(pth_tab); + return 0; } diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index 1e6e7771054..b9a56fa8333 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -104,9 +104,11 @@ static const char *bench_format_str; /* Output/formatting style, exported to benchmark modules: */ int bench_format = BENCH_FORMAT_DEFAULT; +unsigned int bench_repeat = 10; /* default number of times to repeat the run */ static const struct option bench_options[] = { OPT_STRING('f', "format", &bench_format_str, "default", "Specify format style"), + OPT_UINTEGER('r', "repeat", &bench_repeat, "Specify amount of times to repeat the run"), OPT_END() }; @@ -226,6 +228,11 @@ int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused) goto end; } + if (bench_repeat == 0) { + printf("Invalid repeat option: Must specify a positive value\n"); + goto end; + } + if (argc < 1) { print_usage(); goto end; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index f954c26de23..5549cee6168 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1108,6 +1108,7 @@ struct syscall { struct event_format *tp_format; const char *name; bool filtered; + bool is_exit; struct syscall_fmt *fmt; size_t (**arg_scnprintf)(char *bf, size_t size, struct syscall_arg *arg); void **arg_parm; @@ -1276,11 +1277,11 @@ static const char *thread__fd_path(struct thread *thread, int fd, if (fd < 0) return NULL; - if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL)) + if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL)) { if (!trace->live) return NULL; ++trace->stats.proc_getname; - if (thread__read_fd_path(thread, fd)) { + if (thread__read_fd_path(thread, fd)) return NULL; } @@ -1473,6 +1474,8 @@ static int trace__read_syscall_info(struct trace *trace, int id) if (sc->tp_format == NULL) return -1; + sc->is_exit = !strcmp(name, "exit_group") || !strcmp(name, "exit"); + return syscall__set_arg_fmts(sc); } @@ -1629,7 +1632,6 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, return -1; args = perf_evsel__sc_tp_ptr(evsel, args, sample); - ttrace = thread->priv; if (ttrace->entry_str == NULL) { ttrace->entry_str = malloc(1024); @@ -1644,7 +1646,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed, args, trace, thread); - if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { + if (sc->is_exit) { if (!trace->duration_filter && !trace->summary_only) { trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); fprintf(trace->output, "%-70s\n", ttrace->entry_str); @@ -1687,8 +1689,6 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, ++trace->stats.vfs_getname; } - ttrace = thread->priv; - ttrace->exit_time = sample->time; if (ttrace->entry_time) { diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index 3ccf6e14f89..9d2294efc00 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -279,7 +279,7 @@ static void ui_browser__scrollbar_set(struct ui_browser *browser) { int height = browser->height, h = 0, pct = 0, col = browser->width, - row = browser->y - 1; + row = 0; if (browser->nr_entries > 1) { pct = ((browser->index * (browser->height - 1)) / diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 52c03fbbba1..2185091c522 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -17,6 +17,7 @@ #include "../util.h" #include "../ui.h" #include "map.h" +#include "annotate.h" struct hist_browser { struct ui_browser b; @@ -32,8 +33,7 @@ struct hist_browser { extern void hist_browser__init_hpp(void); -static int hists__browser_title(struct hists *hists, char *bf, size_t size, - const char *ev_name); +static int hists__browser_title(struct hists *hists, char *bf, size_t size); static void hist_browser__update_nr_entries(struct hist_browser *hb); static struct rb_node *hists__filter_entries(struct rb_node *nd, @@ -345,7 +345,7 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser) "Or reduce the sampling frequency."); } -static int hist_browser__run(struct hist_browser *browser, const char *ev_name, +static int hist_browser__run(struct hist_browser *browser, struct hist_browser_timer *hbt) { int key; @@ -356,7 +356,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name, browser->b.nr_entries = hist_browser__nr_entries(browser); hist_browser__refresh_dimensions(browser); - hists__browser_title(browser->hists, title, sizeof(title), ev_name); + hists__browser_title(browser->hists, title, sizeof(title)); if (ui_browser__show(&browser->b, title, "Press '?' for help on key bindings") < 0) @@ -383,7 +383,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name, ui_browser__warn_lost_events(&browser->b); } - hists__browser_title(browser->hists, title, sizeof(title), ev_name); + hists__browser_title(browser->hists, title, sizeof(title)); ui_browser__show_title(&browser->b, title); continue; } @@ -1212,8 +1212,7 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *browser return browser->he_selection->thread; } -static int hists__browser_title(struct hists *hists, char *bf, size_t size, - const char *ev_name) +static int hists__browser_title(struct hists *hists, char *bf, size_t size) { char unit; int printed; @@ -1222,6 +1221,7 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size, unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; u64 nr_events = hists->stats.total_period; struct perf_evsel *evsel = hists_to_evsel(hists); + const char *ev_name = perf_evsel__name(evsel); char buf[512]; size_t buflen = sizeof(buf); @@ -1389,7 +1389,7 @@ static void hist_browser__update_nr_entries(struct hist_browser *hb) } static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, - const char *helpline, const char *ev_name, + const char *helpline, bool left_exits, struct hist_browser_timer *hbt, float min_pcnt, @@ -1464,7 +1464,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, nr_options = 0; - key = hist_browser__run(browser, ev_name, hbt); + key = hist_browser__run(browser, hbt); if (browser->he_selection != NULL) { thread = hist_browser__selected_thread(browser); @@ -1593,13 +1593,18 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, bi->to.sym->name) > 0) annotate_t = nr_options++; } else { - if (browser->selection != NULL && browser->selection->sym != NULL && - !browser->selection->map->dso->annotate_warned && - asprintf(&options[nr_options], "Annotate %s", - browser->selection->sym->name) > 0) - annotate = nr_options++; + !browser->selection->map->dso->annotate_warned) { + struct annotation *notes; + + notes = symbol__annotation(browser->selection->sym); + + if (notes->src && + asprintf(&options[nr_options], "Annotate %s", + browser->selection->sym->name) > 0) + annotate = nr_options++; + } } if (thread != NULL && @@ -1656,6 +1661,7 @@ retry_popup_menu: if (choice == annotate || choice == annotate_t || choice == annotate_f) { struct hist_entry *he; + struct annotation *notes; int err; do_annotate: if (!objdump_path && perf_session_env__lookup_objdump(env)) @@ -1679,6 +1685,10 @@ do_annotate: he->ms.map = he->branch_info->to.map; } + notes = symbol__annotation(he->ms.sym); + if (!notes->src) + continue; + /* * Don't let this be freed, say, by hists__decay_entry. */ @@ -1832,7 +1842,7 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, { struct perf_evlist *evlist = menu->b.priv; struct perf_evsel *pos; - const char *ev_name, *title = "Available samples"; + const char *title = "Available samples"; int delay_secs = hbt ? hbt->refresh : 0; int key; @@ -1865,9 +1875,8 @@ browse_hists: */ if (hbt) hbt->timer(hbt->arg); - ev_name = perf_evsel__name(pos); key = perf_evsel__hists_browse(pos, nr_events, help, - ev_name, true, hbt, + true, hbt, menu->min_pcnt, menu->env); ui_browser__show_title(&menu->b, title); @@ -1971,10 +1980,9 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, single_entry: if (nr_entries == 1) { struct perf_evsel *first = perf_evlist__first(evlist); - const char *ev_name = perf_evsel__name(first); return perf_evsel__hists_browse(first, nr_entries, help, - ev_name, false, hbt, min_pcnt, + false, hbt, min_pcnt, env); } diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 59ef2802fcf..c51223ac25f 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1214,10 +1214,11 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, "For your workloads it needs to be <= 1\nHint:\t"); } printed += scnprintf(buf + printed, size - printed, - "For system wide tracing it needs to be set to -1"); + "For system wide tracing it needs to be set to -1.\n"); printed += scnprintf(buf + printed, size - printed, - ".\nHint:\tThe current value is %d.", value); + "Hint:\tTry: 'sudo sh -c \"echo -1 > /proc/sys/kernel/perf_event_paranoid\"'\n" + "Hint:\tThe current value is %d.", value); break; default: scnprintf(buf, size, "%s", emsg); diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 0e5fea95d59..c73e1fc12e5 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -496,18 +496,6 @@ struct process_args { u64 start; }; -static int symbol__in_kernel(void *arg, const char *name, - char type __maybe_unused, u64 start) -{ - struct process_args *args = arg; - - if (strchr(name, '[')) - return 0; - - args->start = start; - return 1; -} - static void machine__get_kallsyms_filename(struct machine *machine, char *buf, size_t bufsz) { @@ -517,27 +505,41 @@ static void machine__get_kallsyms_filename(struct machine *machine, char *buf, scnprintf(buf, bufsz, "%s/proc/kallsyms", machine->root_dir); } -/* Figure out the start address of kernel map from /proc/kallsyms */ -static u64 machine__get_kernel_start_addr(struct machine *machine) +const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL}; + +/* Figure out the start address of kernel map from /proc/kallsyms. + * Returns the name of the start symbol in *symbol_name. Pass in NULL as + * symbol_name if it's not that important. + */ +static u64 machine__get_kernel_start_addr(struct machine *machine, + const char **symbol_name) { char filename[PATH_MAX]; - struct process_args args; + int i; + const char *name; + u64 addr = 0; machine__get_kallsyms_filename(machine, filename, PATH_MAX); if (symbol__restricted_filename(filename, "/proc/kallsyms")) return 0; - if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0) - return 0; + for (i = 0; (name = ref_reloc_sym_names[i]) != NULL; i++) { + addr = kallsyms__get_function_start(filename, name); + if (addr) + break; + } + + if (symbol_name) + *symbol_name = name; - return args.start; + return addr; } int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) { enum map_type type; - u64 start = machine__get_kernel_start_addr(machine); + u64 start = machine__get_kernel_start_addr(machine, NULL); for (type = 0; type < MAP__NR_TYPES; ++type) { struct kmap *kmap; @@ -852,23 +854,11 @@ static int machine__create_modules(struct machine *machine) return 0; } -const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL}; - int machine__create_kernel_maps(struct machine *machine) { struct dso *kernel = machine__get_kernel(machine); - char filename[PATH_MAX]; const char *name; - u64 addr = 0; - int i; - - machine__get_kallsyms_filename(machine, filename, PATH_MAX); - - for (i = 0; (name = ref_reloc_sym_names[i]) != NULL; i++) { - addr = kallsyms__get_function_start(filename, name); - if (addr) - break; - } + u64 addr = machine__get_kernel_start_addr(machine, &name); if (!addr) return -1; diff --git a/tools/power/acpi/Makefile b/tools/power/acpi/Makefile index c2c0f20067a..e5a3c4be2a1 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 00000000000..5140e5edae1 --- /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 00000000000..a302f52e4fd --- /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 adfa99166e5..38f095d86b5 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 00000000000..28c52008e85 --- /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, + ¤t_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 00000000000..733f9e490fc --- /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 00000000000..99b47b6194a --- /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 a84553a0e0d..00000000000 --- 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 00000000000..46f519597fe --- /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 00000000000..3cac1237836 --- /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 00000000000..4488accc010 --- /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 00000000000..51e8d638db1 --- /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 00000000000..b7b0b929bd3 --- /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 00000000000..6b8aaed44f2 --- /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 cbfec92af32..2e2ba2efa0d 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 fd9d4c0d668..1c68f47663b 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 874b78b586e..6e8b89f282e 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 0f10b81e332..5224ee5b392 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 4a1918ea8f9..9c85a382e35 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 6b1607272a5..3e6799d7a79 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 58e21196f17..340bcd0be7d 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 9dbd536518a..2bcc696f449 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 28953c9a7bd..b4b90a97662 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 c78141c5dfa..d45d8d775c0 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 3f68632c28c..136d979e958 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 bcf1d2f0b79..573c75f8e3f 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 7efc570ffba..7cdcf88659c 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 7c9d8e71eb9..d0396af99fa 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 32487ed1835..e66e710cc59 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 750512ba2c8..c7493b8f9b0 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 316194f26ff..54833a791a4 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 ccd9c84c4e3..d1dc3742551 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 e80c42a584f..8ebc58a0931 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 00000000000..357ccbd6bad --- /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 00000000000..3d8e5b033e1 --- /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 7216f009165..b9ff0db42c7 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 00000000000..edbba2affc2 --- /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 00000000000..66ea765c0e7 --- /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 00000000000..0f0423dba18 --- /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 00000000000..d3ed64d5d6c --- /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 00000000000..8b972c2aa39 --- /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 00000000000..8590fc1bfc0 --- /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 00000000000..754b3f2008d --- /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 00000000000..1b46be94b64 --- /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 00000000000..e62bde05bf7 --- /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 00000000000..14274ea206e --- /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 00000000000..c45f948148e --- /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 00000000000..11acf1d55f8 --- /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 00000000000..be4dd5a4e98 --- /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 00000000000..7e78153f08e --- /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 00000000000..b866a0581d3 --- /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 00000000000..9e7af6e7662 --- /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 00000000000..f8190fa2959 --- /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 00000000000..0c9dd9b2e39 --- /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 00000000000..67d78af3284 --- /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 00000000000..b8dc371f933 --- /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 00000000000..2f9bf8edfa6 --- /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 00000000000..986500fd213 --- /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 00000000000..a503fa70c95 --- /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 00000000000..5921b0dfe2e --- /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 00000000000..0cae66f659a --- /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 00000000000..d56607e4ffa --- /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 00000000000..eba32196dbb --- /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 00000000000..251e66ab2aa --- /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 00000000000..926458e28c8 --- /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 2b2d11df245..184b36807d4 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 e6993192ff3..a0ea6b1eef7 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 00000000000..0f6a4731d54 --- /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 00000000000..ca5d72ae3be --- /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 8820e3df144..20c1f0876c4 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 98a22920792..9c6c4e901ab 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 00000000000..51267f4184a --- /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 00000000000..ee98e3886af --- /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 0de064406da..a93777ae068 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 a1be6e62add..9c3f3d39b93 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 9b17e810ddc..d01b865bb10 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 197901ec10b..7c1e56b46de 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 829186e19eb..7f1ff1a8fc4 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 d75b1dc5ae5..307c4b95f32 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 a44daaa259a..ee1f6cae3d7 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 94b28bb37d3..27e544e2951 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 5a78cbf55f0..40285c58653 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 dd0a275d979..3455560ab4e 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 00000000000..61c8d9ce5bb --- /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 00000000000..3b42b8b033c --- /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 00000000000..0a92adaf086 --- /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 00000000000..17d534b1b7b --- /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 00000000000..8510f93f2d1 --- /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 00000000000..90a9293d520 --- /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 00000000000..87216a0c4a8 --- /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 00000000000..8c4a6f0aa82 --- /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 00000000000..b0ad8747d03 --- /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 00000000000..f558664a331 --- /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 00000000000..8c4a6f0aa82 --- /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 00000000000..64b6a57d8ca --- /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 05654f5e48d..c4d6d2e20e0 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) |