plain text document attachment (lguest64-debug-utils.patch) This patch contains some nice features used to debug the lguest64 guest. It has a way to print page tables for either the host or the guest. It incorporates kallsyms, and can do a nice back trace of a guest when it crashes. The guest needs kallsyms obviously compiled in. Note: This code needs to be fixed to be more secure! Implements a lgdebug_print that can be used within the host that will only print when lguest_debug is true. There's a hypercall that the guest can call to turn this on. There's also a function called lguest_set_debug(n) the makes it easy for the guest to turn it on. Where n=1 will turn on debugging prints, and n=0 will turn it off. (well n!=0 will turn it on). Signed-off-by: Steven Rostedt <srostedt@redhat.com> Cc: Glauber de Oliveira Costa <glommer@gmail.com> Cc: Chris Wright <chrisw@sous-sol.org> Index: work-pv/arch/x86_64/lguest/lguest_debug.c ==================================================================--- /dev/null +++ work-pv/arch/x86_64/lguest/lguest_debug.c @@ -0,0 +1,532 @@ +/* + lguest debug utils. Modified from various other parts of Linux. + What was modified is Copyright 2007 Steven Rostedt, Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/freezer.h> +#include <linux/kallsyms.h> +#include <asm/paravirt.h> +#include <asm/hv_vm.h> +#include <asm/pgtable.h> +#include <asm/uaccess.h> +#include "lguest.h" + +int lguest_debug; + +static DEFINE_SPINLOCK(lgdebug_print_lock); +#define LGDEBUG_BUF_SIZ 1024 +static char lgdebug_print_buf[LGDEBUG_BUF_SIZ]; + +void lgdebug_vprint(const char *fmt, va_list ap) +{ + unsigned long flags; + + if (!lguest_debug) + return; + + spin_lock_irqsave(&lgdebug_print_lock, flags); + vsnprintf(lgdebug_print_buf, LGDEBUG_BUF_SIZ-1, fmt, ap); + printk("%s", lgdebug_print_buf); + spin_unlock_irqrestore(&lgdebug_print_lock, flags); +} + +void lgdebug_print(const char *fmt, ...) +{ + va_list ap; + + if (!lguest_debug) + return; + + /* irq save? */ + va_start(ap, fmt); + lgdebug_vprint(fmt, ap); + va_end(ap); +} + +void lguest_dump_vcpu_regs(struct lguest_vcpu *vcpu) +{ + struct lguest_regs *regs = &vcpu->regs; + + printk("Printing VCPU %d regs cr3: %016llx\n", vcpu->id, regs->cr3); + printk("RIP: %04llx: ", regs->cs & 0xffff); + lguest_print_address(vcpu, regs->rip); + printk("RSP: %04llx:%016llx EFLAGS: %08llx\n", regs->ss, regs->rsp, + regs->rflags); + printk("RAX: %016llx RBX: %016llx RCX: %016llx\n", + regs->rax, regs->rbx, regs->rcx); + printk("RDX: %016llx RSI: %016llx RDI: %016llx\n", + regs->rdx, regs->rsi, regs->rdi); + printk("RBP: %016llx R08: %016llx R09: %016llx\n", + regs->rbp, regs->r8, regs->r9); + printk("R10: %016llx R11: %016llx R12: %016llx\n", + regs->r10, regs->r11, regs->r12); + printk("R13: %016llx R14: %016llx R15: %016llx\n", + regs->r13, regs->r14, regs->r15); + + printk("errcode: %llx trapnum: %llx\n", + regs->errcode, regs->trapnum); + + lguest_dump_trace(vcpu, regs); +} + +struct guest_ksym_stuff { + unsigned long *addresses; + unsigned long num_syms; + u8 *names; + u8 *token_table; + u16 *token_index; + unsigned long *markers; +}; + +static struct lguest_text_ptr *get_text_segs(struct lguest_vcpu *vcpu) +{ + struct lguest_guest_info *linfo = vcpu->guest; + struct lguest_text_ptr *segs, **p; + struct lguest_text_ptr *g; + unsigned long addr; + int i; + + if (!linfo->lguest_data) + return NULL; + + addr = lhread_u64(vcpu, (u64)&linfo->lguest_data->text); + if (!addr) + return NULL; + + g = (struct lguest_text_ptr*)addr; + + p = &segs; + + /* only allow for 10 segs */ + for (i=0; i < 10; i++) { + *p = kmalloc(sizeof(*segs), GFP_KERNEL); + if (!*p) + goto free_me; + (*p)->start = lhread_u64(vcpu, (u64)&g->start); + (*p)->end = lhread_u64(vcpu, (u64)&g->end); + addr = lhread_u64(vcpu, (u64)&g->next); + p = (struct lguest_text_ptr**)&((*p)->next); + if (!addr) + break; + g = (struct lguest_text_ptr*)addr; + } + *p = NULL; + + return segs; + +free_me: + while (segs) { + g = (struct lguest_text_ptr*)segs->next; + kfree(segs); + segs = g; + } + return NULL; +} + +static int is_text_seg(struct lguest_text_ptr *segs, unsigned long addr) +{ + while (segs) { + if (addr >= segs->start && + addr <= segs->end) + return 1; + segs = (struct lguest_text_ptr*)segs->next; + } + return 0; +} + +static void put_text_segs(struct lguest_text_ptr *segs) +{ + struct lguest_text_ptr *p; + + while (segs) { + p = (struct lguest_text_ptr*)segs->next; + kfree(segs); + segs = p; + } +} + +static unsigned int expand_symbol(struct lguest_vcpu *vcpu, + struct guest_ksym_stuff *kstuff, + unsigned int off, char *result) +{ + int len, skipped_first = 0; + const u8 *tptr, *data; + + /* get the compressed symbol length from the first symbol byte */ + data = &kstuff->names[off]; + + len = lhread_u8(vcpu, (u64)data); + + data++; + + /* update the offset to return the offset for the next symbol on + * the compressed stream */ + off += len + 1; + + /* for every byte on the compressed symbol data, copy the table + entry for that byte */ + while(len) { + u8 idx; + u16 tok; + idx = lhread_u8(vcpu, (u64)data); + tok = lhread_u16(vcpu, (u64)(&kstuff->token_index[idx])); + tptr = &kstuff->token_table[ tok ]; + data++; + len--; + + idx = lhread_u8(vcpu, (u64)tptr); + while (idx) { + if(skipped_first) { + *result = idx; + result++; + } else + skipped_first = 1; + tptr++; + idx = lhread_u8(vcpu, (u64)tptr); + } + } + + *result = '\0'; + + /* return to offset to the next symbol */ + return off; +} + +static unsigned long get_symbol_pos(struct lguest_vcpu *vcpu, + struct guest_ksym_stuff *kstuff, + unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset) +{ + unsigned long symbol_start = 0, symbol_end = 0; + unsigned long i, low, high, mid; + + /* do a binary search on the sorted kallsyms_addresses array */ + low = 0; + high = kstuff->num_syms; + + while (high - low > 1) { + mid = (low + high) / 2; + if (kstuff->addresses[mid] <= addr) + low = mid; + else + high = mid; + } + + /* + * search for the first aliased symbol. Aliased + * symbols are symbols with the same address + */ + while (low && kstuff->addresses[low-1] == kstuff->addresses[low]) + --low; + + symbol_start = kstuff->addresses[low]; + + /* Search for next non-aliased symbol */ + for (i = low + 1; i < kstuff->num_syms; i++) { + if (kstuff->addresses[i] > symbol_start) { + symbol_end = kstuff->addresses[i]; + break; + } + } + + /* if we found no next symbol, we use the end of the section */ + if (!symbol_end) { + return (unsigned long)(-1UL); +#if 0 + if (is_kernel_inittext(addr)) + symbol_end = (unsigned long)_einittext; + else if (all_var) + symbol_end = (unsigned long)_end; + else + symbol_end = (unsigned long)_etext; +#endif + } + + *symbolsize = symbol_end - symbol_start; + *offset = addr - symbol_start; + + return low; +} + +static int is_ksym_addr(struct lguest_guest_info *linfo, + unsigned long addr) +{ + /* need to look up the segs */ + return 1; +} + +static unsigned int get_symbol_offset(struct lguest_vcpu *vcpu, + struct guest_ksym_stuff *kstuff, + unsigned long pos) +{ + const u8 *name; + int i; + unsigned long idx; + + idx = lhread_u64(vcpu, (u64)&kstuff->markers[pos>>8]); + + /* use the closest marker we have. We have markers every 256 positions, + * so that should be close enough */ + name = &kstuff->names[ idx ]; + + /* sequentially scan all the symbols up to the point we're searching for. + * Every symbol is stored in a [<len>][<len> bytes of data] format, so we + * just need to add the len to the current pointer for every symbol we + * wish to skip */ + for(i = 0; i < (pos&0xFF); i++) { + u8 c; + c = lhread_u8(vcpu, (u64)name); + name = name + c + 1; + } + + return name - kstuff->names; +} + +static const char *lguest_syms_lookup(struct lguest_vcpu *vcpu, + unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset, + char **modname, char *namebuf) +{ + struct lguest_guest_info *linfo = vcpu->guest; + struct lguest_data *data = linfo->lguest_data; + struct guest_ksym_stuff kstuff; + const char *msym; + unsigned long *ptr; + int i; + + kstuff.addresses = (unsigned long*)lhread_u64(vcpu, (u64)&data->kallsyms_addresses); + kstuff.num_syms = lhread_u64(vcpu, (u64)&data->kallsyms_num_syms); + kstuff.names = (u8*)lhread_u64(vcpu, (u64)&data->kallsyms_names); + kstuff.token_table = (u8*)lhread_u64(vcpu, (u64)&data->kallsyms_token_table); + kstuff.token_index = (u16*)lhread_u64(vcpu, (u64)&data->kallsyms_token_index); + kstuff.markers = (unsigned long*)lhread_u64(vcpu, (u64)&data->kallsyms_markers); + + if (!kstuff.addresses || !kstuff.num_syms || !kstuff.names || + !kstuff.token_table || !kstuff.token_index || !kstuff.markers) + return NULL; + + /* FIXME: Validate all the kstuff here!! */ + + ptr = kmalloc(sizeof(unsigned long)*kstuff.num_syms, GFP_KERNEL); + if (!ptr) + return NULL; + + for (i=0; i < kstuff.num_syms; i++) { + /* FIXME: do this better! */ + ptr[i] = lhread_u64(vcpu, (u64)&kstuff.addresses[i]); + if (i && ptr[i] < ptr[i-1]) { + kill_guest(linfo, "bad kallsyms table\n"); + kstuff.addresses = ptr; + goto out; + } + } + kstuff.addresses = ptr; + + namebuf[KSYM_NAME_LEN] = 0; + namebuf[0] = 0; + + if (is_ksym_addr(linfo, addr)) { + unsigned long pos; + + pos = get_symbol_pos(vcpu, &kstuff, addr, symbolsize, offset); + if (pos == (unsigned long)(-1UL)) + goto out; + + /* Grab name */ + expand_symbol(vcpu, &kstuff, + get_symbol_offset(vcpu, &kstuff, pos), namebuf); + *modname = NULL; + kfree(kstuff.addresses); + return namebuf; + } + + /* see if it's in a module */ + msym = module_address_lookup(addr, symbolsize, offset, modname); + if (msym) { + kfree(kstuff.addresses); + return strncpy(namebuf, msym, KSYM_NAME_LEN); + } + +out: + kfree(kstuff.addresses); + return NULL; +} + +void lguest_print_address(struct lguest_vcpu *vcpu, unsigned long address) +{ + unsigned long offset = 0, symsize; + const char *symname; + char *modname; + char *delim = ":"; + char namebuf[KSYM_NAME_LEN+1]; + + symname = lguest_syms_lookup(vcpu, address, &symsize, &offset, + &modname, namebuf); + if (!symname) { + printk(" [<%016lx>]\n", address); + return; + } + if (!modname) + modname = delim = ""; + printk(" [<%016lx>] %s%s%s%s+0x%lx/0x%lx\n", + address, delim, modname, delim, symname, offset, symsize); + +} + +void lguest_dump_trace(struct lguest_vcpu *vcpu, struct lguest_regs *regs) +{ + unsigned long stack = regs->rsp; + unsigned long stack_end = (regs->rsp & PAGE_MASK) + PAGE_SIZE; + unsigned long start_kernel_map; + unsigned long page_offset; + unsigned long addr; + struct lguest_text_ptr *segs; + + printk("Stack Dump:\n"); + start_kernel_map = vcpu->guest->start_kernel_map; + page_offset = vcpu->guest->page_offset; + + segs = get_text_segs(vcpu); + if (!segs) + return; + + for (; stack < stack_end; stack += sizeof(stack)) { + addr = lhread_u64(vcpu, guest_pa(vcpu->guest, stack)); + if (is_text_seg(segs, addr)) { + lguest_print_address(vcpu, addr); + } + } + + put_text_segs(segs); +} + +static u64 read_page(struct lguest_vcpu *vcpu, u64 page, u64 idx) +{ + u64 *ptr; + + if (!vcpu) { + ptr = __va(page); + return ptr[idx]; + } + + return lhread_u64(vcpu, page+idx*sizeof(u64)); +} + +static void print_pte(u64 pte, u64 pgd_idx, u64 pud_idx, u64 pmd_idx, u64 pte_idx) +{ + printk(" %3llx: %llx\n", pte_idx, pte); + printk (" (%llx)\n", + ((pgd_idx&(1<<8)?(-1ULL):0ULL)<<48) | + (pgd_idx<<PGDIR_SHIFT) | + (pud_idx<<PUD_SHIFT) | + (pmd_idx<<PMD_SHIFT) | + (pte_idx<<PAGE_SHIFT)); +} + +static void print_pmd(struct lguest_vcpu *vcpu, + u64 pmd, u64 pgd_idx, u64 pud_idx, u64 pmd_idx) +{ + u64 pte; + u64 ptr; + u64 i; + + printk(" %3llx: %llx\n", pmd_idx, pmd); + + /* 2M page? */ + if (pmd & (1<<7)) { + printk (" (%llx)\n", + ((pgd_idx&(1<<8)?(-1ULL):0ULL)<<48) | + (pgd_idx<<PGDIR_SHIFT) | + (pud_idx<<PUD_SHIFT) | + (pmd_idx<<PMD_SHIFT)); + } else { + pte = pmd & ~(0xfff) & ~(1UL << 63); + for (i=0; i < PTRS_PER_PTE; i++) { + ptr = read_page(vcpu, pte, i); + if (ptr) + print_pte(ptr, pgd_idx, pud_idx, pmd_idx, i); + } + } +} + +static void print_pud(struct lguest_vcpu *vcpu, + u64 pud, u64 pgd_idx, u64 pud_idx) +{ + u64 pmd; + u64 ptr; + u64 i; + + printk(" %3llx: %llx\n", pud_idx, pud); + + pmd = pud & ~(0xfff) & ~(1UL << 63); + for (i=0; i < PTRS_PER_PMD; i++) { + ptr = read_page(vcpu, pmd, i); + if (ptr) + print_pmd(vcpu, ptr, pgd_idx, pud_idx, i); + } +} + +static void print_pgd(struct lguest_vcpu *vcpu, + u64 pgd, u64 pgd_idx) +{ + u64 pud; + u64 ptr; + u64 i; + + printk(" %3llx: %llx\n", pgd_idx, pgd); + pud = pgd & ~(0xfff) & ~(1UL << 63); + for (i=0; i < PTRS_PER_PUD; i++) { + ptr = read_page(vcpu, pud, i); + if (ptr) + print_pud(vcpu, ptr, pgd_idx, i); + } + +} + +static void print_page_tables(struct lguest_vcpu *vcpu, + u64 cr3) +{ + u64 pgd; + u64 ptr; + u64 i; + + printk("cr3: %016llx\n", cr3); + pgd = cr3; + + for (i=0; i < PTRS_PER_PGD; i++) { + ptr = read_page(vcpu, pgd, i); + if (ptr) + print_pgd(vcpu, ptr, i); + } +} + +void lguest_print_page_tables(u64 *cr3) +{ + if (!cr3) { + printk("NULL cr3 pointer????\n"); + return; + } + print_page_tables(NULL, __pa(cr3)); +} + +void lguest_print_guest_page_tables(struct lguest_vcpu *vcpu, u64 cr3) +{ + print_page_tables(vcpu, cr3); +} --