#include <sys/param.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/systm.h>
#include <sys/mutex.h>
#include <uvm/uvm_extern.h>
#include <dev/cons.h>
#include <machine/cpufunc.h>
#include <machine/db_machdep.h>
#include <machine/pmap.h>
#include <ddb/db_sym.h>
#include <ddb/db_command.h>
#include <ddb/db_extern.h>
#include <ddb/db_output.h>
#include <ddb/db_run.h>
#include <ddb/db_variables.h>
struct db_variable db_regs[] = {
{ "x0", (long *)&DDB_REGS->tf_x[0], FCN_NULL, },
{ "x1", (long *)&DDB_REGS->tf_x[1], FCN_NULL, },
{ "x2", (long *)&DDB_REGS->tf_x[2], FCN_NULL, },
{ "x3", (long *)&DDB_REGS->tf_x[3], FCN_NULL, },
{ "x4", (long *)&DDB_REGS->tf_x[4], FCN_NULL, },
{ "x5", (long *)&DDB_REGS->tf_x[5], FCN_NULL, },
{ "x6", (long *)&DDB_REGS->tf_x[6], FCN_NULL, },
{ "x7", (long *)&DDB_REGS->tf_x[7], FCN_NULL, },
{ "x8", (long *)&DDB_REGS->tf_x[8], FCN_NULL, },
{ "x9", (long *)&DDB_REGS->tf_x[9], FCN_NULL, },
{ "x10", (long *)&DDB_REGS->tf_x[10], FCN_NULL, },
{ "x11", (long *)&DDB_REGS->tf_x[11], FCN_NULL, },
{ "x12", (long *)&DDB_REGS->tf_x[12], FCN_NULL, },
{ "x13", (long *)&DDB_REGS->tf_x[13], FCN_NULL, },
{ "x14", (long *)&DDB_REGS->tf_x[14], FCN_NULL, },
{ "x15", (long *)&DDB_REGS->tf_x[15], FCN_NULL, },
{ "x16", (long *)&DDB_REGS->tf_x[16], FCN_NULL, },
{ "x17", (long *)&DDB_REGS->tf_x[17], FCN_NULL, },
{ "x18", (long *)&DDB_REGS->tf_x[18], FCN_NULL, },
{ "x19", (long *)&DDB_REGS->tf_x[19], FCN_NULL, },
{ "x20", (long *)&DDB_REGS->tf_x[20], FCN_NULL, },
{ "x21", (long *)&DDB_REGS->tf_x[21], FCN_NULL, },
{ "x22", (long *)&DDB_REGS->tf_x[22], FCN_NULL, },
{ "x23", (long *)&DDB_REGS->tf_x[23], FCN_NULL, },
{ "x24", (long *)&DDB_REGS->tf_x[24], FCN_NULL, },
{ "x25", (long *)&DDB_REGS->tf_x[25], FCN_NULL, },
{ "x26", (long *)&DDB_REGS->tf_x[26], FCN_NULL, },
{ "x27", (long *)&DDB_REGS->tf_x[27], FCN_NULL, },
{ "x28", (long *)&DDB_REGS->tf_x[28], FCN_NULL, },
{ "x29", (long *)&DDB_REGS->tf_x[29], FCN_NULL, },
{ "x30", (long *)&DDB_REGS->tf_x[30], FCN_NULL, },
{ "sp", (long *)&DDB_REGS->tf_sp, FCN_NULL, },
{ "spsr", (long *)&DDB_REGS->tf_spsr, FCN_NULL, },
{ "elr", (long *)&DDB_REGS->tf_elr, FCN_NULL, },
{ "lr", (long *)&DDB_REGS->tf_lr, FCN_NULL, },
};
#ifdef MULTIPROCESSOR
struct db_mutex ddb_mp_mutex = DB_MUTEX_INITIALIZER;
volatile int ddb_state = DDB_STATE_NOT_RUNNING;
volatile cpuid_t ddb_active_cpu;
int db_switch_cpu;
long db_switch_to_cpu;
void db_cpuinfo_cmd(db_expr_t, int, db_expr_t, char *);
void db_startproc_cmd(db_expr_t, int, db_expr_t, char *);
void db_stopproc_cmd(db_expr_t, int, db_expr_t, char *);
void db_ddbproc_cmd(db_expr_t, int, db_expr_t, char *);
void db_stopcpu(int cpu);
void db_startcpu(int cpu);
int db_enter_ddb(void);
#endif
extern label_t *db_recover;
struct db_variable * db_eregs = db_regs + nitems(db_regs);
#ifdef DDB
int
db_ktrap(int type, db_regs_t *regs)
{
uint64_t mdscr;
int s;
#ifdef MULTIPROCESSOR
db_mtx_enter(&ddb_mp_mutex);
if (ddb_state == DDB_STATE_EXITING)
ddb_state = DDB_STATE_NOT_RUNNING;
db_mtx_leave(&ddb_mp_mutex);
while (db_enter_ddb()) {
#endif
switch (type) {
case EXCP_BRK:
case EXCP_WATCHPT_EL1:
case EXCP_SOFTSTP_EL1:
case -1:
break;
default:
if (db_recover != 0) {
db_error("Faulted in DDB; continuing...\n");
}
break;
}
ddb_regs = *regs;
s = splhigh();
db_active++;
cnpollc(1);
db_trap(type, 0);
cnpollc(0);
db_active--;
splx(s);
*regs = ddb_regs;
#ifdef MULTIPROCESSOR
if (!db_switch_cpu)
ddb_state = DDB_STATE_EXITING;
}
#endif
mdscr = READ_SPECIALREG(mdscr_el1);
if (regs->tf_spsr & PSR_SS) {
mdscr |= (DBG_MDSCR_KDE | DBG_MDSCR_SS);
regs->tf_spsr &= ~PSR_D;
} else {
mdscr &= ~(DBG_MDSCR_KDE | DBG_MDSCR_SS);
regs->tf_spsr |= PSR_D;
}
WRITE_SPECIALREG(mdscr_el1, mdscr);
return (1);
}
#endif
#define INKERNEL(va) (((vaddr_t)(va)) & (1ULL << 63))
static int db_validate_address(vaddr_t addr);
static int
db_validate_address(vaddr_t addr)
{
struct proc *p = curproc;
struct pmap *pmap;
if (!p || !p->p_vmspace || !p->p_vmspace->vm_map.pmap ||
INKERNEL(addr))
pmap = pmap_kernel();
else
pmap = p->p_vmspace->vm_map.pmap;
return (pmap_extract(pmap, addr, NULL) == FALSE);
}
void
db_read_bytes(vaddr_t addr, size_t size, void *datap)
{
char *data = datap, *src = (char *)addr;
if (db_validate_address((vaddr_t)src)) {
db_printf("address %p is invalid\n", src);
return;
}
if (size == 8 && (addr & 7) == 0 && ((vaddr_t)data & 7) == 0) {
*((uint64_t*)data) = *((uint64_t*)src);
return;
}
if (size == 4 && (addr & 3) == 0 && ((vaddr_t)data & 3) == 0) {
*((int*)data) = *((int*)src);
return;
}
if (size == 2 && (addr & 1) == 0 && ((vaddr_t)data & 1) == 0) {
*((short*)data) = *((short*)src);
return;
}
while (size-- > 0) {
if (db_validate_address((vaddr_t)src)) {
db_printf("address %p is invalid\n", src);
return;
}
*data++ = *src++;
}
}
static void
db_write_text(vaddr_t addr, size_t size, char *data)
{
vaddr_t pgva;
size_t limit;
char *dst;
if (size == 0)
return;
dst = (char *)addr;
do {
pgva = trunc_page((vaddr_t)dst);
limit = PAGE_SIZE - ((vaddr_t)dst & PGOFSET);
if (limit > size)
limit = size;
size -= limit;
pmap_page_rw(pmap_kernel(), pgva);
for (; limit > 0; limit--)
*dst++ = *data++;
pmap_page_ro(pmap_kernel(), pgva, PROT_READ|PROT_EXEC);
} while (size != 0);
}
void
db_write_bytes(vaddr_t addr, size_t size, void *datap)
{
extern char etext[];
char *data = datap, *dst;
size_t loop;
if (addr >= KERNBASE && addr < (vaddr_t)&etext) {
db_write_text(addr, size, data);
goto sync;
}
dst = (char *)addr;
loop = size;
while (loop-- > 0) {
if (db_validate_address((vaddr_t)dst)) {
db_printf("address %p is invalid\n", dst);
return;
}
*dst++ = *data++;
}
sync:
cpu_icache_sync_range(addr, size);
cpu_tlb_flush();
}
void
db_enter(void)
{
__asm volatile("brk #0xf000");
}
#ifdef MULTIPROCESSOR
void
db_cpuinfo_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
int i;
for (i = 0; i < MAXCPUS; i++) {
if (cpu_info[i] != NULL) {
db_printf("%c%4d: ", (i == cpu_number()) ? '*' : ' ',
CPU_INFO_UNIT(cpu_info[i]));
switch(cpu_info[i]->ci_ddb_paused) {
case CI_DDB_RUNNING:
db_printf("running\n");
break;
case CI_DDB_SHOULDSTOP:
db_printf("stopping\n");
break;
case CI_DDB_STOPPED:
db_printf("stopped\n");
break;
case CI_DDB_ENTERDDB:
db_printf("entering ddb\n");
break;
case CI_DDB_INDDB:
db_printf("ddb\n");
break;
default:
db_printf("? (%d)\n",
cpu_info[i]->ci_ddb_paused);
break;
}
}
}
}
void
db_startproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
int i;
if (have_addr) {
if (addr >= 0 && addr < MAXCPUS &&
cpu_info[addr] != NULL && addr != cpu_number())
db_startcpu(addr);
else
db_printf("Invalid cpu %d\n", (int)addr);
} else {
for (i = 0; i < MAXCPUS; i++) {
if (cpu_info[i] != NULL && i != cpu_number())
db_startcpu(i);
}
}
}
void
db_stopproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
int i;
if (have_addr) {
if (addr >= 0 && addr < MAXCPUS &&
cpu_info[addr] != NULL && addr != cpu_number())
db_stopcpu(addr);
else
db_printf("Invalid cpu %d\n", (int)addr);
} else {
for (i = 0; i < MAXCPUS; i++) {
if (cpu_info[i] != NULL && i != cpu_number())
db_stopcpu(i);
}
}
}
void
db_ddbproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
if (have_addr) {
if (addr >= 0 && addr < MAXCPUS &&
cpu_info[addr] != NULL && addr != cpu_number()) {
db_stopcpu(addr);
db_switch_to_cpu = addr;
db_switch_cpu = 1;
db_cmd_loop_done = 1;
} else {
db_printf("Invalid cpu %d\n", (int)addr);
}
} else {
db_printf("CPU not specified\n");
}
}
int
db_enter_ddb(void)
{
int i;
db_mtx_enter(&ddb_mp_mutex);
if (ddb_state == DDB_STATE_NOT_RUNNING) {
ddb_active_cpu = cpu_number();
ddb_state = DDB_STATE_RUNNING;
curcpu()->ci_ddb_paused = CI_DDB_INDDB;
db_mtx_leave(&ddb_mp_mutex);
for (i = 0; i < MAXCPUS; i++) {
if (cpu_info[i] != NULL && i != cpu_number() &&
cpu_info[i]->ci_ddb_paused != CI_DDB_STOPPED) {
cpu_info[i]->ci_ddb_paused = CI_DDB_SHOULDSTOP;
arm_send_ipi(cpu_info[i], ARM_IPI_DDB);
}
}
return (1);
}
if (ddb_active_cpu == cpu_number() && ddb_state == DDB_STATE_EXITING) {
for (i = 0; i < MAXCPUS; i++) {
if (cpu_info[i] != NULL) {
cpu_info[i]->ci_ddb_paused = CI_DDB_RUNNING;
}
}
db_mtx_leave(&ddb_mp_mutex);
return (0);
}
if (ddb_active_cpu == cpu_number() && db_switch_cpu) {
curcpu()->ci_ddb_paused = CI_DDB_SHOULDSTOP;
db_switch_cpu = 0;
ddb_active_cpu = db_switch_to_cpu;
cpu_info[db_switch_to_cpu]->ci_ddb_paused = CI_DDB_ENTERDDB;
}
while (ddb_active_cpu != cpu_number() &&
curcpu()->ci_ddb_paused != CI_DDB_RUNNING) {
if (curcpu()->ci_ddb_paused == CI_DDB_SHOULDSTOP)
curcpu()->ci_ddb_paused = CI_DDB_STOPPED;
db_mtx_leave(&ddb_mp_mutex);
while (ddb_active_cpu != cpu_number() &&
curcpu()->ci_ddb_paused != CI_DDB_RUNNING)
CPU_BUSY_CYCLE();
db_mtx_enter(&ddb_mp_mutex);
}
if (ddb_active_cpu == cpu_number() && ddb_state == DDB_STATE_RUNNING) {
curcpu()->ci_ddb_paused = CI_DDB_INDDB;
db_mtx_leave(&ddb_mp_mutex);
return (1);
} else {
db_mtx_leave(&ddb_mp_mutex);
return (0);
}
}
void
db_startcpu(int cpu)
{
if (cpu != cpu_number() && cpu_info[cpu] != NULL) {
db_mtx_enter(&ddb_mp_mutex);
cpu_info[cpu]->ci_ddb_paused = CI_DDB_RUNNING;
db_mtx_leave(&ddb_mp_mutex);
}
}
void
db_stopcpu(int cpu)
{
db_mtx_enter(&ddb_mp_mutex);
if (cpu != cpu_number() && cpu_info[cpu] != NULL &&
cpu_info[cpu]->ci_ddb_paused != CI_DDB_STOPPED) {
cpu_info[cpu]->ci_ddb_paused = CI_DDB_SHOULDSTOP;
db_mtx_leave(&ddb_mp_mutex);
arm_send_ipi(cpu_info[cpu], ARM_IPI_DDB);
} else {
db_mtx_leave(&ddb_mp_mutex);
}
}
#endif
const struct db_command db_machine_command_table[] = {
#ifdef MULTIPROCESSOR
{ "cpuinfo", db_cpuinfo_cmd, 0, NULL },
{ "startcpu", db_startproc_cmd, 0, NULL },
{ "stopcpu", db_stopproc_cmd, 0, NULL },
{ "ddbcpu", db_ddbproc_cmd, 0, NULL },
#endif
{ NULL, NULL, 0, NULL }
};
extern vaddr_t esym;
extern vaddr_t end;
void
db_machine_init(void)
{
#ifdef MULTIPROCESSOR
int i;
for (i = 0; i < MAXCPUS; i++) {
if (cpu_info[i] != NULL)
cpu_info[i]->ci_ddb_paused = CI_DDB_RUNNING;
}
#endif
}
vaddr_t
db_branch_taken(u_int insn, vaddr_t pc, db_regs_t *db_regs)
{
return pc + 4;
}