290ede2724ff4d8d31a665ec5ad99b616663be3d
[cascardo/linux.git] / arch / arm / plat-samsung / pm-check.c
1 /* linux/arch/arm/plat-s3c/pm-check.c
2  *  originally in linux/arch/arm/plat-s3c24xx/pm.c
3  *
4  * Copyright (c) 2004-2008 Simtec Electronics
5  *      http://armlinux.simtec.co.uk
6  *      Ben Dooks <ben@simtec.co.uk>
7  *
8  * S3C Power Mangament - suspend/resume memory corruptiuon check.
9  *
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.
13 */
14
15 #include <linux/kernel.h>
16
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>
27
28 #include <plat/pm.h>
29
30 #include <mach/bitfix-snow.h>
31
32 #if CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE < 1
33 #error CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE must be a positive non-zero value
34 #endif
35
36 /* suspend checking code...
37  *
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.
40  *
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
44 */
45
46 /*
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).
53  */
54 #define CHECKSUM_TYPE_CRC       "crc"
55 #define CHECKSUM_TYPE_SUM       "sum"
56 #define CHECKSUM_TYPE_XOR       "xor"
57
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;
66
67 static u32 crc_size;    /* size needed for the crc block */
68 static u32 *crcs;       /* allocated over suspend/resume */
69
70 static phys_addr_t stack_base_phys;
71 static phys_addr_t crcs_phys;
72
73 static u32 (*checksum_func)(u32 val, unsigned char const *ptr, size_t len);
74
75 /* number of errors found this resume */
76 static __suspend_volatile_bss u32 crc_err_cnt;
77
78
79 typedef u32 *(run_fn_t)(const struct resource *ptr, u32 *arg);
80
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");
98
99
100 static inline void s3c_pm_printskip(char *desc, unsigned long addr)
101 {
102         if (!pm_check_print_skips)
103                 return;
104         S3C_PMDBG("s3c_pm_check: skipping %08lx, has %s in\n", addr, desc);
105 }
106
107 static bool s3c_pm_should_skip_page(phys_addr_t addr)
108 {
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);
113                         return true;
114                 }
115         }
116         if (phys_addrs_overlap(addr, PAGE_SIZE, stack_base_phys, THREAD_SIZE)) {
117                 s3c_pm_printskip("stack", addr);
118                 return true;
119         }
120         if (phys_addrs_overlap(addr, PAGE_SIZE, crcs_phys, crc_size)) {
121                 s3c_pm_printskip("crc block", addr);
122                 return true;
123         }
124         if (pm_does_overlap_suspend_volatile(addr, PAGE_SIZE)) {
125                 s3c_pm_printskip("volatile suspend data", addr);
126                 return true;
127         }
128         if (bitfix_does_overlap_reserved(addr)) {
129                 s3c_pm_printskip("bitfix", addr);
130                 return true;
131         }
132
133         return false;
134 }
135
136 /**
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.
141  */
142
143 static u32 s3c_pm_xor_mem(u32 val, unsigned char const *ptr, size_t len)
144 {
145         /* using 64-bit quantities helps the compiler to optimize */
146         const u64 *wptr = (const u64 *)ptr;
147         u64 *end_ptr = (u64 *)(ptr + len);
148         u64 result = val;
149
150         while (wptr < end_ptr) {
151                 prefetch(wptr + 128); /* 16 cachelines ahead */
152                 result ^= *wptr++;
153                 result ^= *wptr++;
154                 result ^= *wptr++;
155                 result ^= *wptr++;
156                 result ^= *wptr++;
157                 result ^= *wptr++;
158                 result ^= *wptr++;
159                 result ^= *wptr++;
160         }
161         BUG_ON(wptr != end_ptr);
162         return (u32)((result >> 32) ^ (result & 0xffffffff));
163 }
164
165 /**
166  * s3c_pm_sum_mem() - Sum all the words in the memory range passed
167  *
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.
171  *
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.
175  */
176 static u32 s3c_pm_sum_mem(u32 val, unsigned char const *ptr, size_t len)
177 {
178         /* using 64-bit quantities helps the compiler to optimize */
179         const u64 *wptr = (const u64 *)ptr;
180         u64 *end_ptr = (u64 *)(ptr + len);
181         u64 result = val;
182
183         while (wptr < end_ptr) {
184                 prefetch(wptr + 128); /* 16 cachelines ahead */
185                 result += *wptr++;
186                 result += *wptr++;
187                 result += *wptr++;
188                 result += *wptr++;
189                 result += *wptr++;
190                 result += *wptr++;
191                 result += *wptr++;
192                 result += *wptr++;
193         }
194         BUG_ON(wptr != end_ptr);
195         return (u32)((result >> 32) + (result & 0xffffffff));
196 }
197
198 /**
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.
204  *
205  * Loop through the chunk in PAGE_SIZE blocks, ensuring each
206  * page is mapped.  Use either crc32 or xor checksum and return
207  * back.
208  */
209 static inline u32 s3c_pm_process_mem(u32 val, u32 addr, size_t len,
210                                      bool for_save)
211 {
212         size_t processed = 0;
213
214         if (unlikely(addr & (PAGE_SIZE - 1))) {
215                 printk(KERN_ERR "s3c_pm_check: Unaligned address: 0x%x\n",
216                         addr);
217                 return val;
218         }
219         for (processed = 0; processed < len; processed += PAGE_SIZE) {
220                 int pfn = __phys_to_pfn(addr + processed);
221                 unsigned long left = len - processed;
222                 void *virt;
223
224                 if (left > PAGE_SIZE)
225                         left = PAGE_SIZE;
226                 if (s3c_pm_should_skip_page(addr + processed))
227                         continue;
228                 virt = kmap_atomic(pfn_to_page(pfn));
229                 if (!virt) {
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);
233                         return val;
234                 }
235                 val = checksum_func(val, virt, left);
236                 kunmap_atomic(virt);
237
238                 if (for_save)
239                         bitfix_process_page(addr + processed);
240         }
241         return val;
242 }
243
244 /* s3c_pm_run_res
245  *
246  * go through the given resource list, and look for system ram
247 */
248
249 static void s3c_pm_run_res(const struct resource *ptr, run_fn_t fn, u32 *arg)
250 {
251         while (ptr != NULL) {
252                 if (ptr->child != NULL)
253                         s3c_pm_run_res(ptr->child, fn, arg);
254
255                 if ((ptr->flags & IORESOURCE_MEM) &&
256                     strcmp(ptr->name, "System RAM") == 0) {
257                         S3C_PMDBG("s3c_pm_check: Found system RAM at "
258                                   "%08lx..%08lx\n",
259                                   (unsigned long)ptr->start,
260                                   (unsigned long)ptr->end);
261                         arg = (fn)(ptr, arg);
262                 }
263
264                 ptr = ptr->sibling;
265         }
266 }
267
268 static void s3c_pm_run_sysram(run_fn_t fn, u32 *arg)
269 {
270         s3c_pm_run_res(&iomem_resource, fn, arg);
271 }
272
273 static u32 *s3c_pm_countram(const struct resource *res, u32 *val)
274 {
275         u32 size = (u32)resource_size(res);
276
277         size = DIV_ROUND_UP(size, pm_check_chunksize);
278
279         S3C_PMDBG("s3c_pm_check: Area %08lx..%08lx, %d blocks\n",
280                   (unsigned long)res->start, (unsigned long)res->end, size);
281
282         *val += size * sizeof(u32);
283         return val;
284 }
285
286 /* s3c_pm_prepare_check
287  *
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
291  * know about.
292 */
293 void s3c_pm_check_prepare(void)
294 {
295         ktime_t start, stop, delta;
296
297         if (!pm_check_enabled)
298                 return;
299
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;
304         } else {
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;
309         }
310
311         crc_size = 0;
312         /* Timing code generates warnings at this point in suspend */
313         if (pm_check_print_timings)
314                 start = ktime_get();
315         bitfix_prepare();
316         s3c_pm_run_sysram(s3c_pm_countram, &crc_size);
317         if (pm_check_print_timings) {
318                 stop = ktime_get();
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);
322         }
323
324         S3C_PMDBG("s3c_pm_check: Chunk size: %d, %u bytes for checks needed\n",
325                 pm_check_chunksize, crc_size);
326
327         crcs = kmalloc(crc_size+4, GFP_KERNEL);
328         if (crcs == NULL)
329                 printk(KERN_ERR "Cannot allocated CRC save area\n");
330 }
331
332 /* externs from sleep.S */
333 extern u32 *sleep_save_sp;
334
335 /**
336  * Zero memory that the sleep code may have used but doesn't care about.
337  *
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.
341  *
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
344  * doesn't run).
345  */
346 static void zero_temp_memory(void)
347 {
348         sleep_save_sp = 0;
349 }
350
351 /**
352  * Run checks but interleave which memory is checked.
353  *
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.
356  *
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:
359  * - 0x80000000
360  * - 0x80010000
361  * - 0x80020000
362  * - 0x80030000
363  * - 0x80002000
364  * - 0x80012000
365  * - 0x80022000
366  * - 0x80032000
367  * - 0x80004000
368  * - 0x80014000
369  * - 0x80024000
370  * - ....
371  * - 0x8001e000
372  * - 0x8002e000
373  */
374 static u32 *s3c_pm_makecheck_interleave(const struct resource *res, u32 *val)
375 {
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;
380
381         while ((offset < total) && (lower_offset < pm_check_interleave_bytes)) {
382                 unsigned long addr = res->start + offset;
383
384                 val[offset / pm_check_chunksize] =
385                         s3c_pm_process_mem(~0, addr, pm_check_chunksize, true);
386
387                 upper_offset += pm_check_interleave_bytes;
388                 offset = upper_offset + lower_offset;
389                 if (offset >= total) {
390                         upper_offset = 0;
391                         lower_offset += pm_check_chunksize;
392                         offset = upper_offset + lower_offset;
393                 }
394         }
395         return val + (total / pm_check_chunksize);
396 }
397
398 static u32 *s3c_pm_makecheck(const struct resource *res, u32 *val)
399 {
400         const unsigned long total = res->end - res->start + 1;
401         unsigned long addr, left;
402
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);
409
410                 /* Interleaving was requested but sizes mismatched */
411                 WARN_ON(1);
412         }
413
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;
419
420                 *val = s3c_pm_process_mem(~0, addr, left, true);
421         }
422         return val;
423 }
424
425 /* s3c_pm_check_store
426  *
427  * compute the CRC values for the memory blocks before the final
428  * sleep.
429 */
430
431 void s3c_pm_check_store(void)
432 {
433         ktime_t start, stop, delta;
434
435         if (crcs == NULL)
436                 return;
437
438         if (pm_check_print_timings)
439                 start = ktime_get();
440
441         zero_temp_memory();
442         stack_base_phys = virt_to_phys(current_thread_info());
443         crcs_phys = virt_to_phys(crcs);
444
445         s3c_pm_run_sysram(s3c_pm_makecheck, crcs);
446         if (pm_check_print_timings) {
447                 stop = ktime_get();
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);
451         }
452 }
453
454 /**
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.
458  *
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.
463  */
464 static u32 *s3c_pm_runcheck(const struct resource *res, u32 *val)
465 {
466         unsigned long addr;
467         unsigned long left;
468         u32 calc;
469
470         for (addr = res->start; addr <= res->end;
471              addr += pm_check_chunksize, val++) {
472                 left = res->end - addr + 1;
473
474                 if (left > pm_check_chunksize)
475                         left = pm_check_chunksize;
476                 /* calculate and check the checksum */
477
478                 calc = s3c_pm_process_mem(~0, addr, left, false);
479                 if (calc != *val) {
480                         S3C_PMDBG("s3c_pm_check: Restore CRC error at %08lx "
481                                 "(%08x vs %08x)\n",
482                                 addr, calc, *val);
483
484                         bitfix_recover_chunk(addr, s3c_pm_should_skip_page);
485                         calc = s3c_pm_process_mem(~0, addr, left, false);
486                         if (calc != *val) {
487                                 crc_err_cnt++;
488                                 S3C_PMDBG("s3c_pm_check: ERROR: "
489                                           "recovery failed!\n");
490                         } else {
491                                 S3C_PMDBG("s3c_pm_check: Recovered\n");
492                         }
493                 }
494         }
495
496         return val;
497 }
498
499 /**
500  * s3c_pm_check_restore() - memory check called on resume
501  *
502  * check the CRCs after the restore event and free the memory used
503  * to hold them
504 */
505 void s3c_pm_check_restore(void)
506 {
507         ktime_t start, stop, delta;
508
509         if (crcs == NULL)
510                 return;
511
512         crc_err_cnt = 0;
513         if (pm_check_print_timings)
514                 start = ktime_get();
515         zero_temp_memory();
516         s3c_pm_run_sysram(s3c_pm_runcheck, crcs);
517         if (pm_check_print_timings) {
518                 stop = ktime_get();
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);
522         }
523         if (crc_err_cnt) {
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);
527         }
528 }
529
530 /**
531  * s3c_pm_check_cleanup() - free memory resources
532  *
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.
537  */
538 void s3c_pm_check_cleanup(void)
539 {
540         kfree(crcs);
541         crcs = NULL;
542         bitfix_finish();
543 }
544
545 /**
546  * s3c_pm_check_enable() - enable suspend/resume memory checks
547  * @enabled: True to enable, false to disable
548  *
549  */
550 void s3c_pm_check_set_enable(bool enabled)
551 {
552         pm_check_enabled = enabled;
553 }
554
555 /**
556  * s3c_pm_check_set_chunksize() - set chunksize
557  * @chunksize: The value to set the chunksize to.
558  */
559 void s3c_pm_check_set_chunksize(int chunksize)
560 {
561         pm_check_chunksize = chunksize;
562 }
563
564 /**
565  * s3c_pm_check_set_interleave_bytes() - set interleaving for suspend
566  *
567  * This changes the order that chunks are processed, but only during suspend.
568  * The idea here is to optimize bitfix code.
569  *
570  * @interleave_bytes: The value to set the interleave_bytes to.
571  */
572 void s3c_pm_check_set_interleave_bytes(int interleave_bytes)
573 {
574         pm_check_interleave_bytes = interleave_bytes;
575 }