1
0

11 Commits

8 changed files with 427 additions and 23 deletions

View File

@@ -234,7 +234,7 @@ The `/proc/modules` file give us more details about the state of the module. We
|> dmesg | tail -5
[ 3559.279143] number: 1
[ 3581.198562] Linux module skeleton unloaded
[ 3583.616662] Linux module skeleton ex02 loaded
[ 3583.616662] Linux module skeleton ex03 loaded
[ 3583.621085] text: The answer to the Ultimate Question of Life, The Universe, and Everything
[ 3583.621085] number: 42
|> modprobe -r mymodule
@@ -297,6 +297,23 @@ This number matches with this table (#link("https://www.kernel.org/doc/html/late
]
)
To allocate memory in the kernel, we can use the `kcalloc` function. It allows to allocate directly the memory for all element. It's also possible to use `kzalloc` in a loop to allocate memory for each element. We prefer allocate all the memory at once to avoid fragmentation and to be sure all the memory can be allocated.
```bash
struct element* element_ptr = kcalloc(elements, sizeof(struct element), GFP_KERNEL);
for (int i = 0; i < elements; i++) {
struct element* e = element_ptr + i;
if (e != 0) {
strncpy(e->text, text, TEXT_LENGTH_MAX - 1);
e->unique_number = i;
list_add_tail(&e->node, &list_unique_elements);
pr_info ("add element %d: %s\n", e->unique_number, e->text);
}
}
```
#pagebreak()
//-------------------
// Exercise 5: Display the processor chip ID, CPU temperature and the MAC adress of the Ethernet controller
//-------------------
@@ -316,13 +333,28 @@ This number matches with this table (#link("https://www.kernel.org/doc/html/late
$
The chip ID can be verified in ```/proc/iomem```.
The register value of the temperature can be verified in the file: ```/sys/class/thermal/thermal_zone0/temp```.
The MAC address can be verified with ``` ifconfig```.
]
)
The resources are savec in a struct:
```c
static struct resource* resources[3] = {[0] = 0,};
```
resources[0] is reserved for the chip ID, resources[1] for the temperature sensor and resources[2] for the Ethernet controller.
We first allocate the resources with `request_mem_region` function. Then we can map the physical address to a virtual address with `ioremap` function. Finally, we can read the value of the registers with `ioread32` function. The request fail because we have an overlap with the EEPROM, but we can ignore this error because we can still read the registers with `ioremap` function.
```c
// Request the resource at (CHIP_ID_BASE_ADDR)
resources[0] = request_mem_region(CHIP_ID_BASE_ADDR, 0x1000, "nanopi - chip ID");
// Map the physical address (CHIP_ID_BASE_ADDR) to a virtual address (registers[0])
registers[0] = ioremap(CHIP_ID_BASE_ADDR, 0x1000);
```
//-------------------
// Exercise 6: Kernel thread
//-------------------
@@ -337,6 +369,8 @@ This number matches with this table (#link("https://www.kernel.org/doc/html/late
]
)
Easy exercice, a thread in the kernet is a `struct task_struct*` that can be created with `kthread_run`
//-------------------
// Exercise 7: Sleeping
//-------------------
@@ -350,6 +384,15 @@ This number matches with this table (#link("https://www.kernel.org/doc/html/late
]
)
This exercice make 2 threads in concurrency with wait queue. Here the queue ware declare
statically with the macro `DECLARE_WAIT_QUEUE_HEAD`. Then for this exercice we use an atomic
trigger with 2 queues. It important that the trigger is atomic or protected by mutex because
there is concurrency. The wait queues are used to wait until the trigger has changed to keep
synchronization between the threads.
It is very important to add `kthread_should_stop()` as a condition to wake up queue, because if there is
a problem during the implementation, we cannot kill the code.
//-------------------
// Exercise 8: Interrupts
//-------------------

View File

@@ -0,0 +1,15 @@
#include <linux/module.h> // needed by all modules
#include <linux/init.h> // needed for macros
#include <linux/kernel.h> // needed for debugging
#include <linux/moduleparam.h>
static char* text = "dummy text";
module_param(text, charp, 0664);
static int elements = 1;
module_param(elements, int, 0);
void parameters_print(void) {
pr_debug("text: %s\n", text);
pr_debug("elements: %d\n", elements);
}

View File

@@ -0,0 +1,58 @@
#include <linux/module.h> // needed by all modules
#include <linux/init.h> // needed for macros
#include <linux/kernel.h> // needed for debugging
#include <linux/slab.h> // dynamic memory allocation
#include <linux/list.h> // linked list
#include <linux/string.h>
#define TEXT_LENGTH_MAX 255
struct element {
char text[TEXT_LENGTH_MAX];
int32_t unique_number;
struct list_head node;
};
static LIST_HEAD (list_unique_elements);
void dynAlloc_init(void) {
pr_info("Initialize dynamic allocation and linked list\n");
struct element* element_ptr = kcalloc(elements, sizeof(struct element), GFP_KERNEL);
if (element_ptr == 0) {
pr_err("Failed to allocate memory for %d elements\n", elements);
return;
}
uint8_t i;
const uint8_t length = TEXT_LENGTH_MAX - 1;
for (i = 0; i < elements; i++) {
struct element* e = element_ptr + i;
if (e != 0) {
strncpy(e->text, text, length);
e->unique_number = i;
list_add_tail(&e->node, &list_unique_elements);
pr_info ("add element %d: %s\n", e->unique_number, e->text);
}
}
pr_info("Dynamic allocation and linked list initialized\n");
}
void dynAlloc_exit(void) {
pr_info("Free memory allocated for dynamic allocation and linked list\n");
struct element* e;
while (!list_empty(&list_unique_elements)) {
e = list_entry(list_unique_elements.next, struct element, node);
pr_info ("delete element %d: %s\n", e->unique_number, e->text);
list_del(&e->node);
if (e != 0) {
kfree(e);
}
}
pr_info("Memory allocated for dynamic allocation and linked list freed\n");
}

View File

@@ -0,0 +1,105 @@
#include <linux/module.h> // needed by all modules
#include <linux/init.h> // needed for macros
#include <linux/kernel.h> // needed for debugging
#include <linux/ioport.h>
#include <linux/io.h>
#define CHIP_ID_BASE_ADDR 0x01c14000
#define TEMPERATURE_SENSOR_BASE_ADDR 0x01C25000
#define ETHERNET_CONTROLLER_BASE_ADDR 0x01C30000
static struct resource* resources[3] = {[0] = 0,};
void ioMemoryMapped_init(void) {
pr_info("Initialize memory-mapped I/O\n");
// Declare variables
unsigned char* registers[3] = {[0] = 0,};
uint32_t chipid[4] = {[0] = 0,};
uint32_t temperature = 0;
uint32_t mac_address[2] = {[0] = 0,};
// Request memory
resources[0] = request_mem_region(CHIP_ID_BASE_ADDR, 0x1000, "nanopi - chip ID");
if (resources[0] == 0) {
pr_info("Failed to reserve memory region for chip ID\n");
}
resources[1] = request_mem_region(TEMPERATURE_SENSOR_BASE_ADDR, 0x1000, "nanopi - temperature sensor");
if (resources[1] == 0) {
pr_info("Failed to reserve memory region for temperature sensor\n");
}
resources[2] = request_mem_region(ETHERNET_CONTROLLER_BASE_ADDR, 0x1000, "nanopi - Ethernet controller");
if (resources[2] == 0) {
pr_info("Failed to reserve memory region for Ethernet controller\n");
}
// Map memory
registers[0] = ioremap(CHIP_ID_BASE_ADDR, 0x1000);
if (registers[0] == 0) {
pr_err("Failed to map processor registers for chip ID\n");
return;
}
registers[1] = ioremap(TEMPERATURE_SENSOR_BASE_ADDR, 0x1000);
if (registers[1] == 0) {
pr_err("Failed to map processor registers for temperature sensor\n");
return;
}
registers[2] = ioremap(ETHERNET_CONTROLLER_BASE_ADDR, 0x1000);
if (registers[2] == 0) {
pr_err("Failed to map processor registers for Ethernet controller\n");
return;
}
// Read values - Chip ID
chipid[0] = ioread32(registers[0] + 0x200);
chipid[1] = ioread32(registers[0] + 0x204);
chipid[2] = ioread32(registers[0] + 0x208);
chipid[3] = ioread32(registers[0] + 0x20c);
pr_info(
"chipid=%08x'%08x'%08x'%08x\n",
chipid[0], chipid[1], chipid[2], chipid[3]
);
// Read values - Temperature
temperature = -1191 * (int32_t) ioread32(registers[1] + 0x80) / 10 + 223000;
pr_info(
"temperature=%d (register value: %d)\n",
temperature, ioread32(registers[1] + 0x80)
);
// Read values - MAC address
mac_address[0] = ioread32(registers[2] + 0x50);
mac_address[1] = ioread32(registers[2] + 0x54);
pr_info(
"mac-addr=%02x:%02x:%02x:%02x:%02x:%02x\n",
(mac_address[1] >> 0) & 0xff,
(mac_address[1] >> 8) & 0xff,
(mac_address[1] >> 16) & 0xff,
(mac_address[1] >> 24) & 0xff,
(mac_address[0] >> 0) & 0xff,
(mac_address[0] >> 8) & 0xff
);
// Unmap memory
iounmap(registers[0]);
iounmap(registers[1]);
iounmap(registers[2]);
pr_info("Memory-mapped I/O initialized\n");
}
void ioMemoryMapped_exit(void) {
pr_info("Free memory-mapped I/O\n");
// Release memory
if (resources[0] != 0) release_mem_region(CHIP_ID_BASE_ADDR, 0x1000);
if (resources[1] != 0) release_mem_region(TEMPERATURE_SENSOR_BASE_ADDR, 0x1000);
if (resources[2] != 0) release_mem_region(ETHERNET_CONTROLLER_BASE_ADDR, 0x1000);
}

View File

@@ -0,0 +1,42 @@
#include <linux/module.h> // needed by all modules
#include <linux/init.h> // needed for macros
#include <linux/kernel.h> // needed for debugging
#include <linux/kthread.h>
#include <linux/delay.h>
static struct task_struct* sample_thread;
#define DELAY_S 5
int thread_skeletonThread (void* data) {
pr_info("Thread started\n");
while (!kthread_should_stop()) {
pr_info("PING!\n");
ssleep(DELAY_S);
}
return 0;
}
void thread_init(void) {
pr_info("Initialize kernel thread\n");
sample_thread = kthread_run(thread_skeletonThread, NULL, "The Machine that goes");
if (IS_ERR(sample_thread)) {
pr_err("Failed to create kernel thread\n");
return;
}
pr_info("Kernel thread initialized\n");
}
void thread_exit(void) {
pr_info("Exiting kernel thread\n");
kthread_stop(sample_thread);
pr_info("Kernel thread exited\n");
}

View File

@@ -0,0 +1,94 @@
#include <linux/module.h> // needed by all modules
#include <linux/init.h> // needed for macros
#include <linux/kernel.h> // needed for debugging
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/atomic.h>
#define TIMEOUT_S 5
DECLARE_WAIT_QUEUE_HEAD(queue_1);
DECLARE_WAIT_QUEUE_HEAD(queue_2);
static struct task_struct* thread_1;
static struct task_struct* thread_2;
static atomic_t trigger = ATOMIC_INIT(0);
int thread_skeleton_1(void* data) {
// must wait 5 seconds and start thread 2
pr_info("Thread 1 started\n");
while (!kthread_should_stop()) {
pr_info("Thread 1 wakes up\n");
ssleep(TIMEOUT_S);
// Setup trigger for condition of the thread 2
atomic_set(&trigger, 1);
// Wake up thread 2
wake_up_interruptible(&queue_2);
// Wait until thread 2 has reset the trigger
wait_event_interruptible(queue_1, atomic_read(&trigger) == 0 || kthread_should_stop());
}
return 0;
}
int thread_skeleton_2(void* data) {
// have to PING when wakes up
pr_info("Thread 2 started\n");
while (!kthread_should_stop()) {
// wait until trigger is set up
wait_event_interruptible(queue_2, atomic_read(&trigger) == 1 || kthread_should_stop());
pr_info("Thread 2 wakes up\n");
// reset trigger
atomic_set(&trigger, 0);
// wake up thread 1
wake_up_interruptible(&queue_1);
}
return 0;
}
void sleeping_init(void) {
pr_info("Initialize kernel thread\n");
atomic_set(&trigger, 0);
thread_1 = kthread_run(thread_skeleton_1, NULL, "Thread 1 - sleeping");
if (IS_ERR(thread_1)) {
pr_err("Failed to create kernel thread 1\n");
return;
}
thread_2 = kthread_run(thread_skeleton_2, NULL, "Thread 2 - sleeping");
if (IS_ERR(thread_2)) {
pr_err("Failed to create kernel thread 1\n");
return;
}
pr_info("Kernel thread sleeping initialized\n");
}
void sleeping_exit(void) {
pr_info("Exiting kernel sleeping thread\n");
kthread_stop(thread_1);
kthread_stop(thread_2);
pr_info("Kernel thread sleeping exited\n");
}

View File

@@ -3,29 +3,76 @@
#include <linux/init.h> // needed for macros
#include <linux/kernel.h> // needed for debugging
#include <linux/moduleparam.h> // needed for module parameters
#include "s02e02-parameters.c"
#include "s02e04-dynamic_allocation.c"
#include "s02e05-io_memory_mapped.c"
#include "s02e06-thread.c"
#include "s02e07-sleeping.c"
static char* text = "dummy text";
module_param(text, charp, 0664);
static int number = 1;
module_param(number, int, 0);
static int __init skeleton_init(void)
{
pr_info ("Linux module skeleton ex02 loaded\n");
pr_debug (" text: %s\n number: %d\n", text, number);
static int __init skeleton_init(void) {
pr_info("Linux module skeleton ex05 loading...\n");
pr_info("--------------------\n");
// Lab02 - Exercise 2: Parameters
// parameters_print();
pr_info("--------------------\n");
// Lab02 - Exercise 4: Dynamic memory allocation and linked list
// dynAlloc_init();
pr_info("--------------------\n");
// Lab02 - Exercise 5: Memory-mapped I/O
// ioMemoryMapped_init();
pr_info("--------------------\n");
// Lab02 - Exercise 6: Kernel thread
// thread_init();
pr_info("--------------------\n");
// Lab02 - Exercise 7: Sleeping
sleeping_init();
pr_info("--------------------\n");
pr_info("Linux module skeleton loaded\n");
return 0;
}
static void __exit skeleton_exit(void)
{
pr_info ("Linux module skeleton unloaded\n");
static void __exit skeleton_exit(void) {
// Lab02 - Exercise 4: Dynamic memory allocation and linked list
// dynAlloc_exit();
pr_info("--------------------\n");
// Lab02 - Exercise 5: Memory-mapped I/O
// ioMemoryMapped_exit();
pr_info("--------------------\n");
// Lab02 - Exercise 6: Kernel thread
// thread_exit();
pr_info("--------------------\n");
// Lab02 - Exercise 7: Sleeping
sleeping_exit();
pr_info("--------------------\n");
pr_info ("Linux module skeleton unloaded\n");
}
module_init (skeleton_init);
module_exit (skeleton_exit);
module_init(skeleton_init);
module_exit(skeleton_exit);
MODULE_AUTHOR ("Fastium <fastium.pro@proton.me>");
MODULE_AUTHOR ("Klagarge <remi@heredero.ch>");
MODULE_DESCRIPTION ("Module skeleton");
MODULE_LICENSE ("GPL");
MODULE_AUTHOR("Fastium <fastium.pro@proton.me>");
MODULE_AUTHOR("Klagarge <remi@heredero.ch>");
MODULE_DESCRIPTION("Module skeleton");
MODULE_LICENSE("GPL");

View File

@@ -1 +1 @@
options mymodule number=42 text="The answer to the Ultimate Question of Life, The Universe, and Everything"
options mymodule elements=5 text="Some element"