linux/arch/um/kernel/physmem.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
   3 * Licensed under the GPL
   4 */
   5
   6#include "linux/mm.h"
   7#include "linux/rbtree.h"
   8#include "linux/slab.h"
   9#include "linux/vmalloc.h"
  10#include "linux/bootmem.h"
  11#include "linux/module.h"
  12#include "asm/types.h"
  13#include "asm/pgtable.h"
  14#include "kern_util.h"
  15#include "user_util.h"
  16#include "mode_kern.h"
  17#include "mem.h"
  18#include "mem_user.h"
  19#include "os.h"
  20#include "kern.h"
  21#include "init.h"
  22
  23struct phys_desc {
  24        struct rb_node rb;
  25        int fd;
  26        __u64 offset;
  27        void *virt;
  28        unsigned long phys;
  29        struct list_head list;
  30};
  31
  32static struct rb_root phys_mappings = RB_ROOT;
  33
  34static struct rb_node **find_rb(void *virt)
  35{
  36        struct rb_node **n = &phys_mappings.rb_node;
  37        struct phys_desc *d;
  38
  39        while(*n != NULL){
  40                d = rb_entry(*n, struct phys_desc, rb);
  41                if(d->virt == virt)
  42                        return(n);
  43
  44                if(d->virt > virt)
  45                        n = &(*n)->rb_left;
  46                else
  47                        n = &(*n)->rb_right;
  48        }
  49
  50        return(n);
  51}
  52
  53static struct phys_desc *find_phys_mapping(void *virt)
  54{
  55        struct rb_node **n = find_rb(virt);
  56
  57        if(*n == NULL)
  58                return(NULL);
  59
  60        return(rb_entry(*n, struct phys_desc, rb));
  61}
  62
  63static void insert_phys_mapping(struct phys_desc *desc)
  64{
  65        struct rb_node **n = find_rb(desc->virt);
  66
  67        if(*n != NULL)
  68                panic("Physical remapping for %p already present",
  69                      desc->virt);
  70
  71        rb_link_node(&desc->rb, (*n)->rb_parent, n);
  72        rb_insert_color(&desc->rb, &phys_mappings);
  73}
  74
  75LIST_HEAD(descriptor_mappings);
  76
  77struct desc_mapping {
  78        int fd;
  79        struct list_head list;
  80        struct list_head pages;
  81};
  82
  83static struct desc_mapping *find_mapping(int fd)
  84{
  85        struct desc_mapping *desc;
  86        struct list_head *ele;
  87
  88        list_for_each(ele, &descriptor_mappings){
  89                desc = list_entry(ele, struct desc_mapping, list);
  90                if(desc->fd == fd)
  91                        return(desc);
  92        }
  93
  94        return(NULL);
  95}
  96
  97static struct desc_mapping *descriptor_mapping(int fd)
  98{
  99        struct desc_mapping *desc;
 100
 101        desc = find_mapping(fd);
 102        if(desc != NULL)
 103                return(desc);
 104
 105        desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
 106        if(desc == NULL)
 107                return(NULL);
 108
 109        *desc = ((struct desc_mapping)
 110                { .fd =         fd,
 111                  .list =       LIST_HEAD_INIT(desc->list),
 112                  .pages =      LIST_HEAD_INIT(desc->pages) });
 113        list_add(&desc->list, &descriptor_mappings);
 114
 115        return(desc);
 116}
 117
 118int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w)
 119{
 120        struct desc_mapping *fd_maps;
 121        struct phys_desc *desc;
 122        unsigned long phys;
 123        int err;
 124
 125        fd_maps = descriptor_mapping(fd);
 126        if(fd_maps == NULL)
 127                return(-ENOMEM);
 128
 129        phys = __pa(virt);
 130        desc = find_phys_mapping(virt);
 131        if(desc != NULL)
 132                panic("Address 0x%p is already substituted\n", virt);
 133
 134        err = -ENOMEM;
 135        desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
 136        if(desc == NULL)
 137                goto out;
 138
 139        *desc = ((struct phys_desc)
 140                { .fd =                 fd,
 141                  .offset =             offset,
 142                  .virt =               virt,
 143                  .phys =               __pa(virt),
 144                  .list =               LIST_HEAD_INIT(desc->list) });
 145        insert_phys_mapping(desc);
 146
 147        list_add(&desc->list, &fd_maps->pages);
 148
 149        virt = (void *) ((unsigned long) virt & PAGE_MASK);
 150        err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0);
 151        if(!err)
 152                goto out;
 153
 154        rb_erase(&desc->rb, &phys_mappings);
 155        kfree(desc);
 156 out:
 157        return(err);
 158}
 159
 160static int physmem_fd = -1;
 161
 162static void remove_mapping(struct phys_desc *desc)
 163{
 164        void *virt = desc->virt;
 165        int err;
 166
 167        rb_erase(&desc->rb, &phys_mappings);
 168        list_del(&desc->list);
 169        kfree(desc);
 170
 171        err = os_map_memory(virt, physmem_fd, __pa(virt), PAGE_SIZE, 1, 1, 0);
 172        if(err)
 173                panic("Failed to unmap block device page from physical memory, "
 174                      "errno = %d", -err);
 175}
 176
 177int physmem_remove_mapping(void *virt)
 178{
 179        struct phys_desc *desc;
 180
 181        virt = (void *) ((unsigned long) virt & PAGE_MASK);
 182        desc = find_phys_mapping(virt);
 183        if(desc == NULL)
 184                return(0);
 185
 186        remove_mapping(desc);
 187        return(1);
 188}
 189
 190void physmem_forget_descriptor(int fd)
 191{
 192        struct desc_mapping *desc;
 193        struct phys_desc *page;
 194        struct list_head *ele, *next;
 195        __u64 offset;
 196        void *addr;
 197        int err;
 198
 199        desc = find_mapping(fd);
 200        if(desc == NULL)
 201                return;
 202
 203        list_for_each_safe(ele, next, &desc->pages){
 204                page = list_entry(ele, struct phys_desc, list);
 205                offset = page->offset;
 206                addr = page->virt;
 207                remove_mapping(page);
 208                err = os_seek_file(fd, offset);
 209                if(err)
 210                        panic("physmem_forget_descriptor - failed to seek "
 211                              "to %lld in fd %d, error = %d\n",
 212                              offset, fd, -err);
 213                err = os_read_file(fd, addr, PAGE_SIZE);
 214                if(err < 0)
 215                        panic("physmem_forget_descriptor - failed to read "
 216                              "from fd %d to 0x%p, error = %d\n",
 217                              fd, addr, -err);
 218        }
 219
 220        list_del(&desc->list);
 221        kfree(desc);
 222}
 223
 224EXPORT_SYMBOL(physmem_forget_descriptor);
 225EXPORT_SYMBOL(physmem_remove_mapping);
 226EXPORT_SYMBOL(physmem_subst_mapping);
 227
 228void arch_free_page(struct page *page, int order)
 229{
 230        void *virt;
 231        int i;
 232
 233        for(i = 0; i < (1 << order); i++){
 234                virt = __va(page_to_phys(page + i));
 235                physmem_remove_mapping(virt);
 236        }
 237}
 238
 239int is_remapped(void *virt)
 240{
 241        struct phys_desc *desc = find_phys_mapping(virt);
 242
 243        return(desc != NULL);
 244}
 245
 246/* Changed during early boot */
 247unsigned long high_physmem;
 248
 249extern unsigned long physmem_size;
 250
 251void *to_virt(unsigned long phys)
 252{
 253        return((void *) uml_physmem + phys);
 254}
 255
 256unsigned long to_phys(void *virt)
 257{
 258        return(((unsigned long) virt) - uml_physmem);
 259}
 260
 261int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem)
 262{
 263        struct page *p, *map;
 264        unsigned long phys_len, phys_pages, highmem_len, highmem_pages;
 265        unsigned long iomem_len, iomem_pages, total_len, total_pages;
 266        int i;
 267
 268        phys_pages = physmem >> PAGE_SHIFT;
 269        phys_len = phys_pages * sizeof(struct page);
 270
 271        iomem_pages = iomem >> PAGE_SHIFT;
 272        iomem_len = iomem_pages * sizeof(struct page);
 273
 274        highmem_pages = highmem >> PAGE_SHIFT;
 275        highmem_len = highmem_pages * sizeof(struct page);
 276
 277        total_pages = phys_pages + iomem_pages + highmem_pages;
 278        total_len = phys_len + iomem_pages + highmem_len;
 279
 280        if(kmalloc_ok){
 281                map = kmalloc(total_len, GFP_KERNEL);
 282                if(map == NULL)
 283                        map = vmalloc(total_len);
 284        }
 285        else map = alloc_bootmem_low_pages(total_len);
 286
 287        if(map == NULL)
 288                return(-ENOMEM);
 289
 290        for(i = 0; i < total_pages; i++){
 291                p = &map[i];
 292                set_page_count(p, 0);
 293                SetPageReserved(p);
 294                INIT_LIST_HEAD(&p->lru);
 295        }
 296
 297        mem_map = map;
 298        max_mapnr = total_pages;
 299        return(0);
 300}
 301
 302struct page *phys_to_page(const unsigned long phys)
 303{
 304        return(&mem_map[phys >> PAGE_SHIFT]);
 305}
 306
 307struct page *__virt_to_page(const unsigned long virt)
 308{
 309        return(&mem_map[__pa(virt) >> PAGE_SHIFT]);
 310}
 311
 312phys_t page_to_phys(struct page *page)
 313{
 314        return((page - mem_map) << PAGE_SHIFT);
 315}
 316
 317pte_t mk_pte(struct page *page, pgprot_t pgprot)
 318{
 319        pte_t pte;
 320
 321        pte_set_val(pte, page_to_phys(page), pgprot);
 322        if(pte_present(pte))
 323                pte_mknewprot(pte_mknewpage(pte));
 324        return(pte);
 325}
 326
 327/* Changed during early boot */
 328static unsigned long kmem_top = 0;
 329
 330unsigned long get_kmem_end(void)
 331{
 332        if(kmem_top == 0)
 333                kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas);
 334        return(kmem_top);
 335}
 336
 337void map_memory(unsigned long virt, unsigned long phys, unsigned long len,
 338                int r, int w, int x)
 339{
 340        __u64 offset;
 341        int fd, err;
 342
 343        fd = phys_mapping(phys, &offset);
 344        err = os_map_memory((void *) virt, fd, offset, len, r, w, x);
 345        if(err) {
 346                if(err == -ENOMEM)
 347                        printk("try increasing the host's "
 348                               "/proc/sys/vm/max_map_count to <physical "
 349                               "memory size>/4096\n");
 350                panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, "
 351                      "err = %d\n", virt, fd, offset, len, r, w, x, err);
 352        }
 353}
 354
 355#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
 356
 357void setup_physmem(unsigned long start, unsigned long reserve_end,
 358                   unsigned long len, unsigned long highmem)
 359{
 360        unsigned long reserve = reserve_end - start;
 361        int pfn = PFN_UP(__pa(reserve_end));
 362        int delta = (len - reserve) >> PAGE_SHIFT;
 363        int err, offset, bootmap_size;
 364
 365        physmem_fd = create_mem_file(len + highmem);
 366
 367        offset = uml_reserved - uml_physmem;
 368        err = os_map_memory((void *) uml_reserved, physmem_fd, offset,
 369                            len - offset, 1, 1, 0);
 370        if(err < 0){
 371                os_print_error(err, "Mapping memory");
 372                exit(1);
 373        }
 374
 375        bootmap_size = init_bootmem(pfn, pfn + delta);
 376        free_bootmem(__pa(reserve_end) + bootmap_size,
 377                     len - bootmap_size - reserve);
 378}
 379
 380int phys_mapping(unsigned long phys, __u64 *offset_out)
 381{
 382        struct phys_desc *desc = find_phys_mapping(__va(phys & PAGE_MASK));
 383        int fd = -1;
 384
 385        if(desc != NULL){
 386                fd = desc->fd;
 387                *offset_out = desc->offset;
 388        }
 389        else if(phys < physmem_size){
 390                fd = physmem_fd;
 391                *offset_out = phys;
 392        }
 393        else if(phys < __pa(end_iomem)){
 394                struct iomem_region *region = iomem_regions;
 395
 396                while(region != NULL){
 397                        if((phys >= region->phys) &&
 398                           (phys < region->phys + region->size)){
 399                                fd = region->fd;
 400                                *offset_out = phys - region->phys;
 401                                break;
 402                        }
 403                        region = region->next;
 404                }
 405        }
 406        else if(phys < __pa(end_iomem) + highmem){
 407                fd = physmem_fd;
 408                *offset_out = phys - iomem_size;
 409        }
 410
 411        return(fd);
 412}
 413
 414static int __init uml_mem_setup(char *line, int *add)
 415{
 416        char *retptr;
 417        physmem_size = memparse(line,&retptr);
 418        return 0;
 419}
 420__uml_setup("mem=", uml_mem_setup,
 421"mem=<Amount of desired ram>\n"
 422"    This controls how much \"physical\" memory the kernel allocates\n"
 423"    for the system. The size is specified as a number followed by\n"
 424"    one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n"
 425"    This is not related to the amount of memory in the host.  It can\n"
 426"    be more, and the excess, if it's ever used, will just be swapped out.\n"
 427"       Example: mem=64M\n\n"
 428);
 429
 430unsigned long find_iomem(char *driver, unsigned long *len_out)
 431{
 432        struct iomem_region *region = iomem_regions;
 433
 434        while(region != NULL){
 435                if(!strcmp(region->driver, driver)){
 436                        *len_out = region->size;
 437                        return(region->virt);
 438                }
 439        }
 440
 441        return(0);
 442}
 443
 444int setup_iomem(void)
 445{
 446        struct iomem_region *region = iomem_regions;
 447        unsigned long iomem_start = high_physmem + PAGE_SIZE;
 448        int err;
 449
 450        while(region != NULL){
 451                err = os_map_memory((void *) iomem_start, region->fd, 0,
 452                                    region->size, 1, 1, 0);
 453                if(err)
 454                        printk("Mapping iomem region for driver '%s' failed, "
 455                               "errno = %d\n", region->driver, -err);
 456                else {
 457                        region->virt = iomem_start;
 458                        region->phys = __pa(region->virt);
 459                }
 460
 461                iomem_start += region->size + PAGE_SIZE;
 462                region = region->next;
 463        }
 464
 465        return(0);
 466}
 467
 468__initcall(setup_iomem);
 469
 470/*
 471 * Overrides for Emacs so that we follow Linus's tabbing style.
 472 * Emacs will notice this stuff at the end of the file and automatically
 473 * adjust the settings for this buffer only.  This must remain at the end
 474 * of the file.
 475 * ---------------------------------------------------------------------------
 476 * Local variables:
 477 * c-file-style: "linux"
 478 * End:
 479 */
 480
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.