#include <sys/types.h>
#include <sys/segments.h>
#include <sys/trap.h>
#include <sys/cpuvar.h>
#include <sys/reboot.h>
#include <sys/sunddi.h>
#include <sys/archsystm.h>
#include <sys/kdi_impl.h>
#include <sys/x_call.h>
#include <sys/psw.h>
#include <vm/hat_i86.h>
#define KDI_GATE_NVECS 3
#define KDI_IDT_NOSAVE 0
#define KDI_IDT_SAVE 1
#define KDI_IDT_DTYPE_KERNEL 0
#define KDI_IDT_DTYPE_BOOT 1
kdiregs_t kdi_regs;
kdi_cpusave_t *kdi_cpusave;
int kdi_ncpusave;
static kdi_main_t kdi_kmdb_main;
kdi_drreg_t kdi_drreg;
uintptr_t kdi_kernel_handler;
int kdi_trap_switch;
#define KDI_MEMRANGES_MAX 2
kdi_memrange_t kdi_memranges[KDI_MEMRANGES_MAX];
int kdi_nmemranges;
typedef void idt_hdlr_f(void);
extern idt_hdlr_f kdi_trap0, kdi_trap1, kdi_int2, kdi_trap3, kdi_trap4;
extern idt_hdlr_f kdi_trap5, kdi_trap6, kdi_trap7, kdi_trap9;
extern idt_hdlr_f kdi_traperr10, kdi_traperr11, kdi_traperr12;
extern idt_hdlr_f kdi_traperr13, kdi_traperr14, kdi_trap16, kdi_traperr17;
extern idt_hdlr_f kdi_trap18, kdi_trap19, kdi_trap20, kdi_ivct32;
extern idt_hdlr_f kdi_invaltrap;
extern size_t kdi_ivct_size;
typedef struct kdi_gate_spec {
uint_t kgs_vec;
uint_t kgs_dpl;
} kdi_gate_spec_t;
static const kdi_gate_spec_t kdi_gate_specs[KDI_GATE_NVECS] = {
{ T_SGLSTP, TRP_KPL },
{ T_BPTFLT, TRP_UPL },
{ T_DBGENTR, TRP_KPL }
};
static gate_desc_t kdi_kgates[KDI_GATE_NVECS];
extern gate_desc_t kdi_idt[NIDT];
struct idt_description {
uint_t id_low;
uint_t id_high;
idt_hdlr_f *id_basehdlr;
size_t *id_incrp;
} idt_description[] = {
{ T_ZERODIV, 0, kdi_trap0, NULL },
{ T_SGLSTP, 0, kdi_trap1, NULL },
{ T_NMIFLT, 0, kdi_int2, NULL },
{ T_BPTFLT, 0, kdi_trap3, NULL },
{ T_OVFLW, 0, kdi_trap4, NULL },
{ T_BOUNDFLT, 0, kdi_trap5, NULL },
{ T_ILLINST, 0, kdi_trap6, NULL },
{ T_NOEXTFLT, 0, kdi_trap7, NULL },
#if !defined(__xpv)
{ T_DBLFLT, 0, syserrtrap, NULL },
#endif
{ T_EXTOVRFLT, 0, kdi_trap9, NULL },
{ T_TSSFLT, 0, kdi_traperr10, NULL },
{ T_SEGFLT, 0, kdi_traperr11, NULL },
{ T_STKFLT, 0, kdi_traperr12, NULL },
{ T_GPFLT, 0, kdi_traperr13, NULL },
{ T_PGFLT, 0, kdi_traperr14, NULL },
{ 15, 0, kdi_invaltrap, NULL },
{ T_EXTERRFLT, 0, kdi_trap16, NULL },
{ T_ALIGNMENT, 0, kdi_traperr17, NULL },
{ T_MCE, 0, kdi_trap18, NULL },
{ T_SIMDFPE, 0, kdi_trap19, NULL },
{ T_DBGENTR, 0, kdi_trap20, NULL },
{ 21, 31, kdi_invaltrap, NULL },
{ 32, 255, kdi_ivct32, &kdi_ivct_size },
{ 0, 0, NULL },
};
void
kdi_idt_init(selector_t sel)
{
struct idt_description *id;
int i;
for (id = idt_description; id->id_basehdlr != NULL; id++) {
uint_t high = id->id_high != 0 ? id->id_high : id->id_low;
size_t incr = id->id_incrp != NULL ? *id->id_incrp : 0;
#if !defined(__xpv)
if (kpti_enable && sel == KCS_SEL && id->id_low == T_DBLFLT)
id->id_basehdlr = tr_syserrtrap;
#endif
for (i = id->id_low; i <= high; i++) {
caddr_t hdlr = (caddr_t)id->id_basehdlr +
incr * (i - id->id_low);
set_gatesegd(&kdi_idt[i], (void (*)())hdlr, sel,
SDT_SYSIGT, TRP_KPL, IST_DBG);
}
}
}
static void
kdi_idt_gates_install(selector_t sel, int saveold)
{
gate_desc_t gates[KDI_GATE_NVECS];
int i;
bzero(gates, sizeof (*gates));
for (i = 0; i < KDI_GATE_NVECS; i++) {
const kdi_gate_spec_t *gs = &kdi_gate_specs[i];
uintptr_t func = GATESEG_GETOFFSET(&kdi_idt[gs->kgs_vec]);
set_gatesegd(&gates[i], (void (*)())func, sel, SDT_SYSIGT,
gs->kgs_dpl, IST_DBG);
}
for (i = 0; i < KDI_GATE_NVECS; i++) {
uint_t vec = kdi_gate_specs[i].kgs_vec;
if (saveold)
kdi_kgates[i] = CPU->cpu_m.mcpu_idt[vec];
kdi_idt_write(&gates[i], vec);
}
}
static void
kdi_idt_gates_restore(void)
{
int i;
for (i = 0; i < KDI_GATE_NVECS; i++)
kdi_idt_write(&kdi_kgates[i], kdi_gate_specs[i].kgs_vec);
}
void
kdi_idt_sync(void)
{
kdi_idt_init(KCS_SEL);
kdi_idt_gates_install(KCS_SEL, KDI_IDT_SAVE);
}
void
kdi_update_drreg(kdi_drreg_t *drreg)
{
kdi_drreg = *drreg;
}
void
kdi_memrange_add(caddr_t base, size_t len)
{
kdi_memrange_t *mr = &kdi_memranges[kdi_nmemranges];
ASSERT(kdi_nmemranges != KDI_MEMRANGES_MAX);
mr->mr_base = base;
mr->mr_lim = base + len - 1;
kdi_nmemranges++;
}
void
kdi_idt_switch(kdi_cpusave_t *cpusave)
{
if (cpusave == NULL)
kdi_idtr_set(kdi_idt, sizeof (kdi_idt) - 1);
else
kdi_idtr_set(cpusave->krs_idt, (sizeof (*idt0) * NIDT) - 1);
}
void
kdi_cpu_init(void)
{
kdi_idt_gates_install(KCS_SEL, KDI_IDT_NOSAVE);
kdi_cpu_debug_init(&kdi_cpusave[CPU->cpu_id]);
}
static int
kdi_cpu_activate(xc_arg_t arg1 __unused, xc_arg_t arg2 __unused,
xc_arg_t arg3 __unused)
{
kdi_idt_gates_install(KCS_SEL, KDI_IDT_SAVE);
return (0);
}
void
kdi_activate(kdi_main_t main, kdi_cpusave_t *cpusave, uint_t ncpusave)
{
int i;
cpuset_t cpuset;
CPUSET_ALL(cpuset);
kdi_cpusave = cpusave;
kdi_ncpusave = ncpusave;
kdi_kmdb_main = main;
for (i = 0; i < kdi_ncpusave; i++) {
kdi_cpusave[i].krs_cpu_id = i;
kdi_cpusave[i].krs_curcrumb =
&kdi_cpusave[i].krs_crumbs[KDI_NCRUMBS - 1];
kdi_cpusave[i].krs_curcrumbidx = KDI_NCRUMBS - 1;
}
if (boothowto & RB_KMDB)
kdi_idt_init(KMDBCODE_SEL);
else
kdi_idt_init(KCS_SEL);
kdi_memranges[0].mr_base = kdi_segdebugbase;
kdi_memranges[0].mr_lim = kdi_segdebugbase + kdi_segdebugsize - 1;
kdi_nmemranges = 1;
kdi_drreg.dr_ctl = KDIREG_DRCTL_RESERVED;
kdi_drreg.dr_stat = KDIREG_DRSTAT_RESERVED;
if (boothowto & RB_KMDB) {
kdi_idt_gates_install(KMDBCODE_SEL, KDI_IDT_NOSAVE);
} else {
xc_call(0, 0, 0, CPUSET2BV(cpuset), kdi_cpu_activate);
}
}
static int
kdi_cpu_deactivate(xc_arg_t arg1 __unused, xc_arg_t arg2 __unused,
xc_arg_t arg3 __unused)
{
kdi_idt_gates_restore();
return (0);
}
void
kdi_deactivate(void)
{
cpuset_t cpuset;
CPUSET_ALL(cpuset);
xc_call(0, 0, 0, CPUSET2BV(cpuset), kdi_cpu_deactivate);
kdi_nmemranges = 0;
}
int
kdi_trap_pass(kdi_cpusave_t *cpusave)
{
greg_t tt = cpusave->krs_gregs[KDIREG_TRAPNO];
greg_t pc = cpusave->krs_gregs[KDIREG_PC];
greg_t cs = cpusave->krs_gregs[KDIREG_CS];
if (USERMODE(cs))
return (1);
if (tt != T_BPTFLT && tt != T_SGLSTP)
return (0);
if (tt == T_BPTFLT && kdi_dtrace_get_state() ==
KDI_DTSTATE_DTRACE_ACTIVE)
return (2);
#if !defined(__xpv)
if (tt == T_SGLSTP &&
(pc == (greg_t)sys_sysenter || pc == (greg_t)brand_sys_sysenter ||
pc == (greg_t)tr_sys_sysenter ||
pc == (greg_t)tr_brand_sys_sysenter)) {
#else
if (tt == T_SGLSTP &&
(pc == (greg_t)sys_sysenter || pc == (greg_t)brand_sys_sysenter)) {
#endif
return (1);
}
return (0);
}
void
kdi_debugger_entry(kdi_cpusave_t *cpusave)
{
if (cpusave->krs_gregs[KDIREG_TRAPNO] == T_BPTFLT)
cpusave->krs_gregs[KDIREG_PC]--;
kdi_kmdb_main(cpusave);
}