/root/src/xen/xen/arch/x86/debug.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2009, Mukesh Rathor, Oracle Corp. All rights reserved. |
3 | | * |
4 | | * This program is free software; you can redistribute it and/or |
5 | | * modify it under the terms of the GNU General Public |
6 | | * License v2 as published by the Free Software Foundation. |
7 | | * |
8 | | * This program is distributed in the hope that it will be useful, |
9 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
11 | | * General Public License for more details. |
12 | | * |
13 | | * You should have received a copy of the GNU General Public |
14 | | * License along with this program; If not, see <http://www.gnu.org/licenses/>. |
15 | | */ |
16 | | |
17 | | #include <xen/sched.h> |
18 | | #include <xen/compile.h> |
19 | | #include <xen/mm.h> |
20 | | #include <xen/domain_page.h> |
21 | | #include <xen/guest_access.h> |
22 | | #include <asm/debugger.h> |
23 | | #include <asm/p2m.h> |
24 | | |
25 | | /* |
26 | | * This file for general routines common to more than one debugger, like kdb, |
27 | | * gdbsx, etc.. |
28 | | */ |
29 | | |
30 | | #ifdef XEN_KDB_CONFIG |
31 | | #include "../kdb/include/kdbdefs.h" |
32 | | #include "../kdb/include/kdbproto.h" |
33 | | #define DBGP(...) {(kdbdbg) ? kdbp(__VA_ARGS__):0;} |
34 | | #define DBGP1(...) {(kdbdbg>1) ? kdbp(__VA_ARGS__):0;} |
35 | | #define DBGP2(...) {(kdbdbg>2) ? kdbp(__VA_ARGS__):0;} |
36 | | #else |
37 | 0 | #define DBGP1(...) ((void)0) |
38 | 0 | #define DBGP2(...) ((void)0) |
39 | | #endif |
40 | | |
41 | | typedef unsigned long dbgva_t; |
42 | | typedef unsigned char dbgbyte_t; |
43 | | |
44 | | /* Returns: mfn for the given (hvm guest) vaddr */ |
45 | | static mfn_t |
46 | | dbg_hvm_va2mfn(dbgva_t vaddr, struct domain *dp, int toaddr, gfn_t *gfn) |
47 | 0 | { |
48 | 0 | mfn_t mfn; |
49 | 0 | uint32_t pfec = PFEC_page_present; |
50 | 0 | p2m_type_t gfntype; |
51 | 0 |
|
52 | 0 | DBGP2("vaddr:%lx domid:%d\n", vaddr, dp->domain_id); |
53 | 0 |
|
54 | 0 | *gfn = _gfn(paging_gva_to_gfn(dp->vcpu[0], vaddr, &pfec)); |
55 | 0 | if ( gfn_eq(*gfn, INVALID_GFN) ) |
56 | 0 | { |
57 | 0 | DBGP2("kdb:bad gfn from gva_to_gfn\n"); |
58 | 0 | return INVALID_MFN; |
59 | 0 | } |
60 | 0 |
|
61 | 0 | mfn = get_gfn(dp, gfn_x(*gfn), &gfntype); |
62 | 0 | if ( p2m_is_readonly(gfntype) && toaddr ) |
63 | 0 | { |
64 | 0 | DBGP2("kdb:p2m_is_readonly: gfntype:%x\n", gfntype); |
65 | 0 | mfn = INVALID_MFN; |
66 | 0 | } |
67 | 0 | else |
68 | 0 | DBGP2("X: vaddr:%lx domid:%d mfn:%#"PRI_mfn"\n", |
69 | 0 | vaddr, dp->domain_id, mfn_x(mfn)); |
70 | 0 |
|
71 | 0 | if ( mfn_eq(mfn, INVALID_MFN) ) |
72 | 0 | { |
73 | 0 | put_gfn(dp, gfn_x(*gfn)); |
74 | 0 | *gfn = INVALID_GFN; |
75 | 0 | } |
76 | 0 |
|
77 | 0 | return mfn; |
78 | 0 | } |
79 | | |
80 | | /* |
81 | | * pgd3val: this is the value of init_mm.pgd[3] in a PV guest. It is optional. |
82 | | * This to assist debug of modules in the guest. The kernel address |
83 | | * space seems is always mapped, but modules are not necessarily |
84 | | * mapped in any arbitraty guest cr3 that we pick if pgd3val is 0. |
85 | | * Modules should always be addressible if we use cr3 from init_mm. |
86 | | * Since pgd3val is already a pgd value, cr3->pgd[3], we just need to |
87 | | * do 2 level lookups. |
88 | | * |
89 | | * NOTE: 4 level paging works for 32 PAE guests also because cpu runs in IA32-e |
90 | | * mode. |
91 | | * Returns: mfn for the given (pv guest) vaddr |
92 | | */ |
93 | | static mfn_t |
94 | | dbg_pv_va2mfn(dbgva_t vaddr, struct domain *dp, uint64_t pgd3val) |
95 | 0 | { |
96 | 0 | l4_pgentry_t l4e, *l4t; |
97 | 0 | l3_pgentry_t l3e, *l3t; |
98 | 0 | l2_pgentry_t l2e, *l2t; |
99 | 0 | l1_pgentry_t l1e, *l1t; |
100 | 0 | unsigned long cr3 = (pgd3val ? pgd3val : dp->vcpu[0]->arch.cr3); |
101 | 0 | mfn_t mfn = maddr_to_mfn(cr3); |
102 | 0 |
|
103 | 0 | DBGP2("vaddr:%lx domid:%d cr3:%lx pgd3:%lx\n", vaddr, dp->domain_id, |
104 | 0 | cr3, pgd3val); |
105 | 0 |
|
106 | 0 | if ( pgd3val == 0 ) |
107 | 0 | { |
108 | 0 | l4t = map_domain_page(mfn); |
109 | 0 | l4e = l4t[l4_table_offset(vaddr)]; |
110 | 0 | unmap_domain_page(l4t); |
111 | 0 | mfn = l4e_get_mfn(l4e); |
112 | 0 | DBGP2("l4t:%p l4to:%lx l4e:%lx mfn:%#"PRI_mfn"\n", l4t, |
113 | 0 | l4_table_offset(vaddr), l4e, mfn_x(mfn)); |
114 | 0 | if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) |
115 | 0 | { |
116 | 0 | DBGP1("l4 PAGE not present. vaddr:%lx cr3:%lx\n", vaddr, cr3); |
117 | 0 | return INVALID_MFN; |
118 | 0 | } |
119 | 0 |
|
120 | 0 | l3t = map_domain_page(mfn); |
121 | 0 | l3e = l3t[l3_table_offset(vaddr)]; |
122 | 0 | unmap_domain_page(l3t); |
123 | 0 | mfn = l3e_get_mfn(l3e); |
124 | 0 | DBGP2("l3t:%p l3to:%lx l3e:%lx mfn:%#"PRI_mfn"\n", l3t, |
125 | 0 | l3_table_offset(vaddr), l3e, mfn_x(mfn)); |
126 | 0 | if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || |
127 | 0 | (l3e_get_flags(l3e) & _PAGE_PSE) ) |
128 | 0 | { |
129 | 0 | DBGP1("l3 PAGE not present. vaddr:%lx cr3:%lx\n", vaddr, cr3); |
130 | 0 | return INVALID_MFN; |
131 | 0 | } |
132 | 0 | } |
133 | 0 |
|
134 | 0 | l2t = map_domain_page(mfn); |
135 | 0 | l2e = l2t[l2_table_offset(vaddr)]; |
136 | 0 | unmap_domain_page(l2t); |
137 | 0 | mfn = l2e_get_mfn(l2e); |
138 | 0 | DBGP2("l2t:%p l2to:%lx l2e:%lx mfn:%#"PRI_mfn"\n", |
139 | 0 | l2t, l2_table_offset(vaddr), l2e, mfn_x(mfn)); |
140 | 0 | if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || |
141 | 0 | (l2e_get_flags(l2e) & _PAGE_PSE) ) |
142 | 0 | { |
143 | 0 | DBGP1("l2 PAGE not present. vaddr:%lx cr3:%lx\n", vaddr, cr3); |
144 | 0 | return INVALID_MFN; |
145 | 0 | } |
146 | 0 | l1t = map_domain_page(mfn); |
147 | 0 | l1e = l1t[l1_table_offset(vaddr)]; |
148 | 0 | unmap_domain_page(l1t); |
149 | 0 | mfn = l1e_get_mfn(l1e); |
150 | 0 | DBGP2("l1t:%p l1to:%lx l1e:%lx mfn:%#"PRI_mfn"\n", l1t, l1_table_offset(vaddr), |
151 | 0 | l1e, mfn_x(mfn)); |
152 | 0 |
|
153 | 0 | return mfn_valid(mfn) ? mfn : INVALID_MFN; |
154 | 0 | } |
155 | | |
156 | | /* Returns: number of bytes remaining to be copied */ |
157 | | static unsigned int dbg_rw_guest_mem(struct domain *dp, void * __user gaddr, |
158 | | void * __user buf, unsigned int len, |
159 | | bool toaddr, uint64_t pgd3) |
160 | 0 | { |
161 | 0 | while ( len > 0 ) |
162 | 0 | { |
163 | 0 | char *va; |
164 | 0 | unsigned long addr = (unsigned long)gaddr; |
165 | 0 | mfn_t mfn; |
166 | 0 | gfn_t gfn = INVALID_GFN; |
167 | 0 | unsigned long pagecnt; |
168 | 0 |
|
169 | 0 | pagecnt = min_t(long, PAGE_SIZE - (addr & ~PAGE_MASK), len); |
170 | 0 |
|
171 | 0 | mfn = (is_hvm_domain(dp) |
172 | 0 | ? dbg_hvm_va2mfn(addr, dp, toaddr, &gfn) |
173 | 0 | : dbg_pv_va2mfn(addr, dp, pgd3)); |
174 | 0 | if ( mfn_eq(mfn, INVALID_MFN) ) |
175 | 0 | break; |
176 | 0 |
|
177 | 0 | va = map_domain_page(mfn); |
178 | 0 | va = va + (addr & (PAGE_SIZE-1)); |
179 | 0 |
|
180 | 0 | if ( toaddr ) |
181 | 0 | { |
182 | 0 | copy_from_user(va, buf, pagecnt); /* va = buf */ |
183 | 0 | paging_mark_dirty(dp, mfn); |
184 | 0 | } |
185 | 0 | else |
186 | 0 | { |
187 | 0 | copy_to_user(buf, va, pagecnt); /* buf = va */ |
188 | 0 | } |
189 | 0 |
|
190 | 0 | unmap_domain_page(va); |
191 | 0 | if ( !gfn_eq(gfn, INVALID_GFN) ) |
192 | 0 | put_gfn(dp, gfn_x(gfn)); |
193 | 0 |
|
194 | 0 | addr += pagecnt; |
195 | 0 | buf += pagecnt; |
196 | 0 | len -= pagecnt; |
197 | 0 | } |
198 | 0 |
|
199 | 0 | return len; |
200 | 0 | } |
201 | | |
202 | | /* |
203 | | * addr is hypervisor addr if domid == DOMID_IDLE, else it's guest addr |
204 | | * buf is debugger buffer. |
205 | | * if toaddr, then addr = buf (write to addr), else buf = addr (rd from guest) |
206 | | * pgd3: value of init_mm.pgd[3] in guest. see above. |
207 | | * Returns: number of bytes remaining to be copied. |
208 | | */ |
209 | | unsigned int dbg_rw_mem(void * __user addr, void * __user buf, |
210 | | unsigned int len, domid_t domid, bool toaddr, |
211 | | uint64_t pgd3) |
212 | 0 | { |
213 | 0 | DBGP2("gmem:addr:%lx buf:%p len:$%u domid:%d toaddr:%x\n", |
214 | 0 | addr, buf, len, domid, toaddr); |
215 | 0 |
|
216 | 0 | if ( domid == DOMID_IDLE ) |
217 | 0 | { |
218 | 0 | if ( toaddr ) |
219 | 0 | len = __copy_to_user(addr, buf, len); |
220 | 0 | else |
221 | 0 | len = __copy_from_user(buf, addr, len); |
222 | 0 | } |
223 | 0 | else |
224 | 0 | { |
225 | 0 | struct domain *d = get_domain_by_id(domid); |
226 | 0 |
|
227 | 0 | if ( d ) |
228 | 0 | { |
229 | 0 | if ( !d->is_dying ) |
230 | 0 | len = dbg_rw_guest_mem(d, addr, buf, len, toaddr, pgd3); |
231 | 0 | put_domain(d); |
232 | 0 | } |
233 | 0 | } |
234 | 0 |
|
235 | 0 | DBGP2("gmem:exit:len:$%d\n", len); |
236 | 0 | return len; |
237 | 0 | } |
238 | | |
239 | | /* |
240 | | * Local variables: |
241 | | * mode: C |
242 | | * c-file-style: "BSD" |
243 | | * c-basic-offset: 4 |
244 | | * indent-tabs-mode: nil |
245 | | * End: |
246 | | */ |