145749Smckusick /* 245749Smckusick * Copyright (c) 1990 University of Utah. 345749Smckusick * Copyright (c) 1991 The Regents of the University of California. 445749Smckusick * All rights reserved. 545749Smckusick * 645749Smckusick * This code is derived from software contributed to Berkeley by 745749Smckusick * the Systems Programming Group of the University of Utah Computer 845749Smckusick * Science Department. 945749Smckusick * 1045749Smckusick * %sccs.include.redist.c% 1145749Smckusick * 12*53338Sbostic * @(#)device_pager.c 7.6 (Berkeley) 05/04/92 1345749Smckusick */ 1445749Smckusick 1545749Smckusick /* 1645749Smckusick * Page to/from special files. 1745749Smckusick */ 1845749Smckusick 1945749Smckusick #include "devpager.h" 2045749Smckusick #if NDEVPAGER > 0 2145749Smckusick 22*53338Sbostic #include <sys/param.h> 23*53338Sbostic #include <sys/systm.h> 24*53338Sbostic #include <sys/conf.h> 25*53338Sbostic #include <sys/mman.h> 26*53338Sbostic #include <sys/malloc.h> 2745749Smckusick 28*53338Sbostic #include <vm/vm.h> 29*53338Sbostic #include <vm/vm_kern.h> 30*53338Sbostic #include <vm/vm_page.h> 31*53338Sbostic #include <vm/device_pager.h> 3245749Smckusick 3345749Smckusick queue_head_t dev_pager_list; /* list of managed devices */ 3445749Smckusick 3545749Smckusick #ifdef DEBUG 3645749Smckusick int dpagerdebug = 0; 3745749Smckusick #define DDB_FOLLOW 0x01 3845749Smckusick #define DDB_INIT 0x02 3945749Smckusick #define DDB_ALLOC 0x04 4045749Smckusick #define DDB_FAIL 0x08 4145749Smckusick #endif 4245749Smckusick 43*53338Sbostic static vm_pager_t dev_pager_alloc __P((caddr_t, vm_size_t, vm_prot_t)); 44*53338Sbostic static void dev_pager_dealloc __P((vm_pager_t)); 45*53338Sbostic static int dev_pager_getpage 46*53338Sbostic __P((vm_pager_t, vm_page_t, boolean_t)); 47*53338Sbostic static boolean_t dev_pager_haspage __P((vm_pager_t, vm_offset_t)); 48*53338Sbostic static void dev_pager_init __P((void)); 49*53338Sbostic static int dev_pager_putpage 50*53338Sbostic __P((vm_pager_t, vm_page_t, boolean_t)); 51*53338Sbostic 52*53338Sbostic struct pagerops devicepagerops = { 53*53338Sbostic dev_pager_init, 54*53338Sbostic dev_pager_alloc, 55*53338Sbostic dev_pager_dealloc, 56*53338Sbostic dev_pager_getpage, 57*53338Sbostic dev_pager_putpage, 58*53338Sbostic dev_pager_haspage 59*53338Sbostic }; 60*53338Sbostic 61*53338Sbostic static void 6245749Smckusick dev_pager_init() 6345749Smckusick { 6445749Smckusick #ifdef DEBUG 6545749Smckusick if (dpagerdebug & DDB_FOLLOW) 6645749Smckusick printf("dev_pager_init()\n"); 6745749Smckusick #endif 6845749Smckusick queue_init(&dev_pager_list); 6945749Smckusick } 7045749Smckusick 71*53338Sbostic static vm_pager_t 7245749Smckusick dev_pager_alloc(handle, size, prot) 7345749Smckusick caddr_t handle; 7445749Smckusick vm_size_t size; 7545749Smckusick vm_prot_t prot; 7645749Smckusick { 7745749Smckusick dev_t dev; 7845749Smckusick vm_pager_t pager; 7945749Smckusick int (*mapfunc)(), nprot; 8045749Smckusick register vm_object_t object; 8145749Smckusick register vm_page_t page; 8245749Smckusick register dev_pager_t devp; 8345749Smckusick register int npages, off; 8448386Skarels extern int nullop(), enodev(); 8545749Smckusick 8645749Smckusick 8745749Smckusick #ifdef DEBUG 8845749Smckusick if (dpagerdebug & DDB_FOLLOW) 8945749Smckusick printf("dev_pager_alloc(%x, %x, %x)\n", handle, size, prot); 9045749Smckusick #endif 9145749Smckusick /* 9245749Smckusick * Pageout to device, should never happen. 9345749Smckusick */ 9445749Smckusick if (handle == NULL) 9545749Smckusick panic("dev_pager_alloc called"); 9645749Smckusick 9745749Smckusick /* 9845749Smckusick * Look it up, creating as necessary 9945749Smckusick */ 10045749Smckusick pager = vm_pager_lookup(&dev_pager_list, handle); 10148386Skarels if (pager == NULL) { 10245749Smckusick /* 10345749Smckusick * Validation. Make sure this device can be mapped 10445749Smckusick * and that range to map is acceptible to device. 10545749Smckusick */ 10645749Smckusick dev = (dev_t)handle; 10745749Smckusick mapfunc = cdevsw[major(dev)].d_mmap; 10848386Skarels if (!mapfunc || mapfunc == enodev || mapfunc == nullop) 10948386Skarels return(NULL); 11045749Smckusick nprot = 0; 11145749Smckusick if (prot & VM_PROT_READ) 11245749Smckusick nprot |= PROT_READ; 11345749Smckusick if (prot & VM_PROT_WRITE) 11445749Smckusick nprot |= PROT_WRITE; 11545749Smckusick if (prot & VM_PROT_EXECUTE) 11645749Smckusick nprot |= PROT_EXEC; 11745749Smckusick npages = atop(round_page(size)); 11845749Smckusick for (off = 0; npages--; off += PAGE_SIZE) 11945749Smckusick if ((*mapfunc)(dev, off, nprot) == -1) 12048386Skarels return(NULL); 12145749Smckusick /* 12245749Smckusick * Allocate and initialize pager structs 12345749Smckusick */ 12445749Smckusick pager = (vm_pager_t)malloc(sizeof *pager, M_VMPAGER, M_WAITOK); 12548386Skarels if (pager == NULL) 12648386Skarels return(NULL); 12745749Smckusick devp = (dev_pager_t)malloc(sizeof *devp, M_VMPGDATA, M_WAITOK); 12848386Skarels if (devp == NULL) { 12945749Smckusick free((caddr_t)pager, M_VMPAGER); 13048386Skarels return(NULL); 13145749Smckusick } 13245749Smckusick devp->devp_dev = dev; 13345749Smckusick devp->devp_npages = atop(round_page(size)); 13445749Smckusick pager->pg_handle = handle; 13545749Smckusick pager->pg_ops = &devicepagerops; 13645749Smckusick pager->pg_type = PG_DEVICE; 13745749Smckusick pager->pg_data = (caddr_t)devp; 13845749Smckusick /* 13945749Smckusick * Allocate object and vm_page structures to describe memory 14045749Smckusick */ 14145749Smckusick npages = devp->devp_npages; 14245749Smckusick object = devp->devp_object = vm_object_allocate(ptoa(npages)); 14345749Smckusick vm_object_enter(object, pager); 14445749Smckusick vm_object_setpager(object, pager, (vm_offset_t)0, FALSE); 14545749Smckusick devp->devp_pages = (vm_page_t) 14645749Smckusick kmem_alloc(kernel_map, npages*sizeof(struct vm_page)); 14745749Smckusick off = 0; 14845749Smckusick for (page = devp->devp_pages; 14945749Smckusick page < &devp->devp_pages[npages]; page++) { 15045749Smckusick vm_object_lock(object); 15152595Storek VM_PAGE_INIT(page, object, off); 15245749Smckusick page->phys_addr = 15345749Smckusick pmap_phys_address((*mapfunc)(dev, off, nprot)); 15445749Smckusick page->wire_count = 1; 15545749Smckusick page->fictitious = TRUE; 15645749Smckusick PAGE_WAKEUP(page); 15745749Smckusick vm_object_unlock(object); 15845749Smckusick off += PAGE_SIZE; 15945749Smckusick } 16045749Smckusick /* 16145749Smckusick * Finally, put it on the managed list so other can find it. 16245749Smckusick */ 16345749Smckusick queue_enter(&dev_pager_list, devp, dev_pager_t, devp_list); 16445749Smckusick #ifdef DEBUG 16550857Smckusick if (dpagerdebug & DDB_ALLOC) { 16645749Smckusick printf("dev_pager_alloc: pages %d@%x\n", 16745749Smckusick devp->devp_npages, devp->devp_pages); 16850857Smckusick printf("dev_pager_alloc: pager %x devp %x object %x\n", 16950857Smckusick pager, devp, object); 17050857Smckusick vm_object_print(object, FALSE); 17150857Smckusick } 17245749Smckusick #endif 17345749Smckusick } else { 17445749Smckusick /* 17545749Smckusick * vm_object_lookup() gains a reference and also 17645749Smckusick * removes the object from the cache. 17745749Smckusick */ 17845749Smckusick devp = (dev_pager_t)pager->pg_data; 17945749Smckusick if (vm_object_lookup(pager) != devp->devp_object) 18045749Smckusick panic("dev_pager_setup: bad object"); 18145749Smckusick } 18245749Smckusick return(pager); 18345749Smckusick 18445749Smckusick } 18545749Smckusick 186*53338Sbostic static void 18745749Smckusick dev_pager_dealloc(pager) 18845749Smckusick vm_pager_t pager; 18945749Smckusick { 19045749Smckusick dev_pager_t devp = (dev_pager_t)pager->pg_data; 19145749Smckusick register vm_object_t object; 19245749Smckusick 19345749Smckusick #ifdef DEBUG 19445749Smckusick if (dpagerdebug & DDB_FOLLOW) 19545749Smckusick printf("dev_pager_dealloc(%x)\n", pager); 19645749Smckusick #endif 19745749Smckusick queue_remove(&dev_pager_list, devp, dev_pager_t, devp_list); 19845749Smckusick object = devp->devp_object; 19945749Smckusick #ifdef DEBUG 20045749Smckusick if (dpagerdebug & DDB_ALLOC) 20145749Smckusick printf("dev_pager_dealloc: devp %x object %x pages %d@%x\n", 20245749Smckusick devp, object, devp->devp_npages, devp->devp_pages); 20345749Smckusick #endif 20445749Smckusick while (!queue_empty(&object->memq)) 20545749Smckusick vm_page_remove((vm_page_t)queue_first(&object->memq)); 20652613Smckusick kmem_free(kernel_map, (vm_offset_t)devp->devp_pages, 20745749Smckusick devp->devp_npages * sizeof(struct vm_page)); 20845749Smckusick free((caddr_t)devp, M_VMPGDATA); 20945749Smckusick pager->pg_data = 0; 21045749Smckusick } 21145749Smckusick 212*53338Sbostic static int 21345749Smckusick dev_pager_getpage(pager, m, sync) 21445749Smckusick vm_pager_t pager; 21545749Smckusick vm_page_t m; 21645749Smckusick boolean_t sync; 21745749Smckusick { 21845749Smckusick #ifdef DEBUG 21945749Smckusick if (dpagerdebug & DDB_FOLLOW) 22045749Smckusick printf("dev_pager_getpage(%x, %x)\n", pager, m); 22145749Smckusick #endif 22245749Smckusick return(VM_PAGER_BAD); 22345749Smckusick } 22445749Smckusick 225*53338Sbostic static int 22645749Smckusick dev_pager_putpage(pager, m, sync) 22745749Smckusick vm_pager_t pager; 22845749Smckusick vm_page_t m; 22945749Smckusick boolean_t sync; 23045749Smckusick { 23145749Smckusick #ifdef DEBUG 23245749Smckusick if (dpagerdebug & DDB_FOLLOW) 23345749Smckusick printf("dev_pager_putpage(%x, %x)\n", pager, m); 23445749Smckusick #endif 23548386Skarels if (pager == NULL) 23645749Smckusick return; 23745749Smckusick panic("dev_pager_putpage called"); 23845749Smckusick } 23945749Smckusick 240*53338Sbostic static boolean_t 24145749Smckusick dev_pager_haspage(pager, offset) 24245749Smckusick vm_pager_t pager; 24345749Smckusick vm_offset_t offset; 24445749Smckusick { 24545749Smckusick #ifdef DEBUG 24645749Smckusick if (dpagerdebug & DDB_FOLLOW) 24745749Smckusick printf("dev_pager_haspage(%x, %x)\n", pager, offset); 24845749Smckusick #endif 24945749Smckusick return(TRUE); 25045749Smckusick } 25145749Smckusick #endif 252