Kernel Modules KLD Device Drivers The kld interface allows system - - PDF document

kernel modules
SMART_READER_LITE
LIVE PREVIEW

Kernel Modules KLD Device Drivers The kld interface allows system - - PDF document

Dynamic Kernel Linker Facility - Kernel Modules KLD Device Drivers The kld interface allows system administrators to dynamically add and remove functionality from a Pseudo device example running system. Joystick device example This


slide-1
SLIDE 1

1

Kernel Modules Device Drivers Pseudo device example Joystick device example

Dynamic Kernel Linker Facility - KLD

  • The kld interface allows system administrators to

dynamically add and remove functionality from a running system.

  • This allows device driver writers to load their

new changes into a running kernel without constantly rebooting to test changes.

  • kld interface is used through the following

privileged commands:

– kldload - loads a new kernel module – kldunload - unloads a kernel module – kldstat - lists the currently loaded modules

Skeleton Layout of a kernel module

/* * KLD Skeleton * Inspired by Andrew Reiter's Daemonnews article */ #include <sys/types.h> #include <sys/module.h> #include <sys/systm.h> /* uprintf */ #include <sys/errno.h> #include <sys/param.h> /* defines used in kernel.h */ #include <sys/kernel.h> /* types used in module initialization */ / * Load handler that deals with the loading and unloading of a KLD. */ static int skel_loader(struct module *m, int what, void *arg) { int err = 0; switch (what) { case MOD_LOAD: /* kldload */ uprintf("Skeleton KLD loaded.\n"); break; case MOD_UNLOAD: uprintf("Skeleton KLD unloaded.\n"); break; default: err = EINVAL; break; } return(err); } /* Declare this module to the rest of the kernel */ static moduledata_t skel_mod = { "skel", skel_loader, NULL }; DECLARE_MODULE(skeleton, skel_mod, SI_SUB_KLD, SI_ORDER_ANY); }

The loader

/* * Load handler that deals with the loading and unloading of a KLD. */ static int skel_loader(struct module *m, int what, void *arg) { int err = 0; switch (what) { case MOD_LOAD: /* kldload */ uprintf("Skeleton KLD loaded.\n"); break; case MOD_UNLOAD: uprintf("Skeleton KLD unloaded.\n"); break; default: err = EINVAL; break; } return(err); }

Declaring the module

/* Declare this module to the rest of the kernel */ static moduledata_t skel_mod = { "skel", skel_loader, NULL }; DECLARE_MODULE(skeleton, skel_mod, SI_SUB_KLD, SI_ORDER_ANY);

Compiling and Loading

  • The makefile should look like:

SRCS=skeleton.c KMOD=skeleton .include <bsd.kmod.mk>

  • Running make with this makefile will

create a file skeleton.ko

  • It can be loaded into your system by

typing:

# kldload -v ./skeleton.ko

slide-2
SLIDE 2

2

Special Files

  • hardware devices have file names.

– /dev/tty – /dev/ad0s1a – /dev/zero

  • may be accessed by the same system calls

used for regular files

  • created by the mknode system call.
  • MAKEDEV script.
  • can be:

– structured - blocked oriented – unstructured - character oriented

what's in a mode

... #define S_ISDIR(m) ((m & 0170000) == 0040000) /* directory */ #define S_ISCHR(m) ((m & 0170000) == 0020000) /* char special */ #define S_ISBLK(m) ((m & 0170000) == 0060000) /* block special */ #define S_ISREG(m) ((m & 0170000) == 0100000) /* regular file */ #define S_ISFIFO(m) ((m & 0170000) == 0010000 || \ (m & 0170000) == 0140000) /* fifo or socket */ #ifndef _POSIX_SOURCE #define S_ISLNK(m) ((m & 0170000) == 0120000) /* symbolic link */ #define S_ISSOCK(m) ((m & 0170000) == 0010000 || \ (m & 0170000) == 0140000) /* fifo or socket */ #define S_ISWHT(m) ((m & 0170000) == 0160000) /* whiteout */ #endif ... struct stat { dev_t st_dev; /* inode's device */ ino_t st_ino; /* inode's number */ mode_t st_mode; /* inode protection mode */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of the file's owner */ gid_t st_gid; /* group ID of the file's group */ dev_t st_rdev; /* device type */ ... } from /usr/include/sys/stat.h % ls -ls /dev ... 0 crw-r----- 2 root operator 116, 0x00010002 Mar 18 2002 ad0 0 crw-r----- 2 root operator 116, 0 Mar 18 2002 ad0a 0 crw-r----- 2 root operator 116, 1 Mar 18 2002 ad0b 0 crw-r----- 2 root operator 116, 2 Mar 18 2002 ad0c 0 crw-r----- 2 root operator 116, 3 Mar 18 2002 ad0d 0 crw-r----- 2 root operator 116, 4 Mar 18 2002 ad0e 0 crw-r----- 2 root operator 116, 5 Mar 18 2002 ad0f 0 crw-r----- 2 root operator 116, 6 Mar 18 2002 ad0g 0 crw-r----- 2 root operator 116, 7 Mar 18 2002 ad0h ... 0 crw-r----- 2 root operator 13, 8 Mar 18 2002 da1a 0 crw-r----- 2 root operator 13, 9 Mar 18 2002 da1b ... 0 crw-r----- 1 root kmem 2, 0 Mar 18 2002 mem 0 crw-rw-rw- 1 root wheel 67, 0 Jul 22 2002 meteor0 0 crw-rw-rw- 1 root wheel 30, 2 Mar 18 2002 midi0 0 lrwxrwxrwx 1 root wheel 6 Mar 18 2002 mixer -> mixer0 0 crw-rw-rw- 1 root wheel 30, 0 Mar 18 2002 mixer0 0 lrwxr-xr-x 1 root wheel 8 Mar 20 2002 mouse -> sysmouse ... 0 crw-rw-rw- 1 root wheel 2, 12 Mar 18 2002 zero

minor device number major device number

Device number (major – minor)

  • inode has the major/minor number

– st_rdev

  • major number

– identifies the driver – used by the kernel to find the driver

  • minor number

– identifies a sub-unit - disk n, tape n – sets some flags/options – used by the driver

Major Minor numbers

  • crw-rw-rw- 1 root wheel 2, 2 Apr 17 06:16 /dev/null
  • crw-r--r-- 1 root wheel 2, 3 Oct 27 17:26 /dev/random
  • crw-rw-rw- 1 root wheel 2, 12 Oct 27 17:26 /dev/zero
  • crw------- 1 root wheel 28, 0 Oct 27 17:26 /dev/ttyd0
  • crw------- 1 root wheel 28, 1 Oct 27 17:26 /dev/ttyd1
  • crw------- 1 root wheel 28, 2 Oct 27 17:26 /dev/ttyd2
  • crw------- 1 root wheel 28, 3 Oct 27 17:26 /dev/ttyd3
  • 0 crw-r----- 2 root operator 116, 0 Oct 27 17:26 /dev/ad0a
  • 0 crw-r----- 2 root operator 116, 1 Oct 27 17:26 /dev/ad0b
  • 0 crw-r----- 2 root operator 116, 2 Oct 27 17:26 /dev/ad0c
  • 0 crw-r----- 2 root operator 116, 3 Oct 27 17:26 /dev/ad0d

Device Driver

  • autoconfiguration and initialization
  • routines to service I/O requests

– invoked via system call. – executes synchronously – top half of the driver

  • interrupt service routines

– invoked by the hardware fielding an interrupt. – bottom half of the driver

slide-3
SLIDE 3

3

#include <fcntl.h> #include <sys/joystick.h> #define DEVJOY "/dev/joy0" ... main(int cc, char **vv) { int fd; if((fd = open(DEVJOY, O_RDONLY)) < 0) { perror(DEVJOY); exit(1); }

lea 0x5,%eax int $0x80

user mode kernel mode

trap handler system-call handler

  • pen

system-call handler

kernel mode

int

  • pen(p, uap)

struct proc *p; register struct open_args /* { syscallarg(char *) path; syscallarg(int) flags; syscallarg(int) mode; } */ *uap; { ... return ( spec_open(...));

the open system call

if (code >= p->p_sysent->sv_size) callp = &p->p_sysent->sv_table[0]; else callp = &p->p_sysent->sv_table[code]; ... narg = callp->sy_narg & SYF_ARGMASK; ... error = (*callp->sy_call)(p, args);

the system call handler

spec_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct proc *p = ap->a_p; struct vnode *vp = ap->a_vp; dev_t dev = vp->v_rdev; int error; dsw = devsw(dev); if ( (dsw == NULL) || (dsw->d_open == NULL)) return ENXIO; ... error = (*dsw->d_open)(dev, ap->a_mode, S_IFCHR, p); if (error) return (error);

special file open

struct cdevsw { d_open_t *d_open; d_close_t *d_close; d_read_t *d_read; d_write_t *d_write; d_ioctl_t *d_ioctl; d_poll_t *d_poll; d_mmap_t *d_mmap; d_strategy_t *d_strategy; const char *d_name; /* base device name, e.g. 'vn' */ int d_maj; d_dump_t *d_dump; d_psize_t *d_psize; u_int d_flags; int d_bmaj; /* additions below are not binary compatible with 4.2 and below */ d_kqfilter_t *d_kqfilter; };

from /usr/include/sys/conf.h

/* * this file is created by 'mkconf.c' */ int (*bdevsw[])() { &nulldev, &nulldev, &rkstrategy, &rktab, /* 0 - rk */ &nodev, &nodev, &nodev, 0, /* 1 - rp */ ... }; int (*cdevsw[])() { &klopen, &klclose, &klread, &klwrite, &klgstty, /* 0 - console */ &pcopen, &pcclose, &pcread, &pcwrite, &nodev, /* 1 - pc */ &lpopen, &lpclose, &nodev, &lpwrite, &nodev, /* 2 - lp */ ... &nulldev, &nulldev, &rkread, &rewrite, &nodev, /* 33 - rk */ ... };

Device driver - sections

  • kernel interface: needed for auto-configuration

– probe/attach

  • user interface

– open/close – read/write – ioctl – poll/select/map

slide-4
SLIDE 4

4 Character Device KLD Implementation Skeleton

  • Char device is a useful type of device

– Act as an interface to the kernel – Also to real devices

  • The following are usually common:

– Prototype device functions – Declaring the cdevsw structure – Functions implementations – Load handler and DEV_MODULE macro

Our example device

  • The idea behind this driver is to show some

interaction with the device driver

  • The code flow this driver is aimed to is

– open -> write -> read -> close

  • First we open the device in /dev dir
  • We then write a small string via write(2)

– The string will be stored in a static buffer

  • This buffer will be accessible via read(2)
  • Finally we close the device

Prototypes

  • The prototypes functions are the only calls

available to directly access the new device

– d_open_t example_open; – d_close_t example_close; – d_read_t example_read; – d_write_t example_write;

cdevsw

  • Not all the function pointers have to be defined

static struct cdevsw example_cdevsw = { example_open, example_close, example_read, example_write, noioctl, nopoll, nommap, nostrategy, "example", 33, /* /usr/src/sys/conf/majors */ nodump, nopsize, D_TTY,

  • 1

};

Global variables

/* Stores string recv'd by _write() */ static char buf[512+1]; static int len; /* * Used as the variable that is the reference to our * device in devfs... we must keep this variable * sane until we call kldunload. */ static dev_t sdev;

  • pen()

/* This open function solely checks for open(2) flags. We are only allowing for the flags to be O_RDWR for the purpose of showing how one could only allow a read-

  • nly device, for example.

*/ int example_open(dev_t dev, int oflags, int devtype, struct proc *p) { int err = 0; memset(&buf, '\0', 513); len = 0; uprintf("Opened device \"example\" successfully.\n"); return(err); }

slide-5
SLIDE 5

5

close()

/* Simply "closes" our device that was opened with example_open. */ int example_close(dev_t dev, int fflag, int devtype, struct proc *p) { memset(&buf, '\0', 513); len = 0; uprintf("Closing device \"example.\"\n"); return(0); }

write()

/* example_write takes in a character string and saves it to buf for later accessing. */ int example_write(dev_t dev, struct uio *uio, int ioflag) { int err = 0; err = copyinstr(uio->uio_iov->iov_base, &buf, 512, &len); if (err != 0) { uprintf("Write to \"example\" failed.\n"); } return(err); }

read()

/* The read function just takes the buf that was saved via example_write() and returns it to userland for accessing. */ int example_read(dev_t dev, struct uio *uio, int ioflag) { int err = 0; if (len <= 0) { err = -1; } else { /* copy buf to userland */ err = copystr(&buf, uio->uio_iov->iov_base, 513, &len); } return(err); }

Loading and Unloading

  • For device drivers, we must do one thing

specific to load and unload.

  • On MOD_LOAD, we must register our device

with devfs using make_dev.

  • devfs is the Device File System which provides

access to the device namespace in the FreeBSD kernel

  • on MOD_UNLOAD, we must call destroy_dev,

using the dev_t variable that was returned from make_dev as the sole parameter.

/* chardev_example_load() This is used as the function that handles what is to

  • ccur when the KLD binary is loaded and unloaded via the kldload and

kldunload programs. */ static int chardev_example_load(struct module *m, int what, void *arg) { int err = 0; switch (what) { case MOD_LOAD: /* kldload */ sdev = make_dev(&example_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "example"); printf("Example device loaded.\n"); break; case MOD_UNLOAD: destroy_dev(sdev); /* explained below */ printf("Example device unloaded.\n"); break; default: err = EINVAL; break; } return(err); }

Loading

  • As with any KLD, we must have a

*_MODULE macro

  • It basically says which function is our load

handler and a name for our kld for reference purposes.

  • DEV_MODULE(chardev_example,

chardev_example_load, NULL);

slide-6
SLIDE 6

6

The device file + example

# cd /dev # mknod example c 33 0 # ls -al | grep example crw-r--r-- 1 root wheel 33, 0 Aug 14 04:40 example #

  • verlord# echo "Hi There" > /dev/echo

Opened device "echo" successfully. Closing device "echo."

  • verlord# cat /dev/echo

Opened device "echo" successfully. Hi There Closing device "echo."

  • verlord#

Joystick when is a bus a bus?

bus NIC sound card disc controller

static struct isa_pnp_id joy_ids[] = { {0x0100630e, "CSC0001 PnP Joystick"}, /* CSC0001 */ ... {0x2200a865, "YMH0022 PnP Joystick"}, /* YMH0022 */ {0x82719304, NULL}, /* ADS7182 */ {0} }; static int joy_probe (device_t dev) { if (ISA_PNP_PROBE(device_get_parent(dev), dev, joy_ids) == ENXIO) return ENXIO; #ifdef WANT_JOYSTICK_CONNECTED #ifdef notyet

  • utb (dev->id_iobase, 0xff);

DELAY (10000); /* 10 ms delay */ return (inb (dev->id_iobase) & 0x0f) != 0x0f; #endif #else return 0; #endif

the probe

#define CDEV_MAJOR 51 static d_open_t joyopen; static d_close_t joyclose; static d_read_t joyread; static d_ioctl_t joyioctl; static struct cdevsw joy_cdevsw = { /* open */ joyopen, /* close */ joyclose, /* read */ joyread, /* write */ nowrite, /* ioctl */ joyioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "joy", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; static int joy_attach (device_t dev) { int unit = device_get_unit(dev); int rid = 0; struct resource *res; struct joy_softc *joy = device_get_softc(dev); res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (res == NULL) return ENXIO; joy->bt = rman_get_bustag(res); joy->port = rman_get_bushandle(res); joy->timeout[0] = joy->timeout[1] = 0; make_dev(&joy_cdevsw, 0, 0, 0, 0600, "joy%d", unit); return 0; }

the attach

slide-7
SLIDE 7

7

static device_method_t joy_methods[] = { DEVMETHOD(device_probe, joy_probe), DEVMETHOD(device_attach, joy_attach), { 0, 0 } }; static driver_t joy_isa_driver = { "joy", joy_methods, sizeof (struct joy_softc) }; devclass_t joy_devclass; DRIVER_MODULE(joy, // name isa, // busname joy_isa_driver, // driver_t joy_devclass, // devclass_t 0, // evh 0); for more info: man DRIVER_MODULE

tying it up

parent bus of the device e.g. pci, ppbus name prefix for the driver

user interface - open

struct joy_softc { bus_space_tag_t bt; bus_space_handle_t port; int x_off[2], y_off[2]; int timeout[2]; }; #define JOY_SOFTC(unit) (struct joy_softc *) \ devclass_get_softc(joy_devclass,(unit)) static int joyopen(dev_t dev, int flags, int fmt, struct proc *p) { int i = joypart (dev); struct joy_softc *joy = JOY_SOFTC(UNIT(dev)); if (joy->timeout[i]) return EBUSY; joy->x_off[i] = joy->y_off[i] = 0; joy->timeout[i] = JOY_TIMEOUT; return 0; }

user interface - read

while(read(fd, &jc, sizeof(struct joysitck)) > 0) { ... }

user mode

static int joyread(dev_t dev, struct uio *uio, int flag) { struct joy_softc *joy = JOY_SOFTC(UNIT(dev)); bus_space_handle_t port = joy->port; bus_space_tag_t bt = joy->bt; struct joystick c; #ifndef i386 int s; s = splhigh(); #else disable_intr (); #endif bus_space_write_1 (bt, port, 0, 0xff); nanotime(&start); ... return uiomove ((caddr_t)&c, sizeof(struct joystick), uio); }

kernel mode

static int joyioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct joy_softc *joy = JOY_SOFTC(UNIT(dev)); int i = joypart (dev); int x; switch (cmd) { case JOY_SETTIMEOUT: x = *(int *) data; if (x < 1 || x > 10000) /* 10ms maximum! */ return EINVAL; joy->timeout[i] = x; break; ... case JOY_GET_Y_OFFSET: *(int *) data = joy->y_off[i]; break; default: return ENXIO; } return 0; }

user interface - ioctl C optimizer trap

main() { int *p; int i; p = (int *)0x555; i = 0; while(*p & 01) { i++; } } main: pushl %ebp movl %esp,%ebp subl $24,%esp movl $1365,-4(%ebp) movl $0,-8(%ebp) .p2align 2,0x90 .L3: movl -4(%ebp),%eax movl (%eax),%edx andl $1,%edx testl %edx,%edx jne .L5 jmp .L4 .L5: incl -8(%ebp) jmp .L3

no optimization

main: pushl %ebp movl %esp,%ebp movl 1365,%eax andl $1,%eax .L7: testl %eax,%eax jne .L7 leave ret

  • ptimized

C optimizer trap cont.

main() { volatile int *p; int i; p = (int *)0x555; i = 0; while(*p & 01) { i++; } } main: pushl %ebp movl %esp,%ebp movl $1365,%edx movl 1365,%eax testb $1,%al je .L4 .p2align 2,0x90 .L5: movl (%edx),%eax testb $1,%al jne .L5 .L4: leave ret

  • ptimized
slide-8
SLIDE 8

8

main() { volatile int i, *p; p = (int *)0x555; i = 0; while(*p & 01) { i++; } }

C optimizer trap cont.

8 main: 9 0000 55 pushl %ebp 10 0001 89E5 movl %esp,%ebp 11 0003 83EC18 subl $24,%esp 12 0006 BA550500 movl $1365,%edx 12 00 13 000b C745FC00 movl $0,-4(%ebp) 13 000000 14 0012 A1550500 movl 1365,%eax 14 00 15 0017 A801 testb $1,%al 16 0019 740E je .L4 17 001b 90 .p2align 2,0x90 18 .L5: 19 001c 8B45FC movl -4(%ebp),%eax 20 001f 40 incl %eax 21 0020 8945FC movl %eax,-4(%ebp) 22 0023 8B02 movl (%edx),%eax 23 0025 A801 testb $1,%al 24 0027 75F3 jne .L5 25 .L4: 26 0029 C9 leave 27 002a C3 ret 28 .Lfe1:

line number memory address memory content