looking at kernel objects how a character-mode linux device driver can be useful in viewing a...

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

Post on 21-Dec-2015

221 views

Category:

Documents


0 download

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