//------------------------------------------------------------------- // mynopage.c // // This character driver is a revision of our earlier 'mysis.c' // module. It provides the same memory-mapping functionality, // but does not use 'remap_page_range()' to perform all of the // page-directory and page-table modifications at the time the // 'mmap()' system-call is issued by an application. Instead, // it installs a 'nopage()' callback-function, to modify those // page-directory and page-table entries that are needed, when // needed, as indicated by the occurrence of page-faults. The // previous 'mmwhere.cpp' application may be used to test this // driver. // // programmer: ALLAN CRUSE // written on: 28 APR 2003 //------------------------------------------------------------------- #define __KERNEL__ #define MODULE #include // for init_module() #include // for pci_find_class() #include // for register_chrdev() #include // for remap_page_range() #define SUCCESS 0 #define VGA_CLASS 0x030000 // for VGA display-adapters static char modname[] = "mynopage"; static int my_major = 99; static unsigned long fb_base; // initialized by 'init_module()' static unsigned long fb_size; // initialized by 'init_module()' static unsigned long region_length; // initialized by 'my_mmap()' static unsigned long region_origin; // initialized by 'my_mmap()' static unsigned long virt_address; // initialized by 'my_mmap()' static unsigned long phys_address; // initialized by 'my_mmap()' static struct page * my_vma_nopage( struct vm_area_struct *vma, unsigned long address, int write_access ) { struct page *pageptr = NULL; unsigned long where = address + ( phys_address - virt_address ); printk( "<1> my_vma_nopage: address=%08X \n", address ); pageptr = virt_to_page( __va( where ) ); get_page( pageptr ); return pageptr; } static struct vm_operations_struct my_vm_ops = { nopage: my_vma_nopage, }; static int my_mmap( struct file *file, struct vm_area_struct *vma ) { region_length = vma->vm_end - vma->vm_start; region_origin = vma->vm_pgoff*PAGE_SIZE; virt_address = vma->vm_start; phys_address = fb_base + region_origin; printk( "<1> my_mmap: %08X", phys_address ); printk( "-%08X ", phys_address + region_length ); printk( "to %08X", virt_address ); printk( "-%08X \n", virt_address + region_length ); if ( region_origin + region_length > fb_size ) return -EINVAL; vma->vm_flags |= VM_RESERVED; // we are deferring the actual remapping until page-faults occur // if ( remap_page_range( virt_address, phys_address, // region_length, vma->vm_page_prot ) < 0 ) // return -EAGAIN; // we install our 'nopage' handler for page-fault exceptions vma->vm_ops = &my_vm_ops; return SUCCESS; } static loff_t my_llseek( struct file *file, loff_t offset, int whence ) { loff_t newpos = -1; switch( whence ) { case 0: newpos = offset; break; case 1: newpos = file->f_pos + offset; break; case 2: newpos = fb_size + offset; break; } if (( newpos < 0 )||( newpos > fb_size )) return -EINVAL; file->f_pos = newpos; return newpos; } static struct file_operations my_fops = { owner: THIS_MODULE, llseek: my_llseek, mmap: my_mmap, }; 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");