Skip to content

Commit 1a1cf93

Browse files
hbathinimpe
authored andcommitted
powerpc/kexec_file: Setup backup region for kdump kernel
Though kdump kernel boots from loaded address, the first 64KB of it is copied down to real 0. So, setup a backup region and let purgatory copy the first 64KB of crashed kernel into this backup region before booting into kdump kernel. Update reserve map with backup region and crashed kernel's memory to avoid kdump kernel from accidentially using that memory. Signed-off-by: Hari Bathini <[email protected]> Reviewed-by: Thiago Jung Bauermann <[email protected]> Signed-off-by: Michael Ellerman <[email protected]> Link: https://lore.kernel.org/r/159602294718.575379.16216507537038008623.stgit@hbathini
1 parent 7c64e21 commit 1a1cf93

File tree

5 files changed

+159
-7
lines changed

5 files changed

+159
-7
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
#ifndef _ASM_POWERPC_CRASHDUMP_PPC64_H
3+
#define _ASM_POWERPC_CRASHDUMP_PPC64_H
4+
5+
/*
6+
* Backup region - first 64KB of System RAM
7+
*
8+
* If ever the below macros are to be changed, please be judicious.
9+
* The implicit assumptions are:
10+
* - start, end & size are less than UINT32_MAX.
11+
* - start & size are at least 8 byte aligned.
12+
*
13+
* For implementation details: arch/powerpc/purgatory/trampoline_64.S
14+
*/
15+
#define BACKUP_SRC_START 0
16+
#define BACKUP_SRC_END 0xffff
17+
#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1)
18+
19+
#endif /* __ASM_POWERPC_CRASHDUMP_PPC64_H */

arch/powerpc/include/asm/kexec.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ extern const struct kexec_file_ops kexec_elf64_ops;
105105
struct kimage_arch {
106106
struct crash_mem *exclude_ranges;
107107

108+
unsigned long backup_start;
109+
void *backup_buf;
110+
108111
#ifdef CONFIG_IMA_KEXEC
109112
phys_addr_t ima_buffer_addr;
110113
size_t ima_buffer_size;
@@ -120,6 +123,10 @@ int setup_new_fdt(const struct kimage *image, void *fdt,
120123
int delete_fdt_mem_rsv(void *fdt, unsigned long start, unsigned long size);
121124

122125
#ifdef CONFIG_PPC64
126+
struct kexec_buf;
127+
128+
int load_crashdump_segments_ppc64(struct kimage *image,
129+
struct kexec_buf *kbuf);
123130
int setup_purgatory_ppc64(struct kimage *image, const void *slave_code,
124131
const void *fdt, unsigned long kernel_load_addr,
125132
unsigned long fdt_load_addr);

arch/powerpc/kexec/elf_64.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,15 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
6868

6969
pr_debug("Loaded purgatory at 0x%lx\n", pbuf.mem);
7070

71+
/* Load additional segments needed for panic kernel */
72+
if (image->type == KEXEC_TYPE_CRASH) {
73+
ret = load_crashdump_segments_ppc64(image, &kbuf);
74+
if (ret) {
75+
pr_err("Failed to load kdump kernel segments\n");
76+
goto out;
77+
}
78+
}
79+
7180
if (initrd != NULL) {
7281
kbuf.buffer = initrd;
7382
kbuf.bufsz = kbuf.memsz = initrd_len;

arch/powerpc/kexec/file_load_64.c

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
#include <linux/of_device.h>
2121
#include <linux/memblock.h>
2222
#include <linux/slab.h>
23+
#include <linux/vmalloc.h>
2324
#include <asm/drmem.h>
2425
#include <asm/kexec_ranges.h>
26+
#include <asm/crashdump-ppc64.h>
2527

2628
struct umem_info {
2729
u64 *buf; /* data buffer for usable-memory property */
@@ -605,6 +607,70 @@ static int update_usable_mem_fdt(void *fdt, struct crash_mem *usable_mem)
605607
return ret;
606608
}
607609

610+
/**
611+
* load_backup_segment - Locate a memory hole to place the backup region.
612+
* @image: Kexec image.
613+
* @kbuf: Buffer contents and memory parameters.
614+
*
615+
* Returns 0 on success, negative errno on error.
616+
*/
617+
static int load_backup_segment(struct kimage *image, struct kexec_buf *kbuf)
618+
{
619+
void *buf;
620+
int ret;
621+
622+
/*
623+
* Setup a source buffer for backup segment.
624+
*
625+
* A source buffer has no meaning for backup region as data will
626+
* be copied from backup source, after crash, in the purgatory.
627+
* But as load segment code doesn't recognize such segments,
628+
* setup a dummy source buffer to keep it happy for now.
629+
*/
630+
buf = vzalloc(BACKUP_SRC_SIZE);
631+
if (!buf)
632+
return -ENOMEM;
633+
634+
kbuf->buffer = buf;
635+
kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
636+
kbuf->bufsz = kbuf->memsz = BACKUP_SRC_SIZE;
637+
kbuf->top_down = false;
638+
639+
ret = kexec_add_buffer(kbuf);
640+
if (ret) {
641+
vfree(buf);
642+
return ret;
643+
}
644+
645+
image->arch.backup_buf = buf;
646+
image->arch.backup_start = kbuf->mem;
647+
return 0;
648+
}
649+
650+
/**
651+
* load_crashdump_segments_ppc64 - Initialize the additional segements needed
652+
* to load kdump kernel.
653+
* @image: Kexec image.
654+
* @kbuf: Buffer contents and memory parameters.
655+
*
656+
* Returns 0 on success, negative errno on error.
657+
*/
658+
int load_crashdump_segments_ppc64(struct kimage *image,
659+
struct kexec_buf *kbuf)
660+
{
661+
int ret;
662+
663+
/* Load backup segment - first 64K bytes of the crashing kernel */
664+
ret = load_backup_segment(image, kbuf);
665+
if (ret) {
666+
pr_err("Failed to load backup segment\n");
667+
return ret;
668+
}
669+
pr_debug("Loaded the backup region at 0x%lx\n", kbuf->mem);
670+
671+
return 0;
672+
}
673+
608674
/**
609675
* setup_purgatory_ppc64 - initialize PPC64 specific purgatory's global
610676
* variables and call setup_purgatory() to initialize
@@ -643,6 +709,11 @@ int setup_purgatory_ppc64(struct kimage *image, const void *slave_code,
643709
goto out;
644710
}
645711

712+
/* Tell purgatory where to look for backup region */
713+
ret = kexec_purgatory_get_set_symbol(image, "backup_start",
714+
&image->arch.backup_start,
715+
sizeof(image->arch.backup_start),
716+
false);
646717
out:
647718
if (ret)
648719
pr_err("Failed to setup purgatory symbols");
@@ -674,7 +745,7 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
674745

675746
/*
676747
* Restrict memory usage for kdump kernel by setting up
677-
* usable memory ranges.
748+
* usable memory ranges and memory reserve map.
678749
*/
679750
if (image->type == KEXEC_TYPE_CRASH) {
680751
ret = get_usable_memory_ranges(&umem);
@@ -687,13 +758,26 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
687758
goto out;
688759
}
689760

690-
/* Ensure we don't touch crashed kernel's memory */
691-
ret = fdt_add_mem_rsv(fdt, 0, crashk_res.start);
761+
/*
762+
* Ensure we don't touch crashed kernel's memory except the
763+
* first 64K of RAM, which will be backed up.
764+
*/
765+
ret = fdt_add_mem_rsv(fdt, BACKUP_SRC_END + 1,
766+
crashk_res.start - BACKUP_SRC_SIZE);
692767
if (ret) {
693768
pr_err("Error reserving crash memory: %s\n",
694769
fdt_strerror(ret));
695770
goto out;
696771
}
772+
773+
/* Ensure backup region is not used by kdump/capture kernel */
774+
ret = fdt_add_mem_rsv(fdt, image->arch.backup_start,
775+
BACKUP_SRC_SIZE);
776+
if (ret) {
777+
pr_err("Error reserving memory for backup: %s\n",
778+
fdt_strerror(ret));
779+
goto out;
780+
}
697781
}
698782

699783
out:
@@ -800,5 +884,8 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image)
800884
kfree(image->arch.exclude_ranges);
801885
image->arch.exclude_ranges = NULL;
802886

887+
vfree(image->arch.backup_buf);
888+
image->arch.backup_buf = NULL;
889+
803890
return kexec_image_post_load_cleanup_default(image);
804891
}

arch/powerpc/purgatory/trampoline_64.S

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*/
1111

1212
#include <asm/asm-compat.h>
13+
#include <asm/crashdump-ppc64.h>
1314

1415
.machine ppc64
1516
.balign 256
@@ -43,14 +44,39 @@ master:
4344
mr %r17,%r3 /* save cpu id to r17 */
4445
mr %r15,%r4 /* save physical address in reg15 */
4546

47+
/* Work out where we're running */
48+
bcl 20, 31, 0f
49+
0: mflr %r18
50+
51+
/*
52+
* Copy BACKUP_SRC_SIZE bytes from BACKUP_SRC_START to
53+
* backup_start 8 bytes at a time.
54+
*
55+
* Use r3 = dest, r4 = src, r5 = size, r6 = count
56+
*/
57+
ld %r3, (backup_start - 0b)(%r18)
58+
cmpdi %cr0, %r3, 0
59+
beq .Lskip_copy /* skip if there is no backup region */
60+
lis %r5, BACKUP_SRC_SIZE@h
61+
ori %r5, %r5, BACKUP_SRC_SIZE@l
62+
cmpdi %cr0, %r5, 0
63+
beq .Lskip_copy /* skip if copy size is zero */
64+
lis %r4, BACKUP_SRC_START@h
65+
ori %r4, %r4, BACKUP_SRC_START@l
66+
li %r6, 0
67+
.Lcopy_loop:
68+
ldx %r0, %r6, %r4
69+
stdx %r0, %r6, %r3
70+
addi %r6, %r6, 8
71+
cmpld %cr0, %r6, %r5
72+
blt .Lcopy_loop
73+
74+
.Lskip_copy:
4675
or %r3,%r3,%r3 /* ok now to high priority, lets boot */
4776
lis %r6,0x1
4877
mtctr %r6 /* delay a bit for slaves to catch up */
4978
bdnz . /* before we overwrite 0-100 again */
5079

51-
bl 0f /* Work out where we're running */
52-
0: mflr %r18
53-
5480
/* load device-tree address */
5581
ld %r3, (dt_offset - 0b)(%r18)
5682
mr %r16,%r3 /* save dt address in reg16 */
@@ -89,7 +115,6 @@ master:
89115

90116
rfid /* update MSR and start kernel */
91117

92-
93118
.balign 8
94119
.globl kernel
95120
kernel:
@@ -102,6 +127,11 @@ dt_offset:
102127
.8byte 0x0
103128
.size dt_offset, . - dt_offset
104129

130+
.balign 8
131+
.globl backup_start
132+
backup_start:
133+
.8byte 0x0
134+
.size backup_start, . - backup_start
105135

106136
.data
107137
.balign 8

0 commit comments

Comments
 (0)