Reading the Interrupt source from GIC - linux-kernel

In past I have asked questions on Interrupt handing on ARM and it helped me quite a lot.
I have few more basic doubt, hope it will not be much stupid.
Device that generates interrupt and wanted it to handled, first in Device driver
register the handler using request_irq()
int request_irq(unsigned int irq,
irqreturn_t (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags,
const char *devname,
void *dev_id)
Generally first parameter to it, an irq number has been defined in DTS file and specific to Soc package
What "irq number" signifies, is it to tell that a particular hardwired line goes from device's interrupt pin to interrupt controller(GICVv2/3) ?
So, when an Interrupt comes device would assert this hardwired irq line and GIC on particular clock would sense/read it.
In fact GIC's distributor part would see all the global interrupt from peripheral and passes it to particular CPU interface of GIC(GIC's 2nd
important component)
How Distributor gets to know to which CPU interface interrupt need to be send(what goes between these two) ?
CPU interface would now assert the IRQ line that goes between CPU core and interrupt controller and CPU core on a particular clock
would sense it.
CPU core would now executes irq-gic.c and read the GIC_IIAR.
What read to GIC_IIAR returns, does it return the Interrupt source, the same number we used as first parameter to request_irq ?
How CPU core would get to know about the Interrupt source and call the appropriate Interrupt handler ?
EDIT
Tried to provide few debug logs, in order to see what GIC_IAR return.
Return value from GIC_IAR is different from irq number used in request_irq
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index fbc4ae2..fc2cc46 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
## -336,7 +336,9 ## static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
do {
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
+ printk_once(KERN_ALERT "***sumit0 is %d\n", irqstat);
irqnr = irqstat & GICC_IAR_INT_ID_MASK;
+ printk_once(KERN_ALERT "***sumit1 is %d\n", irqnr);
if (likely(irqnr > 15 && irqnr < 1020)) {
if (static_key_true(&supports_deactivate))
diff --git a/drivers/net/ethernet/allwinner/sun8i-emac.c b/drivers /net/ethernet/allwinner/sun8i-emac.c
index 155df32..ca3240c 100644
--- a/drivers/net/ethernet/allwinner/sun8i-emac.c
+++ b/drivers/net/ethernet/allwinner/sun8i-emac.c
## -1850,6 +1850,7 ## static int sun8i_emac_probe(struct platform_device *pdev)
}
priv->irq = platform_get_irq(pdev, 0);
+ dev_info(&pdev->dev, "***amit priv->irq is %d\n", priv->irq);
if (priv->irq < 0) {
ret = priv->irq;
dev_err(&pdev->dev, "Cannot claim IRQ: %d\n", ret);
(END)
root#localhost:~# dmesg | grep sumit
[ 0.003926] ***sumit0 is 30
[ 0.003932] ***sumit1 is 30
root#localhost:~# dmesg | grep amit
[ 1.032009] sun8i-emac 1c30000.ethernet: ***amit priv->irq is 19

Related

commenting out a printk statement causes crash in a linux device driver test

I'm seeing a weird case in a simple linux driver test(arm64).
The user program calls ioctl of a device driver and passes array 'arg' of uint64_t as argument. By the way, arg[2] contains a pointer to a variable in the app. Below is the code snippet.
case SetRunParameters:
copy_from_user(args, (void __user *)arg, 8*3);
offs = args[2] % PAGE_SIZE;
down_read(&current->mm->mmap_sem);
res = get_user_pages( (unsigned long)args[2], 1, 1, &pages, NULL);
if (res) {
kv_page_addr = kmap(pages);
kv_addr = ((unsigned long long int)(kv_page_addr)+offs);
args[2] = page_to_phys(pages) + offset; // args[2] changed to physical
}
else {
printk("get_user_pages failed!\n");
}
up_read(&current->mm->mmap_sem);
*(vaddr + REG_IOCTL_ARG/4) = virt_to_phys(args); // from axpu_regs.h
printk("ldd:writing %x at %px\n",cmdx,vaddr + REG_IOCTL_CMD/4); // <== line 248. not ok w/o this printk line why?..
*(vaddr + REG_IOCTL_CMD/4) = cmdx; // this command is different from ioctl cmd!
put_page(pages); //page_cache_release(page);
break;
case ...
I have marked line 248 in above code. If I comment out the printk there, a trap occurs and the virtual machine collapses(I'm doing this on a qemu virtual machine). The cmdx is a integer value set according to the ioctl command from the app, and vaddr is the virtual address of the device (obtained from ioremap). If I keep the printk, it works as I expect. What case can make this happen? (cache or tlb?)
Accessing memory-mapped registers by simple C constructs such as *(vaddr + REG_IOCTL_ARG/4) is a bad idea. You might get away with it on some platforms if the access is volatile-qualified, but it won't work reliably or at all on some platforms. The proper way to access memory-mapped registers is via the functions declared by #include <asm/io.h> or #include <linux/io.h>. These will take care of any arch-specific requirements to ensure that writes are properly ordered as far as the CPU is concerned1.
The functions for memory-mapped register access are described in the Linux kernel documentation under Bus-Independent Device Accesses.
This code:
*(vaddr + REG_IOCTL_ARG/4) = virt_to_phys(args);
*(vaddr + REG_IOCTL_CMD/4) = cmdx;
can be rewritten as:
writel(virt_to_phys(args), vaddr + REG_IOCTL_ARG/4);
writel(cmdx, vaddr + REG_IOCTL_CMD/4);
1 Write-ordering for specific bus types such as PCI may need extra code to read a register inbetween writes to different registers if the ordering of the register writes is important. That is because writes are "posted" asynchronously to the PCI bus, and the PCI device may process writes to different registers out of order. An intermediate register read will not be handled by the device until all preceding writes have been handled, so it can be used to enforce ordering of posted writes.

Access Raspberry Pi 4 system timer

I'm having trouble reading the Raspberry Pi 4 system timer.
My understanding is that the LO 32 bits should be at address 0x7e003004.
My reads always return -1.
Here's how I am trying:
int fd;
unsigned char* start;
uint32_t* t4lo;
fd = open("/dev/mem", O_RDONLY);
if (fd == -1)
{
perror("open /dev/mem");
exit(1);
}
start = (unsigned char*)mmap(0, getpagesize(), PROT_READ, MAP_SHARED,
fd, 0x7e003000);
t4lo = (unsigned int *)(start + 0x04);
...
uint32_t Rpi::readTimer(void)
{
return *t4lo;
}
I should be checking the value of start, but gdb tells me it's reasonable so I don't think that's the problem.
(gdb) p t4lo
$4 = (uint32_t *) 0xb6f3a004
and gdb won't let me access *t4lo. Any ideas?
Edit: clock_gettime() is fulfilling my needs, but I'm still curious.
A closer look at https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf
figure 1 on page 5 shows that addresses vary depending upon who's looking at things. If you start with 0x7c00_0000 on the left side and follow it over to the right, it's apparent that it shows up at 0xfc00_0000 to the processor. So changing the timer base address to 0xfe00_3000 fixed the problem.
The secret is hidden in section 1.2.4:
So a peripheral described in this document as being at legacy address 0x7Enn_nnnn
is available in the 35-bit address space at 0x4_7Enn_nnnn, and visible to the ARM
at 0x0_FEnn_nnnn if Low Peripheral mode is enabled.
The address of the BCM2711 ARM Peripherals is the bus address which is not the same as the physical address in most systems. The bus address is easily used by DMA(Direct Memory Access) controller. mmap creates a new mapping from physical address to virtual address not bus address. So you can't use mmap funtion with parameter 0x7e003000. The rich answer is right.
So changing the timer base address to 0xfe00_3000 fixed the problem.
In addtion, your program run in the User space, only virtual address can you directly use.

Yocto Patch Linux Kernel In-Tree-Module with extern symbol exported from Out-Of-Tree Module

I am using Yocto to build an SD Card image for my Embedded Linux Project. The Yocto branch is Warrior and the Linux kernel version is 4.19.78-linux4sam-6.2.
I am currently working on a way to read memory from an external QSPI device in the initramfs and stick the contents into a file in procfs. That part works and I echo data into the proc file and read it out successfully later in user space Linux after the board has booted.
Now I need to use the Linux Kernel module EXPORT_SYMBOL() functionality to allow an in-tree kernel module to know about my out-of-tree custom kernel module exported symbol.
In my custom module, I do this:
static unsigned char lan9730_mac_address_buffer[6];
EXPORT_SYMBOL(lan9730_mac_address_buffer);
And I patched the official kernel build in a bitbake bbappend file with this:
diff -Naur kernel-source/drivers/net/usb/smsc95xx.c kernel-source.new/drivers/net/usb/smsc95xx.c
--- kernel-source/drivers/net/usb/smsc95xx.c 2020-08-04 22:34:02.767157368 +0000
+++ kernel-source.new/drivers/net/usb/smsc95xx.c 2020-08-04 23:34:27.528435689 +0000
## -917,6 +917,27 ##
{
const u8 *mac_addr;
+ printk("=== smsc95xx_init_mac_address ===\n");
+ printk("%x:%x:%x:%x:%x:%x\n",
+ lan9730_mac_address_buffer[0],
+ lan9730_mac_address_buffer[1],
+ lan9730_mac_address_buffer[2],
+ lan9730_mac_address_buffer[3],
+ lan9730_mac_address_buffer[4],
+ lan9730_mac_address_buffer[5]);
+ printk("=== mac_addr is set ===\n");
+ if (lan9730_mac_address_buffer[0] != 0xff &&
+ lan9730_mac_address_buffer[1] != 0xff &&
+ lan9730_mac_address_buffer[2] != 0xff &&
+ lan9730_mac_address_buffer[3] != 0xff &&
+ lan9730_mac_address_buffer[4] != 0xff &&
+ lan9730_mac_address_buffer[5] != 0xff) {
+ printk("=== SUCCESS ===\n");
+ memcpy(dev->net->dev_addr, lan9730_mac_address_buffer, ETH_ALEN);
+ return;
+ }
+ printk("=== FAILURE ===\n");
+
/* maybe the boot loader passed the MAC address in devicetree */
mac_addr = of_get_mac_address(dev->udev->dev.of_node);
if (!IS_ERR(mac_addr)) {
diff -Naur kernel-source/drivers/net/usb/smsc95xx.h kernel-source.new/drivers/net/usb/smsc95xx.h
--- kernel-source/drivers/net/usb/smsc95xx.h 2020-08-04 22:32:30.824951447 +0000
+++ kernel-source.new/drivers/net/usb/smsc95xx.h 2020-08-04 23:33:50.486778978 +0000
## -361,4 +361,6 ##
#define INT_ENP_TDFO_ ((u32)BIT(12)) /* TX FIFO Overrun */
#define INT_ENP_RXDF_ ((u32)BIT(11)) /* RX Dropped Frame */
+extern unsigned char lan9730_mac_address_buffer[6];
+
#endif /* _SMSC95XX_H */
However, the Problem is that the Kernel fails to build with this error:
| GEN ./Makefile
| Using /home/me/Desktop/poky/build-microchip/tmp/work-shared/sama5d27-som1-ek-sd/kernel-source as source for kernel
| CALL /home/me/Desktop/poky/build-microchip/tmp/work-shared/sama5d27-som1-ek-sd/kernel-source/scripts/checksyscalls.sh
| Building modules, stage 2.
| MODPOST 279 modules
| ERROR: "lan9730_mac_address_buffer" [drivers/net/usb/smsc95xx.ko] undefined!
How can I refer to the Out-Of-Tree kernel module exported symbol in a patched In-Tree kernel module?
initramfs relevant code:
msg "Inserting lan9730-mac-address.ko..."
insmod /mnt/lib/modules/4.19.78-linux4sam-6.2/extra/lan9730-mac-address.ko
ls -rlt /proc/lan9730-mac-address
head -c 6 /dev/mtdblock0 > /proc/lan9730-mac-address
Out-Of-Tree module:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
const int BUFFER_SIZE = 6;
int write_length, read_length;
unsigned char lan9730_mac_address_buffer[6];
EXPORT_SYMBOL(lan9730_mac_address_buffer);
int read_proc(struct file *filp, char *buf, size_t count, loff_t *offp)
{
// Read bytes (returning the byte count) until all bytes are read.
// Then return count=0 to signal the end of the operation.
if (count > read_length)
count = read_length;
read_length = read_length - count;
copy_to_user(buf, lan9730_mac_address_buffer, count);
if (count == 0)
read_length = write_length;
return count;
}
int write_proc(struct file *filp, const char *buf, size_t count, loff_t *offp)
{
if (count > BUFFER_SIZE)
count = BUFFER_SIZE;
copy_from_user(lan9730_mac_address_buffer, buf, count);
write_length = count;
read_length = count;
return count;
}
struct file_operations proc_fops = {
read: read_proc,
write: write_proc
};
void create_new_proc_entry(void) //use of void for no arguments is compulsory now
{
proc_create("lan9730-mac-address", 0, NULL, &proc_fops);
}
int proc_init (void) {
create_new_proc_entry();
memset(lan9730_mac_address_buffer, 0x00, sizeof(lan9730_mac_address_buffer));
return 0;
}
void proc_cleanup(void) {
remove_proc_entry("lan9730-mac-address", NULL);
}
MODULE_LICENSE("GPL");
module_init(proc_init);
module_exit(proc_cleanup);
There are several ways to achieve what you want (taking into account different aspects, like module can be compiled in or be a module).
Convert Out-Of-Tree module to be In-Tree one (in your custom kernel build). This will require simple export and import as you basically done and nothing special is required, just maybe providing a header with the symbol and depmod -a run after module installation. Note, you have to use modprobe in-tree which reads and satisfies dependencies.
Turn other way around, i.e. export symbol from in-tree module and file it in the out-of-tree. In this case you simply have to check if it has been filed or not (since it's a MAC address the check against all 0's will work, no additional flags needed)
BUT, these ways are simply wrong. The driver and even your patch clearly show that it supports OF (Device Tree) and your board has support of it. So, this is a first part of the solution, you may provide correct MAC to the network card using Device Tree.
In the case you want to change it runtime the procfs approach is very strange to begin with. Network device interface in Linux has all means to update MAC from user space at any time user wants to do it. Just use ip command, like /sbin/ip link set <$ETH> addr <$MACADDR>, where <$ETH> is a network interface, for example, eth0 and <$MACADDR> is a desired address to set.
So, if this question rather about module symbols, you need to find better example for it because it's really depends to use case. You may consider to read How to export symbol from Linux kernel module in this case? as an alternative way to exporting. Another possibility how to do it right is to use software nodes (it's a new concept in recent Linux kernel).

How to detect a kernel panic after reboot

I have some unexpected reboot on a embedded device. I am currently able to detect a hardware watchdog issue thanks to an ioctl call. Now I would like be able to detect if a kernel panic was the reason for a reboot. I find some articles concerning crashkernel and crashdump but I was not able to make it work properly. And I dont want to store the kernel panic log. Just be able to know if kernel panic happens.
My current idea was to write in a reserved space on mmc. I am currently using a reserved space to handle a double distribution system. It is a good idea ? Is it possible to write in mmc during a kernel panic ? I am not sure but its seems that I can use kind of kernel panic hook to run routine on this event.
There is no standard way to be able to check that kernel panic happened on boot ?
I was able to detect and debug kernel panic thanks to the comment from #0andriy How to detect a kernel panic after reboot
Enable ramoops in kernel defconfig :
+CONFIG_PSTORE=y
+CONFIG_PSTORE_ZLIB_COMPRESS=y
+CONFIG_PSTORE_CONSOLE=y
+CONFIG_PSTORE_RAM=y
Add code in your kernel board init to declare the ramoops memory space, you
can also use the device tree or even use a parameter in kernel procline
This is an example using the code method, in my usecase it was in
arch/arm/mach-imx/mach-imx6ul.c
--- a/arch/arm/mach-imx/mach-imx6ul.c
+++ b/arch/arm/mach-imx/mach-imx6ul.c
## -21,6 +21,24 ##
#include "cpuidle.h"
#include "hardware.h"
+#include <linux/pstore_ram.h>
+#include <linux/memblock.h>
+
+static struct ramoops_platform_data ramoops_data = {
+ .mem_address = 0xXXXXXXXX, // Depending of the hardware
+ .mem_size = 0x00005000, // 5 Mb
+ .record_size = 0x00002000, // 1 Mb
+ .dump_oops = 1,
+};
+
+static struct platform_device ramoops_dev = {
+ .name = "ramoops",
+ .dev = {
+ .platform_data = &ramoops_data,
+ },
+};
+
+
static void __init imx6ul_enet_clk_init(void)
{
struct regmap *gpr;
## -170,6 +188,14 ## static inline void imx6ul_enet_init(void)
static void __init imx6ul_init_machine(void)
{
struct device *parent;
+ int ret;
+
+ ret = platform_device_register(&ramoops_dev);
+ if (ret) {
+ printk(KERN_ERR "unable to register platform device\n");
+ return;
+ }
+ memblock_reserve(ramoops_data.mem_address, ramoops_data.mem_size);
parent = imx_soc_device_init();
if (parent == NULL)
Then on boot I just have to check the content of ramoops to check if there is some kernel panic log available. I can mount the ramoops memory space with :
mount -t pstore -o kmsg_bytes=1000 - /sys/fs/pstore
Here's how Windows handles it:
do not use drivers any more
write to disk using BIOS routines (or something low level as this)
write the kernel dump into the page file (the only known place which is contiguous and known that we can write to without damaging anything)
on next boot, check if the page file contains a crash dump signature
You might be able to apply this concept to Linux, e.g. write to the swap partition and check the contents of the swap partition at next startup.

copy_to_user not copying data?

for a project we are reading and writing data from an embedded FPGA on a SoC system. Writing works (for now just 1 byte, but oh well). The read function correctly accesses the FPGA (and gets the correct value) but for some reason the copy_to_user does not copy anything to the user. Running cat on my device doesn't not return anything. I hope somebody can tell me where I'm doing something wrong.
Additional information: We're targeting a Altrera Cyclone V SoC system with a ARMv7 Processor. We're using a buildroot system with kernel 4.3.0 as recommended by Altera.
Code:
// Read function is called whenever a read in performed on one of the /dev devices
static ssize_t mydevice_read(struct file *file, char *buffer, size_t len, loff_t *offset) {
int success = 0;
u32 read_value32 = 0;
// Get the device struct out of the miscdev struct
struct mydevice_dev *dev = container_of(file->private_data, struct mydevice_dev, miscdev);
// Read data from FPGA
read_value32 = ioread32(dev->regs);
pr_info("Data received from FPGA: %d", read_value32);
success = copy_to_user(buffer, &read_value32, sizeof(read_value32));
pr_info("%d: %d bytes copied to userspace pointer 0x%p, value: %d!\n", success, sizeof(read_value32), buffer, dev->data_value8);
// If copy_to_user failed
if (success != 0) {
pr_info("Failed to copy current value to userspace!\n");
return -EFAULT;
}
return 0;
}
Output (including kernel messages and debug prints):
# insmod mymodule.ko
[ 701.922707] Initializing mymodule module
[ 701.926681] Probing for fpga devices...
[ 701.931382] Probing successful!
[ 701.935429] FPGA successfully initialized!
# echo -n -e \\x81 > /dev/mydevice
# cat /dev/mydevice
[ 721.555795] Data received from FPGA: 129
[ 721.559539] 0: 4 bytes copied to userspace pointer 0xbec67c78, value: 129!
Thanks a bunch!
Are you sure about return 0;? I think this function should return the amount of bytes copied, in your case this should be return sizeof(read_value32);

Resources