looking at kernel objects how a character-mode linux device driver can be useful in viewing a...
Post on 21-Dec-2015
221 views
TRANSCRIPT
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
lo struct net_device
eth0 struct net_device
eth1 struct net_device
The LKM’s source-code
#include <linux/module.h>#include <linux/proc_fs.h>
char modname[ ] = “netdevs”;…
MODULE_LICENSE(“GPL”);
netdevs.c
module_init()
module_exit()
my_get_info()
some header-files
some global data
this module’s ‘payload’ function
required module administration functions
User-space/Kernel-space
‘cat’ application program
open read write (etc)
standard runtime library
operating system kernel
netdevs.ko installable module
LINUX
privilege barrier
user-space (restricted privileges)
kernel-space (unrestricted privileges)
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’
#include <linux/module.h>#include <linux/highmem.h>…char modname[ ] = “dram”;int my_major = 85;…
MODULE_LICENSE(“GPL”);
dram.c
module_init()
module_exit()
my_read()
some header-files
some global data
this module’s ‘payload’ (its ‘method’ functions and its ‘file_operations’ structure)
required module administration functions
my_llseek()
my_fops
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
user space
kernel space
CPU’s virtual address-space
HMA
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
= persistent mapping = transient mappings
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 );
// There’s nothing to be ‘read’ beyond the end of physical RAM if ( *pos >= dram_size ) return 0;
number of bytes that caller wants to read
the current position of the file-pointer
Physical RAM
*pos dram_size
‘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_SETcase 1: newpos = file->f_pos + offset; break; // SEEK_CURcase 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’)