250 likes | 474 Views
Looking at kernel objects. How a character-mode Linux device driver can be useful in viewing a ‘net_device’ structure. Our ‘/proc/netdevs’ pseudo-file. We wrote a Loadable Kernel Module that creates a pseudo-file allowing users to see some information about the kernel’s data .
E N D
Looking at kernel objects How a character-mode Linux device driver can be useful in viewing a ‘net_device’ structure
Our ‘/proc/netdevs’ pseudo-file • We wrote a Loadable Kernel Module that creates a pseudo-file allowing users to see some information about the kernel’s data struct net_device struct net_device struct net_device lo eth0 eth1
The LKM’s source-code netdevs.c #include <linux/module.h> #include <linux/proc_fs.h> char modname[ ] = “netdevs”; … MODULE_LICENSE(“GPL”); some header-files some global data my_get_info() this module’s ‘payload’ function module_init() required module administration functions module_exit()
User-space/Kernel-space user-space (restricted privileges) kernel-space (unrestricted privileges) operating system kernel LINUX open read write (etc) standard runtime library application program ‘cat’ netdevs.ko installable module privilege barrier
Linux device drivers • There is another kind of LKM, written to control the system’s hardware devices rather than merely to expose information • Its code-structure will depend on the type of hardware device it is intended to control • ‘character’ devices • ‘block’ devices • ‘network interface’ devices
Hardware’s operations • In order to write the software that controls a particular device, the programmer needs to know details about its capabilities and about its mechanisms for being controlled • This information is found in programming manuals, produced by the manufacturer • These manuals may or not be available to the general public (often are ‘proprietary’)
A few devices are ‘simple’ • If a particular device’s operations are very simple to understand, we may not need to consult the manufacturer’s documentation (just use ‘common sense’ and guesswork) • EXAMPLE: The computer system’s main memory offers us an easy-to-understand hardware component for which we can directly write a device-driver module
‘dram.c’ • Two benefits of having a device-driver for the computer’s physical memory are: • We can directly look at kernel data-structures using ‘unprivileged’ application-programs • We get to see the general code-structure for Linux device-drivers in the simplest of cases
Using our ‘fileview’ utility • Our previous ‘netdevs.c’ module tells us where the ‘struct net_device’ objects are located in our system’s physical memory • So we can use ‘fileview’ to inspect these kernel data-structures once we’ve loaded our ‘dram.ko’ device-driver into the kernel Timeout for an in-class demonstration
The code-structure for ‘dram.c’ dram.c #include <linux/module.h> #include <linux/highmem.h> … char modname[ ] = “dram”; int my_major = 85; … MODULE_LICENSE(“GPL”); some header-files some global data this module’s ‘payload’ (its ‘method’ functions and its ‘file_operations’ structure) my_read() my_llseek() my_fops required module administration functions module_init() module_exit()
Kernel’s ‘helper-functions’ • The Linux kernel provides quite a few aids to the authors of device-driver code: • ‘register_chrdev()’ and ‘unregister_chrdev()’ • ‘copy_to_user()’ and ‘copy_from_user()’ • ‘kmap()’ and ‘kunmap()’ • The kernel also exports some of its ‘global variables’ (which drivers can reference): • ‘num_physpages’ and ‘mem_map[ ]’
Memory-mapping = persistent mapping = transient mappings kernel space HMA user space 896-MB physical RAM There is more physical RAM in our classroom’s systems than can be ‘mapped’ into the available address-range for kernel virtual addresses CPU’s virtual address-space
What does ‘kmap()’ do? • The ‘kmap()’ helper-function allows your driver to create a temporary mapping for any one 4-KB ‘page’ of physical memory to some unused virtual address in kernel-space, then later ‘kunmap()’ lets your driver discard that mapping when it’s no longer needed (so there will be available that kernel-address for later reuse)
The ‘mem_map[ ]’ array • The kernel creates an array of structures, named ‘mem_map[ ]’, whose entries hold detailed information about how each 4KB page of physical RAM is now being used • The global variable named ‘phys_mem’ stores the total number of array-entries, and hence can be used by your driver to determine the amount of installed RAM
The function-prototypes void *kmap( struct page *page_ptr ); This function accepts a pointer to an entry of type ‘struct page’ in the kernel’s ‘mem_map[ ]’ array, and returns a kernel address where that page of physical RAM has been temporarily ‘mapped’ void kunmap( void *virt_addr ); This function accepts an address where the kernel temporarily has mapped a page of physical RAM and it deletes that mapping, thus freeing the address for reuse later when the kernel is asked to setup a different temporary mapping of physical RAM into kernel-space
Our driver ‘read()’ method… • It has to support the traditional stream-of-bytes paradigm, so a ‘sanity check’ will be needed for the caller’s argument-values ssize_t my_read( struct file *file, char *buf, size_t count, loff_t *pos ); number of bytes that caller wants to read the current position of the file-pointer // There’s nothing to be ‘read’ beyond the end of physical RAM if ( *pos >= dram_size ) return 0; Physical RAM dram_size *pos
‘read()’ method (continued) • Our driver has to accommodate the CPU’s ‘page-granular’ memory-architecture, and the ‘kmap()’ function’s ability to map one-page-at-a-time *pos int page_number = *pos / PAGE_SIZE; int page_indent = *pos % PAGE_SIZE; if ( page_indent + count > PAGE_SIZE ) count = PAGE_SIZE – page_indent; struct page *pp = &mem_map[ page_number ]; void *from = kmap( pp ) + page_indent; int more = copy_to_user( buf, from, count );
Another argument-value pitfall… • It is possible that the caller did not supply a large-enough buffer for the amount of data that is supposed to be transferred • That potential ‘buffer-overflow’ problem could be detected during execution of the ‘copy_to_user()’ helper-function, if fewer than ‘count’ bytes can be copied without triggering a ‘segmentation violation’
The driver’s solution… • The ‘copy_to_user()’ function return the number of bytes that remain to be copied (normally this is zero: all copying got done) • But if it’s NOT zero, the driver’s duty is to notify the user that a ‘segmentation fault’ error occurred – but AFTER ‘kunmap()’ int more = copy_to_user( buf, from, count ); // first unmap the page, then notify the user if necessary kunmap( pp ); if ( more ) return –EFAULT;
The ‘llseek()’ method • Our ‘dram.c’ driver needs to implement its own ‘llseek()’ function, in order to allow an application-program to ‘seek’ to the end of the device-file (so it will know what total amount of physical RAM is installed) • This feature is used by our ‘fileview’ tool when a user hits the <END>-key, and to display the total size for the device-file
‘llseek()’ implementation unsigned int dram_size; // equals PAGE_SIZE * num_physpages 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 = dram_size + offset; break; // SEEK_END } if (( newpos < 0 )||( newpos > dram_size )) return –EINVAL; file->f_pos = newpos; return newpos; }
Demo: ‘vwnetdev.cpp’ • This application makes use of information from the ‘/proc/netdevs’ pseudo-file, plus the information that can be read from the computer’s physical memory using the capabilities implemented by our ‘dram.c’ device-driver • It lets a user view the ‘struct net_device’ object for a specified network-interface
Our ‘offsets.c’ module • This module creates a pseudo-file that can help a user to interpret the hexadecimal output produced by ‘vwnetdev’ • It shows the locations within a ‘net_device’ structure for some structure-members of particular significance for network device drivers (which we shall explore next time)
In-class exercise #1 • One of the ‘struct net_device’ fields that is significant in a Linux network device driver is the ‘get_stats’ function-pointer field • Modify our ‘offsets.c’ module so that the pseudo-file this module creates will include the offset for the ‘get_stats’ member • Turn in a printout of the enhanced output (created using our ‘ljpages’ printing tool); be sure your name is handwritten on it
In-class exercise #2 • Take a look at our kernel’s definition for a ‘struct net_device’ object, in header-file: </usr/src/linux/include/linux/netdevice.h> and identify three additional member-fields that you would like to show the offsets for • Then implement the display of those three offsets (by adding code to our ‘offsets.c’)