-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add three new files, kexec.h, machine_kexec.c and relocate_kernel.S to the arm64 architecture that add support for the kexec re-boot mechanism (CONFIG_KEXEC) on arm64 platforms. Signed-off-by: Geoff Levand <geoff@infradead.org> Reviewed-by: James Morse <james.morse@arm.com> [catalin.marinas@arm.com: removed dead code following James Morse's comments] Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
- Loading branch information
Geoff Levand
authored and
Catalin Marinas
committed
Jun 27, 2016
1 parent
f9076ec
commit d28f6df
Showing
6 changed files
with
361 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* | ||
* kexec for arm64 | ||
* | ||
* Copyright (C) Linaro. | ||
* Copyright (C) Huawei Futurewei Technologies. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
*/ | ||
|
||
#ifndef _ARM64_KEXEC_H | ||
#define _ARM64_KEXEC_H | ||
|
||
/* Maximum physical address we can use pages from */ | ||
|
||
#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL) | ||
|
||
/* Maximum address we can reach in physical address mode */ | ||
|
||
#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL) | ||
|
||
/* Maximum address we can use for the control code buffer */ | ||
|
||
#define KEXEC_CONTROL_MEMORY_LIMIT (-1UL) | ||
|
||
#define KEXEC_CONTROL_PAGE_SIZE 4096 | ||
|
||
#define KEXEC_ARCH KEXEC_ARCH_AARCH64 | ||
|
||
#ifndef __ASSEMBLY__ | ||
|
||
/** | ||
* crash_setup_regs() - save registers for the panic kernel | ||
* | ||
* @newregs: registers are saved here | ||
* @oldregs: registers to be saved (may be %NULL) | ||
*/ | ||
|
||
static inline void crash_setup_regs(struct pt_regs *newregs, | ||
struct pt_regs *oldregs) | ||
{ | ||
/* Empty routine needed to avoid build errors. */ | ||
} | ||
|
||
#endif /* __ASSEMBLY__ */ | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
/* | ||
* kexec for arm64 | ||
* | ||
* Copyright (C) Linaro. | ||
* Copyright (C) Huawei Futurewei Technologies. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
*/ | ||
|
||
#include <linux/kexec.h> | ||
#include <linux/smp.h> | ||
|
||
#include <asm/cacheflush.h> | ||
#include <asm/cpu_ops.h> | ||
#include <asm/mmu_context.h> | ||
|
||
#include "cpu-reset.h" | ||
|
||
/* Global variables for the arm64_relocate_new_kernel routine. */ | ||
extern const unsigned char arm64_relocate_new_kernel[]; | ||
extern const unsigned long arm64_relocate_new_kernel_size; | ||
|
||
static unsigned long kimage_start; | ||
|
||
void machine_kexec_cleanup(struct kimage *kimage) | ||
{ | ||
/* Empty routine needed to avoid build errors. */ | ||
} | ||
|
||
/** | ||
* machine_kexec_prepare - Prepare for a kexec reboot. | ||
* | ||
* Called from the core kexec code when a kernel image is loaded. | ||
* Forbid loading a kexec kernel if we have no way of hotplugging cpus or cpus | ||
* are stuck in the kernel. This avoids a panic once we hit machine_kexec(). | ||
*/ | ||
int machine_kexec_prepare(struct kimage *kimage) | ||
{ | ||
kimage_start = kimage->start; | ||
|
||
if (kimage->type != KEXEC_TYPE_CRASH && cpus_are_stuck_in_kernel()) { | ||
pr_err("Can't kexec: CPUs are stuck in the kernel.\n"); | ||
return -EBUSY; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
/** | ||
* kexec_list_flush - Helper to flush the kimage list and source pages to PoC. | ||
*/ | ||
static void kexec_list_flush(struct kimage *kimage) | ||
{ | ||
kimage_entry_t *entry; | ||
|
||
for (entry = &kimage->head; ; entry++) { | ||
unsigned int flag; | ||
void *addr; | ||
|
||
/* flush the list entries. */ | ||
__flush_dcache_area(entry, sizeof(kimage_entry_t)); | ||
|
||
flag = *entry & IND_FLAGS; | ||
if (flag == IND_DONE) | ||
break; | ||
|
||
addr = phys_to_virt(*entry & PAGE_MASK); | ||
|
||
switch (flag) { | ||
case IND_INDIRECTION: | ||
/* Set entry point just before the new list page. */ | ||
entry = (kimage_entry_t *)addr - 1; | ||
break; | ||
case IND_SOURCE: | ||
/* flush the source pages. */ | ||
__flush_dcache_area(addr, PAGE_SIZE); | ||
break; | ||
case IND_DESTINATION: | ||
break; | ||
default: | ||
BUG(); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* kexec_segment_flush - Helper to flush the kimage segments to PoC. | ||
*/ | ||
static void kexec_segment_flush(const struct kimage *kimage) | ||
{ | ||
unsigned long i; | ||
|
||
pr_debug("%s:\n", __func__); | ||
|
||
for (i = 0; i < kimage->nr_segments; i++) { | ||
pr_debug(" segment[%lu]: %016lx - %016lx, 0x%lx bytes, %lu pages\n", | ||
i, | ||
kimage->segment[i].mem, | ||
kimage->segment[i].mem + kimage->segment[i].memsz, | ||
kimage->segment[i].memsz, | ||
kimage->segment[i].memsz / PAGE_SIZE); | ||
|
||
__flush_dcache_area(phys_to_virt(kimage->segment[i].mem), | ||
kimage->segment[i].memsz); | ||
} | ||
} | ||
|
||
/** | ||
* machine_kexec - Do the kexec reboot. | ||
* | ||
* Called from the core kexec code for a sys_reboot with LINUX_REBOOT_CMD_KEXEC. | ||
*/ | ||
void machine_kexec(struct kimage *kimage) | ||
{ | ||
phys_addr_t reboot_code_buffer_phys; | ||
void *reboot_code_buffer; | ||
|
||
/* | ||
* New cpus may have become stuck_in_kernel after we loaded the image. | ||
*/ | ||
BUG_ON(cpus_are_stuck_in_kernel() || (num_online_cpus() > 1)); | ||
|
||
reboot_code_buffer_phys = page_to_phys(kimage->control_code_page); | ||
reboot_code_buffer = phys_to_virt(reboot_code_buffer_phys); | ||
|
||
/* | ||
* Copy arm64_relocate_new_kernel to the reboot_code_buffer for use | ||
* after the kernel is shut down. | ||
*/ | ||
memcpy(reboot_code_buffer, arm64_relocate_new_kernel, | ||
arm64_relocate_new_kernel_size); | ||
|
||
/* Flush the reboot_code_buffer in preparation for its execution. */ | ||
__flush_dcache_area(reboot_code_buffer, arm64_relocate_new_kernel_size); | ||
flush_icache_range((uintptr_t)reboot_code_buffer, | ||
arm64_relocate_new_kernel_size); | ||
|
||
/* Flush the kimage list and its buffers. */ | ||
kexec_list_flush(kimage); | ||
|
||
/* Flush the new image if already in place. */ | ||
if (kimage->head & IND_DONE) | ||
kexec_segment_flush(kimage); | ||
|
||
pr_info("Bye!\n"); | ||
|
||
/* Disable all DAIF exceptions. */ | ||
asm volatile ("msr daifset, #0xf" : : : "memory"); | ||
|
||
/* | ||
* cpu_soft_restart will shutdown the MMU, disable data caches, then | ||
* transfer control to the reboot_code_buffer which contains a copy of | ||
* the arm64_relocate_new_kernel routine. arm64_relocate_new_kernel | ||
* uses physical addressing to relocate the new image to its final | ||
* position and transfers control to the image entry point when the | ||
* relocation is complete. | ||
*/ | ||
|
||
cpu_soft_restart(1, reboot_code_buffer_phys, kimage->head, | ||
kimage_start, 0); | ||
|
||
BUG(); /* Should never get here. */ | ||
} | ||
|
||
void machine_crash_shutdown(struct pt_regs *regs) | ||
{ | ||
/* Empty routine needed to avoid build errors. */ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* | ||
* kexec for arm64 | ||
* | ||
* Copyright (C) Linaro. | ||
* Copyright (C) Huawei Futurewei Technologies. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
*/ | ||
|
||
#include <linux/kexec.h> | ||
#include <linux/linkage.h> | ||
|
||
#include <asm/assembler.h> | ||
#include <asm/kexec.h> | ||
#include <asm/page.h> | ||
#include <asm/sysreg.h> | ||
|
||
/* | ||
* arm64_relocate_new_kernel - Put a 2nd stage image in place and boot it. | ||
* | ||
* The memory that the old kernel occupies may be overwritten when coping the | ||
* new image to its final location. To assure that the | ||
* arm64_relocate_new_kernel routine which does that copy is not overwritten, | ||
* all code and data needed by arm64_relocate_new_kernel must be between the | ||
* symbols arm64_relocate_new_kernel and arm64_relocate_new_kernel_end. The | ||
* machine_kexec() routine will copy arm64_relocate_new_kernel to the kexec | ||
* control_code_page, a special page which has been set up to be preserved | ||
* during the copy operation. | ||
*/ | ||
ENTRY(arm64_relocate_new_kernel) | ||
|
||
/* Setup the list loop variables. */ | ||
mov x17, x1 /* x17 = kimage_start */ | ||
mov x16, x0 /* x16 = kimage_head */ | ||
dcache_line_size x15, x0 /* x15 = dcache line size */ | ||
mov x14, xzr /* x14 = entry ptr */ | ||
mov x13, xzr /* x13 = copy dest */ | ||
|
||
/* Clear the sctlr_el2 flags. */ | ||
mrs x0, CurrentEL | ||
cmp x0, #CurrentEL_EL2 | ||
b.ne 1f | ||
mrs x0, sctlr_el2 | ||
ldr x1, =SCTLR_ELx_FLAGS | ||
bic x0, x0, x1 | ||
msr sctlr_el2, x0 | ||
isb | ||
1: | ||
|
||
/* Check if the new image needs relocation. */ | ||
tbnz x16, IND_DONE_BIT, .Ldone | ||
|
||
.Lloop: | ||
and x12, x16, PAGE_MASK /* x12 = addr */ | ||
|
||
/* Test the entry flags. */ | ||
.Ltest_source: | ||
tbz x16, IND_SOURCE_BIT, .Ltest_indirection | ||
|
||
/* Invalidate dest page to PoC. */ | ||
mov x0, x13 | ||
add x20, x0, #PAGE_SIZE | ||
sub x1, x15, #1 | ||
bic x0, x0, x1 | ||
2: dc ivac, x0 | ||
add x0, x0, x15 | ||
cmp x0, x20 | ||
b.lo 2b | ||
dsb sy | ||
|
||
mov x20, x13 | ||
mov x21, x12 | ||
copy_page x20, x21, x0, x1, x2, x3, x4, x5, x6, x7 | ||
|
||
/* dest += PAGE_SIZE */ | ||
add x13, x13, PAGE_SIZE | ||
b .Lnext | ||
|
||
.Ltest_indirection: | ||
tbz x16, IND_INDIRECTION_BIT, .Ltest_destination | ||
|
||
/* ptr = addr */ | ||
mov x14, x12 | ||
b .Lnext | ||
|
||
.Ltest_destination: | ||
tbz x16, IND_DESTINATION_BIT, .Lnext | ||
|
||
/* dest = addr */ | ||
mov x13, x12 | ||
|
||
.Lnext: | ||
/* entry = *ptr++ */ | ||
ldr x16, [x14], #8 | ||
|
||
/* while (!(entry & DONE)) */ | ||
tbz x16, IND_DONE_BIT, .Lloop | ||
|
||
.Ldone: | ||
/* wait for writes from copy_page to finish */ | ||
dsb nsh | ||
ic iallu | ||
dsb nsh | ||
isb | ||
|
||
/* Start new image. */ | ||
mov x0, xzr | ||
mov x1, xzr | ||
mov x2, xzr | ||
mov x3, xzr | ||
br x17 | ||
|
||
ENDPROC(arm64_relocate_new_kernel) | ||
|
||
.ltorg | ||
|
||
.align 3 /* To keep the 64-bit values below naturally aligned. */ | ||
|
||
.Lcopy_end: | ||
.org KEXEC_CONTROL_PAGE_SIZE | ||
|
||
/* | ||
* arm64_relocate_new_kernel_size - Number of bytes to copy to the | ||
* control_code_page. | ||
*/ | ||
.globl arm64_relocate_new_kernel_size | ||
arm64_relocate_new_kernel_size: | ||
.quad .Lcopy_end - arm64_relocate_new_kernel |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters