Blame view

arch/arm/plat-samsung/pm-check.c 5.48 KB
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
1
2
3
  /* linux/arch/arm/plat-s3c/pm-check.c
   *  originally in linux/arch/arm/plat-s3c24xx/pm.c
   *
ccae941ee   Ben Dooks   ARM: S3C: Update ...
4
   * Copyright (c) 2004-2008 Simtec Electronics
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   *	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>
5a0e3ad6a   Tejun Heo   include cleanup: ...
20
  #include <linux/slab.h>
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
21
22
  
  #include <plat/pm.h>
8005745d6   Ben Dooks   ARM: SAMSUNG: Mov...
23
24
  #if CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE < 1
  #error CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE must be a positive non-zero value
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
25
26
27
28
29
30
31
  #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.
   *
8005745d6   Ben Dooks   ARM: SAMSUNG: Mov...
32
   * CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE defines the block-size for the CRC,
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
33
34
35
   * 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
  */
8005745d6   Ben Dooks   ARM: SAMSUNG: Mov...
36
  #define CHECK_CHUNKSIZE (CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE * 1024)
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  
  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
  ",
840eeeb88   Ben Dooks   [ARM] S3C: Fix wa...
58
59
  				  (unsigned long)ptr->start,
  				  (unsigned long)ptr->end);
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  			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)
  {
28f65c11f   Joe Perches   treewide: Convert...
74
  	u32 size = (u32)resource_size(res);
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
75
76
77
  
  	size += CHECK_CHUNKSIZE-1;
  	size /= CHECK_CHUNKSIZE;
840eeeb88   Ben Dooks   [ARM] S3C: Fix wa...
78
79
80
  	S3C_PMDBG("Area %08lx..%08lx, %d blocks
  ",
  		  (unsigned long)res->start, (unsigned long)res->end, size);
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
  
  	*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
  ", crc_size);
  
  	crcs = kmalloc(crc_size+4, GFP_KERNEL);
  	if (crcs == NULL)
  		printk(KERN_ERR "Cannot allocated CRC save area
  ");
  }
  
  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;
  }
  
  /**
663a83048   Ben Dooks   [ARM] S3C: Avoid ...
157
   * s3c_pm_runcheck() - helper to check a resource on restore.
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
158
159
160
161
162
163
164
165
166
167
   * @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)
  {
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
168
169
  	unsigned long addr;
  	unsigned long left;
663a83048   Ben Dooks   [ARM] S3C: Avoid ...
170
  	void *stkpage;
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
171
172
  	void *ptr;
  	u32 calc;
663a83048   Ben Dooks   [ARM] S3C: Avoid ...
173
  	stkpage = (void *)((u32)&calc & ~PAGE_MASK);
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
174
175
176
177
178
179
180
181
  	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);
663a83048   Ben Dooks   [ARM] S3C: Avoid ...
182
183
184
185
186
  		if (in_region(ptr, left, stkpage, 4096)) {
  			S3C_PMDBG("skipping %08lx, has stack in
  ", addr);
  			goto skip_check;
  		}
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
187
188
189
190
191
  		if (in_region(ptr, left, crcs, crc_size)) {
  			S3C_PMDBG("skipping %08lx, has crc block in
  ", addr);
  			goto skip_check;
  		}
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
  		/* 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)
  ", addr, calc, *val);
  
  			S3C_PMDBG("Restore CRC error at %08lx (%08x vs %08x)
  ",
  			    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)
  {
aa8aba694   Ben Dooks   [ARM] S3C: Do not...
220
  	if (crcs != NULL)
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
221
  		s3c_pm_run_sysram(s3c_pm_runcheck, crcs);
549c7e33a   Ben Dooks   [ARM] S3C: Split ...
222
  }
aa8aba694   Ben Dooks   [ARM] S3C: Do not...
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  
  /**
   * s3c_pm_check_cleanup() - free memory resources
   *
   * Free the resources that where allocated by the suspend
   * memory check code. We do this separately from the
   * s3c_pm_check_restore() function as we cannot call any
   * functions that might sleep during that resume.
   */
  void s3c_pm_check_cleanup(void)
  {
  	kfree(crcs);
  	crcs = NULL;
  }