1e64c8aa0SThomas Gleixner /* 2e64c8aa0SThomas Gleixner * Re-map IO memory to kernel address space so that we can access it. 3e64c8aa0SThomas Gleixner * This is needed for high PCI addresses that aren't mapped in the 4e64c8aa0SThomas Gleixner * 640k-1MB IO memory area on PC's 5e64c8aa0SThomas Gleixner * 6e64c8aa0SThomas Gleixner * (C) Copyright 1995 1996 Linus Torvalds 7e64c8aa0SThomas Gleixner */ 8e64c8aa0SThomas Gleixner 9e64c8aa0SThomas Gleixner #include <linux/bootmem.h> 10e64c8aa0SThomas Gleixner #include <linux/init.h> 11e64c8aa0SThomas Gleixner #include <linux/io.h> 129de94dbbSIngo Molnar #include <linux/ioport.h> 13e64c8aa0SThomas Gleixner #include <linux/slab.h> 14e64c8aa0SThomas Gleixner #include <linux/vmalloc.h> 15d61fc448SPekka Paalanen #include <linux/mmiotrace.h> 16e64c8aa0SThomas Gleixner 17*d1163651SLaura Abbott #include <asm/set_memory.h> 1866441bd3SIngo Molnar #include <asm/e820/api.h> 19e64c8aa0SThomas Gleixner #include <asm/fixmap.h> 20e64c8aa0SThomas Gleixner #include <asm/pgtable.h> 21e64c8aa0SThomas Gleixner #include <asm/tlbflush.h> 22f6df72e7SJeremy Fitzhardinge #include <asm/pgalloc.h> 23d7677d40Svenkatesh.pallipadi@intel.com #include <asm/pat.h> 24e64c8aa0SThomas Gleixner 2578c86e5eSJeremy Fitzhardinge #include "physaddr.h" 26e64c8aa0SThomas Gleixner 27e64c8aa0SThomas Gleixner /* 28e64c8aa0SThomas Gleixner * Fix up the linear direct mapping of the kernel to avoid cache attribute 29e64c8aa0SThomas Gleixner * conflicts. 30e64c8aa0SThomas Gleixner */ 313a96ce8cSvenkatesh.pallipadi@intel.com int ioremap_change_attr(unsigned long vaddr, unsigned long size, 32b14097bdSJuergen Gross enum page_cache_mode pcm) 33e64c8aa0SThomas Gleixner { 34d806e5eeSThomas Gleixner unsigned long nrpages = size >> PAGE_SHIFT; 3593809be8SHarvey Harrison int err; 36e64c8aa0SThomas Gleixner 37b14097bdSJuergen Gross switch (pcm) { 38b14097bdSJuergen Gross case _PAGE_CACHE_MODE_UC: 39d806e5eeSThomas Gleixner default: 401219333dSvenkatesh.pallipadi@intel.com err = _set_memory_uc(vaddr, nrpages); 41d806e5eeSThomas Gleixner break; 42b14097bdSJuergen Gross case _PAGE_CACHE_MODE_WC: 43b310f381Svenkatesh.pallipadi@intel.com err = _set_memory_wc(vaddr, nrpages); 44b310f381Svenkatesh.pallipadi@intel.com break; 45623dffb2SToshi Kani case _PAGE_CACHE_MODE_WT: 46623dffb2SToshi Kani err = _set_memory_wt(vaddr, nrpages); 47623dffb2SToshi Kani break; 48b14097bdSJuergen Gross case _PAGE_CACHE_MODE_WB: 491219333dSvenkatesh.pallipadi@intel.com err = _set_memory_wb(vaddr, nrpages); 50d806e5eeSThomas Gleixner break; 51d806e5eeSThomas Gleixner } 52e64c8aa0SThomas Gleixner 53e64c8aa0SThomas Gleixner return err; 54e64c8aa0SThomas Gleixner } 55e64c8aa0SThomas Gleixner 56c81c8a1eSRoland Dreier static int __ioremap_check_ram(unsigned long start_pfn, unsigned long nr_pages, 57c81c8a1eSRoland Dreier void *arg) 58c81c8a1eSRoland Dreier { 59c81c8a1eSRoland Dreier unsigned long i; 60c81c8a1eSRoland Dreier 61c81c8a1eSRoland Dreier for (i = 0; i < nr_pages; ++i) 62c81c8a1eSRoland Dreier if (pfn_valid(start_pfn + i) && 63c81c8a1eSRoland Dreier !PageReserved(pfn_to_page(start_pfn + i))) 64c81c8a1eSRoland Dreier return 1; 65c81c8a1eSRoland Dreier 66c81c8a1eSRoland Dreier return 0; 67c81c8a1eSRoland Dreier } 68c81c8a1eSRoland Dreier 69e64c8aa0SThomas Gleixner /* 70e64c8aa0SThomas Gleixner * Remap an arbitrary physical address space into the kernel virtual 715d72b4fbSToshi Kani * address space. It transparently creates kernel huge I/O mapping when 725d72b4fbSToshi Kani * the physical address is aligned by a huge page size (1GB or 2MB) and 735d72b4fbSToshi Kani * the requested size is at least the huge page size. 745d72b4fbSToshi Kani * 755d72b4fbSToshi Kani * NOTE: MTRRs can override PAT memory types with a 4KB granularity. 765d72b4fbSToshi Kani * Therefore, the mapping code falls back to use a smaller page toward 4KB 775d72b4fbSToshi Kani * when a mapping range is covered by non-WB type of MTRRs. 78e64c8aa0SThomas Gleixner * 79e64c8aa0SThomas Gleixner * NOTE! We need to allow non-page-aligned mappings too: we will obviously 80e64c8aa0SThomas Gleixner * have to convert them into an offset in a page-aligned mapping, but the 81e64c8aa0SThomas Gleixner * caller shouldn't need to know that small detail. 82e64c8aa0SThomas Gleixner */ 8323016969SChristoph Lameter static void __iomem *__ioremap_caller(resource_size_t phys_addr, 84b14097bdSJuergen Gross unsigned long size, enum page_cache_mode pcm, void *caller) 85e64c8aa0SThomas Gleixner { 86ffa71f33SKenji Kaneshige unsigned long offset, vaddr; 87ffa71f33SKenji Kaneshige resource_size_t pfn, last_pfn, last_addr; 8887e547feSPekka Paalanen const resource_size_t unaligned_phys_addr = phys_addr; 8987e547feSPekka Paalanen const unsigned long unaligned_size = size; 90e64c8aa0SThomas Gleixner struct vm_struct *area; 91b14097bdSJuergen Gross enum page_cache_mode new_pcm; 92d806e5eeSThomas Gleixner pgprot_t prot; 93dee7cbb2SVenki Pallipadi int retval; 94d61fc448SPekka Paalanen void __iomem *ret_addr; 95e64c8aa0SThomas Gleixner 96e64c8aa0SThomas Gleixner /* Don't allow wraparound or zero size */ 97e64c8aa0SThomas Gleixner last_addr = phys_addr + size - 1; 98e64c8aa0SThomas Gleixner if (!size || last_addr < phys_addr) 99e64c8aa0SThomas Gleixner return NULL; 100e64c8aa0SThomas Gleixner 101e3100c82SThomas Gleixner if (!phys_addr_valid(phys_addr)) { 1026997ab49Svenkatesh.pallipadi@intel.com printk(KERN_WARNING "ioremap: invalid physical address %llx\n", 1034c8337acSRandy Dunlap (unsigned long long)phys_addr); 104e3100c82SThomas Gleixner WARN_ON_ONCE(1); 105e3100c82SThomas Gleixner return NULL; 106e3100c82SThomas Gleixner } 107e3100c82SThomas Gleixner 108e64c8aa0SThomas Gleixner /* 109e64c8aa0SThomas Gleixner * Don't remap the low PCI/ISA area, it's always mapped.. 110e64c8aa0SThomas Gleixner */ 111bcc643dcSAndreas Herrmann if (is_ISA_range(phys_addr, last_addr)) 112e64c8aa0SThomas Gleixner return (__force void __iomem *)phys_to_virt(phys_addr); 113e64c8aa0SThomas Gleixner 114e64c8aa0SThomas Gleixner /* 115e64c8aa0SThomas Gleixner * Don't allow anybody to remap normal RAM that we're using.. 116e64c8aa0SThomas Gleixner */ 117c81c8a1eSRoland Dreier pfn = phys_addr >> PAGE_SHIFT; 118ffa71f33SKenji Kaneshige last_pfn = last_addr >> PAGE_SHIFT; 119c81c8a1eSRoland Dreier if (walk_system_ram_range(pfn, last_pfn - pfn + 1, NULL, 1201c9cf9b2SToshi Kani __ioremap_check_ram) == 1) { 1218a0a5da6SThomas Gleixner WARN_ONCE(1, "ioremap on RAM at %pa - %pa\n", 1228a0a5da6SThomas Gleixner &phys_addr, &last_addr); 123e64c8aa0SThomas Gleixner return NULL; 124906e36c5SMike Travis } 1259a58eebeSToshi Kani 126d7677d40Svenkatesh.pallipadi@intel.com /* 127d7677d40Svenkatesh.pallipadi@intel.com * Mappings have to be page-aligned 128d7677d40Svenkatesh.pallipadi@intel.com */ 129d7677d40Svenkatesh.pallipadi@intel.com offset = phys_addr & ~PAGE_MASK; 130ffa71f33SKenji Kaneshige phys_addr &= PHYSICAL_PAGE_MASK; 131d7677d40Svenkatesh.pallipadi@intel.com size = PAGE_ALIGN(last_addr+1) - phys_addr; 132d7677d40Svenkatesh.pallipadi@intel.com 133e213e877SAndi Kleen retval = reserve_memtype(phys_addr, (u64)phys_addr + size, 134e00c8cc9SJuergen Gross pcm, &new_pcm); 135dee7cbb2SVenki Pallipadi if (retval) { 136279e669bSVenkatesh Pallipadi printk(KERN_ERR "ioremap reserve_memtype failed %d\n", retval); 137dee7cbb2SVenki Pallipadi return NULL; 138dee7cbb2SVenki Pallipadi } 139dee7cbb2SVenki Pallipadi 140b14097bdSJuergen Gross if (pcm != new_pcm) { 141b14097bdSJuergen Gross if (!is_new_memtype_allowed(phys_addr, size, pcm, new_pcm)) { 142279e669bSVenkatesh Pallipadi printk(KERN_ERR 143b14097bdSJuergen Gross "ioremap error for 0x%llx-0x%llx, requested 0x%x, got 0x%x\n", 1444c8337acSRandy Dunlap (unsigned long long)phys_addr, 1454c8337acSRandy Dunlap (unsigned long long)(phys_addr + size), 146b14097bdSJuergen Gross pcm, new_pcm); 147de2a47cfSXiaotian Feng goto err_free_memtype; 148d7677d40Svenkatesh.pallipadi@intel.com } 149b14097bdSJuergen Gross pcm = new_pcm; 150d7677d40Svenkatesh.pallipadi@intel.com } 151d7677d40Svenkatesh.pallipadi@intel.com 152be43d728SJeremy Fitzhardinge prot = PAGE_KERNEL_IO; 153b14097bdSJuergen Gross switch (pcm) { 154b14097bdSJuergen Gross case _PAGE_CACHE_MODE_UC: 155b14097bdSJuergen Gross default: 156b14097bdSJuergen Gross prot = __pgprot(pgprot_val(prot) | 157b14097bdSJuergen Gross cachemode2protval(_PAGE_CACHE_MODE_UC)); 158b14097bdSJuergen Gross break; 159b14097bdSJuergen Gross case _PAGE_CACHE_MODE_UC_MINUS: 160b14097bdSJuergen Gross prot = __pgprot(pgprot_val(prot) | 161b14097bdSJuergen Gross cachemode2protval(_PAGE_CACHE_MODE_UC_MINUS)); 162b14097bdSJuergen Gross break; 163b14097bdSJuergen Gross case _PAGE_CACHE_MODE_WC: 164b14097bdSJuergen Gross prot = __pgprot(pgprot_val(prot) | 165b14097bdSJuergen Gross cachemode2protval(_PAGE_CACHE_MODE_WC)); 166b14097bdSJuergen Gross break; 167d838270eSToshi Kani case _PAGE_CACHE_MODE_WT: 168d838270eSToshi Kani prot = __pgprot(pgprot_val(prot) | 169d838270eSToshi Kani cachemode2protval(_PAGE_CACHE_MODE_WT)); 170d838270eSToshi Kani break; 171b14097bdSJuergen Gross case _PAGE_CACHE_MODE_WB: 172d806e5eeSThomas Gleixner break; 173d806e5eeSThomas Gleixner } 174e64c8aa0SThomas Gleixner 175e64c8aa0SThomas Gleixner /* 176e64c8aa0SThomas Gleixner * Ok, go for it.. 177e64c8aa0SThomas Gleixner */ 17823016969SChristoph Lameter area = get_vm_area_caller(size, VM_IOREMAP, caller); 179e64c8aa0SThomas Gleixner if (!area) 180de2a47cfSXiaotian Feng goto err_free_memtype; 181e64c8aa0SThomas Gleixner area->phys_addr = phys_addr; 182e66aadbeSThomas Gleixner vaddr = (unsigned long) area->addr; 18343a432b1SSuresh Siddha 184b14097bdSJuergen Gross if (kernel_map_sync_memtype(phys_addr, size, pcm)) 185de2a47cfSXiaotian Feng goto err_free_area; 186e64c8aa0SThomas Gleixner 187de2a47cfSXiaotian Feng if (ioremap_page_range(vaddr, vaddr + size, phys_addr, prot)) 188de2a47cfSXiaotian Feng goto err_free_area; 189e64c8aa0SThomas Gleixner 190d61fc448SPekka Paalanen ret_addr = (void __iomem *) (vaddr + offset); 19187e547feSPekka Paalanen mmiotrace_ioremap(unaligned_phys_addr, unaligned_size, ret_addr); 192d61fc448SPekka Paalanen 193c7a7b814STim Gardner /* 194c7a7b814STim Gardner * Check if the request spans more than any BAR in the iomem resource 195c7a7b814STim Gardner * tree. 196c7a7b814STim Gardner */ 1979abb0ecdSLaura Abbott if (iomem_map_sanity_check(unaligned_phys_addr, unaligned_size)) 1989abb0ecdSLaura Abbott pr_warn("caller %pS mapping multiple BARs\n", caller); 199c7a7b814STim Gardner 200d61fc448SPekka Paalanen return ret_addr; 201de2a47cfSXiaotian Feng err_free_area: 202de2a47cfSXiaotian Feng free_vm_area(area); 203de2a47cfSXiaotian Feng err_free_memtype: 204de2a47cfSXiaotian Feng free_memtype(phys_addr, phys_addr + size); 205de2a47cfSXiaotian Feng return NULL; 206e64c8aa0SThomas Gleixner } 207e64c8aa0SThomas Gleixner 208e64c8aa0SThomas Gleixner /** 209e64c8aa0SThomas Gleixner * ioremap_nocache - map bus memory into CPU space 2109efc31b8SWanpeng Li * @phys_addr: bus address of the memory 211e64c8aa0SThomas Gleixner * @size: size of the resource to map 212e64c8aa0SThomas Gleixner * 213e64c8aa0SThomas Gleixner * ioremap_nocache performs a platform specific sequence of operations to 214e64c8aa0SThomas Gleixner * make bus memory CPU accessible via the readb/readw/readl/writeb/ 215e64c8aa0SThomas Gleixner * writew/writel functions and the other mmio helpers. The returned 216e64c8aa0SThomas Gleixner * address is not guaranteed to be usable directly as a virtual 217e64c8aa0SThomas Gleixner * address. 218e64c8aa0SThomas Gleixner * 219e64c8aa0SThomas Gleixner * This version of ioremap ensures that the memory is marked uncachable 220e64c8aa0SThomas Gleixner * on the CPU as well as honouring existing caching rules from things like 221e64c8aa0SThomas Gleixner * the PCI bus. Note that there are other caches and buffers on many 222e64c8aa0SThomas Gleixner * busses. In particular driver authors should read up on PCI writes 223e64c8aa0SThomas Gleixner * 224e64c8aa0SThomas Gleixner * It's useful if some control registers are in such an area and 225e64c8aa0SThomas Gleixner * write combining or read caching is not desirable: 226e64c8aa0SThomas Gleixner * 227e64c8aa0SThomas Gleixner * Must be freed with iounmap. 228e64c8aa0SThomas Gleixner */ 229b9e76a00SLinus Torvalds void __iomem *ioremap_nocache(resource_size_t phys_addr, unsigned long size) 230e64c8aa0SThomas Gleixner { 231de33c442SSuresh Siddha /* 232de33c442SSuresh Siddha * Ideally, this should be: 233cb32edf6SLuis R. Rodriguez * pat_enabled() ? _PAGE_CACHE_MODE_UC : _PAGE_CACHE_MODE_UC_MINUS; 234de33c442SSuresh Siddha * 235de33c442SSuresh Siddha * Till we fix all X drivers to use ioremap_wc(), we will use 236e4b6be33SLuis R. Rodriguez * UC MINUS. Drivers that are certain they need or can already 237e4b6be33SLuis R. Rodriguez * be converted over to strong UC can use ioremap_uc(). 238de33c442SSuresh Siddha */ 239b14097bdSJuergen Gross enum page_cache_mode pcm = _PAGE_CACHE_MODE_UC_MINUS; 240de33c442SSuresh Siddha 241b14097bdSJuergen Gross return __ioremap_caller(phys_addr, size, pcm, 24223016969SChristoph Lameter __builtin_return_address(0)); 243e64c8aa0SThomas Gleixner } 244e64c8aa0SThomas Gleixner EXPORT_SYMBOL(ioremap_nocache); 245e64c8aa0SThomas Gleixner 246b310f381Svenkatesh.pallipadi@intel.com /** 247e4b6be33SLuis R. Rodriguez * ioremap_uc - map bus memory into CPU space as strongly uncachable 248e4b6be33SLuis R. Rodriguez * @phys_addr: bus address of the memory 249e4b6be33SLuis R. Rodriguez * @size: size of the resource to map 250e4b6be33SLuis R. Rodriguez * 251e4b6be33SLuis R. Rodriguez * ioremap_uc performs a platform specific sequence of operations to 252e4b6be33SLuis R. Rodriguez * make bus memory CPU accessible via the readb/readw/readl/writeb/ 253e4b6be33SLuis R. Rodriguez * writew/writel functions and the other mmio helpers. The returned 254e4b6be33SLuis R. Rodriguez * address is not guaranteed to be usable directly as a virtual 255e4b6be33SLuis R. Rodriguez * address. 256e4b6be33SLuis R. Rodriguez * 257e4b6be33SLuis R. Rodriguez * This version of ioremap ensures that the memory is marked with a strong 258e4b6be33SLuis R. Rodriguez * preference as completely uncachable on the CPU when possible. For non-PAT 259e4b6be33SLuis R. Rodriguez * systems this ends up setting page-attribute flags PCD=1, PWT=1. For PAT 260e4b6be33SLuis R. Rodriguez * systems this will set the PAT entry for the pages as strong UC. This call 261e4b6be33SLuis R. Rodriguez * will honor existing caching rules from things like the PCI bus. Note that 262e4b6be33SLuis R. Rodriguez * there are other caches and buffers on many busses. In particular driver 263e4b6be33SLuis R. Rodriguez * authors should read up on PCI writes. 264e4b6be33SLuis R. Rodriguez * 265e4b6be33SLuis R. Rodriguez * It's useful if some control registers are in such an area and 266e4b6be33SLuis R. Rodriguez * write combining or read caching is not desirable: 267e4b6be33SLuis R. Rodriguez * 268e4b6be33SLuis R. Rodriguez * Must be freed with iounmap. 269e4b6be33SLuis R. Rodriguez */ 270e4b6be33SLuis R. Rodriguez void __iomem *ioremap_uc(resource_size_t phys_addr, unsigned long size) 271e4b6be33SLuis R. Rodriguez { 272e4b6be33SLuis R. Rodriguez enum page_cache_mode pcm = _PAGE_CACHE_MODE_UC; 273e4b6be33SLuis R. Rodriguez 274e4b6be33SLuis R. Rodriguez return __ioremap_caller(phys_addr, size, pcm, 275e4b6be33SLuis R. Rodriguez __builtin_return_address(0)); 276e4b6be33SLuis R. Rodriguez } 277e4b6be33SLuis R. Rodriguez EXPORT_SYMBOL_GPL(ioremap_uc); 278e4b6be33SLuis R. Rodriguez 279e4b6be33SLuis R. Rodriguez /** 280b310f381Svenkatesh.pallipadi@intel.com * ioremap_wc - map memory into CPU space write combined 2819efc31b8SWanpeng Li * @phys_addr: bus address of the memory 282b310f381Svenkatesh.pallipadi@intel.com * @size: size of the resource to map 283b310f381Svenkatesh.pallipadi@intel.com * 284b310f381Svenkatesh.pallipadi@intel.com * This version of ioremap ensures that the memory is marked write combining. 285b310f381Svenkatesh.pallipadi@intel.com * Write combining allows faster writes to some hardware devices. 286b310f381Svenkatesh.pallipadi@intel.com * 287b310f381Svenkatesh.pallipadi@intel.com * Must be freed with iounmap. 288b310f381Svenkatesh.pallipadi@intel.com */ 289d639bab8Svenkatesh.pallipadi@intel.com void __iomem *ioremap_wc(resource_size_t phys_addr, unsigned long size) 290b310f381Svenkatesh.pallipadi@intel.com { 291b14097bdSJuergen Gross return __ioremap_caller(phys_addr, size, _PAGE_CACHE_MODE_WC, 29223016969SChristoph Lameter __builtin_return_address(0)); 293b310f381Svenkatesh.pallipadi@intel.com } 294b310f381Svenkatesh.pallipadi@intel.com EXPORT_SYMBOL(ioremap_wc); 295b310f381Svenkatesh.pallipadi@intel.com 296d838270eSToshi Kani /** 297d838270eSToshi Kani * ioremap_wt - map memory into CPU space write through 298d838270eSToshi Kani * @phys_addr: bus address of the memory 299d838270eSToshi Kani * @size: size of the resource to map 300d838270eSToshi Kani * 301d838270eSToshi Kani * This version of ioremap ensures that the memory is marked write through. 302d838270eSToshi Kani * Write through stores data into memory while keeping the cache up-to-date. 303d838270eSToshi Kani * 304d838270eSToshi Kani * Must be freed with iounmap. 305d838270eSToshi Kani */ 306d838270eSToshi Kani void __iomem *ioremap_wt(resource_size_t phys_addr, unsigned long size) 307d838270eSToshi Kani { 308d838270eSToshi Kani return __ioremap_caller(phys_addr, size, _PAGE_CACHE_MODE_WT, 309d838270eSToshi Kani __builtin_return_address(0)); 310d838270eSToshi Kani } 311d838270eSToshi Kani EXPORT_SYMBOL(ioremap_wt); 312d838270eSToshi Kani 313b9e76a00SLinus Torvalds void __iomem *ioremap_cache(resource_size_t phys_addr, unsigned long size) 3145f868152SThomas Gleixner { 315b14097bdSJuergen Gross return __ioremap_caller(phys_addr, size, _PAGE_CACHE_MODE_WB, 31623016969SChristoph Lameter __builtin_return_address(0)); 3175f868152SThomas Gleixner } 3185f868152SThomas Gleixner EXPORT_SYMBOL(ioremap_cache); 3195f868152SThomas Gleixner 32028b2ee20SRik van Riel void __iomem *ioremap_prot(resource_size_t phys_addr, unsigned long size, 32128b2ee20SRik van Riel unsigned long prot_val) 32228b2ee20SRik van Riel { 323b14097bdSJuergen Gross return __ioremap_caller(phys_addr, size, 324b14097bdSJuergen Gross pgprot2cachemode(__pgprot(prot_val)), 32528b2ee20SRik van Riel __builtin_return_address(0)); 32628b2ee20SRik van Riel } 32728b2ee20SRik van Riel EXPORT_SYMBOL(ioremap_prot); 32828b2ee20SRik van Riel 329e64c8aa0SThomas Gleixner /** 330e64c8aa0SThomas Gleixner * iounmap - Free a IO remapping 331e64c8aa0SThomas Gleixner * @addr: virtual address from ioremap_* 332e64c8aa0SThomas Gleixner * 333e64c8aa0SThomas Gleixner * Caller must ensure there is only one unmapping for the same pointer. 334e64c8aa0SThomas Gleixner */ 335e64c8aa0SThomas Gleixner void iounmap(volatile void __iomem *addr) 336e64c8aa0SThomas Gleixner { 337e64c8aa0SThomas Gleixner struct vm_struct *p, *o; 338e64c8aa0SThomas Gleixner 339e64c8aa0SThomas Gleixner if ((void __force *)addr <= high_memory) 340e64c8aa0SThomas Gleixner return; 341e64c8aa0SThomas Gleixner 342e64c8aa0SThomas Gleixner /* 343e64c8aa0SThomas Gleixner * __ioremap special-cases the PCI/ISA range by not instantiating a 344e64c8aa0SThomas Gleixner * vm_area and by simply returning an address into the kernel mapping 345e64c8aa0SThomas Gleixner * of ISA space. So handle that here. 346e64c8aa0SThomas Gleixner */ 3476e92a5a6SThomas Gleixner if ((void __force *)addr >= phys_to_virt(ISA_START_ADDRESS) && 3486e92a5a6SThomas Gleixner (void __force *)addr < phys_to_virt(ISA_END_ADDRESS)) 349e64c8aa0SThomas Gleixner return; 350e64c8aa0SThomas Gleixner 351e64c8aa0SThomas Gleixner addr = (volatile void __iomem *) 352e64c8aa0SThomas Gleixner (PAGE_MASK & (unsigned long __force)addr); 353e64c8aa0SThomas Gleixner 354d61fc448SPekka Paalanen mmiotrace_iounmap(addr); 355d61fc448SPekka Paalanen 356e64c8aa0SThomas Gleixner /* Use the vm area unlocked, assuming the caller 357e64c8aa0SThomas Gleixner ensures there isn't another iounmap for the same address 358e64c8aa0SThomas Gleixner in parallel. Reuse of the virtual address is prevented by 359e64c8aa0SThomas Gleixner leaving it in the global lists until we're done with it. 360e64c8aa0SThomas Gleixner cpa takes care of the direct mappings. */ 361ef932473SJoonsoo Kim p = find_vm_area((void __force *)addr); 362e64c8aa0SThomas Gleixner 363e64c8aa0SThomas Gleixner if (!p) { 364e64c8aa0SThomas Gleixner printk(KERN_ERR "iounmap: bad address %p\n", addr); 365e64c8aa0SThomas Gleixner dump_stack(); 366e64c8aa0SThomas Gleixner return; 367e64c8aa0SThomas Gleixner } 368e64c8aa0SThomas Gleixner 369d7677d40Svenkatesh.pallipadi@intel.com free_memtype(p->phys_addr, p->phys_addr + get_vm_area_size(p)); 370d7677d40Svenkatesh.pallipadi@intel.com 371e64c8aa0SThomas Gleixner /* Finally remove it */ 3726e92a5a6SThomas Gleixner o = remove_vm_area((void __force *)addr); 373e64c8aa0SThomas Gleixner BUG_ON(p != o || o == NULL); 374e64c8aa0SThomas Gleixner kfree(p); 375e64c8aa0SThomas Gleixner } 376e64c8aa0SThomas Gleixner EXPORT_SYMBOL(iounmap); 377e64c8aa0SThomas Gleixner 3781e6277deSJan Beulich int __init arch_ioremap_pud_supported(void) 3795d72b4fbSToshi Kani { 3805d72b4fbSToshi Kani #ifdef CONFIG_X86_64 381b8291adcSBorislav Petkov return boot_cpu_has(X86_FEATURE_GBPAGES); 3825d72b4fbSToshi Kani #else 3835d72b4fbSToshi Kani return 0; 3845d72b4fbSToshi Kani #endif 3855d72b4fbSToshi Kani } 3865d72b4fbSToshi Kani 3871e6277deSJan Beulich int __init arch_ioremap_pmd_supported(void) 3885d72b4fbSToshi Kani { 38916bf9226SBorislav Petkov return boot_cpu_has(X86_FEATURE_PSE); 3905d72b4fbSToshi Kani } 3915d72b4fbSToshi Kani 392e045fb2aSvenkatesh.pallipadi@intel.com /* 393e045fb2aSvenkatesh.pallipadi@intel.com * Convert a physical pointer to a virtual kernel pointer for /dev/mem 394e045fb2aSvenkatesh.pallipadi@intel.com * access 395e045fb2aSvenkatesh.pallipadi@intel.com */ 3964707a341SThierry Reding void *xlate_dev_mem_ptr(phys_addr_t phys) 397e045fb2aSvenkatesh.pallipadi@intel.com { 398e045fb2aSvenkatesh.pallipadi@intel.com unsigned long start = phys & PAGE_MASK; 39994d4b476SIngo Molnar unsigned long offset = phys & ~PAGE_MASK; 400562bfca4SIngo Molnar void *vaddr; 401e045fb2aSvenkatesh.pallipadi@intel.com 402e045fb2aSvenkatesh.pallipadi@intel.com /* If page is RAM, we can use __va. Otherwise ioremap and unmap. */ 403e045fb2aSvenkatesh.pallipadi@intel.com if (page_is_ram(start >> PAGE_SHIFT)) 404e045fb2aSvenkatesh.pallipadi@intel.com return __va(phys); 405e045fb2aSvenkatesh.pallipadi@intel.com 406562bfca4SIngo Molnar vaddr = ioremap_cache(start, PAGE_SIZE); 40794d4b476SIngo Molnar /* Only add the offset on success and return NULL if the ioremap() failed: */ 40894d4b476SIngo Molnar if (vaddr) 40994d4b476SIngo Molnar vaddr += offset; 410e045fb2aSvenkatesh.pallipadi@intel.com 411562bfca4SIngo Molnar return vaddr; 412e045fb2aSvenkatesh.pallipadi@intel.com } 413e045fb2aSvenkatesh.pallipadi@intel.com 4144707a341SThierry Reding void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr) 415e045fb2aSvenkatesh.pallipadi@intel.com { 416e045fb2aSvenkatesh.pallipadi@intel.com if (page_is_ram(phys >> PAGE_SHIFT)) 417e045fb2aSvenkatesh.pallipadi@intel.com return; 418e045fb2aSvenkatesh.pallipadi@intel.com 419e045fb2aSvenkatesh.pallipadi@intel.com iounmap((void __iomem *)((unsigned long)addr & PAGE_MASK)); 420e045fb2aSvenkatesh.pallipadi@intel.com } 421e045fb2aSvenkatesh.pallipadi@intel.com 42245c7b28fSJeremy Fitzhardinge static pte_t bm_pte[PAGE_SIZE/sizeof(pte_t)] __page_aligned_bss; 423e64c8aa0SThomas Gleixner 424551889a6SIan Campbell static inline pmd_t * __init early_ioremap_pmd(unsigned long addr) 425e64c8aa0SThomas Gleixner { 42637cc8d7fSJeremy Fitzhardinge /* Don't assume we're using swapper_pg_dir at this point */ 42737cc8d7fSJeremy Fitzhardinge pgd_t *base = __va(read_cr3()); 42837cc8d7fSJeremy Fitzhardinge pgd_t *pgd = &base[pgd_index(addr)]; 429e0c4f675SKirill A. Shutemov p4d_t *p4d = p4d_offset(pgd, addr); 430e0c4f675SKirill A. Shutemov pud_t *pud = pud_offset(p4d, addr); 431551889a6SIan Campbell pmd_t *pmd = pmd_offset(pud, addr); 432551889a6SIan Campbell 433551889a6SIan Campbell return pmd; 434e64c8aa0SThomas Gleixner } 435e64c8aa0SThomas Gleixner 436551889a6SIan Campbell static inline pte_t * __init early_ioremap_pte(unsigned long addr) 437e64c8aa0SThomas Gleixner { 438551889a6SIan Campbell return &bm_pte[pte_index(addr)]; 439e64c8aa0SThomas Gleixner } 440e64c8aa0SThomas Gleixner 441fef5ba79SJeremy Fitzhardinge bool __init is_early_ioremap_ptep(pte_t *ptep) 442fef5ba79SJeremy Fitzhardinge { 443fef5ba79SJeremy Fitzhardinge return ptep >= &bm_pte[0] && ptep < &bm_pte[PAGE_SIZE/sizeof(pte_t)]; 444fef5ba79SJeremy Fitzhardinge } 445fef5ba79SJeremy Fitzhardinge 446e64c8aa0SThomas Gleixner void __init early_ioremap_init(void) 447e64c8aa0SThomas Gleixner { 448551889a6SIan Campbell pmd_t *pmd; 449e64c8aa0SThomas Gleixner 45073159fdcSAndy Lutomirski #ifdef CONFIG_X86_64 45173159fdcSAndy Lutomirski BUILD_BUG_ON((fix_to_virt(0) + PAGE_SIZE) & ((1 << PMD_SHIFT) - 1)); 45273159fdcSAndy Lutomirski #else 45373159fdcSAndy Lutomirski WARN_ON((fix_to_virt(0) + PAGE_SIZE) & ((1 << PMD_SHIFT) - 1)); 45473159fdcSAndy Lutomirski #endif 45573159fdcSAndy Lutomirski 4565b7c73e0SMark Salter early_ioremap_setup(); 4578827247fSWang Chen 458551889a6SIan Campbell pmd = early_ioremap_pmd(fix_to_virt(FIX_BTMAP_BEGIN)); 459e64c8aa0SThomas Gleixner memset(bm_pte, 0, sizeof(bm_pte)); 460b6fbb669SIan Campbell pmd_populate_kernel(&init_mm, pmd, bm_pte); 461551889a6SIan Campbell 462e64c8aa0SThomas Gleixner /* 463551889a6SIan Campbell * The boot-ioremap range spans multiple pmds, for which 464e64c8aa0SThomas Gleixner * we are not prepared: 465e64c8aa0SThomas Gleixner */ 466499a5f1eSJan Beulich #define __FIXADDR_TOP (-PAGE_SIZE) 467499a5f1eSJan Beulich BUILD_BUG_ON((__fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT) 468499a5f1eSJan Beulich != (__fix_to_virt(FIX_BTMAP_END) >> PMD_SHIFT)); 469499a5f1eSJan Beulich #undef __FIXADDR_TOP 470551889a6SIan Campbell if (pmd != early_ioremap_pmd(fix_to_virt(FIX_BTMAP_END))) { 471e64c8aa0SThomas Gleixner WARN_ON(1); 472551889a6SIan Campbell printk(KERN_WARNING "pmd %p != %p\n", 473551889a6SIan Campbell pmd, early_ioremap_pmd(fix_to_virt(FIX_BTMAP_END))); 474e64c8aa0SThomas Gleixner printk(KERN_WARNING "fix_to_virt(FIX_BTMAP_BEGIN): %08lx\n", 475e64c8aa0SThomas Gleixner fix_to_virt(FIX_BTMAP_BEGIN)); 476e64c8aa0SThomas Gleixner printk(KERN_WARNING "fix_to_virt(FIX_BTMAP_END): %08lx\n", 477e64c8aa0SThomas Gleixner fix_to_virt(FIX_BTMAP_END)); 478e64c8aa0SThomas Gleixner 479e64c8aa0SThomas Gleixner printk(KERN_WARNING "FIX_BTMAP_END: %d\n", FIX_BTMAP_END); 480e64c8aa0SThomas Gleixner printk(KERN_WARNING "FIX_BTMAP_BEGIN: %d\n", 481e64c8aa0SThomas Gleixner FIX_BTMAP_BEGIN); 482e64c8aa0SThomas Gleixner } 483e64c8aa0SThomas Gleixner } 484e64c8aa0SThomas Gleixner 4855b7c73e0SMark Salter void __init __early_set_fixmap(enum fixed_addresses idx, 4869b987aebSMasami Hiramatsu phys_addr_t phys, pgprot_t flags) 487e64c8aa0SThomas Gleixner { 488551889a6SIan Campbell unsigned long addr = __fix_to_virt(idx); 489551889a6SIan Campbell pte_t *pte; 490e64c8aa0SThomas Gleixner 491e64c8aa0SThomas Gleixner if (idx >= __end_of_fixed_addresses) { 492e64c8aa0SThomas Gleixner BUG(); 493e64c8aa0SThomas Gleixner return; 494e64c8aa0SThomas Gleixner } 495e64c8aa0SThomas Gleixner pte = early_ioremap_pte(addr); 4964583ed51SJeremy Fitzhardinge 497e64c8aa0SThomas Gleixner if (pgprot_val(flags)) 498551889a6SIan Campbell set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, flags)); 499e64c8aa0SThomas Gleixner else 5004f9c11ddSJeremy Fitzhardinge pte_clear(&init_mm, addr, pte); 501e64c8aa0SThomas Gleixner __flush_tlb_one(addr); 502e64c8aa0SThomas Gleixner } 503