lesson03.ppt

advertisement

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 lo struct net_device eth0 struct net_device eth1

The LKM’s source-code

some header-files some global data netdevs.c

#include <linux/module.h>

#include <linux/proc_fs.h> char modname[ ] = “netdevs”;

… my_get_info() this module’s

‘payload’ function required module administration functions module_init() module_exit()

MODULE_LICENSE(“GPL”);

standard runtime library application program

User-space/Kernel-space

user-space

(restricted privileges) kernel-space

(unrestricted privileges) operating system kernel

LINUX open read write

(etc)

‘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 datastructures once we’ve loaded our ‘dram.ko’ device-driver into the kernel

Timeout for an in-class demonstration

The codestructure for ‘dram.c’

some header-files some global data required module administration functions dram.c

#include <linux/module.h>

#include <linux/highmem.h>

… char modname[ ] = “dram”; int my_major = 85;

… my_read() my_llseek() my_fops module_init() module_exit()

MODULE_LICENSE(“GPL”); this module’s

‘payload’

(its ‘method’ functions and its

‘file_operations’ structure)

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

HMA kernel 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 user space

CPU’s virtual address-space

What does ‘kmap()’ do?

• The ‘ kmap ()’ helper-function allows your driver to create a temporary mapping for any one 4KB ‘page’ of physical memory to some unused virtual address in kernelspace, 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-ofbytes 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)

*pos

• Our driver has to accommodate the CPU’s

‘page-granular’ memory-architecture, and the ‘kmap()’ function’s ability to map onepage-at-a-time 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 void int

*pp = &mem_map[ page_number ];

*from = kmap( pp ) + page_indent; more = copy_to_user( buf, from, count );

Another argumentvalue 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 applicationprogram 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’)

Download