Loadable Kernel Modules

Download Report

Transcript Loadable Kernel Modules

Il kernel di Linux
Riferimenti:
– http://www.ltn.lv/~guntis/unix/kernel_modules.ppt (Dzintars
Lepešs, University of Latvia)
– http://freeelectrons.com
Struttura dei sorgenti
•
•
•
•
•
•
•
arch
Codice dipendente dall'architettura
documentation
Documentazione del kernel;
drivers Tutti i device drivers
fs
Filesystems
include File header del kernel
kernel
Scheduler, …
lib
Librerie
• …
• Makefile Makefile principale
Compilazione
Configurazione kernel:
– make xconfig
– oppure make menuconfig
– oppure make oldconfig
Generano il file .config con le impostazioni
scelte.
Compilazione:
make [opzioni]
Compilazione
• make bzImage
– crea il kernel compilato
• make modules
– compila i moduli
• make modules_install
– installa i moduli
• make … -j 4
– compilazione parallela
• make mrproper
– cancellazione file intermedi
Implementazione
• Vengono memorizzati come file oggetto in formato ELF;
• Viene memorizzato l’indirizzo di tutti i simboli esportati
(/proc/syms <2.6 /proc/kallsyms - 2.6);
• Viene momorizzato l’uso e le dipendenze dei moduli usati
(/proc/modules), caricati con insmod o modprobe.
• Per ogni modulo vengono mantenuti:
– Una struttura dati con le informazioni su di esso;
– Una stringa identificativa;
– L’implementazione.
Module Object
Linking e Unlinking di moduli
•
Programs for linking and
unlinking
insmod
– Reads from the name of the module to be linked
– Locates the file containing the module's object code
– Computes the size of the memory area needed to store the module
code, its name, and the module object
– Invokes the create_module( ) system call
– Invokes the query_module( ) system call
– Using the kernel symbol table, the module symbol tables, and the
address returned by the create_module( ) system call, relocates the
object code included in the module's file.
– Allocates a memory area in the User Mode address space and
loads with a copy of the module object
– Invokes the init_module( ) system call, passing to it the address of
the User Mode memory area
– Releases the User Mode memory area and terminates
Programs for linking and
unlinking
• lsmod
reads /proc/modules
• rmmod
– From reads the name of the module to be unlinked.
– Invokes the query_module( )
– Invokes the delete_module( ) system call, with the QM_REFS
subcommand several times, to retrieve dependency information on
the linked modules.
• modprobe
takes care of possible complications due to module dependencies,
uses depmod program and /etc/modules.conf file
Device drivers
• There are two major ways
for a kernel module to talk
to processes:
• To use the proc file
system (/proc directory)
• Through device files (/dev
directory)
• Device driver sits between
some hardware and the
kernel I/O subsystem. Its
purpose is to give the
kernel a consistent
interface to the type of
hardware it "drives".
Driver a caratteri
Accesso tramite flusso sequenziale di caratteri singoli
Individuabili per il loro tipo di file c (ls -l):
crwrw1
crww1
crw1
crwrwrw1
root
jdoe
root
root
uucp 4, 64 Feb 23 2004 /dev/ttyS0
tty 136, 1 Sep 13 06:51 /dev/pts/1
root 13, 32 Feb 23 2004 /dev/input/mouse0
root 1, 3 Feb 23 2004 /dev/null
Esempio: tastiera, mouse, porta parallela, IrDA, porta
Bluetooth, console, terminale...
Driver a blocchi
Accesso casuale a blocchi di dati di dimensioni fisse.
Individuabili per il loro tipo di file b (ls l):
brwrw1 root disk 3, 1 Feb 23 2004 /dev/hda1
Esempi: hard disk e floppy disk, ram disk, dispositivi
loop...
Numeri di device
Ogni device ha due numeri associati:
• major number
– Associato ad ogni driver in maniera unica
• minor number
– Associato ad ogni device in maniera unica
Creazione dei file di device
I file di device non vengono creati al momento di
caricare il driver, devono essere creati esplicitamente:
mknod /dev/<device> [c|b] <major> <minor>
Esempi:
• mknod /dev/ttyS0 c 4 64
• mknod /dev/hda1 b 3 1
Modulo minimale
/* hello.c */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
static int hello_init(void)
{
printk(KERN_ALERT “inizio\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT “fine\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
Funzioni di libreria
• Non si possono usare le funzioni della libreria
standard C come printf(), strcat(), ...
• Linux fornisce alcune funzioni, come ad esempio
printk(), la cui interfaccia e molto simile a quella di
printf().
• Si possono usare solo gli header del kernel!
Compilazione di un kernel module
• A kernel module is not an independent executable, but an
object file which will be linked into the kernel in runtime and
they should be compiled with
–
–
–
–
–
-c flag
_KERNEL_ symbol
MODULE symbol
LINUX symbol
CONFIG_MODVERSIONS symbol
Example of simple char device
/* The necessary header files */
/* Standard in kernel modules */
#include <linux/kernel.h> /* We’re doing kernel work */
#include <linux/module.h> /* Specifically, a module */
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/fs.h>
#include <linux/wrapper.h>
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
#endif
#include <asm/uaccess.h>
#define SUCCESS 0
/* Device Declarations */
/* The name for our device, as it will appear
/* in /proc/devices */
#define DEVICE_NAME "char_dev"
#define BUF_LEN 80
/* Used to prevent */
/* concurent access into the same device */
static int Device_Open = 0;
/* The message the device will give when asked */
static char Message[BUF_LEN];
static char *Message_Ptr;
/* This function is called whenever a process
* attempts to open the device file */
static int device_open(struct inode *inode, struct file *file)
{
static int counter = 0;
#ifdef DEBUG
printk ("device_open(%p,%p)\n", inode, file);
#endif
printk("Device: %d.%d\n“, inode->i_rdev >> 8, inode->i_rdev & 0xFF);
if (Device_Open)
return -EBUSY;
Device_Open++;
sprintf(Message,
counter++,
Message_Ptr = Message;
MOD_INC_USE_COUNT;
return SUCCESS;
}
if (Device_Open)
return -EBUSY;
Device_Open++;
sprintf(Message,
counter++,
Message_Ptr = Message;
MOD_INC_USE_COUNT;
return SUCCESS;
}
static int device_release(struct inode *inode, struct file *file)
{
Device_Open --;
MOD_DEC_USE_COUNT;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
return 0;
#endif
}
static ssize_t device_read(struct file *file,
char *buffer, /* The buffer to fill with data */
size_t length, /* The length of the buffer */
loff_t *offset) /* Our offset in the file */
{
/* Number of bytes actually written to the buffer */
int bytes_read = 0;
/* If we’re at the end of the message, return 0
if (*Message_Ptr == 0)
return 0;
/* Actually put the data into the buffer */
while (length && *Message_Ptr) {
put_user(*(Message_Ptr++), buffer++);
length --;
bytes_read ++;
}
#ifdef DEBUG
printk ("Read %d bytes, %d left\n", bytes_read, length);
#endif
return bytes_read;
}
static ssize_t device_write(struct file *file,
const char *buffer, /* The buffer */
size_t length, /* The length of the buffer */
loff_t *offset) /* Our offset in the file */
{
return -EINVAL;
}
/* Module Declarations */
struct file_operations Fops = {
NULL, /* seek */
device_read,
device_write,
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
device_open,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
NULL, /* flush */
#endif
device_release /* a.k.a. close */
};
/* Initialize the module - Register the character device */
int init_module()
{
/* Register the character device */
Major = module_register_chrdev(0, DEVICE_NAME, &Fops);
/* Negative values signify an error */
if (Major < 0) {
printk ("%s device failed with %d\n", "Sorry, registering the character", Major);
return Major;
}
return 0;
}
/* Cleanup - unregister the appropriate file from /proc */
void cleanup_module()
{
int ret;
/* Unregister the device */
ret = module_unregister_chrdev(Major, DEVICE_NAME);
if (ret < 0)
printk("Error in unregister_chrdev: %d\n", ret);
}
Internal components of the NTbased operating system
Types of Kernel-Mode Drivers
•
Current
Driver
Models
Windows has several different driver models
– Windows Driver Model (WDM) is the generic model
– Specific driver models for popular devices classes
• Storage, Networking, Printing, Imaging, etc...
• Some built on top of WDM. Others run as user-mode services.
• WDM Features
–
–
–
–
–
–
Asynchronous, packet-based I/O
I/O Cancellation
Layering of drivers
Dynamic loading and unloading of drivers
Plug and Play & Power management
Low-level high-performance interfaces
Limitations with Current Models
• Generic driver model (WDM) is too complex
– Focuses on very advanced drivers which punishes
simple ones
• Many drivers must be written in kernel mode
– Even though much functionality could be user mode
• Developers spend too much time driving our
software
– Cannot concentrate on driving their hardware
– Driver quality suffers as a result
• Do not allow extension and future growth
Overview of Current Driver
Models
Device/Driver Classes
Current Model
Display Adapters
Video port
Storage Adapters (SCSI & ATA)
SCSIport, Storport, ATAport,
Network Adapters
NDIS
Video Capture
AvStream
Audio Adapters
AVStream, PortCls
File System filters
FS Mini filter
Printers
UniDrv
Scanners,Cameras
WIA
PCI, PC Card, generic filter drivers
WDM
Modems, Cable Modem
WDM & NDIS WDM
Biometric Devices
WDM
Smart Card Devices
WDM
Keyboard/Mouse Filters
WDM
Legacy Devices (Serial, Parallel)
WDM
Portable Media Players
WMDM
UPnP & Network Connected Devices, Cell
No support
Kernel-Mode or User-Mode
Driver?
You must use kernel mode when you:
• Need direct hardware access, for example,
– Require DMA
– Must handle interrupts
– Need access to device registers
• Have strict timing requirements
– UMDF will have increased latency
• Need kernel-only resources
– Or kernel components need access to your driver
Introduction to WDM
To allow driver developers to write device drivers that are
source-code compatible across all Microsoft Windows
operating systems, the Windows Driver Model (WDM)
was introduced. Kernel-mode drivers that follow WDM
rules are called WDM drivers. All WDM drivers must:
Include wdm.h, not ntddk.h. (Note that wdm.h is a subset of
ntddk.h.)
• Be designed as a bus driver, a function driver, or a filter
driver.
• Create device objects.
• Support Plug and Play.
• Support power management.
• Support Windows Management Instrumentation (WMI)
Esempio
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
{
UNREFERENCED_PARAMETER (RegistryPath);
DebugPrint (("Entered Driver Entry\n"));
DriverObject->MajorFunction[IRP_MJ_CREATE]
= GpdDispatch;
DriverObject->MajorFunction[IRP_MJ_CLOSE]
= GpdDispatch;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = GpdDispatch;
DriverObject->DriverUnload
= GpdUnload;
DriverObject->MajorFunction[IRP_MJ_PNP]
= GpdDispatchPnp;
DriverObject->MajorFunction[IRP_MJ_POWER]
= GpdDispatchPower;
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = GpdDispatchSystemControl;
DriverObject->DriverExtension->AddDevice
= GpdAddDevice;
return STATUS_SUCCESS;
}
Esempio
NTSTATUS GpdAddDevice(/* Plug & Play */
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
{
NTSTATUS
status = STATUS_SUCCESS;
PDEVICE_OBJECT
deviceObject = NULL;
PLOCAL_DEVICE_INFO
deviceInfo;
UNICODE_STRING
ntDeviceName;
UNICODE_STRING
win32DeviceName;
PAGED_CODE();
DebugPrint(("Entered AddDevice: %p\n", PhysicalDeviceObject));
RtlInitUnicodeString(&ntDeviceName, GPD_DEVICE_NAME);
status = IoCreateDevice(DriverObject,
sizeof (LOCAL_DEVICE_INFO),
&ntDeviceName,
GPD_TYPE,
FILE_DEVICE_SECURE_OPEN, // do security checks on relative open
FALSE,
&deviceObject);
Esempio
if (!NT_SUCCESS (status)) {
DebugPrint(("IoCreateDevice failed: %x\n", status));
return status;
}
RtlInitUnicodeString(&win32DeviceName, DOS_DEVICE_NAME);
status = IoCreateSymbolicLink( &win32DeviceName, &ntDeviceName );
if (!NT_SUCCESS(status)) // If we we couldn't create the link then
{
// abort installation.
IoDeleteDevice(deviceObject);
return status;
}
deviceInfo = (PLOCAL_DEVICE_INFO) deviceObject->DeviceExtension;
deviceInfo->NextLowerDriver = IoAttachDeviceToDeviceStack (
deviceObject,
PhysicalDeviceObject);
if(NULL == deviceInfo->NextLowerDriver) {
IoDeleteSymbolicLink(&win32DeviceName);
IoDeleteDevice(deviceObject);
return STATUS_NO_SUCH_DEVICE;
}
Esempio
IoInitializeRemoveLock (&deviceInfo->RemoveLock ,
PORTIO_TAG,
1, // MaxLockedMinutes
5); // HighWatermark, this parameter is
// used only on checked build.
deviceObject->Flags |= DO_POWER_PAGABLE;
deviceInfo->DeviceObject = deviceObject;
INITIALIZE_PNP_STATE(deviceInfo);
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
deviceInfo->PortMemoryType = 1;
DebugPrint(("AddDevice: %p to %p->%p \n", deviceObject,
deviceInfo->NextLowerDriver,
PhysicalDeviceObject));
return STATUS_SUCCESS;
}