LCOV - code coverage report
Current view: top level - arch/arm/kernel - vdso.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_qemu_modules_combined.info Lines: 73 101 72.3 %
Date: 2020-09-30 20:25:01 Functions: 7 10 70.0 %
Branches: 23 46 50.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-only
       2                 :            : /*
       3                 :            :  * Adapted from arm64 version.
       4                 :            :  *
       5                 :            :  * Copyright (C) 2012 ARM Limited
       6                 :            :  * Copyright (C) 2015 Mentor Graphics Corporation.
       7                 :            :  */
       8                 :            : 
       9                 :            : #include <linux/cache.h>
      10                 :            : #include <linux/elf.h>
      11                 :            : #include <linux/err.h>
      12                 :            : #include <linux/kernel.h>
      13                 :            : #include <linux/mm.h>
      14                 :            : #include <linux/of.h>
      15                 :            : #include <linux/printk.h>
      16                 :            : #include <linux/slab.h>
      17                 :            : #include <linux/timekeeper_internal.h>
      18                 :            : #include <linux/vmalloc.h>
      19                 :            : #include <asm/arch_timer.h>
      20                 :            : #include <asm/barrier.h>
      21                 :            : #include <asm/cacheflush.h>
      22                 :            : #include <asm/page.h>
      23                 :            : #include <asm/vdso.h>
      24                 :            : #include <asm/vdso_datapage.h>
      25                 :            : #include <clocksource/arm_arch_timer.h>
      26                 :            : 
      27                 :            : #define MAX_SYMNAME     64
      28                 :            : 
      29                 :            : static struct page **vdso_text_pagelist;
      30                 :            : 
      31                 :            : extern char vdso_start[], vdso_end[];
      32                 :            : 
      33                 :            : /* Total number of pages needed for the data and text portions of the VDSO. */
      34                 :            : unsigned int vdso_total_pages __ro_after_init;
      35                 :            : 
      36                 :            : /*
      37                 :            :  * The VDSO data page.
      38                 :            :  */
      39                 :            : static union vdso_data_store vdso_data_store __page_aligned_data;
      40                 :            : static struct vdso_data *vdso_data = &vdso_data_store.data;
      41                 :            : 
      42                 :            : static struct page *vdso_data_page __ro_after_init;
      43                 :            : static const struct vm_special_mapping vdso_data_mapping = {
      44                 :            :         .name = "[vvar]",
      45                 :            :         .pages = &vdso_data_page,
      46                 :            : };
      47                 :            : 
      48                 :          0 : static int vdso_mremap(const struct vm_special_mapping *sm,
      49                 :            :                 struct vm_area_struct *new_vma)
      50                 :            : {
      51                 :          0 :         unsigned long new_size = new_vma->vm_end - new_vma->vm_start;
      52                 :            :         unsigned long vdso_size;
      53                 :            : 
      54                 :            :         /* without VVAR page */
      55                 :          0 :         vdso_size = (vdso_total_pages - 1) << PAGE_SHIFT;
      56                 :            : 
      57         [ #  # ]:          0 :         if (vdso_size != new_size)
      58                 :            :                 return -EINVAL;
      59                 :            : 
      60                 :          0 :         current->mm->context.vdso = new_vma->vm_start;
      61                 :            : 
      62                 :          0 :         return 0;
      63                 :            : }
      64                 :            : 
      65                 :            : static struct vm_special_mapping vdso_text_mapping __ro_after_init = {
      66                 :            :         .name = "[vdso]",
      67                 :            :         .mremap = vdso_mremap,
      68                 :            : };
      69                 :            : 
      70                 :            : struct elfinfo {
      71                 :            :         Elf32_Ehdr      *hdr;           /* ptr to ELF */
      72                 :            :         Elf32_Sym       *dynsym;        /* ptr to .dynsym section */
      73                 :            :         unsigned long   dynsymsize;     /* size of .dynsym section */
      74                 :            :         char            *dynstr;        /* ptr to .dynstr section */
      75                 :            : };
      76                 :            : 
      77                 :            : /* Cached result of boot-time check for whether the arch timer exists,
      78                 :            :  * and if so, whether the virtual counter is useable.
      79                 :            :  */
      80                 :            : static bool cntvct_ok __ro_after_init;
      81                 :            : 
      82                 :        404 : static bool __init cntvct_functional(void)
      83                 :            : {
      84                 :            :         struct device_node *np;
      85                 :            :         bool ret = false;
      86                 :            : 
      87                 :            :         if (!IS_ENABLED(CONFIG_ARM_ARCH_TIMER))
      88                 :            :                 goto out;
      89                 :            : 
      90                 :            :         /* The arm_arch_timer core should export
      91                 :            :          * arch_timer_use_virtual or similar so we don't have to do
      92                 :            :          * this.
      93                 :            :          */
      94                 :        404 :         np = of_find_compatible_node(NULL, NULL, "arm,armv7-timer");
      95         [ -  + ]:        404 :         if (!np)
      96                 :          0 :                 np = of_find_compatible_node(NULL, NULL, "arm,armv8-timer");
      97         [ +  - ]:        404 :         if (!np)
      98                 :            :                 goto out_put;
      99                 :            : 
     100         [ +  - ]:        404 :         if (of_property_read_bool(np, "arm,cpu-registers-not-fw-configured"))
     101                 :            :                 goto out_put;
     102                 :            : 
     103                 :            :         ret = true;
     104                 :            : 
     105                 :            : out_put:
     106                 :        404 :         of_node_put(np);
     107                 :            : out:
     108                 :        404 :         return ret;
     109                 :            : }
     110                 :            : 
     111                 :        808 : static void * __init find_section(Elf32_Ehdr *ehdr, const char *name,
     112                 :            :                                   unsigned long *size)
     113                 :            : {
     114                 :            :         Elf32_Shdr *sechdrs;
     115                 :            :         unsigned int i;
     116                 :            :         char *secnames;
     117                 :            : 
     118                 :            :         /* Grab section headers and strings so we can tell who is who */
     119                 :        808 :         sechdrs = (void *)ehdr + ehdr->e_shoff;
     120                 :        808 :         secnames = (void *)ehdr + sechdrs[ehdr->e_shstrndx].sh_offset;
     121                 :            : 
     122                 :            :         /* Find the section they want */
     123         [ +  - ]:       2020 :         for (i = 1; i < ehdr->e_shnum; i++) {
     124         [ +  + ]:       2020 :                 if (strcmp(secnames + sechdrs[i].sh_name, name) == 0) {
     125         [ +  + ]:        808 :                         if (size)
     126                 :        404 :                                 *size = sechdrs[i].sh_size;
     127                 :        808 :                         return (void *)ehdr + sechdrs[i].sh_offset;
     128                 :            :                 }
     129                 :            :         }
     130                 :            : 
     131         [ #  # ]:          0 :         if (size)
     132                 :          0 :                 *size = 0;
     133                 :            :         return NULL;
     134                 :            : }
     135                 :            : 
     136                 :          0 : static Elf32_Sym * __init find_symbol(struct elfinfo *lib, const char *symname)
     137                 :            : {
     138                 :            :         unsigned int i;
     139                 :            : 
     140         [ #  # ]:          0 :         for (i = 0; i < (lib->dynsymsize / sizeof(Elf32_Sym)); i++) {
     141                 :            :                 char name[MAX_SYMNAME], *c;
     142                 :            : 
     143         [ #  # ]:          0 :                 if (lib->dynsym[i].st_name == 0)
     144                 :          0 :                         continue;
     145                 :          0 :                 strlcpy(name, lib->dynstr + lib->dynsym[i].st_name,
     146                 :            :                         MAX_SYMNAME);
     147                 :          0 :                 c = strchr(name, '@');
     148         [ #  # ]:          0 :                 if (c)
     149                 :          0 :                         *c = 0;
     150         [ #  # ]:          0 :                 if (strcmp(symname, name) == 0)
     151                 :          0 :                         return &lib->dynsym[i];
     152                 :            :         }
     153                 :            :         return NULL;
     154                 :            : }
     155                 :            : 
     156                 :          0 : static void __init vdso_nullpatch_one(struct elfinfo *lib, const char *symname)
     157                 :            : {
     158                 :            :         Elf32_Sym *sym;
     159                 :            : 
     160                 :          0 :         sym = find_symbol(lib, symname);
     161         [ #  # ]:          0 :         if (!sym)
     162                 :          0 :                 return;
     163                 :            : 
     164                 :          0 :         sym->st_name = 0;
     165                 :            : }
     166                 :            : 
     167                 :        404 : static void __init patch_vdso(void *ehdr)
     168                 :            : {
     169                 :            :         struct elfinfo einfo;
     170                 :            : 
     171                 :        404 :         einfo = (struct elfinfo) {
     172                 :            :                 .hdr = ehdr,
     173                 :            :         };
     174                 :            : 
     175                 :        404 :         einfo.dynsym = find_section(einfo.hdr, ".dynsym", &einfo.dynsymsize);
     176                 :        404 :         einfo.dynstr = find_section(einfo.hdr, ".dynstr", NULL);
     177                 :            : 
     178                 :            :         /* If the virtual counter is absent or non-functional we don't
     179                 :            :          * want programs to incur the slight additional overhead of
     180                 :            :          * dispatching through the VDSO only to fall back to syscalls.
     181                 :            :          */
     182         [ -  + ]:        404 :         if (!cntvct_ok) {
     183                 :          0 :                 vdso_nullpatch_one(&einfo, "__vdso_gettimeofday");
     184                 :          0 :                 vdso_nullpatch_one(&einfo, "__vdso_clock_gettime");
     185                 :            :         }
     186                 :        404 : }
     187                 :            : 
     188                 :        404 : static int __init vdso_init(void)
     189                 :            : {
     190                 :            :         unsigned int text_pages;
     191                 :            :         int i;
     192                 :            : 
     193         [ -  + ]:        404 :         if (memcmp(vdso_start, "\177ELF", 4)) {
     194                 :          0 :                 pr_err("VDSO is not a valid ELF object!\n");
     195                 :          0 :                 return -ENOEXEC;
     196                 :            :         }
     197                 :            : 
     198                 :        404 :         text_pages = (vdso_end - vdso_start) >> PAGE_SHIFT;
     199                 :            : 
     200                 :            :         /* Allocate the VDSO text pagelist */
     201                 :        404 :         vdso_text_pagelist = kcalloc(text_pages, sizeof(struct page *),
     202                 :            :                                      GFP_KERNEL);
     203         [ +  - ]:        404 :         if (vdso_text_pagelist == NULL)
     204                 :            :                 return -ENOMEM;
     205                 :            : 
     206                 :            :         /* Grab the VDSO data page. */
     207                 :        404 :         vdso_data_page = virt_to_page(vdso_data);
     208                 :            : 
     209                 :            :         /* Grab the VDSO text pages. */
     210         [ +  + ]:        808 :         for (i = 0; i < text_pages; i++) {
     211                 :            :                 struct page *page;
     212                 :            : 
     213                 :        404 :                 page = virt_to_page(vdso_start + i * PAGE_SIZE);
     214                 :        404 :                 vdso_text_pagelist[i] = page;
     215                 :            :         }
     216                 :            : 
     217                 :        404 :         vdso_text_mapping.pages = vdso_text_pagelist;
     218                 :            : 
     219                 :        404 :         vdso_total_pages = 1; /* for the data/vvar page */
     220                 :        404 :         vdso_total_pages += text_pages;
     221                 :            : 
     222                 :        404 :         cntvct_ok = cntvct_functional();
     223                 :            : 
     224                 :        404 :         patch_vdso(vdso_start);
     225                 :            : 
     226                 :        404 :         return 0;
     227                 :            : }
     228                 :            : arch_initcall(vdso_init);
     229                 :            : 
     230                 :            : static int install_vvar(struct mm_struct *mm, unsigned long addr)
     231                 :            : {
     232                 :            :         struct vm_area_struct *vma;
     233                 :            : 
     234                 :    1522552 :         vma = _install_special_mapping(mm, addr, PAGE_SIZE,
     235                 :            :                                        VM_READ | VM_MAYREAD,
     236                 :            :                                        &vdso_data_mapping);
     237                 :            : 
     238                 :            :         return PTR_ERR_OR_ZERO(vma);
     239                 :            : }
     240                 :            : 
     241                 :            : /* assumes mmap_sem is write-locked */
     242                 :    1522550 : void arm_install_vdso(struct mm_struct *mm, unsigned long addr)
     243                 :            : {
     244                 :            :         struct vm_area_struct *vma;
     245                 :            :         unsigned long len;
     246                 :            : 
     247                 :    1522550 :         mm->context.vdso = 0;
     248                 :            : 
     249         [ +  - ]:    1522550 :         if (vdso_text_pagelist == NULL)
     250                 :            :                 return;
     251                 :            : 
     252         [ +  + ]:    1522558 :         if (install_vvar(mm, addr))
     253                 :            :                 return;
     254                 :            : 
     255                 :            :         /* Account for vvar page. */
     256                 :    1522556 :         addr += PAGE_SIZE;
     257                 :    1522556 :         len = (vdso_total_pages - 1) << PAGE_SHIFT;
     258                 :            : 
     259                 :    1522556 :         vma = _install_special_mapping(mm, addr, len,
     260                 :            :                 VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
     261                 :            :                 &vdso_text_mapping);
     262                 :            : 
     263         [ +  - ]:    1522556 :         if (!IS_ERR(vma))
     264                 :    1522558 :                 mm->context.vdso = addr;
     265                 :            : }
     266                 :            : 
     267                 :            : static void vdso_write_begin(struct vdso_data *vdata)
     268                 :            : {
     269                 :    8195822 :         ++vdso_data->seq_count;
     270                 :    8195822 :         smp_wmb(); /* Pairs with smp_rmb in vdso_read_retry */
     271                 :            : }
     272                 :            : 
     273                 :            : static void vdso_write_end(struct vdso_data *vdata)
     274                 :            : {
     275                 :    8195822 :         smp_wmb(); /* Pairs with smp_rmb in vdso_read_begin */
     276                 :    8195822 :         ++vdso_data->seq_count;
     277                 :            : }
     278                 :            : 
     279                 :            : static bool tk_is_cntvct(const struct timekeeper *tk)
     280                 :            : {
     281                 :            :         if (!IS_ENABLED(CONFIG_ARM_ARCH_TIMER))
     282                 :            :                 return false;
     283                 :            : 
     284         [ +  + ]:    8195822 :         if (tk->tkr_mono.clock->archdata.clock_mode != VDSO_CLOCKMODE_ARCHTIMER)
     285                 :            :                 return false;
     286                 :            : 
     287                 :            :         return true;
     288                 :            : }
     289                 :            : 
     290                 :            : /**
     291                 :            :  * update_vsyscall - update the vdso data page
     292                 :            :  *
     293                 :            :  * Increment the sequence counter, making it odd, indicating to
     294                 :            :  * userspace that an update is in progress.  Update the fields used
     295                 :            :  * for coarse clocks and, if the architected system timer is in use,
     296                 :            :  * the fields used for high precision clocks.  Increment the sequence
     297                 :            :  * counter again, making it even, indicating to userspace that the
     298                 :            :  * update is finished.
     299                 :            :  *
     300                 :            :  * Userspace is expected to sample seq_count before reading any other
     301                 :            :  * fields from the data page.  If seq_count is odd, userspace is
     302                 :            :  * expected to wait until it becomes even.  After copying data from
     303                 :            :  * the page, userspace must sample seq_count again; if it has changed
     304                 :            :  * from its previous value, userspace must retry the whole sequence.
     305                 :            :  *
     306                 :            :  * Calls to update_vsyscall are serialized by the timekeeping core.
     307                 :            :  */
     308                 :    8209616 : void update_vsyscall(struct timekeeper *tk)
     309                 :            : {
     310                 :            :         struct timespec64 *wtm = &tk->wall_to_monotonic;
     311                 :            : 
     312         [ +  + ]:    8209616 :         if (!cntvct_ok) {
     313                 :            :                 /* The entry points have been zeroed, so there is no
     314                 :            :                  * point in updating the data page.
     315                 :            :                  */
     316                 :    8209616 :                 return;
     317                 :            :         }
     318                 :            : 
     319                 :            :         vdso_write_begin(vdso_data);
     320                 :            : 
     321                 :   16391644 :         vdso_data->tk_is_cntvct                      = tk_is_cntvct(tk);
     322                 :    8195822 :         vdso_data->xtime_coarse_sec          = tk->xtime_sec;
     323                 :   16391644 :         vdso_data->xtime_coarse_nsec         = (u32)(tk->tkr_mono.xtime_nsec >>
     324                 :    8195822 :                                                         tk->tkr_mono.shift);
     325                 :    8195822 :         vdso_data->wtm_clock_sec             = wtm->tv_sec;
     326                 :    8195822 :         vdso_data->wtm_clock_nsec            = wtm->tv_nsec;
     327                 :            : 
     328         [ +  + ]:    8195822 :         if (vdso_data->tk_is_cntvct) {
     329                 :    8186650 :                 vdso_data->cs_cycle_last     = tk->tkr_mono.cycle_last;
     330                 :    8186650 :                 vdso_data->xtime_clock_sec   = tk->xtime_sec;
     331                 :    8186650 :                 vdso_data->xtime_clock_snsec = tk->tkr_mono.xtime_nsec;
     332                 :    8186650 :                 vdso_data->cs_mult           = tk->tkr_mono.mult;
     333                 :    8186650 :                 vdso_data->cs_shift          = tk->tkr_mono.shift;
     334                 :    8186650 :                 vdso_data->cs_mask           = tk->tkr_mono.mask;
     335                 :            :         }
     336                 :            : 
     337                 :            :         vdso_write_end(vdso_data);
     338                 :            : 
     339                 :    8195822 :         flush_dcache_page(virt_to_page(vdso_data));
     340                 :            : }
     341                 :            : 
     342                 :        404 : void update_vsyscall_tz(void)
     343                 :            : {
     344                 :        404 :         vdso_data->tz_minuteswest    = sys_tz.tz_minuteswest;
     345                 :        404 :         vdso_data->tz_dsttime                = sys_tz.tz_dsttime;
     346                 :        404 :         flush_dcache_page(virt_to_page(vdso_data));
     347                 :        404 : }

Generated by: LCOV version 1.14