18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// originally in linux/arch/arm/plat-s3c24xx/pm.c 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (c) 2004-2008 Simtec Electronics 68c2ecf20Sopenharmony_ci// http://armlinux.simtec.co.uk 78c2ecf20Sopenharmony_ci// Ben Dooks <ben@simtec.co.uk> 88c2ecf20Sopenharmony_ci// 98c2ecf20Sopenharmony_ci// S3C Power Mangament - suspend/resume memory corruption check. 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/suspend.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/crc32.h> 158c2ecf20Sopenharmony_ci#include <linux/ioport.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/soc/samsung/s3c-pm.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#if CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE < 1 218c2ecf20Sopenharmony_ci#error CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE must be a positive non-zero value 228c2ecf20Sopenharmony_ci#endif 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* suspend checking code... 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * this next area does a set of crc checks over all the installed 278c2ecf20Sopenharmony_ci * memory, so the system can verify if the resume was ok. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE defines the block-size for the CRC, 308c2ecf20Sopenharmony_ci * increasing it will mean that the area corrupted will be less easy to spot, 318c2ecf20Sopenharmony_ci * and reducing the size will cause the CRC save area to grow 328c2ecf20Sopenharmony_ci*/ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define CHECK_CHUNKSIZE (CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE * 1024) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic u32 crc_size; /* size needed for the crc block */ 378c2ecf20Sopenharmony_cistatic u32 *crcs; /* allocated over suspend/resume */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_citypedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* s3c_pm_run_res 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * go through the given resource list, and look for system ram 448c2ecf20Sopenharmony_ci*/ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic void s3c_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci while (ptr != NULL) { 498c2ecf20Sopenharmony_ci if (ptr->child != NULL) 508c2ecf20Sopenharmony_ci s3c_pm_run_res(ptr->child, fn, arg); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if ((ptr->flags & IORESOURCE_SYSTEM_RAM) 538c2ecf20Sopenharmony_ci == IORESOURCE_SYSTEM_RAM) { 548c2ecf20Sopenharmony_ci S3C_PMDBG("Found system RAM at %08lx..%08lx\n", 558c2ecf20Sopenharmony_ci (unsigned long)ptr->start, 568c2ecf20Sopenharmony_ci (unsigned long)ptr->end); 578c2ecf20Sopenharmony_ci arg = (fn)(ptr, arg); 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci ptr = ptr->sibling; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void s3c_pm_run_sysram(run_fn_t fn, u32 *arg) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci s3c_pm_run_res(&iomem_resource, fn, arg); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic u32 *s3c_pm_countram(struct resource *res, u32 *val) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci u32 size = (u32)resource_size(res); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci size += CHECK_CHUNKSIZE-1; 748c2ecf20Sopenharmony_ci size /= CHECK_CHUNKSIZE; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci S3C_PMDBG("Area %08lx..%08lx, %d blocks\n", 778c2ecf20Sopenharmony_ci (unsigned long)res->start, (unsigned long)res->end, size); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci *val += size * sizeof(u32); 808c2ecf20Sopenharmony_ci return val; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* s3c_pm_prepare_check 848c2ecf20Sopenharmony_ci * 858c2ecf20Sopenharmony_ci * prepare the necessary information for creating the CRCs. This 868c2ecf20Sopenharmony_ci * must be done before the final save, as it will require memory 878c2ecf20Sopenharmony_ci * allocating, and thus touching bits of the kernel we do not 888c2ecf20Sopenharmony_ci * know about. 898c2ecf20Sopenharmony_ci*/ 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_civoid s3c_pm_check_prepare(void) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci crc_size = 0; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci s3c_pm_run_sysram(s3c_pm_countram, &crc_size); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci S3C_PMDBG("s3c_pm_prepare_check: %u checks needed\n", crc_size); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci crcs = kmalloc(crc_size+4, GFP_KERNEL); 1008c2ecf20Sopenharmony_ci if (crcs == NULL) 1018c2ecf20Sopenharmony_ci printk(KERN_ERR "Cannot allocated CRC save area\n"); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic u32 *s3c_pm_makecheck(struct resource *res, u32 *val) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci unsigned long addr, left; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci for (addr = res->start; addr < res->end; 1098c2ecf20Sopenharmony_ci addr += CHECK_CHUNKSIZE) { 1108c2ecf20Sopenharmony_ci left = res->end - addr; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (left > CHECK_CHUNKSIZE) 1138c2ecf20Sopenharmony_ci left = CHECK_CHUNKSIZE; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci *val = crc32_le(~0, phys_to_virt(addr), left); 1168c2ecf20Sopenharmony_ci val++; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return val; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* s3c_pm_check_store 1238c2ecf20Sopenharmony_ci * 1248c2ecf20Sopenharmony_ci * compute the CRC values for the memory blocks before the final 1258c2ecf20Sopenharmony_ci * sleep. 1268c2ecf20Sopenharmony_ci*/ 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_civoid s3c_pm_check_store(void) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci if (crcs != NULL) 1318c2ecf20Sopenharmony_ci s3c_pm_run_sysram(s3c_pm_makecheck, crcs); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* in_region 1358c2ecf20Sopenharmony_ci * 1368c2ecf20Sopenharmony_ci * return TRUE if the area defined by ptr..ptr+size contains the 1378c2ecf20Sopenharmony_ci * what..what+whatsz 1388c2ecf20Sopenharmony_ci*/ 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic inline int in_region(void *ptr, int size, void *what, size_t whatsz) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci if ((what+whatsz) < ptr) 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (what > (ptr+size)) 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return 1; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/** 1528c2ecf20Sopenharmony_ci * s3c_pm_runcheck() - helper to check a resource on restore. 1538c2ecf20Sopenharmony_ci * @res: The resource to check 1548c2ecf20Sopenharmony_ci * @vak: Pointer to list of CRC32 values to check. 1558c2ecf20Sopenharmony_ci * 1568c2ecf20Sopenharmony_ci * Called from the s3c_pm_check_restore() via s3c_pm_run_sysram(), this 1578c2ecf20Sopenharmony_ci * function runs the given memory resource checking it against the stored 1588c2ecf20Sopenharmony_ci * CRC to ensure that memory is restored. The function tries to skip as 1598c2ecf20Sopenharmony_ci * many of the areas used during the suspend process. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cistatic u32 *s3c_pm_runcheck(struct resource *res, u32 *val) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci unsigned long addr; 1648c2ecf20Sopenharmony_ci unsigned long left; 1658c2ecf20Sopenharmony_ci void *stkpage; 1668c2ecf20Sopenharmony_ci void *ptr; 1678c2ecf20Sopenharmony_ci u32 calc; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci stkpage = (void *)((u32)&calc & ~PAGE_MASK); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci for (addr = res->start; addr < res->end; 1728c2ecf20Sopenharmony_ci addr += CHECK_CHUNKSIZE) { 1738c2ecf20Sopenharmony_ci left = res->end - addr; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (left > CHECK_CHUNKSIZE) 1768c2ecf20Sopenharmony_ci left = CHECK_CHUNKSIZE; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ptr = phys_to_virt(addr); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (in_region(ptr, left, stkpage, 4096)) { 1818c2ecf20Sopenharmony_ci S3C_PMDBG("skipping %08lx, has stack in\n", addr); 1828c2ecf20Sopenharmony_ci goto skip_check; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (in_region(ptr, left, crcs, crc_size)) { 1868c2ecf20Sopenharmony_ci S3C_PMDBG("skipping %08lx, has crc block in\n", addr); 1878c2ecf20Sopenharmony_ci goto skip_check; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* calculate and check the checksum */ 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci calc = crc32_le(~0, ptr, left); 1938c2ecf20Sopenharmony_ci if (calc != *val) { 1948c2ecf20Sopenharmony_ci printk(KERN_ERR "Restore CRC error at " 1958c2ecf20Sopenharmony_ci "%08lx (%08x vs %08x)\n", addr, calc, *val); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci S3C_PMDBG("Restore CRC error at %08lx (%08x vs %08x)\n", 1988c2ecf20Sopenharmony_ci addr, calc, *val); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci skip_check: 2028c2ecf20Sopenharmony_ci val++; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return val; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/** 2098c2ecf20Sopenharmony_ci * s3c_pm_check_restore() - memory check called on resume 2108c2ecf20Sopenharmony_ci * 2118c2ecf20Sopenharmony_ci * check the CRCs after the restore event and free the memory used 2128c2ecf20Sopenharmony_ci * to hold them 2138c2ecf20Sopenharmony_ci*/ 2148c2ecf20Sopenharmony_civoid s3c_pm_check_restore(void) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci if (crcs != NULL) 2178c2ecf20Sopenharmony_ci s3c_pm_run_sysram(s3c_pm_runcheck, crcs); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/** 2218c2ecf20Sopenharmony_ci * s3c_pm_check_cleanup() - free memory resources 2228c2ecf20Sopenharmony_ci * 2238c2ecf20Sopenharmony_ci * Free the resources that where allocated by the suspend 2248c2ecf20Sopenharmony_ci * memory check code. We do this separately from the 2258c2ecf20Sopenharmony_ci * s3c_pm_check_restore() function as we cannot call any 2268c2ecf20Sopenharmony_ci * functions that might sleep during that resume. 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_civoid s3c_pm_check_cleanup(void) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci kfree(crcs); 2318c2ecf20Sopenharmony_ci crcs = NULL; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 234