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 instruct device_private`? this structure i'm using data need. i'd appreciate commenting on answer explain one.

thanks @myaut , @alexhoppus


Comments

Popular posts from this blog

android - MPAndroidChart - How to add Annotations or images to the chart -

javascript - Add class to another page attribute using URL id - Jquery -

firefox - Where is 'webgl.osmesalib' parameter? -