Compare commits
8 Commits
02-KernelM
...
02-KernelM
| Author | SHA1 | Date | |
|---|---|---|---|
|
8fff875529
|
|||
|
e4089d2e05
|
|||
|
a6ef142912
|
|||
|
409e1565fb
|
|||
|
7f46bd3940
|
|||
|
786ed475ba
|
|||
|
fb8de73be8
|
|||
|
da2639d6f3
|
@@ -234,7 +234,7 @@ The `/proc/modules` file give us more details about the state of the module. We
|
|||||||
|> dmesg | tail -5
|
|> dmesg | tail -5
|
||||||
[ 3559.279143] number: 1
|
[ 3559.279143] number: 1
|
||||||
[ 3581.198562] Linux module skeleton unloaded
|
[ 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] text: The answer to the Ultimate Question of Life, The Universe, and Everything
|
||||||
[ 3583.621085] number: 42
|
[ 3583.621085] number: 42
|
||||||
|> modprobe -r mymodule
|
|> 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
|
// Exercise 5: Display the processor chip ID, CPU temperature and the MAC adress of the Ethernet controller
|
||||||
//-------------------
|
//-------------------
|
||||||
@@ -315,14 +332,29 @@ This number matches with this table (#link("https://www.kernel.org/doc/html/late
|
|||||||
"temperature" = -1991 dot "register value" / 10 + 223000
|
"temperature" = -1991 dot "register value" / 10 + 223000
|
||||||
$
|
$
|
||||||
|
|
||||||
The chip ID can be verified in ```/proc/iomem```.
|
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 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 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
|
// 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
|
// Exercise 7: Sleeping
|
||||||
//-------------------
|
//-------------------
|
||||||
|
|||||||
15
src/01-skeleton/s02e02-parameters.c
Normal file
15
src/01-skeleton/s02e02-parameters.c
Normal 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);
|
||||||
|
}
|
||||||
58
src/01-skeleton/s02e04-dynamic_allocation.c
Normal file
58
src/01-skeleton/s02e04-dynamic_allocation.c
Normal 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");
|
||||||
|
}
|
||||||
105
src/01-skeleton/s02e05-io_memory_mapped.c
Normal file
105
src/01-skeleton/s02e05-io_memory_mapped.c
Normal 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);
|
||||||
|
}
|
||||||
42
src/01-skeleton/s02e06-thread.c
Normal file
42
src/01-skeleton/s02e06-thread.c
Normal 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");
|
||||||
|
}
|
||||||
@@ -3,29 +3,65 @@
|
|||||||
#include <linux/init.h> // needed for macros
|
#include <linux/init.h> // needed for macros
|
||||||
#include <linux/kernel.h> // needed for debugging
|
#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"
|
||||||
|
|
||||||
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)
|
static int __init skeleton_init(void) {
|
||||||
{
|
pr_info("Linux module skeleton ex05 loading...\n");
|
||||||
pr_info ("Linux module skeleton ex02 loaded\n");
|
pr_info("--------------------\n");
|
||||||
pr_debug (" text: %s\n number: %d\n", text, number);
|
|
||||||
|
// 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");
|
||||||
|
|
||||||
|
pr_info("Linux module skeleton loaded\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit skeleton_exit(void)
|
static void __exit skeleton_exit(void) {
|
||||||
{
|
|
||||||
pr_info ("Linux module skeleton unloaded\n");
|
|
||||||
|
// 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");
|
||||||
|
|
||||||
|
pr_info ("Linux module skeleton unloaded\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init (skeleton_init);
|
module_init(skeleton_init);
|
||||||
module_exit (skeleton_exit);
|
module_exit(skeleton_exit);
|
||||||
|
|
||||||
MODULE_AUTHOR ("Fastium <fastium.pro@proton.me>");
|
MODULE_AUTHOR("Fastium <fastium.pro@proton.me>");
|
||||||
MODULE_AUTHOR ("Klagarge <remi@heredero.ch>");
|
MODULE_AUTHOR("Klagarge <remi@heredero.ch>");
|
||||||
MODULE_DESCRIPTION ("Module skeleton");
|
MODULE_DESCRIPTION("Module skeleton");
|
||||||
MODULE_LICENSE ("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|||||||
@@ -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"
|
||||||
Reference in New Issue
Block a user