LCOV - code coverage report
Current view: top level - arch/x86/kernel - check.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 41 76 53.9 %
Date: 2022-04-01 14:35:51 Functions: 4 7 57.1 %
Branches: 19 48 39.6 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0
       2                 :            : 
       3                 :            : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
       4                 :            : 
       5                 :            : #include <linux/init.h>
       6                 :            : #include <linux/sched.h>
       7                 :            : #include <linux/kthread.h>
       8                 :            : #include <linux/workqueue.h>
       9                 :            : #include <linux/memblock.h>
      10                 :            : 
      11                 :            : #include <asm/proto.h>
      12                 :            : #include <asm/setup.h>
      13                 :            : 
      14                 :            : /*
      15                 :            :  * Some BIOSes seem to corrupt the low 64k of memory during events
      16                 :            :  * like suspend/resume and unplugging an HDMI cable.  Reserve all
      17                 :            :  * remaining free memory in that area and fill it with a distinct
      18                 :            :  * pattern.
      19                 :            :  */
      20                 :            : #define MAX_SCAN_AREAS  8
      21                 :            : 
      22                 :            : static int __read_mostly memory_corruption_check = -1;
      23                 :            : 
      24                 :            : static unsigned __read_mostly corruption_check_size = 64*1024;
      25                 :            : static unsigned __read_mostly corruption_check_period = 60; /* seconds */
      26                 :            : 
      27                 :            : static struct scan_area {
      28                 :            :         u64 addr;
      29                 :            :         u64 size;
      30                 :            : } scan_areas[MAX_SCAN_AREAS];
      31                 :            : static int num_scan_areas;
      32                 :            : 
      33                 :          0 : static __init int set_corruption_check(char *arg)
      34                 :            : {
      35                 :          0 :         ssize_t ret;
      36                 :          0 :         unsigned long val;
      37                 :            : 
      38         [ #  # ]:          0 :         if (!arg) {
      39                 :          0 :                 pr_err("memory_corruption_check config string not provided\n");
      40                 :          0 :                 return -EINVAL;
      41                 :            :         }
      42                 :            : 
      43                 :          0 :         ret = kstrtoul(arg, 10, &val);
      44         [ #  # ]:          0 :         if (ret)
      45                 :            :                 return ret;
      46                 :            : 
      47                 :          0 :         memory_corruption_check = val;
      48                 :            : 
      49                 :          0 :         return 0;
      50                 :            : }
      51                 :            : early_param("memory_corruption_check", set_corruption_check);
      52                 :            : 
      53                 :          0 : static __init int set_corruption_check_period(char *arg)
      54                 :            : {
      55                 :          0 :         ssize_t ret;
      56                 :          0 :         unsigned long val;
      57                 :            : 
      58         [ #  # ]:          0 :         if (!arg) {
      59                 :          0 :                 pr_err("memory_corruption_check_period config string not provided\n");
      60                 :          0 :                 return -EINVAL;
      61                 :            :         }
      62                 :            : 
      63                 :          0 :         ret = kstrtoul(arg, 10, &val);
      64         [ #  # ]:          0 :         if (ret)
      65                 :            :                 return ret;
      66                 :            : 
      67                 :          0 :         corruption_check_period = val;
      68                 :          0 :         return 0;
      69                 :            : }
      70                 :            : early_param("memory_corruption_check_period", set_corruption_check_period);
      71                 :            : 
      72                 :          0 : static __init int set_corruption_check_size(char *arg)
      73                 :            : {
      74                 :          0 :         char *end;
      75                 :          0 :         unsigned size;
      76                 :            : 
      77         [ #  # ]:          0 :         if (!arg) {
      78                 :          0 :                 pr_err("memory_corruption_check_size config string not provided\n");
      79                 :          0 :                 return -EINVAL;
      80                 :            :         }
      81                 :            : 
      82                 :          0 :         size = memparse(arg, &end);
      83                 :            : 
      84         [ #  # ]:          0 :         if (*end == '\0')
      85                 :          0 :                 corruption_check_size = size;
      86                 :            : 
      87         [ #  # ]:          0 :         return (size == corruption_check_size) ? 0 : -EINVAL;
      88                 :            : }
      89                 :            : early_param("memory_corruption_check_size", set_corruption_check_size);
      90                 :            : 
      91                 :            : 
      92                 :         21 : void __init setup_bios_corruption_check(void)
      93                 :            : {
      94                 :         21 :         phys_addr_t start, end;
      95                 :         21 :         u64 i;
      96                 :            : 
      97         [ +  - ]:         21 :         if (memory_corruption_check == -1) {
      98                 :         21 :                 memory_corruption_check =
      99                 :            : #ifdef CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK
     100                 :            :                         1
     101                 :            : #else
     102                 :            :                         0
     103                 :            : #endif
     104                 :            :                         ;
     105                 :            :         }
     106                 :            : 
     107         [ -  + ]:         21 :         if (corruption_check_size == 0)
     108                 :          0 :                 memory_corruption_check = 0;
     109                 :            : 
     110         [ -  + ]:         21 :         if (!memory_corruption_check)
     111                 :          0 :                 return;
     112                 :            : 
     113                 :         21 :         corruption_check_size = round_up(corruption_check_size, PAGE_SIZE);
     114                 :            : 
     115         [ +  + ]:         84 :         for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, &start, &end,
     116                 :            :                                 NULL) {
     117                 :         63 :                 start = clamp_t(phys_addr_t, round_up(start, PAGE_SIZE),
     118                 :            :                                 PAGE_SIZE, corruption_check_size);
     119                 :         63 :                 end = clamp_t(phys_addr_t, round_down(end, PAGE_SIZE),
     120                 :            :                               PAGE_SIZE, corruption_check_size);
     121         [ +  + ]:         63 :                 if (start >= end)
     122                 :         42 :                         continue;
     123                 :            : 
     124                 :         21 :                 memblock_reserve(start, end - start);
     125                 :         21 :                 scan_areas[num_scan_areas].addr = start;
     126                 :         21 :                 scan_areas[num_scan_areas].size = end - start;
     127                 :            : 
     128                 :            :                 /* Assume we've already mapped this early memory */
     129                 :         21 :                 memset(__va(start), 0, end - start);
     130                 :            : 
     131         [ +  - ]:         21 :                 if (++num_scan_areas >= MAX_SCAN_AREAS)
     132                 :            :                         break;
     133                 :            :         }
     134                 :            : 
     135         [ +  - ]:         21 :         if (num_scan_areas)
     136                 :         21 :                 pr_info("Scanning %d areas for low memory corruption\n", num_scan_areas);
     137                 :            : }
     138                 :            : 
     139                 :            : 
     140                 :         21 : static void check_for_bios_corruption(void)
     141                 :            : {
     142                 :         21 :         int i;
     143                 :         21 :         int corruption = 0;
     144                 :            : 
     145         [ +  - ]:         21 :         if (!memory_corruption_check)
     146                 :            :                 return;
     147                 :            : 
     148         [ +  + ]:         42 :         for (i = 0; i < num_scan_areas; i++) {
     149                 :         21 :                 unsigned long *addr = __va(scan_areas[i].addr);
     150                 :         21 :                 unsigned long size = scan_areas[i].size;
     151                 :            : 
     152         [ +  + ]:     161301 :                 for (; size; addr++, size -= sizeof(unsigned long)) {
     153         [ +  - ]:     161280 :                         if (!*addr)
     154                 :     161280 :                                 continue;
     155         [ #  # ]:          0 :                         pr_err("Corrupted low memory at %p (%lx phys) = %08lx\n", addr, __pa(addr), *addr);
     156                 :          0 :                         corruption = 1;
     157                 :          0 :                         *addr = 0;
     158                 :            :                 }
     159                 :            :         }
     160                 :            : 
     161   [ -  +  -  - ]:         21 :         WARN_ONCE(corruption, KERN_ERR "Memory corruption detected in low memory\n");
     162                 :            : }
     163                 :            : 
     164                 :            : static void check_corruption(struct work_struct *dummy);
     165                 :            : static DECLARE_DELAYED_WORK(bios_check_work, check_corruption);
     166                 :            : 
     167                 :         21 : static void check_corruption(struct work_struct *dummy)
     168                 :            : {
     169                 :         21 :         check_for_bios_corruption();
     170                 :         21 :         schedule_delayed_work(&bios_check_work,
     171                 :         21 :                 round_jiffies_relative(corruption_check_period*HZ));
     172                 :         21 : }
     173                 :            : 
     174                 :         21 : static int start_periodic_check_for_corruption(void)
     175                 :            : {
     176   [ +  -  +  -  :         21 :         if (!num_scan_areas || !memory_corruption_check || corruption_check_period == 0)
                   +  - ]
     177                 :            :                 return 0;
     178                 :            : 
     179                 :         21 :         pr_info("Scanning for low memory corruption every %d seconds\n", corruption_check_period);
     180                 :            : 
     181                 :            :         /* First time we run the checks right away */
     182                 :         21 :         schedule_delayed_work(&bios_check_work, 0);
     183                 :            : 
     184                 :         21 :         return 0;
     185                 :            : }
     186                 :            : device_initcall(start_periodic_check_for_corruption);
     187                 :            : 

Generated by: LCOV version 1.14