1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
29e5c33d7SMark Salter /*
39e5c33d7SMark Salter * Provide common bits of early_ioremap() support for architectures needing
49e5c33d7SMark Salter * temporary mappings during boot before ioremap() is available.
59e5c33d7SMark Salter *
69e5c33d7SMark Salter * This is mostly a direct copy of the x86 early_ioremap implementation.
79e5c33d7SMark Salter *
89e5c33d7SMark Salter * (C) Copyright 1995 1996, 2014 Linus Torvalds
99e5c33d7SMark Salter *
109e5c33d7SMark Salter */
119e5c33d7SMark Salter #include <linux/kernel.h>
129e5c33d7SMark Salter #include <linux/init.h>
139e5c33d7SMark Salter #include <linux/io.h>
149e5c33d7SMark Salter #include <linux/module.h>
159e5c33d7SMark Salter #include <linux/slab.h>
169e5c33d7SMark Salter #include <linux/mm.h>
179e5c33d7SMark Salter #include <linux/vmalloc.h>
189e5c33d7SMark Salter #include <asm/fixmap.h>
194f1af60bSArd Biesheuvel #include <asm/early_ioremap.h>
20be4893d9SVlastimil Babka #include "internal.h"
219e5c33d7SMark Salter
229e5c33d7SMark Salter #ifdef CONFIG_MMU
239e5c33d7SMark Salter static int early_ioremap_debug __initdata;
249e5c33d7SMark Salter
early_ioremap_debug_setup(char * str)259e5c33d7SMark Salter static int __init early_ioremap_debug_setup(char *str)
269e5c33d7SMark Salter {
279e5c33d7SMark Salter early_ioremap_debug = 1;
289e5c33d7SMark Salter
299e5c33d7SMark Salter return 0;
309e5c33d7SMark Salter }
319e5c33d7SMark Salter early_param("early_ioremap_debug", early_ioremap_debug_setup);
329e5c33d7SMark Salter
33*5fd8391cSHou Wenlong #define early_ioremap_dbg(fmt, args...) \
34*5fd8391cSHou Wenlong do { \
35*5fd8391cSHou Wenlong if (unlikely(early_ioremap_debug)) { \
36*5fd8391cSHou Wenlong pr_warn(fmt, ##args); \
37*5fd8391cSHou Wenlong dump_stack(); \
38*5fd8391cSHou Wenlong } \
39*5fd8391cSHou Wenlong } while (0)
40*5fd8391cSHou Wenlong
419e5c33d7SMark Salter static int after_paging_init __initdata;
429e5c33d7SMark Salter
early_memremap_pgprot_adjust(resource_size_t phys_addr,unsigned long size,pgprot_t prot)438f716c9bSTom Lendacky pgprot_t __init __weak early_memremap_pgprot_adjust(resource_size_t phys_addr,
448f716c9bSTom Lendacky unsigned long size,
458f716c9bSTom Lendacky pgprot_t prot)
468f716c9bSTom Lendacky {
478f716c9bSTom Lendacky return prot;
488f716c9bSTom Lendacky }
498f716c9bSTom Lendacky
early_ioremap_reset(void)509e5c33d7SMark Salter void __init early_ioremap_reset(void)
519e5c33d7SMark Salter {
529e5c33d7SMark Salter after_paging_init = 1;
539e5c33d7SMark Salter }
549e5c33d7SMark Salter
559e5c33d7SMark Salter /*
569e5c33d7SMark Salter * Generally, ioremap() is available after paging_init() has been called.
579e5c33d7SMark Salter * Architectures wanting to allow early_ioremap after paging_init() can
589e5c33d7SMark Salter * define __late_set_fixmap and __late_clear_fixmap to do the right thing.
599e5c33d7SMark Salter */
609e5c33d7SMark Salter #ifndef __late_set_fixmap
__late_set_fixmap(enum fixed_addresses idx,phys_addr_t phys,pgprot_t prot)619e5c33d7SMark Salter static inline void __init __late_set_fixmap(enum fixed_addresses idx,
629e5c33d7SMark Salter phys_addr_t phys, pgprot_t prot)
639e5c33d7SMark Salter {
649e5c33d7SMark Salter BUG();
659e5c33d7SMark Salter }
669e5c33d7SMark Salter #endif
679e5c33d7SMark Salter
689e5c33d7SMark Salter #ifndef __late_clear_fixmap
__late_clear_fixmap(enum fixed_addresses idx)699e5c33d7SMark Salter static inline void __init __late_clear_fixmap(enum fixed_addresses idx)
709e5c33d7SMark Salter {
719e5c33d7SMark Salter BUG();
729e5c33d7SMark Salter }
739e5c33d7SMark Salter #endif
749e5c33d7SMark Salter
759e5c33d7SMark Salter static void __iomem *prev_map[FIX_BTMAPS_SLOTS] __initdata;
769e5c33d7SMark Salter static unsigned long prev_size[FIX_BTMAPS_SLOTS] __initdata;
779e5c33d7SMark Salter static unsigned long slot_virt[FIX_BTMAPS_SLOTS] __initdata;
789e5c33d7SMark Salter
early_ioremap_setup(void)799e5c33d7SMark Salter void __init early_ioremap_setup(void)
809e5c33d7SMark Salter {
819e5c33d7SMark Salter int i;
829e5c33d7SMark Salter
835e074725SLiam Ni for (i = 0; i < FIX_BTMAPS_SLOTS; i++) {
845e074725SLiam Ni WARN_ON_ONCE(prev_map[i]);
859e5c33d7SMark Salter slot_virt[i] = __fix_to_virt(FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*i);
869e5c33d7SMark Salter }
875e074725SLiam Ni }
889e5c33d7SMark Salter
check_early_ioremap_leak(void)899e5c33d7SMark Salter static int __init check_early_ioremap_leak(void)
909e5c33d7SMark Salter {
919e5c33d7SMark Salter int count = 0;
929e5c33d7SMark Salter int i;
939e5c33d7SMark Salter
949e5c33d7SMark Salter for (i = 0; i < FIX_BTMAPS_SLOTS; i++)
959e5c33d7SMark Salter if (prev_map[i])
969e5c33d7SMark Salter count++;
979e5c33d7SMark Salter
989e5c33d7SMark Salter if (WARN(count, KERN_WARNING
999e5c33d7SMark Salter "Debug warning: early ioremap leak of %d areas detected.\n"
1009e5c33d7SMark Salter "please boot with early_ioremap_debug and report the dmesg.\n",
1019e5c33d7SMark Salter count))
1029e5c33d7SMark Salter return 1;
1039e5c33d7SMark Salter return 0;
1049e5c33d7SMark Salter }
1059e5c33d7SMark Salter late_initcall(check_early_ioremap_leak);
1069e5c33d7SMark Salter
1079e5c33d7SMark Salter static void __init __iomem *
__early_ioremap(resource_size_t phys_addr,unsigned long size,pgprot_t prot)1089e5c33d7SMark Salter __early_ioremap(resource_size_t phys_addr, unsigned long size, pgprot_t prot)
1099e5c33d7SMark Salter {
1109e5c33d7SMark Salter unsigned long offset;
1119e5c33d7SMark Salter resource_size_t last_addr;
1129e5c33d7SMark Salter unsigned int nrpages;
1139e5c33d7SMark Salter enum fixed_addresses idx;
1149e5c33d7SMark Salter int i, slot;
1159e5c33d7SMark Salter
1167f6f60a1SDave Young WARN_ON(system_state >= SYSTEM_RUNNING);
1179e5c33d7SMark Salter
1189e5c33d7SMark Salter slot = -1;
1199e5c33d7SMark Salter for (i = 0; i < FIX_BTMAPS_SLOTS; i++) {
1209e5c33d7SMark Salter if (!prev_map[i]) {
1219e5c33d7SMark Salter slot = i;
1229e5c33d7SMark Salter break;
1239e5c33d7SMark Salter }
1249e5c33d7SMark Salter }
1259e5c33d7SMark Salter
1267b69d79fSAndy Shevchenko if (WARN(slot < 0, "%s(%pa, %08lx) not found slot\n",
1277b69d79fSAndy Shevchenko __func__, &phys_addr, size))
1289e5c33d7SMark Salter return NULL;
1299e5c33d7SMark Salter
1309e5c33d7SMark Salter /* Don't allow wraparound or zero size */
1319e5c33d7SMark Salter last_addr = phys_addr + size - 1;
1329e5c33d7SMark Salter if (WARN_ON(!size || last_addr < phys_addr))
1339e5c33d7SMark Salter return NULL;
1349e5c33d7SMark Salter
1359e5c33d7SMark Salter prev_size[slot] = size;
1369e5c33d7SMark Salter /*
1379e5c33d7SMark Salter * Mappings have to be page-aligned
1389e5c33d7SMark Salter */
1395d57b014SAlexander Kuleshov offset = offset_in_page(phys_addr);
1409e5c33d7SMark Salter phys_addr &= PAGE_MASK;
1419e5c33d7SMark Salter size = PAGE_ALIGN(last_addr + 1) - phys_addr;
1429e5c33d7SMark Salter
1439e5c33d7SMark Salter /*
1449e5c33d7SMark Salter * Mappings have to fit in the FIX_BTMAP area.
1459e5c33d7SMark Salter */
1469e5c33d7SMark Salter nrpages = size >> PAGE_SHIFT;
1479e5c33d7SMark Salter if (WARN_ON(nrpages > NR_FIX_BTMAPS))
1489e5c33d7SMark Salter return NULL;
1499e5c33d7SMark Salter
150*5fd8391cSHou Wenlong early_ioremap_dbg("%s(%pa, %08lx) [%d] => %08lx + %08lx\n",
1510cc3197bSHou Wenlong __func__, &phys_addr, size, slot, slot_virt[slot], offset);
1520cc3197bSHou Wenlong
1539e5c33d7SMark Salter /*
1549e5c33d7SMark Salter * Ok, go for it..
1559e5c33d7SMark Salter */
1569e5c33d7SMark Salter idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot;
1579e5c33d7SMark Salter while (nrpages > 0) {
1589e5c33d7SMark Salter if (after_paging_init)
1599e5c33d7SMark Salter __late_set_fixmap(idx, phys_addr, prot);
1609e5c33d7SMark Salter else
1619e5c33d7SMark Salter __early_set_fixmap(idx, phys_addr, prot);
1629e5c33d7SMark Salter phys_addr += PAGE_SIZE;
1639e5c33d7SMark Salter --idx;
1649e5c33d7SMark Salter --nrpages;
1659e5c33d7SMark Salter }
1669e5c33d7SMark Salter
1679e5c33d7SMark Salter prev_map[slot] = (void __iomem *)(offset + slot_virt[slot]);
1689e5c33d7SMark Salter return prev_map[slot];
1699e5c33d7SMark Salter }
1709e5c33d7SMark Salter
early_iounmap(void __iomem * addr,unsigned long size)1719e5c33d7SMark Salter void __init early_iounmap(void __iomem *addr, unsigned long size)
1729e5c33d7SMark Salter {
1739e5c33d7SMark Salter unsigned long virt_addr;
1749e5c33d7SMark Salter unsigned long offset;
1759e5c33d7SMark Salter unsigned int nrpages;
1769e5c33d7SMark Salter enum fixed_addresses idx;
1779e5c33d7SMark Salter int i, slot;
1789e5c33d7SMark Salter
1799e5c33d7SMark Salter slot = -1;
1809e5c33d7SMark Salter for (i = 0; i < FIX_BTMAPS_SLOTS; i++) {
1819e5c33d7SMark Salter if (prev_map[i] == addr) {
1829e5c33d7SMark Salter slot = i;
1839e5c33d7SMark Salter break;
1849e5c33d7SMark Salter }
1859e5c33d7SMark Salter }
1869e5c33d7SMark Salter
18787005394SStephen Zhang if (WARN(slot < 0, "%s(%p, %08lx) not found slot\n",
18887005394SStephen Zhang __func__, addr, size))
1899e5c33d7SMark Salter return;
1909e5c33d7SMark Salter
1919e5c33d7SMark Salter if (WARN(prev_size[slot] != size,
19287005394SStephen Zhang "%s(%p, %08lx) [%d] size not consistent %08lx\n",
19387005394SStephen Zhang __func__, addr, size, slot, prev_size[slot]))
1949e5c33d7SMark Salter return;
1959e5c33d7SMark Salter
196*5fd8391cSHou Wenlong early_ioremap_dbg("%s(%p, %08lx) [%d]\n", __func__, addr, size, slot);
1979e5c33d7SMark Salter
1989e5c33d7SMark Salter virt_addr = (unsigned long)addr;
1999e5c33d7SMark Salter if (WARN_ON(virt_addr < fix_to_virt(FIX_BTMAP_BEGIN)))
2009e5c33d7SMark Salter return;
2019e5c33d7SMark Salter
2025d57b014SAlexander Kuleshov offset = offset_in_page(virt_addr);
2039e5c33d7SMark Salter nrpages = PAGE_ALIGN(offset + size) >> PAGE_SHIFT;
2049e5c33d7SMark Salter
2059e5c33d7SMark Salter idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot;
2069e5c33d7SMark Salter while (nrpages > 0) {
2079e5c33d7SMark Salter if (after_paging_init)
2089e5c33d7SMark Salter __late_clear_fixmap(idx);
2099e5c33d7SMark Salter else
2109e5c33d7SMark Salter __early_set_fixmap(idx, 0, FIXMAP_PAGE_CLEAR);
2119e5c33d7SMark Salter --idx;
2129e5c33d7SMark Salter --nrpages;
2139e5c33d7SMark Salter }
2149e5c33d7SMark Salter prev_map[slot] = NULL;
2159e5c33d7SMark Salter }
2169e5c33d7SMark Salter
2179e5c33d7SMark Salter /* Remap an IO device */
2189e5c33d7SMark Salter void __init __iomem *
early_ioremap(resource_size_t phys_addr,unsigned long size)2199e5c33d7SMark Salter early_ioremap(resource_size_t phys_addr, unsigned long size)
2209e5c33d7SMark Salter {
2219e5c33d7SMark Salter return __early_ioremap(phys_addr, size, FIXMAP_PAGE_IO);
2229e5c33d7SMark Salter }
2239e5c33d7SMark Salter
2249e5c33d7SMark Salter /* Remap memory */
2259e5c33d7SMark Salter void __init *
early_memremap(resource_size_t phys_addr,unsigned long size)2269e5c33d7SMark Salter early_memremap(resource_size_t phys_addr, unsigned long size)
2279e5c33d7SMark Salter {
2288f716c9bSTom Lendacky pgprot_t prot = early_memremap_pgprot_adjust(phys_addr, size,
2299e5c33d7SMark Salter FIXMAP_PAGE_NORMAL);
2308f716c9bSTom Lendacky
2318f716c9bSTom Lendacky return (__force void *)__early_ioremap(phys_addr, size, prot);
2329e5c33d7SMark Salter }
2332592dbbbSJuergen Gross #ifdef FIXMAP_PAGE_RO
2342592dbbbSJuergen Gross void __init *
early_memremap_ro(resource_size_t phys_addr,unsigned long size)2352592dbbbSJuergen Gross early_memremap_ro(resource_size_t phys_addr, unsigned long size)
2362592dbbbSJuergen Gross {
2378f716c9bSTom Lendacky pgprot_t prot = early_memremap_pgprot_adjust(phys_addr, size,
2388f716c9bSTom Lendacky FIXMAP_PAGE_RO);
2398f716c9bSTom Lendacky
2408f716c9bSTom Lendacky return (__force void *)__early_ioremap(phys_addr, size, prot);
2412592dbbbSJuergen Gross }
2422592dbbbSJuergen Gross #endif
2436b0f68e3SMark Salter
244f88a68faSTom Lendacky #ifdef CONFIG_ARCH_USE_MEMREMAP_PROT
245f88a68faSTom Lendacky void __init *
early_memremap_prot(resource_size_t phys_addr,unsigned long size,unsigned long prot_val)246f88a68faSTom Lendacky early_memremap_prot(resource_size_t phys_addr, unsigned long size,
247f88a68faSTom Lendacky unsigned long prot_val)
248f88a68faSTom Lendacky {
249f88a68faSTom Lendacky return (__force void *)__early_ioremap(phys_addr, size,
250f88a68faSTom Lendacky __pgprot(prot_val));
251f88a68faSTom Lendacky }
252f88a68faSTom Lendacky #endif
253f88a68faSTom Lendacky
2546b0f68e3SMark Salter #define MAX_MAP_CHUNK (NR_FIX_BTMAPS << PAGE_SHIFT)
2556b0f68e3SMark Salter
256ccd58205SGuo Weikang /*
257ccd58205SGuo Weikang * If no empty slot, handle that and return -ENOMEM.
258ccd58205SGuo Weikang */
copy_from_early_mem(void * dest,phys_addr_t src,unsigned long size)259ccd58205SGuo Weikang int __init copy_from_early_mem(void *dest, phys_addr_t src, unsigned long size)
2606b0f68e3SMark Salter {
2616b0f68e3SMark Salter unsigned long slop, clen;
2626b0f68e3SMark Salter char *p;
2636b0f68e3SMark Salter
2646b0f68e3SMark Salter while (size) {
2655d57b014SAlexander Kuleshov slop = offset_in_page(src);
2666b0f68e3SMark Salter clen = size;
2676b0f68e3SMark Salter if (clen > MAX_MAP_CHUNK - slop)
2686b0f68e3SMark Salter clen = MAX_MAP_CHUNK - slop;
2696b0f68e3SMark Salter p = early_memremap(src & PAGE_MASK, clen + slop);
270ccd58205SGuo Weikang if (!p)
271ccd58205SGuo Weikang return -ENOMEM;
2726b0f68e3SMark Salter memcpy(dest, p + slop, clen);
2736b0f68e3SMark Salter early_memunmap(p, clen + slop);
2746b0f68e3SMark Salter dest += clen;
2756b0f68e3SMark Salter src += clen;
2766b0f68e3SMark Salter size -= clen;
2776b0f68e3SMark Salter }
278ccd58205SGuo Weikang return 0;
2796b0f68e3SMark Salter }
2806b0f68e3SMark Salter
2819e5c33d7SMark Salter #else /* CONFIG_MMU */
2829e5c33d7SMark Salter
2839e5c33d7SMark Salter void __init __iomem *
early_ioremap(resource_size_t phys_addr,unsigned long size)2849e5c33d7SMark Salter early_ioremap(resource_size_t phys_addr, unsigned long size)
2859e5c33d7SMark Salter {
2869e5c33d7SMark Salter return (__force void __iomem *)phys_addr;
2879e5c33d7SMark Salter }
2889e5c33d7SMark Salter
2899e5c33d7SMark Salter /* Remap memory */
2909e5c33d7SMark Salter void __init *
early_memremap(resource_size_t phys_addr,unsigned long size)2919e5c33d7SMark Salter early_memremap(resource_size_t phys_addr, unsigned long size)
2929e5c33d7SMark Salter {
2939e5c33d7SMark Salter return (void *)phys_addr;
2949e5c33d7SMark Salter }
2952592dbbbSJuergen Gross void __init *
early_memremap_ro(resource_size_t phys_addr,unsigned long size)2962592dbbbSJuergen Gross early_memremap_ro(resource_size_t phys_addr, unsigned long size)
2972592dbbbSJuergen Gross {
2982592dbbbSJuergen Gross return (void *)phys_addr;
2992592dbbbSJuergen Gross }
3009e5c33d7SMark Salter
early_iounmap(void __iomem * addr,unsigned long size)3019e5c33d7SMark Salter void __init early_iounmap(void __iomem *addr, unsigned long size)
3029e5c33d7SMark Salter {
3039e5c33d7SMark Salter }
3049e5c33d7SMark Salter
3059e5c33d7SMark Salter #endif /* CONFIG_MMU */
3069e5c33d7SMark Salter
3079e5c33d7SMark Salter
early_memunmap(void * addr,unsigned long size)3089e5c33d7SMark Salter void __init early_memunmap(void *addr, unsigned long size)
3099e5c33d7SMark Salter {
3109e5c33d7SMark Salter early_iounmap((__force void __iomem *)addr, size);
3119e5c33d7SMark Salter }
312