* [PATCH 03/18] pseries/fadump: move out platform specific support from generic code
From: Hari Bathini @ 2019-02-21 17:35 UTC (permalink / raw)
To: Ananth N Mavinakayanahalli, Michael Ellerman, Mahesh J Salgaonkar,
Vasant Hegde, linuxppc-dev, Stewart Smith
In-Reply-To: <155077048463.21014.13936958730316555495.stgit@hbathini.in.ibm.com>
Introduce callbacks for platform specific operations like register,
unregister, invalidate & such, and move pseries specific code into
platform code.
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
---
arch/powerpc/include/asm/fadump.h | 75 ---
arch/powerpc/kernel/fadump.c | 501 +--------------------
arch/powerpc/kernel/fadump_internal.h | 39 ++
arch/powerpc/platforms/pseries/Makefile | 1
arch/powerpc/platforms/pseries/pseries_fadump.c | 538 +++++++++++++++++++++++
arch/powerpc/platforms/pseries/pseries_fadump.h | 96 ++++
6 files changed, 711 insertions(+), 539 deletions(-)
create mode 100644 arch/powerpc/platforms/pseries/pseries_fadump.c
create mode 100644 arch/powerpc/platforms/pseries/pseries_fadump.h
diff --git a/arch/powerpc/include/asm/fadump.h b/arch/powerpc/include/asm/fadump.h
index 028a8ef..d27cde7 100644
--- a/arch/powerpc/include/asm/fadump.h
+++ b/arch/powerpc/include/asm/fadump.h
@@ -24,79 +24,8 @@
#ifdef CONFIG_FA_DUMP
-/* Firmware provided dump sections */
-#define FADUMP_CPU_STATE_DATA 0x0001
-#define FADUMP_HPTE_REGION 0x0002
-#define FADUMP_REAL_MODE_REGION 0x0011
-
-/* Dump request flag */
-#define FADUMP_REQUEST_FLAG 0x00000001
-
-/* Dump status flag */
-#define FADUMP_ERROR_FLAG 0x2000
-
-/* Utility macros */
-#define SKIP_TO_NEXT_CPU(reg_entry) \
-({ \
- while (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUEND")) \
- reg_entry++; \
- reg_entry++; \
-})
-
extern int crashing_cpu;
-/* Kernel Dump section info */
-struct fadump_section {
- __be32 request_flag;
- __be16 source_data_type;
- __be16 error_flags;
- __be64 source_address;
- __be64 source_len;
- __be64 bytes_dumped;
- __be64 destination_address;
-};
-
-/* ibm,configure-kernel-dump header. */
-struct fadump_section_header {
- __be32 dump_format_version;
- __be16 dump_num_sections;
- __be16 dump_status_flag;
- __be32 offset_first_dump_section;
-
- /* Fields for disk dump option. */
- __be32 dd_block_size;
- __be64 dd_block_offset;
- __be64 dd_num_blocks;
- __be32 dd_offset_disk_path;
-
- /* Maximum time allowed to prevent an automatic dump-reboot. */
- __be32 max_time_auto;
-};
-
-/*
- * Firmware Assisted dump memory structure. This structure is required for
- * registering future kernel dump with power firmware through rtas call.
- *
- * No disk dump option. Hence disk dump path string section is not included.
- */
-struct fadump_mem_struct {
- struct fadump_section_header header;
-
- /* Kernel dump sections */
- struct fadump_section cpu_state_data;
- struct fadump_section hpte_region;
- struct fadump_section rmr_region;
-};
-
-#define REGSAVE_AREA_MAGIC STR_TO_HEX("REGSAVE")
-
-/* Register save area header. */
-struct fadump_reg_save_area_header {
- __be64 magic_number;
- __be32 version;
- __be32 num_cpu_offset;
-};
-
extern int is_fadump_memory_area(u64 addr, ulong size);
extern int early_init_dt_scan_fw_dump(unsigned long node, const char *uname,
int depth, void *data);
@@ -111,5 +40,5 @@ extern void fadump_cleanup(void);
static inline int is_fadump_active(void) { return 0; }
static inline int should_fadump_crash(void) { return 0; }
static inline void crash_fadump(struct pt_regs *regs, const char *str) { }
-#endif
-#endif
+#endif /* !CONFIG_FA_DUMP */
+#endif /* __PPC64_FA_DUMP_H__ */
diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c
index fcd50bd..eb88447 100644
--- a/arch/powerpc/kernel/fadump.c
+++ b/arch/powerpc/kernel/fadump.c
@@ -40,15 +40,12 @@
#include <asm/debugfs.h>
#include <asm/page.h>
#include <asm/prom.h>
-#include <asm/rtas.h>
#include <asm/fadump.h>
#include <asm/setup.h>
#include "fadump_internal.h"
static struct fw_dump fw_dump;
-static struct fadump_mem_struct fdm;
-static const struct fadump_mem_struct *fdm_active;
static DEFINE_MUTEX(fadump_mutex);
struct fad_crash_memory_ranges *crash_memory_ranges;
@@ -123,63 +120,13 @@ static int __init fadump_cma_init(void) { return 1; }
int __init early_init_dt_scan_fw_dump(unsigned long node, const char *uname,
int depth, void *data)
{
- const __be32 *sections;
- int i, num_sections;
- int size;
- const __be32 *token;
-
- if (depth != 1 || strcmp(uname, "rtas") != 0)
+ if (depth != 1)
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 1;
-
- fw_dump.fadump_supported = 1;
- fw_dump.ibm_configure_kernel_dump = be32_to_cpu(*token);
-
- /*
- * The 'ibm,kernel-dump' rtas node is present only if there is
- * dump data waiting for us.
- */
- fdm_active = of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL);
- if (fdm_active)
- 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 1;
-
- num_sections = size / (3 * sizeof(u32));
+ if (strcmp(uname, "rtas") == 0)
+ return pseries_dt_scan_fadump(&fw_dump, node);
- 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;
+ return 0;
}
/*
@@ -231,61 +178,6 @@ static void fadump_show_config(void)
pr_debug("Boot memory size : %lx\n", fw_dump.boot_memory_size);
}
-static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm,
- unsigned long addr)
-{
- if (!fdm)
- return 0;
-
- memset(fdm, 0, sizeof(struct fadump_mem_struct));
- addr = addr & PAGE_MASK;
-
- fdm->header.dump_format_version = cpu_to_be32(0x00000001);
- fdm->header.dump_num_sections = cpu_to_be16(3);
- fdm->header.dump_status_flag = 0;
- fdm->header.offset_first_dump_section =
- cpu_to_be32((u32)offsetof(struct fadump_mem_struct, cpu_state_data));
-
- /*
- * Fields for disk dump option.
- * We are not using disk dump option, hence set these fields to 0.
- */
- fdm->header.dd_block_size = 0;
- fdm->header.dd_block_offset = 0;
- fdm->header.dd_num_blocks = 0;
- fdm->header.dd_offset_disk_path = 0;
-
- /* set 0 to disable an automatic dump-reboot. */
- fdm->header.max_time_auto = 0;
-
- /* Kernel dump sections */
- /* cpu state data section. */
- fdm->cpu_state_data.request_flag = cpu_to_be32(FADUMP_REQUEST_FLAG);
- fdm->cpu_state_data.source_data_type = cpu_to_be16(FADUMP_CPU_STATE_DATA);
- fdm->cpu_state_data.source_address = 0;
- fdm->cpu_state_data.source_len = cpu_to_be64(fw_dump.cpu_state_data_size);
- fdm->cpu_state_data.destination_address = cpu_to_be64(addr);
- addr += fw_dump.cpu_state_data_size;
-
- /* hpte region section */
- fdm->hpte_region.request_flag = cpu_to_be32(FADUMP_REQUEST_FLAG);
- fdm->hpte_region.source_data_type = cpu_to_be16(FADUMP_HPTE_REGION);
- fdm->hpte_region.source_address = 0;
- fdm->hpte_region.source_len = cpu_to_be64(fw_dump.hpte_region_size);
- fdm->hpte_region.destination_address = cpu_to_be64(addr);
- addr += fw_dump.hpte_region_size;
-
- /* RMA region section */
- fdm->rmr_region.request_flag = cpu_to_be32(FADUMP_REQUEST_FLAG);
- fdm->rmr_region.source_data_type = cpu_to_be16(FADUMP_REAL_MODE_REGION);
- fdm->rmr_region.source_address = cpu_to_be64(RMA_START);
- fdm->rmr_region.source_len = cpu_to_be64(fw_dump.boot_memory_size);
- fdm->rmr_region.destination_address = cpu_to_be64(addr);
- addr += fw_dump.boot_memory_size;
-
- return addr;
-}
-
/**
* fadump_calculate_reserve_size(): reserve variable boot area 5% of System RAM
*
@@ -416,8 +308,8 @@ int __init fadump_reserve_mem(void)
* If dump is active then we have already calculated the size during
* first kernel.
*/
- if (fdm_active)
- fw_dump.boot_memory_size = be64_to_cpu(fdm_active->rmr_region.source_len);
+ if (fw_dump.dump_active)
+ fw_dump.boot_memory_size = fw_dump.rmr_source_len;
else {
fw_dump.boot_memory_size = fadump_calculate_reserve_size();
#ifdef CONFIG_CMA
@@ -426,8 +318,11 @@ int __init fadump_reserve_mem(void)
ALIGN(fw_dump.boot_memory_size,
FADUMP_CMA_ALIGNMENT);
#endif
+ fw_dump.rmr_source_len = fw_dump.boot_memory_size;
}
+ size = get_fadump_area_size();
+
/*
* Calculate the memory boundary.
* If memory_limit is less than actual memory boundary then reserve
@@ -436,13 +331,11 @@ int __init fadump_reserve_mem(void)
* 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", memory_limit);
+ pr_info("memory_limit adjusted to %#016llx\n", memory_limit);
}
if (memory_limit)
memory_boundary = memory_limit;
@@ -450,8 +343,6 @@ int __init fadump_reserve_mem(void)
memory_boundary = memblock_end_of_DRAM();
if (fw_dump.dump_active) {
- pr_info("Firmware-assisted dump is active.\n");
-
#ifdef CONFIG_HUGETLB_PAGE
/*
* FADump capture kernel doesn't care much about hugepages.
@@ -470,15 +361,11 @@ int __init fadump_reserve_mem(void)
size = memory_boundary - base;
fadump_reserve_crash_area(base, size);
- fw_dump.fadumphdr_addr =
- be64_to_cpu(fdm_active->rmr_region.destination_address) +
- be64_to_cpu(fdm_active->rmr_region.source_len);
- pr_debug("fadumphdr_addr = %pa\n", &fw_dump.fadumphdr_addr);
+ fw_dump.fadumphdr_addr = fw_dump.meta_area_start;
+ pr_debug("fadumphdr_addr = %#016lx\n", fw_dump.fadumphdr_addr);
fw_dump.reserve_dump_area_start = base;
fw_dump.reserve_dump_area_size = size;
} else {
- size = get_fadump_area_size();
-
/*
* Reserve memory at an offset closer to bottom of the RAM to
* minimize the impact of memory hot-remove operation. We can't
@@ -548,61 +435,6 @@ static int __init early_fadump_reserve_mem(char *p)
}
early_param("fadump_reserve_mem", early_fadump_reserve_mem);
-static int register_fw_dump(struct fadump_mem_struct *fdm)
-{
- int rc, err;
- unsigned int wait_time;
-
- pr_debug("Registering for firmware-assisted kernel dump...\n");
-
- /* TODO: Add upper time limit for the delay */
- do {
- rc = rtas_call(fw_dump.ibm_configure_kernel_dump, 3, 1, NULL,
- FADUMP_REGISTER, fdm,
- sizeof(struct fadump_mem_struct));
-
- wait_time = rtas_busy_delay_time(rc);
- if (wait_time)
- mdelay(wait_time);
-
- } while (wait_time);
-
- err = -EIO;
- switch (rc) {
- default:
- pr_err("Failed to register. Unknown Error(%d).\n", rc);
- break;
- case -1:
- printk(KERN_ERR "Failed to register firmware-assisted kernel"
- " dump. Hardware Error(%d).\n", rc);
- break;
- case -3:
- if (!is_boot_memory_area_contiguous(&fw_dump))
- pr_err("Can't have holes in boot memory area while registering fadump\n");
- else if (!is_reserved_memory_area_contiguous(&fw_dump))
- pr_err("Can't have holes in reserved memory area while"
- " registering fadump\n");
-
- printk(KERN_ERR "Failed to register firmware-assisted kernel"
- " dump. Parameter Error(%d).\n", rc);
- err = -EINVAL;
- break;
- case -9:
- printk(KERN_ERR "firmware-assisted kernel dump is already "
- " registered.");
- fw_dump.dump_registered = 1;
- err = -EEXIST;
- break;
- case 0:
- printk(KERN_INFO "firmware-assisted kernel dump registration"
- " is successful\n");
- fw_dump.dump_registered = 1;
- err = 0;
- break;
- }
- return err;
-}
-
void crash_fadump(struct pt_regs *regs, const char *str)
{
struct fadump_crash_info_header *fdh = NULL;
@@ -645,173 +477,7 @@ void crash_fadump(struct pt_regs *regs, const char *str)
fdh->online_mask = *cpu_online_mask;
- /* Call ibm,os-term rtas call to trigger firmware assisted dump */
- rtas_os_term((char *)str);
-}
-
-static struct fadump_reg_entry*
-fadump_read_registers(struct fadump_reg_entry *reg_entry, struct pt_regs *regs)
-{
- memset(regs, 0, sizeof(struct pt_regs));
-
- while (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUEND")) {
- fadump_set_regval(regs, be64_to_cpu(reg_entry->reg_id),
- be64_to_cpu(reg_entry->reg_value));
- reg_entry++;
- }
- reg_entry++;
- return reg_entry;
-}
-
-/*
- * Read CPU state dump data and convert it into ELF notes.
- * The CPU dump starts with magic number "REGSAVE". NumCpusOffset should be
- * used to access the data to allow for additional fields to be added without
- * affecting compatibility. Each list of registers for a CPU starts with
- * "CPUSTRT" and ends with "CPUEND". Each register entry is of 16 bytes,
- * 8 Byte ASCII identifier and 8 Byte register value. The register entry
- * with identifier "CPUSTRT" and "CPUEND" contains 4 byte cpu id as part
- * of register value. For more details refer to PAPR document.
- *
- * Only for the crashing cpu we ignore the CPU dump data and get exact
- * state from fadump crash info structure populated by first kernel at the
- * time of crash.
- */
-static int __init fadump_build_cpu_notes(const struct fadump_mem_struct *fdm)
-{
- struct fadump_reg_save_area_header *reg_header;
- struct fadump_reg_entry *reg_entry;
- struct fadump_crash_info_header *fdh = NULL;
- void *vaddr;
- unsigned long addr;
- u32 num_cpus, *note_buf;
- struct pt_regs regs;
- int i, rc = 0, cpu = 0;
-
- if (!fdm->cpu_state_data.bytes_dumped)
- return -EINVAL;
-
- addr = be64_to_cpu(fdm->cpu_state_data.destination_address);
- vaddr = __va(addr);
-
- reg_header = vaddr;
- if (be64_to_cpu(reg_header->magic_number) != REGSAVE_AREA_MAGIC) {
- printk(KERN_ERR "Unable to read register save area.\n");
- return -ENOENT;
- }
- pr_debug("--------CPU State Data------------\n");
- pr_debug("Magic Number: %llx\n", be64_to_cpu(reg_header->magic_number));
- pr_debug("NumCpuOffset: %x\n", be32_to_cpu(reg_header->num_cpu_offset));
-
- vaddr += be32_to_cpu(reg_header->num_cpu_offset);
- num_cpus = be32_to_cpu(*((__be32 *)(vaddr)));
- pr_debug("NumCpus : %u\n", num_cpus);
- vaddr += sizeof(u32);
- reg_entry = (struct fadump_reg_entry *)vaddr;
-
- /* Allocate buffer to hold cpu crash notes. */
- fw_dump.cpu_notes_buf_size = num_cpus * sizeof(note_buf_t);
- fw_dump.cpu_notes_buf_size = PAGE_ALIGN(fw_dump.cpu_notes_buf_size);
- note_buf = fadump_cpu_notes_buf_alloc(fw_dump.cpu_notes_buf_size);
- if (!note_buf) {
- printk(KERN_ERR "Failed to allocate 0x%lx bytes for "
- "cpu notes buffer\n", fw_dump.cpu_notes_buf_size);
- return -ENOMEM;
- }
- fw_dump.cpu_notes_buf = __pa(note_buf);
-
- pr_debug("Allocated buffer for cpu notes of size %ld at %p\n",
- (num_cpus * sizeof(note_buf_t)), note_buf);
-
- if (fw_dump.fadumphdr_addr)
- fdh = __va(fw_dump.fadumphdr_addr);
-
- for (i = 0; i < num_cpus; i++) {
- if (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUSTRT")) {
- printk(KERN_ERR "Unable to read CPU state data\n");
- rc = -ENOENT;
- goto error_out;
- }
- /* Lower 4 bytes of reg_value contains logical cpu id */
- cpu = be64_to_cpu(reg_entry->reg_value) & FADUMP_CPU_ID_MASK;
- if (fdh && !cpumask_test_cpu(cpu, &fdh->online_mask)) {
- SKIP_TO_NEXT_CPU(reg_entry);
- continue;
- }
- pr_debug("Reading register data for cpu %d...\n", cpu);
- if (fdh && fdh->crashing_cpu == cpu) {
- regs = fdh->regs;
- note_buf = fadump_regs_to_elf_notes(note_buf, ®s);
- SKIP_TO_NEXT_CPU(reg_entry);
- } else {
- reg_entry++;
- reg_entry = fadump_read_registers(reg_entry, ®s);
- note_buf = fadump_regs_to_elf_notes(note_buf, ®s);
- }
- }
- final_note(note_buf);
-
- if (fdh) {
- addr = fdh->elfcorehdr_addr;
- pr_debug("Updating elfcore header(%lx) with cpu notes\n", addr);
- fadump_update_elfcore_header(&fw_dump, (char *)__va(addr));
- }
- return 0;
-
-error_out:
- fadump_cpu_notes_buf_free((unsigned long)__va(fw_dump.cpu_notes_buf),
- fw_dump.cpu_notes_buf_size);
- fw_dump.cpu_notes_buf = 0;
- fw_dump.cpu_notes_buf_size = 0;
- return rc;
-
-}
-
-/*
- * Validate and process the dump data stored by firmware before exporting
- * it through '/proc/vmcore'.
- */
-static int __init process_fadump(const struct fadump_mem_struct *fdm_active)
-{
- struct fadump_crash_info_header *fdh;
- int rc = 0;
-
- if (!fdm_active || !fw_dump.fadumphdr_addr)
- return -EINVAL;
-
- /* Check if the dump data is valid. */
- if ((be16_to_cpu(fdm_active->header.dump_status_flag) == FADUMP_ERROR_FLAG) ||
- (fdm_active->cpu_state_data.error_flags != 0) ||
- (fdm_active->rmr_region.error_flags != 0)) {
- printk(KERN_ERR "Dump taken by platform is not valid\n");
- return -EINVAL;
- }
- if ((fdm_active->rmr_region.bytes_dumped !=
- fdm_active->rmr_region.source_len) ||
- !fdm_active->cpu_state_data.bytes_dumped) {
- printk(KERN_ERR "Dump taken by platform is incomplete\n");
- return -EINVAL;
- }
-
- /* Validate the fadump crash info header */
- fdh = __va(fw_dump.fadumphdr_addr);
- if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) {
- printk(KERN_ERR "Crash info header is not valid.\n");
- return -EINVAL;
- }
-
- rc = fadump_build_cpu_notes(fdm_active);
- if (rc)
- return rc;
-
- /*
- * We are done validating dump info and elfcore header is now ready
- * to be exported. set elfcorehdr_addr so that vmcore module will
- * export the elfcore header through '/proc/vmcore'.
- */
- elfcorehdr_addr = fdh->elfcorehdr_addr;
-
- return 0;
+ fw_dump.ops->crash_fadump(fdh, str);
}
static void free_crash_memory_ranges(void)
@@ -1008,7 +674,7 @@ static int fadump_setup_crash_memory_ranges(void)
static inline unsigned long fadump_relocate(unsigned long paddr)
{
if (paddr > RMA_START && paddr < fw_dump.boot_memory_size)
- return be64_to_cpu(fdm.rmr_region.destination_address) + paddr;
+ return fw_dump.rmr_destination_addr + paddr;
else
return paddr;
}
@@ -1081,7 +747,7 @@ static int fadump_create_elfcore_headers(char *bufp)
* to the specified destination_address. Hence set
* the correct offset.
*/
- phdr->p_offset = be64_to_cpu(fdm.rmr_region.destination_address);
+ phdr->p_offset = fw_dump.rmr_destination_addr;
}
phdr->p_paddr = mbase;
@@ -1133,7 +799,8 @@ static int register_fadump(void)
if (ret)
return ret;
- addr = be64_to_cpu(fdm.rmr_region.destination_address) + be64_to_cpu(fdm.rmr_region.source_len);
+ addr = fw_dump.meta_area_start;
+
/* Initialize fadump crash info header. */
addr = init_fadump_header(addr);
vaddr = __va(addr);
@@ -1142,72 +809,19 @@ static int register_fadump(void)
fadump_create_elfcore_headers(vaddr);
/* register the future kernel dump with firmware. */
- return register_fw_dump(&fdm);
-}
-
-static int fadump_unregister_dump(struct fadump_mem_struct *fdm)
-{
- int rc = 0;
- unsigned int wait_time;
-
- pr_debug("Un-register firmware-assisted dump\n");
-
- /* TODO: Add upper time limit for the delay */
- do {
- rc = rtas_call(fw_dump.ibm_configure_kernel_dump, 3, 1, NULL,
- FADUMP_UNREGISTER, fdm,
- sizeof(struct fadump_mem_struct));
-
- wait_time = rtas_busy_delay_time(rc);
- if (wait_time)
- mdelay(wait_time);
- } while (wait_time);
-
- if (rc) {
- printk(KERN_ERR "Failed to un-register firmware-assisted dump."
- " unexpected error(%d).\n", rc);
- return rc;
- }
- fw_dump.dump_registered = 0;
- return 0;
-}
-
-static int fadump_invalidate_dump(const struct fadump_mem_struct *fdm)
-{
- int rc = 0;
- unsigned int wait_time;
-
- pr_debug("Invalidating firmware-assisted dump registration\n");
-
- /* TODO: Add upper time limit for the delay */
- do {
- rc = rtas_call(fw_dump.ibm_configure_kernel_dump, 3, 1, NULL,
- FADUMP_INVALIDATE, fdm,
- sizeof(struct fadump_mem_struct));
-
- wait_time = rtas_busy_delay_time(rc);
- if (wait_time)
- mdelay(wait_time);
- } while (wait_time);
-
- if (rc) {
- pr_err("Failed to invalidate firmware-assisted dump registration. Unexpected error (%d).\n", rc);
- return rc;
- }
- fw_dump.dump_active = 0;
- fdm_active = NULL;
- return 0;
+ pr_debug("Registering for firmware-assisted kernel dump...\n");
+ return fw_dump.ops->register_fadump(&fw_dump);
}
void fadump_cleanup(void)
{
/* Invalidate the registration only if dump is active. */
if (fw_dump.dump_active) {
- /* pass the same memory dump structure provided by platform */
- fadump_invalidate_dump(fdm_active);
+ pr_debug("Invalidating firmware-assisted dump registration\n");
+ fw_dump.ops->invalidate_fadump(&fw_dump);
} else if (fw_dump.dump_registered) {
/* Un-register Firmware-assisted dump if it was registered. */
- fadump_unregister_dump(&fdm);
+ fw_dump.ops->unregister_fadump(&fw_dump);
free_crash_memory_ranges();
}
}
@@ -1290,7 +904,7 @@ static void fadump_invalidate_release_mem(void)
return;
}
- destination_address = be64_to_cpu(fdm_active->cpu_state_data.destination_address);
+ destination_address = fw_dump.preserv_area_start;
fadump_cleanup();
mutex_unlock(&fadump_mutex);
@@ -1316,8 +930,9 @@ static void fadump_invalidate_release_mem(void)
fw_dump.cpu_notes_buf = 0;
fw_dump.cpu_notes_buf_size = 0;
}
+
/* Initialize the kernel dump memory structure for FAD registration. */
- init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start);
+ fw_dump.ops->init_fadump_mem_struct(&fw_dump);
}
static ssize_t fadump_release_memory_store(struct kobject *kobj,
@@ -1368,7 +983,7 @@ static ssize_t fadump_register_store(struct kobject *kobj,
int ret = 0;
int input = -1;
- if (!fw_dump.fadump_enabled || fdm_active)
+ if (!fw_dump.fadump_enabled || fw_dump.dump_active)
return -EPERM;
if (kstrtoint(buf, 0, &input))
@@ -1381,13 +996,15 @@ static ssize_t fadump_register_store(struct kobject *kobj,
if (fw_dump.dump_registered == 0) {
goto unlock_out;
}
+
/* Un-register Firmware-assisted dump */
- fadump_unregister_dump(&fdm);
+ pr_debug("Un-register firmware-assisted dump\n");
+ fw_dump.ops->unregister_fadump(&fw_dump);
break;
case 1:
if (fw_dump.dump_registered == 1) {
/* Un-register Firmware-assisted dump */
- fadump_unregister_dump(&fdm);
+ fw_dump.ops->unregister_fadump(&fw_dump);
}
/* Register Firmware-assisted dump */
ret = register_fadump();
@@ -1404,62 +1021,13 @@ static ssize_t fadump_register_store(struct kobject *kobj,
static int fadump_region_show(struct seq_file *m, void *private)
{
- const struct fadump_mem_struct *fdm_ptr;
-
if (!fw_dump.fadump_enabled)
return 0;
mutex_lock(&fadump_mutex);
- if (fdm_active)
- fdm_ptr = fdm_active;
- else {
- mutex_unlock(&fadump_mutex);
- fdm_ptr = &fdm;
- }
+ fw_dump.ops->fadump_region_show(&fw_dump, m);
+ mutex_unlock(&fadump_mutex);
- seq_printf(m,
- "CPU : [%#016llx-%#016llx] %#llx bytes, "
- "Dumped: %#llx\n",
- be64_to_cpu(fdm_ptr->cpu_state_data.destination_address),
- be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) +
- be64_to_cpu(fdm_ptr->cpu_state_data.source_len) - 1,
- be64_to_cpu(fdm_ptr->cpu_state_data.source_len),
- be64_to_cpu(fdm_ptr->cpu_state_data.bytes_dumped));
- seq_printf(m,
- "HPTE: [%#016llx-%#016llx] %#llx bytes, "
- "Dumped: %#llx\n",
- be64_to_cpu(fdm_ptr->hpte_region.destination_address),
- be64_to_cpu(fdm_ptr->hpte_region.destination_address) +
- be64_to_cpu(fdm_ptr->hpte_region.source_len) - 1,
- be64_to_cpu(fdm_ptr->hpte_region.source_len),
- be64_to_cpu(fdm_ptr->hpte_region.bytes_dumped));
- seq_printf(m,
- "DUMP: [%#016llx-%#016llx] %#llx bytes, "
- "Dumped: %#llx\n",
- be64_to_cpu(fdm_ptr->rmr_region.destination_address),
- be64_to_cpu(fdm_ptr->rmr_region.destination_address) +
- be64_to_cpu(fdm_ptr->rmr_region.source_len) - 1,
- be64_to_cpu(fdm_ptr->rmr_region.source_len),
- be64_to_cpu(fdm_ptr->rmr_region.bytes_dumped));
-
- if (!fdm_active ||
- (fw_dump.reserve_dump_area_start ==
- be64_to_cpu(fdm_ptr->cpu_state_data.destination_address)))
- goto out;
-
- /* Dump is active. Show reserved memory region. */
- seq_printf(m,
- " : [%#016llx-%#016llx] %#llx bytes, "
- "Dumped: %#llx\n",
- (unsigned long long)fw_dump.reserve_dump_area_start,
- be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) - 1,
- be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) -
- fw_dump.reserve_dump_area_start,
- be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) -
- fw_dump.reserve_dump_area_start);
-out:
- if (fdm_active)
- mutex_unlock(&fadump_mutex);
return 0;
}
@@ -1530,12 +1098,13 @@ int __init setup_fadump(void)
* if dump process fails then invalidate the registration
* and release memory before proceeding for re-registration.
*/
- if (process_fadump(fdm_active) < 0)
+ if (fw_dump.ops->process_fadump(&fw_dump) < 0)
fadump_invalidate_release_mem();
}
/* Initialize the kernel dump memory structure for FAD registration. */
else if (fw_dump.reserve_dump_area_size)
- init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start);
+ fw_dump.ops->init_fadump_mem_struct(&fw_dump);
+
fadump_init_files();
return 1;
diff --git a/arch/powerpc/kernel/fadump_internal.h b/arch/powerpc/kernel/fadump_internal.h
index 8ccd96d..f926145 100644
--- a/arch/powerpc/kernel/fadump_internal.h
+++ b/arch/powerpc/kernel/fadump_internal.h
@@ -47,6 +47,12 @@
#define FADUMP_UNREGISTER 2
#define FADUMP_INVALIDATE 3
+/* Firmware-Assited Dump platforms */
+enum fadump_platform_type {
+ FADUMP_PLATFORM_UNKNOWN = 0,
+ FADUMP_PLATFORM_PSERIES,
+};
+
#define FADUMP_CPU_ID_MASK ((1UL << 32) - 1)
#define CPU_UNKNOWN (~((u32)0))
@@ -91,6 +97,9 @@ struct fad_crash_memory_ranges {
unsigned long long size;
};
+/* Platform specific callback functions */
+struct fadump_ops;
+
/* Firmware-assisted dump configuration details. */
struct fw_dump {
unsigned long cpu_state_data_size;
@@ -98,6 +107,8 @@ struct fw_dump {
unsigned long boot_memory_size;
unsigned long reserve_dump_area_start;
unsigned long reserve_dump_area_size;
+ unsigned long meta_area_start;
+ unsigned long preserv_area_start;
/* cmd line option during boot */
unsigned long reserve_bootvar;
@@ -105,6 +116,9 @@ struct fw_dump {
unsigned long cpu_notes_buf;
unsigned long cpu_notes_buf_size;
+ unsigned long rmr_source_len;
+ unsigned long rmr_destination_addr;
+
int ibm_configure_kernel_dump;
unsigned long fadump_enabled:1;
@@ -112,6 +126,21 @@ struct fw_dump {
unsigned long dump_active:1;
unsigned long dump_registered:1;
unsigned long nocma:1;
+
+ enum fadump_platform_type fadump_platform;
+ struct fadump_ops *ops;
+};
+
+struct fadump_ops {
+ ulong (*init_fadump_mem_struct)(struct fw_dump *fadump_config);
+ int (*register_fadump)(struct fw_dump *fadump_config);
+ int (*unregister_fadump)(struct fw_dump *fadump_config);
+ int (*invalidate_fadump)(struct fw_dump *fadump_config);
+ int (*process_fadump)(struct fw_dump *fadump_config);
+ void (*fadump_region_show)(struct fw_dump *fadump_config,
+ struct seq_file *m);
+ void (*crash_fadump)(struct fadump_crash_info_header *fdh,
+ const char *msg);
};
/* Helper functions */
@@ -123,4 +152,14 @@ void fadump_update_elfcore_header(struct fw_dump *fadump_config, char *bufp);
int is_boot_memory_area_contiguous(struct fw_dump *fadump_conf);
int is_reserved_memory_area_contiguous(struct fw_dump *fadump_conf);
+#ifdef CONFIG_PPC_PSERIES
+extern int pseries_dt_scan_fadump(struct fw_dump *fadump_config, ulong node);
+#else
+static inline int
+pseries_dt_scan_fadump(struct fw_dump *fadump_config, ulong node)
+{
+ return 1;
+}
+#endif
+
#endif /* __PPC64_FA_DUMP_INTERNAL_H__ */
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index a43ec84..dace1a4 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_LPARCFG) += lparcfg.o
obj-$(CONFIG_IBMVIO) += vio.o
obj-$(CONFIG_IBMEBUS) += ibmebus.o
obj-$(CONFIG_PAPR_SCM) += papr_scm.o
+obj-$(CONFIG_FA_DUMP) += pseries_fadump.o
ifdef CONFIG_PPC_PSERIES
obj-$(CONFIG_SUSPEND) += suspend.o
diff --git a/arch/powerpc/platforms/pseries/pseries_fadump.c b/arch/powerpc/platforms/pseries/pseries_fadump.c
new file mode 100644
index 0000000..e93a7a4
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/pseries_fadump.c
@@ -0,0 +1,538 @@
+/*
+ * Firmware-Assisted Dump support on POWERVM platform.
+ *
+ * Copyright 2011, IBM Corporation
+ * Author: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
+ *
+ * Copyright 2018-2019, IBM Corp.
+ * Author: Hari Bathini <hbathini@linux.vnet.ibm.com>
+ *
+ * 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.
+ */
+
+#undef DEBUG
+#define pr_fmt(fmt) "pseries fadump: " fmt
+
+#include <linux/string.h>
+#include <linux/memblock.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/crash_dump.h>
+
+#include <asm/page.h>
+#include <asm/prom.h>
+#include <asm/rtas.h>
+#include <asm/fadump.h>
+
+#include "../../kernel/fadump_internal.h"
+#include "pseries_fadump.h"
+
+static struct pseries_fadump_mem_struct fdm;
+static const struct pseries_fadump_mem_struct *fdm_active;
+
+static void pseries_set_preserv_area_start(struct fw_dump *fadump_conf)
+{
+ const struct pseries_fadump_mem_struct *fdm_ptr;
+
+ if (fdm_active)
+ fdm_ptr = fdm_active;
+ else
+ fdm_ptr = &fdm;
+
+ fadump_conf->preserv_area_start =
+ be64_to_cpu(fdm_ptr->cpu_state_data.destination_address);
+
+ pr_debug("Preserve area start address: 0x%lx\n",
+ fadump_conf->preserv_area_start);
+}
+
+static void pseries_set_meta_area_start(struct fw_dump *fadump_conf)
+{
+ fadump_conf->meta_area_start = (fadump_conf->rmr_destination_addr +
+ fadump_conf->rmr_source_len);
+
+ pr_debug("Meta area start address: 0x%lx\n",
+ fadump_conf->meta_area_start);
+}
+
+static void update_fadump_config(struct fw_dump *fadump_conf,
+ const struct pseries_fadump_mem_struct *fdm)
+{
+ fadump_conf->rmr_destination_addr =
+ be64_to_cpu(fdm->rmr_region.destination_address);
+
+ if (fadump_conf->dump_active) {
+ fadump_conf->rmr_source_len =
+ be64_to_cpu(fdm->rmr_region.source_len);
+ }
+
+ pseries_set_meta_area_start(fadump_conf);
+ pseries_set_preserv_area_start(fadump_conf);
+}
+
+static ulong pseries_init_fadump_mem_struct(struct fw_dump *fadump_conf)
+{
+ ulong addr = fadump_conf->reserve_dump_area_start;
+
+ memset(&fdm, 0, sizeof(struct pseries_fadump_mem_struct));
+ addr = addr & PAGE_MASK;
+
+ fdm.header.dump_format_version = cpu_to_be32(0x00000001);
+ fdm.header.dump_num_sections = cpu_to_be16(3);
+ fdm.header.dump_status_flag = 0;
+ fdm.header.offset_first_dump_section =
+ cpu_to_be32((u32)offsetof(struct pseries_fadump_mem_struct,
+ cpu_state_data));
+
+ /*
+ * Fields for disk dump option.
+ * We are not using disk dump option, hence set these fields to 0.
+ */
+ fdm.header.dd_block_size = 0;
+ fdm.header.dd_block_offset = 0;
+ fdm.header.dd_num_blocks = 0;
+ fdm.header.dd_offset_disk_path = 0;
+
+ /* set 0 to disable an automatic dump-reboot. */
+ fdm.header.max_time_auto = 0;
+
+ /* Kernel dump sections */
+ /* cpu state data section. */
+ fdm.cpu_state_data.request_flag =
+ cpu_to_be32(PSERIES_FADUMP_REQUEST_FLAG);
+ fdm.cpu_state_data.source_data_type =
+ cpu_to_be16(PSERIES_FADUMP_CPU_STATE_DATA);
+ fdm.cpu_state_data.source_address = 0;
+ fdm.cpu_state_data.source_len =
+ cpu_to_be64(fadump_conf->cpu_state_data_size);
+ fdm.cpu_state_data.destination_address = cpu_to_be64(addr);
+ addr += fadump_conf->cpu_state_data_size;
+
+ /* hpte region section */
+ fdm.hpte_region.request_flag = cpu_to_be32(PSERIES_FADUMP_REQUEST_FLAG);
+ fdm.hpte_region.source_data_type =
+ cpu_to_be16(PSERIES_FADUMP_HPTE_REGION);
+ fdm.hpte_region.source_address = 0;
+ fdm.hpte_region.source_len =
+ cpu_to_be64(fadump_conf->hpte_region_size);
+ fdm.hpte_region.destination_address = cpu_to_be64(addr);
+ addr += fadump_conf->hpte_region_size;
+
+ /* RMA region section */
+ fdm.rmr_region.request_flag = cpu_to_be32(PSERIES_FADUMP_REQUEST_FLAG);
+ fdm.rmr_region.source_data_type =
+ cpu_to_be16(PSERIES_FADUMP_REAL_MODE_REGION);
+ fdm.rmr_region.source_address = cpu_to_be64(RMA_START);
+ fdm.rmr_region.source_len =
+ cpu_to_be64(fadump_conf->boot_memory_size);
+ fdm.rmr_region.destination_address = cpu_to_be64(addr);
+ addr += fadump_conf->boot_memory_size;
+
+ update_fadump_config(fadump_conf, &fdm);
+
+ return addr;
+}
+
+static int pseries_register_fadump(struct fw_dump *fadump_conf)
+{
+ int rc, err;
+ unsigned int wait_time;
+
+ /* TODO: Add upper time limit for the delay */
+ do {
+ rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
+ NULL, FADUMP_REGISTER, &fdm,
+ sizeof(struct pseries_fadump_mem_struct));
+
+ wait_time = rtas_busy_delay_time(rc);
+ if (wait_time)
+ mdelay(wait_time);
+
+ } while (wait_time);
+
+ err = -EIO;
+ switch (rc) {
+ default:
+ pr_err("Failed to register. Unknown Error(%d).\n", rc);
+ break;
+ case -1:
+ pr_err("Failed to register. Hardware Error(%d).\n", rc);
+ break;
+ case -3:
+ if (!is_boot_memory_area_contiguous(fadump_conf))
+ pr_err("Can't hot-remove boot memory area.\n");
+ else if (!is_reserved_memory_area_contiguous(fadump_conf))
+ pr_err("Can't hot-remove reserved memory area.\n");
+
+ pr_err("Failed to register. Parameter Error(%d).\n", rc);
+ err = -EINVAL;
+ break;
+ case -9:
+ pr_err("Already registered!\n");
+ fadump_conf->dump_registered = 1;
+ err = -EEXIST;
+ break;
+ case 0:
+ pr_err("Registration is successful!\n");
+ fadump_conf->dump_registered = 1;
+ err = 0;
+ break;
+ }
+
+ return err;
+}
+
+static int pseries_unregister_fadump(struct fw_dump *fadump_conf)
+{
+ int rc = 0;
+ unsigned int wait_time;
+
+ /* TODO: Add upper time limit for the delay */
+ do {
+ rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
+ NULL, FADUMP_UNREGISTER, &fdm,
+ sizeof(struct pseries_fadump_mem_struct));
+
+ wait_time = rtas_busy_delay_time(rc);
+ if (wait_time)
+ mdelay(wait_time);
+ } while (wait_time);
+
+ if (rc) {
+ pr_err("Failed to un-register - unexpected error(%d).\n", rc);
+ return rc;
+ }
+
+ fadump_conf->dump_registered = 0;
+ return 0;
+}
+
+static int pseries_invalidate_fadump(struct fw_dump *fadump_conf)
+{
+ int rc = 0;
+ unsigned int wait_time;
+
+ /* TODO: Add upper time limit for the delay */
+ do {
+ rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
+ NULL, FADUMP_INVALIDATE, fdm_active,
+ sizeof(struct pseries_fadump_mem_struct));
+
+ wait_time = rtas_busy_delay_time(rc);
+ if (wait_time)
+ mdelay(wait_time);
+ } while (wait_time);
+
+ if (rc) {
+ pr_err("Failed to invalidate - unexpected error (%d).\n", rc);
+ return rc;
+ }
+
+ fadump_conf->dump_active = 0;
+ fdm_active = NULL;
+ return 0;
+}
+
+static struct fadump_reg_entry*
+fadump_read_registers(struct fadump_reg_entry *reg_entry, struct pt_regs *regs)
+{
+ memset(regs, 0, sizeof(struct pt_regs));
+
+ while (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUEND")) {
+ fadump_set_regval(regs, be64_to_cpu(reg_entry->reg_id),
+ be64_to_cpu(reg_entry->reg_value));
+ reg_entry++;
+ }
+ reg_entry++;
+ return reg_entry;
+}
+
+/*
+ * Read CPU state dump data and convert it into ELF notes.
+ * The CPU dump starts with magic number "REGSAVE". NumCpusOffset should be
+ * used to access the data to allow for additional fields to be added without
+ * affecting compatibility. Each list of registers for a CPU starts with
+ * "CPUSTRT" and ends with "CPUEND". Each register entry is of 16 bytes,
+ * 8 Byte ASCII identifier and 8 Byte register value. The register entry
+ * with identifier "CPUSTRT" and "CPUEND" contains 4 byte cpu id as part
+ * of register value. For more details refer to PAPR document.
+ *
+ * Only for the crashing cpu we ignore the CPU dump data and get exact
+ * state from fadump crash info structure populated by first kernel at the
+ * time of crash.
+ */
+static int __init fadump_build_cpu_notes(struct fw_dump *fadump_conf)
+{
+ struct fadump_reg_save_area_header *reg_header;
+ struct fadump_reg_entry *reg_entry;
+ struct fadump_crash_info_header *fdh = NULL;
+ void *vaddr;
+ unsigned long addr;
+ u32 num_cpus, *note_buf;
+ struct pt_regs regs;
+ int i, rc = 0, cpu = 0;
+
+ addr = be64_to_cpu(fdm_active->cpu_state_data.destination_address);
+ vaddr = __va(addr);
+
+ reg_header = vaddr;
+ if (be64_to_cpu(reg_header->magic_number) != REGSAVE_AREA_MAGIC) {
+ pr_err("Unable to read register save area.\n");
+ return -ENOENT;
+ }
+
+ pr_debug("--------CPU State Data------------\n");
+ pr_debug("Magic Number: %llx\n", be64_to_cpu(reg_header->magic_number));
+ pr_debug("NumCpuOffset: %x\n", be32_to_cpu(reg_header->num_cpu_offset));
+
+ vaddr += be32_to_cpu(reg_header->num_cpu_offset);
+ num_cpus = be32_to_cpu(*((__be32 *)(vaddr)));
+ pr_debug("NumCpus : %u\n", num_cpus);
+ vaddr += sizeof(u32);
+ reg_entry = (struct fadump_reg_entry *)vaddr;
+
+ /* Allocate buffer to hold cpu crash notes. */
+ fadump_conf->cpu_notes_buf_size = num_cpus * sizeof(note_buf_t);
+ fadump_conf->cpu_notes_buf_size =
+ PAGE_ALIGN(fadump_conf->cpu_notes_buf_size);
+ note_buf = fadump_cpu_notes_buf_alloc(fadump_conf->cpu_notes_buf_size);
+ if (!note_buf) {
+ pr_err("Failed to allocate 0x%lx bytes for cpu notes buffer\n",
+ fadump_conf->cpu_notes_buf_size);
+ return -ENOMEM;
+ }
+ fadump_conf->cpu_notes_buf = __pa(note_buf);
+
+ pr_debug("Allocated buffer for cpu notes of size %ld at %p\n",
+ (num_cpus * sizeof(note_buf_t)), note_buf);
+
+ if (fadump_conf->fadumphdr_addr)
+ fdh = __va(fadump_conf->fadumphdr_addr);
+
+ for (i = 0; i < num_cpus; i++) {
+ if (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUSTRT")) {
+ pr_err("Unable to read CPU state data\n");
+ rc = -ENOENT;
+ goto error_out;
+ }
+ /* Lower 4 bytes of reg_value contains logical cpu id */
+ cpu = be64_to_cpu(reg_entry->reg_value) & FADUMP_CPU_ID_MASK;
+ if (fdh && !cpumask_test_cpu(cpu, &fdh->online_mask)) {
+ SKIP_TO_NEXT_CPU(reg_entry);
+ continue;
+ }
+ pr_debug("Reading register data for cpu %d...\n", cpu);
+ if (fdh && fdh->crashing_cpu == cpu) {
+ regs = fdh->regs;
+ note_buf = fadump_regs_to_elf_notes(note_buf, ®s);
+ SKIP_TO_NEXT_CPU(reg_entry);
+ } else {
+ reg_entry++;
+ reg_entry = fadump_read_registers(reg_entry, ®s);
+ note_buf = fadump_regs_to_elf_notes(note_buf, ®s);
+ }
+ }
+ final_note(note_buf);
+
+ if (fdh) {
+ pr_debug("Updating elfcore header (%llx) with cpu notes\n",
+ fdh->elfcorehdr_addr);
+ fadump_update_elfcore_header(fadump_conf,
+ __va(fdh->elfcorehdr_addr));
+ }
+ return 0;
+
+error_out:
+ fadump_cpu_notes_buf_free((ulong)__va(fadump_conf->cpu_notes_buf),
+ fadump_conf->cpu_notes_buf_size);
+ fadump_conf->cpu_notes_buf = 0;
+ fadump_conf->cpu_notes_buf_size = 0;
+ return rc;
+
+}
+
+/*
+ * Validate and process the dump data stored by firmware before exporting
+ * it through '/proc/vmcore'.
+ */
+static int __init pseries_process_fadump(struct fw_dump *fadump_conf)
+{
+ struct fadump_crash_info_header *fdh;
+ int rc = 0;
+
+ if (!fdm_active || !fadump_conf->fadumphdr_addr)
+ return -EINVAL;
+
+ /* Check if the dump data is valid. */
+ if ((be16_to_cpu(fdm_active->header.dump_status_flag) ==
+ PSERIES_FADUMP_ERROR_FLAG) ||
+ (fdm_active->cpu_state_data.error_flags != 0) ||
+ (fdm_active->rmr_region.error_flags != 0)) {
+ pr_err("Dump taken by platform is not valid\n");
+ return -EINVAL;
+ }
+ if ((fdm_active->rmr_region.bytes_dumped !=
+ fdm_active->rmr_region.source_len) ||
+ !fdm_active->cpu_state_data.bytes_dumped) {
+ pr_err("Dump taken by platform is incomplete\n");
+ return -EINVAL;
+ }
+
+ /* Validate the fadump crash info header */
+ fdh = __va(fadump_conf->fadumphdr_addr);
+ if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) {
+ pr_err("Crash info header is not valid.\n");
+ return -EINVAL;
+ }
+
+ if (!fdm_active->cpu_state_data.bytes_dumped)
+ return -EINVAL;
+
+ rc = fadump_build_cpu_notes(fadump_conf);
+ if (rc)
+ return rc;
+
+ /*
+ * We are done validating dump info and elfcore header is now ready
+ * to be exported. set elfcorehdr_addr so that vmcore module will
+ * export the elfcore header through '/proc/vmcore'.
+ */
+ elfcorehdr_addr = fdh->elfcorehdr_addr;
+
+ return 0;
+}
+
+static void pseries_fadump_region_show(struct fw_dump *fadump_conf,
+ struct seq_file *m)
+{
+ const struct pseries_fadump_mem_struct *fdm_ptr;
+ const struct pseries_fadump_section *cpu_data_section;
+
+ if (fdm_active)
+ fdm_ptr = fdm_active;
+ else
+ fdm_ptr = &fdm;
+
+ cpu_data_section = &(fdm_ptr->cpu_state_data);
+ seq_printf(m,
+ "CPU :[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
+ be64_to_cpu(cpu_data_section->destination_address),
+ be64_to_cpu(cpu_data_section->destination_address) +
+ be64_to_cpu(cpu_data_section->source_len) - 1,
+ be64_to_cpu(cpu_data_section->source_len),
+ be64_to_cpu(cpu_data_section->bytes_dumped));
+ seq_printf(m,
+ "HPTE:[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
+ be64_to_cpu(fdm_ptr->hpte_region.destination_address),
+ be64_to_cpu(fdm_ptr->hpte_region.destination_address) +
+ be64_to_cpu(fdm_ptr->hpte_region.source_len) - 1,
+ be64_to_cpu(fdm_ptr->hpte_region.source_len),
+ be64_to_cpu(fdm_ptr->hpte_region.bytes_dumped));
+ seq_printf(m,
+ "DUMP:[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
+ be64_to_cpu(fdm_ptr->rmr_region.destination_address),
+ be64_to_cpu(fdm_ptr->rmr_region.destination_address) +
+ be64_to_cpu(fdm_ptr->rmr_region.source_len) - 1,
+ be64_to_cpu(fdm_ptr->rmr_region.source_len),
+ be64_to_cpu(fdm_ptr->rmr_region.bytes_dumped));
+
+ if (!fdm_active ||
+ (fadump_conf->reserve_dump_area_start ==
+ be64_to_cpu(cpu_data_section->destination_address)))
+ return;
+
+ /* Dump is active. Show reserved memory region. */
+ seq_printf(m,
+ " :[%#016lx-%#016llx] %#llx bytes, Dumped: %#llx\n",
+ fadump_conf->reserve_dump_area_start,
+ be64_to_cpu(cpu_data_section->destination_address) - 1,
+ be64_to_cpu(cpu_data_section->destination_address) -
+ fadump_conf->reserve_dump_area_start,
+ be64_to_cpu(cpu_data_section->destination_address) -
+ fadump_conf->reserve_dump_area_start);
+}
+
+static void pseries_crash_fadump(struct fadump_crash_info_header *fdh,
+ const char *msg)
+{
+ /* Call ibm,os-term rtas call to trigger firmware assisted dump */
+ rtas_os_term((char *)msg);
+}
+
+
+static struct fadump_ops pseries_fadump_ops = {
+ .init_fadump_mem_struct = pseries_init_fadump_mem_struct,
+ .register_fadump = pseries_register_fadump,
+ .unregister_fadump = pseries_unregister_fadump,
+ .invalidate_fadump = pseries_invalidate_fadump,
+ .process_fadump = pseries_process_fadump,
+ .fadump_region_show = pseries_fadump_region_show,
+ .crash_fadump = pseries_crash_fadump,
+};
+
+int __init pseries_dt_scan_fadump(struct fw_dump *fadump_conf, ulong node)
+{
+ const __be32 *sections;
+ int i, num_sections;
+ int size;
+ const __be32 *token;
+
+ /*
+ * 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 1;
+
+ fadump_conf->ibm_configure_kernel_dump = be32_to_cpu(*token);
+
+ /*
+ * The 'ibm,kernel-dump' rtas node is present only if there is
+ * dump data waiting for us.
+ */
+ fdm_active = of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL);
+ if (fdm_active) {
+ pr_info("Firmware-assisted dump is active.\n");
+ fadump_conf->dump_active = 1;
+ update_fadump_config(fadump_conf, fdm_active);
+ }
+
+ /* 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 the size of the section in bytes.
+ */
+ sections = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes",
+ &size);
+
+ if (!sections)
+ return 1;
+
+ 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 PSERIES_FADUMP_CPU_STATE_DATA:
+ fadump_conf->cpu_state_data_size =
+ of_read_ulong(§ions[1], 2);
+ break;
+ case PSERIES_FADUMP_HPTE_REGION:
+ fadump_conf->hpte_region_size =
+ of_read_ulong(§ions[1], 2);
+ break;
+ }
+ }
+
+ fadump_conf->ops = &pseries_fadump_ops;
+ fadump_conf->fadump_platform = FADUMP_PLATFORM_PSERIES;
+ fadump_conf->fadump_supported = 1;
+
+ return 1;
+}
diff --git a/arch/powerpc/platforms/pseries/pseries_fadump.h b/arch/powerpc/platforms/pseries/pseries_fadump.h
new file mode 100644
index 0000000..d61e5d9
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/pseries_fadump.h
@@ -0,0 +1,96 @@
+/*
+ * Firmware-Assisted Dump support on POWERVM platform.
+ *
+ * Copyright 2018-2019, IBM Corp.
+ * Author: Hari Bathini <hbathini@linux.vnet.ibm.com>
+ *
+ * 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.
+ */
+
+#ifndef __PPC64_PSERIES_FA_DUMP_H__
+#define __PPC64_PSERIES_FA_DUMP_H__
+
+/* Firmware provided dump sections */
+#define PSERIES_FADUMP_CPU_STATE_DATA 0x0001
+#define PSERIES_FADUMP_HPTE_REGION 0x0002
+#define PSERIES_FADUMP_REAL_MODE_REGION 0x0011
+
+/* Dump request flag */
+#define PSERIES_FADUMP_REQUEST_FLAG 0x00000001
+
+/* Dump status flag */
+#define PSERIES_FADUMP_ERROR_FLAG 0x2000
+
+/* Utility macros */
+#define SKIP_TO_NEXT_CPU(reg_entry) \
+({ \
+ while (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUEND")) \
+ reg_entry++; \
+ reg_entry++; \
+})
+
+/* Kernel Dump section info */
+struct pseries_fadump_section {
+ __be32 request_flag;
+ __be16 source_data_type;
+ __be16 error_flags;
+ __be64 source_address;
+ __be64 source_len;
+ __be64 bytes_dumped;
+ __be64 destination_address;
+};
+
+/* ibm,configure-kernel-dump header. */
+struct pseries_fadump_section_header {
+ __be32 dump_format_version;
+ __be16 dump_num_sections;
+ __be16 dump_status_flag;
+ __be32 offset_first_dump_section;
+
+ /* Fields for disk dump option. */
+ __be32 dd_block_size;
+ __be64 dd_block_offset;
+ __be64 dd_num_blocks;
+ __be32 dd_offset_disk_path;
+
+ /* Maximum time allowed to prevent an automatic dump-reboot. */
+ __be32 max_time_auto;
+};
+
+/*
+ * Firmware Assisted dump memory structure. This structure is required for
+ * registering future kernel dump with power firmware through rtas call.
+ *
+ * No disk dump option. Hence disk dump path string section is not included.
+ */
+struct pseries_fadump_mem_struct {
+ struct pseries_fadump_section_header header;
+
+ /* Kernel dump sections */
+ struct pseries_fadump_section cpu_state_data;
+ struct pseries_fadump_section hpte_region;
+ struct pseries_fadump_section rmr_region;
+};
+
+#define REGSAVE_AREA_MAGIC STR_TO_HEX("REGSAVE")
+
+/* The firmware-assisted dump format.
+ *
+ * The register save area is an area in the partition's memory used to preserve
+ * the register contents (CPU state data) for the active CPUs during a firmware
+ * assisted dump. The dump format contains register save area header followed
+ * by register entries. On pseries, each list of registers for a CPU starts with
+ * "CPUSTRT" and ends with "CPUEND".
+ */
+
+/* Register save area header. */
+struct fadump_reg_save_area_header {
+ __be64 magic_number;
+ __be32 version;
+ __be32 num_cpu_offset;
+};
+
+#endif /* __PPC64_PSERIES_FA_DUMP_H__ */
^ permalink raw reply related
* [PATCH 02/18] powerpc/fadump: Improve fadump documentation
From: Hari Bathini @ 2019-02-21 17:35 UTC (permalink / raw)
To: Ananth N Mavinakayanahalli, Michael Ellerman, Mahesh J Salgaonkar,
Vasant Hegde, linuxppc-dev, Stewart Smith
In-Reply-To: <155077048463.21014.13936958730316555495.stgit@hbathini.in.ibm.com>
The figures depicting FADump's (Firmware-Assisted Dump) memory layout
are missing some finer details like different memory regions and what
they represent. Improve the documentation by updating those details.
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
---
Documentation/powerpc/firmware-assisted-dump.txt | 65 ++++++++++++----------
1 file changed, 35 insertions(+), 30 deletions(-)
diff --git a/Documentation/powerpc/firmware-assisted-dump.txt b/Documentation/powerpc/firmware-assisted-dump.txt
index 18c5fee..059993b 100644
--- a/Documentation/powerpc/firmware-assisted-dump.txt
+++ b/Documentation/powerpc/firmware-assisted-dump.txt
@@ -74,8 +74,9 @@ as follows:
there is crash data available from a previous boot. During
the early boot OS will reserve rest of the memory above
boot memory size effectively booting with restricted memory
- size. This will make sure that the second kernel will not
- touch any of the dump memory area.
+ size. This will make sure that this kernel (also, referred
+ to as second kernel or capture kernel) will not touch any
+ of the dump memory area.
-- User-space tools will read /proc/vmcore to obtain the contents
of memory, which holds the previous crashed kernel dump in ELF
@@ -125,48 +126,52 @@ space memory except the user pages that were present in CMA region.
o Memory Reservation during first kernel
- Low memory Top of memory
- 0 boot memory size |
- | | |<--Reserved dump area -->| |
- V V | Permanent Reservation | V
- +-----------+----------/ /---+---+----+-----------+----+------+
- | | |CPU|HPTE| DUMP |ELF | |
- +-----------+----------/ /---+---+----+-----------+----+------+
- | ^
- | |
- \ /
- -------------------------------------------
- Boot memory content gets transferred to
- reserved area by firmware at the time of
- crash
+ Low memory Top of memory
+ 0 boot memory size |<--Reserved dump area --->| |
+ | | | Permanent Reservation | |
+ V V | (Preserve area) | V
+ +-----------+----------/ /---+---+----+--------+---+----+------+
+ | | |CPU|HPTE| DUMP |HDR|ELF | |
+ +-----------+----------/ /---+---+----+--------+---+----+------+
+ | ^ ^
+ | | |
+ \ / |
+ ----------------------------------- FADump Header
+ Boot memory content gets transferred (meta area)
+ to reserved area by firmware at the
+ time of crash
+
Fig. 1
+
o Memory Reservation during second kernel after crash
- Low memory Top of memory
- 0 boot memory size |
- | |<------------- Reserved dump area ----------- -->|
- V V V
- +-----------+----------/ /---+---+----+-----------+----+------+
- | | |CPU|HPTE| DUMP |ELF | |
- +-----------+----------/ /---+---+----+-----------+----+------+
+ Low memory Top of memory
+ 0 boot memory size |
+ | |<------------- Reserved dump area --------------->|
+ V V |<---- Preserve area ----->| V
+ +-----------+----------/ /---+---+----+--------+---+----+------+
+ | | |CPU|HPTE| DUMP |HDR|ELF | |
+ +-----------+----------/ /---+---+----+--------+---+----+------+
| |
V V
Used by second /proc/vmcore
kernel to boot
Fig. 2
-Currently the dump will be copied from /proc/vmcore to a
-a new file upon user intervention. The dump data available through
-/proc/vmcore will be in ELF format. Hence the existing kdump
-infrastructure (kdump scripts) to save the dump works fine with
-minor modifications.
+Currently the dump will be copied from /proc/vmcore to a new file upon
+user intervention. The dump data available through /proc/vmcore will be
+in ELF format. Hence the existing kdump infrastructure (kdump scripts)
+to save the dump works fine with minor modifications. KDump scripts on
+major Distro releases have already been modified to work seemlessly (no
+user intervention in saving the dump) when FADump is used, instead of
+KDump, as dump mechanism.
The tools to examine the dump will be same as the ones
used for kdump.
How to enable firmware-assisted dump (fadump):
--------------------------------------
+---------------------------------------------
1. Set config option CONFIG_FA_DUMP=y and build kernel.
2. Boot into linux kernel with 'fadump=on' kernel cmdline option.
@@ -189,7 +194,7 @@ NOTE: 1. 'fadump_reserve_mem=' parameter has been deprecated. Instead
old behaviour.
Sysfs/debugfs files:
-------------
+-------------------
Firmware-assisted dump feature uses sysfs file system to hold
the control files and debugfs file to display memory reserved region.
^ permalink raw reply related
* [PATCH 01/18] powerpc/fadump: move internal fadump code to a new file
From: Hari Bathini @ 2019-02-21 17:35 UTC (permalink / raw)
To: Ananth N Mavinakayanahalli, Michael Ellerman, Mahesh J Salgaonkar,
Vasant Hegde, linuxppc-dev, Stewart Smith
In-Reply-To: <155077048463.21014.13936958730316555495.stgit@hbathini.in.ibm.com>
Refactoring fadump code means internal fadump code is referenced from
different places. For ease, move internal code to a new file.
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
---
arch/powerpc/include/asm/fadump.h | 112 -------------------
arch/powerpc/kernel/Makefile | 2
arch/powerpc/kernel/fadump.c | 194 ++-------------------------------
arch/powerpc/kernel/fadump_internal.c | 184 +++++++++++++++++++++++++++++++
arch/powerpc/kernel/fadump_internal.h | 126 +++++++++++++++++++++
5 files changed, 324 insertions(+), 294 deletions(-)
create mode 100644 arch/powerpc/kernel/fadump_internal.c
create mode 100644 arch/powerpc/kernel/fadump_internal.h
diff --git a/arch/powerpc/include/asm/fadump.h b/arch/powerpc/include/asm/fadump.h
index 188776b..028a8ef 100644
--- a/arch/powerpc/include/asm/fadump.h
+++ b/arch/powerpc/include/asm/fadump.h
@@ -24,34 +24,6 @@
#ifdef CONFIG_FA_DUMP
-/*
- * The RMA region will be saved for later dumping when kernel crashes.
- * RMA is Real Mode Area, the first block of logical memory address owned
- * by logical partition, containing the storage that may be accessed with
- * translate off.
- */
-#define RMA_START 0x0
-#define RMA_END (ppc64_rma_size)
-
-/*
- * On some Power systems where RMO is 128MB, it still requires minimum of
- * 256MB for kernel to boot successfully. When kdump infrastructure is
- * configured to save vmcore over network, we run into OOM issue while
- * loading modules related to network setup. Hence we need aditional 64M
- * of memory to avoid OOM issue.
- */
-#define MIN_BOOT_MEM (((RMA_END < (0x1UL << 28)) ? (0x1UL << 28) : RMA_END) \
- + (0x1UL << 26))
-
-/* The upper limit percentage for user specified boot memory size (25%) */
-#define MAX_BOOT_MEM_RATIO 4
-
-#define memblock_num_regions(memblock_type) (memblock.memblock_type.cnt)
-
-/* Alignement per CMA requirement. */
-#define FADUMP_CMA_ALIGNMENT (PAGE_SIZE << \
- max_t(unsigned long, MAX_ORDER - 1, pageblock_order))
-
/* Firmware provided dump sections */
#define FADUMP_CPU_STATE_DATA 0x0001
#define FADUMP_HPTE_REGION 0x0002
@@ -60,18 +32,9 @@
/* Dump request flag */
#define FADUMP_REQUEST_FLAG 0x00000001
-/* FAD commands */
-#define FADUMP_REGISTER 1
-#define FADUMP_UNREGISTER 2
-#define FADUMP_INVALIDATE 3
-
/* Dump status flag */
#define FADUMP_ERROR_FLAG 0x2000
-#define FADUMP_CPU_ID_MASK ((1UL << 32) - 1)
-
-#define CPU_UNKNOWN (~((u32)0))
-
/* Utility macros */
#define SKIP_TO_NEXT_CPU(reg_entry) \
({ \
@@ -125,59 +88,8 @@ struct fadump_mem_struct {
struct fadump_section rmr_region;
};
-/* Firmware-assisted dump configuration details. */
-struct fw_dump {
- unsigned long cpu_state_data_size;
- unsigned long hpte_region_size;
- unsigned long boot_memory_size;
- unsigned long reserve_dump_area_start;
- unsigned long reserve_dump_area_size;
- /* cmd line option during boot */
- unsigned long reserve_bootvar;
-
- unsigned long fadumphdr_addr;
- unsigned long cpu_notes_buf;
- unsigned long cpu_notes_buf_size;
-
- int ibm_configure_kernel_dump;
-
- unsigned long fadump_enabled:1;
- unsigned long fadump_supported:1;
- unsigned long dump_active:1;
- unsigned long dump_registered:1;
- unsigned long nocma:1;
-};
-
-/*
- * Copy the ascii values for first 8 characters from a string into u64
- * variable at their respective indexes.
- * e.g.
- * The string "FADMPINF" will be converted into 0x4641444d50494e46
- */
-static inline u64 str_to_u64(const char *str)
-{
- u64 val = 0;
- int i;
-
- for (i = 0; i < sizeof(val); i++)
- val = (*str) ? (val << 8) | *str++ : val << 8;
- return val;
-}
-#define STR_TO_HEX(x) str_to_u64(x)
-#define REG_ID(x) str_to_u64(x)
-
-#define FADUMP_CRASH_INFO_MAGIC STR_TO_HEX("FADMPINF")
#define REGSAVE_AREA_MAGIC STR_TO_HEX("REGSAVE")
-/* The firmware-assisted dump format.
- *
- * The register save area is an area in the partition's memory used to preserve
- * the register contents (CPU state data) for the active CPUs during a firmware
- * assisted dump. The dump format contains register save area header followed
- * by register entries. Each list of registers for a CPU starts with
- * "CPUSTRT" and ends with "CPUEND".
- */
-
/* Register save area header. */
struct fadump_reg_save_area_header {
__be64 magic_number;
@@ -185,29 +97,9 @@ struct fadump_reg_save_area_header {
__be32 num_cpu_offset;
};
-/* Register entry. */
-struct fadump_reg_entry {
- __be64 reg_id;
- __be64 reg_value;
-};
-
-/* fadump crash info structure */
-struct fadump_crash_info_header {
- u64 magic_number;
- u64 elfcorehdr_addr;
- u32 crashing_cpu;
- struct pt_regs regs;
- struct cpumask online_mask;
-};
-
-struct fad_crash_memory_ranges {
- unsigned long long base;
- unsigned long long size;
-};
-
extern int is_fadump_memory_area(u64 addr, ulong size);
-extern int early_init_dt_scan_fw_dump(unsigned long node,
- const char *uname, int depth, void *data);
+extern int early_init_dt_scan_fw_dump(unsigned long node, const char *uname,
+ int depth, void *data);
extern int fadump_reserve_mem(void);
extern int setup_fadump(void);
extern int is_fadump_active(void);
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index cb7f0bb..eb50ac3 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -65,7 +65,7 @@ obj-$(CONFIG_EEH) += eeh.o eeh_pe.o eeh_dev.o eeh_cache.o \
eeh_driver.o eeh_event.o eeh_sysfs.o
obj-$(CONFIG_GENERIC_TBSYNC) += smp-tbsync.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
-obj-$(CONFIG_FA_DUMP) += fadump.o
+obj-$(CONFIG_FA_DUMP) += fadump.o fadump_internal.o
ifdef CONFIG_PPC32
obj-$(CONFIG_E500) += idle_e500.o
endif
diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c
index 45a8d0b..fcd50bd 100644
--- a/arch/powerpc/kernel/fadump.c
+++ b/arch/powerpc/kernel/fadump.c
@@ -44,12 +44,11 @@
#include <asm/fadump.h>
#include <asm/setup.h>
+#include "fadump_internal.h"
+
static struct fw_dump fw_dump;
static struct fadump_mem_struct fdm;
static const struct fadump_mem_struct *fdm_active;
-#ifdef CONFIG_CMA
-static struct cma *fadump_cma;
-#endif
static DEFINE_MUTEX(fadump_mutex);
struct fad_crash_memory_ranges *crash_memory_ranges;
@@ -58,6 +57,8 @@ int crash_mem_ranges;
int max_crash_mem_ranges;
#ifdef CONFIG_CMA
+static struct cma *fadump_cma;
+
/*
* fadump_cma_init() - Initialize CMA area from a fadump reserved memory
*
@@ -119,8 +120,8 @@ static int __init fadump_cma_init(void) { return 1; }
#endif /* CONFIG_CMA */
/* 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)
+int __init early_init_dt_scan_fw_dump(unsigned long node, const char *uname,
+ int depth, void *data)
{
const __be32 *sections;
int i, num_sections;
@@ -211,67 +212,6 @@ int is_fadump_active(void)
return fw_dump.dump_active;
}
-/*
- * Returns 1, if there are no holes in boot memory area,
- * 0 otherwise.
- */
-static int is_boot_memory_area_contiguous(void)
-{
- struct memblock_region *reg;
- unsigned long tstart, tend;
- unsigned long start_pfn = PHYS_PFN(RMA_START);
- unsigned long end_pfn = PHYS_PFN(RMA_START + fw_dump.boot_memory_size);
- unsigned int ret = 0;
-
- for_each_memblock(memory, reg) {
- tstart = max(start_pfn, memblock_region_memory_base_pfn(reg));
- tend = min(end_pfn, memblock_region_memory_end_pfn(reg));
- if (tstart < tend) {
- /* Memory hole from start_pfn to tstart */
- if (tstart > start_pfn)
- break;
-
- if (tend == end_pfn) {
- ret = 1;
- break;
- }
-
- start_pfn = tend + 1;
- }
- }
-
- return ret;
-}
-
-/*
- * Returns true, if there are no holes in reserved memory area,
- * false otherwise.
- */
-static bool is_reserved_memory_area_contiguous(void)
-{
- struct memblock_region *reg;
- unsigned long start, end;
- unsigned long d_start = fw_dump.reserve_dump_area_start;
- unsigned long d_end = d_start + fw_dump.reserve_dump_area_size;
-
- for_each_memblock(memory, reg) {
- start = max(d_start, (unsigned long)reg->base);
- end = min(d_end, (unsigned long)(reg->base + reg->size));
- if (d_start < end) {
- /* Memory hole from d_start to start */
- if (start > d_start)
- break;
-
- if (end == d_end)
- return true;
-
- d_start = end + 1;
- }
- }
-
- return false;
-}
-
/* Print firmware assisted dump configurations for debugging purpose. */
static void fadump_show_config(void)
{
@@ -637,9 +577,9 @@ static int register_fw_dump(struct fadump_mem_struct *fdm)
" dump. Hardware Error(%d).\n", rc);
break;
case -3:
- if (!is_boot_memory_area_contiguous())
+ if (!is_boot_memory_area_contiguous(&fw_dump))
pr_err("Can't have holes in boot memory area while registering fadump\n");
- else if (!is_reserved_memory_area_contiguous())
+ else if (!is_reserved_memory_area_contiguous(&fw_dump))
pr_err("Can't have holes in reserved memory area while"
" registering fadump\n");
@@ -709,52 +649,6 @@ void crash_fadump(struct pt_regs *regs, const char *str)
rtas_os_term((char *)str);
}
-#define GPR_MASK 0xffffff0000000000
-static inline int fadump_gpr_index(u64 id)
-{
- int i = -1;
- char str[3];
-
- if ((id & GPR_MASK) == REG_ID("GPR")) {
- /* get the digits at the end */
- id &= ~GPR_MASK;
- id >>= 24;
- str[2] = '\0';
- str[1] = id & 0xff;
- str[0] = (id >> 8) & 0xff;
- sscanf(str, "%d", &i);
- if (i > 31)
- i = -1;
- }
- return i;
-}
-
-static inline void fadump_set_regval(struct pt_regs *regs, u64 reg_id,
- u64 reg_val)
-{
- int i;
-
- i = fadump_gpr_index(reg_id);
- if (i >= 0)
- regs->gpr[i] = (unsigned long)reg_val;
- else if (reg_id == REG_ID("NIA"))
- regs->nip = (unsigned long)reg_val;
- else if (reg_id == REG_ID("MSR"))
- regs->msr = (unsigned long)reg_val;
- else if (reg_id == REG_ID("CTR"))
- regs->ctr = (unsigned long)reg_val;
- else if (reg_id == REG_ID("LR"))
- regs->link = (unsigned long)reg_val;
- else if (reg_id == REG_ID("XER"))
- regs->xer = (unsigned long)reg_val;
- else if (reg_id == REG_ID("CR"))
- regs->ccr = (unsigned long)reg_val;
- else if (reg_id == REG_ID("DAR"))
- regs->dar = (unsigned long)reg_val;
- else if (reg_id == REG_ID("DSISR"))
- regs->dsisr = (unsigned long)reg_val;
-}
-
static struct fadump_reg_entry*
fadump_read_registers(struct fadump_reg_entry *reg_entry, struct pt_regs *regs)
{
@@ -769,72 +663,6 @@ fadump_read_registers(struct fadump_reg_entry *reg_entry, struct pt_regs *regs)
return reg_entry;
}
-static u32 *fadump_regs_to_elf_notes(u32 *buf, struct pt_regs *regs)
-{
- struct elf_prstatus prstatus;
-
- memset(&prstatus, 0, sizeof(prstatus));
- /*
- * FIXME: How do i get PID? Do I really need it?
- * prstatus.pr_pid = ????
- */
- elf_core_copy_kernel_regs(&prstatus.pr_reg, regs);
- buf = append_elf_note(buf, CRASH_CORE_NOTE_NAME, NT_PRSTATUS,
- &prstatus, sizeof(prstatus));
- return buf;
-}
-
-static void fadump_update_elfcore_header(char *bufp)
-{
- struct elfhdr *elf;
- struct elf_phdr *phdr;
-
- elf = (struct elfhdr *)bufp;
- bufp += sizeof(struct elfhdr);
-
- /* First note is a place holder for cpu notes info. */
- phdr = (struct elf_phdr *)bufp;
-
- if (phdr->p_type == PT_NOTE) {
- phdr->p_paddr = fw_dump.cpu_notes_buf;
- phdr->p_offset = phdr->p_paddr;
- phdr->p_filesz = fw_dump.cpu_notes_buf_size;
- phdr->p_memsz = fw_dump.cpu_notes_buf_size;
- }
- return;
-}
-
-static void *fadump_cpu_notes_buf_alloc(unsigned long size)
-{
- void *vaddr;
- struct page *page;
- unsigned long order, count, i;
-
- order = get_order(size);
- vaddr = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, order);
- if (!vaddr)
- return NULL;
-
- count = 1 << order;
- page = virt_to_page(vaddr);
- for (i = 0; i < count; i++)
- SetPageReserved(page + i);
- return vaddr;
-}
-
-static void fadump_cpu_notes_buf_free(unsigned long vaddr, unsigned long size)
-{
- struct page *page;
- unsigned long order, count, i;
-
- order = get_order(size);
- count = 1 << order;
- page = virt_to_page(vaddr);
- for (i = 0; i < count; i++)
- ClearPageReserved(page + i);
- __free_pages(page, order);
-}
-
/*
* Read CPU state dump data and convert it into ELF notes.
* The CPU dump starts with magic number "REGSAVE". NumCpusOffset should be
@@ -924,9 +752,9 @@ static int __init fadump_build_cpu_notes(const struct fadump_mem_struct *fdm)
final_note(note_buf);
if (fdh) {
- pr_debug("Updating elfcore header (%llx) with cpu notes\n",
- fdh->elfcorehdr_addr);
- fadump_update_elfcore_header((char *)__va(fdh->elfcorehdr_addr));
+ addr = fdh->elfcorehdr_addr;
+ pr_debug("Updating elfcore header(%lx) with cpu notes\n", addr);
+ fadump_update_elfcore_header(&fw_dump, (char *)__va(addr));
}
return 0;
diff --git a/arch/powerpc/kernel/fadump_internal.c b/arch/powerpc/kernel/fadump_internal.c
new file mode 100644
index 0000000..570c357
--- /dev/null
+++ b/arch/powerpc/kernel/fadump_internal.c
@@ -0,0 +1,184 @@
+/*
+ * Firmware-Assisted Dump internal code.
+ *
+ * Copyright 2018-2019, IBM Corp.
+ * Author: Hari Bathini <hbathini@linux.vnet.ibm.com>
+ *
+ * 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.
+ */
+
+#include <linux/memblock.h>
+#include <linux/elf.h>
+#include <linux/mm.h>
+#include <linux/crash_core.h>
+
+#include "fadump_internal.h"
+
+void *fadump_cpu_notes_buf_alloc(unsigned long size)
+{
+ void *vaddr;
+ struct page *page;
+ unsigned long order, count, i;
+
+ order = get_order(size);
+ vaddr = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, order);
+ if (!vaddr)
+ return NULL;
+
+ count = 1 << order;
+ page = virt_to_page(vaddr);
+ for (i = 0; i < count; i++)
+ SetPageReserved(page + i);
+ return vaddr;
+}
+
+void fadump_cpu_notes_buf_free(unsigned long vaddr, unsigned long size)
+{
+ struct page *page;
+ unsigned long order, count, i;
+
+ order = get_order(size);
+ count = 1 << order;
+ page = virt_to_page(vaddr);
+ for (i = 0; i < count; i++)
+ ClearPageReserved(page + i);
+ __free_pages(page, order);
+}
+
+#define GPR_MASK 0xffffff0000000000
+static inline int fadump_gpr_index(u64 id)
+{
+ int i = -1;
+ char str[3];
+
+ if ((id & GPR_MASK) == REG_ID("GPR")) {
+ /* get the digits at the end */
+ id &= ~GPR_MASK;
+ id >>= 24;
+ str[2] = '\0';
+ str[1] = id & 0xff;
+ str[0] = (id >> 8) & 0xff;
+ if (kstrtoint(str, 10, &i))
+ i = -EINVAL;
+ if (i > 31)
+ i = -1;
+ }
+ return i;
+}
+
+void fadump_set_regval(struct pt_regs *regs, u64 reg_id, u64 reg_val)
+{
+ int i;
+
+ i = fadump_gpr_index(reg_id);
+ if (i >= 0)
+ regs->gpr[i] = (unsigned long)reg_val;
+ else if (reg_id == REG_ID("NIA"))
+ regs->nip = (unsigned long)reg_val;
+ else if (reg_id == REG_ID("MSR"))
+ regs->msr = (unsigned long)reg_val;
+ else if (reg_id == REG_ID("CTR"))
+ regs->ctr = (unsigned long)reg_val;
+ else if (reg_id == REG_ID("LR"))
+ regs->link = (unsigned long)reg_val;
+ else if (reg_id == REG_ID("XER"))
+ regs->xer = (unsigned long)reg_val;
+ else if (reg_id == REG_ID("CR"))
+ regs->ccr = (unsigned long)reg_val;
+ else if (reg_id == REG_ID("DAR"))
+ regs->dar = (unsigned long)reg_val;
+ else if (reg_id == REG_ID("DSISR"))
+ regs->dsisr = (unsigned long)reg_val;
+}
+
+u32 *fadump_regs_to_elf_notes(u32 *buf, struct pt_regs *regs)
+{
+ struct elf_prstatus prstatus;
+
+ memset(&prstatus, 0, sizeof(prstatus));
+ /*
+ * FIXME: How do i get PID? Do I really need it?
+ * prstatus.pr_pid = ????
+ */
+ elf_core_copy_kernel_regs(&prstatus.pr_reg, regs);
+ buf = append_elf_note(buf, CRASH_CORE_NOTE_NAME, NT_PRSTATUS,
+ &prstatus, sizeof(prstatus));
+ return buf;
+}
+
+void fadump_update_elfcore_header(struct fw_dump *fadump_conf, char *bufp)
+{
+ struct elfhdr *elf;
+ struct elf_phdr *phdr;
+
+ elf = (struct elfhdr *)bufp;
+ bufp += sizeof(struct elfhdr);
+
+ /* First note is a place holder for cpu notes info. */
+ phdr = (struct elf_phdr *)bufp;
+
+ if (phdr->p_type == PT_NOTE) {
+ phdr->p_paddr = fadump_conf->cpu_notes_buf;
+ phdr->p_offset = phdr->p_paddr;
+ phdr->p_memsz = fadump_conf->cpu_notes_buf_size;
+ phdr->p_filesz = phdr->p_memsz;
+ }
+}
+
+/*
+ * Returns 1, if there are no holes in memory area between d_start to d_end,
+ * 0 otherwise.
+ */
+static int is_memory_area_contiguous(unsigned long d_start,
+ unsigned long d_end)
+{
+ struct memblock_region *reg;
+ unsigned long start, end;
+ int ret = 0;
+
+ for_each_memblock(memory, reg) {
+ start = max_t(unsigned long, d_start, reg->base);
+ end = min_t(unsigned long, d_end, (reg->base + reg->size));
+ if (d_start < end) {
+ /* Memory hole from d_start to start */
+ if (start > d_start)
+ break;
+
+ if (end == d_end) {
+ ret = 1;
+ break;
+ }
+
+ d_start = end + 1;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Returns 1, if there are no holes in boot memory area,
+ * 0 otherwise.
+ */
+int is_boot_memory_area_contiguous(struct fw_dump *fadump_conf)
+{
+ unsigned long d_start = RMA_START;
+ unsigned long d_end = RMA_START + fadump_conf->boot_memory_size;
+
+ return is_memory_area_contiguous(d_start, d_end);
+}
+
+/*
+ * Returns 1, if there are no holes in reserved memory area,
+ * 0 otherwise.
+ */
+int is_reserved_memory_area_contiguous(struct fw_dump *fadump_conf)
+{
+ unsigned long d_start = fadump_conf->reserve_dump_area_start;
+ unsigned long d_end = d_start + fadump_conf->reserve_dump_area_size;
+
+ return is_memory_area_contiguous(d_start, d_end);
+}
diff --git a/arch/powerpc/kernel/fadump_internal.h b/arch/powerpc/kernel/fadump_internal.h
new file mode 100644
index 0000000..8ccd96d
--- /dev/null
+++ b/arch/powerpc/kernel/fadump_internal.h
@@ -0,0 +1,126 @@
+/*
+ * Firmware-Assisted Dump internal code.
+ *
+ * Copyright 2018-2019, IBM Corp.
+ * Author: Hari Bathini <hbathini@linux.vnet.ibm.com>
+ *
+ * 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.
+ */
+
+#ifndef __PPC64_FA_DUMP_INTERNAL_H__
+#define __PPC64_FA_DUMP_INTERNAL_H__
+
+/*
+ * The RMA region will be saved for later dumping when kernel crashes.
+ * RMA is Real Mode Area, the first block of logical memory address owned
+ * by logical partition, containing the storage that may be accessed with
+ * translate off.
+ */
+#define RMA_START 0x0
+#define RMA_END (ppc64_rma_size)
+
+/*
+ * On some Power systems where RMO is 128MB, it still requires minimum of
+ * 256MB for kernel to boot successfully. When kdump infrastructure is
+ * configured to save vmcore over network, we run into OOM issue while
+ * loading modules related to network setup. Hence we need additional 64M
+ * of memory to avoid OOM issue.
+ */
+#define MIN_BOOT_MEM (((RMA_END < (0x1UL << 28)) ? (0x1UL << 28) : RMA_END) \
+ + (0x1UL << 26))
+
+/* The upper limit percentage for user specified boot memory size (25%) */
+#define MAX_BOOT_MEM_RATIO 4
+
+#define memblock_num_regions(memblock_type) (memblock.memblock_type.cnt)
+
+/* Alignment per CMA requirement. */
+#define FADUMP_CMA_ALIGNMENT (PAGE_SIZE << \
+ max_t(unsigned long, MAX_ORDER - 1, \
+ pageblock_order))
+
+/* FAD commands */
+#define FADUMP_REGISTER 1
+#define FADUMP_UNREGISTER 2
+#define FADUMP_INVALIDATE 3
+
+#define FADUMP_CPU_ID_MASK ((1UL << 32) - 1)
+
+#define CPU_UNKNOWN (~((u32)0))
+
+/*
+ * Copy the ascii values for first 8 characters from a string into u64
+ * variable at their respective indexes.
+ * e.g.
+ * The string "FADMPINF" will be converted into 0x4641444d50494e46
+ */
+static inline u64 str_to_u64(const char *str)
+{
+ u64 val = 0;
+ int i;
+
+ for (i = 0; i < sizeof(val); i++)
+ val = (*str) ? (val << 8) | *str++ : val << 8;
+ return val;
+}
+#define STR_TO_HEX(x) str_to_u64(x)
+#define REG_ID(x) str_to_u64(x)
+
+/* Register entry. */
+struct fadump_reg_entry {
+ __be64 reg_id;
+ __be64 reg_value;
+};
+
+#define FADUMP_CRASH_INFO_MAGIC STR_TO_HEX("FADMPINF")
+
+/* fadump crash info structure */
+struct fadump_crash_info_header {
+ u64 magic_number;
+ u64 elfcorehdr_addr;
+ u32 crashing_cpu;
+ struct pt_regs regs;
+ struct cpumask online_mask;
+};
+
+struct fad_crash_memory_ranges {
+ unsigned long long base;
+ unsigned long long size;
+};
+
+/* Firmware-assisted dump configuration details. */
+struct fw_dump {
+ unsigned long cpu_state_data_size;
+ unsigned long hpte_region_size;
+ unsigned long boot_memory_size;
+ unsigned long reserve_dump_area_start;
+ unsigned long reserve_dump_area_size;
+ /* cmd line option during boot */
+ unsigned long reserve_bootvar;
+
+ unsigned long fadumphdr_addr;
+ unsigned long cpu_notes_buf;
+ unsigned long cpu_notes_buf_size;
+
+ int ibm_configure_kernel_dump;
+
+ unsigned long fadump_enabled:1;
+ unsigned long fadump_supported:1;
+ unsigned long dump_active:1;
+ unsigned long dump_registered:1;
+ unsigned long nocma:1;
+};
+
+/* Helper functions */
+void *fadump_cpu_notes_buf_alloc(unsigned long size);
+void fadump_cpu_notes_buf_free(unsigned long vaddr, unsigned long size);
+void fadump_set_regval(struct pt_regs *regs, u64 reg_id, u64 reg_val);
+u32 *fadump_regs_to_elf_notes(u32 *buf, struct pt_regs *regs);
+void fadump_update_elfcore_header(struct fw_dump *fadump_config, char *bufp);
+int is_boot_memory_area_contiguous(struct fw_dump *fadump_conf);
+int is_reserved_memory_area_contiguous(struct fw_dump *fadump_conf);
+
+#endif /* __PPC64_FA_DUMP_INTERNAL_H__ */
^ permalink raw reply related
* [PATCH 00/18] Add FADump support on PowerNV platform
From: Hari Bathini @ 2019-02-21 17:35 UTC (permalink / raw)
To: Ananth N Mavinakayanahalli, Michael Ellerman, Mahesh J Salgaonkar,
Vasant Hegde, linuxppc-dev, Stewart Smith
Firmware-Assisted Dump (FADump) is currently supported only on pseries
platform. This patch series adds support for powernv platform too.
The first and third patches refactor the FADump code to make use of common
code across multiple platforms. The fifth patch adds basic FADump support
for powernv platform. Patches seven & eight honour reserved-ranges DT node
while reserving/releasing memory used by FADump. The next patch processes
CPU state data provided by firmware to create and append core notes to the
ELF core file. The tenth patch adds support for preserving crash data for
subsequent boots (useful in cases like petitboot). Patch twelve provides
support to export opalcore. This is to make debugging of failures in OPAL
code easier. The subsequent patch ensures vmcore processing is skipped
when only OPAL core is exported by f/w. The next patch provides option to
release the kernel memory used to export opalcore. Patch seventeen adds
backup area (an area populated before crash and used in the capture kernel
to setup vmcore file robustly) support on PowerNV platform. The remaining
patches update Firmware-Assisted Dump documentation appropriately.
Note that the quantam of increase in robustness due to patch seventeen may
not be worth breaking backward compatibility for older kernel versions.
Would like to hear thoughts from others on it.
The patch series is tested with the latest firmware plus the below skiboot
changes for MPIPL support:
https://patchwork.ozlabs.org/project/skiboot/list/?series=78497
("MPIPL support")
---
Hari Bathini (18):
powerpc/fadump: move internal fadump code to a new file
powerpc/fadump: Improve fadump documentation
pseries/fadump: move out platform specific support from generic code
powerpc/fadump: use FADump instead of fadump for how it is pronounced
powerpc/fadump: enable fadump support on OPAL based POWER platform
powerpc/fadump: Update documentation about OPAL platform support
powerpc/fadump: consider reserved ranges while reserving memory
powerpc/fadump: consider reserved ranges while releasing memory
powernv/fadump: process architected register state data provided by firmware
powernv/fadump: add support to preserve crash data on FADUMP disabled kernel
powerpc/fadump: update documentation about CONFIG_PRESERVE_FA_DUMP
powerpc/powernv: export /proc/opalcore for analysing opal crashes
powernv/fadump: Skip processing /proc/vmcore when only OPAL core exists
powernv/opalcore: provide an option to invalidate /proc/opalcore file
powernv/fadump: consider f/w load area
powernv/fadump: update documentation about option to release opalcore
powernv/fadump: use backup area to map PIR to logical CPUs
powerpc/fadump: Update documentation about backup area support
Documentation/powerpc/firmware-assisted-dump.txt | 208 ++--
arch/powerpc/Kconfig | 23
arch/powerpc/include/asm/fadump.h | 190 ---
arch/powerpc/include/asm/opal-api.h | 58 +
arch/powerpc/include/asm/opal.h | 1
arch/powerpc/kernel/Makefile | 6
arch/powerpc/kernel/fadump.c | 1199 ++++++++--------------
arch/powerpc/kernel/fadump_internal.c | 297 +++++
arch/powerpc/kernel/fadump_internal.h | 250 +++++
arch/powerpc/kernel/prom.c | 4
arch/powerpc/platforms/powernv/Makefile | 3
arch/powerpc/platforms/powernv/opal-core.c | 602 +++++++++++
arch/powerpc/platforms/powernv/opal-fadump.c | 670 ++++++++++++
arch/powerpc/platforms/powernv/opal-fadump.h | 116 ++
arch/powerpc/platforms/powernv/opal-wrappers.S | 1
arch/powerpc/platforms/pseries/Makefile | 1
arch/powerpc/platforms/pseries/pseries_fadump.c | 535 ++++++++++
arch/powerpc/platforms/pseries/pseries_fadump.h | 96 ++
18 files changed, 3252 insertions(+), 1008 deletions(-)
create mode 100644 arch/powerpc/kernel/fadump_internal.c
create mode 100644 arch/powerpc/kernel/fadump_internal.h
create mode 100644 arch/powerpc/platforms/powernv/opal-core.c
create mode 100644 arch/powerpc/platforms/powernv/opal-fadump.c
create mode 100644 arch/powerpc/platforms/powernv/opal-fadump.h
create mode 100644 arch/powerpc/platforms/pseries/pseries_fadump.c
create mode 100644 arch/powerpc/platforms/pseries/pseries_fadump.h
--
Signature
^ permalink raw reply
* Re: [PATCH 0/7] Kernel Userspace Protection for radix
From: Kees Cook @ 2019-02-21 16:07 UTC (permalink / raw)
To: Russell Currey; +Cc: PowerPC, Nick Piggin, Kernel Hardening
In-Reply-To: <20190221093601.27920-1-ruscur@russell.cc>
On Thu, Feb 21, 2019 at 1:36 AM Russell Currey <ruscur@russell.cc> wrote:
> The first three patches of these series are from Christophe's work and are
> the bare minimum framework needed to implement the support for radix.
>
> In patch 3, I have removed from Christophe's patch my implementation of
> the 64-bit exception handling code, since we don't have an answer for
> making nested exceptions work yet. This is mentioned in the final KUAP
> patch. Regardless, this is still a significant security improvement
> and greatly narrows the attack surface.
Nice! Am I understanding correctly that with this series powerpc9 and
later, using radix, will pass the lkdtm tests for KUAP and KUEP (i.e.
EXEC_USERSPACE and ACCESS_USERSPACE)?
--
Kees Cook
^ permalink raw reply
* Re: [PATCH 3/7] powerpc/mm: Add a framework for Kernel Userspace Access Protection
From: Mark Rutland @ 2019-02-21 14:48 UTC (permalink / raw)
To: Christophe Leroy; +Cc: kernel-hardening, linuxppc-dev, npiggin
In-Reply-To: <adb2b7c9-3472-2b9c-37e1-6150697d0940@c-s.fr>
On Thu, Feb 21, 2019 at 11:46:06AM +0100, Christophe Leroy wrote:
>
>
> Le 21/02/2019 à 10:35, Russell Currey a écrit :
> > From: Christophe Leroy <christophe.leroy@c-s.fr>
> >
> > This patch implements a framework for Kernel Userspace Access
> > Protection.
> >
> > Then subarches will have to possibility to provide their own
> > implementation by providing setup_kuap() and lock/unlock_user_access()
> >
> > Some platform will need to know the area accessed and whether it is
> > accessed from read, write or both. Therefore source, destination and
> > size and handed over to the two functions.
>
> Should we also consider adding user_access_begin() and the 3 other
> associated macros ?
>
> See x86 for details.
As a heads-up, there are some potential issues with
user_access_{begin,end}() that may affect PPC. There's a long thread
starting at:
https://lkml.kernel.org/r/1547560709-56207-4-git-send-email-julien.thierry@arm.com/
Thanks,
Mark.
^ permalink raw reply
* Re: [PATCH v4 04/16] powerpc/mm/32s: use generic mmu_mapin_ram() for all blocks.
From: Christophe Leroy @ 2019-02-21 14:38 UTC (permalink / raw)
To: Michael Ellerman, Benjamin Herrenschmidt, Paul Mackerras,
j.neuschaefer
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <87o9755jy2.fsf@concordia.ellerman.id.au>
Le 21/02/2019 à 14:51, Michael Ellerman a écrit :
> Christophe Leroy <christophe.leroy@c-s.fr> writes:
>
>> Now that mmu_mapin_ram() is able to handle other blocks
>> than the one starting at 0, the WII can use it for all
>> its blocks.
>>
>> Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
>> ---
>> arch/powerpc/mm/pgtable_32.c | 27 +++++++++------------------
>> 1 file changed, 9 insertions(+), 18 deletions(-)
>>
>> diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c
>> index b4858818523f..2bf9bf8867d4 100644
>> --- a/arch/powerpc/mm/pgtable_32.c
>> +++ b/arch/powerpc/mm/pgtable_32.c
>> @@ -254,26 +254,17 @@ static void __init __mapin_ram_chunk(unsigned long offset, unsigned long top)
>>
>> void __init mapin_ram(void)
>> {
>> - unsigned long s, top;
>> -
>> -#ifndef CONFIG_WII
>> - top = total_lowmem;
>> - s = mmu_mapin_ram(0, top);
>> - __mapin_ram_chunk(s, top);
>> -#else
>> - if (!wii_hole_size) {
>> - s = mmu_mapin_ram(0, total_lowmem);
>> - __mapin_ram_chunk(s, total_lowmem);
>> - } else {
>> - top = wii_hole_start;
>> - s = mmu_mapin_ram(0, top);
>> - __mapin_ram_chunk(s, top);
>> + struct memblock_region *reg;
>> +
>> + for_each_memblock(memory, reg) {
>> + unsigned long base = reg->base;
>> + unsigned long top = min(base + reg->size, (unsigned long)total_lowmem);
>
> This gives me:
>
> In file included from ../arch/powerpc/mm/pgtable_32.c:22:
> ../arch/powerpc/mm/pgtable_32.c: In function ‘mapin_ram’:
> ../include/linux/kernel.h:846:29: error: comparison of distinct pointer types lacks a cast [-Werror]
> (!!(sizeof((typeof(x) *)1 == (typeof(y) *)1)))
> ^~
> ../include/linux/kernel.h:860:4: note: in expansion of macro ‘__typecheck’
> (__typecheck(x, y) && __no_side_effects(x, y))
> ^~~~~~~~~~~
> ../include/linux/kernel.h:870:24: note: in expansion of macro ‘__safe_cmp’
> __builtin_choose_expr(__safe_cmp(x, y), \
> ^~~~~~~~~~
> ../include/linux/kernel.h:879:19: note: in expansion of macro ‘__careful_cmp’
> #define min(x, y) __careful_cmp(x, y, <)
> ^~~~~~~~~~~~~
> ../arch/powerpc/mm/pgtable_32.c:261:23: note: in expansion of macro ‘min’
> unsigned long top = min(base + reg->size, (unsigned long)total_lowmem);
> ^~~
> cc1: all warnings being treated as errors
> ../scripts/Makefile.build:276: recipe for target 'arch/powerpc/mm/pgtable_32.o' failed
>
>
> For 44x/bamboo_defconfig.
>
Argh ! I had the same kind of warning this morning with
pmac32_defconfig, that's the reason why I added the cast to unsigned long.
Christophe
^ permalink raw reply
* Re: [PATCH v4 3/3] locking/rwsem: Optimize down_read_trylock()
From: Will Deacon @ 2019-02-21 14:14 UTC (permalink / raw)
To: Waiman Long
Cc: linux-ia64, linux-sh, Peter Zijlstra, linux-mips, H. Peter Anvin,
sparclinux, linux-riscv, linux-arch, linux-s390, Davidlohr Bueso,
linux-c6x-dev, linux-hexagon, x86, Ingo Molnar, uclinux-h8-devel,
linux-xtensa, Arnd Bergmann, linux-um, linux-m68k, openrisc,
Borislav Petkov, Thomas Gleixner, linux-arm-kernel, Tim Chen,
linux-parisc, Linus Torvalds, linux-kernel, linux-alpha,
nios2-dev, Andrew Morton, linuxppc-dev
In-Reply-To: <1550095217-12047-4-git-send-email-longman@redhat.com>
On Wed, Feb 13, 2019 at 05:00:17PM -0500, Waiman Long wrote:
> Modify __down_read_trylock() to optimize for an unlocked rwsem and make
> it generate slightly better code.
>
> Before this patch, down_read_trylock:
>
> 0x0000000000000000 <+0>: callq 0x5 <down_read_trylock+5>
> 0x0000000000000005 <+5>: jmp 0x18 <down_read_trylock+24>
> 0x0000000000000007 <+7>: lea 0x1(%rdx),%rcx
> 0x000000000000000b <+11>: mov %rdx,%rax
> 0x000000000000000e <+14>: lock cmpxchg %rcx,(%rdi)
> 0x0000000000000013 <+19>: cmp %rax,%rdx
> 0x0000000000000016 <+22>: je 0x23 <down_read_trylock+35>
> 0x0000000000000018 <+24>: mov (%rdi),%rdx
> 0x000000000000001b <+27>: test %rdx,%rdx
> 0x000000000000001e <+30>: jns 0x7 <down_read_trylock+7>
> 0x0000000000000020 <+32>: xor %eax,%eax
> 0x0000000000000022 <+34>: retq
> 0x0000000000000023 <+35>: mov %gs:0x0,%rax
> 0x000000000000002c <+44>: or $0x3,%rax
> 0x0000000000000030 <+48>: mov %rax,0x20(%rdi)
> 0x0000000000000034 <+52>: mov $0x1,%eax
> 0x0000000000000039 <+57>: retq
>
> After patch, down_read_trylock:
>
> 0x0000000000000000 <+0>: callq 0x5 <down_read_trylock+5>
> 0x0000000000000005 <+5>: xor %eax,%eax
> 0x0000000000000007 <+7>: lea 0x1(%rax),%rdx
> 0x000000000000000b <+11>: lock cmpxchg %rdx,(%rdi)
> 0x0000000000000010 <+16>: jne 0x29 <down_read_trylock+41>
> 0x0000000000000012 <+18>: mov %gs:0x0,%rax
> 0x000000000000001b <+27>: or $0x3,%rax
> 0x000000000000001f <+31>: mov %rax,0x20(%rdi)
> 0x0000000000000023 <+35>: mov $0x1,%eax
> 0x0000000000000028 <+40>: retq
> 0x0000000000000029 <+41>: test %rax,%rax
> 0x000000000000002c <+44>: jns 0x7 <down_read_trylock+7>
> 0x000000000000002e <+46>: xor %eax,%eax
> 0x0000000000000030 <+48>: retq
>
> By using a rwsem microbenchmark, the down_read_trylock() rate (with a
> load of 10 to lengthen the lock critical section) on a x86-64 system
> before and after the patch were:
>
> Before Patch After Patch
> # of Threads rlock rlock
> ------------ ----- -----
> 1 14,496 14,716
> 2 8,644 8,453
> 4 6,799 6,983
> 8 5,664 7,190
>
> On a ARM64 system, the performance results were:
>
> Before Patch After Patch
> # of Threads rlock rlock
> ------------ ----- -----
> 1 23,676 24,488
> 2 7,697 9,502
> 4 4,945 3,440
> 8 2,641 1,603
>
> For the uncontended case (1 thread), the new down_read_trylock() is a
> little bit faster. For the contended cases, the new down_read_trylock()
> perform pretty well in x86-64, but performance degrades at high
> contention level on ARM64.
>
> Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
> kernel/locking/rwsem.h | 13 ++++++++-----
> 1 file changed, 8 insertions(+), 5 deletions(-)
>
> diff --git a/kernel/locking/rwsem.h b/kernel/locking/rwsem.h
> index 45ee002..1f5775a 100644
> --- a/kernel/locking/rwsem.h
> +++ b/kernel/locking/rwsem.h
> @@ -174,14 +174,17 @@ static inline int __down_read_killable(struct rw_semaphore *sem)
>
> static inline int __down_read_trylock(struct rw_semaphore *sem)
> {
> - long tmp;
> + /*
> + * Optimize for the case when the rwsem is not locked at all.
> + */
> + long tmp = RWSEM_UNLOCKED_VALUE;
>
> - while ((tmp = atomic_long_read(&sem->count)) >= 0) {
> - if (tmp == atomic_long_cmpxchg_acquire(&sem->count, tmp,
> - tmp + RWSEM_ACTIVE_READ_BIAS)) {
> + do {
> + if (atomic_long_try_cmpxchg_acquire(&sem->count, &tmp,
> + tmp + RWSEM_ACTIVE_READ_BIAS)) {
> return 1;
> }
> - }
> + } while (tmp >= 0);
Nit: but I guess that should be RWSEM_UNLOCKED_VALUE instead of 0.
Will
^ permalink raw reply
* Re: [PATCH v4 04/16] powerpc/mm/32s: use generic mmu_mapin_ram() for all blocks.
From: Michael Ellerman @ 2019-02-21 13:51 UTC (permalink / raw)
To: Christophe Leroy, Benjamin Herrenschmidt, Paul Mackerras,
j.neuschaefer
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <790cf44815f8c56b3c600560bad9139f63be9f68.1550692943.git.christophe.leroy@c-s.fr>
Christophe Leroy <christophe.leroy@c-s.fr> writes:
> Now that mmu_mapin_ram() is able to handle other blocks
> than the one starting at 0, the WII can use it for all
> its blocks.
>
> Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
> ---
> arch/powerpc/mm/pgtable_32.c | 27 +++++++++------------------
> 1 file changed, 9 insertions(+), 18 deletions(-)
>
> diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c
> index b4858818523f..2bf9bf8867d4 100644
> --- a/arch/powerpc/mm/pgtable_32.c
> +++ b/arch/powerpc/mm/pgtable_32.c
> @@ -254,26 +254,17 @@ static void __init __mapin_ram_chunk(unsigned long offset, unsigned long top)
>
> void __init mapin_ram(void)
> {
> - unsigned long s, top;
> -
> -#ifndef CONFIG_WII
> - top = total_lowmem;
> - s = mmu_mapin_ram(0, top);
> - __mapin_ram_chunk(s, top);
> -#else
> - if (!wii_hole_size) {
> - s = mmu_mapin_ram(0, total_lowmem);
> - __mapin_ram_chunk(s, total_lowmem);
> - } else {
> - top = wii_hole_start;
> - s = mmu_mapin_ram(0, top);
> - __mapin_ram_chunk(s, top);
> + struct memblock_region *reg;
> +
> + for_each_memblock(memory, reg) {
> + unsigned long base = reg->base;
> + unsigned long top = min(base + reg->size, (unsigned long)total_lowmem);
This gives me:
In file included from ../arch/powerpc/mm/pgtable_32.c:22:
../arch/powerpc/mm/pgtable_32.c: In function ‘mapin_ram’:
../include/linux/kernel.h:846:29: error: comparison of distinct pointer types lacks a cast [-Werror]
(!!(sizeof((typeof(x) *)1 == (typeof(y) *)1)))
^~
../include/linux/kernel.h:860:4: note: in expansion of macro ‘__typecheck’
(__typecheck(x, y) && __no_side_effects(x, y))
^~~~~~~~~~~
../include/linux/kernel.h:870:24: note: in expansion of macro ‘__safe_cmp’
__builtin_choose_expr(__safe_cmp(x, y), \
^~~~~~~~~~
../include/linux/kernel.h:879:19: note: in expansion of macro ‘__careful_cmp’
#define min(x, y) __careful_cmp(x, y, <)
^~~~~~~~~~~~~
../arch/powerpc/mm/pgtable_32.c:261:23: note: in expansion of macro ‘min’
unsigned long top = min(base + reg->size, (unsigned long)total_lowmem);
^~~
cc1: all warnings being treated as errors
../scripts/Makefile.build:276: recipe for target 'arch/powerpc/mm/pgtable_32.o' failed
For 44x/bamboo_defconfig.
cheers
^ permalink raw reply
* Re: [PATCH 3/7] powerpc/mm: Add a framework for Kernel Userspace Access Protection
From: kbuild test robot @ 2019-02-21 12:56 UTC (permalink / raw)
To: Russell Currey; +Cc: kernel-hardening, linuxppc-dev, kbuild-all, npiggin
In-Reply-To: <20190221093601.27920-4-ruscur@russell.cc>
[-- Attachment #1: Type: text/plain, Size: 3051 bytes --]
Hi Christophe,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on powerpc/next]
[also build test ERROR on v5.0-rc4 next-20190221]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Russell-Currey/Kernel-Userspace-Protection-for-radix/20190221-193749
base: https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git next
config: powerpc-allyesconfig (attached as .config)
compiler: powerpc64-linux-gnu-gcc (Debian 8.2.0-11) 8.2.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=8.2.0 make.cross ARCH=powerpc
Note: the linux-review/Russell-Currey/Kernel-Userspace-Protection-for-radix/20190221-193749 HEAD 5f2951ce63da41c63227ed597252cea801adb5a4 builds fine.
It only hurts bisectibility.
All errors (new ones prefixed by >>):
In file included from arch/powerpc/kernel/asm-offsets.c:31:
arch/powerpc/kernel/asm-offsets.c: In function 'main':
>> include/linux/compiler_types.h:127:35: error: 'struct paca_struct' has no member named 'user_access_allowed'
#define __compiler_offsetof(a, b) __builtin_offsetof(a, b)
^~~~~~~~~~~~~~~~~~
include/linux/kbuild.h:6:62: note: in definition of macro 'DEFINE'
asm volatile("\n.ascii \"->" #sym " %0 " #val "\"" : : "i" (val))
^~~
include/linux/stddef.h:17:32: note: in expansion of macro '__compiler_offsetof'
#define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER)
^~~~~~~~~~~~~~~~~~~
include/linux/kbuild.h:11:14: note: in expansion of macro 'offsetof'
DEFINE(sym, offsetof(struct str, mem))
^~~~~~~~
arch/powerpc/kernel/asm-offsets.c:266:2: note: in expansion of macro 'OFFSET'
OFFSET(PACA_USER_ACCESS_ALLOWED, paca_struct, user_access_allowed);
^~~~~~
make[2]: *** [arch/powerpc/kernel/asm-offsets.s] Error 1
make[2]: Target '__build' not remade because of errors.
make[1]: *** [prepare0] Error 2
make[1]: Target 'prepare' not remade because of errors.
make: *** [sub-make] Error 2
vim +127 include/linux/compiler_types.h
71391bdd Xiaozhou Liu 2018-12-14 126
71391bdd Xiaozhou Liu 2018-12-14 @127 #define __compiler_offsetof(a, b) __builtin_offsetof(a, b)
71391bdd Xiaozhou Liu 2018-12-14 128
:::::: The code at line 127 was first introduced by commit
:::::: 71391bdd2e9aab188f86bf1ecd9b232531ec7eea include/linux/compiler_types.h: don't pollute userspace with macro definitions
:::::: TO: Xiaozhou Liu <liuxiaozhou@bytedance.com>
:::::: CC: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 59719 bytes --]
^ permalink raw reply
* Re: [PATCH 1/3] powerpc: sstep: Add tests for compute type instructions
From: Michael Ellerman @ 2019-02-21 11:13 UTC (permalink / raw)
To: Sandipan Das; +Cc: naveen.n.rao, paulus, linuxppc-dev, ravi.bangoria, dja
In-Reply-To: <df7e1d69a6ac2ec3db8bb5481431d8ad8a28157a.1550645549.git.sandipan@linux.ibm.com>
Sandipan Das <sandipan@linux.ibm.com> writes:
> This enhances the current selftest framework for validating
> the in-kernel instruction emulation infrastructure by adding
> support for compute type instructions i.e. integer ALU-based
> instructions. Originally, this framework was limited to only
> testing load and store instructions.
>
> While most of the GPRs can be validated, support for SPRs is
> limited to LR, CR and XER for now.
>
> When writing the test cases, one must ensure that the Stack
> Pointer (GPR1) or the Thread Pointer (GPR13) are not touched
> by any means as these are vital non-volatile registers.
>
> Signed-off-by: Sandipan Das <sandipan@linux.ibm.com>
> ---
> arch/powerpc/lib/Makefile | 3 +-
> arch/powerpc/lib/test_emulate_step.c | 167 +++++++++++++++++-
> .../lib/test_emulate_step_exec_instr.S | 150 ++++++++++++++++
> 3 files changed, 315 insertions(+), 5 deletions(-)
> create mode 100644 arch/powerpc/lib/test_emulate_step_exec_instr.S
Hi Sandipan,
Thanks for the exceptionally well written asm, I wish all our asm code
was that neat and well commented :)
I'd like to get this merged today so I tweaked it slightly when
applying to use the new patch_site helpers we added recently, see diff
below.
cheers
diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c
index 1c13b3bebeca..9992c1ea7a1d 100644
--- a/arch/powerpc/lib/test_emulate_step.c
+++ b/arch/powerpc/lib/test_emulate_step.c
@@ -865,14 +865,14 @@ static int __init emulate_compute_instr(struct pt_regs *regs,
static int __init execute_compute_instr(struct pt_regs *regs,
unsigned int instr)
{
- extern unsigned int exec_instr_execute[];
extern int exec_instr(struct pt_regs *regs);
+ extern s32 patch__exec_instr;
if (!regs || !instr)
return -EINVAL;
/* Patch the NOP with the actual instruction */
- patch_instruction(&exec_instr_execute[0], instr);
+ patch_instruction_site(&patch__exec_instr, instr);
if (exec_instr(regs)) {
pr_info("execution failed, instruction = 0x%08x\n", instr);
return -EFAULT;
diff --git a/arch/powerpc/lib/test_emulate_step_exec_instr.S b/arch/powerpc/lib/test_emulate_step_exec_instr.S
index 84cef7d78d9d..1580f34f4f4f 100644
--- a/arch/powerpc/lib/test_emulate_step_exec_instr.S
+++ b/arch/powerpc/lib/test_emulate_step_exec_instr.S
@@ -8,6 +8,7 @@
#include <asm/asm-offsets.h>
#include <asm/ppc_asm.h>
+#include <asm/code-patching-asm.h>
#include <linux/errno.h>
/* int exec_instr(struct pt_regs *regs) */
@@ -78,10 +79,9 @@ _GLOBAL(exec_instr)
REST_GPR(12, r31)
REST_NVGPRS(r31)
- .global exec_instr_execute
-exec_instr_execute:
/* Placeholder for the test instruction */
1: nop
+ patch_site 1b patch__exec_instr
/*
* Since GPR3 is overwritten, temporarily restore it back to its
^ permalink raw reply related
* Re: [PATCH 3/7] powerpc/mm: Add a framework for Kernel Userspace Access Protection
From: Christophe Leroy @ 2019-02-21 10:46 UTC (permalink / raw)
To: Russell Currey, linuxppc-dev; +Cc: npiggin, kernel-hardening
In-Reply-To: <20190221093601.27920-4-ruscur@russell.cc>
Le 21/02/2019 à 10:35, Russell Currey a écrit :
> From: Christophe Leroy <christophe.leroy@c-s.fr>
>
> This patch implements a framework for Kernel Userspace Access
> Protection.
>
> Then subarches will have to possibility to provide their own
> implementation by providing setup_kuap() and lock/unlock_user_access()
>
> Some platform will need to know the area accessed and whether it is
> accessed from read, write or both. Therefore source, destination and
> size and handed over to the two functions.
Should we also consider adding user_access_begin() and the 3 other
associated macros ?
See x86 for details.
Christophe
>
> Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
> [ruscur: remove 64-bit exception handling code]
> Signed-off-by: Russell Currey <ruscur@russell.cc>
> ---
> .../admin-guide/kernel-parameters.txt | 2 +-
> arch/powerpc/include/asm/exception-64e.h | 3 ++
> arch/powerpc/include/asm/exception-64s.h | 3 ++
> arch/powerpc/include/asm/futex.h | 4 ++
> arch/powerpc/include/asm/kup.h | 21 ++++++++++
> arch/powerpc/include/asm/paca.h | 3 ++
> arch/powerpc/include/asm/processor.h | 3 ++
> arch/powerpc/include/asm/ptrace.h | 3 ++
> arch/powerpc/include/asm/uaccess.h | 38 +++++++++++++++----
> arch/powerpc/kernel/asm-offsets.c | 7 ++++
> arch/powerpc/kernel/entry_32.S | 8 +++-
> arch/powerpc/kernel/process.c | 3 ++
> arch/powerpc/lib/checksum_wrappers.c | 4 ++
> arch/powerpc/mm/fault.c | 17 +++++++--
> arch/powerpc/mm/init-common.c | 10 +++++
> arch/powerpc/platforms/Kconfig.cputype | 12 ++++++
> 16 files changed, 127 insertions(+), 14 deletions(-)
>
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index 8448a56a9eb9..0a76dbb39011 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -2808,7 +2808,7 @@
> noexec=on: enable non-executable mappings (default)
> noexec=off: disable non-executable mappings
>
> - nosmap [X86]
> + nosmap [X86,PPC]
> Disable SMAP (Supervisor Mode Access Prevention)
> even if it is supported by processor.
>
> diff --git a/arch/powerpc/include/asm/exception-64e.h b/arch/powerpc/include/asm/exception-64e.h
> index 555e22d5e07f..bf25015834ee 100644
> --- a/arch/powerpc/include/asm/exception-64e.h
> +++ b/arch/powerpc/include/asm/exception-64e.h
> @@ -215,5 +215,8 @@ exc_##label##_book3e:
> #define RFI_TO_USER \
> rfi
>
> +#define UNLOCK_USER_ACCESS(reg)
> +#define LOCK_USER_ACCESS(reg)
> +
> #endif /* _ASM_POWERPC_EXCEPTION_64E_H */
>
> diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h
> index 3b4767ed3ec5..8ae2d7855cfe 100644
> --- a/arch/powerpc/include/asm/exception-64s.h
> +++ b/arch/powerpc/include/asm/exception-64s.h
> @@ -264,6 +264,9 @@ BEGIN_FTR_SECTION_NESTED(943) \
> std ra,offset(r13); \
> END_FTR_SECTION_NESTED(ftr,ftr,943)
>
> +#define UNLOCK_USER_ACCESS(reg)
> +#define LOCK_USER_ACCESS(reg)
> +
> #define EXCEPTION_PROLOG_0(area) \
> GET_PACA(r13); \
> std r9,area+EX_R9(r13); /* save r9 */ \
> diff --git a/arch/powerpc/include/asm/futex.h b/arch/powerpc/include/asm/futex.h
> index 88b38b37c21b..f85facf3739b 100644
> --- a/arch/powerpc/include/asm/futex.h
> +++ b/arch/powerpc/include/asm/futex.h
> @@ -35,6 +35,7 @@ static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
> {
> int oldval = 0, ret;
>
> + unlock_user_access(uaddr, NULL, sizeof(*uaddr));
> pagefault_disable();
>
> switch (op) {
> @@ -62,6 +63,7 @@ static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
> if (!ret)
> *oval = oldval;
>
> + lock_user_access(uaddr, NULL, sizeof(*uaddr));
> return ret;
> }
>
> @@ -75,6 +77,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
> if (!access_ok(uaddr, sizeof(u32)))
> return -EFAULT;
>
> + unlock_user_access(uaddr, NULL, sizeof(*uaddr));
> __asm__ __volatile__ (
> PPC_ATOMIC_ENTRY_BARRIER
> "1: lwarx %1,0,%3 # futex_atomic_cmpxchg_inatomic\n\
> @@ -95,6 +98,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
> : "cc", "memory");
>
> *uval = prev;
> + lock_user_access(uaddr, NULL, sizeof(*uaddr));
> return ret;
> }
>
> diff --git a/arch/powerpc/include/asm/kup.h b/arch/powerpc/include/asm/kup.h
> index af4b5f854ca4..2ac540fb488f 100644
> --- a/arch/powerpc/include/asm/kup.h
> +++ b/arch/powerpc/include/asm/kup.h
> @@ -4,6 +4,8 @@
>
> #ifndef __ASSEMBLY__
>
> +#include <asm/pgtable.h>
> +
> void setup_kup(void);
>
> #ifdef CONFIG_PPC_KUEP
> @@ -12,6 +14,25 @@ void setup_kuep(bool disabled);
> static inline void setup_kuep(bool disabled) { }
> #endif
>
> +#ifdef CONFIG_PPC_KUAP
> +void setup_kuap(bool disabled);
> +#else
> +static inline void setup_kuap(bool disabled) { }
> +static inline void unlock_user_access(void __user *to, const void __user *from,
> + unsigned long size) { }
> +static inline void lock_user_access(void __user *to, const void __user *from,
> + unsigned long size) { }
> +#endif
> +
> #endif /* !__ASSEMBLY__ */
>
> +#ifndef CONFIG_PPC_KUAP
> +
> +#ifdef CONFIG_PPC32
> +#define LOCK_USER_ACCESS(val, sp, sr, srmax, curr)
> +#define REST_USER_ACCESS(val, sp, sr, srmax, curr)
> +#endif
> +
> +#endif
> +
> #endif /* _ASM_POWERPC_KUP_H_ */
> diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
> index e843bc5d1a0f..56236f6d8c89 100644
> --- a/arch/powerpc/include/asm/paca.h
> +++ b/arch/powerpc/include/asm/paca.h
> @@ -169,6 +169,9 @@ struct paca_struct {
> u64 saved_r1; /* r1 save for RTAS calls or PM or EE=0 */
> u64 saved_msr; /* MSR saved here by enter_rtas */
> u16 trap_save; /* Used when bad stack is encountered */
> +#ifdef CONFIG_PPC_KUAP
> + u8 user_access_allowed; /* can the kernel access user memory? */
> +#endif
> u8 irq_soft_mask; /* mask for irq soft masking */
> u8 irq_happened; /* irq happened while soft-disabled */
> u8 io_sync; /* writel() needs spin_unlock sync */
> diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
> index ee58526cb6c2..4a9a10e86828 100644
> --- a/arch/powerpc/include/asm/processor.h
> +++ b/arch/powerpc/include/asm/processor.h
> @@ -250,6 +250,9 @@ struct thread_struct {
> #ifdef CONFIG_PPC32
> void *pgdir; /* root of page-table tree */
> unsigned long ksp_limit; /* if ksp <= ksp_limit stack overflow */
> +#ifdef CONFIG_PPC_KUAP
> + unsigned long kuap; /* state of user access protection */
> +#endif
> #endif
> /* Debug Registers */
> struct debug_reg debug;
> diff --git a/arch/powerpc/include/asm/ptrace.h b/arch/powerpc/include/asm/ptrace.h
> index 0b8a735b6d85..0321ba5b3d12 100644
> --- a/arch/powerpc/include/asm/ptrace.h
> +++ b/arch/powerpc/include/asm/ptrace.h
> @@ -55,6 +55,9 @@ struct pt_regs
> #ifdef CONFIG_PPC64
> unsigned long ppr;
> unsigned long __pad; /* Maintain 16 byte interrupt stack alignment */
> +#elif defined(CONFIG_PPC_KUAP)
> + unsigned long kuap;
> + unsigned long __pad[3]; /* Maintain 16 byte interrupt stack alignment */
> #endif
> };
> #endif
> diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
> index e3a731793ea2..9db6a4c7c058 100644
> --- a/arch/powerpc/include/asm/uaccess.h
> +++ b/arch/powerpc/include/asm/uaccess.h
> @@ -6,6 +6,7 @@
> #include <asm/processor.h>
> #include <asm/page.h>
> #include <asm/extable.h>
> +#include <asm/kup.h>
>
> /*
> * The fs value determines whether argument validity checking should be
> @@ -141,6 +142,7 @@ extern long __put_user_bad(void);
> #define __put_user_size(x, ptr, size, retval) \
> do { \
> retval = 0; \
> + unlock_user_access(ptr, NULL, size); \
> switch (size) { \
> case 1: __put_user_asm(x, ptr, retval, "stb"); break; \
> case 2: __put_user_asm(x, ptr, retval, "sth"); break; \
> @@ -148,6 +150,7 @@ do { \
> case 8: __put_user_asm2(x, ptr, retval); break; \
> default: __put_user_bad(); \
> } \
> + lock_user_access(ptr, NULL, size); \
> } while (0)
>
> #define __put_user_nocheck(x, ptr, size) \
> @@ -240,6 +243,7 @@ do { \
> __chk_user_ptr(ptr); \
> if (size > sizeof(x)) \
> (x) = __get_user_bad(); \
> + unlock_user_access(NULL, ptr, size); \
> switch (size) { \
> case 1: __get_user_asm(x, ptr, retval, "lbz"); break; \
> case 2: __get_user_asm(x, ptr, retval, "lhz"); break; \
> @@ -247,6 +251,7 @@ do { \
> case 8: __get_user_asm2(x, ptr, retval); break; \
> default: (x) = __get_user_bad(); \
> } \
> + lock_user_access(NULL, ptr, size); \
> } while (0)
>
> /*
> @@ -306,15 +311,21 @@ extern unsigned long __copy_tofrom_user(void __user *to,
> static inline unsigned long
> raw_copy_in_user(void __user *to, const void __user *from, unsigned long n)
> {
> - return __copy_tofrom_user(to, from, n);
> + unsigned long ret;
> +
> + unlock_user_access(to, from, n);
> + ret = __copy_tofrom_user(to, from, n);
> + lock_user_access(to, from, n);
> + return ret;
> }
> #endif /* __powerpc64__ */
>
> static inline unsigned long raw_copy_from_user(void *to,
> const void __user *from, unsigned long n)
> {
> + unsigned long ret;
> if (__builtin_constant_p(n) && (n <= 8)) {
> - unsigned long ret = 1;
> + ret = 1;
>
> switch (n) {
> case 1:
> @@ -339,14 +350,18 @@ static inline unsigned long raw_copy_from_user(void *to,
> }
>
> barrier_nospec();
> - return __copy_tofrom_user((__force void __user *)to, from, n);
> + unlock_user_access(NULL, from, n);
> + ret = __copy_tofrom_user((__force void __user *)to, from, n);
> + lock_user_access(NULL, from, n);
> + return ret;
> }
>
> static inline unsigned long raw_copy_to_user(void __user *to,
> const void *from, unsigned long n)
> {
> + unsigned long ret;
> if (__builtin_constant_p(n) && (n <= 8)) {
> - unsigned long ret = 1;
> + ret = 1;
>
> switch (n) {
> case 1:
> @@ -366,17 +381,24 @@ static inline unsigned long raw_copy_to_user(void __user *to,
> return 0;
> }
>
> - return __copy_tofrom_user(to, (__force const void __user *)from, n);
> + unlock_user_access(to, NULL, n);
> + ret = __copy_tofrom_user(to, (__force const void __user *)from, n);
> + lock_user_access(to, NULL, n);
> + return ret;
> }
>
> extern unsigned long __clear_user(void __user *addr, unsigned long size);
>
> static inline unsigned long clear_user(void __user *addr, unsigned long size)
> {
> + unsigned long ret = size;
> might_fault();
> - if (likely(access_ok(addr, size)))
> - return __clear_user(addr, size);
> - return size;
> + if (likely(access_ok(addr, size))) {
> + unlock_user_access(addr, NULL, size);
> + ret = __clear_user(addr, size);
> + lock_user_access(addr, NULL, size);
> + }
> + return ret;
> }
>
> extern long strncpy_from_user(char *dst, const char __user *src, long count);
> diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
> index 9ffc72ded73a..98e94299e728 100644
> --- a/arch/powerpc/kernel/asm-offsets.c
> +++ b/arch/powerpc/kernel/asm-offsets.c
> @@ -93,6 +93,9 @@ int main(void)
> OFFSET(THREAD_INFO, task_struct, stack);
> DEFINE(THREAD_INFO_GAP, _ALIGN_UP(sizeof(struct thread_info), 16));
> OFFSET(KSP_LIMIT, thread_struct, ksp_limit);
> +#ifdef CONFIG_PPC_KUAP
> + OFFSET(KUAP, thread_struct, kuap);
> +#endif
> #endif /* CONFIG_PPC64 */
>
> #ifdef CONFIG_LIVEPATCH
> @@ -260,6 +263,7 @@ int main(void)
> OFFSET(ACCOUNT_STARTTIME_USER, paca_struct, accounting.starttime_user);
> OFFSET(ACCOUNT_USER_TIME, paca_struct, accounting.utime);
> OFFSET(ACCOUNT_SYSTEM_TIME, paca_struct, accounting.stime);
> + OFFSET(PACA_USER_ACCESS_ALLOWED, paca_struct, user_access_allowed);
> OFFSET(PACA_TRAP_SAVE, paca_struct, trap_save);
> OFFSET(PACA_NAPSTATELOST, paca_struct, nap_state_lost);
> OFFSET(PACA_SPRG_VDSO, paca_struct, sprg_vdso);
> @@ -320,6 +324,9 @@ int main(void)
> */
> STACK_PT_REGS_OFFSET(_DEAR, dar);
> STACK_PT_REGS_OFFSET(_ESR, dsisr);
> +#ifdef CONFIG_PPC_KUAP
> + STACK_PT_REGS_OFFSET(_KUAP, kuap);
> +#endif
> #else /* CONFIG_PPC64 */
> STACK_PT_REGS_OFFSET(SOFTE, softe);
> STACK_PT_REGS_OFFSET(_PPR, ppr);
> diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S
> index 0768dfd8a64e..f8f2268f8b12 100644
> --- a/arch/powerpc/kernel/entry_32.S
> +++ b/arch/powerpc/kernel/entry_32.S
> @@ -36,6 +36,7 @@
> #include <asm/asm-405.h>
> #include <asm/feature-fixups.h>
> #include <asm/barrier.h>
> +#include <asm/kup.h>
>
> /*
> * MSR_KERNEL is > 0x10000 on 4xx/Book-E since it include MSR_CE.
> @@ -156,6 +157,7 @@ transfer_to_handler:
> stw r12,_CTR(r11)
> stw r2,_XER(r11)
> mfspr r12,SPRN_SPRG_THREAD
> + LOCK_USER_ACCESS(r2, r11, r9, r0, r12)
> addi r2,r12,-THREAD
> tovirt(r2,r2) /* set r2 to current */
> beq 2f /* if from user, fix up THREAD.regs */
> @@ -442,6 +444,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_NEED_PAIRED_STWCX)
> ACCOUNT_CPU_USER_EXIT(r4, r5, r7)
> 3:
> #endif
> + REST_USER_ACCESS(r7, r1, r4, r5, r2)
> lwz r4,_LINK(r1)
> lwz r5,_CCR(r1)
> mtlr r4
> @@ -739,7 +742,8 @@ fast_exception_return:
> beq 1f /* if not, we've got problems */
> #endif
>
> -2: REST_4GPRS(3, r11)
> +2: REST_USER_ACCESS(r3, r11, r4, r5, r2)
> + REST_4GPRS(3, r11)
> lwz r10,_CCR(r11)
> REST_GPR(1, r11)
> mtcr r10
> @@ -957,6 +961,8 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_TYPE_47x)
> 1:
> #endif /* CONFIG_TRACE_IRQFLAGS */
>
> + REST_USER_ACCESS(r3, r1, r4, r5, r2)
> +
> lwz r0,GPR0(r1)
> lwz r2,GPR2(r1)
> REST_4GPRS(3, r1)
> diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
> index ce393df243aa..995ef82a583d 100644
> --- a/arch/powerpc/kernel/process.c
> +++ b/arch/powerpc/kernel/process.c
> @@ -1771,6 +1771,9 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp)
> regs->mq = 0;
> regs->nip = start;
> regs->msr = MSR_USER;
> +#ifdef CONFIG_PPC_KUAP
> + regs->kuap = KUAP_START;
> +#endif
> #else
> if (!is_32bit_task()) {
> unsigned long entry;
> diff --git a/arch/powerpc/lib/checksum_wrappers.c b/arch/powerpc/lib/checksum_wrappers.c
> index 890d4ddd91d6..48e0c91e0083 100644
> --- a/arch/powerpc/lib/checksum_wrappers.c
> +++ b/arch/powerpc/lib/checksum_wrappers.c
> @@ -28,6 +28,7 @@ __wsum csum_and_copy_from_user(const void __user *src, void *dst,
> {
> unsigned int csum;
>
> + unlock_user_access(NULL, src, len);
> might_sleep();
>
> *err_ptr = 0;
> @@ -60,6 +61,7 @@ __wsum csum_and_copy_from_user(const void __user *src, void *dst,
> }
>
> out:
> + lock_user_access(NULL, src, len);
> return (__force __wsum)csum;
> }
> EXPORT_SYMBOL(csum_and_copy_from_user);
> @@ -69,6 +71,7 @@ __wsum csum_and_copy_to_user(const void *src, void __user *dst, int len,
> {
> unsigned int csum;
>
> + unlock_user_access(dst, NULL, len);
> might_sleep();
>
> *err_ptr = 0;
> @@ -97,6 +100,7 @@ __wsum csum_and_copy_to_user(const void *src, void __user *dst, int len,
> }
>
> out:
> + lock_user_access(dst, NULL, len);
> return (__force __wsum)csum;
> }
> EXPORT_SYMBOL(csum_and_copy_to_user);
> diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
> index ad65e336721a..5f4a5391f41e 100644
> --- a/arch/powerpc/mm/fault.c
> +++ b/arch/powerpc/mm/fault.c
> @@ -223,9 +223,11 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr,
> }
>
> /* Is this a bad kernel fault ? */
> -static bool bad_kernel_fault(bool is_exec, unsigned long error_code,
> +static bool bad_kernel_fault(struct pt_regs *regs, unsigned long error_code,
> unsigned long address)
> {
> + int is_exec = TRAP(regs) == 0x400;
> +
> /* NX faults set DSISR_PROTFAULT on the 8xx, DSISR_NOEXEC_OR_G on others */
> if (is_exec && (error_code & (DSISR_NOEXEC_OR_G | DSISR_KEYFAULT |
> DSISR_PROTFAULT))) {
> @@ -236,7 +238,13 @@ static bool bad_kernel_fault(bool is_exec, unsigned long error_code,
> address, from_kuid(&init_user_ns,
> current_uid()));
> }
> - return is_exec || (address >= TASK_SIZE);
> + if (!is_exec && address < TASK_SIZE && (error_code & DSISR_PROTFAULT) &&
> + !search_exception_tables(regs->nip))
> + printk_ratelimited(KERN_CRIT "Kernel attempted to access user"
> + " page (%lx) - exploit attempt? (uid: %d)\n",
> + address, from_kuid(&init_user_ns,
> + current_uid()));
> + return is_exec || (address >= TASK_SIZE) || !search_exception_tables(regs->nip);
> }
>
> static bool bad_stack_expansion(struct pt_regs *regs, unsigned long address,
> @@ -456,9 +464,10 @@ static int __do_page_fault(struct pt_regs *regs, unsigned long address,
>
> /*
> * The kernel should never take an execute fault nor should it
> - * take a page fault to a kernel address.
> + * take a page fault to a kernel address or a page fault to a user
> + * address outside of dedicated places
> */
> - if (unlikely(!is_user && bad_kernel_fault(is_exec, error_code, address)))
> + if (unlikely(!is_user && bad_kernel_fault(regs, error_code, address)))
> return SIGSEGV;
>
> /*
> diff --git a/arch/powerpc/mm/init-common.c b/arch/powerpc/mm/init-common.c
> index 83f95a5565d6..ecaedfff9992 100644
> --- a/arch/powerpc/mm/init-common.c
> +++ b/arch/powerpc/mm/init-common.c
> @@ -27,6 +27,7 @@
> #include <asm/kup.h>
>
> static bool disable_kuep = !IS_ENABLED(CONFIG_PPC_KUEP);
> +static bool disable_kuap = !IS_ENABLED(CONFIG_PPC_KUAP);
>
> static int __init parse_nosmep(char *p)
> {
> @@ -36,9 +37,18 @@ static int __init parse_nosmep(char *p)
> }
> early_param("nosmep", parse_nosmep);
>
> +static int __init parse_nosmap(char *p)
> +{
> + disable_kuap = true;
> + pr_warn("Disabling Kernel Userspace Access Protection\n");
> + return 0;
> +}
> +early_param("nosmap", parse_nosmap);
> +
> void __init setup_kup(void)
> {
> setup_kuep(disable_kuep);
> + setup_kuap(disable_kuap);
> }
>
> #define CTOR(shift) static void ctor_##shift(void *addr) \
> diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype
> index 410c3443652c..7fa5ddbdce12 100644
> --- a/arch/powerpc/platforms/Kconfig.cputype
> +++ b/arch/powerpc/platforms/Kconfig.cputype
> @@ -351,6 +351,18 @@ config PPC_KUEP
>
> If you're unsure, say Y.
>
> +config PPC_HAVE_KUAP
> + bool
> +
> +config PPC_KUAP
> + bool "Kernel Userspace Access Protection"
> + depends on PPC_HAVE_KUAP
> + default y
> + help
> + Enable support for Kernel Userspace Access Protection (KUAP)
> +
> + If you're unsure, say Y.
> +
> config ARCH_ENABLE_HUGEPAGE_MIGRATION
> def_bool y
> depends on PPC_BOOK3S_64 && HUGETLB_PAGE && MIGRATION
>
^ permalink raw reply
* [PATCH] soc: fsl: guts: make fsl_guts_get_svr() static
From: Horia Geantă @ 2019-02-21 10:37 UTC (permalink / raw)
To: Li Yang; +Cc: linuxppc-dev, linux-kernel, linux-arm-kernel, Yangbo Lu
The export of fsl_guts_get_svr() is a left-over, it's currently used
only internally and users needing SoC information should use the generic
soc_device infrastructure.
Signed-off-by: Horia Geantă <horia.geanta@nxp.com>
---
drivers/soc/fsl/guts.c | 3 +--
include/linux/fsl/guts.h | 2 --
2 files changed, 1 insertion(+), 4 deletions(-)
diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c
index 4f9655087bd7..63f6df86f9e5 100644
--- a/drivers/soc/fsl/guts.c
+++ b/drivers/soc/fsl/guts.c
@@ -115,7 +115,7 @@ static const struct fsl_soc_die_attr *fsl_soc_die_match(
return NULL;
}
-u32 fsl_guts_get_svr(void)
+static u32 fsl_guts_get_svr(void)
{
u32 svr = 0;
@@ -129,7 +129,6 @@ u32 fsl_guts_get_svr(void)
return svr;
}
-EXPORT_SYMBOL(fsl_guts_get_svr);
static int fsl_guts_probe(struct platform_device *pdev)
{
diff --git a/include/linux/fsl/guts.h b/include/linux/fsl/guts.h
index 941b11811f85..1fc0edd71c52 100644
--- a/include/linux/fsl/guts.h
+++ b/include/linux/fsl/guts.h
@@ -135,8 +135,6 @@ struct ccsr_guts {
u32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */
} __attribute__ ((packed));
-u32 fsl_guts_get_svr(void);
-
/* Alternate function signal multiplex control */
#define MPC85xx_PMUXCR_QE(x) (0x8000 >> (x))
--
2.16.2
^ permalink raw reply related
* [PATCH v2 09/10] powerpc/603: Don't worry about _PAGE_USER in TLB miss handlers
From: Christophe Leroy @ 2019-02-21 10:38 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1550745320.git.christophe.leroy@c-s.fr>
PP bits take user access into account, so no need to check _PAGE_USER
here. A DSI or ISI will be generated if needed.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/kernel/head_32.S | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
index b071f328b4b0..6b5cb7551a72 100644
--- a/arch/powerpc/kernel/head_32.S
+++ b/arch/powerpc/kernel/head_32.S
@@ -503,11 +503,9 @@ InstructionTLBMiss:
cmplw 0,r1,r3
#endif
mfspr r2, SPRN_SPRG_PGDIR
- li r1,_PAGE_USER|_PAGE_PRESENT|_PAGE_EXEC /* low addresses tested as user */
+ li r1,_PAGE_PRESENT | _PAGE_EXEC
#if defined(CONFIG_MODULES) || defined(CONFIG_DEBUG_PAGEALLOC)
bge- 112f
- mfspr r2,SPRN_SRR1 /* and MSR_PR bit from SRR1 */
- rlwimi r1,r2,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */
lis r2, (swapper_pg_dir - PAGE_OFFSET)@ha /* if kernel address, use */
addi r2, r2, (swapper_pg_dir - PAGE_OFFSET)@l /* kernel page table */
#endif
@@ -573,10 +571,8 @@ DataLoadTLBMiss:
lis r1,PAGE_OFFSET@h /* check if kernel address */
cmplw 0,r1,r3
mfspr r2, SPRN_SPRG_PGDIR
- li r1,_PAGE_USER|_PAGE_PRESENT /* low addresses tested as user */
+ li r1, _PAGE_PRESENT
bge- 112f
- mfspr r2,SPRN_SRR1 /* and MSR_PR bit from SRR1 */
- rlwimi r1,r2,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */
lis r2, (swapper_pg_dir - PAGE_OFFSET)@ha /* if kernel address, use */
addi r2, r2, (swapper_pg_dir - PAGE_OFFSET)@l /* kernel page table */
112: rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */
@@ -653,10 +649,8 @@ DataStoreTLBMiss:
lis r1,PAGE_OFFSET@h /* check if kernel address */
cmplw 0,r1,r3
mfspr r2, SPRN_SPRG_PGDIR
- li r1,_PAGE_RW|_PAGE_USER|_PAGE_PRESENT /* access flags */
+ li r1, _PAGE_RW | _PAGE_PRESENT /* access flags */
bge- 112f
- mfspr r2,SPRN_SRR1 /* and MSR_PR bit from SRR1 */
- rlwimi r1,r2,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */
lis r2, (swapper_pg_dir - PAGE_OFFSET)@ha /* if kernel address, use */
addi r2, r2, (swapper_pg_dir - PAGE_OFFSET)@l /* kernel page table */
112: rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */
--
2.13.3
^ permalink raw reply related
* [PATCH v2 10/10] powerpc/603: don't handle PAGE_ACCESSED in TLB miss handlers.
From: Christophe Leroy @ 2019-02-21 10:38 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1550745320.git.christophe.leroy@c-s.fr>
PAGE_ACCESSED is only needed for CONFIG_SWAP. When CONFIG_SWAP
is not set, just ignore it. If CONFIG_SWAP is set and PAGE_ACCESSED
is not, let's take a minor fault.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/kernel/head_32.S | 24 +++++++++++++-----------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
index 6b5cb7551a72..fdb587c96a80 100644
--- a/arch/powerpc/kernel/head_32.S
+++ b/arch/powerpc/kernel/head_32.S
@@ -503,7 +503,11 @@ InstructionTLBMiss:
cmplw 0,r1,r3
#endif
mfspr r2, SPRN_SPRG_PGDIR
+#ifdef CONFIG_SWAP
+ li r1,_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_EXEC
+#else
li r1,_PAGE_PRESENT | _PAGE_EXEC
+#endif
#if defined(CONFIG_MODULES) || defined(CONFIG_DEBUG_PAGEALLOC)
bge- 112f
lis r2, (swapper_pg_dir - PAGE_OFFSET)@ha /* if kernel address, use */
@@ -517,12 +521,6 @@ InstructionTLBMiss:
lwz r0,0(r2) /* get linux-style pte */
andc. r1,r1,r0 /* check access & ~permission */
bne- InstructionAddressInvalid /* return if access not permitted */
- ori r0,r0,_PAGE_ACCESSED /* set _PAGE_ACCESSED in pte */
- /*
- * NOTE! We are assuming this is not an SMP system, otherwise
- * we would need to update the pte atomically with lwarx/stwcx.
- */
- stw r0,0(r2) /* update PTE (accessed bit) */
/* Convert linux-style PTE to low word of PPC-style PTE */
rlwimi r0,r0,32-1,30,30 /* _PAGE_USER -> PP msb */
ori r1, r1, 0xe05 /* clear out reserved bits */
@@ -571,7 +569,11 @@ DataLoadTLBMiss:
lis r1,PAGE_OFFSET@h /* check if kernel address */
cmplw 0,r1,r3
mfspr r2, SPRN_SPRG_PGDIR
+#ifdef CONFIG_SWAP
+ li r1, _PAGE_PRESENT | _PAGE_ACCESSED
+#else
li r1, _PAGE_PRESENT
+#endif
bge- 112f
lis r2, (swapper_pg_dir - PAGE_OFFSET)@ha /* if kernel address, use */
addi r2, r2, (swapper_pg_dir - PAGE_OFFSET)@l /* kernel page table */
@@ -583,12 +585,10 @@ DataLoadTLBMiss:
lwz r0,0(r2) /* get linux-style pte */
andc. r1,r1,r0 /* check access & ~permission */
bne- DataAddressInvalid /* return if access not permitted */
- ori r0,r0,_PAGE_ACCESSED /* set _PAGE_ACCESSED in pte */
/*
* NOTE! We are assuming this is not an SMP system, otherwise
* we would need to update the pte atomically with lwarx/stwcx.
*/
- stw r0,0(r2) /* update PTE (accessed bit) */
/* Convert linux-style PTE to low word of PPC-style PTE */
rlwinm r1,r0,32-10,31,31 /* _PAGE_RW -> PP lsb */
rlwimi r0,r0,32-1,30,30 /* _PAGE_USER -> PP msb */
@@ -649,7 +649,11 @@ DataStoreTLBMiss:
lis r1,PAGE_OFFSET@h /* check if kernel address */
cmplw 0,r1,r3
mfspr r2, SPRN_SPRG_PGDIR
- li r1, _PAGE_RW | _PAGE_PRESENT /* access flags */
+#ifdef CONFIG_SWAP
+ li r1, _PAGE_RW | _PAGE_PRESENT | _PAGE_ACCESSED
+#else
+ li r1, _PAGE_RW | _PAGE_PRESENT
+#endif
bge- 112f
lis r2, (swapper_pg_dir - PAGE_OFFSET)@ha /* if kernel address, use */
addi r2, r2, (swapper_pg_dir - PAGE_OFFSET)@l /* kernel page table */
@@ -661,12 +665,10 @@ DataStoreTLBMiss:
lwz r0,0(r2) /* get linux-style pte */
andc. r1,r1,r0 /* check access & ~permission */
bne- DataAddressInvalid /* return if access not permitted */
- ori r0,r0,_PAGE_ACCESSED
/*
* NOTE! We are assuming this is not an SMP system, otherwise
* we would need to update the pte atomically with lwarx/stwcx.
*/
- stw r0,0(r2) /* update PTE (accessed/dirty bits) */
/* Convert linux-style PTE to low word of PPC-style PTE */
rlwimi r0,r0,32-1,30,30 /* _PAGE_USER -> PP msb */
li r1,0xe05 /* clear out reserved bits & PP lsb */
--
2.13.3
^ permalink raw reply related
* [PATCH v2 08/10] powerpc/603: let's handle PAGE_DIRTY directly
From: Christophe Leroy @ 2019-02-21 10:38 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1550745320.git.christophe.leroy@c-s.fr>
PAGE_DIRTY corresponds to the C bit. If writing on
a page for which the C bit is not set, a DataStoreTLBMiss
is generated. No need to check it in DataLoadTLBMiss.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/kernel/head_32.S | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
index 6db54425f1d9..b071f328b4b0 100644
--- a/arch/powerpc/kernel/head_32.S
+++ b/arch/powerpc/kernel/head_32.S
@@ -595,12 +595,10 @@ DataLoadTLBMiss:
stw r0,0(r2) /* update PTE (accessed bit) */
/* Convert linux-style PTE to low word of PPC-style PTE */
rlwinm r1,r0,32-10,31,31 /* _PAGE_RW -> PP lsb */
- rlwinm r2,r0,32-7,31,31 /* _PAGE_DIRTY -> PP lsb */
- and r1,r1,r2 /* writable if _RW and _DIRTY */
rlwimi r0,r0,32-1,30,30 /* _PAGE_USER -> PP msb */
rlwimi r0,r0,32-1,31,31 /* _PAGE_USER -> PP lsb */
ori r1,r1,0xe04 /* clear out reserved bits */
- andc r1,r0,r1 /* PP = user? (rw&dirty? 2: 3): 0 */
+ andc r1,r0,r1 /* PP = user? rw? 2: 3: 0 */
BEGIN_FTR_SECTION
rlwinm r1,r1,0,~_PAGE_COHERENT /* clear M (coherence not required) */
END_FTR_SECTION_IFCLR(CPU_FTR_NEED_COHERENT)
@@ -669,7 +667,7 @@ DataStoreTLBMiss:
lwz r0,0(r2) /* get linux-style pte */
andc. r1,r1,r0 /* check access & ~permission */
bne- DataAddressInvalid /* return if access not permitted */
- ori r0,r0,_PAGE_ACCESSED|_PAGE_DIRTY
+ ori r0,r0,_PAGE_ACCESSED
/*
* NOTE! We are assuming this is not an SMP system, otherwise
* we would need to update the pte atomically with lwarx/stwcx.
--
2.13.3
^ permalink raw reply related
* [PATCH v2 07/10] powerpc/603: Don't handle _PAGE_RW and _PAGE_DIRTY on ITLB misses
From: Christophe Leroy @ 2019-02-21 10:37 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1550745320.git.christophe.leroy@c-s.fr>
_PAGE_RW and _PAGE_DIRTY do not matter for ITLB misses.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/kernel/head_32.S | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
index e4338d785a94..6db54425f1d9 100644
--- a/arch/powerpc/kernel/head_32.S
+++ b/arch/powerpc/kernel/head_32.S
@@ -526,13 +526,9 @@ InstructionTLBMiss:
*/
stw r0,0(r2) /* update PTE (accessed bit) */
/* Convert linux-style PTE to low word of PPC-style PTE */
- rlwinm r1,r0,32-10,31,31 /* _PAGE_RW -> PP lsb */
- rlwinm r2,r0,32-7,31,31 /* _PAGE_DIRTY -> PP lsb */
- and r1,r1,r2 /* writable if _RW and _DIRTY */
rlwimi r0,r0,32-1,30,30 /* _PAGE_USER -> PP msb */
- rlwimi r0,r0,32-1,31,31 /* _PAGE_USER -> PP lsb */
- ori r1,r1,0xe04 /* clear out reserved bits */
- andc r1,r0,r1 /* PP = user? (rw&dirty? 2: 3): 0 */
+ ori r1, r1, 0xe05 /* clear out reserved bits */
+ andc r1, r0, r1 /* PP = user? 2 : 0 */
BEGIN_FTR_SECTION
rlwinm r1,r1,0,~_PAGE_COHERENT /* clear M (coherence not required) */
END_FTR_SECTION_IFCLR(CPU_FTR_NEED_COHERENT)
--
2.13.3
^ permalink raw reply related
* [PATCH v2 05/10] powerpc/hash32: use physical address directly in hash handlers.
From: Christophe Leroy @ 2019-02-21 10:37 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1550745320.git.christophe.leroy@c-s.fr>
Since commit c62ce9ef97ba ("powerpc: remove remaining bits from
CONFIG_APUS"), tophys() has become a pure constant operation.
PAGE_OFFSET is known at compile time so the physical address
can be builtin directly.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/mm/hash_low_32.S | 62 +++++++++++++++++++------------------------
arch/powerpc/mm/ppc_mmu_32.c | 6 +++--
2 files changed, 31 insertions(+), 37 deletions(-)
diff --git a/arch/powerpc/mm/hash_low_32.S b/arch/powerpc/mm/hash_low_32.S
index 82e7dd0c0220..d94fef524ef5 100644
--- a/arch/powerpc/mm/hash_low_32.S
+++ b/arch/powerpc/mm/hash_low_32.S
@@ -47,14 +47,13 @@ mmu_hash_lock:
* Returns to the caller if the access is illegal or there is no
* mapping for the address. Otherwise it places an appropriate PTE
* in the hash table and returns from the exception.
- * Uses r0, r3 - r8, r10, ctr, lr.
+ * Uses r0, r3 - r6, r8, r10, ctr, lr.
*/
.text
_GLOBAL(hash_page)
- tophys(r7,0) /* gets -KERNELBASE into r7 */
#ifdef CONFIG_SMP
- addis r8,r7,mmu_hash_lock@h
- ori r8,r8,mmu_hash_lock@l
+ lis r8, (mmu_hash_lock - PAGE_OFFSET)@h
+ ori r8, r8, (mmu_hash_lock - PAGE_OFFSET)@l
lis r0,0x0fff
b 10f
11: lwz r6,0(r8)
@@ -76,7 +75,7 @@ _GLOBAL(hash_page)
lis r5,swapper_pg_dir@ha /* if kernel address, use */
addi r5,r5,swapper_pg_dir@l /* kernel page table */
rlwimi r3,r9,32-12,29,29 /* MSR_PR -> _PAGE_USER */
-112: add r5,r5,r7 /* convert to phys addr */
+112: tophys(r5, r5)
#ifndef CONFIG_PTE_64BIT
rlwimi r5,r4,12,20,29 /* insert top 10 bits of address */
lwz r8,0(r5) /* get pmd entry */
@@ -143,25 +142,24 @@ retry:
#ifdef CONFIG_SMP
eieio
- addis r8,r7,mmu_hash_lock@ha
+ lis r8, (mmu_hash_lock - PAGE_OFFSET)@ha
li r0,0
- stw r0,mmu_hash_lock@l(r8)
+ stw r0, (mmu_hash_lock - PAGE_OFFSET)@l(r8)
#endif
/* Return from the exception */
lwz r5,_CTR(r11)
mtctr r5
lwz r0,GPR0(r11)
- lwz r7,GPR7(r11)
lwz r8,GPR8(r11)
b fast_exception_return
#ifdef CONFIG_SMP
hash_page_out:
eieio
- addis r8,r7,mmu_hash_lock@ha
+ lis r8, (mmu_hash_lock - PAGE_OFFSET)@ha
li r0,0
- stw r0,mmu_hash_lock@l(r8)
+ stw r0, (mmu_hash_lock - PAGE_OFFSET)@l(r8)
blr
#endif /* CONFIG_SMP */
@@ -207,11 +205,9 @@ _GLOBAL(add_hash_page)
SYNC_601
isync
- tophys(r7,0)
-
#ifdef CONFIG_SMP
- addis r6,r7,mmu_hash_lock@ha
- addi r6,r6,mmu_hash_lock@l
+ lis r6, (mmu_hash_lock - PAGE_OFFSET)@ha
+ addi r6, r6, (mmu_hash_lock - PAGE_OFFSET)@l
10: lwarx r0,0,r6 /* take the mmu_hash_lock */
cmpi 0,r0,0
bne- 11f
@@ -256,8 +252,8 @@ _GLOBAL(add_hash_page)
9:
#ifdef CONFIG_SMP
- addis r6,r7,mmu_hash_lock@ha
- addi r6,r6,mmu_hash_lock@l
+ lis r6, (mmu_hash_lock - PAGE_OFFSET)@ha
+ addi r6, r6, (mmu_hash_lock - PAGE_OFFSET)@l
eieio
li r0,0
stw r0,0(r6) /* clear mmu_hash_lock */
@@ -277,10 +273,8 @@ _GLOBAL(add_hash_page)
* It is designed to be called with the MMU either on or off.
* r3 contains the VSID, r4 contains the virtual address,
* r5 contains the linux PTE, r6 contains the old value of the
- * linux PTE (before setting _PAGE_HASHPTE) and r7 contains the
- * offset to be added to addresses (0 if the MMU is on,
- * -KERNELBASE if it is off). r10 contains the upper half of
- * the PTE if CONFIG_PTE_64BIT.
+ * linux PTE (before setting _PAGE_HASHPTE). r10 contains the
+ * upper half of the PTE if CONFIG_PTE_64BIT.
* On SMP, the caller should have the mmu_hash_lock held.
* We assume that the caller has (or will) set the _PAGE_HASHPTE
* bit in the linux PTE in memory. The value passed in r6 should
@@ -341,7 +335,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_NEED_COHERENT)
patch_site 1f, patch__hash_page_A1
patch_site 2f, patch__hash_page_A2
/* Get the address of the primary PTE group in the hash table (r3) */
-0: addis r0,r7,Hash_base@h /* base address of hash table */
+0: lis r0, (Hash_base - PAGE_OFFSET)@h /* base address of hash table */
1: rlwimi r0,r3,LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT /* VSID -> hash */
2: rlwinm r3,r4,20+LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT /* PI -> hash */
xor r3,r3,r0 /* make primary hash */
@@ -355,10 +349,10 @@ END_FTR_SECTION_IFCLR(CPU_FTR_NEED_COHERENT)
beq+ 10f /* no PTE: go look for an empty slot */
tlbie r4
- addis r4,r7,htab_hash_searches@ha
- lwz r6,htab_hash_searches@l(r4)
+ lis r4, (htab_hash_searches - PAGE_OFFSET)@ha
+ lwz r6, (htab_hash_searches - PAGE_OFFSET)@l(r4)
addi r6,r6,1 /* count how many searches we do */
- stw r6,htab_hash_searches@l(r4)
+ stw r6, (htab_hash_searches - PAGE_OFFSET)@l(r4)
/* Search the primary PTEG for a PTE whose 1st (d)word matches r5 */
mtctr r0
@@ -390,10 +384,10 @@ END_FTR_SECTION_IFCLR(CPU_FTR_NEED_COHERENT)
beq+ found_empty
/* update counter of times that the primary PTEG is full */
- addis r4,r7,primary_pteg_full@ha
- lwz r6,primary_pteg_full@l(r4)
+ lis r4, (primary_pteg_full - PAGE_OFFSET)@ha
+ lwz r6, (primary_pteg_full - PAGE_OFFSET)@l(r4)
addi r6,r6,1
- stw r6,primary_pteg_full@l(r4)
+ stw r6, (primary_pteg_full - PAGE_OFFSET)@l(r4)
patch_site 0f, patch__hash_page_C
/* Search the secondary PTEG for an empty slot */
@@ -427,8 +421,8 @@ END_FTR_SECTION_IFCLR(CPU_FTR_NEED_COHERENT)
* lockup here but that shouldn't happen
*/
-1: addis r4,r7,next_slot@ha /* get next evict slot */
- lwz r6,next_slot@l(r4)
+1: lis r4, (next_slot - PAGE_OFFSET)@ha /* get next evict slot */
+ lwz r6, (next_slot - PAGE_OFFSET)@l(r4)
addi r6,r6,HPTE_SIZE /* search for candidate */
andi. r6,r6,7*HPTE_SIZE
stw r6,next_slot@l(r4)
@@ -500,8 +494,6 @@ htab_hash_searches:
* We assume that there is a hash table in use (Hash != 0).
*/
_GLOBAL(flush_hash_pages)
- tophys(r7,0)
-
/*
* We disable interrupts here, even on UP, because we want
* the _PAGE_HASHPTE bit to be a reliable indication of
@@ -546,10 +538,10 @@ _GLOBAL(flush_hash_pages)
SET_V(r11) /* set V (valid) bit */
#ifdef CONFIG_SMP
- addis r9,r7,mmu_hash_lock@ha
- addi r9,r9,mmu_hash_lock@l
+ lis r9, (mmu_hash_lock - PAGE_OFFSET)@ha
+ addi r9, r9, (mmu_hash_lock - PAGE_OFFSET)@l
CURRENT_THREAD_INFO(r8, r1)
- add r8,r8,r7
+ tophys(r8, r8)
lwz r8,TI_CPU(r8)
oris r8,r8,9
10: lwarx r0,0,r9
@@ -583,7 +575,7 @@ _GLOBAL(flush_hash_pages)
patch_site 1f, patch__flush_hash_A1
patch_site 2f, patch__flush_hash_A2
/* Get the address of the primary PTE group in the hash table (r3) */
-0: addis r8,r7,Hash_base@h /* base address of hash table */
+0: lis r8, (Hash_base - PAGE_OFFSET)@h /* base address of hash table */
1: rlwimi r8,r3,LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT /* VSID -> hash */
2: rlwinm r0,r4,20+LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT /* PI -> hash */
xor r8,r0,r8 /* make primary hash */
diff --git a/arch/powerpc/mm/ppc_mmu_32.c b/arch/powerpc/mm/ppc_mmu_32.c
index 3f4193201ee7..fb747bb0b3e4 100644
--- a/arch/powerpc/mm/ppc_mmu_32.c
+++ b/arch/powerpc/mm/ppc_mmu_32.c
@@ -231,7 +231,8 @@ void __init MMU_init_hw(void)
if (lg_n_hpteg > 16)
mb2 = 16 - LG_HPTEG_SIZE;
- modify_instruction_site(&patch__hash_page_A0, 0xffff, (unsigned int)Hash >> 16);
+ modify_instruction_site(&patch__hash_page_A0, 0xffff,
+ ((unsigned int)Hash - PAGE_OFFSET) >> 16);
modify_instruction_site(&patch__hash_page_A1, 0x7c0, mb << 6);
modify_instruction_site(&patch__hash_page_A2, 0x7c0, mb2 << 6);
modify_instruction_site(&patch__hash_page_B, 0xffff, hmask);
@@ -240,7 +241,8 @@ void __init MMU_init_hw(void)
/*
* Patch up the instructions in hashtable.S:flush_hash_page
*/
- modify_instruction_site(&patch__flush_hash_A0, 0xffff, (unsigned int)Hash >> 16);
+ modify_instruction_site(&patch__flush_hash_A0, 0xffff,
+ ((unsigned int)Hash - PAGE_OFFSET) >> 16);
modify_instruction_site(&patch__flush_hash_A1, 0x7c0, mb << 6);
modify_instruction_site(&patch__flush_hash_A2, 0x7c0, mb2 << 6);
modify_instruction_site(&patch__flush_hash_B, 0xffff, hmask);
--
2.13.3
^ permalink raw reply related
* [PATCH v2 06/10] powerpc/603: Don't handle kernel page TLB misses when not need
From: Christophe Leroy @ 2019-02-21 10:37 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1550745320.git.christophe.leroy@c-s.fr>
ITLB miss on kernel pages only occur with CONFIG_MODULES and
CONFIG_DEBUG_PAGEALLOC.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/kernel/head_32.S | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
index 4c2cc42399aa..e4338d785a94 100644
--- a/arch/powerpc/kernel/head_32.S
+++ b/arch/powerpc/kernel/head_32.S
@@ -498,15 +498,19 @@ InstructionTLBMiss:
*/
/* Get PTE (linux-style) and check access */
mfspr r3,SPRN_IMISS
+#if defined(CONFIG_MODULES) || defined(CONFIG_DEBUG_PAGEALLOC)
lis r1,PAGE_OFFSET@h /* check if kernel address */
cmplw 0,r1,r3
+#endif
mfspr r2, SPRN_SPRG_PGDIR
li r1,_PAGE_USER|_PAGE_PRESENT|_PAGE_EXEC /* low addresses tested as user */
+#if defined(CONFIG_MODULES) || defined(CONFIG_DEBUG_PAGEALLOC)
bge- 112f
mfspr r2,SPRN_SRR1 /* and MSR_PR bit from SRR1 */
rlwimi r1,r2,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */
lis r2, (swapper_pg_dir - PAGE_OFFSET)@ha /* if kernel address, use */
addi r2, r2, (swapper_pg_dir - PAGE_OFFSET)@l /* kernel page table */
+#endif
112: rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */
lwz r2,0(r2) /* get pmd entry */
rlwinm. r2,r2,0,0,19 /* extract address of pte page */
--
2.13.3
^ permalink raw reply related
* [PATCH v2 04/10] powerpc/603: use physical address directly in TLB miss handlers.
From: Christophe Leroy @ 2019-02-21 10:37 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1550745320.git.christophe.leroy@c-s.fr>
Since commit c62ce9ef97ba ("powerpc: remove remaining bits from
CONFIG_APUS"), tophys() has become a pure constant operation.
PAGE_OFFSET is known at compile time so the physical address
can be builtin directly.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/kernel/head_32.S | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
index 2b0a26f66115..4c2cc42399aa 100644
--- a/arch/powerpc/kernel/head_32.S
+++ b/arch/powerpc/kernel/head_32.S
@@ -505,9 +505,8 @@ InstructionTLBMiss:
bge- 112f
mfspr r2,SPRN_SRR1 /* and MSR_PR bit from SRR1 */
rlwimi r1,r2,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */
- lis r2,swapper_pg_dir@ha /* if kernel address, use */
- addi r2,r2,swapper_pg_dir@l /* kernel page table */
- tophys(r2,r2)
+ lis r2, (swapper_pg_dir - PAGE_OFFSET)@ha /* if kernel address, use */
+ addi r2, r2, (swapper_pg_dir - PAGE_OFFSET)@l /* kernel page table */
112: rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */
lwz r2,0(r2) /* get pmd entry */
rlwinm. r2,r2,0,0,19 /* extract address of pte page */
@@ -578,9 +577,8 @@ DataLoadTLBMiss:
bge- 112f
mfspr r2,SPRN_SRR1 /* and MSR_PR bit from SRR1 */
rlwimi r1,r2,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */
- lis r2,swapper_pg_dir@ha /* if kernel address, use */
- addi r2,r2,swapper_pg_dir@l /* kernel page table */
- tophys(r2,r2)
+ lis r2, (swapper_pg_dir - PAGE_OFFSET)@ha /* if kernel address, use */
+ addi r2, r2, (swapper_pg_dir - PAGE_OFFSET)@l /* kernel page table */
112: rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */
lwz r2,0(r2) /* get pmd entry */
rlwinm. r2,r2,0,0,19 /* extract address of pte page */
@@ -661,9 +659,8 @@ DataStoreTLBMiss:
bge- 112f
mfspr r2,SPRN_SRR1 /* and MSR_PR bit from SRR1 */
rlwimi r1,r2,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */
- lis r2,swapper_pg_dir@ha /* if kernel address, use */
- addi r2,r2,swapper_pg_dir@l /* kernel page table */
- tophys(r2,r2)
+ lis r2, (swapper_pg_dir - PAGE_OFFSET)@ha /* if kernel address, use */
+ addi r2, r2, (swapper_pg_dir - PAGE_OFFSET)@l /* kernel page table */
112: rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */
lwz r2,0(r2) /* get pmd entry */
rlwinm. r2,r2,0,0,19 /* extract address of pte page */
--
2.13.3
^ permalink raw reply related
* [PATCH v2 03/10] powerpc/6xx: Store PGDIR physical address in a SPRG
From: Christophe Leroy @ 2019-02-21 10:37 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1550745320.git.christophe.leroy@c-s.fr>
Use SPRN_SPRG2 to store the current thread PGDIR and
avoid reading thread_struct.pgdir at every TLB miss.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/include/asm/reg.h | 1 +
arch/powerpc/kernel/cpu_setup_6xx.S | 4 ++++
arch/powerpc/kernel/head_32.S | 25 ++++++++++++-------------
arch/powerpc/mm/hash_low_32.S | 3 +--
4 files changed, 18 insertions(+), 15 deletions(-)
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index 371ef6e8248e..1f79e1d8fb0b 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -1167,6 +1167,7 @@
#ifdef CONFIG_PPC_BOOK3S_32
#define SPRN_SPRG_SCRATCH0 SPRN_SPRG0
#define SPRN_SPRG_SCRATCH1 SPRN_SPRG1
+#define SPRN_SPRG_PGDIR SPRN_SPRG2
#define SPRN_SPRG_603_LRU SPRN_SPRG4
#endif
diff --git a/arch/powerpc/kernel/cpu_setup_6xx.S b/arch/powerpc/kernel/cpu_setup_6xx.S
index 8c069e96c478..6f1c11e0691f 100644
--- a/arch/powerpc/kernel/cpu_setup_6xx.S
+++ b/arch/powerpc/kernel/cpu_setup_6xx.S
@@ -24,6 +24,10 @@ BEGIN_MMU_FTR_SECTION
li r10,0
mtspr SPRN_SPRG_603_LRU,r10 /* init SW LRU tracking */
END_MMU_FTR_SECTION_IFSET(MMU_FTR_NEED_DTLB_SW_LRU)
+ lis r10, (swapper_pg_dir - PAGE_OFFSET)@h
+ ori r10, r10, (swapper_pg_dir - PAGE_OFFSET)@l
+ mtspr SPRN_SPRG_PGDIR, r10
+
BEGIN_FTR_SECTION
bl __init_fpu_registers
END_FTR_SECTION_IFCLR(CPU_FTR_FPU_UNAVAILABLE)
diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
index 04128899a0a5..2b0a26f66115 100644
--- a/arch/powerpc/kernel/head_32.S
+++ b/arch/powerpc/kernel/head_32.S
@@ -500,16 +500,15 @@ InstructionTLBMiss:
mfspr r3,SPRN_IMISS
lis r1,PAGE_OFFSET@h /* check if kernel address */
cmplw 0,r1,r3
- mfspr r2,SPRN_SPRG_THREAD
+ mfspr r2, SPRN_SPRG_PGDIR
li r1,_PAGE_USER|_PAGE_PRESENT|_PAGE_EXEC /* low addresses tested as user */
- lwz r2,PGDIR(r2)
bge- 112f
mfspr r2,SPRN_SRR1 /* and MSR_PR bit from SRR1 */
rlwimi r1,r2,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */
lis r2,swapper_pg_dir@ha /* if kernel address, use */
addi r2,r2,swapper_pg_dir@l /* kernel page table */
-112: tophys(r2,r2)
- rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */
+ tophys(r2,r2)
+112: rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */
lwz r2,0(r2) /* get pmd entry */
rlwinm. r2,r2,0,0,19 /* extract address of pte page */
beq- InstructionAddressInvalid /* return if no mapping */
@@ -574,16 +573,15 @@ DataLoadTLBMiss:
mfspr r3,SPRN_DMISS
lis r1,PAGE_OFFSET@h /* check if kernel address */
cmplw 0,r1,r3
- mfspr r2,SPRN_SPRG_THREAD
+ mfspr r2, SPRN_SPRG_PGDIR
li r1,_PAGE_USER|_PAGE_PRESENT /* low addresses tested as user */
- lwz r2,PGDIR(r2)
bge- 112f
mfspr r2,SPRN_SRR1 /* and MSR_PR bit from SRR1 */
rlwimi r1,r2,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */
lis r2,swapper_pg_dir@ha /* if kernel address, use */
addi r2,r2,swapper_pg_dir@l /* kernel page table */
-112: tophys(r2,r2)
- rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */
+ tophys(r2,r2)
+112: rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */
lwz r2,0(r2) /* get pmd entry */
rlwinm. r2,r2,0,0,19 /* extract address of pte page */
beq- DataAddressInvalid /* return if no mapping */
@@ -658,16 +656,15 @@ DataStoreTLBMiss:
mfspr r3,SPRN_DMISS
lis r1,PAGE_OFFSET@h /* check if kernel address */
cmplw 0,r1,r3
- mfspr r2,SPRN_SPRG_THREAD
+ mfspr r2, SPRN_SPRG_PGDIR
li r1,_PAGE_RW|_PAGE_USER|_PAGE_PRESENT /* access flags */
- lwz r2,PGDIR(r2)
bge- 112f
mfspr r2,SPRN_SRR1 /* and MSR_PR bit from SRR1 */
rlwimi r1,r2,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */
lis r2,swapper_pg_dir@ha /* if kernel address, use */
addi r2,r2,swapper_pg_dir@l /* kernel page table */
-112: tophys(r2,r2)
- rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */
+ tophys(r2,r2)
+112: rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */
lwz r2,0(r2) /* get pmd entry */
rlwinm. r2,r2,0,0,19 /* extract address of pte page */
beq- DataAddressInvalid /* return if no mapping */
@@ -1024,14 +1021,16 @@ _ENTRY(switch_mmu_context)
li r0,NUM_USER_SEGMENTS
mtctr r0
+ lwz r4, MM_PGD(r4)
#ifdef CONFIG_BDI_SWITCH
/* Context switch the PTE pointer for the Abatron BDI2000.
* The PGDIR is passed as second argument.
*/
- lwz r4,MM_PGD(r4)
lis r5, abatron_pteptrs@ha
stw r4, abatron_pteptrs@l + 0x4(r5)
#endif
+ tophys(r4, r4)
+ mtspr SPRN_SPRG_PGDIR, r4
li r4,0
isync
3:
diff --git a/arch/powerpc/mm/hash_low_32.S b/arch/powerpc/mm/hash_low_32.S
index 1e2df3e9f9ea..82e7dd0c0220 100644
--- a/arch/powerpc/mm/hash_low_32.S
+++ b/arch/powerpc/mm/hash_low_32.S
@@ -70,9 +70,8 @@ _GLOBAL(hash_page)
/* Get PTE (linux-style) and check access */
lis r0,KERNELBASE@h /* check if kernel address */
cmplw 0,r4,r0
- mfspr r8,SPRN_SPRG_THREAD /* current task's THREAD (phys) */
ori r3,r3,_PAGE_USER|_PAGE_PRESENT /* test low addresses as user */
- lwz r5,PGDIR(r8) /* virt page-table root */
+ mfspr r5, SPRN_SPRG_PGDIR /* virt page-table root */
blt+ 112f /* assume user more likely */
lis r5,swapper_pg_dir@ha /* if kernel address, use */
addi r5,r5,swapper_pg_dir@l /* kernel page table */
--
2.13.3
^ permalink raw reply related
* [PATCH v2 02/10] powerpc/6xx: Don't use SPRN_SPRG2 for storing stack pointer while in RTAS
From: Christophe Leroy @ 2019-02-21 10:37 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1550745320.git.christophe.leroy@c-s.fr>
When calling RTAS, the stack pointer is stored in SPRN_SPRG2
in order to be able to restore it in case of machine check in RTAS.
As machine check is not a perfomance critical path, this patch
frees SPRN_SPRG2 by using a field in thread struct instead.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/include/asm/processor.h | 3 +++
arch/powerpc/include/asm/reg.h | 1 -
arch/powerpc/kernel/asm-offsets.c | 3 +++
arch/powerpc/kernel/entry_32.S | 5 +++--
arch/powerpc/kernel/head_32.S | 22 ++++++++++++----------
5 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index ee58526cb6c2..e8682122ea3d 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -250,6 +250,9 @@ struct thread_struct {
#ifdef CONFIG_PPC32
void *pgdir; /* root of page-table tree */
unsigned long ksp_limit; /* if ksp <= ksp_limit stack overflow */
+#ifdef CONFIG_PPC_RTAS
+ unsigned long rtas_sp; /* stack pointer for when in RTAS */
+#endif
#endif
/* Debug Registers */
struct debug_reg debug;
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index 1c98ef1f2d5b..371ef6e8248e 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -1167,7 +1167,6 @@
#ifdef CONFIG_PPC_BOOK3S_32
#define SPRN_SPRG_SCRATCH0 SPRN_SPRG0
#define SPRN_SPRG_SCRATCH1 SPRN_SPRG1
-#define SPRN_SPRG_RTAS SPRN_SPRG2
#define SPRN_SPRG_603_LRU SPRN_SPRG4
#endif
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 9ffc72ded73a..d6f9bdb1eb2e 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -93,6 +93,9 @@ int main(void)
OFFSET(THREAD_INFO, task_struct, stack);
DEFINE(THREAD_INFO_GAP, _ALIGN_UP(sizeof(struct thread_info), 16));
OFFSET(KSP_LIMIT, thread_struct, ksp_limit);
+#ifdef CONFIG_PPC_RTAS
+ OFFSET(RTAS_SP, thread_struct, rtas_sp);
+#endif
#endif /* CONFIG_PPC64 */
#ifdef CONFIG_LIVEPATCH
diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S
index 0768dfd8a64e..394772b0a6e2 100644
--- a/arch/powerpc/kernel/entry_32.S
+++ b/arch/powerpc/kernel/entry_32.S
@@ -1335,7 +1335,7 @@ _GLOBAL(enter_rtas)
MTMSRD(r0) /* don't get trashed */
li r9,MSR_KERNEL & ~(MSR_IR|MSR_DR)
mtlr r6
- mtspr SPRN_SPRG_RTAS,r7
+ stw r7, THREAD + RTAS_SP(r2)
mtspr SPRN_SRR0,r8
mtspr SPRN_SRR1,r9
RFI
@@ -1344,7 +1344,8 @@ _GLOBAL(enter_rtas)
lwz r9,8(r9) /* original msr value */
addi r1,r1,INT_FRAME_SIZE
li r0,0
- mtspr SPRN_SPRG_RTAS,r0
+ tophys(r7, r2)
+ stw r0, THREAD + RTAS_SP(r7)
mtspr SPRN_SRR0,r8
mtspr SPRN_SRR1,r9
RFI /* return to caller */
diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
index c2f564690778..04128899a0a5 100644
--- a/arch/powerpc/kernel/head_32.S
+++ b/arch/powerpc/kernel/head_32.S
@@ -352,9 +352,8 @@ i##n: \
* registers that might have bad values includes all the GPRs
* and all the BATs. We indicate that we are in RTAS by putting
* a non-zero value, the address of the exception frame to use,
- * in SPRG2. The machine check handler checks SPRG2 and uses its
- * value if it is non-zero. If we ever needed to free up SPRG2,
- * we could use a field in the thread_info or thread_struct instead.
+ * in thread.rtas_sp. The machine check handler checks thread.rtas_sp
+ * and uses its value if it is non-zero.
* (Other exception handlers assume that r1 is a valid kernel stack
* pointer when we take an exception from supervisor mode.)
* -- paulus.
@@ -365,16 +364,15 @@ i##n: \
mtspr SPRN_SPRG_SCRATCH1,r11
mfcr r10
#ifdef CONFIG_PPC_CHRP
- mfspr r11,SPRN_SPRG_RTAS
- cmpwi 0,r11,0
- bne 7f
+ mfspr r11, SPRN_SPRG_THREAD
+ lwz r11, RTAS_SP(r11)
+ cmpwi cr1, r11, 0
+ bne cr1, 7f
#endif /* CONFIG_PPC_CHRP */
EXCEPTION_PROLOG_1
7: EXCEPTION_PROLOG_2
addi r3,r1,STACK_FRAME_OVERHEAD
#ifdef CONFIG_PPC_CHRP
- mfspr r4,SPRN_SPRG_RTAS
- cmpwi cr1,r4,0
bne cr1,1f
#endif
EXC_XFER_STD(0x200, machine_check_exception)
@@ -865,8 +863,10 @@ __secondary_start:
tophys(r4,r2)
addi r4,r4,THREAD /* phys address of our thread_struct */
mtspr SPRN_SPRG_THREAD,r4
+#ifdef CONFIG_PPC_RTAS
li r3,0
- mtspr SPRN_SPRG_RTAS,r3 /* 0 => not in RTAS */
+ stw r3, RTAS_SP(r4) /* 0 => not in RTAS */
+#endif
/* enable MMU and jump to start_secondary */
li r4,MSR_KERNEL
@@ -950,8 +950,10 @@ start_here:
tophys(r4,r2)
addi r4,r4,THREAD /* init task's THREAD */
mtspr SPRN_SPRG_THREAD,r4
+#ifdef CONFIG_PPC_RTAS
li r3,0
- mtspr SPRN_SPRG_RTAS,r3 /* 0 => not in RTAS */
+ stw r3, RTAS_SP(r4) /* 0 => not in RTAS */
+#endif
/* stack */
lis r1,init_thread_union@ha
--
2.13.3
^ permalink raw reply related
* [PATCH v2 01/10] powerpc: simplify BDI switch
From: Christophe Leroy @ 2019-02-21 10:37 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1550745320.git.christophe.leroy@c-s.fr>
There is no reason to re-read each time the pointer at
location 0xf0 as it is fixed and known.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
---
arch/powerpc/include/asm/mmu.h | 2 ++
arch/powerpc/kernel/head_32.S | 5 ++---
arch/powerpc/kernel/head_40x.S | 5 ++---
arch/powerpc/kernel/head_8xx.S | 1 +
arch/powerpc/mm/8xx_mmu.c | 7 ++-----
5 files changed, 9 insertions(+), 11 deletions(-)
diff --git a/arch/powerpc/include/asm/mmu.h b/arch/powerpc/include/asm/mmu.h
index 25607604a7a5..6d22a8e78fe2 100644
--- a/arch/powerpc/include/asm/mmu.h
+++ b/arch/powerpc/include/asm/mmu.h
@@ -356,6 +356,8 @@ extern void early_init_mmu_secondary(void);
extern void setup_initial_memory_limit(phys_addr_t first_memblock_base,
phys_addr_t first_memblock_size);
static inline void mmu_early_init_devtree(void) { }
+
+extern void *abatron_pteptrs[2];
#endif /* __ASSEMBLY__ */
#endif
diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
index 05b08db3901d..c2f564690778 100644
--- a/arch/powerpc/kernel/head_32.S
+++ b/arch/powerpc/kernel/head_32.S
@@ -1027,9 +1027,8 @@ _ENTRY(switch_mmu_context)
* The PGDIR is passed as second argument.
*/
lwz r4,MM_PGD(r4)
- lis r5, KERNELBASE@h
- lwz r5, 0xf0(r5)
- stw r4, 0x4(r5)
+ lis r5, abatron_pteptrs@ha
+ stw r4, abatron_pteptrs@l + 0x4(r5)
#endif
li r4,0
isync
diff --git a/arch/powerpc/kernel/head_40x.S b/arch/powerpc/kernel/head_40x.S
index b19d78410511..11dd09d0ce1a 100644
--- a/arch/powerpc/kernel/head_40x.S
+++ b/arch/powerpc/kernel/head_40x.S
@@ -953,9 +953,8 @@ _GLOBAL(set_context)
/* Context switch the PTE pointer for the Abatron BDI2000.
* The PGDIR is the second parameter.
*/
- lis r5, KERNELBASE@h
- lwz r5, 0xf0(r5)
- stw r4, 0x4(r5)
+ lis r5, abatron_pteptrs@ha
+ stw r4, abatron_pteptrs@l + 0x4(r5)
#endif
sync
mtspr SPRN_PID,r3
diff --git a/arch/powerpc/kernel/head_8xx.S b/arch/powerpc/kernel/head_8xx.S
index 20cc816b3508..ec12abc2a8ce 100644
--- a/arch/powerpc/kernel/head_8xx.S
+++ b/arch/powerpc/kernel/head_8xx.S
@@ -989,5 +989,6 @@ swapper_pg_dir:
/* Room for two PTE table poiners, usually the kernel and current user
* pointer to their respective root page table (pgdir).
*/
+ .globl abatron_pteptrs
abatron_pteptrs:
.space 8
diff --git a/arch/powerpc/mm/8xx_mmu.c b/arch/powerpc/mm/8xx_mmu.c
index bfa503cff351..f12ec85e965c 100644
--- a/arch/powerpc/mm/8xx_mmu.c
+++ b/arch/powerpc/mm/8xx_mmu.c
@@ -162,14 +162,11 @@ void set_context(unsigned long id, pgd_t *pgd)
{
s16 offset = (s16)(__pa(swapper_pg_dir));
-#ifdef CONFIG_BDI_SWITCH
- pgd_t **ptr = *(pgd_t ***)(KERNELBASE + 0xf0);
-
/* Context switch the PTE pointer for the Abatron BDI2000.
* The PGDIR is passed as second argument.
*/
- *(ptr + 1) = pgd;
-#endif
+ if (IS_ENABLED(CONFIG_BDI_SWITCH))
+ abatron_pteptrs[1] = pgd;
/* Register M_TWB will contain base address of level 1 table minus the
* lower part of the kernel PGDIR base address, so that all accesses to
--
2.13.3
^ permalink raw reply related
* [PATCH v2 00/10] Optimise TLB miss handlers on 6xx (603/e300)
From: Christophe Leroy @ 2019-02-21 10:37 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
The main purpose of this series is to optimise the handling of
TLB misses on the 603/e300.
Today the TLB miss handlers are implemented by more or less
copying the actions performed by the hash page handlers used
on processors having HASH pagetable.
This series brings some simplification.
It also improves TLB misses on all 6xx by storing the PGDIR phys address
in an SPRG instead of reading it from thread struct.
v2:
- Added a patch which frees SPRN_SPRG2 by saving RTAS entry stack pointer in
the thread struct, allowing to use it for storing the PGDIR for all 6xx.
- Now using SPRN_SPRG2 for storing the PGDIR instead of SPRN_SPRG5. This allows
the old 6xx having only 4 SPRGs to also get the benefit of this change.
- Removed the last patch of the series which was reordering the _PAGE_FLAGS bits
because it fails on hash 6xx. Will be re-added later once fixed.
Christophe Leroy (10):
powerpc: simplify BDI switch
powerpc/6xx: Don't use SPRN_SPRG2 for storing stack pointer while in
RTAS
powerpc/6xx: Store PGDIR physical address in a SPRG
powerpc/603: use physical address directly in TLB miss handlers.
powerpc/hash32: use physical address directly in hash handlers.
powerpc/603: Don't handle kernel page TLB misses when not need
powerpc/603: Don't handle _PAGE_RW and _PAGE_DIRTY on ITLB misses
powerpc/603: let's handle PAGE_DIRTY directly
powerpc/603: Don't worry about _PAGE_USER in TLB miss handlers
powerpc/603: don't handle PAGE_ACCESSED in TLB miss handlers.
arch/powerpc/include/asm/mmu.h | 2 +
arch/powerpc/include/asm/processor.h | 3 +
arch/powerpc/include/asm/reg.h | 2 +-
arch/powerpc/kernel/asm-offsets.c | 3 +
arch/powerpc/kernel/cpu_setup_6xx.S | 4 ++
arch/powerpc/kernel/entry_32.S | 5 +-
arch/powerpc/kernel/head_32.S | 111 ++++++++++++++++-------------------
arch/powerpc/kernel/head_40x.S | 5 +-
arch/powerpc/kernel/head_8xx.S | 1 +
arch/powerpc/mm/8xx_mmu.c | 7 +--
arch/powerpc/mm/hash_low_32.S | 65 +++++++++-----------
arch/powerpc/mm/ppc_mmu_32.c | 6 +-
12 files changed, 104 insertions(+), 110 deletions(-)
--
2.13.3
^ permalink raw reply
* Re: [PATCH] ASoC: fsl_esai: fix channel swap issue when stream starts
From: Fabio Estevam @ 2019-02-21 10:29 UTC (permalink / raw)
To: S.j. Wang
Cc: alsa-devel@alsa-project.org, timur@kernel.org,
Xiubo.Lee@gmail.com, nicoleotsuka@gmail.com, broonie@kernel.org,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <1550742787-3268-1-git-send-email-shengjiu.wang@nxp.com>
Hi Shengjiu.
On Thu, Feb 21, 2019 at 6:53 AM S.j. Wang <shengjiu.wang@nxp.com> wrote:
>
> From: Shengjiu Wang <shengjiu.wang@freescale.com>
Better use your nxp.com address as the freescale.com domain is gone
for a long time.
> There is very low possibility ( < 0.1% ) that channel swap happened
> in beginning when multi output/input pin is enabled. The issue is
> that hardware can't send data to correct pin in the begginning with
s/begginning/beginning
> the normal enable flow.
>
> This is hardware issue, the workaround flow is that: Each time
Is there an erratum reference for this issue? If so, please add it here.
> playback/recording, firstly clear the xSMA/xSMB, then enable TE/RE,
> then enable xSMB and xSMA (xSMB must be enabled before xSMA).
> Which is to use the xSMA as the trigger start register, previously
> the xCR_TE or xCR_RE is the bit for starting
Please add a Fixes tag and Cc stable.
> Signed-off-by: Shengjiu Wang <shengjiu.wang@freescale.com>
Reviewed-by: Fabio Estevam <festevam@gmail.com>
Thanks
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox