如何解决Linux 内核:AXI 互连,它在哪里以及如何处理?
我正在为我的公司编写 Linux 驱动程序,以便将我们的硬件移植到 GNU/Linux 桌面。我根本不是硬件专家,我正在努力理解内核和硬件之间的通信是如何进行的。
我们基本上有一个 AXI 互连,在上面连接了一些 IP(我们使用的是运行 PetaLinux 的 Xilinx 板)。
我已经能够向硬件发送请求,它运行良好,但我觉得我遗漏了一些东西。
在内核中,由于 ioremap()
,我将物理地址映射到虚拟地址,并且我自己实现了读/写,如下所示:
static void __iomem *mailbox;
static ssize_t mailbox_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos)
{
ssize_t retval = 0;
uint32_t mlb_data = 0;
if(down_interruptible(&sem))
return -ERESTARTSYS;
// *f_pos must be a multiple of 4
// *f_pos must be in bounds
// count must be 4 (mailbox supports only reading 4 bytes at a time)
if(*f_pos % 4 || *f_pos >= MAILBOX_SIZE || count != sizeof(mlb_data)) {
retval = -EINVAL;
goto out;
}
mlb_data = readl(mailbox + *f_pos);
if(copy_to_user(buf,&mlb_data,count)) {
retval = -EFAULT;
goto out;
}
*f_pos += count;
retval = count;
out:
up(&sem);
return retval;
}
int mailbox_init(dev_t device)
{
mailbox = ioremap_nocache(MLB_BASE_ADDR,MAILBOX_SIZE);
if(!mailbox) {
printk(KERN_ERR DRIVER_NAME ": cannot map mailbox,ioremap failed.\n");
return err;
}
return 0;
}
在用户方面,我尝试像这样读/写:
int dev_fd = open("/dev/" DRIVER_NAME,O_RDWR);
if(dev_fd < 0) return whatever;
int data;
pread(dev_fd,&data,sizeof data,0);
close(dev_fd);
效果很好,但我不明白怎么会这么简单,所有 AXI 的东西都在哪里处理?我以为这是我必须做的事情,但我惊讶地发现一切都已经正常了。
我觉得一切都是透明的真是太好了,但除此之外,我想实现一些错误处理,但我不知道如何去做。
例如,如果我尝试使用 devmem(内部使用 /dev/mem)从不受支持的地址读取,我会收到总线错误:
root@petalinux:~# devmem 0xCAFEBABE
Bus error
但是如果我尝试通过我自己的角色设备来做同样的事情,它就会挂起。似乎没有收到 SIGBUS。
我不确定我是否已经很清楚了,所以总结一下我的两个问题:
- Linux 内核中的 AXI 内容在哪里处理?会不会是 Xilinx 驱动程序“覆盖”了我的驱动程序?
- 如何像
/dev/mem
那样通过向用户空间应用发送 SIGBUS 来处理硬件故障?
解决方法
-
查看Xilinx AXI Ethernet Driver(drivers/net/ethernet/xilinx/xilinx_axienet_main.c),设置iomem cookie(
mailbox
)导致readl()
、writel()
、{{1} } 等等以正确处理访问。如何在硬件级别完成此操作的详细信息取决于硬件架构。例如,mach-ipx4xx 使用 __is_io_address() 宏来确定应该使用 ipx4xx_pci_read()(通过
memcpy_fromio()
)还是 __raw_readl()/__indirect_readl()。 -
inl()
是有符号的,当偏移量无效时,返回 -EFAULT 而不是 -EINVAL 可能更有意义:loff_t
-
考虑使用较新的
// *f_pos must be within bounds if (*f_pos < 0 || *f_pos >= MAILBOX_SIZE) { retval = -EFAULT; goto out; } // *f_pos must be a multiple of 4,and // count must be 4 (mailbox supports only reading 4 bytes at a time) if ((*f_pos & 3) || count != sizeof mlb_data) { retval = -EINVAL; goto out; }
(或readl()
,如果设备始终使用大端字节序),而不是ioread32()
。有关详细信息,请参阅 include/asm-generic/iomap.h。在许多架构上,
ioread32be()
只是调用了ioread32()
,所以这不是功能上的改变;通过保持当前推荐的内部内核接口,这更多是为了更易于长期维护。(“[某些] 架构不能使用这个通用接口”的评论是针对架构维护者的,他们不能仅仅依赖通用接口,而是需要在特定于架构的文件中为他们的硬件架构实现宏。驱动程序开发人员可以依赖这些宏的可用和正常工作。)
-
在这里提高 SIGBUS 并没有什么意义,因为用户空间不是试图直接访问内存,而是使用系统调用;返回 -EFAULT 在这里绝对更合适。但是,要提高 SIGBUS,您可以
readl()
我希望这会有所帮助,并且即使只有少数用户,您也会将 GPL 驱动程序推向上游。特别是 Greg KH 过去曾帮助过此类工作。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。