1 /* linux/arch/arm/plat-s3c/pm-check.c
2 * originally in linux/arch/arm/plat-s3c24xx/pm.c
4 * Copyright (c) 2004-2008 Simtec Electronics
5 * http://armlinux.simtec.co.uk
6 * Ben Dooks <ben@simtec.co.uk>
8 * S3C Power Mangament - suspend/resume memory corruptiuon check.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
15 #include <linux/kernel.h>
17 #include <linux/addr_overlap.h>
18 #include <linux/suspend.h>
19 #include <linux/init.h>
20 #include <linux/crc32.h>
21 #include <linux/highmem.h>
22 #include <linux/ioport.h>
23 #include <asm/memory.h>
24 #include <linux/module.h>
25 #include <linux/moduleparam.h>
26 #include <linux/slab.h>
30 #include <mach/bitfix-snow.h>
32 #if CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE < 1
33 #error CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE must be a positive non-zero value
36 /* suspend checking code...
38 * this next area does a set of crc checks over all the installed
39 * memory, so the system can verify if the resume was ok.
41 * CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE defines the block-size for the CRC,
42 * increasing it will mean that the area corrupted will be less easy to spot,
43 * and reducing the size will cause the CRC save area to grow
47 * Possible values for pm_check_checksum_type:
48 * - crc: Best (but slowest). Catches the most problems.
49 * - sum: Simple 32-bit sum.
50 * - xor: Simple 32-bit xor. Bad at catching problems but gives you more
51 * information about the problems you caught (you can tell if all
52 * errors were in the same byte, etc).
54 #define CHECKSUM_TYPE_CRC "crc"
55 #define CHECKSUM_TYPE_SUM "sum"
56 #define CHECKSUM_TYPE_XOR "xor"
58 static char *pm_check_checksum_type = CHECKSUM_TYPE_SUM;
59 static bool pm_check_enabled;
60 static bool pm_check_should_panic = 1;
61 static bool pm_check_print_skips;
62 static bool pm_check_print_timings;
63 static int pm_check_chunksize = CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE * 1024;
64 static int pm_check_interleave_bytes;
65 static bool pm_check_skip_unused = 1;
67 static u32 crc_size; /* size needed for the crc block */
68 static u32 *crcs; /* allocated over suspend/resume */
70 static phys_addr_t stack_base_phys;
71 static phys_addr_t crcs_phys;
73 static u32 (*checksum_func)(u32 val, unsigned char const *ptr, size_t len);
75 /* number of errors found this resume */
76 static __suspend_volatile_bss u32 crc_err_cnt;
79 typedef u32 *(run_fn_t)(const struct resource *ptr, u32 *arg);
81 module_param(pm_check_checksum_type, charp, S_IRUGO | S_IWUSR);
82 MODULE_PARM_DESC(pm_check_checksum_type, "Checksum type (crc, xor, or sum)");
83 module_param(pm_check_enabled, bool, S_IRUGO | S_IWUSR);
84 MODULE_PARM_DESC(pm_check_enabled,
85 "Enable memory validation on suspend/resume");
86 module_param(pm_check_should_panic, bool, S_IRUGO | S_IWUSR);
87 MODULE_PARM_DESC(pm_check_should_panic, "Panic on CRC errors");
88 module_param(pm_check_print_skips, bool, S_IRUGO | S_IWUSR);
89 MODULE_PARM_DESC(pm_check_print_skips, "Print skipped regions");
90 module_param(pm_check_print_timings, bool, S_IRUGO | S_IWUSR);
91 MODULE_PARM_DESC(pm_check_print_timings,
92 "Print time to compute checks (causes runtime warnings)");
93 module_param(pm_check_chunksize, int, S_IRUGO | S_IWUSR);
94 MODULE_PARM_DESC(pm_check_chunksize,
95 "Size of blocks for CRCs, shold be 1K multiples");
96 module_param(pm_check_skip_unused, bool, S_IRUGO | S_IWUSR);
97 MODULE_PARM_DESC(pm_check_skip_unused, "Skip checking unallocated pages");
100 static inline void s3c_pm_printskip(char *desc, unsigned long addr)
102 if (!pm_check_print_skips)
104 S3C_PMDBG("s3c_pm_check: skipping %08lx, has %s in\n", addr, desc);
107 static bool s3c_pm_should_skip_page(phys_addr_t addr)
109 if (pm_check_skip_unused) {
110 struct page *page = phys_to_page(addr);
111 if (page->_count.counter == 0) {
112 s3c_pm_printskip("unused", addr);
116 if (phys_addrs_overlap(addr, PAGE_SIZE, stack_base_phys, THREAD_SIZE)) {
117 s3c_pm_printskip("stack", addr);
120 if (phys_addrs_overlap(addr, PAGE_SIZE, crcs_phys, crc_size)) {
121 s3c_pm_printskip("crc block", addr);
124 if (pm_does_overlap_suspend_volatile(addr, PAGE_SIZE)) {
125 s3c_pm_printskip("volatile suspend data", addr);
128 if (bitfix_does_overlap_reserved(addr)) {
129 s3c_pm_printskip("bitfix", addr);
137 * s3c_pm_xor_mem() - XOR all the words in the memory range passed
138 * @val: Initial value to start the XOR from; must be 32-bit aligned.
139 * @ptr: Pointer to the start of the memory range
140 * @len: Length (in bytes) of the memory range; must be multiple of 64 bytes.
143 static u32 s3c_pm_xor_mem(u32 val, unsigned char const *ptr, size_t len)
145 /* using 64-bit quantities helps the compiler to optimize */
146 const u64 *wptr = (const u64 *)ptr;
147 u64 *end_ptr = (u64 *)(ptr + len);
150 while (wptr < end_ptr) {
151 prefetch(wptr + 128); /* 16 cachelines ahead */
161 BUG_ON(wptr != end_ptr);
162 return (u32)((result >> 32) ^ (result & 0xffffffff));
166 * s3c_pm_sum_mem() - Sum all the words in the memory range passed
168 * Doesn't quite give you a simple sum. Since we work 64-bits at a time
169 * the carry bit from the lower 32-bits get added to the upper 32-bits, but
170 * this is a close enough approximation.
172 * @val: Initial value to start the sum from; must be 32-bit aligned.
173 * @ptr: Pointer to the start of the memory range
174 * @len: Length (in bytes) of the memory range; must be multiple of 64 bytes.
176 static u32 s3c_pm_sum_mem(u32 val, unsigned char const *ptr, size_t len)
178 /* using 64-bit quantities helps the compiler to optimize */
179 const u64 *wptr = (const u64 *)ptr;
180 u64 *end_ptr = (u64 *)(ptr + len);
183 while (wptr < end_ptr) {
184 prefetch(wptr + 128); /* 16 cachelines ahead */
194 BUG_ON(wptr != end_ptr);
195 return (u32)((result >> 32) + (result & 0xffffffff));
199 * s3c_pm_process_mem() - Process one memory chunk for checksum
200 * @val: Starting point for checksum function
201 * @addr: Pointer to the physical memory range. This must be page-aligned.
202 * @len: Length (in bytes) of the memory range
203 * @for_save: If true we're processing memory for saving data.
205 * Loop through the chunk in PAGE_SIZE blocks, ensuring each
206 * page is mapped. Use either crc32 or xor checksum and return
209 static inline u32 s3c_pm_process_mem(u32 val, u32 addr, size_t len,
212 size_t processed = 0;
214 if (unlikely(addr & (PAGE_SIZE - 1))) {
215 printk(KERN_ERR "s3c_pm_check: Unaligned address: 0x%x\n",
219 for (processed = 0; processed < len; processed += PAGE_SIZE) {
220 int pfn = __phys_to_pfn(addr + processed);
221 unsigned long left = len - processed;
224 if (left > PAGE_SIZE)
226 if (s3c_pm_should_skip_page(addr + processed))
228 virt = kmap_atomic(pfn_to_page(pfn));
230 printk(KERN_ERR "s3c_pm_check: Could not map "
231 "page for pfn %d with addr 0x%x\n",
232 pfn, addr + len - processed);
235 val = checksum_func(val, virt, left);
239 bitfix_process_page(addr + processed);
246 * go through the given resource list, and look for system ram
249 static void s3c_pm_run_res(const struct resource *ptr, run_fn_t fn, u32 *arg)
251 while (ptr != NULL) {
252 if (ptr->child != NULL)
253 s3c_pm_run_res(ptr->child, fn, arg);
255 if ((ptr->flags & IORESOURCE_MEM) &&
256 strcmp(ptr->name, "System RAM") == 0) {
257 S3C_PMDBG("s3c_pm_check: Found system RAM at "
259 (unsigned long)ptr->start,
260 (unsigned long)ptr->end);
261 arg = (fn)(ptr, arg);
268 static void s3c_pm_run_sysram(run_fn_t fn, u32 *arg)
270 s3c_pm_run_res(&iomem_resource, fn, arg);
273 static u32 *s3c_pm_countram(const struct resource *res, u32 *val)
275 u32 size = (u32)resource_size(res);
277 size = DIV_ROUND_UP(size, pm_check_chunksize);
279 S3C_PMDBG("s3c_pm_check: Area %08lx..%08lx, %d blocks\n",
280 (unsigned long)res->start, (unsigned long)res->end, size);
282 *val += size * sizeof(u32);
286 /* s3c_pm_prepare_check
288 * prepare the necessary information for creating the CRCs. This
289 * must be done before the final save, as it will require memory
290 * allocating, and thus touching bits of the kernel we do not
293 void s3c_pm_check_prepare(void)
295 ktime_t start, stop, delta;
297 if (!pm_check_enabled)
300 if (strcasecmp(pm_check_checksum_type, CHECKSUM_TYPE_CRC) == 0) {
301 checksum_func = crc32_le;
302 } else if (strcasecmp(pm_check_checksum_type, CHECKSUM_TYPE_XOR) == 0) {
303 checksum_func = s3c_pm_xor_mem;
305 if (strcasecmp(pm_check_checksum_type, CHECKSUM_TYPE_SUM) != 0)
306 pr_warn("pm_check: Unknown checksum '%s'; using '%s'\n",
307 pm_check_checksum_type, CHECKSUM_TYPE_SUM);
308 checksum_func = s3c_pm_sum_mem;
312 /* Timing code generates warnings at this point in suspend */
313 if (pm_check_print_timings)
316 s3c_pm_run_sysram(s3c_pm_countram, &crc_size);
317 if (pm_check_print_timings) {
319 delta = ktime_sub(stop, start);
320 S3C_PMDBG("s3c_pm_check: Suspend prescan took %lld usecs\n",
321 (unsigned long long)ktime_to_ns(delta) >> 10);
324 S3C_PMDBG("s3c_pm_check: Chunk size: %d, %u bytes for checks needed\n",
325 pm_check_chunksize, crc_size);
327 crcs = kmalloc(crc_size+4, GFP_KERNEL);
329 printk(KERN_ERR "Cannot allocated CRC save area\n");
332 /* externs from sleep.S */
333 extern u32 *sleep_save_sp;
336 * Zero memory that the sleep code may have used but doesn't care about.
338 * The sleep code uses some temporary memory that it doesn't need anymore
339 * after resume. We zero it out here so we don't have to skip a whole chunk.
340 * This keeps CRCs consistent.
342 * Alternatively we could change sleep code to zero this memory, but we run
343 * into some issues if we ever get a failed suspend (the normal resume code
346 static void zero_temp_memory(void)
352 * Run checks but interleave which memory is checked.
354 * Interleaving memory that is checked can make bitfix much faster since we
355 * can process all memory with the same destination at the same time.
357 * If interleave_bytes is 0x00010000, chunk_size is 0x2000 and memory goes
358 * from 0x80000000 to 0x80034000, we'd see this type of ordering:
374 static u32 *s3c_pm_makecheck_interleave(const struct resource *res, u32 *val)
376 const unsigned long total = res->end - res->start + 1;
377 unsigned long lower_offset = 0;
378 unsigned long upper_offset = 0;
379 unsigned long offset = 0;
381 while ((offset < total) && (lower_offset < pm_check_interleave_bytes)) {
382 unsigned long addr = res->start + offset;
384 val[offset / pm_check_chunksize] =
385 s3c_pm_process_mem(~0, addr, pm_check_chunksize, true);
387 upper_offset += pm_check_interleave_bytes;
388 offset = upper_offset + lower_offset;
389 if (offset >= total) {
391 lower_offset += pm_check_chunksize;
392 offset = upper_offset + lower_offset;
395 return val + (total / pm_check_chunksize);
398 static u32 *s3c_pm_makecheck(const struct resource *res, u32 *val)
400 const unsigned long total = res->end - res->start + 1;
401 unsigned long addr, left;
403 /* Bitfix code might ask us to interleave memory processing */
404 if (pm_check_interleave_bytes) {
405 /* Can only interleave if sizes are multiples of each other */
406 if ((pm_check_interleave_bytes % pm_check_chunksize) == 0 &&
407 (total % pm_check_chunksize) == 0)
408 return s3c_pm_makecheck_interleave(res, val);
410 /* Interleaving was requested but sizes mismatched */
414 for (addr = res->start; addr <= res->end;
415 addr += pm_check_chunksize, val++) {
416 left = res->end - addr + 1;
417 if (left > pm_check_chunksize)
418 left = pm_check_chunksize;
420 *val = s3c_pm_process_mem(~0, addr, left, true);
425 /* s3c_pm_check_store
427 * compute the CRC values for the memory blocks before the final
431 void s3c_pm_check_store(void)
433 ktime_t start, stop, delta;
438 if (pm_check_print_timings)
442 stack_base_phys = virt_to_phys(current_thread_info());
443 crcs_phys = virt_to_phys(crcs);
445 s3c_pm_run_sysram(s3c_pm_makecheck, crcs);
446 if (pm_check_print_timings) {
448 delta = ktime_sub(stop, start);
449 S3C_PMDBG("s3c_pm_check: Suspend memory scan took %lld usecs\n",
450 (unsigned long long)ktime_to_ns(delta) >> 10);
455 * s3c_pm_runcheck() - helper to check a resource on restore.
456 * @res: The resource to check
457 * @vak: Pointer to list of CRC32 values to check.
459 * Called from the s3c_pm_check_restore() via s3c_pm_run_sysram(), this
460 * function runs the given memory resource checking it against the stored
461 * CRC to ensure that memory is restored. The function tries to skip as
462 * many of the areas used during the suspend process.
464 static u32 *s3c_pm_runcheck(const struct resource *res, u32 *val)
470 for (addr = res->start; addr <= res->end;
471 addr += pm_check_chunksize, val++) {
472 left = res->end - addr + 1;
474 if (left > pm_check_chunksize)
475 left = pm_check_chunksize;
476 /* calculate and check the checksum */
478 calc = s3c_pm_process_mem(~0, addr, left, false);
480 S3C_PMDBG("s3c_pm_check: Restore CRC error at %08lx "
484 bitfix_recover_chunk(addr, s3c_pm_should_skip_page);
485 calc = s3c_pm_process_mem(~0, addr, left, false);
488 S3C_PMDBG("s3c_pm_check: ERROR: "
489 "recovery failed!\n");
491 S3C_PMDBG("s3c_pm_check: Recovered\n");
500 * s3c_pm_check_restore() - memory check called on resume
502 * check the CRCs after the restore event and free the memory used
505 void s3c_pm_check_restore(void)
507 ktime_t start, stop, delta;
513 if (pm_check_print_timings)
516 s3c_pm_run_sysram(s3c_pm_runcheck, crcs);
517 if (pm_check_print_timings) {
519 delta = ktime_sub(stop, start);
520 S3C_PMDBG("s3c_pm_check: Resume memory scan took %lld usecs\n",
521 (unsigned long long)ktime_to_ns(delta) >> 10);
524 S3C_PMDBG("s3c_pm_check: %d CRC errors\n", crc_err_cnt);
525 if (pm_check_should_panic)
526 panic("%d CRC errors\n", crc_err_cnt);
531 * s3c_pm_check_cleanup() - free memory resources
533 * Free the resources that where allocated by the suspend
534 * memory check code. We do this separately from the
535 * s3c_pm_check_restore() function as we cannot call any
536 * functions that might sleep during that resume.
538 void s3c_pm_check_cleanup(void)
546 * s3c_pm_check_enable() - enable suspend/resume memory checks
547 * @enabled: True to enable, false to disable
550 void s3c_pm_check_set_enable(bool enabled)
552 pm_check_enabled = enabled;
556 * s3c_pm_check_set_chunksize() - set chunksize
557 * @chunksize: The value to set the chunksize to.
559 void s3c_pm_check_set_chunksize(int chunksize)
561 pm_check_chunksize = chunksize;
565 * s3c_pm_check_set_interleave_bytes() - set interleaving for suspend
567 * This changes the order that chunks are processed, but only during suspend.
568 * The idea here is to optimize bitfix code.
570 * @interleave_bytes: The value to set the interleave_bytes to.
572 void s3c_pm_check_set_interleave_bytes(int interleave_bytes)
574 pm_check_interleave_bytes = interleave_bytes;