diff options
Diffstat (limited to 'device')
-rw-r--r-- | device/ds_routines.c | 55 | ||||
-rw-r--r-- | device/ds_routines.h | 3 | ||||
-rw-r--r-- | device/intr.c | 209 | ||||
-rw-r--r-- | device/ramdisk.c | 160 | ||||
-rw-r--r-- | device/ramdisk.h | 47 |
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 |