diff options
Diffstat (limited to 'device/ramdisk.c')
-rw-r--r-- | device/ramdisk.c | 160 |
1 files changed, 160 insertions, 0 deletions
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)); +} + |