linux device driver - Kernel oops when executing function to read hardware registers -
i'm referencing this answer crash in analyzing bit of code caused problems. context everyone, i'm working character driver, act pass through user space directly hardware, ahci driver. i'm modifying ahci driver accordingly purpose.
i'm starting small. want peek @ port registers hba port 0 of ahci hba on vm. character driver ioctl code:
switch (cmd) { case ahcip_gport_reg: ppcidev = pci_get_device(0x8086, 0x2829, null); if (ppcidev) { /* set ret value needs be. * true of __put_user() */ if ((ret = __get_user(off, (u32*)obj))) { printk(kern_info "unable read 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 write user space\n"); } pci_dev_put(ppcidev); } // break wasn't in code when crashed break; default: // posix compliance 1 (ref of ldd3) ret = -enotty; }
the code modified version of ahci.c character driver calls into:
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);
the kernel oops caused, happened here:
pid: 3357 task: ffff88011c9b7500 cpu: 0 command: "peek" #0 [ffff8800abfc79f0] machine_kexec @ ffffffff8103b5bb #1 [ffff8800abfc7a50] crash_kexec @ ffffffff810c9852 #2 [ffff8800abfc7b20] oops_end @ ffffffff8152e0f0 #3 [ffff8800abfc7b50] no_context @ ffffffff8104c80b #4 [ffff8800abfc7ba0] __bad_area_nosemaphore @ ffffffff8104ca95 #5 [ffff8800abfc7bf0] bad_area @ ffffffff8104cbbe #6 [ffff8800abfc7c20] __do_page_fault @ ffffffff8104d36f #7 [ffff8800abfc7d40] do_page_fault @ ffffffff8153003e #8 [ffff8800abfc7d70] page_fault @ 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
as can see, instruction pointer get_port_reg+18
. since function quite small, here's full disassembly
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)
as might have guessed, i'm of assembly neophyte. line of code get_port_reg+18
? i'm puzzled because i'm calling functions on each line of function call see ioread32()
.
for reference, i've modeled function get_port_reg
after ahci_show_port_cmd()
within same file. not think of other means of getting struct pci_dev
structure necessary on operate. making bad use of get_pci_device()
, pci_dev_put()
? not issue @ all?
thanks help
andy
i going post own answer. 2 commentators of question have put me onto correct path fixing this. mentioned, approach i'd seen done elsewhere in ahci driver (ahci.c). basically, assumption simple, this function in ahci.c required struct device*
, able ata_port
information required. i'd seen, in ahci.c, author had done struct device* = &pdev->dev;
occasionally. in other words, figured dev member of struct pci_dev
getting me needed. apparently unaware of "class types" or similar (see @myaut's first comment). @alexhoppus draws same/similar conclusion based on code , disassembly posted.
the fix have employed, , work nicely, follows:
/* 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 pci device\n"); /* devices driver data */ phost = pci_get_drvdata(ppcidev); if (!phost) { ret = -efault; goto ioctl_valid_pci_dev_out; } /* test, we'll use port 0 */ pport = phost->ports[0]; if (!pport) { ret = -efault; goto ioctl_valid_pci_dev_out; } /* set ret value needs be. * true of __put_user() */ if ((ret = __get_user(off, (u32*)obj))) { printk(kern_info "unable read 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 write user space\n"); } } break; default: // posix compliance 1 (ref of ldd3) ret = -enotty; }
the ahci driver modified thusly well
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);
though has fixed issue me, appreciate explaining me placed in (struct pci_dev)device.dev.p->driver_data. can use, , have, linux cross referencing tools see data types. supposed stored in
struct device_private`? this structure i'm using data need. i'd appreciate commenting on answer explain one.
thanks @myaut , @alexhoppus
Comments
Post a Comment