mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-16 07:24:39 +08:00
247 lines
7.0 KiB
C
247 lines
7.0 KiB
C
|
/*
|
||
|
* Firmware Assisted dump: A robust mechanism to get reliable kernel crash
|
||
|
* dump with assistance from firmware. This approach does not use kexec,
|
||
|
* instead firmware assists in booting the kdump kernel while preserving
|
||
|
* memory contents. The most of the code implementation has been adapted
|
||
|
* from phyp assisted dump implementation written by Linas Vepstas and
|
||
|
* Manish Ahuja
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
|
*
|
||
|
* Copyright 2011 IBM Corporation
|
||
|
* Author: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
|
||
|
*/
|
||
|
|
||
|
#undef DEBUG
|
||
|
#define pr_fmt(fmt) "fadump: " fmt
|
||
|
|
||
|
#include <linux/string.h>
|
||
|
#include <linux/memblock.h>
|
||
|
|
||
|
#include <asm/page.h>
|
||
|
#include <asm/prom.h>
|
||
|
#include <asm/rtas.h>
|
||
|
#include <asm/fadump.h>
|
||
|
|
||
|
static struct fw_dump fw_dump;
|
||
|
|
||
|
/* Scan the Firmware Assisted dump configuration details. */
|
||
|
int __init early_init_dt_scan_fw_dump(unsigned long node,
|
||
|
const char *uname, int depth, void *data)
|
||
|
{
|
||
|
__be32 *sections;
|
||
|
int i, num_sections;
|
||
|
unsigned long size;
|
||
|
const int *token;
|
||
|
|
||
|
if (depth != 1 || strcmp(uname, "rtas") != 0)
|
||
|
return 0;
|
||
|
|
||
|
/*
|
||
|
* Check if Firmware Assisted dump is supported. if yes, check
|
||
|
* if dump has been initiated on last reboot.
|
||
|
*/
|
||
|
token = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL);
|
||
|
if (!token)
|
||
|
return 0;
|
||
|
|
||
|
fw_dump.fadump_supported = 1;
|
||
|
fw_dump.ibm_configure_kernel_dump = *token;
|
||
|
|
||
|
/*
|
||
|
* The 'ibm,kernel-dump' rtas node is present only if there is
|
||
|
* dump data waiting for us.
|
||
|
*/
|
||
|
if (of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL))
|
||
|
fw_dump.dump_active = 1;
|
||
|
|
||
|
/* Get the sizes required to store dump data for the firmware provided
|
||
|
* dump sections.
|
||
|
* For each dump section type supported, a 32bit cell which defines
|
||
|
* the ID of a supported section followed by two 32 bit cells which
|
||
|
* gives teh size of the section in bytes.
|
||
|
*/
|
||
|
sections = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes",
|
||
|
&size);
|
||
|
|
||
|
if (!sections)
|
||
|
return 0;
|
||
|
|
||
|
num_sections = size / (3 * sizeof(u32));
|
||
|
|
||
|
for (i = 0; i < num_sections; i++, sections += 3) {
|
||
|
u32 type = (u32)of_read_number(sections, 1);
|
||
|
|
||
|
switch (type) {
|
||
|
case FADUMP_CPU_STATE_DATA:
|
||
|
fw_dump.cpu_state_data_size =
|
||
|
of_read_ulong(§ions[1], 2);
|
||
|
break;
|
||
|
case FADUMP_HPTE_REGION:
|
||
|
fw_dump.hpte_region_size =
|
||
|
of_read_ulong(§ions[1], 2);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* fadump_calculate_reserve_size(): reserve variable boot area 5% of System RAM
|
||
|
*
|
||
|
* Function to find the largest memory size we need to reserve during early
|
||
|
* boot process. This will be the size of the memory that is required for a
|
||
|
* kernel to boot successfully.
|
||
|
*
|
||
|
* This function has been taken from phyp-assisted dump feature implementation.
|
||
|
*
|
||
|
* returns larger of 256MB or 5% rounded down to multiples of 256MB.
|
||
|
*
|
||
|
* TODO: Come up with better approach to find out more accurate memory size
|
||
|
* that is required for a kernel to boot successfully.
|
||
|
*
|
||
|
*/
|
||
|
static inline unsigned long fadump_calculate_reserve_size(void)
|
||
|
{
|
||
|
unsigned long size;
|
||
|
|
||
|
/*
|
||
|
* Check if the size is specified through fadump_reserve_mem= cmdline
|
||
|
* option. If yes, then use that.
|
||
|
*/
|
||
|
if (fw_dump.reserve_bootvar)
|
||
|
return fw_dump.reserve_bootvar;
|
||
|
|
||
|
/* divide by 20 to get 5% of value */
|
||
|
size = memblock_end_of_DRAM() / 20;
|
||
|
|
||
|
/* round it down in multiples of 256 */
|
||
|
size = size & ~0x0FFFFFFFUL;
|
||
|
|
||
|
/* Truncate to memory_limit. We don't want to over reserve the memory.*/
|
||
|
if (memory_limit && size > memory_limit)
|
||
|
size = memory_limit;
|
||
|
|
||
|
return (size > MIN_BOOT_MEM ? size : MIN_BOOT_MEM);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Calculate the total memory size required to be reserved for
|
||
|
* firmware-assisted dump registration.
|
||
|
*/
|
||
|
static unsigned long get_fadump_area_size(void)
|
||
|
{
|
||
|
unsigned long size = 0;
|
||
|
|
||
|
size += fw_dump.cpu_state_data_size;
|
||
|
size += fw_dump.hpte_region_size;
|
||
|
size += fw_dump.boot_memory_size;
|
||
|
|
||
|
size = PAGE_ALIGN(size);
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
int __init fadump_reserve_mem(void)
|
||
|
{
|
||
|
unsigned long base, size, memory_boundary;
|
||
|
|
||
|
if (!fw_dump.fadump_enabled)
|
||
|
return 0;
|
||
|
|
||
|
if (!fw_dump.fadump_supported) {
|
||
|
printk(KERN_INFO "Firmware-assisted dump is not supported on"
|
||
|
" this hardware\n");
|
||
|
fw_dump.fadump_enabled = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
/* Initialize boot memory size */
|
||
|
fw_dump.boot_memory_size = fadump_calculate_reserve_size();
|
||
|
|
||
|
/*
|
||
|
* Calculate the memory boundary.
|
||
|
* If memory_limit is less than actual memory boundary then reserve
|
||
|
* the memory for fadump beyond the memory_limit and adjust the
|
||
|
* memory_limit accordingly, so that the running kernel can run with
|
||
|
* specified memory_limit.
|
||
|
*/
|
||
|
if (memory_limit && memory_limit < memblock_end_of_DRAM()) {
|
||
|
size = get_fadump_area_size();
|
||
|
if ((memory_limit + size) < memblock_end_of_DRAM())
|
||
|
memory_limit += size;
|
||
|
else
|
||
|
memory_limit = memblock_end_of_DRAM();
|
||
|
printk(KERN_INFO "Adjusted memory_limit for firmware-assisted"
|
||
|
" dump, now %#016llx\n",
|
||
|
(unsigned long long)memory_limit);
|
||
|
}
|
||
|
if (memory_limit)
|
||
|
memory_boundary = memory_limit;
|
||
|
else
|
||
|
memory_boundary = memblock_end_of_DRAM();
|
||
|
|
||
|
if (fw_dump.dump_active) {
|
||
|
printk(KERN_INFO "Firmware-assisted dump is active.\n");
|
||
|
/*
|
||
|
* If last boot has crashed then reserve all the memory
|
||
|
* above boot_memory_size so that we don't touch it until
|
||
|
* dump is written to disk by userspace tool. This memory
|
||
|
* will be released for general use once the dump is saved.
|
||
|
*/
|
||
|
base = fw_dump.boot_memory_size;
|
||
|
size = memory_boundary - base;
|
||
|
memblock_reserve(base, size);
|
||
|
printk(KERN_INFO "Reserved %ldMB of memory at %ldMB "
|
||
|
"for saving crash dump\n",
|
||
|
(unsigned long)(size >> 20),
|
||
|
(unsigned long)(base >> 20));
|
||
|
} else {
|
||
|
/* Reserve the memory at the top of memory. */
|
||
|
size = get_fadump_area_size();
|
||
|
base = memory_boundary - size;
|
||
|
memblock_reserve(base, size);
|
||
|
printk(KERN_INFO "Reserved %ldMB of memory at %ldMB "
|
||
|
"for firmware-assisted dump\n",
|
||
|
(unsigned long)(size >> 20),
|
||
|
(unsigned long)(base >> 20));
|
||
|
}
|
||
|
fw_dump.reserve_dump_area_start = base;
|
||
|
fw_dump.reserve_dump_area_size = size;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* Look for fadump= cmdline option. */
|
||
|
static int __init early_fadump_param(char *p)
|
||
|
{
|
||
|
if (!p)
|
||
|
return 1;
|
||
|
|
||
|
if (strncmp(p, "on", 2) == 0)
|
||
|
fw_dump.fadump_enabled = 1;
|
||
|
else if (strncmp(p, "off", 3) == 0)
|
||
|
fw_dump.fadump_enabled = 0;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
early_param("fadump", early_fadump_param);
|
||
|
|
||
|
/* Look for fadump_reserve_mem= cmdline option */
|
||
|
static int __init early_fadump_reserve_mem(char *p)
|
||
|
{
|
||
|
if (p)
|
||
|
fw_dump.reserve_bootvar = memparse(p, &p);
|
||
|
return 0;
|
||
|
}
|
||
|
early_param("fadump_reserve_mem", early_fadump_reserve_mem);
|