Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | // SPDX-License-Identifier: GPL-2.0-or-later /* * * Copyright (C) 2007 Lemote, Inc. & Institute of Computing Technology * Author: Fuxin Zhang, zhangfx@lemote.com * Copyright (C) 2009 Lemote, Inc. * Author: Zhangjin Wu, wuzhangjin@gmail.com */ #include <linux/cpu.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/kexec.h> #include <linux/pm.h> #include <linux/slab.h> #include <asm/bootinfo.h> #include <asm/idle.h> #include <asm/reboot.h> #include <loongson.h> #include <boot_param.h> static void loongson_restart(char *command) { void (*fw_restart)(void) = (void *)loongson_sysconf.restart_addr; fw_restart(); while (1) { if (cpu_wait) cpu_wait(); } } static void loongson_poweroff(void) { void (*fw_poweroff)(void) = (void *)loongson_sysconf.poweroff_addr; fw_poweroff(); while (1) { if (cpu_wait) cpu_wait(); } } static void loongson_halt(void) { pr_notice("\n\n** You can safely turn off the power now **\n\n"); while (1) { if (cpu_wait) cpu_wait(); } } #ifdef CONFIG_KEXEC /* 0X80000000~0X80200000 is safe */ #define MAX_ARGS 64 #define KEXEC_CTRL_CODE 0xFFFFFFFF80100000UL #define KEXEC_ARGV_ADDR 0xFFFFFFFF80108000UL #define KEXEC_ARGV_SIZE COMMAND_LINE_SIZE #define KEXEC_ENVP_SIZE 4800 static int kexec_argc; static int kdump_argc; static void *kexec_argv; static void *kdump_argv; static void *kexec_envp; static int loongson_kexec_prepare(struct kimage *image) { int i, argc = 0; unsigned int *argv; char *str, *ptr, *bootloader = "kexec"; /* argv at offset 0, argv[] at offset KEXEC_ARGV_SIZE/2 */ if (image->type == KEXEC_TYPE_DEFAULT) argv = (unsigned int *)kexec_argv; else argv = (unsigned int *)kdump_argv; argv[argc++] = (unsigned int)(KEXEC_ARGV_ADDR + KEXEC_ARGV_SIZE/2); for (i = 0; i < image->nr_segments; i++) { if (!strncmp(bootloader, (char *)image->segment[i].buf, strlen(bootloader))) { /* * convert command line string to array * of parameters (as bootloader does). */ int offt; str = (char *)argv + KEXEC_ARGV_SIZE/2; memcpy(str, image->segment[i].buf, KEXEC_ARGV_SIZE/2); ptr = strchr(str, ' '); while (ptr && (argc < MAX_ARGS)) { *ptr = '\0'; if (ptr[1] != ' ') { offt = (int)(ptr - str + 1); argv[argc] = KEXEC_ARGV_ADDR + KEXEC_ARGV_SIZE/2 + offt; argc++; } ptr = strchr(ptr + 1, ' '); } break; } } if (image->type == KEXEC_TYPE_DEFAULT) kexec_argc = argc; else kdump_argc = argc; /* kexec/kdump need a safe page to save reboot_code_buffer */ image->control_code_page = virt_to_page((void *)KEXEC_CTRL_CODE); return 0; } static void loongson_kexec_shutdown(void) { #ifdef CONFIG_SMP int cpu; /* All CPUs go to reboot_code_buffer */ for_each_possible_cpu(cpu) if (!cpu_online(cpu)) cpu_device_up(get_cpu_device(cpu)); secondary_kexec_args[0] = TO_UNCAC(0x3ff01000); #endif kexec_args[0] = kexec_argc; kexec_args[1] = fw_arg1; kexec_args[2] = fw_arg2; memcpy((void *)fw_arg1, kexec_argv, KEXEC_ARGV_SIZE); memcpy((void *)fw_arg2, kexec_envp, KEXEC_ENVP_SIZE); } static void loongson_crash_shutdown(struct pt_regs *regs) { default_machine_crash_shutdown(regs); kexec_args[0] = kdump_argc; kexec_args[1] = fw_arg1; kexec_args[2] = fw_arg2; #ifdef CONFIG_SMP secondary_kexec_args[0] = TO_UNCAC(0x3ff01000); #endif memcpy((void *)fw_arg1, kdump_argv, KEXEC_ARGV_SIZE); memcpy((void *)fw_arg2, kexec_envp, KEXEC_ENVP_SIZE); } #endif static int __init mips_reboot_setup(void) { _machine_restart = loongson_restart; _machine_halt = loongson_halt; pm_power_off = loongson_poweroff; #ifdef CONFIG_KEXEC kexec_argv = kmalloc(KEXEC_ARGV_SIZE, GFP_KERNEL); kdump_argv = kmalloc(KEXEC_ARGV_SIZE, GFP_KERNEL); kexec_envp = kmalloc(KEXEC_ENVP_SIZE, GFP_KERNEL); fw_arg1 = KEXEC_ARGV_ADDR; memcpy(kexec_envp, (void *)fw_arg2, KEXEC_ENVP_SIZE); _machine_kexec_prepare = loongson_kexec_prepare; _machine_kexec_shutdown = loongson_kexec_shutdown; _machine_crash_shutdown = loongson_crash_shutdown; #endif return 0; } arch_initcall(mips_reboot_setup); |