Halt instruction from Linux kernel module is not working - c

I wrote a simple Linux kernel module to issue hlt instruction
#include <linux/kernel.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
static int __init test_hello_init(void)
{
asm("hlt");
return 0;
}
static void __exit test_hello_exit(void)
{
}
module_init(test_hello_init);
module_exit(test_hello_exit);
Loading this module on my Virtual Machine, I don't see my VM is halted.
Am I missing something?

HLT doesn't stop your machine, only make that core sleep (in C1 idle) until the next interrupt.
You can try adding cli instruction before hlt, so only an NMI can wake that CPU up and make the function return.
static int __init test_hello_init(void) {
asm("cli");
asm("hlt");
return 0;
}

Related

LKM scheduling while atomic

I am developing a linux kernel module, which looks like this:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Me");
MODULE_DESCRIPTION("Something Something");
int checkSomething(void) {
int someCpuFeature = 0;
__asm__("mov $1, %eax");
__asm__("cpuid");
__asm__("mov %%ecx, %0" : "=r" (someCpuFeature));
if (someCpuFeature & 32) {
return 1;
}
return 0;
}
int __init init_module(void) {
if (!checkSomething()) {
printk(KERN_INFO "Exiting\n");
return 0;
} else {
printk(KERN_INFO "Continuing\n");
}
return 0;
}
static void __exit exit_module(void) {
printk(KERN_INFO "Unloading Module\n");
}
And when i loaded it, i tried to see it's output from dmesg.
but instead of only printing Exiting/Continuing, it also printed a call trace,
and said BUG: scheduling while atomic: insmod/24641/0x06100800.
I searched this bug and found that it has some connection to the scheduler and sleeping at places you shouldn't sleep in, but this is the only functionality of the code,
So i think it has something to do with the cpuid instruction, but i don't know exactly what it is.
any ideas?
Hm, considering the interrupts raised on my machine now, it would look like your assembler is incorrect.
Instead of writing this yourself, I'd recommend relying on the cpuid kernel module (and thus, reading /dev/cpu/NUMBER/cpuid after seeking to your level of interest), as that's future-proof external API. You can also look at /arch/x86/include/asm/processor.h, and use kernel functions like cpu_has to detect your feature. Don't reinvent the wheel – querying details of a CPU on an SMP machine is bound to be painful, and the poor kernel developers had to go through the pain to make this work themselves.

nr_cpu_ids vs NR_CPUS in Linux Kernel

I am trying to understand how many processors are supported by Linux Kernel.
grep NR_CPUS /boot/config-`uname -r`
Will give me the maximum number of processors supported by kernel, which I can override using kernel command line parameter nr_cpus.
To find number of online cpus, i can use num_online_cpus() function
Then what is nr_cpu_ids?
#include <linux/kernel.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
static int __init test_hello_init(void)
{
pr_info("%s: In init NR_CPUs=%d, nr_cpu_ids=%d\n", __func__, NR_CPUS, nr_cpu_ids);
pr_info("Number of cpus available:%d\n", num_online_cpus());
return -1;
}
static void __exit test_hello_exit(void)
{
pr_info("%s: In exit\n", __func__);
}
module_init(test_hello_init);
module_exit(test_hello_exit);
[11548.627338] test_hello_init: In init NR_CPUs=8192, nr_cpu_ids=128
[11548.627340] Number of cpus available:6
What is the difference between NR_CPUs and ncr_cpu_ids. Are they not same?
nr_cpu_ids is the total number of CPUs or processors in the machine while NR_CPUS is the total number of CPUs the Linux O/S can handle.

How to get high precision nano second delay in linux

I have written kernel module to measure the correctness of ndelay() kernel function.
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/delay.h>
static int __init initialize(void)
{
ktime_t start, end;
s64 actual_time;
int i;
for(i=0;i<10;i++)
{
start = ktime_get();
ndelay(100);
end = ktime_get();
actual_time = ktime_to_ns(ktime_sub(end, start));
printk("%lld\n",(long long)actual_time);
}
return 0;
}
static void __exit final(void)
{
printk(KERN_INFO "Unload module\n");
}
module_init(initialize);
module_exit(final);
MODULE_AUTHOR("Bhaskar");
MODULE_DESCRIPTION("delay of 100ns");
MODULE_LICENSE("GPL");
the dmesg output is like this:
[16603.805783] 514
[16603.805787] 350
[16603.805789] 373
[16603.805791] 323
[16603.805793] 362
[16603.805794] 320
[16603.805796] 331
[16603.805797] 312
[16603.805799] 304
[16603.805801] 350
I have gone through one of the posts in stackoverflow:
Why udelay and ndelay is not accurate in linux kernel?
But I want a fine tuned nanosecond delay (probably in the range of 100-250ns) in kernel space. Can anyone please suggest me any alternative for doing this?
You can use
High resolution timers (or hrtimers)
hrtimer_init
hrtimer_start
hrtimer_cancel
functions. An example is available here
If you are targeting x86 only system, you can use rdtsc() call to get the CPU clock counts. The rdtsc() api has very little overhead. But you do need to convert from CPU clock to the ns, it is dependent on how fast your CPU clock is running.
static unsigned long long rdtsc(void)
{
unsigned int low, high;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
return low | ((unsigned long long)high) << 32;
}
Otherwise you can use the kernel high resolution timers API.
The high-resolution timer API
Linux Kernel ktime - high resolution timer type, APIs, RDTSC usages

How to load a module (not a driver) when a USB device is plugged in

#include<linux/init.h>
#include<linux/module.h>
#include <linux/usb/input.h>
#include <linux/hid.h>
/*
* Version information
*/
#define DRIVER_VERSION ""
#define DRIVER_DESC "Hello World module"
#define DRIVER_LICENSE "GPL"
MODULE_LICENSE(DRIVER_LICENSE);
MODULE_AUTHOR(DRIVER_AUTHOR);
static void __exit hello_world_exit(void)
{
pr_debug("Bye!\n");
}
static int __init hello_world_init(void)
{
pr_debug("Hello, USB!");
return 0;
}
static struct usb_device_id usb_kbd_id_table[] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID,
USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_KEYBOARD) },
{}
};
MODULE_DEVICE_TABLE(usb, usb_kbd_id_table);
module_init(hello_world_init);
module_exit(hello_world_exit);
How do I make the kernel load this module load when a USB mouse is plugged in (using the userspace hotplug tools) ? Right now, I have put the hello_world.ko file in /lib/modules/$(uname -r) and run depmod -a.
In modern Linux the functionality for loading drivers/modules (or invoking any other commands) whenever new hardware is detected is handled by udev. You will have to write a udev rule for your device that will instruct the kernel to load your module when your device has been detected and the corresponding event has occurred. Read more about it here.

How to get data segment of Linux kernel from LKM

I'm writing a kernel module which involves the tasklist_lock, __bss_start.
These symbols are not exported. I'm pretty sure even if not exported, we can access the symbols from text sections using kernsym_lookup_name()
Reference How my custom module on linux 3.2.28 can make a call to print_cpu_info?
$ vim System.map
...
80017be0 T register_undef_hook
80017c28 T unregister_undef_hook
80017c70 T do_unexp_fiq
...
806eb000 D mmlist_lock
806eb040 D tasklist_lock
806eb080 d softirq_vec
....
T represents text symbol.
D and d represents data segment symbol.
I'm able to access register_undef_hook() and unregister_undef_hook() using kallsyms_lookup_name().
But not tasklist_lock.
Please share your knowledge to access tasklist_lock from kernel module(LKM).
See this noble post
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/string.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Access non-exported symbols");
MODULE_AUTHOR("Stephen Zhang");
static int __init lkm_init(void)
{
char *sym_name = "__bss_start";
unsigned long sym_addr = kallsyms_lookup_name(sym_name);
char filename[256];
strncpy(filename, (char *)sym_addr, 255);
printk(KERN_INFO "[%s] %s (0x%lx): %s\n", __this_module.name, sym_name, sym_addr, filename);
return 0;
}
static void __exit lkm_exit(void)
{
}
module_init(lkm_init);
module_exit(lkm_exit);

Resources