xref: /openbmc/linux/arch/um/kernel/physmem.c (revision a6ea4cce)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
31da177e4SLinus Torvalds  * Licensed under the GPL
41da177e4SLinus Torvalds  */
51da177e4SLinus Torvalds 
61da177e4SLinus Torvalds #include "linux/mm.h"
71da177e4SLinus Torvalds #include "linux/rbtree.h"
81da177e4SLinus Torvalds #include "linux/slab.h"
91da177e4SLinus Torvalds #include "linux/vmalloc.h"
101da177e4SLinus Torvalds #include "linux/bootmem.h"
111da177e4SLinus Torvalds #include "linux/module.h"
1222a9835cSDave Hansen #include "linux/pfn.h"
131da177e4SLinus Torvalds #include "asm/types.h"
141da177e4SLinus Torvalds #include "asm/pgtable.h"
151da177e4SLinus Torvalds #include "kern_util.h"
164ff83ce1SJeff Dike #include "as-layout.h"
171da177e4SLinus Torvalds #include "mode_kern.h"
181da177e4SLinus Torvalds #include "mem.h"
191da177e4SLinus Torvalds #include "mem_user.h"
201da177e4SLinus Torvalds #include "os.h"
211da177e4SLinus Torvalds #include "kern.h"
221da177e4SLinus Torvalds #include "init.h"
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds struct phys_desc {
251da177e4SLinus Torvalds 	struct rb_node rb;
261da177e4SLinus Torvalds 	int fd;
271da177e4SLinus Torvalds 	__u64 offset;
281da177e4SLinus Torvalds 	void *virt;
291da177e4SLinus Torvalds 	unsigned long phys;
301da177e4SLinus Torvalds 	struct list_head list;
311da177e4SLinus Torvalds };
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds static struct rb_root phys_mappings = RB_ROOT;
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds static struct rb_node **find_rb(void *virt)
361da177e4SLinus Torvalds {
371da177e4SLinus Torvalds 	struct rb_node **n = &phys_mappings.rb_node;
381da177e4SLinus Torvalds 	struct phys_desc *d;
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds 	while(*n != NULL){
411da177e4SLinus Torvalds 		d = rb_entry(*n, struct phys_desc, rb);
421da177e4SLinus Torvalds 		if(d->virt == virt)
4360678bbcSJeff Dike 			return n;
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds 		if(d->virt > virt)
461da177e4SLinus Torvalds 			n = &(*n)->rb_left;
471da177e4SLinus Torvalds 		else
481da177e4SLinus Torvalds 			n = &(*n)->rb_right;
491da177e4SLinus Torvalds 	}
501da177e4SLinus Torvalds 
5160678bbcSJeff Dike 	return n;
521da177e4SLinus Torvalds }
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds static struct phys_desc *find_phys_mapping(void *virt)
551da177e4SLinus Torvalds {
561da177e4SLinus Torvalds 	struct rb_node **n = find_rb(virt);
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 	if(*n == NULL)
5960678bbcSJeff Dike 		return NULL;
601da177e4SLinus Torvalds 
6160678bbcSJeff Dike 	return rb_entry(*n, struct phys_desc, rb);
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds static void insert_phys_mapping(struct phys_desc *desc)
651da177e4SLinus Torvalds {
661da177e4SLinus Torvalds 	struct rb_node **n = find_rb(desc->virt);
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds 	if(*n != NULL)
691da177e4SLinus Torvalds 		panic("Physical remapping for %p already present",
701da177e4SLinus Torvalds 		      desc->virt);
711da177e4SLinus Torvalds 
72aa783a8fSAndrew Morton 	rb_link_node(&desc->rb, rb_parent(*n), n);
731da177e4SLinus Torvalds 	rb_insert_color(&desc->rb, &phys_mappings);
741da177e4SLinus Torvalds }
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds LIST_HEAD(descriptor_mappings);
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds struct desc_mapping {
791da177e4SLinus Torvalds 	int fd;
801da177e4SLinus Torvalds 	struct list_head list;
811da177e4SLinus Torvalds 	struct list_head pages;
821da177e4SLinus Torvalds };
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds static struct desc_mapping *find_mapping(int fd)
851da177e4SLinus Torvalds {
861da177e4SLinus Torvalds 	struct desc_mapping *desc;
871da177e4SLinus Torvalds 	struct list_head *ele;
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 	list_for_each(ele, &descriptor_mappings){
901da177e4SLinus Torvalds 		desc = list_entry(ele, struct desc_mapping, list);
911da177e4SLinus Torvalds 		if(desc->fd == fd)
9260678bbcSJeff Dike 			return desc;
931da177e4SLinus Torvalds 	}
941da177e4SLinus Torvalds 
9560678bbcSJeff Dike 	return NULL;
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds static struct desc_mapping *descriptor_mapping(int fd)
991da177e4SLinus Torvalds {
1001da177e4SLinus Torvalds 	struct desc_mapping *desc;
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds 	desc = find_mapping(fd);
1031da177e4SLinus Torvalds 	if(desc != NULL)
10460678bbcSJeff Dike 		return desc;
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds 	desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
1071da177e4SLinus Torvalds 	if(desc == NULL)
10860678bbcSJeff Dike 		return NULL;
1091da177e4SLinus Torvalds 
1101da177e4SLinus Torvalds 	*desc = ((struct desc_mapping)
1111da177e4SLinus Torvalds 		{ .fd =		fd,
1121da177e4SLinus Torvalds 		  .list =	LIST_HEAD_INIT(desc->list),
1131da177e4SLinus Torvalds 		  .pages =	LIST_HEAD_INIT(desc->pages) });
1141da177e4SLinus Torvalds 	list_add(&desc->list, &descriptor_mappings);
1151da177e4SLinus Torvalds 
11660678bbcSJeff Dike 	return desc;
1171da177e4SLinus Torvalds }
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w)
1201da177e4SLinus Torvalds {
1211da177e4SLinus Torvalds 	struct desc_mapping *fd_maps;
1221da177e4SLinus Torvalds 	struct phys_desc *desc;
1231da177e4SLinus Torvalds 	unsigned long phys;
1241da177e4SLinus Torvalds 	int err;
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	fd_maps = descriptor_mapping(fd);
1271da177e4SLinus Torvalds 	if(fd_maps == NULL)
12860678bbcSJeff Dike 		return -ENOMEM;
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds 	phys = __pa(virt);
1311da177e4SLinus Torvalds 	desc = find_phys_mapping(virt);
1321da177e4SLinus Torvalds 	if(desc != NULL)
1331da177e4SLinus Torvalds 		panic("Address 0x%p is already substituted\n", virt);
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	err = -ENOMEM;
1361da177e4SLinus Torvalds 	desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
1371da177e4SLinus Torvalds 	if(desc == NULL)
1381da177e4SLinus Torvalds 		goto out;
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds 	*desc = ((struct phys_desc)
1411da177e4SLinus Torvalds 		{ .fd =			fd,
1421da177e4SLinus Torvalds 		  .offset =		offset,
1431da177e4SLinus Torvalds 		  .virt =		virt,
1441da177e4SLinus Torvalds 		  .phys =		__pa(virt),
1451da177e4SLinus Torvalds 		  .list = 		LIST_HEAD_INIT(desc->list) });
1461da177e4SLinus Torvalds 	insert_phys_mapping(desc);
1471da177e4SLinus Torvalds 
1481da177e4SLinus Torvalds 	list_add(&desc->list, &fd_maps->pages);
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds 	virt = (void *) ((unsigned long) virt & PAGE_MASK);
1511da177e4SLinus Torvalds 	err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0);
1521da177e4SLinus Torvalds 	if(!err)
1531da177e4SLinus Torvalds 		goto out;
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 	rb_erase(&desc->rb, &phys_mappings);
1561da177e4SLinus Torvalds 	kfree(desc);
1571da177e4SLinus Torvalds  out:
15860678bbcSJeff Dike 	return err;
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds static int physmem_fd = -1;
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds static void remove_mapping(struct phys_desc *desc)
1641da177e4SLinus Torvalds {
1651da177e4SLinus Torvalds 	void *virt = desc->virt;
1661da177e4SLinus Torvalds 	int err;
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds 	rb_erase(&desc->rb, &phys_mappings);
1691da177e4SLinus Torvalds 	list_del(&desc->list);
1701da177e4SLinus Torvalds 	kfree(desc);
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	err = os_map_memory(virt, physmem_fd, __pa(virt), PAGE_SIZE, 1, 1, 0);
1731da177e4SLinus Torvalds 	if(err)
1741da177e4SLinus Torvalds 		panic("Failed to unmap block device page from physical memory, "
1751da177e4SLinus Torvalds 		      "errno = %d", -err);
1761da177e4SLinus Torvalds }
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds int physmem_remove_mapping(void *virt)
1791da177e4SLinus Torvalds {
1801da177e4SLinus Torvalds 	struct phys_desc *desc;
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds 	virt = (void *) ((unsigned long) virt & PAGE_MASK);
1831da177e4SLinus Torvalds 	desc = find_phys_mapping(virt);
1841da177e4SLinus Torvalds 	if(desc == NULL)
18560678bbcSJeff Dike 		return 0;
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 	remove_mapping(desc);
18860678bbcSJeff Dike 	return 1;
1891da177e4SLinus Torvalds }
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds void physmem_forget_descriptor(int fd)
1921da177e4SLinus Torvalds {
1931da177e4SLinus Torvalds 	struct desc_mapping *desc;
1941da177e4SLinus Torvalds 	struct phys_desc *page;
1951da177e4SLinus Torvalds 	struct list_head *ele, *next;
1961da177e4SLinus Torvalds 	__u64 offset;
1971da177e4SLinus Torvalds 	void *addr;
1981da177e4SLinus Torvalds 	int err;
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds 	desc = find_mapping(fd);
2011da177e4SLinus Torvalds 	if(desc == NULL)
2021da177e4SLinus Torvalds 		return;
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	list_for_each_safe(ele, next, &desc->pages){
2051da177e4SLinus Torvalds 		page = list_entry(ele, struct phys_desc, list);
2061da177e4SLinus Torvalds 		offset = page->offset;
2071da177e4SLinus Torvalds 		addr = page->virt;
2081da177e4SLinus Torvalds 		remove_mapping(page);
2091da177e4SLinus Torvalds 		err = os_seek_file(fd, offset);
2101da177e4SLinus Torvalds 		if(err)
2111da177e4SLinus Torvalds 			panic("physmem_forget_descriptor - failed to seek "
2121da177e4SLinus Torvalds 			      "to %lld in fd %d, error = %d\n",
2131da177e4SLinus Torvalds 			      offset, fd, -err);
214a6ea4cceSJeff Dike 		err = os_read_file(fd, addr, PAGE_SIZE);
2151da177e4SLinus Torvalds 		if(err < 0)
2161da177e4SLinus Torvalds 			panic("physmem_forget_descriptor - failed to read "
2171da177e4SLinus Torvalds 			      "from fd %d to 0x%p, error = %d\n",
2181da177e4SLinus Torvalds 			      fd, addr, -err);
2191da177e4SLinus Torvalds 	}
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds 	list_del(&desc->list);
2221da177e4SLinus Torvalds 	kfree(desc);
2231da177e4SLinus Torvalds }
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds EXPORT_SYMBOL(physmem_forget_descriptor);
2261da177e4SLinus Torvalds EXPORT_SYMBOL(physmem_remove_mapping);
2271da177e4SLinus Torvalds EXPORT_SYMBOL(physmem_subst_mapping);
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds void arch_free_page(struct page *page, int order)
2301da177e4SLinus Torvalds {
2311da177e4SLinus Torvalds 	void *virt;
2321da177e4SLinus Torvalds 	int i;
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds 	for(i = 0; i < (1 << order); i++){
2351da177e4SLinus Torvalds 		virt = __va(page_to_phys(page + i));
2361da177e4SLinus Torvalds 		physmem_remove_mapping(virt);
2371da177e4SLinus Torvalds 	}
2381da177e4SLinus Torvalds }
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds int is_remapped(void *virt)
2411da177e4SLinus Torvalds {
2421da177e4SLinus Torvalds  	struct phys_desc *desc = find_phys_mapping(virt);
2431da177e4SLinus Torvalds 
24460678bbcSJeff Dike 	return desc != NULL;
2451da177e4SLinus Torvalds }
2461da177e4SLinus Torvalds 
2471da177e4SLinus Torvalds /* Changed during early boot */
2481da177e4SLinus Torvalds unsigned long high_physmem;
2491da177e4SLinus Torvalds 
250ae173816SJeff Dike extern unsigned long long physmem_size;
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem)
2531da177e4SLinus Torvalds {
2541da177e4SLinus Torvalds 	struct page *p, *map;
2551da177e4SLinus Torvalds 	unsigned long phys_len, phys_pages, highmem_len, highmem_pages;
2561da177e4SLinus Torvalds 	unsigned long iomem_len, iomem_pages, total_len, total_pages;
2571da177e4SLinus Torvalds 	int i;
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	phys_pages = physmem >> PAGE_SHIFT;
2601da177e4SLinus Torvalds 	phys_len = phys_pages * sizeof(struct page);
2611da177e4SLinus Torvalds 
2621da177e4SLinus Torvalds 	iomem_pages = iomem >> PAGE_SHIFT;
2631da177e4SLinus Torvalds 	iomem_len = iomem_pages * sizeof(struct page);
2641da177e4SLinus Torvalds 
2651da177e4SLinus Torvalds 	highmem_pages = highmem >> PAGE_SHIFT;
2661da177e4SLinus Torvalds 	highmem_len = highmem_pages * sizeof(struct page);
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds 	total_pages = phys_pages + iomem_pages + highmem_pages;
2693dfd95b3SPaolo 'Blaisorblade' Giarrusso 	total_len = phys_len + iomem_len + highmem_len;
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 	if(kmalloc_ok){
2721da177e4SLinus Torvalds 		map = kmalloc(total_len, GFP_KERNEL);
2731da177e4SLinus Torvalds 		if(map == NULL)
2741da177e4SLinus Torvalds 			map = vmalloc(total_len);
2751da177e4SLinus Torvalds 	}
2761da177e4SLinus Torvalds 	else map = alloc_bootmem_low_pages(total_len);
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 	if(map == NULL)
27960678bbcSJeff Dike 		return -ENOMEM;
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 	for(i = 0; i < total_pages; i++){
2821da177e4SLinus Torvalds 		p = &map[i];
28370dc991dSNick Piggin 		memset(p, 0, sizeof(struct page));
2841da177e4SLinus Torvalds 		SetPageReserved(p);
2851da177e4SLinus Torvalds 		INIT_LIST_HEAD(&p->lru);
2861da177e4SLinus Torvalds 	}
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds 	max_mapnr = total_pages;
28960678bbcSJeff Dike 	return 0;
2901da177e4SLinus Torvalds }
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds /* Changed during early boot */
2931da177e4SLinus Torvalds static unsigned long kmem_top = 0;
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds unsigned long get_kmem_end(void)
2961da177e4SLinus Torvalds {
2971da177e4SLinus Torvalds 	if(kmem_top == 0)
2981da177e4SLinus Torvalds 		kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas);
29960678bbcSJeff Dike 	return kmem_top;
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds void map_memory(unsigned long virt, unsigned long phys, unsigned long len,
3031da177e4SLinus Torvalds 		int r, int w, int x)
3041da177e4SLinus Torvalds {
3051da177e4SLinus Torvalds 	__u64 offset;
3061da177e4SLinus Torvalds 	int fd, err;
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds 	fd = phys_mapping(phys, &offset);
3091da177e4SLinus Torvalds 	err = os_map_memory((void *) virt, fd, offset, len, r, w, x);
3101da177e4SLinus Torvalds 	if(err) {
3111da177e4SLinus Torvalds 		if(err == -ENOMEM)
3121da177e4SLinus Torvalds 			printk("try increasing the host's "
3131da177e4SLinus Torvalds 			       "/proc/sys/vm/max_map_count to <physical "
3141da177e4SLinus Torvalds 			       "memory size>/4096\n");
3151da177e4SLinus Torvalds 		panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, "
3161da177e4SLinus Torvalds 		      "err = %d\n", virt, fd, offset, len, r, w, x, err);
3171da177e4SLinus Torvalds 	}
3181da177e4SLinus Torvalds }
3191da177e4SLinus Torvalds 
32023bbd586SJeff Dike extern int __syscall_stub_start;
321d67b569fSJeff Dike 
3221da177e4SLinus Torvalds void setup_physmem(unsigned long start, unsigned long reserve_end,
323ae173816SJeff Dike 		   unsigned long len, unsigned long long highmem)
3241da177e4SLinus Torvalds {
3251da177e4SLinus Torvalds 	unsigned long reserve = reserve_end - start;
3261da177e4SLinus Torvalds 	int pfn = PFN_UP(__pa(reserve_end));
3271da177e4SLinus Torvalds 	int delta = (len - reserve) >> PAGE_SHIFT;
3281da177e4SLinus Torvalds 	int err, offset, bootmap_size;
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds 	physmem_fd = create_mem_file(len + highmem);
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 	offset = uml_reserved - uml_physmem;
3331da177e4SLinus Torvalds 	err = os_map_memory((void *) uml_reserved, physmem_fd, offset,
3341da177e4SLinus Torvalds 			    len - offset, 1, 1, 0);
3351da177e4SLinus Torvalds 	if(err < 0){
3361da177e4SLinus Torvalds 		os_print_error(err, "Mapping memory");
3371da177e4SLinus Torvalds 		exit(1);
3381da177e4SLinus Torvalds 	}
3391da177e4SLinus Torvalds 
340d67b569fSJeff Dike 	/* Special kludge - This page will be mapped in to userspace processes
341d67b569fSJeff Dike 	 * from physmem_fd, so it needs to be written out there.
342d67b569fSJeff Dike 	 */
343d67b569fSJeff Dike 	os_seek_file(physmem_fd, __pa(&__syscall_stub_start));
344a6ea4cceSJeff Dike 	os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE);
345d67b569fSJeff Dike 
3461da177e4SLinus Torvalds 	bootmap_size = init_bootmem(pfn, pfn + delta);
3471da177e4SLinus Torvalds 	free_bootmem(__pa(reserve_end) + bootmap_size,
3481da177e4SLinus Torvalds 		     len - bootmap_size - reserve);
3491da177e4SLinus Torvalds }
3501da177e4SLinus Torvalds 
3511da177e4SLinus Torvalds int phys_mapping(unsigned long phys, __u64 *offset_out)
3521da177e4SLinus Torvalds {
3531da177e4SLinus Torvalds 	struct phys_desc *desc = find_phys_mapping(__va(phys & PAGE_MASK));
3541da177e4SLinus Torvalds 	int fd = -1;
3551da177e4SLinus Torvalds 
3561da177e4SLinus Torvalds 	if(desc != NULL){
3571da177e4SLinus Torvalds 		fd = desc->fd;
3581da177e4SLinus Torvalds 		*offset_out = desc->offset;
3591da177e4SLinus Torvalds 	}
3601da177e4SLinus Torvalds 	else if(phys < physmem_size){
3611da177e4SLinus Torvalds 		fd = physmem_fd;
3621da177e4SLinus Torvalds 		*offset_out = phys;
3631da177e4SLinus Torvalds 	}
3641da177e4SLinus Torvalds 	else if(phys < __pa(end_iomem)){
3651da177e4SLinus Torvalds 		struct iomem_region *region = iomem_regions;
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds 		while(region != NULL){
3681da177e4SLinus Torvalds 			if((phys >= region->phys) &&
3691da177e4SLinus Torvalds 			   (phys < region->phys + region->size)){
3701da177e4SLinus Torvalds 				fd = region->fd;
3711da177e4SLinus Torvalds 				*offset_out = phys - region->phys;
3721da177e4SLinus Torvalds 				break;
3731da177e4SLinus Torvalds 			}
3741da177e4SLinus Torvalds 			region = region->next;
3751da177e4SLinus Torvalds 		}
3761da177e4SLinus Torvalds 	}
3771da177e4SLinus Torvalds 	else if(phys < __pa(end_iomem) + highmem){
3781da177e4SLinus Torvalds 		fd = physmem_fd;
3791da177e4SLinus Torvalds 		*offset_out = phys - iomem_size;
3801da177e4SLinus Torvalds 	}
3811da177e4SLinus Torvalds 
38260678bbcSJeff Dike 	return fd;
3831da177e4SLinus Torvalds }
3841da177e4SLinus Torvalds 
3851da177e4SLinus Torvalds static int __init uml_mem_setup(char *line, int *add)
3861da177e4SLinus Torvalds {
3871da177e4SLinus Torvalds 	char *retptr;
3881da177e4SLinus Torvalds 	physmem_size = memparse(line,&retptr);
3891da177e4SLinus Torvalds 	return 0;
3901da177e4SLinus Torvalds }
3911da177e4SLinus Torvalds __uml_setup("mem=", uml_mem_setup,
3921da177e4SLinus Torvalds "mem=<Amount of desired ram>\n"
3931da177e4SLinus Torvalds "    This controls how much \"physical\" memory the kernel allocates\n"
3941da177e4SLinus Torvalds "    for the system. The size is specified as a number followed by\n"
3951da177e4SLinus Torvalds "    one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n"
3961da177e4SLinus Torvalds "    This is not related to the amount of memory in the host.  It can\n"
3971da177e4SLinus Torvalds "    be more, and the excess, if it's ever used, will just be swapped out.\n"
3981da177e4SLinus Torvalds "	Example: mem=64M\n\n"
3991da177e4SLinus Torvalds );
4001da177e4SLinus Torvalds 
40194c282d7SJeff Dike extern int __init parse_iomem(char *str, int *add);
40294c282d7SJeff Dike 
40394c282d7SJeff Dike __uml_setup("iomem=", parse_iomem,
40494c282d7SJeff Dike "iomem=<name>,<file>\n"
40594c282d7SJeff Dike "    Configure <file> as an IO memory region named <name>.\n\n"
40694c282d7SJeff Dike );
40794c282d7SJeff Dike 
40894c282d7SJeff Dike /*
40994c282d7SJeff Dike  * This list is constructed in parse_iomem and addresses filled in in
41094c282d7SJeff Dike  * setup_iomem, both of which run during early boot.  Afterwards, it's
41194c282d7SJeff Dike  * unchanged.
41294c282d7SJeff Dike  */
41394c282d7SJeff Dike struct iomem_region *iomem_regions = NULL;
41494c282d7SJeff Dike 
41594c282d7SJeff Dike /* Initialized in parse_iomem */
41694c282d7SJeff Dike int iomem_size = 0;
41794c282d7SJeff Dike 
4181da177e4SLinus Torvalds unsigned long find_iomem(char *driver, unsigned long *len_out)
4191da177e4SLinus Torvalds {
4201da177e4SLinus Torvalds 	struct iomem_region *region = iomem_regions;
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	while(region != NULL){
4231da177e4SLinus Torvalds 		if(!strcmp(region->driver, driver)){
4241da177e4SLinus Torvalds 			*len_out = region->size;
42560678bbcSJeff Dike 			return region->virt;
4261da177e4SLinus Torvalds 		}
427c39e50b4SVictor V. Vengerov 
428c39e50b4SVictor V. Vengerov 		region = region->next;
4291da177e4SLinus Torvalds 	}
4301da177e4SLinus Torvalds 
43160678bbcSJeff Dike 	return 0;
4321da177e4SLinus Torvalds }
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds int setup_iomem(void)
4351da177e4SLinus Torvalds {
4361da177e4SLinus Torvalds 	struct iomem_region *region = iomem_regions;
4371da177e4SLinus Torvalds 	unsigned long iomem_start = high_physmem + PAGE_SIZE;
4381da177e4SLinus Torvalds 	int err;
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds 	while(region != NULL){
4411da177e4SLinus Torvalds 		err = os_map_memory((void *) iomem_start, region->fd, 0,
4421da177e4SLinus Torvalds 				    region->size, 1, 1, 0);
4431da177e4SLinus Torvalds 		if(err)
4441da177e4SLinus Torvalds 			printk("Mapping iomem region for driver '%s' failed, "
4451da177e4SLinus Torvalds 			       "errno = %d\n", region->driver, -err);
4461da177e4SLinus Torvalds 		else {
4471da177e4SLinus Torvalds 			region->virt = iomem_start;
4481da177e4SLinus Torvalds 			region->phys = __pa(region->virt);
4491da177e4SLinus Torvalds 		}
4501da177e4SLinus Torvalds 
4511da177e4SLinus Torvalds 		iomem_start += region->size + PAGE_SIZE;
4521da177e4SLinus Torvalds 		region = region->next;
4531da177e4SLinus Torvalds 	}
4541da177e4SLinus Torvalds 
45560678bbcSJeff Dike 	return 0;
4561da177e4SLinus Torvalds }
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds __initcall(setup_iomem);
459