l4ka-hazelnut/kernel/kdb/x86.c

1498 lines
37 KiB
C

/*********************************************************************
*
* Copyright (C) 1999, 2000, 2001, Karlsruhe University
*
* File path: x86.c
* Description: Various x86 specifuc kdebug stuff.
*
* @LICENSE@
*
* $Id: x86.c,v 1.81 2001/12/09 04:21:02 ud3 Exp $
*
********************************************************************/
#include <universe.h>
#include <tracepoints.h>
#include "kdebug.h"
#ifdef CONFIG_TRACEBUFFER
tracebuffer_t *tracebuffer;
#endif
#if defined(CONFIG_DEBUGGER_NEW_KDB)
static dword_t pmc_overflow_interrupt_mask = 0;
extern "C" void apic_pmc_of_int();
#endif
#define any_to_virt(x) ((x) < KERNEL_OFFSET ? ((x)+KERNEL_OFFSET) : (x))
/* x86-only prototypes */
void kdebug_init_serial(void) L4_SECT_KDEBUG;
void kdebug_setperfctr(dword_t sel, dword_t value) L4_SECT_KDEBUG;
void kdebug_init_arch()
{
#if defined(CONFIG_DEBUGGER_IO_INCOM) || defined(CONFIG_DEBUGGER_IO_OUTCOM)
kdebug_init_serial();
#endif
}
void kdebug_hwreset()
{
__asm__ __volatile__ (
" movb $0xFE, %al \n"
" outb %al, $0x64 \n"
);
}
void kdebug_single_stepping(exception_frame_t *frame, dword_t onoff)
{
frame->eflags &= ~(1 << 8);
frame->eflags |= (onoff << 8);
}
#if defined(CONFIG_DEBUGGER_KDB) || defined(CONFIG_DEBUGGER_GDB)
enum kdebug_steping_t {
STEPPING_NONE,
STEPPING_INSTR,
STEPPING_BLOCK
} kdebug_stepping = STEPPING_NONE;
void dump_count()
{
#define MAX_DUMP_COUNT 10
static int count = 0;
count++;
if (count == MAX_DUMP_COUNT)
{
getc();
count = 0;
}
}
void kdebug_dump_pgtable(ptr_t pgtable)
{
int d_beg = 0, d_end = 0;
char c;
if ((dword_t) pgtable < TCB_AREA)
/* seems to be a physical address */
pgtable = phys_to_virt(pgtable);
c = kdebug_get_choice("Dump pagetable", "All/User/Kernel/Tcbs"
#if defined(CONFIG_ENABLE_SMALL_AS)
"/Small spaces"
#endif
, 'a');
switch (c)
{
case 'a': d_beg = 0; d_end = 1024; break;
case 'u':
d_beg = 0;
d_end = (USER_AREA_MAX >> PAGEDIR_BITS);
break;
case 'k':
d_beg = (KERNEL_VIRT >> PAGEDIR_BITS);
d_end = 1024;
break;
case 't':
d_beg = (TCB_AREA >> PAGEDIR_BITS);
d_end = ((TCB_AREA + TCB_AREA_SIZE) >> PAGEDIR_BITS);
break;
#if defined(CONFIG_ENABLE_SMALL_AS)
case 's':
d_beg = (SMALL_SPACE_START >> PAGEDIR_BITS);
d_end = (TCB_AREA >> PAGEDIR_BITS);
break;
#endif
}
for (int i = d_beg; i < d_end; i++)
{
if (pgtable[i]) {
if (0) {}
#if defined(CONFIG_ENABLE_SMALL_AS)
else if ( i == SPACE_ID_IDX )
{
space_t * space = (space_t *) virt_to_phys (pgtable);
if (!space->is_small ())
printf("%x -> small_space=%x [INVALID]\n",
0x400000 * i, pgtable[i]);
else
printf("%x -> small_space = %x [size=%dMB num=%d]\n",
0x400000 * i, pgtable[i],
(space->smallid ().bytesize ()) / (1024*1024),
space->smallid ().num ());
}
else if ( i >= (int) GDT_ENTRY1_IDX && i <= (int) GDT_ENTRY2_IDX )
{
printf("%x -> GDT Entry %d = %x\n",
0x400000 * i, i - GDT_ENTRY1_IDX, pgtable[i]);
}
#endif
else if ( (pgtable[i] & PAGE_SUPER) && (pgtable[i] & PAGE_SUPER) )
{
printf("%x -> %x (4MB) addr=%x [%c%c%c%c%c%c]\n",
0x400000 * i, pgtable[i],
pgtable[i] & PAGEDIR_MASK,
pgtable[i] & PAGE_WRITABLE ? 'w' : 'r',
pgtable[i] & PAGE_USER ? 'u' : 'k',
pgtable[i] & PAGE_ACCESSED ? 'a' : '.',
pgtable[i] & PAGE_DIRTY ? 'd' : '.',
pgtable[i] & PAGE_GLOBAL ? 'g' : '.',
pgtable[i] & PAGE_CACHE_DISABLE ? '.' : 'c');
//dump_count();
}
else {
printf("%x -> %x (PT) addr=%x\n", 0x400000 * i, pgtable[i],
pgtable[i] & PAGE_MASK);
dword_t * ptab = phys_to_virt(ptr_t(pgtable[i] & PAGE_MASK));
for (int j = 0; j < 1024; j++) {
if (ptab[j] & PAGE_VALID) {
printf("%x -> %x (4K) addr=%x [%c%c%c%c%c%c]\n",
(0x400000 * i + 0x1000 * j), ptab[j],
ptab[j] & PAGE_MASK,
ptab[j] & PAGE_WRITABLE ? 'w' : 'r',
ptab[j] & PAGE_USER ? 'u' : 'k',
ptab[j] & PAGE_ACCESSED ? 'a' : '.',
ptab[j] & PAGE_DIRTY ? 'd' : '.',
ptab[j] & PAGE_GLOBAL ? 'g' : '.',
ptab[j] & PAGE_CACHE_DISABLE ? '.' : 'c');
//dump_count();
}
}
}
}
}
}
void dump_eflags(const dword_t eflags)
{
printf("%c%c%c%c%c%c%c%c%c%c%c",
eflags & (1 << 0) ? 'C' : 'c',
eflags & (1 << 2) ? 'P' : 'p',
eflags & (1 << 4) ? 'A' : 'a',
eflags & (1 << 6) ? 'Z' : 'z',
eflags & (1 << 7) ? 'S' : 's',
eflags & (1 << 11) ? 'O' : 'o',
eflags & (1 << 10) ? 'D' : 'd',
eflags & (1 << 9) ? 'I' : 'i',
eflags & (1 << 8) ? 'T' : 't',
eflags & (1 << 16) ? 'R' : 'r',
((eflags >> 12) & 3) + '0'
);
}
void kdebug_dump_frame(exception_frame_t *frame)
{
#if !defined(CONFIG_SMP)
printf("exception %d (current: %p, cr3: %p)\n", frame->fault_code,
frame, get_current_pagetable());
#else
printf("exception %d (current: %p, cr3: %p, cpu: %d, apic: %d)\n",
frame->fault_code, frame, get_current_pagetable(), get_cpu_id(),
get_apic_cpu_id());
#endif
printf("fault addr: %x\tstack: %x\terror code %x\n",
frame->fault_address, frame->user_stack, frame->error_code);
printf("eax: %x\tebx: %x\n", frame->eax, frame->ebx);
printf("ecx: %x\tedx: %x\n", frame->ecx, frame->edx);
printf("esi: %x\tedi: %x\n", frame->esi, frame->edi);
printf("ebp: %x\tefl: %x [", frame->ebp, frame->eflags);
dump_eflags(frame->eflags);printf("]\n");
#if defined(CONFIG_ENABLE_SMALL_AS)
printf("cs: %x\tss: %x\n", (word_t) frame->cs, (word_t) frame->ss);
printf("ds: %x\tes: %x\n", (word_t) frame->ds, (word_t) frame->es);
#endif
}
void kdebug_dump_frame_short(exception_frame_t *frame)
{
printf("eip=%p ", frame->fault_address);
#if defined(CONFIG_DEBUG_DISAS)
extern int x86_disas(dword_t pc);
x86_disas(frame->fault_address);
#endif
printf("\n"
" "
"eax=%p ebx=%p ecx=%p edx=%p ebp=%p\n"
" "
"esi=%p edi=%p esp=%p efl=%p [",
frame->eax, frame->ebx, frame->ecx, frame->edx, frame->ebp,
frame->esi, frame->edi,frame->user_stack, frame->eflags);
dump_eflags(frame->eflags);printf("]\n");
}
#if !defined(CONFIG_DEBUGGER_NEW_KDB) /* not necessary in the new debugger */
void kdebug_pf_tracing(int state)
{
extern int __kdebug_pf_tracing;
__kdebug_pf_tracing = state;
}
#endif
#if defined(CONFIG_DEBUGGER_NEW_KDB)
static qword_t saved_time;
static int cache_enabled = 0;
void use_cache()
{
(cache_enabled ^= 1) ? printf("cache enabled\n") : printf("cache disabled\n");
}
dword_t get_user_time() {
return ((dword_t*)saved_time)[1];
}
dword_t* change_idt_entry(int interrupt_no, ptr_t new_handler) {
extern idt_desc_t idt[];
dword_t original_handler_address;
//idt = read_idt_register();
if ((interrupt_no < IDT_SIZE) && (interrupt_no >= 0) ) {
//storing the old value of the IDT entry
original_handler_address =
idt[interrupt_no].x.d.offset_high <<16 |
idt[interrupt_no].x.d.offset_low;
//here i register the new handler
idt[interrupt_no].x.d.offset_high = ((dword_t) (new_handler) >> 16);
idt[interrupt_no].x.d.offset_low = ((dword_t) (new_handler));
printf("the address of the new handler is: %x\n", new_handler);
printf("the address of the old handler is: %x\n", original_handler_address);
}
else {
printf("interrupt no. out of range!\n");
original_handler_address = 0; //error!
}
return (ptr_t) original_handler_address;
}
dword_t* change_sysenter_eip_msr(ptr_t new_handler){
ptr_t original_sysenter_eip_msr = (ptr_t) rdmsr(0x176);
wrmsr(0x176, (qword_t)new_handler);
return original_sysenter_eip_msr;
}
//#if defined(...) x686
static dword_t *original_pmc_of_int = 0;
void enable_pmc_of_int() {
/* set the mask for enable interrupt
it is enabled for both counters till it is disabled. */
pmc_overflow_interrupt_mask = (1<<20);
//the counters which should generate the interrupts must be reseted.
kdebug_perfmon();
//enter the new handler to the idt
original_pmc_of_int = change_idt_entry(APIC_PERFCTR_INT_VECTOR, (ptr_t)(*apic_pmc_of_int));
}
void disable_pmc_of_int() {
//clear the mask to disable interrupt
pmc_overflow_interrupt_mask = 0;
// clear bit 20 in the eventselect registers (disable the interrupt). the event dosn't change.
qword_t dummy;
dummy = rdmsr(390);
dummy = dummy & (1 << 20);
wrmsr(390, dummy);
//bring the old handler back.
change_idt_entry(APIC_PERFCTR_INT_VECTOR, original_pmc_of_int);
}
/**
* test if the pagefault was generated in usermode
* last change: 01.18.2001
**/
int pf_usermode(dword_t errcode) {
#define PF_USERMODE(x) (x & PAGE_USER)
#define PAGE_USER (1<<2)
return( PF_USERMODE(errcode) );
}
dword_t get_pf_address() {
register dword_t tmp;
asm ("movl %%cr2, %0\n"
:"=r" (tmp));
return tmp;
}
#endif /*new debugger*/
int kdebug_arch_entry(exception_frame_t * frame)
{
#if defined (CONFIG_DEBUGGER_NEW_KDB)
/* save the time */
saved_time = rdtsc();
# if defined(CONFIG_ARCH_X86_I686)
//only on Intel 686
/* disable the performancecounters while in debugger */
qword_t dummy;
dummy = rdmsr(0x186);
dummy = dummy & (1 << 22);
wrmsr(0x186, dummy);
# endif /* config_arch_x86_i686 */
if(cache_enabled)
{
/* disable cache while in debugger */
asm volatile(
"mov %%cr0, %%eax\n" /* copy CR0 into eax */
"orl $((1<<30)|(1<<29)), %%eax\n" /* set bits CD and NW */
"mov %%eax, %%cr0\n" /* write back in CR0 */
: /* no outputs */
: /* no inputs */
: "eax"
);
}
#endif /*new debugger*/
/*
* The return value of this function determines whether
* the interactive kernel debugger is invoked or not.
* Returning 0 invokes the KDB.
*/
if (frame->fault_code == 3)
{
/* breakpoint exception - KDB interface
*
* The KDB interface is based on the int3 instruction. The
* int3-handler checks the instructions that caused the
* exception to decode the requested operation.
*
* The following code sequences are known so far:
*
* --- enter_kdebug --- print text thru KDIO and enter KDB
*
* int3
* jmp 1f
* .ascii "some text"
* 1:
*
* If "some text" starts with *, the text is written to
* KDB's trace and the interactive KDB is not invoked.
*
*
* --- kd_display --- print text thru KDIO
* int3
* nop
* jmp 1f
* .ascii "some text"
* 1:
*
*
*
* --- other KDB ops ---
* int3
* cmp x, %al
*
* with x
* 0x00 print character in %al
* 0x01 print pascal string at %eax
* 0x02 print 0-terminated string at %eax
* 0x03 clear page/screen
* 0x04 cursor control ???
* 0x05 print %eax as 32-bit hex
* 0x06 print %eax as 20-bit hex
* 0x07 print %eax as 16-bit hex
* 0x08 print %eax as 12-bit hex
* 0x09 print %eax as 8-bit hex
* 0x0a print %eax as 4-bit hex
* 0x0b print %eax as decimal
* 0x0c read character into %al (non-blocking)
* 0x0d read character into %al (blocking)
* 0x0e read 32-bit hex into %eax
* 0x0f read 16-bit hex into %eax
* 0x10 read 8-bit hex into %eax
* 0x11 read 4-bit hex into %eax ???
* 0x1E set EvntSel0 MSR to %eax (proposed)
* 0x1F set EvntSel1 MSR to %eax (proposed)
*
*/
unsigned char * c = (unsigned char *) frame->fault_address;
#if defined(CONFIG_ENABLE_SMALL_AS)
/*
* Make sure that we translate user-level addresses into
* something that can be accessed through kernel segments.
*/
tcb_t *current = ptr_to_tcb((ptr_t) frame);
if ( (dword_t) current >= TCB_AREA &&
(dword_t) current < TCB_AREA + TCB_AREA_SIZE )
{
smallid_t small = current->space->smallid ();
if (small.is_valid () && (dword_t) c < small.bytesize ())
c += small.offset ();
}
#endif
#if !defined(CONFIG_DEBUGGER_GDB)
/* The fault_address points to the instruction following the int3.
For readability, we let the fault_address instead point to the
int3 instruction itself and restore it in kdebug_arch_exit */
frame->fault_address--;
#endif
switch(*c)
{
case 0x3c:
TRACEPOINT(KDB_OPERATION, printf("KDB operation (0x%02x)\n",c[1]));
switch(c[1])
{
case 0x00: /* outchar */
putc(frame->eax & 0xff);
return 1;
case 0x01: /* outstring */
{
/* print a string: <length>,<string> */
char* p = (char*) any_to_virt(frame->eax);
for (int i = *p++; i--; p++)
putc(*p);
return 1;
}
case 0x02: /* kd_display */
printf((char*)frame->eax);
return 1;
case 0x05: /* outhex32 */
printf("%x", frame->eax);
return 1;
case 0x07: /* outhex16 */
printf("%4x", frame->eax & 0xFFFF);
return 1;
case 0x08: /* outhex8 */
printf("%3x", frame->eax & 0xFFF);
return 1;
case 0x09: /* outhex8 */
printf("%2x", frame->eax & 0xFF);
return 1;
case 0x0b: /* outdec */
printf("%d", frame->eax);
return 1;
case 0x0d: /* inchar */
frame->eax = getc();
return 1;
case 0x0e: /* inhex32 */
frame->eax = kdebug_get_hex();
return 1;
case 0x0f: /* inhex16 */
frame->eax &= 0xFFFF0000;
frame->eax |= (kdebug_get_hex() & 0xFFFF);
return 1;
case 0x11: /* inhext */
frame->eax = kdebug_get_hex();
return 1;
case 0x1E: /* set EvntSel0 */
case 0x1F: /* set EvntSel1 */
#if defined(CONFIG_PERFMON)
kdebug_setperfctr(c[1] & 1, frame->eax);
#endif
return 1;
default:
printf("unknown KDIO call %2x\n", c[1]);
return 0;
}
break;
case 0x90: /* kd_display */
c++;
if (c[0] == 0xeb)
{
for (int i = 0; i < c[1]; i++)
putc(c[i+2]);
return 1;
} break;
case 0xeb: /* enter_kdebug */
{
if (c[1] > 0 && c[2] == '*')
/* trace event - not implemented */
return 1;
printf("--- ");
for (int i = 0; i < c[1]; i++)
putc(c[i+2]);
printf(" ---\n");
return 0;
}
}
return 0;
}
if ( frame->fault_code == 1 )
{
/* Debug exception */
switch ( kdebug_stepping )
{
case STEPPING_BLOCK:
dword_t last_branch_ip;
wrmsr(IA32_DEBUGCTL, 0);
#if defined(CONFIG_ARCH_X86_I686)
last_branch_ip = rdmsr(IA32_LASTBRANCHFROMIP);
#elif defined(CONFIG_ARCH_X86_P4)
last_branch_ip = (rdmsr(IA32_LASTBRANCH_0 +
rdmsr(IA32_LASTBRANCH_TOS)) >> 32);
#endif
#if defined(CONFIG_DEBUG_DISAS)
int x86_disas(dword_t);
printf("eip=%p ", last_branch_ip);
x86_disas(last_branch_ip);
putc('\n');
#else
printf("eip=%p branch to\n", last_branch_ip);
#endif
/* FALLTHROUGH */
case STEPPING_INSTR:
kdebug_dump_frame_short(frame);
kdebug_stepping = STEPPING_NONE;
kdebug_single_stepping(frame, 0);
return 0;
default:
{
dword_t *dr3;
asm ("mov %%dr3, %0" :"=r"(dr3));
if (dr3 != 0)
{
if ((*dr3 & 0xff) != 0) {
frame->eflags |= (1 << 16);
return 1;
}
}
}
frame->eflags |= (1 << 16);
kdebug_dump_frame(frame);
return 0;
}
}
if ( frame->fault_code == 2 )
{
/* NMI */
printf("\nNMI\n");
kdebug_dump_frame(frame);
return 0;
}
return 0;
}
void kdebug_arch_exit(exception_frame_t * frame)
{
#if defined(CONFIG_DEBUGGER_NEW_KDB)
/* reset the clock */
wrmsr(0x10, saved_time);
# if defined(CONFIG_ARCH_X86_I686)
//only on Intel 686
//#if defined CONFIG_PERFMON || defined CONFIG_ENABLE_PROFILING
qword_t dummy;
dummy = rdmsr(0x186);
dummy = dummy | (1 << 22);
wrmsr(0x186, dummy);
# endif
if(cache_enabled)
{
/* enable cache after debugging */
asm volatile(
"mov %%cr0, %%eax\n" /* copy CR0 into eax */
"xor $((1<<30)|(1<<29)), %%eax\n" /* clear bits CD and NW */
"mov %%eax, %%cr0\n" /* write back in CR0 */
: /* no outputs */
: /* no inputs */
: "eax"
);
}
#endif /*new debugger*/
if (frame->fault_code == 2) /* NMI */
{
/* re-enable nmi */
outb(0x61, inb(0x61) | (0xC));
for (volatile int i = 10000000; i--; );
/* re-enable nmi */
outb(0x61, inb(0x61) & ~(0xC));
in_rtc(0x8f);
in_rtc(0x0f);
};
#if !defined(CONFIG_DEBUGGER_GDB)
if (frame->fault_code == 3)
{
/* now readjust the fault_adress we faked in
kdebug_arch_entry to point to the int3 itself */
frame->fault_address++;
};
#endif
};
#if defined(CONFIG_PERFMON) || defined(CONFIG_DEBUGGER_NEW_KDB)
// this is needed for the new debugger
void kdebug_setperfctr(dword_t sel, dword_t value)
{
/* validate counter number */
switch (sel)
{
case 0: break;
case 1: break;
default: return;
};
/* set the EvntSelX and clear the associated PerfCtrX */
__asm__ __volatile__ (
"/* write EvntSel */\n"
"lea 0x186(%1),%%ecx\n"
"sub %%edx,%%edx\n"
"wrmsr\n"
"/* clear the counter */\n"
"lea 0xc1(%1),%%ecx\n"
"sub %%edx,%%edx\n"
"sub %%eax,%%eax\n"
"wrmsr\n"
: "+a"(value) : "r"(sel) :"ecx", "edx");
}
#endif
void kdebug_perfmon()
{
#if defined(CONFIG_PERFMON)
# if defined(CONFIG_ARCH_X86_I686)
switch(kdebug_get_choice("What", "Set/Print/Clear"
#if defined(CONFIG_DEBUGGER_NEW_KDB)
"/Write/Interrupt enabled/interrupt Disabled\n"
#endif
, 'p'))
{
case 's':
{
dword_t num;
dword_t val;
char c;
printf("num(0,1) ");
do {
c = getc();
if ((c == '0') || (c == '1'))
break;
} while (1);
putc(c);
num = c - '0';
printf(" (u,k,a) ");
val = 0;
do {
c = getc();
switch (c)
{
case 'u': val = 0x10000; break;
case 'k': val = 0x20000; break;
case 'a': val = 0x30000; break;
}
} while (!val);
printf("%c ", c);
/* read event number to count */
val |= kdebug_get_perfctr("event", 0xff);
/* enable counter */
val |= (1 << 22);
#if defined(CONFIG_DEBUGGER_NEW_KDB)
// to enable the pmc overflow interrupt
val |= pmc_overflow_interrupt_mask;
#endif
kdebug_setperfctr(num, val);
} break;
case 'p':
printf("rdpmc: 0=%x, 1=%x\n", rdpmc(0), rdpmc(1));
break;
case 'c':
__asm__ (
"/* clear the counter */\n"
"mov $0xc1,%%ecx\n"
"sub %%edx,%%edx\n"
"sub %%eax,%%eax\n"
"wrmsr \n"
"mov $0xc2,%%ecx\n"
"wrmsr \n"
:
:
: "eax", "ecx", "edx");
break;
}
#if defined (CONFIG_DEBUGGER_NEW_KDB)
case 'w': /* write the counter */
//select counter
printf("set counter: num(0,1) ");
do {
c = getc();
if ((c == '0') || (c =='1')) {
break;
}
} while (1);
putc(c);
//get the value
dword_t value_low;
printf(" low order 32 bits [0]: ");
value_low = kdebug_get_hex(0x0, "0");
//set the counter
int counter_address;
counter_address = PERFCTR0 + c - '0';
wrmsr(counter_address, (qword_t) value_low);
putc('\n');
break;
case 'i':
printf("pmc overflow interrupt enabled. you have to reset the counter(s)\n");
enable_pmc_of_int();
break;
case 'd':
printf("pmc overflow interrupt disabled\n");
disable_pmc_of_int();
break;
}
#endif /*new debugger*/
# elif defined(CONFIG_ARCH_X86_P4)
dword_t ctr;
switch ( kdebug_get_choice("Performance counter",
"Enable/Disable/Clear/clear All/View", 'v') )
{
case 'e':
{
qword_t escr, cccr;
dword_t escrmsr;
ctr = kdebug_get_perfctr(&escrmsr, &escr, &cccr);
if ( ctr != ~0UL )
{
wrmsr(escrmsr, escr);
wrmsr(IA32_CCCR_BASE + ctr, cccr);
wrmsr(IA32_COUNTER_BASE + ctr, 0);
}
break;
}
case 'd':
printf("Counter (0-17): ");
ctr = kdebug_get_dec(99, "");
if ( ctr >= 0 && ctr <= 17 )
{
wrmsr(IA32_CCCR_BASE + ctr, 0);
wrmsr(IA32_COUNTER_BASE + ctr, 0);
}
break;
case 'c':
printf("Counter (0-17): ");
ctr = kdebug_get_dec(99, "");
if ( ctr >= 0 && ctr <= 17 )
wrmsr(IA32_COUNTER_BASE + ctr, 0);
break;
case 'a':
for ( int i = 0; i < 18; i++ )
wrmsr(IA32_COUNTER_BASE + i, 0);
break;
case 'v':
for ( int i = 0; i < 18; i++ )
{
qword_t count = rdmsr(IA32_COUNTER_BASE + i);
printf("%2d: %02x%08x ", i,
(dword_t) ((count >> 32) & 0xff), (dword_t) count);
kdebug_describe_perfctr(i);
putc('\n');
}
break;
}
# endif /* CONFIG_ARCH_X86_P4 */
#endif /* CONFIG_PERFMON */
}
void kdebug_breakpoint()
{
printf("breakpoint [-/?/0..3]: ");
/* the breakpoint register to be used */
dword_t db7;
/* breakpoint address */
dword_t addr = 0;
int num = getc();putc(num);
switch (num) {
/* set debug register 0..3 manually */
case '0'...'3':
num -= '0';
break;
/* reset all debug registers */
case '-':
__asm__ __volatile__ ("mov %%db7,%0": "=r" (db7));
db7 &= ~(0x00000FF);
__asm__ __volatile__ ("mov %0, %%db7": :"r" (db7));
return;break;
/* any key dumps debug registers */
case '?':
default:
__asm__ ("mov %%db7,%0": "=r"(db7));
printf("\nDR7: %x\n", db7);
__asm__ ("mov %%db6,%0": "=r"(db7));
printf("DR6: %x\n", db7); addr=db7;
__asm__ ("mov %%db3,%0": "=r"(db7));
printf("DR3: %x %c\n", db7, addr & 8 ? '*' : ' ');
__asm__ ("mov %%db2,%0": "=r"(db7));
printf("DR2: %x %c\n", db7, addr & 4 ? '*' : ' ');
__asm__ ("mov %%db1,%0": "=r"(db7));
printf("DR1: %x %c\n", db7, addr & 2 ? '*' : ' ');
__asm__ ("mov %%db0,%0": "=r"(db7));
printf("DR0: %x %c\n", db7, addr & 1 ? '*' : ' ');
return;
}
/* read debug control register */
__asm__ __volatile__ ("mov %%db7,%0" : "=r" (db7));
printf(" [Instr/Access/pOrt/Write/-/+] :");
/* breakpoint type */
char t;
while (42)
{
t = getc();
if ((t=='i') || (t=='a') || (t=='o') ||
(t=='w') || (t=='-') || (t=='+'))
break;
}
putc(t);
switch (t)
{
case '-':
db7 &= ~(2 << (num * 2)); /* disable */
num = -1;
break;
case '+':
db7 |= (2 << (num * 2)); /* enable */
num = -1;
break;
case 'i': /* instruction execution */
addr = kdebug_get_hex();
db7 &= ~(0x000F0000 << (num * 4));
db7 |= (0x00000000 << (num * 4));
db7 |= (2 << (num * 2)); /* enable */
break;
case 'w': /* data write */
addr = kdebug_get_hex();
db7 &= ~(0x000F0000 << (num * 4));
db7 |= (0x00010000 << (num * 4));
db7 |= (2 << (num * 2)); /* enable */
break;
case 'o': /* I/O */
addr = kdebug_get_hex();
db7 &= ~(0x000F0000 << (num * 4));
db7 |= (0x00020000 << (num * 4));
db7 |= (2 << (num * 2)); /* enable */
break;
case 'a': /* read/write */
addr = kdebug_get_hex();
db7 &= ~(0x000F0000 << (num * 4));
db7 |= (0x00030000 << (num * 4));
db7 |= (2 << (num * 2)); /* enable */
break;
};
if (num==0) __asm__ __volatile__ ("mov %0, %%db0" : : "r" (addr));
if (num==1) __asm__ __volatile__ ("mov %0, %%db1" : : "r" (addr));
if (num==2) __asm__ __volatile__ ("mov %0, %%db2" : : "r" (addr));
if (num==3) __asm__ __volatile__ ("mov %0, %%db3" : : "r" (addr));
__asm__ __volatile__ ("mov %0, %%db7" : : "r" (db7));
}
void kdebug_arch_help()
{
printf(
"c profile C dump profile\n"
"T single stepping\n"
"e perfmon\n"
"b breakpoint\n"
" b? list\n"
" b- disable all\n"
" b#i<addr> instruction\n"
" b#w<addr> write\n"
" b#o<addr> I/O\n"
" b#a<addr> access\n"
" b#- disable this dr\n"
);
}
INLINE void cpuid(dword_t index,
dword_t* eax, dword_t* ebx, dword_t* ecx, dword_t* edx)
{
__asm__ (
"cpuid"
: "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
: "a" (index)
);
}
#if defined(CONFIG_X86_IOAPIC)
#include INC_ARCH(apic.h)
#endif
#if defined(CONFIG_ENABLE_SMALL_AS)
void kdebug_small_spaces(tcb_t *current)
{
switch ( kdebug_get_choice("\nSmall spaces",
"View/make Small/make Large", 'v') )
{
case 's':
{
dword_t size, s, idx;
tcb_t * tcb = kdebug_get_task(current);
tcb = kdebug_find_task_tcb(tasknum(tcb), current);
if ( tcb == NULL )
break;
printf("Size (MB) [64]: ");
size = kdebug_get_dec(64) / SMALL_SPACE_MIN;
printf("Index [0]: ");
idx = kdebug_get_dec(0);
for ( s = 0; ((1 << s) & size) == 0; s++ ) ;
if ( size )
make_small_space (tcb->space, ((idx << 1) | 1) << s);
break;
}
case 'l':
{
tcb_t * tcb = kdebug_get_task(current);
tcb = kdebug_find_task_tcb(tasknum(tcb), current);
if (tcb && tcb->space->is_small ())
small_space_to_large (tcb->space);
break;
}
case 'v':
{
extern space_t *small_space_owner[];
space_t *owner;
dword_t i, k, n;
tcb_t *tcb;
putc('\n');
for ( i = 0; i < MAX_SMALL_SPACES; i++ )
{
if ( small_space_owner[i] == NULL )
continue;
owner = small_space_owner[i];
for ( n = 0; i+n < MAX_SMALL_SPACES; n++ )
if ( small_space_owner[i+n] != owner )
break;
for ( tcb = get_idle_tcb()->present_next;
tcb->space != owner && tcb != get_idle_tcb();
tcb = tcb->present_next )
;
k = printf("%3d-%d", i, i+n-1);
for ( k = 9-k; k--; ) putc(' ');
printf("<%p,%p> size=%3dMB space=%p task=",
SMALL_SPACE_START + (i << SMALL_SPACE_SHIFT),
SMALL_SPACE_START + ((i+n) << SMALL_SPACE_SHIFT),
n * SMALL_SPACE_MIN, owner);
printf(tcb->space == owner ? "%2x\n" : "?\n",
tcb->myself.x0id.task);
i += n-1;
}
break;
}
}
}
#endif /* CONFIG_SMALL_SPACES */
static void kdebug_msrs()
{
dword_t msr;
qword_t val;
switch ( kdebug_get_choice("\nModel Specific Registers",
"Show/Read/Write", 's') )
{
case 'r':
printf("msr=");
msr = kdebug_get_hex();
val = rdmsr(msr);
printf("rdmsr(%x)=%x\n", msr, val);
break;
case 'w':
printf("msr=");
msr = kdebug_get_hex();
printf("val=");
val = kdebug_get_hex();
wrmsr(msr, val);
break;
default:
printf("SYSENTER_CS_MSR: %x\n", rdmsr(IA32_SYSENTER_CS_MSR));
printf("SYSENTER_EIP_MSR: %x\n", rdmsr(IA32_SYSENTER_EIP_MSR));
printf("SYSENTER_ESP_MSR: %x\n", rdmsr(IA32_SYSENTER_ESP_MSR));
#if defined(CONFIG_ARCH_X86_I686)
printf("LASTBRANCH_FROM_IP: %x\n", rdmsr(IA32_LASTBRANCHFROMIP));
printf("LASTBRANCH_TO_IP: %x\n", rdmsr(IA32_LASTBRANCHTOIP));
printf("LASTINT_FROM_IP: %x\n", rdmsr(IA32_LASTINTFROMIP));
printf("LASTINT_TO_IP: %x\n", rdmsr(IA32_LASTINTTOIP));
#endif
#if defined(CONFIG_ARCH_X86_P4)
printf("LER_FROM_IP: %x\n", rdmsr(IA32_LER_FROM_LIP));
printf("LER_TO_IP: %x\n", rdmsr(IA32_LER_TO_LIP));
dword_t tos = rdmsr(IA32_LASTBRANCH_TOS);
for ( int i = 0; i < 4; i++ )
{
qword_t br = rdmsr(IA32_LASTBRANCH_0 + ((i + tos) & 0x03));
printf("LASTBRANCH_%d: %x -> %x\n", (i + tos) & 0x03,
(dword_t) (br >> 32), (dword_t) br);
}
#endif
break;
};
}
void kdebug_cpustate(tcb_t *current)
{
int l;
char c;
dword_t foo;
printf("\nx86 - ");
l = printf("[Gdt/Crs/Idt/iOapic/Ports/Msr/cpUid"
#if defined(CONFIG_ENABLE_SMALL_AS)
"/Smallspaces"
#endif
#if defined(CONFIG_X86_P4_BTS)
"/Bts"
#endif
#if defined(CONFIG_X86_P4_PEBS)
"/pEbs"
#endif
"]: ");
switch (getc())
{
case 'c':
__asm__ ("mov %%cr4,%0": "=r"(foo));
printf("\nCR4: %x\n", foo);
__asm__ ("mov %%cr3,%0": "=r"(foo));
printf("CR3: %x\n", foo);
__asm__ ("mov %%cr2,%0": "=r"(foo));
printf("CR2: %x\n", foo);
__asm__ ("mov %%cr0,%0": "=r"(foo));
printf("CR0: %x\n", foo);
break;
case 'C':
printf("\nset CR");
while (1) { c = getc(); if ((c >= '0') && (c <= '4')) break; };
printf("%d=", c-'0');
foo = kdebug_get_hex();
switch (c)
{
case '0': __asm__ __volatile__ ("mov %0,%%cr0": : "r"(foo)); break;
case '2': __asm__ __volatile__ ("mov %0,%%cr2": : "r"(foo)); break;
case '3': __asm__ __volatile__ ("mov %0,%%cr3": : "r"(foo)); break;
case '4': __asm__ __volatile__ ("mov %0,%%cr4": : "r"(foo)); break;
};
break;
case 'm':
kdebug_msrs();
break;
case 'g':
extern seg_desc_t gdt[];
printf("\nGDT-dump: gdt at %x\n", gdt);
for (int i = 0; i < GDT_SIZE; i++)
{
seg_desc_t *ent = gdt+i;
printf("GDT[%d] = %x:%x", i, ent->x.raw[0], ent->x.raw[1]);
if ( (ent->x.raw[0] == 0 && ent->x.raw[1] == 0) ||
(! ent->x.d.s) )
{
printf("\n");
continue;
}
printf(" <%p,%p> ",
ent->x.d.base_low + (ent->x.d.base_high << 24),
ent->x.d.base_low + (ent->x.d.base_high << 24) +
(ent->x.d.g ? 0xfff |
(ent->x.d.limit_low + (ent->x.d.limit_high << 16)) << 12 :
(ent->x.d.limit_low + (ent->x.d.limit_high << 16))));
printf("dpl=%d %d-bit ", ent->x.d.dpl, ent->x.d.d ? 32 : 16);
if ( ent->x.d.type & 0x8 )
printf("code %cC %cR ",
ent->x.d.type & 0x4 ? ' ' : '!',
ent->x.d.type & 0x2 ? ' ' : '!');
else
printf("data E%c R%c ",
ent->x.d.type & 0x4 ? 'D' : 'U',
ent->x.d.type & 0x2 ? 'W' : 'O');
printf("%cP %cA\n",
ent->x.d.p ? ' ' : '!',
ent->x.d.type & 0x1 ? ' ' : '!');
}
break;
case 'i':
extern idt_desc_t idt[];
printf("\nIDT-dump: idt at %x\n", idt);
for (int i = 0; i < (IDT_SIZE) ; i++)
if (idt[i].x.d.p)
printf("%2x -> %4x:%x , dpl=%d, type=%d (%x:%x)\n", i,
idt[i].x.d.sel,
idt[i].x.d.offset_low | (idt[i].x.d.offset_high << 16),
idt[i].x.d.dpl, idt[i].x.d.type,
idt[i].x.raw[0], idt[i].x.raw[1]);
break;
case 'o':
#if defined(CONFIG_X86_IOAPIC)
printf("\nIO-APIC redirection table\n");
for (int i = 0; i < 24; i++)
printf("ioredir(%d): %x, %x\n", i, get_io_apic(0x10 + (i*2)), get_io_apic(0x11 + (i*2)));
#endif
break;
case 'p':
#define B(x) do { for (int __i =0; __i < (x); __i++) { putc(8); putc(' '); putc(8); } } while (0)
word_t port;
B(l);
l = printf("[In/Out]: ");
c = getc();
switch(c)
{
case 'i':
B(l);
printf("in "); port = kdebug_get_hex();
printf(" -> %2x\n", inb(port));
break;
case 'o':
B(l);
printf("out "); port = kdebug_get_hex();
printf(", "); outb(port, kdebug_get_hex());
break;
};
break;
#if defined(CONFIG_ENABLE_SMALL_AS)
case 's':
kdebug_small_spaces(current);
break;
#endif
#if defined(CONFIG_X86_P4_BTS)
case 'b':
kdebug_x86_bts();
break;
#endif
#if defined(CONFIG_X86_P4_PEBS)
case 'e':
kdebug_x86_pebs();
break;
#endif
case 'u':
/* Reference:
AP-485
APPLICATION NOTE
Intel Processor Identification and
the CPUID Instruction
June 2001
Order Number: 241618-018
http://developer.intel.com/design/pentiumII/applnots/241618.htm
*/
/* from above document, table 5, pages 14-15 */
const char* features[] = {
"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
"cx8", "apic", "?", "sep", "mtrr", "pge", "mca", "cmov",
"pat", "pse-36", "psn", "cflsh", "?", "ds", "acpi", "mmx",
"fxsr", "sse", "sse2", "ss", "ht", "tm", "ia64", "?" };
/* from above document, table 7, page 17 */
const char* cachecfg[16][16] =
{
{ /* 0x00 */
"",
"ITLB: 32*4K, 4w", "ITLB: 2*4M",
"DTLB: 64*4K, 4w", "DTLB: 8*4M, 4w", 0,
"ICache: 8K, 4w, 32", 0, "ICache: 16K, 4w, 32", 0,
"DCache: 8K, 2w, 32", 0, "DCache: 16K, 4w, 32" },
{ /* 0x10 */ }, { /* 0x20 */ }, { /* 0x30 */ },
{ /* 0x40 */
"no L2 or L3",
"Cache: 128K, 4w, 32", "Cache: 256K, 4w, 32",
"Cache: 512K, 4w, 32", "Cache: 1M, 4w, 32",
"Cache: 2M, 4w, 32",
},
{ /* 0x50 */
"ITLB: 64*{4K,2M/4M}", "ITLB: 128*{4K,2M/4M}",
"ITLB: 256*{4K,2M/4M}", 0, 0, 0, 0, 0, 0, 0, 0,
"DTLB: 64*{4K,4M}", "DTLB: 128*{4K,4M}",
"DTLB: 256*{4K,4M}"
},
{ /* 0x60 */
0, 0, 0, 0, 0, 0,
"DCache: 8K, 4w, 64", "DCache: 16K, 4w, 64",
"DCache: 32K, 4w, 64"
},
{ /* 0x70 */
"TC: 12Kuop, 8w", "TC: 16Kuop, 8w", "TC: 32Kuop, 8w", 0,
0, 0, 0, 0, 0,
"Cache: 128K, 8w, 64", "Cache: 256K, 8w, 64",
"Cache: 512K, 8w, 64", "Cache: 1M, 8w, 64"
},
{ /* 0x80 */
0, 0,
"Cache: 256K, 8w, 32", "Cache: 512K, 8w, 32",
"Cache: 1M, 8w, 32", "Cache: 2M, 8w, 32"
}
};
dword_t id[4][4];
dword_t i;
printf("\n");
for (i = 0; i < 4; i++)
cpuid(i, &id[i][0], &id[i][1], &id[i][2], &id[i][3]);
for (i = 0; i <= id[0][0]; i++)
printf("cpuid(%d):%x:%x:%x:%x\n", i,
id[i][0], id[i][1], id[i][2], id[i][3]);
printf("0: max=%d \"", id[0][0]);
for (i = 0; i < 12; i++) putc(((char*) &id[0][1])[i]);
printf("\"\n1: fam=%d, mod=%d, step=%d\n1: ",
(id[1][0] >> 8) & 0xF,
(id[1][0] >> 4) & 0xF,
(id[1][0] >> 0) & 0xF);
for (i = 0; i < 32; i++)
if ((id[1][3] >> i) & 1) printf("%s ", features[i]);
printf("\n");
/* 2: eax[7:0] determines, how often 2 must be called - noimp */
for (i = 1; i < 16; i++)
{
int j = ((char*)id[2])[i];
if (((id[2][i/4] & 0x80000000) == 0) && (j != 0))
printf("%s\n", cachecfg[0][j]);
};
break;
}
}
int kdebug_step_instruction(exception_frame_t * frame)
{
kdebug_single_stepping(frame, 1);
kdebug_stepping = STEPPING_INSTR;
frame->eflags |= (1 << 16);
putc('s'); putc('\n');
return 1;
}
int kdebug_step_block(exception_frame_t * frame)
{
#if defined(CONFIG_ARCH_X86_I686) || defined(CONFIG_ARCH_X86_P4)
kdebug_single_stepping(frame, 1);
kdebug_stepping = STEPPING_BLOCK;
frame->eflags |= (1 << 16);
wrmsr(IA32_DEBUGCTL, ((1<<0) + (1<<1))); /* LBR + BTF */
putc('S'); putc('\n');
return 1;
#else
/* Single stepping on branches is not supported on older CPUs. */
printf("S - Unsupported\n");
return 0;
#endif
}
void kdebug_disassemble(exception_frame_t * frame)
{
#if defined(CONFIG_DEBUG_DISAS)
extern int x86_disas(dword_t pc);
char c;
dword_t pc;
restart:
printf("ip [current]: ");
pc = kdebug_get_hex(frame->fault_address, "current");
do {
printf("%x: ", pc);
pc += x86_disas(pc);
printf("\n");
c = getc();
} while ((c != 'q') && (c != 'u'));
if (c == 'u')
goto restart;
#endif
};
#if defined(CONFIG_TRACEBUFFER)
void kdebug_dump_tracebuffer()
{
dword_t count = tracebuffer->current / sizeof(trace_t);
dword_t index, top = 0, bottom = count, chunk = (count<32) ? count : 32;
tracestatus_t old = { 0,0,0 }, sum = { 0,0,0 };
trace_t *current;
char c;
if (tracebuffer->magic != TBUF_MAGIC)
{
printf("Bad tracebuffer signature at %x\n",(dword_t)(&tracebuffer->magic));
return;
}
c = kdebug_get_choice("Dump tracebuffer", "All/Region/Top/Bottom", 'b');
switch (c)
{
case 'a' : break;
case 'r' : printf("From record [0]: ");
top = kdebug_get_dec();
printf("To record [%d]: ", bottom);
bottom = kdebug_get_dec(bottom);
break;
case 't' : printf("Record count [%d]: ", chunk);
bottom = kdebug_get_dec(chunk);
break;
default : printf("Record count [%d]: ", chunk);
top = bottom - kdebug_get_dec(chunk);
break;
}
if (bottom > count)
bottom = count;
if (top > bottom)
top = bottom;
printf("\nRecord Cycles UL Ins Event1 Identifier\n");
for (index = top; index<bottom; index++)
{
current = &(tracebuffer->trace[index]);
if (!old.cycles)
old = current->status;
printf("%6d%10d%10d%10d ", index,
(current->status.cycles - old.cycles),
(current->status.pmc0 - old.pmc0),
(current->status.pmc1 - old.pmc1));
sum.cycles += (current->status.cycles - old.cycles);
sum.pmc0 += (current->status.pmc0 - old.pmc0);
sum.pmc1 += (current->status.pmc1 - old.pmc1);
old = current->status;
switch (current->identifier)
{
#define DEFINE_TP(x, str) \
case TP_##x: \
printf("*KRNL* "); \
printf(str, current->data[0], current->data[1], \
current->data[2], current->data[3]); \
break;
#include <tracepoint_list.h>
#undef DEFINE_TP
/* Entries from user level are displayed as plain text */
default:
printf("[%c%c%c%c]", (char)((current->identifier>>24)&0xFF),
(char)((current->identifier>>16)&0xFF),
(char)((current->identifier>>8)&0xFF),
(char)(current->identifier&0xFF));
c = (current->identifier>>24)&0xFF;
if ((c>='1') && (c<='4'))
{
int i=0;
printf(", par=(%xh", current->data[i++]);
while ((--c)>='1')
printf(",%xh", current->data[i++]);
putc(')');
}
break;
}
putc('\n');
}
printf("----------------------------------------------------\n");
printf(" %10d%10d%10d %6d entries\n", sum.cycles, sum.pmc0, sum.pmc1, bottom-top);
}
void kdebug_flush_tracebuffer()
{
printf("Tracebuffer flushed\n");
tracebuffer->current = 0;
}
#endif /* defined(CONFIG_TRACEBUFFER) */
#endif /* defined(CONFIG_DEBUGGER_KDB) */