gpt4 book ai didi

linux-kernel - 执行读取硬件寄存器的函数时内核 oops

转载 作者:行者123 更新时间:2023-12-01 23:43:34 25 4
gpt4 key购买 nike

我引用的是 this answer for crash帮助分析导致问题的这段代码。每个人的上下文,我正在开发一个字符驱动程序,它将作为 ahci 驱动程序从用户空间直接到硬件的 channel 。为此,我正在相应地修改 ahci 驱动程序。

我从小做起。我想查看 VM 上 AHCI HBA 的 HBA 端口 0 的端口寄存器。我的字符驱动程序 ioctl 代码:

switch (cmd) {
case AHCIP_GPORT_REG:
pPciDev = pci_get_device(0x8086, 0x2829, NULL);

if (pPciDev) {
/* This will set ret to the value that it needs to be. This
* is true of __put_user() too */
if ((ret = __get_user(off, (u32*)obj))) {
printk(KERN_INFO "unable to read from user space\n");
goto ioctl_quick_out;
}

reg = get_port_reg(&pPciDev->dev, off);
if ((ret = __put_user(reg, (u32*)obj)))
{
printk(KERN_INFO "Unable to write to user space\n");
}

pci_dev_put(pPciDev);
}

// This break wasn't in the code when it crashed
break;

default:
// POSIX compliance with this one (REF of LDD3)
ret = -ENOTTY;
}

我修改后的 ahci.c 中的代码,这个字符驱动程序调用了它:

u32 get_port_reg(struct device *dev, u32 off)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
void __iomem *port_mmio = ahci_port_base(ap);

return ioread32(port_mmio + off);
}
EXPORT_SYMBOL(get_port_reg);

这导致的内核 oops 发生在这里:

PID: 3357   TASK: ffff88011c9b7500  CPU: 0   COMMAND: "peek"
#0 [ffff8800abfc79f0] machine_kexec at ffffffff8103b5bb
#1 [ffff8800abfc7a50] crash_kexec at ffffffff810c9852
#2 [ffff8800abfc7b20] oops_end at ffffffff8152e0f0
#3 [ffff8800abfc7b50] no_context at ffffffff8104c80b
#4 [ffff8800abfc7ba0] __bad_area_nosemaphore at ffffffff8104ca95
#5 [ffff8800abfc7bf0] bad_area at ffffffff8104cbbe
#6 [ffff8800abfc7c20] __do_page_fault at ffffffff8104d36f
#7 [ffff8800abfc7d40] do_page_fault at ffffffff8153003e
#8 [ffff8800abfc7d70] page_fault at ffffffff8152d3f5
[exception RIP: get_port_reg+18]
RIP: ffffffffa03c4cd2 RSP: ffff8800abfc7e28 RFLAGS: 00010246
RAX: 0000000000020101 RBX: 00007fff17273960 RCX: ffffffff812b0710
RDX: ffff88011ddd5000 RSI: 0000000000000000 RDI: ffff88011ddd5090
RBP: ffff8800abfc7e28 R8: 0000000000000000 R9: 0000000000000000
R10: 00000000000007d5 R11: 0000000000000006 R12: ffff88011ddd5000
R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0018

如您所见,指令指针为 get_port_reg+18。由于这个函数很小,这里是完整的反汇编

crash> dis get_port_reg
0xffffffffa03c4cc0 <get_port_reg>: push %rbp
0xffffffffa03c4cc1 <get_port_reg+1>: mov %rsp,%rbp
0xffffffffa03c4cc4 <get_port_reg+4>: nopl 0x0(%rax,%rax,1)
0xffffffffa03c4cc9 <get_port_reg+9>: mov 0x240(%rdi),%rax
0xffffffffa03c4cd0 <get_port_reg+16>: mov %esi,%esi
0xffffffffa03c4cd2 <get_port_reg+18>: mov 0x2838(%rax),%rdx
0xffffffffa03c4cd9 <get_port_reg+25>: mov 0x28(%rax),%eax
0xffffffffa03c4cdc <get_port_reg+28>: mov 0x10(%rdx),%rdx
0xffffffffa03c4ce0 <get_port_reg+32>: shl $0x7,%eax
0xffffffffa03c4ce3 <get_port_reg+35>: mov %eax,%eax
0xffffffffa03c4ce5 <get_port_reg+37>: add 0x28(%rdx),%rax
0xffffffffa03c4ce9 <get_port_reg+41>: lea 0x100(%rax,%rsi,1),%rdi
0xffffffffa03c4cf1 <get_port_reg+49>: callq 0xffffffff8129dde0 <ioread32>
0xffffffffa03c4cf6 <get_port_reg+54>: leaveq
0xffffffffa03c4cf7 <get_port_reg+55>: retq
0xffffffffa03c4cf8 <get_port_reg+56>: nopl 0x0(%rax,%rax,1)

您可能已经猜到了,我是一个汇编新手。哪一行代码是 get_port_reg+18?我很困惑,因为我在该函数的每一行调用函数,但我看到的唯一调用是 ioread32()

作为引用,我在 ahci_show_port_cmd() within the same file 之后对我的函数 get_port_reg 进行了建模.我想不出任何其他方法来获取操作所需的 struct pci_dev 结构。我是否滥用了 get_pci_device()pci_dev_put()?这根本不是问题吗?

感谢您的帮助
安迪

最佳答案

我将发布我自己的答案。我的问题的两位评论员让我走上了解决这个问题的正确道路。正如我提到的,我的方法是做一些我曾在 ahci 驱动程序 (ahci.c) 的其他地方看到过的事情。基本上,假设很简单,this function in ahci.c需要一个 struct device* 并从中能够获取所需的 ata_port 信息。我在 ahci.c 中看到,作者偶尔会执行 struct device* = &pdev->dev; 。换句话说,我认为 struct pci_devdev 成员正在为我提供我需要的东西。我显然没有意识到“类类型”或类似的东西(请参阅@myaut 的第一条评论)。 @alexhoppus 根据我发布的代码和反汇编基本上得出了相同/相似的结论。

我采用的修复方法如下:

/* ioctl code in character driver */
switch (cmd) {
case AHCIP_GPORT_REG:
pPciDev = pci_get_device(0x8086, 0x2829, NULL);

if (pPciDev) {
struct ata_host *pHost = NULL;
struct ata_port *pPort = NULL;
printk(KERN_INFO "found the PCI device\n");
/* Get the devices driver data */
pHost = pci_get_drvdata(pPciDev);
if (!pHost) {
ret = -EFAULT;
goto ioctl_valid_pci_dev_out;
}

/* for this test, we'll use just port 0 */
pPort = pHost->ports[0];
if (!pPort) {
ret = -EFAULT;
goto ioctl_valid_pci_dev_out;
}

/* This will set ret to the value that it needs to be. This
* is true of __put_user() too */
if ((ret = __get_user(off, (u32*)obj))) {
printk(KERN_INFO "unable to read from user space\n");
goto ioctl_valid_pci_dev_out;
}

reg = get_port_reg(pPort, off);
if ((ret = __put_user(reg, (u32*)obj)))
{
printk(KERN_INFO "Unable to write to user space\n");
}
}

break;

default:
// POSIX compliance with this one (REF of LDD3)
ret = -ENOTTY;
}

ahci驱动也被修改

u32 get_port_reg(struct ata_port* pPort, u32 off)
{
void __iomem *port_mmio = ahci_port_base(pPort);

return ioread32(port_mmio + off);
}
EXPORT_SYMBOL(get_port_reg);

虽然这已经解决了我的问题,但我非常感谢有人向我解释 (struct pci_dev)device.dev.p->driver_data 中的内容。我可以使用并拥有 Linux 交叉引用工具来查看数据类型。什么应该存储在 struct device_private` 中? This is the structure我现在用它来获取我需要的数据。我真的很感激有人评论这个答案来解释那个。

感谢@myaut 和@alexhoppus

关于linux-kernel - 执行读取硬件寄存器的函数时内核 oops,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30174387/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com