Skip to content

Commit 7dee93a

Browse files
hbathinimpe
authored andcommitted
powerpc/fadump: support holes in kernel boot memory area
With support to copy multiple kernel boot memory regions owing to copy size limitation, also handle holes in the memory area to be preserved. Support as many as 128 kernel boot memory regions. This allows having an adequate FADump capture kernel size for different scenarios. Signed-off-by: Hari Bathini <[email protected]> Signed-off-by: Michael Ellerman <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent becd91d commit 7dee93a

File tree

6 files changed

+219
-59
lines changed

6 files changed

+219
-59
lines changed

arch/powerpc/include/asm/fadump-internal.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
#ifndef _ASM_POWERPC_FADUMP_INTERNAL_H
1010
#define _ASM_POWERPC_FADUMP_INTERNAL_H
1111

12+
/* Maximum number of memory regions kernel supports */
13+
#define FADUMP_MAX_MEM_REGS 128
14+
1215
#ifndef CONFIG_PRESERVE_FA_DUMP
1316

1417
/* The upper limit percentage for user specified boot memory size (25%) */
@@ -88,11 +91,20 @@ struct fw_dump {
8891

8992
unsigned long boot_memory_size;
9093
u64 boot_mem_dest_addr;
94+
u64 boot_mem_addr[FADUMP_MAX_MEM_REGS];
95+
u64 boot_mem_sz[FADUMP_MAX_MEM_REGS];
96+
u64 boot_mem_top;
97+
u64 boot_mem_regs_cnt;
9198

9299
unsigned long fadumphdr_addr;
93100
unsigned long cpu_notes_buf_vaddr;
94101
unsigned long cpu_notes_buf_size;
95102

103+
/*
104+
* Maximum size supported by firmware to copy from source to
105+
* destination address per entry.
106+
*/
107+
u64 max_copy_size;
96108
u64 kernel_metadata;
97109

98110
int ibm_configure_kernel_dump;

arch/powerpc/kernel/fadump.c

Lines changed: 162 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ int is_fadump_memory_area(u64 addr, unsigned long size)
143143
if (((addr + size) > d_start) && (addr <= d_end))
144144
return 1;
145145

146-
return (addr <= fw_dump.boot_memory_size);
146+
return (addr <= fw_dump.boot_mem_top);
147147
}
148148

149149
int should_fadump_crash(void)
@@ -194,7 +194,20 @@ static bool is_fadump_mem_area_contiguous(u64 d_start, u64 d_end)
194194
*/
195195
bool is_fadump_boot_mem_contiguous(void)
196196
{
197-
return is_fadump_mem_area_contiguous(0, fw_dump.boot_memory_size);
197+
unsigned long d_start, d_end;
198+
bool ret = false;
199+
int i;
200+
201+
for (i = 0; i < fw_dump.boot_mem_regs_cnt; i++) {
202+
d_start = fw_dump.boot_mem_addr[i];
203+
d_end = d_start + fw_dump.boot_mem_sz[i];
204+
205+
ret = is_fadump_mem_area_contiguous(d_start, d_end);
206+
if (!ret)
207+
break;
208+
}
209+
210+
return ret;
198211
}
199212

200213
/*
@@ -213,6 +226,8 @@ bool is_fadump_reserved_mem_contiguous(void)
213226
/* Print firmware assisted dump configurations for debugging purpose. */
214227
static void fadump_show_config(void)
215228
{
229+
int i;
230+
216231
pr_debug("Support for firmware-assisted dump (fadump): %s\n",
217232
(fw_dump.fadump_supported ? "present" : "no support"));
218233

@@ -226,7 +241,13 @@ static void fadump_show_config(void)
226241
pr_debug("Dump section sizes:\n");
227242
pr_debug(" CPU state data size: %lx\n", fw_dump.cpu_state_data_size);
228243
pr_debug(" HPTE region size : %lx\n", fw_dump.hpte_region_size);
229-
pr_debug("Boot memory size : %lx\n", fw_dump.boot_memory_size);
244+
pr_debug(" Boot memory size : %lx\n", fw_dump.boot_memory_size);
245+
pr_debug(" Boot memory top : %llx\n", fw_dump.boot_mem_top);
246+
pr_debug("Boot memory regions cnt: %llx\n", fw_dump.boot_mem_regs_cnt);
247+
for (i = 0; i < fw_dump.boot_mem_regs_cnt; i++) {
248+
pr_debug("[%03d] base = %llx, size = %llx\n", i,
249+
fw_dump.boot_mem_addr[i], fw_dump.boot_mem_sz[i]);
250+
}
230251
}
231252

232253
/**
@@ -326,6 +347,88 @@ static unsigned long get_fadump_area_size(void)
326347
return size;
327348
}
328349

350+
static int __init add_boot_mem_region(unsigned long rstart,
351+
unsigned long rsize)
352+
{
353+
int i = fw_dump.boot_mem_regs_cnt++;
354+
355+
if (fw_dump.boot_mem_regs_cnt > FADUMP_MAX_MEM_REGS) {
356+
fw_dump.boot_mem_regs_cnt = FADUMP_MAX_MEM_REGS;
357+
return 0;
358+
}
359+
360+
pr_debug("Added boot memory range[%d] [%#016lx-%#016lx)\n",
361+
i, rstart, (rstart + rsize));
362+
fw_dump.boot_mem_addr[i] = rstart;
363+
fw_dump.boot_mem_sz[i] = rsize;
364+
return 1;
365+
}
366+
367+
/*
368+
* Firmware usually has a hard limit on the data it can copy per region.
369+
* Honour that by splitting a memory range into multiple regions.
370+
*/
371+
static int __init add_boot_mem_regions(unsigned long mstart,
372+
unsigned long msize)
373+
{
374+
unsigned long rstart, rsize, max_size;
375+
int ret = 1;
376+
377+
rstart = mstart;
378+
max_size = fw_dump.max_copy_size ? fw_dump.max_copy_size : msize;
379+
while (msize) {
380+
if (msize > max_size)
381+
rsize = max_size;
382+
else
383+
rsize = msize;
384+
385+
ret = add_boot_mem_region(rstart, rsize);
386+
if (!ret)
387+
break;
388+
389+
msize -= rsize;
390+
rstart += rsize;
391+
}
392+
393+
return ret;
394+
}
395+
396+
static int __init fadump_get_boot_mem_regions(void)
397+
{
398+
unsigned long base, size, cur_size, hole_size, last_end;
399+
unsigned long mem_size = fw_dump.boot_memory_size;
400+
struct memblock_region *reg;
401+
int ret = 1;
402+
403+
fw_dump.boot_mem_regs_cnt = 0;
404+
405+
last_end = 0;
406+
hole_size = 0;
407+
cur_size = 0;
408+
for_each_memblock(memory, reg) {
409+
base = reg->base;
410+
size = reg->size;
411+
hole_size += (base - last_end);
412+
413+
if ((cur_size + size) >= mem_size) {
414+
size = (mem_size - cur_size);
415+
ret = add_boot_mem_regions(base, size);
416+
break;
417+
}
418+
419+
mem_size -= size;
420+
cur_size += size;
421+
ret = add_boot_mem_regions(base, size);
422+
if (!ret)
423+
break;
424+
425+
last_end = base + size;
426+
}
427+
fw_dump.boot_mem_top = PAGE_ALIGN(fw_dump.boot_memory_size + hole_size);
428+
429+
return ret;
430+
}
431+
329432
int __init fadump_reserve_mem(void)
330433
{
331434
u64 base, size, mem_boundary, bootmem_min, align = PAGE_SIZE;
@@ -362,6 +465,11 @@ int __init fadump_reserve_mem(void)
362465
fw_dump.boot_memory_size, bootmem_min);
363466
goto error_out;
364467
}
468+
469+
if (!fadump_get_boot_mem_regions()) {
470+
pr_err("Too many holes in boot memory area to enable fadump\n");
471+
goto error_out;
472+
}
365473
}
366474

367475
/*
@@ -385,7 +493,7 @@ int __init fadump_reserve_mem(void)
385493
else
386494
mem_boundary = memblock_end_of_DRAM();
387495

388-
base = fw_dump.boot_memory_size;
496+
base = fw_dump.boot_mem_top;
389497
size = get_fadump_area_size();
390498
fw_dump.reserve_dump_area_size = size;
391499
if (fw_dump.dump_active) {
@@ -769,34 +877,35 @@ static int fadump_setup_crash_memory_ranges(void)
769877
{
770878
struct memblock_region *reg;
771879
u64 start, end;
772-
int ret;
880+
int i, ret;
773881

774882
pr_debug("Setup crash memory ranges.\n");
775883
crash_mrange_info.mem_range_cnt = 0;
776884

777885
/*
778-
* add the first memory chunk (0 through boot_memory_size) as
779-
* a separate memory chunk. The reason is, at the time crash firmware
780-
* will move the content of this memory chunk to different location
781-
* specified during fadump registration. We need to create a separate
782-
* program header for this chunk with the correct offset.
886+
* Boot memory region(s) registered with firmware are moved to
887+
* different location at the time of crash. Create separate program
888+
* header(s) for this memory chunk(s) with the correct offset.
783889
*/
784-
ret = fadump_add_mem_range(&crash_mrange_info,
785-
0, fw_dump.boot_memory_size);
786-
if (ret)
787-
return ret;
890+
for (i = 0; i < fw_dump.boot_mem_regs_cnt; i++) {
891+
start = fw_dump.boot_mem_addr[i];
892+
end = start + fw_dump.boot_mem_sz[i];
893+
ret = fadump_add_mem_range(&crash_mrange_info, start, end);
894+
if (ret)
895+
return ret;
896+
}
788897

789898
for_each_memblock(memory, reg) {
790899
start = (u64)reg->base;
791900
end = start + (u64)reg->size;
792901

793902
/*
794-
* skip the first memory chunk that is already added
795-
* (0 through boot_memory_size).
903+
* skip the memory chunk that is already added
904+
* (0 through boot_memory_top).
796905
*/
797-
if (start < fw_dump.boot_memory_size) {
798-
if (end > fw_dump.boot_memory_size)
799-
start = fw_dump.boot_memory_size;
906+
if (start < fw_dump.boot_mem_top) {
907+
if (end > fw_dump.boot_mem_top)
908+
start = fw_dump.boot_mem_top;
800909
else
801910
continue;
802911
}
@@ -817,17 +926,35 @@ static int fadump_setup_crash_memory_ranges(void)
817926
*/
818927
static inline unsigned long fadump_relocate(unsigned long paddr)
819928
{
820-
if ((paddr > 0) && (paddr < fw_dump.boot_memory_size))
821-
return fw_dump.boot_mem_dest_addr + paddr;
822-
else
823-
return paddr;
929+
unsigned long raddr, rstart, rend, rlast, hole_size;
930+
int i;
931+
932+
hole_size = 0;
933+
rlast = 0;
934+
raddr = paddr;
935+
for (i = 0; i < fw_dump.boot_mem_regs_cnt; i++) {
936+
rstart = fw_dump.boot_mem_addr[i];
937+
rend = rstart + fw_dump.boot_mem_sz[i];
938+
hole_size += (rstart - rlast);
939+
940+
if (paddr >= rstart && paddr < rend) {
941+
raddr += fw_dump.boot_mem_dest_addr - hole_size;
942+
break;
943+
}
944+
945+
rlast = rend;
946+
}
947+
948+
pr_debug("vmcoreinfo: paddr = 0x%lx, raddr = 0x%lx\n", paddr, raddr);
949+
return raddr;
824950
}
825951

826952
static int fadump_create_elfcore_headers(char *bufp)
827953
{
828-
struct elfhdr *elf;
954+
unsigned long long raddr, offset;
829955
struct elf_phdr *phdr;
830-
int i;
956+
struct elfhdr *elf;
957+
int i, j;
831958

832959
fadump_init_elfcore_header(bufp);
833960
elf = (struct elfhdr *)bufp;
@@ -870,7 +997,9 @@ static int fadump_create_elfcore_headers(char *bufp)
870997
(elf->e_phnum)++;
871998

872999
/* setup PT_LOAD sections. */
873-
1000+
j = 0;
1001+
offset = 0;
1002+
raddr = fw_dump.boot_mem_addr[0];
8741003
for (i = 0; i < crash_mrange_info.mem_range_cnt; i++) {
8751004
u64 mbase, msize;
8761005

@@ -885,13 +1014,17 @@ static int fadump_create_elfcore_headers(char *bufp)
8851014
phdr->p_flags = PF_R|PF_W|PF_X;
8861015
phdr->p_offset = mbase;
8871016

888-
if (mbase == 0) {
1017+
if (mbase == raddr) {
8891018
/*
8901019
* The entire real memory region will be moved by
8911020
* firmware to the specified destination_address.
8921021
* Hence set the correct offset.
8931022
*/
894-
phdr->p_offset = fw_dump.boot_mem_dest_addr;
1023+
phdr->p_offset = fw_dump.boot_mem_dest_addr + offset;
1024+
if (j < (fw_dump.boot_mem_regs_cnt - 1)) {
1025+
offset += fw_dump.boot_mem_sz[j];
1026+
raddr = fw_dump.boot_mem_addr[++j];
1027+
}
8951028
}
8961029

8971030
phdr->p_paddr = mbase;
@@ -1177,7 +1310,7 @@ static void fadump_invalidate_release_mem(void)
11771310
fadump_cleanup();
11781311
mutex_unlock(&fadump_mutex);
11791312

1180-
fadump_release_memory(fw_dump.boot_memory_size, memblock_end_of_DRAM());
1313+
fadump_release_memory(fw_dump.boot_mem_top, memblock_end_of_DRAM());
11811314
fadump_free_cpu_notes_buf();
11821315

11831316
/*

0 commit comments

Comments
 (0)