Skip to content

Commit

Permalink
[ARM] S3C: Split the resume memory check code from pm.c
Browse files Browse the repository at this point in the history
Split the optional memory check code out of the pm.c file
as it is quite a big #ifdef block and as-such can be moved
out and simply compiled when the configuration is set.

Signed-off-by: Ben Dooks <ben-linux@fluff.org>
  • Loading branch information
Ben Dooks committed Mar 8, 2009
1 parent 6419711 commit 549c7e3
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 203 deletions.
1 change: 1 addition & 0 deletions arch/arm/plat-s3c/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ obj-y += gpio-config.o
# PM support

obj-$(CONFIG_PM) += pm.o
obj-$(CONFIG_S3C2410_PM_CHECK) += pm-check.o

# devices

Expand Down
12 changes: 12 additions & 0 deletions arch/arm/plat-s3c/include/plat/pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,15 @@ extern void s3c_pm_dbg(const char *msg, ...);
#else
#define S3C_PMDBG(fmt...) printk(KERN_DEBUG fmt)
#endif

/* suspend memory checking */

#ifdef CONFIG_S3C2410_PM_CHECK
extern void s3c_pm_check_prepare(void);
extern void s3c_pm_check_restore(void);
extern void s3c_pm_check_store(void);
#else
#define s3c_pm_check_prepare() do { } while(0)
#define s3c_pm_check_restore() do { } while(0)
#define s3c_pm_check_store() do { } while(0)
#endif
220 changes: 220 additions & 0 deletions arch/arm/plat-s3c/pm-check.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/* linux/arch/arm/plat-s3c/pm-check.c
* originally in linux/arch/arm/plat-s3c24xx/pm.c
*
* Copyright (c) 2004,2006,2008 Simtec Electronics
* http://armlinux.simtec.co.uk
* Ben Dooks <ben@simtec.co.uk>
*
* S3C Power Mangament - suspend/resume memory corruptiuon check.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/

#include <linux/kernel.h>
#include <linux/suspend.h>
#include <linux/init.h>
#include <linux/crc32.h>
#include <linux/ioport.h>

#include <plat/pm.h>

#if CONFIG_S3C2410_PM_CHECK_CHUNKSIZE < 1
#error CONFIG_S3C2410_PM_CHECK_CHUNKSIZE must be a positive non-zero value
#endif

/* suspend checking code...
*
* this next area does a set of crc checks over all the installed
* memory, so the system can verify if the resume was ok.
*
* CONFIG_S3C2410_PM_CHECK_CHUNKSIZE defines the block-size for the CRC,
* increasing it will mean that the area corrupted will be less easy to spot,
* and reducing the size will cause the CRC save area to grow
*/

#define CHECK_CHUNKSIZE (CONFIG_S3C2410_PM_CHECK_CHUNKSIZE * 1024)

static u32 crc_size; /* size needed for the crc block */
static u32 *crcs; /* allocated over suspend/resume */

typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg);

/* s3c_pm_run_res
*
* go through the given resource list, and look for system ram
*/

static void s3c_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg)
{
while (ptr != NULL) {
if (ptr->child != NULL)
s3c_pm_run_res(ptr->child, fn, arg);

if ((ptr->flags & IORESOURCE_MEM) &&
strcmp(ptr->name, "System RAM") == 0) {
S3C_PMDBG("Found system RAM at %08lx..%08lx\n",
ptr->start, ptr->end);
arg = (fn)(ptr, arg);
}

ptr = ptr->sibling;
}
}

static void s3c_pm_run_sysram(run_fn_t fn, u32 *arg)
{
s3c_pm_run_res(&iomem_resource, fn, arg);
}

static u32 *s3c_pm_countram(struct resource *res, u32 *val)
{
u32 size = (u32)(res->end - res->start)+1;

size += CHECK_CHUNKSIZE-1;
size /= CHECK_CHUNKSIZE;

S3C_PMDBG("Area %08lx..%08lx, %d blocks\n", res->start, res->end, size);

*val += size * sizeof(u32);
return val;
}

/* s3c_pm_prepare_check
*
* prepare the necessary information for creating the CRCs. This
* must be done before the final save, as it will require memory
* allocating, and thus touching bits of the kernel we do not
* know about.
*/

void s3c_pm_check_prepare(void)
{
crc_size = 0;

s3c_pm_run_sysram(s3c_pm_countram, &crc_size);

S3C_PMDBG("s3c_pm_prepare_check: %u checks needed\n", crc_size);

crcs = kmalloc(crc_size+4, GFP_KERNEL);
if (crcs == NULL)
printk(KERN_ERR "Cannot allocated CRC save area\n");
}

static u32 *s3c_pm_makecheck(struct resource *res, u32 *val)
{
unsigned long addr, left;

for (addr = res->start; addr < res->end;
addr += CHECK_CHUNKSIZE) {
left = res->end - addr;

if (left > CHECK_CHUNKSIZE)
left = CHECK_CHUNKSIZE;

*val = crc32_le(~0, phys_to_virt(addr), left);
val++;
}

return val;
}

/* s3c_pm_check_store
*
* compute the CRC values for the memory blocks before the final
* sleep.
*/

void s3c_pm_check_store(void)
{
if (crcs != NULL)
s3c_pm_run_sysram(s3c_pm_makecheck, crcs);
}

/* in_region
*
* return TRUE if the area defined by ptr..ptr+size contains the
* what..what+whatsz
*/

static inline int in_region(void *ptr, int size, void *what, size_t whatsz)
{
if ((what+whatsz) < ptr)
return 0;

if (what > (ptr+size))
return 0;

return 1;
}

/**
* s3c_pm_runcheck*() - helper to check a resource on restore.
* @res: The resource to check
* @vak: Pointer to list of CRC32 values to check.
*
* Called from the s3c_pm_check_restore() via s3c_pm_run_sysram(), this
* function runs the given memory resource checking it against the stored
* CRC to ensure that memory is restored. The function tries to skip as
* many of the areas used during the suspend process.
*/
static u32 *s3c_pm_runcheck(struct resource *res, u32 *val)
{
void *save_at = phys_to_virt(s3c_sleep_save_phys);
unsigned long addr;
unsigned long left;
void *ptr;
u32 calc;

for (addr = res->start; addr < res->end;
addr += CHECK_CHUNKSIZE) {
left = res->end - addr;

if (left > CHECK_CHUNKSIZE)
left = CHECK_CHUNKSIZE;

ptr = phys_to_virt(addr);

if (in_region(ptr, left, crcs, crc_size)) {
S3C_PMDBG("skipping %08lx, has crc block in\n", addr);
goto skip_check;
}

if (in_region(ptr, left, save_at, 32*4 )) {
S3C_PMDBG("skipping %08lx, has save block in\n", addr);
goto skip_check;
}

/* calculate and check the checksum */

calc = crc32_le(~0, ptr, left);
if (calc != *val) {
printk(KERN_ERR "Restore CRC error at "
"%08lx (%08x vs %08x)\n", addr, calc, *val);

S3C_PMDBG("Restore CRC error at %08lx (%08x vs %08x)\n",
addr, calc, *val);
}

skip_check:
val++;
}

return val;
}

/**
* s3c_pm_check_restore() - memory check called on resume
*
* check the CRCs after the restore event and free the memory used
* to hold them
*/
void s3c_pm_check_restore(void)
{
if (crcs != NULL) {
s3c_pm_run_sysram(s3c_pm_runcheck, crcs);
kfree(crcs);
crcs = NULL;
}
}
Loading

0 comments on commit 549c7e3

Please sign in to comment.