From 755135db10255fd8d372551c9dde613314481dc8 Mon Sep 17 00:00:00 2001 From: kettenis Date: Tue, 30 Jan 2018 15:46:12 +0000 Subject: [PATCH] Add support for switching CPUs in ddb on arm64. Based on a diff from drahn@. ok patrick@ --- sys/arch/arm64/arm64/db_interface.c | 242 ++++++++++++++++++++++++++-- sys/arch/arm64/include/cpu.h | 10 +- sys/arch/arm64/include/db_machdep.h | 6 +- 3 files changed, 247 insertions(+), 11 deletions(-) diff --git a/sys/arch/arm64/arm64/db_interface.c b/sys/arch/arm64/arm64/db_interface.c index d5d85b36e77..64675c08f8b 100644 --- a/sys/arch/arm64/arm64/db_interface.c +++ b/sys/arch/arm64/arm64/db_interface.c @@ -1,4 +1,4 @@ -/* $OpenBSD: db_interface.c,v 1.3 2017/04/30 16:45:45 mpi Exp $ */ +/* $OpenBSD: db_interface.c,v 1.4 2018/01/30 15:46:12 kettenis Exp $ */ /* $NetBSD: db_interface.c,v 1.34 2003/10/26 23:11:15 chris Exp $ */ /* @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -100,6 +101,23 @@ struct db_variable db_regs[] = { { "lr", (long *)&DDB_REGS->tf_lr, FCN_NULL, }, }; +#ifdef MULTIPROCESSOR +struct mutex ddb_mp_mutex = + MUTEX_INITIALIZER_FLAGS(IPL_HIGH, "ddb_mp_mutex", MTX_NOWITNESS); +volatile int ddb_state = DDB_STATE_NOT_RUNNING; +volatile cpuid_t ddb_active_cpu; +boolean_t 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); @@ -115,6 +133,14 @@ kdb_trap(int type, db_regs_t *regs) { int s; +#ifdef MULTIPROCESSOR + mtx_enter(&ddb_mp_mutex); + if (ddb_state == DDB_STATE_EXITING) + ddb_state = DDB_STATE_NOT_RUNNING; + mtx_leave(&ddb_mp_mutex); + while (db_enter_ddb()) { +#endif + switch (type) { case T_BREAKPOINT: /* breakpoint */ case -1: /* keyboard interrupt */ @@ -141,11 +167,15 @@ kdb_trap(int type, db_regs_t *regs) *regs = ddb_regs; +#ifdef MULTIPROCESSOR + if (!db_switch_cpu) + ddb_state = DDB_STATE_EXITING; + } +#endif return (1); } #endif - #define INKERNEL(va) (((vaddr_t)(va)) & (1ULL << 63)) static int db_validate_address(vaddr_t addr); @@ -256,8 +286,199 @@ db_enter(void) asm("brk 0"); } +#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; + + mtx_enter(&ddb_mp_mutex); + + /* If we are first in, grab ddb and stop all other CPUs */ + if (ddb_state == DDB_STATE_NOT_RUNNING) { + ddb_active_cpu = cpu_number(); + ddb_state = DDB_STATE_RUNNING; + curcpu()->ci_ddb_paused = CI_DDB_INDDB; + 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); + } + + /* Leaving ddb completely. Start all other CPUs and return 0 */ + 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; + } + } + mtx_leave(&ddb_mp_mutex); + return (0); + } + + /* We're switching to another CPU. db_ddbproc_cmd() has made sure + * it is waiting for ddb, we just have to set ddb_active_cpu. */ + 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; + } + + /* Wait until we should enter ddb or resume */ + 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; + mtx_leave(&ddb_mp_mutex); + + /* Busy wait without locking, we'll confirm with lock later */ + while (ddb_active_cpu != cpu_number() && + curcpu()->ci_ddb_paused != CI_DDB_RUNNING) + CPU_BUSY_CYCLE(); + + mtx_enter(&ddb_mp_mutex); + } + + /* Either enter ddb or exit */ + if (ddb_active_cpu == cpu_number() && ddb_state == DDB_STATE_RUNNING) { + curcpu()->ci_ddb_paused = CI_DDB_INDDB; + mtx_leave(&ddb_mp_mutex); + return (1); + } else { + mtx_leave(&ddb_mp_mutex); + return (0); + } +} + +void +db_startcpu(int cpu) +{ + if (cpu != cpu_number() && cpu_info[cpu] != NULL) { + mtx_enter(&ddb_mp_mutex); + cpu_info[cpu]->ci_ddb_paused = CI_DDB_RUNNING; + mtx_leave(&ddb_mp_mutex); + } +} + +void +db_stopcpu(int cpu) +{ + 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; + mtx_leave(&ddb_mp_mutex); + arm_send_ipi(cpu_info[cpu], ARM_IPI_DDB); + } else { + mtx_leave(&ddb_mp_mutex); + } +} +#endif + struct db_command db_machine_command_table[] = { - { NULL, NULL, 0, NULL } +#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 } }; int @@ -278,14 +499,17 @@ extern vaddr_t end; void db_machine_init(void) { - /* - * We get called before malloc() is available, so supply a static - * struct undefined_handler. - */ - //db_uh.uh_handler = db_trapper; - //install_coproc_handler_static(0, &db_uh); +#ifdef MULTIPROCESSOR + int i; +#endif db_machine_commands_install(db_machine_command_table); +#ifdef MULTIPROCESSOR + for (i = 0; i < MAXCPUS; i++) { + if (cpu_info[i] != NULL) + cpu_info[i]->ci_ddb_paused = CI_DDB_RUNNING; + } +#endif } db_addr_t diff --git a/sys/arch/arm64/include/cpu.h b/sys/arch/arm64/include/cpu.h index 8c274035376..e2b177b0a7a 100644 --- a/sys/arch/arm64/include/cpu.h +++ b/sys/arch/arm64/include/cpu.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.h,v 1.6 2018/01/28 13:17:45 kettenis Exp $ */ +/* $OpenBSD: cpu.h,v 1.7 2018/01/30 15:46:12 kettenis Exp $ */ /* * Copyright (c) 2016 Dale Rahn * @@ -111,6 +111,14 @@ struct cpu_info { volatile int ci_flags; uint64_t ci_ttbr1; vaddr_t ci_el1_stkend; + + volatile int ci_ddb_paused; +#define CI_DDB_RUNNING 0 +#define CI_DDB_SHOULDSTOP 1 +#define CI_DDB_STOPPED 2 +#define CI_DDB_ENTERDDB 3 +#define CI_DDB_INDDB 4 + #endif #ifdef GPROF diff --git a/sys/arch/arm64/include/db_machdep.h b/sys/arch/arm64/include/db_machdep.h index 7f97a74eaf5..f8af409bea2 100644 --- a/sys/arch/arm64/include/db_machdep.h +++ b/sys/arch/arm64/include/db_machdep.h @@ -1,4 +1,4 @@ -/* $OpenBSD: db_machdep.h,v 1.1 2016/12/17 23:38:33 patrick Exp $ */ +/* $OpenBSD: db_machdep.h,v 1.2 2018/01/30 15:46:12 kettenis Exp $ */ /* $NetBSD: db_machdep.h,v 1.5 2001/11/22 18:00:00 thorpej Exp $ */ /* @@ -89,4 +89,8 @@ void db_machine_init (void); void db_show_frame_cmd(db_expr_t, int, db_expr_t, char *); +#define DDB_STATE_NOT_RUNNING 0 +#define DDB_STATE_RUNNING 1 +#define DDB_STATE_EXITING 2 + #endif /* _MACHINE_DB_MACHDEP_H_ */ -- 2.20.1