linux/kernel/module development
overview
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 with author of this article is working. However, it may be very simple to translate this for other Linux distributions.
Requirements
1) First of all, you need to have FPC installed
apt-get install fp-compiler
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 an my not work, especially previous releases. 2) you need to have kernel headers packages installed
apt-get install linux-headers-$(uname -r)
3) Then you need make utility
apt-get install make
Please note that installing linux-headers-* will install automatically the required version of GCC and binutils.
Kernel RTL
It is obvious that when programming Linux kernel modules, one can not use the standard FPC RTL. As nos standard kernel RTL is provided, you have to write your own one. This is not very easy. A good start point could be the following
unit system;
{$TYPEINFO OFF}
interface
{Paѕcal common type aliases}
type
{Basic embedded types}
u8 = Byte;
u16 = Word;
u32 = LongWord;
u64 = QWord;
s8 = ShortInt;
s16 = SmallInt;
s32 = LongInt;
s64 = Int64;
{Integer types}
DWord = LongWord;
Cardinal = LongWord;
Integer = SmallInt;
UInt64 = QWord;
{$ifdef CPU64}
SizeInt = Int64;
SizeUInt = QWord;
PtrInt = Int64;
PtrUInt = QWord;
ValSInt = int64;
ValUInt = qword;
{$endif CPU64}
{$ifdef CPU32}
SizeInt = Longint;
SizeUInt = DWord;
PtrInt = Longint;
PtrUInt = DWord;
ValSInt = Longint;
ValUInt = Cardinal;
{$endif CPU32}
{Zero - terminated strings }
PChar = ^Char;
PPChar = ^PChar;
{Pointers}
PSmallInt = ^Smallint;
PShortInt = ^Shortint;
PInteger = ^Integer;
PByte = ^Byte;
PWord = ^word;
PDWord = ^DWord;
PLongWord = ^LongWord;
PLongint = ^Longint;
PCardinal = ^Cardinal;
PQWord = ^QWord;
PInt64 = ^Int64;
PPtrInt = ^PtrInt;
PPtrUInt = ^PtrUInt;
PSizeInt = ^SizeInt;
PPointer = ^Pointer;
PPPointer = ^PPointer;
PBoolean = ^Boolean;
PWordBool = ^WordBool;
PLongBool = ^LongBool;
{Other types}
HRESULT = type Longint;
TDateTime = type Double;
TError = type Longint;
const
KERN_INFO='KERNEL:INFO:';
KERN_ALERT='KERNEL:ALERT:';
DEVICE_NAME='kpmod';
PROCFS_MAX_SIZE=1024;
PROCFS_NAME='kpmod';
EPERM = 1;{Operation not permitted}
ENOENT = 2;{No such file or directory}
ESRCH = 3;{No such process}
EINTR = 4;{Interrupted system call}
EIO = 5;{I/O error}
ENXIO = 6;{No such device or address}
E2BIG = 7;{Argument list too long}
ENOEXEC = 8;{Exec format error}
EBADF = 9;{Bad file number}
ECHILD = 10;{No child processes}
EAGAIN = 11;{Try again}
ENOMEM = 12;{Out of memory}
EACCES = 13;{Permission denied}
EFAULT = 14;{Bad address}
ENOTBLK = 15;{Block device required}
EBUSY = 16;{Device or resource busy}
EEXIST = 17;{File exists}
EXDEV = 18;{Cross-device link}
ENODEV = 19;{No such device}
ENOTDIR = 20;{Not a directory}
EISDIR = 21;{Is a directory}
EINVAL = 22;{Invalid argument}
ENFILE = 23;{File table overflow}
EMFILE = 24;{Too many open files}
ENOTTY = 25;{Not a typewriter}
ETXTBSY = 26;{Text file busy}
EFBIG = 27;{File too large}
ENOSPC = 28;{No space left on device}
ESPIPE = 29;{Illegal seek}
EROFS = 30;{Read-only file system}
EMLINK = 31;{Too many links}
EPIPE = 32;{Broken pipe}
EDOM = 33;{Math argument out of domain of func}
ERANGE = 34;{Math result not representable}
type
{Kernel types}
mode_t = Word;
nlink_t = DWord;
uid_t = Word;
fl_owner_t = ^files_struct;
gid_t = Word;
off_t = LongInt;
loff_t = Int64;
Ploff_t = ^loff_t;
size_t = DWord;
ssize_t = LongInt;
{$PACKRECORDS C}
atomic_t = record
counter:LongInt;
end;
files_struct = record
{read mostly part}
count:atomic_t;
//struct fdtable *fdt;
//struct fdtable fdtab;
{written part on a separate cache line in SMP}
//spinlock_t file_lock ____cacheline_aligned_in_smp;
//int next_fd;
//struct embedded_fd_set close_on_exec_init;
//struct embedded_fd_set open_fds_init;
//struct file * fd_array[NR_OPEN_DEFAULT];
end;
filldir_t = function(arg1:Pointer; arg2:PChar; arg3:LongInt; arg4:loff_t; arg5:u64; arg6:DWord):LongInt; cdecl;
read_actor_t = function(arg1:Pointer{read_descriptor_t *}; arg2:Pointer{struct page *}; arg3:DWord; arg4:DWord):LongInt; cdecl;
Pfile_operations = ^file_operations;
file_operations = record
owner:Pointer;{struct module *}
llseek:function(filp:Pointer{struct file *}; offset:loff_t; whence:LongInt):loff_t; cdecl;
read:function(filp:Pointer{struct file *}; buf:PChar; count:size_t; offset:Ploff_t):ssize_t; cdecl;
write:function(filp:Pointer{struct file *}; buf:PChar; count:size_t; offset:Ploff_t):ssize_t; cdecl;
aio_read:function(kiocb:Pointer{struct kiocb *}; iovec:Pointer{const struct iovec *}; count:DWord; offset:loff_t):ssize_t; cdecl;
aio_write:function(kiocb:Pointer{struct kiocb *}; iovec:Pointer{const struct iovec *}; count:DWord; offset:loff_t):ssize_t; cdecl;
readdir:function(filp:Pointer{struct file *}; buf:Pointer; filldir:filldir_t):LongInt; cdecl;
poll:function(filp:Pointer{struct file *}; poll_table:Pointer{struct poll_table_struct *}):DWord; cdecl;
ioctl:function(inode:Pointer{struct inode *}; filp:Pointer{struct file *}; arg1:DWord; arg2:DWord):LongInt; cdecl;
unlocked_ioctl:function(filp:Pointer{struct file *}; arg1:DWord; arg2:DWord):LongInt; cdecl;
compat_ioctl:function(filp:Pointer{struct file *}; arg1:DWord; arg2:DWord):LongInt; cdecl;
mmap:function(filp:Pointer{struct file *}; vm_area_struct:Pointer{ struct vm_area_struct *}):LongInt; cdecl;
open:function(inode:Pointer{struct inode *}; filp:Pointer{struct file *}):LongInt; cdecl;
flush:function(filp:Pointer{struct file *}; id:fl_owner_t):LongInt; cdecl;
release:function(inode:Pointer{struct inode *}; filp:Pointer{struct file *}):LongInt; cdecl;
fsync:function(filp:Pointer{struct file *}; arg1:Pointer{ struct dentry *}; datasync:LongInt):LongInt; cdecl;
aio_fsync:function(kiocb:Pointer{struct kiocb *}; datasync:LongInt):LongInt; cdecl;
fasync:function(arg1:LongInt; filp:Pointer{struct file *}; arg2:LongInt):LongInt; cdecl;
lock:function(filp:Pointer{struct file *}; arg1:LongInt; arg2:Pointer{ struct file_lock *}):LongInt; cdecl;
sendfile:function(filp:Pointer{struct file *}; arg1:Pointer{ loff_t *}; arg2:size_t; arg3:read_actor_t; arg4:Pointer{ void *}):ssize_t; cdecl;
sendpage:function(filp:Pointer{struct file *}; arg1:Pointer{ struct page *}; arg2:LongInt; arg3:size_t; arg4:Pointer{ loff_t *}; arg5:LongInt):ssize_t; cdecl;
get_unmapped_area:function(filp:Pointer{struct file *}; arg1:DWord; arg2:DWord; arg3:DWord; arg4:DWord):DWord; cdecl;
check_flags:function(falgs:LongInt):LongInt; cdecl;
dir_notify:function(filp:Pointer{struct file *}; arg:DWord):LongInt; cdecl;
flock:function(filp:Pointer{struct file *}; arg1:LongInt; arg2:Pointer{ struct file_lock *}):LongInt; cdecl;
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;
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;
end;
Pproc_dir_entry = ^proc_dir_entry;
proc_dir_entry = record
low_ino:Cardinal;
namelen:Integer;
name:PChar;
mode:mode_t;
nlink:nlink_t;
uid:uid_t;
gid:gid_t;
size:loff_t;
proc_iops:Pointer;{const struct inode_operations *}
proc_fops:Pfile_operations;{const struct file_operations *}
get_info:Pointer;{get_info_t *}
owner:Pointer;{struct module *}
next, parent, subdir:Pproc_dir_entry;
data:Pointer;
read_proc:Pointer;{read_proc_t *}
write_proc:Pointer;{write_proc_t *}
count:atomic_t;{use count}
deleted:LongInt;{delete flag}
_set:Pointer;
end;
var
proc_root:proc_dir_entry; cvar; external;
//function copy_from_user(_to:Pointer; _from:Pointer; count:Cardinal):Cardinal; cdecl;
function create_proc_entry(name:PChar; mode:mode_t; parent:Pproc_dir_entry):Pproc_dir_entry; cdecl;
procedure remove_proc_entry(name:PChar; parent:Pproc_dir_entry); cdecl;
procedure printk(fmt:PChar); cdecl;
procedure printk(fmt:PChar; param:LongInt); cdecl;
procedure printk(fmt:PChar; param1:PChar); cdecl;
procedure printk(fmt:PChar; param1:PChar; param2:LongInt); cdecl;
function register_chrdev(devMajor:LongInt; devName:PChar; fops:Pfile_operations):LongInt; cdecl;
function unregister_chrdev(devMajor:LongInt; devName:PChar):LongInt; cdecl;
implementation
{$LINK kernel_module_info}
//function copy_from_user(_to:Pointer; _from:Pointer; count:Cardinal):Cardinal; cdecl; external;
function create_proc_entry(name:PChar; mode:mode_t; parent:Pproc_dir_entry):Pproc_dir_entry; cdecl; external;
procedure remove_proc_entry(name:PChar; parent:Pproc_dir_entry); cdecl; external;
procedure printk(fmt:PChar); cdecl; external;
procedure printk(fmt:PChar; param:LongInt); cdecl; external;
procedure printk(fmt:PChar; param1:PChar); cdecl; external;
procedure printk(fmt:PChar; param1:PChar; param2:LongInt); cdecl; external;
function register_chrdev(devMajor:LongInt; devName:PChar; fops:Pfile_operations):LongInt; cdecl; external;
function unregister_chrdev(devMajor:LongInt; devName:PChar):LongInt; cdecl; external;
end.