fiq_debugger: merge from linux 3.10
[firefly-linux-kernel-4.4.55.git] / drivers / staging / android / fiq_debugger / fiq_debugger.c
index 1d733624d70a2ad078b6592339a67bac9a91fb17..7eb678b6b3ebbb48ad3e80cee1e25eeb2efdc6fe 100644 (file)
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/wakelock.h>
+#include <linux/ptrace.h>
+
+#ifdef CONFIG_FIQ_DEBUGGER_TRUST_ZONE
+#include <linux/rockchip/rockchip_sip.h>
+#endif
 
 #ifdef CONFIG_FIQ_GLUE
 #include <asm/fiq_glue.h>
 #endif
 
+#ifdef CONFIG_FIQ_DEBUGGER_UART_OVERLAY
+#include <linux/of.h>
+#endif
+
 #include <linux/uaccess.h>
 
 #include "fiq_debugger.h"
 #include "fiq_debugger_ringbuf.h"
 
 #define DEBUG_MAX 64
+#define CMD_COUNT 0x0f
 #define MAX_UNHANDLED_FIQ_COUNT 1000000
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+#define MAX_FIQ_DEBUGGER_PORTS 1
+#else
 #define MAX_FIQ_DEBUGGER_PORTS 4
+#endif
 
 struct fiq_debugger_state {
 #ifdef CONFIG_FIQ_GLUE
@@ -72,6 +86,12 @@ struct fiq_debugger_state {
        char debug_buf[DEBUG_MAX];
        int debug_count;
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+       char cmd_buf[CMD_COUNT + 1][DEBUG_MAX];
+       int back_pointer;
+       int current_pointer;
+#endif
+
        bool no_sleep;
        bool debug_enable;
        bool ignore_next_wakeup_irq;
@@ -96,8 +116,13 @@ struct fiq_debugger_state {
        bool syslog_dumping;
 #endif
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+       unsigned int last_irqs[1024];
+       unsigned int last_local_irqs[NR_CPUS][32];
+#else
        unsigned int last_irqs[NR_IRQS];
        unsigned int last_local_timer_irqs[NR_CPUS];
+#endif
 };
 
 #ifdef CONFIG_FIQ_DEBUGGER_CONSOLE
@@ -118,12 +143,18 @@ static bool initial_debug_enable;
 static bool initial_console_enable;
 #endif
 
+#ifdef CONFIG_FIQ_DEBUGGER_TRUST_ZONE
+static struct fiq_debugger_state *state_tf;
+#endif
+
 static bool fiq_kgdb_enable;
+static bool fiq_debugger_disable;
 
 module_param_named(no_sleep, initial_no_sleep, bool, 0644);
 module_param_named(debug_enable, initial_debug_enable, bool, 0644);
 module_param_named(console_enable, initial_console_enable, bool, 0644);
 module_param_named(kgdb_enable, fiq_kgdb_enable, bool, 0644);
+module_param_named(disable, fiq_debugger_disable, bool, 0644);
 
 #ifdef CONFIG_FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON
 static inline
@@ -156,17 +187,21 @@ static inline bool fiq_debugger_have_fiq(struct fiq_debugger_state *state)
        return (state->fiq >= 0);
 }
 
-#ifdef CONFIG_FIQ_GLUE
+#if defined(CONFIG_FIQ_GLUE) || defined(CONFIG_FIQ_DEBUGGER_TRUST_ZONE)
 static void fiq_debugger_force_irq(struct fiq_debugger_state *state)
 {
        unsigned int irq = state->signal_irq;
 
        if (WARN_ON(!fiq_debugger_have_fiq(state)))
                return;
+       if (irq < 0)
+               return;
+
        if (state->pdata->force_irq) {
                state->pdata->force_irq(state->pdev, irq);
        } else {
                struct irq_chip *chip = irq_get_chip(irq);
+
                if (chip && chip->irq_retrigger)
                        chip->irq_retrigger(irq_get_irq_data(irq));
        }
@@ -408,6 +443,8 @@ static void fiq_debugger_work(struct work_struct *work)
 /* This function CANNOT be called in FIQ context */
 static void fiq_debugger_irq_exec(struct fiq_debugger_state *state, char *cmd)
 {
+       int invalid_cmd = 0;
+
        if (!strcmp(cmd, "ps"))
                fiq_debugger_do_ps(state);
        if (!strcmp(cmd, "sysrq"))
@@ -420,8 +457,51 @@ static void fiq_debugger_irq_exec(struct fiq_debugger_state *state, char *cmd)
 #endif
        if (!strncmp(cmd, "reboot", 6))
                fiq_debugger_schedule_work(state, cmd);
+#ifdef CONFIG_ARCH_ROCKCHIP
+       else {
+               invalid_cmd = 1;
+               memset(state->debug_buf, 0, DEBUG_MAX);
+       }
+
+       if (invalid_cmd == 0) {
+               state->current_pointer =
+                               (state->current_pointer - 1) & CMD_COUNT;
+               if (strcmp(state->cmd_buf[state->current_pointer], state->debug_buf)) {
+                       state->current_pointer =
+                               (state->current_pointer + 1) & CMD_COUNT;
+                       memset(state->cmd_buf[state->current_pointer], 0, DEBUG_MAX);
+                       strcpy(state->cmd_buf[state->current_pointer], state->debug_buf);
+               }
+               memset(state->debug_buf, 0, DEBUG_MAX);
+               state->current_pointer = (state->current_pointer + 1) & CMD_COUNT;
+               state->back_pointer = state->current_pointer;
+       }
+#endif
 }
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+static char cmd_buf[][16] = {
+               {"pc"},
+               {"regs"},
+               {"allregs"},
+               {"bt"},
+               {"reboot"},
+               {"irqs"},
+               {"kmsg"},
+               {"version"},
+               {"sleep"},
+               {"nosleep"},
+               {"console"},
+               {"cpu"},
+               {"ps"},
+               {"sysrq"},
+               {"reset"},
+#ifdef CONFIG_KGDB
+               {"kgdb"},
+#endif
+};
+#endif
+
 static void fiq_debugger_help(struct fiq_debugger_state *state)
 {
        fiq_debugger_printf(&state->output,
@@ -429,11 +509,11 @@ static void fiq_debugger_help(struct fiq_debugger_state *state)
                                " pc            PC status\n"
                                " regs          Register dump\n"
                                " allregs       Extended Register dump\n"
-                               " bt            Stack trace\n");
-       fiq_debugger_printf(&state->output,
+                               " bt            Stack trace\n"
                                " reboot [<c>]  Reboot with command <c>\n"
                                " reset [<c>]   Hard reset with command <c>\n"
-                               " irqs          Interupt status\n"
+                               " irqs          Interupt status\n");
+       fiq_debugger_printf(&state->output,
                                " kmsg          Kernel log\n"
                                " version       Kernel version\n");
        fiq_debugger_printf(&state->output,
@@ -459,15 +539,42 @@ static void fiq_debugger_take_affinity(void *info)
 
        cpumask_clear(&cpumask);
        cpumask_set_cpu(get_cpu(), &cpumask);
+       put_cpu();
 
        irq_set_affinity(state->uart_irq, &cpumask);
 }
 
 static void fiq_debugger_switch_cpu(struct fiq_debugger_state *state, int cpu)
 {
+       if (!cpu_online(cpu)) {
+               fiq_debugger_printf(&state->output, "cpu %d offline\n", cpu);
+               return;
+       }
+
        if (!fiq_debugger_have_fiq(state))
                smp_call_function_single(cpu, fiq_debugger_take_affinity, state,
                                false);
+#ifdef CONFIG_ARCH_ROCKCHIP
+       else {
+#ifdef CONFIG_FIQ_DEBUGGER_TRUST_ZONE
+               if (sip_fiq_debugger_is_enabled()) {
+                       if (state->pdata->switch_cpu) {
+                               state->pdata->switch_cpu(state->pdev, cpu);
+                               state->current_cpu = cpu;
+                       }
+                       return;
+               }
+#else
+               struct cpumask cpumask;
+
+               cpumask_clear(&cpumask);
+               cpumask_set_cpu(cpu, &cpumask);
+
+               irq_set_affinity(state->fiq, &cpumask);
+               irq_set_affinity(state->uart_irq, &cpumask);
+#endif
+       }
+#endif
        state->current_cpu = cpu;
 }
 
@@ -486,7 +593,11 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state,
        } else if (!strcmp(cmd, "allregs")) {
                fiq_debugger_dump_allregs(&state->output, regs);
        } else if (!strcmp(cmd, "bt")) {
-               fiq_debugger_dump_stacktrace(&state->output, regs, 100, svc_sp);
+               if (!user_mode((struct pt_regs *)regs))
+                       fiq_debugger_dump_stacktrace(&state->output, regs,
+                                                    100, svc_sp);
+               else
+                       fiq_debugger_printf(&state->output, "User mode\n");
        } else if (!strncmp(cmd, "reset", 5)) {
                cmd += 5;
                while (*cmd == ' ')
@@ -514,14 +625,21 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state,
                fiq_debugger_printf(&state->output, "console mode\n");
                fiq_debugger_uart_flush(state);
                state->console_enable = true;
+#ifdef CONFIG_FIQ_DEBUGGER_TRUST_ZONE
+               if (sip_fiq_debugger_is_enabled()) {
+                       if (state->pdata->enable_debug)
+                               state->pdata->enable_debug(state->pdev, false);
+               }
+#endif
        } else if (!strcmp(cmd, "cpu")) {
                fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu);
        } else if (!strncmp(cmd, "cpu ", 4)) {
                unsigned long cpu = 0;
-               if (strict_strtoul(cmd + 4, 10, &cpu) == 0)
+               if (kstrtoul(cmd + 4, 10, &cpu) == 0)
                        fiq_debugger_switch_cpu(state, cpu);
                else
                        fiq_debugger_printf(&state->output, "invalid cpu\n");
+
                fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu);
        } else {
                if (state->debug_busy) {
@@ -632,6 +750,105 @@ static int fiq_debugger_getc(struct fiq_debugger_state *state)
        return state->pdata->uart_getc(state->pdev);
 }
 
+static int fiq_debugger_cmd_check_back(struct fiq_debugger_state *state, char c)
+{
+       char *s;
+       int i = 0;
+
+       if (c == 'A') {
+               state->back_pointer = (state->back_pointer - 1) & CMD_COUNT;
+               if (state->back_pointer != state->current_pointer) {
+                       s = state->cmd_buf[state->back_pointer];
+                       if (*s != 0) {
+                               for (i = 0; i < strlen(state->debug_buf) - 1; i++) {
+                                       state->pdata->uart_putc(state->pdev, 8);
+                                       state->pdata->uart_putc(state->pdev, ' ');
+                                       state->pdata->uart_putc(state->pdev, 8);
+                               }
+                               memset(state->debug_buf, 0, DEBUG_MAX);
+                               strcpy(state->debug_buf, s);
+                               state->debug_count = strlen(state->debug_buf);
+                               fiq_debugger_printf(&state->output, state->debug_buf);
+                       } else {
+                               state->back_pointer = (state->back_pointer + 1) & CMD_COUNT;
+                       }
+
+               } else {
+                       state->back_pointer = (state->back_pointer + 1) & CMD_COUNT;
+               }
+       } else if (c == 'B') {
+               if (state->back_pointer != state->current_pointer) {
+                       state->back_pointer = (state->back_pointer + 1) & CMD_COUNT;
+                       if (state->back_pointer == state->current_pointer) {
+                               goto cmd_clear;
+                       } else {
+                               s = state->cmd_buf[state->back_pointer];
+                               if (*s != 0) {
+                                       for (i = 0; i < strlen(state->debug_buf) - 1; i++) {
+                                               state->pdata->uart_putc(state->pdev, 8);
+                                               state->pdata->uart_putc(state->pdev, ' ');
+                                               state->pdata->uart_putc(state->pdev, 8);
+                                       }
+                                       memset(state->debug_buf, 0, DEBUG_MAX);
+                                       strcpy(state->debug_buf, s);
+                                       state->debug_count = strlen(state->debug_buf);
+                                       fiq_debugger_printf(&state->output, state->debug_buf);
+                               }
+                       }
+               } else {
+cmd_clear:
+                       for (i = 0; i < strlen(state->debug_buf) - 1; i++) {
+                               state->pdata->uart_putc(state->pdev, 8);
+                               state->pdata->uart_putc(state->pdev, ' ');
+                               state->pdata->uart_putc(state->pdev, 8);
+                       }
+                       memset(state->debug_buf, 0, DEBUG_MAX);
+                       state->debug_count = 0;
+               }
+       }
+       return 0;
+}
+
+static void fiq_debugger_cmd_tab(struct fiq_debugger_state *state)
+{
+       int i, j;
+       int count = 0;
+
+       for (i = 0; i < ARRAY_SIZE(cmd_buf); i++)
+               cmd_buf[i][15] = 1;
+
+       for (j = 1; j <= strlen(state->debug_buf); j++) {
+               count = 0;
+               for (i = 0; i < ARRAY_SIZE(cmd_buf); i++) {
+                       if (cmd_buf[i][15] == 1) {
+                               if (strncmp(state->debug_buf, cmd_buf[i], j))
+                                       cmd_buf[i][15] = 0;
+                               else
+                                       count++;
+                       }
+               }
+               if (count == 0)
+                       break;
+       }
+
+       if (count == 1) {
+               for (i = 0; i < ARRAY_SIZE(cmd_buf); i++) {
+                       if (cmd_buf[i][15] == 1)
+                               break;
+               }
+
+               for (j = 0; j < strlen(state->debug_buf); j++) {
+                       state->pdata->uart_putc(state->pdev, 8);
+                       state->pdata->uart_putc(state->pdev, ' ');
+                       state->pdata->uart_putc(state->pdev, 8);
+               }
+               memset(state->debug_buf, 0, DEBUG_MAX);
+               strcpy(state->debug_buf, cmd_buf[i]);
+               state->debug_count = strlen(state->debug_buf);
+               fiq_debugger_printf(&state->output, state->debug_buf);
+       }
+}
+
 static bool fiq_debugger_handle_uart_interrupt(struct fiq_debugger_state *state,
                        int this_cpu, const struct pt_regs *regs, void *svc_sp)
 {
@@ -640,7 +857,8 @@ static bool fiq_debugger_handle_uart_interrupt(struct fiq_debugger_state *state,
        int count = 0;
        bool signal_helper = false;
 
-       if (this_cpu != state->current_cpu) {
+       if ((this_cpu != state->current_cpu) &&
+           (cpu_online(state->current_cpu))) {
                if (state->in_fiq)
                        return false;
 
@@ -658,6 +876,9 @@ static bool fiq_debugger_handle_uart_interrupt(struct fiq_debugger_state *state,
                return false;
        }
 
+       if (this_cpu != state->current_cpu)
+               state->current_cpu = this_cpu;
+
        state->in_fiq = true;
 
        while ((c = fiq_debugger_getc(state)) != FIQ_DEBUGGER_NO_CHAR) {
@@ -670,13 +891,49 @@ static bool fiq_debugger_handle_uart_interrupt(struct fiq_debugger_state *state,
                        }
                } else if (c == FIQ_DEBUGGER_BREAK) {
                        state->console_enable = false;
-                       fiq_debugger_puts(state, "fiq debugger mode\n");
+#ifdef CONFIG_ARCH_ROCKCHIP
+                       fiq_debugger_puts(state, "\nWelcome to ");
+#endif
+                       if (fiq_debugger_have_fiq(state))
+                               fiq_debugger_puts(state,
+                                                 "fiq debugger mode\n");
+                       else
+                               fiq_debugger_puts(state,
+                                                 "irq debugger mode\n");
                        state->debug_count = 0;
+#ifdef CONFIG_ARCH_ROCKCHIP
+                       fiq_debugger_puts(state, "Enter ? to get command help\n");
+                       state->back_pointer = CMD_COUNT;
+                       state->current_pointer = CMD_COUNT;
+                       memset(state->cmd_buf, 0, (CMD_COUNT + 1) * DEBUG_MAX);
+#endif
+
+#ifdef CONFIG_FIQ_DEBUGGER_TRUST_ZONE
+                       if (sip_fiq_debugger_is_enabled()) {
+                               if (state->pdata->enable_debug)
+                                       state->pdata->enable_debug(state->pdev,
+                                                                  true);
+                       }
+#endif
                        fiq_debugger_prompt(state);
+                       fiq_debugger_ringbuf_push(state->tty_rbuf, 8);
+                       fiq_debugger_ringbuf_push(state->tty_rbuf, 8);
 #ifdef CONFIG_FIQ_DEBUGGER_CONSOLE
                } else if (state->console_enable && state->tty_rbuf) {
                        fiq_debugger_ringbuf_push(state->tty_rbuf, c);
                        signal_helper = true;
+#endif
+#ifdef CONFIG_ARCH_ROCKCHIP
+               } else if (last_c == '[' && (c == 'A' || c == 'B' || c == 'C' || c == 'D')) {
+                       if (state->debug_count > 0) {
+                               state->debug_count--;
+                               state->pdata->uart_putc(state->pdev, 8);
+                               state->pdata->uart_putc(state->pdev, ' ');
+                               state->pdata->uart_putc(state->pdev, 8);
+                       }
+                       fiq_debugger_cmd_check_back(state, c);
+               } else if (c == 9) {
+                       fiq_debugger_cmd_tab(state);
 #endif
                } else if ((c >= ' ') && (c < 127)) {
                        if (state->debug_count < (DEBUG_MAX - 1)) {
@@ -702,6 +959,23 @@ static bool fiq_debugger_handle_uart_interrupt(struct fiq_debugger_state *state,
                                        fiq_debugger_fiq_exec(state,
                                                        state->debug_buf,
                                                        regs, svc_sp);
+#ifdef CONFIG_ARCH_ROCKCHIP
+                               if (signal_helper == false) {
+                                       state->current_pointer =
+                                                       (state->current_pointer - 1) & CMD_COUNT;
+                                       if (strcmp(state->cmd_buf[state->current_pointer], state->debug_buf)) {
+                                               state->current_pointer =
+                                                       (state->current_pointer + 1) & CMD_COUNT;
+                                               memset(state->cmd_buf[state->current_pointer], 0, DEBUG_MAX);
+                                               strcpy(state->cmd_buf[state->current_pointer], state->debug_buf);
+                                       }
+                                       memset(state->debug_buf, 0, DEBUG_MAX);
+                                       state->current_pointer =
+                                               (state->current_pointer + 1) & CMD_COUNT;
+                                       state->back_pointer =
+                                               state->current_pointer;
+                               }
+#endif
                        } else {
                                fiq_debugger_prompt(state);
                        }
@@ -739,6 +1013,27 @@ static void fiq_debugger_fiq(struct fiq_glue_handler *h,
 }
 #endif
 
+#ifdef CONFIG_FIQ_DEBUGGER_TRUST_ZONE
+void fiq_debugger_fiq(void *regs, u32 cpu)
+{
+       struct fiq_debugger_state *state = state_tf;
+       bool need_irq;
+
+       if (!state)
+               return;
+
+       if (!user_mode((struct pt_regs *)regs))
+               need_irq = fiq_debugger_handle_uart_interrupt(state,
+                                               smp_processor_id(),
+                                               regs, current_thread_info());
+       else
+               need_irq = fiq_debugger_handle_uart_interrupt(state, cpu,
+                                               regs, current_thread_info());
+       if (need_irq)
+               fiq_debugger_force_irq(state);
+}
+#endif
+
 /*
  * When not using FIQs, we only use this single interrupt as an entry point.
  * This just effectively takes over the UART interrupt and does all the work
@@ -806,6 +1101,13 @@ static void fiq_debugger_console_write(struct console *co,
        if (!state->console_enable && !state->syslog_dumping)
                return;
 
+#ifdef CONFIG_RK_CONSOLE_THREAD
+       if (state->pdata->console_write) {
+               state->pdata->console_write(state->pdev, s, count);
+               return;
+       }
+#endif
+
        fiq_debugger_uart_enable(state);
        spin_lock_irqsave(&state->console_lock, flags);
        while (count--) {
@@ -1105,8 +1407,13 @@ static int fiq_debugger_probe(struct platform_device *pdev)
                                "<hit enter %sto activate fiq debugger>\n",
                                state->no_sleep ? "" : "twice ");
 
-#ifdef CONFIG_FIQ_GLUE
        if (fiq_debugger_have_fiq(state)) {
+#ifdef CONFIG_FIQ_GLUE
+#ifdef CONFIG_FIQ_DEBUGGER_TRUST_ZONE
+               if (sip_fiq_debugger_is_enabled()) {
+               } else
+#endif
+               {
                state->handler.fiq = fiq_debugger_fiq;
                state->handler.resume = fiq_debugger_resume;
                ret = fiq_glue_register_handler(&state->handler);
@@ -1114,11 +1421,19 @@ static int fiq_debugger_probe(struct platform_device *pdev)
                        pr_err("%s: could not install fiq handler\n", __func__);
                        goto err_register_irq;
                }
-
+#ifdef CONFIG_ARCH_ROCKCHIP
+               /* set state->fiq to secure state, so fiq is available */
+               gic_set_irq_secure(irq_get_irq_data(state->fiq));
+               /*
+               * set state->fiq priority a little higher than other
+               * interrupts (normal is 0xa0)
+               */
+               gic_set_irq_priority(irq_get_irq_data(state->fiq), 0x90);
+#endif
                pdata->fiq_enable(pdev, state->fiq, 1);
-       } else
+               }
 #endif
-       {
+       } else {
                ret = request_irq(state->uart_irq, fiq_debugger_uart_irq,
                                  IRQF_NO_SUSPEND, "debug", state);
                if (ret) {
@@ -1145,7 +1460,7 @@ static int fiq_debugger_probe(struct platform_device *pdev)
        if (state->wakeup_irq >= 0) {
                ret = request_irq(state->wakeup_irq,
                                  fiq_debugger_wakeup_irq_handler,
-                                 IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+                                 IRQF_TRIGGER_FALLING,
                                  "debug-wakeup", state);
                if (ret) {
                        pr_err("serial_debugger: "
@@ -1163,6 +1478,10 @@ static int fiq_debugger_probe(struct platform_device *pdev)
        if (state->no_sleep)
                fiq_debugger_handle_wakeup(state);
 
+#ifdef CONFIG_FIQ_DEBUGGER_TRUST_ZONE
+       state_tf = state;
+#endif
+
 #if defined(CONFIG_FIQ_DEBUGGER_CONSOLE)
        spin_lock_init(&state->console_lock);
        state->console = fiq_debugger_console;
@@ -1202,10 +1521,40 @@ static struct platform_driver fiq_debugger_driver = {
        },
 };
 
+#if defined(CONFIG_FIQ_DEBUGGER_UART_OVERLAY)
+int fiq_debugger_uart_overlay(void)
+{
+       struct device_node *onp = of_find_node_by_path("/uart_overlay@0");
+       int ret;
+
+       if (!onp) {
+               pr_err("serial_debugger: uart overlay not found\n");
+               return -ENODEV;
+       }
+
+       ret = of_overlay_create(onp);
+       if (ret < 0) {
+               pr_err("serial_debugger: fail to create overlay: %d\n", ret);
+               of_node_put(onp);
+               return ret;
+       }
+
+       pr_info("serial_debugger: uart overlay applied\n");
+       return 0;
+}
+#endif
+
 static int __init fiq_debugger_init(void)
 {
+       if (fiq_debugger_disable) {
+               pr_err("serial_debugger: disabled\n");
+               return -ENODEV;
+       }
 #if defined(CONFIG_FIQ_DEBUGGER_CONSOLE)
        fiq_debugger_tty_init();
+#endif
+#if defined(CONFIG_FIQ_DEBUGGER_UART_OVERLAY)
+       fiq_debugger_uart_overlay();
 #endif
        return platform_driver_register(&fiq_debugger_driver);
 }