commit c6305f10f2f63c5e096b48c0cc6c7be941e01208 Author: Florian RICHER Date: Sat Feb 8 17:01:11 2025 +0100 Add basic and chardev modules diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2cfd27c --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Ignore dev env files +.direnv +.idea + +# Ignore generated files +*.cmd +*.mod.c + +# Ignore all .ko files +*.ko + +# Ignore other compiled files and directories +*.o +*.mod +*.symvers +*.order diff --git a/01_basic_module/Makefile b/01_basic_module/Makefile new file mode 100644 index 0000000..b909b3c --- /dev/null +++ b/01_basic_module/Makefile @@ -0,0 +1,7 @@ +obj-m += test_module.o + +all: + make -C $(LINUX_MODULES_FOLDER)/build M=$(PWD) modules + +clean: + make -C $(LINUX_MODULES_FOLDER)/build M=$(PWD) clean diff --git a/01_basic_module/test_module.c b/01_basic_module/test_module.c new file mode 100644 index 0000000..752de04 --- /dev/null +++ b/01_basic_module/test_module.c @@ -0,0 +1,20 @@ +#include +#include +#include + +static int __init basic_module_init(void) { + printk(KERN_INFO "Bonjour! Le module basique est chargé.\n"); + return 0; +} + +static void __exit basic_module_exit(void) { + printk(KERN_INFO "Au revoir! Le module basique est déchargé.\n"); +} + +module_init(basic_module_init); +module_exit(basic_module_exit); + +MODULE_LICENSE("MIT License"); +MODULE_AUTHOR("Florian RICHER"); +MODULE_DESCRIPTION("Un module noyau basique qui affiche un message"); +MODULE_VERSION("1.0"); diff --git a/02_character_device/Makefile b/02_character_device/Makefile new file mode 100644 index 0000000..b909b3c --- /dev/null +++ b/02_character_device/Makefile @@ -0,0 +1,7 @@ +obj-m += test_module.o + +all: + make -C $(LINUX_MODULES_FOLDER)/build M=$(PWD) modules + +clean: + make -C $(LINUX_MODULES_FOLDER)/build M=$(PWD) clean diff --git a/02_character_device/README.md b/02_character_device/README.md new file mode 100644 index 0000000..b96aa75 --- /dev/null +++ b/02_character_device/README.md @@ -0,0 +1,31 @@ +## Additionnal informations + +To test character device, your need create the device. + +Step 1: Get major number of your module device + +```bash +cat /proc/devices | grep flodev +``` + +Step 2: Create device (as root) + +```bash +mknod /dev/[wanted name] -c 0 +``` + +Exemple (as root): + +```bash +cat /proc/devices | grep flodev # => 236 flodev +mknod /dev/flodev0 c 236 0 +echo "Salut" >> /dev/flodev0 +dmesg | tail # => +# flodev - Ouverture du périphérique +# flodev - Message reçu: Salut +# flodev - Fermeture du périphérique +rm /dev/flodev0 +``` + + + diff --git a/02_character_device/test_module.c b/02_character_device/test_module.c new file mode 100644 index 0000000..c3321dd --- /dev/null +++ b/02_character_device/test_module.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include + +#define DEVICE_NAME "flodev" +#define CLASS_NAME "chardrv" + +static int major_number; +static char msg[256] = {0}; +static short size_of_msg; +static int device_open(struct inode *, struct file *); +static int device_release(struct inode *, struct file *); +static ssize_t device_read(struct file *, char *, size_t, loff_t *); +static ssize_t device_write(struct file *, const char *, size_t, loff_t *); + +static struct file_operations fops = { + .read = device_read, + .write = device_write, + .open = device_open, + .release = device_release, +}; + +static int __init basic_module_init(void) { + printk(KERN_INFO "Bonjour! Le module est chargé.\n"); + major_number = register_chrdev(0, DEVICE_NAME, &fops); + if (major_number < 0) { + printk(KERN_ALERT "Erreur lors de l'enregistrement du périphérique de caractère\n"); + return major_number; + } + printk(KERN_INFO "Périphérique de caractère enregistré avec le numéro de majeur %d\n", major_number); + return 0; +} + +static void __exit basic_module_exit(void) { + unregister_chrdev(major_number, DEVICE_NAME); + printk(KERN_INFO "Au revoir! Le module est déchargé.\n"); +} + +static int device_open(struct inode *inode, struct file *file) { + printk(KERN_INFO "flodev - Ouverture du périphérique\n"); + return 0; +} + +static int device_release(struct inode *inode, struct file *file) { + printk(KERN_INFO "flodev - Fermeture du périphérique\n"); + return 0; +} + +static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset) { + int bytes_read = 0; + if (*offset >= size_of_msg) { + return 0; + } + if (*offset + length > size_of_msg) { + length = size_of_msg - *offset; + } + if (copy_to_user(buffer, msg + *offset, length)) { + return -EFAULT; + } + *offset += length; + bytes_read = length; + printk(KERN_INFO "flodev - Lecture de %d bytes\n", bytes_read); + return bytes_read; +} + +static ssize_t device_write(struct file *filp, const char *buff, size_t len, loff_t *off) { + if (copy_from_user(msg, buff, len)) { + return -EFAULT; + } + size_of_msg = len; + printk(KERN_INFO "flodev - Message reçu: %s\n", msg); + return len; +} + +module_init(basic_module_init); +module_exit(basic_module_exit); + +MODULE_LICENSE("MIT License"); +MODULE_AUTHOR("Florian RICHER"); +MODULE_DESCRIPTION("Un module noyau avec un périphérique de caractère"); +MODULE_VERSION("1.0"); diff --git a/README.md b/README.md new file mode 100644 index 0000000..d765a32 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +## Setup env + +On NixOS: + - Ensure Linux Kernel is the same as you configuration. +Otherwise, change linux_dev variable with you kernel variant and change flake.lock nixpkgs with your version. + - Use direnv allow or nix develop to setup shell. + + +On other distro + - You need to setup LINUX_MODULES_FOLDER to linux modules folder of your distro (ex: `/lib/modules/$(uname -r)/build`) + - + + +## make : targets list + +- all : Compiling kernel +- clean : Cleaning build folder + +All subfolder is configured to use LINUX_MODULES_FOLDER env variable set by flake develop + +## How test module + +Step 1: Load module + +```bash +sudo insmod [module_name].ko +``` + +**Warning**: Can fail if secure boot is enabled + +Step 2: Check logs + +```bash +sudo dmesg | tail +``` + +Step 3: Unload module + +```bash +sudo rmmod [module_name].ko +``` \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..32604a9 --- /dev/null +++ b/flake.lock @@ -0,0 +1,60 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1739024019, + "narHash": "sha256-xvTo0Aw0+veek7hvEVLzErmJyQkEcRk6PSR4zsRQFEc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3a228057f5b619feb3186e986dbe76278d707b6e", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..da8fff5 --- /dev/null +++ b/flake.nix @@ -0,0 +1,32 @@ +{ + description = "Environnement de développement pour des modules noyaux Linux"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { + nixpkgs, + flake-utils, + ... + }: flake-utils.lib.eachSystem flake-utils.lib.allSystems (system: + let + pkgs = import nixpkgs { inherit system; }; + + linux_dev = pkgs.linuxKernel.kernels.linux_zen.dev; + in { + devShells = { + default = pkgs.mkShell { + packages = [ linux_dev ]; + + LINUX_MODULES_FOLDER = "${linux_dev}/lib/modules/${linux_dev.modDirVersion}"; + + shellHook = '' + echo "Current Linux Kernel used : ${linux_dev.version}" + ''; + }; + }; + } + ); +}