Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to access and debug a FDT/DTS device tree from a Linux driver (seg-fault)

I have written a Linux kernel-module to act as an FPGA driver for a custom board based off the Freescale P2020RDB. The driver contains several #defines to specify various addresses, sizes, bus-widths, etc. I would like to access the board's Flattened Device Tree (FDT) from within the driver to configure these addresses, so the driver can be used for other boards, where the FPGA has different sizes or resides at different addresses.

I have added the following simple code to my module's initialization function, which code I found while cruising the Linux kernel source code tree:

...
#include <linux/of_device.h>
#include <linux/of_platform.h>

static int __init fpga_init(void) {

  struct device_node *dt_node;
  const u8 *property;
  int len;

  printk(KERN_INFO "(I) FPGA module loaded at 0x%p\n", fpga_init);
  dt_node = of_find_node_by_path("/fpga_dt@c0000000");
  if (!dt_node) {
    printk(KERN_ERR "(E) Failed to find device-tree node: /fpga_dt@c0000000\n");
    return -ENODEV;
  }
  printk(KERN_INFO "(I) Found device-tree node.  Now retrieving property.\n");

  property = of_get_property(dt_node, "reg", &len);

  printk(KERN_INFO "(I) reg=0x%08lX\n", (unsigned long) property[0]);
  ...
  return 0;
}

Unfortunately, inserting the module produces a segmentation fault while just trying to find the device node.

# insmod fpga_drv.ko 
(I) FPGA module loaded at 0xe112d000
Unable to handle kernel paging request for data at address 0x00000000
Faulting instruction address: 0xe112d07c
Oops: Kernel access of bad area, sig: 11 [#1]
SMP NR_CPUS=2 P2020 RDB
Modules linked in: fpga_drv(P+)
NIP: e112d07c LR: e112d078 CTR: c03ed6a4
REGS: df043e10 TRAP: 0300   Tainted: P            (2.6.32.13)
MSR: 00029000 <EE,ME,CE>  CR: 24000222  XER: 20000000
DEAR: 00000000, ESR: 00000000
TASK = dfb85300[1167] 'insmod' THREAD: df042000 CPU: 1
GPR00: e112d078 df043ec0 dfb85300 00000000 e11761f4 c05838c4 00000000 dfffc650 
GPR08: 00000020 00000000 00000012 c03ed6a4 24000282 10098374 1ff92100 10081fc8 
GPR16: 1007a3e0 1007a434 00000000 00000002 00000000 00000000 bfbe6364 4801f468 
GPR24: 10094009 1007ca88 c064d07c 00000000 e112d000 c0690000 e1170000 e1170000 
NIP [e112d07c] fpga_init+0x7c/0x460 [fpga_drv]
LR [e112d078] fpga_init+0x78/0x460 [fpga_drv]
Call Trace:
[df043ec0] [e112d078] fpga_init+0x78/0x460 [fpga_drv] (unreliable)
[df043ef0] [c0001d94] do_one_initcall+0x3c/0x1e8
[df043f20] [c0077720] sys_init_module+0xf8/0x220
[df043f40] [c0010644] ret_from_syscall+0x0/0x3c
Instruction dump:
3860ffed 80010034 bb410018 38210030 7c0803a6 4e800020 3c80e117 38a10008 
388461f4 3fe0e117 4800038d 3fc0e117 <80830000> 3c60e117 386361f8 4cc63182 
---[ end trace 40317dd8a9588d98 ]---
Segmentation fault

What does this indicate? Is there some way to verify that the device tree blob was properly loaded and usable? Do I need some more "setup" code to prepare for such a query? Or, am I trying to use a screwdriver as a hammer?

Thanks!

BTW, Here is my FDT (DTS) source:

/dts-v1/;
/ {
    model = "fsl,P2020";
    compatible = "fsl,P2020RDB";
    #address-cells = <2>;
    #size-cells = <2>;
    ...
    fpga_dt@c0000000 {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "xilinx,xc6vlx240t", "virtex6";
        model = "xilinx,XC6VLX240T";
        reg = <0xc0000000 1 0xc8000000 0x08000000>;
        label = "Xilinx FPGA XC6VLX240T for My Custom Board";
    };
};
like image 1000
Trevor Avatar asked Mar 07 '12 17:03

Trevor


2 Answers

I will specifically answer this question:

Is there some way to verify that the device tree blob was properly loaded and usable?

2 ways to verify if the FDT is correct.

First in u-boot. You can dump the FDT.
For example if your FDT resides in NV memory first copy it to RAM.

cp.b 0xFFF70000 0x800000 0x200


Setup the fdt, then dump the fdt tree (as seen by u-boot)

fdt addr 800000
fdt print


This should work because your node is static. You will easily see if the device tree is incorrect or not at this point.

Second is in kernel but you have to recompile with debugging!

You have to enable CONFIG_DEBUG_FS=y and #define DEBUG in arch/powerpc/kernel/prom.c. This will export the device tree in /proc :-)

There is a third way. You can dump the device tree as the kernel parses it very early during the bootup. The exact method escapes me now. I'm pretty certain you need to recompile the kernel and add a bootarg.

Goodluck.

like image 108
sessyargc.jp Avatar answered Oct 10 '22 10:10

sessyargc.jp


sessyargc.jp's answer was sufficient; however, just for completeness, I wanted to offer a bit of C code that I used to print out some basic tree info from inside the driver:

#include <linux/of_device.h>
#include <linux/of_platform.h>

...
print_device_tree_node(of_find_node_by_path("/"), 0);
...

static void print_device_tree_node(struct device_node *node, int depth) {
  int i = 0;
  struct device_node *child;
  struct property    *properties;
  char                indent[255] = "";

  for(i = 0; i < depth * 3; i++) {
    indent[i] = ' ';
  }
  indent[i] = '\0';
  ++depth;

  for_each_child_of_node(node, child) {
    printk(KERN_INFO "%s{ name = %s\n", indent, child->name);
    printk(KERN_INFO "%s  type = %s\n", indent, child->type);
    for (properties = child->properties; properties != NULL; properties = properties->next) {
      printk(KERN_INFO "%s  %s (%d)\n", indent, properties->name, properties->length);
    }
    print_device_tree_node(child, depth);
    printk(KERN_INFO "%s}\n", indent);
  }
}

I wish I knew how to determine each properties type, so I could format the value and output it correctly. Any suggestions?

Lastly, here is the original snippet, modified oh-so slightly:

char *path = "/fpga_dt@c0000000";
struct device_node *dt_node;
const u32 *property;
int len;

dt_node = of_find_node_by_path(path);
if (!dt_node) {
  printk(KERN_ERR "(E) Failed to find device-tree node: %s\n", path);
  return -ENODEV;
}
printk(KERN_INFO "(I) Found device-tree node.  Now retrieving property.\n");

property = of_get_property(dt_node, "reg", &len);

printk(KERN_INFO "(I) len=%d\n", len);
printk(KERN_INFO "(I) reg[0]=0x%08lX\n", (unsigned long) property[0]);
printk(KERN_INFO "(I) reg[1]=0x%08lX\n", (unsigned long) property[1]);
printk(KERN_INFO "(I) reg[2]=0x%08lX\n", (unsigned long) property[2]);
printk(KERN_INFO "(I) reg[3]=0x%08lX\n", (unsigned long) property[3]);

The seg-fault was happening on some bad device-tree paths. Apparently, there was some typeo error. I ultimately fixed the problem by probing the root path, and then some other basic nodes (like, /cpu0, /memory, etc.), and finally I was able to probe my fpga. I am not sure what really changed, but I am able to reference my FPGA's device-tree node correctly now, when using the above code.

Thanks for all the help! :)

like image 40
Trevor Avatar answered Oct 10 '22 10:10

Trevor