
   Writing Linux kernel modules

   Only  the  most  important  drivers  of  the basic devices (like hard
   drives)  are compiled into the kernel, because putting all drivers in
   the  kernel  is a waste of memory and time for enabling and disabling
   drivers  for  non-existent  devices.  This  is  why optional devices'
   drives were put into kernel modules, loaded on demand.
   A  kernel module is a plain compiled file in the standard ELF format.
   It  needs  to  export  two  functions:  init_module, which is used to
   initialize    the   module   (and   run   during   module   loading),
   cleanup_module,  used  to  do  whatever  is  needed  to  finish  work
   correctly (run during the removing of the module from the kernel).
   The init_module functions should be written to return zero in case of
   success,  and  in  case  of error - one of the negative errno values,
   best describing the problem.
   Many  information  about  2.4  kernel  modules  is  suitable  for 2.6
   kernels,  the  section of this page dedicated to 2.6 kernels will add
   only what's changed from 2.4.
     ________________________________________________________________

   Ths simplest 2.4 kernel module

   (skip the simplest module)
   According  to  what  I've  said above, the simplest module looks like
   this:
   (skip the simplest module's code)
        format ELF

        section ".text" executable      ; code section start

        ; exporting two required functions
        public  init_module
        public  cleanup_module

        ; declaration of an external function used for displaying messages
        extrn   printk

        init_module:
                push    dword str1      ; string to display
                call    printk
                pop     eax             ; remove arguments from the stack

                xor     eax, eax        ; zero means no errors
                ret

        cleanup_module:
                push    dword str2
                call    printk
                pop     eax

                ret

        section ".data" writeable
        str1            db      "<1> Inside init_module."   , 10, 0
        str2            db      "<1> Inside cleanup_module.", 10, 0

        section ".modinfo"
        __module_kernel_version db      "kernel_version=2.4.26", 0
        __module_license        db      "license=GPL", 0
        __module_author         db      "author=Bogdan D.", 0
        __module_description    db "description=first kernel module.", 0

   Notice a few things:
    1. Printing  strings  is  done  using the internal kernel function -
       printk.  It  works  siimilarly to the C language printf function,
       which is of course unreachable when the kernel is booting.
       In  short:  address  of the string is put on the stack, after any
       extra  data  put  in  the  reverse  order, if the function should
       display any variables in the string, like %d (integer). This will
       be shown in the example module.
       the  string  should  start with <N>, N being a number. This tells
       the  kernel  about the severity of the message. For us, setting N
       to 1 is enough.
       If  the  printed strings don't show up on the screen, they surely
       show  up  after the dmesg command (usually at the end) and in the
       kernel log file /var/log/messages.
    2. FASM syntax.
       Unknown  reasons  made NASM-assembled modules not insertable into
       the kernel.
    3. Each  function is called in the C calling convention, which means
       we cleanup the stack.
    4. New section - modinfo.
       It  contains  the following information: for which kernel version
       this  module  is for, who is the module's author, module license,
       module  parameters.  The variable names must remain the same, you
       should change the text after the equal signs.

   After assembling (fasm module_hello.asm), you can install this module
   as root with the command
        insmod ./modul_hello.o

   and remove it with the command
        rmmod modul_hello

   (notice the missing .o extension).
   The  list  of  all  currently loaded modules can be obtained with the
   command lsmod.
   Now,  I'll  show  how  to  register  a  character  device and reserve
   resources for it: IRQ and memory + port ranges.
     ________________________________________________________________

   Registering a character device

   (skip register a character device)
   To register a character device (a device which permits reading single
   bytes,  as  opposed  to,  say,  a  hard disk) use the kernel-exported
   register_chrdev  function.  It accepts 3 arguments. Starting from the
   left (last put on the stack), these are:
    1. Device  major number, chosen by us. You can specify zero here. In
       that case, the kernel will assign us an unused number. This major
       number  is the first of the two numbers (the second one is called
       the  minor  number) you can see in the detail listing of the /dev
       directory, e.g.
        crw-rw-rw-  1 root root 1, 5 aug 16 15:28 /dev/zero
       The  zero  device  has  major  number 1 and minor number 5. The C
       letter  at  the  beginning tells that this is a character device.
       Other  marks  are: D (directory), S (socket), B (block device), P
       (pipe, FIFO), L (symbolic link).
    2. Address  of  the  device  name, which is a sequence of characters
       ending with byte zero.
    3. Address  of a file_operations structure, inside which we will put
       addresses  of  functions  used for operations on this device. The
       most  important  are:  opening, closing, writing and reading from
       the  device.  The  structure  itself  for a 2.4 kernel looks like
       this:
       (skip file_operations)
        struct file_operations {
                struct module *owner;
                loff_t (*llseek) (struct file *, loff_t, int);
                ssize_t (*read) (struct file*, char*, size_t, loff_t *);
                ssize_t (*write) (struct file *, const char *, size_t,
                        loff_t *);
                int (*readdir) (struct file *, void *, filldir_t);
                unsigned int (*poll) (struct file *,
                        struct poll_table_struct *);
                int (*ioctl) (struct inode*, struct file*, unsigned int,
                        unsigned long);
                int (*mmap) (struct file *, struct vm_area_struct *);
                int (*open) (struct inode *, struct file *);
                int (*flush) (struct file *);
                int (*release) (struct inode *, struct file *);
                int (*fsync) (struct file*,struct dentry*, int datasync);
                int (*fasync) (int, struct file *, int);
                int (*lock) (struct file *, int, struct file_lock *);
                ssize_t (*readv) (struct file *, const struct iovec *,
                        unsigned long, loff_t *);
                ssize_t (*writev) (struct file *, const struct iovec *,
                        unsigned long, loff_t *);
        };
       Each field of this structure is a DWORD. For basic operations, we
       only  need  the  third, fourth, ninth and eleventh (file closing)
       fields.  If you aren't planning to implement some function, put a
       zero in the corresponding field of this structure.

   If  we  call this function with our own major number and the function
   succeedes,  it  will  return zero. If we asked the kernel for a major
   number and the function succeedes, it will return a positive integer,
   which is the kernel-assigned major number for our device.
   NOTE:  The  register_chrdev function does not create a device file in
   the  /dev  directory.  We  have to do it ourselves, after loading the
   module.
   To   unregister   a  character  device,  call  the  unregister_chrdev
   function.  Its first argument (last put on the stack) is the assigned
   major number, the second argument is the address of the device name.
     ________________________________________________________________

   Registering input-output ports and memory areas

   (skip registering resources)
   Reserving  these  resources  is  easy. All you need to do is call the
   __request_region  function.  It  accepts  4  arguments. Starting from
   left, (last put on the stack), they are:
    1. Type  of  the  resource.  If  you want to reserve ports, give the
       address   of   the   ioport_resource   variable,   if   memory  -
       iomem_resource. Both variables are exported by the kernel, so you
       can declare them as external to your module.
    2. Starting port number or starting memory address.
    3. Length of the port or memory range
    4. Address of the device name.

   In case of failure, this function returns zero (in EAX).
   Both  types  of  resources can be released using the __release_region
   function. It has 3 arguments, which are the same as the first 3 above
   (type, start and range length).
     ________________________________________________________________

   Registering IRQ resources

   (skip IRQ registering)
   Interrupt request (IRQ) resources are registered with the request_irq
   function.  It takes 5 arguments of type DWORD. Starting from the left
   (last put on the stack), they are:
    1. IRQ number we wish to reserve.
    2. Address of our interrupt service function. This function has this
       prototype:
        void handler (int irq, void *dev_id, struct pt_regs *regs);
       As  you can see, we can tell which interrupt was generated and by
       which device. The last argument is said to be used rarely.
    3. Integer SA_INTERRUPT = 0x20000000
    4. Address of the device name.
    5. Address  of  a  file_operations  structure,  filled with function
       addresses.

   If this function fails, it will return a negative value.
   Releasing  an  IRQ  is  done  using  the free_irq function. Its first
   argument  (last  on  the stack) is our IRQ number, second argument is
   the address of our file_operations structure.
     ________________________________________________________________

   Example 2.4 kernel module

   (skip to installation script)
   The  module  shown below will register a software character device (a
   device  which has no hardware, like /dev/null) with IRQ 4, port range
   600h-6FFh,  memory  range 80000000h - 8000FFFFh and basic operations:
   opening,  closing,  reading,  writing,  seeking. For simplicity, this
   code  doesn't  check  if the resources are occupied. If they are, the
   kernel will return an error and the module won't load.
   (skip the module code)
; Example 2.4 kernel module
;
; Author: Bogdan D., bogdandr (na) op . pl
;
; assemble:
;   fasm modul_dev_fasm.asm

format ELF
section ".text" executable

; exporting required functions
public  init_module
public  cleanup_module

; iimporting used functions and symbols
extrn   printk
extrn   register_chrdev
extrn   unregister_chrdev
extrn   request_irq
extrn   free_irq

extrn   __check_region
extrn   __request_region
extrn   __release_region
extrn   ioport_resource
extrn   iomem_resource

; resource ranges we're going to ask for
PORTY_START     = 0x600
PORTY_ILE       = 0x100

RAM_START       = 0x80000000
RAM_ILE         = 0x00010000

; constants needed for IRQ reservation
SA_INTERRUPT    = 0x20000000
NUMER_IRQ       = 4

; module initialization function
init_module:
        pushfd

        ; registering a character device
        push    dword file_oper
        push    dword nazwa
        push    dword 0                 ; assign the number dynamically
        call    register_chrdev
        add     esp, 3*4                ; remove arguments from the stack

        cmp     eax, 0                  ; check for error
        jg      .dev_ok

        ; if we are here, there was an error. Show it.
        push    eax                     ; argument for the error string
        push    dword dev_blad          ; address of the error string
        call    printk                  ; print the error
        add     esp, 1*4                ; removing only 1*4 bytes, because:

        pop     eax                     ; will exit with the error code in EAX
        jmp     .koniec

.dev_ok:

        mov     [major], eax

        ; reserve I/O ports
        push    dword nazwa
        push    dword PORTY_ILE
        push    dword PORTY_START
        push    dword ioport_resource
        call    __request_region
        add     esp, 4*4

        test    eax, eax                ; check for error
        jnz     .iop_ok

        push    eax                     ; argument for the error string
        push    dword porty_blad        ; address of the error string
        call    printk                  ; print the error
        add     esp, 1*4                ; will 'pop eax' later

        ; unregister the device
        push    dword nazwa
        push    dword [major]
        call    unregister_chrdev
        add     esp, 2*4

        pop     eax                     ; will exit with the error code in EAX
        jmp     .koniec

.iop_ok:

        ; reserve memory
        push    dword nazwa
        push    dword RAM_ILE
        push    dword RAM_START
        push    dword iomem_resource
        call    __request_region
        add     esp, 4*4

        test    eax, eax                ; check for error
        jnz     .iomem_ok

        push    eax
        push    dword ram_blad
        call    printk                  ; print the error
        add     esp, 1*4                ; will 'pop eax' later

        ; unregister the device
        push    dword nazwa
        push    dword [major]
        call    unregister_chrdev
        add     esp, 2*4

        ; free reserved ports
        push    dword PORTY_ILE
        push    dword PORTY_START
        push    dword ioport_resource
        call    __release_region
        add     esp, 3*4

        pop     eax                     ; will exit with the error code in EAX
        jmp     .koniec

.iomem_ok:
        ; assigneing IRQ:
        push    dword file_oper
        push    dword nazwa
        push    dword SA_INTERRUPT
        push    dword obsluga_irq
        push    dword NUMER_IRQ
        call    request_irq
        add     esp, 5*4

        cmp     eax, 0
        jge     .irq_ok

        push    eax
        push    dword irq_blad
        call    printk                  ; print the error
        add     esp, 1*4                ; will 'pop eax' later

        ; unregister the device
        push    dword nazwa
        push    dword [major]
        call    unregister_chrdev
        add     esp, 2*4

        ; free reserved ports
        push    dword PORTY_ILE
        push    dword PORTY_START
        push    dword ioport_resource
        call    __release_region
        add     esp, 3*4

        ; free reserved memory
        push    dword RAM_ILE
        push    dword RAM_START
        push    dword iomem_resource
        call    __release_region
        add     esp, 3*4

        pop     eax                     ; will exit with the error code in EAX
        jmp     .koniec

.irq_ok:

        ; print info about successful module load
        push    dword NUMER_IRQ
        push    dword [major]
        push    dword uruch
        call    printk
        add     esp, 3*4

        xor     eax, eax                ; zero - no errors

.koniec:

        popfd
        ret

; called when the module is unloaded
cleanup_module:
        pushfd
        push    eax

        ; free the IRQ
        push    dword file_oper
        push    dword NUMER_IRQ
        call    free_irq
        add     esp, 2*4

        ; unregister the device:
        push    dword nazwa
        push    dword [major]
        call    unregister_chrdev
        add     esp, 2*4

        ; free reserved ports
        push    dword PORTY_ILE
        push    dword PORTY_START
        push    dword ioport_resource
        call    __release_region
        add     esp, 3*4

        ; free reserved memory
        push    dword RAM_ILE
        push    dword RAM_START
        push    dword iomem_resource
        call    __release_region
        add     esp, 3*4

        ; print info about successful module unload
        push    dword usun
        call    printk
        add     esp, 1*4

        pop     eax
        popfd
        ret

; Out interrupt service function. This one does nothing, but argument placemen
t
;       on the stack is shown
obsluga_irq:
        push    ebp
        mov     ebp, esp

; [ebp] = old EBP
; [ebp+4] = return address
; [ebp+8] = arg1
; ...

                irq     equ     ebp+8
                dev_id  equ     ebp+12
                regs    equ     ebp+16

        leave
        ret


; Define device operations

; Reading from device - return a sequence of 1Eh bytes of the specified length
.
; This device is an infinite source, just like /dev/zero
czytanie:
        push    ebp
        mov     ebp, esp

        ; argument placement on the stack:
        s_file  equ     ebp+8   ; pointer to a file structure
        bufor   equ     ebp+12  ; data buffer address
        l_jedn  equ     ebp+16  ; requested number of bytes
        loff    equ     ebp+20  ; requested start position of reading

        pushfd
        push    edi
        push    ecx

        mov     ecx, [l_jedn]
        mov     al, 0x1e
        cld
        mov     edi, [bufor]
        rep     stosb           ; fill buffer with 1Eh bytes

        pop     ecx
        pop     edi
        popfd

        mov     eax, [l_jedn]   ; return the number of requested bytes

        leave
        ret

; writing to the device - infinite well (will consume anything)
zapis:
        push    ebp
        mov     ebp, esp

        ; don't write physically anything, just return the number
        ;       of bytes we were supposed to write.
        mov     eax, [l_jedn]

        leave
        ret

; seek
przejscie:
; close:
zamykanie:
; open:
otwieranie:
        xor     eax, eax        ; all 3 functions always return success
        ret



section ".data" writeable

major   dd      0       ; kernel-assigned edvice major number

; addresses of the functions for device operations
file_oper:      dd 0, przejscie, czytanie, zapis, 0, 0, 0, 0, otwieranie, 0
                dd zamykanie, 0, 0, 0, 0, 0

dev_blad        db      "<1>Device register error: %d.", 10, 0
irq_blad        db      "<1>IRQ assignment error: %d.", 10, 0
porty_blad      db      "<1>Port assignment error:  EAX=%d", 10, 0
ram_blad        db      "<1>Memory assignment error: EAX=%d", 10, 0


uruch           db      "<1>Module loaded. Maj=%d, IRQ=%d", 10, 0
usun            db      "<1>Module removed.", 10, 0

nazwa           db      "test00", 0
sciezka         db      "/dev/test00", 0

section ".modinfo"
__module_kernel_version db      "kernel_version=2.4.26", 0
__module_license        db      "license=GPL", 0
__module_author         db      "author=Bogdan D.", 0
__module_description    db      "description=Example kernel module", 0
__module_device         db      "device=test00", 0

   The  above  module,  after assembling, is easiest installed using the
   following script:
   (skip install script)
#!/bin/bash

PLIK="modul_dev_fasm.o"         # Put your module's name here
NAZWA="test00"

# Inserting the module.
/sbin/insmod $PLIK $* || { echo "insmod problem!" ; exit -1; }

# finding and printing our module name
/sbin/lsmod | grep `echo $PLIK | sed 's/[^a-z]/ /g' | awk '{print $1}' `
# print resource information
grep $NAZWA /proc/devices
grep $NAZWA /proc/ioports
grep $NAZWA /proc/iomem
grep $NAZWA /proc/interrupts

# find and print device major number
NR=`grep $NAZWA /proc/devices | awk '{print $1}'`
echo "Major = $NR"

# remove old device file
rm -f /dev/$NAZWA

# creating the device file in /dev
# sys_mknod from inside the module does NOT work
mknod /dev/$NAZWA c $NR 0
ls -l /dev/$NAZWA

# short test: read 512 bytes and check their contents
dd count=1 if=/dev/$NAZWA of=/x && hexdump /x && rm -f /x

   All  you  have  to  do  is  save  this  script  under some name, like
   instal.sh,  allow  it  to  be  executed  using  the command chmod u+x
   instal.sh  and  run  it using ./instal.sh, as root, of course. If the
   module  is successfully loaded, the script will display the resources
   assigned  to  the module - I/O ports, IRQ and memory - by reading the
   necessary  files  in the /proc directory. The script will also create
   the device file in the /dev directory, with the correct major number.
   After that, a short test will be performed.
   You can easily uninstall the module using the script:
#!/bin/bash

PLIK="modul_dev_fasm"   # Enter you module name here, without the .o
NAZWA="test00"

/sbin/rmmod $PLIK && rm -f /dev/$NAZWA
     ________________________________________________________________

   The simplest 2.6 kernel module

   (skip the simplest 2.6 kernel module)
   The simplest 2.6 kernel module looks like this:
   (skip the simplest 2.6 kernel module code)
format ELF
section ".init.text" executable align 1
section ".text" executable align 4

public init_module
public cleanup_module

extrn printk

init_module:
        push    dword str1
        call    printk
        pop     eax
        xor     eax, eax
        ret

cleanup_module:
        push    dword str2
        call    printk
        pop     eax
        ret

section ".modinfo" align 32
__kernel_version        db      "kernel_version=2.6.16", 0
__mod_vermagic db "vermagic=2.6.16 686 REGPARM 4KSTACKS gcc-4.0", 0
__module_license        db      "license=GPL", 0
__module_author         db      "author=Bogdan D.", 0
__module_description    db      "description=First 2.6 kernel module", 0

section "__versions" align 32
        dd      0xfa02c634
  n1:   db      "struct_module"
        times   64-4-($-n1) db 0

        dd      0x1b7d4074
  n2:   db      "printk"
        times   64-4-($-n2) db 0

section ".data" writeable align 4

str1            db      "<1> Inside init_module(). ", 10, 0
str2            db      "<1> Inside cleanup_module(). ", 10, 0

section ".gnu.linkonce.this_module" writeable align 128

align 128
__this_module:          ; total length: 512 bytes
                        dd 0, 0, 0

                .nazwa: db "modul", 0
                        times 64-4-($-.nazwa) db 0

                        times 100 db 0
                        dd init_module
                        times 220 db 0
                        dd cleanup_module
                        times 112 db 0

   You  can  surely  see  many  differences,  right?  We'll discuss them
   section by section now:
    1. .init.text
       In  general, there should be at least two: .init.text, containing
       the  initialization procedure and .exit.text, containing the exit
       procedure.
       Additionally,  you  can of course have a data section .data and a
       code section .text.
       If  during  installing the module, you get "Accessing a corrupted
       shared  library"  messages,  you should do some shuffling wit the
       sections - add a .text, remove .init.text, change the order etc.
    2. .gnu.linkonce.this_module
       This  is  the  most  important  one.  Without  this section, each
       attempt  to  install the module will result in a "No module found
       in object" message. Contents of this section is a structure named
       "__this_module"  of  type "module". The best you can do right now
       is  to  copy  the above example one to your modules, changing the
       module  name (between the quotation marks) and the entry and exit
       points' names.
       You can also use the following macro:
        macro   gen_this_module         name*, entry, exit
        {
                section '.gnu.linkonce.this_module' writeable align 128

                align 128
                __this_module:
                                dd 0, 0, 0
                .mod_nazwa:     db name, 0
                                times 64-4-($-.mod_nazwa) db 0
                                times 100 db 0
                                if entry eq
                                        dd init_module
                                else
                                        dd entry
                                end if
                                times 220 db 0
                                if exit eq
                                        dd cleanup_module
                                else
                                        dd exit
                                end if
                                times 112 db 0

        }
       Using  this  macro  is  very  easy:  just pass it the name of the
       module,  which should be displayed with the lsmod command and the
       names (addressed) of the entry and exit procedures, e.g.
        gen_this_module "your_module", init_module, cleanup_module
       This macro call should be placed where the section should be, for
       example  - after the last declaration in the data section. In any
       case NOT inside any section.
    3. modinfo
       This  section,  compared to the one in the 2.4 kernel module, has
       only  one,  but  very  important  new entry - "vermagic". In your
       kernel  this  string  will  probably differ from mine only in the
       kernel version. Original string looks like this:
       (skip vermagic)
        #define VERMAGIC_STRING                                 \
          UTS_RELEASE " "                                       \
          MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT           \
          MODULE_ARCH_VERMAGIC                                  \
          "gcc-" __stringify(__GNUC__) "." __stringify(__GNUC_MINOR__)
        #define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY \
                 MODULE_REGPARM MODULE_STACKSIZE
       and  you  can  find  it in the asm* subdirectories of the INCLUDE
       directory in the kernel source tree and in the VERMAGIC.H file.
    4. __versions
       This   section   contains   information  about  versions  of  the
       procedures  which  this module uses. Structure of this section is
       fairly easy: first put a DWORD with the number matching the given
       kernel  function, which can be found in the MODULE.SYMVERS in the
       kernel  source  main  directory.  Right after the number goes the
       name of the used function, filled with zeros up to 64 bytes.
       this section is not required for the module to operate correctly,
       but  should  be in every module or "kernel tainted" messages will
       appear.
       You   can   generate   this   whole   section   using  my  script
       symvers-fasm.txt.  All you have to do is run perl symvers-fasm.pl
       your_module.asm.
     ________________________________________________________________

   Reserving resources in 2.6 kernels

   (skip reserving resources in 2.6 kernels)
   Reserving resources in 2.6 kernels from outside view (the perspective
   of  the C language) isn't much different from the one in 2.4 kernels.
   But inside, two major changes have been made:
    1. The file_operations structure
       In 2.6 kernels it looks like this:
       (skip file_operations 2.6 kernel structure)
        struct file_operations {
                struct module *owner;
                loff_t (*llseek) (struct file *, loff_t, int);
                ssize_t (*read) (struct file*,char __user*,size_t,
                        loff_t*);
                ssize_t (*aio_read) (struct kiocb *, char __user *,
                        size_t, loff_t);
                ssize_t (*write) (struct file *, const char __user *,
                        size_t, loff_t *);
                ssize_t (*aio_write) (struct kiocb *, const char __user*,
                        size_t, loff_t);
                int (*readdir) (struct file *, void *, filldir_t);
                unsigned int (*poll) (struct file *,
                        struct poll_table_struct *);
                int (*ioctl) (struct inode *, struct file *,
                        unsigned int, unsigned long);
                long (*unlocked_ioctl) (struct file *, unsigned int,
                        unsigned long);
                long (*compat_ioctl) (struct file *, unsigned int,
                        unsigned long);
                int (*mmap) (struct file *, struct vm_area_struct *);
                int (*open) (struct inode *, struct file *);
                int (*flush) (struct file *);
                int (*release) (struct inode *, struct file *);
                int (*fsync) (struct file *, struct dentry *,
                        int datasync);
                int (*aio_fsync) (struct kiocb *, int datasync);
                int (*fasync) (int, struct file *, int);
                int (*lock) (struct file *, int, struct file_lock *);
                ssize_t (*readv) (struct file *, const struct iovec *,
                        unsigned long, loff_t *);
                ssize_t (*writev) (struct file *, const struct iovec *,
                        unsigned long, loff_t *);
                ssize_t (*sendfile) (struct file *, loff_t *, size_t,
                        read_actor_t, void *);
                ssize_t (*sendpage) (struct file *, struct page *, int,
                        size_t, loff_t *, int);
                unsigned long (*get_unmapped_area)(struct file *,
                        unsigned long, unsigned long, unsigned long,
                        unsigned long);
                int (*check_flags)(int);
                int (*dir_notify)(struct file *filp, unsigned long arg);
                int (*flock) (struct file *, int, struct file_lock *);
        };
    2. Parameter passing
       My  distribution  kernel  was compiled in such a way that 3 first
       parameters   to  each  procedure  except  printk  are  passed  in
       registers:  EAX, EDX, ECX, and the rest on the stack. To check if
       your kernel does and expects the same, use the commands
        grep -R regpar /lib/modules/`uname -r`/build/|grep Makefile
        grep -R REGPAR /lib/modules/`uname -r`/build/|grep config
       If the results contain something similar to:
        CONFIG_REGPARM=y
        #define CONFIG_REGPARM 1
       then  your  kernel is probably compiled like mine. If so, you can
       use the below macro URUCHOM to call kernel functions. If not, you
       can  modify  the macro. If your system hangs when you try to load
       the module, you probably need to modify the macro.
     ________________________________________________________________

   Example kernel 2.6 module

   (skip example kernel 2.6 module)
   Just  like the 2.4 example, the module below will register a software
   character  device  (a  device  which has no hardware, like /dev/null)
   with  IRQ 4, port range 600h-6FFh, memory range 80000000h - 8000FFFFh
   and  basic  operations:  opening, closing, reading, writing, seeking.
   For  simplicity,  this  code  doesn't  check  if  the  resources  are
   occupied. If they are, the kernel will return an error and the module
   won't load.
format ELF
section ".text" executable align 4

public  init_module
public  cleanup_module

extrn   printk
extrn   register_chrdev
extrn   unregister_chrdev
extrn   request_irq
extrn   free_irq

extrn   __request_region
extrn   __release_region
extrn   ioport_resource
extrn   iomem_resource

PORTY_START     = 0x600
PORTY_ILE       = 0x100

RAM_START       = 0x80000000
RAM_ILE         = 0x00010000

SA_INTERRUPT    = 0x20000000
NUMER_IRQ       = 4

macro   uruchom         funkcja, par1, par2, par3, par4, par5
{
        if ~ par5 eq
                push    dword par5
        end if
        if ~ par4 eq
                push    dword par4
        end if
        if ~ par3 eq
                mov     ecx, par3
        end if
        if ~ par2 eq
                mov     edx, par2
        end if
        if ~ par1 eq
                mov     eax, par1
        end if
        call    funkcja
        if ~ par5 eq
                add     esp, 4
        end if
        if ~ par4 eq
                add     esp, 4
        end if
}

init_module:
        pushfd

        ; registering character device:
        uruchom register_chrdev, 0, nazwa, file_oper

        cmp     eax, 0
        jg      .dev_ok

        ; print error
        push    eax
        push    dword dev_blad
        call    printk
        add     esp, 1*4                ; removing only 1*4 bytes, because:

        pop     eax                     ; will exit with the error code in EAX
        jmp     .koniec

.dev_ok:

        mov     [major], eax

        ; reserve I/O ports
 uruchom __request_region, ioport_resource, PORTY_START, PORTY_ILE, nazwa

        test    eax, eax
        jnz     .iop_ok

        push    eax
        push    dword porty_blad
        call    printk
        add     esp, 1*4                ; will 'pop eax' later

        ; unregister the device
        uruchom unregister_chrdev, [major], nazwa

        pop     eax                     ; will exit with the error code in EAX
        jmp     .koniec

.iop_ok:

        ; reserve memory
        uruchom __request_region, iomem_resource, RAM_START, RAM_ILE, nazwa

        test    eax, eax
        jnz     .iomem_ok

        push    eax
        push    dword ram_blad
        call    printk
        add     esp, 1*4                ; will 'pop eax' later

        ; unregister the device
        uruchom unregister_chrdev, [major], nazwa

        ; free reserved ports
        uruchom __release_region, ioport_resource, PORTY_START, PORTY_ILE

        pop     eax                     ; will exit with the error code in EAX
        jmp     .koniec

.iomem_ok:

        ; przydzielanie przerwania IRQ:
 uruchom request_irq, NUMER_IRQ, obsluga_irq, SA_INTERRUPT, nazwa, file_oper

        cmp     eax, 0
        jge     .irq_ok

        push    eax
        push    dword irq_blad
        call    printk
        add     esp, 1*4                ; will 'pop eax' later

        ; unregister the device
        uruchom unregister_chrdev, [major], nazwa

        ; free reserved ports
        uruchom __release_region, ioport_resource, PORTY_START, PORTY_ILE

        ; free reserved memory
        uruchom __release_region, iomem_resource, RAM_START, RAM_ILE

        pop     eax                     ; will exit with the error code in EAX
        jmp     .koniec

.irq_ok:

        ; print info about successful module load
        push    dword NUMER_IRQ
        push    dword [major]
        push    dword uruch
        call    printk
        add     esp, 3*4

        xor     eax, eax

.koniec:

        popfd
        ret

; called when the module is unloaded
cleanup_module:
        pushfd
        push    eax

        ; free the IRQ:
        uruchom free_irq, NUMER_IRQ, file_oper

        ; unregister the device:
        uruchom unregister_chrdev, [major], nazwa

        ; wyrejestrowanie zakresu portow
        uruchom __release_region, ioport_resource, PORTY_START, PORTY_ILE

        ; wyrejestrowanie zakresu pamieci
        uruchom __release_region, iomem_resource, RAM_START, RAM_ILE

        push    dword usun
        call    printk
        add     esp, 1*4

        pop     eax
        popfd
        ret

; Out interrupt service function. This one does nothing, but argument placemen
t
;       on the stack is shown
; void handler (int irq, void *dev_id, struct pt_regs *regs);

section ".text" executable align 4

obsluga_irq:
        push    ebp
        mov     ebp, esp

; [ebp] = old EBP
; [ebp+4] = return address
; [ebp+8] = arg1
; ...

                irq     equ     ebp+8
                dev_id  equ     ebp+12
                regs    equ     ebp+16

        ; your code here

        leave
        ret

; Define device operations

; Reading from device - return a sequence of 1Eh bytes of the specified length
.
; This device is an infinite source, just like /dev/zero
czytanie:
;       ssize_t (*read) (struct file *, char *, size_t, loff_t *);
        push    ebp
        mov     ebp, esp

        ; argument placement on the stack (3 params in registers):
        loff    equ     ebp+8

        pushfd
        push    edi
        push    ecx

        mov     al, 0x1e
        cld
        mov     edi, edx
        rep     stosb

        pop     ecx
        pop     edi
        popfd

        ; as many as requested was read
        mov     eax, ecx

        leave
        ret

zapis:
;       ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
        push    ebp
        mov     ebp, esp

        ; don't write physically anything, just return the number
        ;       of bytes we were supposed to write (third parameter).
        mov     eax, ecx

        leave
        ret

; seek
przejscie:
; close
zamykanie:
; open
otwieranie:
        xor     eax, eax        ; all 3 functions always return success
        ret



section ".data" writeable align 4

major   dd      0       ; kernel-assigned device major number

; addresses of the functions for device operations
file_oper:      dd 0, przejscie, czytanie, 0, zapis, 0, 0, 0, 0, 0, 0, 0
                dd otwieranie, 0, zamykanie, 0, 0, 0, 0, 0, 0, 0, 0, 0
                dd 0, 0, 0
                dd 0, 0, 0

dev_blad        db      "<1>Device register error: %d.", 10, 0
irq_blad        db      "<1>IRQ assignment error: %d.", 10, 0
porty_blad      db      "<1>Port assignment error:  EAX=%d", 10, 0
ram_blad        db      "<1>Memory assignment error: EAX=%d", 10, 0


uruch           db      "<1>Module loaded. Maj=%d, IRQ=%d", 10, 0
usun            db      "<1>Module removed.", 10, 0

nazwa           db      "test00", 0, 0
sciezka         db      "/dev/test00", 0

section ".modinfo" align 32
__kernel_version        db      "kernel_version=2.6.16", 0
__mod_vermagic db "vermagic=2.6.16 686 REGPARM 4KSTACKS gcc-4.0",0
__module_license        db      "license=GPL", 0
__module_author         db      "author=Bogdan D.", 0
__module_description    db      "description=Example 2.6 kernel module", 0
__module_device         db      "device=test00", 0
__module_depends        db      "depends=", 0

; irrevelant, taken from a compiled C module:
__mod_srcversion        db      "srcversion=F5CE0CFFE0191EDB2F816D4", 0

section "__versions" align 32

____versions:
        dd      0xfa02c634              ; from MODULE.SYMVERS
  n1:   db      "struct_module", 0
        times   64-4-($-n1) db 0

        dd      0x1b7d4074
  n2:   db      "printk", 0
        times   64-4-($-n2) db 0

        dd      0xb5145e00
  n3:   db      "register_chrdev", 0
        times   64-4-($-n3) db 0

        dd      0xc192d491
  n4:   db      "unregister_chrdev", 0
        times   64-4-($-n4) db 0

        dd      0x26e96637
  n5:   db      "request_irq", 0
        times   64-4-($-n5) db 0

        dd      0xf20dabd8
  n6:   db      "free_irq", 0
        times   64-4-($-n6) db 0

        dd      0x1a1a4f09
  n7:   db      "__request_region", 0
        times   64-4-($-n7) db 0

        dd      0xd49501d4
  n8:   db      "__release_region", 0
        times   64-4-($-n8) db 0

        dd      0x865ebccd
  n9:   db      "ioport_resource", 0
        times   64-4-($-n9) db 0

        dd      0x9efed5af
  n10:  db      "iomem_resource", 0
        times   64-4-($-n10) db 0


section ".gnu.linkonce.this_module" writeable align 128

align 128
__this_module:          ; total length: 512 bytes
                        dd 0, 0, 0
        .mod_nazwa:     db "modul_dev_fasm", 0
                        times 64-4-($-.mod_nazwa) db 0
                        times 100 db 0
                        dd init_module
                        times 220 db 0
                        dd cleanup_module
                        times 112 db 0

   To install and remove the module from the kernel you can use the same
   scripts as for 2.4 kernel.

   On-line contents (Alt+2)
   Helpers for people with disabilities (Alt+0)
