linux/arch/arm/mm/highmem.c
<<
>>
Prefs
   1/*
   2 * arch/arm/mm/highmem.c -- ARM highmem support
   3 *
   4 * Author:      Nicolas Pitre
   5 * Created:     september 8, 2008
   6 * Copyright:   Marvell Semiconductors Inc.
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/highmem.h>
  15#include <linux/interrupt.h>
  16#include <asm/fixmap.h>
  17#include <asm/cacheflush.h>
  18#include <asm/tlbflush.h>
  19#include "mm.h"
  20
  21static inline void set_fixmap_pte(int idx, pte_t pte)
  22{
  23        unsigned long vaddr = __fix_to_virt(idx);
  24        pte_t *ptep = pte_offset_kernel(pmd_off_k(vaddr), vaddr);
  25
  26        set_pte_ext(ptep, pte, 0);
  27        local_flush_tlb_kernel_page(vaddr);
  28}
  29
  30static inline pte_t get_fixmap_pte(unsigned long vaddr)
  31{
  32        pte_t *ptep = pte_offset_kernel(pmd_off_k(vaddr), vaddr);
  33
  34        return *ptep;
  35}
  36
  37void *kmap(struct page *page)
  38{
  39        might_sleep();
  40        if (!PageHighMem(page))
  41                return page_address(page);
  42        return kmap_high(page);
  43}
  44EXPORT_SYMBOL(kmap);
  45
  46void kunmap(struct page *page)
  47{
  48        BUG_ON(in_interrupt());
  49        if (!PageHighMem(page))
  50                return;
  51        kunmap_high(page);
  52}
  53EXPORT_SYMBOL(kunmap);
  54
  55void *kmap_atomic(struct page *page)
  56{
  57        unsigned int idx;
  58        unsigned long vaddr;
  59        void *kmap;
  60        int type;
  61
  62        pagefault_disable();
  63        if (!PageHighMem(page))
  64                return page_address(page);
  65
  66#ifdef CONFIG_DEBUG_HIGHMEM
  67        /*
  68         * There is no cache coherency issue when non VIVT, so force the
  69         * dedicated kmap usage for better debugging purposes in that case.
  70         */
  71        if (!cache_is_vivt())
  72                kmap = NULL;
  73        else
  74#endif
  75                kmap = kmap_high_get(page);
  76        if (kmap)
  77                return kmap;
  78
  79        type = kmap_atomic_idx_push();
  80
  81        idx = type + KM_TYPE_NR * smp_processor_id();
  82        vaddr = __fix_to_virt(idx);
  83#ifdef CONFIG_DEBUG_HIGHMEM
  84        /*
  85         * With debugging enabled, kunmap_atomic forces that entry to 0.
  86         * Make sure it was indeed properly unmapped.
  87         */
  88        BUG_ON(!pte_none(get_fixmap_pte(vaddr)));
  89#endif
  90        /*
  91         * When debugging is off, kunmap_atomic leaves the previous mapping
  92         * in place, so the contained TLB flush ensures the TLB is updated
  93         * with the new mapping.
  94         */
  95        set_fixmap_pte(idx, mk_pte(page, kmap_prot));
  96
  97        return (void *)vaddr;
  98}
  99EXPORT_SYMBOL(kmap_atomic);
 100
 101void __kunmap_atomic(void *kvaddr)
 102{
 103        unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
 104        int idx, type;
 105
 106        if (kvaddr >= (void *)FIXADDR_START) {
 107                type = kmap_atomic_idx();
 108                idx = type + KM_TYPE_NR * smp_processor_id();
 109
 110                if (cache_is_vivt())
 111                        __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE);
 112#ifdef CONFIG_DEBUG_HIGHMEM
 113                BUG_ON(vaddr != __fix_to_virt(idx));
 114                set_fixmap_pte(idx, __pte(0));
 115#else
 116                (void) idx;  /* to kill a warning */
 117#endif
 118                kmap_atomic_idx_pop();
 119        } else if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) {
 120                /* this address was obtained through kmap_high_get() */
 121                kunmap_high(pte_page(pkmap_page_table[PKMAP_NR(vaddr)]));
 122        }
 123        pagefault_enable();
 124}
 125EXPORT_SYMBOL(__kunmap_atomic);
 126
 127void *kmap_atomic_pfn(unsigned long pfn)
 128{
 129        unsigned long vaddr;
 130        int idx, type;
 131        struct page *page = pfn_to_page(pfn);
 132
 133        pagefault_disable();
 134        if (!PageHighMem(page))
 135                return page_address(page);
 136
 137        type = kmap_atomic_idx_push();
 138        idx = type + KM_TYPE_NR * smp_processor_id();
 139        vaddr = __fix_to_virt(idx);
 140#ifdef CONFIG_DEBUG_HIGHMEM
 141        BUG_ON(!pte_none(get_fixmap_pte(vaddr)));
 142#endif
 143        set_fixmap_pte(idx, pfn_pte(pfn, kmap_prot));
 144
 145        return (void *)vaddr;
 146}
 147
 148struct page *kmap_atomic_to_page(const void *ptr)
 149{
 150        unsigned long vaddr = (unsigned long)ptr;
 151
 152        if (vaddr < FIXADDR_START)
 153                return virt_to_page(ptr);
 154
 155        return pte_page(get_fixmap_pte(vaddr));
 156}
 157