123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- /*
- * Mach Operating System
- * Copyright (c) 1993-1989 Carnegie Mellon University
- * All Rights Reserved.
- *
- * Permission to use, copy, modify and distribute this software and its
- * documentation is hereby granted, provided that both the copyright
- * notice and this permission notice appear in all copies of the
- * software, derivative works or modified versions, and any portions
- * thereof, and that both notices appear in supporting documentation.
- *
- * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
- * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
- * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
- *
- * Carnegie Mellon requests users of this software to return to
- *
- * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
- * School of Computer Science
- * Carnegie Mellon University
- * Pittsburgh PA 15213-3890
- *
- * any improvements or extensions that they make and grant Carnegie Mellon
- * the rights to redistribute these changes.
- */
- /*
- * Author: David B. Golub, Carnegie Mellon University
- * Date: 3/89
- *
- * Device pager.
- */
- #include <string.h>
- #include <mach/boolean.h>
- #include <mach/port.h>
- #include <mach/message.h>
- #include <mach/std_types.h>
- #include <mach/mach_types.h>
- #include <ipc/ipc_port.h>
- #include <ipc/ipc_space.h>
- #include <kern/debug.h>
- #include <kern/printf.h>
- #include <kern/queue.h>
- #include <kern/slab.h>
- #include <vm/vm_page.h>
- #include <vm/vm_kern.h>
- #include <vm/vm_user.h>
- #include <device/device_types.h>
- #include <device/ds_routines.h>
- #include <device/dev_hdr.h>
- #include <device/io_req.h>
- #include <device/memory_object_reply.user.h>
- #include <device/dev_pager.h>
- #include <device/blkio.h>
- /*
- * The device pager routines are called directly from the message
- * system (via mach_msg), and thus run in the kernel-internal
- * environment. All ports are in internal form (ipc_port_t),
- * and must be correctly reference-counted in order to be saved
- * in other data structures. Kernel routines may be called
- * directly. Kernel types are used for data objects (tasks,
- * memory objects, ports). The only IPC routines that may be
- * called are ones that masquerade as the kernel task (via
- * msg_send_from_kernel).
- *
- * Port rights and references are maintained as follows:
- * Memory object port:
- * The device_pager task has all rights.
- * Memory object control port:
- * The device_pager task has only send rights.
- * Memory object name port:
- * The device_pager task has only send rights.
- * The name port is not even recorded.
- * Regardless how the object is created, the control and name
- * ports are created by the kernel and passed through the memory
- * management interface.
- *
- * The device_pager assumes that access to its memory objects
- * will not be propagated to more that one host, and therefore
- * provides no consistency guarantees beyond those made by the
- * kernel.
- *
- * In the event that more than one host attempts to use a device
- * memory object, the device_pager will only record the last set
- * of port names. [This can happen with only one host if a new
- * mapping is being established while termination of all previous
- * mappings is taking place.] Currently, the device_pager assumes
- * that its clients adhere to the initialization and termination
- * protocols in the memory management interface; otherwise, port
- * rights or out-of-line memory from erroneous messages may be
- * allowed to accumulate.
- *
- * [The phrase "currently" has been used above to denote aspects of
- * the implementation that could be altered without changing the rest
- * of the basic documentation.]
- */
- /*
- * Basic device pager structure.
- */
- struct dev_pager {
- decl_simple_lock_data(, lock) /* lock for reference count */
- int ref_count; /* reference count */
- int client_count; /* How many memory_object_create
- * calls have we received */
- ipc_port_t pager; /* pager port */
- ipc_port_t pager_request; /* Known request port */
- ipc_port_t pager_name; /* Known name port */
- mach_device_t device; /* Device handle */
- int type; /* to distinguish */
- #define DEV_PAGER_TYPE 0
- #define CHAR_PAGER_TYPE 1
- /* char pager specifics */
- int prot;
- vm_size_t size;
- };
- typedef struct dev_pager *dev_pager_t;
- #define DEV_PAGER_NULL ((dev_pager_t)0)
- struct kmem_cache dev_pager_cache;
- void dev_pager_reference(dev_pager_t ds)
- {
- simple_lock(&ds->lock);
- ds->ref_count++;
- simple_unlock(&ds->lock);
- }
- void dev_pager_deallocate(dev_pager_t ds)
- {
- simple_lock(&ds->lock);
- if (--ds->ref_count > 0) {
- simple_unlock(&ds->lock);
- return;
- }
- simple_unlock(&ds->lock);
- kmem_cache_free(&dev_pager_cache, (vm_offset_t)ds);
- }
- /*
- * A hash table of ports for device_pager backed objects.
- */
- #define DEV_PAGER_HASH_COUNT 127
- struct dev_pager_entry {
- queue_chain_t links;
- ipc_port_t name;
- dev_pager_t pager_rec;
- };
- typedef struct dev_pager_entry *dev_pager_entry_t;
- queue_head_t dev_pager_hashtable[DEV_PAGER_HASH_COUNT];
- struct kmem_cache dev_pager_hash_cache;
- decl_simple_lock_data(,
- dev_pager_hash_lock)
- #define dev_pager_hash(name_port) \
- (((vm_offset_t)(name_port) & 0xffffff) % DEV_PAGER_HASH_COUNT)
- void dev_pager_hash_init(void)
- {
- int i;
- vm_size_t size;
- size = sizeof(struct dev_pager_entry);
- kmem_cache_init(&dev_pager_hash_cache, "dev_pager_entry", size, 0,
- NULL, 0);
- for (i = 0; i < DEV_PAGER_HASH_COUNT; i++)
- queue_init(&dev_pager_hashtable[i]);
- simple_lock_init(&dev_pager_hash_lock);
- }
- void dev_pager_hash_insert(
- const ipc_port_t name_port,
- const dev_pager_t rec)
- {
- dev_pager_entry_t new_entry;
- new_entry = (dev_pager_entry_t) kmem_cache_alloc(&dev_pager_hash_cache);
- new_entry->name = name_port;
- new_entry->pager_rec = rec;
- simple_lock(&dev_pager_hash_lock);
- queue_enter(&dev_pager_hashtable[dev_pager_hash(name_port)],
- new_entry, dev_pager_entry_t, links);
- simple_unlock(&dev_pager_hash_lock);
- }
- void dev_pager_hash_delete(const ipc_port_t name_port)
- {
- queue_t bucket;
- dev_pager_entry_t entry;
- bucket = &dev_pager_hashtable[dev_pager_hash(name_port)];
- simple_lock(&dev_pager_hash_lock);
- for (entry = (dev_pager_entry_t)queue_first(bucket);
- !queue_end(bucket, &entry->links);
- entry = (dev_pager_entry_t)queue_next(&entry->links)) {
- if (entry->name == name_port) {
- queue_remove(bucket, entry, dev_pager_entry_t, links);
- break;
- }
- }
- simple_unlock(&dev_pager_hash_lock);
- if (entry)
- kmem_cache_free(&dev_pager_hash_cache, (vm_offset_t)entry);
- }
- dev_pager_t dev_pager_hash_lookup(const ipc_port_t name_port)
- {
- queue_t bucket;
- dev_pager_entry_t entry;
- dev_pager_t pager;
- bucket = &dev_pager_hashtable[dev_pager_hash(name_port)];
- simple_lock(&dev_pager_hash_lock);
- for (entry = (dev_pager_entry_t)queue_first(bucket);
- !queue_end(bucket, &entry->links);
- entry = (dev_pager_entry_t)queue_next(&entry->links)) {
- if (entry->name == name_port) {
- pager = entry->pager_rec;
- dev_pager_reference(pager);
- simple_unlock(&dev_pager_hash_lock);
- return (pager);
- }
- }
- simple_unlock(&dev_pager_hash_lock);
- return (DEV_PAGER_NULL);
- }
- kern_return_t device_pager_setup(
- const mach_device_t device,
- int prot,
- vm_offset_t offset,
- vm_size_t size,
- mach_port_t *pager)
- {
- dev_pager_t d;
- /*
- * Verify the device is indeed mappable
- */
- if (!device->dev_ops->d_mmap || (device->dev_ops->d_mmap == nomap))
- return (D_INVALID_OPERATION);
- /*
- * Allocate a structure to hold the arguments
- * and port to represent this object.
- */
- d = dev_pager_hash_lookup((ipc_port_t)device); /* HACK */
- if (d != DEV_PAGER_NULL) {
- *pager = (mach_port_t) ipc_port_make_send(d->pager);
- dev_pager_deallocate(d);
- return (D_SUCCESS);
- }
- d = (dev_pager_t) kmem_cache_alloc(&dev_pager_cache);
- if (d == DEV_PAGER_NULL)
- return (KERN_RESOURCE_SHORTAGE);
- simple_lock_init(&d->lock);
- d->ref_count = 1;
- /*
- * Allocate the pager port.
- */
- d->pager = ipc_port_alloc_kernel();
- if (d->pager == IP_NULL) {
- dev_pager_deallocate(d);
- return (KERN_RESOURCE_SHORTAGE);
- }
- d->client_count = 0;
- d->pager_request = IP_NULL;
- d->pager_name = IP_NULL;
- d->device = device;
- mach_device_reference(device);
- d->prot = prot;
- d->size = round_page(size);
- if (device->dev_ops->d_mmap == block_io_mmap) {
- d->type = DEV_PAGER_TYPE;
- } else {
- d->type = CHAR_PAGER_TYPE;
- }
- dev_pager_hash_insert(d->pager, d);
- dev_pager_hash_insert((ipc_port_t)device, d); /* HACK */
- *pager = (mach_port_t) ipc_port_make_send(d->pager);
- return (KERN_SUCCESS);
- }
- /*
- * Routine: device_pager_release
- * Purpose:
- * Relinquish any references or rights that were
- * associated with the result of a call to
- * device_pager_setup.
- */
- void device_pager_release(memory_object_t object)
- {
- if (MACH_PORT_VALID(object))
- ipc_port_release_send((ipc_port_t) object);
- }
- boolean_t device_pager_debug = FALSE;
- kern_return_t device_pager_data_request(
- const ipc_port_t pager,
- const ipc_port_t pager_request,
- vm_offset_t offset,
- vm_size_t length,
- vm_prot_t protection_required)
- {
- dev_pager_t ds;
- if (device_pager_debug)
- printf("(device_pager)data_request: pager=%p, offset=0x%lx, length=0x%x\n",
- pager, offset, length);
- ds = dev_pager_hash_lookup(pager);
- if (ds == DEV_PAGER_NULL)
- panic("(device_pager)data_request: lookup failed");
- if (ds->pager_request != pager_request)
- panic("(device_pager)data_request: bad pager_request");
- if (ds->type == CHAR_PAGER_TYPE) {
- vm_object_t object;
- object = vm_object_lookup(pager_request);
- if (object == VM_OBJECT_NULL) {
- (void) r_memory_object_data_error(pager_request,
- offset, length,
- KERN_FAILURE);
- dev_pager_deallocate(ds);
- return (KERN_SUCCESS);
- }
- vm_object_page_map(object,
- offset, length,
- device_map_page, (void *)ds);
- vm_object_deallocate(object);
- }
- else {
- panic("(device_pager)data_request: dev pager");
- }
- dev_pager_deallocate(ds);
- return (KERN_SUCCESS);
- }
- kern_return_t device_pager_copy(
- const ipc_port_t pager,
- const ipc_port_t pager_request,
- vm_offset_t offset,
- vm_size_t length,
- const ipc_port_t new_pager)
- {
- panic("(device_pager)copy: called");
- }
- kern_return_t
- device_pager_supply_completed(
- const ipc_port_t pager,
- const ipc_port_t pager_request,
- vm_offset_t offset,
- vm_size_t length,
- kern_return_t result,
- vm_offset_t error_offset)
- {
- panic("(device_pager)supply_completed: called");
- }
- kern_return_t
- device_pager_data_return(
- const ipc_port_t pager,
- const ipc_port_t pager_request,
- vm_offset_t offset,
- pointer_t addr,
- vm_size_t data_cnt,
- boolean_t dirty,
- boolean_t kernel_copy)
- {
- panic("(device_pager)data_return: called");
- }
- kern_return_t
- device_pager_change_completed(
- const ipc_port_t pager,
- boolean_t may_cache,
- memory_object_copy_strategy_t copy_strategy)
- {
- panic("(device_pager)change_completed: called");
- }
- /*
- * The mapping function takes a byte offset, but returns
- * a machine-dependent page frame number. We convert
- * that into something that the pmap module will
- * accept later.
- */
- vm_offset_t device_map_page(
- void *dsp,
- vm_offset_t offset)
- {
- dev_pager_t ds = (dev_pager_t) dsp;
- return pmap_phys_address(
- (*(ds->device->dev_ops->d_mmap))
- (ds->device->dev_number, offset, ds->prot));
- }
- kern_return_t device_pager_init_pager(
- const ipc_port_t pager,
- const ipc_port_t pager_request,
- const ipc_port_t pager_name,
- vm_size_t pager_page_size)
- {
- dev_pager_t ds;
- if (device_pager_debug)
- printf("(device_pager)init: pager=%p, request=%p, name=%p\n",
- pager, pager_request, pager_name);
- assert(pager_page_size == PAGE_SIZE);
- assert(IP_VALID(pager_request));
- assert(IP_VALID(pager_name));
- ds = dev_pager_hash_lookup(pager);
- assert(ds != DEV_PAGER_NULL);
- assert(ds->client_count == 0);
- assert(ds->pager_request == IP_NULL);
- assert(ds->pager_name == IP_NULL);
- ds->client_count = 1;
- /*
- * We save the send rights for the request and name ports.
- */
- ds->pager_request = pager_request;
- ds->pager_name = pager_name;
- if (ds->type == CHAR_PAGER_TYPE) {
- /*
- * Reply that the object is ready
- */
- (void) r_memory_object_ready(pager_request,
- FALSE, /* do not cache */
- MEMORY_OBJECT_COPY_NONE);
- } else {
- (void) r_memory_object_ready(pager_request,
- TRUE, /* cache */
- MEMORY_OBJECT_COPY_DELAY);
- }
- dev_pager_deallocate(ds);
- return (KERN_SUCCESS);
- }
- kern_return_t device_pager_terminate(
- const ipc_port_t pager,
- const ipc_port_t pager_request,
- const ipc_port_t pager_name)
- {
- dev_pager_t ds;
- assert(IP_VALID(pager_request));
- assert(IP_VALID(pager_name));
- ds = dev_pager_hash_lookup(pager);
- assert(ds != DEV_PAGER_NULL);
- assert(ds->client_count == 1);
- assert(ds->pager_request == pager_request);
- assert(ds->pager_name == pager_name);
- dev_pager_hash_delete(ds->pager);
- dev_pager_hash_delete((ipc_port_t)ds->device); /* HACK */
- mach_device_deallocate(ds->device);
- /* release the send rights we have saved from the init call */
- ipc_port_release_send(pager_request);
- ipc_port_release_send(pager_name);
- /* release the naked receive rights we just acquired */
- ipc_port_release_receive(pager_request);
- ipc_port_release_receive(pager_name);
- /* release the kernel's receive right for the pager port */
- ipc_port_dealloc_kernel(pager);
- /* once for ref from lookup, once to make it go away */
- dev_pager_deallocate(ds);
- dev_pager_deallocate(ds);
- return (KERN_SUCCESS);
- }
- kern_return_t device_pager_data_unlock(
- const ipc_port_t memory_object,
- const ipc_port_t memory_control_port,
- vm_offset_t offset,
- vm_size_t length,
- vm_prot_t desired_access)
- {
- panic("(device_pager)data_unlock: called");
- return (KERN_FAILURE);
- }
- kern_return_t device_pager_lock_completed(
- const ipc_port_t memory_object,
- const ipc_port_t pager_request_port,
- vm_offset_t offset,
- vm_size_t length)
- {
- panic("(device_pager)lock_completed: called");
- return (KERN_FAILURE);
- }
- void device_pager_init(void)
- {
- vm_size_t size;
- /*
- * Initialize cache of paging structures.
- */
- size = sizeof(struct dev_pager);
- kmem_cache_init(&dev_pager_cache, "dev_pager", size, 0,
- NULL, 0);
- /*
- * Initialize the name port hashing stuff.
- */
- dev_pager_hash_init();
- }
|