[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [RFC PATCH 1/2]: hypervisor debugger



> diff -r 32034d1914a6 xen/Rules.mk
> --- a/xen/Rules.mk      Thu Jun 07 19:46:57 2012 +0100
> +++ b/xen/Rules.mk      Wed Aug 29 14:39:57 2012 -0700
> @@ -53,6 +55,7 @@
>  CFLAGS-$(HAS_ACPI)      += -DHAS_ACPI
>  CFLAGS-$(HAS_PASSTHROUGH) += -DHAS_PASSTHROUGH
>  CFLAGS-$(frame_pointer) += -fno-omit-frame-pointer -DCONFIG_FRAME_POINTER
> +CFLAGS-$(kdb)           += -DXEN_KDB_CONFIG

Pretty much everywhere else uses CONFIG_FOO not FOO_CONFIG. Also isn't
XEN rather redundant in the context?

> 
>  ifneq ($(max_phys_cpus),)
>  CFLAGS-y                += -DMAX_PHYS_CPUS=$(max_phys_cpus)
> diff -r 32034d1914a6 xen/arch/x86/hvm/svm/entry.S
> --- a/xen/arch/x86/hvm/svm/entry.S      Thu Jun 07 19:46:57 2012 +0100
> +++ b/xen/arch/x86/hvm/svm/entry.S      Wed Aug 29 14:39:57 2012 -0700
> @@ -59,12 +59,23 @@
>          get_current(bx)
>          CLGI
> 
> +#ifdef XEN_KDB_CONFIG
> +#if defined(__x86_64__)
> +        testl $1, kdb_session_begun(%rip)
> +#else
> +        testl $1, kdb_session_begun
> +#endif

This inner #ifdef is what the addr_of macro does.

> +        jnz  .Lkdb_skip_softirq
> +#endif
>          mov  VCPU_processor(r(bx)),%eax
>          shl  $IRQSTAT_shift,r(ax)
>          lea  addr_of(irq_stat),r(dx)
>          testl $~0,(r(dx),r(ax),1)
>          jnz  .Lsvm_process_softirqs
> 
> +#ifdef XEN_KDB_CONFIG
> +.Lkdb_skip_softirq:
> +#endif

Does gas complain about unused labels? IOW can you omit the ifdef here?

Why does kdb skip soft irqs? I presume the final submission will include
a commit log which will explain this sort of thing?

>          testb $0, VCPU_nsvm_hap_enabled(r(bx))
>  UNLIKELY_START(nz, nsvm_hap)
>          mov  VCPU_nhvm_p2m(r(bx)),r(ax)
> diff -r 32034d1914a6 xen/arch/x86/hvm/svm/svm.c
> --- a/xen/arch/x86/hvm/svm/svm.c        Thu Jun 07 19:46:57 2012 +0100
> +++ b/xen/arch/x86/hvm/svm/svm.c        Wed Aug 29 14:39:57 2012 -0700
> @@ -2170,6 +2170,10 @@
>          break;
> 
>      case VMEXIT_EXCEPTION_DB:
> +#ifdef XEN_KDB_CONFIG
> +        if (kdb_handle_trap_entry(TRAP_debug, regs))
> +           break;

defining kdb_handle_trap_entry as a nop when !KDB might make this and
the following similar examples tidier.

> +#endif
>          if ( !v->domain->debugger_attached )
>              goto exit_and_crash;
>          domain_pause_for_debugger();
> @@ -2182,6 +2186,10 @@
>          if ( (inst_len = __get_instruction_length(v, INSTR_INT3)) == 0 )
>              break;
>          __update_guest_eip(regs, inst_len);
> +#ifdef XEN_KDB_CONFIG
> +        if (kdb_handle_trap_entry(TRAP_int3, regs))
> +            break;
> +#endif
>          current->arch.gdbsx_vcpu_event = TRAP_int3;
>          domain_pause_for_debugger();
>          break;
> diff -r 32034d1914a6 xen/arch/x86/hvm/svm/vmcb.c
> --- a/xen/arch/x86/hvm/svm/vmcb.c       Thu Jun 07 19:46:57 2012 +0100
> +++ b/xen/arch/x86/hvm/svm/vmcb.c       Wed Aug 29 14:39:57 2012 -0700
> @@ -315,6 +315,36 @@
>      register_keyhandler('v', &vmcb_dump_keyhandler);
>  }
> 
> +#if defined(XEN_KDB_CONFIG)
> +/* did == 0 : display for all HVM domains. domid 0 is never HVM.

Ahem, I think you have a patch which kind of changes that ;-)

(I notice you have _or_hyb_domain below so I guess you've thought of
it ;-). Perhaps you culd use DOMID_INVALID or something as your flag?

> + *  * vid == -1 : display for all HVM VCPUs
> + *   */
> +void kdb_dump_vmcb(domid_t did, int vid)
> +{
> +    struct domain *dp;
> +    struct vcpu *vp;
> +
> +    rcu_read_lock(&domlist_read_lock);
> +    for_each_domain (dp) {
> +        if (!is_hvm_or_hyb_domain(dp) || dp->is_dying)
> +            continue;
> +        if (did != 0 && did != dp->domain_id)
> +            continue;
> +
> +        for_each_vcpu (dp, vp) {
> +            if (vid != -1 && vid != vp->vcpu_id)
> +                continue;
> +
> +            kdbp("  VMCB [domid: %d  vcpu:%d]:\n", dp->domain_id, 
> vp->vcpu_id);
> +            svm_vmcb_dump("kdb", vp->arch.hvm_svm.vmcb);
> +            kdbp("\n");
> +        }
> +        kdbp("\n");
> +    }
> +    rcu_read_unlock(&domlist_read_lock);
> +}
> +#endif
> +
>  /*
>   * Local variables:
>   * mode: C
> diff -r 32034d1914a6 xen/arch/x86/hvm/vmx/entry.S
> --- a/xen/arch/x86/hvm/vmx/entry.S      Thu Jun 07 19:46:57 2012 +0100
> +++ b/xen/arch/x86/hvm/vmx/entry.S      Wed Aug 29 14:39:57 2012 -0700
> @@ -124,12 +124,23 @@
>          get_current(bx)
>          cli
> 
> +#ifdef XEN_KDB_CONFIG
> +#if defined(__x86_64__)
> +        testl $1, kdb_session_begun(%rip)
> +#else
> +        testl $1, kdb_session_begun
> +#endif
> +        jnz  .Lkdb_skip_softirq
> +#endif
>          mov  VCPU_processor(r(bx)),%eax
>          shl  $IRQSTAT_shift,r(ax)
>          lea  addr_of(irq_stat),r(dx)
>          cmpl $0,(r(dx),r(ax),1)
>          jnz  .Lvmx_process_softirqs
> 
> +#ifdef XEN_KDB_CONFIG
> +.Lkdb_skip_softirq:
> +#endif

Same comments here as to the svm version.

>          testb $0xff,VCPU_vmx_emulate(r(bx))
>          jnz .Lvmx_goto_emulator
>          testb $0xff,VCPU_vmx_realmode(r(bx))
> diff -r 32034d1914a6 xen/arch/x86/hvm/vmx/vmcs.c
> --- a/xen/arch/x86/hvm/vmx/vmcs.c       Thu Jun 07 19:46:57 2012 +0100
> +++ b/xen/arch/x86/hvm/vmx/vmcs.c       Wed Aug 29 14:39:57 2012 -0700
> @@ -1117,6 +1117,13 @@
>          hvm_asid_flush_vcpu(v);
>      }
> 
> +#if defined(XEN_KDB_CONFIG)
> +    if (kdb_dr7)
> +        __vmwrite(GUEST_DR7, kdb_dr7);
> +    else
> +        __vmwrite(GUEST_DR7, 0);

Isn't this the same as
        __vmwrite(GUEST_DR7, kdb_dr7) ?

Why does kdb maintain it's own global here instead of manipulating the
guest dr7 values in the state?

> +#endif
> +
>      debug_state = v->domain->debugger_attached
>                    || 
> v->domain->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_INT3]
>                    || 
> v->domain->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_SINGLE_STEP];
> @@ -1326,6 +1333,220 @@
>      register_keyhandler('v', &vmcs_dump_keyhandler);
>  }
> 
> +#if defined(XEN_KDB_CONFIG)
> +#define GUEST_EFER      0x2806   /* see page 23-20 */
> +#define GUEST_EFER_HIGH 0x2807   /* see page 23-20 */

23-20 of what?

These belong with the other GUEST_* VMCS offset definitions in vmcs.h

> +
> +/* it's a shame we can't use vmcs_dump_vcpu(), but it does vmx_vmcs_enter 
> which
> + * will IPI other CPUs. also, print a subset relevant to software debugging 
> */
> +static void noinline kdb_print_vmcs(struct vcpu *vp)
> +{
> +    struct cpu_user_regs *regs = &vp->arch.user_regs;
> +    unsigned long long x;
> +
> +    kdbp("*** Guest State ***\n");
> +    kdbp("CR0: actual=0x%016llx, shadow=0x%016llx, gh_mask=%016llx\n",
> +         (unsigned long long)vmr(GUEST_CR0),

vmr returns an unsigned long, why the mismatched format string+ cast?

This seems to duplicate a fairly large chunk of vmcs_dump_vcpu. If kdbp
and printk can't co-exist then perhaps make a common function which
takes a printer as a function pointer?

Same goes for the SVM version I think I saw before.

> +/* Flush VMCS on this cpu if it needs to:
> + *   - Upon leaving kdb, the HVM cpu will resume in vmx_vmexit_handler() and
> + *     do __vmreads. So, the VMCS pointer can't be left cleared.
> + *   - Doing __vmpclear will set the vmx state to 'clear', so to resume a
> + *     vmlaunch must be done and not vmresume. This means, we must clear
> + *     arch_vmx->launched.
> + */
> +void kdb_curr_cpu_flush_vmcs(void)

Is this function actually kdb specific? Other than the printing it looks
like a pretty generic help to me.

> +{
> +    struct domain *dp;
> +    struct vcpu *vp;
> +    int ccpu = smp_processor_id();
> +    struct vmcs_struct *cvp = this_cpu(current_vmcs);
> +
> +    if (this_cpu(current_vmcs) == NULL)
> +        return;             /* no HVM active on this CPU */
> +
> +    kdbp("KDB:[%d] curvmcs:%lx/%lx\n", ccpu, cvp, virt_to_maddr(cvp));
> +
> +    /* looks like we got one. unfortunately, current_vmcs points to vmcs
> +     * and not VCPU, so we gotta search the entire list... */
> +    for_each_domain (dp) {
> +        if ( !(is_hvm_or_hyb_domain(dp)) || dp->is_dying)
> +            continue;
> +        for_each_vcpu (dp, vp) {
> +            if ( vp->arch.hvm_vmx.vmcs == cvp ) {
> +                __vmpclear(virt_to_maddr(vp->arch.hvm_vmx.vmcs));
> +                __vmptrld(virt_to_maddr(vp->arch.hvm_vmx.vmcs));
> +                vp->arch.hvm_vmx.launched = 0;
> +                this_cpu(current_vmcs) = NULL;
> +                kdbp("KDB:[%d] %d:%d current_vmcs:%lx flushed\n",
> +                    ccpu, dp->domain_id, vp->vcpu_id, cvp, 
> virt_to_maddr(cvp));
> +            }
> +        }
> +    }
> +}
> +
> +/*
> + * domid == 0 : display for all HVM domains  (dom0 is never an HVM domain)

Not any more...

> + * vcpu id == -1 : display all vcpuids
> + * PreCondition: all HVM cpus (including current cpu) have flushed VMCS
> + */
> +void kdb_dump_vmcs(domid_t did, int vid)
> +{
> +    struct domain *dp;
> +    struct vcpu *vp;
> +    struct vmcs_struct  *vmcsp;
> +    u64 addr = -1;
> +
> +    ASSERT(!local_irq_is_enabled());     /* kdb should always run disabled */
> +    __vmptrst(&addr);
> +
> +    for_each_domain (dp) {
> +        if ( !(is_hvm_or_hyb_domain(dp)) || dp->is_dying)
> +            continue;
> +        if (did != 0 && did != dp->domain_id)
> +            continue;
> +
> +        for_each_vcpu (dp, vp) {
> +            if (vid != -1 && vid != vp->vcpu_id)
> +                continue;

A lot of this scaffolding is the same as for svm. Perhaps pull it up
into a common hvm function with only the inner arch specific bit  in a
per-VMX/SVM function?

> +
> +           vmcsp = vp->arch.hvm_vmx.vmcs;
> +            kdbp("VMCS %lx/%lx [domid:%d (%p)  vcpu:%d (%p)]:\n", vmcsp,
> +                virt_to_maddr(vmcsp), dp->domain_id, dp, vp->vcpu_id, vp);
> +            __vmptrld(virt_to_maddr(vmcsp));
> +            kdb_print_vmcs(vp);
> +            __vmpclear(virt_to_maddr(vmcsp));
> +            vp->arch.hvm_vmx.launched = 0;
> +        }
> +        kdbp("\n");
> +    }
> +    /* restore orig vmcs pointer for __vmreads in vmx_vmexit_handler() */
> +    if (addr && addr != (u64)-1)
> +        __vmptrld(addr);
> +}
> +#endif
> 
>  /*
>   * Local variables:
> diff -r 32034d1914a6 xen/arch/x86/hvm/vmx/vmx.c
> --- a/xen/arch/x86/hvm/vmx/vmx.c        Thu Jun 07 19:46:57 2012 +0100
> +++ b/xen/arch/x86/hvm/vmx/vmx.c        Wed Aug 29 14:39:57 2012 -0700
> @@ -2183,11 +2183,14 @@
>          printk("reason not known yet!");
>          break;
>      }
> -
> +#if defined(XEN_KDB_CONFIG)
> +    kdbp("\n************* VMCS Area **************\n");
> +    kdb_dump_vmcs(curr->domain->domain_id, (curr)->vcpu_id);
> +#else
>      printk("************* VMCS Area **************\n");
>      vmcs_dump_vcpu(curr);

Is kdb_dump_vmcs better than/different to vmcs_dump_vcpu in this
context?
>      printk("**************************************\n");
> -
> +#endif
>      domain_crash(curr->domain);
>  }
> 
> @@ -2415,6 +2418,12 @@
>              write_debugreg(6, exit_qualification | 0xffff0ff0);
>              if ( !v->domain->debugger_attached || cpu_has_monitor_trap_flag )
>                  goto exit_and_crash;
> +
> +#if defined(XEN_KDB_CONFIG)
> +            /* TRAP_debug: IP points correctly to next instr */
> +            if (kdb_handle_trap_entry(vector, regs))
> +                break;
> +#endif
>              domain_pause_for_debugger();
>              break;
>          case TRAP_int3:
> @@ -2423,6 +2432,13 @@
>              if ( v->domain->debugger_attached )
>              {
>                  update_guest_eip(); /* Safe: INT3 */
> +#if defined(XEN_KDB_CONFIG)
> +                /* vmcs.IP points to bp, kdb expects bp+1. Hence after the 
> above
> +                 * update_guest_eip which updates to bp+1. works for gdbsx 
> too
> +                 */
> +                if (kdb_handle_trap_entry(vector, regs))
> +                    break;
> +#endif
>                  current->arch.gdbsx_vcpu_event = TRAP_int3;
>                  domain_pause_for_debugger();
>                  break;
> @@ -2707,6 +2723,10 @@
>      case EXIT_REASON_MONITOR_TRAP_FLAG:
>          v->arch.hvm_vmx.exec_control &= ~CPU_BASED_MONITOR_TRAP_FLAG;
>          vmx_update_cpu_exec_control(v);
> +#if defined(XEN_KDB_CONFIG)
> +        if (kdb_handle_trap_entry(TRAP_debug, regs))
> +            break;
> +#endif
>          if ( v->arch.hvm_vcpu.single_step ) {
>            hvm_memory_event_single_step(regs->eip);
>            if ( v->domain->debugger_attached )
> diff -r 32034d1914a6 xen/arch/x86/irq.c
> --- a/xen/arch/x86/irq.c        Thu Jun 07 19:46:57 2012 +0100
> +++ b/xen/arch/x86/irq.c        Wed Aug 29 14:39:57 2012 -0700
> @@ -2305,3 +2305,29 @@
>      return is_hvm_domain(d) && pirq &&
>             pirq->arch.hvm.emuirq != IRQ_UNBOUND;
>  }
> +
> +#ifdef XEN_KDB_CONFIG
> +void kdb_prnt_guest_mapped_irqs(void)
> +{
> +    int irq, j;
> +    char affstr[NR_CPUS/4+NR_CPUS/32+2];    /* courtesy dump_irqs() */

Can this share some code with dump_irqs then?

I don't see this construct there though -- but that magic expression
could use some explanation.

> +
> +    kdbp("irq  vec  aff  type  domid:mapped-pirq pairs  (all in decimal)\n");
> +    for (irq=0; irq < nr_irqs; irq++) {
> +        irq_desc_t  *dp = irq_to_desc(irq);
> +        struct arch_irq_desc *archp = &dp->arch;
> +        irq_guest_action_t *actp = (irq_guest_action_t *)dp->action;
> +
> +        if (!dp->handler ||dp->handler==&no_irq_type || 
> !(dp->status&IRQ_GUEST))
> +            continue;
> +
> +        cpumask_scnprintf(affstr, sizeof(affstr), dp->affinity);
> +        kdbp("[%3ld] %3d %3s %-13s ", irq, archp->vector, affstr,
> +             dp->handler->typename);
> +        for (j=0; j < actp->nr_guests; j++)
> +            kdbp("%03d:%04d ", actp->guest[j]->domain_id,
> +                 domain_irq_to_pirq(actp->guest[j], irq));
> +        kdbp("\n");
> +    }
> +}
> +#endif

> diff -r 32034d1914a6 xen/arch/x86/smp.c
> --- a/xen/arch/x86/smp.c        Thu Jun 07 19:46:57 2012 +0100
> +++ b/xen/arch/x86/smp.c        Wed Aug 29 14:39:57 2012 -0700
> @@ -273,7 +273,7 @@
>   * Structure and data for smp_call_function()/on_selected_cpus().
>   */
> 
> -static void __smp_call_function_interrupt(void);
> +static void __smp_call_function_interrupt(struct cpu_user_regs *regs);

I think you can just use get_irq_regs in the functions which need it
instead of adding this to every irq path?

>  static DEFINE_SPINLOCK(call_lock);
>  static struct call_data_struct {
>      void (*func) (void *info);


> diff -r 32034d1914a6 xen/common/sched_credit.c
> --- a/xen/common/sched_credit.c Thu Jun 07 19:46:57 2012 +0100
> +++ b/xen/common/sched_credit.c Wed Aug 29 14:39:57 2012 -0700
> @@ -1475,6 +1475,33 @@
>      printk("\n");
>  }
> 
> +#ifdef XEN_KDB_CONFIG
> +static void kdb_csched_dump(int cpu)
> +{
> +    struct csched_pcpu *pcpup = CSCHED_PCPU(cpu);
> +    struct vcpu *scurrvp = (CSCHED_VCPU(current))->vcpu;
> +    struct list_head *tmp, *runq = RUNQ(cpu);
> +
> +    kdbp("    csched_pcpu: %p\n", pcpup);
> +    kdbp("    curr csched:%p {vcpu:%p id:%d domid:%d}\n", 
> (current)->sched_priv,
> +         scurrvp, scurrvp->vcpu_id, scurrvp->domain->domain_id);
> +    kdbp("    runq:\n");
> +
> +    /* next is top of struct, so screw stupid, ugly hard to follow macros */
> +    if (offsetof(struct csched_vcpu, runq_elem.next) != 0) {
> +        kdbp("next is not first in struct csched_vcpu. please fixme\n");

er, that's why the stupid macros are there!

at least this could be a build time check!

There seems to be a lot of code in  this patch which adds a kdb_FOO_dump
function which duplicates the content (if not the exact layout) of an
exsiting FOO_dump function but using kdbp instead of printk.

I think that a recipe for one or the other getting out of sync or
bit-rotting and should be replaced either with making printk usable
while in kdb or if that isn't possible by abstracting out the printing
function.

> +        return;        /* otherwise for loop will crash */
> +    }
> +    for (tmp = runq->next; tmp != runq; tmp = tmp->next) {
> +
> +        struct csched_vcpu *csp = (struct csched_vcpu *)tmp;
> +        struct vcpu *vp = csp->vcpu;
> +        kdbp("      csp:%p pri:%02d vcpu: {p:%p id:%d domid:%d}\n", csp,
> +             csp->pri, vp, vp->vcpu_id, vp->domain->domain_id);
> +    };
> +}
> +#endif
> +
>  static void
>  csched_dump_pcpu(const struct scheduler *ops, int cpu)
>  {
> @@ -1484,6 +1511,10 @@
>      int loop;
>  #define cpustr keyhandler_scratch
> 
> +#ifdef XEN_KDB_CONFIG
> +    kdb_csched_dump(cpu);
> +    return;

Return ? 

> +#endif
>      spc = CSCHED_PCPU(cpu);
>      runq = &spc->runq;
> 
> diff -r 32034d1914a6 xen/include/xen/sched.h
> --- a/xen/include/xen/sched.h   Thu Jun 07 19:46:57 2012 +0100
> +++ b/xen/include/xen/sched.h   Wed Aug 29 14:39:57 2012 -0700
> @@ -576,11 +576,14 @@
>  unsigned long hypercall_create_continuation(
>      unsigned int op, const char *format, ...);
>  void hypercall_cancel_continuation(void);
> -
> +#ifdef XEN_KDB_CONFIG
> +#define hypercall_preempt_check() (0)

Erm, really?

Ian.


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.