//------------------------------------------------------------------- // mymmfb.c // // This character driver gives an application direct access to // the display frame-buffer memory at a predetermined address, // but instead of doing this via the 'mmap()' system call, the // required page-directory entries are directly installed when // the device file is opened (and removed when the device file // is closed). Doing this is a bold and risky scheme, because // it completely bypasses the kernel memory-management system, // and there is no guarantee that the kernel will never try to // 'overwrite' our assigned frame-buffer region. However, for // purposes of exploration it is reasonable to test this idea. // One advantage it offers over the Linux 'mmap()' function is // that no additional ram is consumed (by creating page-tables // and a 'vm_area_struct' object). But disadvantages exist as // well, which we can explore in our class lectures/exercises. // // NOTE: Developed and tested with Linux kernel version 2.4.20 // // programmer: ALLAN CRUSE // written on: 28 APR 2003 //------------------------------------------------------------------- #define __KERNEL__ #define MODULE #include // for init_module() #include // for pci_find_class() #include // for remap_page_range() #define SUCCESS 0 #define VGA_CLASS 0x030000 // for VGA Display Adapters #define USERADDR 0x10000000 // fixed frame-buffer address static char modname[] = "mysis"; static char devname[] = "/dev/vram"; static int my_major = 99; // driver's assigned major number static unsigned long fb_base; // physical address of frame buffer static unsigned long fb_size; // size of the frame buffer (bytes) static 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 = fb_size + offset; break; // SEEK_END } if (( newpos < 0 )||( newpos > fb_base )) return -EINVAL; file->f_pos = newpos; return newpos; } static int my_open( struct inode *inode, struct file *file ) { unsigned long *pgd = (unsigned long*)current->mm->pgd; unsigned long physaddr = fb_base; unsigned long virtaddr = USERADDR; int entry_index, entry_count, i; // map the frame-buffer to the predetermined user-space address entry_index = virtaddr >> 22; // index of starting entry entry_count = fb_size >> 22; // divide by 4MB page-size for (i = 0; i < entry_count; i++) { pgd[ entry_index ] = physaddr | 0x87; // Present, 4MB-entry physaddr += (4<<20); // advance 4 megabytes ++entry_index; } return SUCCESS; } static int my_release( struct inode *inode, struct file *file ) { unsigned long *pgd = (unsigned long*)current->mm->pgd; unsigned long virtaddr = USERADDR; int entry_index, entry_count, i; // unmap the frame-buffer at the agreed user-space address entry_index = virtaddr >> 22; // index of starting entry entry_count = fb_size >> 22; // divide by 4MB page-size for (i = 0; i < entry_count; i++) { pgd[ entry_index ] = 0; // Page Not Present ++entry_index; } return SUCCESS; } static struct file_operations my_fops = { owner: THIS_MODULE, open: my_open, llseek: my_llseek, release: my_release, }; int init_module( void ) { struct pci_dev *devp = NULL; u32 save, temp, i; printk( "<1>\nInstalling \'%s\' (major=%d)\n", modname, my_major ); // detect VGA device and store frame-buffer address devp = pci_find_class( VGA_CLASS, devp ); if ( !devp ) return -ENODEV; fb_base = pci_resource_start( devp, 0 ); // determine the frame-buffer's size temp = ~0; pci_read_config_dword( devp, 0x10, &save ); pci_write_config_dword( devp, 0x10, temp ); pci_read_config_dword( devp, 0x10, &temp ); pci_write_config_dword( devp, 0x10, save ); for (i = 4; i < 32; i++) if ( temp & (1< VGA Device: %s \n", devp->name ); printk( "<1> Frame-Buffer at %08X (%d MB)\n", fb_base, fb_size>>20 ); // register this device-driver's operations with the kernel return register_chrdev( my_major, modname, &my_fops ); } void cleanup_module( void ) { unregister_chrdev( my_major, modname ); printk( "<1>Removing \'%s\' module\n", modname ); } MODULE_LICENSE("GPL");