kdi_asm.s revision ae115bc77f6fcde83175c75b4206dc2e50747966
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Debugger entry for both master and slave CPUs
*/
#if defined(__lint)
#include <sys/types.h>
#endif
#include <sys/segments.h>
#include <sys/asm_linkage.h>
#include <sys/controlregs.h>
#include <sys/x86_archext.h>
#include <sys/privregs.h>
#include <sys/machprivregs.h>
#include <sys/kdi_regs.h>
#include <sys/uadmin.h>
#include <sys/psw.h>
#ifdef _ASM
#include <kdi_assym.h>
#include <assym.h>
/* clobbers %edx, %ecx, returns addr in %eax, cpu id in %ebx */
#define GET_CPUSAVE_ADDR \
movl %gs:CPU_ID, %ebx; \
movl %ebx, %eax; \
movl $KRS_SIZE, %ecx; \
mull %ecx; \
movl $kdi_cpusave, %edx; \
/*CSTYLED*/ \
addl (%edx), %eax
/*
* Save copies of the IDT and GDT descriptors. Note that we only save the IDT
* and GDT if the IDT isn't ours, as we may be legitimately re-entering the
* debugger through the trap handler. We don't want to clobber the saved IDT
* in the process, as we'd end up resuming the world on our IDT.
*/
#define SAVE_IDTGDT \
movl %gs:CPU_IDT, %edx; \
cmpl $kdi_idt, %edx; \
je 1f; \
movl %edx, KRS_IDT(%eax); \
movl %gs:CPU_GDT, %edx; \
movl %edx, KRS_GDT(%eax); \
1:
/*
* Each cpusave buffer has an area set aside for a ring buffer of breadcrumbs.
* The following macros manage the buffer.
*/
/* Advance the ring buffer */
#define ADVANCE_CRUMB_POINTER(cpusave, tmp1, tmp2) \
movl KRS_CURCRUMBIDX(cpusave), tmp1; \
cmpl $[KDI_NCRUMBS - 1], tmp1; \
jge 1f; \
/* Advance the pointer and index */ \
addl $1, tmp1; \
movl tmp1, KRS_CURCRUMBIDX(cpusave); \
movl KRS_CURCRUMB(cpusave), tmp1; \
addl $KRM_SIZE, tmp1; \
jmp 2f; \
1: /* Reset the pointer and index */ \
movw $0, KRS_CURCRUMBIDX(cpusave); \
leal KRS_CRUMBS(cpusave), tmp1; \
2: movl tmp1, KRS_CURCRUMB(cpusave); \
/* Clear the new crumb */ \
movl $KDI_NCRUMBS, tmp2; \
3: movl $0, -4(tmp1, tmp2, 4); \
decl tmp2; \
jnz 3b
/* Set a value in the current breadcrumb buffer */
#define ADD_CRUMB(cpusave, offset, value, tmp) \
movl KRS_CURCRUMB(cpusave), tmp; \
movl value, offset(tmp)
#endif /* _ASM */
/*
* The main entry point for master CPUs. It also serves as the trap handler
* for all traps and interrupts taken during single-step.
*/
#if defined(__lint)
void
kdi_cmnint(void)
{
}
#else /* __lint */
/* XXX implement me */
ENTRY_NP(kdi_nmiint)
clr %ecx
movl (%ecx), %ecx
SET_SIZE(kdi_nmiint)
ENTRY_NP(kdi_cmnint)
ALTENTRY(kdi_master_entry)
/* Save all registers and selectors */
pushal
pushl %ds
pushl %es
pushl %fs
pushl %gs
pushl %ss
subl $8, %esp
movl %ebp, REG_OFF(KDIREG_SAVFP)(%esp)
movl REG_OFF(KDIREG_EIP)(%esp), %eax
movl %eax, REG_OFF(KDIREG_SAVPC)(%esp)
/*
* If the kernel has started using its own selectors, we should too.
* Update our saved selectors if they haven't been updated already.
*/
movw %cs, %ax
cmpw $KCS_SEL, %ax
jne 1f /* The kernel hasn't switched yet */
movw $KDS_SEL, %ax
movw %ax, %ds
movw kdi_cs, %ax
cmpw $KCS_SEL, %ax
je 1f /* We already switched */
/*
* The kernel switched, but we haven't. Update our saved selectors
* to match the kernel's copies for use below.
*/
movl $KCS_SEL, kdi_cs
movl $KDS_SEL, kdi_ds
movl $KFS_SEL, kdi_fs
movl $KGS_SEL, kdi_gs
1:
/*
* Set the selectors to a known state. If we come in from kmdb's IDT,
* we'll be on boot's %cs. This will cause GET_CPUSAVE_ADDR to return
* CPU 0's cpusave, regardless of which CPU we're on, and chaos will
* ensue. So, if we've got $KCSSEL in kdi_cs, switch to it. The other
* selectors are restored normally.
*/
movw %cs:kdi_cs, %ax
cmpw $KCS_SEL, %ax
jne 1f
ljmp $KCS_SEL, $1f
1:
movw %cs:kdi_ds, %ds
movw kdi_ds, %es
movw kdi_fs, %fs
movw kdi_gs, %gs
movw kdi_ds, %ss
/*
* This has to come after we set %gs to the kernel descriptor. Since
* we've hijacked some IDT entries used in user-space such as the
* breakpoint handler, we can enter kdi_cmnint() with GDT_LWPGS used
* in %gs. On the hypervisor, CLI() needs GDT_GS to access the machcpu.
*/
CLI(%eax)
GET_CPUSAVE_ADDR /* %eax = cpusave, %ebx = CPU ID */
ADVANCE_CRUMB_POINTER(%eax, %ecx, %edx)
ADD_CRUMB(%eax, KRM_CPU_STATE, $KDI_CPU_STATE_MASTER, %edx)
movl REG_OFF(KDIREG_EIP)(%esp), %ecx
ADD_CRUMB(%eax, KRM_PC, %ecx, %edx)
ADD_CRUMB(%eax, KRM_SP, %esp, %edx)
movl REG_OFF(KDIREG_TRAPNO)(%esp), %ecx
ADD_CRUMB(%eax, KRM_TRAPNO, %ecx, %edx)
movl %esp, %ebp
pushl %eax
/*
* Were we in the debugger when we took the trap (i.e. was %esp in one
* of the debugger's memory ranges)?
*/
leal kdi_memranges, %ecx
movl kdi_nmemranges, %edx
1: cmpl MR_BASE(%ecx), %esp
jl 2f /* below this range -- try the next one */
cmpl MR_LIM(%ecx), %esp
jg 2f /* above this range -- try the next one */
jmp 3f /* matched within this range */
2: decl %edx
jz kdi_save_common_state /* %esp not within debugger memory */
addl $MR_SIZE, %ecx
jmp 1b
3: /*
* %esp was within one of the debugger's memory ranges. This should
* only happen when we take a trap while running in the debugger.
* kmdb_dpi_handle_fault will determine whether or not it was an
* expected trap, and will take the appropriate action.
*/
pushl %ebx /* cpuid */
movl REG_OFF(KDIREG_ESP)(%ebp), %ecx
addl $REG_OFF(KDIREG_EFLAGS - KDIREG_EAX), %ecx
pushl %ecx
pushl REG_OFF(KDIREG_EIP)(%ebp)
pushl REG_OFF(KDIREG_TRAPNO)(%ebp)
call kdi_dvec_handle_fault
addl $16, %esp
/*
* If we're here, we ran into a debugger problem, and the user
* elected to solve it by having the debugger debug itself. The
* state we're about to save is that of the debugger when it took
* the fault.
*/
jmp kdi_save_common_state
SET_SIZE(kdi_master_entry)
SET_SIZE(kdi_cmnint)
#endif /* __lint */
/*
* The cross-call handler for slave CPUs.
*
* The debugger is single-threaded, so only one CPU, called the master, may be
* running it at any given time. The other CPUs, known as slaves, spin in a
* busy loop until there's something for them to do. This is the entry point
* for the slaves - they'll be sent here in response to a cross-call sent by the
* master.
*/
#if defined(__lint)
char kdi_slave_entry_patch;
void
kdi_slave_entry(void)
{
}
#else /* __lint */
.globl kdi_slave_entry_patch;
ENTRY_NP(kdi_slave_entry)
/* kdi_msr_add_clrentry knows where this is */
kdi_slave_entry_patch:
KDI_MSR_PATCH;
/*
* Cross calls are implemented as function calls, so our stack
* currently looks like one you'd get from a zero-argument function
* call. There's an %eip at %esp, and that's about it. We want to
* make it look like the master CPU's stack. By doing this, we can
* use the same resume code for both master and slave. We need to
* make our stack look like a `struct regs' before we jump into the
* common save routine.
*/
pushl %cs
pushfl
pushl $-1 /* A phony trap error code */
pushl $-1 /* A phony trap number */
pushal
pushl %ds
pushl %es
pushl %fs
pushl %gs
pushl %ss
subl $8, %esp
movl %ebp, REG_OFF(KDIREG_SAVFP)(%esp)
movl REG_OFF(KDIREG_EIP)(%esp), %eax
movl %eax, REG_OFF(KDIREG_SAVPC)(%esp)
/*
* Swap our saved EFLAGS and %eip. Each is where the other
* should be.
*/
movl REG_OFF(KDIREG_EFLAGS)(%esp), %eax
xchgl REG_OFF(KDIREG_EIP)(%esp), %eax
movl %eax, REG_OFF(KDIREG_EFLAGS)(%esp)
/*
* Our stack now matches struct regs, and is irettable. We don't need
* to do anything special for the hypervisor w.r.t. PS_IE since we
* iret twice anyway; the second iret back to the hypervisor
* will re-enable interrupts.
*/
CLI(%eax)
/* Load sanitized segment selectors */
movw kdi_ds, %ds
movw kdi_ds, %es
movw kdi_fs, %fs
movw kdi_gs, %gs
movw kdi_ds, %ss
GET_CPUSAVE_ADDR /* %eax = cpusave, %ebx = CPU ID */
ADVANCE_CRUMB_POINTER(%eax, %ecx, %edx)
ADD_CRUMB(%eax, KRM_CPU_STATE, $KDI_CPU_STATE_SLAVE, %edx)
movl REG_OFF(KDIREG_EIP)(%esp), %ecx
ADD_CRUMB(%eax, KRM_PC, %ecx, %edx)
pushl %eax
jmp kdi_save_common_state
SET_SIZE(kdi_slave_entry)
#endif /* __lint */
#if !defined(__lint)
ENTRY_NP(kdi_save_common_state)
/*
* The state of the world:
*
* The stack has a complete set of saved registers and segment
* selectors, arranged in `struct regs' order (or vice-versa), up to
* and including EFLAGS. It also has a pointer to our cpusave area.
*
* We need to save a pointer to these saved registers. We also want
* to adjust the saved %esp - it should point just beyond the saved
* registers to the last frame of the thread we interrupted. Finally,
* we want to clear out bits 16-31 of the saved selectors, as the
* selector pushls don't automatically clear them.
*/
popl %eax /* the cpusave area */
movl %esp, KRS_GREGS(%eax) /* save ptr to current saved regs */
SAVE_IDTGDT
addl $REG_OFF(KDIREG_EFLAGS - KDIREG_EAX), KDIREG_OFF(KDIREG_ESP)(%esp)
andl $0xffff, KDIREG_OFF(KDIREG_SS)(%esp)
andl $0xffff, KDIREG_OFF(KDIREG_GS)(%esp)
andl $0xffff, KDIREG_OFF(KDIREG_FS)(%esp)
andl $0xffff, KDIREG_OFF(KDIREG_ES)(%esp)
andl $0xffff, KDIREG_OFF(KDIREG_DS)(%esp)
/* Save off %cr0, and clear write protect */
movl %cr0, %ecx
movl %ecx, KRS_CR0(%eax)
andl $_BITNOT(CR0_WP), %ecx
movl %ecx, %cr0
pushl %edi
movl %eax, %edi
/* Save the debug registers and disable any active watchpoints */
pushl $7
call kdi_dreg_get
addl $4, %esp
movl %eax, KRS_DRCTL(%edi)
andl $_BITNOT(KDIREG_DRCTL_WPALLEN_MASK), %eax
pushl %eax
pushl $7
call kdi_dreg_set
addl $8, %esp
pushl $6
call kdi_dreg_get
addl $4, %esp
movl %eax, KRS_DRSTAT(%edi)
pushl $0
call kdi_dreg_get
addl $4, %esp
movl %eax, KRS_DROFF(0)(%edi)
pushl $1
call kdi_dreg_get
addl $4, %esp
movl %eax, KRS_DROFF(1)(%edi)
pushl $2
call kdi_dreg_get
addl $4, %esp
movl %eax, KRS_DROFF(2)(%edi)
pushl $3
call kdi_dreg_get
addl $4, %esp
movl %eax, KRS_DROFF(3)(%edi)
movl %edi, %eax
popl %edi
/*
* Save any requested MSRs.
*/
movl KRS_MSR(%eax), %ecx
cmpl $0, %ecx
je no_msr
pushl %eax /* rdmsr clobbers %eax */
movl %ecx, %ebx
1:
movl MSR_NUM(%ebx), %ecx
cmpl $0, %ecx
je msr_done
movl MSR_TYPE(%ebx), %edx
cmpl $KDI_MSR_READ, %edx
jne msr_next
rdmsr /* addr in %ecx, value into %edx:%eax */
movl %eax, MSR_VAL(%ebx)
movl %edx, _CONST(MSR_VAL + 4)(%ebx)
msr_next:
addl $MSR_SIZE, %ebx
jmp 1b
msr_done:
popl %eax
no_msr:
clr %ebp /* stack traces should end here */
pushl %eax
call kdi_debugger_entry
pushl %eax /* leave cpusave on the stack */
jmp kdi_resume
SET_SIZE(kdi_save_common_state)
#endif /* !__lint */
/*
* Given the address of the current CPU's cpusave area in %edi, the following
* macro restores the debugging state to said CPU. Restored state includes
* the debug registers from the global %dr variables, and debugging MSRs from
* the CPU save area. This code would be in a separate routine, but for the
* fact that some of the MSRs are jump-sensitive. As such, we need to minimize
* the number of jumps taken subsequent to the update of said MSRs. We can
* remove one jump (the ret) by using a macro instead of a function for the
* debugging state restoration code.
*
* Takes the cpusave area in %edi as a parameter, clobbers %eax-%edx
*/
#define KDI_RESTORE_DEBUGGING_STATE \
leal kdi_drreg, %ebx; \
\
pushl DR_CTL(%ebx); \
pushl $7; \
call kdi_dreg_set; \
addl $8, %esp; \
\
pushl $KDIREG_DRSTAT_RESERVED; \
pushl $6; \
call kdi_dreg_set; \
addl $8, %esp; \
\
pushl DRADDR_OFF(0)(%ebx); \
pushl $0; \
call kdi_dreg_set; \
addl $8, %esp; \
\
pushl DRADDR_OFF(1)(%ebx); \
pushl $1; \
call kdi_dreg_set; \
addl $8, %esp; \
\
pushl DRADDR_OFF(2)(%ebx); \
pushl $2; \
call kdi_dreg_set; \
addl $8, %esp; \
\
pushl DRADDR_OFF(3)(%ebx); \
pushl $3; \
call kdi_dreg_set; \
addl $8, %esp; \
\
/* \
* Write any requested MSRs. \
*/ \
movl KRS_MSR(%edi), %ebx; \
cmpl $0, %ebx; \
je 3f; \
1: \
movl MSR_NUM(%ebx), %ecx; \
cmpl $0, %ecx; \
je 3f; \
\
movl MSR_TYPE(%ebx), %edx; \
cmpl $KDI_MSR_WRITE, %edx; \
jne 2f; \
\
movl MSR_VALP(%ebx), %edx; \
movl 0(%edx), %eax; \
movl 4(%edx), %edx; \
wrmsr; \
2: \
addl $MSR_SIZE, %ebx; \
jmp 1b; \
3: \
/* \
* We must not branch after re-enabling LBR. If \
* kdi_wsr_wrexit_msr is set, it contains the number \
* of the MSR that controls LBR. kdi_wsr_wrexit_valp \
* contains the value that is to be written to enable \
* LBR. \
*/ \
movl kdi_msr_wrexit_msr, %ecx; \
cmpl $0, %ecx; \
je 1f; \
\
movl kdi_msr_wrexit_valp, %edx; \
movl 0(%edx), %eax; \
movl 4(%edx), %edx; \
\
wrmsr; \
1:
#if defined(__lint)
/*ARGSUSED*/
void
kdi_cpu_debug_init(kdi_cpusave_t *save)
{
}
#else /* __lint */
ENTRY_NP(kdi_cpu_debug_init)
pushl %ebp
movl %esp, %ebp
pushl %edi
pushl %ebx
movl 8(%ebp), %edi
KDI_RESTORE_DEBUGGING_STATE
popl %ebx
popl %edi
leave
ret
SET_SIZE(kdi_cpu_debug_init)
#endif /* !__lint */
/*
* Resume the world. The code that calls kdi_resume has already
* decided whether or not to restore the IDT.
*/
#if defined(__lint)
void
kdi_resume(void)
{
}
#else /* __lint */
ENTRY_NP(kdi_resume)
popl %ebx /* command */
popl %eax /* cpusave */
cmpl $KDI_RESUME_PASS_TO_KERNEL, %ebx
je kdi_pass_to_kernel
/*
* Send this CPU back into the world
*/
movl KRS_CR0(%eax), %edx
movl %edx, %cr0
pushl %edi
movl %eax, %edi
KDI_RESTORE_DEBUGGING_STATE
popl %edi
addl $8, %esp /* Discard savfp and savpc */
popl %ss
popl %gs
popl %fs
popl %es
popl %ds
popal
addl $8, %esp /* Discard TRAPNO and ERROR */
IRET
SET_SIZE(kdi_resume)
#endif /* __lint */
#if !defined(__lint)
ENTRY_NP(kdi_pass_to_kernel)
/* cpusave is still in %eax */
movl KRS_CR0(%eax), %edx
movl %edx, %cr0
/*
* When we replaced the kernel's handlers in the IDT, we made note of
* the handlers being replaced, thus allowing us to pass traps directly
* to said handlers here. We won't have any registers available for use
* after we start popping, and we know we're single-threaded here, so
* we have to use a global to store the handler address.
*/
pushl REG_OFF(KDIREG_TRAPNO)(%esp)
call kdi_kernel_trap2hdlr
addl $4, %esp
movl %eax, kdi_kernel_handler
/*
* The trap handler will expect the stack to be in trap order, with
* %eip being the last entry. Our stack is currently in KDIREG_*
* order, so we'll need to pop (and restore) our way back down.
*/
addl $8, %esp /* Discard savfp and savpc */
popl %ss
popl %gs
popl %fs
popl %es
popl %ds
popal
addl $8, %esp /* Discard trapno and err */
ljmp $KCS_SEL, $1f
1: jmp *%cs:kdi_kernel_handler
/*NOTREACHED*/
SET_SIZE(kdi_pass_to_kernel)
/*
* Reboot the system. This routine is to be called only by the master
* CPU.
*/
ENTRY_NP(kdi_reboot)
pushl $AD_BOOT
pushl $A_SHUTDOWN
call *psm_shutdownf
addl $8, %esp
/*
* psm_shutdown didn't work or it wasn't set, try pc_reset.
*/
call pc_reset
/*NOTREACHED*/
SET_SIZE(kdi_reboot)
#endif /* !__lint */