Sign in Join
Wiki copytemporary

linux/kernel/module development: what changed

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

OldNewLine
11# Overview
22
33The 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.
44
55## Language enhancement
66
77The 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.
88
99# Requirements
1010
1111## Compiler
1212
1313First of all, you need to have FPC installed
1414
15-`apt-get install fp-compiler`
15+```text
16+apt-get install fp-compiler
17+```
1618
1719Please 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.
1820
1921## Kernel headers
2022
2123You need to have the kernel headers packages installed
2224
23-`apt-get install linux-headers-$(uname -r)`
25+```text
26+apt-get install linux-headers-$(uname -r)
27+```
2428
2529## GNU make
2630
2731Then you need the make utility
2832
29-`apt-get install make`
33+```text
34+apt-get install make
35+```
3036
3137Please note that installing linux-headers-* will automatically install the required version of GCC and binutils.
3238
3339## Virtual machine
3440
3541It 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
3642
3743**Using buggy kernel modules may definitely cause harmful effects on your machines**
3844
3945# Kernel RTL
4046
4147It 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
4248
4349``` pascal
4450 unit system;
4551 {$TYPEINFO OFF}
4652 interface
4753 {Paѕcal common type aliases}
4854 type
4955 {Basic embedded types}
5056 u8 = Byte;
5157 u16 = Word;
5258 u32 = LongWord;
5359 u64 = QWord;
5460 s8 = ShortInt;
5561 s16 = SmallInt;
5662 s32 = LongInt;
5763 s64 = Int64;
5864 {Integer types}
5965 DWord = LongWord;
6066 Cardinal = LongWord;
6167 Integer = SmallInt;
6268 UInt64 = QWord;
6369 {$ifdef CPU64}
6470 SizeInt = Int64;
6571 SizeUInt = QWord;
6672 PtrInt = Int64;
6773 PtrUInt = QWord;
6874 ValSInt = int64;
6975 ValUInt = qword;
7076 {$endif CPU64}
7177 {$ifdef CPU32}
7278 SizeInt = Longint;
7379 SizeUInt = DWord;
7480 PtrInt = Longint;
7581 PtrUInt = DWord;
7682 ValSInt = Longint;
7783 ValUInt = Cardinal;
7884 {$endif CPU32}
7985 {Zero - terminated strings }
8086 PChar = ^Char;
8187 PPChar = ^PChar;
8288 {Pointers}
8389 PSmallInt = ^Smallint;
8490 PShortInt = ^Shortint;
8591 PInteger = ^Integer;
8692 PByte = ^Byte;
8793 PWord = ^word;
8894 PDWord = ^DWord;
8995 PLongWord = ^LongWord;
9096 PLongint = ^Longint;
9197 PCardinal = ^Cardinal;
9298 PQWord = ^QWord;
9399 PInt64 = ^Int64;
94100 PPtrInt = ^PtrInt;
95101 PPtrUInt = ^PtrUInt;
96102 PSizeInt = ^SizeInt;
97103 PPointer = ^Pointer;
98104 PPPointer = ^PPointer;
99105 PBoolean = ^Boolean;
100106 PWordBool = ^WordBool;
101107 PLongBool = ^LongBool;
102108 {Other types}
103109 HRESULT = type Longint;
104110 TDateTime = type Double;
105111 TError = type Longint;
106112 const
107113 KERN_INFO='KERNEL:INFO:';
108114 KERN_ALERT='KERNEL:ALERT:';
109115 DEVICE_NAME='kpmod';
110116 EPERM = 1;{Operation not permitted}
111117 ENOENT = 2;{No such file or directory}
112118 ESRCH = 3;{No such process}
113119 EINTR = 4;{Interrupted system call}
114120 EIO = 5;{I/O error}
115121 ENXIO = 6;{No such device or address}
116122 E2BIG = 7;{Argument list too long}
117123 ENOEXEC = 8;{Exec format error}
118124 EBADF = 9;{Bad file number}
119125 ECHILD = 10;{No child processes}
120126 EAGAIN = 11;{Try again}
121127 ENOMEM = 12;{Out of memory}
122128 EACCES = 13;{Permission denied}
123129 EFAULT = 14;{Bad address}
124130 ENOTBLK = 15;{Block device required}
125131 EBUSY = 16;{Device or resource busy}
126132 EEXIST = 17;{File exists}
127133 EXDEV = 18;{Cross-device link}
128134 ENODEV = 19;{No such device}
129135 ENOTDIR = 20;{Not a directory}
130136 EISDIR = 21;{Is a directory}
131137 EINVAL = 22;{Invalid argument}
132138 ENFILE = 23;{File table overflow}
133139 EMFILE = 24;{Too many open files}
134140 ENOTTY = 25;{Not a typewriter}
135141 ETXTBSY = 26;{Text file busy}
136142 EFBIG = 27;{File too large}
137143 ENOSPC = 28;{No space left on device}
138144 ESPIPE = 29;{Illegal seek}
139145 EROFS = 30;{Read-only file system}
140146 EMLINK = 31;{Too many links}
141147 EPIPE = 32;{Broken pipe}
142148 EDOM = 33;{Math argument out of domain of func}
143149 ERANGE = 34;{Math result not representable}
144150 type
145151 {Kernel types}
146152 mode_t = Word;
147153 nlink_t = DWord;
148154 uid_t = Word;
149155 fl_owner_t = ^files_struct;
150156 gid_t = Word;
151157 off_t = LongInt;
152158 loff_t = Int64;
153159 Ploff_t = ^loff_t;
154160 size_t = DWord;
155161 ssize_t = LongInt;
156162 {$PACKRECORDS C}
157163 atomic_t = record
158164 counter:LongInt;
159165 end;
160166 files_struct = record
161167 {read mostly part}
162168 count:atomic_t;
163169 //struct fdtable *fdt;
164170 //struct fdtable fdtab;
165171 {written part on a separate cache line in SMP}
166172 //spinlock_t file_lock ____cacheline_aligned_in_smp;
167173 //int next_fd;
168174 //struct embedded_fd_set close_on_exec_init;
169175 //struct embedded_fd_set open_fds_init;
170176 //struct file * fd_array[NR_OPEN_DEFAULT];
171177 end;
172178 filldir_t = function(arg1:Pointer; arg2:PChar; arg3:LongInt; arg4:loff_t; arg5:u64; arg6:DWord):LongInt; cdecl;
173179 read_actor_t = function(arg1:Pointer{read_descriptor_t *}; arg2:Pointer{struct page *}; arg3:DWord; arg4:DWord):LongInt; cdecl;
174180 Pfile_operations = ^file_operations;
175181 file_operations = record
176182 owner:Pointer;{struct module *}
177183 llseek:function(filp:Pointer{struct file *}; offset:loff_t; whence:LongInt):loff_t; cdecl;
178184 read:function(filp:Pointer{struct file *}; buf:PChar; count:size_t; offset:Ploff_t):ssize_t; cdecl;
179185 write:function(filp:Pointer{struct file *}; buf:PChar; count:size_t; offset:Ploff_t):ssize_t; cdecl;
180186 aio_read:function(kiocb:Pointer{struct kiocb *}; iovec:Pointer{const struct iovec *}; count:DWord; offset:loff_t):ssize_t; cdecl;
181187 aio_write:function(kiocb:Pointer{struct kiocb *}; iovec:Pointer{const struct iovec *}; count:DWord; offset:loff_t):ssize_t; cdecl;
182188 readdir:function(filp:Pointer{struct file *}; buf:Pointer; filldir:filldir_t):LongInt; cdecl;
183189 poll:function(filp:Pointer{struct file *}; poll_table:Pointer{struct poll_table_struct *}):DWord; cdecl;
184190 ioctl:function(inode:Pointer{struct inode *}; filp:Pointer{struct file *}; arg1:DWord; arg2:DWord):LongInt; cdecl;
185191 unlocked_ioctl:function(filp:Pointer{struct file *}; arg1:DWord; arg2:DWord):LongInt; cdecl;
186192 compat_ioctl:function(filp:Pointer{struct file *}; arg1:DWord; arg2:DWord):LongInt; cdecl;
187193 mmap:function(filp:Pointer{struct file *}; vm_area_struct:Pointer{ struct vm_area_struct *}):LongInt; cdecl;
188194 open:function(inode:Pointer{struct inode *}; filp:Pointer{struct file *}):LongInt; cdecl;
189195 flush:function(filp:Pointer{struct file *}; id:fl_owner_t):LongInt; cdecl;
190196 release:function(inode:Pointer{struct inode *}; filp:Pointer{struct file *}):LongInt; cdecl;
191197 fsync:function(filp:Pointer{struct file *}; arg1:Pointer{ struct dentry *}; datasync:LongInt):LongInt; cdecl;
192198 aio_fsync:function(kiocb:Pointer{struct kiocb *}; datasync:LongInt):LongInt; cdecl;
193199 fasync:function(arg1:LongInt; filp:Pointer{struct file *}; arg2:LongInt):LongInt; cdecl;
194200 lock:function(filp:Pointer{struct file *}; arg1:LongInt; arg2:Pointer{ struct file_lock *}):LongInt; cdecl;
195201 sendfile:function(filp:Pointer{struct file *}; arg1:Pointer{ loff_t *}; arg2:size_t; arg3:read_actor_t; arg4:Pointer{ void *}):ssize_t; cdecl;
196202 sendpage:function(filp:Pointer{struct file *}; arg1:Pointer{ struct page *}; arg2:LongInt; arg3:size_t; arg4:Pointer{ loff_t *}; arg5:LongInt):ssize_t; cdecl;
197203 get_unmapped_area:function(filp:Pointer{struct file *}; arg1:DWord; arg2:DWord; arg3:DWord; arg4:DWord):DWord; cdecl;
198204 check_flags:function(falgs:LongInt):LongInt; cdecl;
199205 dir_notify:function(filp:Pointer{struct file *}; arg:DWord):LongInt; cdecl;
200206 flock:function(filp:Pointer{struct file *}; arg1:LongInt; arg2:Pointer{ struct file_lock *}):LongInt; cdecl;
201207 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;
202208 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;
203209 end;
204210 Pproc_dir_entry = ^proc_dir_entry;
205211 proc_dir_entry = record
206212 low_ino:Cardinal;
207213 namelen:Integer;
208214 name:PChar;
209215 mode:mode_t;
210216 nlink:nlink_t;
211217 uid:uid_t;
212218 gid:gid_t;
213219 size:loff_t;
214220 proc_iops:Pointer;{const struct inode_operations *}
215221 proc_fops:Pfile_operations;{const struct file_operations *}
216222 get_info:Pointer;{get_info_t *}
217223 owner:Pointer;{struct module *}
218224 next, parent, subdir:Pproc_dir_entry;
219225 data:Pointer;
220226 read_proc:Pointer;{read_proc_t *}
221227 write_proc:Pointer;{write_proc_t *}
222228 count:atomic_t;{use count}
223229 deleted:LongInt;{delete flag}
224230 _set:Pointer;
225231 end;
226232 var
227233 proc_root:proc_dir_entry; cvar; external;
228234 //function copy_from_user(_to:Pointer; _from:Pointer; count:Cardinal):Cardinal; cdecl;
229235 function create_proc_entry(name:PChar; mode:mode_t; parent:Pproc_dir_entry):Pproc_dir_entry; cdecl;
230236 procedure remove_proc_entry(name:PChar; parent:Pproc_dir_entry); cdecl;
231237 procedure printk(fmt:PChar); cdecl;
232238 procedure printk(fmt:PChar; param:LongInt); cdecl;
233239 procedure printk(fmt:PChar; param1:PChar); cdecl;
234240 procedure printk(fmt:PChar; param1:PChar; param2:LongInt); cdecl;
235241 function register_chrdev(devMajor:LongInt; devName:PChar; fops:Pfile_operations):LongInt; cdecl;
236242 function unregister_chrdev(devMajor:LongInt; devName:PChar):LongInt; cdecl;
237243 implementation
238244 {$LINK kernel_module_info}
239245 //function copy_from_user(_to:Pointer; _from:Pointer; count:Cardinal):Cardinal; cdecl; external;
240246 function create_proc_entry(name:PChar; mode:mode_t; parent:Pproc_dir_entry):Pproc_dir_entry; cdecl; external;
241247 procedure remove_proc_entry(name:PChar; parent:Pproc_dir_entry); cdecl; external;
242248 procedure printk(fmt:PChar); cdecl; external;
243249 procedure printk(fmt:PChar; param:LongInt); cdecl; external;
244250 procedure printk(fmt:PChar; param1:PChar); cdecl; external;
245251 procedure printk(fmt:PChar; param1:PChar; param2:LongInt); cdecl; external;
246252 function register_chrdev(devMajor:LongInt; devName:PChar; fops:Pfile_operations):LongInt; cdecl; external;
247253 function unregister_chrdev(devMajor:LongInt; devName:PChar):LongInt; cdecl; external;
248254 end.
249255```
250256
251257# Module code
252258
253259``` pascal
254260 unit kernel_module;
255261 {$TYPEINFO OFF}
256262 interface
257263
258264 implementation
259265 const
260266 PROCFS_MAX_SIZE = 1024;
261267 PROCFS_NAME = 'kpmod';
262268 BUF_LEN = 256;
263269 var
264270 {Major number assigned to our device driver}
265271 devMajor:LongInt;
266272 fops:file_operations;
267273 i, j, k:LongInt;
268274 {Is device open? Used to prevent multiple access to device}
269275 devIsOpen:Boolean = False;
270276 {pattern:Array[0..1, 0..7, 0..7]of LongInt = (
271277 (($A,$9,$5,$6,$A,$9,$5,$6), ($A,$8,$9,$1,$5,$4,$6,$2)),
272278 (($6,$5,$9,$A,$6,$5,$9,$A), ($2,$6,$4,$5,$1,$9,$8,$A)));}
273279 {This structure hold information about the /proc file}
274280 Our_Proc_File:Pproc_dir_entry;
275281 {The buffer used to store character for this module}
276282 //procfs_buffer:Array[0..PROCFS_MAX_SIZE - 1]of Char;
277283 {The size of the buffer}
278284 procfs_buffer_size:Cardinal = 0;
279285 {The msg the device will give when asked}
280286 msg:Array[0..BUF_LEN-1]of Char;
281287 msg_Ptr:PChar;
282288
283289 {* This function is called then the /proc file is read
284290 *
285291 * Arguments
286292 * =========
287293 * 1. The buffer where the data is to be inserted, if
288294 * you decide to use it.
289295 * 2. A pointer to a pointer to characters. This is
290296 * useful if you don't want to use the buffer
291297 * allocated by the kernel.
292298 * 3. The current position in the file
293299 * 4. The size of the buffer in the first argument.
294300 * 5. Write a "1" here to indicate EOF.
295301 * 6. A pointer to data (useful in case one common
296302 * read for multiple /proc/... entries)
297303 *
298304 * Usage and Return Value
299305 * ======================
300306 * A return value of zero means you have no further
301307 * information at this time (end of file). A negative
302308 * return value is an error condition.
303309 *
304310 * For More Information
305311 * ====================
306312 * The way I discovered what to do with this function
307313 * wasn't by reading documentation, but by reading the
308314 * code which used it. I just looked to see what uses
309315 * the get_info field of proc_dir_entry struct (I used a
310316 * combination of find and grep, if you're interested),
311317 * and I saw that it is used in <kernel source
312318 * directory>/fs/proc/array.c.
313319 *
314320 * If something is unknown about the kernel, this is
315321 * usually the way to go. In Linux we have the great
316322 * advantage of having the kernel source code for
317323 * free - use it.
318324 *}
319325 function procfile_read(buffer:PChar; buffer_location:PPChar; offset:off_t; buffer_length:LongInt; eof:PLongInt; data:Pointer):LongInt; cdecl;
320326 begin
321327 printk(KERN_INFO + 'procfile_read (/proc/%s) called'#10, PROCFS_NAME);
322328 {*
323329 * We give all of our information in one go, so if the
324330 * user asks us if we have more information the
325331 * answer should always be no.
326332 *
327333 * This is important because the standard read
328334 * function from the library would continue to issue
329335 * the read system call until the kernel replies
330336 * that it has no more information, or until its
331337 * buffer is filled.
332338 *}
333339 if offset > 0 then begin
334340 {we have finished to read, return 0}
335341 procfile_read := 0;
336342 end else begin
337343 {fill the buffer, return the buffer size}
338344 //move('HelloWorld!'#10#0, buffer, 13);
339345 //memcpy(buffer, procfs_buffer, procfs_buffer_size);
340346 buffer[0] := 'H';
341347 buffer[1] := 'e';
342348 buffer[2] := 'l';
343349 buffer[3] := 'l';
344350 buffer[4] := 'o';
345351 buffer[5] := #10;
346352 buffer[6] := #0;
347353 procfile_read := 6;
348354 end;
349355 end;
350356 {This function is called with the /proc file is written}
351357 function procfile_write(_file:Pointer{struct file *}; buffer:PChar; count:Cardinal; data:Pointer):LongInt; cdecl;
352358 begin
353359 printk(KERN_INFO + 'procfile_write (/proc/%s) called'#10, PROCFS_NAME);
354360 {get buffer size}
355361 procfs_buffer_size := count;
356362 if procfs_buffer_size > PROCFS_MAX_SIZE then
357363 procfs_buffer_size := PROCFS_MAX_SIZE;
358364 {write data to the buffer}
359365 //if copy_from_user(@procfs_buffer, buffer, procfs_buffer_size) <> 0 then
360366 // procfile_write := -EFAULT
361367 //else
362368 procfile_write := procfs_buffer_size;
363369 end;
364370 {Called when a process tries to open the device file, like "cat /dev/kpmod"}
365371 function device_open(inode:Pointer{struct inode *}; filp:Pointer{struct file *}):LongInt; cdecl;
366372 begin
367373 //static int counter = 0;
368374 if devIsOpen then
369375 Exit(-EBUSY);
370376 printk('Ouverture en mode Lecture/Ecriture ...'#10);
371377 //sprintf(msg, "I already told you %d times Hello world!'#10, counter += 1);
372378 //msg_Ptr = msg;
373379 //try_module_get(THIS_MODULE);
374380 devIsOpen := True;
375381 device_open := 0;
376382 end;
377383 {Called when a process, which already opened the dev file, attempts to read from it.}
378384 function device_read(filp:Pointer{struct file *}; buffer:PChar; len:size_t; offset:Ploff_t):ssize_t; cdecl;
379385 var
380386 bytes_read:LongInt;
381387 begin
382388 {Number of bytes actually written to the buffer}
383389 bytes_read := 0;
384390 printk('Writing %d data into buffer from offset %%lld'#10, len{, offset^});
385391 {Actually put the data into the buffer}
386392 //while(len > 0) and (msg_Ptr^ <> #0)do begin
387393 {The buffer is in the user data segment, not the kernel
388394 segment so "*" assignment won't work. We have to use
389395 put_user which copies data from the kernel data segment to
390396 the user data segment.}
391397 //put_user(msg_Ptr^, buffer += 1);
392398 // msg_Ptr += 1;
393399 // len -= 1;
394400 // bytes_read += 1;
395401 //end;
396402 {If we're at the end of the message, return 0 signifying end of file}
397403 {Most read functions return the number of bytes put into the buffer}
398404 device_read := bytes_read;
399405 end;
400406 {Called when a process writes to dev file: "echo 'hi' > /dev/hello"}
401407 function device_write(filp:Pointer{struct file *}; buff:Pchar; len:size_t; offset:Ploff_t):ssize_t; cdecl;
402408 function step:LongInt;
403409 begin
404410 if k < 8 then begin
405411 //printk('%d'#10,pattern[i][j][k]);
406412 //outb(pattern[i][j][k],LPT_BASE);
407413 k += 1;
408414 end else begin
409415 k := 0;
410416 //outb(pattern[i][j][k],LPT_BASE);
411417 //printk('%d'#10,pattern[i][j][k]);
412418 k += 1;
413419 end;
414420 step := 0;
415421 end;
416422 var
417423 data:Char;
418424 cmd:Char;
419425 begin
420426 printk(KERN_ALERT + 'Sorry, write operation on "%s" isn''t supported yet.'#10, '/dev/kpmod');
421427 device_write := -EINVAL;
422428 {
423429 //get_user(data,buffer);
424430 cmd := data;
425431 case cmd of
426432 'H':begin
427433 printk('Reportez-vous au fichier README'#10);
428434 end;
429435 'h':begin
430436 printk('Initialisation en mode demi pas'#10);
431437 j=0;
432438 end;
433439 'f':begin
434440 printk('Initialisation en mode pas entier'#10);
435441 j=1;
436442 end;
437443 'F':begin
438444 i=0;
439445 step();
440446 end;
441447 'R':begin
442448 i=1;
443449 step();
444450 end;
445451 else
446452 printk('Reportez-vous au fichier README, les commandes sont H, h, F, f, et R'#10);
447453 end;
448454 device_write := 1;}
449455 end;
450456 {Called when a process closes the device file.}
451457 function device_release(inode:Pointer{struct inode *}; filp:Pointer{struct file *}):LongInt; cdecl;
452458 begin
453459 printk('Fermeture ...'#10);
454460 {Decrement the usage count, or else once you opened the file, you'll never get get rid of the module.}
455461 //module_put(THIS_MODULE);
456462 {We're now ready for our next caller}
457463 devIsOpen := False;
458464 device_release := 0;
459465 end;
460466 {This function is called when the module is loaded}
461467 function init_module: Integer; cdecl; export;
462468 begin
463469 with fops do begin
464470 open := @device_open;
465471 read := @device_read;
466472 write := @device_write;
467473 release := @device_release;
468474 end;
469475 devMajor := register_chrdev(0, DEVICE_NAME, @fops);
470476 if devMajor < 0 then begin
471477 printk(KERN_ALERT + 'Registering char device failed with %d'#10, devMajor);
472478 Exit(devMajor);
473479 end;
474480 printk(KERN_INFO + 'I was assigned major number %d. To talk to'#10, devMajor);
475481 printk(KERN_INFO + 'the driver, create a dev file with'#10);
476482 printk(KERN_INFO + '"mknod /dev/%s c %d 0".'#10, DEVICE_NAME, devMajor);
477483 printk(KERN_INFO + 'Try various minor numbers. Try to cat and echo to'#10);
478484 printk(KERN_INFO + 'the device file.'#10);
479485 printk(KERN_INFO + 'Remove the device file and module when done.'#10);
480486 printk(KERN_INFO + '@fops = $%X'#10, LongInt(@fops));
481487 {create the /proc file}
482488 Our_Proc_File := create_proc_entry(PROCFS_NAME, 0644, Nil);
483489 if Our_Proc_File = Nil then begin
484490 remove_proc_entry(PROCFS_NAME, @proc_root);
485491 printk(KERN_ALERT + 'Could not initialize /proc/%s'#10, PROCFS_NAME);
486492 Exit(-ENOMEM);
487493 end;
488494 printk(KERN_INFO + 'Our_Proc_File = 0x%X'#10, PtrUInt(Our_Proc_File));
489495 with Our_Proc_File^ do begin
490496 read_proc := @procfile_read;
491497 write_proc := @procfile_write;
492498 //owner := THIS_MODULE;
493499 //mode := S_IFREG OR S_IRUGO;
494500 uid := 0;
495501 gid := 0;
496502 size := 37;
497503 end;
498504 printk(KERN_INFO + '/proc/%s created'#10, PROCFS_NAME);
499505 {everything is ok}
500506 init_module := 0;
501507 end;
502508
503509 {This function is called when the module is unloaded}
504510 procedure cleanup_module; cdecl; export;
505511 var
506512 ret:LongInt;
507513 begin
508514 remove_proc_entry(PROCFS_NAME, @proc_root);
509515 printk(KERN_INFO + '/proc/%s removed'#10, PROCFS_NAME);
510516 ret := unregister_chrdev(devMajor, DEVICE_NAME);
511517 if ret < 0 then
512518 printk(KERN_ALERT + 'Error in unregister_chrdev: %d'#10, ret);
513519 end;
514520
515521 end.
516522```
517523
518524# Compilation
519525
520526In 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.
521527
522528``` pascal
523529 #include <linux/module.h>
524530
525531 MODULE_DESCRIPTION("Test Module written using FPC");
526532 MODULE_AUTHOR("Mazen Neifer");
527533 MODULE_LICENSE("GPL");
528534```
529535
530536All 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.
531537
532538``` pascal
533539 obj-m := kernel_pmodule.o
534540 kernel_pmodule-objs := kernel_module_info.o kernel_module.o system.o
535541 KDIR := /lib/modules/$(shell uname -r)/build
536542 PWD := $(shell pwd)
537543
538544 default:kernel_module.o
539545 $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
540546 kernel_module.o: kernel_module.pas system.pas
541547 fpc kernel_module
542548 clean:
543549 ${RM} *.ko *.mod.c *.o *.ppu Module.symvers .*.ko.cmd .*.o.cmd
544550 ${RM} -r .tmp_versions
545551```
546552
547553Now, to compile the module, just type
548554
549-`make`
555+```text
556+make
557+```
550558
551559You will get a **kernel_pmodule.ko** as output. Then you can load it using
552560
553-`insmod kernel_pmodule.ko`
561+```text
562+insmod kernel_pmodule.ko
563+```
554564
555565## Limitation
556566
557567Due 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**.
558568
559-`make`\
560-`rm kernel_module.o`\
561-`make`
569+```text
570+make
571+rm kernel_module.o
572+make
573+```
562574
563575Then 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*
564576
565577# Building kernel modules (for fun) thread
566578
567579As 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 ? ;)
568580
569581Oh 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.
570582
571583Oh 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 ;)
572584
573585I 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 ;)
574586
575587So, 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.
576588
577589El es 14:05, 23 January 2011 (CET)
578590
579591## Prerequisites
580592
581593### Installation
582594
583595All 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.
584596
585597### Suggested reading
586598
587599**\<\<linux source\>\>/Documentation/stable_api_nonsense.txt**
588600
589601**\<\<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)
590602
591603The 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.
592604
593605### Hello World Example
594606
595607Copy and paste the file contents from here to appropriately named file(s) in your directory of choice.
596608
597609#### Makefile
598610
599611``` pascal
600612 # Free Pascal Demo Kernel Module
601613 # * the Makefile
602614 # * Based on work by Mazen Neifer, originally posted on.
603615 # * http://wiki.freepascal.org/linux/kernel/module_development
604616 # * Stripped down to bare minimum needed for usable Hello World!
605617 # * Some definitions also rearranged.
606618 # * (c) Lukasz Sokol, 2011 License : GPL
607619 # *)
608620
609621 obj-m := fpkm_hello.o
610622 fpkm_hello-objs := module_include.o fp_hello.o system.o
611623 KDIR := /lib/modules/$(shell uname -r)/build
612624 PWD := $(shell pwd)
613625
614626 default:fp_hello.o
615627 $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
616628
617629 fp_hello.o: system.pas fp_printk.pas fp_hello.pas
618630 fpc fp_hello
619631
620632
621633 clean:
622634 ${RM} *.ko *.mod.c *.o *.ppu Module.symvers .*.ko.cmd .*.o.cmd
623635 ${RM} -r .tmp_versions makeout modules.order
624636```
625637
626638#### fp_printk.pas
627639
628640``` pascal
629641 (* Free Pascal Headers for Linux Kernel
630642 * Pascal Code for kernel printk function
631643 * Based on work by Mazen Neifer, originally posted on
632644 * http://wiki.freepascal.org/linux/kernel/module_development
633645 * (c) Lukasz Sokol, 2011 License : GPL
634646 *)
635647 unit fp_printk;
636648 {$TYPEINFO OFF}
637649 interface
638650 const
639651 KERN_INFO='KERNEL:INFO:';
640652 KERN_ALERT='KERNEL:ALERT:';
641653
642654 procedure printk(fmt:PChar); cdecl; external;
643655
644656 implementation
645657
646658 end.
647659```
648660
649661#### fp_hello.pas
650662
651663``` pascal
652664 (* Free Pascal Demo Kernel Module
653665 * Pascal Module Code
654666 * Based on work by Mazen Neifer, originally posted on
655667 * http://wiki.freepascal.org/linux/kernel/module_development
656668 * Stripped down to bare minimum needed for usable Hello World!
657669 * Some definitions also rearranged.
658670 * (c) Lukasz Sokol, 2011 License : GPL
659671 *)
660672
661673 unit kernel_module;
662674 {$TYPEINFO OFF}
663675 interface
664676
665677
666678 implementation
667679 uses fp_printk;
668680
669681 {$LINK kernel_module_info}
670682
671683 {This function is called when the module is loaded}
672684 function init_module: Integer; cdecl; export;
673685 begin
674686 printk(KERN_INFO + 'Hello World!'+#10+#0);
675687 init_module := 0;
676688 end;
677689
678690 {This function is called when the module is unloaded}
679691 procedure cleanup_module; cdecl; export;
680692 begin
681693 printk(KERN_INFO + 'Bye-bye!'+#10+#0);
682694 end;
683695
684696 end.
685697```
686698
687699#### system.pas
688700
689701``` pascal
690702 (* Free Pascal Demo Kernel Module
691703 * System Unit Code
692704 * Based on work by Mazen Neifer, originally posted on
693705 * http://wiki.freepascal.org/linux/kernel/module_development
694706 * Stripped down to bare minimum needed for usable Hello World!
695707 * Some definitions also rearranged.
696708 * (c) Lukasz Sokol, 2011 License : GPL
697709 *)
698710 unit system;
699711 {$TYPEINFO OFF}
700712 interface
701713 {Paѕcal common type aliases}
702714 type
703715 {Basic embedded types}
704716 u8 = Byte;
705717 u16 = Word;
706718 u32 = LongWord;
707719 u64 = QWord;
708720 s8 = ShortInt;
709721 s16 = SmallInt;
710722 s32 = LongInt;
711723 s64 = Int64;
712724 {Integer types}
713725 DWord = LongWord;
714726 Cardinal = LongWord;
715727 Integer = SmallInt;
716728 UInt64 = QWord;
717729 {$ifdef CPU64}
718730 SizeInt = Int64;
719731 SizeUInt = QWord;
720732 PtrInt = Int64;
721733 PtrUInt = QWord;
722734 ValSInt = int64;
723735 ValUInt = qword;
724736 {$endif CPU64}
725737 {$ifdef CPU32}
726738 SizeInt = Longint;
727739 SizeUInt = DWord;
728740 PtrInt = Longint;
729741 PtrUInt = DWord;
730742 ValSInt = Longint;
731743 ValUInt = Cardinal;
732744 {$endif CPU32}
733745 {Zero - terminated strings }
734746 PChar = ^Char;
735747 PPChar = ^PChar;
736748 {Pointers}
737749 PSmallInt = ^Smallint;
738750 PShortInt = ^Shortint;
739751 PInteger = ^Integer;
740752 PByte = ^Byte;
741753 PWord = ^word;
742754 PDWord = ^DWord;
743755 PLongWord = ^LongWord;
744756 PLongint = ^Longint;
745757 PCardinal = ^Cardinal;
746758 PQWord = ^QWord;
747759 PInt64 = ^Int64;
748760 PPtrInt = ^PtrInt;
749761 PPtrUInt = ^PtrUInt;
750762 PSizeInt = ^SizeInt;
751763 PPointer = ^Pointer;
752764 PPPointer = ^PPointer;
753765 PBoolean = ^Boolean;
754766 PWordBool = ^WordBool;
755767 PLongBool = ^LongBool;
756768 {Other types}
757769 HRESULT = type Longint;
758770 TDateTime = type Double;
759771 TError = type Longint;
760772 const
761773 EPERM = 1;{Operation not permitted}
762774 ENOENT = 2;{No such file or directory}
763775 ESRCH = 3;{No such process}
764776 EINTR = 4;{Interrupted system call}
765777 EIO = 5;{I/O error}
766778 ENXIO = 6;{No such device or address}
767779 E2BIG = 7;{Argument list too long}
768780 ENOEXEC = 8;{Exec format error}
769781 EBADF = 9;{Bad file number}
770782 ECHILD = 10;{No child processes}
771783 EAGAIN = 11;{Try again}
772784 ENOMEM = 12;{Out of memory}
773785 EACCES = 13;{Permission denied}
774786 EFAULT = 14;{Bad address}
775787 ENOTBLK = 15;{Block device required}
776788 EBUSY = 16;{Device or resource busy}
777789 EEXIST = 17;{File exists}
778790 EXDEV = 18;{Cross-device link}
779791 ENODEV = 19;{No such device}
780792 ENOTDIR = 20;{Not a directory}
781793 EISDIR = 21;{Is a directory}
782794 EINVAL = 22;{Invalid argument}
783795 ENFILE = 23;{File table overflow}
784796 EMFILE = 24;{Too many open files}
785797 ENOTTY = 25;{Not a typewriter}
786798 ETXTBSY = 26;{Text file busy}
787799 EFBIG = 27;{File too large}
788800 ENOSPC = 28;{No space left on device}
789801 ESPIPE = 29;{Illegal seek}
790802 EROFS = 30;{Read-only file system}
791803 EMLINK = 31;{Too many links}
792804 EPIPE = 32;{Broken pipe}
793805 EDOM = 33;{Math argument out of domain of func}
794806 ERANGE = 34;{Math result not representable}
795807 type
796808 {Kernel types}
797809 mode_t = Word;
798810 nlink_t = DWord;
799811 uid_t = Word;
800812 gid_t = Word;
801813 off_t = LongInt;
802814 loff_t = Int64;
803815 Ploff_t = ^loff_t;
804816 size_t = DWord;
805817 ssize_t = LongInt;
806818
807819 implementation
808820
809821 end.
810822```
811823
812824#### module_include.c
813825
814826``` pascal
815827 /* Free Pascal Demo Kernel Module
816828 * c code that is linked into Pascal after compile - needed for symbols
817829 * Based on work by Mazen Neifer, originally posted on
818830 * http://wiki.freepascal.org/linux/kernel/module_development
819831 * Stripped down to bare minimum needed for usable Hello World!
820832 * Some definitions also rearranged.
821833 * (c) Lukasz Sokol, 2011 License : GPL
822834 */
823835
824836 #include <linux/module.h>
825837
826838 MODULE_DESCRIPTION("Test Module written using FPC");
827839 MODULE_AUTHOR("Lukasz Sokol (fork)");
828840 MODULE_LICENSE("GPL");
829841```
830842
831843### Enhancing printk
832844
833845If you make your fp_printk file look like this:
834846
835847#### fp_printk.pas
836848
837849``` pascal
838850 (* Free Pascal Headers for Linux Kernel
839851 * Pascal Code for kernel printk function
840852 * Based on work by Mazen Neifer, originally posted on
841853 * http://wiki.freepascal.org/linux/kernel/module_development
842854 * (c) Lukasz Sokol, 2011 License : GPL
843855 *)
844856 unit fp_printk;
845857 {$TYPEINFO OFF}
846858 interface
847859
848860 const
849861 // codes for printk() call, copied from <src>include/linux/kernel.h
850862 KERN_EMERG = '<0>'; //* system is unusable */
851863 KERN_ALERT = '<1>'; //* action must be taken immediately */
852864 KERN_CRIT = '<2>'; //* critical conditions */
853865 KERN_ERR = '<3>'; //* error conditions */
854866 KERN_WARNING = '<4>'; //* warning conditions */
855867 KERN_NOTICE = '<5>'; //* normal but significant condition */
856868 KERN_INFO = '<6>'; //* informational */
857869 KERN_DEBUG = '<7>'; //* debug-level messages */
858870
859871 procedure printk(fmt:PChar); cdecl; varargs; external;
860872
861873 implementation
862874
863875 end.
864876```
865877
866878I.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 :
867879
868880#### fp_hello.pas
869881
870882``` pascal
871883 (* Free Pascal Demo Kernel Module
872884 * Pascal Module Code
873885 * Based on work by Mazen Neifer, originally posted on
874886 * http://wiki.freepascal.org/linux/kernel/module_development
875887 * Stripped down to bare minimum needed for usable Hello World!
876888 * Some definitions also rearranged.
877889 * (c) Lukasz Sokol, 2011 License : GPL
878890 *)
879891
880892 unit fp_hello;
881893 {$TYPEINFO OFF}
882894 interface
883895
884896 implementation
885897 uses fp_printk;
886898
887899 const LFEOL = #10#0;
888900 {$LINK kernel_module_info}
889901
890902 {This function is called when the module is loaded}
891903 function init_module: Integer; cdecl; export;
892904 begin
893905 printk(KERN_INFO + 'Hello World!'+LFEOL);
894906 printk(KERN_INFO + 'Also works with variable number of arguments like %d, 0x%.2x, %u '+LFEOL, -10, 15, 10);
895907 init_module := 0;
896908 end;
897909
898910 {This function is called when the module is unloaded}
899911 procedure cleanup_module; cdecl; export;
900912 begin
901913 printk(KERN_INFO + 'Bye-bye!'+LFEOL);
902914 end;
903915
904916 end.
905917```
906918
907919producing
908920
909921### dmesg output
910922
911-`[39432.662017]  Hello World!`\
912-`[39432.662028] Also works with variable number of arguments like -10, 0x0f, 10 `\
913-`[39432.679324] Bye-bye!`
923+```text
924+[39432.662017] Hello World!
925+[39432.662028] Also works with variable number of arguments like -10, 0x0f, 10
926+[39432.679324] Bye-bye!
927+```
914928
915929Nice innit ?
916930
917931### Hello World Compilation
918932
919933Having Midnight Commander on hand is, erm, handy. Change directory into where your above files are, and call **make**.
920934
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$ `
935+```text
936+ fpc fp_hello
937+ Free Pascal Compiler version 2.4.0-2 [2010/03/06] for i386
938+ Copyright (c) 1993-2009 by Florian Klaempfl
939+ Target OS: Linux for i386
940+ Compiling fp_hello.pas
941+ Compiling system.pas
942+ Compiling fp_printk.pas
943+ 189 lines compiled, 0.1 sec
944+ make -C /lib/modules/2.6.32-27-generic/build SUBDIRS=/home/lukasz/projects/kfpc/hello_world modules
945+ make[1]: Entering directory `/usr/src/linux-headers-2.6.32-27-generic'
946+ CC [M] /home/lukasz/projects/kfpc/hello_world/module_include.o
947+ LD [M] /home/lukasz/projects/kfpc/hello_world/fpkm_hello.o
948+ Building modules, stage 2.
949+ MODPOST 1 modules
950+ WARNING: could not find /home/lukasz/projects/kfpc/hello_world/.fp_hello.o.cmd for /home/lukasz/projects/kfpc/hello_world/fp_hello.o
951+ CC /home/lukasz/projects/kfpc/hello_world/fpkm_hello.mod.o
952+ LD [M] /home/lukasz/projects/kfpc/hello_world/fpkm_hello.ko
953+ make[1]: Leaving directory `/usr/src/linux-headers-2.6.32-27-generic'
954+ lukasz@lukasz-laptop:~/projects/kfpc$
955+```
940956
941957### Result
942958
943959With 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
944960
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$ `
961+```text
962+ lukasz@lukasz-laptop:~/projects/kfpc$ sudo insmod fp_hello.ko && sudo rmmod fp_hello
963+ [sudo] password for lukasz:
964+ lukasz@lukasz-laptop:~/projects/kfpc$
965+```
948966
949967(this means : no errors)
950968
951969and **tail /var/log/messages** yields
952970
953-`[54219.552565] Hello World!`\
954-`[54219.587641] Bye-bye!`
971+```text
972+[54219.552565] Hello World!
973+[54219.587641] Bye-bye!
974+```
955975
956976---
957977*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).*
958978Category: Linux
959979Category: Proposals

Back to history