debuggers.hg

annotate xen/arch/x86/debug.c @ 20964:a3fa6d444b25

Fix domain reference leaks

Besides two unlikely/rarely hit ones in x86 code, the main offender
was tmh_client_from_cli_id(), which didn't even have a counterpart
(albeit it had a comment correctly saying that it causes d->refcnt to
get incremented). Unfortunately(?) this required a bit of code
restructuring (as I needed to change the code anyway, I also fixed
a couple os missing bounds checks which would sooner or later be
reported as security vulnerabilities), so I would hope Dan could give
it his blessing before it gets applied.

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