mirror of
https://github.com/team-infusion-developers/android_kernel_samsung_msm8976.git
synced 2024-10-31 18:09:19 +00:00
845958d358
detected corruption It's sometimes useful to know the physical address which has beencorrupted, especially in systems with multiple bus masters and DMA engines the capability of writing to memory. It's may also be useful for identifying the location of failures of memory cells in cases of device-specific corruption. Print the physical start address of the page to help in these scenarios. Change-Id: I081edd8b1c06913c0057a6cb9dda18077cfbdc30 Signed-off-by: Matt Wagantall <mattw@codeaurora.org>
115 lines
2.4 KiB
C
115 lines
2.4 KiB
C
#include <linux/kernel.h>
|
|
#include <linux/string.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/highmem.h>
|
|
#include <linux/page-debug-flags.h>
|
|
#include <linux/poison.h>
|
|
#include <linux/ratelimit.h>
|
|
|
|
#ifndef mark_addr_rdonly
|
|
#define mark_addr_rdonly(a)
|
|
#endif
|
|
|
|
#ifndef mark_addr_rdwrite
|
|
#define mark_addr_rdwrite(a)
|
|
#endif
|
|
|
|
static inline void set_page_poison(struct page *page)
|
|
{
|
|
__set_bit(PAGE_DEBUG_FLAG_POISON, &page->debug_flags);
|
|
}
|
|
|
|
static inline void clear_page_poison(struct page *page)
|
|
{
|
|
__clear_bit(PAGE_DEBUG_FLAG_POISON, &page->debug_flags);
|
|
}
|
|
|
|
static inline bool page_poison(struct page *page)
|
|
{
|
|
return test_bit(PAGE_DEBUG_FLAG_POISON, &page->debug_flags);
|
|
}
|
|
|
|
static void poison_page(struct page *page)
|
|
{
|
|
void *addr = kmap_atomic(page);
|
|
|
|
set_page_poison(page);
|
|
memset(addr, PAGE_POISON, PAGE_SIZE);
|
|
mark_addr_rdonly(addr);
|
|
kunmap_atomic(addr);
|
|
}
|
|
|
|
static void poison_pages(struct page *page, int n)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < n; i++)
|
|
poison_page(page + i);
|
|
}
|
|
|
|
static bool single_bit_flip(unsigned char a, unsigned char b)
|
|
{
|
|
unsigned char error = a ^ b;
|
|
|
|
return error && !(error & (error - 1));
|
|
}
|
|
|
|
static void check_poison_mem(struct page *page,
|
|
unsigned char *mem, size_t bytes)
|
|
{
|
|
static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 10);
|
|
unsigned char *start;
|
|
unsigned char *end;
|
|
|
|
start = memchr_inv(mem, PAGE_POISON, bytes);
|
|
if (!start)
|
|
return;
|
|
|
|
for (end = mem + bytes - 1; end > start; end--) {
|
|
if (*end != PAGE_POISON)
|
|
break;
|
|
}
|
|
|
|
if (!__ratelimit(&ratelimit))
|
|
return;
|
|
else if (start == end && single_bit_flip(*start, PAGE_POISON))
|
|
printk(KERN_ERR "pagealloc: single bit error on page with phys start 0x%lx\n",
|
|
(unsigned long)page_to_phys(page));
|
|
else
|
|
printk(KERN_ERR "pagealloc: memory corruption on page with phys start 0x%lx\n",
|
|
(unsigned long)page_to_phys(page));
|
|
print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 16, 1, start,
|
|
end - start + 1, 1);
|
|
BUG_ON(PANIC_CORRUPTION);
|
|
dump_stack();
|
|
}
|
|
|
|
static void unpoison_page(struct page *page)
|
|
{
|
|
void *addr;
|
|
|
|
if (!page_poison(page))
|
|
return;
|
|
|
|
addr = kmap_atomic(page);
|
|
check_poison_mem(page, addr, PAGE_SIZE);
|
|
mark_addr_rdwrite(addr);
|
|
clear_page_poison(page);
|
|
kunmap_atomic(addr);
|
|
}
|
|
|
|
static void unpoison_pages(struct page *page, int n)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < n; i++)
|
|
unpoison_page(page + i);
|
|
}
|
|
|
|
void kernel_map_pages(struct page *page, int numpages, int enable)
|
|
{
|
|
if (enable)
|
|
unpoison_pages(page, numpages);
|
|
else
|
|
poison_pages(page, numpages);
|
|
}
|