How does VirtualBox handle interrupt for the guest Linux? - c

I am practicing writing a simple keyboard driver in VirtualBox guest Linux. The problem is, my code just register an interrupt handler and print scancode to the log file. And I don't send those incoming scancode to any upper level codes, like Linux input core. After insmod, I can see those captured scancode using dmesg. But why my terminal still gets correct input? There should not be anything received by the terminal.
My code looks like this:
static int __init init_simple_keyboard_driver(void)
{
free_irq (IRQ_1, NULL);
return request_irq (IRQ_1, my_handler, ...);
}
static irqreturn_t my_handler(int irq, void *dev_id)
{
unsigned char scancode = get_scancode_from_port_0x60();
printk(...scancode...);
}
After insmod, I can see messages in the kernel log.
My free_irq call causes some messages like Can't free already freed IRQ. (I don't know why... It should not be freed already.)
atkbd driver complains that there is someone ask to handle IRQ_1 instead.
Those scancode can be correctly printed.
[The Most Weird One] The active console still gets correct keyboard input. Thus I can just perform a rmmod using this simple driver.
After rmmod, the guest Linux just dead because it can't receive any keyboard anymore.
Do you have any idea? Thank you!

The driver should not and could not un-register interrupt handler that is not registered by itself.
For preventing the original driver handle the keyboard interrupt, you can do ether:
1) return IRQ_HANDLED in your interrupt handler: This value indicates the interrupted is well handled and the linux kernel's interrupt processing mechanism would stop calling next interrupt handler. Or
2) clear input buffer in hardware, you can reference the original keyboard driver's code to know the status register and input buffer used during a keyboard hit event.

Related

How to Exit HardFault handler (cortex-m4) properly

I am running on SOC using cortex-m4, when one of the HW components fail it raises bus-fault that cause Hard-Fault.
I want in the hard fault handler not only to print some registers and reset, I want instead to open UART connection to the device, the problem is that UART driver is using interrupts and I cannot open UART driver from Hard-Fault handler (cant call from within ISR)
Is there anyway to leave the hard-fault handler in a smart way (not including having the hard-fault raise a flag, then return and then the main() function will call the uart-driver) ?

How to properly disable a PCIe device?

I am writing a device driver in linux for PCIe endpoint implemented on Xilinx UltrascaleMPSoC FPGA part. I have implemented the remove function correctly. I connect my device using an adapter to my PC, turn on device, enable its Endpoint and then turn on PC and everything works correctly. However, when I try to unload the driver module using rmmod command, the process hangs.
I went through Linux documentation and for pci_disable_device() documentation, it says
Note we don't actually disable the device until all callers of pci_enable_device() have called pci_disable_device().
Does it mean, my driver has to wait untill every other pcie driver in linux calls pci_disable_device() and only then device gets disabled? I really doubt upon this :(
I tried using modprobe -r and also listed the module usage count using "lsmod". lsmod shows usage count as "0". But still I am not able to unload the module :( I also added print statements.
void remove(struct pci_dev pdev)
{
pci_unmap_single(pdev, privdata->dma_mem,
PAGE_SIZE * (1 << memorder),
PCI_DMA_FROMDEVICE);
printk(KERN_INFO"unmap_single() complete\n");
free_pages ((unsigned long) privdata->mem, memorder);
printk(KERN_INFO"free_pages() complete\n");
free_irq(pdev->irq, privdata);
printk(KERN_INFO"free_irq() complete\n");
pci_disable_msi(pdev);
printk(KERN_INFO"MSI disable complete\n");
pci_clear_master(pdev); /* Nobody seems to do this */
printk(KERN_INFO"clear_master() complete\n");
pci_iounmap(pdev, privdata->registers);
printk(KERN_INFO"iounmap() complete\n");
pci_disable_device(pdev);
printk(KERN_INFO"disable_device() complete\n");
pci_release_regions(pdev);
printk(KERN_INFO"release_regions() complete\n");
}
Expected: The device must get disabled. I can't conclude. When I do a dmesg in another terminal, I get prints till "disable_device() complete" and the terminal hangs.Also,I checked the files: /proc/iomem and /proc/interrupts-> when I unload the module, corresponding entries of my device is removed in both of my files! I only complete the execution of pci_disable_device() and the process hangs.
Note: Only the rmmod process hangs and I cannot use the current terminal, but I can open another terminal, do all things.
Finally, when the do a restart, the system hangs again and everytime, I am doing force restart by holding power button for 10-15 seconds.
Check this reboot_hang
Why is the pci_release_regions() hanging my system even though lsmod shows my module usage count as "0".
Thanks in advance :)
Also, should I interchange pci_disable_device() and pci_release_regions() methods in above code? -> I tried this as well, but then I get prints till release_regions() complete but disable_device complete is not printed. And process hangs :(
Please help
Difficult to answer without more information on what you really use on your driver.
But, I will try to give you an answer.
Does it mean, my driver has to wait untill every other pcie driver in linux calls pci_disable_device() and only then device gets disabled?
No, this would make impractical and not very modular.
It seems you're freeing the DMA pages. How are you allocating them ? Are you using coherent allocation? Take a look here to see how to use properly the DMA.
For the remainder, can you try to move release regions before clear_master? Example :
pci_disable_msi(pdev);
ioummap(ADDRESS);
pci_release_regions(pdev);
pci_clear_master(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
For more information on how to write PCI Drivers, check the documentation here. It provides a lot of information.

Why does read not terminate for my USB device?

I have an USB-device connected with my RaspberryPi 3 B+ (Raspbian Buster Lite 2019-07-10). I also write an small program to read data from the USB Device. The device has an custom, CDC conform firmware, so the device is detected correctly by the OS and and tty is attached.
But when I call 'read' for the device, the syscall never terminates. The odd thing is, when I access the device with MiniCom, CuteCom or even H-Term, it works correct.
I already tried the answers from the following questions:
Reading and writing to serial port in C on Linux
How to open, read, and write from serial port in C?
C program to read data from USB device connected to the system
None of them worked.
I also tried to flush the tty with tcflush and tcdrain.
int dev = open("/dev/ttyACM0", O_RWDR | O_NOCTTY);
// I tried to adapt the device parameters with termios, see above
uint8_t req[] = {0x21, 0x42, 0x00, 0x12}; // the actual request
write(dev, req, 4);
uint8_t resp[12];
read(dev, resp, 8); // does not terminate
I expect a result, 12 bytes long, but read just blocks. If I try the O_NONBLOCK/O_NDELAY option, read terminates with the EAGAIN error.
Of course I checked the result values from every syscall and library function, they all returned/terminated as expected. And as I mentioned above, when connected with a terminal program the device works how it should, so it can't be the firmware.
Also I traced with strace what minicom and cutecom did, but they also did nothing more than open, write and read and of course I tried everything with sudo, so the rights are not the problem.
Seems like the device has never taken the request, so its never processing and getting blocked. so, something prob in the way the request is written. Did you try setting the speed of the port/interface?
I found the Answer myself: the ICANON bit in the termios struct, c_lflag field musst be cleared and the VMIN field in the c_cc array should be set to 1.
Further explanations are found here:
https://www.gnu.org/software/libc/manual/html_node/Local-Modes.html#Local-Modes
https://www.gnu.org/software/libc/manual/html_node/Mode-Data-Types.html
(be carefull, some parts of this documentary are a little bit outdated, for example the CCTS_OFLOW Flag does not exist on Raspbian-Buster)

Can a shared interrupt line in Linux have different interrupt handlers?

Can an interrupt line shared by different drivers have different unique interrupt handlers? For example, would something like this be possible?
on driver1 : request_irq(irq, &handler1, IRQF_SHARED,"dev1", dev1);
on driver2 : request_irq(irq, &handler2, IRQF_SHARED,"dev2", dev2);
If all drivers that want to share this IRQ are requesting it with IRQF_SHARED this works - If only one of them does not set that flag, i.e. wants the IRQ exclusively, your request_irq will fail.
Another precondition is that your handler needs to be able to recognise somehow from the hardware whether it was his device that triggered the IRQ or not. This determines the return value of the handler.
The kernel will call all handlers that share the IRQ in sequence until it found one that actually handled it.

FreeRTOS queues for IO before scheduler starts (or after it stops)

I'm looking for advice on how to best implement thread-safe IO (e.g. for printf going to a debug serial port) in an environment in which the operating system scheduler may yet to have started, be running, suspended or may have stopped or crashed. I'm using Newlib and FreeRTOS.
At the moment, I'm doing the (seemingly-standard) FreeRTOS approach of having the _write system call (Newlib) queue characters into a FreeRTOS queue which is then emptied from an interrupt service routine (filling the serial port hardware FIFO then waiting for the FIFO empty interrupt).
This has the disadvantage that (at least on FreeRTOS) the queue can only be used safely when the scheduler is running, and debug output can not be printed when interrupts are disabled (as they are during boot until the scheduler starts, or after a fatal error condition (exactly where debug printf output would be most useful :-).
Would it be best to have the _write system call query the scheduler and/or interrupt status, and use queues if the scheduler is running and use blocking/polling serial IO when interrupts are disabled? Is there a more elegant idea I haven't thought of yet?
Thanks
Perhaps slightly more elegant would be to use indirection in the _write system call. Instead of checking the status, use a function pointer to achieve the desired functionality based on whether the scheduler is running, interrupts enabled, etc. Then set the appropriate callback function when the program state changes.
int (*writeCallback)(int, const void *, unsigned int);
int
_write(int fd, const void *buffer, unsigned int count)
{
return writeCallback(fd, buffer, count);
}
...
writeCallback = polling_write;
...
writeCallback = rtos_write;
...
Alternatively, you define a different debug function that bypasses the standard library stream system (i.e. printf) to use in places that you know will not have interrupt/RTOS facilities (e.g. exception handlers).

Resources