linux/arch/ia64/kernel/crash.c
<<
>>
Prefs
   1/*
   2 * arch/ia64/kernel/crash.c
   3 *
   4 * Architecture specific (ia64) functions for kexec based crash dumps.
   5 *
   6 * Created by: Khalid Aziz <khalid.aziz@hp.com>
   7 * Copyright (C) 2005 Hewlett-Packard Development Company, L.P.
   8 * Copyright (C) 2005 Intel Corp        Zou Nan hai <nanhai.zou@intel.com>
   9 *
  10 */
  11#include <linux/smp.h>
  12#include <linux/delay.h>
  13#include <linux/crash_dump.h>
  14#include <linux/bootmem.h>
  15#include <linux/kexec.h>
  16#include <linux/elfcore.h>
  17#include <linux/sysctl.h>
  18#include <linux/init.h>
  19#include <linux/kdebug.h>
  20
  21#include <asm/mca.h>
  22
  23int kdump_status[NR_CPUS];
  24static atomic_t kdump_cpu_frozen;
  25atomic_t kdump_in_progress;
  26static int kdump_on_init = 1;
  27
  28static inline Elf64_Word
  29*append_elf_note(Elf64_Word *buf, char *name, unsigned type, void *data,
  30                size_t data_len)
  31{
  32        struct elf_note *note = (struct elf_note *)buf;
  33        note->n_namesz = strlen(name) + 1;
  34        note->n_descsz = data_len;
  35        note->n_type   = type;
  36        buf += (sizeof(*note) + 3)/4;
  37        memcpy(buf, name, note->n_namesz);
  38        buf += (note->n_namesz + 3)/4;
  39        memcpy(buf, data, data_len);
  40        buf += (data_len + 3)/4;
  41        return buf;
  42}
  43
  44static void
  45final_note(void *buf)
  46{
  47        memset(buf, 0, sizeof(struct elf_note));
  48}
  49
  50extern void ia64_dump_cpu_regs(void *);
  51
  52static DEFINE_PER_CPU(struct elf_prstatus, elf_prstatus);
  53
  54void
  55crash_save_this_cpu(void)
  56{
  57        void *buf;
  58        unsigned long cfm, sof, sol;
  59
  60        int cpu = smp_processor_id();
  61        struct elf_prstatus *prstatus = &per_cpu(elf_prstatus, cpu);
  62
  63        elf_greg_t *dst = (elf_greg_t *)&(prstatus->pr_reg);
  64        memset(prstatus, 0, sizeof(*prstatus));
  65        prstatus->pr_pid = current->pid;
  66
  67        ia64_dump_cpu_regs(dst);
  68        cfm = dst[43];
  69        sol = (cfm >> 7) & 0x7f;
  70        sof = cfm & 0x7f;
  71        dst[46] = (unsigned long)ia64_rse_skip_regs((unsigned long *)dst[46],
  72                        sof - sol);
  73
  74        buf = (u64 *) per_cpu_ptr(crash_notes, cpu);
  75        if (!buf)
  76                return;
  77        buf = append_elf_note(buf, KEXEC_CORE_NOTE_NAME, NT_PRSTATUS, prstatus,
  78                        sizeof(*prstatus));
  79        final_note(buf);
  80}
  81
  82#ifdef CONFIG_SMP
  83static int
  84kdump_wait_cpu_freeze(void)
  85{
  86        int cpu_num = num_online_cpus() - 1;
  87        int timeout = 1000;
  88        while(timeout-- > 0) {
  89                if (atomic_read(&kdump_cpu_frozen) == cpu_num)
  90                        return 0;
  91                udelay(1000);
  92        }
  93        return 1;
  94}
  95#endif
  96
  97void
  98machine_crash_shutdown(struct pt_regs *pt)
  99{
 100        /* This function is only called after the system
 101         * has paniced or is otherwise in a critical state.
 102         * The minimum amount of code to allow a kexec'd kernel
 103         * to run successfully needs to happen here.
 104         *
 105         * In practice this means shooting down the other cpus in
 106         * an SMP system.
 107         */
 108        kexec_disable_iosapic();
 109#ifdef CONFIG_SMP
 110        kdump_smp_send_stop();
 111        /* not all cpu response to IPI, send INIT to freeze them */
 112        if (kdump_wait_cpu_freeze() && kdump_on_init)   {
 113                kdump_smp_send_init();
 114        }
 115#endif
 116}
 117
 118static void
 119machine_kdump_on_init(void)
 120{
 121        local_irq_disable();
 122        kexec_disable_iosapic();
 123        machine_kexec(ia64_kimage);
 124}
 125
 126void
 127kdump_cpu_freeze(struct unw_frame_info *info, void *arg)
 128{
 129        int cpuid;
 130        local_irq_disable();
 131        cpuid = smp_processor_id();
 132        crash_save_this_cpu();
 133        current->thread.ksp = (__u64)info->sw - 16;
 134        atomic_inc(&kdump_cpu_frozen);
 135        kdump_status[cpuid] = 1;
 136        mb();
 137#ifdef CONFIG_HOTPLUG_CPU
 138        if (cpuid != 0)
 139                ia64_jump_to_sal(&sal_boot_rendez_state[cpuid]);
 140#endif
 141        for (;;)
 142                cpu_relax();
 143}
 144
 145static int
 146kdump_init_notifier(struct notifier_block *self, unsigned long val, void *data)
 147{
 148        struct ia64_mca_notify_die *nd;
 149        struct die_args *args = data;
 150
 151        if (!kdump_on_init)
 152                return NOTIFY_DONE;
 153
 154        if (!ia64_kimage) {
 155                if (val == DIE_INIT_MONARCH_LEAVE)
 156                        ia64_mca_printk(KERN_NOTICE
 157                                        "%s: kdump not configured\n",
 158                                        __FUNCTION__);
 159                return NOTIFY_DONE;
 160        }
 161
 162        if (val != DIE_INIT_MONARCH_LEAVE &&
 163            val != DIE_INIT_SLAVE_LEAVE &&
 164            val != DIE_INIT_MONARCH_PROCESS &&
 165            val != DIE_MCA_RENDZVOUS_LEAVE &&
 166            val != DIE_MCA_MONARCH_LEAVE)
 167                return NOTIFY_DONE;
 168
 169        nd = (struct ia64_mca_notify_die *)args->err;
 170        /* Reason code 1 means machine check rendezvous*/
 171        if ((val == DIE_INIT_MONARCH_LEAVE || val == DIE_INIT_SLAVE_LEAVE
 172            || val == DIE_INIT_MONARCH_PROCESS) && nd->sos->rv_rc == 1)
 173                return NOTIFY_DONE;
 174
 175        switch (val) {
 176                case DIE_INIT_MONARCH_PROCESS:
 177                        atomic_set(&kdump_in_progress, 1);
 178                        *(nd->monarch_cpu) = -1;
 179                        break;
 180                case DIE_INIT_MONARCH_LEAVE:
 181                        machine_kdump_on_init();
 182                        break;
 183                case DIE_INIT_SLAVE_LEAVE:
 184                        if (atomic_read(&kdump_in_progress))
 185                                unw_init_running(kdump_cpu_freeze, NULL);
 186                        break;
 187                case DIE_MCA_RENDZVOUS_LEAVE:
 188                        if (atomic_read(&kdump_in_progress))
 189                                unw_init_running(kdump_cpu_freeze, NULL);
 190                        break;
 191                case DIE_MCA_MONARCH_LEAVE:
 192                     /* die_register->signr indicate if MCA is recoverable */
 193                        if (!args->signr)
 194                                machine_kdump_on_init();
 195                        break;
 196        }
 197        return NOTIFY_DONE;
 198}
 199
 200#ifdef CONFIG_SYSCTL
 201static ctl_table kdump_on_init_table[] = {
 202        {
 203                .ctl_name = CTL_UNNUMBERED,
 204                .procname = "kdump_on_init",
 205                .data = &kdump_on_init,
 206                .maxlen = sizeof(int),
 207                .mode = 0644,
 208                .proc_handler = &proc_dointvec,
 209        },
 210        { .ctl_name = 0 }
 211};
 212
 213static ctl_table sys_table[] = {
 214        {
 215          .ctl_name = CTL_KERN,
 216          .procname = "kernel",
 217          .mode = 0555,
 218          .child = kdump_on_init_table,
 219        },
 220        { .ctl_name = 0 }
 221};
 222#endif
 223
 224static int
 225machine_crash_setup(void)
 226{
 227        /* be notified before default_monarch_init_process */
 228        static struct notifier_block kdump_init_notifier_nb = {
 229                .notifier_call = kdump_init_notifier,
 230                .priority = 1,
 231        };
 232        int ret;
 233        if((ret = register_die_notifier(&kdump_init_notifier_nb)) != 0)
 234                return ret;
 235#ifdef CONFIG_SYSCTL
 236        register_sysctl_table(sys_table);
 237#endif
 238        return 0;
 239}
 240
 241__initcall(machine_crash_setup);
 242
 243