1 / 25

Looking at kernel objects

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 .

sovann
Download Presentation

Looking at kernel objects

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Looking at kernel objects How a character-mode Linux device driver can be useful in viewing a ‘net_device’ structure

  2. 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

  3. 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()

  4. 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

  5. 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

  6. 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’)

  7. 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

  8. ‘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

  9. 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

  10. 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()

  11. 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[ ]’

  12. 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

  13. 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)

  14. 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

  15. 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

  16. 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

  17. ‘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 );

  18. 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’

  19. 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;

  20. 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

  21. ‘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; }

  22. 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

  23. 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)

  24. 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

  25. 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’)

More Related