Skip to content

Commit 408cddd

Browse files
Hari Bathinimpe
authored andcommitted
powerpc/fadump: Fix endianess issues in firmware assisted dump handling
Firmware-assisted dump (fadump) kernel code is not endian safe. The below patch fixes this issue. Tested this patch with upstream kernel. Below output shows crash tool successfully opening LE fadump vmcore. # crash vmlinux vmcore GNU gdb (GDB) 7.6 This GDB was configured as "powerpc64le-unknown-linux-gnu"... KERNEL: vmlinux DUMPFILE: vmcore CPUS: 16 DATE: Wed Dec 31 19:00:00 1969 UPTIME: 00:03:28 LOAD AVERAGE: 0.46, 0.86, 0.41 TASKS: 268 NODENAME: linux-dhr2 RELEASE: 3.17.0-rc5-7-default VERSION: #6 SMP Tue Sep 30 01:06:34 EDT 2014 MACHINE: ppc64le (4116 Mhz) MEMORY: 40 GB PANIC: "Oops: Kernel access of bad area, sig: 11 [#1]" (check log for details) PID: 6223 COMMAND: "bash" TASK: c0000009661b2500 [THREAD_INFO: c000000967ac0000] CPU: 2 STATE: TASK_RUNNING (PANIC) Signed-off-by: Hari Bathini <hbathini@linux.vnet.ibm.com> [mpe: Make the comment in pSeries_lpar_hptab_clear() clearer] Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
1 parent 94966b7 commit 408cddd

3 files changed

Lines changed: 95 additions & 85 deletions

File tree

arch/powerpc/include/asm/fadump.h

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -70,39 +70,39 @@
7070
#define CPU_UNKNOWN (~((u32)0))
7171

7272
/* Utility macros */
73-
#define SKIP_TO_NEXT_CPU(reg_entry) \
74-
({ \
75-
while (reg_entry->reg_id != REG_ID("CPUEND")) \
76-
reg_entry++; \
77-
reg_entry++; \
73+
#define SKIP_TO_NEXT_CPU(reg_entry) \
74+
({ \
75+
while (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUEND")) \
76+
reg_entry++; \
77+
reg_entry++; \
7878
})
7979

8080
/* Kernel Dump section info */
8181
struct fadump_section {
82-
u32 request_flag;
83-
u16 source_data_type;
84-
u16 error_flags;
85-
u64 source_address;
86-
u64 source_len;
87-
u64 bytes_dumped;
88-
u64 destination_address;
82+
__be32 request_flag;
83+
__be16 source_data_type;
84+
__be16 error_flags;
85+
__be64 source_address;
86+
__be64 source_len;
87+
__be64 bytes_dumped;
88+
__be64 destination_address;
8989
};
9090

9191
/* ibm,configure-kernel-dump header. */
9292
struct fadump_section_header {
93-
u32 dump_format_version;
94-
u16 dump_num_sections;
95-
u16 dump_status_flag;
96-
u32 offset_first_dump_section;
93+
__be32 dump_format_version;
94+
__be16 dump_num_sections;
95+
__be16 dump_status_flag;
96+
__be32 offset_first_dump_section;
9797

9898
/* Fields for disk dump option. */
99-
u32 dd_block_size;
100-
u64 dd_block_offset;
101-
u64 dd_num_blocks;
102-
u32 dd_offset_disk_path;
99+
__be32 dd_block_size;
100+
__be64 dd_block_offset;
101+
__be64 dd_num_blocks;
102+
__be32 dd_offset_disk_path;
103103

104104
/* Maximum time allowed to prevent an automatic dump-reboot. */
105-
u32 max_time_auto;
105+
__be32 max_time_auto;
106106
};
107107

108108
/*
@@ -174,15 +174,15 @@ static inline u64 str_to_u64(const char *str)
174174

175175
/* Register save area header. */
176176
struct fadump_reg_save_area_header {
177-
u64 magic_number;
178-
u32 version;
179-
u32 num_cpu_offset;
177+
__be64 magic_number;
178+
__be32 version;
179+
__be32 num_cpu_offset;
180180
};
181181

182182
/* Register entry. */
183183
struct fadump_reg_entry {
184-
u64 reg_id;
185-
u64 reg_value;
184+
__be64 reg_id;
185+
__be64 reg_value;
186186
};
187187

188188
/* fadump crash info structure */

arch/powerpc/kernel/fadump.c

Lines changed: 57 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ int __init early_init_dt_scan_fw_dump(unsigned long node,
5858
const __be32 *sections;
5959
int i, num_sections;
6060
int size;
61-
const int *token;
61+
const __be32 *token;
6262

6363
if (depth != 1 || strcmp(uname, "rtas") != 0)
6464
return 0;
@@ -72,7 +72,7 @@ int __init early_init_dt_scan_fw_dump(unsigned long node,
7272
return 1;
7373

7474
fw_dump.fadump_supported = 1;
75-
fw_dump.ibm_configure_kernel_dump = *token;
75+
fw_dump.ibm_configure_kernel_dump = be32_to_cpu(*token);
7676

7777
/*
7878
* The 'ibm,kernel-dump' rtas node is present only if there is
@@ -147,11 +147,11 @@ static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm,
147147
memset(fdm, 0, sizeof(struct fadump_mem_struct));
148148
addr = addr & PAGE_MASK;
149149

150-
fdm->header.dump_format_version = 0x00000001;
151-
fdm->header.dump_num_sections = 3;
150+
fdm->header.dump_format_version = cpu_to_be32(0x00000001);
151+
fdm->header.dump_num_sections = cpu_to_be16(3);
152152
fdm->header.dump_status_flag = 0;
153153
fdm->header.offset_first_dump_section =
154-
(u32)offsetof(struct fadump_mem_struct, cpu_state_data);
154+
cpu_to_be32((u32)offsetof(struct fadump_mem_struct, cpu_state_data));
155155

156156
/*
157157
* Fields for disk dump option.
@@ -167,27 +167,27 @@ static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm,
167167

168168
/* Kernel dump sections */
169169
/* cpu state data section. */
170-
fdm->cpu_state_data.request_flag = FADUMP_REQUEST_FLAG;
171-
fdm->cpu_state_data.source_data_type = FADUMP_CPU_STATE_DATA;
170+
fdm->cpu_state_data.request_flag = cpu_to_be32(FADUMP_REQUEST_FLAG);
171+
fdm->cpu_state_data.source_data_type = cpu_to_be16(FADUMP_CPU_STATE_DATA);
172172
fdm->cpu_state_data.source_address = 0;
173-
fdm->cpu_state_data.source_len = fw_dump.cpu_state_data_size;
174-
fdm->cpu_state_data.destination_address = addr;
173+
fdm->cpu_state_data.source_len = cpu_to_be64(fw_dump.cpu_state_data_size);
174+
fdm->cpu_state_data.destination_address = cpu_to_be64(addr);
175175
addr += fw_dump.cpu_state_data_size;
176176

177177
/* hpte region section */
178-
fdm->hpte_region.request_flag = FADUMP_REQUEST_FLAG;
179-
fdm->hpte_region.source_data_type = FADUMP_HPTE_REGION;
178+
fdm->hpte_region.request_flag = cpu_to_be32(FADUMP_REQUEST_FLAG);
179+
fdm->hpte_region.source_data_type = cpu_to_be16(FADUMP_HPTE_REGION);
180180
fdm->hpte_region.source_address = 0;
181-
fdm->hpte_region.source_len = fw_dump.hpte_region_size;
182-
fdm->hpte_region.destination_address = addr;
181+
fdm->hpte_region.source_len = cpu_to_be64(fw_dump.hpte_region_size);
182+
fdm->hpte_region.destination_address = cpu_to_be64(addr);
183183
addr += fw_dump.hpte_region_size;
184184

185185
/* RMA region section */
186-
fdm->rmr_region.request_flag = FADUMP_REQUEST_FLAG;
187-
fdm->rmr_region.source_data_type = FADUMP_REAL_MODE_REGION;
188-
fdm->rmr_region.source_address = RMA_START;
189-
fdm->rmr_region.source_len = fw_dump.boot_memory_size;
190-
fdm->rmr_region.destination_address = addr;
186+
fdm->rmr_region.request_flag = cpu_to_be32(FADUMP_REQUEST_FLAG);
187+
fdm->rmr_region.source_data_type = cpu_to_be16(FADUMP_REAL_MODE_REGION);
188+
fdm->rmr_region.source_address = cpu_to_be64(RMA_START);
189+
fdm->rmr_region.source_len = cpu_to_be64(fw_dump.boot_memory_size);
190+
fdm->rmr_region.destination_address = cpu_to_be64(addr);
191191
addr += fw_dump.boot_memory_size;
192192

193193
return addr;
@@ -272,7 +272,7 @@ int __init fadump_reserve_mem(void)
272272
* first kernel.
273273
*/
274274
if (fdm_active)
275-
fw_dump.boot_memory_size = fdm_active->rmr_region.source_len;
275+
fw_dump.boot_memory_size = be64_to_cpu(fdm_active->rmr_region.source_len);
276276
else
277277
fw_dump.boot_memory_size = fadump_calculate_reserve_size();
278278

@@ -314,8 +314,8 @@ int __init fadump_reserve_mem(void)
314314
(unsigned long)(base >> 20));
315315

316316
fw_dump.fadumphdr_addr =
317-
fdm_active->rmr_region.destination_address +
318-
fdm_active->rmr_region.source_len;
317+
be64_to_cpu(fdm_active->rmr_region.destination_address) +
318+
be64_to_cpu(fdm_active->rmr_region.source_len);
319319
pr_debug("fadumphdr_addr = %p\n",
320320
(void *) fw_dump.fadumphdr_addr);
321321
} else {
@@ -472,9 +472,9 @@ fadump_read_registers(struct fadump_reg_entry *reg_entry, struct pt_regs *regs)
472472
{
473473
memset(regs, 0, sizeof(struct pt_regs));
474474

475-
while (reg_entry->reg_id != REG_ID("CPUEND")) {
476-
fadump_set_regval(regs, reg_entry->reg_id,
477-
reg_entry->reg_value);
475+
while (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUEND")) {
476+
fadump_set_regval(regs, be64_to_cpu(reg_entry->reg_id),
477+
be64_to_cpu(reg_entry->reg_value));
478478
reg_entry++;
479479
}
480480
reg_entry++;
@@ -603,20 +603,20 @@ static int __init fadump_build_cpu_notes(const struct fadump_mem_struct *fdm)
603603
if (!fdm->cpu_state_data.bytes_dumped)
604604
return -EINVAL;
605605

606-
addr = fdm->cpu_state_data.destination_address;
606+
addr = be64_to_cpu(fdm->cpu_state_data.destination_address);
607607
vaddr = __va(addr);
608608

609609
reg_header = vaddr;
610-
if (reg_header->magic_number != REGSAVE_AREA_MAGIC) {
610+
if (be64_to_cpu(reg_header->magic_number) != REGSAVE_AREA_MAGIC) {
611611
printk(KERN_ERR "Unable to read register save area.\n");
612612
return -ENOENT;
613613
}
614614
pr_debug("--------CPU State Data------------\n");
615-
pr_debug("Magic Number: %llx\n", reg_header->magic_number);
616-
pr_debug("NumCpuOffset: %x\n", reg_header->num_cpu_offset);
615+
pr_debug("Magic Number: %llx\n", be64_to_cpu(reg_header->magic_number));
616+
pr_debug("NumCpuOffset: %x\n", be32_to_cpu(reg_header->num_cpu_offset));
617617

618-
vaddr += reg_header->num_cpu_offset;
619-
num_cpus = *((u32 *)(vaddr));
618+
vaddr += be32_to_cpu(reg_header->num_cpu_offset);
619+
num_cpus = be32_to_cpu(*((__be32 *)(vaddr)));
620620
pr_debug("NumCpus : %u\n", num_cpus);
621621
vaddr += sizeof(u32);
622622
reg_entry = (struct fadump_reg_entry *)vaddr;
@@ -639,13 +639,13 @@ static int __init fadump_build_cpu_notes(const struct fadump_mem_struct *fdm)
639639
fdh = __va(fw_dump.fadumphdr_addr);
640640

641641
for (i = 0; i < num_cpus; i++) {
642-
if (reg_entry->reg_id != REG_ID("CPUSTRT")) {
642+
if (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUSTRT")) {
643643
printk(KERN_ERR "Unable to read CPU state data\n");
644644
rc = -ENOENT;
645645
goto error_out;
646646
}
647647
/* Lower 4 bytes of reg_value contains logical cpu id */
648-
cpu = reg_entry->reg_value & FADUMP_CPU_ID_MASK;
648+
cpu = be64_to_cpu(reg_entry->reg_value) & FADUMP_CPU_ID_MASK;
649649
if (fdh && !cpumask_test_cpu(cpu, &fdh->cpu_online_mask)) {
650650
SKIP_TO_NEXT_CPU(reg_entry);
651651
continue;
@@ -692,7 +692,7 @@ static int __init process_fadump(const struct fadump_mem_struct *fdm_active)
692692
return -EINVAL;
693693

694694
/* Check if the dump data is valid. */
695-
if ((fdm_active->header.dump_status_flag == FADUMP_ERROR_FLAG) ||
695+
if ((be16_to_cpu(fdm_active->header.dump_status_flag) == FADUMP_ERROR_FLAG) ||
696696
(fdm_active->cpu_state_data.error_flags != 0) ||
697697
(fdm_active->rmr_region.error_flags != 0)) {
698698
printk(KERN_ERR "Dump taken by platform is not valid\n");
@@ -828,7 +828,7 @@ static void fadump_setup_crash_memory_ranges(void)
828828
static inline unsigned long fadump_relocate(unsigned long paddr)
829829
{
830830
if (paddr > RMA_START && paddr < fw_dump.boot_memory_size)
831-
return fdm.rmr_region.destination_address + paddr;
831+
return be64_to_cpu(fdm.rmr_region.destination_address) + paddr;
832832
else
833833
return paddr;
834834
}
@@ -902,7 +902,7 @@ static int fadump_create_elfcore_headers(char *bufp)
902902
* to the specified destination_address. Hence set
903903
* the correct offset.
904904
*/
905-
phdr->p_offset = fdm.rmr_region.destination_address;
905+
phdr->p_offset = be64_to_cpu(fdm.rmr_region.destination_address);
906906
}
907907

908908
phdr->p_paddr = mbase;
@@ -951,7 +951,7 @@ static void register_fadump(void)
951951

952952
fadump_setup_crash_memory_ranges();
953953

954-
addr = fdm.rmr_region.destination_address + fdm.rmr_region.source_len;
954+
addr = be64_to_cpu(fdm.rmr_region.destination_address) + be64_to_cpu(fdm.rmr_region.source_len);
955955
/* Initialize fadump crash info header. */
956956
addr = init_fadump_header(addr);
957957
vaddr = __va(addr);
@@ -1023,7 +1023,7 @@ void fadump_cleanup(void)
10231023
/* Invalidate the registration only if dump is active. */
10241024
if (fw_dump.dump_active) {
10251025
init_fadump_mem_struct(&fdm,
1026-
fdm_active->cpu_state_data.destination_address);
1026+
be64_to_cpu(fdm_active->cpu_state_data.destination_address));
10271027
fadump_invalidate_dump(&fdm);
10281028
}
10291029
}
@@ -1063,7 +1063,7 @@ static void fadump_invalidate_release_mem(void)
10631063
return;
10641064
}
10651065

1066-
destination_address = fdm_active->cpu_state_data.destination_address;
1066+
destination_address = be64_to_cpu(fdm_active->cpu_state_data.destination_address);
10671067
fadump_cleanup();
10681068
mutex_unlock(&fadump_mutex);
10691069

@@ -1183,42 +1183,42 @@ static int fadump_region_show(struct seq_file *m, void *private)
11831183
seq_printf(m,
11841184
"CPU : [%#016llx-%#016llx] %#llx bytes, "
11851185
"Dumped: %#llx\n",
1186-
fdm_ptr->cpu_state_data.destination_address,
1187-
fdm_ptr->cpu_state_data.destination_address +
1188-
fdm_ptr->cpu_state_data.source_len - 1,
1189-
fdm_ptr->cpu_state_data.source_len,
1190-
fdm_ptr->cpu_state_data.bytes_dumped);
1186+
be64_to_cpu(fdm_ptr->cpu_state_data.destination_address),
1187+
be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) +
1188+
be64_to_cpu(fdm_ptr->cpu_state_data.source_len) - 1,
1189+
be64_to_cpu(fdm_ptr->cpu_state_data.source_len),
1190+
be64_to_cpu(fdm_ptr->cpu_state_data.bytes_dumped));
11911191
seq_printf(m,
11921192
"HPTE: [%#016llx-%#016llx] %#llx bytes, "
11931193
"Dumped: %#llx\n",
1194-
fdm_ptr->hpte_region.destination_address,
1195-
fdm_ptr->hpte_region.destination_address +
1196-
fdm_ptr->hpte_region.source_len - 1,
1197-
fdm_ptr->hpte_region.source_len,
1198-
fdm_ptr->hpte_region.bytes_dumped);
1194+
be64_to_cpu(fdm_ptr->hpte_region.destination_address),
1195+
be64_to_cpu(fdm_ptr->hpte_region.destination_address) +
1196+
be64_to_cpu(fdm_ptr->hpte_region.source_len) - 1,
1197+
be64_to_cpu(fdm_ptr->hpte_region.source_len),
1198+
be64_to_cpu(fdm_ptr->hpte_region.bytes_dumped));
11991199
seq_printf(m,
12001200
"DUMP: [%#016llx-%#016llx] %#llx bytes, "
12011201
"Dumped: %#llx\n",
1202-
fdm_ptr->rmr_region.destination_address,
1203-
fdm_ptr->rmr_region.destination_address +
1204-
fdm_ptr->rmr_region.source_len - 1,
1205-
fdm_ptr->rmr_region.source_len,
1206-
fdm_ptr->rmr_region.bytes_dumped);
1202+
be64_to_cpu(fdm_ptr->rmr_region.destination_address),
1203+
be64_to_cpu(fdm_ptr->rmr_region.destination_address) +
1204+
be64_to_cpu(fdm_ptr->rmr_region.source_len) - 1,
1205+
be64_to_cpu(fdm_ptr->rmr_region.source_len),
1206+
be64_to_cpu(fdm_ptr->rmr_region.bytes_dumped));
12071207

12081208
if (!fdm_active ||
12091209
(fw_dump.reserve_dump_area_start ==
1210-
fdm_ptr->cpu_state_data.destination_address))
1210+
be64_to_cpu(fdm_ptr->cpu_state_data.destination_address)))
12111211
goto out;
12121212

12131213
/* Dump is active. Show reserved memory region. */
12141214
seq_printf(m,
12151215
" : [%#016llx-%#016llx] %#llx bytes, "
12161216
"Dumped: %#llx\n",
12171217
(unsigned long long)fw_dump.reserve_dump_area_start,
1218-
fdm_ptr->cpu_state_data.destination_address - 1,
1219-
fdm_ptr->cpu_state_data.destination_address -
1218+
be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) - 1,
1219+
be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) -
12201220
fw_dump.reserve_dump_area_start,
1221-
fdm_ptr->cpu_state_data.destination_address -
1221+
be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) -
12221222
fw_dump.reserve_dump_area_start);
12231223
out:
12241224
if (fdm_active)

arch/powerpc/platforms/pseries/lpar.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <asm/trace.h>
4444
#include <asm/firmware.h>
4545
#include <asm/plpar_wrappers.h>
46+
#include <asm/fadump.h>
4647

4748
#include "pseries.h"
4849

@@ -247,8 +248,17 @@ static void pSeries_lpar_hptab_clear(void)
247248
}
248249

249250
#ifdef __LITTLE_ENDIAN__
250-
/* Reset exceptions to big endian */
251-
if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
251+
/*
252+
* Reset exceptions to big endian.
253+
*
254+
* FIXME this is a hack for kexec, we need to reset the exception
255+
* endian before starting the new kernel and this is a convenient place
256+
* to do it.
257+
*
258+
* This is also called on boot when a fadump happens. In that case we
259+
* must not change the exception endian mode.
260+
*/
261+
if (firmware_has_feature(FW_FEATURE_SET_MODE) && !is_fadump_active()) {
252262
long rc;
253263

254264
rc = pseries_big_endian_exceptions();

0 commit comments

Comments
 (0)