summaryrefslogtreecommitdiff
path: root/device
diff options
context:
space:
mode:
Diffstat (limited to 'device')
-rw-r--r--device/ds_routines.c55
-rw-r--r--device/ds_routines.h3
-rw-r--r--device/intr.c209
-rw-r--r--device/ramdisk.c160
-rw-r--r--device/ramdisk.h47
5 files changed, 474 insertions, 0 deletions
diff --git a/device/ds_routines.c b/device/ds_routines.c
index 1fabec3c..c1cb9d43 100644
--- a/device/ds_routines.c
+++ b/device/ds_routines.c
@@ -318,6 +318,43 @@ ds_device_map (device_t dev, vm_prot_t prot, vm_offset_t offset,
offset, size, pager, unmap);
}
+io_return_t
+experimental_device_intr_register (ipc_port_t master_port, int line,
+ int id, int flags, ipc_port_t receive_port)
+{
+#ifdef MACH_XEN
+ return D_INVALID_OPERATION;
+#else /* MACH_XEN */
+ extern int install_user_intr_handler (unsigned int line,
+ unsigned long flags,
+ ipc_port_t dest);
+ io_return_t ret;
+
+ /* Open must be called on the master device port. */
+ if (master_port != master_device_port)
+ return D_INVALID_OPERATION;
+
+ /* XXX: move to arch-specific */
+ if (line < 0 || line >= 16)
+ return D_INVALID_OPERATION;
+
+ ret = insert_intr_entry (line, receive_port);
+ if (ret)
+ return ret;
+ // TODO The original port should be replaced
+ // when the same device driver calls it again,
+ // in order to handle the case that the device driver crashes and restarts.
+ ret = install_user_intr_handler (line, flags, receive_port);
+
+ /* If the port is installed successfully, increase its reference by 1.
+ * Thus, the port won't be destroyed after its task is terminated. */
+ if (ret == 0)
+ ip_reference (receive_port);
+
+ return ret;
+#endif /* MACH_XEN */
+}
+
boolean_t
ds_notify (mach_msg_header_t *msg)
{
@@ -1798,6 +1835,24 @@ device_writev_trap (mach_device_t device, dev_mode_t mode,
return (result);
}
+kern_return_t
+experimental_device_intr_enable(ipc_port_t master_port, int line, char status)
+{
+#ifdef MACH_XEN
+ return D_INVALID_OPERATION;
+#else /* MACH_XEN */
+ if (master_port != master_device_port)
+ return D_INVALID_OPERATION;
+
+ if (status)
+ /* TODO: better name for generic-to-arch-specific call */
+ enable_irq (line);
+ else
+ disable_irq (line);
+ return 0;
+#endif /* MACH_XEN */
+}
+
struct device_emulation_ops mach_device_emulation_ops =
{
(void*) mach_device_reference,
diff --git a/device/ds_routines.h b/device/ds_routines.h
index c0543cbc..e9f115fc 100644
--- a/device/ds_routines.h
+++ b/device/ds_routines.h
@@ -83,4 +83,7 @@ io_return_t ds_device_writev_trap(
io_buf_vec_t *iovec,
vm_size_t count);
+/* XXX arch-specific */
+extern ipc_port_t intr_rcv_ports[16];
+
#endif /* DS_ROUTINES_H */
diff --git a/device/intr.c b/device/intr.c
new file mode 100644
index 00000000..476625cb
--- /dev/null
+++ b/device/intr.c
@@ -0,0 +1,209 @@
+#include <device/intr.h>
+#include <device/ds_routines.h>
+#include <kern/queue.h>
+#include <kern/printf.h>
+
+#ifndef MACH_XEN
+// TODO this is only for x86 system
+#include <i386/spl.h>
+#define cli() __asm__ __volatile__ ("cli": : :"memory")
+
+static boolean_t deliver_intr (int line, ipc_port_t dest_port);
+
+struct intr_entry
+{
+ queue_chain_t chain;
+ ipc_port_t dest;
+ int line;
+ /* The number of interrupts occur since last run of intr_thread. */
+ int interrupts;
+};
+
+static queue_head_t intr_queue;
+/* The total number of unprocessed interrupts. */
+static int tot_num_intr;
+
+static struct intr_entry *
+search_intr (int line, ipc_port_t dest)
+{
+ struct intr_entry *e;
+ queue_iterate (&intr_queue, e, struct intr_entry *, chain)
+ {
+ if (e->dest == dest && e->line == line)
+ return e;
+ }
+ return NULL;
+}
+
+/* This function can only be used in the interrupt handler. */
+void
+queue_intr (int line, ipc_port_t dest)
+{
+ extern void intr_thread ();
+ struct intr_entry *e;
+
+ spl_t s = splhi();
+ cli ();
+ e = search_intr (line, dest);
+ assert (e);
+ e->interrupts++;
+ tot_num_intr++;
+ splx(s);
+
+ thread_wakeup ((event_t) &intr_thread);
+}
+
+/* insert an interrupt entry in the queue.
+ * This entry exists in the queue until
+ * the corresponding interrupt port is removed.*/
+int
+insert_intr_entry (int line, ipc_port_t dest)
+{
+ int err = 0;
+ struct intr_entry *e, *new;
+ int free = 0;
+ spl_t s;
+
+ new = (struct intr_entry *) kalloc (sizeof (*new));
+ if (new == NULL)
+ return D_NO_MEMORY;
+
+ /* check whether the intr entry has been in the queue. */
+
+ s = splhi();
+ cli ();
+ e = search_intr (line, dest);
+ if (e)
+ {
+ printf ("the interrupt entry for line %d and port %p has been inserted\n",
+ line, dest);
+ free = 1;
+ err = D_ALREADY_OPEN;
+ goto out;
+ }
+ new->line = line;
+ new->dest = dest;
+ new->interrupts = 0;
+ queue_enter (&intr_queue, new, struct intr_entry *, chain);
+out:
+ splx(s);
+ if (free)
+ kfree ((vm_offset_t) new, sizeof (*new));
+ return err;
+}
+
+/* this function should be called when line is disabled. */
+void mark_intr_removed (int line, ipc_port_t dest)
+{
+ struct intr_entry *e;
+
+ e = search_intr (line, dest);
+ if (e)
+ e->dest = NULL;
+}
+
+void
+intr_thread ()
+{
+ struct intr_entry *e;
+ int line;
+ ipc_port_t dest;
+ queue_init (&intr_queue);
+ spl_t s;
+
+ for (;;)
+ {
+ assert_wait ((event_t) &intr_thread, FALSE);
+ s = splhi();
+ cli();
+ while (tot_num_intr)
+ {
+ int del = 0;
+
+ queue_iterate (&intr_queue, e, struct intr_entry *, chain)
+ {
+ /* if an entry doesn't have dest port,
+ * we should remove it. */
+ if (e->dest == NULL)
+ {
+ clear_wait (current_thread (), 0, 0);
+ del = 1;
+ break;
+ }
+
+ if (e->interrupts)
+ {
+ clear_wait (current_thread (), 0, 0);
+ line = e->line;
+ dest = e->dest;
+ e->interrupts--;
+ tot_num_intr--;
+
+ splx(s);
+ deliver_intr (line, dest);
+ s = splhi();
+ cli();
+ }
+ }
+
+ /* remove the entry without dest port from the queue and free it. */
+ if (del)
+ {
+ assert (!queue_empty (&intr_queue));
+ queue_remove (&intr_queue, e, struct intr_entry *, chain);
+
+ splx(s);
+ kfree ((vm_offset_t) e, sizeof (*e));
+ s = splhi();
+ cli();
+ }
+ }
+ splx(s);
+ thread_block (NULL);
+ }
+}
+
+static boolean_t
+deliver_intr (int line, ipc_port_t dest_port)
+{
+ ipc_kmsg_t kmsg;
+ mach_intr_notification_t *n;
+ mach_port_t dest = (mach_port_t) dest_port;
+
+ if (dest == MACH_PORT_NULL)
+ return FALSE;
+
+ kmsg = ikm_alloc(sizeof *n);
+ if (kmsg == IKM_NULL)
+ return FALSE;
+
+ ikm_init(kmsg, sizeof *n);
+ n = (mach_intr_notification_t *) &kmsg->ikm_header;
+
+ mach_msg_header_t *m = &n->intr_header;
+ mach_msg_type_t *t = &n->intr_type;
+
+ m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0);
+ m->msgh_size = sizeof *n;
+ m->msgh_seqno = INTR_NOTIFY_MSGH_SEQNO;
+ m->msgh_local_port = MACH_PORT_NULL;
+ m->msgh_remote_port = MACH_PORT_NULL;
+ m->msgh_id = MACH_INTR_NOTIFY;
+
+ t->msgt_name = MACH_MSG_TYPE_INTEGER_32;
+ t->msgt_size = 32;
+ t->msgt_number = 1;
+ t->msgt_inline = TRUE;
+ t->msgt_longform = FALSE;
+ t->msgt_deallocate = FALSE;
+ t->msgt_unused = 0;
+
+ n->intr_header.msgh_remote_port = dest;
+ n->line = line;
+
+ ipc_port_copy_send (dest_port);
+ ipc_mqueue_send_always(kmsg);
+
+ return TRUE;
+}
+#endif /* MACH_XEN */
diff --git a/device/ramdisk.c b/device/ramdisk.c
new file mode 100644
index 00000000..daf70436
--- /dev/null
+++ b/device/ramdisk.c
@@ -0,0 +1,160 @@
+#include <mach/vm_param.h>
+#include <machine/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_user.h>
+#include <device/device_types.h>
+#include <device/ds_routines.h>
+#include <device/conf.h>
+#include <device/ramdisk.h>
+#include <kern/printf.h>
+#include <string.h>
+
+static struct ramdisk {
+ void *data;
+ vm_size_t size;
+} ramdisk[RAMDISK_MAX];
+
+static int ramdisk_num = 0;
+
+/* Initial ramdisks are created from the boot scripts */
+int ramdisk_create(vm_size_t size, const void *initdata, int *out_no)
+{
+ struct ramdisk *rd = &ramdisk[ramdisk_num];
+ int err;
+
+ if(ramdisk_num >= RAMDISK_MAX)
+ return -1;
+
+ /* allocate the memory */
+ rd->size = round_page(size);
+ err = kmem_alloc(kernel_map, (vm_offset_t *) &rd->data, rd->size);
+ if(err != KERN_SUCCESS)
+ return err;
+
+ /* initialize */
+ if(initdata)
+ memcpy(rd->data, initdata, rd->size);
+ else
+ memset(rd->data, 0, rd->size);
+
+ /* report */
+ if(out_no) *out_no = ramdisk_num;
+ printf("%s%d: %lu bytes @%p\n", RAMDISK_NAME, ramdisk_num,
+ (unsigned long) rd->size, rd->data);
+
+ ramdisk_num++;
+ return KERN_SUCCESS;
+}
+
+/* On d_open() we just check whether the ramdisk exists */
+int ramdisk_open(dev_t dev, int mode, io_req_t ior)
+{
+ return (dev < ramdisk_num) ? D_SUCCESS : D_NO_SUCH_DEVICE;
+}
+
+/* d_getstat() is used to query the device characteristics */
+int ramdisk_getstat(dev_t dev, dev_flavor_t flavor, dev_status_t status,
+ mach_msg_type_number_t *status_count)
+{
+ switch(flavor) {
+ case DEV_GET_SIZE:
+ status[DEV_GET_SIZE_DEVICE_SIZE] = ramdisk[dev].size;
+ status[DEV_GET_SIZE_RECORD_SIZE] = RAMDISK_BLOCKSZ;
+ *status_count = DEV_GET_SIZE_COUNT;
+ return D_SUCCESS;
+
+ case DEV_GET_RECORDS:
+ status[DEV_GET_RECORDS_DEVICE_RECORDS]
+ = ramdisk[dev].size / RAMDISK_BLOCKSZ;
+ status[DEV_GET_RECORDS_RECORD_SIZE] = RAMDISK_BLOCKSZ;
+ *status_count = DEV_GET_RECORDS_COUNT;
+ return D_SUCCESS;
+ }
+ return D_INVALID_OPERATION;
+}
+
+/* TODO: implement freeramdisk with setstat() ? */
+
+/* Check the given io request and compute a pointer to the ramdisk data and the
+ * amount to be handled. */
+static int ramdisk_ioreq(int dev, io_req_t ior, void **data, int *amt)
+{
+ vm_offset_t ofs = ior->io_recnum * RAMDISK_BLOCKSZ;
+ if(ofs >= ramdisk[dev].size)
+ return D_INVALID_RECNUM;
+
+ *data = (char*) ramdisk[dev].data + ofs;
+ *amt = ior->io_count;
+ if(ofs + *amt > ramdisk[dev].size)
+ *amt = ramdisk[dev].size - ofs;
+
+ return KERN_SUCCESS;
+}
+
+/* Copy data from a vm_map_copy by mapping it temporarily. */
+static int mem_map_cpy(void *dst, vm_map_copy_t src, int amt)
+{
+ vm_offset_t srcaddr;
+ int err;
+
+ err = vm_map_copyout(device_io_map, &srcaddr, src);
+ if (err != KERN_SUCCESS)
+ return err;
+
+ memcpy(dst, (void *) srcaddr, amt);
+ vm_deallocate(device_io_map, srcaddr, amt);
+ return KERN_SUCCESS;
+}
+
+int ramdisk_read(dev_t dev, io_req_t ior)
+{
+ void *data;
+ int amt, err;
+
+ err = ramdisk_ioreq(dev, ior, &data, &amt);
+ if(err != KERN_SUCCESS)
+ return err;
+
+ err = device_read_alloc (ior, ior->io_count);
+ if (err != KERN_SUCCESS)
+ return err;
+
+ memcpy(ior->io_data, data, amt);
+ ior->io_residual = ior->io_count - amt;
+
+ return D_SUCCESS;
+}
+
+int ramdisk_write(dev_t dev, io_req_t ior)
+{
+ void *data;
+ int amt, err;
+
+ err = ramdisk_ioreq(dev, ior, &data, &amt);
+ if(err != KERN_SUCCESS)
+ return err;
+
+ if (!(ior->io_op & IO_INBAND)) {
+ /* Out-of-band data is transmitted as a vm_map_copy */
+ err = mem_map_cpy(data, (vm_map_copy_t) ior->io_data, amt);
+ if(err != KERN_SUCCESS)
+ return err;
+ } else {
+ /* In-band data can be accessed directly */
+ memcpy(data, ior->io_data, amt);
+ }
+
+ ior->io_residual = ior->io_count - amt;
+ return D_SUCCESS;
+}
+
+vm_offset_t ramdisk_mmap(dev_t dev, vm_offset_t off, vm_prot_t prot)
+{
+ if(dev >= ramdisk_num)
+ return -1;
+ if(off >= ramdisk[dev].size)
+ return -1;
+
+ return pmap_phys_to_frame(kvtophys((vm_offset_t) ramdisk[dev].data + off));
+}
+
diff --git a/device/ramdisk.h b/device/ramdisk.h
new file mode 100644
index 00000000..3a232460
--- /dev/null
+++ b/device/ramdisk.h
@@ -0,0 +1,47 @@
+#ifndef _KERN_RAMDISK_H_
+#define _KERN_RAMDISK_H_
+
+#include <vm/pmap.h>
+#include <device/io_req.h>
+#include <device/conf.h>
+
+/* Maximum number of ramdisk devices */
+#define RAMDISK_MAX 4
+
+/* The block size used (userspace requires 512) */
+#define RAMDISK_BLOCKSZ 512
+
+/* Name associated to the ramdisk major */
+#define RAMDISK_NAME "rd"
+#define RAMDISK_NAMESZ (sizeof RAMDISK_NAME + sizeof (int) * 3 + 1)
+
+/* Create a new ramdisk of the given size. On success, if out_no and/or out_ptr
+ * are not NULL, the device number and pointer to the ramdisk's data are stored
+ * there. Returns D_SUCCESS or D_NO_MEMORY. */
+int ramdisk_create(vm_size_t size, const void *initdata, int *out_no);
+
+/* Device operations */
+int ramdisk_open(dev_t, int, io_req_t);
+int ramdisk_getstat(dev_t, dev_flavor_t, dev_status_t, mach_msg_type_number_t *);
+int ramdisk_read(dev_t, io_req_t);
+int ramdisk_write(dev_t, io_req_t);
+vm_offset_t ramdisk_mmap(dev_t, vm_offset_t, vm_prot_t);
+
+/* dev_ops initializer to be used from <machine>/conf.c */
+#define RAMDISK_DEV_OPS { \
+ .d_name = RAMDISK_NAME, \
+ .d_open = ramdisk_open, \
+ .d_close = nulldev_close, \
+ .d_read = ramdisk_read, \
+ .d_write = ramdisk_write, \
+ .d_getstat = ramdisk_getstat, \
+ .d_setstat = nulldev_setstat, \
+ .d_mmap = ramdisk_mmap, \
+ .d_async_in = nodev, \
+ .d_reset = nulldev, \
+ .d_port_death = nulldev_portdeath, \
+ .d_subdev = 0, \
+ .d_dev_info = nodev, \
+ }
+
+#endif