aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/arm64
Commit message (Expand)AuthorAgeFilesLines
* arm64: add ARM 64-bits targetFlorian Fainelli2014-11-246-0/+362
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
/******************************************************************************
 * common/trace.c
 *
 * Xen Trace Buffer
 *
 * Copyright (C) 2004 by Intel Research Cambridge
 *
 * Authors: Mark Williamson, mark.a.williamson@intel.com
 *          Rob Gardner, rob.gardner@hp.com
 * Date:    October 2005
 *
 * Copyright (C) 2005 Bin Ren
 *
 * The trace buffer code is designed to allow debugging traces of Xen to be
 * generated on UP / SMP machines.  Each trace entry is timestamped so that
 * it's possible to reconstruct a chronological record of trace events.
 *
 * See also include/xen/trace.h and the dom0 op in
 * include/public/dom0_ops.h
 */

#include <xen/config.h>
#include <asm/types.h>
#include <asm/io.h>
#include <xen/lib.h>
#include <xen/sched.h>
#include <xen/smp.h>
#include <xen/trace.h>
#include <xen/errno.h>
#include <xen/init.h>
#include <asm/atomic.h>
#include <public/dom0_ops.h>

/* opt_tbuf_size: trace buffer size (in pages) */
static unsigned int opt_tbuf_size = 0;
integer_param("tbuf_size", opt_tbuf_size);

/* Pointers to the meta-data objects for all system trace buffers */
static struct t_buf *t_bufs[NR_CPUS];
static struct t_rec *t_recs[NR_CPUS];
static int nr_recs;

/* a flag recording whether initialization has been done */
/* or more properly, if the tbuf subsystem is enabled right now */
int tb_init_done;

/* which CPUs tracing is enabled on */
static unsigned long tb_cpu_mask = (~0UL);

/* which tracing events are enabled */
static u32 tb_event_mask = TRC_ALL;

/**
 * alloc_trace_bufs - performs initialization of the per-cpu trace buffers.
 *
 * This function is called at start of day in order to initialize the per-cpu
 * trace buffers.  The trace buffers are then available for debugging use, via
 * the %TRACE_xD macros exported in <xen/trace.h>.
 *
 * This function may also be called later when enabling trace buffers 
 * via the SET_SIZE hypercall.
 */
static int alloc_trace_bufs(void)
{
    int           i, order;
    unsigned long nr_pages;
    char         *rawbuf;
    struct t_buf *buf;

    if ( opt_tbuf_size == 0 )
        return -EINVAL;

    nr_pages = num_online_cpus() * opt_tbuf_size;
    order    = get_order_from_pages(nr_pages);
    nr_recs  = (opt_tbuf_size * PAGE_SIZE - sizeof(struct t_buf)) /
        sizeof(struct t_rec);
    
    if ( (rawbuf = alloc_xenheap_pages(order)) == NULL )
    {
        printk("Xen trace buffers: memory allocation failed\n");
        return -EINVAL;
    }

    /* Share pages so that xentrace can map them. */
    for ( i = 0; i < nr_pages; i++ )
        SHARE_PFN_WITH_DOMAIN(virt_to_page(rawbuf + i * PAGE_SIZE), dom0);
    
    for_each_online_cpu ( i )
    {
        buf = t_bufs[i] = (struct t_buf *)&rawbuf[i*opt_tbuf_size*PAGE_SIZE];
        buf->cons = buf->prod = 0;
        t_recs[i] = (struct t_rec *)(buf + 1);
    }

    return 0;
}


/**
 * tb_set_size - handle the logic involved with dynamically
 * allocating and deallocating tbufs
 *
 * This function is called when the SET_SIZE hypercall is done.
 */
static int tb_set_size(int size)
{
    /*
     * Setting size is a one-shot operation. It can be done either at
     * boot time or via control tools, but not by both. Once buffers
     * are created they cannot be destroyed.
     */
    if ( (opt_tbuf_size != 0) || (size <= 0) )
    {
        DPRINTK("tb_set_size from %d to %d not implemented\n",
                opt_tbuf_size, size);
        return -EINVAL;
    }

    opt_tbuf_size = size;
    if ( alloc_trace_bufs() != 0 )
    {
        opt_tbuf_size = 0;
        return -EINVAL;
    }

    printk("Xen trace buffers: initialized\n");
    return 0;
}


/**
 * init_trace_bufs - performs initialization of the per-cpu trace buffers.
 *
 * This function is called at start of day in order to initialize the per-cpu
 * trace buffers.  The trace buffers are then available for debugging use, via
 * the %TRACE_xD macros exported in <xen/trace.h>.
 */
void init_trace_bufs(void)
{
    if ( opt_tbuf_size == 0 )
    {
        printk("Xen trace buffers: disabled\n");
        return;
    }

    if ( alloc_trace_bufs() == 0 )
    {
        printk("Xen trace buffers: initialised\n");
        wmb(); /* above must be visible before tb_init_done flag set */
        tb_init_done = 1;
    }
}


/**
 * tb_control - DOM0 operations on trace buffers.
 * @tbc: a pointer to a dom0_tbufcontrol_t to be filled out
 */
int tb_control(dom0_tbufcontrol_t *tbc)
{
    static spinlock_t lock = SPIN_LOCK_UNLOCKED;
    int rc = 0;

    spin_lock(&lock);

    if ( !tb_init_done &&
         (tbc->op != DOM0_TBUF_SET_SIZE) &&
         (tbc->op != DOM0_TBUF_ENABLE) )
    {
        spin_unlock(&lock);
        return -EINVAL;
    }

    switch ( tbc->op )
    {
    case DOM0_TBUF_GET_INFO:
        tbc->cpu_mask   = tb_cpu_mask;
        tbc->evt_mask   = tb_event_mask;
        tbc->buffer_mfn = __pa(t_bufs[0]) >> PAGE_SHIFT;
        tbc->size       = opt_tbuf_size * PAGE_SIZE;
        break;
    case DOM0_TBUF_SET_CPU_MASK:
        tb_cpu_mask = tbc->cpu_mask;
        break;
    case DOM0_TBUF_SET_EVT_MASK:
        tb_event_mask = tbc->evt_mask;
        break;
    case DOM0_TBUF_SET_SIZE:
        rc = !tb_init_done ? tb_set_size(tbc->size) : -EINVAL;
        break;
    case DOM0_TBUF_ENABLE:
        /* Enable trace buffers. Check buffers are already allocated. */
        if ( opt_tbuf_size == 0 ) 
            rc = -EINVAL;
        else
            tb_init_done = 1;
        break;
    case DOM0_TBUF_DISABLE:
        /*
         * Disable trace buffers. Just stops new records from being written,
         * does not deallocate any memory.
         */
        tb_init_done = 0;
        break;
    default:
        rc = -EINVAL;
        break;
    }

    spin_unlock(&lock);

    return rc;
}

/**
 * trace - Enters a trace tuple into the trace buffer for the current CPU.
 * @event: the event type being logged
 * @d1...d5: the data items for the event being logged
 *
 * Logs a trace record into the appropriate buffer.  Returns nonzero on
 * failure, otherwise 0.  Failure occurs only if the trace buffers are not yet
 * initialised.
 */
void trace(u32 event, unsigned long d1, unsigned long d2,
           unsigned long d3, unsigned long d4, unsigned long d5)
{
    struct t_buf *buf;
    struct t_rec *rec;
    unsigned long flags;

    BUG_ON(!tb_init_done);

    if ( (tb_event_mask & event) == 0 )
        return;

    /* match class */
    if ( ((tb_event_mask >> TRC_CLS_SHIFT) & (event >> TRC_CLS_SHIFT)) == 0 )
        return;

    /* then match subclass */
    if ( (((tb_event_mask >> TRC_SUBCLS_SHIFT) & 0xf )
                & ((event >> TRC_SUBCLS_SHIFT) & 0xf )) == 0 )
        return;

    if ( (tb_cpu_mask & (1UL << smp_processor_id())) == 0 )
        return;

    /* Read tb_init_done /before/ t_bufs. */
    rmb();

    buf = t_bufs[smp_processor_id()];

    local_irq_save(flags);

    if ( (buf->prod - buf->cons) >= nr_recs )
    {
        local_irq_restore(flags);
        return;
    }

    rec = &t_recs[smp_processor_id()][buf->prod % nr_recs];
    rec->cycles  = (u64)get_cycles();
    rec->event   = event;
    rec->data[0] = d1;
    rec->data[1] = d2;
    rec->data[2] = d3;
    rec->data[3] = d4;
    rec->data[4] = d5;

    wmb();
    buf->prod++;

    local_irq_restore(flags);
}

/*
 * Local variables:
 * mode: C
 * c-set-style: "BSD"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */