Skip to content

Commit 32dcc8e

Browse files
author
Takahiro Akashi
committed
arm64: kdump: protect crash dump kernel memory
arch_kexec_protect_crashkres() and arch_kexec_unprotect_crashkres() are meant to be called by kexec_load() in order to protect the memory allocated for crash dump kernel once the image is loaded. The protection is implemented by unmapping the relevant segments in crash dump kernel memory, rather than making it read-only as other archs do, to prevent coherency issues due to potential cache aliasing (with mismatched attributes). Page-level mappings are consistently used here so that we can change the attributes of segments in page granularity as well as shrink the region also in page granularity through /sys/kernel/kexec_crash_size, putting the freed memory back to buddy system. Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org> Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Conflicts: arch/arm64/mm/mmu.c The file has been heavily refactored in v4.12, in particular, by commit 951366d4b7 ("arm64/mmu: replace 'page_mappings_only' parameter with flags argument") commit f14c66ce81b5 ("arm64: mm: replace 'block_mappings_allowed' with 'page_mappings_only'") commit 5ea5306c3235 ("arm64: alternatives: apply boot time fixups via the linear mapping")
1 parent 8d8d32a commit 32dcc8e

2 files changed

Lines changed: 73 additions & 48 deletions

File tree

arch/arm64/kernel/machine_kexec.c

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@
1414

1515
#include <asm/cacheflush.h>
1616
#include <asm/cpu_ops.h>
17+
#include <asm/mmu.h>
1718
#include <asm/mmu_context.h>
19+
#include <asm/page.h>
1820

1921
#include "cpu-reset.h"
2022

2123
/* Global variables for the arm64_relocate_new_kernel routine. */
2224
extern const unsigned char arm64_relocate_new_kernel[];
2325
extern const unsigned long arm64_relocate_new_kernel_size;
2426

25-
static unsigned long kimage_start;
26-
2727
/**
2828
* kexec_image_info - For debugging output.
2929
*/
@@ -64,8 +64,6 @@ void machine_kexec_cleanup(struct kimage *kimage)
6464
*/
6565
int machine_kexec_prepare(struct kimage *kimage)
6666
{
67-
kimage_start = kimage->start;
68-
6967
kexec_image_info(kimage);
7068

7169
if (kimage->type != KEXEC_TYPE_CRASH && cpus_are_stuck_in_kernel()) {
@@ -183,7 +181,7 @@ void machine_kexec(struct kimage *kimage)
183181
kexec_list_flush(kimage);
184182

185183
/* Flush the new image if already in place. */
186-
if (kimage->head & IND_DONE)
184+
if ((kimage != kexec_crash_image) && (kimage->head & IND_DONE))
187185
kexec_segment_flush(kimage);
188186

189187
pr_info("Bye!\n");
@@ -201,7 +199,7 @@ void machine_kexec(struct kimage *kimage)
201199
*/
202200

203201
cpu_soft_restart(1, reboot_code_buffer_phys, kimage->head,
204-
kimage_start, 0);
202+
kimage->start, 0);
205203

206204
BUG(); /* Should never get here. */
207205
}
@@ -210,3 +208,25 @@ void machine_crash_shutdown(struct pt_regs *regs)
210208
{
211209
/* Empty routine needed to avoid build errors. */
212210
}
211+
212+
void arch_kexec_protect_crashkres(void)
213+
{
214+
int i;
215+
216+
kexec_segment_flush(kexec_crash_image);
217+
218+
for (i = 0; i < kexec_crash_image->nr_segments; i++)
219+
set_memory_valid(
220+
__phys_to_virt(kexec_crash_image->segment[i].mem),
221+
kexec_crash_image->segment[i].memsz >> PAGE_SHIFT, 0);
222+
}
223+
224+
void arch_kexec_unprotect_crashkres(void)
225+
{
226+
int i;
227+
228+
for (i = 0; i < kexec_crash_image->nr_segments; i++)
229+
set_memory_valid(
230+
__phys_to_virt(kexec_crash_image->segment[i].mem),
231+
kexec_crash_image->segment[i].memsz >> PAGE_SHIFT, 1);
232+
}

arch/arm64/mm/mmu.c

Lines changed: 47 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include <linux/kernel.h>
2222
#include <linux/errno.h>
2323
#include <linux/init.h>
24+
#include <linux/ioport.h>
25+
#include <linux/kexec.h>
2426
#include <linux/libfdt.h>
2527
#include <linux/mman.h>
2628
#include <linux/nodemask.h>
@@ -368,67 +370,70 @@ static void create_mapping_late(phys_addr_t phys, unsigned long virt,
368370
late_pgtable_alloc, !debug_pagealloc_enabled());
369371
}
370372

371-
static void __init __map_memblock(pgd_t *pgd, phys_addr_t start, phys_addr_t end)
373+
static void __init __map_memblock(pgd_t *pgd, phys_addr_t start,
374+
phys_addr_t end, pgprot_t prot,
375+
bool allow_block_mappings)
376+
{
377+
__create_pgd_mapping(pgd, start, __phys_to_virt(start), end - start,
378+
prot, early_pgtable_alloc, allow_block_mappings);
379+
}
380+
381+
static void __init map_mem(pgd_t *pgd)
372382
{
373383
unsigned long kernel_start = __pa(_text);
374384
unsigned long kernel_end = __pa(_etext);
385+
struct memblock_region *reg;
375386

376387
/*
377388
* Take care not to create a writable alias for the
378389
* read-only text and rodata sections of the kernel image.
390+
* So temporarily mark them as NOMAP to skip mappings in
391+
* the following for-loop
379392
*/
393+
memblock_mark_nomap(kernel_start, kernel_end - kernel_start);
394+
#ifdef CONFIG_KEXEC_CORE
395+
if (crashk_res.end)
396+
memblock_mark_nomap(crashk_res.start,
397+
resource_size(&crashk_res));
398+
#endif
380399

381-
/* No overlap with the kernel text */
382-
if (end < kernel_start || start >= kernel_end) {
383-
__create_pgd_mapping(pgd, start, __phys_to_virt(start),
384-
end - start, PAGE_KERNEL,
385-
early_pgtable_alloc,
386-
!debug_pagealloc_enabled());
387-
return;
388-
}
400+
/* map all the memory banks */
401+
for_each_memblock(memory, reg) {
402+
phys_addr_t start = reg->base;
403+
phys_addr_t end = start + reg->size;
389404

390-
/*
391-
* This block overlaps the kernel text mapping.
392-
* Map the portion(s) which don't overlap.
393-
*/
394-
if (start < kernel_start)
395-
__create_pgd_mapping(pgd, start,
396-
__phys_to_virt(start),
397-
kernel_start - start, PAGE_KERNEL,
398-
early_pgtable_alloc,
399-
!debug_pagealloc_enabled());
400-
if (kernel_end < end)
401-
__create_pgd_mapping(pgd, kernel_end,
402-
__phys_to_virt(kernel_end),
403-
end - kernel_end, PAGE_KERNEL,
404-
early_pgtable_alloc,
405-
!debug_pagealloc_enabled());
405+
if (start >= end)
406+
break;
407+
if (memblock_is_nomap(reg))
408+
continue;
409+
410+
__map_memblock(pgd, start, end,
411+
PAGE_KERNEL, !debug_pagealloc_enabled());
412+
}
406413

407414
/*
408415
* Map the linear alias of the [_text, _etext) interval as
409416
* read-only/non-executable. This makes the contents of the
410417
* region accessible to subsystems such as hibernate, but
411418
* protects it from inadvertent modification or execution.
412419
*/
413-
__create_pgd_mapping(pgd, kernel_start, __phys_to_virt(kernel_start),
414-
kernel_end - kernel_start, PAGE_KERNEL_RO,
415-
early_pgtable_alloc, !debug_pagealloc_enabled());
416-
}
420+
__map_memblock(pgd, kernel_start, kernel_end,
421+
PAGE_KERNEL_RO, !debug_pagealloc_enabled());
422+
memblock_clear_nomap(kernel_start, kernel_end - kernel_start);
417423

418-
static void __init map_mem(pgd_t *pgd)
419-
{
420-
struct memblock_region *reg;
421-
422-
/* map all the memory banks */
423-
for_each_memblock(memory, reg) {
424-
phys_addr_t start = reg->base;
425-
phys_addr_t end = start + reg->size;
426-
427-
if (start >= end)
428-
break;
429-
430-
__map_memblock(pgd, start, end);
424+
#ifdef CONFIG_KEXEC_CORE
425+
/*
426+
* Use page-level mappings here so that we can shrink the region
427+
* in page granularity and put back unused memory to buddy system
428+
* through /sys/kernel/kexec_crash_size interface.
429+
*/
430+
if (crashk_res.end) {
431+
__map_memblock(pgd, crashk_res.start, crashk_res.end + 1,
432+
PAGE_KERNEL, false);
433+
memblock_clear_nomap(crashk_res.start,
434+
resource_size(&crashk_res));
431435
}
436+
#endif
432437
}
433438

434439
void mark_rodata_ro(void)

0 commit comments

Comments
 (0)