/* * vmdisk.c - LilyVM disk driver * * Copyright 2004 Hideki EIRAKU * Licensed under the GPL */ /* gcc -DMODULE -D__KERNEL__ -I linux/linux-2.4.22/include/ -Wall -c vmdisk.c */ #include #include #include #include #include #define DEVICE_NAME "vmdisk" static int Major; static int *vmdisk_sizes, *vmdisk_blksizes; static int num_disks; static int device_open (struct inode *inode, struct file *file); static struct block_device_operations bd_op = { .open = device_open, }; #define lilyvm_call_int (*(int(**)(int,...))0xbfafefac) #define lilyvm_call_longlong (*(long long(**)(int,...))0xbfafef9c) #define lilyvm_call lilyvm_call_int #define LILYVM_CALL_VMDISK_NUM_DISKS 3 #define LILYVM_CALL_VMDISK_GETSIZE 4 #define LILYVM_CALL_VMDISK_PREAD 5 #define LILYVM_CALL_VMDISK_PWRITE 6 static int vmdisk_make_request (request_queue_t *q, int rw, struct buffer_head *sbh) { unsigned int minor, len; unsigned long long offset; unsigned char *p; minor = MINOR (sbh->b_rdev); if (minor >= num_disks) goto fail; if (rw == READA) rw = READ; if (rw != READ && rw != WRITE) goto fail; offset = sbh->b_rsector << 9; len = sbh->b_size; if ((offset + len) >= (vmdisk_sizes[minor] << BLOCK_SIZE_BITS)) goto fail; p = bh_kmap (sbh); if (sbh->b_size > 0) { if (rw == READ) { lilyvm_call (LILYVM_CALL_VMDISK_PREAD, minor, p, len, offset); } else { lilyvm_call (LILYVM_CALL_VMDISK_PWRITE, minor, p, len, offset); } } sbh->b_end_io (sbh, 1); return 0; fail: buffer_IO_error (sbh); return 0; } int init_module (void) { int i; Major = register_blkdev (0, DEVICE_NAME, &bd_op); if (Major < 0) { printk (KERN_ALERT "Registering the block device failed with %d\n", Major); return Major; } num_disks = lilyvm_call (LILYVM_CALL_VMDISK_NUM_DISKS); vmdisk_sizes = kmalloc (sizeof (*vmdisk_sizes) * num_disks, GFP_KERNEL); if (vmdisk_sizes == NULL) return -ENOMEM; vmdisk_blksizes = kmalloc (sizeof (*vmdisk_blksizes) * num_disks, GFP_KERNEL); if (vmdisk_blksizes == NULL) { kfree (vmdisk_sizes); return -ENOMEM; } printk (KERN_ALERT "%s: major %d %d disks\n", DEVICE_NAME, Major, num_disks); for (i = 0; i < num_disks; i++) { vmdisk_sizes[i] = (lilyvm_call_longlong (LILYVM_CALL_VMDISK_GETSIZE, i) >> BLOCK_SIZE_BITS) + 1; printk (KERN_ALERT "%s: minor %d %d blocks\n", DEVICE_NAME, i, vmdisk_sizes[i] - 1); } memset (vmdisk_blksizes, 0, sizeof vmdisk_blksizes); blk_size[Major] = vmdisk_sizes; blksize_size[Major] = vmdisk_blksizes; blk_queue_make_request (BLK_DEFAULT_QUEUE (Major), &vmdisk_make_request); for (i = 0; i < num_disks; i++) register_disk (NULL, MKDEV (Major, i), 1, &bd_op, 0); return 0; } void cleanup_module (void) { int ret; ret = unregister_blkdev (Major, DEVICE_NAME); if (ret < 0) printk (KERN_ALERT "Error in unregister_blkdev: %d\n", ret); else printk (KERN_ALERT "%s: removed\n", DEVICE_NAME); } static int device_open (struct inode *inode, struct file *file) { unsigned int minor; minor = MINOR (inode->i_rdev); if (minor < num_disks) return 0; else return -ENXIO; }