ioremap.c 11.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8
/*
 * Re-map IO memory to kernel address space so that we can access it.
 * This is needed for high PCI addresses that aren't mapped in the
 * 640k-1MB IO memory area on PC's
 *
 * (C) Copyright 1995 1996 Linus Torvalds
 */

9
#include <linux/bootmem.h>
L
Linus Torvalds 已提交
10
#include <linux/init.h>
11
#include <linux/io.h>
T
Thomas Gleixner 已提交
12 13 14 15
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>

L
Linus Torvalds 已提交
16
#include <asm/cacheflush.h>
T
Thomas Gleixner 已提交
17 18
#include <asm/e820.h>
#include <asm/fixmap.h>
L
Linus Torvalds 已提交
19
#include <asm/pgtable.h>
T
Thomas Gleixner 已提交
20
#include <asm/tlbflush.h>
L
Linus Torvalds 已提交
21

T
Thomas Gleixner 已提交
22 23 24 25 26 27 28 29 30 31 32 33
#ifdef CONFIG_X86_64

unsigned long __phys_addr(unsigned long x)
{
	if (x >= __START_KERNEL_map)
		return x - __START_KERNEL_map + phys_base;
	return x - PAGE_OFFSET;
}
EXPORT_SYMBOL(__phys_addr);

#endif

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/*
 * Fix up the linear direct mapping of the kernel to avoid cache attribute
 * conflicts.
 */
static int ioremap_change_attr(unsigned long phys_addr, unsigned long size,
			       pgprot_t prot)
{
	unsigned long npages, vaddr, last_addr = phys_addr + size - 1;
	int err, level;

	/* No change for pages after the last mapping */
	if (last_addr >= (max_pfn_mapped << PAGE_SHIFT))
		return 0;

	npages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
	vaddr = (unsigned long) __va(phys_addr);

	/*
	 * If there is no identity map for this address,
	 * change_page_attr_addr is unnecessary
	 */
	if (!lookup_address(vaddr, &level))
		return 0;

	/*
	 * Must use an address here and not struct page because the
	 * phys addr can be a in hole between nodes and not have a
	 * memmap entry.
	 */
	err = change_page_attr_addr(vaddr, npages, prot);
T
Thomas Gleixner 已提交
64

65 66 67 68 69 70
	if (!err)
		global_flush_tlb();

	return err;
}

L
Linus Torvalds 已提交
71 72 73 74 75 76 77 78 79
/*
 * Remap an arbitrary physical address space into the kernel virtual
 * address space. Needed when the kernel wants to access high addresses
 * directly.
 *
 * NOTE! We need to allow non-page-aligned mappings too: we will obviously
 * have to convert them into an offset in a page-aligned mapping, but the
 * caller shouldn't need to know that small detail.
 */
80 81
void __iomem *__ioremap(unsigned long phys_addr, unsigned long size,
			unsigned long flags)
L
Linus Torvalds 已提交
82
{
83 84
	void __iomem *addr;
	struct vm_struct *area;
L
Linus Torvalds 已提交
85
	unsigned long offset, last_addr;
86
	pgprot_t pgprot;
L
Linus Torvalds 已提交
87 88 89 90 91 92 93 94 95 96

	/* Don't allow wraparound or zero size */
	last_addr = phys_addr + size - 1;
	if (!size || last_addr < phys_addr)
		return NULL;

	/*
	 * Don't remap the low PCI/ISA area, it's always mapped..
	 */
	if (phys_addr >= ISA_START_ADDRESS && last_addr < ISA_END_ADDRESS)
T
Thomas Gleixner 已提交
97
		return (__force void __iomem *)phys_to_virt(phys_addr);
L
Linus Torvalds 已提交
98

T
Thomas Gleixner 已提交
99
#ifdef CONFIG_X86_32
L
Linus Torvalds 已提交
100 101 102 103 104 105 106 107 108
	/*
	 * Don't allow anybody to remap normal RAM that we're using..
	 */
	if (phys_addr <= virt_to_phys(high_memory - 1)) {
		char *t_addr, *t_end;
		struct page *page;

		t_addr = __va(phys_addr);
		t_end = t_addr + (size - 1);
109 110 111 112

		for (page = virt_to_page(t_addr);
		     page <= virt_to_page(t_end); page++)
			if (!PageReserved(page))
L
Linus Torvalds 已提交
113 114
				return NULL;
	}
T
Thomas Gleixner 已提交
115
#endif
L
Linus Torvalds 已提交
116

117
	pgprot = MAKE_GLOBAL(__PAGE_KERNEL | flags);
118

L
Linus Torvalds 已提交
119 120 121 122 123 124 125 126 127 128
	/*
	 * Mappings have to be page-aligned
	 */
	offset = phys_addr & ~PAGE_MASK;
	phys_addr &= PAGE_MASK;
	size = PAGE_ALIGN(last_addr+1) - phys_addr;

	/*
	 * Ok, go for it..
	 */
129
	area = get_vm_area(size, VM_IOREMAP);
L
Linus Torvalds 已提交
130 131 132 133
	if (!area)
		return NULL;
	area->phys_addr = phys_addr;
	addr = (void __iomem *) area->addr;
134 135
	if (ioremap_page_range((unsigned long)addr, (unsigned long)addr + size,
			       phys_addr, pgprot)) {
136
		remove_vm_area((void *)(PAGE_MASK & (unsigned long) addr));
L
Linus Torvalds 已提交
137 138
		return NULL;
	}
139 140 141 142 143 144

	if (ioremap_change_attr(phys_addr, size, pgprot) < 0) {
		vunmap(addr);
		return NULL;
	}

L
Linus Torvalds 已提交
145 146
	return (void __iomem *) (offset + (char __iomem *)addr);
}
147
EXPORT_SYMBOL(__ioremap);
L
Linus Torvalds 已提交
148 149 150 151 152 153 154 155 156 157

/**
 * ioremap_nocache     -   map bus memory into CPU space
 * @offset:    bus address of the memory
 * @size:      size of the resource to map
 *
 * ioremap_nocache performs a platform specific sequence of operations to
 * make bus memory CPU accessible via the readb/readw/readl/writeb/
 * writew/writel functions and the other mmio helpers. The returned
 * address is not guaranteed to be usable directly as a virtual
158
 * address.
L
Linus Torvalds 已提交
159 160 161
 *
 * This version of ioremap ensures that the memory is marked uncachable
 * on the CPU as well as honouring existing caching rules from things like
162
 * the PCI bus. Note that there are other caches and buffers on many
L
Linus Torvalds 已提交
163 164 165 166
 * busses. In particular driver authors should read up on PCI writes
 *
 * It's useful if some control registers are in such an area and
 * write combining or read caching is not desirable:
167
 *
L
Linus Torvalds 已提交
168 169
 * Must be freed with iounmap.
 */
170
void __iomem *ioremap_nocache(unsigned long phys_addr, unsigned long size)
L
Linus Torvalds 已提交
171
{
172
	return __ioremap(phys_addr, size, _PAGE_PCD | _PAGE_PWT);
L
Linus Torvalds 已提交
173
}
174
EXPORT_SYMBOL(ioremap_nocache);
L
Linus Torvalds 已提交
175

176 177 178 179 180 181
/**
 * iounmap - Free a IO remapping
 * @addr: virtual address from ioremap_*
 *
 * Caller must ensure there is only one unmapping for the same pointer.
 */
L
Linus Torvalds 已提交
182 183
void iounmap(volatile void __iomem *addr)
{
184
	struct vm_struct *p, *o;
A
Andrew Morton 已提交
185 186

	if ((void __force *)addr <= high_memory)
L
Linus Torvalds 已提交
187 188 189 190 191 192 193 194
		return;

	/*
	 * __ioremap special-cases the PCI/ISA range by not instantiating a
	 * vm_area and by simply returning an address into the kernel mapping
	 * of ISA space.   So handle that here.
	 */
	if (addr >= phys_to_virt(ISA_START_ADDRESS) &&
195
	    addr < phys_to_virt(ISA_END_ADDRESS))
L
Linus Torvalds 已提交
196 197
		return;

198 199
	addr = (volatile void __iomem *)
		(PAGE_MASK & (unsigned long __force)addr);
200 201 202 203 204 205 206 207 208 209 210 211 212 213

	/* Use the vm area unlocked, assuming the caller
	   ensures there isn't another iounmap for the same address
	   in parallel. Reuse of the virtual address is prevented by
	   leaving it in the global lists until we're done with it.
	   cpa takes care of the direct mappings. */
	read_lock(&vmlist_lock);
	for (p = vmlist; p; p = p->next) {
		if (p->addr == addr)
			break;
	}
	read_unlock(&vmlist_lock);

	if (!p) {
214
		printk(KERN_ERR "iounmap: bad address %p\n", addr);
A
Andrew Morton 已提交
215
		dump_stack();
216
		return;
L
Linus Torvalds 已提交
217 218
	}

219
	/* Reset the direct mapping. Can block */
220
	ioremap_change_attr(p->phys_addr, p->size, PAGE_KERNEL);
221 222 223 224

	/* Finally remove it */
	o = remove_vm_area((void *)addr);
	BUG_ON(p != o || o == NULL);
225
	kfree(p);
L
Linus Torvalds 已提交
226
}
227
EXPORT_SYMBOL(iounmap);
L
Linus Torvalds 已提交
228

T
Thomas Gleixner 已提交
229
#ifdef CONFIG_X86_32
I
Ingo Molnar 已提交
230 231 232 233 234 235 236

int __initdata early_ioremap_debug;

static int __init early_ioremap_debug_setup(char *str)
{
	early_ioremap_debug = 1;

237
	return 0;
I
Ingo Molnar 已提交
238
}
239
early_param("early_ioremap_debug", early_ioremap_debug_setup);
I
Ingo Molnar 已提交
240

241 242 243 244
static __initdata int after_paging_init;
static __initdata unsigned long bm_pte[1024]
				__attribute__((aligned(PAGE_SIZE)));

245
static inline unsigned long * __init early_ioremap_pgd(unsigned long addr)
246 247 248 249
{
	return (unsigned long *)swapper_pg_dir + ((addr >> 22) & 1023);
}

250
static inline unsigned long * __init early_ioremap_pte(unsigned long addr)
251 252 253 254
{
	return bm_pte + ((addr >> PAGE_SHIFT) & 1023);
}

255
void __init early_ioremap_init(void)
256 257 258
{
	unsigned long *pgd;

I
Ingo Molnar 已提交
259
	if (early_ioremap_debug)
260
		printk(KERN_DEBUG "early_ioremap_init()\n");
I
Ingo Molnar 已提交
261

262
	pgd = early_ioremap_pgd(fix_to_virt(FIX_BTMAP_BEGIN));
263 264
	*pgd = __pa(bm_pte) | _PAGE_TABLE;
	memset(bm_pte, 0, sizeof(bm_pte));
265 266 267 268 269 270
	/*
	 * The boot-ioremap range spans multiple pgds, for which
	 * we are not prepared:
	 */
	if (pgd != early_ioremap_pgd(fix_to_virt(FIX_BTMAP_END))) {
		WARN_ON(1);
271 272 273 274 275 276 277 278 279 280
		printk(KERN_WARNING "pgd %p != %p\n",
		       pgd, early_ioremap_pgd(fix_to_virt(FIX_BTMAP_END)));
		printk(KERN_WARNING "fix_to_virt(FIX_BTMAP_BEGIN): %08lx\n",
		       fix_to_virt(FIX_BTMAP_BEGIN));
		printk(KERN_WARNING "fix_to_virt(FIX_BTMAP_END):   %08lx\n",
		       fix_to_virt(FIX_BTMAP_END));

		printk(KERN_WARNING "FIX_BTMAP_END:       %d\n", FIX_BTMAP_END);
		printk(KERN_WARNING "FIX_BTMAP_BEGIN:     %d\n",
		       FIX_BTMAP_BEGIN);
281
	}
282 283
}

284
void __init early_ioremap_clear(void)
285 286 287
{
	unsigned long *pgd;

I
Ingo Molnar 已提交
288
	if (early_ioremap_debug)
289
		printk(KERN_DEBUG "early_ioremap_clear()\n");
I
Ingo Molnar 已提交
290

291
	pgd = early_ioremap_pgd(fix_to_virt(FIX_BTMAP_BEGIN));
292 293 294 295
	*pgd = 0;
	__flush_tlb_all();
}

296
void __init early_ioremap_reset(void)
297 298 299 300 301
{
	enum fixed_addresses idx;
	unsigned long *pte, phys, addr;

	after_paging_init = 1;
H
Huang, Ying 已提交
302
	for (idx = FIX_BTMAP_BEGIN; idx >= FIX_BTMAP_END; idx--) {
303
		addr = fix_to_virt(idx);
304
		pte = early_ioremap_pte(addr);
305 306 307 308 309 310 311
		if (!*pte & _PAGE_PRESENT) {
			phys = *pte & PAGE_MASK;
			set_fixmap(idx, phys);
		}
	}
}

312
static void __init __early_set_fixmap(enum fixed_addresses idx,
313 314 315 316 317 318 319 320
				   unsigned long phys, pgprot_t flags)
{
	unsigned long *pte, addr = __fix_to_virt(idx);

	if (idx >= __end_of_fixed_addresses) {
		BUG();
		return;
	}
321
	pte = early_ioremap_pte(addr);
322 323 324 325 326 327 328
	if (pgprot_val(flags))
		*pte = (phys & PAGE_MASK) | pgprot_val(flags);
	else
		*pte = 0;
	__flush_tlb_one(addr);
}

329
static inline void __init early_set_fixmap(enum fixed_addresses idx,
330 331 332 333 334
					unsigned long phys)
{
	if (after_paging_init)
		set_fixmap(idx, phys);
	else
335
		__early_set_fixmap(idx, phys, PAGE_KERNEL);
336 337
}

338
static inline void __init early_clear_fixmap(enum fixed_addresses idx)
339 340 341 342
{
	if (after_paging_init)
		clear_fixmap(idx);
	else
343
		__early_set_fixmap(idx, 0, __pgprot(0));
344 345
}

I
Ingo Molnar 已提交
346 347 348

int __initdata early_ioremap_nested;

349 350 351 352 353 354
static int __init check_early_ioremap_leak(void)
{
	if (!early_ioremap_nested)
		return 0;

	printk(KERN_WARNING
355 356
	       "Debug warning: early ioremap leak of %d areas detected.\n",
	       early_ioremap_nested);
357
	printk(KERN_WARNING
358
	       "please boot with early_ioremap_debug and report the dmesg.\n");
359 360 361 362 363 364
	WARN_ON(1);

	return 1;
}
late_initcall(check_early_ioremap_leak);

365
void __init *early_ioremap(unsigned long phys_addr, unsigned long size)
L
Linus Torvalds 已提交
366 367
{
	unsigned long offset, last_addr;
I
Ingo Molnar 已提交
368 369 370 371 372 373
	unsigned int nrpages, nesting;
	enum fixed_addresses idx0, idx;

	WARN_ON(system_state != SYSTEM_BOOTING);

	nesting = early_ioremap_nested;
I
Ingo Molnar 已提交
374
	if (early_ioremap_debug) {
375 376
		printk(KERN_DEBUG "early_ioremap(%08lx, %08lx) [%d] => ",
		       phys_addr, size, nesting);
I
Ingo Molnar 已提交
377 378
		dump_stack();
	}
L
Linus Torvalds 已提交
379 380 381

	/* Don't allow wraparound or zero size */
	last_addr = phys_addr + size - 1;
382 383
	if (!size || last_addr < phys_addr) {
		WARN_ON(1);
L
Linus Torvalds 已提交
384
		return NULL;
385
	}
L
Linus Torvalds 已提交
386

387 388
	if (nesting >= FIX_BTMAPS_NESTING) {
		WARN_ON(1);
I
Ingo Molnar 已提交
389
		return NULL;
390
	}
I
Ingo Molnar 已提交
391
	early_ioremap_nested++;
L
Linus Torvalds 已提交
392 393 394 395 396 397 398 399 400 401 402
	/*
	 * Mappings have to be page-aligned
	 */
	offset = phys_addr & ~PAGE_MASK;
	phys_addr &= PAGE_MASK;
	size = PAGE_ALIGN(last_addr) - phys_addr;

	/*
	 * Mappings have to fit in the FIX_BTMAP area.
	 */
	nrpages = size >> PAGE_SHIFT;
403 404
	if (nrpages > NR_FIX_BTMAPS) {
		WARN_ON(1);
L
Linus Torvalds 已提交
405
		return NULL;
406
	}
L
Linus Torvalds 已提交
407 408 409 410

	/*
	 * Ok, go for it..
	 */
I
Ingo Molnar 已提交
411 412
	idx0 = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*nesting;
	idx = idx0;
L
Linus Torvalds 已提交
413
	while (nrpages > 0) {
414
		early_set_fixmap(idx, phys_addr);
L
Linus Torvalds 已提交
415 416 417 418
		phys_addr += PAGE_SIZE;
		--idx;
		--nrpages;
	}
I
Ingo Molnar 已提交
419 420
	if (early_ioremap_debug)
		printk(KERN_CONT "%08lx + %08lx\n", offset, fix_to_virt(idx0));
I
Ingo Molnar 已提交
421

422
	return (void *) (offset + fix_to_virt(idx0));
L
Linus Torvalds 已提交
423 424
}

425
void __init early_iounmap(void *addr, unsigned long size)
L
Linus Torvalds 已提交
426 427 428 429 430
{
	unsigned long virt_addr;
	unsigned long offset;
	unsigned int nrpages;
	enum fixed_addresses idx;
I
Ingo Molnar 已提交
431 432 433
	unsigned int nesting;

	nesting = --early_ioremap_nested;
434
	WARN_ON(nesting < 0);
L
Linus Torvalds 已提交
435

I
Ingo Molnar 已提交
436
	if (early_ioremap_debug) {
437 438
		printk(KERN_DEBUG "early_iounmap(%p, %08lx) [%d]\n", addr,
		       size, nesting);
I
Ingo Molnar 已提交
439 440 441
		dump_stack();
	}

L
Linus Torvalds 已提交
442
	virt_addr = (unsigned long)addr;
443 444
	if (virt_addr < fix_to_virt(FIX_BTMAP_BEGIN)) {
		WARN_ON(1);
L
Linus Torvalds 已提交
445
		return;
446
	}
L
Linus Torvalds 已提交
447 448 449
	offset = virt_addr & ~PAGE_MASK;
	nrpages = PAGE_ALIGN(offset + size - 1) >> PAGE_SHIFT;

I
Ingo Molnar 已提交
450
	idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*nesting;
L
Linus Torvalds 已提交
451
	while (nrpages > 0) {
452
		early_clear_fixmap(idx);
L
Linus Torvalds 已提交
453 454 455 456
		--idx;
		--nrpages;
	}
}
I
Ingo Molnar 已提交
457 458 459 460 461

void __this_fixmap_does_not_exist(void)
{
	WARN_ON(1);
}
T
Thomas Gleixner 已提交
462 463

#endif /* CONFIG_X86_32 */