1f984b51eSPekka Paalanen /* 2f984b51eSPekka Paalanen * Memory mapped I/O tracing 3f984b51eSPekka Paalanen * 4f984b51eSPekka Paalanen * Copyright (C) 2008 Pekka Paalanen <pq@iki.fi> 5f984b51eSPekka Paalanen */ 6f984b51eSPekka Paalanen 7f984b51eSPekka Paalanen #define DEBUG 1 8f984b51eSPekka Paalanen 9f984b51eSPekka Paalanen #include <linux/kernel.h> 10f984b51eSPekka Paalanen #include <linux/mmiotrace.h> 11*13829537SPekka Paalanen #include <linux/pci.h> 12f984b51eSPekka Paalanen 13f984b51eSPekka Paalanen #include "trace.h" 14f984b51eSPekka Paalanen 15f984b51eSPekka Paalanen static struct trace_array *mmio_trace_array; 16f984b51eSPekka Paalanen 17bd8ac686SPekka Paalanen static void mmio_reset_data(struct trace_array *tr) 18bd8ac686SPekka Paalanen { 19bd8ac686SPekka Paalanen int cpu; 20bd8ac686SPekka Paalanen 21bd8ac686SPekka Paalanen tr->time_start = ftrace_now(tr->cpu); 22bd8ac686SPekka Paalanen 23bd8ac686SPekka Paalanen for_each_online_cpu(cpu) 24bd8ac686SPekka Paalanen tracing_reset(tr->data[cpu]); 25bd8ac686SPekka Paalanen } 26f984b51eSPekka Paalanen 27f984b51eSPekka Paalanen static void mmio_trace_init(struct trace_array *tr) 28f984b51eSPekka Paalanen { 29f984b51eSPekka Paalanen pr_debug("in %s\n", __func__); 30f984b51eSPekka Paalanen mmio_trace_array = tr; 31bd8ac686SPekka Paalanen if (tr->ctrl) { 32bd8ac686SPekka Paalanen mmio_reset_data(tr); 33f984b51eSPekka Paalanen enable_mmiotrace(); 34f984b51eSPekka Paalanen } 35bd8ac686SPekka Paalanen } 36f984b51eSPekka Paalanen 37f984b51eSPekka Paalanen static void mmio_trace_reset(struct trace_array *tr) 38f984b51eSPekka Paalanen { 39f984b51eSPekka Paalanen pr_debug("in %s\n", __func__); 40f984b51eSPekka Paalanen if (tr->ctrl) 41f984b51eSPekka Paalanen disable_mmiotrace(); 42bd8ac686SPekka Paalanen mmio_reset_data(tr); 43bd8ac686SPekka Paalanen mmio_trace_array = NULL; 44f984b51eSPekka Paalanen } 45f984b51eSPekka Paalanen 46f984b51eSPekka Paalanen static void mmio_trace_ctrl_update(struct trace_array *tr) 47f984b51eSPekka Paalanen { 48f984b51eSPekka Paalanen pr_debug("in %s\n", __func__); 49bd8ac686SPekka Paalanen if (tr->ctrl) { 50bd8ac686SPekka Paalanen mmio_reset_data(tr); 51f984b51eSPekka Paalanen enable_mmiotrace(); 52bd8ac686SPekka Paalanen } else { 53f984b51eSPekka Paalanen disable_mmiotrace(); 54f984b51eSPekka Paalanen } 55bd8ac686SPekka Paalanen } 56bd8ac686SPekka Paalanen 57*13829537SPekka Paalanen static int mmio_print_pcidev(struct trace_seq *s, const struct pci_dev *dev) 58*13829537SPekka Paalanen { 59*13829537SPekka Paalanen int ret = 0; 60*13829537SPekka Paalanen int i; 61*13829537SPekka Paalanen resource_size_t start, end; 62*13829537SPekka Paalanen const struct pci_driver *drv = pci_dev_driver(dev); 63*13829537SPekka Paalanen 64*13829537SPekka Paalanen /* XXX: incomplete checks for trace_seq_printf() return value */ 65*13829537SPekka Paalanen ret += trace_seq_printf(s, "PCIDEV %02x%02x %04x%04x %x", 66*13829537SPekka Paalanen dev->bus->number, dev->devfn, 67*13829537SPekka Paalanen dev->vendor, dev->device, dev->irq); 68*13829537SPekka Paalanen /* 69*13829537SPekka Paalanen * XXX: is pci_resource_to_user() appropriate, since we are 70*13829537SPekka Paalanen * supposed to interpret the __ioremap() phys_addr argument based on 71*13829537SPekka Paalanen * these printed values? 72*13829537SPekka Paalanen */ 73*13829537SPekka Paalanen for (i = 0; i < 7; i++) { 74*13829537SPekka Paalanen pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); 75*13829537SPekka Paalanen ret += trace_seq_printf(s, " %llx", 76*13829537SPekka Paalanen (unsigned long long)(start | 77*13829537SPekka Paalanen (dev->resource[i].flags & PCI_REGION_FLAG_MASK))); 78*13829537SPekka Paalanen } 79*13829537SPekka Paalanen for (i = 0; i < 7; i++) { 80*13829537SPekka Paalanen pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); 81*13829537SPekka Paalanen ret += trace_seq_printf(s, " %llx", 82*13829537SPekka Paalanen dev->resource[i].start < dev->resource[i].end ? 83*13829537SPekka Paalanen (unsigned long long)(end - start) + 1 : 0); 84*13829537SPekka Paalanen } 85*13829537SPekka Paalanen if (drv) 86*13829537SPekka Paalanen ret += trace_seq_printf(s, " %s\n", drv->name); 87*13829537SPekka Paalanen else 88*13829537SPekka Paalanen ret += trace_seq_printf(s, " \n"); 89*13829537SPekka Paalanen return ret; 90*13829537SPekka Paalanen } 91*13829537SPekka Paalanen 92bd8ac686SPekka Paalanen /* XXX: This is not called for trace_pipe file! */ 93*13829537SPekka Paalanen static void mmio_print_header(struct trace_iterator *iter) 94bd8ac686SPekka Paalanen { 95bd8ac686SPekka Paalanen struct trace_seq *s = &iter->seq; 96*13829537SPekka Paalanen struct pci_dev *dev = NULL; 97*13829537SPekka Paalanen 98*13829537SPekka Paalanen trace_seq_printf(s, "VERSION 20070824\n"); 99*13829537SPekka Paalanen 100*13829537SPekka Paalanen for_each_pci_dev(dev) 101*13829537SPekka Paalanen mmio_print_pcidev(s, dev); 102*13829537SPekka Paalanen /* XXX: return value? What if header is very long? */ 103bd8ac686SPekka Paalanen } 104bd8ac686SPekka Paalanen 105bd8ac686SPekka Paalanen static int mmio_print_rw(struct trace_iterator *iter) 106bd8ac686SPekka Paalanen { 107bd8ac686SPekka Paalanen struct trace_entry *entry = iter->ent; 108bd8ac686SPekka Paalanen struct mmiotrace_rw *rw = &entry->mmiorw; 109bd8ac686SPekka Paalanen struct trace_seq *s = &iter->seq; 110bd8ac686SPekka Paalanen unsigned long long t = ns2usecs(entry->t); 111bd8ac686SPekka Paalanen unsigned long usec_rem = do_div(t, 1000000ULL); 112bd8ac686SPekka Paalanen unsigned secs = (unsigned long)t; 113bd8ac686SPekka Paalanen int ret = 1; 114bd8ac686SPekka Paalanen 115bd8ac686SPekka Paalanen switch (entry->mmiorw.opcode) { 116bd8ac686SPekka Paalanen case MMIO_READ: 117bd8ac686SPekka Paalanen ret = trace_seq_printf(s, 118bd8ac686SPekka Paalanen "R %d %lu.%06lu %d 0x%lx 0x%lx 0x%lx %d\n", 119bd8ac686SPekka Paalanen rw->width, secs, usec_rem, rw->map_id, rw->phys, 120bd8ac686SPekka Paalanen rw->value, rw->pc, entry->pid); 121bd8ac686SPekka Paalanen break; 122bd8ac686SPekka Paalanen case MMIO_WRITE: 123bd8ac686SPekka Paalanen ret = trace_seq_printf(s, 124bd8ac686SPekka Paalanen "W %d %lu.%06lu %d 0x%lx 0x%lx 0x%lx %d\n", 125bd8ac686SPekka Paalanen rw->width, secs, usec_rem, rw->map_id, rw->phys, 126bd8ac686SPekka Paalanen rw->value, rw->pc, entry->pid); 127bd8ac686SPekka Paalanen break; 128bd8ac686SPekka Paalanen case MMIO_UNKNOWN_OP: 129bd8ac686SPekka Paalanen ret = trace_seq_printf(s, 130bd8ac686SPekka Paalanen "UNKNOWN %lu.%06lu %d 0x%lx %02x,%02x,%02x 0x%lx %d\n", 131bd8ac686SPekka Paalanen secs, usec_rem, rw->map_id, rw->phys, 132bd8ac686SPekka Paalanen (rw->value >> 16) & 0xff, (rw->value >> 8) & 0xff, 133bd8ac686SPekka Paalanen (rw->value >> 0) & 0xff, rw->pc, entry->pid); 134bd8ac686SPekka Paalanen break; 135bd8ac686SPekka Paalanen default: 136bd8ac686SPekka Paalanen ret = trace_seq_printf(s, "rw what?\n"); 137bd8ac686SPekka Paalanen break; 138bd8ac686SPekka Paalanen } 139bd8ac686SPekka Paalanen if (ret) 140bd8ac686SPekka Paalanen return 1; 141bd8ac686SPekka Paalanen return 0; 142bd8ac686SPekka Paalanen } 143bd8ac686SPekka Paalanen 144bd8ac686SPekka Paalanen static int mmio_print_map(struct trace_iterator *iter) 145bd8ac686SPekka Paalanen { 146bd8ac686SPekka Paalanen struct trace_entry *entry = iter->ent; 147bd8ac686SPekka Paalanen struct mmiotrace_map *m = &entry->mmiomap; 148bd8ac686SPekka Paalanen struct trace_seq *s = &iter->seq; 149bd8ac686SPekka Paalanen unsigned long long t = ns2usecs(entry->t); 150bd8ac686SPekka Paalanen unsigned long usec_rem = do_div(t, 1000000ULL); 151bd8ac686SPekka Paalanen unsigned secs = (unsigned long)t; 152bd8ac686SPekka Paalanen int ret = 1; 153bd8ac686SPekka Paalanen 154bd8ac686SPekka Paalanen switch (entry->mmiorw.opcode) { 155bd8ac686SPekka Paalanen case MMIO_PROBE: 156bd8ac686SPekka Paalanen ret = trace_seq_printf(s, 157bd8ac686SPekka Paalanen "MAP %lu.%06lu %d 0x%lx 0x%lx 0x%lx 0x%lx %d\n", 158bd8ac686SPekka Paalanen secs, usec_rem, m->map_id, m->phys, m->virt, m->len, 159bd8ac686SPekka Paalanen 0UL, entry->pid); 160bd8ac686SPekka Paalanen break; 161bd8ac686SPekka Paalanen case MMIO_UNPROBE: 162bd8ac686SPekka Paalanen ret = trace_seq_printf(s, 163bd8ac686SPekka Paalanen "UNMAP %lu.%06lu %d 0x%lx %d\n", 164bd8ac686SPekka Paalanen secs, usec_rem, m->map_id, 0UL, entry->pid); 165bd8ac686SPekka Paalanen break; 166bd8ac686SPekka Paalanen default: 167bd8ac686SPekka Paalanen ret = trace_seq_printf(s, "map what?\n"); 168bd8ac686SPekka Paalanen break; 169bd8ac686SPekka Paalanen } 170bd8ac686SPekka Paalanen if (ret) 171bd8ac686SPekka Paalanen return 1; 172bd8ac686SPekka Paalanen return 0; 173bd8ac686SPekka Paalanen } 174bd8ac686SPekka Paalanen 175bd8ac686SPekka Paalanen /* return 0 to abort printing without consuming current entry in pipe mode */ 176bd8ac686SPekka Paalanen static int mmio_print_line(struct trace_iterator *iter) 177bd8ac686SPekka Paalanen { 178bd8ac686SPekka Paalanen switch (iter->ent->type) { 179bd8ac686SPekka Paalanen case TRACE_MMIO_RW: 180bd8ac686SPekka Paalanen return mmio_print_rw(iter); 181bd8ac686SPekka Paalanen case TRACE_MMIO_MAP: 182bd8ac686SPekka Paalanen return mmio_print_map(iter); 183bd8ac686SPekka Paalanen default: 184bd8ac686SPekka Paalanen return 1; /* ignore unknown entries */ 185bd8ac686SPekka Paalanen } 186bd8ac686SPekka Paalanen } 187f984b51eSPekka Paalanen 188f984b51eSPekka Paalanen static struct tracer mmio_tracer __read_mostly = 189f984b51eSPekka Paalanen { 190f984b51eSPekka Paalanen .name = "mmiotrace", 191f984b51eSPekka Paalanen .init = mmio_trace_init, 192f984b51eSPekka Paalanen .reset = mmio_trace_reset, 193bd8ac686SPekka Paalanen .open = mmio_print_header, 194f984b51eSPekka Paalanen .ctrl_update = mmio_trace_ctrl_update, 195bd8ac686SPekka Paalanen .print_line = mmio_print_line, 196f984b51eSPekka Paalanen }; 197f984b51eSPekka Paalanen 198f984b51eSPekka Paalanen __init static int init_mmio_trace(void) 199f984b51eSPekka Paalanen { 200f984b51eSPekka Paalanen return register_tracer(&mmio_tracer); 201f984b51eSPekka Paalanen } 202f984b51eSPekka Paalanen device_initcall(init_mmio_trace); 203f984b51eSPekka Paalanen 204bd8ac686SPekka Paalanen void mmio_trace_rw(struct mmiotrace_rw *rw) 205f984b51eSPekka Paalanen { 206f984b51eSPekka Paalanen struct trace_array *tr = mmio_trace_array; 207f984b51eSPekka Paalanen struct trace_array_cpu *data = tr->data[smp_processor_id()]; 208bd8ac686SPekka Paalanen __trace_mmiotrace_rw(tr, data, rw); 209bd8ac686SPekka Paalanen } 210f984b51eSPekka Paalanen 211bd8ac686SPekka Paalanen void mmio_trace_mapping(struct mmiotrace_map *map) 212bd8ac686SPekka Paalanen { 213bd8ac686SPekka Paalanen struct trace_array *tr = mmio_trace_array; 214bd8ac686SPekka Paalanen struct trace_array_cpu *data; 215bd8ac686SPekka Paalanen 216bd8ac686SPekka Paalanen preempt_disable(); 217bd8ac686SPekka Paalanen data = tr->data[smp_processor_id()]; 218bd8ac686SPekka Paalanen __trace_mmiotrace_map(tr, data, map); 219bd8ac686SPekka Paalanen preempt_enable(); 220f984b51eSPekka Paalanen } 221