xref: /openbmc/linux/arch/um/kernel/physmem.c (revision ae173816)
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"
121da177e4SLinus Torvalds #include "asm/types.h"
131da177e4SLinus Torvalds #include "asm/pgtable.h"
141da177e4SLinus Torvalds #include "kern_util.h"
151da177e4SLinus Torvalds #include "user_util.h"
161da177e4SLinus Torvalds #include "mode_kern.h"
171da177e4SLinus Torvalds #include "mem.h"
181da177e4SLinus Torvalds #include "mem_user.h"
191da177e4SLinus Torvalds #include "os.h"
201da177e4SLinus Torvalds #include "kern.h"
211da177e4SLinus Torvalds #include "init.h"
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds struct phys_desc {
241da177e4SLinus Torvalds 	struct rb_node rb;
251da177e4SLinus Torvalds 	int fd;
261da177e4SLinus Torvalds 	__u64 offset;
271da177e4SLinus Torvalds 	void *virt;
281da177e4SLinus Torvalds 	unsigned long phys;
291da177e4SLinus Torvalds 	struct list_head list;
301da177e4SLinus Torvalds };
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds static struct rb_root phys_mappings = RB_ROOT;
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds static struct rb_node **find_rb(void *virt)
351da177e4SLinus Torvalds {
361da177e4SLinus Torvalds 	struct rb_node **n = &phys_mappings.rb_node;
371da177e4SLinus Torvalds 	struct phys_desc *d;
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds 	while(*n != NULL){
401da177e4SLinus Torvalds 		d = rb_entry(*n, struct phys_desc, rb);
411da177e4SLinus Torvalds 		if(d->virt == virt)
421da177e4SLinus Torvalds 			return(n);
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds 		if(d->virt > virt)
451da177e4SLinus Torvalds 			n = &(*n)->rb_left;
461da177e4SLinus Torvalds 		else
471da177e4SLinus Torvalds 			n = &(*n)->rb_right;
481da177e4SLinus Torvalds 	}
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds 	return(n);
511da177e4SLinus Torvalds }
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds static struct phys_desc *find_phys_mapping(void *virt)
541da177e4SLinus Torvalds {
551da177e4SLinus Torvalds 	struct rb_node **n = find_rb(virt);
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 	if(*n == NULL)
581da177e4SLinus Torvalds 		return(NULL);
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	return(rb_entry(*n, struct phys_desc, rb));
611da177e4SLinus Torvalds }
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds static void insert_phys_mapping(struct phys_desc *desc)
641da177e4SLinus Torvalds {
651da177e4SLinus Torvalds 	struct rb_node **n = find_rb(desc->virt);
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds 	if(*n != NULL)
681da177e4SLinus Torvalds 		panic("Physical remapping for %p already present",
691da177e4SLinus Torvalds 		      desc->virt);
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds 	rb_link_node(&desc->rb, (*n)->rb_parent, n);
721da177e4SLinus Torvalds 	rb_insert_color(&desc->rb, &phys_mappings);
731da177e4SLinus Torvalds }
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds LIST_HEAD(descriptor_mappings);
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds struct desc_mapping {
781da177e4SLinus Torvalds 	int fd;
791da177e4SLinus Torvalds 	struct list_head list;
801da177e4SLinus Torvalds 	struct list_head pages;
811da177e4SLinus Torvalds };
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds static struct desc_mapping *find_mapping(int fd)
841da177e4SLinus Torvalds {
851da177e4SLinus Torvalds 	struct desc_mapping *desc;
861da177e4SLinus Torvalds 	struct list_head *ele;
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 	list_for_each(ele, &descriptor_mappings){
891da177e4SLinus Torvalds 		desc = list_entry(ele, struct desc_mapping, list);
901da177e4SLinus Torvalds 		if(desc->fd == fd)
911da177e4SLinus Torvalds 			return(desc);
921da177e4SLinus Torvalds 	}
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds 	return(NULL);
951da177e4SLinus Torvalds }
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds static struct desc_mapping *descriptor_mapping(int fd)
981da177e4SLinus Torvalds {
991da177e4SLinus Torvalds 	struct desc_mapping *desc;
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	desc = find_mapping(fd);
1021da177e4SLinus Torvalds 	if(desc != NULL)
1031da177e4SLinus Torvalds 		return(desc);
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 	desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
1061da177e4SLinus Torvalds 	if(desc == NULL)
1071da177e4SLinus Torvalds 		return(NULL);
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 	*desc = ((struct desc_mapping)
1101da177e4SLinus Torvalds 		{ .fd =		fd,
1111da177e4SLinus Torvalds 		  .list =	LIST_HEAD_INIT(desc->list),
1121da177e4SLinus Torvalds 		  .pages =	LIST_HEAD_INIT(desc->pages) });
1131da177e4SLinus Torvalds 	list_add(&desc->list, &descriptor_mappings);
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds 	return(desc);
1161da177e4SLinus Torvalds }
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w)
1191da177e4SLinus Torvalds {
1201da177e4SLinus Torvalds 	struct desc_mapping *fd_maps;
1211da177e4SLinus Torvalds 	struct phys_desc *desc;
1221da177e4SLinus Torvalds 	unsigned long phys;
1231da177e4SLinus Torvalds 	int err;
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds 	fd_maps = descriptor_mapping(fd);
1261da177e4SLinus Torvalds 	if(fd_maps == NULL)
1271da177e4SLinus Torvalds 		return(-ENOMEM);
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 	phys = __pa(virt);
1301da177e4SLinus Torvalds 	desc = find_phys_mapping(virt);
1311da177e4SLinus Torvalds   	if(desc != NULL)
1321da177e4SLinus Torvalds 		panic("Address 0x%p is already substituted\n", virt);
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds 	err = -ENOMEM;
1351da177e4SLinus Torvalds 	desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
1361da177e4SLinus Torvalds 	if(desc == NULL)
1371da177e4SLinus Torvalds 		goto out;
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds 	*desc = ((struct phys_desc)
1401da177e4SLinus Torvalds 		{ .fd =			fd,
1411da177e4SLinus Torvalds 		  .offset =		offset,
1421da177e4SLinus Torvalds 		  .virt =		virt,
1431da177e4SLinus Torvalds 		  .phys =		__pa(virt),
1441da177e4SLinus Torvalds 		  .list = 		LIST_HEAD_INIT(desc->list) });
1451da177e4SLinus Torvalds 	insert_phys_mapping(desc);
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds 	list_add(&desc->list, &fd_maps->pages);
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds 	virt = (void *) ((unsigned long) virt & PAGE_MASK);
1501da177e4SLinus Torvalds 	err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0);
1511da177e4SLinus Torvalds 	if(!err)
1521da177e4SLinus Torvalds 		goto out;
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds 	rb_erase(&desc->rb, &phys_mappings);
1551da177e4SLinus Torvalds 	kfree(desc);
1561da177e4SLinus Torvalds  out:
1571da177e4SLinus Torvalds 	return(err);
1581da177e4SLinus Torvalds }
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds static int physmem_fd = -1;
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds static void remove_mapping(struct phys_desc *desc)
1631da177e4SLinus Torvalds {
1641da177e4SLinus Torvalds 	void *virt = desc->virt;
1651da177e4SLinus Torvalds 	int err;
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds 	rb_erase(&desc->rb, &phys_mappings);
1681da177e4SLinus Torvalds 	list_del(&desc->list);
1691da177e4SLinus Torvalds 	kfree(desc);
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds 	err = os_map_memory(virt, physmem_fd, __pa(virt), PAGE_SIZE, 1, 1, 0);
1721da177e4SLinus Torvalds 	if(err)
1731da177e4SLinus Torvalds 		panic("Failed to unmap block device page from physical memory, "
1741da177e4SLinus Torvalds 		      "errno = %d", -err);
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds int physmem_remove_mapping(void *virt)
1781da177e4SLinus Torvalds {
1791da177e4SLinus Torvalds 	struct phys_desc *desc;
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds 	virt = (void *) ((unsigned long) virt & PAGE_MASK);
1821da177e4SLinus Torvalds 	desc = find_phys_mapping(virt);
1831da177e4SLinus Torvalds 	if(desc == NULL)
1841da177e4SLinus Torvalds 		return(0);
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds 	remove_mapping(desc);
1871da177e4SLinus Torvalds 	return(1);
1881da177e4SLinus Torvalds }
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds void physmem_forget_descriptor(int fd)
1911da177e4SLinus Torvalds {
1921da177e4SLinus Torvalds 	struct desc_mapping *desc;
1931da177e4SLinus Torvalds 	struct phys_desc *page;
1941da177e4SLinus Torvalds 	struct list_head *ele, *next;
1951da177e4SLinus Torvalds 	__u64 offset;
1961da177e4SLinus Torvalds 	void *addr;
1971da177e4SLinus Torvalds 	int err;
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds 	desc = find_mapping(fd);
2001da177e4SLinus Torvalds 	if(desc == NULL)
2011da177e4SLinus Torvalds 		return;
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 	list_for_each_safe(ele, next, &desc->pages){
2041da177e4SLinus Torvalds 		page = list_entry(ele, struct phys_desc, list);
2051da177e4SLinus Torvalds 		offset = page->offset;
2061da177e4SLinus Torvalds 		addr = page->virt;
2071da177e4SLinus Torvalds 		remove_mapping(page);
2081da177e4SLinus Torvalds 		err = os_seek_file(fd, offset);
2091da177e4SLinus Torvalds 		if(err)
2101da177e4SLinus Torvalds 			panic("physmem_forget_descriptor - failed to seek "
2111da177e4SLinus Torvalds 			      "to %lld in fd %d, error = %d\n",
2121da177e4SLinus Torvalds 			      offset, fd, -err);
2131da177e4SLinus Torvalds 		err = os_read_file(fd, addr, PAGE_SIZE);
2141da177e4SLinus Torvalds 		if(err < 0)
2151da177e4SLinus Torvalds 			panic("physmem_forget_descriptor - failed to read "
2161da177e4SLinus Torvalds 			      "from fd %d to 0x%p, error = %d\n",
2171da177e4SLinus Torvalds 			      fd, addr, -err);
2181da177e4SLinus Torvalds 	}
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds 	list_del(&desc->list);
2211da177e4SLinus Torvalds 	kfree(desc);
2221da177e4SLinus Torvalds }
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds EXPORT_SYMBOL(physmem_forget_descriptor);
2251da177e4SLinus Torvalds EXPORT_SYMBOL(physmem_remove_mapping);
2261da177e4SLinus Torvalds EXPORT_SYMBOL(physmem_subst_mapping);
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds void arch_free_page(struct page *page, int order)
2291da177e4SLinus Torvalds {
2301da177e4SLinus Torvalds 	void *virt;
2311da177e4SLinus Torvalds 	int i;
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	for(i = 0; i < (1 << order); i++){
2341da177e4SLinus Torvalds 		virt = __va(page_to_phys(page + i));
2351da177e4SLinus Torvalds 		physmem_remove_mapping(virt);
2361da177e4SLinus Torvalds 	}
2371da177e4SLinus Torvalds }
2381da177e4SLinus Torvalds 
2391da177e4SLinus Torvalds int is_remapped(void *virt)
2401da177e4SLinus Torvalds {
2411da177e4SLinus Torvalds   	struct phys_desc *desc = find_phys_mapping(virt);
2421da177e4SLinus Torvalds 
2431da177e4SLinus Torvalds 	return(desc != NULL);
2441da177e4SLinus Torvalds }
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds /* Changed during early boot */
2471da177e4SLinus Torvalds unsigned long high_physmem;
2481da177e4SLinus Torvalds 
249ae173816SJeff Dike extern unsigned long long physmem_size;
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem)
2521da177e4SLinus Torvalds {
2531da177e4SLinus Torvalds 	struct page *p, *map;
2541da177e4SLinus Torvalds 	unsigned long phys_len, phys_pages, highmem_len, highmem_pages;
2551da177e4SLinus Torvalds 	unsigned long iomem_len, iomem_pages, total_len, total_pages;
2561da177e4SLinus Torvalds 	int i;
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds 	phys_pages = physmem >> PAGE_SHIFT;
2591da177e4SLinus Torvalds 	phys_len = phys_pages * sizeof(struct page);
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds 	iomem_pages = iomem >> PAGE_SHIFT;
2621da177e4SLinus Torvalds 	iomem_len = iomem_pages * sizeof(struct page);
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds 	highmem_pages = highmem >> PAGE_SHIFT;
2651da177e4SLinus Torvalds 	highmem_len = highmem_pages * sizeof(struct page);
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 	total_pages = phys_pages + iomem_pages + highmem_pages;
2681da177e4SLinus Torvalds 	total_len = phys_len + iomem_pages + highmem_len;
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds 	if(kmalloc_ok){
2711da177e4SLinus Torvalds 		map = kmalloc(total_len, GFP_KERNEL);
2721da177e4SLinus Torvalds 		if(map == NULL)
2731da177e4SLinus Torvalds 			map = vmalloc(total_len);
2741da177e4SLinus Torvalds 	}
2751da177e4SLinus Torvalds 	else map = alloc_bootmem_low_pages(total_len);
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds 	if(map == NULL)
2781da177e4SLinus Torvalds 		return(-ENOMEM);
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds 	for(i = 0; i < total_pages; i++){
2811da177e4SLinus Torvalds 		p = &map[i];
2821da177e4SLinus Torvalds 		set_page_count(p, 0);
2831da177e4SLinus Torvalds 		SetPageReserved(p);
2841da177e4SLinus Torvalds 		INIT_LIST_HEAD(&p->lru);
2851da177e4SLinus Torvalds 	}
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds 	max_mapnr = total_pages;
2881da177e4SLinus Torvalds 	return(0);
2891da177e4SLinus Torvalds }
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds /* Changed during early boot */
2921da177e4SLinus Torvalds static unsigned long kmem_top = 0;
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds unsigned long get_kmem_end(void)
2951da177e4SLinus Torvalds {
2961da177e4SLinus Torvalds 	if(kmem_top == 0)
2971da177e4SLinus Torvalds 		kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas);
2981da177e4SLinus Torvalds 	return(kmem_top);
2991da177e4SLinus Torvalds }
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds void map_memory(unsigned long virt, unsigned long phys, unsigned long len,
3021da177e4SLinus Torvalds 		int r, int w, int x)
3031da177e4SLinus Torvalds {
3041da177e4SLinus Torvalds 	__u64 offset;
3051da177e4SLinus Torvalds 	int fd, err;
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 	fd = phys_mapping(phys, &offset);
3081da177e4SLinus Torvalds 	err = os_map_memory((void *) virt, fd, offset, len, r, w, x);
3091da177e4SLinus Torvalds 	if(err) {
3101da177e4SLinus Torvalds 		if(err == -ENOMEM)
3111da177e4SLinus Torvalds 			printk("try increasing the host's "
3121da177e4SLinus Torvalds 			       "/proc/sys/vm/max_map_count to <physical "
3131da177e4SLinus Torvalds 			       "memory size>/4096\n");
3141da177e4SLinus Torvalds 		panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, "
3151da177e4SLinus Torvalds 		      "err = %d\n", virt, fd, offset, len, r, w, x, err);
3161da177e4SLinus Torvalds 	}
3171da177e4SLinus Torvalds }
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
3201da177e4SLinus Torvalds 
321d67b569fSJeff Dike extern int __syscall_stub_start, __binary_start;
322d67b569fSJeff Dike 
3231da177e4SLinus Torvalds void setup_physmem(unsigned long start, unsigned long reserve_end,
324ae173816SJeff Dike 		   unsigned long len, unsigned long long highmem)
3251da177e4SLinus Torvalds {
3261da177e4SLinus Torvalds 	unsigned long reserve = reserve_end - start;
3271da177e4SLinus Torvalds 	int pfn = PFN_UP(__pa(reserve_end));
3281da177e4SLinus Torvalds 	int delta = (len - reserve) >> PAGE_SHIFT;
3291da177e4SLinus Torvalds 	int err, offset, bootmap_size;
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds 	physmem_fd = create_mem_file(len + highmem);
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds 	offset = uml_reserved - uml_physmem;
3341da177e4SLinus Torvalds 	err = os_map_memory((void *) uml_reserved, physmem_fd, offset,
3351da177e4SLinus Torvalds 			    len - offset, 1, 1, 0);
3361da177e4SLinus Torvalds 	if(err < 0){
3371da177e4SLinus Torvalds 		os_print_error(err, "Mapping memory");
3381da177e4SLinus Torvalds 		exit(1);
3391da177e4SLinus Torvalds 	}
3401da177e4SLinus Torvalds 
341d67b569fSJeff Dike 	/* Special kludge - This page will be mapped in to userspace processes
342d67b569fSJeff Dike 	 * from physmem_fd, so it needs to be written out there.
343d67b569fSJeff Dike 	 */
344d67b569fSJeff Dike 	os_seek_file(physmem_fd, __pa(&__syscall_stub_start));
345d67b569fSJeff Dike 	os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE);
346d67b569fSJeff Dike 
3471da177e4SLinus Torvalds 	bootmap_size = init_bootmem(pfn, pfn + delta);
3481da177e4SLinus Torvalds 	free_bootmem(__pa(reserve_end) + bootmap_size,
3491da177e4SLinus Torvalds 		     len - bootmap_size - reserve);
3501da177e4SLinus Torvalds }
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds int phys_mapping(unsigned long phys, __u64 *offset_out)
3531da177e4SLinus Torvalds {
3541da177e4SLinus Torvalds 	struct phys_desc *desc = find_phys_mapping(__va(phys & PAGE_MASK));
3551da177e4SLinus Torvalds 	int fd = -1;
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds 	if(desc != NULL){
3581da177e4SLinus Torvalds 		fd = desc->fd;
3591da177e4SLinus Torvalds 		*offset_out = desc->offset;
3601da177e4SLinus Torvalds 	}
3611da177e4SLinus Torvalds 	else if(phys < physmem_size){
3621da177e4SLinus Torvalds 		fd = physmem_fd;
3631da177e4SLinus Torvalds 		*offset_out = phys;
3641da177e4SLinus Torvalds 	}
3651da177e4SLinus Torvalds 	else if(phys < __pa(end_iomem)){
3661da177e4SLinus Torvalds 		struct iomem_region *region = iomem_regions;
3671da177e4SLinus Torvalds 
3681da177e4SLinus Torvalds 		while(region != NULL){
3691da177e4SLinus Torvalds 			if((phys >= region->phys) &&
3701da177e4SLinus Torvalds 			   (phys < region->phys + region->size)){
3711da177e4SLinus Torvalds 				fd = region->fd;
3721da177e4SLinus Torvalds 				*offset_out = phys - region->phys;
3731da177e4SLinus Torvalds 				break;
3741da177e4SLinus Torvalds 			}
3751da177e4SLinus Torvalds 			region = region->next;
3761da177e4SLinus Torvalds 		}
3771da177e4SLinus Torvalds 	}
3781da177e4SLinus Torvalds 	else if(phys < __pa(end_iomem) + highmem){
3791da177e4SLinus Torvalds 		fd = physmem_fd;
3801da177e4SLinus Torvalds 		*offset_out = phys - iomem_size;
3811da177e4SLinus Torvalds 	}
3821da177e4SLinus Torvalds 
3831da177e4SLinus Torvalds 	return(fd);
3841da177e4SLinus Torvalds }
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds static int __init uml_mem_setup(char *line, int *add)
3871da177e4SLinus Torvalds {
3881da177e4SLinus Torvalds 	char *retptr;
3891da177e4SLinus Torvalds 	physmem_size = memparse(line,&retptr);
3901da177e4SLinus Torvalds 	return 0;
3911da177e4SLinus Torvalds }
3921da177e4SLinus Torvalds __uml_setup("mem=", uml_mem_setup,
3931da177e4SLinus Torvalds "mem=<Amount of desired ram>\n"
3941da177e4SLinus Torvalds "    This controls how much \"physical\" memory the kernel allocates\n"
3951da177e4SLinus Torvalds "    for the system. The size is specified as a number followed by\n"
3961da177e4SLinus Torvalds "    one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n"
3971da177e4SLinus Torvalds "    This is not related to the amount of memory in the host.  It can\n"
3981da177e4SLinus Torvalds "    be more, and the excess, if it's ever used, will just be swapped out.\n"
3991da177e4SLinus Torvalds "	Example: mem=64M\n\n"
4001da177e4SLinus Torvalds );
4011da177e4SLinus Torvalds 
4021da177e4SLinus Torvalds unsigned long find_iomem(char *driver, unsigned long *len_out)
4031da177e4SLinus Torvalds {
4041da177e4SLinus Torvalds 	struct iomem_region *region = iomem_regions;
4051da177e4SLinus Torvalds 
4061da177e4SLinus Torvalds 	while(region != NULL){
4071da177e4SLinus Torvalds 		if(!strcmp(region->driver, driver)){
4081da177e4SLinus Torvalds 			*len_out = region->size;
4091da177e4SLinus Torvalds 			return(region->virt);
4101da177e4SLinus Torvalds 		}
4111da177e4SLinus Torvalds 	}
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 	return(0);
4141da177e4SLinus Torvalds }
4151da177e4SLinus Torvalds 
4161da177e4SLinus Torvalds int setup_iomem(void)
4171da177e4SLinus Torvalds {
4181da177e4SLinus Torvalds 	struct iomem_region *region = iomem_regions;
4191da177e4SLinus Torvalds 	unsigned long iomem_start = high_physmem + PAGE_SIZE;
4201da177e4SLinus Torvalds 	int err;
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	while(region != NULL){
4231da177e4SLinus Torvalds 		err = os_map_memory((void *) iomem_start, region->fd, 0,
4241da177e4SLinus Torvalds 				    region->size, 1, 1, 0);
4251da177e4SLinus Torvalds 		if(err)
4261da177e4SLinus Torvalds 			printk("Mapping iomem region for driver '%s' failed, "
4271da177e4SLinus Torvalds 			       "errno = %d\n", region->driver, -err);
4281da177e4SLinus Torvalds 		else {
4291da177e4SLinus Torvalds 			region->virt = iomem_start;
4301da177e4SLinus Torvalds 			region->phys = __pa(region->virt);
4311da177e4SLinus Torvalds 		}
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 		iomem_start += region->size + PAGE_SIZE;
4341da177e4SLinus Torvalds 		region = region->next;
4351da177e4SLinus Torvalds 	}
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 	return(0);
4381da177e4SLinus Torvalds }
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds __initcall(setup_iomem);
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds /*
4431da177e4SLinus Torvalds  * Overrides for Emacs so that we follow Linus's tabbing style.
4441da177e4SLinus Torvalds  * Emacs will notice this stuff at the end of the file and automatically
4451da177e4SLinus Torvalds  * adjust the settings for this buffer only.  This must remain at the end
4461da177e4SLinus Torvalds  * of the file.
4471da177e4SLinus Torvalds  * ---------------------------------------------------------------------------
4481da177e4SLinus Torvalds  * Local variables:
4491da177e4SLinus Torvalds  * c-file-style: "linux"
4501da177e4SLinus Torvalds  * End:
4511da177e4SLinus Torvalds  */
452