//------------------------------------------------------------------- // mbr.c // // This module implements a character-mode device-driver which // lets applications read the hard disk's Master Boot Record. // // Reference: Draft ANSI ATA/ATAPI-5 Standard v.3 (INCITS-T13) // // NOTE: Developed and tested with Linux kernel version 2.6.10 // // programmer: ALLAN CRUSE // written on: 23 FEB 2005 //------------------------------------------------------------------- #include // for init_module() #include // for register_chrdev() #include // for ndelay() #include // for copy_to_user() #include // for inb(), outb() #define IDE_DATA 0x1F0 #define IDE_ERROR 0x1F1 #define IDE_FEATURES 0x1F1 #define IDE_SECTOR_COUNT 0x1F2 #define IDE_SECTOR_LOW 0x1F3 #define IDE_SECTOR_MID 0x1F4 #define IDE_SECTOR_HIGH 0x1F5 #define IDE_DEVICE 0x1F6 #define IDE_STATUS 0x1F7 #define IDE_COMMAND 0x1F7 #define IDE_DEVICE_CONTROL 0x3F6 #define CMD_READ_SECTOR 0x20 char modname[] = "mbr"; // for displaying module's name char devname[] = "hd"; // for registering this driver int my_major = 97; // note static major assignment loff_t mbr_size = 512; // length of Master Boot Record loff_t my_llseek( struct file *file, loff_t offset, int whence ) { loff_t newpos = -1; switch( whence ) { case 0: newpos = offset; break; // SEEK_SET case 1: newpos = file->f_pos + offset; break; // SEEK_CUR case 2: newpos = mbr_size + offset; break; // SEEK_END } if (( newpos < 0 )||( newpos > mbr_size )) return -EINVAL; file->f_pos = newpos; return newpos; } ssize_t my_read( struct file *file, char *buf, size_t count, loff_t *pos ) { ssize_t retval = 0; char *from; void *kbuf; int more, mbr_read( short *inbuf ); // we cannot read beyond the end-of-file if ( *pos >= mbr_size ) return retval; // allocate a kernel buffer to hold the MBR if ( !(kbuf = vmalloc( 512 )) ) return -ENOMEM; // read the disk's Master Boot Record if ( mbr_read( kbuf ) ) { vfree( kbuf ); return -EIO; } // we cannot return more bytes than the MBR contains if ( *pos + count > mbr_size ) count = mbr_size - *pos; // ok, now transfer count bytes to user-supplied buffer from = kbuf + *pos; more = copy_to_user( buf, from, count ); vfree( kbuf ); if ( more ) return -EFAULT; // otherwise advance file-pointer and report number of bytes read *pos += count; return count; } struct file_operations my_fops = { owner: THIS_MODULE, llseek: my_llseek, read: my_read, }; int init_module( void ) { printk( "<1>\nInstalling \'%s\' module ", modname ); printk( "(major=%d)\n", my_major ); return register_chrdev( my_major, devname, &my_fops ); } void cleanup_module( void ) { unregister_chrdev( my_major, devname ); printk( "<1>Removing \'%s\' module\n", modname ); } MODULE_LICENSE("GPL"); int mbr_read( short *inbuf ) { int timeout, status, error, i; // Hard Disk Device-Selection Protocol // wait until BSY==0 (bit 7) and DREQ==0 (bit 3) timeout = 1000; do { status = inb( IDE_STATUS ); if ( (status & 0x88 ) == 0x00 ) break; } while ( --timeout ); if ( timeout == 0 ) return -EBUSY; // select the disk drive outb( 0xE0, IDE_DEVICE ); // master disk, LBA addressing ndelay( 400 ); // delay for 400 nanoseconds // setup command-parameters and issue 'read' command status = inb( IDE_STATUS ); if ( (status & 0xC8 ) != 0x40 ) return -EFAULT; // drive is now ready for the command outb( 1, IDE_SECTOR_COUNT ); outb( 0, IDE_SECTOR_LOW ); outb( 0, IDE_SECTOR_MID ); outb( 0, IDE_SECTOR_HIGH ); outb( 0xE0, IDE_DEVICE ); outb( CMD_READ_SECTOR, IDE_COMMAND ); error = 0; ndelay( 400 ); // delay for 400 nanoseconds status = inb( IDE_STATUS ); // was any error info posted? if ( (status & 0x81) == 0x01 ) { error = inb( IDE_ERROR ); return error; } // wait for the DRQ indicator timeout = 1000000; do { status = inb( IDE_STATUS ); if ( (status & 0x88) == 0x08 ) break; } while ( --timeout ); if ( timeout == 0 ) return -EFAULT; // transfer data from the controller's sector-buffer for (i = 0; i < 256; i++) inbuf[i] = inw( IDE_DATA ); // read status register (clears any pending interrupt) status = inb( IDE_STATUS ); return 0; //SUCCESS }