Sign in Join
Wiki copytemporary

linux/kernel/module development: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: linux/kernel/module development

OldNewLine
1+# Overview
2+
3+The purpose of this page is to give basic material to write Linux kernel modules using FPC. As Linux distributions may differ, most actions on installing packages are described for Debian distribution, on which the author of this article works. However, it may be very simple to translate this for other Linux distributions.
4+
5+## Language enhancement
6+
7+The ultimate goal of these wiki pages is to provide a proposal for a future support of natural linux kernel module support in particular and driver support in general (for win32...) A dedicated page [[linux/kernel/module language feature|module language feature]] is intended to provide specification for this future support.
8+
9+# Requirements
10+
11+## Compiler
12+
13+First of all, you need to have FPC installed
14+
15+`apt-get install fp-compiler`
16+
17+Please notice that this will install fp-compiler and fp-units-rtl. The author of this page is using 2.2.0, but 2.0.4 is also working. However other versions were not tested and may not work, especially previous releases.
18+
19+## Kernel headers
20+
21+You need to have the kernel headers packages installed
22+
23+`apt-get install linux-headers-$(uname -r)`
24+
25+## GNU make
26+
27+Then you need the make utility
28+
29+`apt-get install make`
30+
31+Please note that installing linux-headers-* will automatically install the required version of GCC and binutils.
32+
33+## Virtual machine
34+
35+It is very recommended to not directly develop kernel modules on your computer. Either you should have a dedicated machine for running your module or have a virtual machine like [[linux/kernel/user mode VM|User Mode Linux (UML)]] Virtual Machine
36+
37+**Using buggy kernel modules may definitely cause harmful effects on your machines**
38+
39+# Kernel RTL
40+
41+It is obvious that when programming Linux kernel modules, one can not use the standard FPC RTL. As no *standard kernel RTL* is provided, you have to write your own one. This is not very easy. A good starting point could be the following
42+
43+``` pascal
44+ unit system;
45+ {$TYPEINFO OFF}
46+ interface
47+ {Paѕcal common type aliases}
48+ type
49+ {Basic embedded types}
50+ u8 = Byte;
51+ u16 = Word;
52+ u32 = LongWord;
53+ u64 = QWord;
54+ s8 = ShortInt;
55+ s16 = SmallInt;
56+ s32 = LongInt;
57+ s64 = Int64;
58+ {Integer types}
59+ DWord = LongWord;
60+ Cardinal = LongWord;
61+ Integer = SmallInt;
62+ UInt64 = QWord;
63+ {$ifdef CPU64}
64+ SizeInt = Int64;
65+ SizeUInt = QWord;
66+ PtrInt = Int64;
67+ PtrUInt = QWord;
68+ ValSInt = int64;
69+ ValUInt = qword;
70+ {$endif CPU64}
71+ {$ifdef CPU32}
72+ SizeInt = Longint;
73+ SizeUInt = DWord;
74+ PtrInt = Longint;
75+ PtrUInt = DWord;
76+ ValSInt = Longint;
77+ ValUInt = Cardinal;
78+ {$endif CPU32}
79+ {Zero - terminated strings }
80+ PChar = ^Char;
81+ PPChar = ^PChar;
82+ {Pointers}
83+ PSmallInt = ^Smallint;
84+ PShortInt = ^Shortint;
85+ PInteger = ^Integer;
86+ PByte = ^Byte;
87+ PWord = ^word;
88+ PDWord = ^DWord;
89+ PLongWord = ^LongWord;
90+ PLongint = ^Longint;
91+ PCardinal = ^Cardinal;
92+ PQWord = ^QWord;
93+ PInt64 = ^Int64;
94+ PPtrInt = ^PtrInt;
95+ PPtrUInt = ^PtrUInt;
96+ PSizeInt = ^SizeInt;
97+ PPointer = ^Pointer;
98+ PPPointer = ^PPointer;
99+ PBoolean = ^Boolean;
100+ PWordBool = ^WordBool;
101+ PLongBool = ^LongBool;
102+ {Other types}
103+ HRESULT = type Longint;
104+ TDateTime = type Double;
105+ TError = type Longint;
106+ const
107+ KERN_INFO='KERNEL:INFO:';
108+ KERN_ALERT='KERNEL:ALERT:';
109+ DEVICE_NAME='kpmod';
110+ EPERM = 1;{Operation not permitted}
111+ ENOENT = 2;{No such file or directory}
112+ ESRCH = 3;{No such process}
113+ EINTR = 4;{Interrupted system call}
114+ EIO = 5;{I/O error}
115+ ENXIO = 6;{No such device or address}
116+ E2BIG = 7;{Argument list too long}
117+ ENOEXEC = 8;{Exec format error}
118+ EBADF = 9;{Bad file number}
119+ ECHILD = 10;{No child processes}
120+ EAGAIN = 11;{Try again}
121+ ENOMEM = 12;{Out of memory}
122+ EACCES = 13;{Permission denied}
123+ EFAULT = 14;{Bad address}
124+ ENOTBLK = 15;{Block device required}
125+ EBUSY = 16;{Device or resource busy}
126+ EEXIST = 17;{File exists}
127+ EXDEV = 18;{Cross-device link}
128+ ENODEV = 19;{No such device}
129+ ENOTDIR = 20;{Not a directory}
130+ EISDIR = 21;{Is a directory}
131+ EINVAL = 22;{Invalid argument}
132+ ENFILE = 23;{File table overflow}
133+ EMFILE = 24;{Too many open files}
134+ ENOTTY = 25;{Not a typewriter}
135+ ETXTBSY = 26;{Text file busy}
136+ EFBIG = 27;{File too large}
137+ ENOSPC = 28;{No space left on device}
138+ ESPIPE = 29;{Illegal seek}
139+ EROFS = 30;{Read-only file system}
140+ EMLINK = 31;{Too many links}
141+ EPIPE = 32;{Broken pipe}
142+ EDOM = 33;{Math argument out of domain of func}
143+ ERANGE = 34;{Math result not representable}
144+ type
145+ {Kernel types}
146+ mode_t = Word;
147+ nlink_t = DWord;
148+ uid_t = Word;
149+ fl_owner_t = ^files_struct;
150+ gid_t = Word;
151+ off_t = LongInt;
152+ loff_t = Int64;
153+ Ploff_t = ^loff_t;
154+ size_t = DWord;
155+ ssize_t = LongInt;
156+ {$PACKRECORDS C}
157+ atomic_t = record
158+ counter:LongInt;
159+ end;
160+ files_struct = record
161+ {read mostly part}
162+ count:atomic_t;
163+ //struct fdtable *fdt;
164+ //struct fdtable fdtab;
165+ {written part on a separate cache line in SMP}
166+ //spinlock_t file_lock ____cacheline_aligned_in_smp;
167+ //int next_fd;
168+ //struct embedded_fd_set close_on_exec_init;
169+ //struct embedded_fd_set open_fds_init;
170+ //struct file * fd_array[NR_OPEN_DEFAULT];
171+ end;
172+ filldir_t = function(arg1:Pointer; arg2:PChar; arg3:LongInt; arg4:loff_t; arg5:u64; arg6:DWord):LongInt; cdecl;
173+ read_actor_t = function(arg1:Pointer{read_descriptor_t *}; arg2:Pointer{struct page *}; arg3:DWord; arg4:DWord):LongInt; cdecl;
174+ Pfile_operations = ^file_operations;
175+ file_operations = record
176+ owner:Pointer;{struct module *}
177+ llseek:function(filp:Pointer{struct file *}; offset:loff_t; whence:LongInt):loff_t; cdecl;
178+ read:function(filp:Pointer{struct file *}; buf:PChar; count:size_t; offset:Ploff_t):ssize_t; cdecl;
179+ write:function(filp:Pointer{struct file *}; buf:PChar; count:size_t; offset:Ploff_t):ssize_t; cdecl;
180+ aio_read:function(kiocb:Pointer{struct kiocb *}; iovec:Pointer{const struct iovec *}; count:DWord; offset:loff_t):ssize_t; cdecl;
181+ aio_write:function(kiocb:Pointer{struct kiocb *}; iovec:Pointer{const struct iovec *}; count:DWord; offset:loff_t):ssize_t; cdecl;
182+ readdir:function(filp:Pointer{struct file *}; buf:Pointer; filldir:filldir_t):LongInt; cdecl;
183+ poll:function(filp:Pointer{struct file *}; poll_table:Pointer{struct poll_table_struct *}):DWord; cdecl;
184+ ioctl:function(inode:Pointer{struct inode *}; filp:Pointer{struct file *}; arg1:DWord; arg2:DWord):LongInt; cdecl;
185+ unlocked_ioctl:function(filp:Pointer{struct file *}; arg1:DWord; arg2:DWord):LongInt; cdecl;
186+ compat_ioctl:function(filp:Pointer{struct file *}; arg1:DWord; arg2:DWord):LongInt; cdecl;
187+ mmap:function(filp:Pointer{struct file *}; vm_area_struct:Pointer{ struct vm_area_struct *}):LongInt; cdecl;
188+ open:function(inode:Pointer{struct inode *}; filp:Pointer{struct file *}):LongInt; cdecl;
189+ flush:function(filp:Pointer{struct file *}; id:fl_owner_t):LongInt; cdecl;
190+ release:function(inode:Pointer{struct inode *}; filp:Pointer{struct file *}):LongInt; cdecl;
191+ fsync:function(filp:Pointer{struct file *}; arg1:Pointer{ struct dentry *}; datasync:LongInt):LongInt; cdecl;
192+ aio_fsync:function(kiocb:Pointer{struct kiocb *}; datasync:LongInt):LongInt; cdecl;
193+ fasync:function(arg1:LongInt; filp:Pointer{struct file *}; arg2:LongInt):LongInt; cdecl;
194+ lock:function(filp:Pointer{struct file *}; arg1:LongInt; arg2:Pointer{ struct file_lock *}):LongInt; cdecl;
195+ sendfile:function(filp:Pointer{struct file *}; arg1:Pointer{ loff_t *}; arg2:size_t; arg3:read_actor_t; arg4:Pointer{ void *}):ssize_t; cdecl;
196+ sendpage:function(filp:Pointer{struct file *}; arg1:Pointer{ struct page *}; arg2:LongInt; arg3:size_t; arg4:Pointer{ loff_t *}; arg5:LongInt):ssize_t; cdecl;
197+ get_unmapped_area:function(filp:Pointer{struct file *}; arg1:DWord; arg2:DWord; arg3:DWord; arg4:DWord):DWord; cdecl;
198+ check_flags:function(falgs:LongInt):LongInt; cdecl;
199+ dir_notify:function(filp:Pointer{struct file *}; arg:DWord):LongInt; cdecl;
200+ flock:function(filp:Pointer{struct file *}; arg1:LongInt; arg2:Pointer{ struct file_lock *}):LongInt; cdecl;
201+ splice_write:function(arg1:Pointer{struct pipe_inode_info *}; filp:Pointer{struct file *}; arg2:Pointer{ loff_t *}; arg3:size_t; arg4:DWord):ssize_t; cdecl;
202+ splice_read:function(filp:Pointer{struct file *}; arg1:Pointer{ loff_t *}; arg2:Pointer{ struct pipe_inode_info *}; arg3:size_t; arg4:DWord):ssize_t; cdecl;
203+ end;
204+ Pproc_dir_entry = ^proc_dir_entry;
205+ proc_dir_entry = record
206+ low_ino:Cardinal;
207+ namelen:Integer;
208+ name:PChar;
209+ mode:mode_t;
210+ nlink:nlink_t;
211+ uid:uid_t;
212+ gid:gid_t;
213+ size:loff_t;
214+ proc_iops:Pointer;{const struct inode_operations *}
215+ proc_fops:Pfile_operations;{const struct file_operations *}
216+ get_info:Pointer;{get_info_t *}
217+ owner:Pointer;{struct module *}
218+ next, parent, subdir:Pproc_dir_entry;
219+ data:Pointer;
220+ read_proc:Pointer;{read_proc_t *}
221+ write_proc:Pointer;{write_proc_t *}
222+ count:atomic_t;{use count}
223+ deleted:LongInt;{delete flag}
224+ _set:Pointer;
225+ end;
226+ var
227+ proc_root:proc_dir_entry; cvar; external;
228+ //function copy_from_user(_to:Pointer; _from:Pointer; count:Cardinal):Cardinal; cdecl;
229+ function create_proc_entry(name:PChar; mode:mode_t; parent:Pproc_dir_entry):Pproc_dir_entry; cdecl;
230+ procedure remove_proc_entry(name:PChar; parent:Pproc_dir_entry); cdecl;
231+ procedure printk(fmt:PChar); cdecl;
232+ procedure printk(fmt:PChar; param:LongInt); cdecl;
233+ procedure printk(fmt:PChar; param1:PChar); cdecl;
234+ procedure printk(fmt:PChar; param1:PChar; param2:LongInt); cdecl;
235+ function register_chrdev(devMajor:LongInt; devName:PChar; fops:Pfile_operations):LongInt; cdecl;
236+ function unregister_chrdev(devMajor:LongInt; devName:PChar):LongInt; cdecl;
237+ implementation
238+ {$LINK kernel_module_info}
239+ //function copy_from_user(_to:Pointer; _from:Pointer; count:Cardinal):Cardinal; cdecl; external;
240+ function create_proc_entry(name:PChar; mode:mode_t; parent:Pproc_dir_entry):Pproc_dir_entry; cdecl; external;
241+ procedure remove_proc_entry(name:PChar; parent:Pproc_dir_entry); cdecl; external;
242+ procedure printk(fmt:PChar); cdecl; external;
243+ procedure printk(fmt:PChar; param:LongInt); cdecl; external;
244+ procedure printk(fmt:PChar; param1:PChar); cdecl; external;
245+ procedure printk(fmt:PChar; param1:PChar; param2:LongInt); cdecl; external;
246+ function register_chrdev(devMajor:LongInt; devName:PChar; fops:Pfile_operations):LongInt; cdecl; external;
247+ function unregister_chrdev(devMajor:LongInt; devName:PChar):LongInt; cdecl; external;
248+ end.
249+```
250+
251+# Module code
252+
253+``` pascal
254+ unit kernel_module;
255+ {$TYPEINFO OFF}
256+ interface
257+
258+ implementation
259+ const
260+ PROCFS_MAX_SIZE = 1024;
261+ PROCFS_NAME = 'kpmod';
262+ BUF_LEN = 256;
263+ var
264+ {Major number assigned to our device driver}
265+ devMajor:LongInt;
266+ fops:file_operations;
267+ i, j, k:LongInt;
268+ {Is device open? Used to prevent multiple access to device}
269+ devIsOpen:Boolean = False;
270+ {pattern:Array[0..1, 0..7, 0..7]of LongInt = (
271+ (($A,$9,$5,$6,$A,$9,$5,$6), ($A,$8,$9,$1,$5,$4,$6,$2)),
272+ (($6,$5,$9,$A,$6,$5,$9,$A), ($2,$6,$4,$5,$1,$9,$8,$A)));}
273+ {This structure hold information about the /proc file}
274+ Our_Proc_File:Pproc_dir_entry;
275+ {The buffer used to store character for this module}
276+ //procfs_buffer:Array[0..PROCFS_MAX_SIZE - 1]of Char;
277+ {The size of the buffer}
278+ procfs_buffer_size:Cardinal = 0;
279+ {The msg the device will give when asked}
280+ msg:Array[0..BUF_LEN-1]of Char;
281+ msg_Ptr:PChar;
282+
283+ {* This function is called then the /proc file is read
284+ *
285+ * Arguments
286+ * =========
287+ * 1. The buffer where the data is to be inserted, if
288+ * you decide to use it.
289+ * 2. A pointer to a pointer to characters. This is
290+ * useful if you don't want to use the buffer
291+ * allocated by the kernel.
292+ * 3. The current position in the file
293+ * 4. The size of the buffer in the first argument.
294+ * 5. Write a "1" here to indicate EOF.
295+ * 6. A pointer to data (useful in case one common
296+ * read for multiple /proc/... entries)
297+ *
298+ * Usage and Return Value
299+ * ======================
300+ * A return value of zero means you have no further
301+ * information at this time (end of file). A negative
302+ * return value is an error condition.
303+ *
304+ * For More Information
305+ * ====================
306+ * The way I discovered what to do with this function
307+ * wasn't by reading documentation, but by reading the
308+ * code which used it. I just looked to see what uses
309+ * the get_info field of proc_dir_entry struct (I used a
310+ * combination of find and grep, if you're interested),
311+ * and I saw that it is used in <kernel source
312+ * directory>/fs/proc/array.c.
313+ *
314+ * If something is unknown about the kernel, this is
315+ * usually the way to go. In Linux we have the great
316+ * advantage of having the kernel source code for
317+ * free - use it.
318+ *}
319+ function procfile_read(buffer:PChar; buffer_location:PPChar; offset:off_t; buffer_length:LongInt; eof:PLongInt; data:Pointer):LongInt; cdecl;
320+ begin
321+ printk(KERN_INFO + 'procfile_read (/proc/%s) called'#10, PROCFS_NAME);
322+ {*
323+ * We give all of our information in one go, so if the
324+ * user asks us if we have more information the
325+ * answer should always be no.
326+ *
327+ * This is important because the standard read
328+ * function from the library would continue to issue
329+ * the read system call until the kernel replies
330+ * that it has no more information, or until its
331+ * buffer is filled.
332+ *}
333+ if offset > 0 then begin
334+ {we have finished to read, return 0}
335+ procfile_read := 0;
336+ end else begin
337+ {fill the buffer, return the buffer size}
338+ //move('HelloWorld!'#10#0, buffer, 13);
339+ //memcpy(buffer, procfs_buffer, procfs_buffer_size);
340+ buffer[0] := 'H';
341+ buffer[1] := 'e';
342+ buffer[2] := 'l';
343+ buffer[3] := 'l';
344+ buffer[4] := 'o';
345+ buffer[5] := #10;
346+ buffer[6] := #0;
347+ procfile_read := 6;
348+ end;
349+ end;
350+ {This function is called with the /proc file is written}
351+ function procfile_write(_file:Pointer{struct file *}; buffer:PChar; count:Cardinal; data:Pointer):LongInt; cdecl;
352+ begin
353+ printk(KERN_INFO + 'procfile_write (/proc/%s) called'#10, PROCFS_NAME);
354+ {get buffer size}
355+ procfs_buffer_size := count;
356+ if procfs_buffer_size > PROCFS_MAX_SIZE then
357+ procfs_buffer_size := PROCFS_MAX_SIZE;
358+ {write data to the buffer}
359+ //if copy_from_user(@procfs_buffer, buffer, procfs_buffer_size) <> 0 then
360+ // procfile_write := -EFAULT
361+ //else
362+ procfile_write := procfs_buffer_size;
363+ end;
364+ {Called when a process tries to open the device file, like "cat /dev/kpmod"}
365+ function device_open(inode:Pointer{struct inode *}; filp:Pointer{struct file *}):LongInt; cdecl;
366+ begin
367+ //static int counter = 0;
368+ if devIsOpen then
369+ Exit(-EBUSY);
370+ printk('Ouverture en mode Lecture/Ecriture ...'#10);
371+ //sprintf(msg, "I already told you %d times Hello world!'#10, counter += 1);
372+ //msg_Ptr = msg;
373+ //try_module_get(THIS_MODULE);
374+ devIsOpen := True;
375+ device_open := 0;
376+ end;
377+ {Called when a process, which already opened the dev file, attempts to read from it.}
378+ function device_read(filp:Pointer{struct file *}; buffer:PChar; len:size_t; offset:Ploff_t):ssize_t; cdecl;
379+ var
380+ bytes_read:LongInt;
381+ begin
382+ {Number of bytes actually written to the buffer}
383+ bytes_read := 0;
384+ printk('Writing %d data into buffer from offset %%lld'#10, len{, offset^});
385+ {Actually put the data into the buffer}
386+ //while(len > 0) and (msg_Ptr^ <> #0)do begin
387+ {The buffer is in the user data segment, not the kernel
388+ segment so "*" assignment won't work. We have to use
389+ put_user which copies data from the kernel data segment to
390+ the user data segment.}
391+ //put_user(msg_Ptr^, buffer += 1);
392+ // msg_Ptr += 1;
393+ // len -= 1;
394+ // bytes_read += 1;
395+ //end;
396+ {If we're at the end of the message, return 0 signifying end of file}
397+ {Most read functions return the number of bytes put into the buffer}
398+ device_read := bytes_read;
399+ end;
400+ {Called when a process writes to dev file: "echo 'hi' > /dev/hello"}
401+ function device_write(filp:Pointer{struct file *}; buff:Pchar; len:size_t; offset:Ploff_t):ssize_t; cdecl;
402+ function step:LongInt;
403+ begin
404+ if k < 8 then begin
405+ //printk('%d'#10,pattern[i][j][k]);
406+ //outb(pattern[i][j][k],LPT_BASE);
407+ k += 1;
408+ end else begin
409+ k := 0;
410+ //outb(pattern[i][j][k],LPT_BASE);
411+ //printk('%d'#10,pattern[i][j][k]);
412+ k += 1;
413+ end;
414+ step := 0;
415+ end;
416+ var
417+ data:Char;
418+ cmd:Char;
419+ begin
420+ printk(KERN_ALERT + 'Sorry, write operation on "%s" isn''t supported yet.'#10, '/dev/kpmod');
421+ device_write := -EINVAL;
422+ {
423+ //get_user(data,buffer);
424+ cmd := data;
425+ case cmd of
426+ 'H':begin
427+ printk('Reportez-vous au fichier README'#10);
428+ end;
429+ 'h':begin
430+ printk('Initialisation en mode demi pas'#10);
431+ j=0;
432+ end;
433+ 'f':begin
434+ printk('Initialisation en mode pas entier'#10);
435+ j=1;
436+ end;
437+ 'F':begin
438+ i=0;
439+ step();
440+ end;
441+ 'R':begin
442+ i=1;
443+ step();
444+ end;
445+ else
446+ printk('Reportez-vous au fichier README, les commandes sont H, h, F, f, et R'#10);
447+ end;
448+ device_write := 1;}
449+ end;
450+ {Called when a process closes the device file.}
451+ function device_release(inode:Pointer{struct inode *}; filp:Pointer{struct file *}):LongInt; cdecl;
452+ begin
453+ printk('Fermeture ...'#10);
454+ {Decrement the usage count, or else once you opened the file, you'll never get get rid of the module.}
455+ //module_put(THIS_MODULE);
456+ {We're now ready for our next caller}
457+ devIsOpen := False;
458+ device_release := 0;
459+ end;
460+ {This function is called when the module is loaded}
461+ function init_module: Integer; cdecl; export;
462+ begin
463+ with fops do begin
464+ open := @device_open;
465+ read := @device_read;
466+ write := @device_write;
467+ release := @device_release;
468+ end;
469+ devMajor := register_chrdev(0, DEVICE_NAME, @fops);
470+ if devMajor < 0 then begin
471+ printk(KERN_ALERT + 'Registering char device failed with %d'#10, devMajor);
472+ Exit(devMajor);
473+ end;
474+ printk(KERN_INFO + 'I was assigned major number %d. To talk to'#10, devMajor);
475+ printk(KERN_INFO + 'the driver, create a dev file with'#10);
476+ printk(KERN_INFO + '"mknod /dev/%s c %d 0".'#10, DEVICE_NAME, devMajor);
477+ printk(KERN_INFO + 'Try various minor numbers. Try to cat and echo to'#10);
478+ printk(KERN_INFO + 'the device file.'#10);
479+ printk(KERN_INFO + 'Remove the device file and module when done.'#10);
480+ printk(KERN_INFO + '@fops = $%X'#10, LongInt(@fops));
481+ {create the /proc file}
482+ Our_Proc_File := create_proc_entry(PROCFS_NAME, 0644, Nil);
483+ if Our_Proc_File = Nil then begin
484+ remove_proc_entry(PROCFS_NAME, @proc_root);
485+ printk(KERN_ALERT + 'Could not initialize /proc/%s'#10, PROCFS_NAME);
486+ Exit(-ENOMEM);
487+ end;
488+ printk(KERN_INFO + 'Our_Proc_File = 0x%X'#10, PtrUInt(Our_Proc_File));
489+ with Our_Proc_File^ do begin
490+ read_proc := @procfile_read;
491+ write_proc := @procfile_write;
492+ //owner := THIS_MODULE;
493+ //mode := S_IFREG OR S_IRUGO;
494+ uid := 0;
495+ gid := 0;
496+ size := 37;
497+ end;
498+ printk(KERN_INFO + '/proc/%s created'#10, PROCFS_NAME);
499+ {everything is ok}
500+ init_module := 0;
501+ end;
502+
503+ {This function is called when the module is unloaded}
504+ procedure cleanup_module; cdecl; export;
505+ var
506+ ret:LongInt;
507+ begin
508+ remove_proc_entry(PROCFS_NAME, @proc_root);
509+ printk(KERN_INFO + '/proc/%s removed'#10, PROCFS_NAME);
510+ ret := unregister_chrdev(devMajor, DEVICE_NAME);
511+ if ret < 0 then
512+ printk(KERN_ALERT + 'Error in unregister_chrdev: %d'#10, ret);
513+ end;
514+
515+ end.
516+```
517+
518+# Compilation
519+
520+In order to compile the kernel module, you need an object file called **kernel_module_info.c**. This file should contain information about the driver : its description string, its author and its licence.
521+
522+``` pascal
523+ #include <linux/module.h>
524+
525+ MODULE_DESCRIPTION("Test Module written using FPC");
526+ MODULE_AUTHOR("Mazen Neifer");
527+ MODULE_LICENSE("GPL");
528+```
529+
530+All you need now to generate your kernel module is to integrate your module sources within the kernel build system. The following make file gives an example how to do that.
531+
532+``` pascal
533+ obj-m := kernel_pmodule.o
534+ kernel_pmodule-objs := kernel_module_info.o kernel_module.o system.o
535+ KDIR := /lib/modules/$(shell uname -r)/build
536+ PWD := $(shell pwd)
537+
538+ default:kernel_module.o
539+ $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
540+ kernel_module.o: kernel_module.pas system.pas
541+ fpc kernel_module
542+ clean:
543+ ${RM} *.ko *.mod.c *.o *.ppu Module.symvers .*.ko.cmd .*.o.cmd
544+ ${RM} -r .tmp_versions
545+```
546+
547+Now, to compile the module, just type
548+
549+`make`
550+
551+You will get a **kernel_pmodule.ko** as output. Then you can load it using
552+
553+`insmod kernel_pmodule.ko`
554+
555+## Limitation
556+
557+Due to a mysterious bug somewhere, the result of the first compilation may hang your kernel. A known workaround is to recompile the **kernel_module.pas** without recompiling **system.pas**.
558+
559+`make`\
560+`rm kernel_module.o`\
561+`make`
562+
563+Then the **kernel_pmodule.ko** is now usable. It seems that when **system** unit is compiled within compilation process of **kernel_module** unit, the object code is not suitable for kernel usage. *To be monitored*
564+
565+# Building kernel modules (for fun) thread
566+
567+As posted at <http://thread.gmane.org/gmane.comp.compilers.free-pascal.general/21846> We all know writing in Pascal is fun, creating kernel modules is easy, so why not combine both. How hard can it be ? ;)
568+
569+Oh well, the below code is what's made of the _above_ code, maybe resulting in no significant result changes (apart from being stripped down to just create a proof-of-concept Hello World program). Frankly I don't even know whether all these definitions in system.pas are really required at all.
570+
571+Oh and I don't usually have _much_ time to play with this. If the concept is more or less along with what you Readers like, maybe I'll put the resulting headers and examples onto a git repo for eternity ;)
572+
573+I don't post this to Linux kernel mailing list (yet), because they think that basing some new development (especially in kernel space) on _stable_ and longterm version of kernel is \[censored\] and \[censored\] and generally retarded. And that completely apart from the old Pascal vs C war(s). But I just wanted to have some fun with it, see where I can get to, with some help of you guys out there. So, expect newbie questions ;)
574+
575+So, usual warnings apply : if it eats your hard disk, draws smileys over your family photos, whatever, don't call me. I wouldn't know why, anyway... It may even try to kill your dog. My predecessor's advice to use User Mode Linux is a good and fair point. As for me, I don't have family photos on this hard disk (any more) and the hardware starts to die, so I don't care that much.
576+
577+El es 14:05, 23 January 2011 (CET)
578+
579+## Prerequisites
580+
581+### Installation
582+
583+All is happening on Ubuntu 10.04 LTS, installation instructions above are valid, only use **sudo** in front of them. It also is good to have the kernel-source package installed; it gives you the source tarball into **/usr/src** which you may want to unpack into another directory.
584+
585+### Suggested reading
586+
587+**\<\<linux source\>\>/Documentation/stable_api_nonsense.txt**
588+
589+**\<\<linux source\>\>/Documentation/DocBook/kernel-hacking.tmpl** (or make htmldoc in the main source directory - warning: this needs **xmlto** installed. Which on my system wanted to download 447 MB from the Internet and take up 756 MB on my disk, so I didn't do it; the tmpl file is sort of readable without making it into a doc)
590+
591+The latter contains a nice insider technical view of what we expect the kernel to be doing. The former contains the reasons why they think hacking on a stable kernel is... see above.
592+
593+### Hello World Example
594+
595+Copy and paste the file contents from here to appropriately named file(s) in your directory of choice.
596+
597+#### Makefile
598+
599+``` pascal
600+ # Free Pascal Demo Kernel Module
601+ # * the Makefile
602+ # * Based on work by Mazen Neifer, originally posted on.
603+ # * http://wiki.freepascal.org/linux/kernel/module_development
604+ # * Stripped down to bare minimum needed for usable Hello World!
605+ # * Some definitions also rearranged.
606+ # * (c) Lukasz Sokol, 2011 License : GPL
607+ # *)
608+
609+ obj-m := fpkm_hello.o
610+ fpkm_hello-objs := module_include.o fp_hello.o system.o
611+ KDIR := /lib/modules/$(shell uname -r)/build
612+ PWD := $(shell pwd)
613+
614+ default:fp_hello.o
615+ $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
616+
617+ fp_hello.o: system.pas fp_printk.pas fp_hello.pas
618+ fpc fp_hello
619+
620+
621+ clean:
622+ ${RM} *.ko *.mod.c *.o *.ppu Module.symvers .*.ko.cmd .*.o.cmd
623+ ${RM} -r .tmp_versions makeout modules.order
624+```
625+
626+#### fp_printk.pas
627+
628+``` pascal
629+ (* Free Pascal Headers for Linux Kernel
630+ * Pascal Code for kernel printk function
631+ * Based on work by Mazen Neifer, originally posted on
632+ * http://wiki.freepascal.org/linux/kernel/module_development
633+ * (c) Lukasz Sokol, 2011 License : GPL
634+ *)
635+ unit fp_printk;
636+ {$TYPEINFO OFF}
637+ interface
638+ const
639+ KERN_INFO='KERNEL:INFO:';
640+ KERN_ALERT='KERNEL:ALERT:';
641+
642+ procedure printk(fmt:PChar); cdecl; external;
643+
644+ implementation
645+
646+ end.
647+```
648+
649+#### fp_hello.pas
650+
651+``` pascal
652+ (* Free Pascal Demo Kernel Module
653+ * Pascal Module Code
654+ * Based on work by Mazen Neifer, originally posted on
655+ * http://wiki.freepascal.org/linux/kernel/module_development
656+ * Stripped down to bare minimum needed for usable Hello World!
657+ * Some definitions also rearranged.
658+ * (c) Lukasz Sokol, 2011 License : GPL
659+ *)
660+
661+ unit kernel_module;
662+ {$TYPEINFO OFF}
663+ interface
664+
665+
666+ implementation
667+ uses fp_printk;
668+
669+ {$LINK kernel_module_info}
670+
671+ {This function is called when the module is loaded}
672+ function init_module: Integer; cdecl; export;
673+ begin
674+ printk(KERN_INFO + 'Hello World!'+#10+#0);
675+ init_module := 0;
676+ end;
677+
678+ {This function is called when the module is unloaded}
679+ procedure cleanup_module; cdecl; export;
680+ begin
681+ printk(KERN_INFO + 'Bye-bye!'+#10+#0);
682+ end;
683+
684+ end.
685+```
686+
687+#### system.pas
688+
689+``` pascal
690+ (* Free Pascal Demo Kernel Module
691+ * System Unit Code
692+ * Based on work by Mazen Neifer, originally posted on
693+ * http://wiki.freepascal.org/linux/kernel/module_development
694+ * Stripped down to bare minimum needed for usable Hello World!
695+ * Some definitions also rearranged.
696+ * (c) Lukasz Sokol, 2011 License : GPL
697+ *)
698+ unit system;
699+ {$TYPEINFO OFF}
700+ interface
701+ {Paѕcal common type aliases}
702+ type
703+ {Basic embedded types}
704+ u8 = Byte;
705+ u16 = Word;
706+ u32 = LongWord;
707+ u64 = QWord;
708+ s8 = ShortInt;
709+ s16 = SmallInt;
710+ s32 = LongInt;
711+ s64 = Int64;
712+ {Integer types}
713+ DWord = LongWord;
714+ Cardinal = LongWord;
715+ Integer = SmallInt;
716+ UInt64 = QWord;
717+ {$ifdef CPU64}
718+ SizeInt = Int64;
719+ SizeUInt = QWord;
720+ PtrInt = Int64;
721+ PtrUInt = QWord;
722+ ValSInt = int64;
723+ ValUInt = qword;
724+ {$endif CPU64}
725+ {$ifdef CPU32}
726+ SizeInt = Longint;
727+ SizeUInt = DWord;
728+ PtrInt = Longint;
729+ PtrUInt = DWord;
730+ ValSInt = Longint;
731+ ValUInt = Cardinal;
732+ {$endif CPU32}
733+ {Zero - terminated strings }
734+ PChar = ^Char;
735+ PPChar = ^PChar;
736+ {Pointers}
737+ PSmallInt = ^Smallint;
738+ PShortInt = ^Shortint;
739+ PInteger = ^Integer;
740+ PByte = ^Byte;
741+ PWord = ^word;
742+ PDWord = ^DWord;
743+ PLongWord = ^LongWord;
744+ PLongint = ^Longint;
745+ PCardinal = ^Cardinal;
746+ PQWord = ^QWord;
747+ PInt64 = ^Int64;
748+ PPtrInt = ^PtrInt;
749+ PPtrUInt = ^PtrUInt;
750+ PSizeInt = ^SizeInt;
751+ PPointer = ^Pointer;
752+ PPPointer = ^PPointer;
753+ PBoolean = ^Boolean;
754+ PWordBool = ^WordBool;
755+ PLongBool = ^LongBool;
756+ {Other types}
757+ HRESULT = type Longint;
758+ TDateTime = type Double;
759+ TError = type Longint;
760+ const
761+ EPERM = 1;{Operation not permitted}
762+ ENOENT = 2;{No such file or directory}
763+ ESRCH = 3;{No such process}
764+ EINTR = 4;{Interrupted system call}
765+ EIO = 5;{I/O error}
766+ ENXIO = 6;{No such device or address}
767+ E2BIG = 7;{Argument list too long}
768+ ENOEXEC = 8;{Exec format error}
769+ EBADF = 9;{Bad file number}
770+ ECHILD = 10;{No child processes}
771+ EAGAIN = 11;{Try again}
772+ ENOMEM = 12;{Out of memory}
773+ EACCES = 13;{Permission denied}
774+ EFAULT = 14;{Bad address}
775+ ENOTBLK = 15;{Block device required}
776+ EBUSY = 16;{Device or resource busy}
777+ EEXIST = 17;{File exists}
778+ EXDEV = 18;{Cross-device link}
779+ ENODEV = 19;{No such device}
780+ ENOTDIR = 20;{Not a directory}
781+ EISDIR = 21;{Is a directory}
782+ EINVAL = 22;{Invalid argument}
783+ ENFILE = 23;{File table overflow}
784+ EMFILE = 24;{Too many open files}
785+ ENOTTY = 25;{Not a typewriter}
786+ ETXTBSY = 26;{Text file busy}
787+ EFBIG = 27;{File too large}
788+ ENOSPC = 28;{No space left on device}
789+ ESPIPE = 29;{Illegal seek}
790+ EROFS = 30;{Read-only file system}
791+ EMLINK = 31;{Too many links}
792+ EPIPE = 32;{Broken pipe}
793+ EDOM = 33;{Math argument out of domain of func}
794+ ERANGE = 34;{Math result not representable}
795+ type
796+ {Kernel types}
797+ mode_t = Word;
798+ nlink_t = DWord;
799+ uid_t = Word;
800+ gid_t = Word;
801+ off_t = LongInt;
802+ loff_t = Int64;
803+ Ploff_t = ^loff_t;
804+ size_t = DWord;
805+ ssize_t = LongInt;
806+
807+ implementation
808+
809+ end.
810+```
811+
812+#### module_include.c
813+
814+``` pascal
815+ /* Free Pascal Demo Kernel Module
816+ * c code that is linked into Pascal after compile - needed for symbols
817+ * Based on work by Mazen Neifer, originally posted on
818+ * http://wiki.freepascal.org/linux/kernel/module_development
819+ * Stripped down to bare minimum needed for usable Hello World!
820+ * Some definitions also rearranged.
821+ * (c) Lukasz Sokol, 2011 License : GPL
822+ */
823+
824+ #include <linux/module.h>
825+
826+ MODULE_DESCRIPTION("Test Module written using FPC");
827+ MODULE_AUTHOR("Lukasz Sokol (fork)");
828+ MODULE_LICENSE("GPL");
829+```
830+
831+### Enhancing printk
832+
833+If you make your fp_printk file look like this:
834+
835+#### fp_printk.pas
836+
837+``` pascal
838+ (* Free Pascal Headers for Linux Kernel
839+ * Pascal Code for kernel printk function
840+ * Based on work by Mazen Neifer, originally posted on
841+ * http://wiki.freepascal.org/linux/kernel/module_development
842+ * (c) Lukasz Sokol, 2011 License : GPL
843+ *)
844+ unit fp_printk;
845+ {$TYPEINFO OFF}
846+ interface
847+
848+ const
849+ // codes for printk() call, copied from <src>include/linux/kernel.h
850+ KERN_EMERG = '<0>'; //* system is unusable */
851+ KERN_ALERT = '<1>'; //* action must be taken immediately */
852+ KERN_CRIT = '<2>'; //* critical conditions */
853+ KERN_ERR = '<3>'; //* error conditions */
854+ KERN_WARNING = '<4>'; //* warning conditions */
855+ KERN_NOTICE = '<5>'; //* normal but significant condition */
856+ KERN_INFO = '<6>'; //* informational */
857+ KERN_DEBUG = '<7>'; //* debug-level messages */
858+
859+ procedure printk(fmt:PChar); cdecl; varargs; external;
860+
861+ implementation
862+
863+ end.
864+```
865+
866+I.e. using varargs clause after the cdecl, as per \[this link [1](http://www.freepascal.org/docs-html/ref/refsu77.html#x159-16900011.9.17)\], you can use the full power of kernel printk, as described among others \[here [2](http://www.mjmwired.net/kernel/Documentation/printk-formats.txt)\] making your main module source look like :
867+
868+#### fp_hello.pas
869+
870+``` pascal
871+ (* Free Pascal Demo Kernel Module
872+ * Pascal Module Code
873+ * Based on work by Mazen Neifer, originally posted on
874+ * http://wiki.freepascal.org/linux/kernel/module_development
875+ * Stripped down to bare minimum needed for usable Hello World!
876+ * Some definitions also rearranged.
877+ * (c) Lukasz Sokol, 2011 License : GPL
878+ *)
879+
880+ unit fp_hello;
881+ {$TYPEINFO OFF}
882+ interface
883+
884+ implementation
885+ uses fp_printk;
886+
887+ const LFEOL = #10#0;
888+ {$LINK kernel_module_info}
889+
890+ {This function is called when the module is loaded}
891+ function init_module: Integer; cdecl; export;
892+ begin
893+ printk(KERN_INFO + 'Hello World!'+LFEOL);
894+ printk(KERN_INFO + 'Also works with variable number of arguments like %d, 0x%.2x, %u '+LFEOL, -10, 15, 10);
895+ init_module := 0;
896+ end;
897+
898+ {This function is called when the module is unloaded}
899+ procedure cleanup_module; cdecl; export;
900+ begin
901+ printk(KERN_INFO + 'Bye-bye!'+LFEOL);
902+ end;
903+
904+ end.
905+```
906+
907+producing
908+
909+### dmesg output
910+
911+`[39432.662017]  Hello World!`\
912+`[39432.662028] Also works with variable number of arguments like -10, 0x0f, 10 `\
913+`[39432.679324] Bye-bye!`
914+
915+Nice innit ?
916+
917+### Hello World Compilation
918+
919+Having Midnight Commander on hand is, erm, handy. Change directory into where your above files are, and call **make**.
920+
921+` fpc fp_hello`\
922+` Free Pascal Compiler version 2.4.0-2 [2010/03/06] for i386`\
923+` Copyright (c) 1993-2009 by Florian Klaempfl`\
924+` Target OS: Linux for i386`\
925+` Compiling fp_hello.pas`\
926+` Compiling system.pas`\
927+` Compiling fp_printk.pas`\
928+` 189 lines compiled, 0.1 sec `\
929+` make -C /lib/modules/2.6.32-27-generic/build SUBDIRS=/home/lukasz/projects/kfpc/hello_world modules`\
930+``  make[1]: Entering directory `/usr/src/linux-headers-2.6.32-27-generic' ``\
931+`  CC [M]  /home/lukasz/projects/kfpc/hello_world/module_include.o`\
932+`  LD [M]  /home/lukasz/projects/kfpc/hello_world/fpkm_hello.o`\
933+`  Building modules, stage 2.`\
934+`  MODPOST 1 modules`\
935+` WARNING: could not find /home/lukasz/projects/kfpc/hello_world/.fp_hello.o.cmd for /home/lukasz/projects/kfpc/hello_world/fp_hello.o`\
936+`   CC      /home/lukasz/projects/kfpc/hello_world/fpkm_hello.mod.o`\
937+`   LD [M]  /home/lukasz/projects/kfpc/hello_world/fpkm_hello.ko`\
938+``  make[1]: Leaving directory `/usr/src/linux-headers-2.6.32-27-generic' ``\
939+` lukasz@lukasz-laptop:~/projects/kfpc$ `
940+
941+### Result
942+
943+With the tools at current versions of Ubuntu 10.04(LTS), the #Limitation does not seem to apply - it works straight away, i.e. did not hang MY machine. After that **sudo insmod kernel_pmodule.ko** and **sudo rmmod kernel_pmodule** will give you
944+
945+` lukasz@lukasz-laptop:~/projects/kfpc$ sudo insmod fp_hello.ko && sudo rmmod fp_hello`\
946+` [sudo] password for lukasz: `\
947+` lukasz@lukasz-laptop:~/projects/kfpc$ `
948+
949+(this means : no errors)
950+
951+and **tail /var/log/messages** yields
952+
953+`[54219.552565] Hello World!`\
954+`[54219.587641] Bye-bye!`
955+
956+---
957+*From the Free Pascal wiki, page "linux/kernel/module development", as it stood on 2020-01-26 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
958+Category: Linux
959+Category: Proposals

Back to history