summaryrefslogtreecommitdiff
path: root/device/dev_pager.c
diff options
context:
space:
mode:
Diffstat (limited to 'device/dev_pager.c')
-rw-r--r--device/dev_pager.c741
1 files changed, 741 insertions, 0 deletions
diff --git a/device/dev_pager.c b/device/dev_pager.c
new file mode 100644
index 0000000..007942d
--- /dev/null
+++ b/device/dev_pager.c
@@ -0,0 +1,741 @@
+/*
+ * 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 <norma_vm.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/queue.h>
+#include <kern/zalloc.h>
+#include <kern/kalloc.h>
+
+#include <vm/vm_page.h>
+#include <vm/vm_kern.h>
+
+#include <device/device_types.h>
+#include <device/ds_routines.h>
+#include <device/dev_hdr.h>
+#include <device/io_req.h>
+
+extern vm_offset_t block_io_mmap(); /* dummy routine to allow
+ mmap for block devices */
+
+/*
+ * 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)
+
+
+zone_t dev_pager_zone;
+
+void dev_pager_reference(register dev_pager_t ds)
+{
+ simple_lock(&ds->lock);
+ ds->ref_count++;
+ simple_unlock(&ds->lock);
+}
+
+void dev_pager_deallocate(register dev_pager_t ds)
+{
+ simple_lock(&ds->lock);
+ if (--ds->ref_count > 0) {
+ simple_unlock(&ds->lock);
+ return;
+ }
+
+ simple_unlock(&ds->lock);
+ zfree(dev_pager_zone, (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];
+zone_t dev_pager_hash_zone;
+decl_simple_lock_data(,
+ dev_pager_hash_lock)
+
+#define dev_pager_hash(name_port) \
+ (((natural_t)(name_port) & 0xffffff) % DEV_PAGER_HASH_COUNT)
+
+void dev_pager_hash_init(void)
+{
+ register int i;
+ register vm_size_t size;
+
+ size = sizeof(struct dev_pager_entry);
+ dev_pager_hash_zone = zinit(
+ size,
+ size * 1000,
+ PAGE_SIZE,
+ FALSE,
+ "dev_pager port hash");
+ 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(
+ ipc_port_t name_port,
+ dev_pager_t rec)
+{
+ register dev_pager_entry_t new_entry;
+
+ new_entry = (dev_pager_entry_t) zalloc(dev_pager_hash_zone);
+ 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(ipc_port_t name_port)
+{
+ register queue_t bucket;
+ register 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)
+ zfree(dev_pager_hash_zone, (vm_offset_t)entry);
+}
+
+dev_pager_t dev_pager_hash_lookup(ipc_port_t name_port)
+{
+ register queue_t bucket;
+ register dev_pager_entry_t entry;
+ register 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(
+ mach_device_t device,
+ int prot,
+ vm_offset_t offset,
+ vm_size_t size,
+ mach_port_t *pager)
+{
+ register 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) zalloc(dev_pager_zone);
+ 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;
+
+boolean_t device_pager_data_request_done(); /* forward */
+boolean_t device_pager_data_write_done(); /* forward */
+
+
+kern_return_t device_pager_data_request(
+ ipc_port_t pager,
+ ipc_port_t pager_request,
+ vm_offset_t offset,
+ vm_size_t length,
+ vm_prot_t protection_required)
+{
+ register dev_pager_t ds;
+
+#ifdef lint
+ protection_required++;
+#endif lint
+
+ if (device_pager_debug)
+ printf("(device_pager)data_request: pager=%d, offset=0x%x, length=0x%x\n",
+ pager, offset, length);
+
+ ds = dev_pager_hash_lookup((ipc_port_t)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) {
+ register vm_object_t object;
+ vm_offset_t device_map_page(void *,vm_offset_t);
+
+#if NORMA_VM
+ object = vm_object_lookup(pager);
+#else NORMA_VM
+ object = vm_object_lookup(pager_request);
+#endif NORMA_VM
+ 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, (char *)ds);
+
+ vm_object_deallocate(object);
+ }
+ else {
+ register io_req_t ior;
+ register mach_device_t device;
+ io_return_t result;
+
+ panic("(device_pager)data_request: dev pager");
+
+ device = ds->device;
+ mach_device_reference(device);
+ dev_pager_deallocate(ds);
+
+ /*
+ * Package the read for the device driver.
+ */
+ io_req_alloc(ior, 0);
+
+ ior->io_device = device;
+ ior->io_unit = device->dev_number;
+ ior->io_op = IO_READ | IO_CALL;
+ ior->io_mode = 0;
+ ior->io_recnum = offset / device->bsize;
+ ior->io_data = 0; /* driver must allocate */
+ ior->io_count = length;
+ ior->io_alloc_size = 0; /* no data allocated yet */
+ ior->io_residual = 0;
+ ior->io_error = 0;
+ ior->io_done = device_pager_data_request_done;
+ ior->io_reply_port = pager_request;
+ ior->io_reply_port_type = MACH_MSG_TYPE_PORT_SEND;
+
+ result = (*device->dev_ops->d_read)(device->dev_number, ior);
+ if (result == D_IO_QUEUED)
+ return (KERN_SUCCESS);
+
+ /*
+ * Return by queuing IOR for io_done thread, to reply in
+ * correct environment (kernel).
+ */
+ ior->io_error = result;
+ iodone(ior);
+ }
+
+ dev_pager_deallocate(ds);
+
+ return (KERN_SUCCESS);
+}
+
+/*
+ * Always called by io_done thread.
+ */
+boolean_t device_pager_data_request_done(register io_req_t ior)
+{
+ vm_offset_t start_alloc, end_alloc;
+ vm_size_t size_read;
+
+ if (ior->io_error == D_SUCCESS) {
+ size_read = ior->io_count;
+ if (ior->io_residual) {
+ if (device_pager_debug)
+ printf("(device_pager)data_request_done: r: 0x%x\n",ior->io_residual);
+ bzero( (char *) (&ior->io_data[ior->io_count -
+ ior->io_residual]),
+ (unsigned) ior->io_residual);
+ }
+ } else {
+ size_read = ior->io_count - ior->io_residual;
+ }
+
+ start_alloc = trunc_page((vm_offset_t)ior->io_data);
+ end_alloc = start_alloc + round_page(ior->io_alloc_size);
+
+ if (ior->io_error == D_SUCCESS) {
+ vm_map_copy_t copy;
+ kern_return_t kr;
+
+ kr = vm_map_copyin(kernel_map, (vm_offset_t)ior->io_data,
+ size_read, TRUE, &copy);
+ if (kr != KERN_SUCCESS)
+ panic("device_pager_data_request_done");
+
+ (void) r_memory_object_data_provided(
+ ior->io_reply_port,
+ ior->io_recnum * ior->io_device->bsize,
+ (vm_offset_t)copy,
+ size_read,
+ VM_PROT_NONE);
+ }
+ else {
+ (void) r_memory_object_data_error(
+ ior->io_reply_port,
+ ior->io_recnum * ior->io_device->bsize,
+ (vm_size_t)ior->io_count,
+ ior->io_error);
+ }
+
+ (void)vm_deallocate(kernel_map,
+ start_alloc,
+ end_alloc - start_alloc);
+ mach_device_deallocate(ior->io_device);
+ return (TRUE);
+}
+
+kern_return_t device_pager_data_write(
+ ipc_port_t pager,
+ ipc_port_t pager_request,
+ register vm_offset_t offset,
+ register pointer_t addr,
+ vm_size_t data_count)
+{
+ register dev_pager_t ds;
+ register mach_device_t device;
+ register io_req_t ior;
+ kern_return_t result;
+
+ panic("(device_pager)data_write: called");
+
+ ds = dev_pager_hash_lookup((ipc_port_t)pager);
+ if (ds == DEV_PAGER_NULL)
+ panic("(device_pager)data_write: lookup failed");
+
+ if (ds->pager_request != pager_request)
+ panic("(device_pager)data_write: bad pager_request");
+
+ if (ds->type == CHAR_PAGER_TYPE)
+ panic("(device_pager)data_write: char pager");
+
+ device = ds->device;
+ mach_device_reference(device);
+ dev_pager_deallocate(ds);
+
+ /*
+ * Package the write request for the device driver.
+ */
+ io_req_alloc(ior, data_count);
+
+ ior->io_device = device;
+ ior->io_unit = device->dev_number;
+ ior->io_op = IO_WRITE | IO_CALL;
+ ior->io_mode = 0;
+ ior->io_recnum = offset / device->bsize;
+ ior->io_data = (io_buf_ptr_t)addr;
+ ior->io_count = data_count;
+ ior->io_alloc_size = data_count; /* amount to deallocate */
+ ior->io_residual = 0;
+ ior->io_error = 0;
+ ior->io_done = device_pager_data_write_done;
+ ior->io_reply_port = IP_NULL;
+
+ result = (*device->dev_ops->d_write)(device->dev_number, ior);
+
+ if (result != D_IO_QUEUED) {
+ device_write_dealloc(ior);
+ io_req_free((vm_offset_t)ior);
+ mach_device_deallocate(device);
+ }
+
+ return (KERN_SUCCESS);
+}
+
+boolean_t device_pager_data_write_done(ior)
+ register io_req_t ior;
+{
+ device_write_dealloc(ior);
+ mach_device_deallocate(ior->io_device);
+
+ return (TRUE);
+}
+
+kern_return_t device_pager_copy(
+ ipc_port_t pager,
+ ipc_port_t pager_request,
+ register vm_offset_t offset,
+ register vm_size_t length,
+ ipc_port_t new_pager)
+{
+ panic("(device_pager)copy: called");
+}
+
+kern_return_t
+device_pager_supply_completed(
+ ipc_port_t pager,
+ 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(
+ ipc_port_t pager,
+ ipc_port_t pager_request,
+ vm_offset_t offset,
+ register 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(
+ 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)
+{
+ register 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(
+ ipc_port_t pager,
+ ipc_port_t pager_request,
+ ipc_port_t pager_name,
+ vm_size_t pager_page_size)
+{
+ register dev_pager_t ds;
+
+ if (device_pager_debug)
+ printf("(device_pager)init: pager=%d, request=%d, name=%d\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_set_attributes(pager_request,
+ TRUE, /* ready */
+ FALSE, /* do not cache */
+ MEMORY_OBJECT_COPY_NONE);
+ } else {
+ (void) r_memory_object_set_attributes(pager_request,
+ TRUE, /* ready */
+ TRUE, /* cache */
+ MEMORY_OBJECT_COPY_DELAY);
+ }
+
+ dev_pager_deallocate(ds);
+ return (KERN_SUCCESS);
+}
+
+kern_return_t device_pager_terminate(
+ ipc_port_t pager,
+ ipc_port_t pager_request,
+ ipc_port_t pager_name)
+{
+ register 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(
+ ipc_port_t memory_object,
+ ipc_port_t memory_control_port,
+ vm_offset_t offset,
+ vm_size_t length,
+ vm_prot_t desired_access)
+{
+#ifdef lint
+ memory_object++; memory_control_port++; offset++; length++; desired_access++;
+#endif lint
+
+ panic("(device_pager)data_unlock: called");
+ return (KERN_FAILURE);
+}
+
+kern_return_t device_pager_lock_completed(
+ ipc_port_t memory_object,
+ ipc_port_t pager_request_port,
+ vm_offset_t offset,
+ vm_size_t length)
+{
+#ifdef lint
+ memory_object++; pager_request_port++; offset++; length++;
+#endif lint
+
+ panic("(device_pager)lock_completed: called");
+ return (KERN_FAILURE);
+}
+
+void device_pager_init(void)
+{
+ register vm_size_t size;
+
+ /*
+ * Initialize zone of paging structures.
+ */
+ size = sizeof(struct dev_pager);
+ dev_pager_zone = zinit(size,
+ (vm_size_t) size * 1000,
+ PAGE_SIZE,
+ FALSE,
+ "device pager structures");
+
+ /*
+ * Initialize the name port hashing stuff.
+ */
+ dev_pager_hash_init();
+}