aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/x86/oprofile/backtrace.c
blob: b3ea7f386e3565a367d8588bcf7ad237c3992a97 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
/**
 * @file backtrace.c
 *
 * @remark Copyright 2002 OProfile authors
 * @remark Read the file COPYING
 *
 * @author John Levon
 * @author David Smith
 * Modified for Xen by Amitabha Roy
 *
 */

#include <xen/types.h>
#include <asm/page.h>
#include <xen/xenoprof.h>
#include <xen/guest_access.h>

struct frame_head {
    struct frame_head * ebp;
    unsigned long ret;
} __attribute__((packed));
typedef struct frame_head frame_head_t;
DEFINE_XEN_GUEST_HANDLE(frame_head_t);

struct frame_head_32bit {
    uint32_t ebp;
    uint32_t ret;
} __attribute__((packed));
typedef struct frame_head_32bit frame_head32_t;
DEFINE_COMPAT_HANDLE(frame_head32_t);

static struct frame_head *
dump_hypervisor_backtrace(struct vcpu *vcpu, const struct frame_head *head,
                          int mode)
{
    if (!xenoprof_add_trace(vcpu, head->ret, mode))
        return 0;
    
    /* frame pointers should strictly progress back up the stack
     * (towards higher addresses) */
    if (head >= head->ebp)
        return NULL;
    
    return head->ebp;
}

static inline int is_32bit_vcpu(struct vcpu *vcpu)
{
    if (is_hvm_vcpu(vcpu))
        return !hvm_long_mode_enabled(vcpu);
    else
        return is_pv_32bit_vcpu(vcpu);
}

static struct frame_head *
dump_guest_backtrace(struct vcpu *vcpu, const struct frame_head *head,
                     int mode)
{
    frame_head_t bufhead;

    if ( is_32bit_vcpu(vcpu) )
    {
        __compat_handle_const_frame_head32_t guest_head =
            { .c = (unsigned long)head };
        frame_head32_t bufhead32;

        /* Also check accessibility of one struct frame_head beyond */
        if (!compat_handle_okay(guest_head, 2))
            return 0;
        if (__copy_from_compat(&bufhead32, guest_head, 1))
            return 0;
        bufhead.ebp = (struct frame_head *)(unsigned long)bufhead32.ebp;
        bufhead.ret = bufhead32.ret;
    }
    else
    {
        XEN_GUEST_HANDLE(const_frame_head_t) guest_head;
        XEN_GUEST_HANDLE_PARAM(const_frame_head_t) guest_head_param =
            const_guest_handle_from_ptr(head, frame_head_t);
        guest_head = guest_handle_from_param(guest_head_param,
					     const_frame_head_t);

        /* Also check accessibility of one struct frame_head beyond */
        if (!guest_handle_okay(guest_head, 2))
            return 0;
        if (__copy_from_guest(&bufhead, guest_head, 1))
            return 0;
    }
    
    if (!xenoprof_add_trace(vcpu, bufhead.ret, mode))
        return 0;
    
    /* frame pointers should strictly progress back up the stack
     * (towards higher addresses) */
    if (head >= bufhead.ebp)
        return NULL;
    
    return bufhead.ebp;
}

/*
 * |             | /\ Higher addresses
 * |             |
 * --------------- stack base (address of current_thread_info)
 * | thread info |
 * .             .
 * |    stack    |
 * --------------- saved regs->ebp value if valid (frame_head address)
 * .             .
 * --------------- saved regs->rsp value if x86_64
 * |             |
 * --------------- struct pt_regs * stored on stack if 32-bit
 * |             |
 * .             .
 * |             |
 * --------------- %esp
 * |             |
 * |             | \/ Lower addresses
 *
 * Thus, regs (or regs->rsp for x86_64) <-> stack base restricts the
 * valid(ish) ebp values. Note: (1) for x86_64, NMI and several other
 * exceptions use special stacks, maintained by the interrupt stack table
 * (IST). These stacks are set up in trap_init() in
 * arch/x86_64/kernel/traps.c. Thus, for x86_64, regs now does not point
 * to the kernel stack; instead, it points to some location on the NMI
 * stack. On the other hand, regs->rsp is the stack pointer saved when the
 * NMI occurred. (2) For 32-bit, regs->esp is not valid because the
 * processor does not save %esp on the kernel stack when interrupts occur
 * in the kernel mode.
 */
#if defined(CONFIG_FRAME_POINTER)
static int valid_hypervisor_stack(const struct frame_head *head,
				  const struct cpu_user_regs *regs)
{
    unsigned long headaddr = (unsigned long)head;
    unsigned long stack = (unsigned long)regs->rsp;
    unsigned long stack_base = (stack & ~(STACK_SIZE - 1)) + STACK_SIZE;

    return headaddr > stack && headaddr < stack_base;
}
#else
/* without fp, it's just junk */
static int valid_hypervisor_stack(const struct frame_head *head,
				  const struct cpu_user_regs *regs)
{
    return 0;
}
#endif

void xenoprof_backtrace(struct vcpu *vcpu, const struct cpu_user_regs *regs,
			unsigned long depth, int mode)
{
    const struct frame_head *head = (void *)regs->ebp;

    if (mode > 1) {
        while (depth-- && valid_hypervisor_stack(head, regs))
            head = dump_hypervisor_backtrace(vcpu, head, mode);
        return;
    }

    while (depth-- && head)
        head = dump_guest_backtrace(vcpu, head, mode);
}