diff options
| author | Ralf Baechle <ralf@linux-mips.org> | 2012-10-25 10:23:31 -0400 |
|---|---|---|
| committer | Ralf Baechle <ralf@linux-mips.org> | 2012-12-13 11:00:39 -0500 |
| commit | abe77f90dc9c65a7c9a4d61c2cbb8db4d5566e4f (patch) | |
| tree | 69fd0cfe31a9041e8e1a881829c011892075ea2c | |
| parent | 7aa1c8f47e7e792d11f898cbdddaf6fa21ff08cc (diff) | |
MIPS: Octeon: Add kexec and kdump support
[ralf@linux-mips.org: Original patch by Maxim Uvarov <muvarov@gmail.com>
with plenty of further shining, polishing, debugging and testing by me.]
Signed-off-by: Maxim Uvarov <muvarov@gmail.com>
Cc: linux-mips@linux-mips.org
Cc: kexec@lists.infradead.org
Cc: horms@verge.net.au
Patchwork: https://patchwork.linux-mips.org/patch/1026/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
| -rw-r--r-- | arch/mips/Makefile | 4 | ||||
| -rw-r--r-- | arch/mips/cavium-octeon/executive/cvmx-bootmem.c | 5 | ||||
| -rw-r--r-- | arch/mips/cavium-octeon/setup.c | 312 | ||||
| -rw-r--r-- | arch/mips/include/asm/octeon/cvmx-bootmem.h | 2 | ||||
| -rw-r--r-- | arch/mips/kernel/crash_dump.c | 2 | ||||
| -rw-r--r-- | arch/mips/kernel/relocate_kernel.S | 19 |
6 files changed, 335 insertions, 9 deletions
diff --git a/arch/mips/Makefile b/arch/mips/Makefile index 654b1ad39f0..033395ec82f 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile | |||
| @@ -192,6 +192,10 @@ endif | |||
| 192 | # | 192 | # |
| 193 | include $(srctree)/arch/mips/Kbuild.platforms | 193 | include $(srctree)/arch/mips/Kbuild.platforms |
| 194 | 194 | ||
| 195 | ifdef CONFIG_PHYSICAL_START | ||
| 196 | load-y = $(CONFIG_PHYSICAL_START) | ||
| 197 | endif | ||
| 198 | |||
| 195 | cflags-y += -I$(srctree)/arch/mips/include/asm/mach-generic | 199 | cflags-y += -I$(srctree)/arch/mips/include/asm/mach-generic |
| 196 | drivers-$(CONFIG_PCI) += arch/mips/pci/ | 200 | drivers-$(CONFIG_PCI) += arch/mips/pci/ |
| 197 | 201 | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-bootmem.c b/arch/mips/cavium-octeon/executive/cvmx-bootmem.c index fdf5f19bfdb..6d5ddbc112c 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-bootmem.c +++ b/arch/mips/cavium-octeon/executive/cvmx-bootmem.c | |||
| @@ -688,3 +688,8 @@ int64_t cvmx_bootmem_phy_named_block_alloc(uint64_t size, uint64_t min_addr, | |||
| 688 | cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock)); | 688 | cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock)); |
| 689 | return addr_allocated; | 689 | return addr_allocated; |
| 690 | } | 690 | } |
| 691 | |||
| 692 | struct cvmx_bootmem_desc *cvmx_bootmem_get_desc(void) | ||
| 693 | { | ||
| 694 | return cvmx_bootmem_desc; | ||
| 695 | } | ||
diff --git a/arch/mips/cavium-octeon/setup.c b/arch/mips/cavium-octeon/setup.c index 67aa3b942f0..7c2b7aab77b 100644 --- a/arch/mips/cavium-octeon/setup.c +++ b/arch/mips/cavium-octeon/setup.c | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #include <linux/serial_8250.h> | 25 | #include <linux/serial_8250.h> |
| 26 | #include <linux/of_fdt.h> | 26 | #include <linux/of_fdt.h> |
| 27 | #include <linux/libfdt.h> | 27 | #include <linux/libfdt.h> |
| 28 | #include <linux/kexec.h> | ||
| 28 | 29 | ||
| 29 | #include <asm/processor.h> | 30 | #include <asm/processor.h> |
| 30 | #include <asm/reboot.h> | 31 | #include <asm/reboot.h> |
| @@ -58,11 +59,208 @@ struct octeon_boot_descriptor *octeon_boot_desc_ptr; | |||
| 58 | struct cvmx_bootinfo *octeon_bootinfo; | 59 | struct cvmx_bootinfo *octeon_bootinfo; |
| 59 | EXPORT_SYMBOL(octeon_bootinfo); | 60 | EXPORT_SYMBOL(octeon_bootinfo); |
| 60 | 61 | ||
| 62 | static unsigned long long RESERVE_LOW_MEM = 0ull; | ||
| 63 | #ifdef CONFIG_KEXEC | ||
| 64 | #ifdef CONFIG_SMP | ||
| 65 | /* | ||
| 66 | * Wait for relocation code is prepared and send | ||
| 67 | * secondary CPUs to spin until kernel is relocated. | ||
| 68 | */ | ||
| 69 | static void octeon_kexec_smp_down(void *ignored) | ||
| 70 | { | ||
| 71 | int cpu = smp_processor_id(); | ||
| 72 | |||
| 73 | local_irq_disable(); | ||
| 74 | set_cpu_online(cpu, false); | ||
| 75 | while (!atomic_read(&kexec_ready_to_reboot)) | ||
| 76 | cpu_relax(); | ||
| 77 | |||
| 78 | asm volatile ( | ||
| 79 | " sync \n" | ||
| 80 | " synci ($0) \n"); | ||
| 81 | |||
| 82 | relocated_kexec_smp_wait(NULL); | ||
| 83 | } | ||
| 84 | #endif | ||
| 85 | |||
| 86 | #define OCTEON_DDR0_BASE (0x0ULL) | ||
| 87 | #define OCTEON_DDR0_SIZE (0x010000000ULL) | ||
| 88 | #define OCTEON_DDR1_BASE (0x410000000ULL) | ||
| 89 | #define OCTEON_DDR1_SIZE (0x010000000ULL) | ||
| 90 | #define OCTEON_DDR2_BASE (0x020000000ULL) | ||
| 91 | #define OCTEON_DDR2_SIZE (0x3e0000000ULL) | ||
| 92 | #define OCTEON_MAX_PHY_MEM_SIZE (16*1024*1024*1024ULL) | ||
| 93 | |||
| 94 | static struct kimage *kimage_ptr; | ||
| 95 | |||
| 96 | static void kexec_bootmem_init(uint64_t mem_size, uint32_t low_reserved_bytes) | ||
| 97 | { | ||
| 98 | int64_t addr; | ||
| 99 | struct cvmx_bootmem_desc *bootmem_desc; | ||
| 100 | |||
| 101 | bootmem_desc = cvmx_bootmem_get_desc(); | ||
| 102 | |||
| 103 | if (mem_size > OCTEON_MAX_PHY_MEM_SIZE) { | ||
| 104 | mem_size = OCTEON_MAX_PHY_MEM_SIZE; | ||
| 105 | pr_err("Error: requested memory too large," | ||
| 106 | "truncating to maximum size\n"); | ||
| 107 | } | ||
| 108 | |||
| 109 | bootmem_desc->major_version = CVMX_BOOTMEM_DESC_MAJ_VER; | ||
| 110 | bootmem_desc->minor_version = CVMX_BOOTMEM_DESC_MIN_VER; | ||
| 111 | |||
| 112 | addr = (OCTEON_DDR0_BASE + RESERVE_LOW_MEM + low_reserved_bytes); | ||
| 113 | bootmem_desc->head_addr = 0; | ||
| 114 | |||
| 115 | if (mem_size <= OCTEON_DDR0_SIZE) { | ||
| 116 | __cvmx_bootmem_phy_free(addr, | ||
| 117 | mem_size - RESERVE_LOW_MEM - | ||
| 118 | low_reserved_bytes, 0); | ||
| 119 | return; | ||
| 120 | } | ||
| 121 | |||
| 122 | __cvmx_bootmem_phy_free(addr, | ||
| 123 | OCTEON_DDR0_SIZE - RESERVE_LOW_MEM - | ||
| 124 | low_reserved_bytes, 0); | ||
| 125 | |||
| 126 | mem_size -= OCTEON_DDR0_SIZE; | ||
| 127 | |||
| 128 | if (mem_size > OCTEON_DDR1_SIZE) { | ||
| 129 | __cvmx_bootmem_phy_free(OCTEON_DDR1_BASE, OCTEON_DDR1_SIZE, 0); | ||
| 130 | __cvmx_bootmem_phy_free(OCTEON_DDR2_BASE, | ||
| 131 | mem_size - OCTEON_DDR1_SIZE, 0); | ||
| 132 | } else | ||
| 133 | __cvmx_bootmem_phy_free(OCTEON_DDR1_BASE, mem_size, 0); | ||
| 134 | } | ||
| 135 | |||
| 136 | static int octeon_kexec_prepare(struct kimage *image) | ||
| 137 | { | ||
| 138 | int i; | ||
| 139 | char *bootloader = "kexec"; | ||
| 140 | |||
| 141 | octeon_boot_desc_ptr->argc = 0; | ||
| 142 | for (i = 0; i < image->nr_segments; i++) { | ||
| 143 | if (!strncmp(bootloader, (char *)image->segment[i].buf, | ||
| 144 | strlen(bootloader))) { | ||
| 145 | /* | ||
| 146 | * convert command line string to array | ||
| 147 | * of parameters (as bootloader does). | ||
| 148 | */ | ||
| 149 | int argc = 0, offt; | ||
| 150 | char *str = (char *)image->segment[i].buf; | ||
| 151 | char *ptr = strchr(str, ' '); | ||
| 152 | while (ptr && (OCTEON_ARGV_MAX_ARGS > argc)) { | ||
| 153 | *ptr = '\0'; | ||
| 154 | if (ptr[1] != ' ') { | ||
| 155 | offt = (int)(ptr - str + 1); | ||
| 156 | octeon_boot_desc_ptr->argv[argc] = | ||
| 157 | image->segment[i].mem + offt; | ||
| 158 | argc++; | ||
| 159 | } | ||
| 160 | ptr = strchr(ptr + 1, ' '); | ||
| 161 | } | ||
| 162 | octeon_boot_desc_ptr->argc = argc; | ||
| 163 | break; | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | /* | ||
| 168 | * Information about segments will be needed during pre-boot memory | ||
| 169 | * initialization. | ||
| 170 | */ | ||
| 171 | kimage_ptr = image; | ||
| 172 | return 0; | ||
| 173 | } | ||
| 174 | |||
| 175 | static void octeon_generic_shutdown(void) | ||
| 176 | { | ||
| 177 | int cpu, i; | ||
| 178 | struct cvmx_bootmem_desc *bootmem_desc; | ||
| 179 | void *named_block_array_ptr; | ||
| 180 | |||
| 181 | bootmem_desc = cvmx_bootmem_get_desc(); | ||
| 182 | named_block_array_ptr = | ||
| 183 | cvmx_phys_to_ptr(bootmem_desc->named_block_array_addr); | ||
| 184 | |||
| 185 | #ifdef CONFIG_SMP | ||
| 186 | /* disable watchdogs */ | ||
| 187 | for_each_online_cpu(cpu) | ||
| 188 | cvmx_write_csr(CVMX_CIU_WDOGX(cpu_logical_map(cpu)), 0); | ||
| 189 | #else | ||
| 190 | cvmx_write_csr(CVMX_CIU_WDOGX(cvmx_get_core_num()), 0); | ||
| 191 | #endif | ||
| 192 | if (kimage_ptr != kexec_crash_image) { | ||
| 193 | memset(named_block_array_ptr, | ||
| 194 | 0x0, | ||
| 195 | CVMX_BOOTMEM_NUM_NAMED_BLOCKS * | ||
| 196 | sizeof(struct cvmx_bootmem_named_block_desc)); | ||
| 197 | /* | ||
| 198 | * Mark all memory (except low 0x100000 bytes) as free. | ||
| 199 | * It is the same thing that bootloader does. | ||
| 200 | */ | ||
| 201 | kexec_bootmem_init(octeon_bootinfo->dram_size*1024ULL*1024ULL, | ||
| 202 | 0x100000); | ||
| 203 | /* | ||
| 204 | * Allocate all segments to avoid their corruption during boot. | ||
| 205 | */ | ||
| 206 | for (i = 0; i < kimage_ptr->nr_segments; i++) | ||
| 207 | cvmx_bootmem_alloc_address( | ||
| 208 | kimage_ptr->segment[i].memsz + 2*PAGE_SIZE, | ||
| 209 | kimage_ptr->segment[i].mem - PAGE_SIZE, | ||
| 210 | PAGE_SIZE); | ||
| 211 | } else { | ||
| 212 | /* | ||
| 213 | * Do not mark all memory as free. Free only named sections | ||
| 214 | * leaving the rest of memory unchanged. | ||
| 215 | */ | ||
| 216 | struct cvmx_bootmem_named_block_desc *ptr = | ||
| 217 | (struct cvmx_bootmem_named_block_desc *) | ||
